Compare commits
52 Commits
a5a5cbb734
...
v0.35.1
Author | SHA1 | Date | |
---|---|---|---|
1644e68e43 | |||
f5d4cc49fb | |||
f22f7841a8 | |||
b6c6e64cfe | |||
6d3db31b11 | |||
f0eb496d6a | |||
e984646ae6 | |||
60c0d63583 | |||
3bd8b511a7 | |||
8c5aaaa33d | |||
bd0a312487 | |||
023cc8505b | |||
2e476dea63 | |||
f398d16c22 | |||
d045a5760a | |||
07bf20dbc0 | |||
518bf03e04 | |||
00dee52320 | |||
48873c01b9 | |||
951fe400ae | |||
bacc9e35df | |||
724b960112 | |||
ff2e274e5e | |||
7c821d541a | |||
7aca5791b1 | |||
8b3783096b | |||
59b702aa15 | |||
edf0109095 | |||
ecf0f91464 | |||
69f9d9ff3c | |||
1d44bd180e | |||
3301da1ef1 | |||
138934f30c | |||
5396c0783e | |||
213481ef53 | |||
2d5cfcb41d | |||
f460895cc0 | |||
5193164719 | |||
9013e589dd | |||
997cd8f829 | |||
5a13b9892b | |||
91975865b8 | |||
3491e754ac | |||
1408c32a4d | |||
30944579d7 | |||
a12596e1a2 | |||
df8d36c661 | |||
5018184e7c | |||
5c73f043cd | |||
013e805ece | |||
97def1bab1 | |||
feda45f803 |
@ -26,18 +26,24 @@ REDLIB_DEFAULT_WIDE=off
|
|||||||
REDLIB_DEFAULT_POST_SORT=hot
|
REDLIB_DEFAULT_POST_SORT=hot
|
||||||
# Set the default comment sort method (options: confidence, top, new, controversial, old)
|
# Set the default comment sort method (options: confidence, top, new, controversial, old)
|
||||||
REDLIB_DEFAULT_COMMENT_SORT=confidence
|
REDLIB_DEFAULT_COMMENT_SORT=confidence
|
||||||
|
# Enable blurring Spoiler content by default
|
||||||
|
REDLIB_DEFAULT_BLUR_SPOILER=off
|
||||||
# Enable showing NSFW content by default
|
# Enable showing NSFW content by default
|
||||||
REDLIB_DEFAULT_SHOW_NSFW=off
|
REDLIB_DEFAULT_SHOW_NSFW=off
|
||||||
# Enable blurring NSFW content by default
|
# Enable blurring NSFW content by default
|
||||||
REDLIB_DEFAULT_BLUR_NSFW=off
|
REDLIB_DEFAULT_BLUR_NSFW=off
|
||||||
# Enable HLS video format by default
|
# Enable HLS video format by default
|
||||||
REDLIB_DEFAULT_USE_HLS=off
|
REDLIB_DEFAULT_USE_HLS=off
|
||||||
|
# Enable audio+video downloads with ffmpeg.wasm
|
||||||
|
REDLIB_DEFAULT_FFMPEG_VIDEO_DOWNLOADS=off
|
||||||
# Hide HLS notification by default
|
# Hide HLS notification by default
|
||||||
REDLIB_DEFAULT_HIDE_HLS_NOTIFICATION=off
|
REDLIB_DEFAULT_HIDE_HLS_NOTIFICATION=off
|
||||||
# Disable autoplay videos by default
|
# Disable autoplay videos by default
|
||||||
REDLIB_DEFAULT_AUTOPLAY_VIDEOS=off
|
REDLIB_DEFAULT_AUTOPLAY_VIDEOS=off
|
||||||
# Define a default list of subreddit subscriptions (format: sub1+sub2+sub3)
|
# Define a default list of subreddit subscriptions (format: sub1+sub2+sub3)
|
||||||
REDLIB_DEFAULT_SUBSCRIPTIONS=
|
REDLIB_DEFAULT_SUBSCRIPTIONS=
|
||||||
|
# Define a default list of subreddit filters (format: sub1+sub2+sub3)
|
||||||
|
REDLIB_DEFAULT_FILTERS=
|
||||||
# Hide awards by default
|
# Hide awards by default
|
||||||
REDLIB_DEFAULT_HIDE_AWARDS=off
|
REDLIB_DEFAULT_HIDE_AWARDS=off
|
||||||
# Hide sidebar and summary
|
# Hide sidebar and summary
|
||||||
|
2
.gitignore
vendored
2
.gitignore
vendored
@ -1,5 +1,7 @@
|
|||||||
/target
|
/target
|
||||||
.env
|
.env
|
||||||
|
redlib.toml
|
||||||
|
|
||||||
# Idea Files
|
# Idea Files
|
||||||
.idea/
|
.idea/
|
||||||
|
|
||||||
|
2
.replit
2
.replit
@ -1,2 +0,0 @@
|
|||||||
run = "while :; do set -ex; nix-env -iA nixpkgs.unzip; curl -o./redlib.zip -fsSL -- https://nightly.link/redlib-org/redlib/workflows/main-rust/main/redlib.zip; unzip -n redlib.zip; mv target/x86_64-unknown-linux-musl/release/redlib .; chmod +x redlib; set +e; ./redlib -H 63115200; sleep 1; done"
|
|
||||||
language = "bash"
|
|
151
Cargo.lock
generated
151
Cargo.lock
generated
@ -92,7 +92,7 @@ dependencies = [
|
|||||||
"mime_guess",
|
"mime_guess",
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
"syn 2.0.66",
|
"syn",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@ -118,7 +118,7 @@ checksum = "c6fa2087f2753a7da8cc1c0dbfcf89579dd57458e36769de5ac750b4671737ca"
|
|||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
"syn 2.0.66",
|
"syn",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@ -129,9 +129,9 @@ checksum = "0c4b4d0bd25bd0b74681c0ad21497610ce1b7c91b1022cd21c80c6fbdd9476b0"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "backtrace"
|
name = "backtrace"
|
||||||
version = "0.3.72"
|
version = "0.3.73"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "17c6a35df3749d2e8bb1b7b21a976d82b15548788d2735b9d82f329268f71a11"
|
checksum = "5cc23269a4f8976d0a4d2e7109211a419fe30e8d88d677cd60b6bc79c5732e0a"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"addr2line",
|
"addr2line",
|
||||||
"cc",
|
"cc",
|
||||||
@ -150,9 +150,9 @@ checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "bitflags"
|
name = "bitflags"
|
||||||
version = "2.5.0"
|
version = "2.6.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "cf4b9d6a944f767f8e5e0db018570623c85f3d925ac718db4e06d0187adb21c1"
|
checksum = "b048fb63fd8b5923fc5aa7b340d8e156aec7ec02f0c78fa8a6ddc2613f6f71de"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "block-buffer"
|
name = "block-buffer"
|
||||||
@ -208,9 +208,9 @@ checksum = "514de17de45fdb8dc022b1a7975556c53c86f9f0aa5f534b98977b171857c2c9"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "cached"
|
name = "cached"
|
||||||
version = "0.51.3"
|
version = "0.51.4"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "dd93a9f06ec296ca66b4c26fafa9ed63f32c473d7a708a5f28563ee64c948515"
|
checksum = "0feb64151eed3da6107fddd2d717a6ca4b9dbd74e43784c55c841d1abfe5a295"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"ahash",
|
"ahash",
|
||||||
"async-trait",
|
"async-trait",
|
||||||
@ -233,7 +233,7 @@ dependencies = [
|
|||||||
"darling",
|
"darling",
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
"syn 2.0.66",
|
"syn",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@ -244,9 +244,9 @@ checksum = "ade8366b8bd5ba243f0a58f036cc0ca8a2f069cff1a2351ef1cac6b083e16fc0"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "cc"
|
name = "cc"
|
||||||
version = "1.0.98"
|
version = "1.0.101"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "41c270e7540d725e65ac7f1b212ac8ce349719624d7bcff99f8e2e488e8cf03f"
|
checksum = "ac367972e516d45567c7eafc73d24e1c193dcf200a8d94e9db7b3d38b349572d"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "cfg-if"
|
name = "cfg-if"
|
||||||
@ -256,18 +256,18 @@ checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "clap"
|
name = "clap"
|
||||||
version = "4.5.4"
|
version = "4.5.7"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "90bc066a67923782aa8515dbaea16946c5bcc5addbd668bb80af688e53e548a0"
|
checksum = "5db83dced34638ad474f39f250d7fea9598bdd239eaced1bdf45d597da0f433f"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"clap_builder",
|
"clap_builder",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "clap_builder"
|
name = "clap_builder"
|
||||||
version = "4.5.2"
|
version = "4.5.7"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "ae129e2e766ae0ec03484e609954119f123cc1fe650337e155d03b022f24f7b4"
|
checksum = "f7e204572485eb3fbf28f871612191521df159bc3e15a9f5064c66dba3a8c05f"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anstyle",
|
"anstyle",
|
||||||
"clap_lex",
|
"clap_lex",
|
||||||
@ -275,9 +275,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "clap_lex"
|
name = "clap_lex"
|
||||||
version = "0.7.0"
|
version = "0.7.1"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "98cc8fbded0c607b7ba9dd60cd98df59af97e84d24e49c8557331cfc26d301ce"
|
checksum = "4b82cf0babdbd58558212896d1a4272303a57bdb245c2bf1147185fb45640e70"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "cookie"
|
name = "cookie"
|
||||||
@ -363,7 +363,7 @@ dependencies = [
|
|||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
"strsim",
|
"strsim",
|
||||||
"syn 2.0.66",
|
"syn",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@ -374,7 +374,7 @@ checksum = "733cabb43482b1a1b53eee8583c2b9e8684d592215ea83efd305dd31bc2f0178"
|
|||||||
dependencies = [
|
dependencies = [
|
||||||
"darling_core",
|
"darling_core",
|
||||||
"quote",
|
"quote",
|
||||||
"syn 2.0.66",
|
"syn",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@ -637,9 +637,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "httparse"
|
name = "httparse"
|
||||||
version = "1.8.0"
|
version = "1.9.4"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "d897f394bad6a705d5f4104762e116a75639e470d80901eed05a860a95cb1904"
|
checksum = "0fcc0b4a115bf80b728eb8ea024ad5bd707b615bfed49e0665b6e0f86fd082d9"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "httpdate"
|
name = "httpdate"
|
||||||
@ -655,9 +655,9 @@ checksum = "9a3a5bfb195931eeb336b2a7b4d761daec841b97f947d34394601737a7bba5e4"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "hyper"
|
name = "hyper"
|
||||||
version = "0.14.28"
|
version = "0.14.29"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "bf96e135eb83a2a8ddf766e426a841d8ddd7449d5f00d34ea02b41d2f19eef80"
|
checksum = "f361cde2f109281a220d4307746cdfd5ee3f410da58a70377762396775634b33"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bytes",
|
"bytes",
|
||||||
"futures-channel",
|
"futures-channel",
|
||||||
@ -810,9 +810,9 @@ checksum = "90ed8c1e510134f979dbc4f070f87d4313098b704861a105fe34231c70a3901c"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "memchr"
|
name = "memchr"
|
||||||
version = "2.7.2"
|
version = "2.7.4"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "6c8640c5d730cb13ebd907d8d04b52f55ac9a2eec55b440c8892f40d56c76c1d"
|
checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "mime"
|
name = "mime"
|
||||||
@ -838,9 +838,9 @@ checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "miniz_oxide"
|
name = "miniz_oxide"
|
||||||
version = "0.7.3"
|
version = "0.7.4"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "87dfd01fe195c66b572b37921ad8803d010623c0aca821bea2302239d155cdae"
|
checksum = "b8a240ddb74feaf34a79a7add65a741f3167852fba007066dcac1ca548d89c08"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"adler",
|
"adler",
|
||||||
]
|
]
|
||||||
@ -893,9 +893,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "object"
|
name = "object"
|
||||||
version = "0.35.0"
|
version = "0.36.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "b8ec7ab813848ba4522158d5517a6093db1ded27575b070f4177b8d12b41db5e"
|
checksum = "576dfe1fc8f9df304abb159d767a29d0476f7750fbf8aa7ad07816004a207434"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"memchr",
|
"memchr",
|
||||||
]
|
]
|
||||||
@ -983,9 +983,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "proc-macro2"
|
name = "proc-macro2"
|
||||||
version = "1.0.84"
|
version = "1.0.86"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "ec96c6a92621310b51366f1e28d05ef11489516e93be030060e5fc12024a49d6"
|
checksum = "5e719e8df665df0d1c8fbfd238015744736151d4445ec0836b8e628aae103b77"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"unicode-ident",
|
"unicode-ident",
|
||||||
]
|
]
|
||||||
@ -1041,7 +1041,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "redsunlib"
|
name = "redsunlib"
|
||||||
version = "0.34.0"
|
version = "0.35.1"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"askama",
|
"askama",
|
||||||
"base64",
|
"base64",
|
||||||
@ -1077,9 +1077,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "regex"
|
name = "regex"
|
||||||
version = "1.10.4"
|
version = "1.10.5"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "c117dbdfde9c8308975b6a18d71f3f385c89461f7b3fb054288ecf2a2058ba4c"
|
checksum = "b91213439dad192326a0d7c6ee3955910425f441d7038e0d6933b0aec5c4517f"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"aho-corasick",
|
"aho-corasick",
|
||||||
"memchr",
|
"memchr",
|
||||||
@ -1089,9 +1089,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "regex-automata"
|
name = "regex-automata"
|
||||||
version = "0.4.6"
|
version = "0.4.7"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "86b83b8b9847f9bf95ef68afb0b8e6cdb80f498442f5179a29fad448fcc1eaea"
|
checksum = "38caf58cc5ef2fed281f89292ef23f6365465ed9a41b7a7754eb4e26496c92df"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"aho-corasick",
|
"aho-corasick",
|
||||||
"memchr",
|
"memchr",
|
||||||
@ -1100,9 +1100,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "regex-syntax"
|
name = "regex-syntax"
|
||||||
version = "0.8.3"
|
version = "0.8.4"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "adad44e29e4c806119491a7f06f03de4d1af22c3a680dd47f1e6e179439d1f56"
|
checksum = "7a66a03ae7c801facd77a29370b4faec201768915ac14a721ba36f20bc9c209b"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "ring"
|
name = "ring"
|
||||||
@ -1151,7 +1151,7 @@ dependencies = [
|
|||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
"rust-embed-utils",
|
"rust-embed-utils",
|
||||||
"syn 2.0.66",
|
"syn",
|
||||||
"walkdir",
|
"walkdir",
|
||||||
]
|
]
|
||||||
|
|
||||||
@ -1283,9 +1283,9 @@ checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "sealed_test"
|
name = "sealed_test"
|
||||||
version = "1.0.0"
|
version = "1.1.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "1a608d94641cc17fe203b102db2ae86d47a236630192f0244ddbbbb0044c0272"
|
checksum = "2a1867f8f005bd7fb73c367e2e45dd628417906a2ca27597fe59cbf04279a222"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"fs_extra",
|
"fs_extra",
|
||||||
"rusty-forkfork",
|
"rusty-forkfork",
|
||||||
@ -1295,12 +1295,12 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "sealed_test_derive"
|
name = "sealed_test_derive"
|
||||||
version = "1.0.0"
|
version = "1.1.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "7b672e005ae58fef5da619d90b9f1c5b44b061890f4a371b3c96257a8a15e697"
|
checksum = "77253fb2d4451418d07025826028bcb96ee42d3e58859689a70ce62908009db6"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"quote",
|
"quote",
|
||||||
"syn 1.0.109",
|
"syn",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@ -1343,14 +1343,14 @@ checksum = "500cbc0ebeb6f46627f50f3f5811ccf6bf00643be300b4c3eabc0ef55dc5b5ba"
|
|||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
"syn 2.0.66",
|
"syn",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "serde_json"
|
name = "serde_json"
|
||||||
version = "1.0.117"
|
version = "1.0.118"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "455182ea6142b14f93f4bc5320a2b31c1f266b66a4a5c858b013302a5d8cbfc3"
|
checksum = "d947f6b3163d8857ea16c4fa0dd4840d52f3041039a85decd46867eb1abef2e4"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"itoa",
|
"itoa",
|
||||||
"ryu",
|
"ryu",
|
||||||
@ -1438,26 +1438,15 @@ checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "subtle"
|
name = "subtle"
|
||||||
version = "2.5.0"
|
version = "2.6.1"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "81cdd64d312baedb58e21336b31bc043b77e01cc99033ce76ef539f78e965ebc"
|
checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "syn"
|
name = "syn"
|
||||||
version = "1.0.109"
|
version = "2.0.68"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237"
|
checksum = "901fa70d88b9d6c98022e23b4136f9f3e54e4662c3bc1bd1d84a42a9a0f0c1e9"
|
||||||
dependencies = [
|
|
||||||
"proc-macro2",
|
|
||||||
"quote",
|
|
||||||
"unicode-ident",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "syn"
|
|
||||||
version = "2.0.66"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "c42f3f41a2de00b01c0aaad383c5a45241efc8b2d1eda5661812fda5f3cdcff5"
|
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
@ -1502,7 +1491,7 @@ checksum = "46c3384250002a6d5af4d114f2845d37b57521033f30d5c3f46c4d70e1197533"
|
|||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
"syn 2.0.66",
|
"syn",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@ -1540,9 +1529,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "tinyvec"
|
name = "tinyvec"
|
||||||
version = "1.6.0"
|
version = "1.6.1"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "87cc5ceb3875bb20c2890005a4e226a4651264a5c75edb2421b52861a0a0cb50"
|
checksum = "c55115c6fbe2d2bef26eb09ad74bde02d8255476fc0c7b515ef09fbb35742d82"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"tinyvec_macros",
|
"tinyvec_macros",
|
||||||
]
|
]
|
||||||
@ -1555,9 +1544,9 @@ checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "tokio"
|
name = "tokio"
|
||||||
version = "1.37.0"
|
version = "1.38.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "1adbebffeca75fcfd058afa480fb6c0b81e165a0323f9c9d39c9697e37c46787"
|
checksum = "ba4f4a02a7a80d6f274636f0aa95c7e383b912d41fe721a31f29e29698585a4a"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"backtrace",
|
"backtrace",
|
||||||
"bytes",
|
"bytes",
|
||||||
@ -1574,13 +1563,13 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "tokio-macros"
|
name = "tokio-macros"
|
||||||
version = "2.2.0"
|
version = "2.3.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "5b8a1e28f2deaa14e508979454cb3a223b10b938b45af148bc0986de36f1923b"
|
checksum = "5f5ae998a069d4b5aba8ee9dad856af7d520c3699e6159b185c2acd48155d39a"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
"syn 2.0.66",
|
"syn",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@ -1609,9 +1598,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "toml"
|
name = "toml"
|
||||||
version = "0.8.13"
|
version = "0.8.14"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "a4e43f8cc456c9704c851ae29c67e17ef65d2c30017c17a9765b89c382dc8bba"
|
checksum = "6f49eb2ab21d2f26bd6db7bf383edc527a7ebaee412d17af4d40fdccd442f335"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"serde",
|
"serde",
|
||||||
"serde_spanned",
|
"serde_spanned",
|
||||||
@ -1630,9 +1619,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "toml_edit"
|
name = "toml_edit"
|
||||||
version = "0.22.13"
|
version = "0.22.14"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "c127785850e8c20836d49732ae6abfa47616e60bf9d9f57c43c250361a9db96c"
|
checksum = "f21c7aaf97f1bd9ca9d4f9e73b0a6c74bd5afef56f2bc931943a6e1c37e04e38"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"indexmap",
|
"indexmap",
|
||||||
"serde",
|
"serde",
|
||||||
@ -1722,9 +1711,9 @@ checksum = "8ecb6da28b8a351d773b68d5825ac39017e680750f980f3a1a85cd8dd28a47c1"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "url"
|
name = "url"
|
||||||
version = "2.5.0"
|
version = "2.5.2"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "31e6302e3bb753d46e83516cae55ae196fc0c309407cf11ab35cc51a4c2a4633"
|
checksum = "22784dbdf76fdde8af1aeda5622b546b422b6fc585325248a2bf9f5e41e94d6c"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"form_urlencoded",
|
"form_urlencoded",
|
||||||
"idna",
|
"idna",
|
||||||
@ -1733,9 +1722,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "uuid"
|
name = "uuid"
|
||||||
version = "1.8.0"
|
version = "1.9.1"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "a183cf7feeba97b4dd1c0d46788634f6221d87fa961b305bed08c851829efcc0"
|
checksum = "5de17fd2f7da591098415cff336e12965a28061ddace43b59cb3c430179c9439"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"getrandom",
|
"getrandom",
|
||||||
]
|
]
|
||||||
@ -1930,9 +1919,9 @@ checksum = "bec47e5bfd1bff0eeaf6d8b485cc1074891a197ab4225d504cb7a1ab88b02bf0"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "winnow"
|
name = "winnow"
|
||||||
version = "0.6.9"
|
version = "0.6.13"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "86c949fede1d13936a99f14fafd3e76fd642b556dd2ce96287fbe2e0151bfac6"
|
checksum = "59b5e5f6c299a3c7890b876a2a587f3115162487e704907d9b6cd29473052ba1"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"memchr",
|
"memchr",
|
||||||
]
|
]
|
||||||
@ -1954,7 +1943,7 @@ checksum = "15e934569e47891f7d9411f1a451d947a60e000ab3bd24fbb970f000387d1b3b"
|
|||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
"syn 2.0.66",
|
"syn",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
@ -3,7 +3,7 @@ name = "redsunlib"
|
|||||||
description = " Alternative private front-end to Reddit"
|
description = " Alternative private front-end to Reddit"
|
||||||
license = "AGPL-3.0"
|
license = "AGPL-3.0"
|
||||||
repository = "https://git.stardust.wtf/iridium/redlib"
|
repository = "https://git.stardust.wtf/iridium/redlib"
|
||||||
version = "0.34.0"
|
version = "0.35.1"
|
||||||
authors = [
|
authors = [
|
||||||
"Matthew Esposito <matt+cargo@matthew.science>",
|
"Matthew Esposito <matt+cargo@matthew.science>",
|
||||||
"spikecodes <19519553+spikecodes@users.noreply.github.com>",
|
"spikecodes <19519553+spikecodes@users.noreply.github.com>",
|
||||||
|
124
README.md
124
README.md
@ -1,37 +1,45 @@
|
|||||||
<img align="left" width="128" height="128" src="https://git.stardust.wtf/attachments/842086e3-b718-4379-b718-c3a542842152" alt="logo">
|
<img align="left" width="128" height="128" src="https://git.stardust.wtf/attachments/842086e3-b718-4379-b718-c3a542842152" alt="logo">
|
||||||
|
|
||||||
# Redsunlib
|
# Redsunlib
|
||||||
> An alternative private front-end to Reddit, a fork of [Redlib](https://github.com/redlib-org/redlib) with some function and cosmetic changes.
|
> An alternative private front-end to Reddit, a fork of [Redlib](https://github.com/redlib-org/redlib) with some <sup><sub>(minor)</sub></sup> function and cosmetic changes.
|
||||||
|
|
||||||
<br>
|
<br>
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
## Table of Contents
|
### Disclaimer
|
||||||
|
|
||||||
1. [Redlib](#redlib)
|
There are rapid changes/features in this fork that can<sup>(will)</sup> change without notice. If you want to host this version, be aware that it's likely to break at some point. I still wouldn't recommend it in a production environment unless you know what you're doing. Or like living on the edge.......
|
||||||
2. [Instances](#instances)
|
|
||||||
3. [About](#about)
|
> I would also like to thank the maintainers and contributors of both [Redlib](https://github.com/redlib-org/redlib) and [Libreddit](https://github.com/libreddit/libreddit) for all the work they did while I just added some low quality tacky features. ❤️
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Table of Contents
|
||||||
|
1. [Redsunlib](#redsunlib)
|
||||||
|
- [Disclaimer](#disclaimer)
|
||||||
|
2. [Table of Contents](#table-of-contents)
|
||||||
|
3. [Instances](#instances)
|
||||||
|
4. [About](#about)
|
||||||
|
- [The Name](#the-name)
|
||||||
- [Built with](#built-with)
|
- [Built with](#built-with)
|
||||||
- [How is it different from other Reddit front ends?](#how-is-it-different-from-other-reddit-front-ends)
|
- [How is it different from other Reddit front ends?](#how-is-it-different-from-other-reddit-front-ends)
|
||||||
- [Teddit](#teddit)
|
- [Teddit](#teddit)
|
||||||
- [Libreddit](#libreddit)
|
- [Libreddit](#libreddit)
|
||||||
4. [Comparison](#comparison)
|
5. [Comparison](#comparison)
|
||||||
- [Speed](#speed)
|
- [Speed](#speed)
|
||||||
- [Privacy](#privacy)
|
- [Privacy](#privacy)
|
||||||
- [Reddit](#reddit)
|
- [Reddit](#reddit)
|
||||||
- [Redlib](#redlib-1)
|
- [Redlib](#redlib-1)
|
||||||
- [Server](#server)
|
- [Server](#server)
|
||||||
5. [Deployment](#deployment)
|
6. [Deployment](#deployment)
|
||||||
- [Docker](#docker)
|
- [Docker](#docker)
|
||||||
- [Docker Compose](#docker-compose)
|
- [Docker Compose](#docker-compose)
|
||||||
- [Docker CLI](#docker-cli)
|
- [Docker CLI](#docker-cli)
|
||||||
- [Binary](#binary)
|
- [Binary](#binary)
|
||||||
- [Running as a systemd service](#running-as-a-systemd-service)
|
- [Running as a systemd service](#running-as-a-systemd-service)
|
||||||
- [Building from source](#building-from-source)
|
- [Building from source](#building-from-source)
|
||||||
- [Replit/Heroku/Glitch](#replit-heroku-glitch)
|
7. [Configuration](#configuration)
|
||||||
- [launchd (macOS)](#launchd-macos)
|
|
||||||
6. [Configuration](#configuration)
|
|
||||||
- [Instance settings](#instance-settings)
|
- [Instance settings](#instance-settings)
|
||||||
- [Default user settings](#default-user-settings)
|
- [Default user settings](#default-user-settings)
|
||||||
|
|
||||||
@ -39,14 +47,10 @@
|
|||||||
|
|
||||||
# Instances
|
# Instances
|
||||||
|
|
||||||
> [!TIP]
|
> [!WARNING]
|
||||||
> 🔗 **Want to automatically redirect Reddit links to Redlib? Use [LibRedirect](https://github.com/libredirect/libredirect) or [Privacy Redirect](https://github.com/SimonBrazell/privacy-redirect)!**
|
> 🔗 **Currently public Redsunlib instance are not available, consider using a [redlib](https://github.com/redlib-org/redlib-instances/blob/main/instances.md) instance if you are not comfortable running your own**
|
||||||
|
|
||||||
An up-to-date table of instances is available in [Markdown](https://github.com/redlib-org/redlib-instances/blob/main/instances.md) and [machine-readable JSON](https://github.com/redlib-org/redlib-instances/blob/main/instances.json).
|
You are more than welcome to host an instance and submit an issue if you want it added. That is, if you've read the [Disclaimer](#disclaimer) and it's within your "personal risk tolerance." ;)
|
||||||
|
|
||||||
Both files are part of the [redlib-instances](https://github.com/redlib-org/redlib-instances) repository. To contribute your [self-hosted instance](#deployment) to the list, see the [redlib-instances README](https://github.com/redlib-org/redlib-instances/blob/main/README.md).
|
|
||||||
|
|
||||||
For information on instance uptime, see the [Uptime Robot status page](https://stats.uptimerobot.com/mpmqAs1G2Q).
|
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
@ -56,6 +60,13 @@ Redlib hopes to provide an easier way to browse Reddit, without the ads, tracker
|
|||||||
|
|
||||||
Redlib currently implements most of Reddit's (signed-out) functionalities but still lacks [a few features](https://github.com/redlib-org/redlib/issues).
|
Redlib currently implements most of Reddit's (signed-out) functionalities but still lacks [a few features](https://github.com/redlib-org/redlib/issues).
|
||||||
|
|
||||||
|
## The Name
|
||||||
|
|
||||||
|
**Red sun** in the sky + Red**lib** = Redsunlib
|
||||||
|
|
||||||
|
And at the time, I was reading an excerpt from Mao Zedong, so the name seemed appropriate. But paradoxically named since Reddit is basically the sinophobia capital of the internet :/
|
||||||
|
|
||||||
|
|
||||||
## Built with
|
## Built with
|
||||||
|
|
||||||
- [Rust](https://www.rust-lang.org/) - Programming language
|
- [Rust](https://www.rust-lang.org/) - Programming language
|
||||||
@ -158,7 +169,7 @@ For configuration options, see the [Configuration section](#Configuration).
|
|||||||
|
|
||||||
[Docker](https://www.docker.com) lets you run containerized applications. Containers are loosely isolated environments that are lightweight and contain everything needed to run the application, so there's no need to rely on what's installed on the host.
|
[Docker](https://www.docker.com) lets you run containerized applications. Containers are loosely isolated environments that are lightweight and contain everything needed to run the application, so there's no need to rely on what's installed on the host.
|
||||||
|
|
||||||
Docker images for Redlib are available at [quay.io](https://quay.io/repository/redlib/redlib), with support for `amd64`, `arm64`, and `armv7` platforms.
|
Docker images for Redsunlib are available at our [Gitea container registry](https://git.stardust.wtf/iridium/-/packages/container/redsunlib/latest), currently only with support for `amd64`, if you need `arm64`, or `armv7` platforms you can either build Redsunlib yourself or open an [issue](https://git.stardust.wtf/iridium/redsunlib/issues) :)
|
||||||
|
|
||||||
### Docker Compose
|
### Docker Compose
|
||||||
|
|
||||||
@ -184,15 +195,15 @@ docker logs -f redlib
|
|||||||
Deploy Redlib:
|
Deploy Redlib:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
docker pull quay.io/redlib/redlib:latest
|
docker pull git.stardust.wtf/iridium/redsunlib:latest
|
||||||
docker run -d --name redlib -p 8080:8080 quay.io/redlib/redlib:latest
|
docker run -d --name redlib -p 8080:8080 git.stardust.wtf/iridium/redsunlib:latest
|
||||||
```
|
```
|
||||||
|
|
||||||
Deploy using a different port on the host (in this case, port 80):
|
Deploy using a different port on the host (in this case, port 80):
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
docker pull quay.io/redlib/redlib:latest
|
docker pull git.stardust.wtf/iridium/redsunlib:latest
|
||||||
docker run -d --name redlib -p 80:8080 quay.io/redlib/redlib:latest
|
docker run -d --name redlib -p 80:8080 git.stardust.wtf/iridium/redsunlib:latest
|
||||||
```
|
```
|
||||||
|
|
||||||
If you're using a reverse proxy in front of Redlib, prefix the port numbers with `127.0.0.1` so that Redlib only listens on the host port **locally**. For example, if the host port for Redlib is `8080`, specify `127.0.0.1:8080:8080`.
|
If you're using a reverse proxy in front of Redlib, prefix the port numbers with `127.0.0.1` so that Redlib only listens on the host port **locally**. For example, if the host port for Redlib is `8080`, specify `127.0.0.1:8080:8080`.
|
||||||
@ -205,19 +216,7 @@ docker logs -f redlib
|
|||||||
|
|
||||||
## Binary
|
## Binary
|
||||||
|
|
||||||
If you're on Linux, you can grab a binary from [the newest release](https://github.com/redlib-org/redlib/releases/latest) from GitHub.
|
Currently binaries are not supplied at this moment but will be at some point in the future but can be [built from source](#building-from-source)
|
||||||
|
|
||||||
Download the binary using [Wget](https://www.gnu.org/software/wget/):
|
|
||||||
|
|
||||||
```bash
|
|
||||||
wget https://github.com/redlib-org/redlib/releases/download/v0.31.0/redlib
|
|
||||||
```
|
|
||||||
|
|
||||||
Make the binary executable and change its ownership to `root`:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
sudo chmod +x redlib && sudo chown root:root redlib
|
|
||||||
```
|
|
||||||
|
|
||||||
Copy the binary to `/usr/bin`:
|
Copy the binary to `/usr/bin`:
|
||||||
|
|
||||||
@ -261,59 +260,13 @@ Before=nginx.service
|
|||||||
|
|
||||||
## Building from source
|
## Building from source
|
||||||
|
|
||||||
To deploy Redlib with changes not yet included in the latest release, you can build the application from source.
|
To deploy Redsunlib with changes not yet included in the latest release, you can build the application from source.
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
git clone https://github.com/redlib-org/redlib && cd redlib
|
git clone https://git.stardust.wtf/iridium/redsunlib && cd redsunlib
|
||||||
cargo run
|
cargo run
|
||||||
```
|
```
|
||||||
|
|
||||||
## Replit/Heroku
|
|
||||||
|
|
||||||
> [!WARNING]
|
|
||||||
> These are free hosting options, but they are _not_ private and will monitor server usage to prevent abuse. If you need a free and easy setup, this method may work best for you.
|
|
||||||
|
|
||||||
<a href="https://repl.it/github/redlib-org/redlib"><img src="https://repl.it/badge/github/redlib-org/redlib" alt="Run on Repl.it" height="32" /></a>
|
|
||||||
[](https://heroku.com/deploy?template=https://github.com/redlib-org/redlib)
|
|
||||||
|
|
||||||
## launchd (macOS)
|
|
||||||
|
|
||||||
If you are on macOS, you can use the [launchd](https://en.wikipedia.org/wiki/Launchd) service available in `contrib/redlib.plist`.
|
|
||||||
|
|
||||||
Install it with `cp contrib/redlib.plist ~/Library/LaunchAgents/`.
|
|
||||||
|
|
||||||
Load and start it with `launchctl load ~/Library/LaunchAgents/redlib.plist`.
|
|
||||||
|
|
||||||
<!-- ## Cargo
|
|
||||||
|
|
||||||
Make sure Rust stable is installed along with `cargo`, Rust's package manager.
|
|
||||||
|
|
||||||
```bash
|
|
||||||
cargo install libreddit
|
|
||||||
``` -->
|
|
||||||
|
|
||||||
<!-- ## AUR
|
|
||||||
|
|
||||||
For ArchLinux users, Redlib is available from the AUR as [`libreddit-git`](https://aur.archlinux.org/packages/libreddit-git).
|
|
||||||
|
|
||||||
```bash
|
|
||||||
yay -S libreddit-git
|
|
||||||
```
|
|
||||||
## NetBSD/pkgsrc
|
|
||||||
|
|
||||||
For NetBSD users, Redlib is available from the official repositories.
|
|
||||||
|
|
||||||
```bash
|
|
||||||
pkgin install libreddit
|
|
||||||
```
|
|
||||||
|
|
||||||
Or, if you prefer to build from source
|
|
||||||
|
|
||||||
```bash
|
|
||||||
cd /usr/pkgsrc/libreddit
|
|
||||||
make install
|
|
||||||
``` -->
|
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
# Configuration
|
# Configuration
|
||||||
@ -343,7 +296,7 @@ REDLIB_DEFAULT_USE_HLS = "on"
|
|||||||
> If using the Docker CLI, add ` --env-file .env` to the command that runs Redlib. For example:
|
> If using the Docker CLI, add ` --env-file .env` to the command that runs Redlib. For example:
|
||||||
>
|
>
|
||||||
> ```bash
|
> ```bash
|
||||||
> docker run -d --name redlib -p 8080:8080 --env-file .env quay.io/redlib/redlib:latest
|
> docker run -d --name redlib -p 8080:8080 --env-file .env git.stardust.wtf/iridium/redsunlib:latest
|
||||||
> ```
|
> ```
|
||||||
>
|
>
|
||||||
> If using Docker Compose, no changes are needed as the `.env` file is already referenced in `compose.yaml` via the `env_file: .env` line.
|
> If using Docker Compose, no changes are needed as the `.env` file is already referenced in `compose.yaml` via the `env_file: .env` line.
|
||||||
@ -367,14 +320,17 @@ Assign a default value for each user-modifiable setting by passing environment v
|
|||||||
| Name | Possible values | Default value |
|
| Name | Possible values | Default value |
|
||||||
| ----------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------- | ------------- |
|
| ----------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------- | ------------- |
|
||||||
| `THEME` | `["system", "light", "dark", "black", "dracula", "nord", "laserwave", "violet", "gold", "rosebox", "gruvboxdark", "gruvboxlight", "tokyoNight", "icebergDark"]` | `system` |
|
| `THEME` | `["system", "light", "dark", "black", "dracula", "nord", "laserwave", "violet", "gold", "rosebox", "gruvboxdark", "gruvboxlight", "tokyoNight", "icebergDark"]` | `system` |
|
||||||
|
| `MASCOT` | `["BoymoderBlahaj", "redsunlib" ... Add more at ./static/mascots] ` | _(none)_ |
|
||||||
| `FRONT_PAGE` | `["default", "popular", "all"]` | `default` |
|
| `FRONT_PAGE` | `["default", "popular", "all"]` | `default` |
|
||||||
| `LAYOUT` | `["card", "clean", "compact"]` | `card` |
|
| `LAYOUT` | `["card", "clean", "compact"]` | `card` |
|
||||||
| `WIDE` | `["on", "off"]` | `off` |
|
| `WIDE` | `["on", "off"]` | `off` |
|
||||||
| `POST_SORT` | `["hot", "new", "top", "rising", "controversial"]` | `hot` |
|
| `POST_SORT` | `["hot", "new", "top", "rising", "controversial"]` | `hot` |
|
||||||
| `COMMENT_SORT` | `["confidence", "top", "new", "controversial", "old"]` | `confidence` |
|
| `COMMENT_SORT` | `["confidence", "top", "new", "controversial", "old"]` | `confidence` |
|
||||||
|
| `BLUR_SPOILER` | `["on", "off"]` | `off` |
|
||||||
| `SHOW_NSFW` | `["on", "off"]` | `off` |
|
| `SHOW_NSFW` | `["on", "off"]` | `off` |
|
||||||
| `BLUR_NSFW` | `["on", "off"]` | `off` |
|
| `BLUR_NSFW` | `["on", "off"]` | `off` |
|
||||||
| `USE_HLS` | `["on", "off"]` | `off` |
|
| `USE_HLS` | `["on", "off"]` | `off` |
|
||||||
|
| `FFMPEG_VIDEO_DOWNLOADS` | `["on", "off"]` | `off` |
|
||||||
| `HIDE_HLS_NOTIFICATION` | `["on", "off"]` | `off` |
|
| `HIDE_HLS_NOTIFICATION` | `["on", "off"]` | `off` |
|
||||||
| `AUTOPLAY_VIDEOS` | `["on", "off"]` | `off` |
|
| `AUTOPLAY_VIDEOS` | `["on", "off"]` | `off` |
|
||||||
| `SUBSCRIPTIONS` | `+`-delimited list of subreddits (`sub1+sub2+sub3+...`) | _(none)_ |
|
| `SUBSCRIPTIONS` | `+`-delimited list of subreddits (`sub1+sub2+sub3+...`) | _(none)_ |
|
||||||
|
18
app.json
18
app.json
@ -1,5 +1,5 @@
|
|||||||
{
|
{
|
||||||
"name": "Redlib",
|
"name": "Redsunlib",
|
||||||
"description": "Private front-end for Reddit",
|
"description": "Private front-end for Reddit",
|
||||||
"buildpacks": [
|
"buildpacks": [
|
||||||
{
|
{
|
||||||
@ -14,6 +14,9 @@
|
|||||||
"REDLIB_DEFAULT_THEME": {
|
"REDLIB_DEFAULT_THEME": {
|
||||||
"required": false
|
"required": false
|
||||||
},
|
},
|
||||||
|
"REDLIB_DEFAULT_MASCOT": {
|
||||||
|
"required": false
|
||||||
|
},
|
||||||
"REDLIB_DEFAULT_FRONT_PAGE": {
|
"REDLIB_DEFAULT_FRONT_PAGE": {
|
||||||
"required": false
|
"required": false
|
||||||
},
|
},
|
||||||
@ -29,13 +32,19 @@
|
|||||||
"REDLIB_DEFAULT_POST_SORT": {
|
"REDLIB_DEFAULT_POST_SORT": {
|
||||||
"required": false
|
"required": false
|
||||||
},
|
},
|
||||||
|
"REDLIB_DEFAULT_BLUR_SPOILER": {
|
||||||
|
"required": false
|
||||||
|
},
|
||||||
"REDLIB_DEFAULT_SHOW_NSFW": {
|
"REDLIB_DEFAULT_SHOW_NSFW": {
|
||||||
"required": false
|
"required": false
|
||||||
},
|
},
|
||||||
"REDLIB_DEFAULT_BLUR_NSFW": {
|
"REDLIB_DEFAULT_BLUR_NSFW": {
|
||||||
"required": false
|
"required": false
|
||||||
},
|
},
|
||||||
"REDLIB_USE_HLS": {
|
"REDLIB_DEFAULT_USE_HLS": {
|
||||||
|
"required": false
|
||||||
|
},
|
||||||
|
"REDLIB_DEFAULT_FFMPEG_VIDEO_DOWNLOADS": {
|
||||||
"required": false
|
"required": false
|
||||||
},
|
},
|
||||||
"REDLIB_HIDE_HLS_NOTIFICATION": {
|
"REDLIB_HIDE_HLS_NOTIFICATION": {
|
||||||
@ -54,11 +63,14 @@
|
|||||||
"required": false
|
"required": false
|
||||||
},
|
},
|
||||||
"REDLIB_ROBOTS_DISABLE_INDEXING": {
|
"REDLIB_ROBOTS_DISABLE_INDEXING": {
|
||||||
"required": false
|
"required": false
|
||||||
},
|
},
|
||||||
"REDLIB_DEFAULT_SUBSCRIPTIONS": {
|
"REDLIB_DEFAULT_SUBSCRIPTIONS": {
|
||||||
"required": false
|
"required": false
|
||||||
},
|
},
|
||||||
|
"REDLIB_DEFAULT_FILTERS": {
|
||||||
|
"required": false
|
||||||
|
},
|
||||||
"REDLIB_DEFAULT_DISABLE_VISIT_REDDIT_CONFIRMATION": {
|
"REDLIB_DEFAULT_DISABLE_VISIT_REDDIT_CONFIRMATION": {
|
||||||
"required": false
|
"required": false
|
||||||
},
|
},
|
||||||
|
@ -1,14 +1,17 @@
|
|||||||
ADDRESS=0.0.0.0
|
ADDRESS=0.0.0.0
|
||||||
PORT=12345
|
PORT=12345
|
||||||
#REDLIB_DEFAULT_THEME=default
|
#REDLIB_DEFAULT_THEME=default
|
||||||
|
#REDLIB_DEFAULT_MASCOT=none
|
||||||
#REDLIB_DEFAULT_FRONT_PAGE=default
|
#REDLIB_DEFAULT_FRONT_PAGE=default
|
||||||
#REDLIB_DEFAULT_LAYOUT=card
|
#REDLIB_DEFAULT_LAYOUT=card
|
||||||
#REDLIB_DEFAULT_WIDE=off
|
#REDLIB_DEFAULT_WIDE=off
|
||||||
#REDLIB_DEFAULT_POST_SORT=hot
|
#REDLIB_DEFAULT_POST_SORT=hot
|
||||||
#REDLIB_DEFAULT_COMMENT_SORT=confidence
|
#REDLIB_DEFAULT_COMMENT_SORT=confidence
|
||||||
|
#REDLIB_DEFAULT_BLUR_SPOILER=off
|
||||||
#REDLIB_DEFAULT_SHOW_NSFW=off
|
#REDLIB_DEFAULT_SHOW_NSFW=off
|
||||||
#REDLIB_DEFAULT_BLUR_NSFW=off
|
#REDLIB_DEFAULT_BLUR_NSFW=off
|
||||||
#REDLIB_DEFAULT_USE_HLS=off
|
#REDLIB_DEFAULT_USE_HLS=off
|
||||||
|
#REDLIB_DEFAULT_FFMPEG_VIDEO_DOWNLOADS=off
|
||||||
#REDLIB_DEFAULT_HIDE_HLS_NOTIFICATION=off
|
#REDLIB_DEFAULT_HIDE_HLS_NOTIFICATION=off
|
||||||
#REDLIB_DEFAULT_AUTOPLAY_VIDEOS=off
|
#REDLIB_DEFAULT_AUTOPLAY_VIDEOS=off
|
||||||
#REDLIB_DEFAULT_SUBSCRIPTIONS=off (sub1+sub2+sub3)
|
#REDLIB_DEFAULT_SUBSCRIPTIONS=off (sub1+sub2+sub3)
|
||||||
|
31
scripts/load_test.py
Normal file
31
scripts/load_test.py
Normal file
@ -0,0 +1,31 @@
|
|||||||
|
import requests
|
||||||
|
from bs4 import BeautifulSoup
|
||||||
|
from concurrent.futures import ThreadPoolExecutor
|
||||||
|
|
||||||
|
base_url = "http://localhost:8080"
|
||||||
|
|
||||||
|
full_path = f"{base_url}/r/politics"
|
||||||
|
|
||||||
|
ctr = 0
|
||||||
|
|
||||||
|
def fetch_url(url):
|
||||||
|
global ctr
|
||||||
|
response = requests.get(url)
|
||||||
|
ctr += 1
|
||||||
|
print(f"Request count: {ctr}")
|
||||||
|
return response
|
||||||
|
|
||||||
|
while full_path:
|
||||||
|
response = requests.get(full_path)
|
||||||
|
ctr += 1
|
||||||
|
print(f"Request count: {ctr}")
|
||||||
|
soup = BeautifulSoup(response.text, 'html.parser')
|
||||||
|
comment_links = soup.find_all('a', class_='post_comments')
|
||||||
|
comment_urls = [base_url + link['href'] for link in comment_links]
|
||||||
|
with ThreadPoolExecutor(max_workers=10) as executor:
|
||||||
|
executor.map(fetch_url, comment_urls)
|
||||||
|
next_link = soup.find('a', accesskey='N')
|
||||||
|
if next_link:
|
||||||
|
full_path = base_url + next_link['href']
|
||||||
|
else:
|
||||||
|
break
|
@ -5,11 +5,13 @@ use hyper::client::HttpConnector;
|
|||||||
use hyper::{body, body::Buf, client, header, Body, Client, Method, Request, Response, Uri};
|
use hyper::{body, body::Buf, client, header, Body, Client, Method, Request, Response, Uri};
|
||||||
use hyper_rustls::HttpsConnector;
|
use hyper_rustls::HttpsConnector;
|
||||||
use libflate::gzip;
|
use libflate::gzip;
|
||||||
use log::error;
|
use log::{error, trace, warn};
|
||||||
use once_cell::sync::Lazy;
|
use once_cell::sync::Lazy;
|
||||||
use percent_encoding::{percent_encode, CONTROLS};
|
use percent_encoding::{percent_encode, CONTROLS};
|
||||||
use serde_json::Value;
|
use serde_json::Value;
|
||||||
|
|
||||||
|
use std::sync::atomic::Ordering;
|
||||||
|
use std::sync::atomic::{AtomicU16, Ordering::SeqCst};
|
||||||
use std::{io, result::Result};
|
use std::{io, result::Result};
|
||||||
use tokio::sync::RwLock;
|
use tokio::sync::RwLock;
|
||||||
|
|
||||||
@ -36,6 +38,8 @@ pub static OAUTH_CLIENT: Lazy<RwLock<Oauth>> = Lazy::new(|| {
|
|||||||
RwLock::new(client)
|
RwLock::new(client)
|
||||||
});
|
});
|
||||||
|
|
||||||
|
pub static OAUTH_RATELIMIT_REMAINING: AtomicU16 = AtomicU16::new(99);
|
||||||
|
|
||||||
/// Gets the canonical path for a resource on Reddit. This is accomplished by
|
/// Gets the canonical path for a resource on Reddit. This is accomplished by
|
||||||
/// making a `HEAD` request to Reddit at the path given in `path`.
|
/// making a `HEAD` request to Reddit at the path given in `path`.
|
||||||
///
|
///
|
||||||
@ -170,7 +174,7 @@ fn request(method: &'static Method, path: String, redirect: bool, quarantine: bo
|
|||||||
// Construct the hyper client from the HTTPS connector.
|
// Construct the hyper client from the HTTPS connector.
|
||||||
let client: Client<_, Body> = CLIENT.clone();
|
let client: Client<_, Body> = CLIENT.clone();
|
||||||
|
|
||||||
let (token, vendor_id, device_id, mut user_agent, loid) = {
|
let (token, vendor_id, device_id, user_agent, loid) = {
|
||||||
let client = block_on(OAUTH_CLIENT.read());
|
let client = block_on(OAUTH_CLIENT.read());
|
||||||
(
|
(
|
||||||
client.token.clone(),
|
client.token.clone(),
|
||||||
@ -181,13 +185,6 @@ fn request(method: &'static Method, path: String, redirect: bool, quarantine: bo
|
|||||||
)
|
)
|
||||||
};
|
};
|
||||||
|
|
||||||
// Replace "Android" with a tricky word.
|
|
||||||
// Issues: #78/#115, #116
|
|
||||||
// If you include the word "Android", you will get a number of different errors
|
|
||||||
// I guess they don't expect mobile traffic on the endpoints we use
|
|
||||||
// Scrawled on wall for next poor soul: Run the test suite.
|
|
||||||
user_agent = user_agent.replace("Android", "Andr\u{200B}oid");
|
|
||||||
|
|
||||||
// Build request to Reddit. When making a GET, request gzip compression.
|
// Build request to Reddit. When making a GET, request gzip compression.
|
||||||
// (Reddit doesn't do brotli yet.)
|
// (Reddit doesn't do brotli yet.)
|
||||||
let builder = Request::builder()
|
let builder = Request::builder()
|
||||||
@ -314,19 +311,59 @@ fn request(method: &'static Method, path: String, redirect: bool, quarantine: bo
|
|||||||
#[cached(size = 100, time = 30, result = true)]
|
#[cached(size = 100, time = 30, result = true)]
|
||||||
pub async fn json(path: String, quarantine: bool) -> Result<Value, String> {
|
pub async fn json(path: String, quarantine: bool) -> Result<Value, String> {
|
||||||
// Closure to quickly build errors
|
// Closure to quickly build errors
|
||||||
let err = |msg: &str, e: String| -> Result<Value, String> {
|
let err = |msg: &str, e: String, path: String| -> Result<Value, String> {
|
||||||
// eprintln!("{} - {}: {}", url, msg, e);
|
// eprintln!("{} - {}: {}", url, msg, e);
|
||||||
Err(format!("{msg}: {e}"))
|
Err(format!("{msg}: {e} | {path}"))
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// First, handle rolling over the OAUTH_CLIENT if need be.
|
||||||
|
let current_rate_limit = OAUTH_RATELIMIT_REMAINING.load(Ordering::SeqCst);
|
||||||
|
if current_rate_limit < 10 {
|
||||||
|
warn!("Rate limit {current_rate_limit} is low. Spawning force_refresh_token()");
|
||||||
|
OAUTH_RATELIMIT_REMAINING.store(99, Ordering::SeqCst);
|
||||||
|
tokio::spawn(force_refresh_token());
|
||||||
|
}
|
||||||
|
|
||||||
// Fetch the url...
|
// Fetch the url...
|
||||||
match reddit_get(path.clone(), quarantine).await {
|
match reddit_get(path.clone(), quarantine).await {
|
||||||
Ok(response) => {
|
Ok(response) => {
|
||||||
let status = response.status();
|
let status = response.status();
|
||||||
|
|
||||||
|
// Ratelimit remaining
|
||||||
|
if let Some(Ok(remaining)) = response.headers().get("x-ratelimit-remaining").map(|val| val.to_str()) {
|
||||||
|
trace!("Ratelimit remaining: {}", remaining);
|
||||||
|
if let Ok(remaining) = remaining.parse::<f32>().map(|f| f.round() as u16) {
|
||||||
|
OAUTH_RATELIMIT_REMAINING.store(remaining, SeqCst);
|
||||||
|
} else {
|
||||||
|
warn!("Failed to parse rate limit {remaining} from header.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Ratelimit used
|
||||||
|
if let Some(Ok(used)) = response.headers().get("x-ratelimit-used").map(|val| val.to_str()) {
|
||||||
|
trace!("Ratelimit used: {}", used);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Ratelimit reset
|
||||||
|
let reset = if let Some(Ok(reset)) = response.headers().get("x-ratelimit-reset").map(|val| val.to_str()) {
|
||||||
|
trace!("Ratelimit reset: {}", reset);
|
||||||
|
Some(reset.to_string())
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
};
|
||||||
|
|
||||||
// asynchronously aggregate the chunks of the body
|
// asynchronously aggregate the chunks of the body
|
||||||
match hyper::body::aggregate(response).await {
|
match hyper::body::aggregate(response).await {
|
||||||
Ok(body) => {
|
Ok(body) => {
|
||||||
|
let has_remaining = body.has_remaining();
|
||||||
|
|
||||||
|
if !has_remaining {
|
||||||
|
return match reset {
|
||||||
|
Some(val) => Err(format!("Reddit rate limit exceeded. Will reset in: {val}")),
|
||||||
|
None => Err("Reddit rate limit exceeded".to_string()),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
// Parse the response from Reddit as JSON
|
// Parse the response from Reddit as JSON
|
||||||
match serde_json::from_reader(body.reader()) {
|
match serde_json::from_reader(body.reader()) {
|
||||||
Ok(value) => {
|
Ok(value) => {
|
||||||
@ -339,7 +376,7 @@ pub async fn json(path: String, quarantine: bool) -> Result<Value, String> {
|
|||||||
let () = force_refresh_token().await;
|
let () = force_refresh_token().await;
|
||||||
return Err("OAuth token has expired. Please refresh the page!".to_string());
|
return Err("OAuth token has expired. Please refresh the page!".to_string());
|
||||||
}
|
}
|
||||||
Err(format!("Reddit error {} \"{}\": {}", json["error"], json["reason"], json["message"]))
|
Err(format!("Reddit error {} \"{}\": {} | {path}", json["error"], json["reason"], json["message"]))
|
||||||
} else {
|
} else {
|
||||||
Ok(json)
|
Ok(json)
|
||||||
}
|
}
|
||||||
@ -349,21 +386,24 @@ pub async fn json(path: String, quarantine: bool) -> Result<Value, String> {
|
|||||||
if status.is_server_error() {
|
if status.is_server_error() {
|
||||||
Err("Reddit is having issues, check if there's an outage".to_string())
|
Err("Reddit is having issues, check if there's an outage".to_string())
|
||||||
} else {
|
} else {
|
||||||
err("Failed to parse page JSON data", e.to_string())
|
err("Failed to parse page JSON data", e.to_string(), path)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Err(e) => err("Failed receiving body from Reddit", e.to_string()),
|
Err(e) => err("Failed receiving body from Reddit", e.to_string(), path),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Err(e) => err("Couldn't send request to Reddit", e),
|
Err(e) => err("Couldn't send request to Reddit", e, path),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
static POPULAR_URL: &str = "/r/popular/hot.json?&raw_json=1&geo_filter=GLOBAL";
|
||||||
|
|
||||||
#[tokio::test(flavor = "multi_thread")]
|
#[tokio::test(flavor = "multi_thread")]
|
||||||
async fn test_localization_popular() {
|
async fn test_localization_popular() {
|
||||||
let val = json("/r/popular/hot.json?&raw_json=1&geo_filter=GLOBAL".to_string(), false).await.unwrap();
|
let val = json(POPULAR_URL.to_string(), false).await.unwrap();
|
||||||
assert_eq!("GLOBAL", val["data"]["geo_filter"].as_str().unwrap());
|
assert_eq!("GLOBAL", val["data"]["geo_filter"].as_str().unwrap());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -52,6 +52,10 @@ pub struct Config {
|
|||||||
#[serde(alias = "LIBREDDIT_DEFAULT_POST_SORT")]
|
#[serde(alias = "LIBREDDIT_DEFAULT_POST_SORT")]
|
||||||
pub(crate) default_post_sort: Option<String>,
|
pub(crate) default_post_sort: Option<String>,
|
||||||
|
|
||||||
|
#[serde(rename = "REDLIB_DEFAULT_BLUR_SPOILER")]
|
||||||
|
#[serde(alias = "LIBREDDIT_DEFAULT_BLUR_SPOILER")]
|
||||||
|
pub(crate) default_blur_spoiler: Option<String>,
|
||||||
|
|
||||||
#[serde(rename = "REDLIB_DEFAULT_SHOW_NSFW")]
|
#[serde(rename = "REDLIB_DEFAULT_SHOW_NSFW")]
|
||||||
#[serde(alias = "LIBREDDIT_DEFAULT_SHOW_NSFW")]
|
#[serde(alias = "LIBREDDIT_DEFAULT_SHOW_NSFW")]
|
||||||
pub(crate) default_show_nsfw: Option<String>,
|
pub(crate) default_show_nsfw: Option<String>,
|
||||||
@ -64,6 +68,10 @@ pub struct Config {
|
|||||||
#[serde(alias = "LIBREDDIT_DEFAULT_USE_HLS")]
|
#[serde(alias = "LIBREDDIT_DEFAULT_USE_HLS")]
|
||||||
pub(crate) default_use_hls: Option<String>,
|
pub(crate) default_use_hls: Option<String>,
|
||||||
|
|
||||||
|
#[serde(rename = "REDLIB_DEFAULT_FFMPEG_VIDEO_DOWNLOADS")]
|
||||||
|
#[serde(alias = "LIBREDDIT_DEFAULT_FFMPEG_VIDEO_DOWNLOADS")]
|
||||||
|
pub(crate) default_ffmpeg_video_downloads: Option<String>,
|
||||||
|
|
||||||
#[serde(rename = "REDLIB_DEFAULT_HIDE_HLS_NOTIFICATION")]
|
#[serde(rename = "REDLIB_DEFAULT_HIDE_HLS_NOTIFICATION")]
|
||||||
#[serde(alias = "LIBREDDIT_DEFAULT_HIDE_HLS_NOTIFICATION")]
|
#[serde(alias = "LIBREDDIT_DEFAULT_HIDE_HLS_NOTIFICATION")]
|
||||||
pub(crate) default_hide_hls_notification: Option<String>,
|
pub(crate) default_hide_hls_notification: Option<String>,
|
||||||
@ -84,6 +92,10 @@ pub struct Config {
|
|||||||
#[serde(alias = "LIBREDDIT_DEFAULT_SUBSCRIPTIONS")]
|
#[serde(alias = "LIBREDDIT_DEFAULT_SUBSCRIPTIONS")]
|
||||||
pub(crate) default_subscriptions: Option<String>,
|
pub(crate) default_subscriptions: Option<String>,
|
||||||
|
|
||||||
|
#[serde(rename = "REDLIB_DEFAULT_FILTERS")]
|
||||||
|
#[serde(alias = "LIBREDDIT_DEFAULT_FILTERS")]
|
||||||
|
pub(crate) default_filters: Option<String>,
|
||||||
|
|
||||||
#[serde(rename = "REDLIB_DEFAULT_DISABLE_VISIT_REDDIT_CONFIRMATION")]
|
#[serde(rename = "REDLIB_DEFAULT_DISABLE_VISIT_REDDIT_CONFIRMATION")]
|
||||||
#[serde(alias = "LIBREDDIT_DEFAULT_DISABLE_VISIT_REDDIT_CONFIRMATION")]
|
#[serde(alias = "LIBREDDIT_DEFAULT_DISABLE_VISIT_REDDIT_CONFIRMATION")]
|
||||||
pub(crate) default_disable_visit_reddit_confirmation: Option<String>,
|
pub(crate) default_disable_visit_reddit_confirmation: Option<String>,
|
||||||
@ -131,14 +143,17 @@ impl Config {
|
|||||||
default_post_sort: parse("REDLIB_DEFAULT_POST_SORT"),
|
default_post_sort: parse("REDLIB_DEFAULT_POST_SORT"),
|
||||||
default_wide: parse("REDLIB_DEFAULT_WIDE"),
|
default_wide: parse("REDLIB_DEFAULT_WIDE"),
|
||||||
default_comment_sort: parse("REDLIB_DEFAULT_COMMENT_SORT"),
|
default_comment_sort: parse("REDLIB_DEFAULT_COMMENT_SORT"),
|
||||||
|
default_blur_spoiler: parse("REDLIB_DEFAULT_BLUR_SPOILER"),
|
||||||
default_show_nsfw: parse("REDLIB_DEFAULT_SHOW_NSFW"),
|
default_show_nsfw: parse("REDLIB_DEFAULT_SHOW_NSFW"),
|
||||||
default_blur_nsfw: parse("REDLIB_DEFAULT_BLUR_NSFW"),
|
default_blur_nsfw: parse("REDLIB_DEFAULT_BLUR_NSFW"),
|
||||||
default_use_hls: parse("REDLIB_DEFAULT_USE_HLS"),
|
default_use_hls: parse("REDLIB_DEFAULT_USE_HLS"),
|
||||||
|
default_ffmpeg_video_downloads: parse("REDLIB_DEFAULT_FFMPEG_VIDEO_DOWNLOADS"),
|
||||||
default_hide_hls_notification: parse("REDLIB_DEFAULT_HIDE_HLS_NOTIFICATION"),
|
default_hide_hls_notification: parse("REDLIB_DEFAULT_HIDE_HLS_NOTIFICATION"),
|
||||||
default_hide_awards: parse("REDLIB_DEFAULT_HIDE_AWARDS"),
|
default_hide_awards: parse("REDLIB_DEFAULT_HIDE_AWARDS"),
|
||||||
default_hide_sidebar_and_summary: parse("REDLIB_DEFAULT_HIDE_SIDEBAR_AND_SUMMARY"),
|
default_hide_sidebar_and_summary: parse("REDLIB_DEFAULT_HIDE_SIDEBAR_AND_SUMMARY"),
|
||||||
default_hide_score: parse("REDLIB_DEFAULT_HIDE_SCORE"),
|
default_hide_score: parse("REDLIB_DEFAULT_HIDE_SCORE"),
|
||||||
default_subscriptions: parse("REDLIB_DEFAULT_SUBSCRIPTIONS"),
|
default_subscriptions: parse("REDLIB_DEFAULT_SUBSCRIPTIONS"),
|
||||||
|
default_filters: parse("REDLIB_DEFAULT_FILTERS"),
|
||||||
default_disable_visit_reddit_confirmation: parse("REDLIB_DEFAULT_DISABLE_VISIT_REDDIT_CONFIRMATION"),
|
default_disable_visit_reddit_confirmation: parse("REDLIB_DEFAULT_DISABLE_VISIT_REDDIT_CONFIRMATION"),
|
||||||
banner: parse("REDLIB_BANNER"),
|
banner: parse("REDLIB_BANNER"),
|
||||||
robots_disable_indexing: parse("REDLIB_ROBOTS_DISABLE_INDEXING"),
|
robots_disable_indexing: parse("REDLIB_ROBOTS_DISABLE_INDEXING"),
|
||||||
@ -156,15 +171,18 @@ fn get_setting_from_config(name: &str, config: &Config) -> Option<String> {
|
|||||||
"REDLIB_DEFAULT_LAYOUT" => config.default_layout.clone(),
|
"REDLIB_DEFAULT_LAYOUT" => config.default_layout.clone(),
|
||||||
"REDLIB_DEFAULT_COMMENT_SORT" => config.default_comment_sort.clone(),
|
"REDLIB_DEFAULT_COMMENT_SORT" => config.default_comment_sort.clone(),
|
||||||
"REDLIB_DEFAULT_POST_SORT" => config.default_post_sort.clone(),
|
"REDLIB_DEFAULT_POST_SORT" => config.default_post_sort.clone(),
|
||||||
|
"REDLIB_DEFAULT_BLUR_SPOILER" => config.default_blur_spoiler.clone(),
|
||||||
"REDLIB_DEFAULT_SHOW_NSFW" => config.default_show_nsfw.clone(),
|
"REDLIB_DEFAULT_SHOW_NSFW" => config.default_show_nsfw.clone(),
|
||||||
"REDLIB_DEFAULT_BLUR_NSFW" => config.default_blur_nsfw.clone(),
|
"REDLIB_DEFAULT_BLUR_NSFW" => config.default_blur_nsfw.clone(),
|
||||||
"REDLIB_DEFAULT_USE_HLS" => config.default_use_hls.clone(),
|
"REDLIB_DEFAULT_USE_HLS" => config.default_use_hls.clone(),
|
||||||
|
"REDLIB_DEFAULT_FFMPEG_VIDEO_DOWNLOADS" => config.default_ffmpeg_video_downloads.clone(),
|
||||||
"REDLIB_DEFAULT_HIDE_HLS_NOTIFICATION" => config.default_hide_hls_notification.clone(),
|
"REDLIB_DEFAULT_HIDE_HLS_NOTIFICATION" => config.default_hide_hls_notification.clone(),
|
||||||
"REDLIB_DEFAULT_WIDE" => config.default_wide.clone(),
|
"REDLIB_DEFAULT_WIDE" => config.default_wide.clone(),
|
||||||
"REDLIB_DEFAULT_HIDE_AWARDS" => config.default_hide_awards.clone(),
|
"REDLIB_DEFAULT_HIDE_AWARDS" => config.default_hide_awards.clone(),
|
||||||
"REDLIB_DEFAULT_HIDE_SIDEBAR_AND_SUMMARY" => config.default_hide_sidebar_and_summary.clone(),
|
"REDLIB_DEFAULT_HIDE_SIDEBAR_AND_SUMMARY" => config.default_hide_sidebar_and_summary.clone(),
|
||||||
"REDLIB_DEFAULT_HIDE_SCORE" => config.default_hide_score.clone(),
|
"REDLIB_DEFAULT_HIDE_SCORE" => config.default_hide_score.clone(),
|
||||||
"REDLIB_DEFAULT_SUBSCRIPTIONS" => config.default_subscriptions.clone(),
|
"REDLIB_DEFAULT_SUBSCRIPTIONS" => config.default_subscriptions.clone(),
|
||||||
|
"REDLIB_DEFAULT_FILTERS" => config.default_filters.clone(),
|
||||||
"REDLIB_DEFAULT_DISABLE_VISIT_REDDIT_CONFIRMATION" => config.default_disable_visit_reddit_confirmation.clone(),
|
"REDLIB_DEFAULT_DISABLE_VISIT_REDDIT_CONFIRMATION" => config.default_disable_visit_reddit_confirmation.clone(),
|
||||||
"REDLIB_BANNER" => config.banner.clone(),
|
"REDLIB_BANNER" => config.banner.clone(),
|
||||||
"REDLIB_ROBOTS_DISABLE_INDEXING" => config.robots_disable_indexing.clone(),
|
"REDLIB_ROBOTS_DISABLE_INDEXING" => config.robots_disable_indexing.clone(),
|
||||||
@ -237,6 +255,12 @@ fn test_default_subscriptions() {
|
|||||||
assert_eq!(get_setting("REDLIB_DEFAULT_SUBSCRIPTIONS"), Some("news+bestof".into()));
|
assert_eq!(get_setting("REDLIB_DEFAULT_SUBSCRIPTIONS"), Some("news+bestof".into()));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
#[sealed_test(env = [("REDLIB_DEFAULT_FILTERS", "news+bestof")])]
|
||||||
|
fn test_default_filters() {
|
||||||
|
assert_eq!(get_setting("REDLIB_DEFAULT_FILTERS"), Some("news+bestof".into()));
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
#[sealed_test]
|
#[sealed_test]
|
||||||
fn test_pushshift() {
|
fn test_pushshift() {
|
||||||
|
@ -142,11 +142,14 @@ impl InstanceInfo {
|
|||||||
["Wide", &convert(&self.config.default_wide)],
|
["Wide", &convert(&self.config.default_wide)],
|
||||||
["Comment sort", &convert(&self.config.default_comment_sort)],
|
["Comment sort", &convert(&self.config.default_comment_sort)],
|
||||||
["Post sort", &convert(&self.config.default_post_sort)],
|
["Post sort", &convert(&self.config.default_post_sort)],
|
||||||
|
["Blur Spoiler", &convert(&self.config.default_blur_spoiler)],
|
||||||
["Show NSFW", &convert(&self.config.default_show_nsfw)],
|
["Show NSFW", &convert(&self.config.default_show_nsfw)],
|
||||||
["Blur NSFW", &convert(&self.config.default_blur_nsfw)],
|
["Blur NSFW", &convert(&self.config.default_blur_nsfw)],
|
||||||
["Use HLS", &convert(&self.config.default_use_hls)],
|
["Use HLS", &convert(&self.config.default_use_hls)],
|
||||||
|
["Use FFmpeg", &convert(&self.config.default_ffmpeg_video_downloads)],
|
||||||
["Hide HLS notification", &convert(&self.config.default_hide_hls_notification)],
|
["Hide HLS notification", &convert(&self.config.default_hide_hls_notification)],
|
||||||
["Subscriptions", &convert(&self.config.default_subscriptions)],
|
["Subscriptions", &convert(&self.config.default_subscriptions)],
|
||||||
|
["Filters", &convert(&self.config.default_filters)],
|
||||||
])
|
])
|
||||||
.with_header_row(["Default preferences"]),
|
.with_header_row(["Default preferences"]),
|
||||||
);
|
);
|
||||||
@ -175,11 +178,14 @@ impl InstanceInfo {
|
|||||||
Default wide: {:?}\n
|
Default wide: {:?}\n
|
||||||
Default comment sort: {:?}\n
|
Default comment sort: {:?}\n
|
||||||
Default post sort: {:?}\n
|
Default post sort: {:?}\n
|
||||||
|
Default blur Spoiler: {:?}\n
|
||||||
Default show NSFW: {:?}\n
|
Default show NSFW: {:?}\n
|
||||||
Default blur NSFW: {:?}\n
|
Default blur NSFW: {:?}\n
|
||||||
Default use HLS: {:?}\n
|
Default use HLS: {:?}\n
|
||||||
|
Default use FFmpeg: {:?}\n
|
||||||
Default hide HLS notification: {:?}\n
|
Default hide HLS notification: {:?}\n
|
||||||
Default subscriptions: {:?}\n",
|
Default subscriptions: {:?}\n
|
||||||
|
Default filters: {:?}\n",
|
||||||
self.package_name,
|
self.package_name,
|
||||||
self.crate_version,
|
self.crate_version,
|
||||||
self.git_commit,
|
self.git_commit,
|
||||||
@ -198,11 +204,14 @@ impl InstanceInfo {
|
|||||||
self.config.default_wide,
|
self.config.default_wide,
|
||||||
self.config.default_comment_sort,
|
self.config.default_comment_sort,
|
||||||
self.config.default_post_sort,
|
self.config.default_post_sort,
|
||||||
|
self.config.default_blur_spoiler,
|
||||||
self.config.default_show_nsfw,
|
self.config.default_show_nsfw,
|
||||||
self.config.default_blur_nsfw,
|
self.config.default_blur_nsfw,
|
||||||
self.config.default_use_hls,
|
self.config.default_use_hls,
|
||||||
|
self.config.default_ffmpeg_video_downloads,
|
||||||
self.config.default_hide_hls_notification,
|
self.config.default_hide_hls_notification,
|
||||||
self.config.default_subscriptions,
|
self.config.default_subscriptions,
|
||||||
|
self.config.default_filters,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
StringType::Html => self.to_table(),
|
StringType::Html => self.to_table(),
|
||||||
|
39
src/main.rs
39
src/main.rs
@ -78,6 +78,17 @@ async fn font() -> Result<Response<Body>, String> {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async fn ffmpeg() -> Result<Response<Body>, String> {
|
||||||
|
Ok(
|
||||||
|
Response::builder()
|
||||||
|
.status(200)
|
||||||
|
.header("content-type", "application/wasm")
|
||||||
|
.header("Cache-Control", "public, max-age=1209600, s-maxage=86400")
|
||||||
|
.body(include_bytes!("../static/ffmpeg/ffmpeg-core.wasm").as_ref().into())
|
||||||
|
.unwrap_or_default(),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
async fn resource(body: &str, content_type: &str, cache: bool) -> Result<Response<Body>, String> {
|
async fn resource(body: &str, content_type: &str, cache: bool) -> Result<Response<Body>, String> {
|
||||||
let mut res = Response::builder()
|
let mut res = Response::builder()
|
||||||
.status(200)
|
.status(200)
|
||||||
@ -149,7 +160,7 @@ async fn main() {
|
|||||||
.long("address")
|
.long("address")
|
||||||
.value_name("ADDRESS")
|
.value_name("ADDRESS")
|
||||||
.help("Sets address to listen on")
|
.help("Sets address to listen on")
|
||||||
.default_value("0.0.0.0")
|
.default_value("[::]")
|
||||||
.num_args(1),
|
.num_args(1),
|
||||||
)
|
)
|
||||||
.arg(
|
.arg(
|
||||||
@ -203,7 +214,7 @@ async fn main() {
|
|||||||
"Referrer-Policy" => "no-referrer",
|
"Referrer-Policy" => "no-referrer",
|
||||||
"X-Content-Type-Options" => "nosniff",
|
"X-Content-Type-Options" => "nosniff",
|
||||||
"X-Frame-Options" => "DENY",
|
"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'; script-src 'self' 'wasm-unsafe-eval' 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 'self' blob:;"
|
||||||
};
|
};
|
||||||
|
|
||||||
if let Some(expire_time) = hsts {
|
if let Some(expire_time) = hsts {
|
||||||
@ -238,14 +249,34 @@ async fn main() {
|
|||||||
app.at("/touch-icon-iphone.png").get(|_| iphone_logo().boxed());
|
app.at("/touch-icon-iphone.png").get(|_| iphone_logo().boxed());
|
||||||
app.at("/apple-touch-icon.png").get(|_| iphone_logo().boxed());
|
app.at("/apple-touch-icon.png").get(|_| iphone_logo().boxed());
|
||||||
app
|
app
|
||||||
.at("/playHLSVideo.js")
|
.at("/videoUtils.js")
|
||||||
.get(|_| resource(include_str!("../static/playHLSVideo.js"), "text/javascript", false).boxed());
|
.get(|_| resource(include_str!("../static/videoUtils.js"), "text/javascript", false).boxed());
|
||||||
app
|
app
|
||||||
.at("/hls.min.js")
|
.at("/hls.min.js")
|
||||||
.get(|_| resource(include_str!("../static/hls.min.js"), "text/javascript", false).boxed());
|
.get(|_| resource(include_str!("../static/hls.min.js"), "text/javascript", false).boxed());
|
||||||
app
|
app
|
||||||
.at("/highlighted.js")
|
.at("/highlighted.js")
|
||||||
.get(|_| resource(include_str!("../static/highlighted.js"), "text/javascript", false).boxed());
|
.get(|_| resource(include_str!("../static/highlighted.js"), "text/javascript", false).boxed());
|
||||||
|
// FFmpeg
|
||||||
|
app
|
||||||
|
.at("/ffmpeg/814.ffmpeg.js")
|
||||||
|
.get(|_| resource(include_str!("../static/ffmpeg/814.ffmpeg.js"), "text/javascript", false).boxed());
|
||||||
|
app
|
||||||
|
.at("/ffmpeg/814.ffmpeg.js.map")
|
||||||
|
.get(|_| resource(include_str!("../static/ffmpeg/814.ffmpeg.js.map"), "text/javascript", false).boxed());
|
||||||
|
app
|
||||||
|
.at("/ffmpeg/ffmpeg-core.js")
|
||||||
|
.get(|_| resource(include_str!("../static/ffmpeg/ffmpeg-core.js"), "text/javascript", false).boxed());
|
||||||
|
app.at("/ffmpeg/ffmpeg-core.wasm").get(|_| ffmpeg().boxed());
|
||||||
|
app
|
||||||
|
.at("/ffmpeg/ffmpeg-util.js")
|
||||||
|
.get(|_| resource(include_str!("../static/ffmpeg/ffmpeg-util.js"), "text/javascript", false).boxed());
|
||||||
|
app
|
||||||
|
.at("/ffmpeg/ffmpeg.js")
|
||||||
|
.get(|_| resource(include_str!("../static/ffmpeg/ffmpeg.js"), "text/javascript", false).boxed());
|
||||||
|
app
|
||||||
|
.at("/ffmpeg/ffmpeg.js.map")
|
||||||
|
.get(|_| resource(include_str!("../static/ffmpeg/ffmpeg.js.map"), "text/javascript", false).boxed());
|
||||||
|
|
||||||
// Proxy media through Redlib
|
// Proxy media through Redlib
|
||||||
app.at("/vid/:id/:size").get(|r| proxy(r, "https://v.redd.it/{id}/DASH_{size}").boxed());
|
app.at("/vid/:id/:size").get(|r| proxy(r, "https://v.redd.it/{id}/DASH_{size}").boxed());
|
||||||
|
@ -1,12 +1,12 @@
|
|||||||
use std::{collections::HashMap, time::Duration};
|
use std::{collections::HashMap, sync::atomic::Ordering, time::Duration};
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
client::{CLIENT, OAUTH_CLIENT},
|
client::{CLIENT, OAUTH_CLIENT, OAUTH_RATELIMIT_REMAINING},
|
||||||
oauth_resources::ANDROID_APP_VERSION_LIST,
|
oauth_resources::ANDROID_APP_VERSION_LIST,
|
||||||
};
|
};
|
||||||
use base64::{engine::general_purpose, Engine as _};
|
use base64::{engine::general_purpose, Engine as _};
|
||||||
use hyper::{client, Body, Method, Request};
|
use hyper::{client, Body, Method, Request};
|
||||||
use log::info;
|
use log::{info, trace};
|
||||||
|
|
||||||
use serde_json::json;
|
use serde_json::json;
|
||||||
|
|
||||||
@ -131,6 +131,7 @@ pub async fn token_daemon() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub async fn force_refresh_token() {
|
pub async fn force_refresh_token() {
|
||||||
|
trace!("Rolling over refresh token. Current rate limit: {}", OAUTH_RATELIMIT_REMAINING.load(Ordering::SeqCst));
|
||||||
OAUTH_CLIENT.write().await.refresh().await;
|
OAUTH_CLIENT.write().await.refresh().await;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -19,7 +19,7 @@ struct SettingsTemplate {
|
|||||||
|
|
||||||
// CONSTANTS
|
// CONSTANTS
|
||||||
|
|
||||||
const PREFS: [&str; 17] = [
|
const PREFS: [&str; 19] = [
|
||||||
"theme",
|
"theme",
|
||||||
"mascot",
|
"mascot",
|
||||||
"front_page",
|
"front_page",
|
||||||
@ -27,9 +27,11 @@ const PREFS: [&str; 17] = [
|
|||||||
"wide",
|
"wide",
|
||||||
"comment_sort",
|
"comment_sort",
|
||||||
"post_sort",
|
"post_sort",
|
||||||
|
"blur_spoiler",
|
||||||
"show_nsfw",
|
"show_nsfw",
|
||||||
"blur_nsfw",
|
"blur_nsfw",
|
||||||
"use_hls",
|
"use_hls",
|
||||||
|
"ffmpeg_video_downloads",
|
||||||
"hide_hls_notification",
|
"hide_hls_notification",
|
||||||
"autoplay_videos",
|
"autoplay_videos",
|
||||||
"hide_sidebar_and_summary",
|
"hide_sidebar_and_summary",
|
||||||
@ -82,7 +84,7 @@ pub async fn set(req: Request<Body>) -> Result<Response<Body>, String> {
|
|||||||
Some(value) => response.insert_cookie(
|
Some(value) => response.insert_cookie(
|
||||||
Cookie::build((name.to_owned(), value.clone()))
|
Cookie::build((name.to_owned(), value.clone()))
|
||||||
.path("/")
|
.path("/")
|
||||||
.http_only(true)
|
.http_only(name != "ffmpeg_video_downloads")
|
||||||
.expires(OffsetDateTime::now_utc() + Duration::weeks(52))
|
.expires(OffsetDateTime::now_utc() + Duration::weeks(52))
|
||||||
.into(),
|
.into(),
|
||||||
),
|
),
|
||||||
@ -121,7 +123,7 @@ fn set_cookies_method(req: Request<Body>, remove_cookies: bool) -> Response<Body
|
|||||||
Some(value) => response.insert_cookie(
|
Some(value) => response.insert_cookie(
|
||||||
Cookie::build((name.to_owned(), value.clone()))
|
Cookie::build((name.to_owned(), value.clone()))
|
||||||
.path("/")
|
.path("/")
|
||||||
.http_only(true)
|
.http_only(name != "ffmpeg_video_downloads")
|
||||||
.expires(OffsetDateTime::now_utc() + Duration::weeks(52))
|
.expires(OffsetDateTime::now_utc() + Duration::weeks(52))
|
||||||
.into(),
|
.into(),
|
||||||
),
|
),
|
||||||
|
@ -64,7 +64,7 @@ pub async fn community(req: Request<Body>) -> Result<Response<Body>, String> {
|
|||||||
let post_sort = req.cookie("post_sort").map_or_else(|| "hot".to_string(), |c| c.value().to_string());
|
let post_sort = req.cookie("post_sort").map_or_else(|| "hot".to_string(), |c| c.value().to_string());
|
||||||
let sort = req.param("sort").unwrap_or_else(|| req.param("id").unwrap_or(post_sort));
|
let sort = req.param("sort").unwrap_or_else(|| req.param("id").unwrap_or(post_sort));
|
||||||
|
|
||||||
let sub_name = req.param("sub").unwrap_or(if front_page == "default" || front_page.is_empty() {
|
let mut sub_name = req.param("sub").unwrap_or(if front_page == "default" || front_page.is_empty() {
|
||||||
if subscribed.is_empty() {
|
if subscribed.is_empty() {
|
||||||
"popular".to_string()
|
"popular".to_string()
|
||||||
} else {
|
} else {
|
||||||
@ -84,6 +84,11 @@ pub async fn community(req: Request<Body>) -> Result<Response<Body>, String> {
|
|||||||
return Ok(redirect(&["/user/", &sub_name[2..]].concat()));
|
return Ok(redirect(&["/user/", &sub_name[2..]].concat()));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// If multi-sub, replace + with url encoded +
|
||||||
|
if sub_name.contains('+') {
|
||||||
|
sub_name = sub_name.replace('+', "%2B");
|
||||||
|
}
|
||||||
|
|
||||||
// Request subreddit metadata
|
// Request subreddit metadata
|
||||||
let sub = if !sub_name.contains('+') && sub_name != subscribed && sub_name != "popular" && sub_name != "all" {
|
let sub = if !sub_name.contains('+') && sub_name != subscribed && sub_name != "popular" && sub_name != "all" {
|
||||||
// Regular subreddit
|
// Regular subreddit
|
||||||
|
10
src/utils.rs
10
src/utils.rs
@ -1,3 +1,4 @@
|
|||||||
|
#![allow(dead_code)]
|
||||||
use crate::config::get_setting;
|
use crate::config::get_setting;
|
||||||
//
|
//
|
||||||
// CRATES
|
// CRATES
|
||||||
@ -156,6 +157,7 @@ impl PollOption {
|
|||||||
|
|
||||||
// Post flags with nsfw and stickied
|
// Post flags with nsfw and stickied
|
||||||
pub struct Flags {
|
pub struct Flags {
|
||||||
|
pub spoiler: bool,
|
||||||
pub nsfw: bool,
|
pub nsfw: bool,
|
||||||
pub stickied: bool,
|
pub stickied: bool,
|
||||||
}
|
}
|
||||||
@ -402,6 +404,7 @@ impl Post {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
flags: Flags {
|
flags: Flags {
|
||||||
|
spoiler: data["spoiler"].as_bool().unwrap_or_default(),
|
||||||
nsfw: data["over_18"].as_bool().unwrap_or_default(),
|
nsfw: data["over_18"].as_bool().unwrap_or_default(),
|
||||||
stickied: data["stickied"].as_bool().unwrap_or_default() || data["pinned"].as_bool().unwrap_or_default(),
|
stickied: data["stickied"].as_bool().unwrap_or_default() || data["pinned"].as_bool().unwrap_or_default(),
|
||||||
},
|
},
|
||||||
@ -576,11 +579,13 @@ pub struct Preferences {
|
|||||||
pub front_page: String,
|
pub front_page: String,
|
||||||
pub layout: String,
|
pub layout: String,
|
||||||
pub wide: String,
|
pub wide: String,
|
||||||
|
pub blur_spoiler: String,
|
||||||
pub show_nsfw: String,
|
pub show_nsfw: String,
|
||||||
pub blur_nsfw: String,
|
pub blur_nsfw: String,
|
||||||
pub hide_hls_notification: String,
|
pub hide_hls_notification: String,
|
||||||
pub hide_sidebar_and_summary: String,
|
pub hide_sidebar_and_summary: String,
|
||||||
pub use_hls: String,
|
pub use_hls: String,
|
||||||
|
pub ffmpeg_video_downloads: String,
|
||||||
pub autoplay_videos: String,
|
pub autoplay_videos: String,
|
||||||
pub fixed_navbar: String,
|
pub fixed_navbar: String,
|
||||||
pub disable_visit_reddit_confirmation: String,
|
pub disable_visit_reddit_confirmation: String,
|
||||||
@ -627,10 +632,12 @@ impl Preferences {
|
|||||||
front_page: setting(req, "front_page"),
|
front_page: setting(req, "front_page"),
|
||||||
layout: setting(req, "layout"),
|
layout: setting(req, "layout"),
|
||||||
wide: setting(req, "wide"),
|
wide: setting(req, "wide"),
|
||||||
|
blur_spoiler: setting(req, "blur_spoiler"),
|
||||||
show_nsfw: setting(req, "show_nsfw"),
|
show_nsfw: setting(req, "show_nsfw"),
|
||||||
hide_sidebar_and_summary: setting(req, "hide_sidebar_and_summary"),
|
hide_sidebar_and_summary: setting(req, "hide_sidebar_and_summary"),
|
||||||
blur_nsfw: setting(req, "blur_nsfw"),
|
blur_nsfw: setting(req, "blur_nsfw"),
|
||||||
use_hls: setting(req, "use_hls"),
|
use_hls: setting(req, "use_hls"),
|
||||||
|
ffmpeg_video_downloads: setting(req, "ffmpeg_video_downloads"),
|
||||||
hide_hls_notification: setting(req, "hide_hls_notification"),
|
hide_hls_notification: setting(req, "hide_hls_notification"),
|
||||||
autoplay_videos: setting(req, "autoplay_videos"),
|
autoplay_videos: setting(req, "autoplay_videos"),
|
||||||
fixed_navbar: setting_or_default(req, "fixed_navbar", "on".to_string()),
|
fixed_navbar: setting_or_default(req, "fixed_navbar", "on".to_string()),
|
||||||
@ -747,6 +754,7 @@ pub async fn parse_post(post: &Value) -> Post {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
flags: Flags {
|
flags: Flags {
|
||||||
|
spoiler: post["data"]["spoiler"].as_bool().unwrap_or_default(),
|
||||||
nsfw: post["data"]["over_18"].as_bool().unwrap_or_default(),
|
nsfw: post["data"]["over_18"].as_bool().unwrap_or_default(),
|
||||||
stickied: post["data"]["stickied"].as_bool().unwrap_or_default() || post["data"]["pinned"].as_bool().unwrap_or(false),
|
stickied: post["data"]["stickied"].as_bool().unwrap_or_default() || post["data"]["pinned"].as_bool().unwrap_or(false),
|
||||||
},
|
},
|
||||||
@ -1044,7 +1052,7 @@ pub fn redirect(path: &str) -> Response<Body> {
|
|||||||
|
|
||||||
/// Renders a generic error landing page.
|
/// Renders a generic error landing page.
|
||||||
pub async fn error(req: Request<Body>, msg: &str) -> Result<Response<Body>, String> {
|
pub async fn error(req: Request<Body>, msg: &str) -> Result<Response<Body>, String> {
|
||||||
error!("Error page rendered: {msg}");
|
error!("Error page rendered: {}", msg.split('|').next().unwrap_or_default());
|
||||||
let url = req.uri().to_string();
|
let url = req.uri().to_string();
|
||||||
let body = ErrorTemplate {
|
let body = ErrorTemplate {
|
||||||
msg: msg.to_string(),
|
msg: msg.to_string(),
|
||||||
|
2
static/ffmpeg/814.ffmpeg.js
Normal file
2
static/ffmpeg/814.ffmpeg.js
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
!function(e,t){"object"==typeof exports&&"object"==typeof module?module.exports=t():"function"==typeof define&&define.amd?define([],t):"object"==typeof exports?exports.FFmpegWASM=t():e.FFmpegWASM=t()}(self,(()=>(()=>{var e={454:e=>{function t(e){return Promise.resolve().then((()=>{var t=new Error("Cannot find module '"+e+"'");throw t.code="MODULE_NOT_FOUND",t}))}t.keys=()=>[],t.resolve=t,t.id=454,e.exports=t}},t={};function r(a){var o=t[a];if(void 0!==o)return o.exports;var s=t[a]={exports:{}};return e[a](s,s.exports,r),s.exports}return r.o=(e,t)=>Object.prototype.hasOwnProperty.call(e,t),(()=>{"use strict";var e;!function(e){e.LOAD="LOAD",e.EXEC="EXEC",e.WRITE_FILE="WRITE_FILE",e.READ_FILE="READ_FILE",e.DELETE_FILE="DELETE_FILE",e.RENAME="RENAME",e.CREATE_DIR="CREATE_DIR",e.LIST_DIR="LIST_DIR",e.DELETE_DIR="DELETE_DIR",e.ERROR="ERROR",e.DOWNLOAD="DOWNLOAD",e.PROGRESS="PROGRESS",e.LOG="LOG",e.MOUNT="MOUNT",e.UNMOUNT="UNMOUNT"}(e||(e={}));const t=new Error("unknown message type"),a=new Error("ffmpeg is not loaded, call `await ffmpeg.load()` first"),o=(new Error("called FFmpeg.terminate()"),new Error("failed to import ffmpeg-core.js"));let s;self.onmessage=async({data:{id:n,type:E,data:i}})=>{const c=[];let p;try{if(E!==e.LOAD&&!s)throw a;switch(E){case e.LOAD:p=await(async({coreURL:t="https://unpkg.com/@ffmpeg/core@0.12.1/dist/umd/ffmpeg-core.js",wasmURL:a,workerURL:n})=>{const E=!s,i=t,c=a||t.replace(/.js$/g,".wasm"),p=n||t.replace(/.js$/g,".worker.js");try{importScripts(i)}catch{if(self.createFFmpegCore=(await r(454)(i)).default,!self.createFFmpegCore)throw o}return s=await self.createFFmpegCore({mainScriptUrlOrBlob:`${i}#${btoa(JSON.stringify({wasmURL:c,workerURL:p}))}`}),s.setLogger((t=>self.postMessage({type:e.LOG,data:t}))),s.setProgress((t=>self.postMessage({type:e.PROGRESS,data:t}))),E})(i);break;case e.EXEC:p=(({args:e,timeout:t=-1})=>{s.setTimeout(t),s.exec(...e);const r=s.ret;return s.reset(),r})(i);break;case e.WRITE_FILE:p=(({path:e,data:t})=>(s.FS.writeFile(e,t),!0))(i);break;case e.READ_FILE:p=(({path:e,encoding:t})=>s.FS.readFile(e,{encoding:t}))(i);break;case e.DELETE_FILE:p=(({path:e})=>(s.FS.unlink(e),!0))(i);break;case e.RENAME:p=(({oldPath:e,newPath:t})=>(s.FS.rename(e,t),!0))(i);break;case e.CREATE_DIR:p=(({path:e})=>(s.FS.mkdir(e),!0))(i);break;case e.LIST_DIR:p=(({path:e})=>{const t=s.FS.readdir(e),r=[];for(const a of t){const t=s.FS.stat(`${e}/${a}`),o=s.FS.isDir(t.mode);r.push({name:a,isDir:o})}return r})(i);break;case e.DELETE_DIR:p=(({path:e})=>(s.FS.rmdir(e),!0))(i);break;case e.MOUNT:p=(({fsType:e,options:t,mountPoint:r})=>{let a=e,o=s.FS.filesystems[a];return!!o&&(s.FS.mount(o,t,r),!0)})(i);break;case e.UNMOUNT:p=(({mountPoint:e})=>(s.FS.unmount(e),!0))(i);break;default:throw t}}catch(t){return void self.postMessage({id:n,type:e.ERROR,data:t.toString()})}p instanceof Uint8Array&&c.push(p.buffer),self.postMessage({id:n,type:E,data:p},c)}})(),{}})()));
|
||||||
|
//# sourceMappingURL=814.ffmpeg.js.map
|
1
static/ffmpeg/814.ffmpeg.js.map
Normal file
1
static/ffmpeg/814.ffmpeg.js.map
Normal file
File diff suppressed because one or more lines are too long
21
static/ffmpeg/ffmpeg-core.js
Normal file
21
static/ffmpeg/ffmpeg-core.js
Normal file
File diff suppressed because one or more lines are too long
BIN
static/ffmpeg/ffmpeg-core.wasm
Executable file
BIN
static/ffmpeg/ffmpeg-core.wasm
Executable file
Binary file not shown.
1
static/ffmpeg/ffmpeg-util.js
Normal file
1
static/ffmpeg/ffmpeg-util.js
Normal file
@ -0,0 +1 @@
|
|||||||
|
!function(e,t){"object"==typeof exports&&"object"==typeof module?module.exports=t():"function"==typeof define&&define.amd?define([],t):"object"==typeof exports?exports.FFmpegUtil=t():e.FFmpegUtil=t()}(self,(()=>(()=>{"use strict";var e={591:(e,t)=>{Object.defineProperty(t,"__esModule",{value:!0}),t.HeaderContentLength=void 0,t.HeaderContentLength="Content-Length"},431:(e,t)=>{Object.defineProperty(t,"__esModule",{value:!0}),t.ERROR_INCOMPLETED_DOWNLOAD=t.ERROR_RESPONSE_BODY_READER=void 0,t.ERROR_RESPONSE_BODY_READER=new Error("failed to get response body reader"),t.ERROR_INCOMPLETED_DOWNLOAD=new Error("failed to complete download")},915:function(e,t,o){var r=this&&this.__awaiter||function(e,t,o,r){return new(o||(o=Promise))((function(n,i){function d(e){try{l(r.next(e))}catch(e){i(e)}}function a(e){try{l(r.throw(e))}catch(e){i(e)}}function l(e){var t;e.done?n(e.value):(t=e.value,t instanceof o?t:new o((function(e){e(t)}))).then(d,a)}l((r=r.apply(e,t||[])).next())}))};Object.defineProperty(t,"__esModule",{value:!0}),t.toBlobURL=t.downloadWithProgress=t.importScript=t.fetchFile=void 0;const n=o(431),i=o(591);t.fetchFile=e=>r(void 0,void 0,void 0,(function*(){let t;if("string"==typeof e)t=/data:_data\/([a-zA-Z]*);base64,([^"]*)/.test(e)?atob(e.split(",")[1]).split("").map((e=>e.charCodeAt(0))):yield(yield fetch(e)).arrayBuffer();else if(e instanceof URL)t=yield(yield fetch(e)).arrayBuffer();else{if(!(e instanceof File||e instanceof Blob))return new Uint8Array;t=yield(o=e,new Promise(((e,t)=>{const r=new FileReader;r.onload=()=>{const{result:t}=r;t instanceof ArrayBuffer?e(new Uint8Array(t)):e(new Uint8Array)},r.onerror=e=>{var o,r;t(Error(`File could not be read! Code=${(null===(r=null===(o=null==e?void 0:e.target)||void 0===o?void 0:o.error)||void 0===r?void 0:r.code)||-1}`))},r.readAsArrayBuffer(o)})))}var o;return new Uint8Array(t)})),t.importScript=e=>r(void 0,void 0,void 0,(function*(){return new Promise((t=>{const o=document.createElement("script"),r=()=>{o.removeEventListener("load",r),t()};o.src=e,o.type="text/javascript",o.addEventListener("load",r),document.getElementsByTagName("head")[0].appendChild(o)}))})),t.downloadWithProgress=(e,t)=>r(void 0,void 0,void 0,(function*(){var o;const r=yield fetch(e);let d;try{const a=parseInt(r.headers.get(i.HeaderContentLength)||"-1"),l=null===(o=r.body)||void 0===o?void 0:o.getReader();if(!l)throw n.ERROR_RESPONSE_BODY_READER;const c=[];let s=0;for(;;){const{done:o,value:r}=yield l.read(),i=r?r.length:0;if(o){if(-1!=a&&a!==s)throw n.ERROR_INCOMPLETED_DOWNLOAD;t&&t({url:e,total:a,received:s,delta:i,done:o});break}c.push(r),s+=i,t&&t({url:e,total:a,received:s,delta:i,done:o})}const f=new Uint8Array(s);let u=0;for(const e of c)f.set(e,u),u+=e.length;d=f.buffer}catch(o){console.log("failed to send download progress event: ",o),d=yield r.arrayBuffer(),t&&t({url:e,total:d.byteLength,received:d.byteLength,delta:0,done:!0})}return d})),t.toBlobURL=(e,o,n=!1,i)=>r(void 0,void 0,void 0,(function*(){const r=n?yield(0,t.downloadWithProgress)(e,i):yield(yield fetch(e)).arrayBuffer(),d=new Blob([r],{type:o});return URL.createObjectURL(d)}))}},t={};return function o(r){var n=t[r];if(void 0!==n)return n.exports;var i=t[r]={exports:{}};return e[r].call(i.exports,i,i.exports,o),i.exports}(915)})()));
|
2
static/ffmpeg/ffmpeg.js
Normal file
2
static/ffmpeg/ffmpeg.js
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
!function(e,t){"object"==typeof exports&&"object"==typeof module?module.exports=t():"function"==typeof define&&define.amd?define([],t):"object"==typeof exports?exports.FFmpegWASM=t():e.FFmpegWASM=t()}(self,(()=>(()=>{"use strict";var e={m:{},d:(t,s)=>{for(var r in s)e.o(s,r)&&!e.o(t,r)&&Object.defineProperty(t,r,{enumerable:!0,get:s[r]})},u:e=>e+".ffmpeg.js"};e.g=function(){if("object"==typeof globalThis)return globalThis;try{return this||new Function("return this")()}catch(e){if("object"==typeof window)return window}}(),e.o=(e,t)=>Object.prototype.hasOwnProperty.call(e,t),e.r=e=>{"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},(()=>{var t;e.g.importScripts&&(t=e.g.location+"");var s=e.g.document;if(!t&&s&&(s.currentScript&&(t=s.currentScript.src),!t)){var r=s.getElementsByTagName("script");if(r.length)for(var a=r.length-1;a>-1&&!t;)t=r[a--].src}if(!t)throw new Error("Automatic publicPath is not supported in this browser");t=t.replace(/#.*$/,"").replace(/\?.*$/,"").replace(/\/[^\/]+$/,"/"),e.p=t})(),e.b=document.baseURI||self.location.href;var t,s={};e.r(s),e.d(s,{FFmpeg:()=>i}),function(e){e.LOAD="LOAD",e.EXEC="EXEC",e.WRITE_FILE="WRITE_FILE",e.READ_FILE="READ_FILE",e.DELETE_FILE="DELETE_FILE",e.RENAME="RENAME",e.CREATE_DIR="CREATE_DIR",e.LIST_DIR="LIST_DIR",e.DELETE_DIR="DELETE_DIR",e.ERROR="ERROR",e.DOWNLOAD="DOWNLOAD",e.PROGRESS="PROGRESS",e.LOG="LOG",e.MOUNT="MOUNT",e.UNMOUNT="UNMOUNT"}(t||(t={}));const r=(()=>{let e=0;return()=>e++})(),a=(new Error("unknown message type"),new Error("ffmpeg is not loaded, call `await ffmpeg.load()` first")),o=new Error("called FFmpeg.terminate()");new Error("failed to import ffmpeg-core.js");class i{#e=null;#t={};#s={};#r=[];#a=[];loaded=!1;#o=()=>{this.#e&&(this.#e.onmessage=({data:{id:e,type:s,data:r}})=>{switch(s){case t.LOAD:this.loaded=!0,this.#t[e](r);break;case t.MOUNT:case t.UNMOUNT:case t.EXEC:case t.WRITE_FILE:case t.READ_FILE:case t.DELETE_FILE:case t.RENAME:case t.CREATE_DIR:case t.LIST_DIR:case t.DELETE_DIR:this.#t[e](r);break;case t.LOG:this.#r.forEach((e=>e(r)));break;case t.PROGRESS:this.#a.forEach((e=>e(r)));break;case t.ERROR:this.#s[e](r)}delete this.#t[e],delete this.#s[e]})};#i=({type:e,data:t},s=[],o)=>this.#e?new Promise(((a,i)=>{const n=r();this.#e&&this.#e.postMessage({id:n,type:e,data:t},s),this.#t[n]=a,this.#s[n]=i,o?.addEventListener("abort",(()=>{i(new DOMException(`Message # ${n} was aborted`,"AbortError"))}),{once:!0})})):Promise.reject(a);on(e,t){"log"===e?this.#r.push(t):"progress"===e&&this.#a.push(t)}off(e,t){"log"===e?this.#r=this.#r.filter((e=>e!==t)):"progress"===e&&(this.#a=this.#a.filter((e=>e!==t)))}load=(s={},{signal:r}={})=>(this.#e||(this.#e=new Worker(new URL(e.p+e.u(814),e.b),{type:void 0}),this.#o()),this.#i({type:t.LOAD,data:s},void 0,r));exec=(e,s=-1,{signal:r}={})=>this.#i({type:t.EXEC,data:{args:e,timeout:s}},void 0,r);terminate=()=>{const e=Object.keys(this.#s);for(const t of e)this.#s[t](o),delete this.#s[t],delete this.#t[t];this.#e&&(this.#e.terminate(),this.#e=null,this.loaded=!1)};writeFile=(e,s,{signal:r}={})=>{const a=[];return s instanceof Uint8Array&&a.push(s.buffer),this.#i({type:t.WRITE_FILE,data:{path:e,data:s}},a,r)};mount=(e,s,r)=>this.#i({type:t.MOUNT,data:{fsType:e,options:s,mountPoint:r}},[]);unmount=e=>this.#i({type:t.UNMOUNT,data:{mountPoint:e}},[]);readFile=(e,s="binary",{signal:r}={})=>this.#i({type:t.READ_FILE,data:{path:e,encoding:s}},void 0,r);deleteFile=(e,{signal:s}={})=>this.#i({type:t.DELETE_FILE,data:{path:e}},void 0,s);rename=(e,s,{signal:r}={})=>this.#i({type:t.RENAME,data:{oldPath:e,newPath:s}},void 0,r);createDir=(e,{signal:s}={})=>this.#i({type:t.CREATE_DIR,data:{path:e}},void 0,s);listDir=(e,{signal:s}={})=>this.#i({type:t.LIST_DIR,data:{path:e}},void 0,s);deleteDir=(e,{signal:s}={})=>this.#i({type:t.DELETE_DIR,data:{path:e}},void 0,s)}return s})()));
|
||||||
|
//# sourceMappingURL=ffmpeg.js.map
|
1
static/ffmpeg/ffmpeg.js.map
Normal file
1
static/ffmpeg/ffmpeg.js.map
Normal file
File diff suppressed because one or more lines are too long
1
static/hls.min.js
vendored
1
static/hls.min.js
vendored
File diff suppressed because one or more lines are too long
@ -1,110 +0,0 @@
|
|||||||
// @license http://www.gnu.org/licenses/agpl-3.0.html AGPL-3.0
|
|
||||||
(function () {
|
|
||||||
if (Hls.isSupported()) {
|
|
||||||
var videoSources = document.querySelectorAll("video source[type='application/vnd.apple.mpegurl']");
|
|
||||||
videoSources.forEach(function (source) {
|
|
||||||
var playlist = source.src;
|
|
||||||
|
|
||||||
var oldVideo = source.parentNode;
|
|
||||||
var autoplay = oldVideo.classList.contains("hls_autoplay");
|
|
||||||
|
|
||||||
// If HLS is supported natively then don't use hls.js
|
|
||||||
if (oldVideo.canPlayType(source.type) === "probably") {
|
|
||||||
if (autoplay) {
|
|
||||||
oldVideo.play();
|
|
||||||
}
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Replace video with copy that will have all "source" elements removed
|
|
||||||
var newVideo = oldVideo.cloneNode(true);
|
|
||||||
var allSources = newVideo.querySelectorAll("source");
|
|
||||||
allSources.forEach(function (source) {
|
|
||||||
source.remove();
|
|
||||||
});
|
|
||||||
|
|
||||||
// Empty source to enable play event
|
|
||||||
newVideo.src = "about:blank";
|
|
||||||
|
|
||||||
oldVideo.parentNode.replaceChild(newVideo, oldVideo);
|
|
||||||
|
|
||||||
function initializeHls() {
|
|
||||||
newVideo.removeEventListener('play', initializeHls);
|
|
||||||
var hls = new Hls({ autoStartLoad: false });
|
|
||||||
hls.loadSource(playlist);
|
|
||||||
hls.attachMedia(newVideo);
|
|
||||||
hls.on(Hls.Events.MANIFEST_PARSED, function () {
|
|
||||||
hls.loadLevel = hls.levels.length - 1;
|
|
||||||
var availableLevels = hls.levels.map(function(level) {
|
|
||||||
return {
|
|
||||||
height: level.height,
|
|
||||||
width: level.width,
|
|
||||||
bitrate: level.bitrate,
|
|
||||||
};
|
|
||||||
});
|
|
||||||
|
|
||||||
addQualitySelector(newVideo, hls, availableLevels);
|
|
||||||
|
|
||||||
hls.startLoad();
|
|
||||||
newVideo.play();
|
|
||||||
});
|
|
||||||
|
|
||||||
hls.on(Hls.Events.ERROR, function (event, data) {
|
|
||||||
var errorType = data.type;
|
|
||||||
var errorFatal = data.fatal;
|
|
||||||
if (errorFatal) {
|
|
||||||
switch (errorType) {
|
|
||||||
case Hls.ErrorType.NETWORK_ERROR:
|
|
||||||
hls.startLoad();
|
|
||||||
break;
|
|
||||||
case Hls.ErrorType.MEDIA_ERROR:
|
|
||||||
hls.recoverMediaError();
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
hls.destroy();
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
console.error("HLS error", data);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
function addQualitySelector(videoElement, hlsInstance, availableLevels) {
|
|
||||||
var qualitySelector = document.createElement('select');
|
|
||||||
qualitySelector.classList.add('quality-selector');
|
|
||||||
var last = availableLevels.length - 1;
|
|
||||||
availableLevels.forEach(function (level, index) {
|
|
||||||
var option = document.createElement('option');
|
|
||||||
option.value = index.toString();
|
|
||||||
var bitrate = (level.bitrate / 1_000).toFixed(0);
|
|
||||||
option.text = level.height + 'p (' + bitrate + ' kbps)';
|
|
||||||
if (index === last) {
|
|
||||||
option.selected = "selected";
|
|
||||||
}
|
|
||||||
qualitySelector.appendChild(option);
|
|
||||||
});
|
|
||||||
qualitySelector.selectedIndex = availableLevels.length - 1;
|
|
||||||
qualitySelector.addEventListener('change', function () {
|
|
||||||
var selectedIndex = qualitySelector.selectedIndex;
|
|
||||||
hlsInstance.nextLevel = selectedIndex;
|
|
||||||
hlsInstance.startLoad();
|
|
||||||
});
|
|
||||||
|
|
||||||
videoElement.parentNode.appendChild(qualitySelector);
|
|
||||||
}
|
|
||||||
|
|
||||||
newVideo.addEventListener('play', initializeHls);
|
|
||||||
|
|
||||||
if (autoplay) {
|
|
||||||
newVideo.play();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
var videos = document.querySelectorAll("video.hls_autoplay");
|
|
||||||
videos.forEach(function (video) {
|
|
||||||
video.setAttribute("autoplay", "");
|
|
||||||
});
|
|
||||||
}
|
|
||||||
})();
|
|
||||||
// @license-end
|
|
@ -52,6 +52,7 @@
|
|||||||
--visited: #aaa;
|
--visited: #aaa;
|
||||||
--shadow: 0 1px 3px rgba(0, 0, 0, 0.5);
|
--shadow: 0 1px 3px rgba(0, 0, 0, 0.5);
|
||||||
--popup: #b80a27;
|
--popup: #b80a27;
|
||||||
|
--spoiler: #ddd;
|
||||||
|
|
||||||
/* Hint color theme to browser for scrollbar */
|
/* Hint color theme to browser for scrollbar */
|
||||||
color-scheme: dark;
|
color-scheme: dark;
|
||||||
@ -71,6 +72,7 @@
|
|||||||
--highlighted: white;
|
--highlighted: white;
|
||||||
--visited: #555;
|
--visited: #555;
|
||||||
--shadow: 0 1px 3px rgba(0, 0, 0, 0.1);
|
--shadow: 0 1px 3px rgba(0, 0, 0, 0.1);
|
||||||
|
--spoiler: #0f0f0f;
|
||||||
|
|
||||||
/* Hint color theme to browser for scrollbar */
|
/* Hint color theme to browser for scrollbar */
|
||||||
color-scheme: light;
|
color-scheme: light;
|
||||||
@ -929,6 +931,15 @@ a.search_subreddit:hover {
|
|||||||
font-weight: bold;
|
font-weight: bold;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.spoiler {
|
||||||
|
color: var(--spoiler);
|
||||||
|
margin-left: 5px;
|
||||||
|
border: 1px solid var(--spoiler);
|
||||||
|
padding: 3px;
|
||||||
|
font-size: 12px;
|
||||||
|
border-radius: 5px;
|
||||||
|
}
|
||||||
|
|
||||||
.post_media_content, .post .__NoScript_PlaceHolder__, .gallery {
|
.post_media_content, .post .__NoScript_PlaceHolder__, .gallery {
|
||||||
max-width: calc(100% - 40px);
|
max-width: calc(100% - 40px);
|
||||||
grid-area: post_media;
|
grid-area: post_media;
|
||||||
@ -1391,6 +1402,11 @@ summary.comment_data {
|
|||||||
font-size: 14px;
|
font-size: 14px;
|
||||||
margin-top: 10px;
|
margin-top: 10px;
|
||||||
opacity: 0.75;
|
opacity: 0.75;
|
||||||
|
background: var(--post);
|
||||||
|
border-radius: 5px;
|
||||||
|
padding: 10px 5px 10px 10px;
|
||||||
|
margin-top: 10px;
|
||||||
|
margin-bottom: 20px;
|
||||||
}
|
}
|
||||||
|
|
||||||
#settings_note a {
|
#settings_note a {
|
||||||
@ -1634,6 +1650,14 @@ td, th {
|
|||||||
color: var(--accent);
|
color: var(--accent);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.nsfw-tag {
|
||||||
|
color: var(--nsfw);
|
||||||
|
border: 2px solid var(--nsfw);
|
||||||
|
padding: 3px;
|
||||||
|
border-radius: 5px;
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
|
|
||||||
/* Mobile */
|
/* Mobile */
|
||||||
|
|
||||||
@media screen and (max-width: 800px) {
|
@media screen and (max-width: 800px) {
|
||||||
@ -1760,18 +1784,23 @@ td, th {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.quality-selector {
|
.video-options {
|
||||||
border: 2px var(--outside) solid;
|
border: 2px var(--outside) solid;
|
||||||
margin-top: 8px;
|
margin-top: 8px;
|
||||||
float: right;
|
float: right;
|
||||||
|
border-radius: 5px;
|
||||||
|
height: 35px;
|
||||||
|
height: 35px;
|
||||||
|
margin: 2px;
|
||||||
|
box-sizing: border-box;
|
||||||
}
|
}
|
||||||
|
|
||||||
.quality-selector option {
|
.video-options option {
|
||||||
background-color: var(--background);
|
background-color: var(--background);
|
||||||
color: var(--text);
|
color: var(--text);
|
||||||
}
|
}
|
||||||
|
|
||||||
.quality-selector option:hover {
|
.video-options option:hover {
|
||||||
background-color: var(--accent);
|
background-color: var(--accent);
|
||||||
color: var(--text);
|
color: var(--text);
|
||||||
}
|
}
|
||||||
@ -1787,4 +1816,21 @@ td, th {
|
|||||||
|
|
||||||
.mascot > img {
|
.mascot > img {
|
||||||
max-width: 20em;
|
max-width: 20em;
|
||||||
|
}
|
||||||
|
.download {
|
||||||
|
padding-left: 8px;
|
||||||
|
padding-right: 8px;
|
||||||
|
font-size: 20px;
|
||||||
|
font-weight: 900;
|
||||||
|
color: var(--accent);
|
||||||
|
background-color: var(--outside);
|
||||||
|
}
|
||||||
|
|
||||||
|
.download:hover {
|
||||||
|
background-color: var(--foreground);
|
||||||
|
/*color: var(--);*/
|
||||||
|
}
|
||||||
|
|
||||||
|
.download:active {
|
||||||
|
background-color: var(--background);
|
||||||
}
|
}
|
228
static/videoUtils.js
Normal file
228
static/videoUtils.js
Normal file
@ -0,0 +1,228 @@
|
|||||||
|
// @license http://www.gnu.org/licenses/agpl-3.0.html AGPL-3.0
|
||||||
|
let ffmpeg = null;
|
||||||
|
(function () {
|
||||||
|
if (Hls.isSupported()) {
|
||||||
|
|
||||||
|
var downloadsEnabled = document.cookie.split("; ").find((row) => row.startsWith("ffmpeg_video_downloads="))?.split("=")[1] == "on";
|
||||||
|
|
||||||
|
var videoSources = document.querySelectorAll("video source[type='application/vnd.apple.mpegurl']");
|
||||||
|
videoSources.forEach(function (source) {
|
||||||
|
var playlist = source.src;
|
||||||
|
|
||||||
|
var oldVideo = source.parentNode;
|
||||||
|
var autoplay = oldVideo.classList.contains("hls_autoplay");
|
||||||
|
|
||||||
|
// If HLS is supported natively then don't use hls.js
|
||||||
|
if (oldVideo.canPlayType(source.type) === "probably") {
|
||||||
|
if (autoplay) {
|
||||||
|
oldVideo.play();
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Replace video with copy that will have all "source" elements removed
|
||||||
|
var newVideo = oldVideo.cloneNode(true);
|
||||||
|
var allSources = newVideo.querySelectorAll("source");
|
||||||
|
allSources.forEach(function (source) {
|
||||||
|
source.remove();
|
||||||
|
});
|
||||||
|
|
||||||
|
// Empty source to enable play event
|
||||||
|
newVideo.src = "about:blank";
|
||||||
|
|
||||||
|
oldVideo.parentNode.replaceChild(newVideo, oldVideo);
|
||||||
|
|
||||||
|
function initializeHls() {
|
||||||
|
newVideo.removeEventListener('play', initializeHls);
|
||||||
|
var hls = new Hls({ autoStartLoad: false });
|
||||||
|
hls.loadSource(playlist);
|
||||||
|
hls.attachMedia(newVideo);
|
||||||
|
hls.on(Hls.Events.MANIFEST_PARSED, function () {
|
||||||
|
hls.loadLevel = hls.levels.length - 1;
|
||||||
|
var availableLevels = hls.levels.map(function(level) {
|
||||||
|
return {
|
||||||
|
height: level.height,
|
||||||
|
width: level.width,
|
||||||
|
bitrate: level.bitrate,
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
addQualitySelector(newVideo, hls, availableLevels);
|
||||||
|
if (downloadsEnabled){ addVideoDownload(newVideo, hls); }
|
||||||
|
hls.startLoad();
|
||||||
|
newVideo.play();
|
||||||
|
});
|
||||||
|
|
||||||
|
hls.on(Hls.Events.ERROR, function (event, data) {
|
||||||
|
var errorType = data.type;
|
||||||
|
var errorFatal = data.fatal;
|
||||||
|
if (errorFatal) {
|
||||||
|
switch (errorType) {
|
||||||
|
case Hls.ErrorType.NETWORK_ERROR:
|
||||||
|
hls.startLoad();
|
||||||
|
break;
|
||||||
|
case Hls.ErrorType.MEDIA_ERROR:
|
||||||
|
hls.recoverMediaError();
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
hls.destroy();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
console.error("HLS error", data);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (downloadsEnabled){
|
||||||
|
const { fetchFile } = FFmpegUtil;
|
||||||
|
const { FFmpeg } = FFmpegWASM;
|
||||||
|
|
||||||
|
function addVideoDownload(videoElement, hlsInstance) {
|
||||||
|
var mediaStream = [];
|
||||||
|
var downloadButton = document.createElement('button');
|
||||||
|
downloadButton.classList.add('video-options','download');
|
||||||
|
downloadButton.innerText = "⏳"
|
||||||
|
const mergeStreams = async () => {
|
||||||
|
if (ffmpeg === null) {
|
||||||
|
ffmpeg = new FFmpeg();
|
||||||
|
await ffmpeg.load({
|
||||||
|
coreURL: "/ffmpeg/ffmpeg-core.js",
|
||||||
|
});
|
||||||
|
ffmpeg.on("log", ({ message }) => {
|
||||||
|
console.log(message); // This is quite noisy but i will include it
|
||||||
|
})
|
||||||
|
ffmpeg.on("progress", ({ progress, time }) => { // Progress TODO: show progress ring around button not just ⏳
|
||||||
|
// console.log("ffmpeg prog:",progress * 100)
|
||||||
|
});
|
||||||
|
}
|
||||||
|
// Combine Video Audio Streams
|
||||||
|
await ffmpeg.writeFile("video", await fetchFile(concatBlob(mediaStream['video'])));
|
||||||
|
await ffmpeg.writeFile("audio", await fetchFile(concatBlob(mediaStream['audio'])));
|
||||||
|
console.time('ffmpeg-exec');
|
||||||
|
await ffmpeg.exec(['-i', "video", '-i', "audio",'-c:v', "copy", '-c:a', "aac", 'output.mp4']);
|
||||||
|
console.timeEnd('ffmpeg-exec')
|
||||||
|
|
||||||
|
// Save
|
||||||
|
const toSlug = (str) => {
|
||||||
|
return str
|
||||||
|
.normalize('NFD')
|
||||||
|
.replace(/[\u0300-\u036f]/g, '')
|
||||||
|
.replace(/[\W_]+/g, '-')
|
||||||
|
.toLowerCase()
|
||||||
|
.replace(/^-+|-+$/g, '');
|
||||||
|
}
|
||||||
|
|
||||||
|
var filename = toSlug(videoElement.parentNode.parentNode.id || document.title)
|
||||||
|
const data = await ffmpeg.readFile('output.mp4');
|
||||||
|
saveAs(new Blob([data.buffer]),filename);
|
||||||
|
return
|
||||||
|
}
|
||||||
|
function saveAs(blob, filename) { // Yeah ok...
|
||||||
|
var url = URL.createObjectURL(blob);
|
||||||
|
var a = document.createElement("a");
|
||||||
|
document.body.appendChild(a);
|
||||||
|
a.style = "display: none";
|
||||||
|
a.href = url;
|
||||||
|
a.download = filename;
|
||||||
|
a.click();
|
||||||
|
window.URL.revokeObjectURL(url);
|
||||||
|
}
|
||||||
|
function concatBlob(inputArray) {
|
||||||
|
var totalLength = inputArray.reduce(function (prev, cur) {
|
||||||
|
return prev + cur.length
|
||||||
|
}, 0);
|
||||||
|
var result = new Uint8Array(totalLength);
|
||||||
|
var offset = 0;
|
||||||
|
inputArray.forEach(function (element) {
|
||||||
|
result.set(element, offset);
|
||||||
|
offset += element.length;
|
||||||
|
});
|
||||||
|
return new Blob([result], {
|
||||||
|
type: 'application/octet-stream'
|
||||||
|
});
|
||||||
|
}
|
||||||
|
function getStreams() {
|
||||||
|
var video = document.createElement('video');
|
||||||
|
video.autoplay = true;
|
||||||
|
var dataStreams = {
|
||||||
|
'video': [],
|
||||||
|
'audio': []
|
||||||
|
};
|
||||||
|
mediaStream = dataStreams; // Update stream
|
||||||
|
|
||||||
|
hlsInstance.on(Hls.Events.BUFFER_APPENDING, function (event, data) {
|
||||||
|
dataStreams[data.type].push(data.data);
|
||||||
|
});
|
||||||
|
var isDownloading = false
|
||||||
|
function startDownload() {
|
||||||
|
if (!isDownloading) { isDownloading = true } else { return }
|
||||||
|
downloadButton.innerText = "⏳"
|
||||||
|
mergeStreams()
|
||||||
|
.then(_ => {
|
||||||
|
isDownloading = false
|
||||||
|
downloadButton.innerText = "⭳"
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function waitForLoad() {
|
||||||
|
const poll = resolve => {
|
||||||
|
if(hlsInstance._media.buffered.length === 1 &&
|
||||||
|
hlsInstance._media.buffered.start(0) === 0 &&
|
||||||
|
hlsInstance._media.buffered.end(0) === hlsInstance._media.duration)
|
||||||
|
resolve();
|
||||||
|
else setTimeout(_ => poll(resolve), 400);
|
||||||
|
}
|
||||||
|
return new Promise(poll);
|
||||||
|
}
|
||||||
|
|
||||||
|
waitForLoad(_ => flag === true)
|
||||||
|
.then(_ => {
|
||||||
|
downloadButton.innerText = "⭳"
|
||||||
|
downloadButton.addEventListener('click', startDownload);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
videoElement.parentNode.appendChild(downloadButton);
|
||||||
|
getStreams()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function addQualitySelector(videoElement, hlsInstance, availableLevels) {
|
||||||
|
var qualitySelector = document.createElement('select');
|
||||||
|
qualitySelector.classList.add('video-options');
|
||||||
|
var last = availableLevels.length - 1;
|
||||||
|
availableLevels.forEach(function (level, index) {
|
||||||
|
var option = document.createElement('option');
|
||||||
|
option.value = index.toString();
|
||||||
|
var bitrate = (level.bitrate / 1_000).toFixed(0);
|
||||||
|
option.text = level.height + 'p (' + bitrate + ' kbps)';
|
||||||
|
if (index === last) {
|
||||||
|
option.selected = "selected";
|
||||||
|
}
|
||||||
|
qualitySelector.appendChild(option);
|
||||||
|
});
|
||||||
|
qualitySelector.selectedIndex = availableLevels.length - 1;
|
||||||
|
qualitySelector.addEventListener('change', function () {
|
||||||
|
var selectedIndex = qualitySelector.selectedIndex;
|
||||||
|
hlsInstance.nextLevel = selectedIndex;
|
||||||
|
hlsInstance.startLoad();
|
||||||
|
});
|
||||||
|
|
||||||
|
videoElement.parentNode.appendChild(qualitySelector);
|
||||||
|
}
|
||||||
|
|
||||||
|
newVideo.addEventListener('play', initializeHls);
|
||||||
|
|
||||||
|
if (autoplay) {
|
||||||
|
newVideo.play();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
var videos = document.querySelectorAll("video.hls_autoplay");
|
||||||
|
videos.forEach(function (video) {
|
||||||
|
video.setAttribute("autoplay", "");
|
||||||
|
});
|
||||||
|
}
|
||||||
|
})();
|
||||||
|
// @license-end
|
@ -2,10 +2,14 @@
|
|||||||
{% block title %}Error: {{ msg }}{% endblock %}
|
{% block title %}Error: {{ msg }}{% endblock %}
|
||||||
{% block sortstyle %}{% endblock %}
|
{% block sortstyle %}{% endblock %}
|
||||||
{% block content %}
|
{% block content %}
|
||||||
<div id="error">
|
<div id="error">
|
||||||
<h1>{{ msg }}</h1>
|
<h1>{{ msg }}</h1>
|
||||||
<h3><a href="https://www.redditstatus.com/">Reddit Status</a></h3>
|
<h3><a href="https://www.redditstatus.com/">Reddit Status</a></h3>
|
||||||
<br />
|
<br />
|
||||||
<h3>Head back <a href="/">home</a>?</h3>
|
<h3>Expected something to work? <a
|
||||||
</div>
|
href="https://github.com/redlib-org/redlib/issues/new?assignees=&labels=bug&projects=&template=bug_report.md&title=%F0%9F%90%9B+Bug+Report%3A+{{ msg }}">Report
|
||||||
|
an issue</a></h3>
|
||||||
|
<br />
|
||||||
|
<h3>Head back <a href="/">home</a>?</h3>
|
||||||
|
</div>
|
||||||
{% endblock %}
|
{% endblock %}
|
@ -6,11 +6,11 @@
|
|||||||
<h1>
|
<h1>
|
||||||
😱
|
😱
|
||||||
{% if res_type == crate::utils::ResourceType::Subreddit %}
|
{% if res_type == crate::utils::ResourceType::Subreddit %}
|
||||||
r/{{ res }} is a NSFW community!
|
r/{{ res }} is a <b class="nsfw-tag">NSFW</b> community!
|
||||||
{% else if res_type == crate::utils::ResourceType::User %}
|
{% else if res_type == crate::utils::ResourceType::User %}
|
||||||
u/{{ res }}'s content is NSFW!
|
u/{{ res }}'s content is <b class="nsfw-tag">NSFW</b>!
|
||||||
{% else if res_type == crate::utils::ResourceType::Post %}
|
{% else if res_type == crate::utils::ResourceType::Post %}
|
||||||
This post is NSFW!
|
This post is <b class="nsfw-tag">NSFW</b>!
|
||||||
{% endif %}
|
{% endif %}
|
||||||
</h1>
|
</h1>
|
||||||
<br />
|
<br />
|
||||||
@ -20,6 +20,7 @@
|
|||||||
This instance of Redlib is SFW-only.</p>
|
This instance of Redlib is SFW-only.</p>
|
||||||
{% else %}
|
{% else %}
|
||||||
Enable "Show NSFW posts" in <a href="/settings">settings</a> to view this {% if res_type == crate::utils::ResourceType::Subreddit %}subreddit{% else if res_type == crate::utils::ResourceType::User %}user's posts or comments{% else if res_type == crate::utils::ResourceType::Post %}post{% endif %}. <br>
|
Enable "Show NSFW posts" in <a href="/settings">settings</a> to view this {% if res_type == crate::utils::ResourceType::Subreddit %}subreddit{% else if res_type == crate::utils::ResourceType::User %}user's posts or comments{% else if res_type == crate::utils::ResourceType::Post %}post{% endif %}. <br>
|
||||||
|
<div>Alternatively <a href="/settings/update/?show_nsfw=on&redirect={{self.url[1..self.url.len()]}}">enable NSFW posts</a> now and view this {% if res_type == crate::utils::ResourceType::Subreddit %}subreddit{% else if res_type == crate::utils::ResourceType::User %}profile{% else if res_type == crate::utils::ResourceType::Post %}post{% endif %} immediately</div>
|
||||||
{% if res_type == crate::utils::ResourceType::Post %} You can also temporarily bypass this gate and view the post by clicking on this <a href="{{url}}&bypass_nsfw_landing">link</a>.{% endif %}
|
{% if res_type == crate::utils::ResourceType::Post %} You can also temporarily bypass this gate and view the post by clicking on this <a href="{{url}}&bypass_nsfw_landing">link</a>.{% endif %}
|
||||||
{% endif %}
|
{% endif %}
|
||||||
</p>
|
</p>
|
||||||
|
@ -97,9 +97,13 @@
|
|||||||
{% endif %}
|
{% endif %}
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{% if prefs.use_hls == "on" %}
|
{% if prefs.ffmpeg_video_downloads == "on" %}
|
||||||
|
<script src="/ffmpeg/ffmpeg.js"></script>
|
||||||
|
<script src="/ffmpeg/ffmpeg-util.js"></script>
|
||||||
|
{% endif %}
|
||||||
|
{% if prefs.use_hls == "on" || prefs.ffmpeg_video_downloads == "on" %}
|
||||||
<script src="/hls.min.js"></script>
|
<script src="/hls.min.js"></script>
|
||||||
<script src="/playHLSVideo.js"></script>
|
<script src="/videoUtils.js"></script>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
|
||||||
{% if params.typed != "sr_user" %}
|
{% if params.typed != "sr_user" %}
|
||||||
|
@ -60,6 +60,11 @@
|
|||||||
{% call utils::options(prefs.comment_sort, ["confidence", "top", "new", "controversial", "old"], "confidence") %}
|
{% call utils::options(prefs.comment_sort, ["confidence", "top", "new", "controversial", "old"], "confidence") %}
|
||||||
</select>
|
</select>
|
||||||
</div>
|
</div>
|
||||||
|
<div class="prefs-group">
|
||||||
|
<label for="blur_spoiler">Blur spoiler previews:</label>
|
||||||
|
<input type="hidden" value="off" name="blur_spoiler">
|
||||||
|
<input type="checkbox" name="blur_spoiler" id="blur_spoiler" {% if prefs.blur_spoiler == "on" %}checked{% endif %}>
|
||||||
|
</div>
|
||||||
{% if !crate::utils::sfw_only() %}
|
{% if !crate::utils::sfw_only() %}
|
||||||
<div class="prefs-group">
|
<div class="prefs-group">
|
||||||
<label for="show_nsfw">Show NSFW posts:</label>
|
<label for="show_nsfw">Show NSFW posts:</label>
|
||||||
@ -89,12 +94,24 @@
|
|||||||
</div>
|
</div>
|
||||||
<div class="prefs-group">
|
<div class="prefs-group">
|
||||||
<label for="use_hls">Use HLS for videos</label>
|
<label for="use_hls">Use HLS for videos</label>
|
||||||
|
{% if prefs.ffmpeg_video_downloads != "on" %}
|
||||||
<details id="feeds">
|
<details id="feeds">
|
||||||
<summary>Why?</summary>
|
<summary>Why?</summary>
|
||||||
<div id="feed_list" class="helper">Reddit videos require JavaScript (via HLS.js) to be enabled to be played with audio. Therefore, this toggle lets you either use Redlib JS-free or utilize this feature.</div>
|
<div id="feed_list" class="helper">Reddit videos require JavaScript (via HLS.js) to be enabled to be played with audio. Therefore, this toggle lets you either use Redlib JS-free or utilize this feature.</div>
|
||||||
</details>
|
</details>
|
||||||
|
{% endif %}
|
||||||
|
{% if prefs.ffmpeg_video_downloads == "on" %}<u>ⓘ HLS is required for downloads</u>{% endif %}
|
||||||
<input type="hidden" value="off" name="use_hls">
|
<input type="hidden" value="off" name="use_hls">
|
||||||
<input type="checkbox" name="use_hls" id="use_hls" {% if prefs.use_hls == "on" %}checked{% endif %}>
|
<input type="checkbox" name="use_hls" id="use_hls" {% if prefs.ffmpeg_video_downloads == "on" %}disabled{% endif %} {% if prefs.use_hls == "on" || prefs.ffmpeg_video_downloads == "on" %}checked{% endif %}>
|
||||||
|
</div>
|
||||||
|
<div class="prefs-group">
|
||||||
|
<label for="ffmpeg_video_downloads">Use FFmpeg to download videos</label>
|
||||||
|
<details id="feeds">
|
||||||
|
<summary>Why?</summary>
|
||||||
|
<div id="feed_list" class="helper">Downloading videos with audio requires ffmpeg (via ffmpeg.wasm) to be enabled to combine video and audio tracks. Therefore, this toggle lets you either use Redlib WebAssembly-free or utilize this feature. (videos will still play with audio)</div>
|
||||||
|
</details>
|
||||||
|
<input type="hidden" value="off" name="ffmpeg_video_downloads">
|
||||||
|
<input type="checkbox" name="ffmpeg_video_downloads" id="ffmpeg_video_downloads" {% if prefs.ffmpeg_video_downloads == "on" %}checked{% endif %}>
|
||||||
</div>
|
</div>
|
||||||
<div class="prefs-group">
|
<div class="prefs-group">
|
||||||
<label for="hide_hls_notification">Hide notification about possible HLS usage</label>
|
<label for="hide_hls_notification">Hide notification about possible HLS usage</label>
|
||||||
@ -120,6 +137,10 @@
|
|||||||
<input id="save" type="submit" value="Save">
|
<input id="save" type="submit" value="Save">
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
|
<div id="settings_note">
|
||||||
|
<p><b>Note:</b> settings and subscriptions are saved in browser cookies. Clearing your cookies will reset them.</p><br>
|
||||||
|
<p>You can restore your current settings and subscriptions after clearing your cookies using <a href="/settings/restore/?theme={{ prefs.theme }}&mascot={{ prefs.mascot }}&front_page={{ prefs.front_page }}&layout={{ prefs.layout }}&wide={{ prefs.wide }}&post_sort={{ prefs.post_sort }}&comment_sort={{ prefs.comment_sort }}&show_nsfw={{ prefs.show_nsfw }}&use_hls={{ prefs.use_hls }}&ffmpeg_video_downloads={{ prefs.ffmpeg_video_downloads }}&hide_hls_notification={{ prefs.hide_hls_notification }}&hide_awards={{ prefs.hide_awards }}&fixed_navbar={{ prefs.fixed_navbar }}&hide_sidebar_and_summary={{ prefs.hide_sidebar_and_summary}}&subscriptions={{ prefs.subscriptions.join("%2B") }}&filters={{ prefs.filters.join("%2B") }}">this link</a>.</p>
|
||||||
|
</div>
|
||||||
{% if prefs.subscriptions.len() > 0 %}
|
{% if prefs.subscriptions.len() > 0 %}
|
||||||
<div class="prefs" id="settings_subs">
|
<div class="prefs" id="settings_subs">
|
||||||
<legend>Subscribed Feeds</legend>
|
<legend>Subscribed Feeds</legend>
|
||||||
@ -150,11 +171,6 @@
|
|||||||
{% endfor %}
|
{% endfor %}
|
||||||
</div>
|
</div>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
|
||||||
<div id="settings_note">
|
|
||||||
<p><b>Note:</b> settings and subscriptions are saved in browser cookies. Clearing your cookies will reset them.</p><br>
|
|
||||||
<p>You can restore your current settings and subscriptions after clearing your cookies using <a href="/settings/restore/?theme={{ prefs.theme }}&mascot={{ prefs.mascot }}&front_page={{ prefs.front_page }}&layout={{ prefs.layout }}&wide={{ prefs.wide }}&post_sort={{ prefs.post_sort }}&comment_sort={{ prefs.comment_sort }}&show_nsfw={{ prefs.show_nsfw }}&use_hls={{ prefs.use_hls }}&hide_hls_notification={{ prefs.hide_hls_notification }}&hide_awards={{ prefs.hide_awards }}&fixed_navbar={{ prefs.fixed_navbar }}&hide_sidebar_and_summary={{ prefs.hide_sidebar_and_summary}}&subscriptions={{ prefs.subscriptions.join("%2B") }}&filters={{ prefs.filters.join("%2B") }}">this link</a>.</p>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
@ -64,9 +64,13 @@
|
|||||||
{% call utils::post_in_list(post) %}
|
{% call utils::post_in_list(post) %}
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
{% if prefs.use_hls == "on" %}
|
{% if prefs.ffmpeg_video_downloads == "on" %}
|
||||||
|
<script src="/ffmpeg/ffmpeg.js"></script>
|
||||||
|
<script src="/ffmpeg/ffmpeg-util.js"></script>
|
||||||
|
{% endif %}
|
||||||
|
{% if prefs.use_hls == "on" || prefs.ffmpeg_video_downloads == "on" %}
|
||||||
<script src="/hls.min.js"></script>
|
<script src="/hls.min.js"></script>
|
||||||
<script src="/playHLSVideo.js"></script>
|
<script src="/videoUtils.js"></script>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
</div>
|
</div>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
@ -71,9 +71,13 @@
|
|||||||
</div>
|
</div>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
{% if prefs.use_hls == "on" %}
|
{% if prefs.ffmpeg_video_downloads == "on" %}
|
||||||
|
<script src="/ffmpeg/ffmpeg.js"></script>
|
||||||
|
<script src="/ffmpeg/ffmpeg-util.js"></script>
|
||||||
|
{% endif %}
|
||||||
|
{% if prefs.use_hls == "on" || prefs.ffmpeg_video_downloads == "on" %}
|
||||||
<script src="/hls.min.js"></script>
|
<script src="/hls.min.js"></script>
|
||||||
<script src="/playHLSVideo.js"></script>
|
<script src="/videoUtils.js"></script>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
</div>
|
</div>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
@ -62,6 +62,7 @@
|
|||||||
{%- endmacro %}
|
{%- endmacro %}
|
||||||
|
|
||||||
{% macro post(post) -%}
|
{% macro post(post) -%}
|
||||||
|
{% set post_should_be_blurred = post.flags.spoiler && prefs.blur_spoiler=="on" -%}
|
||||||
<!-- POST CONTENT -->
|
<!-- POST CONTENT -->
|
||||||
<div class="post highlighted">
|
<div class="post highlighted">
|
||||||
<p class="post_header">
|
<p class="post_header">
|
||||||
@ -93,6 +94,7 @@
|
|||||||
style="color:{{ post.flair.foreground_color }}; background:{{ post.flair.background_color }};">{% call render_flair(post.flair.flair_parts) %}</a>
|
style="color:{{ post.flair.foreground_color }}; background:{{ post.flair.background_color }};">{% call render_flair(post.flair.flair_parts) %}</a>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{% if post.flags.nsfw %} <small class="nsfw">NSFW</small>{% endif %}
|
{% if post.flags.nsfw %} <small class="nsfw">NSFW</small>{% endif %}
|
||||||
|
{% if post.flags.spoiler %} <small class="spoiler">Spoiler</small>{% endif %}
|
||||||
</h1>
|
</h1>
|
||||||
|
|
||||||
<!-- POST MEDIA -->
|
<!-- POST MEDIA -->
|
||||||
@ -101,12 +103,13 @@
|
|||||||
<div class="post_media_content">
|
<div class="post_media_content">
|
||||||
<a href="{{ post.media.url }}" class="post_media_image" >
|
<a href="{{ post.media.url }}" class="post_media_image" >
|
||||||
{% if post.media.height == 0 || post.media.width == 0 %}
|
{% if post.media.height == 0 || post.media.width == 0 %}
|
||||||
<!-- i.redd.it images speical case -->
|
<!-- i.redd.it images special case -->
|
||||||
<img width="100%" height="100%" loading="lazy" alt="Post image" src="{{ post.media.url }}"/>
|
<img width="100%" height="100%" loading="lazy" alt="Post image" src="{{ post.media.url }}"{%if post_should_be_blurred %} class="post_nsfw_blur"{% endif %}/>
|
||||||
{% else %}
|
{% else %}
|
||||||
<svg
|
<svg
|
||||||
width="{{ post.media.width }}px"
|
width="{{ post.media.width }}px"
|
||||||
height="{{ post.media.height }}px"
|
height="{{ post.media.height }}px"
|
||||||
|
{%if post_should_be_blurred %}class="post_nsfw_blur"{% endif %}
|
||||||
xmlns="http://www.w3.org/2000/svg">
|
xmlns="http://www.w3.org/2000/svg">
|
||||||
<image width="100%" height="100%" href="{{ post.media.url }}"/>
|
<image width="100%" height="100%" href="{{ post.media.url }}"/>
|
||||||
<desc>
|
<desc>
|
||||||
@ -117,18 +120,22 @@
|
|||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
{% else if post.post_type == "video" || post.post_type == "gif" %}
|
{% else if post.post_type == "video" || post.post_type == "gif" %}
|
||||||
{% if prefs.use_hls == "on" && !post.media.alt_url.is_empty() %}
|
{% if prefs.ffmpeg_video_downloads == "on" %}
|
||||||
|
<script src="/ffmpeg/ffmpeg.js"></script>
|
||||||
|
<script src="/ffmpeg/ffmpeg-util.js"></script>
|
||||||
|
{% endif %}
|
||||||
|
{% if prefs.use_hls == "on" && !post.media.alt_url.is_empty() || prefs.ffmpeg_video_downloads == "on" && !post.media.alt_url.is_empty() %}
|
||||||
<script src="/hls.min.js"></script>
|
<script src="/hls.min.js"></script>
|
||||||
<div class="post_media_content">
|
<div class="post_media_content">
|
||||||
<video class="post_media_video short {% if prefs.autoplay_videos == "on" %}hls_autoplay{% endif %}" {% if post.media.width > 0 && post.media.height > 0 %}width="{{ post.media.width }}" height="{{ post.media.height }}"{% endif %} poster="{{ post.media.poster }}" preload="none" controls>
|
<video class="post_media_video short {% if prefs.autoplay_videos == "on" %}hls_autoplay{% endif %}{%if post_should_be_blurred %} post_nsfw_blur{% endif %}" {% if post.media.width > 0 && post.media.height > 0 %}width="{{ post.media.width }}" height="{{ post.media.height }}"{% endif %} poster="{{ post.media.poster }}" preload="none" controls>
|
||||||
<source src="{{ post.media.alt_url }}" type="application/vnd.apple.mpegurl" />
|
<source src="{{ post.media.alt_url }}" type="application/vnd.apple.mpegurl" />
|
||||||
<source src="{{ post.media.url }}" type="video/mp4" />
|
<source src="{{ post.media.url }}" type="video/mp4" />
|
||||||
</video>
|
</video>
|
||||||
</div>
|
</div>
|
||||||
<script src="/playHLSVideo.js"></script>
|
<script src="/videoUtils.js"></script>
|
||||||
{% else %}
|
{% else %}
|
||||||
<div class="post_media_content">
|
<div class="post_media_content">
|
||||||
<video class="post_media_video" src="{{ post.media.url }}" controls {% if prefs.autoplay_videos == "on" %}autoplay{% endif %} loop><a href={{ post.media.url }}>Video</a></video>
|
<video class="post_media_video{%if post_should_be_blurred %} post_nsfw_blur{% endif %}" src="{{ post.media.url }}" controls {% if prefs.autoplay_videos == "on" %}autoplay{% endif %} loop><a href={{ post.media.url }}>Video</a></video>
|
||||||
</div>
|
</div>
|
||||||
{% call render_hls_notification(post.permalink[1..]) %}
|
{% call render_hls_notification(post.permalink[1..]) %}
|
||||||
{% endif %}
|
{% endif %}
|
||||||
@ -194,6 +201,7 @@
|
|||||||
{% endmacro %}
|
{% endmacro %}
|
||||||
|
|
||||||
{% macro post_in_list(post) -%}
|
{% macro post_in_list(post) -%}
|
||||||
|
{% set post_should_be_blurred = (post.flags.nsfw && prefs.blur_nsfw=="on") || (post.flags.spoiler && prefs.blur_spoiler=="on") -%}
|
||||||
<div class="post {% if post.flags.stickied %}stickied{% endif %}" id="{{ post.id }}">
|
<div class="post {% if post.flags.stickied %}stickied{% endif %}" id="{{ post.id }}">
|
||||||
<p class="post_header">
|
<p class="post_header">
|
||||||
{% let community -%}
|
{% let community -%}
|
||||||
@ -222,7 +230,7 @@
|
|||||||
style="color:{{ post.flair.foreground_color }}; background:{{ post.flair.background_color }};"
|
style="color:{{ post.flair.foreground_color }}; background:{{ post.flair.background_color }};"
|
||||||
dir="ltr">{% call render_flair(post.flair.flair_parts) %}</a>
|
dir="ltr">{% call render_flair(post.flair.flair_parts) %}</a>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
<a href="{{ post.permalink }}">{{ post.title }}</a>{% if post.flags.nsfw %} <small class="nsfw">NSFW</small>{% endif %}
|
<a href="{{ post.permalink }}">{{ post.title }}</a>{% if post.flags.nsfw %} <small class="nsfw">NSFW</small>{% endif %}{% if post.flags.spoiler %} <small class="spoiler">Spoiler</small>{% endif %}
|
||||||
</h2>
|
</h2>
|
||||||
<!-- POST MEDIA/THUMBNAIL -->
|
<!-- POST MEDIA/THUMBNAIL -->
|
||||||
{% if (prefs.layout.is_empty() || prefs.layout == "card") && post.post_type == "image" %}
|
{% if (prefs.layout.is_empty() || prefs.layout == "card") && post.post_type == "image" %}
|
||||||
@ -233,7 +241,7 @@
|
|||||||
<img width="100%" height="100%" loading="lazy" alt="Post image" src="{{ post.media.url }}"/>
|
<img width="100%" height="100%" loading="lazy" alt="Post image" src="{{ post.media.url }}"/>
|
||||||
{% else %}
|
{% else %}
|
||||||
<svg
|
<svg
|
||||||
{%if post.flags.nsfw && prefs.blur_nsfw=="on" %}class="post_nsfw_blur"{% endif %}
|
{%if post_should_be_blurred %}class="post_nsfw_blur"{% endif %}
|
||||||
width="{{ post.media.width }}px"
|
width="{{ post.media.width }}px"
|
||||||
height="{{ post.media.height }}px"
|
height="{{ post.media.height }}px"
|
||||||
xmlns="http://www.w3.org/2000/svg">
|
xmlns="http://www.w3.org/2000/svg">
|
||||||
@ -247,19 +255,19 @@
|
|||||||
</div>
|
</div>
|
||||||
{% else if (prefs.layout.is_empty() || prefs.layout == "card") && post.post_type == "gif" %}
|
{% else if (prefs.layout.is_empty() || prefs.layout == "card") && post.post_type == "gif" %}
|
||||||
<div class="post_media_content">
|
<div class="post_media_content">
|
||||||
<video class="post_media_video short {%if post.flags.nsfw && prefs.blur_nsfw=="on" %}post_nsfw_blur{% endif %}" src="{{ post.media.url }}" {% if post.media.width > 0 && post.media.height > 0 %}width="{{ post.media.width }}" height="{{ post.media.height }}"{% endif %} poster="{{ post.media.poster }}" preload="none" controls loop {% if prefs.autoplay_videos == "on" %}autoplay{% endif %}><a href={{ post.media.url }}>Video</a></video>
|
<video class="post_media_video short {%if post_should_be_blurred %}post_nsfw_blur{% endif %}" src="{{ post.media.url }}" {% if post.media.width > 0 && post.media.height > 0 %}width="{{ post.media.width }}" height="{{ post.media.height }}"{% endif %} poster="{{ post.media.poster }}" preload="none" controls loop {% if prefs.autoplay_videos == "on" %}autoplay{% endif %}><a href={{ post.media.url }}>Video</a></video>
|
||||||
</div>
|
</div>
|
||||||
{% else if (prefs.layout.is_empty() || prefs.layout == "card") && post.post_type == "video" %}
|
{% else if (prefs.layout.is_empty() || prefs.layout == "card") && post.post_type == "video" %}
|
||||||
{% if prefs.use_hls == "on" && !post.media.alt_url.is_empty() %}
|
{% if prefs.use_hls == "on" && !post.media.alt_url.is_empty() || prefs.ffmpeg_video_downloads == "on" && !post.media.alt_url.is_empty() %}
|
||||||
<div class="post_media_content">
|
<div class="post_media_content">
|
||||||
<video class="post_media_video short {%if post.flags.nsfw && prefs.blur_nsfw=="on" %}post_nsfw_blur{% endif %} {% if prefs.autoplay_videos == "on" %}hls_autoplay{% endif %}" {% if post.media.width > 0 && post.media.height > 0 %}width="{{ post.media.width }}" height="{{ post.media.height }}"{% endif %} poster="{{ post.media.poster }}" controls preload="none">
|
<video class="post_media_video short {%if post_should_be_blurred %}post_nsfw_blur{% endif %} {% if prefs.autoplay_videos == "on" %}hls_autoplay{% endif %}" {% if post.media.width > 0 && post.media.height > 0 %}width="{{ post.media.width }}" height="{{ post.media.height }}"{% endif %} poster="{{ post.media.poster }}" controls preload="none">
|
||||||
<source src="{{ post.media.alt_url }}" type="application/vnd.apple.mpegurl" />
|
<source src="{{ post.media.alt_url }}" type="application/vnd.apple.mpegurl" />
|
||||||
<source src="{{ post.media.url }}" type="video/mp4" />
|
<source src="{{ post.media.url }}" type="video/mp4" />
|
||||||
</video>
|
</video>
|
||||||
</div>
|
</div>
|
||||||
{% else %}
|
{% else %}
|
||||||
<div class="post_media_content">
|
<div class="post_media_content">
|
||||||
<video class="post_media_video short {%if post.flags.nsfw && prefs.blur_nsfw=="on" %}post_nsfw_blur{% endif %}" src="{{ post.media.url }}" {% if post.media.width > 0 && post.media.height > 0 %}width="{{ post.media.width }}" height="{{ post.media.height }}"{% endif %} poster="{{ post.media.poster }}" preload="none" controls {% if prefs.autoplay_videos == "on" %}autoplay{% endif %}><a href={{ post.media.url }}>Video</a></video>
|
<video class="post_media_video short {%if post_should_be_blurred %}post_nsfw_blur{% endif %}" src="{{ post.media.url }}" {% if post.media.width > 0 && post.media.height > 0 %}width="{{ post.media.width }}" height="{{ post.media.height }}"{% endif %} poster="{{ post.media.poster }}" preload="none" controls {% if prefs.autoplay_videos == "on" %}autoplay{% endif %}><a href={{ post.media.url }}>Video</a></video>
|
||||||
</div>
|
</div>
|
||||||
{% call render_hls_notification(format!("{}%23{}", &self.url[1..].replace("&", "%26").replace("+", "%2B"), post.id)) %}
|
{% call render_hls_notification(format!("{}%23{}", &self.url[1..].replace("&", "%26").replace("+", "%2B"), post.id)) %}
|
||||||
{% endif %}
|
{% endif %}
|
||||||
@ -272,7 +280,7 @@
|
|||||||
</svg>
|
</svg>
|
||||||
{% else %}
|
{% else %}
|
||||||
<div style="max-width:{{ post.thumbnail.width }}px;max-height:{{ post.thumbnail.height }}px;">
|
<div style="max-width:{{ post.thumbnail.width }}px;max-height:{{ post.thumbnail.height }}px;">
|
||||||
<svg {% if post.flags.nsfw && prefs.blur_nsfw=="on" %} class="thumb_nsfw_blur" {% endif %} width="{{ post.thumbnail.width }}px" height="{{ post.thumbnail.height }}px" xmlns="http://www.w3.org/2000/svg">
|
<svg {% if post_should_be_blurred %} class="thumb_nsfw_blur" {% endif %} width="{{ post.thumbnail.width }}px" height="{{ post.thumbnail.height }}px" xmlns="http://www.w3.org/2000/svg">
|
||||||
<image width="100%" height="100%" href="{{ post.thumbnail.url }}"/>
|
<image width="100%" height="100%" href="{{ post.thumbnail.url }}"/>
|
||||||
<desc>
|
<desc>
|
||||||
<img loading="lazy" alt="Thumbnail" src="{{ post.thumbnail.url }}"/>
|
<img loading="lazy" alt="Thumbnail" src="{{ post.thumbnail.url }}"/>
|
||||||
|
Reference in New Issue
Block a user