Skip to main content

bpi_rs/audio/
musicstream_url.rs

1//! 音频流URL
2//!
3//! [查看 API 文档](https://github.com/Yuelioi/bilibili-API-collect/tree/cfc5fddcc8a94b74d91970bb5b4eaeb349addc47/docs/audio/musicstream_url.md)
4use crate::{ BilibiliRequest, BpiClient, BpiError, BpiResponse };
5use serde::{ Deserialize, Serialize };
6
7/// 音质参数定义
8#[derive(Debug, Clone, Copy, PartialEq, Eq)]
9pub enum AudioQuality {
10    /// 流畅 128K
11    Smooth = 0,
12    /// 标准 192K
13    Standard = 1,
14    /// 高品质 320K
15    HighQuality = 2,
16    /// 无损 FLAC (大会员)
17    Lossless = 3,
18}
19
20impl AudioQuality {
21    pub fn as_u32(self) -> u32 {
22        self as u32
23    }
24}
25
26#[derive(Debug, Clone, Serialize, Deserialize)]
27pub struct AudioStreamUrlWebData {
28    pub sid: u64,
29    pub r#type: u32,
30    pub info: String,
31    pub timeout: u64,
32    pub size: u64,
33    pub cdns: Vec<String>,
34    pub qualities: Option<serde_json::Value>,
35    pub title: String,
36    pub cover: String,
37}
38
39#[derive(Debug, Clone, Serialize, Deserialize)]
40pub struct AudioStreamUrlData {
41    pub sid: u64,
42    pub r#type: u32,
43    pub info: String,
44    pub timeout: u64,
45    pub size: u64,
46    pub cdns: Vec<String>,
47    pub qualities: Vec<AudioQualityInfo>,
48    pub title: String,
49    pub cover: String,
50}
51
52#[derive(Debug, Clone, Serialize, Deserialize)]
53pub struct AudioQualityInfo {
54    pub r#type: u32,
55    pub desc: String,
56    pub size: u64,
57    pub bps: String,
58    pub tag: String,
59    pub require: u32,
60    pub requiredesc: String,
61}
62
63impl BpiClient {
64    /// 获取音频流 URL (web端)
65    ///
66    /// 注:web端无法播放完整付费歌曲,付费歌曲为 30s 试听片段
67    /// 本接口仅能获取 192K 音质的音频
68    ///
69    /// # 参数
70    /// | 名称   | 类型  | 说明         |
71    /// | ------ | ----- | ------------ |
72    /// | `sid`  | u64   | 音频 auid    |
73    ///
74    /// # 文档
75    /// [获取音频流URL(web端)](https://github.com/Yuelioi/bilibili-API-collect/tree/cfc5fddcc8a94b74d91970bb5b4eaeb349addc47/docs/audio/musicstream_url.md#获取音频流urlweb端)
76    pub async fn audio_stream_url_web(
77        &self,
78        sid: u64
79    ) -> Result<BpiResponse<AudioStreamUrlWebData>, BpiError> {
80        let params = [
81            ("sid", sid.to_string()),
82            ("quality", "2".to_string()),
83            ("privilege", "2".to_string()),
84        ];
85
86        self
87            .get("https://www.bilibili.com/audio/music-service-c/web/url")
88            .query(&params)
89            .send_bpi("获取音频流URL(web端)").await
90    }
91
92    /// 获取音频流 URL(可获取付费音频)
93    ///
94    /// 注:付费音乐需要有带大会员或音乐包的账号登录(Cookie或 APP),否则为试听片段
95    /// 无损音质需要登录的用户为会员
96    ///
97    /// # 参数
98    /// | 名称     | 类型          | 说明               |
99    /// | -------- | ------------- | ----------------- |
100    /// | `songid` | u64           | 音频 auid         |
101    /// | `quality`| AudioQuality  | 音质代码           |
102    ///
103    /// # 文档
104    /// [获取音频流URL(可获取付费音频)](https://github.com/Yuelioi/bilibili-API-collect/tree/cfc5fddcc8a94b74d91970bb5b4eaeb349addc47/docs/audio/musicstream_url.md#获取音频流url可获取付费音频)
105    pub async fn audio_stream_url(
106        &self,
107        songid: u64,
108        quality: AudioQuality
109    ) -> Result<BpiResponse<AudioStreamUrlData>, BpiError> {
110        self
111            .get("https://api.bilibili.com/audio/music-service-c/url")
112            .with_bilibili_headers()
113            .query(
114                &[
115                    ("songid", songid.to_string()),
116                    ("quality", quality.as_u32().to_string()),
117                    ("privilege", "2".to_string()),
118                    ("mid", "2".to_string()),
119                    ("platform", "android".to_string()),
120                ]
121            )
122            .send_bpi("获取音频流URL").await
123    }
124}
125
126#[cfg(test)]
127mod tests {
128    use super::*;
129
130    const TEST_SID: u64 = 13603;
131
132    #[tokio::test]
133    async fn test_audio_stream_url_web() {
134        let bpi = BpiClient::new();
135        let result = bpi.audio_stream_url_web(TEST_SID).await;
136        assert!(result.is_ok());
137        let response = result.unwrap();
138        assert_eq!(response.code, 0);
139        let data = response.data.unwrap();
140
141        assert_eq!(data.sid, TEST_SID);
142        assert!(data.timeout > 0);
143        assert!(!data.cdns.is_empty());
144    }
145
146    #[tokio::test]
147    async fn test_audio_stream_url() {
148        let bpi = BpiClient::new();
149        let result = bpi.audio_stream_url(15664, AudioQuality::HighQuality).await;
150        assert!(result.is_ok());
151        let response = result.unwrap();
152        assert_eq!(response.code, 0);
153        let data = response.data.unwrap();
154        assert_eq!(data.sid, 15664);
155        assert!(data.timeout > 0);
156        assert!(!data.cdns.is_empty());
157        assert!(!data.qualities.is_empty());
158    }
159}