mod convert;
mod frameset;
mod ordering;
pub mod paginator;
pub mod richtext;
pub mod traits;
pub use frameset::{Frameset, FramesetUrls};
use std::{collections::BTreeSet, ops::Range};
use serde::{Deserialize, Serialize};
use time::{Date, OffsetDateTime};
use self::{paginator::Paginator, richtext::RichText};
use crate::{client::ClientType, error::Error, param::Country, validate};
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)]
#[non_exhaustive]
pub struct Thumbnail {
pub url: String,
pub width: u32,
pub height: u32,
}
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)]
pub enum UrlTarget {
Video {
id: String,
start_time: u32,
},
Channel {
id: String,
},
Playlist {
id: String,
},
Album {
id: String,
},
}
impl std::fmt::Display for UrlTarget {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.write_str(&self.to_url())
}
}
impl UrlTarget {
pub fn to_url(&self) -> String {
self.to_url_yt_host("https://www.youtube.com")
}
pub fn to_url_yt_host(&self, yt_host: &str) -> String {
match self {
UrlTarget::Video { id, start_time, .. } => match start_time {
0 => format!("{yt_host}/watch?v={id}"),
n => format!("{yt_host}/watch?v={id}&t={n}s"),
},
UrlTarget::Channel { id } => {
format!("{yt_host}/channel/{id}")
}
UrlTarget::Playlist { id } => {
format!("{yt_host}/playlist?list={id}")
}
UrlTarget::Album { id } => {
format!("https://music.youtube.com/browse/{id}")
}
}
}
pub(crate) fn validate(&self) -> Result<(), Error> {
match self {
UrlTarget::Video { id, .. } => validate::video_id(id),
UrlTarget::Channel { id } => validate::channel_id(id),
UrlTarget::Playlist { id } => validate::playlist_id(id),
UrlTarget::Album { id } => validate::album_id(id),
}
}
}
#[derive(Clone, Debug, Serialize, Deserialize)]
#[non_exhaustive]
pub struct VideoPlayer {
pub details: VideoPlayerDetails,
pub video_streams: Vec<VideoStream>,
pub video_only_streams: Vec<VideoStream>,
pub audio_streams: Vec<AudioStream>,
pub subtitles: Vec<Subtitle>,
pub expires_in_seconds: u32,
#[serde(with = "time::serde::rfc3339")]
pub valid_until: OffsetDateTime,
pub hls_manifest_url: Option<String>,
pub dash_manifest_url: Option<String>,
pub preview_frames: Vec<Frameset>,
pub drm: Option<VideoPlayerDrm>,
pub client_type: ClientType,
pub visitor_data: Option<String>,
}
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)]
#[non_exhaustive]
pub struct VideoPlayerDetails {
pub id: String,
pub name: Option<String>,
pub description: Option<String>,
pub duration: u32,
pub thumbnail: Vec<Thumbnail>,
pub channel_id: String,
pub channel_name: Option<String>,
pub view_count: Option<u64>,
pub keywords: Vec<String>,
pub is_live: bool,
pub is_live_content: bool,
}
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)]
#[non_exhaustive]
pub struct VideoStream {
pub url: String,
pub itag: u32,
pub bitrate: u32,
pub average_bitrate: u32,
pub size: Option<u64>,
pub index_range: Option<Range<u32>>,
pub init_range: Option<Range<u32>>,
pub duration_ms: Option<u32>,
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 drm_track_type: Option<DrmTrackType>,
pub drm_systems: Vec<DrmSystem>,
}
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)]
#[non_exhaustive]
pub struct AudioStream {
pub url: String,
pub itag: u32,
pub bitrate: u32,
pub average_bitrate: u32,
pub size: u64,
pub index_range: Option<Range<u32>>,
pub init_range: Option<Range<u32>>,
pub duration_ms: Option<u32>,
pub mime: String,
pub format: AudioFormat,
pub codec: AudioCodec,
pub channels: Option<u8>,
pub loudness_db: Option<f32>,
pub track: Option<AudioTrack>,
pub drm_track_type: Option<DrmTrackType>,
pub drm_systems: Vec<DrmSystem>,
}
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)]
#[serde(rename_all = "snake_case")]
pub struct VideoPlayerDrm {
pub widevine_service_cert: Option<Vec<u8>>,
pub drm_params: String,
pub drm_session_id: String,
pub authorized_track_types: Vec<DrmTrackType>,
}
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)]
#[serde(rename_all = "snake_case")]
pub struct DrmLicense {
pub license: Vec<u8>,
pub authorized_formats: Vec<(DrmTrackType, [u8; 16])>,
}
#[derive(
Default, Copy, Clone, Debug, Serialize, Deserialize, PartialEq, Eq, PartialOrd, Ord, Hash,
)]
#[serde(rename_all = "snake_case")]
#[non_exhaustive]
pub enum VideoCodec {
#[default]
Unknown,
Mp4v,
Avc1,
Vp9,
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,
Opus,
#[serde(rename = "ac-3")]
Ac3,
#[serde(rename = "ec-3")]
Ec3,
}
#[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, Copy, Debug, Serialize, Deserialize, PartialEq, Eq, Hash)]
#[serde(rename_all = "snake_case")]
pub enum DrmTrackType {
Audio,
Sd,
Hd,
Uhd1,
}
#[derive(Clone, Copy, Debug, Serialize, Deserialize, PartialEq, Eq, Hash)]
#[serde(rename_all = "snake_case")]
pub enum DrmSystem {
Widevine,
Playready,
Fairplay,
}
impl DrmSystem {
pub(crate) fn req_param(self) -> &'static str {
match self {
DrmSystem::Widevine => "DRM_SYSTEM_WIDEVINE",
DrmSystem::Playready => "DRM_SYSTEM_PLAYREADY",
DrmSystem::Fairplay => "DRM_SYSTEM_FAIRPLAY",
}
}
}
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)]
#[non_exhaustive]
pub struct AudioTrack {
pub id: String,
pub lang: Option<String>,
pub lang_name: String,
pub is_default: bool,
pub track_type: Option<AudioTrackType>,
}
#[derive(Copy, Clone, Debug, Serialize, Deserialize, PartialEq, Eq, PartialOrd, Ord, Hash)]
#[serde(rename_all = "snake_case")]
#[non_exhaustive]
pub enum AudioFormat {
M4a,
Webm,
}
#[derive(Copy, Clone, Debug, Serialize, Deserialize, PartialEq, Eq, PartialOrd, Ord, Hash)]
#[serde(rename_all = "kebab-case")]
#[non_exhaustive]
pub enum AudioTrackType {
Original,
Dubbed,
DubbedAuto,
Descriptive,
}
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)]
#[non_exhaustive]
pub struct Subtitle {
pub url: String,
pub lang: String,
pub lang_name: String,
pub auto_generated: bool,
}
#[derive(Clone, Debug, Serialize, Deserialize)]
#[non_exhaustive]
pub struct Playlist {
pub id: String,
pub name: String,
pub videos: Paginator<VideoItem>,
pub video_count: u64,
pub thumbnail: Vec<Thumbnail>,
pub description: Option<RichText>,
pub channel: Option<ChannelId>,
pub last_update: Option<Date>,
pub last_update_txt: Option<String>,
pub visitor_data: Option<String>,
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
#[non_exhaustive]
pub struct ChannelId {
pub id: String,
pub name: String,
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
#[non_exhaustive]
pub struct VideoDetails {
pub id: String,
pub name: String,
pub description: RichText,
pub channel: ChannelTag,
pub view_count: u64,
pub like_count: Option<u32>,
#[serde(with = "time::serde::rfc3339::option")]
pub publish_date: Option<OffsetDateTime>,
pub publish_date_txt: Option<String>,
pub is_live: bool,
pub is_ccommons: bool,
pub chapters: Vec<Chapter>,
pub recommended: Paginator<VideoItem>,
pub top_comments: Paginator<Comment>,
pub latest_comments: Paginator<Comment>,
pub visitor_data: Option<String>,
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
#[non_exhaustive]
pub struct Chapter {
pub name: String,
pub position: u32,
pub thumbnail: Vec<Thumbnail>,
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
#[non_exhaustive]
pub struct ChannelTag {
pub id: String,
pub name: String,
pub avatar: Vec<Thumbnail>,
pub verification: Verification,
pub subscriber_count: Option<u64>,
}
#[derive(
Default, Copy, Clone, Debug, Serialize, Deserialize, PartialEq, Eq, PartialOrd, Ord, Hash,
)]
#[serde(rename_all = "snake_case")]
pub enum Verification {
#[default]
None,
Verified,
Artist,
}
impl Verification {
pub fn verified(&self) -> bool {
self != &Self::None
}
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
#[non_exhaustive]
pub struct Comment {
pub id: String,
pub text: RichText,
pub author: Option<ChannelTag>,
#[serde(with = "time::serde::rfc3339::option")]
pub publish_date: Option<OffsetDateTime>,
pub publish_date_txt: String,
pub like_count: Option<u32>,
pub reply_count: u32,
pub replies: Paginator<Comment>,
pub by_owner: bool,
pub pinned: bool,
pub hearted: bool,
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
#[non_exhaustive]
pub struct Channel<T> {
pub id: String,
pub name: String,
pub handle: Option<String>,
pub subscriber_count: Option<u64>,
pub video_count: Option<u64>,
pub avatar: Vec<Thumbnail>,
pub verification: Verification,
pub description: String,
pub tags: Vec<String>,
pub banner: Vec<Thumbnail>,
pub has_shorts: bool,
pub has_live: bool,
pub visitor_data: Option<String>,
pub content: T,
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
#[non_exhaustive]
pub struct ChannelInfo {
pub id: String,
pub url: String,
pub description: String,
pub subscriber_count: Option<u64>,
pub video_count: Option<u64>,
pub create_date: Option<Date>,
pub view_count: Option<u64>,
pub country: Option<Country>,
pub links: Vec<(String, String)>,
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
#[non_exhaustive]
pub struct ChannelRss {
pub id: String,
pub name: String,
pub videos: Vec<ChannelRssVideo>,
#[serde(with = "time::serde::rfc3339")]
pub create_date: OffsetDateTime,
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
#[non_exhaustive]
pub struct ChannelRssVideo {
pub id: String,
pub name: String,
pub description: String,
pub thumbnail: Thumbnail,
#[serde(with = "time::serde::rfc3339")]
pub publish_date: OffsetDateTime,
#[serde(with = "time::serde::rfc3339")]
pub update_date: OffsetDateTime,
pub view_count: u64,
pub like_count: u64,
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
pub struct SearchResult<T> {
pub items: Paginator<T>,
pub corrected_query: Option<String>,
pub visitor_data: Option<String>,
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
pub enum YouTubeItem {
Video(VideoItem),
Playlist(PlaylistItem),
Channel(ChannelItem),
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
#[non_exhaustive]
pub struct VideoItem {
pub id: String,
pub name: String,
pub duration: Option<u32>,
pub thumbnail: Vec<Thumbnail>,
pub channel: Option<ChannelTag>,
#[serde(with = "time::serde::rfc3339::option")]
pub publish_date: Option<OffsetDateTime>,
pub publish_date_txt: Option<String>,
pub view_count: Option<u64>,
pub is_live: bool,
pub is_short: bool,
pub is_upcoming: bool,
pub short_description: Option<String>,
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
#[non_exhaustive]
pub struct ChannelItem {
pub id: String,
pub name: String,
pub handle: Option<String>,
pub avatar: Vec<Thumbnail>,
pub verification: Verification,
pub subscriber_count: Option<u64>,
pub short_description: String,
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
#[non_exhaustive]
pub struct PlaylistItem {
pub id: String,
pub name: String,
pub thumbnail: Vec<Thumbnail>,
pub channel: Option<ChannelTag>,
pub video_count: Option<u64>,
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
#[non_exhaustive]
pub struct VideoId {
pub id: String,
pub name: String,
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
#[non_exhaustive]
pub struct TrackItem {
pub id: String,
pub name: String,
pub duration: Option<u32>,
pub cover: Vec<Thumbnail>,
pub artists: Vec<ArtistId>,
pub artist_id: Option<String>,
pub album: Option<AlbumId>,
pub view_count: Option<u64>,
pub track_type: TrackType,
pub track_nr: Option<u16>,
pub by_va: bool,
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
#[non_exhaustive]
pub struct ArtistItem {
pub id: String,
pub name: String,
pub avatar: Vec<Thumbnail>,
pub subscriber_count: Option<u64>,
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
pub struct UserItem {
pub id: String,
pub name: String,
pub handle: Option<String>,
pub avatar: Vec<Thumbnail>,
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
#[non_exhaustive]
pub struct ArtistId {
pub id: Option<String>,
pub name: String,
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
#[non_exhaustive]
pub struct AlbumItem {
pub id: String,
pub name: String,
pub cover: Vec<Thumbnail>,
pub artists: Vec<ArtistId>,
pub artist_id: Option<String>,
pub album_type: AlbumType,
pub year: Option<u16>,
pub by_va: bool,
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
#[non_exhaustive]
pub struct MusicPlaylistItem {
pub id: String,
pub name: String,
pub thumbnail: Vec<Thumbnail>,
pub channel: Option<ChannelId>,
pub track_count: Option<u64>,
pub from_ytm: bool,
pub is_podcast: bool,
}
#[derive(
Default, Debug, Copy, Clone, Serialize, Deserialize, PartialEq, Eq, PartialOrd, Ord, Hash,
)]
#[serde(rename_all = "snake_case")]
#[non_exhaustive]
pub enum AlbumType {
#[default]
Album,
Ep,
Single,
Audiobook,
Show,
}
#[derive(Debug, Copy, Clone, Serialize, Deserialize, PartialEq, Eq, PartialOrd, Ord, Hash)]
#[serde(rename_all = "snake_case")]
pub enum TrackType {
Track,
Video,
Episode,
}
impl TrackType {
pub fn is_track(&self) -> bool {
self == &Self::Track
}
pub fn is_video(&self) -> bool {
self != &Self::Track
}
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
#[non_exhaustive]
pub struct AlbumId {
pub id: String,
pub name: String,
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
#[non_exhaustive]
pub struct MusicPlaylist {
pub id: String,
pub name: String,
pub thumbnail: Vec<Thumbnail>,
pub channel: Option<ChannelId>,
pub description: Option<RichText>,
pub track_count: Option<u64>,
pub from_ytm: bool,
pub tracks: Paginator<TrackItem>,
pub related_playlists: Paginator<MusicPlaylistItem>,
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
#[non_exhaustive]
pub struct MusicAlbum {
pub id: String,
pub playlist_id: Option<String>,
pub name: String,
pub cover: Vec<Thumbnail>,
pub artists: Vec<ArtistId>,
pub artist_id: Option<String>,
pub description: Option<RichText>,
pub album_type: AlbumType,
pub year: Option<u16>,
pub by_va: bool,
pub track_count: u16,
pub tracks: Vec<TrackItem>,
pub variants: Vec<AlbumItem>,
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
#[non_exhaustive]
pub struct MusicArtist {
pub id: String,
pub name: String,
pub header_image: Vec<Thumbnail>,
pub description: Option<String>,
pub wikipedia_url: Option<String>,
pub subscriber_count: Option<u64>,
pub tracks: Vec<TrackItem>,
pub albums: Vec<AlbumItem>,
pub playlists: Vec<MusicPlaylistItem>,
pub similar_artists: Vec<ArtistItem>,
pub tracks_playlist_id: Option<String>,
pub videos_playlist_id: Option<String>,
pub radio_id: Option<String>,
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
#[allow(missing_docs)]
pub enum MusicItem {
Track(TrackItem),
Album(AlbumItem),
Artist(ArtistItem),
Playlist(MusicPlaylistItem),
User(UserItem),
}
#[derive(Debug, Copy, Clone, Serialize, Deserialize, PartialEq, Eq, PartialOrd, Ord, Hash)]
#[serde(rename_all = "snake_case")]
#[allow(missing_docs)]
pub enum MusicItemType {
Track,
Album,
Artist,
Playlist,
User,
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
#[non_exhaustive]
pub struct MusicSearchResult<T> {
pub items: Paginator<T>,
pub corrected_query: Option<String>,
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
#[non_exhaustive]
pub struct TrackDetails {
pub track: TrackItem,
pub lyrics_id: Option<String>,
pub related_id: Option<String>,
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
#[non_exhaustive]
pub struct Lyrics {
pub body: String,
pub footer: String,
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
#[non_exhaustive]
pub struct MusicRelated {
pub tracks: Vec<TrackItem>,
pub other_versions: Vec<TrackItem>,
pub albums: Vec<AlbumItem>,
pub artists: Vec<ArtistItem>,
pub playlists: Vec<MusicPlaylistItem>,
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
#[non_exhaustive]
pub struct MusicCharts {
pub top_tracks: Vec<TrackItem>,
pub trending_tracks: Vec<TrackItem>,
pub artists: Vec<ArtistItem>,
pub playlists: Vec<MusicPlaylistItem>,
pub top_playlist_id: Option<String>,
pub trending_playlist_id: Option<String>,
pub available_countries: BTreeSet<Country>,
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
#[non_exhaustive]
pub struct MusicGenreItem {
pub id: String,
pub name: String,
pub is_mood: bool,
pub color: u32,
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
#[non_exhaustive]
pub struct MusicGenre {
pub id: String,
pub name: String,
pub sections: Vec<MusicGenreSection>,
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
#[non_exhaustive]
pub struct MusicGenreSection {
pub name: String,
pub subgenre_id: Option<String>,
pub playlists: Vec<MusicPlaylistItem>,
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
#[non_exhaustive]
pub struct MusicSearchSuggestion {
pub terms: Vec<String>,
pub items: Vec<MusicItem>,
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
#[non_exhaustive]
pub struct HistoryItem<T> {
pub item: T,
pub playback_date: Option<Date>,
pub playback_date_txt: Option<String>,
}