bpi_rs/audio/
musicstream_url.rs

1//! 音频流URL
2//!
3//! https://github.com/SocialSisterYi/bilibili-API-collect/blob/master/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/SocialSisterYi/bilibili-API-collect/blob/master/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.get("https://www.bilibili.com/audio/music-service-c/web/url")
87            .query(&params)
88            .send_bpi("获取音频流URL(web端)")
89            .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/SocialSisterYi/bilibili-API-collect/blob/master/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.get("https://api.bilibili.com/audio/music-service-c/url")
111            .with_bilibili_headers()
112            .query(&[
113                ("songid", songid.to_string()),
114                ("quality", quality.as_u32().to_string()),
115                ("privilege", "2".to_string()),
116                ("mid", "2".to_string()),
117                ("platform", "android".to_string()),
118            ])
119            .send_bpi("获取音频流URL")
120            .await
121    }
122}
123
124#[cfg(test)]
125mod tests {
126    use super::*;
127
128    const TEST_SID: u64 = 13603;
129
130    #[tokio::test]
131    async fn test_audio_stream_url_web() {
132        let bpi = BpiClient::new();
133        let result = bpi.audio_stream_url_web(TEST_SID).await;
134        assert!(result.is_ok());
135        let response = result.unwrap();
136        assert_eq!(response.code, 0);
137        let data = response.data.unwrap();
138
139        assert_eq!(data.sid, TEST_SID);
140        assert!(data.timeout > 0);
141        assert!(!data.cdns.is_empty());
142    }
143
144    #[tokio::test]
145    async fn test_audio_stream_url() {
146        let bpi = BpiClient::new();
147        let result = bpi.audio_stream_url(15664, AudioQuality::HighQuality).await;
148        assert!(result.is_ok());
149        let response = result.unwrap();
150        assert_eq!(response.code, 0);
151        let data = response.data.unwrap();
152        assert_eq!(data.sid, 15664);
153        assert!(data.timeout > 0);
154        assert!(!data.cdns.is_empty());
155        assert!(!data.qualities.is_empty());
156    }
157}