use crate::{
youtube::{
self, browse,
innertube::{Browse, ChannelPage},
},
Client,
};
define_id! {
24,
"An Id describing a [`Channel`]",
[
"channel/",
]
}
impl Id {
pub fn uploads(mut self) -> crate::playlist::Id {
self.0[1] = b'U';
crate::playlist::Id(String::from(&*self))
}
}
#[derive(Debug, PartialEq, Eq, Clone, Copy)]
pub enum Badge {
VerifiedArtist,
Verified,
}
impl Badge {
pub(crate) fn from(badge: &youtube::Badge) -> Self {
match badge.metadata_badge_renderer.style.as_str() {
"BADGE_STYLE_TYPE_VERIFIED_ARTIST" => Self::VerifiedArtist,
"BADGE_STYLE_TYPE_VERIFIED" => Self::Verified,
badge => unimplemented!("Unknown badge: '{}'", badge),
}
}
}
#[derive(Clone)]
pub struct Channel {
client: Client,
response: browse::channel::about::Root,
}
impl Channel {
pub(crate) async fn get(client: Client, id: Id) -> crate::Result<Self> {
let response: browse::channel::about::Result = client
.api
.browse(Browse::Channel {
id,
page: ChannelPage::About,
})
.await?;
let response = response.into_std()?;
Ok(Self { client, response })
}
fn contents(&self) -> &browse::channel::about::ChannelAboutFullMetadataRenderer {
&self
.response
.contents()
.section_list_renderer
.contents
.0
.item_section_renderer
.contents
.0
.channel_about_full_metadata_renderer
}
fn header(&self) -> &browse::channel::C4TabbedHeaderRenderer {
&self.response.header.c4_tabbed_header_renderer
}
pub fn id(&self) -> Id {
self.header().channel_id
}
pub fn name(&self) -> &str {
&self.header().title
}
pub fn description(&self) -> &str {
&self.contents().description.simple_text
}
pub fn country(&self) -> Option<&str> {
self.contents()
.country
.as_ref()
.map(|x| x.simple_text.as_str())
}
pub fn views(&self) -> u64 {
self.contents().views()
}
pub fn subscribers(&self) -> Option<u64> {
self.header().subscribers()
}
pub fn avatar(&self) -> impl Iterator<Item = &crate::Thumbnail> {
self.header().avatar.thumbnails.iter()
}
pub fn banner(&self) -> impl Iterator<Item = &crate::Thumbnail> {
self.header().banner.thumbnails.iter()
}
pub fn badges(&self) -> impl Iterator<Item = Badge> + '_ {
self.header().badges.iter().map(Badge::from)
}
pub async fn uploads(
&self,
) -> crate::Result<
impl futures_core::Stream<Item = Result<crate::playlist::Video, crate::playlist::video::Error>>,
> {
Ok(self.client.playlist(self.id().uploads()).await?.videos())
}
}
impl std::fmt::Debug for Channel {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("Channel")
.field("id", &self.id())
.field("name", &self.name())
.field("description", &self.description())
.field("country", &self.country())
.field("views", &self.views())
.field("subscribers", &self.subscribers())
.field("avatar", &self.avatar().collect::<Vec<_>>())
.field("banner", &self.banner().collect::<Vec<_>>())
.field("badges", &self.badges().collect::<Vec<_>>())
.finish()
}
}
impl PartialEq for Channel {
fn eq(&self, other: &Self) -> bool {
self.id() == other.id()
}
}
impl Eq for Channel {}