feat: add channel search
This commit is contained in:
parent
402f5834d2
commit
4c1876cb55
8 changed files with 151 additions and 171 deletions
|
|
@ -1,9 +1,11 @@
|
|||
use serde::Deserialize;
|
||||
use serde_with::{rust::deserialize_ignore_any, serde_as, DefaultOnError, VecSkipError};
|
||||
|
||||
use super::url_endpoint::NavigationEndpoint;
|
||||
use super::{Alert, ChannelBadge, ContentsRenderer, ResponseContext, Thumbnails, YouTubeListItem};
|
||||
use crate::serializer::{text::Text, MapResult, VecLogError};
|
||||
use super::{
|
||||
video_item::YouTubeListRenderer, Alert, ChannelBadge, ContentsRenderer, ResponseContext,
|
||||
Thumbnails,
|
||||
};
|
||||
use crate::serializer::text::Text;
|
||||
|
||||
#[serde_as]
|
||||
#[derive(Debug, Deserialize)]
|
||||
|
|
@ -39,6 +41,7 @@ pub(crate) struct TabsRenderer {
|
|||
#[derive(Debug, Deserialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub(crate) struct TabRendererWrap {
|
||||
#[serde(alias = "expandableTabRenderer")]
|
||||
pub tab_renderer: TabRenderer,
|
||||
}
|
||||
|
||||
|
|
@ -56,11 +59,10 @@ pub(crate) struct TabRenderer {
|
|||
pub(crate) struct TabContent {
|
||||
#[serde(default)]
|
||||
#[serde_as(as = "DefaultOnError")]
|
||||
pub section_list_renderer: Option<ContentsRenderer<ItemSectionRendererWrap>>,
|
||||
/// Seems to be currently A/B tested, as of 11.10.2022
|
||||
pub section_list_renderer: Option<YouTubeListRenderer>,
|
||||
#[serde(default)]
|
||||
#[serde_as(as = "DefaultOnError")]
|
||||
pub rich_grid_renderer: Option<RichGridRenderer>,
|
||||
pub rich_grid_renderer: Option<YouTubeListRenderer>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Deserialize)]
|
||||
|
|
@ -81,35 +83,6 @@ pub(crate) struct ChannelTabWebCommandMetadata {
|
|||
pub url: String,
|
||||
}
|
||||
|
||||
/// Seems to be currently A/B tested, as of 11.10.2022
|
||||
#[serde_as]
|
||||
#[derive(Debug, Deserialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub(crate) struct RichGridRenderer {
|
||||
#[serde_as(as = "VecLogError<_>")]
|
||||
pub contents: MapResult<Vec<YouTubeListItem>>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Deserialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub(crate) struct ItemSectionRendererWrap {
|
||||
pub item_section_renderer: ContentsRenderer<ChannelContent>,
|
||||
}
|
||||
|
||||
#[serde_as]
|
||||
#[derive(Default, Debug, Deserialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub(crate) enum ChannelContent {
|
||||
GridRenderer {
|
||||
#[serde_as(as = "VecLogError<_>")]
|
||||
items: MapResult<Vec<YouTubeListItem>>,
|
||||
},
|
||||
ChannelAboutFullMetadataRenderer(ChannelFullMetadata),
|
||||
#[default]
|
||||
#[serde(other, deserialize_with = "deserialize_ignore_any")]
|
||||
None,
|
||||
}
|
||||
|
||||
#[derive(Debug, Deserialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub(crate) enum Header {
|
||||
|
|
@ -184,25 +157,3 @@ pub(crate) struct MicroformatDataRenderer {
|
|||
#[serde(default)]
|
||||
pub tags: Vec<String>,
|
||||
}
|
||||
|
||||
#[serde_as]
|
||||
#[derive(Debug, Deserialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub(crate) struct ChannelFullMetadata {
|
||||
#[serde_as(as = "Text")]
|
||||
pub joined_date_text: String,
|
||||
#[serde_as(as = "Option<Text>")]
|
||||
pub view_count_text: Option<String>,
|
||||
#[serde(default)]
|
||||
#[serde_as(as = "VecSkipError<_>")]
|
||||
pub primary_links: Vec<PrimaryLink>,
|
||||
}
|
||||
|
||||
#[serde_as]
|
||||
#[derive(Debug, Deserialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub(crate) struct PrimaryLink {
|
||||
#[serde_as(as = "Text")]
|
||||
pub title: String,
|
||||
pub navigation_endpoint: NavigationEndpoint,
|
||||
}
|
||||
|
|
|
|||
|
|
@ -53,6 +53,8 @@ use crate::error::ExtractionError;
|
|||
use crate::serializer::MapResult;
|
||||
use crate::serializer::{text::Text, VecLogError};
|
||||
|
||||
use self::video_item::YouTubeListRenderer;
|
||||
|
||||
#[derive(Debug, Deserialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub(crate) struct ContentRenderer<T> {
|
||||
|
|
@ -215,15 +217,7 @@ pub(crate) struct ContinuationAction {
|
|||
#[derive(Debug, Deserialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub(crate) struct RichGridContinuationContents {
|
||||
pub rich_grid_continuation: RichGridContinuation,
|
||||
}
|
||||
|
||||
#[serde_as]
|
||||
#[derive(Debug, Deserialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub(crate) struct RichGridContinuation {
|
||||
#[serde_as(as = "VecLogError<_>")]
|
||||
pub contents: MapResult<Vec<YouTubeListItem>>,
|
||||
pub rich_grid_continuation: YouTubeListRenderer,
|
||||
}
|
||||
|
||||
#[derive(Debug, Deserialize)]
|
||||
|
|
|
|||
|
|
@ -6,9 +6,12 @@ use serde_with::{
|
|||
};
|
||||
use time::{Duration, OffsetDateTime};
|
||||
|
||||
use super::{ChannelBadge, ContinuationEndpoint, Thumbnails};
|
||||
use super::{url_endpoint::NavigationEndpoint, ChannelBadge, ContinuationEndpoint, Thumbnails};
|
||||
use crate::{
|
||||
model::{Channel, ChannelId, ChannelItem, ChannelTag, PlaylistItem, VideoItem, YouTubeItem},
|
||||
model::{
|
||||
Channel, ChannelId, ChannelInfo, ChannelItem, ChannelTag, PlaylistItem, VideoItem,
|
||||
YouTubeItem,
|
||||
},
|
||||
param::Language,
|
||||
serializer::{
|
||||
text::{AccessibilityText, Text, TextComponent},
|
||||
|
|
@ -45,6 +48,9 @@ pub(crate) enum YouTubeListItem {
|
|||
corrected_query: String,
|
||||
},
|
||||
|
||||
/// Channel metadata (about tab)
|
||||
ChannelAboutFullMetadataRenderer(ChannelFullMetadata),
|
||||
|
||||
/// Contains video on startpage
|
||||
///
|
||||
/// Seems to be currently A/B tested on the channel page,
|
||||
|
|
@ -58,7 +64,9 @@ pub(crate) enum YouTubeListItem {
|
|||
///
|
||||
/// Seems to be currently A/B tested on the video details page,
|
||||
/// as of 11.10.2022
|
||||
#[serde(alias = "expandedShelfContentsRenderer")]
|
||||
///
|
||||
/// GridRenderer: contains videos on channel page
|
||||
#[serde(alias = "expandedShelfContentsRenderer", alias = "gridRenderer")]
|
||||
ItemSectionRenderer {
|
||||
#[serde(alias = "items")]
|
||||
#[serde_as(as = "VecLogError<_>")]
|
||||
|
|
@ -326,6 +334,28 @@ pub(crate) struct ReelPlayerHeaderRenderer {
|
|||
pub timestamp_text: String,
|
||||
}
|
||||
|
||||
#[serde_as]
|
||||
#[derive(Debug, Deserialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub(crate) struct ChannelFullMetadata {
|
||||
#[serde_as(as = "Text")]
|
||||
pub joined_date_text: String,
|
||||
#[serde_as(as = "Option<Text>")]
|
||||
pub view_count_text: Option<String>,
|
||||
#[serde(default)]
|
||||
#[serde_as(as = "VecSkipError<_>")]
|
||||
pub primary_links: Vec<PrimaryLink>,
|
||||
}
|
||||
|
||||
#[serde_as]
|
||||
#[derive(Debug, Deserialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub(crate) struct PrimaryLink {
|
||||
#[serde_as(as = "Text")]
|
||||
pub title: String,
|
||||
pub navigation_endpoint: NavigationEndpoint,
|
||||
}
|
||||
|
||||
trait IsLive {
|
||||
fn is_live(&self) -> bool;
|
||||
}
|
||||
|
|
@ -369,6 +399,7 @@ pub(crate) struct YouTubeListMapper<T> {
|
|||
pub warnings: Vec<String>,
|
||||
pub ctoken: Option<String>,
|
||||
pub corrected_query: Option<String>,
|
||||
pub channel_info: Option<ChannelInfo>,
|
||||
}
|
||||
|
||||
impl<T> YouTubeListMapper<T> {
|
||||
|
|
@ -380,6 +411,7 @@ impl<T> YouTubeListMapper<T> {
|
|||
warnings: Vec::new(),
|
||||
ctoken: None,
|
||||
corrected_query: None,
|
||||
channel_info: None,
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -397,6 +429,7 @@ impl<T> YouTubeListMapper<T> {
|
|||
warnings: Vec::new(),
|
||||
ctoken: None,
|
||||
corrected_query: None,
|
||||
channel_info: None,
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -581,6 +614,28 @@ impl YouTubeListMapper<YouTubeItem> {
|
|||
YouTubeListItem::ShowingResultsForRenderer { corrected_query } => {
|
||||
self.corrected_query = Some(corrected_query);
|
||||
}
|
||||
YouTubeListItem::ChannelAboutFullMetadataRenderer(meta) => {
|
||||
self.channel_info = Some(ChannelInfo {
|
||||
create_date: timeago::parse_textual_date_or_warn(
|
||||
self.lang,
|
||||
&meta.joined_date_text,
|
||||
&mut self.warnings,
|
||||
)
|
||||
.map(OffsetDateTime::date),
|
||||
view_count: meta
|
||||
.view_count_text
|
||||
.and_then(|txt| util::parse_numeric_or_warn(&txt, &mut self.warnings)),
|
||||
links: meta
|
||||
.primary_links
|
||||
.into_iter()
|
||||
.filter_map(|l| {
|
||||
l.navigation_endpoint
|
||||
.url_endpoint
|
||||
.map(|url| (l.title, util::sanitize_yt_url(&url.url)))
|
||||
})
|
||||
.collect(),
|
||||
})
|
||||
}
|
||||
YouTubeListItem::RichItemRenderer { content } => {
|
||||
self.map_item(*content);
|
||||
}
|
||||
|
|
|
|||
Reference in a new issue