bpi_rs/audio/
musicstream_url.rs1use crate::{BilibiliRequest, BpiClient, BpiError, BpiResponse};
5use serde::{Deserialize, Serialize};
6
7#[derive(Debug, Clone, Copy, PartialEq, Eq)]
9pub enum AudioQuality {
10 Smooth = 0,
12 Standard = 1,
14 HighQuality = 2,
16 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 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(¶ms)
88 .send_bpi("获取音频流URL(web端)")
89 .await
90 }
91
92 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}