Skip to main content

bpi_rs/user/
model.rs

1use serde::{Deserialize, Deserializer, Serialize, de};
2
3use crate::ids::{Aid, Bvid, Mid};
4
5/// `/x/web-interface/card` 返回的载荷。
6#[derive(Debug, Clone, Serialize, Deserialize)]
7pub struct UserCardProfile {
8    /// 用户卡片摘要。
9    pub card: UserCardSummary,
10    /// 当前会话是否关注该用户。
11    pub following: bool,
12    /// 已上传稿件数。
13    #[serde(default)]
14    pub archive_count: u64,
15    /// 专栏文章数。
16    #[serde(default)]
17    pub article_count: u64,
18    /// 粉丝数。
19    #[serde(default)]
20    pub follower: u64,
21    /// 收到的总点赞数。
22    #[serde(default)]
23    pub like_num: u64,
24}
25
26/// 嵌套 `card` 对象中的稳定字段。
27#[derive(Debug, Clone, Serialize, Deserialize)]
28pub struct UserCardSummary {
29    /// 用户 member ID。
30    #[serde(deserialize_with = "deserialize_mid_from_string_or_number")]
31    pub mid: Mid,
32    /// 显示名称。
33    pub name: String,
34    /// 个人资料性别文本。
35    #[serde(default)]
36    pub sex: Option<String>,
37    /// 头像 URL。
38    pub face: String,
39    /// 个人签名。
40    #[serde(default)]
41    pub sign: String,
42    /// 卡片摘要中嵌入的粉丝数。
43    #[serde(default)]
44    pub fans: u64,
45    /// 卡片摘要中嵌入的关注数。
46    #[serde(default)]
47    pub attention: u64,
48}
49
50/// `/account/v1/user/cards` 返回的简略用户卡片。
51#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
52pub struct UserBatchCard {
53    /// 用户 member ID。
54    pub mid: Mid,
55    /// 显示名称。
56    pub name: String,
57    /// 头像 URL。
58    pub face: String,
59    /// 个人签名。
60    #[serde(default)]
61    pub sign: String,
62    /// Bilibili 返回的显示 rank。
63    #[serde(default)]
64    pub rank: i32,
65    /// 当前账号等级。
66    #[serde(default)]
67    pub level: i32,
68    /// Bilibili 返回的用户禁言或封禁状态。
69    #[serde(default)]
70    pub silence: i32,
71}
72
73/// `/x/im/user_infos` 返回的批量用户详细信息。
74#[derive(Debug, Clone, Serialize, Deserialize)]
75pub struct UserBatchInfo {
76    /// 用户 member ID。
77    pub mid: Mid,
78    /// 显示名称。
79    pub name: String,
80    /// 个人签名。
81    #[serde(default)]
82    pub sign: String,
83    /// Bilibili 返回的显示 rank。
84    #[serde(default)]
85    pub rank: i32,
86    /// 当前账号等级。
87    #[serde(default)]
88    pub level: i32,
89    /// Bilibili 返回的用户禁言或封禁状态。
90    #[serde(default)]
91    pub silence: i32,
92    /// 个人资料性别文本。
93    #[serde(default, deserialize_with = "deserialize_optional_string")]
94    pub sex: Option<String>,
95    /// 头像 URL。
96    pub face: String,
97    /// VIP 状态摘要。
98    #[serde(default)]
99    pub vip: Option<UserBatchVip>,
100    /// 官方认证摘要。
101    #[serde(default)]
102    pub official: Option<UserOfficialSummary>,
103    /// Bilibili 是否将该账号标记为假账号。
104    #[serde(default)]
105    pub is_fake_account: Option<u32>,
106    /// Bilibili 返回的 expert 载荷;其结构偏展示用途,因此保留原始值。
107    #[serde(default)]
108    pub expert_info: Option<serde_json::Value>,
109}
110
111/// `/x/im/user_infos` 批量响应中嵌入的 VIP 状态字段。
112#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
113pub struct UserBatchVip {
114    /// VIP 类型代码。
115    #[serde(default, rename = "type")]
116    pub kind: i32,
117    /// VIP 状态代码。
118    #[serde(default)]
119    pub status: i32,
120    /// VIP 到期时间戳,单位毫秒。
121    #[serde(default)]
122    pub due_date: i64,
123    /// VIP 支付类型代码。
124    #[serde(default)]
125    pub vip_pay_type: i32,
126    /// VIP 主题类型代码。
127    #[serde(default)]
128    pub theme_type: i32,
129}
130
131/// `/x/space/wbi/acc/info` 返回的公开用户空间信息。
132#[derive(Debug, Clone, Serialize, Deserialize)]
133pub struct UserSpaceProfile {
134    /// 用户 member ID。
135    pub mid: Mid,
136    /// 显示名称。
137    pub name: String,
138    /// 个人资料性别文本。
139    #[serde(default, deserialize_with = "deserialize_optional_string")]
140    pub sex: Option<String>,
141    /// 头像 URL。
142    pub face: String,
143    /// 个人签名。
144    #[serde(default)]
145    pub sign: String,
146    /// 当前账号等级。
147    pub level: u8,
148    /// Bilibili 返回的用户禁言或封禁状态。
149    pub silence: u8,
150    /// 可见硬币数。查询其他用户时 Bilibili 返回 `0`。
151    #[serde(default)]
152    pub coins: f64,
153    /// 该用户是否有粉丝勋章信息。
154    #[serde(default)]
155    pub fans_badge: bool,
156    /// 当前会话是否关注该用户。
157    #[serde(default)]
158    pub is_followed: bool,
159    /// Bilibili 返回时的空间顶部图片 URL。
160    #[serde(default, deserialize_with = "deserialize_optional_string")]
161    pub top_photo: Option<String>,
162    /// 官方认证摘要。
163    #[serde(default)]
164    pub official: Option<UserOfficialSummary>,
165    /// VIP 状态摘要。
166    #[serde(default)]
167    pub vip: Option<UserVipSummary>,
168    /// 直播间摘要。
169    #[serde(default)]
170    pub live_room: Option<UserSpaceLiveRoom>,
171}
172
173/// 用户空间载荷中常见的官方认证字段。
174#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
175pub struct UserOfficialSummary {
176    /// 官方角色代码。
177    #[serde(default)]
178    pub role: i32,
179    /// 认证标题。
180    #[serde(default)]
181    pub title: String,
182    /// 认证描述。
183    #[serde(default)]
184    pub desc: String,
185    /// 认证类型代码。
186    #[serde(default, rename = "type")]
187    pub kind: i32,
188}
189
190/// 用户空间载荷中常见的 VIP 状态字段。
191#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
192pub struct UserVipSummary {
193    /// VIP 类型代码。
194    #[serde(default, rename = "type")]
195    pub kind: i32,
196    /// VIP 状态代码。
197    #[serde(default)]
198    pub status: i32,
199}
200
201/// 用户空间载荷中嵌入的直播间字段。
202#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
203pub struct UserSpaceLiveRoom {
204    /// 该用户是否存在直播间。
205    #[serde(default, rename = "roomStatus")]
206    pub room_status: u8,
207    /// 直播间当前是否开播。
208    #[serde(default, rename = "liveStatus")]
209    pub live_status: u8,
210    /// 直播间 URL。
211    #[serde(default)]
212    pub url: String,
213    /// 直播间标题。
214    #[serde(default)]
215    pub title: String,
216    /// 直播间 ID。
217    #[serde(default, rename = "roomid")]
218    pub room_id: u64,
219}
220
221/// `/x/space/notice` 返回的公开空间公告。
222#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
223#[serde(transparent)]
224pub struct UserSpaceNotice {
225    /// 公告文本。用户没有公开公告时为空。
226    pub content: String,
227}
228
229/// `/x/space/bangumi/follow/list` 返回的已追番剧或影视 season。
230#[derive(Debug, Clone, Serialize, Deserialize)]
231pub struct UserBangumiFollowList {
232    /// 当前页的已追 season。
233    #[serde(default, rename = "list")]
234    pub items: Vec<UserBangumiFollow>,
235    /// 当前页码。
236    #[serde(default, rename = "pn")]
237    pub page: u32,
238    /// 每页数量。
239    #[serde(default, rename = "ps")]
240    pub page_size: u32,
241    /// 该分类下已追 season 总数。
242    #[serde(default)]
243    pub total: u64,
244}
245
246/// 一个已追番剧或影视 season。
247#[derive(Debug, Clone, Serialize, Deserialize)]
248pub struct UserBangumiFollow {
249    /// Season ID。
250    pub season_id: i64,
251    /// Media ID。
252    pub media_id: i64,
253    /// Bilibili 返回的 season 类型代码。
254    #[serde(default)]
255    pub season_type: i64,
256    /// Season 类型显示名称。
257    #[serde(default)]
258    pub season_type_name: String,
259    /// Season 标题。
260    #[serde(default)]
261    pub title: String,
262    /// 封面图片 URL。
263    #[serde(default)]
264    pub cover: String,
265    /// 总集数。
266    #[serde(default)]
267    pub total_count: i64,
268    /// 该 season 是否已完结。
269    #[serde(default)]
270    pub is_finish: i64,
271    /// 该 season 是否已开播。
272    #[serde(default)]
273    pub is_started: i64,
274    /// 该 season 是否可播放。
275    #[serde(default)]
276    pub is_play: i64,
277    /// Bilibili 返回的 badge 文本。
278    #[serde(default)]
279    pub badge: String,
280    /// Badge 类型代码。
281    #[serde(default)]
282    pub badge_type: i64,
283    /// 最新一集摘要。
284    #[serde(default, rename = "new_ep")]
285    pub latest_episode: UserBangumiLatestEpisode,
286    /// Bilibili 返回时的 season 评分。
287    #[serde(default)]
288    pub rating: Option<UserBangumiRating>,
289    /// Season 页面 URL。
290    #[serde(default)]
291    pub url: String,
292    /// Bilibili 返回的短 URL。
293    #[serde(default)]
294    pub short_url: String,
295    /// Season 描述。
296    #[serde(default)]
297    pub summary: String,
298    /// 风格标签。
299    #[serde(default)]
300    pub styles: Vec<String>,
301    /// 追番状态代码。
302    #[serde(default)]
303    pub follow_status: i64,
304    /// 用户进度文本。
305    #[serde(default)]
306    pub progress: String,
307    /// 双方是否都追了该 season。
308    #[serde(default)]
309    pub both_follow: bool,
310}
311
312/// 番剧追番列表项中嵌入的最新一集摘要。
313#[derive(Debug, Clone, Default, PartialEq, Eq, Serialize, Deserialize)]
314pub struct UserBangumiLatestEpisode {
315    /// Episode ID。
316    #[serde(default)]
317    pub id: i64,
318    /// 显示用集数索引。
319    #[serde(default)]
320    pub index_show: String,
321    /// 剧集封面图片 URL。
322    #[serde(default)]
323    pub cover: String,
324    /// 剧集标题。
325    #[serde(default)]
326    pub title: String,
327    /// 剧集长标题。
328    #[serde(default)]
329    pub long_title: Option<String>,
330    /// Bilibili 返回的发布时间文本。
331    #[serde(default)]
332    pub pub_time: String,
333    /// 剧集时长,单位秒。
334    #[serde(default)]
335    pub duration: i64,
336}
337
338/// 番剧追番列表项中嵌入的评分摘要。
339#[derive(Debug, Clone, Copy, PartialEq, Serialize, Deserialize)]
340pub struct UserBangumiRating {
341    /// 评分分数。
342    #[serde(default)]
343    pub score: f64,
344    /// 评分人数。
345    #[serde(default)]
346    pub count: i64,
347}
348
349/// `/x/relation/stat` 返回的关系计数。
350#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
351pub struct UserRelationStat {
352    /// 用户 member ID。
353    pub mid: Mid,
354    /// 公开关注数。
355    pub following: u64,
356    /// 悄悄关注数。
357    #[serde(default)]
358    pub whisper: u64,
359    /// 黑名单数量。
360    #[serde(default)]
361    pub black: u64,
362    /// 粉丝数。
363    pub follower: u64,
364}
365
366/// `/x/relation/followings` 返回的关注列表载荷。
367#[derive(Debug, Clone, Serialize, Deserialize)]
368pub struct UserFollowings {
369    /// 指定用户关注的用户列表。
370    #[serde(default)]
371    pub list: Vec<UserFollowing>,
372    /// Bilibili 关系列表 schema 版本。
373    #[serde(default)]
374    pub re_version: u32,
375    /// 已关注用户总数。
376    #[serde(default)]
377    pub total: u64,
378}
379
380/// [`UserFollowings`] 中的一个已关注用户。
381#[derive(Debug, Clone, Serialize, Deserialize)]
382pub struct UserFollowing {
383    /// 被关注用户 member ID。
384    pub mid: Mid,
385    /// Bilibili 返回的关系属性。
386    #[serde(default)]
387    pub attribute: u8,
388    /// 关注时间戳,单位秒。
389    #[serde(default)]
390    pub mtime: u64,
391    /// 关系分组 ID 列表。
392    #[serde(default)]
393    pub tag: Option<Vec<u64>>,
394    /// 该用户是否被特别关注。
395    #[serde(default)]
396    pub special: u8,
397    /// 显示名称。
398    #[serde(default, rename = "uname")]
399    pub name: String,
400    /// 头像 URL。
401    #[serde(default)]
402    pub face: String,
403    /// 个人签名。
404    #[serde(default)]
405    pub sign: String,
406    /// Bilibili 是否将头像标记为 NFT。
407    #[serde(default)]
408    pub face_nft: u8,
409    /// 关系列表返回的官方认证摘要。
410    #[serde(default)]
411    pub official_verify: Option<UserRelationOfficialVerify>,
412    /// VIP 摘要。Bilibili 经常调整该展示载荷,因此保留原始值。
413    #[serde(default)]
414    pub vip: Option<serde_json::Value>,
415}
416
417/// `/x/relation/fans` 返回的粉丝列表载荷。
418#[derive(Debug, Clone, Serialize, Deserialize)]
419pub struct UserFollowers {
420    /// 关注指定用户的用户列表。
421    #[serde(default)]
422    pub list: Vec<UserFollower>,
423    /// 传给下一次请求的分页 offset。
424    #[serde(default)]
425    pub offset: String,
426    /// Bilibili 关系列表 schema 版本。
427    #[serde(default)]
428    pub re_version: u32,
429    /// 粉丝总数。
430    #[serde(default)]
431    pub total: u64,
432}
433
434/// [`UserFollowers`] 中的一个粉丝。
435#[derive(Debug, Clone, Serialize, Deserialize)]
436pub struct UserFollower {
437    /// 粉丝 member ID。
438    pub mid: Mid,
439    /// Bilibili 返回的关系属性。
440    #[serde(default)]
441    pub attribute: u8,
442    /// Bilibili 返回时的关注时间戳,单位秒。
443    #[serde(default)]
444    pub mtime: Option<u64>,
445    /// 关系分组 ID 列表。
446    #[serde(default)]
447    pub tag: Option<Vec<u64>>,
448    /// 该用户是否被特别关注。
449    #[serde(default)]
450    pub special: u8,
451    /// Contract 展示载荷。该嵌套 schema 不稳定,因此保留原始值。
452    #[serde(default)]
453    pub contract_info: Option<serde_json::Value>,
454    /// 显示名称。
455    #[serde(default, rename = "uname")]
456    pub name: String,
457    /// 头像 URL。
458    #[serde(default)]
459    pub face: String,
460    /// 个人签名。
461    #[serde(default)]
462    pub sign: String,
463    /// Bilibili 是否将头像标记为 NFT。
464    #[serde(default)]
465    pub face_nft: u8,
466    /// 关系列表返回的官方认证摘要。
467    #[serde(default)]
468    pub official_verify: Option<UserRelationOfficialVerify>,
469    /// VIP 摘要。Bilibili 经常调整该展示载荷,因此保留原始值。
470    #[serde(default)]
471    pub vip: Option<serde_json::Value>,
472}
473
474/// `/x/relation/tags` 返回的关注分组条目。
475#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
476pub struct UserFollowTag {
477    /// 关注分组 ID。Bilibili 对内置分组使用负数 ID。
478    #[serde(rename = "tagid")]
479    pub id: i64,
480    /// 关注分组显示名称。
481    pub name: String,
482    /// 分组中的用户数量。
483    #[serde(default)]
484    pub count: i64,
485    /// Bilibili 返回的可选 UI 提示。
486    #[serde(default)]
487    pub tip: Option<String>,
488}
489
490/// `/xlive/web-ucenter/user/MedalWall` 返回的粉丝勋章墙。
491#[derive(Debug, Clone, Serialize, Deserialize)]
492pub struct UserMedalWall {
493    /// 用户勋章墙上可见的勋章条目。
494    #[serde(default)]
495    pub list: Vec<UserMedalWallItem>,
496    /// 可见勋章条目数量。
497    #[serde(default)]
498    pub count: u32,
499    /// 该用户是否关闭空间勋章墙。
500    #[serde(default)]
501    pub close_space_medal: u32,
502    /// 该用户是否只展示正在佩戴的勋章。
503    #[serde(default)]
504    pub only_show_wearing: u32,
505    /// 勋章墙所有者显示名称。
506    #[serde(default)]
507    pub name: String,
508    /// 勋章墙所有者头像 URL。
509    #[serde(default)]
510    pub icon: String,
511    /// 勋章墙所有者 member ID。
512    pub uid: Mid,
513    /// 勋章墙所有者等级。
514    #[serde(default)]
515    pub level: u32,
516}
517
518/// [`UserMedalWall`] 中的一个粉丝勋章条目。
519#[derive(Debug, Clone, Serialize, Deserialize)]
520pub struct UserMedalWallItem {
521    /// 目标创作者的勋章元数据。
522    pub medal_info: UserMedalInfo,
523    /// 目标创作者显示名称。
524    #[serde(default)]
525    pub target_name: String,
526    /// 目标创作者头像 URL。
527    #[serde(default)]
528    pub target_icon: String,
529    /// 目标直播间链接。
530    #[serde(default)]
531    pub link: String,
532    /// 目标当前直播状态。
533    #[serde(default)]
534    pub live_status: u32,
535    /// 官方状态字段。Bilibili 将该 key 拼写为 `offical`。
536    #[serde(default, rename = "offical")]
537    pub official: Option<u32>,
538    /// 从勋章墙所有者视角看到的勋章展示字段。
539    #[serde(default)]
540    pub uinfo_medal: Option<UserMedalOwnerInfo>,
541}
542
543impl UserMedalWallItem {
544    /// 返回该勋章对应目标创作者的 member ID。
545    pub fn target_id(&self) -> Mid {
546        self.medal_info.target_id
547    }
548}
549
550/// 一个目标创作者的稳定粉丝勋章元数据。
551#[derive(Debug, Clone, Serialize, Deserialize)]
552pub struct UserMedalInfo {
553    /// 目标创作者 member ID。
554    pub target_id: Mid,
555    /// 勋章等级。
556    #[serde(default)]
557    pub level: u32,
558    /// 勋章显示名称。
559    #[serde(default, rename = "medal_name")]
560    pub name: String,
561    /// 勋章渐变起始颜色。
562    #[serde(default)]
563    pub medal_color_start: u32,
564    /// 勋章渐变结束颜色。
565    #[serde(default)]
566    pub medal_color_end: u32,
567    /// 勋章边框颜色。
568    #[serde(default)]
569    pub medal_color_border: u32,
570    /// 与该勋章关联的大航海等级。
571    #[serde(default)]
572    pub guard_level: u32,
573    /// 该勋章当前是否正在佩戴。
574    #[serde(default)]
575    pub wearing_status: u32,
576    /// 勋章 ID。
577    #[serde(default)]
578    pub medal_id: u64,
579    /// 当前亲密度分数。
580    #[serde(default)]
581    pub intimacy: u64,
582    /// 升到下一级所需亲密度分数。
583    #[serde(default)]
584    pub next_intimacy: u64,
585    /// 今日已投喂亲密度。
586    #[serde(default)]
587    pub today_feed: u64,
588    /// 每日亲密度投喂上限。
589    #[serde(default)]
590    pub day_limit: u64,
591    /// Bilibili 返回时的大航海图标 URL。
592    #[serde(default)]
593    pub guard_icon: Option<String>,
594    /// Bilibili 返回时的荣誉图标 URL。
595    #[serde(default)]
596    pub honor_icon: Option<String>,
597}
598
599/// 从勋章墙所有者视角看到的勋章展示字段。
600#[derive(Debug, Clone, Serialize, Deserialize)]
601pub struct UserMedalOwnerInfo {
602    /// 勋章显示名称。
603    #[serde(default)]
604    pub name: String,
605    /// 勋章等级。
606    #[serde(default)]
607    pub level: u32,
608    /// 勋章渐变起始颜色。
609    #[serde(default)]
610    pub color_start: u32,
611    /// 勋章渐变结束颜色。
612    #[serde(default)]
613    pub color_end: u32,
614    /// 勋章边框颜色。
615    #[serde(default)]
616    pub color_border: u32,
617    /// 勋章文本颜色。
618    #[serde(default)]
619    pub color: u32,
620    /// 勋章 ID。
621    #[serde(default)]
622    pub id: u64,
623    /// Bilibili 返回的勋章类型。
624    #[serde(default)]
625    pub typ: u32,
626    /// 勋章是否点亮。
627    #[serde(default)]
628    pub is_light: u32,
629    /// 目标创作者 member ID。
630    pub ruid: Mid,
631    /// 与该勋章关联的大航海等级。
632    #[serde(default)]
633    pub guard_level: u32,
634    /// 当前亲密度分数。
635    #[serde(default)]
636    pub score: u64,
637    /// Bilibili 返回时的大航海图标 URL。
638    #[serde(default)]
639    pub guard_icon: Option<String>,
640    /// Bilibili 返回时的荣誉图标 URL。
641    #[serde(default)]
642    pub honor_icon: Option<String>,
643    /// V2 勋章起始颜色 token。
644    #[serde(default)]
645    pub v2_medal_color_start: Option<String>,
646    /// V2 勋章结束颜色 token。
647    #[serde(default)]
648    pub v2_medal_color_end: Option<String>,
649    /// V2 勋章边框颜色 token。
650    #[serde(default)]
651    pub v2_medal_color_border: Option<String>,
652    /// V2 勋章文本颜色 token。
653    #[serde(default)]
654    pub v2_medal_color_text: Option<String>,
655    /// V2 勋章等级颜色 token。
656    #[serde(default)]
657    pub v2_medal_color_level: Option<String>,
658    /// Bilibili 返回时已获得该勋章的用户数量。
659    #[serde(default)]
660    pub user_receive_count: Option<u32>,
661}
662
663/// 关系列表载荷中嵌入的官方认证字段。
664#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
665pub struct UserRelationOfficialVerify {
666    /// 认证类型代码。
667    #[serde(default, rename = "type")]
668    pub kind: i8,
669    /// 认证描述。
670    #[serde(default)]
671    pub desc: String,
672}
673
674/// `/x/space/upstat` 返回的创作者内容统计。
675#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
676pub struct UserUpStat {
677    /// 投稿视频播放摘要。
678    pub archive: UserUpStatArchive,
679    /// 专栏阅读摘要。
680    pub article: UserUpStatArticle,
681    /// 该用户收到的总点赞数。
682    pub likes: u64,
683}
684
685/// [`UserUpStat`] 中的投稿视频播放摘要。
686#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
687pub struct UserUpStatArchive {
688    /// 视频总播放数。
689    #[serde(default)]
690    pub view: u64,
691}
692
693/// [`UserUpStat`] 中的专栏阅读摘要。
694#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
695pub struct UserUpStatArticle {
696    /// 专栏总阅读数。
697    #[serde(default)]
698    pub view: u64,
699}
700
701/// `/x/space/navnum` 返回的用户空间导航计数。
702#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
703pub struct UserNavStat {
704    /// 投稿视频数量。
705    #[serde(default)]
706    pub video: u64,
707    /// 已追番剧数量。
708    #[serde(default)]
709    pub bangumi: u64,
710    /// 已追影视数量。
711    #[serde(default)]
712    pub cinema: u64,
713    /// 视频列表计数。
714    #[serde(default)]
715    pub channel: UserNavStatPair,
716    /// 收藏夹计数。
717    #[serde(default)]
718    pub favourite: UserNavStatPair,
719    /// 关注分组数量。
720    #[serde(default)]
721    pub tag: u64,
722    /// 专栏数量。
723    #[serde(default)]
724    pub article: u64,
725    /// 播单数量。
726    #[serde(default)]
727    pub playlist: u64,
728    /// 相簿数量。
729    #[serde(default)]
730    pub album: u64,
731    /// 音频数量。
732    #[serde(default)]
733    pub audio: u64,
734    /// 课程数量。
735    #[serde(default)]
736    pub pugv: u64,
737    /// 图文数量。
738    #[serde(default)]
739    pub opus: u64,
740    /// 视频合集或列表数量。
741    #[serde(default, rename = "season_num")]
742    pub season_count: u64,
743}
744
745/// [`UserNavStat`] 使用的所有者/访客拆分计数。
746#[derive(Debug, Clone, Copy, Default, PartialEq, Eq, Serialize, Deserialize)]
747pub struct UserNavStatPair {
748    /// 所有者可见的总数。
749    #[serde(default)]
750    pub master: u64,
751    /// 访客可见的公开数量。
752    #[serde(default)]
753    pub guest: u64,
754}
755
756/// `/link_draw/v1/doc/upload_count` 返回的相簿投稿计数。
757#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
758pub struct UserAlbumCount {
759    /// 相簿投稿总数。
760    #[serde(default)]
761    pub all_count: u64,
762    /// 绘画投稿数。
763    #[serde(default)]
764    pub draw_count: u64,
765    /// 摄影投稿数。
766    #[serde(default)]
767    pub photo_count: u64,
768    /// 日常图片动态投稿数。
769    #[serde(default)]
770    pub daily_count: u64,
771}
772
773/// `/x/polymer/web-dynamic/v1/name-to-uid` 返回的用户名查询载荷。
774#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
775pub struct UserNameToUid {
776    /// 匹配到的用户名和 member ID 条目。
777    #[serde(default)]
778    pub uid_list: Vec<UserNameToUidItem>,
779}
780
781/// 一个用户名查询结果。
782#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
783pub struct UserNameToUidItem {
784    /// 匹配查询的显示名称。
785    pub name: String,
786    /// 匹配到的 member ID。
787    #[serde(
788        rename = "uid",
789        deserialize_with = "deserialize_mid_from_string_or_number"
790    )]
791    pub mid: Mid,
792}
793
794/// `/x/space/wbi/arc/search` 返回的投稿视频。
795#[derive(Debug, Clone, Serialize, Deserialize)]
796pub struct UserUploadedVideos {
797    /// 投稿视频列表和分区摘要。
798    pub list: UserUploadedVideoList,
799    /// 分页摘要。
800    pub page: UserUploadedVideosPage,
801    /// Bilibili 返回的可选全部播放按钮。
802    #[serde(default)]
803    pub episodic_button: Option<UserUploadedVideosButton>,
804    /// 响应是否被 Bilibili 风控。
805    #[serde(default)]
806    pub is_risk: bool,
807}
808
809/// 投稿视频列表载荷。
810#[derive(Debug, Clone, Serialize, Deserialize)]
811pub struct UserUploadedVideoList {
812    /// 以分区 ID 为 key 的分区摘要。
813    #[serde(default)]
814    pub tlist: serde_json::Value,
815    /// 当前页的投稿视频。
816    #[serde(default, rename = "vlist")]
817    pub videos: Vec<UserUploadedVideo>,
818}
819
820/// 用户空间中的一个投稿视频。
821#[derive(Debug, Clone, Serialize, Deserialize)]
822pub struct UserUploadedVideo {
823    /// AV 数字视频 ID。
824    pub aid: Aid,
825    /// BV 字符串视频 ID。
826    pub bvid: Bvid,
827    /// UP 主 member ID。
828    pub mid: Mid,
829    /// 视频标题。
830    pub title: String,
831    /// UP 主显示名称。
832    #[serde(default)]
833    pub author: String,
834    /// 封面图片 URL。
835    #[serde(default)]
836    pub pic: String,
837    /// Bilibili 返回的视频时长文本。
838    #[serde(default)]
839    pub length: String,
840    /// 视频描述。
841    #[serde(default)]
842    pub description: String,
843    /// 发布时间戳,单位秒。
844    #[serde(default)]
845    pub created: u64,
846    /// 播放数。
847    #[serde(default)]
848    pub play: u64,
849    /// 回复数。
850    #[serde(default)]
851    pub comment: u64,
852    /// 分区 ID。
853    #[serde(default)]
854    pub typeid: u64,
855    /// 弹幕数。
856    #[serde(default)]
857    pub video_review: u64,
858    /// Bilibili 是否隐藏该视频的点击详情。
859    #[serde(default)]
860    pub hide_click: bool,
861}
862
863/// 投稿视频分页摘要。
864#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
865pub struct UserUploadedVideosPage {
866    /// 匹配到的视频总数。
867    #[serde(default)]
868    pub count: u64,
869    /// 当前页码。
870    #[serde(default)]
871    pub pn: u32,
872    /// 每页数量。
873    #[serde(default)]
874    pub ps: u32,
875}
876
877/// 投稿视频搜索返回的可选全部播放按钮。
878#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
879pub struct UserUploadedVideosButton {
880    /// 按钮文本。
881    #[serde(default)]
882    pub text: String,
883    /// 按钮目标 URI。
884    #[serde(default)]
885    pub uri: String,
886}
887
888fn deserialize_mid_from_string_or_number<'de, D>(deserializer: D) -> Result<Mid, D::Error>
889where
890    D: Deserializer<'de>,
891{
892    let value = serde_json::Value::deserialize(deserializer)?;
893
894    match value {
895        serde_json::Value::Number(number) => {
896            let mid = number
897                .as_u64()
898                .ok_or_else(|| de::Error::custom("mid must be a non-negative integer"))?;
899            Mid::new(mid).map_err(de::Error::custom)
900        }
901        serde_json::Value::String(text) => text.parse::<Mid>().map_err(de::Error::custom),
902        _ => Err(de::Error::custom("mid must be a string or number")),
903    }
904}
905
906fn deserialize_optional_string<'de, D>(deserializer: D) -> Result<Option<String>, D::Error>
907where
908    D: Deserializer<'de>,
909{
910    Ok(Option::<String>::deserialize(deserializer)?
911        .and_then(|value| (!value.trim().is_empty()).then_some(value)))
912}