Skip to main content

bpi_rs/article/
card.rs

1//! 卡片信息
2//!
3//! [查看 API 文档](https://github.com/Yuelioi/bilibili-API-collect/tree/cfc5fddcc8a94b74d91970bb5b4eaeb349addc47/docs/article/card.md)
4
5use super::models::{ ArticleAuthor, ArticleCategory, ArticleMedia, ArticleStats };
6use crate::{ BilibiliRequest, BpiClient, BpiError, BpiResponse };
7use serde::{ Deserialize, Serialize };
8
9/// 卡片信息响应类型
10pub type CardResponse = BpiResponse<std::collections::HashMap<String, CardItem>>;
11
12/// 卡片项目(可以是视频、专栏或直播间)
13#[derive(Debug, Clone, Serialize, Deserialize)]
14#[serde(untagged)]
15pub enum CardItem {
16    /// 视频卡片
17    Video(VideoCard),
18    /// 专栏卡片
19    Article(ArticleCard),
20    /// 直播间卡片
21    Live(LiveCard),
22
23    /// 未知卡片类型
24    Unknown(serde_json::Value),
25}
26
27/// 视频卡片
28#[derive(Debug, Clone, Serialize, Deserialize)]
29pub struct VideoCard {
30    /// 视频aid
31    pub aid: i64,
32    /// 视频bvid
33    pub bvid: String,
34    /// 视频cid
35    pub cid: i64,
36    /// 版权信息
37    pub copyright: i32,
38    /// 封面图片
39    pub pic: String,
40    /// 创建时间
41    pub ctime: i64,
42    /// 视频描述
43    pub desc: String,
44    /// 视频尺寸信息
45    pub dimension: VideoDimension,
46    /// 视频时长
47    pub duration: i64,
48    /// 动态内容
49    pub dynamic: String,
50    /// UP主信息
51    pub owner: VideoOwner,
52    /// 发布时间
53    pub pubdate: i64,
54    /// 视频权限
55    pub rights: VideoRights,
56    /// 短链接
57    pub short_link_v2: String,
58    /// 视频统计信息
59    pub stat: VideoStat,
60    /// 视频状态
61    pub state: i32,
62    /// 分区ID
63    pub tid: i32,
64    /// 视频标题
65    pub title: String,
66    /// 分区名称
67    pub tname: String,
68    /// 分P数量
69    pub videos: i32,
70    /// VT开关
71    pub vt_switch: bool,
72}
73
74/// 视频尺寸信息
75#[derive(Debug, Clone, Serialize, Deserialize)]
76pub struct VideoDimension {
77    /// 高度
78    pub height: i32,
79    /// 旋转角度
80    pub rotate: i32,
81    /// 宽度
82    pub width: i32,
83}
84
85/// 视频UP主信息
86#[derive(Debug, Clone, Serialize, Deserialize)]
87pub struct VideoOwner {
88    /// UP主头像
89    pub face: String,
90    /// UP主mid
91    pub mid: i64,
92    /// UP主昵称
93    pub name: String,
94}
95
96/// 视频权限信息
97#[derive(Debug, Clone, Serialize, Deserialize)]
98pub struct VideoRights {
99    /// 是否付费
100    pub arc_pay: i32,
101    /// 是否自动播放
102    pub autoplay: i32,
103    /// 是否可充电
104    pub bp: i32,
105    /// 是否可下载
106    pub download: i32,
107    /// 是否可充电
108    pub elec: i32,
109    /// 是否高清
110    pub hd5: i32,
111    /// 是否合作视频
112    pub is_cooperation: i32,
113    /// 是否电影
114    pub movie: i32,
115    /// 是否无背景
116    pub no_background: i32,
117    /// 是否禁止转载
118    pub no_reprint: i32,
119    /// 是否付费
120    pub pay: i32,
121    /// 是否付费观看
122    pub pay_free_watch: i32,
123    /// 是否UGC付费
124    pub ugc_pay: i32,
125    /// 是否UGC付费预览
126    pub ugc_pay_preview: i32,
127}
128
129/// 视频统计信息
130#[derive(Debug, Clone, Serialize, Deserialize)]
131pub struct VideoStat {
132    /// 视频aid
133    pub aid: i64,
134    /// 投币数
135    pub coin: i64,
136    /// 弹幕数
137    pub danmaku: i64,
138    /// 点踩数
139    pub dislike: i64,
140    /// 收藏数
141    pub favorite: i64,
142    /// 历史排名
143    pub his_rank: i32,
144    /// 点赞数
145    pub like: i64,
146    /// 当前排名
147    pub now_rank: i32,
148    /// 评论数
149    pub reply: i64,
150    /// 分享数
151    pub share: i64,
152    /// 播放数
153    pub view: i64,
154    /// VT值
155    pub vt: i32,
156    /// VV值
157    pub vv: i32,
158}
159
160/// 专栏卡片
161#[derive(Debug, Clone, Serialize, Deserialize)]
162pub struct ArticleCard {
163    /// 活动ID
164    pub act_id: i64,
165    /// 申请时间
166    pub apply_time: String,
167    /// 属性
168    pub attributes: i32,
169    /// 认证标记
170    #[serde(rename = "authenMark")]
171    pub authen_mark: Option<serde_json::Value>,
172    /// 作者信息
173    pub author: ArticleAuthor,
174    /// 横幅URL
175    pub banner_url: String,
176    /// 分类列表
177    pub categories: Vec<ArticleCategory>,
178    /// 主分类
179    pub category: ArticleCategory,
180    /// 审核状态
181    pub check_state: i32,
182    /// 审核时间
183    pub check_time: String,
184    /// 内容图片列表
185    pub content_pic_list: Option<serde_json::Value>,
186    /// 封面视频ID
187    pub cover_avid: i64,
188    /// 创建时间
189    pub ctime: i64,
190    /// 争议信息
191    pub dispute: Option<serde_json::Value>,
192    /// 动态内容
193    pub dynamic: String,
194    /// 专栏ID
195    pub id: i64,
196    /// 图片URL列表
197    pub image_urls: Vec<String>,
198    /// 是否点赞
199    pub is_like: bool,
200    /// 文集信息
201    pub list: Option<ArticleList>,
202    /// 媒体信息
203    pub media: ArticleMedia,
204    /// 修改时间
205    pub mtime: i64,
206    /// 原始图片URL列表
207    pub origin_image_urls: Vec<String>,
208    /// 原始模板ID
209    pub origin_template_id: i32,
210    /// 是否原创
211    pub original: i32,
212    /// 是否私密发布
213    pub private_pub: i32,
214    /// 发布时间
215    pub publish_time: i64,
216    /// 是否转载
217    pub reprint: i32,
218    /// 状态
219    pub state: i32,
220    /// 统计信息
221    pub stats: ArticleStats,
222    /// 摘要
223    pub summary: String,
224    /// 模板ID
225    pub template_id: i32,
226    /// 标题
227    pub title: String,
228    /// 顶部视频信息
229    pub top_video_info: Option<serde_json::Value>,
230    /// 类型
231    pub r#type: i32,
232    /// 字数
233    pub words: i64,
234}
235
236/// 作者VIP信息
237#[derive(Debug, Clone, Serialize, Deserialize)]
238pub struct AuthorVip {
239    /// 头像订阅
240    pub avatar_subscript: i32,
241    /// 到期时间
242    pub due_date: i64,
243    /// 标签信息
244    pub label: VipLabel,
245    /// 昵称颜色
246    pub nickname_color: String,
247    /// VIP状态
248    pub status: i32,
249    /// 主题类型
250    pub theme_type: i32,
251    /// VIP类型
252    pub r#type: i32,
253    /// 支付类型
254    pub vip_pay_type: i32,
255}
256
257/// VIP标签
258#[derive(Debug, Clone, Serialize, Deserialize)]
259pub struct VipLabel {
260    /// 标签主题
261    pub label_theme: String,
262    /// 标签路径
263    pub path: String,
264    /// 标签文本
265    pub text: String,
266}
267
268/// 专栏文集信息
269#[derive(Debug, Clone, Serialize, Deserialize)]
270pub struct ArticleList {
271    /// 申请时间
272    pub apply_time: String,
273    /// 文章数量
274    pub articles_count: i32,
275    /// 审核时间
276    pub check_time: String,
277    /// 创建时间
278    pub ctime: i64,
279    /// 文集ID
280    pub id: i64,
281    /// 文集图片
282    pub image_url: String,
283    /// 作者ID
284    pub mid: i64,
285    /// 文集名称
286    pub name: String,
287    /// 发布时间
288    pub publish_time: i64,
289    /// 阅读量
290    pub read: i64,
291    /// 原因
292    pub reason: String,
293    /// 状态
294    pub state: i32,
295    /// 摘要
296    pub summary: String,
297    /// 更新时间
298    pub update_time: i64,
299    /// 字数
300    pub words: i64,
301}
302
303/// 直播间卡片
304#[derive(Debug, Clone, Serialize, Deserialize)]
305pub struct LiveCard {
306    /// 分区完整名称
307    pub area_v2_name: String,
308    /// 直播封面
309    pub cover: String,
310    /// 主播头像
311    pub face: String,
312    /// 直播状态
313    pub live_status: i32,
314    /// 在线人数
315    pub online: i64,
316    /// 挂件RU
317    pub pendent_ru: String,
318    /// 挂件RU颜色
319    pub pendent_ru_color: String,
320    /// 挂件RU图片
321    pub pendent_ru_pic: String,
322    /// 角色
323    pub role: i32,
324    /// 直播间长ID
325    pub room_id: i64,
326    /// 直播间标题
327    pub title: String,
328    /// 主播UID
329    pub uid: i64,
330    /// 主播用户名
331    pub uname: String,
332}
333
334impl BpiClient {
335    /// 获取专栏显示卡片信息
336    ///
337    /// # 参数
338    /// | 名称   | 类型    | 说明                                                                 |
339    /// | ------ | ------- | -------------------------------------------------------------------- |
340    /// | `ids`  | String  | 被查询的 id 列表,以逗号分隔;可填视频完整 AV/BV 号、专栏 CV 号、直播间长/短 lv 号 |
341    ///
342    /// # 文档
343    /// [获取专栏显示卡片信息](https://github.com/Yuelioi/bilibili-API-collect/tree/cfc5fddcc8a94b74d91970bb5b4eaeb349addc47/docs/article/card.md#获取专栏显示卡片信息)
344    pub async fn article_cards(&self, ids: &str) -> Result<CardResponse, BpiError> {
345        let params = vec![("ids", ids.to_string()), ("web_location", "333.1305".to_string())];
346
347        let params = self.get_wbi_sign2(params).await?;
348
349        let result: CardResponse = self
350            .get("https://api.bilibili.com/x/article/cards")
351            .with_bilibili_headers()
352            .query(&params)
353            .send_bpi("获取专栏显示卡片信息").await?;
354
355        Ok(result)
356    }
357}
358
359#[cfg(test)]
360mod tests {
361    use super::*;
362
363    #[tokio::test]
364    async fn test_get_article_cards() -> Result<(), Box<BpiError>> {
365        let bpi = BpiClient::new();
366
367        let ids = "av2,cv1,cv2";
368
369        let result = bpi.article_cards(ids).await?;
370        let data = result.into_data()?;
371        tracing::info!("{:#?}", data);
372
373        Ok(())
374    }
375}