Compare commits

..

2 commits

Author SHA1 Message Date
999ebf7c36 refactor: clean up response models 2022-10-17 11:26:00 +02:00
db6b1ab8a7 refactor: update trends response model 2022-10-17 11:04:18 +02:00
12 changed files with 959 additions and 1219 deletions

View file

@ -280,10 +280,7 @@ async fn channel_videos_cont(testfiles: &Path) {
.unwrap(); .unwrap();
let rp = rp_testfile(&json_path); let rp = rp_testfile(&json_path);
rp.query() videos.content.next(rp.query()).await.unwrap().unwrap();
.channel_videos_continuation(&videos.content.ctoken.unwrap())
.await
.unwrap();
} }
async fn channel_playlists_cont(testfiles: &Path) { async fn channel_playlists_cont(testfiles: &Path) {
@ -302,10 +299,7 @@ async fn channel_playlists_cont(testfiles: &Path) {
.unwrap(); .unwrap();
let rp = rp_testfile(&json_path); let rp = rp_testfile(&json_path);
rp.query() playlists.content.next(rp.query()).await.unwrap().unwrap();
.channel_playlists_continuation(&playlists.content.ctoken.unwrap())
.await
.unwrap();
} }
async fn search(testfiles: &Path) { async fn search(testfiles: &Path) {

View file

@ -1,7 +1,6 @@
use crate::error::{Error, ExtractionError}; use crate::error::{Error, ExtractionError};
use crate::model::{ use crate::model::{
Comment, ContinuationEndpoint, Paginator, PlaylistVideo, RecommendedVideo, SearchVideo, Comment, ContinuationEndpoint, Paginator, PlaylistVideo, RecommendedVideo, YouTubeItem,
YouTubeItem,
}; };
use crate::serializer::MapResult; use crate::serializer::MapResult;
use crate::util::TryRemove; use crate::util::TryRemove;
@ -13,8 +12,10 @@ impl RustyPipeQuery {
self, self,
ctoken: &str, ctoken: &str,
endpoint: ContinuationEndpoint, endpoint: ContinuationEndpoint,
visitor_data: Option<&str>,
) -> Result<Paginator<T>, Error> { ) -> Result<Paginator<T>, Error> {
let context = self.get_context(ClientType::Desktop, true).await; let mut context = self.get_context(ClientType::Desktop, true).await;
context.client.visitor_data = visitor_data.map(str::to_owned);
let request_body = QContinuation { let request_body = QContinuation {
context, context,
continuation: ctoken, continuation: ctoken,
@ -140,64 +141,14 @@ paginator!(Comment, RustyPipeQuery::video_comments);
paginator!(PlaylistVideo, RustyPipeQuery::playlist_continuation); paginator!(PlaylistVideo, RustyPipeQuery::playlist_continuation);
paginator!(RecommendedVideo, RustyPipeQuery::video_recommendations); paginator!(RecommendedVideo, RustyPipeQuery::video_recommendations);
impl Paginator<SearchVideo> {
pub async fn next(&self, query: RustyPipeQuery) -> Result<Option<Self>, Error> {
Ok(match (&self.ctoken, &self.visitor_data) {
(Some(ctoken), Some(visitor_data)) => {
Some(query.startpage_continuation(ctoken, visitor_data).await?)
}
_ => None,
})
}
pub async fn extend(&mut self, query: RustyPipeQuery) -> Result<bool, Error> {
match self.next(query).await {
Ok(Some(paginator)) => {
let mut items = paginator.items;
self.items.append(&mut items);
self.ctoken = paginator.ctoken;
Ok(true)
}
Ok(None) => Ok(false),
Err(e) => Err(e),
}
}
pub async fn extend_pages(
&mut self,
query: RustyPipeQuery,
n_pages: usize,
) -> Result<(), Error> {
for _ in 0..n_pages {
match self.extend(query.clone()).await {
Ok(false) => break,
Err(e) => return Err(e),
_ => {}
}
}
Ok(())
}
pub async fn extend_limit(
&mut self,
query: RustyPipeQuery,
n_items: usize,
) -> Result<(), Error> {
while self.items.len() < n_items {
match self.extend(query.clone()).await {
Ok(false) => break,
Err(e) => return Err(e),
_ => {}
}
}
Ok(())
}
}
impl<T: TryFrom<YouTubeItem>> Paginator<T> { impl<T: TryFrom<YouTubeItem>> Paginator<T> {
pub async fn next(&self, query: RustyPipeQuery) -> Result<Option<Self>, Error> { pub async fn next(&self, query: RustyPipeQuery) -> Result<Option<Self>, Error> {
Ok(match &self.ctoken { Ok(match &self.ctoken {
Some(ctoken) => Some(query.continuation(ctoken, self.endpoint).await?), Some(ctoken) => Some(
query
.continuation(ctoken, self.endpoint, self.visitor_data.as_deref())
.await?,
),
_ => None, _ => None,
}) })
} }
@ -261,6 +212,7 @@ mod tests {
#[rstest] #[rstest]
#[case("search", "search/cont")] #[case("search", "search/cont")]
#[case("startpage", "trends/startpage_cont")]
fn map_continuation_items(#[case] name: &str, #[case] path: &str) { fn map_continuation_items(#[case] name: &str, #[case] path: &str) {
let filename = format!("testfiles/{}.json", path); let filename = format!("testfiles/{}.json", path);
let json_path = Path::new(&filename); let json_path = Path::new(&filename);

View file

@ -23,15 +23,6 @@ pub struct Channel {
pub alerts: Option<Vec<Alert>>, pub alerts: Option<Vec<Alert>>,
} }
#[serde_as]
#[derive(Debug, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct ChannelCont {
#[serde(default)]
#[serde_as(as = "VecSkipError<_>")]
pub on_response_received_actions: Vec<OnResponseReceivedAction>,
}
#[derive(Debug, Deserialize)] #[derive(Debug, Deserialize)]
#[serde(rename_all = "camelCase")] #[serde(rename_all = "camelCase")]
pub struct Contents { pub struct Contents {
@ -205,17 +196,3 @@ pub struct PrimaryLink {
pub title: String, pub title: String,
pub navigation_endpoint: NavigationEndpoint, pub navigation_endpoint: NavigationEndpoint,
} }
#[derive(Debug, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct OnResponseReceivedAction {
pub append_continuation_items_action: AppendAction,
}
#[serde_as]
#[derive(Debug, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct AppendAction {
#[serde_as(as = "VecLogError<_>")]
pub continuation_items: MapResult<Vec<YouTubeListItem>>,
}

View file

@ -9,15 +9,12 @@ pub mod video_details;
pub mod video_item; pub mod video_item;
pub use channel::Channel; pub use channel::Channel;
pub use channel::ChannelCont;
pub use player::Player; pub use player::Player;
pub use playlist::Playlist; pub use playlist::Playlist;
pub use playlist::PlaylistCont; pub use playlist::PlaylistCont;
pub use playlist_music::PlaylistMusic; pub use playlist_music::PlaylistMusic;
pub use search::Search; pub use search::Search;
pub use search::SearchCont;
pub use trends::Startpage; pub use trends::Startpage;
pub use trends::StartpageCont;
pub use trends::Trending; pub use trends::Trending;
pub use url_endpoint::ResolvedUrl; pub use url_endpoint::ResolvedUrl;
pub use video_details::VideoComments; pub use video_details::VideoComments;

View file

@ -1,14 +1,7 @@
use serde::Deserialize; use serde::Deserialize;
use serde_with::json::JsonString; use serde_with::{json::JsonString, serde_as};
use serde_with::{serde_as, VecSkipError};
use crate::serializer::ignore_any; use super::video_item::YouTubeListRendererWrap;
use crate::serializer::{text::Text, MapResult, VecLogError};
use super::video_item::YouTubeListItem;
use super::{
ChannelRenderer, ContentsRenderer, ContinuationEndpoint, PlaylistRenderer, VideoRenderer,
};
#[serde_as] #[serde_as]
#[derive(Debug, Deserialize)] #[derive(Debug, Deserialize)]
@ -19,28 +12,6 @@ pub struct Search {
pub contents: Contents, pub contents: Contents,
} }
#[serde_as]
#[derive(Debug, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct SearchCont {
#[serde_as(as = "Option<JsonString>")]
pub estimated_results: Option<u64>,
#[serde_as(as = "VecSkipError<_>")]
pub on_response_received_commands: Vec<SearchContCommand>,
}
#[derive(Debug, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct SearchContCommand {
pub append_continuation_items_action: SearchContAction,
}
#[derive(Debug, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct SearchContAction {
pub continuation_items: Vec<SectionListItem>,
}
#[derive(Debug, Deserialize)] #[derive(Debug, Deserialize)]
#[serde(rename_all = "camelCase")] #[serde(rename_all = "camelCase")]
pub struct Contents { pub struct Contents {
@ -50,52 +21,5 @@ pub struct Contents {
#[derive(Debug, Deserialize)] #[derive(Debug, Deserialize)]
#[serde(rename_all = "camelCase")] #[serde(rename_all = "camelCase")]
pub struct TwoColumnSearchResultsRenderer { pub struct TwoColumnSearchResultsRenderer {
pub primary_contents: PrimaryContents, pub primary_contents: YouTubeListRendererWrap,
}
#[derive(Debug, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct PrimaryContents {
pub section_list_renderer: ContentsRenderer<SectionListItem>,
}
#[serde_as]
#[derive(Debug, Deserialize)]
#[serde(rename_all = "camelCase")]
pub enum SectionListItem {
#[serde(rename_all = "camelCase")]
ItemSectionRenderer {
#[serde_as(as = "VecLogError<_>")]
contents: MapResult<Vec<YouTubeListItem>>,
},
/// Continuation token to fetch more search results
#[serde(rename_all = "camelCase")]
ContinuationItemRenderer {
continuation_endpoint: ContinuationEndpoint,
},
}
#[serde_as]
#[derive(Debug, Deserialize)]
#[serde(rename_all = "camelCase")]
pub enum SearchItem {
/// Video in search results
VideoRenderer(VideoRenderer),
/// Playlist in search results
PlaylistRenderer(PlaylistRenderer),
/// Channel displayed in search results
ChannelRenderer(ChannelRenderer),
/// Corrected search query
#[serde(rename_all = "camelCase")]
ShowingResultsForRenderer {
#[serde_as(as = "Text")]
corrected_query: String,
},
/// No search result item (e.g. ad) or unimplemented item
///
/// Unimplemented:
/// - shelfRenderer (e.g. Latest from channel, For you)
#[serde(other, deserialize_with = "ignore_any")]
None,
} }

View file

@ -1,46 +1,33 @@
use serde::Deserialize; use serde::Deserialize;
use serde_with::{serde_as, VecSkipError}; use serde_with::{serde_as, VecSkipError};
use crate::serializer::{ignore_any, MapResult, VecLogError}; use super::{video_item::YouTubeListRendererWrap, ContentRenderer};
use super::{ContentRenderer, ContentsRenderer, VideoListItem, VideoRenderer};
#[derive(Debug, Deserialize)] #[derive(Debug, Deserialize)]
#[serde(rename_all = "camelCase")] #[serde(rename_all = "camelCase")]
pub struct Startpage { pub struct Startpage {
pub contents: Contents<BrowseResultsStartpage>, pub contents: Contents,
pub response_context: ResponseContext, pub response_context: ResponseContext,
} }
#[serde_as]
#[derive(Debug, Deserialize)] #[derive(Debug, Deserialize)]
#[serde(rename_all = "camelCase")] #[serde(rename_all = "camelCase")]
pub struct StartpageCont { pub struct Trending {
#[serde(default)] pub contents: Contents,
#[serde_as(as = "VecSkipError<_>")]
pub on_response_received_actions: Vec<OnResponseReceivedAction>,
} }
#[derive(Debug, Deserialize)] #[derive(Debug, Deserialize)]
#[serde(rename_all = "camelCase")] #[serde(rename_all = "camelCase")]
pub struct Contents<T> { pub struct Contents {
pub two_column_browse_results_renderer: T, pub two_column_browse_results_renderer: BrowseResults,
} }
#[serde_as] #[serde_as]
#[derive(Debug, Deserialize)] #[derive(Debug, Deserialize)]
#[serde(rename_all = "camelCase")] #[serde(rename_all = "camelCase")]
pub struct BrowseResultsStartpage { pub struct BrowseResults {
#[serde_as(as = "VecSkipError<_>")] #[serde_as(as = "VecSkipError<_>")]
pub tabs: Vec<Tab<StartpageTabContent>>, pub tabs: Vec<Tab<YouTubeListRendererWrap>>,
}
#[serde_as]
#[derive(Debug, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct BrowseResultsTrends {
#[serde_as(as = "VecSkipError<_>")]
pub tabs: Vec<Tab<TrendingTabContent>>,
} }
#[derive(Debug, Deserialize)] #[derive(Debug, Deserialize)]
@ -49,85 +36,8 @@ pub struct Tab<T> {
pub tab_renderer: ContentRenderer<T>, pub tab_renderer: ContentRenderer<T>,
} }
#[derive(Debug, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct StartpageTabContent {
pub rich_grid_renderer: RichGridRenderer,
}
#[serde_as]
#[derive(Debug, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct RichGridRenderer {
#[serde_as(as = "VecLogError<_>")]
pub contents: MapResult<Vec<VideoListItem>>,
}
#[derive(Debug, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct Trending {
pub contents: Contents<BrowseResultsTrends>,
}
#[derive(Debug, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct TrendingTabContent {
pub section_list_renderer: ContentsRenderer<ItemSectionRenderer>,
}
#[derive(Debug, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct ItemSectionRenderer {
pub item_section_renderer: ContentsRenderer<ShelfRenderer>,
}
#[derive(Debug, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct ShelfRenderer {
pub shelf_renderer: ContentRenderer<ShelfContents>,
}
#[derive(Debug, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct ShelfContents {
pub expanded_shelf_contents_renderer: Option<ShelfContentsRenderer>,
}
#[serde_as]
#[derive(Debug, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct ShelfContentsRenderer {
#[serde_as(as = "VecLogError<_>")]
pub items: MapResult<Vec<TrendingListItem>>,
}
#[derive(Debug, Deserialize)] #[derive(Debug, Deserialize)]
#[serde(rename_all = "camelCase")] #[serde(rename_all = "camelCase")]
pub struct ResponseContext { pub struct ResponseContext {
pub visitor_data: Option<String>, pub visitor_data: Option<String>,
} }
#[serde_as]
#[derive(Debug, Deserialize)]
#[serde(rename_all = "camelCase")]
#[allow(clippy::large_enum_variant)]
pub enum TrendingListItem {
VideoRenderer(VideoRenderer),
#[serde(other, deserialize_with = "ignore_any")]
None,
}
#[derive(Debug, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct OnResponseReceivedAction {
pub append_continuation_items_action: AppendAction,
}
#[serde_as]
#[derive(Debug, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct AppendAction {
#[serde_as(as = "VecLogError<_>")]
pub continuation_items: MapResult<Vec<VideoListItem>>,
}

View file

@ -49,6 +49,7 @@ pub enum YouTubeListItem {
/// ///
/// Seems to be currently A/B tested on the channel page, /// Seems to be currently A/B tested on the channel page,
/// as of 11.10.2022 /// as of 11.10.2022
#[serde(alias = "shelfRenderer")]
RichItemRenderer { RichItemRenderer {
content: Box<YouTubeListItem>, content: Box<YouTubeListItem>,
}, },
@ -57,7 +58,9 @@ pub enum YouTubeListItem {
/// ///
/// Seems to be currently A/B tested on the video details page, /// Seems to be currently A/B tested on the video details page,
/// as of 11.10.2022 /// as of 11.10.2022
#[serde(alias = "expandedShelfContentsRenderer")]
ItemSectionRenderer { ItemSectionRenderer {
#[serde(alias = "items")]
#[serde_as(as = "VecLogError<_>")] #[serde_as(as = "VecLogError<_>")]
contents: MapResult<Vec<YouTubeListItem>>, contents: MapResult<Vec<YouTubeListItem>>,
}, },
@ -162,6 +165,21 @@ pub struct ChannelRenderer {
pub owner_badges: Vec<ChannelBadge>, pub owner_badges: Vec<ChannelBadge>,
} }
#[derive(Debug, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct YouTubeListRendererWrap {
#[serde(alias = "richGridRenderer")]
pub section_list_renderer: YouTubeListRenderer,
}
#[serde_as]
#[derive(Debug, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct YouTubeListRenderer {
#[serde_as(as = "VecLogError<_>")]
pub contents: MapResult<Vec<YouTubeListItem>>,
}
/// Result of mapping a list of different YouTube enities /// Result of mapping a list of different YouTube enities
/// (videos, channels, playlists) /// (videos, channels, playlists)
#[derive(Debug)] #[derive(Debug)]
@ -375,32 +393,3 @@ impl YouTubeListMapper<PlaylistItem> {
res.c.into_iter().for_each(|item| self.map_item(item)); res.c.into_iter().for_each(|item| self.map_item(item));
} }
} }
impl YouTubeListMapper<ChannelItem> {
fn map_item(&mut self, item: YouTubeListItem) {
match item {
YouTubeListItem::ChannelRenderer(channel) => {
self.items.push(Self::map_channel(channel));
}
YouTubeListItem::ContinuationItemRenderer {
continuation_endpoint,
} => self.ctoken = Some(continuation_endpoint.continuation_command.token),
YouTubeListItem::ShowingResultsForRenderer { corrected_query } => {
self.corrected_query = Some(corrected_query);
}
YouTubeListItem::RichItemRenderer { content } => {
self.map_item(*content);
}
YouTubeListItem::ItemSectionRenderer { mut contents } => {
self.warnings.append(&mut contents.warnings);
contents.c.into_iter().for_each(|it| self.map_item(it));
}
_ => {}
}
}
pub fn map_response(&mut self, mut res: MapResult<Vec<YouTubeListItem>>) {
self.warnings.append(&mut res.warnings);
res.c.into_iter().for_each(|item| self.map_item(item));
}
}

View file

@ -93,21 +93,19 @@ impl MapResponse<SearchResult> for response::Search {
lang: Language, lang: Language,
_deobf: Option<&Deobfuscator>, _deobf: Option<&Deobfuscator>,
) -> Result<MapResult<SearchResult>, ExtractionError> { ) -> Result<MapResult<SearchResult>, ExtractionError> {
let section_list_items = self let items = self
.contents .contents
.two_column_search_results_renderer .two_column_search_results_renderer
.primary_contents .primary_contents
.section_list_renderer .section_list_renderer
.contents; .contents;
let (items, ctoken) = map_section_list_items(section_list_items)?;
let mut mapper = response::YouTubeListMapper::<YouTubeItem>::new(lang); let mut mapper = response::YouTubeListMapper::<YouTubeItem>::new(lang);
mapper.map_response(items); mapper.map_response(items);
Ok(MapResult { Ok(MapResult {
c: SearchResult { c: SearchResult {
items: Paginator::new(self.estimated_results, mapper.items, ctoken), items: Paginator::new(self.estimated_results, mapper.items, mapper.ctoken),
corrected_query: mapper.corrected_query, corrected_query: mapper.corrected_query,
}, },
warnings: mapper.warnings, warnings: mapper.warnings,
@ -115,32 +113,6 @@ impl MapResponse<SearchResult> for response::Search {
} }
} }
fn map_section_list_items(
section_list_items: Vec<response::search::SectionListItem>,
) -> Result<(MapResult<Vec<response::YouTubeListItem>>, Option<String>), ExtractionError> {
let mut items = None;
let mut ctoken = None;
section_list_items.into_iter().for_each(|item| match item {
response::search::SectionListItem::ItemSectionRenderer { contents } => {
items = Some(contents);
}
response::search::SectionListItem::ContinuationItemRenderer {
continuation_endpoint,
} => {
ctoken = Some(continuation_endpoint.continuation_command.token);
}
});
let items = some_or_bail!(
items,
Err(ExtractionError::InvalidData(
"no item section renderer".into()
))
);
Ok((items, ctoken))
}
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use std::{fs::File, io::BufReader, path::Path}; use std::{fs::File, io::BufReader, path::Path};

View file

@ -1,11 +1,11 @@
--- ---
source: src/client/trends.rs source: src/client/pagination.rs
expression: map_res.c expression: map_res.c
--- ---
Paginator( Paginator(
count: None, count: None,
items: [ items: [
SearchVideo( Video(VideoItem(
id: "mRmlXh7Hams", id: "mRmlXh7Hams",
title: "Extra 3 vom 12.10.2022 im NDR | extra 3 | NDR", title: "Extra 3 vom 12.10.2022 im NDR | extra 3 | NDR",
length: Some(1839), length: Some(1839),
@ -16,7 +16,7 @@ Paginator(
height: 270, height: 270,
), ),
], ],
channel: ChannelTag( channel: Some(ChannelTag(
id: "UCjhkuC_Pi85wGjnB0I1ydxw", id: "UCjhkuC_Pi85wGjnB0I1ydxw",
name: "extra 3", name: "extra 3",
avatar: [ avatar: [
@ -28,15 +28,16 @@ Paginator(
], ],
verification: none, verification: none,
subscriber_count: None, subscriber_count: None,
), )),
publish_date: "[date]", publish_date: "[date]",
publish_date_txt: Some("2 days ago"), publish_date_txt: Some("2 days ago"),
view_count: 585257, view_count: Some(585257),
is_live: false, is_live: false,
is_short: false, is_short: false,
short_description: "Niedersachsen nach der Wahl: Schuld ist immer die Ampel | Die Grünen: Partei der erneuerbaren Prinzipien | Verhütung? Ist Frauensache! | Youtube: Handwerk mit goldenem Boden - Christian Ehring...", is_upcoming: false,
), short_description: Some("Niedersachsen nach der Wahl: Schuld ist immer die Ampel | Die Grünen: Partei der erneuerbaren Prinzipien | Verhütung? Ist Frauensache! | Youtube: Handwerk mit goldenem Boden - Christian Ehring..."),
SearchVideo( )),
Video(VideoItem(
id: "LsXC5r64Pvc", id: "LsXC5r64Pvc",
title: "Most Rarest Plays In Baseball History", title: "Most Rarest Plays In Baseball History",
length: Some(1975), length: Some(1975),
@ -47,7 +48,7 @@ Paginator(
height: 270, height: 270,
), ),
], ],
channel: ChannelTag( channel: Some(ChannelTag(
id: "UCRfKJZ7LHueFudiDgAJDr9Q", id: "UCRfKJZ7LHueFudiDgAJDr9Q",
name: "Top All Sports", name: "Top All Sports",
avatar: [ avatar: [
@ -59,15 +60,16 @@ Paginator(
], ],
verification: none, verification: none,
subscriber_count: None, subscriber_count: None,
), )),
publish_date: "[date]", publish_date: "[date]",
publish_date_txt: Some("3 weeks ago"), publish_date_txt: Some("3 weeks ago"),
view_count: 985521, view_count: Some(985521),
is_live: false, is_live: false,
is_short: false, is_short: false,
short_description: "#baseball #mlb #mlbb", is_upcoming: false,
), short_description: Some("#baseball #mlb #mlbb"),
SearchVideo( )),
Video(VideoItem(
id: "dwPmd1GqQHE", id: "dwPmd1GqQHE",
title: "90S RAP & HIPHOP MIX - Notorious B I G , Dr Dre, 50 Cent, Snoop Dogg, 2Pac, DMX, Lil Jon and more", title: "90S RAP & HIPHOP MIX - Notorious B I G , Dr Dre, 50 Cent, Snoop Dogg, 2Pac, DMX, Lil Jon and more",
length: Some(5457), length: Some(5457),
@ -78,7 +80,7 @@ Paginator(
height: 270, height: 270,
), ),
], ],
channel: ChannelTag( channel: Some(ChannelTag(
id: "UCKICAAGtBLJJ5zRdIxn_B4g", id: "UCKICAAGtBLJJ5zRdIxn_B4g",
name: "#Hip Hop 2022", name: "#Hip Hop 2022",
avatar: [ avatar: [
@ -90,15 +92,16 @@ Paginator(
], ],
verification: none, verification: none,
subscriber_count: None, subscriber_count: None,
), )),
publish_date: "[date]", publish_date: "[date]",
publish_date_txt: Some("5 months ago"), publish_date_txt: Some("5 months ago"),
view_count: 1654055, view_count: Some(1654055),
is_live: false, is_live: false,
is_short: false, is_short: false,
short_description: "", is_upcoming: false,
), short_description: None,
SearchVideo( )),
Video(VideoItem(
id: "qxI-Ob8lpLE", id: "qxI-Ob8lpLE",
title: "Schlatt\'s Chips Tier List", title: "Schlatt\'s Chips Tier List",
length: Some(1071), length: Some(1071),
@ -114,7 +117,7 @@ Paginator(
height: 404, height: 404,
), ),
], ],
channel: ChannelTag( channel: Some(ChannelTag(
id: "UC2mP7il3YV7TxM_3m6U0bwA", id: "UC2mP7il3YV7TxM_3m6U0bwA",
name: "jschlattLIVE", name: "jschlattLIVE",
avatar: [ avatar: [
@ -126,15 +129,16 @@ Paginator(
], ],
verification: verified, verification: verified,
subscriber_count: None, subscriber_count: None,
), )),
publish_date: "[date]", publish_date: "[date]",
publish_date_txt: Some("1 year ago"), publish_date_txt: Some("1 year ago"),
view_count: 9029628, view_count: Some(9029628),
is_live: false, is_live: false,
is_short: false, is_short: false,
short_description: "Schlatt ranks every chip ever made.\nCREATE YOUR OWN TIER LIST: https://tiermaker.com/create/chips-for-big-guy-1146620\n\nSubscribe to me on Twitch:\nhttps://twitch.tv/jschlatt\n\nFollow me on Twitter:...", is_upcoming: false,
), short_description: Some("Schlatt ranks every chip ever made.\nCREATE YOUR OWN TIER LIST: https://tiermaker.com/create/chips-for-big-guy-1146620\n\nSubscribe to me on Twitch:\nhttps://twitch.tv/jschlatt\n\nFollow me on Twitter:..."),
SearchVideo( )),
Video(VideoItem(
id: "qmrzTUmZ4UU", id: "qmrzTUmZ4UU",
title: "850€ für den Verrat am System - UCS AT-AT LEGO® Star Wars 75313", title: "850€ für den Verrat am System - UCS AT-AT LEGO® Star Wars 75313",
length: Some(2043), length: Some(2043),
@ -150,7 +154,7 @@ Paginator(
height: 404, height: 404,
), ),
], ],
channel: ChannelTag( channel: Some(ChannelTag(
id: "UC_EZd3lsmxudu3IQzpTzOgw", id: "UC_EZd3lsmxudu3IQzpTzOgw",
name: "Held der Steine Inh. Thomas Panke", name: "Held der Steine Inh. Thomas Panke",
avatar: [ avatar: [
@ -162,15 +166,16 @@ Paginator(
], ],
verification: verified, verification: verified,
subscriber_count: None, subscriber_count: None,
), )),
publish_date: "[date]", publish_date: "[date]",
publish_date_txt: Some("6 days ago"), publish_date_txt: Some("6 days ago"),
view_count: 600516, view_count: Some(600516),
is_live: false, is_live: false,
is_short: false, is_short: false,
short_description: "Star Wars - erschienen 2021 - 6749 Teile\n\nDieses Set bei Amazon*:\nhttps://amzn.to/3yu9dHX\n\nErwähnt im Video*:\nTassen https://bit.ly/HdSBausteinecke\nBig Boy https://bit.ly/BBLokBigBoy\nBurg...", is_upcoming: false,
), short_description: Some("Star Wars - erschienen 2021 - 6749 Teile\n\nDieses Set bei Amazon*:\nhttps://amzn.to/3yu9dHX\n\nErwähnt im Video*:\nTassen https://bit.ly/HdSBausteinecke\nBig Boy https://bit.ly/BBLokBigBoy\nBurg..."),
SearchVideo( )),
Video(VideoItem(
id: "4q4vpQCIZ6w", id: "4q4vpQCIZ6w",
title: "🌉 Manhattan Jazz 💖 l Relaxing Jazz Piano Music l Background Music", title: "🌉 Manhattan Jazz 💖 l Relaxing Jazz Piano Music l Background Music",
length: Some(23229), length: Some(23229),
@ -186,7 +191,7 @@ Paginator(
height: 404, height: 404,
), ),
], ],
channel: ChannelTag( channel: Some(ChannelTag(
id: "UCBnMxlW70f0SB4ZTJx124lw", id: "UCBnMxlW70f0SB4ZTJx124lw",
name: "몽키비지엠 MONKEYBGM", name: "몽키비지엠 MONKEYBGM",
avatar: [ avatar: [
@ -198,15 +203,16 @@ Paginator(
], ],
verification: verified, verification: verified,
subscriber_count: None, subscriber_count: None,
), )),
publish_date: "[date]", publish_date: "[date]",
publish_date_txt: Some("6 months ago"), publish_date_txt: Some("6 months ago"),
view_count: 2343407, view_count: Some(2343407),
is_live: false, is_live: false,
is_short: false, is_short: false,
short_description: "- Please Subscribe!\n\n🔺Disney OST Collection part 1 \n ➡\u{fe0f} https://youtu.be/lrzKFu85nhE\n\n🔺Disney OST Collection part 2 \n ➡\u{fe0f} https://youtu.be/EtE09lowIbk\n\n🔺Studio Ghibli...", is_upcoming: false,
), short_description: Some("- Please Subscribe!\n\n🔺Disney OST Collection part 1 \n ➡\u{fe0f} https://youtu.be/lrzKFu85nhE\n\n🔺Disney OST Collection part 2 \n ➡\u{fe0f} https://youtu.be/EtE09lowIbk\n\n🔺Studio Ghibli..."),
SearchVideo( )),
Video(VideoItem(
id: "Z_k31kqZxaE", id: "Z_k31kqZxaE",
title: "1 in 1,000,000 NBA Moments", title: "1 in 1,000,000 NBA Moments",
length: Some(567), length: Some(567),
@ -222,7 +228,7 @@ Paginator(
height: 404, height: 404,
), ),
], ],
channel: ChannelTag( channel: Some(ChannelTag(
id: "UCpyoYVlp67N16Lg1_N4VnVw", id: "UCpyoYVlp67N16Lg1_N4VnVw",
name: "dime", name: "dime",
avatar: [ avatar: [
@ -234,15 +240,16 @@ Paginator(
], ],
verification: verified, verification: verified,
subscriber_count: None, subscriber_count: None,
), )),
publish_date: "[date]", publish_date: "[date]",
publish_date_txt: Some("1 month ago"), publish_date_txt: Some("1 month ago"),
view_count: 4334298, view_count: Some(4334298),
is_live: false, is_live: false,
is_short: false, is_short: false,
short_description: "• Instagram - https://instagram.com/dime_nba\n• TikTok - https://tiktok.com/@dime_nba\n\ndime is a Swedish brand, founded in 2022. We produce some of the most entertaining NBA content on YouTube...", is_upcoming: false,
), short_description: Some("• Instagram - https://instagram.com/dime_nba\n• TikTok - https://tiktok.com/@dime_nba\n\ndime is a Swedish brand, founded in 2022. We produce some of the most entertaining NBA content on YouTube..."),
SearchVideo( )),
Video(VideoItem(
id: "zE-a5eqvlv8", id: "zE-a5eqvlv8",
title: "Dua Lipa, Coldplay, Martin Garrix & Kygo, The Chainsmokers Style - Feeling Me", title: "Dua Lipa, Coldplay, Martin Garrix & Kygo, The Chainsmokers Style - Feeling Me",
length: None, length: None,
@ -258,7 +265,7 @@ Paginator(
height: 404, height: 404,
), ),
], ],
channel: ChannelTag( channel: Some(ChannelTag(
id: "UCX-USfenzQlhrEJR1zD5IYw", id: "UCX-USfenzQlhrEJR1zD5IYw",
name: "Deep Mood.", name: "Deep Mood.",
avatar: [ avatar: [
@ -270,15 +277,16 @@ Paginator(
], ],
verification: none, verification: none,
subscriber_count: None, subscriber_count: None,
), )),
publish_date: "[date]", publish_date: "[date]",
publish_date_txt: None, publish_date_txt: None,
view_count: 889, view_count: Some(889),
is_live: false, is_live: true,
is_short: false, is_short: false,
short_description: "#Summermix #DeepHouse #DeepHouseSummerMix\nDua Lipa, Coldplay, Martin Garrix & Kygo, The Chainsmokers Style - Feeling Me\n\n🎵 All songs in this spotify playlist: https://spoti.fi/2TJ4Dyj\nSubmit...", is_upcoming: false,
), short_description: Some("#Summermix #DeepHouse #DeepHouseSummerMix\nDua Lipa, Coldplay, Martin Garrix & Kygo, The Chainsmokers Style - Feeling Me\n\n🎵 All songs in this spotify playlist: https://spoti.fi/2TJ4Dyj\nSubmit..."),
SearchVideo( )),
Video(VideoItem(
id: "gNlOk0LXi5M", id: "gNlOk0LXi5M",
title: "Soll ich dir 1g GOLD schenken? oder JEMAND anderen DOPPELT?", title: "Soll ich dir 1g GOLD schenken? oder JEMAND anderen DOPPELT?",
length: Some(704), length: Some(704),
@ -294,7 +302,7 @@ Paginator(
height: 404, height: 404,
), ),
], ],
channel: ChannelTag( channel: Some(ChannelTag(
id: "UCqcWNPTUVATZt0Dlr2jV0Wg", id: "UCqcWNPTUVATZt0Dlr2jV0Wg",
name: "Mois", name: "Mois",
avatar: [ avatar: [
@ -306,15 +314,16 @@ Paginator(
], ],
verification: verified, verification: verified,
subscriber_count: None, subscriber_count: None,
), )),
publish_date: "[date]", publish_date: "[date]",
publish_date_txt: Some("8 days ago"), publish_date_txt: Some("8 days ago"),
view_count: 463834, view_count: Some(463834),
is_live: false, is_live: false,
is_short: false, is_short: false,
short_description: "Je mehr Menschen mich abonnieren desto mehr Menschen werde ich glücklich machen \n\n24 std ab, viel Glück \n\nhttps://I-Clip.com/?sPartner=Mois", is_upcoming: false,
), short_description: Some("Je mehr Menschen mich abonnieren desto mehr Menschen werde ich glücklich machen \n\n24 std ab, viel Glück \n\nhttps://I-Clip.com/?sPartner=Mois"),
SearchVideo( )),
Video(VideoItem(
id: "dbMvZjs8Yc8", id: "dbMvZjs8Yc8",
title: "Brad Pitt- Die Revanche eines Sexsymbols | Doku HD | ARTE", title: "Brad Pitt- Die Revanche eines Sexsymbols | Doku HD | ARTE",
length: Some(3137), length: Some(3137),
@ -330,7 +339,7 @@ Paginator(
height: 404, height: 404,
), ),
], ],
channel: ChannelTag( channel: Some(ChannelTag(
id: "UCsygZtQQSplGF6JA3XWvsdg", id: "UCsygZtQQSplGF6JA3XWvsdg",
name: "Irgendwas mit ARTE und Kultur", name: "Irgendwas mit ARTE und Kultur",
avatar: [ avatar: [
@ -342,15 +351,16 @@ Paginator(
], ],
verification: none, verification: none,
subscriber_count: None, subscriber_count: None,
), )),
publish_date: "[date]", publish_date: "[date]",
publish_date_txt: Some("5 days ago"), publish_date_txt: Some("5 days ago"),
view_count: 293878, view_count: Some(293878),
is_live: false, is_live: false,
is_short: false, is_short: false,
short_description: "Vom „People“-Magazin wurde er mehrfach zum „Sexiest Man Alive“ gekrönt. Aber sein Aussehen ist nicht alles: In 30 Jahren Karriere drehte Brad Pitt eine Vielzahl herausragender Filme....", is_upcoming: false,
), short_description: Some("Vom „People“-Magazin wurde er mehrfach zum „Sexiest Man Alive“ gekrönt. Aber sein Aussehen ist nicht alles: In 30 Jahren Karriere drehte Brad Pitt eine Vielzahl herausragender Filme...."),
SearchVideo( )),
Video(VideoItem(
id: "mFxi3lOAcFs", id: "mFxi3lOAcFs",
title: "Craziest Soviet Machines You Won\'t Believe Exist - Part 1", title: "Craziest Soviet Machines You Won\'t Believe Exist - Part 1",
length: Some(1569), length: Some(1569),
@ -366,7 +376,7 @@ Paginator(
height: 404, height: 404,
), ),
], ],
channel: ChannelTag( channel: Some(ChannelTag(
id: "UCkQO3QsgTpNTsOw6ujimT5Q", id: "UCkQO3QsgTpNTsOw6ujimT5Q",
name: "BE AMAZED", name: "BE AMAZED",
avatar: [ avatar: [
@ -378,15 +388,16 @@ Paginator(
], ],
verification: verified, verification: verified,
subscriber_count: None, subscriber_count: None,
), )),
publish_date: "[date]", publish_date: "[date]",
publish_date_txt: Some("1 year ago"), publish_date_txt: Some("1 year ago"),
view_count: 14056843, view_count: Some(14056843),
is_live: false, is_live: false,
is_short: false, is_short: false,
short_description: "Coming up are some crazy Soviet-era machines you won\'t believe exist!\nPart 2: https://youtu.be/MBZVOJrhuHY\nSuggest a topic here to be turned into a video: http://bit.ly/2kwqhuh\nSubscribe for...", is_upcoming: false,
), short_description: Some("Coming up are some crazy Soviet-era machines you won\'t believe exist!\nPart 2: https://youtu.be/MBZVOJrhuHY\nSuggest a topic here to be turned into a video: http://bit.ly/2kwqhuh\nSubscribe for..."),
SearchVideo( )),
Video(VideoItem(
id: "eu7ubm7g59E", id: "eu7ubm7g59E",
title: "People Hated Me For Using This Slab", title: "People Hated Me For Using This Slab",
length: Some(1264), length: Some(1264),
@ -402,7 +413,7 @@ Paginator(
height: 404, height: 404,
), ),
], ],
channel: ChannelTag( channel: Some(ChannelTag(
id: "UC6I0KzAD7uFTL1qzxyunkvA", id: "UC6I0KzAD7uFTL1qzxyunkvA",
name: "Blacktail Studio", name: "Blacktail Studio",
avatar: [ avatar: [
@ -414,15 +425,16 @@ Paginator(
], ],
verification: verified, verification: verified,
subscriber_count: None, subscriber_count: None,
), )),
publish_date: "[date]", publish_date: "[date]",
publish_date_txt: Some("3 months ago"), publish_date_txt: Some("3 months ago"),
view_count: 2845035, view_count: Some(2845035),
is_live: false, is_live: false,
is_short: false, is_short: false,
short_description: "Some people were furious I used this slab, and I actually understand why. \nBlacktail bow tie jig (limited first run): https://www.blacktailstudio.com/bowtie-jig\nBlacktail epoxy table workshop:...", is_upcoming: false,
), short_description: Some("Some people were furious I used this slab, and I actually understand why. \nBlacktail bow tie jig (limited first run): https://www.blacktailstudio.com/bowtie-jig\nBlacktail epoxy table workshop:..."),
SearchVideo( )),
Video(VideoItem(
id: "TRGHIN2PGIA", id: "TRGHIN2PGIA",
title: "Christian Bale Breaks Down His Most Iconic Characters | GQ", title: "Christian Bale Breaks Down His Most Iconic Characters | GQ",
length: Some(1381), length: Some(1381),
@ -438,7 +450,7 @@ Paginator(
height: 404, height: 404,
), ),
], ],
channel: ChannelTag( channel: Some(ChannelTag(
id: "UCsEukrAd64fqA7FjwkmZ_Dw", id: "UCsEukrAd64fqA7FjwkmZ_Dw",
name: "GQ", name: "GQ",
avatar: [ avatar: [
@ -450,15 +462,16 @@ Paginator(
], ],
verification: verified, verification: verified,
subscriber_count: None, subscriber_count: None,
), )),
publish_date: "[date]", publish_date: "[date]",
publish_date_txt: Some("9 days ago"), publish_date_txt: Some("9 days ago"),
view_count: 8044465, view_count: Some(8044465),
is_live: false, is_live: false,
is_short: false, is_short: false,
short_description: "Christian Bale breaks down a few of his most iconic characters from \'American Psycho,\' \'The Dark Knight\' Trilogy, \'The Fighter,\' \'The Machinist,\' \'The Big Short,\' \'Vice,\' \'Empire of the Sun,\'...", is_upcoming: false,
), short_description: Some("Christian Bale breaks down a few of his most iconic characters from \'American Psycho,\' \'The Dark Knight\' Trilogy, \'The Fighter,\' \'The Machinist,\' \'The Big Short,\' \'Vice,\' \'Empire of the Sun,\'..."),
SearchVideo( )),
Video(VideoItem(
id: "w3tENzcssDU", id: "w3tENzcssDU",
title: "NFL Trick Plays But They Get Increasingly Higher IQ", title: "NFL Trick Plays But They Get Increasingly Higher IQ",
length: Some(599), length: Some(599),
@ -474,7 +487,7 @@ Paginator(
height: 404, height: 404,
), ),
], ],
channel: ChannelTag( channel: Some(ChannelTag(
id: "UCJka5SDh36_N4pjJd69efkg", id: "UCJka5SDh36_N4pjJd69efkg",
name: "Savage Brick Sports", name: "Savage Brick Sports",
avatar: [ avatar: [
@ -486,15 +499,16 @@ Paginator(
], ],
verification: verified, verification: verified,
subscriber_count: None, subscriber_count: None,
), )),
publish_date: "[date]", publish_date: "[date]",
publish_date_txt: Some("3 months ago"), publish_date_txt: Some("3 months ago"),
view_count: 1172372, view_count: Some(1172372),
is_live: false, is_live: false,
is_short: false, is_short: false,
short_description: "NFL Trick Plays But They Get Increasingly Higher IQ\nCredit to CoshReport for starting this trend.\n\n(if any of the links don\'t work, check most recent video)\nTalkSports Discord: https://discord.gg/n...", is_upcoming: false,
), short_description: Some("NFL Trick Plays But They Get Increasingly Higher IQ\nCredit to CoshReport for starting this trend.\n\n(if any of the links don\'t work, check most recent video)\nTalkSports Discord: https://discord.gg/n..."),
SearchVideo( )),
Video(VideoItem(
id: "gUAd2XXzH7w", id: "gUAd2XXzH7w",
title: "⚓\u{fe0f}Found ABANDONED SHIP!!! Big CRUISE SHIP on a desert island☠\u{fe0f} Where did the people go?!?", title: "⚓\u{fe0f}Found ABANDONED SHIP!!! Big CRUISE SHIP on a desert island☠\u{fe0f} Where did the people go?!?",
length: Some(2949), length: Some(2949),
@ -510,7 +524,7 @@ Paginator(
height: 404, height: 404,
), ),
], ],
channel: ChannelTag( channel: Some(ChannelTag(
id: "UClUZos7yKYtrmr0-azaD8pw", id: "UClUZos7yKYtrmr0-azaD8pw",
name: "Kreosan English", name: "Kreosan English",
avatar: [ avatar: [
@ -522,15 +536,16 @@ Paginator(
], ],
verification: verified, verification: verified,
subscriber_count: None, subscriber_count: None,
), )),
publish_date: "[date]", publish_date: "[date]",
publish_date_txt: Some("1 month ago"), publish_date_txt: Some("1 month ago"),
view_count: 1883533, view_count: Some(1883533),
is_live: false, is_live: false,
is_short: false, is_short: false,
short_description: "We are preparing a continuation of the cruise ship for you! Very soon you will be able to see the next part. If you would like to help us make a video:\n\n► Support us - https://www.patreon.com/k...", is_upcoming: false,
), short_description: Some("We are preparing a continuation of the cruise ship for you! Very soon you will be able to see the next part. If you would like to help us make a video:\n\n► Support us - https://www.patreon.com/k..."),
SearchVideo( )),
Video(VideoItem(
id: "YpGjaJ1ettI", id: "YpGjaJ1ettI",
title: "[Working BGM] Comfortable music that makes you feel positive -- Morning Mood -- Daily Routine", title: "[Working BGM] Comfortable music that makes you feel positive -- Morning Mood -- Daily Routine",
length: Some(3651), length: Some(3651),
@ -546,7 +561,7 @@ Paginator(
height: 404, height: 404,
), ),
], ],
channel: ChannelTag( channel: Some(ChannelTag(
id: "UCpxY9-3iB5Hyho31uBgzh7w", id: "UCpxY9-3iB5Hyho31uBgzh7w",
name: "Daily Routine", name: "Daily Routine",
avatar: [ avatar: [
@ -558,15 +573,16 @@ Paginator(
], ],
verification: none, verification: none,
subscriber_count: None, subscriber_count: None,
), )),
publish_date: "[date]", publish_date: "[date]",
publish_date_txt: Some("2 months ago"), publish_date_txt: Some("2 months ago"),
view_count: 1465389, view_count: Some(1465389),
is_live: false, is_live: false,
is_short: false, is_short: false,
short_description: "Hello everyone. It\'s me again. I will stay at home and study . It\'s full of fun energy today, so it\'s ready to spread to everyone with hilarious music. 🔥🔥🔥\nHave fun together 😊😊😊...", is_upcoming: false,
), short_description: Some("Hello everyone. It\'s me again. I will stay at home and study . It\'s full of fun energy today, so it\'s ready to spread to everyone with hilarious music. 🔥🔥🔥\nHave fun together 😊😊😊..."),
SearchVideo( )),
Video(VideoItem(
id: "rPAhFD8hKxQ", id: "rPAhFD8hKxQ",
title: "Survival Camping 9ft/3m Under Snow - Giant Winter Bushcraft Shelter and Quinzee", title: "Survival Camping 9ft/3m Under Snow - Giant Winter Bushcraft Shelter and Quinzee",
length: Some(1301), length: Some(1301),
@ -582,7 +598,7 @@ Paginator(
height: 404, height: 404,
), ),
], ],
channel: ChannelTag( channel: Some(ChannelTag(
id: "UCfpCQ89W9wjkHc8J_6eTbBg", id: "UCfpCQ89W9wjkHc8J_6eTbBg",
name: "Outdoor Boys", name: "Outdoor Boys",
avatar: [ avatar: [
@ -594,15 +610,16 @@ Paginator(
], ],
verification: verified, verification: verified,
subscriber_count: None, subscriber_count: None,
), )),
publish_date: "[date]", publish_date: "[date]",
publish_date_txt: Some("6 months ago"), publish_date_txt: Some("6 months ago"),
view_count: 20488431, view_count: Some(20488431),
is_live: false, is_live: false,
is_short: false, is_short: false,
short_description: "Solo winter camping and bushcraft 9 feet (3 meters) under the snow. I hiked high up into the mountains during a snow storm with 30 mph/48 kmh winds to build a deep snow bushcraft survival shelter...", is_upcoming: false,
), short_description: Some("Solo winter camping and bushcraft 9 feet (3 meters) under the snow. I hiked high up into the mountains during a snow storm with 30 mph/48 kmh winds to build a deep snow bushcraft survival shelter..."),
SearchVideo( )),
Video(VideoItem(
id: "2rye4u-cCNk", id: "2rye4u-cCNk",
title: "Pink Panther Fights Off Pests | 54 Minute Compilation | The Pink Panther Show", title: "Pink Panther Fights Off Pests | 54 Minute Compilation | The Pink Panther Show",
length: Some(3158), length: Some(3158),
@ -618,7 +635,7 @@ Paginator(
height: 404, height: 404,
), ),
], ],
channel: ChannelTag( channel: Some(ChannelTag(
id: "UCFeUyPY6W8qX8w2o6oSiRmw", id: "UCFeUyPY6W8qX8w2o6oSiRmw",
name: "Official Pink Panther", name: "Official Pink Panther",
avatar: [ avatar: [
@ -630,15 +647,16 @@ Paginator(
], ],
verification: verified, verification: verified,
subscriber_count: None, subscriber_count: None,
), )),
publish_date: "[date]", publish_date: "[date]",
publish_date_txt: Some("11 months ago"), publish_date_txt: Some("11 months ago"),
view_count: 27357653, view_count: Some(27357653),
is_live: false, is_live: false,
is_short: false, is_short: false,
short_description: "(1) Pink Pest Control\n(2) Pink-a-Boo\n(3) Little Beaux Pink\n(4) The Pink Package Plot\n(5) Come On In! The Water\'s Pink\n(6) Psychedelic Pink\n(7) Pink Posies\n(8) G.I. Pink\n\nThe Pink Panther is...", is_upcoming: false,
), short_description: Some("(1) Pink Pest Control\n(2) Pink-a-Boo\n(3) Little Beaux Pink\n(4) The Pink Package Plot\n(5) Come On In! The Water\'s Pink\n(6) Psychedelic Pink\n(7) Pink Posies\n(8) G.I. Pink\n\nThe Pink Panther is..."),
SearchVideo( )),
Video(VideoItem(
id: "O0xAlfSaBNQ", id: "O0xAlfSaBNQ",
title: "FC Nantes vs. SC Freiburg Highlights & Tore | UEFA Europa League", title: "FC Nantes vs. SC Freiburg Highlights & Tore | UEFA Europa League",
length: Some(326), length: Some(326),
@ -654,7 +672,7 @@ Paginator(
height: 404, height: 404,
), ),
], ],
channel: ChannelTag( channel: Some(ChannelTag(
id: "UC8WYi3XQXsf-6FNvqoEvxag", id: "UC8WYi3XQXsf-6FNvqoEvxag",
name: "RTL Sport", name: "RTL Sport",
avatar: [ avatar: [
@ -666,15 +684,16 @@ Paginator(
], ],
verification: verified, verification: verified,
subscriber_count: None, subscriber_count: None,
), )),
publish_date: "[date]", publish_date: "[date]",
publish_date_txt: Some("11 hours ago"), publish_date_txt: Some("11 hours ago"),
view_count: 117395, view_count: Some(117395),
is_live: false, is_live: false,
is_short: false, is_short: false,
short_description: "UEFA Europa League: https://www.rtlplus.com/shows/uefa-europa-league-19818?utm_source=youtube&utm_medium=editorial&utm_campaign=beschreibung&utm_term=rtlsport \nFC Nantes vs. SC Freiburg ...", is_upcoming: false,
), short_description: Some("UEFA Europa League: https://www.rtlplus.com/shows/uefa-europa-league-19818?utm_source=youtube&utm_medium=editorial&utm_campaign=beschreibung&utm_term=rtlsport \nFC Nantes vs. SC Freiburg ..."),
SearchVideo( )),
Video(VideoItem(
id: "Mhs9Sbnw19o", id: "Mhs9Sbnw19o",
title: "Dramatisches Duell: 400 Jahre altes Kästchen erzielt zig-fachen Wunschpreis! | Bares für Rares XXL", title: "Dramatisches Duell: 400 Jahre altes Kästchen erzielt zig-fachen Wunschpreis! | Bares für Rares XXL",
length: Some(744), length: Some(744),
@ -690,7 +709,7 @@ Paginator(
height: 404, height: 404,
), ),
], ],
channel: ChannelTag( channel: Some(ChannelTag(
id: "UC53bIpnef1pwAx69ERmmOLA", id: "UC53bIpnef1pwAx69ERmmOLA",
name: "Bares für Rares", name: "Bares für Rares",
avatar: [ avatar: [
@ -702,15 +721,16 @@ Paginator(
], ],
verification: verified, verification: verified,
subscriber_count: None, subscriber_count: None,
), )),
publish_date: "[date]", publish_date: "[date]",
publish_date_txt: Some("11 days ago"), publish_date_txt: Some("11 days ago"),
view_count: 836333, view_count: Some(836333),
is_live: false, is_live: false,
is_short: false, is_short: false,
short_description: "Du hast Schätze im Keller, die du unseren Expert*innen präsentieren möchtest? Hier geht\'s zum Bewerbungsformular: kurz.zdf.de/lSJ/\n\nEin einmaliges Bieterduell treibt den Preis für dieses...", is_upcoming: false,
), short_description: Some("Du hast Schätze im Keller, die du unseren Expert*innen präsentieren möchtest? Hier geht\'s zum Bewerbungsformular: kurz.zdf.de/lSJ/\n\nEin einmaliges Bieterduell treibt den Preis für dieses..."),
SearchVideo( )),
Video(VideoItem(
id: "Bzzp5Cay7DI", id: "Bzzp5Cay7DI",
title: "Sweet Jazz - Cool autumn Bossa Nova & October Jazz Positive Mood", title: "Sweet Jazz - Cool autumn Bossa Nova & October Jazz Positive Mood",
length: None, length: None,
@ -726,7 +746,7 @@ Paginator(
height: 404, height: 404,
), ),
], ],
channel: ChannelTag( channel: Some(ChannelTag(
id: "UCoGlllJE7aYe_VzIGP3s_wA", id: "UCoGlllJE7aYe_VzIGP3s_wA",
name: "Smooth Jazz Music", name: "Smooth Jazz Music",
avatar: [ avatar: [
@ -738,15 +758,16 @@ Paginator(
], ],
verification: none, verification: none,
subscriber_count: None, subscriber_count: None,
), )),
publish_date: "[date]", publish_date: "[date]",
publish_date_txt: None, publish_date_txt: None,
view_count: 1216, view_count: Some(1216),
is_live: false, is_live: true,
is_short: false, is_short: false,
short_description: "Sweet Jazz - Cool autumn Bossa Nova & October Jazz Positive Mood\nhttps://youtu.be/Bzzp5Cay7DI\n********************************************\nSounds available on: Jazz Bossa Nova\nOFFICIAL VIDEO:...", is_upcoming: false,
), short_description: Some("Sweet Jazz - Cool autumn Bossa Nova & October Jazz Positive Mood\nhttps://youtu.be/Bzzp5Cay7DI\n********************************************\nSounds available on: Jazz Bossa Nova\nOFFICIAL VIDEO:..."),
SearchVideo( )),
Video(VideoItem(
id: "SlskTqc9CEc", id: "SlskTqc9CEc",
title: "The Chick-Fil-A Full Menu Challenge", title: "The Chick-Fil-A Full Menu Challenge",
length: Some(613), length: Some(613),
@ -762,7 +783,7 @@ Paginator(
height: 404, height: 404,
), ),
], ],
channel: ChannelTag( channel: Some(ChannelTag(
id: "UCd1fLoVFooPeWqCEYVUJZqg", id: "UCd1fLoVFooPeWqCEYVUJZqg",
name: "Matt Stonie", name: "Matt Stonie",
avatar: [ avatar: [
@ -774,15 +795,16 @@ Paginator(
], ],
verification: verified, verification: verified,
subscriber_count: None, subscriber_count: None,
), )),
publish_date: "[date]", publish_date: "[date]",
publish_date_txt: Some("3 years ago"), publish_date_txt: Some("3 years ago"),
view_count: 39286403, view_count: Some(39286403),
is_live: false, is_live: false,
is_short: false, is_short: false,
short_description: "Good Video? Like/Fav & Share!!\n\nTBH this is really my 1st time trying Chick-Fil-A, legitimately. My verdict is torn, but that sauce is BOMB!\n\nChallenge\n+ Chick-Fil-A Deluxe\n+ Spicy Deluxe\n+...", is_upcoming: false,
), short_description: Some("Good Video? Like/Fav & Share!!\n\nTBH this is really my 1st time trying Chick-Fil-A, legitimately. My verdict is torn, but that sauce is BOMB!\n\nChallenge\n+ Chick-Fil-A Deluxe\n+ Spicy Deluxe\n+..."),
SearchVideo( )),
Video(VideoItem(
id: "CwRvM2TfYbs", id: "CwRvM2TfYbs",
title: "Gentle healing music of health and to calm the nervous system, deep relaxation! Say Life Yes", title: "Gentle healing music of health and to calm the nervous system, deep relaxation! Say Life Yes",
length: None, length: None,
@ -798,7 +820,7 @@ Paginator(
height: 404, height: 404,
), ),
], ],
channel: ChannelTag( channel: Some(ChannelTag(
id: "UC6jH5GNi0iOR17opA1Vowhw", id: "UC6jH5GNi0iOR17opA1Vowhw",
name: "Lucid Dream", name: "Lucid Dream",
avatar: [ avatar: [
@ -810,15 +832,16 @@ Paginator(
], ],
verification: verified, verification: verified,
subscriber_count: None, subscriber_count: None,
), )),
publish_date: "[date]", publish_date: "[date]",
publish_date_txt: None, publish_date_txt: None,
view_count: 1416, view_count: Some(1416),
is_live: false, is_live: true,
is_short: false, is_short: false,
short_description: "🌿 Music for relaxation, meditation, study, reading, massage, spa or sleep. This music is ideal for dealing with anxiety, stress or insomnia as it promotes relaxation and helps eliminate...", is_upcoming: false,
), short_description: Some("🌿 Music for relaxation, meditation, study, reading, massage, spa or sleep. This music is ideal for dealing with anxiety, stress or insomnia as it promotes relaxation and helps eliminate..."),
SearchVideo( )),
Video(VideoItem(
id: "7jz0pXSe_kI", id: "7jz0pXSe_kI",
title: "Craziest \"Fine...I\'ll Do it Myself\" Moments in Sports History (PART 2)", title: "Craziest \"Fine...I\'ll Do it Myself\" Moments in Sports History (PART 2)",
length: Some(1822), length: Some(1822),
@ -834,7 +857,7 @@ Paginator(
height: 404, height: 404,
), ),
], ],
channel: ChannelTag( channel: Some(ChannelTag(
id: "UCd5hdemikI6GxwGKhJCwzww", id: "UCd5hdemikI6GxwGKhJCwzww",
name: "Highlight Reel", name: "Highlight Reel",
avatar: [ avatar: [
@ -846,14 +869,15 @@ Paginator(
], ],
verification: verified, verification: verified,
subscriber_count: None, subscriber_count: None,
), )),
publish_date: "[date]", publish_date: "[date]",
publish_date_txt: Some("10 months ago"), publish_date_txt: Some("10 months ago"),
view_count: 11601863, view_count: Some(11601863),
is_live: false, is_live: false,
is_short: false, is_short: false,
short_description: "(PART 2) of 👉🏼 Craziest \"Fine...I\'ll Do It Myself\" Moments in Sports History \n\nBIBLE VERSE OF THE DAY: Luke 12:40", is_upcoming: false,
), short_description: Some("(PART 2) of 👉🏼 Craziest \"Fine...I\'ll Do It Myself\" Moments in Sports History \n\nBIBLE VERSE OF THE DAY: Luke 12:40"),
)),
], ],
ctoken: Some("4qmFsgKxAxIPRkV3aGF0X3RvX3dhdGNoGoADQ0RCNmxnSkhUWFpRYzJOVU1UUm1iME5OWjNOSmQzWjZOM0JPWlZWMldqZDFRVlp3ZEVOdGMwdEhXR3d3V0ROQ2FGb3lWbVpqTWpWb1kwaE9iMkl6VW1aamJWWnVZVmM1ZFZsWGQxTklNVlUwVDFSU1dXUXhUbXhXTTBaeVdsaGtSRkpGYkZCWk0yaDZWbXMxTlV4VmVGbE1XRnBSVlcxallVeFJRVUZhVnpSQlFWWldWRUZCUmtWU1VVRkNRVVZhUm1ReWFHaGtSamt3WWpFNU0xbFlVbXBoUVVGQ1FVRkZRa0ZCUVVKQlFVVkJRVUZGUWtGSFNrSkRRVUZUUlROQ2FGb3lWbVpqTWpWb1kwaE9iMkl6VW1aa1J6bHlXbGMwWVVWM2Ftb3hPRkJGT1dWSU5rRm9WVlpZWlVGTFNGaHVSMEp2ZDJsRmQycERObkZmUlRsbFNEWkJhRmRIZG1RMFMwaGxaMGhDTlZnMmJrMWxPVU5SU1VsTlVRJTNEJTNEmgIaYnJvd3NlLWZlZWRGRXdoYXRfdG9fd2F0Y2g%3D"), ctoken: Some("4qmFsgKxAxIPRkV3aGF0X3RvX3dhdGNoGoADQ0RCNmxnSkhUWFpRYzJOVU1UUm1iME5OWjNOSmQzWjZOM0JPWlZWMldqZDFRVlp3ZEVOdGMwdEhXR3d3V0ROQ2FGb3lWbVpqTWpWb1kwaE9iMkl6VW1aamJWWnVZVmM1ZFZsWGQxTklNVlUwVDFSU1dXUXhUbXhXTTBaeVdsaGtSRkpGYkZCWk0yaDZWbXMxTlV4VmVGbE1XRnBSVlcxallVeFJRVUZhVnpSQlFWWldWRUZCUmtWU1VVRkNRVVZhUm1ReWFHaGtSamt3WWpFNU0xbFlVbXBoUVVGQ1FVRkZRa0ZCUVVKQlFVVkJRVUZGUWtGSFNrSkRRVUZUUlROQ2FGb3lWbVpqTWpWb1kwaE9iMkl6VW1aa1J6bHlXbGMwWVVWM2Ftb3hPRkJGT1dWSU5rRm9WVlpZWlVGTFNGaHVSMEp2ZDJsRmQycERObkZmUlRsbFNEWkJhRmRIZG1RMFMwaGxaMGhDTlZnMmJrMWxPVU5SU1VsTlVRJTNEJTNEmgIaYnJvd3NlLWZlZWRGRXdoYXRfdG9fd2F0Y2g%3D"),
endpoint: browse, endpoint: browse,

View file

@ -5,7 +5,7 @@ expression: map_res.c
Paginator( Paginator(
count: None, count: None,
items: [ items: [
SearchVideo( VideoItem(
id: "_cyJhGsXDDM", id: "_cyJhGsXDDM",
title: "Ultimate Criminal Canal Found Magnet Fishing! Police on the Hunt", title: "Ultimate Criminal Canal Found Magnet Fishing! Police on the Hunt",
length: Some(1096), length: Some(1096),
@ -21,7 +21,7 @@ Paginator(
height: 404, height: 404,
), ),
], ],
channel: ChannelTag( channel: Some(ChannelTag(
id: "UCMLXec9-wpON8tZegnDsYLw", id: "UCMLXec9-wpON8tZegnDsYLw",
name: "Bondi Treasure Hunter", name: "Bondi Treasure Hunter",
avatar: [ avatar: [
@ -33,15 +33,16 @@ Paginator(
], ],
verification: verified, verification: verified,
subscriber_count: None, subscriber_count: None,
), )),
publish_date: "[date]", publish_date: "[date]",
publish_date_txt: Some("1 day ago"), publish_date_txt: Some("1 day ago"),
view_count: 700385, view_count: Some(700385),
is_live: false, is_live: false,
is_short: false, is_short: false,
short_description: "Subscribe for more Treasure Hunting videos: https://tinyurl.com/yyl3zerk\n\nMy Magnet! (Use Discount code \'BONDI\'): https://magnetarmagnets.com/\nMy Dive System! (Use Bonus code \'BONDI\'): https://lddy...", is_upcoming: false,
short_description: Some("Subscribe for more Treasure Hunting videos: https://tinyurl.com/yyl3zerk\n\nMy Magnet! (Use Discount code \'BONDI\'): https://magnetarmagnets.com/\nMy Dive System! (Use Bonus code \'BONDI\'): https://lddy..."),
), ),
SearchVideo( VideoItem(
id: "36YnV9STBqc", id: "36YnV9STBqc",
title: "The Good Life Radio\u{a0}•\u{a0}24/7 Live Radio | Best Relax House, Chillout, Study, Running, Gym, Happy Music", title: "The Good Life Radio\u{a0}•\u{a0}24/7 Live Radio | Best Relax House, Chillout, Study, Running, Gym, Happy Music",
length: None, length: None,
@ -57,7 +58,7 @@ Paginator(
height: 404, height: 404,
), ),
], ],
channel: ChannelTag( channel: Some(ChannelTag(
id: "UChs0pSaEoNLV4mevBFGaoKA", id: "UChs0pSaEoNLV4mevBFGaoKA",
name: "The Good Life Radio x Sensual Musique", name: "The Good Life Radio x Sensual Musique",
avatar: [ avatar: [
@ -69,15 +70,16 @@ Paginator(
], ],
verification: verified, verification: verified,
subscriber_count: None, subscriber_count: None,
), )),
publish_date: "[date]", publish_date: "[date]",
publish_date_txt: None, publish_date_txt: None,
view_count: 7202, view_count: Some(7202),
is_live: false, is_live: true,
is_short: false, is_short: false,
short_description: "The Good Life is live streaming the best of Relaxing & Chill House Music, Deep House, Tropical House, EDM, Dance & Pop as well as Music for Sleep, Focus, Study, Workout, Gym, Running etc. in...", is_upcoming: false,
short_description: Some("The Good Life is live streaming the best of Relaxing & Chill House Music, Deep House, Tropical House, EDM, Dance & Pop as well as Music for Sleep, Focus, Study, Workout, Gym, Running etc. in..."),
), ),
SearchVideo( VideoItem(
id: "YYD1qgH5qC4", id: "YYD1qgH5qC4",
title: "چند شنبه با سینــا | فصل چهـارم | قسمت 5 | با حضور نازنین انصاری مدیر روزنامه کیهان لندن", title: "چند شنبه با سینــا | فصل چهـارم | قسمت 5 | با حضور نازنین انصاری مدیر روزنامه کیهان لندن",
length: Some(3261), length: Some(3261),
@ -93,7 +95,7 @@ Paginator(
height: 404, height: 404,
), ),
], ],
channel: ChannelTag( channel: Some(ChannelTag(
id: "UCzH_7hfL6Jd1H0WpNO_eryQ", id: "UCzH_7hfL6Jd1H0WpNO_eryQ",
name: "MBC PERSIA", name: "MBC PERSIA",
avatar: [ avatar: [
@ -105,15 +107,16 @@ Paginator(
], ],
verification: none, verification: none,
subscriber_count: None, subscriber_count: None,
), )),
publish_date: "[date]", publish_date: "[date]",
publish_date_txt: Some("14 hours ago"), publish_date_txt: Some("14 hours ago"),
view_count: 104344, view_count: Some(104344),
is_live: false, is_live: false,
is_short: false, is_short: false,
short_description: "#mbcpersia\n#chandshanbeh\n#چندشنبه\n\nشبكه ام بى سى پرشيا را از حساب هاى مختلف در شبكه هاى اجتماعى دنبال كنيد\n►MBCPERSIA on Facebook:...", is_upcoming: false,
short_description: Some("#mbcpersia\n#chandshanbeh\n#چندشنبه\n\nشبكه ام بى سى پرشيا را از حساب هاى مختلف در شبكه هاى اجتماعى دنبال كنيد\n►MBCPERSIA on Facebook:..."),
), ),
SearchVideo( VideoItem(
id: "BeJqgI6rw9k", id: "BeJqgI6rw9k",
title: "your city is full of fake buildings, here\'s why", title: "your city is full of fake buildings, here\'s why",
length: Some(725), length: Some(725),
@ -129,7 +132,7 @@ Paginator(
height: 404, height: 404,
), ),
], ],
channel: ChannelTag( channel: Some(ChannelTag(
id: "UCqVEHtQoXHmUCfJ-9smpTSg", id: "UCqVEHtQoXHmUCfJ-9smpTSg",
name: "Answer in Progress", name: "Answer in Progress",
avatar: [ avatar: [
@ -141,15 +144,16 @@ Paginator(
], ],
verification: verified, verification: verified,
subscriber_count: None, subscriber_count: None,
), )),
publish_date: "[date]", publish_date: "[date]",
publish_date_txt: Some("7 days ago"), publish_date_txt: Some("7 days ago"),
view_count: 1447008, view_count: Some(1447008),
is_live: false, is_live: false,
is_short: false, is_short: false,
short_description: "Save 33% on your first Native Deodorant Pack - normally $39, youll get it for $26! Click here https://bit.ly/nativeanswer1 and use my code ANSWER #AD\n\nSomewhere on your street there may...", is_upcoming: false,
short_description: Some("Save 33% on your first Native Deodorant Pack - normally $39, youll get it for $26! Click here https://bit.ly/nativeanswer1 and use my code ANSWER #AD\n\nSomewhere on your street there may..."),
), ),
SearchVideo( VideoItem(
id: "ma28eWd1oyA", id: "ma28eWd1oyA",
title: "Post Malone, Maroon 5, Adele, Taylor Swift, Ed Sheeran, Shawn Mendes, Pop Hits 2020 Part 6", title: "Post Malone, Maroon 5, Adele, Taylor Swift, Ed Sheeran, Shawn Mendes, Pop Hits 2020 Part 6",
length: Some(29989), length: Some(29989),
@ -160,7 +164,7 @@ Paginator(
height: 270, height: 270,
), ),
], ],
channel: ChannelTag( channel: Some(ChannelTag(
id: "UCldQuUMYTUGrjvcU2vaPSFQ", id: "UCldQuUMYTUGrjvcU2vaPSFQ",
name: "Music Library", name: "Music Library",
avatar: [ avatar: [
@ -172,15 +176,16 @@ Paginator(
], ],
verification: none, verification: none,
subscriber_count: None, subscriber_count: None,
), )),
publish_date: "[date]", publish_date: "[date]",
publish_date_txt: Some("Streamed 2 years ago"), publish_date_txt: Some("Streamed 2 years ago"),
view_count: 1861814, view_count: Some(1861814),
is_live: false, is_live: false,
is_short: false, is_short: false,
short_description: "Post Malone, Maroon 5, Adele, Taylor Swift, Ed Sheeran, Shawn Mendes, Charlie Puth Pop Hits 2020\nPost Malone, Maroon 5, Adele, Taylor Swift, Ed Sheeran, Shawn Mendes, Charlie Puth Pop Hits...", is_upcoming: false,
short_description: Some("Post Malone, Maroon 5, Adele, Taylor Swift, Ed Sheeran, Shawn Mendes, Charlie Puth Pop Hits 2020\nPost Malone, Maroon 5, Adele, Taylor Swift, Ed Sheeran, Shawn Mendes, Charlie Puth Pop Hits..."),
), ),
SearchVideo( VideoItem(
id: "mL2LBRM5GBI", id: "mL2LBRM5GBI",
title: "Salahs 6-Minuten-Hattrick & Firmino-Gala: Rangers - FC Liverpool 1:7 | UEFA Champions League | DAZN", title: "Salahs 6-Minuten-Hattrick & Firmino-Gala: Rangers - FC Liverpool 1:7 | UEFA Champions League | DAZN",
length: Some(355), length: Some(355),
@ -196,7 +201,7 @@ Paginator(
height: 404, height: 404,
), ),
], ],
channel: ChannelTag( channel: Some(ChannelTag(
id: "UCB-GdMjyokO9lZkKU_oIK6g", id: "UCB-GdMjyokO9lZkKU_oIK6g",
name: "DAZN UEFA Champions League", name: "DAZN UEFA Champions League",
avatar: [ avatar: [
@ -208,15 +213,16 @@ Paginator(
], ],
verification: verified, verification: verified,
subscriber_count: None, subscriber_count: None,
), )),
publish_date: "[date]", publish_date: "[date]",
publish_date_txt: Some("2 days ago"), publish_date_txt: Some("2 days ago"),
view_count: 1471667, view_count: Some(1471667),
is_live: false, is_live: false,
is_short: false, is_short: false,
short_description: "In der Liga läuft es für die Reds weiterhin nicht rund. Am vergangenen Spieltag gab es gegen Arsenal eine 2:3-Niederlage, am Sonntag trifft man auf Man City. Die Champions League soll für...", is_upcoming: false,
short_description: Some("In der Liga läuft es für die Reds weiterhin nicht rund. Am vergangenen Spieltag gab es gegen Arsenal eine 2:3-Niederlage, am Sonntag trifft man auf Man City. Die Champions League soll für..."),
), ),
SearchVideo( VideoItem(
id: "Ang18qz2IeQ", id: "Ang18qz2IeQ",
title: "Satisfying Videos of Workers Doing Their Job Perfectly", title: "Satisfying Videos of Workers Doing Their Job Perfectly",
length: Some(1186), length: Some(1186),
@ -232,7 +238,7 @@ Paginator(
height: 404, height: 404,
), ),
], ],
channel: ChannelTag( channel: Some(ChannelTag(
id: "UCYenDLnIHsoqQ6smwKXQ7Hg", id: "UCYenDLnIHsoqQ6smwKXQ7Hg",
name: "#Mind Warehouse", name: "#Mind Warehouse",
avatar: [ avatar: [
@ -244,15 +250,16 @@ Paginator(
], ],
verification: verified, verification: verified,
subscriber_count: None, subscriber_count: None,
), )),
publish_date: "[date]", publish_date: "[date]",
publish_date_txt: Some("2 days ago"), publish_date_txt: Some("2 days ago"),
view_count: 173121, view_count: Some(173121),
is_live: false, is_live: false,
is_short: false, is_short: false,
short_description: "TechZone ► https://goo.gl/Gj3wZs \n\n #incrediblemoments #mindwarehouse #IncredibleMoments #CaughtOnCamera #InterestingFacts \n\nYou can endlessly watch how others work, but in this selection,...", is_upcoming: false,
short_description: Some("TechZone ► https://goo.gl/Gj3wZs \n\n #incrediblemoments #mindwarehouse #IncredibleMoments #CaughtOnCamera #InterestingFacts \n\nYou can endlessly watch how others work, but in this selection,..."),
), ),
SearchVideo( VideoItem(
id: "fjHN4jsJnEU", id: "fjHN4jsJnEU",
title: "I Made 200 Players Simulate Survival Island in Minecraft...", title: "I Made 200 Players Simulate Survival Island in Minecraft...",
length: Some(2361), length: Some(2361),
@ -268,7 +275,7 @@ Paginator(
height: 404, height: 404,
), ),
], ],
channel: ChannelTag( channel: Some(ChannelTag(
id: "UCqt4mmAqLmH-AwXz31URJsw", id: "UCqt4mmAqLmH-AwXz31URJsw",
name: "Sword4000", name: "Sword4000",
avatar: [ avatar: [
@ -280,15 +287,16 @@ Paginator(
], ],
verification: verified, verification: verified,
subscriber_count: None, subscriber_count: None,
), )),
publish_date: "[date]", publish_date: "[date]",
publish_date_txt: Some("7 days ago"), publish_date_txt: Some("7 days ago"),
view_count: 751909, view_count: Some(751909),
is_live: false, is_live: false,
is_short: false, is_short: false,
short_description: "200 Players Simulate Survival Island Civilizations in Minecraft...\n-------------------------------------------------------------------\nI invited 200 Players to a Survival Island and let them...", is_upcoming: false,
short_description: Some("200 Players Simulate Survival Island Civilizations in Minecraft...\n-------------------------------------------------------------------\nI invited 200 Players to a Survival Island and let them..."),
), ),
SearchVideo( VideoItem(
id: "FI1XrdBJIUI", id: "FI1XrdBJIUI",
title: "Epic Construction Fails | Expensive Fails Compilation | FailArmy", title: "Epic Construction Fails | Expensive Fails Compilation | FailArmy",
length: Some(631), length: Some(631),
@ -304,7 +312,7 @@ Paginator(
height: 404, height: 404,
), ),
], ],
channel: ChannelTag( channel: Some(ChannelTag(
id: "UCPDis9pjXuqyI7RYLJ-TTSA", id: "UCPDis9pjXuqyI7RYLJ-TTSA",
name: "FailArmy", name: "FailArmy",
avatar: [ avatar: [
@ -316,15 +324,16 @@ Paginator(
], ],
verification: verified, verification: verified,
subscriber_count: None, subscriber_count: None,
), )),
publish_date: "[date]", publish_date: "[date]",
publish_date_txt: Some("2 days ago"), publish_date_txt: Some("2 days ago"),
view_count: 2226471, view_count: Some(2226471),
is_live: false, is_live: false,
is_short: false, is_short: false,
short_description: "I don\'t think so, Tim. ►►► Submit your videos for the chance to be featured 🔗 https://www.failarmy.com/pages/submit-video ▼ Follow us for more fails! https://linktr.ee/failarmy\n#fails...", is_upcoming: false,
short_description: Some("I don\'t think so, Tim. ►►► Submit your videos for the chance to be featured 🔗 https://www.failarmy.com/pages/submit-video ▼ Follow us for more fails! https://linktr.ee/failarmy\n#fails..."),
), ),
SearchVideo( VideoItem(
id: "MXdplejK8vU", id: "MXdplejK8vU",
title: "Chilly autumn Jazz ☕ Smooth September Jazz & Bossa Nova for a great relaxing weekend", title: "Chilly autumn Jazz ☕ Smooth September Jazz & Bossa Nova for a great relaxing weekend",
length: Some(86403), length: Some(86403),
@ -340,7 +349,7 @@ Paginator(
height: 404, height: 404,
), ),
], ],
channel: ChannelTag( channel: Some(ChannelTag(
id: "UCeGJ6v6KQt0s88hGKMfybuw", id: "UCeGJ6v6KQt0s88hGKMfybuw",
name: "Cozy Jazz Music", name: "Cozy Jazz Music",
avatar: [ avatar: [
@ -352,15 +361,16 @@ Paginator(
], ],
verification: none, verification: none,
subscriber_count: None, subscriber_count: None,
), )),
publish_date: "[date]", publish_date: "[date]",
publish_date_txt: Some("1 month ago"), publish_date_txt: Some("1 month ago"),
view_count: 148743, view_count: Some(148743),
is_live: false, is_live: false,
is_short: false, is_short: false,
short_description: "Chilly autumn Jazz ☕ Smooth September Jazz & Bossa Nova for a great relaxing weekend\nhttps://youtu.be/MXdplejK8vU\n*******************************************\nSounds available on: Jazz Bossa...", is_upcoming: false,
short_description: Some("Chilly autumn Jazz ☕ Smooth September Jazz & Bossa Nova for a great relaxing weekend\nhttps://youtu.be/MXdplejK8vU\n*******************************************\nSounds available on: Jazz Bossa..."),
), ),
SearchVideo( VideoItem(
id: "Jri4_9vBFiQ", id: "Jri4_9vBFiQ",
title: "Top 100 Best Classic Rock Songs Of All Time 🔥 R.E.M, Queen, Metallica,Guns N Roses,Bon Jovi, U2,CCR", title: "Top 100 Best Classic Rock Songs Of All Time 🔥 R.E.M, Queen, Metallica,Guns N Roses,Bon Jovi, U2,CCR",
length: None, length: None,
@ -376,7 +386,7 @@ Paginator(
height: 404, height: 404,
), ),
], ],
channel: ChannelTag( channel: Some(ChannelTag(
id: "UCiIWdzEVNH8okhlapR9a-xA", id: "UCiIWdzEVNH8okhlapR9a-xA",
name: "Rock Music", name: "Rock Music",
avatar: [ avatar: [
@ -388,15 +398,16 @@ Paginator(
], ],
verification: none, verification: none,
subscriber_count: None, subscriber_count: None,
), )),
publish_date: "[date]", publish_date: "[date]",
publish_date_txt: None, publish_date_txt: None,
view_count: 192, view_count: Some(192),
is_live: false, is_live: true,
is_short: false, is_short: false,
short_description: "Top 100 Best Classic Rock Songs Of All Time 🔥 R.E.M, Queen, Metallica,Guns N Roses,Bon Jovi, U2,CCR\nTop 100 Best Classic Rock Songs Of All Time 🔥 R.E.M, Queen, Metallica,Guns N...", is_upcoming: false,
short_description: Some("Top 100 Best Classic Rock Songs Of All Time 🔥 R.E.M, Queen, Metallica,Guns N Roses,Bon Jovi, U2,CCR\nTop 100 Best Classic Rock Songs Of All Time 🔥 R.E.M, Queen, Metallica,Guns N..."),
), ),
SearchVideo( VideoItem(
id: "ll4d5Lt-Ie8", id: "ll4d5Lt-Ie8",
title: "Relaxing Music Healing Stress, Anxiety and Depressive States Heal Mind, Body and Soul | Sleep music", title: "Relaxing Music Healing Stress, Anxiety and Depressive States Heal Mind, Body and Soul | Sleep music",
length: Some(42896), length: Some(42896),
@ -412,7 +423,7 @@ Paginator(
height: 404, height: 404,
), ),
], ],
channel: ChannelTag( channel: Some(ChannelTag(
id: "UCNS3dqFGBPhxHmOigehpBeg", id: "UCNS3dqFGBPhxHmOigehpBeg",
name: "Love YourSelf", name: "Love YourSelf",
avatar: [ avatar: [
@ -424,15 +435,16 @@ Paginator(
], ],
verification: verified, verification: verified,
subscriber_count: None, subscriber_count: None,
), )),
publish_date: "[date]", publish_date: "[date]",
publish_date_txt: Some("Streamed 5 months ago"), publish_date_txt: Some("Streamed 5 months ago"),
view_count: 5363904, view_count: Some(5363904),
is_live: false, is_live: false,
is_short: false, is_short: false,
short_description: "The study found that listening to relaxing music of the patient\'s choice resulted in \"significant pain relief and increased mobility.\" Researchers believe that music relieves pain because listening...", is_upcoming: false,
short_description: Some("The study found that listening to relaxing music of the patient\'s choice resulted in \"significant pain relief and increased mobility.\" Researchers believe that music relieves pain because listening..."),
), ),
SearchVideo( VideoItem(
id: "Dx2wbKLokuQ", id: "Dx2wbKLokuQ",
title: "W. Putin: Die Sehnsucht nach dem Imperium | Mit offenen Karten | ARTE", title: "W. Putin: Die Sehnsucht nach dem Imperium | Mit offenen Karten | ARTE",
length: Some(729), length: Some(729),
@ -448,7 +460,7 @@ Paginator(
height: 404, height: 404,
), ),
], ],
channel: ChannelTag( channel: Some(ChannelTag(
id: "UCLLibJTCy3sXjHLVaDimnpQ", id: "UCLLibJTCy3sXjHLVaDimnpQ",
name: "ARTEde", name: "ARTEde",
avatar: [ avatar: [
@ -460,15 +472,16 @@ Paginator(
], ],
verification: verified, verification: verified,
subscriber_count: None, subscriber_count: None,
), )),
publish_date: "[date]", publish_date: "[date]",
publish_date_txt: Some("2 weeks ago"), publish_date_txt: Some("2 weeks ago"),
view_count: 539838, view_count: Some(539838),
is_live: false, is_live: false,
is_short: false, is_short: false,
short_description: "Jede Woche untersucht „Mit offenen Karten“ die politischen Kräfteverhältnisse in der ganzen Welt anhand detaillierter geografischer Karten \n\nIm Februar 2022 rechtfertigte Wladimir Putin...", is_upcoming: false,
short_description: Some("Jede Woche untersucht „Mit offenen Karten“ die politischen Kräfteverhältnisse in der ganzen Welt anhand detaillierter geografischer Karten \n\nIm Februar 2022 rechtfertigte Wladimir Putin..."),
), ),
SearchVideo( VideoItem(
id: "jfKfPfyJRdk", id: "jfKfPfyJRdk",
title: "lofi hip hop radio - beats to relax/study to", title: "lofi hip hop radio - beats to relax/study to",
length: None, length: None,
@ -484,7 +497,7 @@ Paginator(
height: 404, height: 404,
), ),
], ],
channel: ChannelTag( channel: Some(ChannelTag(
id: "UCSJ4gkVC6NrvII8umztf0Ow", id: "UCSJ4gkVC6NrvII8umztf0Ow",
name: "Lofi Girl", name: "Lofi Girl",
avatar: [ avatar: [
@ -496,15 +509,16 @@ Paginator(
], ],
verification: verified, verification: verified,
subscriber_count: None, subscriber_count: None,
), )),
publish_date: "[date]", publish_date: "[date]",
publish_date_txt: None, publish_date_txt: None,
view_count: 21262, view_count: Some(21262),
is_live: false, is_live: true,
is_short: false, is_short: false,
short_description: "🤗 Thank you for listening, I hope you will have a good time here\n\n💽 | Get the latest vinyl (limited edition)\n→ https://vinyl-lofirecords.com/\n\n🎼 | Listen on Spotify, Apple music...", is_upcoming: false,
short_description: Some("🤗 Thank you for listening, I hope you will have a good time here\n\n💽 | Get the latest vinyl (limited edition)\n→ https://vinyl-lofirecords.com/\n\n🎼 | Listen on Spotify, Apple music..."),
), ),
SearchVideo( VideoItem(
id: "qmrzTUmZ4UU", id: "qmrzTUmZ4UU",
title: "850€ für den Verrat am System - UCS AT-AT LEGO® Star Wars 75313", title: "850€ für den Verrat am System - UCS AT-AT LEGO® Star Wars 75313",
length: Some(2043), length: Some(2043),
@ -520,7 +534,7 @@ Paginator(
height: 404, height: 404,
), ),
], ],
channel: ChannelTag( channel: Some(ChannelTag(
id: "UC_EZd3lsmxudu3IQzpTzOgw", id: "UC_EZd3lsmxudu3IQzpTzOgw",
name: "Held der Steine Inh. Thomas Panke", name: "Held der Steine Inh. Thomas Panke",
avatar: [ avatar: [
@ -532,15 +546,16 @@ Paginator(
], ],
verification: verified, verification: verified,
subscriber_count: None, subscriber_count: None,
), )),
publish_date: "[date]", publish_date: "[date]",
publish_date_txt: Some("6 days ago"), publish_date_txt: Some("6 days ago"),
view_count: 600150, view_count: Some(600150),
is_live: false, is_live: false,
is_short: false, is_short: false,
short_description: "Star Wars - erschienen 2021 - 6749 Teile\n\nDieses Set bei Amazon*:\nhttps://amzn.to/3yu9dHX\n\nErwähnt im Video*:\nTassen https://bit.ly/HdSBausteinecke\nBig Boy https://bit.ly/BBLokBigBoy\nBurg...", is_upcoming: false,
short_description: Some("Star Wars - erschienen 2021 - 6749 Teile\n\nDieses Set bei Amazon*:\nhttps://amzn.to/3yu9dHX\n\nErwähnt im Video*:\nTassen https://bit.ly/HdSBausteinecke\nBig Boy https://bit.ly/BBLokBigBoy\nBurg..."),
), ),
SearchVideo( VideoItem(
id: "t0Q2otsqC4I", id: "t0Q2otsqC4I",
title: "Tom & Jerry | Tom & Jerry in Full Screen | Classic Cartoon Compilation | WB Kids", title: "Tom & Jerry | Tom & Jerry in Full Screen | Classic Cartoon Compilation | WB Kids",
length: Some(1298), length: Some(1298),
@ -556,7 +571,7 @@ Paginator(
height: 404, height: 404,
), ),
], ],
channel: ChannelTag( channel: Some(ChannelTag(
id: "UC9trsD1jCTXXtN3xIOIU8gg", id: "UC9trsD1jCTXXtN3xIOIU8gg",
name: "WB Kids", name: "WB Kids",
avatar: [ avatar: [
@ -568,15 +583,16 @@ Paginator(
], ],
verification: verified, verification: verified,
subscriber_count: None, subscriber_count: None,
), )),
publish_date: "[date]", publish_date: "[date]",
publish_date_txt: Some("10 months ago"), publish_date_txt: Some("10 months ago"),
view_count: 252381571, view_count: Some(252381571),
is_live: false, is_live: false,
is_short: false, is_short: false,
short_description: "Did you know that there are only 25 classic Tom & Jerry episodes that were displayed in a widescreen CinemaScope from the 1950s? Enjoy a compilation filled with some of the best moments from...", is_upcoming: false,
short_description: Some("Did you know that there are only 25 classic Tom & Jerry episodes that were displayed in a widescreen CinemaScope from the 1950s? Enjoy a compilation filled with some of the best moments from..."),
), ),
SearchVideo( VideoItem(
id: "zE-a5eqvlv8", id: "zE-a5eqvlv8",
title: "Dua Lipa, Coldplay, Martin Garrix & Kygo, The Chainsmokers Style - Feeling Me", title: "Dua Lipa, Coldplay, Martin Garrix & Kygo, The Chainsmokers Style - Feeling Me",
length: None, length: None,
@ -592,7 +608,7 @@ Paginator(
height: 404, height: 404,
), ),
], ],
channel: ChannelTag( channel: Some(ChannelTag(
id: "UCX-USfenzQlhrEJR1zD5IYw", id: "UCX-USfenzQlhrEJR1zD5IYw",
name: "Deep Mood.", name: "Deep Mood.",
avatar: [ avatar: [
@ -604,15 +620,16 @@ Paginator(
], ],
verification: none, verification: none,
subscriber_count: None, subscriber_count: None,
), )),
publish_date: "[date]", publish_date: "[date]",
publish_date_txt: None, publish_date_txt: None,
view_count: 955, view_count: Some(955),
is_live: false, is_live: true,
is_short: false, is_short: false,
short_description: "#Summermix #DeepHouse #DeepHouseSummerMix\nDua Lipa, Coldplay, Martin Garrix & Kygo, The Chainsmokers Style - Feeling Me\n\n🎵 All songs in this spotify playlist: https://spoti.fi/2TJ4Dyj\nSubmit...", is_upcoming: false,
short_description: Some("#Summermix #DeepHouse #DeepHouseSummerMix\nDua Lipa, Coldplay, Martin Garrix & Kygo, The Chainsmokers Style - Feeling Me\n\n🎵 All songs in this spotify playlist: https://spoti.fi/2TJ4Dyj\nSubmit..."),
), ),
SearchVideo( VideoItem(
id: "HxCcKzRAGWk", id: "HxCcKzRAGWk",
title: "(Music for Man ) Relaxing Whiskey Blues Music - Modern Electric Guitar Blues - JAZZ & BLUES", title: "(Music for Man ) Relaxing Whiskey Blues Music - Modern Electric Guitar Blues - JAZZ & BLUES",
length: Some(42899), length: Some(42899),
@ -628,7 +645,7 @@ Paginator(
height: 404, height: 404,
), ),
], ],
channel: ChannelTag( channel: Some(ChannelTag(
id: "UCGr-rTYtP1m-r_-ncspdVQQ", id: "UCGr-rTYtP1m-r_-ncspdVQQ",
name: "JAZZ & BLUES", name: "JAZZ & BLUES",
avatar: [ avatar: [
@ -640,15 +657,16 @@ Paginator(
], ],
verification: none, verification: none,
subscriber_count: None, subscriber_count: None,
), )),
publish_date: "[date]", publish_date: "[date]",
publish_date_txt: Some("Streamed 3 months ago"), publish_date_txt: Some("Streamed 3 months ago"),
view_count: 3156236, view_count: Some(3156236),
is_live: false, is_live: false,
is_short: false, is_short: false,
short_description: "-----------------------------------------------------------------------------------\n✔Thanks for watching! Have a nice day!\n✔Don\'t forget LIKE - SHARE - COMMENT\n#bluesmusic#slowblues#bluesrock...", is_upcoming: false,
short_description: Some("-----------------------------------------------------------------------------------\n✔Thanks for watching! Have a nice day!\n✔Don\'t forget LIKE - SHARE - COMMENT\n#bluesmusic#slowblues#bluesrock..."),
), ),
SearchVideo( VideoItem(
id: "HlHYOdZePSE", id: "HlHYOdZePSE",
title: "Healing Music for Anxiety Disorders, Fears, Depression and Eliminate Negative Thoughts", title: "Healing Music for Anxiety Disorders, Fears, Depression and Eliminate Negative Thoughts",
length: None, length: None,
@ -664,7 +682,7 @@ Paginator(
height: 404, height: 404,
), ),
], ],
channel: ChannelTag( channel: Some(ChannelTag(
id: "UCqNYK5QArQRZSIR8v6_FCfA", id: "UCqNYK5QArQRZSIR8v6_FCfA",
name: "Tranquil Music", name: "Tranquil Music",
avatar: [ avatar: [
@ -676,15 +694,16 @@ Paginator(
], ],
verification: none, verification: none,
subscriber_count: None, subscriber_count: None,
), )),
publish_date: "[date]", publish_date: "[date]",
publish_date_txt: None, publish_date_txt: None,
view_count: 1585, view_count: Some(1585),
is_live: false, is_live: true,
is_short: false, is_short: false,
short_description: "Healing Music for Anxiety Disorders, Fears, Depression and Eliminate Negative Thoughts\n#HealingMusic #RelaxingMusic #TranquilMusic\n__________________________________\nMusic for:\nChakra healing....", is_upcoming: false,
short_description: Some("Healing Music for Anxiety Disorders, Fears, Depression and Eliminate Negative Thoughts\n#HealingMusic #RelaxingMusic #TranquilMusic\n__________________________________\nMusic for:\nChakra healing...."),
), ),
SearchVideo( VideoItem(
id: "CJ2AH3LJeic", id: "CJ2AH3LJeic",
title: "Coldplay Greatest Hits Full Album 2022 New Songs of Coldplay 2022", title: "Coldplay Greatest Hits Full Album 2022 New Songs of Coldplay 2022",
length: Some(7781), length: Some(7781),
@ -700,7 +719,7 @@ Paginator(
height: 404, height: 404,
), ),
], ],
channel: ChannelTag( channel: Some(ChannelTag(
id: "UCdK2lzwelugXGhR9SCWuEew", id: "UCdK2lzwelugXGhR9SCWuEew",
name: "PLAY MUSIC", name: "PLAY MUSIC",
avatar: [ avatar: [
@ -712,15 +731,16 @@ Paginator(
], ],
verification: none, verification: none,
subscriber_count: None, subscriber_count: None,
), )),
publish_date: "[date]", publish_date: "[date]",
publish_date_txt: Some("7 months ago"), publish_date_txt: Some("7 months ago"),
view_count: 5595965, view_count: Some(5595965),
is_live: false, is_live: false,
is_short: false, is_short: false,
short_description: "▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬\nSubscribe channel for more videos:\n🔔Subscribe: https://bit.ly/2UbIZFv\n⚡Facebook: https://bitly.com.vn/gXDsC...", is_upcoming: false,
short_description: Some("▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬\nSubscribe channel for more videos:\n🔔Subscribe: https://bit.ly/2UbIZFv\n⚡Facebook: https://bitly.com.vn/gXDsC..."),
), ),
SearchVideo( VideoItem(
id: "KJwzKxQ81iA", id: "KJwzKxQ81iA",
title: "Handmade Candy Making Collection / 수제 사탕 만들기 모음 / Korean Candy Store", title: "Handmade Candy Making Collection / 수제 사탕 만들기 모음 / Korean Candy Store",
length: Some(3152), length: Some(3152),
@ -736,7 +756,7 @@ Paginator(
height: 404, height: 404,
), ),
], ],
channel: ChannelTag( channel: Some(ChannelTag(
id: "UCdGwDjTgbSwQDZ8dYOdrplg", id: "UCdGwDjTgbSwQDZ8dYOdrplg",
name: "Soon Films 순필름", name: "Soon Films 순필름",
avatar: [ avatar: [
@ -748,13 +768,14 @@ Paginator(
], ],
verification: none, verification: none,
subscriber_count: None, subscriber_count: None,
), )),
publish_date: "[date]", publish_date: "[date]",
publish_date_txt: Some("1 month ago"), publish_date_txt: Some("1 month ago"),
view_count: 3127238, view_count: Some(3127238),
is_live: false, is_live: false,
is_short: false, is_short: false,
short_description: "00:00 Handmade Candy Making\n13:43 Delicate Handmade Candy Making\n28:33 Rainbow Lollipop Handmade Candy Making\n39:10 Cute Handmade Candy Making", is_upcoming: false,
short_description: Some("00:00 Handmade Candy Making\n13:43 Delicate Handmade Candy Making\n28:33 Rainbow Lollipop Handmade Candy Making\n39:10 Cute Handmade Candy Making"),
), ),
], ],
ctoken: Some("4qmFsgKbAxIPRkV3aGF0X3RvX3dhdGNoGuoCQ0JoNmlBSk5aMjlKYjB0NmVtOWlTR3hxVFRSdlYyMHdTMkYzYjFwbFdGSm1ZMGRHYmxwV09YcGliVVozWXpKb2RtUkdPWGxhVjJSd1lqSTFhR0pDU1daWFZFSXhUbFpuZDFSV09YSldNRlp0WkRCT1JWTlZPV3BsU0U1WFZHNWtiRXhWY0ZSa1ZrSlRXbmh2ZEVGQlFteGlaMEZDVmxaTlFVRlZVa1pCUVVWQlVtdFdNMkZIUmpCWU0xSjJXRE5rYUdSSFRtOUJRVVZCUVZGRlFVRkJSVUZCVVVGQlFWRkZRVmxyUlVsQlFrbFVZMGRHYmxwV09YcGliVVozWXpKb2RtUkdPVEJpTW5Sc1ltaHZWRU5MVDNJeFpuSjROR1p2UTBaU1YwSm1RVzlrVkZWSlN6RnBTVlJEUzA5eU1XWnllRFJtYjBOR1VsZENaa0Z2WkZSVlNVc3hkbkZqZURjd1NrRm5aMW8lM0SaAhpicm93c2UtZmVlZEZFd2hhdF90b193YXRjaA%3D%3D"), ctoken: Some("4qmFsgKbAxIPRkV3aGF0X3RvX3dhdGNoGuoCQ0JoNmlBSk5aMjlKYjB0NmVtOWlTR3hxVFRSdlYyMHdTMkYzYjFwbFdGSm1ZMGRHYmxwV09YcGliVVozWXpKb2RtUkdPWGxhVjJSd1lqSTFhR0pDU1daWFZFSXhUbFpuZDFSV09YSldNRlp0WkRCT1JWTlZPV3BsU0U1WFZHNWtiRXhWY0ZSa1ZrSlRXbmh2ZEVGQlFteGlaMEZDVmxaTlFVRlZVa1pCUVVWQlVtdFdNMkZIUmpCWU0xSjJXRE5rYUdSSFRtOUJRVVZCUVZGRlFVRkJSVUZCVVVGQlFWRkZRVmxyUlVsQlFrbFVZMGRHYmxwV09YcGliVVozWXpKb2RtUkdPVEJpTW5Sc1ltaHZWRU5MVDNJeFpuSjROR1p2UTBaU1YwSm1RVzlrVkZWSlN6RnBTVlJEUzA5eU1XWnllRFJtYjBOR1VsZENaa0Z2WkZSVlNVc3hkbkZqZURjd1NrRm5aMW8lM0SaAhpicm93c2UtZmVlZEZFd2hhdF90b193YXRjaA%3D%3D"),

View file

@ -1,18 +1,15 @@
use crate::{ use crate::{
error::{Error, ExtractionError}, error::{Error, ExtractionError},
model::{Paginator, SearchVideo}, model::{Paginator, VideoItem},
param::Language, param::Language,
serializer::MapResult, serializer::MapResult,
util::TryRemove, util::TryRemove,
}; };
use super::{ use super::{response, ClientType, MapResponse, QBrowse, RustyPipeQuery};
response::{self, TryFromWLang},
ClientType, MapResponse, QBrowse, QContinuation, RustyPipeQuery,
};
impl RustyPipeQuery { impl RustyPipeQuery {
pub async fn startpage(self) -> Result<Paginator<SearchVideo>, Error> { pub async fn startpage(self) -> Result<Paginator<VideoItem>, Error> {
let context = self.get_context(ClientType::Desktop, true).await; let context = self.get_context(ClientType::Desktop, true).await;
let request_body = QBrowse { let request_body = QBrowse {
context, context,
@ -29,33 +26,7 @@ impl RustyPipeQuery {
.await .await
} }
pub async fn startpage_continuation( pub async fn trending(self) -> Result<Vec<VideoItem>, Error> {
self,
ctoken: &str,
visitor_data: &str,
) -> Result<Paginator<SearchVideo>, Error> {
let mut context = self.get_context(ClientType::Desktop, true).await;
context.client.visitor_data = Some(visitor_data.to_owned());
let request_body = QContinuation {
context,
continuation: ctoken,
};
self.execute_request::<response::StartpageCont, _, _>(
ClientType::Desktop,
"startpage_continuation",
ctoken,
"browse",
&request_body,
)
.await
.map(|res| Paginator {
visitor_data: Some(visitor_data.to_owned()),
..res
})
}
pub async fn trending(self) -> Result<Vec<SearchVideo>, Error> {
let context = self.get_context(ClientType::Desktop, true).await; let context = self.get_context(ClientType::Desktop, true).await;
let request_body = QBrowse { let request_body = QBrowse {
context, context,
@ -73,20 +44,20 @@ impl RustyPipeQuery {
} }
} }
impl MapResponse<Paginator<SearchVideo>> for response::Startpage { impl MapResponse<Paginator<VideoItem>> for response::Startpage {
fn map_response( fn map_response(
self, self,
_id: &str, _id: &str,
lang: crate::param::Language, lang: crate::param::Language,
_deobf: Option<&crate::deobfuscate::Deobfuscator>, _deobf: Option<&crate::deobfuscate::Deobfuscator>,
) -> Result<MapResult<Paginator<SearchVideo>>, ExtractionError> { ) -> Result<MapResult<Paginator<VideoItem>>, ExtractionError> {
let mut contents = self.contents.two_column_browse_results_renderer.tabs; let mut contents = self.contents.two_column_browse_results_renderer.tabs;
let grid = contents let grid = contents
.try_swap_remove(0) .try_swap_remove(0)
.ok_or_else(|| ExtractionError::InvalidData("no contents".into()))? .ok_or_else(|| ExtractionError::InvalidData("no contents".into()))?
.tab_renderer .tab_renderer
.content .content
.rich_grid_renderer .section_list_renderer
.contents; .contents;
Ok(map_startpage_videos( Ok(map_startpage_videos(
@ -97,33 +68,15 @@ impl MapResponse<Paginator<SearchVideo>> for response::Startpage {
} }
} }
impl MapResponse<Paginator<SearchVideo>> for response::StartpageCont { impl MapResponse<Vec<VideoItem>> for response::Trending {
fn map_response( fn map_response(
self, self,
_id: &str, _id: &str,
lang: crate::param::Language, lang: crate::param::Language,
_deobf: Option<&crate::deobfuscate::Deobfuscator>, _deobf: Option<&crate::deobfuscate::Deobfuscator>,
) -> Result<MapResult<Paginator<SearchVideo>>, ExtractionError> { ) -> Result<MapResult<Vec<VideoItem>>, ExtractionError> {
let mut received_actions = self.on_response_received_actions;
let items = received_actions
.try_swap_remove(0)
.ok_or_else(|| ExtractionError::InvalidData("no contents".into()))?
.append_continuation_items_action
.continuation_items;
Ok(map_startpage_videos(items, lang, None))
}
}
impl MapResponse<Vec<SearchVideo>> for response::Trending {
fn map_response(
self,
_id: &str,
lang: crate::param::Language,
_deobf: Option<&crate::deobfuscate::Deobfuscator>,
) -> Result<MapResult<Vec<SearchVideo>>, ExtractionError> {
let mut contents = self.contents.two_column_browse_results_renderer.tabs; let mut contents = self.contents.two_column_browse_results_renderer.tabs;
let sections = contents let items = contents
.try_swap_remove(0) .try_swap_remove(0)
.ok_or_else(|| ExtractionError::InvalidData("no contents".into()))? .ok_or_else(|| ExtractionError::InvalidData("no contents".into()))?
.tab_renderer .tab_renderer
@ -131,76 +84,27 @@ impl MapResponse<Vec<SearchVideo>> for response::Trending {
.section_list_renderer .section_list_renderer
.contents; .contents;
let mut items = Vec::new(); let mut mapper = response::YouTubeListMapper::<VideoItem>::new(lang);
let mut warnings = Vec::new(); mapper.map_response(items);
for mut section in sections { Ok(MapResult {
let shelf = section c: mapper.items,
.item_section_renderer warnings: mapper.warnings,
.contents })
.try_swap_remove(0)
.and_then(|shelf| {
shelf
.shelf_renderer
.content
.expanded_shelf_contents_renderer
});
if let Some(mut shelf) = shelf {
warnings.append(&mut shelf.items.warnings);
for item in shelf.items.c {
if let response::trends::TrendingListItem::VideoRenderer(video) = item {
match SearchVideo::from_w_lang(video, lang) {
Ok(video) => {
items.push(video);
}
Err(e) => {
warnings.push(e.to_string());
}
}
}
}
}
}
Ok(MapResult { c: items, warnings })
} }
} }
fn map_startpage_videos( fn map_startpage_videos(
videos: MapResult<Vec<response::VideoListItem>>, videos: MapResult<Vec<response::YouTubeListItem>>,
lang: Language, lang: Language,
visitor_data: Option<String>, visitor_data: Option<String>,
) -> MapResult<Paginator<SearchVideo>> { ) -> MapResult<Paginator<VideoItem>> {
let mut warnings = videos.warnings; let mut mapper = response::YouTubeListMapper::<VideoItem>::new(lang);
let mut ctoken = None; mapper.map_response(videos);
let items = videos
.c
.into_iter()
.filter_map(|item| match item {
response::VideoListItem::RichItemRenderer {
content: response::RichItem::VideoRenderer(video),
} => match SearchVideo::from_w_lang(video, lang) {
Ok(video) => Some(video),
Err(e) => {
warnings.push(e.to_string());
None
}
},
response::VideoListItem::ContinuationItemRenderer {
continuation_endpoint,
} => {
ctoken = Some(continuation_endpoint.continuation_command.token);
None
}
_ => None,
})
.collect();
MapResult { MapResult {
c: Paginator::new_with_vdata(None, items, ctoken, visitor_data), c: Paginator::new_with_vdata(None, mapper.items, mapper.ctoken, visitor_data),
warnings, warnings: mapper.warnings,
} }
} }
@ -210,7 +114,7 @@ mod tests {
use crate::{ use crate::{
client::{response, MapResponse}, client::{response, MapResponse},
model::{Paginator, SearchVideo}, model::{Paginator, VideoItem},
param::Language, param::Language,
serializer::MapResult, serializer::MapResult,
}; };
@ -223,7 +127,7 @@ mod tests {
let startpage: response::Startpage = let startpage: response::Startpage =
serde_json::from_reader(BufReader::new(json_file)).unwrap(); serde_json::from_reader(BufReader::new(json_file)).unwrap();
let map_res: MapResult<Paginator<SearchVideo>> = let map_res: MapResult<Paginator<VideoItem>> =
startpage.map_response("", Language::En, None).unwrap(); startpage.map_response("", Language::En, None).unwrap();
assert!( assert!(
@ -237,28 +141,6 @@ mod tests {
}); });
} }
#[test]
fn map_startpage_cont() {
let filename = "testfiles/trends/startpage_cont.json";
let json_path = Path::new(&filename);
let json_file = File::open(json_path).unwrap();
let startpage: response::StartpageCont =
serde_json::from_reader(BufReader::new(json_file)).unwrap();
let map_res: MapResult<Paginator<SearchVideo>> =
startpage.map_response("", Language::En, None).unwrap();
assert!(
map_res.warnings.is_empty(),
"deserialization/mapping warnings: {:?}",
map_res.warnings
);
insta::assert_ron_snapshot!("map_startpage_cont", map_res.c, {
".items[].publish_date" => "[date]",
});
}
#[test] #[test]
fn map_trending() { fn map_trending() {
let filename = "testfiles/trends/trending.json"; let filename = "testfiles/trends/trending.json";
@ -267,7 +149,7 @@ mod tests {
let startpage: response::Trending = let startpage: response::Trending =
serde_json::from_reader(BufReader::new(json_file)).unwrap(); serde_json::from_reader(BufReader::new(json_file)).unwrap();
let map_res: MapResult<Vec<SearchVideo>> = let map_res: MapResult<Vec<VideoItem>> =
startpage.map_response("", Language::En, None).unwrap(); startpage.map_response("", Language::En, None).unwrap();
assert!( assert!(