Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: add DASH support using Video.js #179

Open
wants to merge 4 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions .env.example
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,8 @@ REDLIB_DEFAULT_BLUR_SPOILER=off
REDLIB_DEFAULT_SHOW_NSFW=off
# Enable blurring NSFW content by default
REDLIB_DEFAULT_BLUR_NSFW=off
# Enable Video.js player by default
REDLIB_DEFAULT_USE_VJS=off
# Enable HLS video format by default
REDLIB_DEFAULT_USE_HLS=off
# Hide HLS notification by default
Expand Down
5 changes: 5 additions & 0 deletions .vscode/settings.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
{
"files.associations": {
"**/templates/*.html": "jinja-html"
}
}
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -397,6 +397,7 @@ Assign a default value for each user-modifiable setting by passing environment v
| `BLUR_SPOILER` | `["on", "off"]` | `off` |
| `SHOW_NSFW` | `["on", "off"]` | `off` |
| `BLUR_NSFW` | `["on", "off"]` | `off` |
| `USE_VJS` | `["on", "off"]` | `off` |
| `USE_HLS` | `["on", "off"]` | `off` |
| `HIDE_HLS_NOTIFICATION` | `["on", "off"]` | `off` |
| `AUTOPLAY_VIDEOS` | `["on", "off"]` | `off` |
Expand Down
3 changes: 3 additions & 0 deletions app.json
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,9 @@
"REDLIB_DEFAULT_BLUR_NSFW": {
"required": false
},
"REDLIB_USE_VJS": {
"required": false
},
"REDLIB_USE_HLS": {
"required": false
},
Expand Down
1 change: 1 addition & 0 deletions contrib/redlib.conf
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ PORT=12345
#REDLIB_DEFAULT_BLUR_SPOILER=off
#REDLIB_DEFAULT_SHOW_NSFW=off
#REDLIB_DEFAULT_BLUR_NSFW=off
#REDLIB_DEFAULT_USE_VJS=off
#REDLIB_DEFAULT_USE_HLS=off
#REDLIB_DEFAULT_HIDE_HLS_NOTIFICATION=off
#REDLIB_DEFAULT_AUTOPLAY_VIDEOS=off
Expand Down
6 changes: 6 additions & 0 deletions src/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,10 @@ pub struct Config {
#[serde(alias = "LIBREDDIT_DEFAULT_BLUR_NSFW")]
pub(crate) default_blur_nsfw: Option<String>,

#[serde(rename = "REDLIB_DEFAULT_USE_VJS")]
#[serde(alias = "LIBREDDIT_DEFAULT_USE_VJS")]
pub(crate) default_use_vjs: Option<String>,

#[serde(rename = "REDLIB_DEFAULT_USE_HLS")]
#[serde(alias = "LIBREDDIT_DEFAULT_USE_HLS")]
pub(crate) default_use_hls: Option<String>,
Expand Down Expand Up @@ -137,6 +141,7 @@ impl Config {
default_blur_spoiler: parse("REDLIB_DEFAULT_BLUR_SPOILER"),
default_show_nsfw: parse("REDLIB_DEFAULT_SHOW_NSFW"),
default_blur_nsfw: parse("REDLIB_DEFAULT_BLUR_NSFW"),
default_use_vjs: parse("REDLIB_DEFAULT_USE_VJS"),
default_use_hls: parse("REDLIB_DEFAULT_USE_HLS"),
default_hide_hls_notification: parse("REDLIB_DEFAULT_HIDE_HLS_NOTIFICATION"),
default_hide_awards: parse("REDLIB_DEFAULT_HIDE_AWARDS"),
Expand All @@ -163,6 +168,7 @@ fn get_setting_from_config(name: &str, config: &Config) -> Option<String> {
"REDLIB_DEFAULT_BLUR_SPOILER" => config.default_blur_spoiler.clone(),
"REDLIB_DEFAULT_SHOW_NSFW" => config.default_show_nsfw.clone(),
"REDLIB_DEFAULT_BLUR_NSFW" => config.default_blur_nsfw.clone(),
"REDLIB_DEFAULT_USE_VJS" => config.default_use_vjs.clone(),
"REDLIB_DEFAULT_USE_HLS" => config.default_use_hls.clone(),
"REDLIB_DEFAULT_HIDE_HLS_NOTIFICATION" => config.default_hide_hls_notification.clone(),
"REDLIB_DEFAULT_WIDE" => config.default_wide.clone(),
Expand Down
7 changes: 5 additions & 2 deletions src/instance_info.rs
Original file line number Diff line number Diff line change
Expand Up @@ -144,8 +144,9 @@ impl InstanceInfo {
["Blur Spoiler", &convert(&self.config.default_blur_spoiler)],
["Show NSFW", &convert(&self.config.default_show_nsfw)],
["Blur NSFW", &convert(&self.config.default_blur_nsfw)],
["Use Video.js", &convert(&self.config.default_use_vjs)],
["Use HLS", &convert(&self.config.default_use_hls)],
["Hide HLS notification", &convert(&self.config.default_hide_hls_notification)],
["Hide Video.js/HLS notification", &convert(&self.config.default_hide_hls_notification)],
["Subscriptions", &convert(&self.config.default_subscriptions)],
["Filters", &convert(&self.config.default_filters)],
])
Expand Down Expand Up @@ -178,8 +179,9 @@ impl InstanceInfo {
Default blur Spoiler: {:?}\n
Default show NSFW: {:?}\n
Default blur NSFW: {:?}\n
Default use Video.js: {:?}\n
Default use HLS: {:?}\n
Default hide HLS notification: {:?}\n
Default hide Video.js/HLS notification: {:?}\n
Default subscriptions: {:?}\n
Default filters: {:?}\n",
self.package_name,
Expand All @@ -202,6 +204,7 @@ impl InstanceInfo {
self.config.default_blur_spoiler,
self.config.default_show_nsfw,
self.config.default_blur_nsfw,
self.config.default_use_vjs,
self.config.default_use_hls,
self.config.default_hide_hls_notification,
self.config.default_subscriptions,
Expand Down
21 changes: 20 additions & 1 deletion src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -189,7 +189,7 @@ async fn main() {
"Referrer-Policy" => "no-referrer",
"X-Content-Type-Options" => "nosniff",
"X-Frame-Options" => "DENY",
"Content-Security-Policy" => "default-src 'none'; font-src 'self'; script-src 'self' blob:; manifest-src 'self'; media-src 'self' data: blob: about:; style-src 'self' 'unsafe-inline'; base-uri 'none'; img-src 'self' data:; form-action 'self'; frame-ancestors 'none'; connect-src 'self'; worker-src blob:;"
"Content-Security-Policy" => "default-src 'none'; font-src 'self' data:; script-src 'self' blob:; manifest-src 'self'; media-src 'self' data: blob: about:; style-src 'self' 'unsafe-inline'; base-uri 'none'; img-src 'self' data:; form-action 'self'; frame-ancestors 'none'; connect-src 'self'; worker-src blob:;"
};

if let Some(expire_time) = hsts {
Expand Down Expand Up @@ -232,10 +232,29 @@ async fn main() {
app
.at("/highlighted.js")
.get(|_| resource(include_str!("../static/highlighted.js"), "text/javascript", false).boxed());
app
.at("/video.min.js")
.get(|_| resource(include_str!("../static/video.min.js"), "text/javascript", false).boxed());
app
.at("/video-js.min.css")
.get(|_| resource(include_str!("../static/video-js.min.css"), "text/css", false).boxed());
app
.at("/videojs-contrib-quality-levels.js")
.get(|_| resource(include_str!("../static/videojs-contrib-quality-levels.js"), "text/javascript", false).boxed());
app
.at("/jb-videojs-hls-quality-selector.min.js")
.get(|_| resource(include_str!("../static/jb-videojs-hls-quality-selector.min.js"), "text/javascript", false).boxed());
app
.at("/videojs-player.js")
.get(|_| resource(include_str!("../static/videojs-player.js"), "text/javascript", false).boxed());
app
.at("/videojs-player.css")
.get(|_| resource(include_str!("../static/videojs-player-invidious.css"), "text/css", false).boxed());

// Proxy media through Redlib
app.at("/vid/:id/:size").get(|r| proxy(r, "https://v.redd.it/{id}/DASH_{size}").boxed());
app.at("/hls/:id/*path").get(|r| proxy(r, "https://v.redd.it/{id}/{path}").boxed());
app.at("/dash/:id/*path").get(|r| proxy(r, "https://v.redd.it/{id}/{path}").boxed());
app.at("/img/*path").get(|r| proxy(r, "https://i.redd.it/{path}").boxed());
app.at("/thumb/:point/:id").get(|r| proxy(r, "https://{point}.thumbs.redditmedia.com/{id}").boxed());
app.at("/emoji/:id/:name").get(|r| proxy(r, "https://emoji.redditmedia.com/{id}/{name}").boxed());
Expand Down
3 changes: 2 additions & 1 deletion src/settings.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ struct SettingsTemplate {

// CONSTANTS

const PREFS: [&str; 17] = [
const PREFS: [&str; 18] = [
"theme",
"front_page",
"layout",
Expand All @@ -29,6 +29,7 @@ const PREFS: [&str; 17] = [
"blur_spoiler",
"show_nsfw",
"blur_nsfw",
"use_vjs",
"use_hls",
"hide_hls_notification",
"autoplay_videos",
Expand Down
45 changes: 30 additions & 15 deletions src/utils.rs
Original file line number Diff line number Diff line change
Expand Up @@ -165,7 +165,8 @@ pub struct Flags {
#[derive(Debug)]
pub struct Media {
pub url: String,
pub alt_url: String,
pub hls_url: String,
pub dash_url: String,
pub width: i64,
pub height: i64,
pub poster: String,
Expand All @@ -182,23 +183,26 @@ impl Media {
let crosspost_parent_media = &data["crosspost_parent_list"][0]["secure_media"]["reddit_video"];

// If post is a video, return the video
let (post_type, url_val, alt_url_val) = if data_preview["fallback_url"].is_string() {
let (post_type, url_val, hls_url_val, dash_url_val) = if data_preview["fallback_url"].is_string() {
(
if data_preview["is_gif"].as_bool().unwrap_or(false) { "gif" } else { "video" },
&data_preview["fallback_url"],
Some(&data_preview["hls_url"]),
Some(&data_preview["dash_url"]),
)
} else if secure_media["fallback_url"].is_string() {
(
if secure_media["is_gif"].as_bool().unwrap_or(false) { "gif" } else { "video" },
&secure_media["fallback_url"],
Some(&secure_media["hls_url"]),
Some(&secure_media["dash_url"]),
)
} else if crosspost_parent_media["fallback_url"].is_string() {
(
if crosspost_parent_media["is_gif"].as_bool().unwrap_or(false) { "gif" } else { "video" },
&crosspost_parent_media["fallback_url"],
Some(&crosspost_parent_media["hls_url"]),
Some(&crosspost_parent_media["dash_url"]),
)
} else if data["post_hint"].as_str().unwrap_or("") == "image" {
// Handle images, whether GIFs or pics
Expand All @@ -207,34 +211,35 @@ impl Media {

if mp4.is_object() {
// Return the mp4 if the media is a gif
("gif", &mp4["source"]["url"], None)
("gif", &mp4["source"]["url"], None, None)
} else {
// Return the picture if the media is an image
if data["domain"] == "i.redd.it" {
("image", &data["url"], None)
("image", &data["url"], None, None)
} else {
("image", &preview["source"]["url"], None)
("image", &preview["source"]["url"], None, None)
}
}
} else if data["is_self"].as_bool().unwrap_or_default() {
// If type is self, return permalink
("self", &data["permalink"], None)
("self", &data["permalink"], None, None)
} else if data["is_gallery"].as_bool().unwrap_or_default() {
// If this post contains a gallery of images
gallery = GalleryMedia::parse(&data["gallery_data"]["items"], &data["media_metadata"]);

("gallery", &data["url"], None)
("gallery", &data["url"], None, None)
} else if data["is_reddit_media_domain"].as_bool().unwrap_or_default() && data["domain"] == "i.redd.it" {
// If this post contains a reddit media (image) URL.
("image", &data["url"], None)
("image", &data["url"], None, None)
} else {
// If type can't be determined, return url
("link", &data["url"], None)
("link", &data["url"], None, None)
};

let source = &data["preview"]["images"][0]["source"];

let alt_url = alt_url_val.map_or(String::new(), |val| format_url(val.as_str().unwrap_or_default()));
let hls_url = hls_url_val.map_or(String::new(), |val| format_url(val.as_str().unwrap_or_default()));
let dash_url = dash_url_val.map_or(String::new(), |val| format_url(val.as_str().unwrap_or_default()));

let download_name = if post_type == "image" || post_type == "gif" || post_type == "video" {
let permalink_base = url_path_basename(data["permalink"].as_str().unwrap_or_default());
Expand All @@ -249,7 +254,8 @@ impl Media {
post_type.to_string(),
Self {
url: format_url(url_val.as_str().unwrap_or_default()),
alt_url,
hls_url,
dash_url,
// Note: in the data["is_reddit_media_domain"] path above
// width and height will be 0.
width: source["width"].as_i64().unwrap_or_default(),
Expand Down Expand Up @@ -396,7 +402,8 @@ impl Post {
post_type,
thumbnail: Media {
url: format_url(val(post, "thumbnail").as_str()),
alt_url: String::new(),
hls_url: String::new(),
dash_url: String::new(),
width: data["thumbnail_width"].as_i64().unwrap_or_default(),
height: data["thumbnail_height"].as_i64().unwrap_or_default(),
poster: String::new(),
Expand Down Expand Up @@ -598,6 +605,7 @@ pub struct Preferences {
pub blur_nsfw: String,
pub hide_hls_notification: String,
pub hide_sidebar_and_summary: String,
pub use_vjs: String,
pub use_hls: String,
pub autoplay_videos: String,
pub fixed_navbar: String,
Expand Down Expand Up @@ -635,6 +643,7 @@ impl Preferences {
show_nsfw: setting(req, "show_nsfw"),
hide_sidebar_and_summary: setting(req, "hide_sidebar_and_summary"),
blur_nsfw: setting(req, "blur_nsfw"),
use_vjs: setting(req, "use_vjs"),
use_hls: setting(req, "use_hls"),
hide_hls_notification: setting(req, "hide_hls_notification"),
autoplay_videos: setting(req, "autoplay_videos"),
Expand Down Expand Up @@ -735,7 +744,8 @@ pub async fn parse_post(post: &Value) -> Post {
media,
thumbnail: Media {
url: format_url(val(post, "thumbnail").as_str()),
alt_url: String::new(),
hls_url: String::new(),
dash_url: String::new(),
width: post["data"]["thumbnail_width"].as_i64().unwrap_or_default(),
height: post["data"]["thumbnail_height"].as_i64().unwrap_or_default(),
poster: String::new(),
Expand Down Expand Up @@ -836,6 +846,7 @@ static REGEX_URL_NP: Lazy<Regex> = Lazy::new(|| Regex::new(r"https?://np\.reddit
static REGEX_URL_PLAIN: Lazy<Regex> = Lazy::new(|| Regex::new(r"https?://reddit\.com/(.*)").unwrap());
static REGEX_URL_VIDEOS: Lazy<Regex> = Lazy::new(|| Regex::new(r"https?://v\.redd\.it/(.*)/DASH_([0-9]{2,4}(\.mp4|$|\?source=fallback))").unwrap());
static REGEX_URL_VIDEOS_HLS: Lazy<Regex> = Lazy::new(|| Regex::new(r"https?://v\.redd\.it/(.+)/(HLSPlaylist\.m3u8.*)$").unwrap());
static REGEX_URL_VIDEOS_DASH: Lazy<Regex> = Lazy::new(|| Regex::new(r"https?://v\.redd\.it/(.+)/(DASHPlaylist\.mpd.*)$").unwrap());
static REGEX_URL_IMAGES: Lazy<Regex> = Lazy::new(|| Regex::new(r"https?://i\.redd\.it/(.*)").unwrap());
static REGEX_URL_THUMBS_A: Lazy<Regex> = Lazy::new(|| Regex::new(r"https?://a\.thumbs\.redditmedia\.com/(.*)").unwrap());
static REGEX_URL_THUMBS_B: Lazy<Regex> = Lazy::new(|| Regex::new(r"https?://b\.thumbs\.redditmedia\.com/(.*)").unwrap());
Expand Down Expand Up @@ -868,7 +879,7 @@ pub fn format_url(url: &str) -> String {
}
};

( $first_fn:expr, $($other_fns:expr), *) => {
( $first_fn:expr $(, $other_fns:expr)* $(,)?) => {
{
let result = $first_fn;
if result.is_empty() {
Expand All @@ -887,7 +898,11 @@ pub fn format_url(url: &str) -> String {
"old.reddit.com" => capture(&REGEX_URL_OLD, "/", 1),
"np.reddit.com" => capture(&REGEX_URL_NP, "/", 1),
"reddit.com" => capture(&REGEX_URL_PLAIN, "/", 1),
"v.redd.it" => chain!(capture(&REGEX_URL_VIDEOS, "/vid/", 2), capture(&REGEX_URL_VIDEOS_HLS, "/hls/", 2)),
"v.redd.it" => chain!(
capture(&REGEX_URL_VIDEOS, "/vid/", 2),
capture(&REGEX_URL_VIDEOS_HLS, "/hls/", 2),
capture(&REGEX_URL_VIDEOS_DASH, "/dash/", 2)
),
"i.redd.it" => capture(&REGEX_URL_IMAGES, "/img/", 1),
"a.thumbs.redditmedia.com" => capture(&REGEX_URL_THUMBS_A, "/thumb/a/", 1),
"b.thumbs.redditmedia.com" => capture(&REGEX_URL_THUMBS_B, "/thumb/b/", 1),
Expand Down
2 changes: 2 additions & 0 deletions static/jb-videojs-hls-quality-selector.min.js

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading
Loading