1use serde::{Deserialize, Serialize};
2
3#[derive(Clone, Debug, Eq, PartialEq, Serialize, Deserialize)]
4#[serde(rename_all = "snake_case")]
5pub enum ResolvedContent {
6 Video(VideoMetadata),
7 Season(SeasonResolution),
8}
9
10#[derive(Clone, Debug, Eq, PartialEq, Serialize, Deserialize)]
11pub struct SeasonResolution {
12 pub season: SeasonMetadata,
13 pub selected_episodes: Vec<EpisodeMetadata>,
14}
15
16#[derive(Clone, Debug, Eq, PartialEq, Serialize, Deserialize)]
17pub struct VideoMetadata {
18 pub aid: u64,
19 pub bvid: Option<String>,
20 pub title: String,
21 pub description: String,
22 pub cover_url: Option<String>,
23 pub pub_time: Option<i64>,
24 pub owner: Option<Owner>,
25 pub tags: Vec<Tag>,
26 pub pages: Vec<PageMetadata>,
27}
28
29#[derive(Clone, Debug, Eq, PartialEq, Serialize, Deserialize)]
30pub struct Owner {
31 pub mid: u64,
32 pub name: String,
33}
34
35#[derive(Clone, Debug, Eq, PartialEq, Serialize, Deserialize)]
36pub struct PageMetadata {
37 pub index: u32,
38 pub aid: u64,
39 pub cid: u64,
40 pub epid: Option<u64>,
41 pub title: String,
42 pub duration_seconds: Option<u32>,
43}
44
45#[derive(Clone, Debug, Eq, PartialEq, Serialize, Deserialize)]
46pub struct Tag {
47 pub id: u64,
48 pub name: String,
49}
50
51#[derive(Clone, Debug, Eq, PartialEq, Serialize, Deserialize)]
52pub struct SeasonMetadata {
53 pub season_id: Option<u64>,
54 pub media_id: Option<u64>,
55 pub title: String,
56 pub description: String,
57 pub cover_url: Option<String>,
58 pub main_episode_count: usize,
59 pub areas: Vec<String>,
60 pub tags: Vec<String>,
61 pub episodes: Vec<EpisodeMetadata>,
62}
63
64#[derive(Clone, Debug, Eq, PartialEq, Serialize, Deserialize)]
65pub struct EpisodeMetadata {
66 pub index: u32,
67 pub aid: u64,
68 pub bvid: Option<String>,
69 pub cid: u64,
70 pub epid: u64,
71 pub title: String,
72 pub long_title: Option<String>,
73 pub pub_time: Option<i64>,
74}
75
76#[derive(Clone, Debug, Eq, PartialEq, Serialize, Deserialize)]
77pub struct DownloadPlan {
78 pub title: String,
79 pub entries: Vec<DownloadEntry>,
80}
81
82#[derive(Clone, Debug, Eq, PartialEq, Serialize, Deserialize)]
83pub struct DownloadEntry {
84 pub index: u32,
85 pub aid: u64,
86 pub bvid: Option<String>,
87 pub cid: u64,
88 pub epid: Option<u64>,
89 pub title: String,
90 pub source: StreamSource,
91 pub streams: StreamSet,
92 #[serde(default, skip_serializing_if = "StreamDiagnostics::is_empty")]
93 pub diagnostics: StreamDiagnostics,
94 pub subtitles: Vec<SubtitleTrack>,
95 pub danmaku: DanmakuTrack,
96}
97
98#[derive(Clone, Debug, Eq, PartialEq, Serialize, Deserialize)]
99#[serde(rename_all = "snake_case")]
100pub enum StreamSource {
101 NormalWeb,
102 PgcWeb,
103 PgcProxy,
104 IntlWeb,
105}
106
107#[derive(Clone, Debug, Default, Eq, PartialEq, Serialize, Deserialize)]
108pub struct StreamDiagnostics {
109 pub attempts: Vec<StreamResolverAttempt>,
110}
111
112impl StreamDiagnostics {
113 #[must_use]
114 pub fn is_empty(&self) -> bool {
115 self.attempts.is_empty()
116 }
117}
118
119#[derive(Clone, Debug, Eq, PartialEq, Serialize, Deserialize)]
120pub struct StreamResolverAttempt {
121 pub source: StreamSource,
122 pub outcome: StreamResolverOutcome,
123 #[serde(skip_serializing_if = "Option::is_none")]
124 pub area: Option<String>,
125 #[serde(skip_serializing_if = "Option::is_none")]
126 pub endpoint: Option<String>,
127 #[serde(skip_serializing_if = "Option::is_none")]
128 pub message: Option<String>,
129}
130
131#[derive(Clone, Debug, Eq, PartialEq, Serialize, Deserialize)]
132#[serde(rename_all = "snake_case")]
133pub enum StreamResolverOutcome {
134 Succeeded,
135 Failed,
136 Skipped,
137}
138
139#[derive(Clone, Debug, Eq, PartialEq, Serialize, Deserialize)]
140#[non_exhaustive]
141pub struct StreamSet {
142 pub videos: Vec<MediaStream>,
143 pub audios: Vec<MediaStream>,
144 pub flv_segments: Vec<FlvSegment>,
145 pub accept_quality: Vec<u32>,
146 #[serde(default)]
147 pub qualities: Vec<StreamQuality>,
148 pub duration_seconds: Option<u32>,
149}
150
151#[non_exhaustive]
152#[derive(Clone, Debug, Eq, PartialEq, Serialize, Deserialize)]
153pub struct StreamQuality {
154 pub id: u32,
155 pub description: Option<String>,
156}
157
158#[derive(Clone, Debug, Eq, PartialEq, Serialize, Deserialize)]
159pub struct MediaStream {
160 pub id: u32,
161 pub base_url: String,
162 pub backup_urls: Vec<String>,
163 pub codecs: Option<String>,
164 pub bandwidth: Option<u64>,
165 pub width: Option<u32>,
166 pub height: Option<u32>,
167 pub frame_rate: Option<String>,
168 pub mime_type: Option<String>,
169 pub size: Option<u64>,
170}
171
172#[derive(Clone, Debug, Eq, PartialEq, Serialize, Deserialize)]
173pub struct FlvSegment {
174 pub order: u32,
175 pub url: String,
176 pub backup_urls: Vec<String>,
177 pub size: Option<u64>,
178 pub length_ms: Option<u64>,
179}
180
181#[derive(Clone, Debug, Eq, PartialEq, Serialize, Deserialize)]
182pub struct SubtitleTrack {
183 pub language: String,
184 pub language_doc: Option<String>,
185 pub url: String,
186 pub format: SubtitleFormat,
187}
188
189#[derive(Clone, Debug, Eq, PartialEq, Serialize, Deserialize)]
190#[serde(rename_all = "snake_case")]
191pub enum SubtitleFormat {
192 Json,
193 Ass,
194 Unknown,
195}
196
197#[derive(Clone, Debug, Eq, PartialEq, Serialize, Deserialize)]
198pub struct DanmakuTrack {
199 pub cid: u64,
200 pub xml_url: String,
201}