refactor: use unified models for video/playlist/channel
This commit is contained in:
parent
b22f6995cc
commit
dbcb7fe0df
41 changed files with 2156 additions and 1228 deletions
119
src/model/mod.rs
119
src/model/mod.rs
|
|
@ -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(()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
Reference in a new issue