pub mod locale; mod ordering; mod paginator; pub mod richtext; pub mod stream_filter; pub use locale::{Country, Language}; pub use paginator::Paginator; use std::ops::Range; use chrono::{DateTime, Local}; use serde::{Deserialize, Serialize}; use self::richtext::RichText; /* #PLAYER */ pub trait FileFormat { fn extension(&self) -> &str; } #[derive(Clone, Debug, Serialize, Deserialize)] pub struct VideoPlayer { pub details: VideoPlayerDetails, pub video_streams: Vec, pub video_only_streams: Vec, pub audio_streams: Vec, pub subtitles: Vec, pub expires_in_seconds: u32, } #[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)] pub struct VideoPlayerDetails { pub id: String, pub title: String, pub description: Option, pub length: u32, pub thumbnails: Vec, pub channel: ChannelId, pub publish_date: Option>, pub view_count: u64, pub keywords: Vec, pub category: Option, pub is_live_content: bool, pub is_family_safe: Option, } #[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)] pub struct VideoStream { pub url: String, pub itag: u32, pub bitrate: u32, pub average_bitrate: u32, pub size: Option, pub index_range: Option>, pub init_range: Option>, pub width: u32, pub height: u32, pub fps: u8, pub quality: String, pub hdr: bool, pub mime: String, pub format: VideoFormat, pub codec: VideoCodec, pub throttled: bool, } #[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)] pub struct AudioStream { pub url: String, pub itag: u32, pub bitrate: u32, pub average_bitrate: u32, pub size: u64, pub index_range: Option>, pub init_range: Option>, pub mime: String, pub format: AudioFormat, pub codec: AudioCodec, pub throttled: bool, pub track: Option, } #[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, } #[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, } /// The video file format #[derive(Copy, Clone, Debug, Serialize, Deserialize, PartialEq, Eq, PartialOrd, Ord, Hash)] #[serde(rename_all = "snake_case")] #[non_exhaustive] pub enum VideoFormat { #[serde(rename = "3gp")] ThreeGp, Mp4, Webm, } #[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)] pub struct AudioTrack { pub id: String, pub lang: Option, pub lang_name: String, pub is_default: bool, } impl FileFormat for VideoFormat { fn extension(&self) -> &str { match self { VideoFormat::ThreeGp => ".3gp", VideoFormat::Mp4 => ".mp4", VideoFormat::Webm => ".webm", } } } #[derive(Copy, Clone, Debug, Serialize, Deserialize, PartialEq, Eq, PartialOrd, Ord, Hash)] #[serde(rename_all = "snake_case")] #[non_exhaustive] pub enum AudioFormat { M4a, Webm, } impl FileFormat for AudioFormat { fn extension(&self) -> &str { match self { AudioFormat::M4a => ".m4a", AudioFormat::Webm => ".webm", } } } #[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)] pub struct Thumbnail { pub url: String, pub width: u32, pub height: u32, } #[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)] pub struct Subtitle { pub url: String, pub lang: String, pub lang_name: String, pub auto_generated: bool, } /* #PLAYLIST */ #[derive(Clone, Debug, Serialize, Deserialize)] pub struct Playlist { pub id: String, pub name: String, pub videos: Paginator, pub video_count: u32, pub thumbnail: Vec, pub description: Option, pub channel: Option, pub last_update: Option>, pub last_update_txt: Option, } #[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)] pub struct PlaylistVideo { pub id: String, pub title: String, pub length: u32, pub thumbnail: Vec, pub channel: ChannelId, } #[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)] pub struct ChannelId { pub id: String, pub name: String, } /* #VIDEO DETAILS */ #[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)] pub struct VideoDetails { /// Unique YouTube video ID pub id: String, /// Video title pub title: String, /// Video description pub description: RichText, /// Channel of the video pub channel: Channel, /// Number of views / current viewers in case of a livestream. pub view_count: u64, /// Number of likes /// /// `None` if the like count was hidden by the creator. pub like_count: Option, /// Video publishing date. Start date in case of a livestream. /// /// `None` if the date could not be parsed. pub publish_date: Option>, /// Textual video publishing date (e.g. `Aug 2, 2013`, depends on language) pub publish_date_txt: String, /// Is the video a livestream? pub is_live: bool, /// Is the video published under the Creative Commons BY 3.0 license? /// /// Information about the license: /// /// https://www.youtube.com/t/creative_commons /// /// https://creativecommons.org/licenses/by/3.0/ pub is_ccommons: bool, /// Recommended videos /// /// Note: Recommendations are not available for age-restricted videos pub recommended: Paginator, /// Paginator to fetch comments (most liked first) pub top_comments: Paginator, /// Paginator to fetch comments (latest first) pub latest_comments: Paginator, } /* @RECOMMENDATIONS */ #[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)] pub struct RecommendedVideo { /// Unique YouTube video ID pub id: String, /// Video title pub title: String, /// Video length in seconds. /// /// Is `None` for livestreams. pub length: Option, /// Video thumbnail pub thumbnail: Vec, /// Channel of the video pub channel: Channel, /// Video publishing date. /// /// `None` if the date could not be parsed. pub publish_date: Option>, /// Textual video publish date (e.g. `11 months ago`, depends on language) /// /// Is `None` for livestreams. pub publish_date_txt: Option, /// View count /// /// Is `None` if it could not be parsed pub view_count: Option, /// Is the video an active livestream? pub is_live: bool, } #[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)] pub struct Channel { /// Unique YouTube channel ID pub id: String, /// Channel name pub name: String, /// Channel avatar/profile picture pub avatar: Vec, /// Channel verification mark pub verification: Verification, /// Approximate number of subscribers /// /// Info: This is only present in the `VideoDetails` response pub subscriber_count: Option, /// Textual subscriber count (e.g `1.41M subscribers`, depends on language) pub subscriber_count_txt: Option, } /* @COMMENTS */ #[derive(Default, Copy, Clone, Debug, Serialize, Deserialize, PartialEq, Eq, Hash)] #[serde(rename_all = "snake_case")] pub enum Verification { #[default] /// Unverified channel (default) None, /// Verified channel (✓ checkmark symbol) Verified, /// Verified music artist (♪ music note symbol) Artist, } impl Verification { pub fn verified(&self) -> bool { self != &Self::None } } // TODO: impl popularity comparison #[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)] pub struct Comment { /// Unique YouTube Comment-ID (e.g. `UgynScMrsqGSL8qvePl4AaABAg`) pub id: String, /// Comment text pub text: RichText, /// Comment author /// /// There may be comments with missing authors (possibly deleted users?). pub author: Option, /// Comment publishing date. /// /// `None` if the date could not be parsed. pub publish_date: Option>, /// Textual comment publish date (e.g. `14 hours ago`), depends on language setting pub publish_date_txt: String, /// Number of comment likes pub like_count: Option, /// Number of replies pub reply_count: u32, /// Paginator to fetch comment replies pub replies: Paginator, /// Is the comment from the channel owner? pub by_owner: bool, /// Has the channel owner pinned the comment to the top? pub pinned: bool, /// Has the channel owner marked the comment with a ❤️ heart ? pub hearted: bool, }