feat: extract timestamps from video links

This commit is contained in:
ThetaDev 2022-09-21 17:19:17 +02:00
parent ed522e622d
commit e4b10fcc83
3 changed files with 101 additions and 62 deletions

View file

@ -573,8 +573,9 @@ mod tests {
url: "https://smarturl.it/aespa_BlackMamba"
- Text: "\n🐍The Debut Stage "
- Video:
title: "https://youtu.be/Ky5RT5oGg0w"
text: "https://youtu.be/Ky5RT5oGg0w"
id: Ky5RT5oGg0w
start_time: 0
- Text: "\n\n🎟️ aespa Showcase SYNK in LA! Tickets now on sale: "
- Web:
text: "https://www.ticketmaster.com/event/0A..."
@ -843,8 +844,9 @@ mod tests {
url: "https://www.twitch.tv/linustech"
- Text: "\n\nMUSIC CREDIT\n---------------------------------------------------\nIntro: Laszlo - Supernova\nVideo Link: "
- Video:
title: "https://www.youtube.com/watch?v=PKfxm..."
text: "https://www.youtube.com/watch?v=PKfxm..."
id: PKfxmFU3lWY
start_time: 0
- Text: "\niTunes Download Link: "
- Web:
text: "https://itunes.apple.com/us/album/sup..."
@ -855,8 +857,9 @@ mod tests {
url: "https://soundcloud.com/laszlomusic"
- Text: "\n\nOutro: Approaching Nirvana - Sugar High\nVideo Link: "
- Video:
title: "https://www.youtube.com/watch?v=ngsGB..."
text: "https://www.youtube.com/watch?v=ngsGB..."
id: ngsGBSCDwcI
start_time: 0
- Text: "\nListen on Spotify: "
- Web:
text: "http://spoti.fi/UxWkUw"
@ -883,60 +886,74 @@ mod tests {
url: "https://geni.us/Ps3XfE"
- Text: "\n\nCHAPTERS\n---------------------------------------------------\n"
- Video:
title: "0:00"
text: "0:00"
id: nFDBxBUfE74
start_time: 0
- Text: " Intro\n"
- Video:
title: "0:42"
text: "0:42"
id: nFDBxBUfE74
start_time: 42
- Text: " The PC Built for Super Efficiency\n"
- Video:
title: "2:41"
text: "2:41"
id: nFDBxBUfE74
start_time: 161
- Text: " Our BURIAL ENCLOSURE?!\n"
- Video:
title: "3:31"
text: "3:31"
id: nFDBxBUfE74
start_time: 211
- Text: " Our Power Solution (Thanks Jackery!)\n"
- Video:
title: "4:47"
text: "4:47"
id: nFDBxBUfE74
start_time: 287
- Text: " Diggin' Holes\n"
- Video:
title: "5:30"
text: "5:30"
id: nFDBxBUfE74
start_time: 330
- Text: " Colonoscopy?\n"
- Video:
title: "7:04"
text: "7:04"
id: nFDBxBUfE74
start_time: 424
- Text: " Diggin' like a man\n"
- Video:
title: "8:29"
text: "8:29"
id: nFDBxBUfE74
start_time: 509
- Text: " The world's worst woodsman\n"
- Video:
title: "9:03"
text: "9:03"
id: nFDBxBUfE74
start_time: 543
- Text: " Backyard cable management\n"
- Video:
title: "10:02"
text: "10:02"
id: nFDBxBUfE74
start_time: 602
- Text: " Time to bury this boy\n"
- Video:
title: "10:46"
text: "10:46"
id: nFDBxBUfE74
start_time: 646
- Text: " Solar Power Generation\n"
- Video:
title: "11:37"
text: "11:37"
id: nFDBxBUfE74
start_time: 697
- Text: " Issues\n"
- Video:
title: "12:08"
text: "12:08"
id: nFDBxBUfE74
start_time: 728
- Text: " First Play Test\n"
- Video:
title: "13:20"
text: "13:20"
id: nFDBxBUfE74
start_time: 800
- Text: " Conclusion"
"###);
@ -1045,8 +1062,9 @@ mod tests {
---
- Text: "Live NASA - Views Of Earth from Space\nLive video feed of Earth from the International Space Station (ISS) Cameras\n-----------------------------------------------------------------------------------------------------\nWatch our latest video - The Sun - 4K Video / Solar Flares\n"
- Video:
title: "https://www.youtube.com/watch?v=SEzK4..."
text: "https://www.youtube.com/watch?v=SEzK4..."
id: SEzK4ZfMvUQ
start_time: 0
- Text: "\n-----------------------------------------------------------------------------------------------------\nNasa ISS live stream from aboard the International Space Station as it circles the earth at 240 miles above the planet, on the edge of space in low earth orbit. \n\nThe station is crewed by NASA astronauts as well as Russian Cosmonauts and a mixture of Japanese, Canadian and European astronauts as well.\n\n"
- Text: " "
- Text: " "

View file

@ -10,15 +10,19 @@ pub enum TextComponent {
/// Web link
Web { text: String, url: String },
/// Link to a YouTube video
Video { title: String, id: String },
Video {
text: String,
id: String,
start_time: u32,
},
/// Link to a YouTube channel
Channel { name: String, id: String },
Channel { text: String, id: String },
/// Link to a YouTube playlist
Playlist { name: String, id: String },
Playlist { text: String, id: String },
/// Link to a YouTube Music artist
Artist { name: String, id: String },
Artist { text: String, id: String },
/// Link to a YouTube Music album
Album { name: String, id: String },
Album { text: String, id: String },
}
/// Trait for converting rich text to plain text.
@ -46,12 +50,27 @@ pub trait ToHtml {
fn to_html_yt_host(&self, yt_host: &str) -> String;
}
impl ToPlaintext for TextComponent {
fn to_plaintext_yt_host(&self, yt_host: &str) -> String {
impl TextComponent {
pub fn get_text<'a>(&'a self) -> &'a str {
match self {
TextComponent::Text(text) => text.to_owned(),
TextComponent::Text(text) => text,
TextComponent::Web { text, .. } => text,
TextComponent::Video { text, .. } => text,
TextComponent::Channel { text, .. } => text,
TextComponent::Playlist { text, .. } => text,
TextComponent::Artist { text, .. } => text,
TextComponent::Album { text, .. } => text,
}
}
pub fn get_url(&self, yt_host: &str) -> String {
match self {
TextComponent::Text(_) => "".to_owned(),
TextComponent::Web { url, .. } => url.to_owned(),
TextComponent::Video { id, .. } => format!("{}/watch?v={}", yt_host, id),
TextComponent::Video { id, start_time, .. } => match start_time {
0 => format!("{}/watch?v={}", yt_host, id),
n => format!("{}/watch?v={}&t={}s", yt_host, id, n),
},
TextComponent::Channel { id, .. } | TextComponent::Artist { id, .. } => {
format!("{}/channel/{}", yt_host, id)
}
@ -62,6 +81,15 @@ impl ToPlaintext for TextComponent {
}
}
impl ToPlaintext for TextComponent {
fn to_plaintext_yt_host(&self, yt_host: &str) -> String {
match self {
TextComponent::Text(text) => text.to_owned(),
_ => self.get_url(yt_host),
}
}
}
#[cfg(feature = "html")]
impl ToHtml for TextComponent {
fn to_html_yt_host(&self, yt_host: &str) -> String {
@ -69,35 +97,18 @@ impl ToHtml for TextComponent {
TextComponent::Text(text) => askama_escape::escape(&text, askama_escape::Html)
.to_string()
.replace("\n", "<br>"),
TextComponent::Web { text, url } => {
TextComponent::Web { text, .. } => {
format!(
r#"<a href="{}" target="_blank" rel="noreferrer">{}</a>"#,
url,
askama_escape::escape(&text, askama_escape::Html)
self.get_url(yt_host),
askama_escape::escape(text, askama_escape::Html)
)
}
TextComponent::Video { title, id } => {
_ => {
format!(
r#"<a href="{}/watch?v={}" rel="noreferrer">{}</a>"#,
yt_host,
id,
askama_escape::escape(&title, askama_escape::Html)
)
}
TextComponent::Channel { name, id } | TextComponent::Artist { name, id } => {
format!(
r#"<a href="{}/channel/{}" rel="noreferrer">{}</a>"#,
yt_host,
id,
askama_escape::escape(&name, askama_escape::Html)
)
}
TextComponent::Playlist { name, id } | TextComponent::Album { name, id } => {
format!(
r#"<a href="{}/playlist?list={}" rel="noreferrer">{}</a>"#,
yt_host,
id,
askama_escape::escape(&name, askama_escape::Html)
r#"<a href="{}">{}</a>"#,
self.get_url(yt_host),
askama_escape::escape(self.get_text(), askama_escape::Html)
)
}
}
@ -133,7 +144,7 @@ mod tests {
text::TextComponent::Text { text: "🎧Listen and download aespa's debut single \"Black Mamba\": ".to_owned() },
text::TextComponent::Web { text: "https://smarturl.it/aespa_BlackMamba".to_owned(), url: "https://www.youtube.com/redirect?event=video_description&redir_token=QUFFLUhqbFY1QmpQamJPSms0Z1FnVTlQUS00ZFhBZnBJZ3xBQ3Jtc0tuRGJBanludGoyRnphb2dZWVd3cUNnS3dEd0FnNHFOZEY1NHBJaHFmLXpaWUJwX3ZucDZxVnpGeHNGX1FpMzFkZW9jQkI2Mi1wNGJ1UVFNN3h1MnN3R3JLMzdxU01nZ01POHBGcmxHU2puSUk1WHRzQQ&q=https%3A%2F%2Fsmarturl.it%2Faespa_BlackMamba&v=ZeerrnuLi5E".to_owned() },
text::TextComponent::Text { text: "\n🐍The Debut Stage ".to_owned() },
text::TextComponent::Video { title: "https://youtu.be/Ky5RT5oGg0w".to_owned(), video_id: "Ky5RT5oGg0w".to_owned() },
text::TextComponent::Video { text: "https://youtu.be/Ky5RT5oGg0w".to_owned(), video_id: "Ky5RT5oGg0w".to_owned(), start_time: 0 },
text::TextComponent::Text { text: "\n\n🎟️ aespa Showcase SYNK in LA! Tickets now on sale: ".to_owned() },
text::TextComponent::Web { text: "https://www.ticketmaster.com/event/0A...".to_owned(), url: "https://www.youtube.com/redirect?event=video_description&redir_token=QUFFLUhqbFpUMEZiaXJWWkszaVZXaEM0emxWU1JQV3NoQXxBQ3Jtc0tuU2g4VWNPNE5UY3hoSWYtamFzX0h4bUVQLVJiRy1ubDZrTnh3MUpGdDNSaUo0ZlMyT3lUM28ycUVBdHJLMndGcDhla3BkOFpxSVFfOS1QdVJPVHBUTEV1LXpOV0J2QXdhV05lV210cEJtZUJMeHdaTQ&q=https%3A%2F%2Fwww.ticketmaster.com%2Fevent%2F0A005CCD9E871F6E&v=ZeerrnuLi5E".to_owned() },
text::TextComponent::Text { text: "\n\nSubscribe to aespa Official YouTube Channel!\n".to_owned() },
@ -198,7 +209,7 @@ aespa 에스파 'Black Mamba' MV ℗ SM Entertainment"#
let html = richtext.to_html_yt_host("https://piped.kavin.rocks");
assert_eq!(
html,
"🎧Listen and download aespa&#x27;s debut single &quot;Black Mamba&quot;: <a href=\"https://smarturl.it/aespa_BlackMamba\" target=\"_blank\" rel=\"noreferrer\">https://smarturl.it/aespa_BlackMamba</a><br>🐍The Debut Stage <a href=\"https://piped.kavin.rocks/watch?v=Ky5RT5oGg0w\" rel=\"noreferrer\">https://youtu.be/Ky5RT5oGg0w</a><br><br>🎟\u{fe0f} aespa Showcase SYNK in LA! Tickets now on sale: <a href=\"https://www.ticketmaster.com/event/0A005CCD9E871F6E\" target=\"_blank\" rel=\"noreferrer\">https://www.ticketmaster.com/event/0A...</a><br><br>Subscribe to aespa Official YouTube Channel!<br><a href=\"https://www.youtube.com/aespa?sub_confirmation=1\" target=\"_blank\" rel=\"noreferrer\">https://www.youtube.com/aespa?sub_con...</a><br><br>aespa official<br><a href=\"https://www.youtube.com/c/aespa\" target=\"_blank\" rel=\"noreferrer\">https://www.youtube.com/c/aespa</a><br><a href=\"https://www.instagram.com/aespa_official\" target=\"_blank\" rel=\"noreferrer\">https://www.instagram.com/aespa_official</a><br><a href=\"https://www.tiktok.com/@aespa_official\" target=\"_blank\" rel=\"noreferrer\">https://www.tiktok.com/@aespa_official</a><br><a href=\"https://twitter.com/aespa_Official\" target=\"_blank\" rel=\"noreferrer\">https://twitter.com/aespa_Official</a><br><a href=\"https://www.facebook.com/aespa.official\" target=\"_blank\" rel=\"noreferrer\">https://www.facebook.com/aespa.official</a><br><a href=\"https://weibo.com/aespa\" target=\"_blank\" rel=\"noreferrer\">https://weibo.com/aespa</a><br><br>#aespa #æspa #BlackMamba #블랙맘바 #에스파<br>aespa 에스파 &#x27;Black Mamba&#x27; MV ℗ SM Entertainment"
"🎧Listen and download aespa&#x27;s debut single &quot;Black Mamba&quot;: <a href=\"https://smarturl.it/aespa_BlackMamba\" target=\"_blank\" rel=\"noreferrer\">https://smarturl.it/aespa_BlackMamba</a><br>🐍The Debut Stage <a href=\"https://piped.kavin.rocks/watch?v=Ky5RT5oGg0w\">https://youtu.be/Ky5RT5oGg0w</a><br><br>🎟\u{fe0f} aespa Showcase SYNK in LA! Tickets now on sale: <a href=\"https://www.ticketmaster.com/event/0A005CCD9E871F6E\" target=\"_blank\" rel=\"noreferrer\">https://www.ticketmaster.com/event/0A...</a><br><br>Subscribe to aespa Official YouTube Channel!<br><a href=\"https://www.youtube.com/aespa?sub_confirmation=1\" target=\"_blank\" rel=\"noreferrer\">https://www.youtube.com/aespa?sub_con...</a><br><br>aespa official<br><a href=\"https://www.youtube.com/c/aespa\" target=\"_blank\" rel=\"noreferrer\">https://www.youtube.com/c/aespa</a><br><a href=\"https://www.instagram.com/aespa_official\" target=\"_blank\" rel=\"noreferrer\">https://www.instagram.com/aespa_official</a><br><a href=\"https://www.tiktok.com/@aespa_official\" target=\"_blank\" rel=\"noreferrer\">https://www.tiktok.com/@aespa_official</a><br><a href=\"https://twitter.com/aespa_Official\" target=\"_blank\" rel=\"noreferrer\">https://twitter.com/aespa_Official</a><br><a href=\"https://www.facebook.com/aespa.official\" target=\"_blank\" rel=\"noreferrer\">https://www.facebook.com/aespa.official</a><br><a href=\"https://weibo.com/aespa\" target=\"_blank\" rel=\"noreferrer\">https://weibo.com/aespa</a><br><br>#aespa #æspa #BlackMamba #블랙맘바 #에스파<br>aespa 에스파 &#x27;Black Mamba&#x27; MV ℗ SM Entertainment"
);
}
}

View file

@ -86,8 +86,9 @@ pub struct TextComponents(pub Vec<TextComponent>);
#[derive(Debug, Clone)]
pub enum TextComponent {
Video {
title: String,
text: String,
video_id: String,
start_time: u32,
},
Browse {
text: String,
@ -141,6 +142,8 @@ struct NavigationEndpoint {
#[serde(rename_all = "camelCase")]
struct WatchEndpoint {
video_id: String,
#[serde(default)]
start_time_seconds: u32,
}
#[derive(Deserialize)]
@ -202,8 +205,9 @@ fn map_richtext_run(lr: &RichTextRun) -> Option<TextComponent> {
Some(match &nav.watch_endpoint {
Some(w) => TextComponent::Video {
title: text,
text,
video_id: w.video_id.to_owned(),
start_time: w.start_time_seconds,
},
None => match &nav.browse_endpoint {
Some(b) => TextComponent::Browse {
@ -284,9 +288,14 @@ impl TryFrom<TextComponent> for crate::model::ChannelId {
impl From<TextComponent> for crate::model::richtext::TextComponent {
fn from(component: TextComponent) -> Self {
match component {
TextComponent::Video { title, video_id } => Self::Video {
title,
TextComponent::Video {
text,
video_id,
start_time,
} => Self::Video {
text,
id: video_id,
start_time,
},
TextComponent::Browse {
text,
@ -294,19 +303,19 @@ impl From<TextComponent> for crate::model::richtext::TextComponent {
browse_id,
} => match page_type {
PageType::Artist => Self::Artist {
name: text,
text,
id: browse_id,
},
PageType::Album => Self::Album {
name: text,
text,
id: browse_id,
},
PageType::Channel => Self::Channel {
name: text,
text,
id: browse_id,
},
PageType::Playlist => Self::Playlist {
name: text,
text,
id: browse_id,
},
},
@ -443,8 +452,9 @@ mod tests {
insta::assert_debug_snapshot!(res, @r###"
SLink {
ln: Video {
title: "DEEP",
text: "DEEP",
video_id: "wZIoIgz5mbs",
start_time: 0,
},
}
"###);