refactor: use unified models for video/playlist/channel

This commit is contained in:
ThetaDev 2022-10-17 00:55:49 +02:00
parent b22f6995cc
commit dbcb7fe0df
41 changed files with 2156 additions and 1228 deletions

View file

@ -4,6 +4,7 @@ mod ordering;
mod paginator;
pub mod richtext;
pub use paginator::ContinuationEndpoint;
pub use paginator::Paginator;
use std::ops::Range;
@ -810,7 +811,7 @@ pub struct ChannelRssVideo {
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
pub struct SearchResult {
pub items: Paginator<SearchItem>,
pub items: Paginator<YouTubeItem>,
pub corrected_query: Option<String>,
}
@ -902,3 +903,119 @@ pub struct SearchChannel {
/// Abbreviated channel description
pub short_description: String,
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
pub enum YouTubeItem {
Video(VideoItem),
Playlist(PlaylistItem),
Channel(ChannelItem),
}
/// YouTube video list item
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
#[non_exhaustive]
pub struct VideoItem {
/// Unique YouTube video ID
pub id: String,
/// Video title
pub title: String,
/// Video length in seconds.
///
/// Is [`None`] for livestreams.
pub length: Option<u32>,
/// Video thumbnail
pub thumbnail: Vec<Thumbnail>,
/// Channel of the video
pub channel: Option<ChannelTag>,
/// Video publishing date.
///
/// [`None`] if the date could not be parsed.
pub publish_date: Option<DateTime<Local>>,
/// Textual video publish date (e.g. `11 months ago`, depends on language)
///
/// Is [`None`] for livestreams.
pub publish_date_txt: Option<String>,
/// View count
///
/// [`None`] if it could not be extracted.
pub view_count: Option<u64>,
/// Is the video an active livestream?
pub is_live: bool,
/// Is the video a YouTube Short video (vertical and <60s)?
pub is_short: bool,
/// Is the video announced, but not released yet (YouTube Premiere)?
pub is_upcoming: bool,
/// Abbreviated video description
pub short_description: Option<String>,
}
/// YouTube channel list item
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
#[non_exhaustive]
pub struct ChannelItem {
/// Unique YouTube channel ID
pub id: String,
/// Channel name
pub name: String,
/// Channel avatar/profile picture
pub avatar: Vec<Thumbnail>,
/// Channel verification mark
pub verification: Verification,
/// Approximate number of subscribers
///
/// [`None`] if hidden by the owner or not present.
pub subscriber_count: Option<u64>,
/// Number of videos from the channel
pub video_count: u64,
/// Abbreviated channel description
pub short_description: String,
}
/// YouTube playlist list item
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
#[non_exhaustive]
pub struct PlaylistItem {
/// Unique YouTube Playlist-ID (e.g. `PL5dDx681T4bR7ZF1IuWzOv1omlRbE7PiJ`)
pub id: String,
/// Playlist name
pub name: String,
/// Playlist thumbnail
pub thumbnail: Vec<Thumbnail>,
/// Channel of the playlist
pub channel: Option<ChannelTag>,
/// Number of playlist videos
pub video_count: Option<u64>,
}
impl TryFrom<YouTubeItem> for VideoItem {
type Error = ();
fn try_from(value: YouTubeItem) -> Result<Self, Self::Error> {
match value {
YouTubeItem::Video(video) => Ok(video),
_ => Err(()),
}
}
}
impl TryFrom<YouTubeItem> for PlaylistItem {
type Error = ();
fn try_from(value: YouTubeItem) -> Result<Self, Self::Error> {
match value {
YouTubeItem::Playlist(playlist) => Ok(playlist),
_ => Err(()),
}
}
}
impl TryFrom<YouTubeItem> for ChannelItem {
type Error = ();
fn try_from(value: YouTubeItem) -> Result<Self, Self::Error> {
match value {
YouTubeItem::Channel(channel) => Ok(channel),
_ => Err(()),
}
}
}

View file

@ -31,6 +31,26 @@ pub struct Paginator<T> {
/// YouTube visitor data. Required for fetching the startpage
#[serde(skip_serializing_if = "Option::is_none")]
pub visitor_data: Option<String>,
/// YouTube API endpoint to fetch continuations from
pub endpoint: ContinuationEndpoint,
}
#[derive(Debug, Copy, Clone, Serialize, Deserialize, PartialEq, Eq)]
#[serde(rename_all = "lowercase")]
pub enum ContinuationEndpoint {
Browse,
Search,
Next,
}
impl ContinuationEndpoint {
pub(crate) fn as_str(self) -> &'static str {
match self {
ContinuationEndpoint::Browse => "browse",
ContinuationEndpoint::Search => "search",
ContinuationEndpoint::Next => "next",
}
}
}
impl<T> Default for Paginator<T> {
@ -40,6 +60,7 @@ impl<T> Default for Paginator<T> {
items: Vec::new(),
ctoken: None,
visitor_data: None,
endpoint: ContinuationEndpoint::Browse,
}
}
}
@ -63,6 +84,25 @@ impl<T> Paginator<T> {
items,
ctoken,
visitor_data,
endpoint: ContinuationEndpoint::Browse,
}
}
pub(crate) fn new_with_endpoint(
count: Option<u64>,
items: Vec<T>,
ctoken: Option<String>,
endpoint: ContinuationEndpoint,
) -> Self {
Self {
count: match ctoken {
Some(_) => count,
None => items.len().try_into().ok(),
},
items,
ctoken,
visitor_data: None,
endpoint,
}
}