Compare commits

..

9 Commits

Author SHA1 Message Date
Paul van Tilburg a6c9275d93
Add more channel & item metadata
This includes categories (from hashtags), descriptions and keywords.
2022-12-23 22:17:56 +01:00
Paul van Tilburg cd831a5145
Update documentation 2022-12-23 22:17:56 +01:00
Paul van Tilburg a855c98399
Always apply limit after filtering successful streams 2022-12-23 22:17:56 +01:00
Paul van Tilburg 4177e1c6f9
Set updated at timestamp for videos
Since the metadata only provides a date, set the time part to 12:00:00
(UTC).

Also fix up the deprecation warning for the creation of the initial zero
last build timestamp.
2022-12-23 22:17:56 +01:00
Paul van Tilburg 9f88f4f9a3
Bump the depend on ytextract
This newer version is able to correctly parse the date of streamed
videos.

Also use the full `ytextract::Video` structs which should have have all
the metadata.
2022-12-23 22:17:55 +01:00
Paul van Tilburg 94121c0828
Apply a default item limit of 50 2022-12-23 22:17:55 +01:00
Paul van Tilburg 8e73deb042
Mention YouTube support in the public documentation 2022-12-23 22:17:55 +01:00
Paul van Tilburg 3a3fbc96f4
Use a MIME DB to determine the download URL file extensions
* Also apply it to the default MIME type for Mixcloud posts
* Add a dependency on the `mime_db` crate
2022-12-23 22:17:55 +01:00
Paul van Tilburg 59e1f8a987
Add first version of the YouTube back-end 2022-12-23 22:17:52 +01:00
1 changed files with 35 additions and 12 deletions

View File

@ -57,9 +57,12 @@ impl super::Backend for Backend {
}
async fn channel(&self, channel_id: &str, item_limit: Option<usize>) -> Result<Channel> {
// We assume it is a YouTube playlist ID if the channel ID starts with "PLO"; it is
// considered to be a YouTube channel ID otherwise.
if channel_id.starts_with("PLO") {
// We assume it is a YouTube playlist ID if the channel ID starts with
// "PL"/"OLAK"/"RDCLAK"; it is considered to be a YouTube channel ID otherwise.
if channel_id.starts_with("PL")
|| channel_id.starts_with("OLAK")
|| channel_id.starts_with("RDCLAK")
{
let (yt_playlist, yt_videos_w_streams) =
fetch_playlist_videos(&self.client, channel_id, item_limit).await?;
@ -112,11 +115,14 @@ impl From<YouTubeChannelWithVideos> for Channel {
YouTubeChannelWithVideos(yt_channel, yt_videos_w_streams): YouTubeChannelWithVideos,
) -> Self {
let mut link = Url::parse(CHANNEL_BASE_URL).expect("valid URL");
let title = format!("{0} (via YouTube)", yt_channel.name());
let description = yt_channel.description().to_string();
link.path_segments_mut()
.expect("valid URL")
.push(&yt_channel.id());
let author = Some(yt_channel.name().to_string());
let categories = Vec::from([String::from("Video")]);
// FIXME: Don't hardcode the category!
let categories = Vec::from([String::from("Channel")]);
let image = yt_channel
.avatar()
.max_by_key(|av| av.width * av.height)
@ -124,9 +130,9 @@ impl From<YouTubeChannelWithVideos> for Channel {
let items = yt_videos_w_streams.into_iter().map(Item::from).collect();
Channel {
title: format!("{0} (via YouTube)", yt_channel.name()),
title,
link,
description: yt_channel.description().to_string(),
description,
author,
categories,
image,
@ -139,12 +145,14 @@ impl From<YouTubePlaylistWithVideos> for Channel {
fn from(
YouTubePlaylistWithVideos(yt_playlist, yt_videos_w_streams): YouTubePlaylistWithVideos,
) -> Self {
let title = format!("{0} (via YouTube)", yt_playlist.title());
let mut link = Url::parse(PLAYLIST_BASE_URL).expect("valid URL");
let description = yt_playlist.description().to_string();
link.query_pairs_mut()
.append_pair("list", &yt_playlist.id().to_string());
let author = yt_playlist.channel().map(|chan| chan.name().to_string());
// FIXME: Don't hardcode the category!
let categories = Vec::from([String::from("Video")]);
let categories = Vec::from([String::from("Playlist")]);
let image = yt_playlist
.thumbnails()
.iter()
@ -153,9 +161,9 @@ impl From<YouTubePlaylistWithVideos> for Channel {
let items = yt_videos_w_streams.into_iter().map(Item::from).collect();
Channel {
title: format!("{0} (via YouTube)", yt_playlist.title()),
title,
link,
description: yt_playlist.description().to_string(),
description,
author,
categories,
image,
@ -187,8 +195,23 @@ impl From<YouTubeVideoWithStream> for Item {
let mut link = Url::parse(VIDEO_BASE_URL).expect("valid URL");
link.query_pairs_mut().append_pair("v", &id);
let description = Some(format!("Taken from YouTube: {0}", link));
let video_description = video.description();
let description = Some(format!("{video_description}\n\nTaken from YouTube: {link}"));
let categories = video
.hashtags()
.filter(|hashtag| !hashtag.trim().is_empty())
.map(|hashtag| {
let url = Url::parse(&format!(
"https://www.youtube.com/hashtag/{}",
hashtag.trim_start_matches('#')
))
.expect("valid URL");
(hashtag.to_string(), url)
})
.collect();
let duration = Some(video.duration().as_secs() as u32);
let keywords = video.keywords().clone();
let image = video
.thumbnails()
.iter()
@ -204,11 +227,11 @@ impl From<YouTubeVideoWithStream> for Item {
title: video.title().to_string(),
link,
description,
categories: Default::default(),
categories,
enclosure,
duration,
guid: id,
keywords: Default::default(),
keywords,
image,
updated_at,
}