Compare commits

..

10 Commits

Author SHA1 Message Date
Paul van Tilburg 6b1ac35321
Always apply limit after filtering successful streams 2022-12-19 21:54:59 +01:00
Paul van Tilburg 3318736bbc
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-19 21:53:32 +01:00
Paul van Tilburg c40f85a313
Switch to fixed ytextract version
This fixed version is able to parse the date of streamed videos.

Also use the full `ytextract::Video` structs which should have have all
the metadata.
2022-12-19 21:38:12 +01:00
Paul van Tilburg bf26bcf3b5
Apply a default item limit of 50 2022-12-19 21:36:42 +01:00
Paul van Tilburg f3c652e86a
Mention YouTube support in the public documentation 2022-12-19 21:36:42 +01:00
Paul van Tilburg b05390d9ad
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-19 21:36:42 +01:00
Paul van Tilburg bad0771897
Add first version of the YouTube back-end 2022-12-19 21:36:40 +01:00
Paul van Tilburg 66452cc96d
Add more lints
Not enabling the `trivial_casts` lint, because the `uri!` seems to
trigger it.
2022-10-17 20:10:06 +02:00
Paul van Tilburg 32040f3b0f
Cargo update 2022-10-17 19:51:33 +02:00
Paul van Tilburg bde6135f70
Use public URL instead of URL in configuration
Change the name of the `url` config key to `public_url` to be more clear
about what it is for.
2022-10-17 19:51:24 +02:00
9 changed files with 563 additions and 333 deletions

835
Cargo.lock generated

File diff suppressed because it is too large Load Diff

View File

@ -9,7 +9,7 @@ license = "MIT"
[dependencies]
async-trait = "0.1.57"
cached = { version = "0.38.0", features = ["async"] }
cached = { version = "0.39.0", features = ["async"] }
chrono = { version = "0.4.19", features = ["serde"] }
enum_dispatch = "0.3.8"
mime-db = "1.6.0"
@ -20,7 +20,7 @@ rss = "2.0.1"
thiserror = "1.0.31"
url = { version = "2.2.2", features = ["serde"] }
youtube_dl = { version = "0.7.0", features = ["tokio"] }
ytextract = "0.11.0"
ytextract = { version = "0.11.0", git = "https://github.com/paulvt/ytextract.git", branch = "fix-date-parsing-live" }
[package.metadata.deb]
maintainer = "Paul van Tilburg <paul@luon.net>"

View File

@ -26,7 +26,7 @@ builds when you don't add `--release`.)
### Configuration
For now, you will need to provide Rocket with configuration to tell it at which
URL Podbringer is hosted. This needs to be done even if you are not using a
public URL Podbringer is hosted. This needs to be done even if you are not using a
reverse proxy, in which case you need to provide it with the proxied URL. You
can also use the configuration to configure a different address and/or port.
Just create a `Rocket.toml` file that contains (or copy `Rocket.toml.example`):
@ -35,7 +35,7 @@ Just create a `Rocket.toml` file that contains (or copy `Rocket.toml.example`):
[default]
address = "0.0.0.0"
port = 7062
url = "https://my.domain.tld/podbringer"
public_url = "https://my.domain.tld/podbringer"
```
This will work independent of the type of build. For more about Rocket's
@ -53,7 +53,7 @@ need to use for Podbringer is comprised of the following parts:
```text
https://my.domain.tld/podbringer/feed/mixcloud/myfavouriteband
|------------------------------| |-------||--------------|
The Podbringer location URL Service User @ service
The Podbringer public URL Service User @ service
```
### Feed item limit

View File

@ -1,4 +1,4 @@
[default]
address = "0.0.0.0"
port = 7062
url = "https://my.domain.tld/podbringer"
public_url = "https://my.domain.tld/podbringer"

View File

@ -6,12 +6,13 @@ use std::path::{Path, PathBuf};
use async_trait::async_trait;
use cached::proc_macro::cached;
use chrono::Utc;
use chrono::{DateTime, Utc};
use reqwest::Url;
use rocket::futures::StreamExt;
use ytextract::playlist::video::{Error as YouTubeVideoError, Video as YouTubeVideo};
use ytextract::playlist::video::{Error as YouTubeVideoError, Video as YouTubePlaylistVideo};
use ytextract::{
Channel as YouTubeChannel, Client, Playlist as YouTubePlaylist, Stream as YouTubeStream,
Video as YouTubeVideo,
};
use super::{Channel, Enclosure, Item};
@ -187,12 +188,17 @@ 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 duration = Some(video.length().as_secs() as u32);
let duration = Some(video.duration().as_secs() as u32);
let image = video
.thumbnails()
.iter()
.max_by_key(|tn| tn.width * tn.height)
.map(|tn| tn.url.clone());
let timestamp = video
.date()
.and_hms_opt(12, 0, 0)
.expect("Invalid hour, minute and/or second");
let updated_at = DateTime::from_utc(timestamp, Utc);
Item {
title: video.title().to_string(),
@ -204,7 +210,7 @@ impl From<YouTubeVideoWithStream> for Item {
guid: id,
keywords: Default::default(),
image,
updated_at: Utc::now(), // TODO: Get a decent timestamp somewhere?!
updated_at,
}
}
}
@ -254,8 +260,8 @@ async fn fetch_channel_videos(
let yt_videos_w_streams = yt_channel
.uploads()
.await?
.take(limit)
.filter_map(fetch_stream)
.take(limit)
.collect()
.await;
@ -264,13 +270,14 @@ async fn fetch_channel_videos(
/// Fetches the stream and relevant metadata for a YouTube video result.
///
/// If there is a video retieving the metadata, the video is discarded/ignored.
/// If there is a error retrieving the metadata, the video is discarded/ignored.
/// If there are problems retrieving the streams or metadata, the video is also discarded.
async fn fetch_stream(
yt_video: Result<YouTubeVideo, YouTubeVideoError>,
yt_video: Result<YouTubePlaylistVideo, YouTubeVideoError>,
) -> Option<YouTubeVideoWithStream> {
match yt_video {
Ok(video) => {
let video = video.upgrade().await.ok()?;
let stream = video
.streams()
.await

View File

@ -28,7 +28,9 @@ pub(crate) fn construct(backend_id: &str, config: &Config, channel: Channel) ->
.unwrap_or_default(),
)
.build();
let mut last_build = DateTime::<Utc>::from_utc(NaiveDateTime::from_timestamp(0, 0), Utc);
let unix_timestamp = NaiveDateTime::from_timestamp_opt(0, 0)
.expect("Out-of-range seconds or invalid nanoseconds");
let mut last_build = DateTime::from_utc(unix_timestamp, Utc);
let generator = String::from(concat!(
env!("CARGO_PKG_NAME"),
" ",
@ -92,7 +94,7 @@ fn construct_item(
})
.collect::<Vec<_>>();
let url = uri!(
Absolute::parse(&config.url).expect("valid URL"),
Absolute::parse(&config.public_url).expect("valid URL"),
crate::get_download(backend_id = backend_id, file = item.enclosure.file)
);
let enclosure = EnclosureBuilder::default()

View File

@ -5,7 +5,12 @@
missing_debug_implementations,
rust_2018_idioms,
rustdoc::broken_intra_doc_links,
trivial_numeric_casts
trivial_numeric_casts,
renamed_and_removed_lints,
unsafe_code,
unstable_features,
unused_import_braces,
unused_qualifications
)]
#![deny(missing_docs)]
@ -89,9 +94,9 @@ pub(crate) type Result<T, E = Error> = std::result::Result<T, E>;
#[derive(Debug, Deserialize, Serialize)]
#[serde(crate = "rocket::serde")]
pub(crate) struct Config {
/// The URL at which the application is hosted or proxied from.
/// The public URL at which the application is hosted or proxied from.
#[serde(default)]
url: String,
public_url: String,
}
/// A Rocket responder wrapper type for RSS feeds.
@ -127,7 +132,7 @@ async fn get_feed(
/// Returns a simple index page that explains the usage.
#[get("/")]
pub(crate) async fn get_index(config: &State<Config>) -> Template {
Template::render("index", context! { url: &config.url })
Template::render("index", context! { url: &config.public_url })
}
/// Sets up Rocket.

View File

@ -5,7 +5,12 @@
missing_debug_implementations,
rust_2018_idioms,
rustdoc::broken_intra_doc_links,
trivial_numeric_casts
trivial_numeric_casts,
renamed_and_removed_lints,
unsafe_code,
unstable_features,
unused_import_braces,
unused_qualifications
)]
#![deny(missing_docs)]

View File

@ -11,7 +11,7 @@
<pre>
https://my.domain.tld/podbringer/feed/mixcloud/myfavouriteband
|------------------------------| |-------||--------------|
The Podbringer location URL Service User @ service
The Podbringer public URL Service User @ service
</pre>
</p>
<p>