Skip to main content

bpi_rs/audio/
music_list.rs

1//! 歌单&音频收藏夹详细信息
2//!
3//! [查看 API 文档](https://github.com/SocialSisterYi/bilibili-API-collect/blob/master/docs/audio/music_list.md)
4use crate::{ BilibiliRequest, BpiClient, BpiError, BpiResponse };
5use serde::{ Deserialize, Serialize };
6
7#[derive(Debug, Clone, Serialize, Deserialize)]
8pub struct AudioCollectionsListData {
9    #[serde(rename = "curPage")]
10    pub cur_page: i32,
11
12    #[serde(rename = "pageCount")]
13    pub page_count: i32,
14
15    #[serde(rename = "totalSize")]
16    pub total_size: i32,
17
18    #[serde(rename = "pageSize")]
19    pub page_size: i32,
20
21    pub data: Vec<AudioCollection>,
22}
23
24#[derive(Debug, Clone, Serialize, Deserialize)]
25pub struct AudioCollection {
26    pub id: i64,
27    pub uid: i64,
28    pub uname: String,
29    pub title: String,
30    pub r#type: i32,
31    pub published: i32,
32    pub cover: String,
33    pub ctime: i64,
34    pub song: i32,
35    pub desc: String,
36    pub sids: Vec<i64>,
37    #[serde(rename = "menuId")]
38    pub menu_id: i64,
39    pub statistic: AudioCollectionStatistic,
40}
41
42#[derive(Debug, Clone, Serialize, Deserialize)]
43pub struct AudioCollectionStatistic {
44    pub sid: i64,
45    pub play: i64,
46    pub collect: i64,
47    pub comment: Option<i64>,
48    pub share: i64,
49}
50
51#[derive(Debug, Clone, Serialize, Deserialize)]
52pub struct AudioHotMenuData {
53    #[serde(rename = "curPage")]
54    pub cur_page: i32,
55
56    #[serde(rename = "pageCount")]
57    pub page_count: i32,
58
59    #[serde(rename = "totalSize")]
60    pub total_size: i32,
61
62    #[serde(rename = "pageSize")]
63    pub page_size: i32,
64
65    pub data: Vec<AudioHotMenu>,
66}
67
68#[derive(Debug, Clone, Serialize, Deserialize)]
69pub struct AudioHotMenu {
70    #[serde(rename = "menuId")]
71    pub menu_id: i64,
72
73    pub uid: i64,
74
75    pub uname: String,
76
77    pub title: String,
78
79    pub cover: String,
80
81    pub intro: String,
82
83    pub r#type: i32,
84
85    pub off: i32,
86
87    pub ctime: i64,
88
89    pub curtime: i64,
90
91    pub statistic: AudioHotMenuStatistic,
92
93    pub snum: i32,
94
95    pub attr: i32,
96
97    #[serde(rename = "isDefault")]
98    pub is_default: i32,
99
100    #[serde(rename = "collectionId")]
101    pub collection_id: i64,
102}
103
104#[derive(Debug, Clone, Serialize, Deserialize)]
105pub struct AudioHotMenuStatistic {
106    pub sid: i64,
107    pub play: i64,
108    pub collect: i64,
109    pub comment: i64,
110    pub share: i64,
111}
112
113#[derive(Debug, Clone, Serialize, Deserialize)]
114pub struct AudioRankMenuData {
115    #[serde(rename = "curPage")]
116    pub cur_page: i32,
117
118    #[serde(rename = "pageCount")]
119    pub page_count: i32,
120
121    #[serde(rename = "totalSize")]
122    pub total_size: i32,
123
124    #[serde(rename = "pageSize")]
125    pub page_size: i32,
126
127    pub data: Vec<AudioRankMenu>,
128}
129
130#[derive(Debug, Clone, Serialize, Deserialize)]
131pub struct AudioRankMenu {
132    #[serde(rename = "menuId")]
133    pub menu_id: i64,
134
135    pub uid: i64,
136
137    pub uname: String,
138
139    pub title: String,
140
141    pub cover: String,
142
143    pub intro: String,
144
145    pub r#type: i32,
146
147    pub off: i32,
148
149    pub ctime: i64,
150
151    pub curtime: i64,
152
153    pub statistic: AudioRankMenuStatistic,
154
155    pub snum: i32,
156
157    pub attr: i32,
158
159    #[serde(rename = "isDefault")]
160    pub is_default: i32,
161
162    #[serde(rename = "collectionId")]
163    pub collection_id: i64,
164
165    pub audios: Vec<AudioRankItem>,
166}
167
168#[derive(Debug, Clone, Serialize, Deserialize)]
169pub struct AudioRankMenuStatistic {
170    pub sid: i64,
171    pub play: i64,
172    pub collect: i64,
173    pub comment: i64,
174    pub share: i64,
175}
176
177#[derive(Debug, Clone, Serialize, Deserialize)]
178pub struct AudioRankItem {
179    pub id: i64,
180    pub title: String,
181    pub duration: i64,
182}
183
184impl BpiClient {
185    /// 查询自己创建的歌单
186    ///
187    /// # 参数
188    /// | 名称   | 类型   | 说明     |
189    /// | ------ | ------ | -------- |
190    /// | `pn`   | u32    | 页码     |
191    /// | `ps`   | u32    | 每页项数 |
192    ///
193    /// # 文档
194    /// [查询自己创建的歌单](https://github.com/SocialSisterYi/bilibili-API-collect/blob/master/docs/audio/music_list.md#查询自己创建的歌单)
195    pub async fn audio_collections_list(
196        &self,
197        pn: u32,
198        ps: u32
199    ) -> Result<BpiResponse<AudioCollectionsListData>, BpiError> {
200        self
201            .get("https://www.bilibili.com/audio/music-service-c/web/collections/list")
202            .query(
203                &[
204                    ("pn", pn.to_string()),
205                    ("ps", ps.to_string()),
206                ]
207            )
208            .send_bpi("查询自己创建的歌单").await
209    }
210
211    /// 查询音频收藏夹信息
212    ///
213    /// # 参数
214    /// | 名称   | 类型   | 说明                                     |
215    /// | ------ | ------ | ---------------------------------------- |
216    /// | `sid`  | u64    | 音频收藏夹 mlid(必须为默认收藏夹 mlid) |
217    ///
218    /// # 文档
219    /// [查询音频收藏夹(默认歌单)信息](https://github.com/SocialSisterYi/bilibili-API-collect/blob/master/docs/audio/music_list.md#查询音频收藏夹默认歌单信息)
220    pub async fn audio_collection_info(
221        &self,
222        sid: u64
223    ) -> Result<BpiResponse<AudioCollection>, BpiError> {
224        self
225            .get("https://www.bilibili.com/audio/music-service-c/web/collections/info")
226            .query(&[("sid", sid.to_string())])
227            .send_bpi("查询音频收藏夹信息").await
228    }
229
230    /// 查询热门歌单
231    ///
232    /// # 参数
233    /// | 名称   | 类型   | 说明     |
234    /// | ------ | ------ | -------- |
235    /// | `pn`   | u32    | 页码     |
236    /// | `ps`   | u32    | 每页项数 |
237    ///
238    /// # 文档
239    /// [查询热门歌单](https://github.com/SocialSisterYi/bilibili-API-collect/blob/master/docs/audio/music_list.md#查询热门歌单)
240    pub async fn audio_hot_menu(
241        &self,
242        pn: u32,
243        ps: u32
244    ) -> Result<BpiResponse<AudioHotMenuData>, BpiError> {
245        self
246            .get("https://www.bilibili.com/audio/music-service-c/web/menu/hit")
247            .query(
248                &[
249                    ("pn", pn.to_string()),
250                    ("ps", ps.to_string()),
251                ]
252            )
253            .send_bpi("查询热门歌单").await
254    }
255
256    /// 查询热门榜单
257    ///
258    /// # 参数
259    /// | 名称   | 类型   | 说明     |
260    /// | ------ | ------ | -------- |
261    /// | `pn`   | u32    | 页码     |
262    /// | `ps`   | u32    | 每页项数 |
263    ///
264    /// # 文档
265    /// [查询热门榜单](https://github.com/SocialSisterYi/bilibili-API-collect/blob/master/docs/audio/music_list.md#查询热门榜单)
266    pub async fn audio_rank_menu(
267        &self,
268        pn: u32,
269        ps: u32
270    ) -> Result<BpiResponse<AudioRankMenuData>, BpiError> {
271        self
272            .get("https://www.bilibili.com/audio/music-service-c/web/menu/rank")
273            .query(
274                &[
275                    ("pn", pn.to_string()),
276                    ("ps", ps.to_string()),
277                ]
278            )
279            .send_bpi("查询热门榜单").await
280    }
281}
282
283#[cfg(test)]
284mod tests {
285    use super::*;
286
287    #[tokio::test]
288    async fn test_audio_collections_list() {
289        let bpi = BpiClient::new();
290        let result = bpi.audio_collections_list(1, 2).await;
291        if let Ok(response) = result {
292            assert_eq!(response.code, 0);
293            let data = response.data.unwrap();
294            assert!(data.cur_page >= 1);
295            assert!(data.page_size > 0);
296        }
297    }
298
299    #[tokio::test]
300    async fn test_audio_collection_info() {
301        let bpi = BpiClient::new();
302        let result = bpi.audio_collection_info(15967839).await;
303        if let Ok(response) = result {
304            assert_eq!(response.code, 0);
305        }
306    }
307
308    #[tokio::test]
309    async fn test_audio_hot_menu() {
310        let bpi = BpiClient::new();
311        let result = bpi.audio_hot_menu(1, 3).await;
312        assert!(result.is_ok());
313        let response = result.unwrap();
314        assert_eq!(response.code, 0);
315        let data = response.data.unwrap();
316
317        assert!(data.cur_page >= 1);
318        assert!(data.page_size > 0);
319        assert!(!data.data.is_empty());
320    }
321
322    #[tokio::test]
323    async fn test_audio_rank_menu() {
324        let bpi = BpiClient::new();
325        let result = bpi.audio_rank_menu(1, 6).await;
326        assert!(result.is_ok());
327        let response = result.unwrap();
328
329        assert_eq!(response.code, 0);
330
331        let data = response.data.unwrap();
332
333        assert!(data.cur_page >= 1);
334        assert!(data.page_size > 0);
335        assert!(!data.data.is_empty());
336        // 检查榜单中的音频信息
337        for menu in &data.data {
338            assert!(!menu.audios.is_empty());
339            for audio in &menu.audios {
340                assert!(audio.id > 0);
341                assert!(!audio.title.is_empty());
342                assert!(audio.duration > 0);
343            }
344        }
345    }
346}