Skip to main content

bpi_rs/user/
search.rs

1//! B站用户搜索相关接口
2//!
3//! [查看 API 文档](https://github.com/SocialSisterYi/bilibili-API-collect/tree/master/docs/user)
4use crate::{ BilibiliRequest, BpiClient, BpiError, BpiResponse };
5use serde::{ Deserialize, Serialize };
6
7// --- 响应数据结构体 ---
8
9/// 合集或课堂统计数据
10#[derive(Debug, Clone, Deserialize, Serialize)]
11pub struct CollectionStat {
12    pub coin: u64,
13    pub danmaku: u64,
14    pub favorite: u64,
15    pub like: u64,
16    pub mtime: u64,
17    pub reply: u64,
18    pub season_id: u64,
19    pub share: u64,
20    pub view: u64,
21    pub vt: u64,
22    pub vv: u64,
23}
24
25/// 所属合集或课堂元数据
26#[derive(Debug, Clone, Deserialize, Serialize)]
27pub struct VideoMeta {
28    pub attribute: u64,
29    pub cover: String,
30    pub ep_count: u64,
31    pub ep_num: u64,
32    pub first_aid: u64,
33    pub id: u64,
34    pub intro: String,
35    pub mid: u64,
36    pub ptime: u64,
37    pub sign_state: u64,
38    pub stat: Option<CollectionStat>,
39    pub title: String,
40}
41
42/// 投稿视频列表项
43#[derive(Debug, Clone, Deserialize, Serialize)]
44pub struct ContributedVideo {
45    pub aid: u64,
46    pub attribute: u64,
47    pub author: String,
48    pub bvid: String,
49    pub comment: u64,
50    pub copyright: String,
51    pub created: u64,
52    pub description: String,
53    pub elec_arc_type: u8,
54    pub enable_vt: u8,
55    pub hide_click: bool,
56    pub is_avoided: u8,
57    pub is_charging_arc: bool,
58    pub is_lesson_video: u8,
59    pub is_lesson_finished: u8,
60    pub is_live_playback: u8,
61    pub is_pay: u8,
62    pub is_self_view: bool,
63    pub is_steins_gate: u8,
64    pub is_union_video: u8,
65    pub jump_url: Option<String>,
66    pub length: String,
67    pub mid: u64,
68    pub meta: Option<VideoMeta>,
69    pub pic: String,
70    pub play: u64,
71    pub playback_position: u64,
72    pub review: u64,
73    pub season_id: u64,
74    pub subtitle: String,
75    pub title: String,
76    pub typeid: u64,
77    pub video_review: u64,
78    pub vt: u64,
79    pub vt_display: String,
80}
81
82/// 投稿视频列表
83#[derive(Debug, Clone, Deserialize, Serialize)]
84pub struct ContributedVideoList {
85    pub slist: Vec<serde_json::Value>,
86    pub tlist: serde_json::Value,
87    pub vlist: Vec<ContributedVideo>,
88}
89
90/// 页面信息
91#[derive(Debug, Clone, Deserialize, Serialize)]
92pub struct PageInfo {
93    pub count: u64,
94    pub pn: u32,
95    pub ps: u32,
96}
97
98/// 播放全部按钮
99#[derive(Debug, Clone, Deserialize, Serialize)]
100pub struct EpisodicButton {
101    pub text: String,
102    pub uri: String,
103}
104
105/// 用户投稿视频明细响应数据
106#[derive(Debug, Clone, Deserialize, Serialize)]
107pub struct ContributedVideosResponseData {
108    /// 列表信息
109    pub list: ContributedVideoList,
110    /// 页面信息
111    pub page: PageInfo,
112    /// “播放全部”按钮
113    pub episodic_button: Option<EpisodicButton>,
114    pub is_risk: bool,
115    pub gaia_res_type: u8,
116    pub gaia_data: Option<serde_json::Value>,
117}
118
119// --- API 实现 ---
120
121impl BpiClient {
122    /// 查询用户投稿视频明细
123    ///
124    /// # 文档
125    /// [查看API文档](https://github.com/SocialSisterYi/bilibili-API-collect/tree/master/docs/user)
126    ///
127    /// # 参数
128    ///
129    /// | 名称 | 类型 | 说明 |
130    /// | ---- | ---- | ---- |
131    /// | `mid` | u64 | 目标用户 UID |
132    /// | `order` | `Option<&str>` | 排序方式,默认 `pubdate` |
133    /// | `tid` | `Option<u64>` | 分区筛选,默认 0 |
134    /// | `keyword` | `Option<&str>` | 关键词筛选 |
135    /// | `pn` | `Option<u32>` | 页码,默认 1 |
136    /// | `ps` | `Option<u32>` | 每页项数,默认 30 |
137    pub async fn user_contributed_videos(
138        &self,
139        mid: u64,
140        order: Option<&str>,
141        tid: Option<u64>,
142        keyword: Option<&str>,
143        pn: Option<u32>,
144        ps: Option<u32>
145    ) -> Result<BpiResponse<ContributedVideosResponseData>, BpiError> {
146        let pn_val = pn.unwrap_or(1);
147        let ps_val = ps.unwrap_or(30);
148        let order_val = order.unwrap_or("pubdate");
149        let tid_val = tid.unwrap_or(0);
150
151        let mut params = vec![
152            ("mid", mid.to_string()),
153            ("order", order_val.to_string()),
154            ("tid", tid_val.to_string()),
155            ("pn", pn_val.to_string()),
156            ("ps", ps_val.to_string())
157        ];
158
159        if let Some(k) = keyword {
160            params.push(("keyword", k.to_string()));
161        }
162
163        let params = self.get_wbi_sign2(params).await?;
164
165        let req = self.get("https://api.bilibili.com/x/space/wbi/arc/search").query(&params);
166
167        req.send_bpi("查询用户投稿视频明细").await
168    }
169}
170
171// --- 测试模块 ---
172
173#[cfg(test)]
174mod tests {
175    use super::*;
176    use tracing::info;
177
178    // 假设这是一个已知用户
179    const TEST_MID: u64 = 53456;
180    const TEST_KEYWORD: &str = "科技";
181
182    #[tokio::test]
183    async fn test_user_contributed_videos_default() -> Result<(), BpiError> {
184        let bpi = BpiClient::new();
185        let resp = bpi.user_contributed_videos(TEST_MID, None, None, None, Some(1), Some(2)).await?;
186        let data = resp.into_data()?;
187
188        info!("用户投稿视频明细: {:?}", data);
189        assert_eq!(data.page.pn, 1);
190        assert_eq!(data.page.ps, 2);
191        assert_eq!(data.list.vlist.len(), 2);
192        assert!(data.page.count > 0);
193
194        Ok(())
195    }
196
197    #[tokio::test]
198    async fn test_user_contributed_videos_with_keyword() -> Result<(), BpiError> {
199        let bpi = BpiClient::new();
200        let resp = bpi.user_contributed_videos(
201            TEST_MID,
202            None,
203            None,
204            Some(TEST_KEYWORD),
205            Some(1),
206            Some(10)
207        ).await?;
208        let data = resp.into_data()?;
209
210        info!("用户投稿视频明细(关键词): {:?}", data);
211        assert!(data.page.count > 0);
212
213        Ok(())
214    }
215}