//! YouTube API request and response models pub mod locale; mod ordering; mod paginator; mod param; pub mod richtext; pub mod stream_filter; pub use locale::{Country, Language}; pub use paginator::Paginator; pub use param::ChannelOrder; use std::ops::Range; use chrono::{DateTime, Local, Utc}; use serde::{Deserialize, Serialize}; use self::richtext::RichText; /* #COMMON */ /// Video thumbnail or other image #[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)] #[non_exhaustive] pub struct Thumbnail { pub url: String, pub width: u32, pub height: u32, } /* #PLAYER */ pub trait FileFormat { /// Get the file extension (".xyz") of the file format fn extension(&self) -> &str; } /// Video player data #[derive(Clone, Debug, Serialize, Deserialize)] #[non_exhaustive] pub struct VideoPlayer { /// Video metadata pub details: VideoPlayerDetails, /// List of streams containing both audio and video pub video_streams: Vec, /// List of streams containing video only pub video_only_streams: Vec, /// List of streams containing audio only pub audio_streams: Vec, /// List of subtitles pub subtitles: Vec, /// Lifetime of the stream URLs in seconds pub expires_in_seconds: u32, } /// Video metadata from the player #[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)] #[non_exhaustive] pub struct VideoPlayerDetails { /// Unique YouTube video ID pub id: String, /// Video title pub title: String, /// Video description in plaintext format pub description: Option, /// Video length in seconds pub length: u32, /// Video thumbnail pub thumbnail: Vec, /// Channel of the video pub channel: ChannelId, /// Video publishing date. Start date in case of a livestream. pub publish_date: Option>, /// Number of views / current viewers in case of a livestream. pub view_count: u64, /// List of words that describe the topic of the video pub keywords: Vec, pub category: Option, /// True if the video is/was livestreamed pub is_live_content: bool, /// True if the video is not age-restricted pub is_family_safe: Option, } /// Video stream #[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)] #[non_exhaustive] pub struct VideoStream { /// Video stream URL pub url: String, /// YouTube stream format identifier pub itag: u32, pub bitrate: u32, pub average_bitrate: u32, /// Video file size in bytes pub size: Option, pub index_range: Option>, pub init_range: Option>, /// Video width in pixels pub width: u32, /// Video height in pixels pub height: u32, /// Video frames per second pub fps: u8, /// Quality text (e.g. "1080p60") pub quality: String, /// True if the video is HDR pub hdr: bool, /// MIME file type pub mime: String, /// Video file format pub format: VideoFormat, /// Video codec pub codec: VideoCodec, /// True if the deobfuscation of the nsig url parameter failed /// and the stream will be throttled pub throttled: bool, } /// Audio stream #[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)] #[non_exhaustive] pub struct AudioStream { /// Audio stream URL pub url: String, /// YouTube stream format identifier pub itag: u32, pub bitrate: u32, pub average_bitrate: u32, /// Audio file size in bytes pub size: u64, pub index_range: Option>, pub init_range: Option>, /// MIME file type pub mime: String, /// Audio file format pub format: AudioFormat, /// Audio codec pub codec: AudioCodec, /// True if the deobfuscation of the nsig url parameter failed /// and the stream will be throttled pub throttled: bool, /// Audio track information /// /// Videos can have multiple audio tracks (different languages). /// In this case, this object shows to which track the stream belongs to. /// /// This is None if the video contains only 1 audio track. pub track: Option, } /// Video codec #[derive( Default, Copy, Clone, Debug, Serialize, Deserialize, PartialEq, Eq, PartialOrd, Ord, Hash, )] #[serde(rename_all = "snake_case")] #[non_exhaustive] pub enum VideoCodec { #[default] Unknown, /// MPEG-4 Part 14 Mp4v, /// avc1 aka H.264: Avc1, /// VP9: Vp9, /// AV1, the latest codec: Av01, } /// Audio codec #[derive( Default, Copy, Clone, Debug, Serialize, Deserialize, PartialEq, Eq, PartialOrd, Ord, Hash, )] #[serde(rename_all = "snake_case")] #[non_exhaustive] pub enum AudioCodec { #[default] Unknown, /// MP4A aka AAC: Mp4a, /// Opus: Opus, } /// Video file type #[derive(Copy, Clone, Debug, Serialize, Deserialize, PartialEq, Eq, PartialOrd, Ord, Hash)] #[serde(rename_all = "snake_case")] #[non_exhaustive] pub enum VideoFormat { /// `*.3gp` #[serde(rename = "3gp")] ThreeGp, /// `*.mp4` Mp4, /// `*.webm` Webm, } /// Audio track information /// /// Videos can have multiple audio tracks (different languages). /// In this case, this object shows to which track the stream belongs to. #[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)] #[non_exhaustive] pub struct AudioTrack { /// Track ID (e.g. `en.0`) pub id: String, /// 2/3 letter language code (e.g. `en`) /// /// Extracted from the track ID pub lang: Option, /// Language name (e.g. "English") pub lang_name: String, /// True if this is the default audio track pub is_default: bool, } impl FileFormat for VideoFormat { fn extension(&self) -> &str { match self { VideoFormat::ThreeGp => ".3gp", VideoFormat::Mp4 => ".mp4", VideoFormat::Webm => ".webm", } } } /// Audio file type #[derive(Copy, Clone, Debug, Serialize, Deserialize, PartialEq, Eq, PartialOrd, Ord, Hash)] #[serde(rename_all = "snake_case")] #[non_exhaustive] pub enum AudioFormat { /// `*.m4a` M4a, /// `*.webm` Webm, } impl FileFormat for AudioFormat { fn extension(&self) -> &str { match self { AudioFormat::M4a => ".m4a", AudioFormat::Webm => ".webm", } } } /// YouTube provides subtitles in different formats. /// /// srv1 (XML) is the default format, to request a different format you have /// to append `&fmt=` to the URL. /// /// # Subtitle formats /// /// ### `srv1` (default) /// /// ```xml /// /// /// - [Mr Beast] I built two massive circles /// and put 100 boys in one /// and 100 girls in the other /// /// ``` /// /// ### `srv2` /// /// ```xml /// /// /// - [Mr Beast] I built two massive circles /// and put 100 boys in one /// and 100 girls in the other /// /// ``` /// /// ### `srv3` /// /// ```xml /// /// /// ///

- [Mr Beast] I built two massive circles

///

and put 100 boys in one /// and 100 girls in the other

/// ///
/// ``` /// /// ### `json3` /// /// ```json /// { /// "wireMagic": "pb3", /// "pens": [{}], /// "wsWinStyles": [{}], /// "wpWinPositions": [{}], /// "events": [ /// { /// "tStartMs": 120, /// "dDurationMs": 1590, /// "segs": [ /// { /// "utf8": "- [Mr Beast] I built two massive circles" /// } /// ] /// }, /// { /// "tStartMs": 1710, /// "dDurationMs": 3390, /// "segs": [ /// { /// "utf8": "and put 100 boys in one\nand 100 girls in the other" /// } /// ] /// } /// ] /// } /// ``` /// /// ### Timed Text Markup Language (`ttml`) /// /// ```xml /// /// /// /// ///