feat: add chapter extraction

This commit is contained in:
ThetaDev 2022-09-21 16:36:26 +02:00
parent 8629454b5b
commit ed522e622d
3 changed files with 102 additions and 8 deletions

View file

@ -13,8 +13,8 @@ use crate::serializer::{
};
use super::{
ChannelBadge, ContentsRenderer, ContinuationEndpoint, ContinuationItemRenderer, Icon,
Thumbnails, VideoBadge, VideoListItem, VideoOwner,
ChannelBadge, ContinuationEndpoint, ContinuationItemRenderer, Icon, Thumbnails, VideoBadge,
VideoListItem, VideoOwner,
};
/*
@ -348,7 +348,16 @@ pub enum EngagementPanelRenderer {
#[derive(Clone, Debug, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct ChapterMarkersContent {
pub macro_markers_list_renderer: ContentsRenderer<MacroMarkersListItem>,
pub macro_markers_list_renderer: MacroMarkersListRenderer,
}
/// Chapter markers
#[serde_as]
#[derive(Clone, Debug, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct MacroMarkersListRenderer {
#[serde_as(as = "VecLogError<_>")]
pub contents: MapResult<Vec<MacroMarkersListItem>>,
}
/// Chapter marker
@ -366,9 +375,6 @@ pub struct MacroMarkersListItemRenderer {
/// Contains chapter start time in seconds
pub on_tap: MacroMarkersListItemOnTap,
pub thumbnail: Thumbnails,
/// Textual time (`1:42`)
#[serde_as(as = "Text")]
pub time_description: String,
/// Chapter title
#[serde_as(as = "Text")]
pub title: String,

View file

@ -5,7 +5,9 @@ use reqwest::Method;
use serde::Serialize;
use crate::{
model::{Channel, ChannelId, Comment, Language, Paginator, RecommendedVideo, VideoDetails},
model::{
Channel, ChannelId, Chapter, Comment, Language, Paginator, RecommendedVideo, VideoDetails,
},
serializer::MapResult,
timeago,
util::{self, TryRemove},
@ -258,6 +260,26 @@ impl MapResponse<VideoDetails> for response::VideoDetails {
response::video_details::EngagementPanelRenderer::None => {},
});
let chapters = chapter_panel
.map(|chapters| {
let mut content = chapters.macro_markers_list_renderer.contents;
warnings.append(&mut content.warnings);
content
.c
.into_iter()
.map(|item| Chapter {
title: item.macro_markers_list_item_renderer.title,
position: item
.macro_markers_list_item_renderer
.on_tap
.watch_endpoint
.start_time_seconds,
thumbnail: item.macro_markers_list_item_renderer.thumbnail.into(),
})
.collect::<Vec<_>>()
})
.unwrap_or_default();
let latest_comments_ctoken = comment_panel.and_then(|comments| {
let mut items = comments
.engagement_panel_title_header_renderer
@ -288,6 +310,7 @@ impl MapResponse<VideoDetails> for response::VideoDetails {
publish_date_txt,
is_live,
is_ccommons,
chapters,
recommended,
top_comments: Paginator::new(None, Vec::new(), comment_ctoken),
latest_comments: Paginator::new(None, Vec::new(), latest_comments_ctoken),
@ -942,6 +965,57 @@ mod tests {
assert!(!details.is_live);
assert!(!details.is_ccommons);
insta::assert_yaml_snapshot!(details.chapters, {
"[].thumbnail" => insta::dynamic_redaction(move |value, _path| {
assert!(!value.as_slice().unwrap().is_empty());
"[ok]"
}),
}, @r###"
---
- title: Intro
position: 0
thumbnail: "[ok]"
- title: The PC Built for Super Efficiency
position: 42
thumbnail: "[ok]"
- title: Our BURIAL ENCLOSURE?!
position: 161
thumbnail: "[ok]"
- title: Our Power Solution (Thanks Jackery!)
position: 211
thumbnail: "[ok]"
- title: "Diggin' Holes"
position: 287
thumbnail: "[ok]"
- title: Colonoscopy?
position: 330
thumbnail: "[ok]"
- title: "Diggin' like a man"
position: 424
thumbnail: "[ok]"
- title: "The world's worst woodsman"
position: 509
thumbnail: "[ok]"
- title: Backyard cable management
position: 543
thumbnail: "[ok]"
- title: Time to bury this boy
position: 602
thumbnail: "[ok]"
- title: Solar Power Generation
position: 646
thumbnail: "[ok]"
- title: Issues
position: 697
thumbnail: "[ok]"
- title: First Play Test
position: 728
thumbnail: "[ok]"
- title: Conclusion
position: 800
thumbnail: "[ok]"
"###);
assert!(!details.recommended.items.is_empty());
assert!(!details.recommended.is_exhausted());
@ -1032,7 +1106,7 @@ mod tests {
let rp = RustyPipe::builder().strict().build();
let details = rp.query().video_details("HRKu0cvrr_o").await.unwrap();
dbg!(&details);
// dbg!(&details);
assert_eq!(details.id, "HRKu0cvrr_o");
assert_eq!(

View file

@ -245,6 +245,8 @@ pub struct VideoDetails {
///
/// https://creativecommons.org/licenses/by/3.0/
pub is_ccommons: bool,
/// Chapters of the video
pub chapters: Vec<Chapter>,
/// Recommended videos
///
/// Note: Recommendations are not available for age-restricted videos
@ -255,6 +257,18 @@ pub struct VideoDetails {
pub latest_comments: Paginator<Comment>,
}
/// Videos can consist of different chapters, which YouTube shows
/// on the seek bar and below the description text.
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
pub struct Chapter {
/// Chapter title
pub title: String,
/// Chapter position in seconds
pub position: u32,
/// Chapter thumbnail
pub thumbnail: Vec<Thumbnail>,
}
/*
@RECOMMENDATIONS
*/