//! Traits for working with response models use std::ops::Range; pub use super::{convert::FromYtItem, ordering::QualityOrd}; use super::*; /// Trait for YouTube streams (video and audio) pub trait YtStream { /// Stream URL fn url(&self) -> &str; /// YouTube stream format identifier fn itag(&self) -> u32; /// Stream bitrate (in bits/second) fn bitrate(&self) -> u32; /// Average stream bitrate (in bits/second) fn averate_bitrate(&self) -> u32; /// File size in bytes fn size(&self) -> Option; /// Index range (used for DASH streaming) fn index_range(&self) -> Option>; /// Init range (used for DASH streaming) fn init_range(&self) -> Option>; /// Stream duration in milliseconds fn duration_ms(&self) -> Option; /// MIME file type fn mime(&self) -> &str; } impl YtStream for VideoStream { fn url(&self) -> &str { &self.url } fn itag(&self) -> u32 { self.itag } fn bitrate(&self) -> u32 { self.bitrate } fn averate_bitrate(&self) -> u32 { self.average_bitrate } fn size(&self) -> Option { self.size } fn index_range(&self) -> Option> { self.index_range.clone() } fn init_range(&self) -> Option> { self.init_range.clone() } fn duration_ms(&self) -> Option { self.duration_ms } fn mime(&self) -> &str { &self.mime } } impl YtStream for AudioStream { fn url(&self) -> &str { &self.url } fn itag(&self) -> u32 { self.itag } fn bitrate(&self) -> u32 { self.bitrate } fn averate_bitrate(&self) -> u32 { self.average_bitrate } fn size(&self) -> Option { Some(self.size) } fn index_range(&self) -> Option> { self.index_range.clone() } fn init_range(&self) -> Option> { self.init_range.clone() } fn duration_ms(&self) -> Option { self.duration_ms } fn mime(&self) -> &str { &self.mime } } /// Trait for file types pub trait FileFormat { /// Get the file extension (".xyz") of the file format fn extension(&self) -> &str; } impl FileFormat for VideoFormat { fn extension(&self) -> &str { match self { VideoFormat::ThreeGp => ".3gp", VideoFormat::Mp4 => ".mp4", VideoFormat::Webm => ".webm", } } } impl FileFormat for AudioFormat { fn extension(&self) -> &str { match self { AudioFormat::M4a => ".m4a", AudioFormat::Webm => ".webm", } } } /// Trait for YouTube entities (Videos, Channels, Playlists) pub trait YtEntity { /// ID fn id(&self) -> &str; /// Name fn name(&self) -> &str; /// Channel id /// /// `None` if the entity does not belong to a channel fn channel_id(&self) -> Option<&str>; /// Channel name /// /// `None` if the entity does not belong to a channel fn channel_name(&self) -> Option<&str>; /// YTM item type fn music_item_type(&self) -> Option; } macro_rules! yt_entity { ($entity_type:ty, $music_item_type:expr) => { impl YtEntity for $entity_type { fn id(&self) -> &str { &self.id } fn name(&self) -> &str { &self.name } fn channel_id(&self) -> Option<&str> { None } fn channel_name(&self) -> Option<&str> { None } fn music_item_type(&self) -> Option { $music_item_type } } }; } macro_rules! yt_entity_owner { ($entity_type:ty, $music_item_type:expr) => { impl YtEntity for $entity_type { fn id(&self) -> &str { &self.id } fn name(&self) -> &str { &self.name } fn channel_id(&self) -> Option<&str> { Some(&self.channel.id) } fn channel_name(&self) -> Option<&str> { Some(&self.channel.name) } fn music_item_type(&self) -> Option { Some($music_item_type) } } }; } macro_rules! yt_entity_owner_opt { ($entity_type:ty, $music_item_type:expr) => { impl YtEntity for $entity_type { fn id(&self) -> &str { &self.id } fn name(&self) -> &str { &self.name } fn channel_id(&self) -> Option<&str> { self.channel.as_ref().map(|c| c.id.as_str()) } fn channel_name(&self) -> Option<&str> { self.channel.as_ref().map(|c| c.name.as_str()) } fn music_item_type(&self) -> Option { Some($music_item_type) } } }; } macro_rules! yt_entity_owner_music { ($entity_type:ty, $music_item_type:expr) => { impl YtEntity for $entity_type { fn id(&self) -> &str { &self.id } fn name(&self) -> &str { &self.name } fn channel_id(&self) -> Option<&str> { self.artists.first().and_then(|a| a.id.as_deref()) } fn channel_name(&self) -> Option<&str> { if self.by_va { Some(crate::util::VARIOUS_ARTISTS) } else { self.artists.first().map(|a| a.name.as_str()) } } fn music_item_type(&self) -> Option { Some($music_item_type) } } }; } impl YtEntity for Channel { fn id(&self) -> &str { &self.id } fn name(&self) -> &str { &self.name } fn channel_id(&self) -> Option<&str> { None } fn channel_name(&self) -> Option<&str> { None } fn music_item_type(&self) -> Option { Some(MusicItemType::User) } } impl YtEntity for YouTubeItem { fn id(&self) -> &str { match self { YouTubeItem::Video(v) => &v.id, YouTubeItem::Playlist(p) => &p.id, YouTubeItem::Channel(c) => &c.id, } } fn name(&self) -> &str { match self { YouTubeItem::Video(v) => &v.name, YouTubeItem::Playlist(p) => &p.name, YouTubeItem::Channel(c) => &c.name, } } fn channel_id(&self) -> Option<&str> { match self { YouTubeItem::Video(v) => v.channel_id(), YouTubeItem::Playlist(p) => p.channel_id(), YouTubeItem::Channel(_) => None, } } fn channel_name(&self) -> Option<&str> { match self { YouTubeItem::Video(v) => v.channel_name(), YouTubeItem::Playlist(p) => p.channel_name(), YouTubeItem::Channel(_) => None, } } fn music_item_type(&self) -> Option { Some(match self { YouTubeItem::Video(_) => MusicItemType::Track, YouTubeItem::Playlist(_) => MusicItemType::Playlist, YouTubeItem::Channel(_) => MusicItemType::User, }) } } impl YtEntity for MusicItem { fn id(&self) -> &str { match self { MusicItem::Track(t) => &t.id, MusicItem::Album(b) => &b.id, MusicItem::Artist(a) => &a.id, MusicItem::Playlist(p) => &p.id, MusicItem::User(u) => &u.id, } } fn name(&self) -> &str { match self { MusicItem::Track(t) => &t.name, MusicItem::Album(b) => &b.name, MusicItem::Artist(a) => &a.name, MusicItem::Playlist(p) => &p.name, MusicItem::User(u) => &u.name, } } fn channel_id(&self) -> Option<&str> { match self { MusicItem::Track(t) => t.channel_id(), MusicItem::Album(b) => b.channel_id(), MusicItem::Artist(_) | MusicItem::User(_) => None, MusicItem::Playlist(p) => p.channel_id(), } } fn channel_name(&self) -> Option<&str> { match self { MusicItem::Track(t) => t.channel_name(), MusicItem::Album(b) => b.channel_name(), MusicItem::Artist(_) | MusicItem::User(_) => None, MusicItem::Playlist(p) => p.channel_name(), } } fn music_item_type(&self) -> Option { Some(match self { MusicItem::Track(_) => MusicItemType::Track, MusicItem::Album(_) => MusicItemType::Album, MusicItem::Artist(_) => MusicItemType::Artist, MusicItem::Playlist(_) => MusicItemType::Playlist, MusicItem::User(_) => MusicItemType::User, }) } } yt_entity_owner_opt! {Playlist, MusicItemType::Playlist} yt_entity! {ChannelId, Some(MusicItemType::User)} yt_entity_owner! {VideoDetails, MusicItemType::Track} yt_entity! {ChannelTag, Some(MusicItemType::User)} yt_entity! {ChannelRss, Some(MusicItemType::User)} yt_entity! {ChannelRssVideo, Some(MusicItemType::Track)} yt_entity_owner_opt! {VideoItem, MusicItemType::Track} yt_entity! {ChannelItem, Some(MusicItemType::User)} yt_entity_owner_opt! {PlaylistItem, MusicItemType::Playlist} yt_entity! {VideoId, Some(MusicItemType::Track)} yt_entity_owner_music! {TrackItem, MusicItemType::Track} yt_entity! {ArtistItem, Some(MusicItemType::Artist)} yt_entity_owner_music! {AlbumItem, MusicItemType::Album} yt_entity_owner_opt! {MusicPlaylistItem, MusicItemType::Playlist} yt_entity! {AlbumId, Some(MusicItemType::Album)} yt_entity_owner_opt! {MusicPlaylist, MusicItemType::Playlist} yt_entity_owner_music! {MusicAlbum, MusicItemType::Album} yt_entity! {MusicArtist, Some(MusicItemType::Artist)} yt_entity! {UserItem, Some(MusicItemType::User)} yt_entity! {MusicGenreItem, None} yt_entity! {MusicGenre, None}