Skip to main content

bpi_rs/web_widget/
header.rs

1//! B站首页头图相关接口
2//!
3//! [查看 API 文档](https://socialsisteryi.github.io/bilibili-API-collect/docs/web_widget/header.html)
4use serde::{ Deserialize, Serialize };
5
6use crate::{ BilibiliRequest, BpiClient, BpiError, BpiResponse };
7
8/// B站首页头图数据
9#[derive(Debug, Clone, Deserialize, Serialize)]
10pub struct HeaderData {
11    /// 空
12    pub name: String,
13    /// 静态头图 URL
14    pub pic: String,
15    /// Bilibili logo URL
16    pub litpic: String,
17    /// 空
18    pub url: String,
19    /// 是否分层, 1: 是
20    pub is_split_layer: u32,
21    /// 分层信息,一个套在字符串里的 JSON 对象
22    pub split_layer: String,
23
24    pub split_layer_obj: Option<SplitLayer>,
25}
26
27impl HeaderData {
28    pub fn parse_split_layer(&mut self) -> Result<(), BpiError> {
29        let result = serde_json::from_str(&self.split_layer);
30        match result {
31            Ok(r) => {
32                self.split_layer_obj = Some(r);
33                Ok(())
34            }
35            Err(e) => Err(BpiError::parse(format!("解析split_layer失败: {:?}", e))),
36        }
37    }
38}
39
40/// 分层信息
41#[derive(Debug, Clone, Deserialize, Serialize)]
42pub struct SplitLayer {
43    /// 版本号
44    pub version: String,
45    /// 层信息
46    pub layers: Vec<Layer>,
47}
48#[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize)]
49#[serde(rename_all = "camelCase")]
50pub struct Layer {
51    pub resources: Vec<Resource>,
52    pub scale: Scale,
53    pub rotate: Rotate,
54    pub translate: Translate,
55    pub blur: Blur,
56    pub opacity: Opacity,
57    pub id: i64,
58    pub name: String,
59}
60
61#[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize)]
62#[serde(rename_all = "camelCase")]
63pub struct Resource {
64    pub src: String,
65    pub id: i64,
66}
67
68#[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize)]
69#[serde(rename_all = "camelCase")]
70pub struct Scale {
71    pub initial: Option<f64>,
72}
73
74#[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize)]
75#[serde(rename_all = "camelCase")]
76pub struct Rotate {
77    pub offset: Option<i64>,
78}
79
80#[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize)]
81#[serde(rename_all = "camelCase")]
82pub struct Translate {
83    pub offset: Option<Vec<i64>>,
84    pub initial: Option<Vec<i64>>,
85}
86
87#[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize)]
88#[serde(rename_all = "camelCase")]
89pub struct Blur {
90    pub initial: Option<i64>,
91}
92
93#[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize)]
94#[serde(rename_all = "camelCase")]
95pub struct Opacity {
96    pub wrap: String,
97    pub initial: Option<f64>,
98}
99
100impl BpiClient {
101    /// 获取首页头图
102    ///
103    /// # 文档
104    /// [查看API文档](https://socialsisteryi.github.io/bilibili-API-collect/docs/web_widget/header.html#获取首页头图)
105    ///
106    pub async fn web_widget_header_page(&self) -> Result<BpiResponse<HeaderData>, BpiError> {
107        let mut result = self
108            .get("https://api.bilibili.com/x/web-show/page/header")
109            .query(&[("resource_id", 142)])
110            .send_bpi("获取首页头图").await?;
111        let mut header: HeaderData = result.data.take().ok_or_else(|| BpiError::missing_data())?;
112
113        header.parse_split_layer()?;
114
115        // 将解析后的数据放回 response
116        result.data = Some(header);
117
118        Ok(result)
119    }
120}
121
122#[cfg(test)]
123mod tests {
124    use super::*;
125    use tracing::info;
126
127    #[tokio::test]
128    async fn test_get_header_page() {
129        let bpi = BpiClient::new();
130        let resp = bpi.web_widget_header_page().await;
131        info!("响应: {:?}", resp);
132        assert!(resp.is_ok());
133    }
134}