Skip to main content

bpi_rs/video/info/
detail.rs

1//! 获取视频超详细信息(Web端)
2//!
3//! [查看 API 文档](https://github.com/SocialSisterYi/bilibili-API-collect/tree/master/docs/video)
4
5use crate::models::{ LevelInfo, Nameplate, Official, OfficialVerify, Pendant, VipLabel };
6use crate::{ BilibiliRequest, BpiClient, BpiError, BpiResponse };
7use serde::{ Deserialize, Serialize };
8
9#[derive(Debug, Deserialize, Serialize)]
10pub struct VideoDetailData {
11    #[serde(rename = "View")]
12    pub view: View,
13    #[serde(rename = "Card")]
14    pub card: Card,
15    #[serde(rename = "Tags")]
16    pub tags: Vec<Tag>,
17    #[serde(rename = "Reply")]
18    pub reply: Reply,
19
20    #[serde(rename = "Related")]
21    pub related: Vec<Related>,
22
23    #[serde(rename = "Spec")]
24    pub spec: Option<serde_json::Value>,
25    pub hot_share: HotShare,
26    pub elec: Option<serde_json::Value>,
27    pub emergency: Emergency,
28
29    pub view_addit: ViewAddit,
30    pub guide: Option<serde_json::Value>,
31    pub query_tags: Option<serde_json::Value>,
32    pub participle: Option<serde_json::Value>,
33    pub module_ctrl: Option<serde_json::Value>,
34    pub replace_recommend: bool,
35    pub is_hit_labour_day_activity: bool,
36}
37
38#[derive(Debug, Deserialize, Serialize)]
39pub struct View {
40    pub bvid: String,
41    pub aid: i64,
42    pub videos: i32,
43    pub tid: i32,
44    pub tid_v2: i32,
45    pub tname: String,
46    pub tname_v2: String,
47    pub copyright: i32,
48    pub pic: String,
49    pub title: String,
50    pub pubdate: i64,
51    pub ctime: i64,
52    pub desc: String,
53    pub desc_v2: Vec<DescV2>,
54    pub state: i32,
55    pub duration: i32,
56    pub rights: Rights,
57    pub owner: Owner,
58    pub stat: Stat,
59    pub argue_info: ArgueInfo,
60    pub dynamic: String,
61    pub cid: i64,
62    pub dimension: Dimension,
63    pub premiere: Option<serde_json::Value>,
64
65    pub teenage_mode: i32,
66    pub is_chargeable_season: bool,
67    pub is_story: bool,
68    pub is_upower_exclusive: bool,
69    pub is_upower_play: bool,
70    pub is_upower_preview: bool,
71    pub enable_vt: i32,
72    pub vt_display: String,
73    pub is_upower_exclusive_with_qa: bool,
74    pub no_cache: bool,
75    pub pages: Vec<Page>,
76
77    pub subtitle: Subtitle,
78    pub is_season_display: bool,
79    pub user_garb: UserGarb,
80    pub honor_reply: serde_json::Value,
81    pub like_icon: String,
82    pub need_jump_bv: bool,
83    pub disable_show_up_info: bool,
84    pub is_story_play: i32,
85    pub is_view_self: bool,
86}
87
88#[derive(Debug, Deserialize, Serialize)]
89pub struct DescV2 {
90    pub raw_text: String,
91    #[serde(rename = "type")]
92    pub desc_type: i32,
93    pub biz_id: i32,
94}
95
96#[derive(Debug, Deserialize, Serialize)]
97pub struct Rights {
98    pub bp: i32,
99    pub elec: i32,
100    pub download: i32,
101    pub movie: i32,
102    pub pay: i32,
103    pub hd5: i32,
104    pub no_reprint: i32,
105    pub autoplay: i32,
106    pub ugc_pay: i32,
107    pub is_cooperation: i32,
108    pub ugc_pay_preview: i32,
109    pub no_background: i32,
110    pub clean_mode: i32,
111    pub is_stein_gate: i32,
112    pub is_360: i32,
113    pub no_share: i32,
114    pub arc_pay: i32,
115    pub free_watch: i32,
116}
117
118#[derive(Debug, Deserialize, Serialize)]
119pub struct Owner {
120    pub mid: i64,
121    pub name: String,
122    pub face: String,
123}
124
125#[derive(Debug, Deserialize, Serialize)]
126pub struct Stat {
127    pub aid: i64,
128    pub view: i32,
129    pub danmaku: i32,
130    pub reply: i32,
131    pub favorite: i32,
132    pub coin: i32,
133    pub share: i32,
134    pub now_rank: i32,
135    pub his_rank: i32,
136    pub like: i32,
137    pub dislike: i32,
138    pub evaluation: String,
139    pub vt: i32,
140}
141
142#[derive(Debug, Deserialize, Serialize)]
143pub struct ArgueInfo {
144    pub argue_msg: String,
145    pub argue_type: i32,
146    pub argue_link: String,
147}
148
149#[derive(Debug, Deserialize, Serialize)]
150pub struct Dimension {
151    pub width: i32,
152    pub height: i32,
153    pub rotate: i32,
154}
155
156#[derive(Debug, Deserialize, Serialize)]
157pub struct Page {
158    pub cid: i64,
159    pub page: i32,
160    pub from: String,
161    pub part: String,
162    pub duration: i32,
163    pub vid: String,
164    pub weblink: String,
165    pub dimension: Dimension,
166    pub first_frame: Option<String>,
167    pub ctime: i64,
168}
169
170#[derive(Debug, Deserialize, Serialize)]
171pub struct Subtitle {
172    pub allow_submit: bool,
173    pub list: Vec<serde_json::Value>,
174}
175
176#[derive(Debug, Deserialize, Serialize)]
177pub struct UserGarb {
178    pub url_image_ani_cut: String,
179}
180
181#[derive(Debug, Deserialize, Serialize)]
182pub struct Card {
183    pub card: CardInfo,
184    pub space: Space,
185    pub following: bool,
186    pub archive_count: i32,
187    pub article_count: i32,
188    pub follower: i32,
189    pub like_num: i32,
190}
191
192#[derive(Debug, Deserialize, Serialize)]
193pub struct CardInfo {
194    pub mid: String,
195    pub name: String,
196    pub approve: bool,
197    pub sex: String,
198    pub rank: String,
199    pub face: String,
200    pub face_nft: i32,
201    pub face_nft_type: i32,
202    #[serde(rename = "DisplayRank")]
203    pub display_rank: String,
204    pub regtime: i64,
205    pub spacesta: i32,
206    pub birthday: String,
207    pub place: String,
208    pub description: String,
209    pub article: i32,
210    pub attentions: Vec<serde_json::Value>,
211    pub fans: i32,
212    pub friend: i32,
213    pub attention: i32,
214    pub sign: String,
215    pub level_info: LevelInfo,
216    pub pendant: Pendant,
217    pub nameplate: Nameplate,
218    #[serde(rename = "Official")]
219    pub official: Official,
220    pub official_verify: OfficialVerify,
221    pub vip: VIP,
222    pub is_senior_member: i32,
223    pub name_render: Option<serde_json::Value>,
224}
225
226#[derive(Debug, Deserialize, Serialize)]
227pub struct VIP {
228    #[serde(rename = "type")]
229    pub vip_type: i32,
230    pub status: i32,
231    pub due_date: i64,
232    pub vip_pay_type: i32,
233    pub theme_type: i32,
234    pub label: VipLabel,
235    pub avatar_subscript: i32,
236    pub nickname_color: String,
237    pub role: i32,
238    pub avatar_subscript_url: String,
239    pub tv_vip_status: i32,
240    pub tv_vip_pay_type: i32,
241    pub tv_due_date: i64,
242    pub avatar_icon: AvatarIcon,
243    #[serde(rename = "vipType")]
244    pub vip_type_alt: i32,
245    #[serde(rename = "vipStatus")]
246    pub vip_status_alt: i32,
247}
248
249#[derive(Debug, Deserialize, Serialize)]
250pub struct AvatarIcon {
251    pub icon_resource: serde_json::Value,
252}
253
254#[derive(Debug, Deserialize, Serialize)]
255pub struct Space {
256    pub s_img: String,
257    pub l_img: String,
258}
259
260#[derive(Debug, Deserialize, Serialize)]
261pub struct Tag {
262    pub tag_id: i32,
263    pub tag_name: String,
264    pub music_id: String,
265    pub tag_type: String,
266    pub jump_url: String,
267}
268
269#[derive(Debug, Deserialize, Serialize)]
270pub struct Reply {
271    pub page: Option<serde_json::Value>,
272    pub replies: Vec<ReplyItem>,
273}
274
275#[derive(Debug, Deserialize, Serialize)]
276pub struct ReplyItem {
277    pub rpid: i32,
278    pub oid: i32,
279    #[serde(rename = "type")]
280    pub reply_type: i32,
281    pub mid: i32,
282    pub root: i32,
283    pub parent: i32,
284    pub dialog: i32,
285    pub count: i32,
286    pub rcount: i32,
287    pub state: i32,
288    pub fansgrade: i32,
289    pub attr: i32,
290    pub ctime: i64,
291    pub like: i32,
292    pub action: i32,
293    pub content: Option<serde_json::Value>,
294    pub replies: Option<Vec<ReplyItem>>,
295    pub assist: i32,
296    pub show_follow: bool,
297}
298
299#[derive(Debug, Deserialize, Serialize)]
300pub struct Related {
301    pub aid: i64,
302    pub videos: i32,
303    pub tid: i32,
304    pub tname: String,
305    pub copyright: i32,
306    pub pic: String,
307    pub title: String,
308    pub pubdate: i64,
309    pub ctime: i64,
310    pub desc: String,
311    pub state: i32,
312    pub duration: i32,
313    pub mission_id: Option<i32>,
314    pub rights: RelatedRights,
315    pub owner: Owner,
316    pub stat: RelatedStat,
317    pub dynamic: String,
318
319    pub cid: i64,
320    pub dimension: Dimension,
321    pub short_link_v2: String,
322    pub up_from_v2: Option<i32>,
323    pub first_frame: Option<String>,
324
325    pub pub_location: Option<String>,
326    pub cover43: String,
327    pub tidv2: i32,
328    pub tnamev2: String,
329    pub pid_v2: i32,
330
331    pub pid_name_v2: String,
332    pub bvid: String,
333    pub season_type: i32,
334    pub season_id: Option<i32>,
335    pub is_ogv: bool,
336    pub ogv_info: Option<serde_json::Value>,
337    pub rcmd_reason: String,
338    pub enable_vt: i32,
339    pub ai_rcmd: AIRcmd,
340}
341
342#[derive(Debug, Deserialize, Serialize)]
343pub struct RelatedRights {
344    pub bp: i32,
345    pub elec: i32,
346    pub download: i32,
347    pub movie: i32,
348    pub pay: i32,
349    pub hd5: i32,
350    pub no_reprint: i32,
351    pub autoplay: i32,
352    pub ugc_pay: i32,
353    pub is_cooperation: i32,
354    pub ugc_pay_preview: i32,
355    pub no_background: i32,
356    pub arc_pay: i32,
357    pub pay_free_watch: Option<i32>,
358}
359
360#[derive(Debug, Deserialize, Serialize)]
361pub struct RelatedStat {
362    pub aid: i64,
363    pub view: i32,
364    pub danmaku: i32,
365    pub reply: i32,
366    pub favorite: i32,
367    pub coin: i32,
368    pub share: i32,
369    pub now_rank: i32,
370    pub his_rank: i32,
371    pub like: i32,
372    pub dislike: i32,
373    pub vt: i32,
374    pub vv: i32,
375    pub fav_g: i32,
376    pub like_g: i32,
377}
378
379#[derive(Debug, Deserialize, Serialize)]
380pub struct AIRcmd {
381    pub id: i64,
382    pub goto: String,
383    pub trackid: String,
384    pub uniq_id: String,
385}
386
387#[derive(Debug, Deserialize, Serialize)]
388pub struct HotShare {
389    pub show: bool,
390    pub list: Vec<serde_json::Value>,
391}
392
393#[derive(Debug, Deserialize, Serialize)]
394pub struct Emergency {
395    pub no_like: bool,
396    pub no_coin: bool,
397    pub no_fav: bool,
398    pub no_share: bool,
399}
400
401#[derive(Debug, Deserialize, Serialize)]
402pub struct ViewAddit {
403    #[serde(rename = "63")]
404    pub field_63: bool,
405    #[serde(rename = "64")]
406    pub field_64: bool,
407    #[serde(rename = "69")]
408    pub field_69: bool,
409    #[serde(rename = "71")]
410    pub field_71: bool,
411    #[serde(rename = "72")]
412    pub field_72: bool,
413}
414
415/// 获取视频详细信息响应类型
416pub type VideoDetailResponse = BpiResponse<VideoDetailData>;
417
418impl BpiClient {
419    /// 获取视频超详细信息 (Web端)
420    ///
421    /// # 文档
422    /// [查看API文档](https://socialsisteryi.github.io/bilibili-API-collect/docs/video/video.html#获取视频超详细信息-web端)
423    ///
424    /// # 参数
425    /// | 名称        | 类型         | 说明                 |
426    /// | ----------- | ------------| -------------------- |
427    /// | `aid`       | `Option<u64>` | 稿件 avid,可选      |
428    /// | `bvid`      | `Option<&str>`| 稿件 bvid,可选      |
429    /// | `need_elec` | `Option<u8>`  | 是否获取充电信息 0否 1是,可选 |
430    ///
431    /// `aid` 和 `bvid` 二选一
432    pub async fn video_detail(
433        &self,
434        aid: Option<u64>,
435        bvid: Option<&str>,
436        need_elec: Option<u8>
437    ) -> Result<VideoDetailResponse, BpiError> {
438        let aid = aid.map(|aid| aid.to_string());
439        let bvid = bvid.map(|bvid| bvid.to_string());
440        let need_elec = need_elec.map(|need_elec| need_elec.to_string());
441
442        self
443            .get("https://api.bilibili.com/x/web-interface/view/detail")
444            .query(
445                &[
446                    ("aid", aid),
447                    ("bvid", bvid),
448                    ("need_elec", need_elec),
449                ]
450            )
451            .send_bpi("视频超详细信息").await
452    }
453}
454
455#[cfg(test)]
456mod tests {
457    use super::*;
458
459    #[tokio::test]
460    async fn test_video_detail() {
461        let bpi = BpiClient::new();
462
463        let aid = Some(10001);
464        // let aid = Some(114993303389765);
465        let bvid = None;
466
467        match bpi.video_detail(aid, bvid, Some(0)).await {
468            Ok(resp) => {
469                if resp.code == 0 {
470                    // tracing::info!("视频标题: {}", resp.data.view.title);
471                    // tracing::info!("回复: {:?} )", resp.data.reply.page);
472                } else {
473                    tracing::info!("请求失败: code={}, message={}", resp.code, resp.message);
474                }
475            }
476            Err(err) => {
477                panic!("请求出错: {}", err);
478            }
479        }
480    }
481}