use std::collections::HashMap;
use serde::{Deserialize, Deserializer};
#[derive(Debug, Deserialize)]
pub struct InfoJson {
pub id: String,
pub title: String,
pub formats: Vec<Format>,
pub thumbnails: Vec<ThumbNail>,
pub thumbnail: String,
pub description: String,
pub channel_id: String,
pub channel_url: String,
pub duration: u32,
pub view_count: u32,
pub age_limit: u32,
pub webpage_url: String,
pub categories: Vec<String>,
pub tags: Vec<String>,
pub playable_in_embed: bool,
pub live_status: String,
_format_sort_fields: Vec<String>,
pub automatic_captions: HashMap<String, Vec<Caption>>,
pub subtitles: Subtitles,
pub comment_count: u32,
pub like_count: u32,
pub channel: String,
pub channel_follower_count: u32,
pub channel_is_verified: Option<bool>,
pub uploader: String,
pub uploader_id: String,
pub uploader_url: String,
pub upload_date: String,
pub availability: String,
pub webpage_url_basename: String,
pub webpage_url_domain: String,
pub extractor: String,
pub extractor_key: String,
pub display_id: String,
pub fulltitle: String,
pub duration_string: String,
pub is_live: bool,
pub was_live: bool,
pub epoch: u32,
pub comments: Vec<Comment>,
pub sponsorblock_chapters: Vec<SponsorblockChapter>,
pub format: String,
pub format_id: String,
pub ext: String,
pub protocol: String,
pub language: Option<String>,
pub format_note: String,
pub filesize_approx: u64,
pub tbr: f64,
pub width: u32,
pub height: u32,
pub resolution: String,
pub fps: f64,
pub dynamic_range: String,
pub vcodec: String,
pub vbr: f64,
pub aspect_ratio: f64,
pub acodec: String,
pub abr: f64,
pub asr: u32,
pub audio_channels: u32,
_type: String,
_version: Version,
}
#[derive(Debug, Deserialize)]
pub struct Subtitles {}
#[derive(Debug, Deserialize)]
pub struct Version {
pub version: String,
pub release_git_head: String,
pub repository: String,
}
#[derive(Debug, Deserialize)]
pub struct SponsorblockChapter {}
#[derive(Debug, Deserialize, Clone)]
#[serde(from = "String")]
pub enum Parent {
Root,
Id(String),
}
impl Parent {
pub fn id(&self) -> Option<&str> {
if let Self::Id(id) = self {
Some(id)
} else {
None
}
}
}
impl From<String> for Parent {
fn from(value: String) -> Self {
if value == "root" {
Self::Root
} else {
Self::Id(value)
}
}
}
#[derive(Debug, Deserialize, Clone)]
#[serde(from = "String")]
pub struct Id {
pub id: String,
}
impl From<String> for Id {
fn from(value: String) -> Self {
Self {
// Take the last element if the string is split with dots, otherwise take the full id
id: value.split('.').last().unwrap_or(&value).to_owned(),
}
}
}
#[derive(Debug, Deserialize, Clone)]
pub struct Comment {
pub id: Id,
pub text: String,
#[serde(default = "zero")]
pub like_count: u32,
pub author_id: String,
#[serde(default = "unknown")]
pub author: String,
pub author_thumbnail: String,
pub parent: Parent,
#[serde(deserialize_with = "edited_from_time_text", alias = "_time_text")]
pub edited: bool,
// Can't also be deserialized, as it's already used in 'edited'
// _time_text: String,
pub timestamp: i64,
pub author_url: String,
pub author_is_uploader: bool,
pub is_favorited: bool,
}
fn unknown() -> String {
"<Unknown>".to_string()
}
fn zero() -> u32 {
0
}
fn edited_from_time_text<'de, D>(d: D) -> Result<bool, D::Error>
where
D: Deserializer<'de>,
{
let s = String::deserialize(d)?;
if s.contains(" (edited)") {
Ok(true)
} else {
Ok(false)
}
}
#[derive(Debug, Deserialize)]
pub struct Caption {
pub ext: String,
pub url: String,
pub name: Option<String>,
pub protocol: Option<String>,
}
#[derive(Debug, Deserialize)]
pub struct ThumbNail {
pub url: String,
pub preference: i32,
pub id: String,
pub height: Option<u32>,
pub width: Option<u32>,
pub resolution: Option<String>,
}
#[derive(Debug, Deserialize)]
pub struct Format {
pub format_id: String,
pub format_note: Option<String>,
pub ext: String,
pub protocol: String,
pub acodec: Option<String>,
pub vcodec: String,
pub url: String,
pub width: Option<u32>,
pub height: Option<u32>,
pub fps: Option<f64>,
pub rows: Option<u32>,
pub columns: Option<u32>,
pub fragments: Option<Vec<Fragment>>,
pub resolution: String,
pub aspect_ratio: Option<f64>,
pub http_headers: HttpHeader,
pub audio_ext: String,
pub video_ext: String,
pub vbr: Option<f64>,
pub abr: Option<f64>,
pub format: String,
}
#[derive(Debug, Deserialize)]
pub struct HttpHeader {
#[serde(alias = "User-Agent")]
pub user_agent: String,
#[serde(alias = "Accept")]
pub accept: String,
#[serde(alias = "Accept-Language")]
pub accept_language: String,
#[serde(alias = "Sec-Fetch-Mode")]
pub sec_fetch_mode: String,
}
#[derive(Debug, Deserialize)]
pub struct Fragment {
pub url: String,
pub duration: f64,
}