Compare commits
29 Commits
Author | SHA1 | Date | |
---|---|---|---|
4f09333cd7 | |||
31bf8c802e | |||
e4f9bd7b8d | |||
83a667347d | |||
499a56aed4 | |||
928907086c | |||
dc9fbc1a05 | |||
7ae7a88eed | |||
536a766960 | |||
e34329cfee | |||
97a0680bd0 | |||
c1560f4eba | |||
242ffab0da | |||
1211d781d0 | |||
9e4066658c | |||
560de4e91f | |||
bd1c890961 | |||
6f799b2617 | |||
38e176f59f | |||
8248eca95c | |||
ffc3bfe72d | |||
d713746407 | |||
21b45760eb | |||
e3fb93946a | |||
b6134a39d0 | |||
c844655c98 | |||
cac83493da | |||
b47cfd1ba5 | |||
28ca3589ed |
2
.github/workflows/docker-arm.yml
vendored
2
.github/workflows/docker-arm.yml
vendored
@ -30,7 +30,7 @@ jobs:
|
||||
uses: docker/build-push-action@v2
|
||||
with:
|
||||
context: .
|
||||
file: ./Dockerfile.arm64
|
||||
file: ./Dockerfile.arm
|
||||
platforms: linux/arm64
|
||||
push: true
|
||||
tags: spikecodes/libreddit:arm
|
||||
|
5
.github/workflows/rust.yml
vendored
5
.github/workflows/rust.yml
vendored
@ -32,13 +32,14 @@ jobs:
|
||||
id: version
|
||||
run: |
|
||||
echo "::set-output name=version::$(cargo metadata --format-version 1 --no-deps | jq .packages[0].version -r | sed 's/^/v/')"
|
||||
echo "::set-output name=tag::${GITHUB_REF#refs/*/}"
|
||||
echo "::set-output name=tag::$(git describe --tags)"
|
||||
|
||||
- name: Calculate SHA512 checksum
|
||||
run: sha512sum target/release/libreddit > libreddit.sha512
|
||||
|
||||
- name: Release
|
||||
uses: softprops/action-gh-release@v1
|
||||
if: github.base_ref != 'master'
|
||||
with:
|
||||
tag_name: ${{ steps.version.outputs.version }}
|
||||
name: ${{ steps.version.outputs.version }} - NAME
|
||||
@ -47,7 +48,7 @@ jobs:
|
||||
target/release/libreddit
|
||||
libreddit.sha512
|
||||
body: |
|
||||
- CHANGES
|
||||
- ${{ github.event.head_commit.message }} ${{ github.sha }}
|
||||
|
||||
See full list of changes [here](https://github.com/spikecodes/libreddit/compare/${{ steps.version.outputs.tag }}...${{ steps.version.outputs.version }}).
|
||||
env:
|
||||
|
202
Cargo.lock
generated
202
Cargo.lock
generated
@ -1,10 +1,12 @@
|
||||
# This file is automatically @generated by Cargo.
|
||||
# It is not intended for manual editing.
|
||||
version = 3
|
||||
|
||||
[[package]]
|
||||
name = "aho-corasick"
|
||||
version = "0.7.15"
|
||||
version = "0.7.18"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7404febffaa47dac81aa44dba71523c9d069b1bdc50a77db41195149e17f68e5"
|
||||
checksum = "1e37cfd5e7657ada45f742d6e99ca5788580b5c529dc78faf11ece6dc702656f"
|
||||
dependencies = [
|
||||
"memchr",
|
||||
]
|
||||
@ -78,9 +80,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "async-trait"
|
||||
version = "0.1.48"
|
||||
version = "0.1.50"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "36ea56748e10732c49404c153638a15ec3d6211ec5ff35d9bb20e13b93576adf"
|
||||
checksum = "0b98e84bbb4cbcdd97da190ba0c58a1bb0de2c1fdf67d159e192ed766aeca722"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
@ -194,9 +196,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "const_fn"
|
||||
version = "0.4.6"
|
||||
version = "0.4.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "076a6803b0dacd6a88cfe64deba628b01533ff5ef265687e6938280c1afd0a28"
|
||||
checksum = "402da840495de3f976eaefc3485b7f5eb5b0bf9761f9a47be27fe975b3b8c2ec"
|
||||
|
||||
[[package]]
|
||||
name = "cookie"
|
||||
@ -282,9 +284,9 @@ checksum = "f7531096570974c3a9dcf9e4b8e1cede1ec26cf5046219fb3b9d897503b9be59"
|
||||
|
||||
[[package]]
|
||||
name = "fastrand"
|
||||
version = "1.4.0"
|
||||
version = "1.4.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ca5faf057445ce5c9d4329e382b2ce7ca38550ef3b73a5348362d5f24e0c7fe3"
|
||||
checksum = "77b705829d1e87f762c2df6da140b26af5839e1033aa84aa5f56bb688e4e1bdb"
|
||||
dependencies = [
|
||||
"instant",
|
||||
]
|
||||
@ -313,9 +315,9 @@ checksum = "fed34cd105917e91daa4da6b3728c47b068749d6a62c59811f06ed2ac71d9da7"
|
||||
|
||||
[[package]]
|
||||
name = "futures"
|
||||
version = "0.3.13"
|
||||
version = "0.3.15"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7f55667319111d593ba876406af7c409c0ebb44dc4be6132a783ccf163ea14c1"
|
||||
checksum = "0e7e43a803dae2fa37c1f6a8fe121e1f7bf9548b4dfc0522a42f34145dadfc27"
|
||||
dependencies = [
|
||||
"futures-channel",
|
||||
"futures-core",
|
||||
@ -328,9 +330,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "futures-channel"
|
||||
version = "0.3.13"
|
||||
version = "0.3.15"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8c2dd2df839b57db9ab69c2c9d8f3e8c81984781937fe2807dc6dcf3b2ad2939"
|
||||
checksum = "e682a68b29a882df0545c143dc3646daefe80ba479bcdede94d5a703de2871e2"
|
||||
dependencies = [
|
||||
"futures-core",
|
||||
"futures-sink",
|
||||
@ -338,15 +340,15 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "futures-core"
|
||||
version = "0.3.13"
|
||||
version = "0.3.15"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "15496a72fabf0e62bdc3df11a59a3787429221dd0710ba8ef163d6f7a9112c94"
|
||||
checksum = "0402f765d8a89a26043b889b26ce3c4679d268fa6bb22cd7c6aad98340e179d1"
|
||||
|
||||
[[package]]
|
||||
name = "futures-executor"
|
||||
version = "0.3.13"
|
||||
version = "0.3.15"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "891a4b7b96d84d5940084b2a37632dd65deeae662c114ceaa2c879629c9c0ad1"
|
||||
checksum = "badaa6a909fac9e7236d0620a2f57f7664640c56575b71a7552fbd68deafab79"
|
||||
dependencies = [
|
||||
"futures-core",
|
||||
"futures-task",
|
||||
@ -355,9 +357,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "futures-io"
|
||||
version = "0.3.13"
|
||||
version = "0.3.15"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d71c2c65c57704c32f5241c1223167c2c3294fd34ac020c807ddbe6db287ba59"
|
||||
checksum = "acc499defb3b348f8d8f3f66415835a9131856ff7714bf10dadfc4ec4bdb29a1"
|
||||
|
||||
[[package]]
|
||||
name = "futures-lite"
|
||||
@ -376,10 +378,11 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "futures-macro"
|
||||
version = "0.3.13"
|
||||
version = "0.3.15"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ea405816a5139fb39af82c2beb921d52143f556038378d6db21183a5c37fbfb7"
|
||||
checksum = "a4c40298486cdf52cc00cd6d6987892ba502c7656a16a4192a9992b1ccedd121"
|
||||
dependencies = [
|
||||
"autocfg",
|
||||
"proc-macro-hack",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
@ -388,22 +391,23 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "futures-sink"
|
||||
version = "0.3.13"
|
||||
version = "0.3.15"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "85754d98985841b7d4f5e8e6fbfa4a4ac847916893ec511a2917ccd8525b8bb3"
|
||||
checksum = "a57bead0ceff0d6dde8f465ecd96c9338121bb7717d3e7b108059531870c4282"
|
||||
|
||||
[[package]]
|
||||
name = "futures-task"
|
||||
version = "0.3.13"
|
||||
version = "0.3.15"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "fa189ef211c15ee602667a6fcfe1c1fd9e07d42250d2156382820fba33c9df80"
|
||||
checksum = "8a16bef9fc1a4dddb5bee51c989e3fbba26569cbb0e31f5b303c184e3dd33dae"
|
||||
|
||||
[[package]]
|
||||
name = "futures-util"
|
||||
version = "0.3.13"
|
||||
version = "0.3.15"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1812c7ab8aedf8d6f2701a43e1243acdbcc2b36ab26e2ad421eb99ac963d96d1"
|
||||
checksum = "feb5c238d27e2bf94ffdfd27b2c29e3df4a68c4193bb6427384259e2bf191967"
|
||||
dependencies = [
|
||||
"autocfg",
|
||||
"futures-channel",
|
||||
"futures-core",
|
||||
"futures-io",
|
||||
@ -420,9 +424,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "h2"
|
||||
version = "0.3.2"
|
||||
version = "0.3.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "fc018e188373e2777d0ef2467ebff62a08e66c3f5857b23c8fbec3018210dc00"
|
||||
checksum = "825343c4eef0b63f541f8903f395dc5beb362a979b5799a84062527ef1e37726"
|
||||
dependencies = [
|
||||
"bytes",
|
||||
"fnv",
|
||||
@ -465,9 +469,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "http-body"
|
||||
version = "0.4.1"
|
||||
version = "0.4.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5dfb77c123b4e2f72a2069aeae0b4b4949cc7e966df277813fc16347e7549737"
|
||||
checksum = "60daa14be0e0786db0f03a9e57cb404c9d756eed2b6c62b9ea98ec5743ec75a9"
|
||||
dependencies = [
|
||||
"bytes",
|
||||
"http",
|
||||
@ -476,21 +480,21 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "httparse"
|
||||
version = "1.3.6"
|
||||
version = "1.4.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bc35c995b9d93ec174cf9a27d425c7892722101e14993cd227fdb51d70cf9589"
|
||||
checksum = "f3a87b616e37e93c22fb19bcd386f02f3af5ea98a25670ad0fce773de23c5e68"
|
||||
|
||||
[[package]]
|
||||
name = "httpdate"
|
||||
version = "0.3.2"
|
||||
version = "1.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "494b4d60369511e7dea41cf646832512a94e542f68bb9c49e54518e0f468eb47"
|
||||
checksum = "05842d0d43232b23ccb7060ecb0f0626922c21f30012e97b767b30afd4a5d4b9"
|
||||
|
||||
[[package]]
|
||||
name = "hyper"
|
||||
version = "0.14.5"
|
||||
version = "0.14.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8bf09f61b52cfcf4c00de50df88ae423d6c02354e385a86341133b5338630ad1"
|
||||
checksum = "1e5f105c494081baa3bf9e200b279e27ec1623895cd504c7dbef8d0b080fcf54"
|
||||
dependencies = [
|
||||
"bytes",
|
||||
"futures-channel",
|
||||
@ -535,9 +539,9 @@ checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39"
|
||||
|
||||
[[package]]
|
||||
name = "idna"
|
||||
version = "0.2.2"
|
||||
version = "0.2.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "89829a5d69c23d348314a7ac337fe39173b61149a9864deabd260983aed48c21"
|
||||
checksum = "418a0a6fab821475f634efe3ccc45c013f742efe03d853e8d3355d5cb850ecf8"
|
||||
dependencies = [
|
||||
"matches",
|
||||
"unicode-bidi",
|
||||
@ -571,9 +575,9 @@ checksum = "dd25036021b0de88a0aff6b850051563c6516d0bf53f8638938edbb9de732736"
|
||||
|
||||
[[package]]
|
||||
name = "js-sys"
|
||||
version = "0.3.50"
|
||||
version = "0.3.51"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2d99f9e3e84b8f67f846ef5b4cbbc3b1c29f6c759fcbce6f01aa0e73d932a24c"
|
||||
checksum = "83bdfbace3a0e81a4253f73b49e960b053e396a11012cbd49b9b74d6a2b67062"
|
||||
dependencies = [
|
||||
"wasm-bindgen",
|
||||
]
|
||||
@ -586,9 +590,9 @@ checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646"
|
||||
|
||||
[[package]]
|
||||
name = "lexical-core"
|
||||
version = "0.7.5"
|
||||
version = "0.7.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "21f866863575d0e1d654fbeeabdc927292fdf862873dc3c96c6f753357e13374"
|
||||
checksum = "6607c62aa161d23d17a9072cc5da0be67cdfc89d3afb1e8d9c842bebc2525ffe"
|
||||
dependencies = [
|
||||
"arrayvec",
|
||||
"bitflags",
|
||||
@ -599,13 +603,13 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "libc"
|
||||
version = "0.2.93"
|
||||
version = "0.2.94"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9385f66bf6105b241aa65a61cb923ef20efc665cb9f9bb50ac2f0c4b7f378d41"
|
||||
checksum = "18794a8ad5b29321f790b55d93dfba91e125cb1a9edbd4f8e3150acc771c1a5e"
|
||||
|
||||
[[package]]
|
||||
name = "libreddit"
|
||||
version = "0.10.3"
|
||||
version = "0.13.1"
|
||||
dependencies = [
|
||||
"askama",
|
||||
"async-recursion",
|
||||
@ -626,9 +630,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "lock_api"
|
||||
version = "0.4.3"
|
||||
version = "0.4.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5a3c91c24eae6777794bb1997ad98bbb87daf92890acab859f7eaa4320333176"
|
||||
checksum = "0382880606dff6d15c9476c416d18690b72742aa7b605bb6dd6ec9030fbf07eb"
|
||||
dependencies = [
|
||||
"scopeguard",
|
||||
]
|
||||
@ -650,9 +654,9 @@ checksum = "7ffc5c5338469d4d3ea17d269fa8ea3512ad247247c30bd2df69e68309ed0a08"
|
||||
|
||||
[[package]]
|
||||
name = "memchr"
|
||||
version = "2.3.4"
|
||||
version = "2.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0ee1c47aaa256ecabcaea351eae4a9b01ef39ed810004e298d2511ed284b1525"
|
||||
checksum = "b16bd47d9e329435e309c58469fe0791c2d0d1ba96ec0954152a5ae2b04387dc"
|
||||
|
||||
[[package]]
|
||||
name = "mio"
|
||||
@ -716,9 +720,9 @@ checksum = "af8b08b04175473088b46763e51ee54da5f9a164bc162f615b91bc179dbf15a3"
|
||||
|
||||
[[package]]
|
||||
name = "openssl-probe"
|
||||
version = "0.1.2"
|
||||
version = "0.1.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "77af24da69f9d9341038eba93a073b1fdaaa1b788221b00a69bce9e762cb32de"
|
||||
checksum = "28988d872ab76095a6e6ac88d99b54fd267702734fd7ffe610ca27f533ddb95a"
|
||||
|
||||
[[package]]
|
||||
name = "parking"
|
||||
@ -759,18 +763,18 @@ checksum = "d4fd5641d01c8f18a23da7b6fe29298ff4b55afcccdf78973b24cf3175fee32e"
|
||||
|
||||
[[package]]
|
||||
name = "pin-project"
|
||||
version = "1.0.6"
|
||||
version = "1.0.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bc174859768806e91ae575187ada95c91a29e96a98dc5d2cd9a1fed039501ba6"
|
||||
checksum = "c7509cc106041c40a4518d2af7a61530e1eed0e6285296a3d8c5472806ccc4a4"
|
||||
dependencies = [
|
||||
"pin-project-internal",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "pin-project-internal"
|
||||
version = "1.0.6"
|
||||
version = "1.0.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a490329918e856ed1b083f244e3bfe2d8c4f336407e4ea9e1a9f479ff09049e5"
|
||||
checksum = "48c950132583b500556b1efd71d45b319029f2b71518d979fcc208e16b42426f"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
@ -827,18 +831,18 @@ checksum = "941ba9d78d8e2f7ce474c015eea4d9c6d25b6a3327f9832ee29a4de27f91bbb8"
|
||||
|
||||
[[package]]
|
||||
name = "redox_syscall"
|
||||
version = "0.2.5"
|
||||
version = "0.2.8"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "94341e4e44e24f6b591b59e47a8a027df12e008d73fd5672dbea9cc22f4507d9"
|
||||
checksum = "742739e41cd49414de871ea5e549afb7e2a3ac77b589bcbebe8c82fab37147fc"
|
||||
dependencies = [
|
||||
"bitflags",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "regex"
|
||||
version = "1.4.5"
|
||||
version = "1.5.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "957056ecddbeba1b26965114e191d2e8589ce74db242b6ea25fc4062427a5c19"
|
||||
checksum = "d07a8629359eb56f1e2fb1652bb04212c072a87ba68546a04065d525673ac461"
|
||||
dependencies = [
|
||||
"aho-corasick",
|
||||
"memchr",
|
||||
@ -847,9 +851,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "regex-syntax"
|
||||
version = "0.6.23"
|
||||
version = "0.6.25"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "24d5f089152e60f62d28b835fbff2cd2e8dc0baf1ac13343bef92ab7eed84548"
|
||||
checksum = "f497285884f3fcff424ffc933e56d7cbca511def0c9831a7f9b5f6153e3cc89b"
|
||||
|
||||
[[package]]
|
||||
name = "ring"
|
||||
@ -883,9 +887,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "rustls"
|
||||
version = "0.19.0"
|
||||
version = "0.19.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "064fd21ff87c6e87ed4506e68beb42459caa4a0e2eb144932e6776768556980b"
|
||||
checksum = "35edb675feee39aec9c99fa5ff985081995a06d594114ae14cbe797ad7b7a6d7"
|
||||
dependencies = [
|
||||
"base64",
|
||||
"log",
|
||||
@ -930,9 +934,9 @@ checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd"
|
||||
|
||||
[[package]]
|
||||
name = "sct"
|
||||
version = "0.6.0"
|
||||
version = "0.6.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e3042af939fca8c3453b7af0f1c66e533a15a86169e39de2657310ade8f98d3c"
|
||||
checksum = "b362b83898e0e69f38515b82ee15aa80636befe47c3b6d3d89a911e78fc228ce"
|
||||
dependencies = [
|
||||
"ring",
|
||||
"untrusted",
|
||||
@ -978,18 +982,18 @@ checksum = "388a1df253eca08550bef6c72392cfe7c30914bf41df5269b68cbd6ff8f570a3"
|
||||
|
||||
[[package]]
|
||||
name = "serde"
|
||||
version = "1.0.125"
|
||||
version = "1.0.126"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "558dc50e1a5a5fa7112ca2ce4effcb321b0300c0d4ccf0776a9f60cd89031171"
|
||||
checksum = "ec7505abeacaec74ae4778d9d9328fe5a5d04253220a85c4ee022239fc996d03"
|
||||
dependencies = [
|
||||
"serde_derive",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serde_derive"
|
||||
version = "1.0.125"
|
||||
version = "1.0.126"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b093b7a2bb58203b5da3056c05b4ec1fed827dcfdb37347a8841695263b3d06d"
|
||||
checksum = "963a7dbc9895aeac7ac90e74f34a5d5261828f79df35cbed41e10189d3804d43"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
@ -1024,9 +1028,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "slab"
|
||||
version = "0.4.2"
|
||||
version = "0.4.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c111b5bd5695e56cffe5129854aa230b39c93a305372fdbb2668ca2394eea9f8"
|
||||
checksum = "f173ac3d1a7e3b28003f40de0b5ce7fe2710f9b9dc3fc38664cebee46b3b6527"
|
||||
|
||||
[[package]]
|
||||
name = "smallvec"
|
||||
@ -1122,9 +1126,9 @@ checksum = "6446ced80d6c486436db5c078dde11a9f73d42b57fb273121e160b84f63d894c"
|
||||
|
||||
[[package]]
|
||||
name = "syn"
|
||||
version = "1.0.69"
|
||||
version = "1.0.72"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "48fe99c6bd8b1cc636890bcc071842de909d902c81ac7dab53ba33c421ab8ffb"
|
||||
checksum = "a1e8cdbefb79a9a5a65e0db8b47b723ee907b7c7f8496c76a1770b5c310bab82"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
@ -1201,9 +1205,9 @@ checksum = "cda74da7e1a664f795bb1f8a87ec406fb89a02522cf6e50620d016add6dbbf5c"
|
||||
|
||||
[[package]]
|
||||
name = "tokio"
|
||||
version = "1.5.0"
|
||||
version = "1.6.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "83f0c8e7c0addab50b663055baf787d0af7f413a46e6e7fb9559a4e4db7137a5"
|
||||
checksum = "bd3076b5c8cc18138b8f8814895c11eb4de37114a5d127bafdc5e55798ceef37"
|
||||
dependencies = [
|
||||
"autocfg",
|
||||
"bytes",
|
||||
@ -1221,9 +1225,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "tokio-macros"
|
||||
version = "1.1.0"
|
||||
version = "1.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "caf7b11a536f46a809a8a9f0bb4237020f70ecbf115b842360afb127ea2fda57"
|
||||
checksum = "c49e3df43841dafb86046472506755d8501c5615673955f6aa17181125d13c37"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
@ -1243,9 +1247,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "tokio-util"
|
||||
version = "0.6.5"
|
||||
version = "0.6.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5143d049e85af7fbc36f5454d990e62c2df705b3589f123b71f441b6b59f443f"
|
||||
checksum = "1caa0b0c8d94a049db56b5acf8cba99dc0623aab1b26d5b5f5e2d945846b3592"
|
||||
dependencies = [
|
||||
"bytes",
|
||||
"futures-core",
|
||||
@ -1263,9 +1267,9 @@ checksum = "360dfd1d6d30e05fda32ace2c8c70e9c0a9da713275777f5a4dbb8a1893930c6"
|
||||
|
||||
[[package]]
|
||||
name = "tracing"
|
||||
version = "0.1.25"
|
||||
version = "0.1.26"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "01ebdc2bb4498ab1ab5f5b73c5803825e60199229ccba0698170e3be0e7f959f"
|
||||
checksum = "09adeb8c97449311ccd28a427f96fb563e7fd31aabf994189879d9da2394b89d"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"pin-project-lite",
|
||||
@ -1274,9 +1278,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "tracing-core"
|
||||
version = "0.1.17"
|
||||
version = "0.1.18"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f50de3927f93d202783f4513cda820ab47ef17f624b03c096e86ef00c67e6b5f"
|
||||
checksum = "a9ff14f98b1a4b289c6248a023c1c2fa1491062964e9fed67ab29c4e4da4a052"
|
||||
dependencies = [
|
||||
"lazy_static",
|
||||
]
|
||||
@ -1313,9 +1317,9 @@ checksum = "9337591893a19b88d8d87f2cec1e73fad5cdfd10e5a6f349f498ad6ea2ffb1e3"
|
||||
|
||||
[[package]]
|
||||
name = "unicode-xid"
|
||||
version = "0.2.1"
|
||||
version = "0.2.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f7fe0bb3479651439c9112f72b6c505038574c9fbb575ed1bf3b797fa39dd564"
|
||||
checksum = "8ccb82d61f80a663efe1f787a51b16b5a51e3314d6ac365b08639f52387b33f3"
|
||||
|
||||
[[package]]
|
||||
name = "untrusted"
|
||||
@ -1325,9 +1329,9 @@ checksum = "a156c684c91ea7d62626509bce3cb4e1d9ed5c4d978f7b4352658f96a4c26b4a"
|
||||
|
||||
[[package]]
|
||||
name = "url"
|
||||
version = "2.2.1"
|
||||
version = "2.2.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9ccd964113622c8e9322cfac19eb1004a07e636c545f325da085d5cdde6f1f8b"
|
||||
checksum = "a507c383b2d33b5fc35d1861e77e6b383d158b2da5e14fe51b83dfedf6fd578c"
|
||||
dependencies = [
|
||||
"form_urlencoded",
|
||||
"idna",
|
||||
@ -1359,9 +1363,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "wasm-bindgen"
|
||||
version = "0.2.73"
|
||||
version = "0.2.74"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "83240549659d187488f91f33c0f8547cbfef0b2088bc470c116d1d260ef623d9"
|
||||
checksum = "d54ee1d4ed486f78874278e63e4069fc1ab9f6a18ca492076ffb90c5eb2997fd"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"wasm-bindgen-macro",
|
||||
@ -1369,9 +1373,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "wasm-bindgen-backend"
|
||||
version = "0.2.73"
|
||||
version = "0.2.74"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ae70622411ca953215ca6d06d3ebeb1e915f0f6613e3b495122878d7ebec7dae"
|
||||
checksum = "3b33f6a0694ccfea53d94db8b2ed1c3a8a4c86dd936b13b9f0a15ec4a451b900"
|
||||
dependencies = [
|
||||
"bumpalo",
|
||||
"lazy_static",
|
||||
@ -1384,9 +1388,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "wasm-bindgen-macro"
|
||||
version = "0.2.73"
|
||||
version = "0.2.74"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3e734d91443f177bfdb41969de821e15c516931c3c3db3d318fa1b68975d0f6f"
|
||||
checksum = "088169ca61430fe1e58b8096c24975251700e7b1f6fd91cc9d59b04fb9b18bd4"
|
||||
dependencies = [
|
||||
"quote",
|
||||
"wasm-bindgen-macro-support",
|
||||
@ -1394,9 +1398,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "wasm-bindgen-macro-support"
|
||||
version = "0.2.73"
|
||||
version = "0.2.74"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d53739ff08c8a68b0fdbcd54c372b8ab800b1449ab3c9d706503bc7dd1621b2c"
|
||||
checksum = "be2241542ff3d9f241f5e2cb6dd09b37efe786df8851c54957683a49f0987a97"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
@ -1407,15 +1411,15 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "wasm-bindgen-shared"
|
||||
version = "0.2.73"
|
||||
version = "0.2.74"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d9a543ae66aa233d14bb765ed9af4a33e81b8b58d1584cf1b47ff8cd0b9e4489"
|
||||
checksum = "d7cff876b8f18eed75a66cf49b65e7f967cb354a7aa16003fb55dbfd25b44b4f"
|
||||
|
||||
[[package]]
|
||||
name = "web-sys"
|
||||
version = "0.3.50"
|
||||
version = "0.3.51"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a905d57e488fec8861446d3393670fb50d27a262344013181c2cdf9fff5481be"
|
||||
checksum = "e828417b379f3df7111d3a2a9e5753706cae29c41f7c4029ee9fd77f3e09e582"
|
||||
dependencies = [
|
||||
"js-sys",
|
||||
"wasm-bindgen",
|
||||
|
12
Cargo.toml
12
Cargo.toml
@ -3,7 +3,7 @@ name = "libreddit"
|
||||
description = " Alternative private front-end to Reddit"
|
||||
license = "AGPL-3.0"
|
||||
repository = "https://github.com/spikecodes/libreddit"
|
||||
version = "0.10.3"
|
||||
version = "0.13.1"
|
||||
authors = ["spikecodes <19519553+spikecodes@users.noreply.github.com>"]
|
||||
edition = "2018"
|
||||
|
||||
@ -12,14 +12,14 @@ askama = { version = "0.10.5", default-features = false }
|
||||
async-recursion = "0.3.2"
|
||||
cached = "0.23.0"
|
||||
clap = { version = "2.33.3", default-features = false }
|
||||
regex = "1.4.5"
|
||||
serde = { version = "1.0.125", features = ["derive"] }
|
||||
regex = "1.5.4"
|
||||
serde = { version = "1.0.126", features = ["derive"] }
|
||||
cookie = "0.15.0"
|
||||
futures-lite = "1.11.3"
|
||||
hyper = { version = "0.14.5", features = ["full"] }
|
||||
hyper = { version = "0.14.7", features = ["full"] }
|
||||
hyper-rustls = "0.22.1"
|
||||
route-recognizer = "0.3.0"
|
||||
serde_json = "1.0.64"
|
||||
tokio = { version = "1.5.0", features = ["full"] }
|
||||
tokio = { version = "1.6.0", features = ["full"] }
|
||||
time = "0.2.26"
|
||||
url = "2.2.1"
|
||||
url = "2.2.2"
|
||||
|
34
README.md
34
README.md
@ -31,17 +31,19 @@ Feel free to [open an issue](https://github.com/spikecodes/libreddit/issues/new)
|
||||
| [libreddit.spike.codes](https://libreddit.spike.codes) (official) | 🇺🇸 US | |
|
||||
| [libreddit.dothq.co](https://libreddit.dothq.co) | 🇺🇸 US | |
|
||||
| [libreddit.kavin.rocks](https://libreddit.kavin.rocks) | 🇮🇳 IN | ✅ |
|
||||
| [libreddit.himiko.cloud](https://libreddit.himiko.cloud) | 🇫🇮 FI | |
|
||||
| [libreddit.bcow.xyz](https://libreddit.bcow.xyz) | 🇺🇸 US | |
|
||||
| [libreddit.40two.app](https://libreddit.40two.app) | 🇳🇱 NL | |
|
||||
| [reddit.invak.id](https://reddit.invak.id) | 🇧🇬 BG | |
|
||||
| [reddit.phii.me](https://reddit.phii.me) | 🇺🇸 US | |
|
||||
| [lr.riverside.rocks](https://lr.riverside.rocks) | 🇺🇸 US | |
|
||||
| [libreddit.silkky.cloud](https://libreddit.silkky.cloud) | 🇫🇮 FI | |
|
||||
| [libreddit.database.red](https://libreddit.database.red) | 🇺🇸 US | ✅ |
|
||||
| [libreddit.exonip.de](https://libreddit.exonip.de) | 🇩🇪 DE | |
|
||||
| [libreddit.domain.glass](https://libreddit.domain.glass) | 🇺🇸 US | ✅ |
|
||||
| [spjmllawtheisznfs7uryhxumin26ssv2draj7oope3ok3wuhy43eoyd.onion](http://spjmllawtheisznfs7uryhxumin26ssv2draj7oope3ok3wuhy43eoyd.onion) | 🇮🇳 IN | |
|
||||
| [fwhhsbrbltmrct5hshrnqlqygqvcgmnek3cnka55zj4y7nuus5muwyyd.onion](http://fwhhsbrbltmrct5hshrnqlqygqvcgmnek3cnka55zj4y7nuus5muwyyd.onion) | 🇩🇪 DE | |
|
||||
| [libreddit.himiko7xl2skojc6odi7hykl626gt4qki3vxdbv33u2u3af76d6k32ad.onion](http://libreddit.himiko7xl2skojc6odi7hykl626gt4qki3vxdbv33u2u3af76d6k32ad.onion) | 🇫🇮 FI | |
|
||||
| [dflv6yjt7il3n3tggf4qhcmkzbti2ppytqx3o7pjrzwgntutpewscyid.onion](http://dflv6yjt7il3n3tggf4qhcmkzbti2ppytqx3o7pjrzwgntutpewscyid.onion/) | 🇺🇸 US | |
|
||||
| [kphht2jcflojtqte4b4kyx7p2ahagv4debjj32nre67dxz7y57seqwyd.onion](http://kphht2jcflojtqte4b4kyx7p2ahagv4debjj32nre67dxz7y57seqwyd.onion/) | 🇳🇱 NL | |
|
||||
|
||||
A checkmark in the "Cloudflare" category here refers to the use of the reverse proxy, [Cloudflare](https://cloudflare). The checkmark will not be listed for a site which uses Cloudflare DNS but rather the proxying service which grants Cloudflare the ability to monitor traffic to the website.
|
||||
|
||||
@ -69,7 +71,7 @@ Teddit is another awesome open source project designed to provide an alternative
|
||||
|
||||
If you are looking to compare, the biggest differences I have noticed are:
|
||||
- Libreddit is themed around Reddit's redesign whereas Teddit appears to stick much closer to Reddit's old design. This may suit some users better as design is always subjective.
|
||||
- Libreddit is written in [Rust](https://www.rust-lang.org) for speed and memory safety. It uses [Actix Web](https://actix.rs), which was [benchmarked as the fastest web server for single queries](https://www.techempower.com/benchmarks/#hw=ph&test=db).
|
||||
- Libreddit is written in [Rust](https://www.rust-lang.org) for speed and memory safety. It uses [Hyper](https://hyper.rs), a speedy and lightweight HTTP server/client implementation.
|
||||
|
||||
---
|
||||
|
||||
@ -192,6 +194,32 @@ Once installed, deploy Libreddit to `0.0.0.0:8080` by running:
|
||||
libreddit
|
||||
```
|
||||
|
||||
## Change Default Settings
|
||||
|
||||
Assign a default value for each setting by passing environment variables to Libreddit in the format `LIBREDDIT_DEFAULT_{X}`. Replace `{X}` with the setting name (see list below) in capital letters.
|
||||
|
||||
| Name | Possible values | Default value |
|
||||
|-----------------------|----------------------------------------------------------------------------------------|---------------|
|
||||
| theme | ["system", "light", "dark", "black", "dracula", "nord", "laserwave", "violet", "gold"] | system |
|
||||
| front_page | ["default", "popular", "all"] | default |
|
||||
| layout | ["card", "clean", "compact"] | card |
|
||||
| wide | ["on", "off"] | off |
|
||||
| comment_sort | ["hot", "new", "top", "rising", "controversial"] | hot |
|
||||
| post_sort | ["confidence", "top", "new", "controversial", "old"] | confidence |
|
||||
| show_nsfw | ["on", "off"] | off |
|
||||
| use_hls | ["on", "off"] | off |
|
||||
| hide_hls_notification | ["on", "off"] | off |
|
||||
|
||||
### Examples
|
||||
|
||||
```bash
|
||||
LIBREDDIT_DEFAULT_SHOW_NSFW=on libreddit
|
||||
```
|
||||
|
||||
```bash
|
||||
LIBREDDIT_DEFAULT_WIDE=on LIBREDDIT_DEFAULT_THEME=dark libreddit -r
|
||||
```
|
||||
|
||||
## Proxying using NGINX
|
||||
|
||||
**NOTE** If you're [proxying Libreddit through a NGINX Reverse Proxy](https://github.com/spikecodes/libreddit/issues/122#issuecomment-782226853), add
|
||||
|
@ -13,10 +13,10 @@ pub async fn proxy(req: Request<Body>, format: &str) -> Result<Response<Body>, S
|
||||
url = url.replace(&format!("{{{}}}", name), value);
|
||||
}
|
||||
|
||||
stream(&url).await
|
||||
stream(&url, &req).await
|
||||
}
|
||||
|
||||
async fn stream(url: &str) -> Result<Response<Body>, String> {
|
||||
async fn stream(url: &str, req: &Request<Body>) -> Result<Response<Body>, String> {
|
||||
// First parameter is target URL (mandatory).
|
||||
let url = Uri::from_str(url).map_err(|_| "Couldn't parse URL".to_string())?;
|
||||
|
||||
@ -26,8 +26,20 @@ async fn stream(url: &str) -> Result<Response<Body>, String> {
|
||||
// Build the hyper client from the HTTPS connector.
|
||||
let client: client::Client<_, hyper::Body> = client::Client::builder().build(https);
|
||||
|
||||
let mut builder = Request::get(url);
|
||||
|
||||
// Copy useful headers from original request
|
||||
let headers = req.headers();
|
||||
for &key in &["Range", "If-Modified-Since", "Cache-Control"] {
|
||||
if let Some(value) = headers.get(key) {
|
||||
builder = builder.header(key, value);
|
||||
}
|
||||
}
|
||||
|
||||
let stream_request = builder.body(Body::default()).expect("stream");
|
||||
|
||||
client
|
||||
.get(url)
|
||||
.request(stream_request)
|
||||
.await
|
||||
.map(|mut res| {
|
||||
let mut rm = |key: &str| res.headers_mut().remove(key);
|
||||
@ -40,6 +52,8 @@ async fn stream(url: &str) -> Result<Response<Body>, String> {
|
||||
rm("x-cdn-client-region");
|
||||
rm("x-cdn-name");
|
||||
rm("x-cdn-server-region");
|
||||
rm("x-reddit-cdn");
|
||||
rm("x-reddit-video-features");
|
||||
|
||||
res
|
||||
})
|
||||
|
10
src/main.rs
10
src/main.rs
@ -139,7 +139,7 @@ async fn main() {
|
||||
"Referrer-Policy" => "no-referrer",
|
||||
"X-Content-Type-Options" => "nosniff",
|
||||
"X-Frame-Options" => "DENY",
|
||||
"Content-Security-Policy" => "default-src 'none'; manifest-src 'self'; media-src 'self'; style-src 'self' 'unsafe-inline'; base-uri 'none'; img-src 'self' data:; form-action 'self'; frame-ancestors 'none';"
|
||||
"Content-Security-Policy" => "default-src 'none'; script-src 'self' blob:; manifest-src 'self'; media-src 'self' data: blob: about:; style-src 'self' 'unsafe-inline'; base-uri 'none'; img-src 'self' data:; form-action 'self'; frame-ancestors 'none'; connect-src 'self'; worker-src blob:;"
|
||||
};
|
||||
|
||||
if let Some(expire_time) = hsts {
|
||||
@ -158,9 +158,16 @@ async fn main() {
|
||||
app.at("/logo.png").get(|_| pwa_logo().boxed());
|
||||
app.at("/touch-icon-iphone.png").get(|_| iphone_logo().boxed());
|
||||
app.at("/apple-touch-icon.png").get(|_| iphone_logo().boxed());
|
||||
app
|
||||
.at("/playHLSVideo.js")
|
||||
.get(|_| resource(include_str!("../static/playHLSVideo.js"), "text/javascript", false).boxed());
|
||||
app
|
||||
.at("/hls.min.js")
|
||||
.get(|_| resource(include_str!("../static/hls.min.js"), "text/javascript", false).boxed());
|
||||
|
||||
// Proxy media through Libreddit
|
||||
app.at("/vid/:id/:size").get(|r| proxy(r, "https://v.redd.it/{id}/DASH_{size}").boxed());
|
||||
app.at("/hls/:id/*path").get(|r| proxy(r, "https://v.redd.it/{id}/{path}").boxed());
|
||||
app.at("/img/:id").get(|r| proxy(r, "https://i.redd.it/{id}").boxed());
|
||||
app.at("/thumb/:point/:id").get(|r| proxy(r, "https://{point}.thumbs.redditmedia.com/{id}").boxed());
|
||||
app.at("/emoji/:id/:name").get(|r| proxy(r, "https://emoji.redditmedia.com/{id}/{name}").boxed());
|
||||
@ -184,6 +191,7 @@ async fn main() {
|
||||
// Configure settings
|
||||
app.at("/settings").get(|r| settings::get(r).boxed()).post(|r| settings::set(r).boxed());
|
||||
app.at("/settings/restore").get(|r| settings::restore(r).boxed());
|
||||
app.at("/settings/update").get(|r| settings::update(r).boxed());
|
||||
|
||||
// Subreddit services
|
||||
app.at("/r/:sub").get(|r| subreddit::community(r).boxed());
|
||||
|
@ -2,7 +2,7 @@
|
||||
use crate::client::json;
|
||||
use crate::esc;
|
||||
use crate::server::RequestExt;
|
||||
use crate::utils::{cookie, error, format_num, format_url, param, rewrite_urls, template, time, val, Author, Comment, Flags, Flair, FlairPart, Media, Post, Preferences};
|
||||
use crate::utils::{error, format_num, format_url, param, rewrite_urls, setting, template, time, val, Author, Comment, Flags, Flair, FlairPart, Media, Post, Preferences};
|
||||
use hyper::{Body, Request, Response};
|
||||
|
||||
use async_recursion::async_recursion;
|
||||
@ -28,7 +28,7 @@ pub async fn item(req: Request<Body>) -> Result<Response<Body>, String> {
|
||||
let mut sort: String = param(&path, "sort");
|
||||
|
||||
// Grab default comment sort method from Cookies
|
||||
let default_sort = cookie(&req, "comment_sort");
|
||||
let default_sort = setting(&req, "comment_sort");
|
||||
|
||||
// If there's no sort query but there's a default sort, set sort to default_sort
|
||||
if sort.is_empty() && !default_sort.is_empty() {
|
||||
@ -106,6 +106,7 @@ async fn parse_post(json: &serde_json::Value) -> Post {
|
||||
media,
|
||||
thumbnail: Media {
|
||||
url: format_url(val(post, "thumbnail").as_str()),
|
||||
alt_url: String::new(),
|
||||
width: post["data"]["thumbnail_width"].as_i64().unwrap_or_default(),
|
||||
height: post["data"]["thumbnail_height"].as_i64().unwrap_or_default(),
|
||||
poster: "".to_string(),
|
||||
|
@ -1,5 +1,5 @@
|
||||
// CRATES
|
||||
use crate::utils::{cookie, error, format_num, format_url, param, template, val, Post, Preferences};
|
||||
use crate::utils::{catch_random, error, format_num, format_url, param, setting, template, val, Post, Preferences};
|
||||
use crate::{client::json, RequestExt};
|
||||
use askama::Template;
|
||||
use hyper::{Body, Request, Response};
|
||||
@ -31,13 +31,18 @@ struct SearchTemplate {
|
||||
sub: String,
|
||||
params: SearchParams,
|
||||
prefs: Preferences,
|
||||
url: String,
|
||||
}
|
||||
|
||||
// SERVICES
|
||||
pub async fn find(req: Request<Body>) -> Result<Response<Body>, String> {
|
||||
let nsfw_results = if cookie(&req, "show_nsfw") == "on" { "&include_over_18=on" } else { "" };
|
||||
let nsfw_results = if setting(&req, "show_nsfw") == "on" { "&include_over_18=on" } else { "" };
|
||||
let path = format!("{}.json?{}{}", req.uri().path(), req.uri().query().unwrap_or_default(), nsfw_results);
|
||||
let sub = req.param("sub").unwrap_or_default();
|
||||
// Handle random subreddits
|
||||
if let Ok(random) = catch_random(&sub, "/find").await {
|
||||
return Ok(random);
|
||||
}
|
||||
let query = param(&path, "q");
|
||||
|
||||
let sort = if param(&path, "sort").is_empty() {
|
||||
@ -52,6 +57,8 @@ pub async fn find(req: Request<Body>) -> Result<Response<Body>, String> {
|
||||
Vec::new()
|
||||
};
|
||||
|
||||
let url = String::from(req.uri().path_and_query().map_or("", |val| val.as_str()));
|
||||
|
||||
match Post::fetch(&path, String::new()).await {
|
||||
Ok((posts, after)) => template(SearchTemplate {
|
||||
posts,
|
||||
@ -66,6 +73,7 @@ pub async fn find(req: Request<Body>) -> Result<Response<Body>, String> {
|
||||
restrict_sr: param(&path, "restrict_sr"),
|
||||
},
|
||||
prefs: Preferences::new(req),
|
||||
url,
|
||||
}),
|
||||
Err(msg) => error(req, msg).await,
|
||||
}
|
||||
|
@ -16,6 +16,21 @@ struct SettingsTemplate {
|
||||
prefs: Preferences,
|
||||
}
|
||||
|
||||
// CONSTANTS
|
||||
|
||||
const PREFS: [&str; 10] = [
|
||||
"theme",
|
||||
"front_page",
|
||||
"layout",
|
||||
"wide",
|
||||
"comment_sort",
|
||||
"post_sort",
|
||||
"show_nsfw",
|
||||
"use_hls",
|
||||
"hide_hls_notification",
|
||||
"subscriptions",
|
||||
];
|
||||
|
||||
// FUNCTIONS
|
||||
|
||||
// Retrieve cookies from request "Cookie" header
|
||||
@ -50,9 +65,7 @@ pub async fn set(req: Request<Body>) -> Result<Response<Body>, String> {
|
||||
|
||||
let mut res = redirect("/settings".to_string());
|
||||
|
||||
let names = vec!["theme", "front_page", "layout", "wide", "comment_sort", "post_sort", "show_nsfw"];
|
||||
|
||||
for name in names {
|
||||
for &name in &PREFS {
|
||||
match form.get(name) {
|
||||
Some(value) => res.insert_cookie(
|
||||
Cookie::build(name.to_owned(), value.to_owned())
|
||||
@ -68,8 +81,7 @@ pub async fn set(req: Request<Body>) -> Result<Response<Body>, String> {
|
||||
Ok(res)
|
||||
}
|
||||
|
||||
// Set cookies using response "Set-Cookie" header
|
||||
pub async fn restore(req: Request<Body>) -> Result<Response<Body>, String> {
|
||||
fn set_cookies_method(req: Request<Body>, remove_cookies: bool) -> Response<Body> {
|
||||
// Split the body into parts
|
||||
let (parts, _) = req.into_parts();
|
||||
|
||||
@ -85,16 +97,14 @@ pub async fn restore(req: Request<Body>) -> Result<Response<Body>, String> {
|
||||
|
||||
let form = url::form_urlencoded::parse(query).collect::<HashMap<_, _>>();
|
||||
|
||||
let names = vec!["theme", "front_page", "layout", "wide", "comment_sort", "post_sort", "show_nsfw", "subscriptions"];
|
||||
|
||||
let path = match form.get("redirect") {
|
||||
Some(value) => format!("/{}/", value),
|
||||
Some(value) => format!("/{}", value.replace("%26", "&").replace("%23", "#")),
|
||||
None => "/".to_string(),
|
||||
};
|
||||
|
||||
let mut res = redirect(path);
|
||||
|
||||
for name in names {
|
||||
for &name in &PREFS {
|
||||
match form.get(name) {
|
||||
Some(value) => res.insert_cookie(
|
||||
Cookie::build(name.to_owned(), value.to_owned())
|
||||
@ -103,9 +113,22 @@ pub async fn restore(req: Request<Body>) -> Result<Response<Body>, String> {
|
||||
.expires(OffsetDateTime::now_utc() + Duration::weeks(52))
|
||||
.finish(),
|
||||
),
|
||||
None => res.remove_cookie(name.to_string()),
|
||||
None => {
|
||||
if remove_cookies {
|
||||
res.remove_cookie(name.to_string())
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
Ok(res)
|
||||
res
|
||||
}
|
||||
|
||||
// Set cookies using response "Set-Cookie" header
|
||||
pub async fn restore(req: Request<Body>) -> Result<Response<Body>, String> {
|
||||
Ok(set_cookies_method(req, true))
|
||||
}
|
||||
|
||||
pub async fn update(req: Request<Body>) -> Result<Response<Body>, String> {
|
||||
Ok(set_cookies_method(req, false))
|
||||
}
|
||||
|
102
src/subreddit.rs
102
src/subreddit.rs
@ -1,6 +1,6 @@
|
||||
// CRATES
|
||||
use crate::esc;
|
||||
use crate::utils::{cookie, error, format_num, format_url, param, redirect, rewrite_urls, template, val, Post, Preferences, Subreddit};
|
||||
use crate::utils::{catch_random, error, format_num, format_url, param, redirect, rewrite_urls, setting, template, val, Post, Preferences, Subreddit};
|
||||
use crate::{client::json, server::ResponseExt, RequestExt};
|
||||
use askama::Template;
|
||||
use cookie::Cookie;
|
||||
@ -16,6 +16,7 @@ struct SubredditTemplate {
|
||||
sort: (String, String),
|
||||
ends: (String, String),
|
||||
prefs: Preferences,
|
||||
url: String,
|
||||
}
|
||||
|
||||
#[derive(Template)]
|
||||
@ -30,8 +31,8 @@ struct WikiTemplate {
|
||||
// SERVICES
|
||||
pub async fn community(req: Request<Body>) -> Result<Response<Body>, String> {
|
||||
// Build Reddit API path
|
||||
let subscribed = cookie(&req, "subscriptions");
|
||||
let front_page = cookie(&req, "front_page");
|
||||
let subscribed = setting(&req, "subscriptions");
|
||||
let front_page = setting(&req, "front_page");
|
||||
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));
|
||||
|
||||
@ -45,6 +46,11 @@ pub async fn community(req: Request<Body>) -> Result<Response<Body>, String> {
|
||||
front_page.to_owned()
|
||||
});
|
||||
|
||||
// Handle random subreddits
|
||||
if let Ok(random) = catch_random(&sub, "").await {
|
||||
return Ok(random);
|
||||
}
|
||||
|
||||
if req.param("sub").is_some() && sub.starts_with("u_") {
|
||||
return Ok(redirect(["/user/", &sub[2..]].concat()));
|
||||
}
|
||||
@ -74,12 +80,15 @@ pub async fn community(req: Request<Body>) -> Result<Response<Body>, String> {
|
||||
Subreddit::default()
|
||||
};
|
||||
|
||||
let url = String::from(req.uri().path_and_query().map_or("", |val| val.as_str()));
|
||||
|
||||
template(SubredditTemplate {
|
||||
sub,
|
||||
posts,
|
||||
sort: (sort, param(&path, "t")),
|
||||
ends: (param(&path, "after"), after),
|
||||
prefs: Preferences::new(req),
|
||||
url,
|
||||
})
|
||||
}
|
||||
Err(msg) => match msg.as_str() {
|
||||
@ -94,13 +103,45 @@ pub async fn community(req: Request<Body>) -> Result<Response<Body>, String> {
|
||||
// Sub or unsub by setting subscription cookie using response "Set-Cookie" header
|
||||
pub async fn subscriptions(req: Request<Body>) -> Result<Response<Body>, String> {
|
||||
let sub = req.param("sub").unwrap_or_default();
|
||||
// Handle random subreddits
|
||||
if sub == "random" || sub == "randnsfw" {
|
||||
return Err("Can't subscribe to random subreddit!".to_string());
|
||||
}
|
||||
|
||||
let query = req.uri().query().unwrap_or_default().to_string();
|
||||
let action: Vec<String> = req.uri().path().split('/').map(String::from).collect();
|
||||
|
||||
let mut sub_list = Preferences::new(req).subscriptions;
|
||||
|
||||
// Retrieve list of posts for these subreddits to extract display names
|
||||
let posts = json(format!("/r/{}/hot.json?raw_json=1", sub)).await?;
|
||||
let display_lookup: Vec<(String, &str)> = posts["data"]["children"]
|
||||
.as_array()
|
||||
.map(|list| {
|
||||
list
|
||||
.iter()
|
||||
.map(|post| {
|
||||
let display_name = post["data"]["subreddit"].as_str().unwrap_or_default();
|
||||
(display_name.to_lowercase(), display_name)
|
||||
})
|
||||
.collect::<Vec<_>>()
|
||||
})
|
||||
.unwrap_or_default();
|
||||
|
||||
// Find each subreddit name (separated by '+') in sub parameter
|
||||
for part in sub.split('+') {
|
||||
// Retrieve display name for the subreddit
|
||||
let display;
|
||||
let part = if let Some(&(_, display)) = display_lookup.iter().find(|x| x.0 == part.to_lowercase()) {
|
||||
// This is already known, doesn't require seperate request
|
||||
display
|
||||
} else {
|
||||
// This subreddit display name isn't known, retrieve it
|
||||
let path: String = format!("/r/{}/about.json?raw_json=1", part);
|
||||
display = json(path).await?;
|
||||
display["data"]["display_name"].as_str().ok_or_else(|| "Failed to query subreddit name".to_string())?
|
||||
};
|
||||
|
||||
// Modify sub list based on action
|
||||
if action.contains(&"subscribe".to_string()) && !sub_list.contains(&part.to_owned()) {
|
||||
// Add each sub name to the subscribed list
|
||||
@ -142,13 +183,18 @@ pub async fn subscriptions(req: Request<Body>) -> Result<Response<Body>, String>
|
||||
|
||||
pub async fn wiki(req: Request<Body>) -> Result<Response<Body>, String> {
|
||||
let sub = req.param("sub").unwrap_or_else(|| "reddit.com".to_string());
|
||||
// Handle random subreddits
|
||||
if let Ok(random) = catch_random(&sub, "/wiki").await {
|
||||
return Ok(random);
|
||||
}
|
||||
|
||||
let page = req.param("page").unwrap_or_else(|| "index".to_string());
|
||||
let path: String = format!("/r/{}/wiki/{}.json?raw_json=1", sub, page);
|
||||
|
||||
match json(path).await {
|
||||
Ok(response) => template(WikiTemplate {
|
||||
sub,
|
||||
wiki: rewrite_urls(response["data"]["content_html"].as_str().unwrap_or_default()),
|
||||
wiki: rewrite_urls(response["data"]["content_html"].as_str().unwrap_or("<h3>Wiki not found</h3>")),
|
||||
page,
|
||||
prefs: Preferences::new(req),
|
||||
}),
|
||||
@ -158,6 +204,10 @@ pub async fn wiki(req: Request<Body>) -> Result<Response<Body>, String> {
|
||||
|
||||
pub async fn sidebar(req: Request<Body>) -> Result<Response<Body>, String> {
|
||||
let sub = req.param("sub").unwrap_or_else(|| "reddit.com".to_string());
|
||||
// Handle random subreddits
|
||||
if let Ok(random) = catch_random(&sub, "/about/sidebar").await {
|
||||
return Ok(random);
|
||||
}
|
||||
|
||||
// Build the Reddit JSON API url
|
||||
let path: String = format!("/r/{}/about.json?raw_json=1", sub);
|
||||
@ -166,8 +216,12 @@ pub async fn sidebar(req: Request<Body>) -> Result<Response<Body>, String> {
|
||||
match json(path).await {
|
||||
// If success, receive JSON in response
|
||||
Ok(response) => template(WikiTemplate {
|
||||
wiki: format!(
|
||||
"{}<hr><h1>Moderators</h1><br><ul>{}</ul>",
|
||||
rewrite_urls(&val(&response, "description_html").replace("\\", "")),
|
||||
moderators(&sub).await?.join(""),
|
||||
),
|
||||
sub,
|
||||
wiki: rewrite_urls(&val(&response, "description_html").replace("\\", "")),
|
||||
page: "Sidebar".to_string(),
|
||||
prefs: Preferences::new(req),
|
||||
}),
|
||||
@ -175,6 +229,41 @@ pub async fn sidebar(req: Request<Body>) -> Result<Response<Body>, String> {
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn moderators(sub: &str) -> Result<Vec<String>, String> {
|
||||
// Retrieve and format the html for the moderators list
|
||||
Ok(
|
||||
moderators_list(sub)
|
||||
.await?
|
||||
.iter()
|
||||
.map(|m| format!("<li><a style=\"color: var(--accent)\" href=\"/u/{name}\">{name}</a></li>", name = m))
|
||||
.collect(),
|
||||
)
|
||||
}
|
||||
|
||||
async fn moderators_list(sub: &str) -> Result<Vec<String>, String> {
|
||||
// Build the moderator list URL
|
||||
let path: String = format!("/r/{}/about/moderators.json?raw_json=1", sub);
|
||||
|
||||
// Retrieve response
|
||||
let response = json(path).await?["data"]["children"].clone();
|
||||
Ok(
|
||||
// Traverse json tree and format into list of strings
|
||||
response
|
||||
.as_array()
|
||||
.unwrap_or(&Vec::new())
|
||||
.iter()
|
||||
.filter_map(|moderator| {
|
||||
let name = moderator["name"].as_str().unwrap_or_default();
|
||||
if name.is_empty() {
|
||||
None
|
||||
} else {
|
||||
Some(name.to_string())
|
||||
}
|
||||
})
|
||||
.collect::<Vec<_>>(),
|
||||
)
|
||||
}
|
||||
|
||||
// SUBREDDIT
|
||||
async fn subreddit(sub: &str) -> Result<Subreddit, String> {
|
||||
// Build the Reddit JSON API url
|
||||
@ -189,7 +278,7 @@ async fn subreddit(sub: &str) -> Result<Subreddit, String> {
|
||||
let active: i64 = res["data"]["accounts_active"].as_u64().unwrap_or_default() as i64;
|
||||
|
||||
// Fetch subreddit icon either from the community_icon or icon_img value
|
||||
let community_icon: &str = res["data"]["community_icon"].as_str().map_or("", |s| s.split('?').collect::<Vec<&str>>()[0]);
|
||||
let community_icon: &str = res["data"]["community_icon"].as_str().unwrap_or_default();
|
||||
let icon = if community_icon.is_empty() { val(&res, "icon_img") } else { community_icon.to_string() };
|
||||
|
||||
let sub = Subreddit {
|
||||
@ -197,6 +286,7 @@ async fn subreddit(sub: &str) -> Result<Subreddit, String> {
|
||||
title: esc!(&res, "title"),
|
||||
description: esc!(&res, "public_description"),
|
||||
info: rewrite_urls(&val(&res, "description_html").replace("\\", "")),
|
||||
moderators: moderators_list(sub).await?,
|
||||
icon: format_url(&icon),
|
||||
members: format_num(members),
|
||||
active: format_num(active),
|
||||
|
@ -16,6 +16,7 @@ struct UserTemplate {
|
||||
sort: (String, String),
|
||||
ends: (String, String),
|
||||
prefs: Preferences,
|
||||
url: String,
|
||||
}
|
||||
|
||||
// FUNCTIONS
|
||||
@ -33,6 +34,7 @@ pub async fn profile(req: Request<Body>) -> Result<Response<Body>, String> {
|
||||
|
||||
// Request user posts/comments from Reddit
|
||||
let posts = Post::fetch(&path, "Comment".to_string()).await;
|
||||
let url = String::from(req.uri().path_and_query().map_or("", |val| val.as_str()));
|
||||
|
||||
match posts {
|
||||
Ok((posts, after)) => {
|
||||
@ -45,6 +47,7 @@ pub async fn profile(req: Request<Body>) -> Result<Response<Body>, String> {
|
||||
sort: (sort, param(&path, "t")),
|
||||
ends: (param(&path, "after"), after),
|
||||
prefs: Preferences::new(req),
|
||||
url,
|
||||
})
|
||||
}
|
||||
// If there is an error show error page
|
||||
|
136
src/utils.rs
136
src/utils.rs
@ -75,6 +75,7 @@ pub struct Flags {
|
||||
|
||||
pub struct Media {
|
||||
pub url: String,
|
||||
pub alt_url: String,
|
||||
pub width: i64,
|
||||
pub height: i64,
|
||||
pub poster: String,
|
||||
@ -85,12 +86,28 @@ impl Media {
|
||||
let mut gallery = Vec::new();
|
||||
|
||||
// If post is a video, return the video
|
||||
let (post_type, url_val) = if data["preview"]["reddit_video_preview"]["fallback_url"].is_string() {
|
||||
let (post_type, url_val, alt_url_val) = if data["preview"]["reddit_video_preview"]["fallback_url"].is_string() {
|
||||
// Return reddit video
|
||||
("video", &data["preview"]["reddit_video_preview"]["fallback_url"])
|
||||
(
|
||||
if data["preview"]["reddit_video_preview"]["is_gif"].as_bool().unwrap_or(false) {
|
||||
"gif"
|
||||
} else {
|
||||
"video"
|
||||
},
|
||||
&data["preview"]["reddit_video_preview"]["fallback_url"],
|
||||
Some(&data["preview"]["reddit_video_preview"]["hls_url"]),
|
||||
)
|
||||
} else if data["secure_media"]["reddit_video"]["fallback_url"].is_string() {
|
||||
// Return reddit video
|
||||
("video", &data["secure_media"]["reddit_video"]["fallback_url"])
|
||||
(
|
||||
if data["preview"]["reddit_video_preview"]["is_gif"].as_bool().unwrap_or(false) {
|
||||
"gif"
|
||||
} else {
|
||||
"video"
|
||||
},
|
||||
&data["secure_media"]["reddit_video"]["fallback_url"],
|
||||
Some(&data["secure_media"]["reddit_video"]["hls_url"]),
|
||||
)
|
||||
} else if data["post_hint"].as_str().unwrap_or("") == "image" {
|
||||
// Handle images, whether GIFs or pics
|
||||
let preview = &data["preview"]["images"][0];
|
||||
@ -98,26 +115,26 @@ impl Media {
|
||||
|
||||
if mp4.is_object() {
|
||||
// Return the mp4 if the media is a gif
|
||||
("gif", &mp4["source"]["url"])
|
||||
("gif", &mp4["source"]["url"], None)
|
||||
} else {
|
||||
// Return the picture if the media is an image
|
||||
if data["domain"] == "i.redd.it" {
|
||||
("image", &data["url"])
|
||||
("image", &data["url"], None)
|
||||
} else {
|
||||
("image", &preview["source"]["url"])
|
||||
("image", &preview["source"]["url"], None)
|
||||
}
|
||||
}
|
||||
} else if data["is_self"].as_bool().unwrap_or_default() {
|
||||
// If type is self, return permalink
|
||||
("self", &data["permalink"])
|
||||
("self", &data["permalink"], None)
|
||||
} else if data["is_gallery"].as_bool().unwrap_or_default() {
|
||||
// If this post contains a gallery of images
|
||||
gallery = GalleryMedia::parse(&data["gallery_data"]["items"], &data["media_metadata"]);
|
||||
|
||||
("gallery", &data["url"])
|
||||
("gallery", &data["url"], None)
|
||||
} else {
|
||||
// If type can't be determined, return url
|
||||
("link", &data["url"])
|
||||
("link", &data["url"], None)
|
||||
};
|
||||
|
||||
let source = &data["preview"]["images"][0]["source"];
|
||||
@ -128,10 +145,13 @@ impl Media {
|
||||
format_url(url_val.as_str().unwrap_or_default())
|
||||
};
|
||||
|
||||
let alt_url = alt_url_val.map_or(String::new(), |val| format_url(val.as_str().unwrap_or_default()));
|
||||
|
||||
(
|
||||
post_type.to_string(),
|
||||
Self {
|
||||
url,
|
||||
alt_url,
|
||||
width: source["width"].as_i64().unwrap_or_default(),
|
||||
height: source["height"].as_i64().unwrap_or_default(),
|
||||
poster: format_url(source["url"].as_str().unwrap_or_default()),
|
||||
@ -259,6 +279,7 @@ impl Post {
|
||||
post_type,
|
||||
thumbnail: Media {
|
||||
url: format_url(val(post, "thumbnail").as_str()),
|
||||
alt_url: String::new(),
|
||||
width: data["thumbnail_width"].as_i64().unwrap_or_default(),
|
||||
height: data["thumbnail_height"].as_i64().unwrap_or_default(),
|
||||
poster: "".to_string(),
|
||||
@ -341,6 +362,7 @@ pub struct Subreddit {
|
||||
pub title: String,
|
||||
pub description: String,
|
||||
pub info: String,
|
||||
pub moderators: Vec<String>,
|
||||
pub icon: String,
|
||||
pub members: (String, String),
|
||||
pub active: (String, String),
|
||||
@ -364,6 +386,8 @@ pub struct Preferences {
|
||||
pub layout: String,
|
||||
pub wide: String,
|
||||
pub show_nsfw: String,
|
||||
pub hide_hls_notification: String,
|
||||
pub use_hls: String,
|
||||
pub comment_sort: String,
|
||||
pub post_sort: String,
|
||||
pub subscriptions: Vec<String>,
|
||||
@ -373,14 +397,16 @@ impl Preferences {
|
||||
// Build preferences from cookies
|
||||
pub fn new(req: Request<Body>) -> Self {
|
||||
Self {
|
||||
theme: cookie(&req, "theme"),
|
||||
front_page: cookie(&req, "front_page"),
|
||||
layout: cookie(&req, "layout"),
|
||||
wide: cookie(&req, "wide"),
|
||||
show_nsfw: cookie(&req, "show_nsfw"),
|
||||
comment_sort: cookie(&req, "comment_sort"),
|
||||
post_sort: cookie(&req, "post_sort"),
|
||||
subscriptions: cookie(&req, "subscriptions").split('+').map(String::from).filter(|s| !s.is_empty()).collect(),
|
||||
theme: setting(&req, "theme"),
|
||||
front_page: setting(&req, "front_page"),
|
||||
layout: setting(&req, "layout"),
|
||||
wide: setting(&req, "wide"),
|
||||
show_nsfw: setting(&req, "show_nsfw"),
|
||||
use_hls: setting(&req, "use_hls"),
|
||||
hide_hls_notification: setting(&req, "hide_hls_notification"),
|
||||
comment_sort: setting(&req, "comment_sort"),
|
||||
post_sort: setting(&req, "post_sort"),
|
||||
subscriptions: setting(&req, "subscriptions").split('+').map(String::from).filter(|s| !s.is_empty()).collect(),
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -397,10 +423,34 @@ pub fn param(path: &str, value: &str) -> String {
|
||||
}
|
||||
}
|
||||
|
||||
// Parse a cookie value from request
|
||||
pub fn cookie(req: &Request<Body>, name: &str) -> String {
|
||||
let cookie = req.cookie(name).unwrap_or_else(|| Cookie::named(name));
|
||||
cookie.value().to_string()
|
||||
// Retrieve the value of a setting by name
|
||||
pub fn setting(req: &Request<Body>, name: &str) -> String {
|
||||
// Parse a cookie value from request
|
||||
req
|
||||
.cookie(name)
|
||||
.unwrap_or_else(|| {
|
||||
// If there is no cookie for this setting, try receiving a default from an environment variable
|
||||
if let Ok(default) = std::env::var(format!("LIBREDDIT_DEFAULT_{}", name.to_uppercase())) {
|
||||
Cookie::new(name, default)
|
||||
} else {
|
||||
Cookie::named(name)
|
||||
}
|
||||
})
|
||||
.value()
|
||||
.to_string()
|
||||
}
|
||||
|
||||
// Detect and redirect in the event of a random subreddit
|
||||
pub async fn catch_random(sub: &str, additional: &str) -> Result<Response<Body>, String> {
|
||||
if (sub == "random" || sub == "randnsfw") && !sub.contains('+') {
|
||||
let new_sub = json(format!("/r/{}/about.json?raw_json=1", sub)).await?["data"]["display_name"]
|
||||
.as_str()
|
||||
.unwrap_or_default()
|
||||
.to_string();
|
||||
Ok(redirect(format!("/r/{}{}", new_sub, additional)))
|
||||
} else {
|
||||
Err("No redirect needed".to_string())
|
||||
}
|
||||
}
|
||||
|
||||
// Direct urls to proxy if proxy is enabled
|
||||
@ -425,8 +475,32 @@ pub fn format_url(url: &str) -> String {
|
||||
.unwrap_or_default()
|
||||
};
|
||||
|
||||
macro_rules! chain {
|
||||
() => {
|
||||
{
|
||||
String::new()
|
||||
}
|
||||
};
|
||||
|
||||
( $first_fn:expr, $($other_fns:expr), *) => {
|
||||
{
|
||||
let result = $first_fn;
|
||||
if result.is_empty() {
|
||||
chain!($($other_fns,)*)
|
||||
}
|
||||
else
|
||||
{
|
||||
result
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
match domain {
|
||||
"v.redd.it" => capture(r"https://v\.redd\.it/(.*)/DASH_([0-9]{2,4}(\.mp4|$))", "/vid/", 2),
|
||||
"v.redd.it" => chain!(
|
||||
capture(r"https://v\.redd\.it/(.*)/DASH_([0-9]{2,4}(\.mp4|$))", "/vid/", 2),
|
||||
capture(r"https://v\.redd\.it/(.+)/(HLSPlaylist\.m3u8.*)$", "/hls/", 2)
|
||||
),
|
||||
"i.redd.it" => capture(r"https://i\.redd\.it/(.*)", "/img/", 1),
|
||||
"a.thumbs.redditmedia.com" => capture(r"https://a\.thumbs\.redditmedia\.com/(.*)", "/thumb/a/", 1),
|
||||
"b.thumbs.redditmedia.com" => capture(r"https://b\.thumbs\.redditmedia\.com/(.*)", "/thumb/b/", 1),
|
||||
@ -444,9 +518,21 @@ pub fn format_url(url: &str) -> String {
|
||||
}
|
||||
|
||||
// Rewrite Reddit links to Libreddit in body of text
|
||||
pub fn rewrite_urls(text: &str) -> String {
|
||||
match Regex::new(r#"href="(https|http|)://(www.|old.|np.|amp.|)(reddit).(com)/"#) {
|
||||
Ok(re) => re.replace_all(text, r#"href="/"#).to_string(),
|
||||
pub fn rewrite_urls(input_text: &str) -> String {
|
||||
let text1 = match Regex::new(r#"href="(https|http|)://(www.|old.|np.|amp.|)(reddit).(com)/"#) {
|
||||
Ok(re) => re.replace_all(input_text, r#"href="/"#).to_string(),
|
||||
Err(_) => String::new(),
|
||||
};
|
||||
|
||||
// Rewrite external media previews to Libreddit
|
||||
match Regex::new(r"https://external-preview\.redd\.it(.*)[^?]") {
|
||||
Ok(re) => {
|
||||
if re.is_match(&text1) {
|
||||
re.replace_all(&text1, format_url(re.find(&text1).map(|x| x.as_str()).unwrap_or_default())).to_string()
|
||||
} else {
|
||||
text1
|
||||
}
|
||||
}
|
||||
Err(_) => String::new(),
|
||||
}
|
||||
}
|
||||
|
5
static/hls.min.js
vendored
Normal file
5
static/hls.min.js
vendored
Normal file
File diff suppressed because one or more lines are too long
77
static/playHLSVideo.js
Normal file
77
static/playHLSVideo.js
Normal file
@ -0,0 +1,77 @@
|
||||
// @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)) {
|
||||
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;
|
||||
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);
|
||||
});
|
||||
}
|
||||
|
||||
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
|
@ -92,6 +92,49 @@
|
||||
--highlighted: #3b4252;
|
||||
--shadow: 0 1px 3px rgba(0, 0, 0, 0.1);
|
||||
}
|
||||
|
||||
/* Laserwave theme setting */
|
||||
.laserwave {
|
||||
--accent: #eb64b9;
|
||||
--green: #74dfc4;
|
||||
--text: #e0dfe1;
|
||||
--foreground: #302a36;
|
||||
--background: #27212e;
|
||||
--outside: #3e3647;
|
||||
--post: #3e3647;
|
||||
--panel-border: 2px solid #2f2738;
|
||||
--highlighted: #302a36;
|
||||
--shadow: 0 1px 3px rgba(0, 0, 0, 0.1);
|
||||
}
|
||||
|
||||
/* Violet theme setting */
|
||||
.violet {
|
||||
--accent: #7c71dd;
|
||||
--green: #5cff85;
|
||||
--text: white;
|
||||
--foreground: #1F2347;
|
||||
--background: #12152b;
|
||||
--outside: #181c3a;
|
||||
--post: #181c3a;
|
||||
--panel-border: 1px solid #1F2347;
|
||||
--highlighted: #1F2347;
|
||||
--shadow: 0 2px 5px rgba(0, 0, 0, 0.5);
|
||||
}
|
||||
|
||||
/* Gold theme setting */
|
||||
.gold {
|
||||
--accent: #f2aa4c;
|
||||
--green: #5cff85;
|
||||
--text: white;
|
||||
--foreground: #234;
|
||||
--background: #101820;
|
||||
--outside: #1b2936;
|
||||
--post: #1b2936;
|
||||
--panel-border: 0px solid black;
|
||||
--highlighted: #234;
|
||||
--shadow: 0 2px 5px rgba(0, 0, 0, 0.5);
|
||||
}
|
||||
|
||||
/* General */
|
||||
|
||||
::selection {
|
||||
@ -621,6 +664,7 @@ a.search_subreddit:hover {
|
||||
"post_score post_title post_thumbnail" 1fr
|
||||
"post_score post_media post_thumbnail" auto
|
||||
"post_score post_body post_thumbnail" auto
|
||||
"post_score post_notification post_thumbnail" auto
|
||||
"post_score post_footer post_thumbnail" auto
|
||||
/ minmax(40px, auto) minmax(0, 1fr) fit-content(min(20%, 152px));
|
||||
}
|
||||
@ -663,6 +707,17 @@ a.search_subreddit:hover {
|
||||
grid-area: post_title;
|
||||
}
|
||||
|
||||
.post_notification {
|
||||
grid-area: post_notification;
|
||||
margin: 5px 15px;
|
||||
text-align: center;
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
.post_notification a {
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
.post_flair {
|
||||
background: var(--accent);
|
||||
color: var(--background);
|
||||
@ -1129,6 +1184,8 @@ input[type="submit"] {
|
||||
|
||||
.md table {
|
||||
margin: 5px;
|
||||
display: block;
|
||||
overflow-x: auto;
|
||||
}
|
||||
|
||||
.md code {
|
||||
@ -1211,6 +1268,7 @@ td, th {
|
||||
"post_title post_title post_thumbnail" 1fr
|
||||
"post_media post_media post_thumbnail" auto
|
||||
"post_body post_body post_thumbnail" auto
|
||||
"post_notification post_notification post_thumbnail" auto
|
||||
"post_score post_footer post_thumbnail" auto
|
||||
/ auto 1fr fit-content(min(20%, 152px));
|
||||
}
|
||||
|
@ -68,7 +68,17 @@
|
||||
</svg>
|
||||
</a>
|
||||
{% else if post.post_type == "video" || post.post_type == "gif" %}
|
||||
{% if prefs.use_hls == "on" && !post.media.alt_url.is_empty() %}
|
||||
<script src="/hls.min.js"></script>
|
||||
<video class="post_media_video short hls_autoplay" width="{{ post.media.width }}" height="{{ post.media.height }}" poster="{{ post.media.poster }}" controls preload="none">
|
||||
<source src="{{ post.media.alt_url }}" type="application/vnd.apple.mpegurl" />
|
||||
<source src="{{ post.media.url }}" type="video/mp4" />
|
||||
</video>
|
||||
<script src="/playHLSVideo.js"></script>
|
||||
{% else %}
|
||||
<video class="post_media_video" src="{{ post.media.url }}" controls autoplay loop><a href={{ post.media.url }}>Video</a></video>
|
||||
{% call utils::render_hls_notification(post.permalink[1..]) %}
|
||||
{% endif %}
|
||||
{% else if post.post_type == "gallery" %}
|
||||
<div class="gallery">
|
||||
{% for image in post.gallery -%}
|
||||
@ -77,14 +87,14 @@
|
||||
<figcaption>
|
||||
<p>{{ image.caption }}</p>
|
||||
{% if image.outbound_url.len() > 0 %}
|
||||
<p><a class="outbound_url" href="{{ image.outbound_url }}">{{ image.outbound_url }}</a>
|
||||
<p><a class="outbound_url" href="{{ image.outbound_url }}" rel="nofollow">{{ image.outbound_url }}</a>
|
||||
{% endif %}
|
||||
</figcaption>
|
||||
</figure>
|
||||
{%- endfor %}
|
||||
</div>
|
||||
{% else if post.post_type == "link" %}
|
||||
<a id="post_url" href="{{ post.media.url }}">{{ post.media.url }}</a>
|
||||
<a id="post_url" href="{{ post.media.url }}" rel="nofollow">{{ post.media.url }}</a>
|
||||
{% endif %}
|
||||
|
||||
<!-- POST BODY -->
|
||||
@ -93,7 +103,7 @@
|
||||
<div class="post_footer">
|
||||
<ul id="post_links">
|
||||
<li><a href="/{{ post.id }}">permalink</a></li>
|
||||
<li><a href="https://reddit.com/{{ post.id }}">reddit</a></li>
|
||||
<li><a href="https://reddit.com/{{ post.id }}" rel="nofollow">reddit</a></li>
|
||||
</ul>
|
||||
<p>{{ post.upvote_ratio }}% Upvoted</p>
|
||||
</div>
|
||||
|
@ -15,7 +15,7 @@
|
||||
<div id="theme">
|
||||
<label for="theme">Theme:</label>
|
||||
<select name="theme">
|
||||
{% call utils::options(prefs.theme, ["system", "light", "dark", "black", "dracula", "nord"], "system") %}
|
||||
{% call utils::options(prefs.theme, ["system", "light", "dark", "black", "dracula", "nord", "laserwave", "violet", "gold"], "system") %}
|
||||
</select>
|
||||
</div>
|
||||
<p>Interface</p>
|
||||
@ -33,6 +33,7 @@
|
||||
</div>
|
||||
<div id="wide">
|
||||
<label for="wide">Wide UI:</label>
|
||||
<input type="hidden" value="off" name="wide">
|
||||
<input type="checkbox" name="wide" {% if prefs.wide == "on" %}checked{% endif %}>
|
||||
</div>
|
||||
<p>Content</p>
|
||||
@ -50,8 +51,19 @@
|
||||
</div>
|
||||
<div id="show_nsfw">
|
||||
<label for="show_nsfw">Show NSFW posts:</label>
|
||||
<input type="hidden" value="off" name="show_nsfw">
|
||||
<input type="checkbox" name="show_nsfw" {% if prefs.show_nsfw == "on" %}checked{% endif %}>
|
||||
</div>
|
||||
<div id="use_hls">
|
||||
<label for="use_hls">Use HLS for videos</label>
|
||||
<input type="hidden" value="off" name="use_hls">
|
||||
<input type="checkbox" name="use_hls" {% if prefs.use_hls == "on" %}checked{% endif %}>
|
||||
</div>
|
||||
<div id="hide_hls_notification">
|
||||
<label for="hide_hls_notification">Hide notification about possible HLS usage</label>
|
||||
<input type="hidden" value="off" name="hide_hls_notification">
|
||||
<input type="checkbox" name="hide_hls_notification" {% if prefs.hide_hls_notification == "on" %}checked{% endif %}>
|
||||
</div>
|
||||
<input id="save" type="submit" value="Save">
|
||||
</div>
|
||||
</form>
|
||||
@ -68,10 +80,10 @@
|
||||
{% endfor %}
|
||||
</div>
|
||||
{% 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 }}&front_page={{ prefs.front_page }}&layout={{ prefs.layout }}&wide={{ prefs.wide }}&comment_sort={{ prefs.comment_sort }}&show_nsfw={{ prefs.show_nsfw }}&subscriptions={{ prefs.subscriptions.join("%2B") }}">this link</a>.</p>
|
||||
<p>You can restore your current settings and subscriptions after clearing your cookies using <a href="/settings/restore/?theme={{ prefs.theme }}&front_page={{ prefs.front_page }}&layout={{ prefs.layout }}&wide={{ prefs.wide }}&comment_sort={{ prefs.comment_sort }}&show_nsfw={{ prefs.show_nsfw }}&use_hls={{ prefs.use_hls }}&hide_hls_notification={{ prefs.hide_hls_notification }}&subscriptions={{ prefs.subscriptions.join("%2B") }}">this link</a>.</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
@ -52,6 +52,10 @@
|
||||
{% call utils::post_in_list(post) %}
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
{% if prefs.use_hls == "on" %}
|
||||
<script src="/hls.min.js"></script>
|
||||
<script src="/playHLSVideo.js"></script>
|
||||
{% endif %}
|
||||
</div>
|
||||
|
||||
<footer>
|
||||
@ -99,7 +103,17 @@
|
||||
</div>
|
||||
<details class="panel" id="sidebar">
|
||||
<summary id="sidebar_label">Sidebar</summary>
|
||||
<div id="sidebar_contents">{{ sub.info }}</div>
|
||||
<div id="sidebar_contents">
|
||||
{{ sub.info }}
|
||||
<hr>
|
||||
<h2>Moderators</h2>
|
||||
<br>
|
||||
<ul>
|
||||
{% for moderator in sub.moderators %}
|
||||
<li><a style="color: var(--accent)" href="/u/{{ moderator }}">{{ moderator }}</a></li>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
</div>
|
||||
</details>
|
||||
</aside>
|
||||
{% endif %}
|
||||
|
@ -55,8 +55,14 @@
|
||||
{% endif %}
|
||||
{%- endmacro %}
|
||||
|
||||
{% macro render_hls_notification(redirect_url) -%}
|
||||
{% if post.post_type == "video" && !post.media.alt_url.is_empty() && prefs.hide_hls_notification != "on" %}
|
||||
<div class="post_notification"><p><a href="/settings/update/?use_hls=on&redirect={{ redirect_url }}">Enable HSL</a> to view with audio, or <a href="/settings/update/?hide_hls_notification=on&redirect={{ redirect_url }}">disable this notification</a></p></div>
|
||||
{% endif %}
|
||||
{%- endmacro %}
|
||||
|
||||
{% macro post_in_list(post) -%}
|
||||
<div class="post {% if post.flags.stickied %}stickied{% endif %}">
|
||||
<div class="post {% if post.flags.stickied %}stickied{% endif %}" id="{{ post.id }}">
|
||||
<p class="post_header">
|
||||
{% let community -%}
|
||||
{% if post.community.starts_with("u_") -%}
|
||||
@ -74,7 +80,8 @@
|
||||
{% if post.flair.flair_parts.len() > 0 %}
|
||||
<a href="/r/{{ post.community }}/search?q=flair_name%3A%22{{ post.flair.text }}%22&restrict_sr=on"
|
||||
class="post_flair"
|
||||
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 }};"
|
||||
dir="ltr">{% call render_flair(post.flair.flair_parts) %}</a>
|
||||
{% endif %}
|
||||
<a href="{{ post.permalink }}">{{ post.title }}</a>{% if post.flags.nsfw %} <small class="nsfw">NSFW</small>{% endif %}
|
||||
</p>
|
||||
@ -94,9 +101,17 @@
|
||||
{% else if (prefs.layout.is_empty() || prefs.layout == "card") && post.post_type == "gif" %}
|
||||
<video class="post_media_video short" src="{{ post.media.url }}" width="{{ post.media.width }}" height="{{ post.media.height }}" controls loop autoplay><a href={{ post.media.url }}>Video</a></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() %}
|
||||
<video class="post_media_video short" width="{{ post.media.width }}" height="{{ post.media.height }}" poster="{{ post.media.poster }}" controls preload="none">
|
||||
<source src="{{ post.media.alt_url }}" type="application/vnd.apple.mpegurl" />
|
||||
<source src="{{ post.media.url }}" type="video/mp4" />
|
||||
</video>
|
||||
{% else %}
|
||||
<video class="post_media_video short" src="{{ post.media.url }}" width="{{ post.media.width }}" height="{{ post.media.height }}" poster="{{ post.media.poster }}" preload="none" controls autoplay><a href={{ post.media.url }}>Video</a></video>
|
||||
{% call render_hls_notification(format!("{}%23{}", &self.url[1..].replace("&", "%26"), post.id)) %}
|
||||
{% endif %}
|
||||
{% else if post.post_type != "self" %}
|
||||
<a class="post_thumbnail {% if post.thumbnail.url.is_empty() %}no_thumbnail{% endif %}" href="{% if post.post_type == "link" %}{{ post.media.url }}{% else %}{{ post.permalink }}{% endif %}">
|
||||
<a class="post_thumbnail {% if post.thumbnail.url.is_empty() %}no_thumbnail{% endif %}" href="{% if post.post_type == "link" %}{{ post.media.url }}{% else %}{{ post.permalink }}{% endif %}" rel="nofollow">
|
||||
{% if post.thumbnail.url.is_empty() %}
|
||||
<svg viewBox="0 0 100 106" width="140" height="53" xmlns="http://www.w3.org/2000/svg">
|
||||
<title>Thumbnail</title>
|
||||
|
Reference in New Issue
Block a user