Compare commits
43 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 4c566b9dc9 | |||
| f707f6d4ae | |||
| b5c2b36adc | |||
| 4418c9c8bd | |||
| 0018b035bd | |||
| 2ca68b4efc | |||
| 741ca48f9c | |||
| fbc0e99f5a | |||
| 4b44ea494f | |||
| 21b31fe20e | |||
| f6202b1837 | |||
| 78a7b2d1bb | |||
| d9ac9c9031 | |||
| ba13e8fd00 | |||
| fbceba8320 | |||
| 833e95e9d7 | |||
| 1e4205b311 | |||
| 99152982f1 | |||
| 889d4b4924 | |||
| 215564523c | |||
| ee67670a89 | |||
| b39f28fab6 | |||
| 31842ef5a0 | |||
| c32055f213 | |||
| 8e4fcce86e | |||
| cbd26a619c | |||
| 7ace885a61 | |||
| acd0b2bcd5 | |||
| d9ce7263f3 | |||
| 18a7018a83 | |||
| 4f17cc7d4f | |||
| 12fb9c35c2 | |||
| 5a96d32af4 | |||
| 86d732d463 | |||
| 1eb5b8bfef | |||
| 223ca959ce | |||
| 7c77c5605d | |||
| 94ec4ebc6d | |||
| 8bbbaa1408 | |||
| 6794a9a765 | |||
| 90c1ea7662 | |||
| ebbf794a6c | |||
| a7f69c55fb |
@@ -22,9 +22,13 @@ jobs:
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
|
||||
- run: |
|
||||
cd backend
|
||||
cargo build --verbose
|
||||
- name: Build rust
|
||||
run: |
|
||||
mkdir /tmp/tmp
|
||||
cp -r ./backend /tmp/tmp
|
||||
rm -rf ./*
|
||||
cp -r /tmp/tmp/backend/* .
|
||||
cargo build --release --verbose
|
||||
|
||||
- uses: docker/setup-buildx-action@v2
|
||||
|
||||
|
||||
@@ -48,7 +48,7 @@ jobs:
|
||||
|
||||
- name: Delete Old Prerelease
|
||||
if: env.OLD_PRE_TAG != 'NULL'
|
||||
uses: dev-drprasad/delete-tag-and-release@v0.2.0
|
||||
uses: dev-drprasad/delete-tag-and-release@v1.0
|
||||
with:
|
||||
tag_name: ${{ env.OLD_PRE_TAG }}
|
||||
env:
|
||||
|
||||
@@ -4,6 +4,11 @@ A cute, feature-rich file listing module to replace nginx's autoindex / fancyind
|
||||
|
||||

|
||||
|
||||
## Demo
|
||||
|
||||
* HyDEV ArchLinux Repo: https://arch.hydev.org/
|
||||
* HyDEV Backup CDN: https://cdn.hydev.org/backup/
|
||||
|
||||
## Features
|
||||
|
||||
* [x] List files
|
||||
@@ -14,11 +19,14 @@ A cute, feature-rich file listing module to replace nginx's autoindex / fancyind
|
||||
* [x] Search
|
||||
* [x] Show 404 page
|
||||
|
||||
**Features requiring a backend**
|
||||
|
||||
* [x] Show image/video previews
|
||||
* [x] Use file binary to determine mime type
|
||||
|
||||
**TODO**
|
||||
|
||||
* [ ] Show image/video previews
|
||||
* [ ] Use file magic instead of file suffix to determine mime type
|
||||
|
||||
* [ ] Play videos
|
||||
|
||||
## How to use
|
||||
|
||||
@@ -42,22 +50,6 @@ This module uses the json file listing api in nginx. If you already have an auto
|
||||
|
||||
The following example serves `/data/file-server` on http path `/`
|
||||
|
||||
**Before:**
|
||||
|
||||
```nginx
|
||||
# ...
|
||||
server_name your.domain.com;
|
||||
|
||||
root /data/file-server;
|
||||
|
||||
location / {
|
||||
fancyindex on;
|
||||
fancyindex_exact_size off;
|
||||
}
|
||||
```
|
||||
|
||||
**After:**
|
||||
|
||||
```nginx
|
||||
# ...
|
||||
server_name your.domain.com;
|
||||
@@ -70,3 +62,7 @@ location / {
|
||||
try_files $uri $uri/index.html /__meowindex__/index.html;
|
||||
}
|
||||
```
|
||||
|
||||
## Advanced Usage
|
||||
|
||||
TODO
|
||||
|
||||
Generated
+275
@@ -97,6 +97,62 @@ version = "1.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
|
||||
|
||||
[[package]]
|
||||
name = "console"
|
||||
version = "0.15.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c3d79fbe8970a77e3e34151cc13d3b3e248aa0faaecb9f6091fa07ebefe5ad60"
|
||||
dependencies = [
|
||||
"encode_unicode",
|
||||
"lazy_static",
|
||||
"libc",
|
||||
"unicode-width",
|
||||
"windows-sys 0.42.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "crossbeam-channel"
|
||||
version = "0.5.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c2dd04ddaf88237dc3b8d8f9a3c1004b506b54b3313403944054d23c0870c521"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"crossbeam-utils",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "crossbeam-deque"
|
||||
version = "0.8.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "715e8152b692bba2d374b53d4875445368fdf21a94751410af607a5ac677d1fc"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"crossbeam-epoch",
|
||||
"crossbeam-utils",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "crossbeam-epoch"
|
||||
version = "0.9.13"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "01a9af1f4c2ef74bb8aa1f7e19706bc72d03598c8a570bb5de72243c7a9d9d5a"
|
||||
dependencies = [
|
||||
"autocfg",
|
||||
"cfg-if",
|
||||
"crossbeam-utils",
|
||||
"memoffset",
|
||||
"scopeguard",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "crossbeam-utils"
|
||||
version = "0.8.14"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4fb766fa798726286dbbb842f174001dab8abc7b627a1dd86e0b7222a95d929f"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "dirs-next"
|
||||
version = "2.0.0"
|
||||
@@ -128,6 +184,18 @@ dependencies = [
|
||||
"proc-macro-error",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "either"
|
||||
version = "1.8.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7fcaabb2fef8c910e7f4c7ce9f67a1283a1715879a7c230ca9d6d1ae31f16d91"
|
||||
|
||||
[[package]]
|
||||
name = "encode_unicode"
|
||||
version = "0.3.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a357d28ed41a50f9c765dbfe56cbc04a64e53e5fc58ba79fbc34c10ef3df831f"
|
||||
|
||||
[[package]]
|
||||
name = "env_logger"
|
||||
version = "0.7.1"
|
||||
@@ -156,6 +224,12 @@ dependencies = [
|
||||
"percent-encoding",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "fuchsia-cprng"
|
||||
version = "0.1.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a06f77d526c1a601b7c4cdd98f54b5eaabffc14d5f2f0296febdc7f357c6d3ba"
|
||||
|
||||
[[package]]
|
||||
name = "futures-channel"
|
||||
version = "0.3.26"
|
||||
@@ -354,12 +428,31 @@ dependencies = [
|
||||
"hashbrown",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "indicatif"
|
||||
version = "0.17.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "cef509aa9bc73864d6756f0d34d35504af3cf0844373afe9b8669a5b8005a729"
|
||||
dependencies = [
|
||||
"console",
|
||||
"number_prefix",
|
||||
"portable-atomic",
|
||||
"rayon",
|
||||
"unicode-width",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "itoa"
|
||||
version = "1.0.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "fad582f4b9e86b6caa621cabeb0963332d92eea04729ab12892c2533951e6440"
|
||||
|
||||
[[package]]
|
||||
name = "lazy_static"
|
||||
version = "1.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646"
|
||||
|
||||
[[package]]
|
||||
name = "lexical-core"
|
||||
version = "0.7.6"
|
||||
@@ -404,6 +497,15 @@ version = "2.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d"
|
||||
|
||||
[[package]]
|
||||
name = "memoffset"
|
||||
version = "0.7.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5de893c32cde5f383baa4c04c5d6dbdd735cfd4a794b0debdb2bb1b421da5ff4"
|
||||
dependencies = [
|
||||
"autocfg",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "meow_index"
|
||||
version = "0.1.0"
|
||||
@@ -411,16 +513,21 @@ dependencies = [
|
||||
"anyhow",
|
||||
"duplicate",
|
||||
"hyper",
|
||||
"indicatif",
|
||||
"log",
|
||||
"path-clean",
|
||||
"pathdiff",
|
||||
"pretty_env_logger",
|
||||
"rayon",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"shlex",
|
||||
"tempdir",
|
||||
"tokio",
|
||||
"toml",
|
||||
"url",
|
||||
"url-escape",
|
||||
"walkdir",
|
||||
"xdg-mime",
|
||||
]
|
||||
|
||||
@@ -472,6 +579,12 @@ dependencies = [
|
||||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "number_prefix"
|
||||
version = "0.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "830b246a0e5f20af87141b25c173cd1b609bd7779a4617d6ec582abaf90870f3"
|
||||
|
||||
[[package]]
|
||||
name = "object"
|
||||
version = "0.30.3"
|
||||
@@ -540,6 +653,12 @@ version = "0.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184"
|
||||
|
||||
[[package]]
|
||||
name = "portable-atomic"
|
||||
version = "0.3.19"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "26f6a7b87c2e435a3241addceeeff740ff8b7e76b74c13bf9acb17fa454ea00b"
|
||||
|
||||
[[package]]
|
||||
name = "pretty_env_logger"
|
||||
version = "0.4.0"
|
||||
@@ -598,6 +717,65 @@ dependencies = [
|
||||
"proc-macro2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rand"
|
||||
version = "0.4.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "552840b97013b1a26992c11eac34bdd778e464601a4c2054b5f0bff7c6761293"
|
||||
dependencies = [
|
||||
"fuchsia-cprng",
|
||||
"libc",
|
||||
"rand_core 0.3.1",
|
||||
"rdrand",
|
||||
"winapi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rand_core"
|
||||
version = "0.3.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7a6fdeb83b075e8266dcc8762c22776f6877a63111121f5f8c7411e5be7eed4b"
|
||||
dependencies = [
|
||||
"rand_core 0.4.2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rand_core"
|
||||
version = "0.4.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9c33a3c44ca05fa6f1807d8e6743f3824e8509beca625669633be0acbdf509dc"
|
||||
|
||||
[[package]]
|
||||
name = "rayon"
|
||||
version = "1.6.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6db3a213adf02b3bcfd2d3846bb41cb22857d131789e01df434fb7e7bc0759b7"
|
||||
dependencies = [
|
||||
"either",
|
||||
"rayon-core",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rayon-core"
|
||||
version = "1.10.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "356a0625f1954f730c0201cdab48611198dc6ce21f4acff55089b5a78e6e835b"
|
||||
dependencies = [
|
||||
"crossbeam-channel",
|
||||
"crossbeam-deque",
|
||||
"crossbeam-utils",
|
||||
"num_cpus",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rdrand"
|
||||
version = "0.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "678054eb77286b51581ba43620cc911abf02758c91f93f479767aed0f90458b2"
|
||||
dependencies = [
|
||||
"rand_core 0.3.1",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "redox_syscall"
|
||||
version = "0.2.16"
|
||||
@@ -635,6 +813,15 @@ version = "0.6.28"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "456c603be3e8d448b072f410900c09faf164fbce2d480456f50eea6e25f9c848"
|
||||
|
||||
[[package]]
|
||||
name = "remove_dir_all"
|
||||
version = "0.5.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3acd125665422973a33ac9d3dd2df85edad0f4ae9b00dafb1a05e43a9f5ef8e7"
|
||||
dependencies = [
|
||||
"winapi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rustc-demangle"
|
||||
version = "0.1.21"
|
||||
@@ -647,6 +834,15 @@ version = "1.0.12"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7b4b9743ed687d4b4bcedf9ff5eaa7398495ae14e61cba0a295704edbc7decde"
|
||||
|
||||
[[package]]
|
||||
name = "same-file"
|
||||
version = "1.0.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "93fc1dc3aaa9bfed95e02e6eadabb4baf7e3078b0bd1b4d7b6b0b68378900502"
|
||||
dependencies = [
|
||||
"winapi-util",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "scopeguard"
|
||||
version = "1.1.0"
|
||||
@@ -684,6 +880,15 @@ dependencies = [
|
||||
"serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serde_spanned"
|
||||
version = "0.6.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0efd8caf556a6cebd3b285caf480045fcc1ac04f6bd786b09a6f11af30c4fcf4"
|
||||
dependencies = [
|
||||
"serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "shlex"
|
||||
version = "1.1.0"
|
||||
@@ -741,6 +946,16 @@ dependencies = [
|
||||
"unicode-ident",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tempdir"
|
||||
version = "0.3.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "15f2b5fb00ccdf689e0149d1b1b3c03fead81c2b37735d812fa8bddbbf41b6d8"
|
||||
dependencies = [
|
||||
"rand",
|
||||
"remove_dir_all",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "termcolor"
|
||||
version = "1.2.0"
|
||||
@@ -830,6 +1045,40 @@ dependencies = [
|
||||
"tracing",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "toml"
|
||||
version = "0.7.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f7afcae9e3f0fe2c370fd4657108972cbb2fa9db1b9f84849cefd80741b01cb6"
|
||||
dependencies = [
|
||||
"serde",
|
||||
"serde_spanned",
|
||||
"toml_datetime",
|
||||
"toml_edit",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "toml_datetime"
|
||||
version = "0.6.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3ab8ed2edee10b50132aed5f331333428b011c99402b5a534154ed15746f9622"
|
||||
dependencies = [
|
||||
"serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "toml_edit"
|
||||
version = "0.19.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9a1eb0622d28f4b9c90adc4ea4b2b46b47663fde9ac5fafcb14a1369d5508825"
|
||||
dependencies = [
|
||||
"indexmap",
|
||||
"serde",
|
||||
"serde_spanned",
|
||||
"toml_datetime",
|
||||
"winnow",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tower-service"
|
||||
version = "0.3.2"
|
||||
@@ -892,6 +1141,12 @@ dependencies = [
|
||||
"tinyvec",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "unicode-width"
|
||||
version = "0.1.10"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c0edd1e5b14653f783770bce4a4dabb4a5108a5370a5f5d8cfe8710c361f6c8b"
|
||||
|
||||
[[package]]
|
||||
name = "url"
|
||||
version = "2.3.1"
|
||||
@@ -918,6 +1173,17 @@ version = "0.9.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f"
|
||||
|
||||
[[package]]
|
||||
name = "walkdir"
|
||||
version = "2.3.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "808cf2735cd4b6866113f648b791c6adc5714537bc222d9347bb203386ffda56"
|
||||
dependencies = [
|
||||
"same-file",
|
||||
"winapi",
|
||||
"winapi-util",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "want"
|
||||
version = "0.3.0"
|
||||
@@ -1046,6 +1312,15 @@ version = "0.42.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "447660ad36a13288b1db4d4248e857b510e8c3a225c822ba4fb748c0aafecffd"
|
||||
|
||||
[[package]]
|
||||
name = "winnow"
|
||||
version = "0.3.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "efdd927d1a3d5d98abcfc4cf8627371862ee6abfe52a988050621c50c66b4493"
|
||||
dependencies = [
|
||||
"memchr",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "xdg-mime"
|
||||
version = "0.3.3"
|
||||
|
||||
@@ -13,14 +13,19 @@ path = "src/test.rs"
|
||||
anyhow = { version = "1.0.69", features = ["backtrace"] }
|
||||
duplicate = "0.4.1"
|
||||
hyper = { version = "0.14", features = ["full"] }
|
||||
indicatif = {version = "*", features = ["rayon"]}
|
||||
log = "0.4.17"
|
||||
path-clean = "0.1.0"
|
||||
pathdiff = "0.2.1"
|
||||
pretty_env_logger = "0.4.0"
|
||||
rayon = "1.6.1"
|
||||
serde = { version = "1", features = ["derive"] }
|
||||
serde_json = "1"
|
||||
shlex = "1.1.0"
|
||||
tempdir = "0.3.7"
|
||||
tokio = { version = "1", features = ["full"] }
|
||||
toml = "0.7.2"
|
||||
url = "2.3.1"
|
||||
url-escape = "0.1.1"
|
||||
walkdir = "2.3.2"
|
||||
xdg-mime = "0.3.3"
|
||||
|
||||
+6
-6
@@ -5,12 +5,6 @@
|
||||
|
||||
FROM debian:sid-slim
|
||||
|
||||
# Copy built files
|
||||
WORKDIR /app
|
||||
#COPY --from=builder /app/target/release/meow_index .
|
||||
COPY ./target/release/meow_index .
|
||||
COPY ./res/thumb/* /usr/share/thumbnailers/
|
||||
|
||||
RUN apt-get update \
|
||||
&& apt-get install -y \
|
||||
# Video preview thumbnailer
|
||||
@@ -30,4 +24,10 @@ RUN apt-get update \
|
||||
ffmpeg \
|
||||
&& rm -rf /var/lib/apt/lists/*
|
||||
|
||||
# Copy built files
|
||||
WORKDIR /app
|
||||
#COPY --from=builder /app/target/release/meow_index .
|
||||
COPY ./target/release/meow_index .
|
||||
COPY ./res/thumb/* /usr/share/thumbnailers/
|
||||
|
||||
CMD ["./meow_index"]
|
||||
|
||||
@@ -0,0 +1,19 @@
|
||||
processes = 6
|
||||
|
||||
[[encoders]]
|
||||
name = "H264 Full VBR35"
|
||||
cmd = "ffmpeg -i {{INPUT}} -nostdin -c:v h264_nvenc -cq:v 35 -rc:v vbr -c:a libmp3lame -pix_fmt yuv420p -color_primaries 1 -color_trc 1 -colorspace 1 -movflags +faststart {{OUTPUT}}"
|
||||
suffix = "h264-vbr35.mp4"
|
||||
|
||||
#[[encoders]]
|
||||
#name = "AV1 Full CRF38"
|
||||
#cmd = "ffmpeg -i {{INPUT}} -nostdin -c:v libsvtav1 -crf 38 -c:a libmp3lame -pix_fmt yuv420p -color_primaries 1 -color_trc 1 -colorspace 1 -movflags +faststart {{OUTPUT}}"
|
||||
#suffix = "av1-crf38.mp4"
|
||||
|
||||
#[[encoders]]
|
||||
#name = "x264 Full CRF32"
|
||||
#cmd = "ffmpeg -i %i -c:v libx264 -crf 32 -c:a aac -pix_fmt yuv420p -color_primaries 1 -color_trc 1 -colorspace 1 -movflags +faststart %o"
|
||||
|
||||
#[[encoders]]
|
||||
#name = "HEVC Full VBR35"
|
||||
|
||||
@@ -0,0 +1,68 @@
|
||||
use std::{env, fs};
|
||||
use std::path::Path;
|
||||
use std::process::{Command, Output};
|
||||
use std::time::Instant;
|
||||
use serde::{Deserialize};
|
||||
use anyhow::{Context, Result};
|
||||
use tempdir::TempDir;
|
||||
use crate::utils::run_cmd;
|
||||
|
||||
#[derive(Deserialize, Debug)]
|
||||
pub struct Encoder {
|
||||
pub(crate) name: String,
|
||||
pub(crate) cmd: String,
|
||||
pub(crate) suffix: String,
|
||||
}
|
||||
|
||||
#[derive(Deserialize, Debug)]
|
||||
pub struct Encoders {
|
||||
pub(crate) processes: usize,
|
||||
pub(crate) encoders: Vec<Encoder>
|
||||
}
|
||||
|
||||
impl Encoder {
|
||||
pub fn execute(&self, orig: &str, out: &str) -> Result<Output> {
|
||||
run_cmd(&*self.cmd
|
||||
.replace("{{INPUT}}", &*shlex::quote(orig))
|
||||
.replace("{{OUTPUT}}", &*shlex::quote(out)))
|
||||
}
|
||||
}
|
||||
|
||||
impl Encoders {
|
||||
pub fn load() -> Result<Encoders> {
|
||||
let file = env::var("MEOW_ENCODING_FILE").unwrap_or("encoding.toml".to_string());
|
||||
let content = fs::read_to_string(file)?;
|
||||
Ok(toml::from_str(&*content)?)
|
||||
}
|
||||
|
||||
pub fn exec_all(&self, orig: &str, out: &Path) -> Result<()> {
|
||||
for enc in &self.encoders {
|
||||
let enc_out = out.with_extension(enc.suffix.to_owned());
|
||||
// Skip if encoded video already exists
|
||||
if enc_out.exists() { continue }
|
||||
|
||||
info!("Encoding '{}' for {orig}...", enc.name);
|
||||
let start = Instant::now();
|
||||
|
||||
// Create tmp (this is to prevent partially completed encoding results being detected as completed)
|
||||
let tmp_dir = TempDir::new("meow_encoder_tmp")?;
|
||||
let tmp_out = tmp_dir.path().join(enc_out.file_name().context("No file name")?);
|
||||
|
||||
// Convert to tmp
|
||||
enc.execute(orig, tmp_out.to_str().context("Call to path.to_str failed")?)?;
|
||||
info!("Done, took {:.2} minutes, copying result...", start.elapsed().as_secs_f32() / 60.0);
|
||||
|
||||
// Copy results
|
||||
if let Some(parent) = enc_out.parent() {
|
||||
fs::create_dir_all(parent)?
|
||||
}
|
||||
fs::copy(tmp_out, enc_out)?;
|
||||
|
||||
// Cleanup tmp
|
||||
tmp_dir.close()?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+48
-19
@@ -1,15 +1,20 @@
|
||||
use std::collections::HashSet;
|
||||
use crate::utils::*;
|
||||
|
||||
use std::fs;
|
||||
use std::os::unix::fs::MetadataExt;
|
||||
use pathdiff::diff_paths;
|
||||
use std::path::{PathBuf};
|
||||
use std::path::{Path, PathBuf};
|
||||
use std::fs::{File, Metadata};
|
||||
use std::fs::DirEntry;
|
||||
use std::io::{BufReader};
|
||||
use xdg_mime::{SharedMimeInfo};
|
||||
use anyhow::{Context, Result};
|
||||
use indicatif::ParallelProgressIterator;
|
||||
use rayon::prelude::*;
|
||||
use rayon::ThreadPoolBuilder;
|
||||
use serde::{de, ser};
|
||||
use walkdir::{WalkDir, DirEntry};
|
||||
use crate::encoder::Encoders;
|
||||
use crate::thumbnailer::{Thumbnailers};
|
||||
|
||||
const DOT_PATH: &str = ".meow_index";
|
||||
@@ -17,12 +22,23 @@ const DOT_PATH: &str = ".meow_index";
|
||||
pub struct Generator {
|
||||
pub(crate) mime_db: SharedMimeInfo,
|
||||
pub(crate) thumbnailers: Thumbnailers,
|
||||
pub(crate) encoders: Encoders,
|
||||
pub(crate) base: PathBuf,
|
||||
video_ext: HashSet<String>
|
||||
}
|
||||
|
||||
impl Generator {
|
||||
pub fn new(base: PathBuf) -> Result<Generator> {
|
||||
Ok(Generator { mime_db: SharedMimeInfo::new(), thumbnailers: Thumbnailers::load_all()?, base: fs::canonicalize(base)? })
|
||||
let video = vec!["webm", "mkv", "flv", "vob", "ogv", "drc", "avi", "mov", "wmv",
|
||||
"yuv", "rm", "rmvb", "amv", "mp4", "m4p", "m4v", "mpg", "mpv", "mp2", "mpeg", "mpe", "3gp"];
|
||||
|
||||
Ok(Generator {
|
||||
mime_db: SharedMimeInfo::new(),
|
||||
thumbnailers: Thumbnailers::load_all()?,
|
||||
encoders: Encoders::load()?,
|
||||
base: fs::canonicalize(base)?,
|
||||
video_ext: HashSet::from_iter(video.into_iter().map(|x| x.to_string())),
|
||||
})
|
||||
}
|
||||
|
||||
/// Get the same file location in DOT_PATH directory
|
||||
@@ -88,24 +104,37 @@ impl Generator {
|
||||
}
|
||||
|
||||
/// Process a directory
|
||||
pub fn process_dir(&self, dir: &PathBuf) -> Result<()> {
|
||||
// List files
|
||||
let files: Vec<(DirEntry, Metadata)> = dir.to_owned().read_dir()?
|
||||
.filter_map(|x| x.ok())
|
||||
.filter_map(|x| {
|
||||
let meta = x.metadata();
|
||||
Some((x, meta.ok()?))
|
||||
}).collect();
|
||||
pub fn encode_dir(&self, dir: &PathBuf) -> Result<()> {
|
||||
// Set number of threads
|
||||
ThreadPoolBuilder::new().num_threads(self.encoders.processes).build_global()?;
|
||||
info!("Activating {} threads", self.encoders.processes);
|
||||
|
||||
// Recurse into directories
|
||||
files.iter().for_each(|(f, _m)| {
|
||||
if f.path().is_dir() {
|
||||
// Recurse into directory
|
||||
let _ = self.process_dir(&f.path());
|
||||
// Found file
|
||||
let videos: Vec<PathBuf> = self.list_video_files(dir).collect();
|
||||
info!("Found {} videos", videos.len());
|
||||
|
||||
let results: Result<()> = videos.par_iter().progress().map(|f| {
|
||||
Ok(self.encoders.exec_all(f.to_str().context("Path.to_str failed")?, self.dot_path(&f).as_path())?)
|
||||
}).collect();
|
||||
|
||||
Ok(results?)
|
||||
}
|
||||
|
||||
/// List all video files in a directory
|
||||
pub fn list_video_files<'a>(&'a self, dir: &'a PathBuf) -> impl Iterator<Item = PathBuf> + 'a {
|
||||
WalkDir::new(dir).into_iter().filter_map(|f| f.ok())
|
||||
.filter(|f| f.path().is_file() && self.is_video(f))
|
||||
.map(|f| f.into_path())
|
||||
}
|
||||
|
||||
/// Check if a file is a video file by file name
|
||||
fn is_video(&self, dir: &DirEntry) -> bool {
|
||||
if let Some(name) = dir.file_name().to_str() {
|
||||
if let Some((_, ext)) = name.rsplit_once(".") {
|
||||
return self.video_ext.contains(&*ext)
|
||||
}
|
||||
});
|
||||
|
||||
Ok(())
|
||||
}
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -3,6 +3,7 @@ mod macros;
|
||||
mod generator;
|
||||
mod utils;
|
||||
mod thumbnailer;
|
||||
mod encoder;
|
||||
|
||||
use generator::*;
|
||||
use macros::*;
|
||||
|
||||
+25
-12
@@ -2,9 +2,12 @@ mod generator;
|
||||
mod macros;
|
||||
mod utils;
|
||||
mod thumbnailer;
|
||||
mod encoder;
|
||||
|
||||
use std::path::{Path, PathBuf};
|
||||
use std::process::exit;
|
||||
use generator::*;
|
||||
use crate::encoder::Encoders;
|
||||
use crate::thumbnailer::{Thumbnailer, Thumbnailers};
|
||||
|
||||
extern crate pretty_env_logger;
|
||||
@@ -14,18 +17,28 @@ fn main() {
|
||||
pretty_env_logger::init();
|
||||
|
||||
let gen = Generator::new("/data".into()).unwrap();
|
||||
//
|
||||
// let path: PathBuf = "/data/Anime/1977 Star Wars Collection/01 Star Wars Episode I The Phantom Menace - George Lucas 1999 Eng Subs 720p [H264-mp4].mp4".into();
|
||||
// let mime = gen.get_mime(&path)
|
||||
// .expect("Panic");
|
||||
// info!("mime {mime}");
|
||||
//
|
||||
// let thumbnailer_path = "/usr/share/thumbnailers/totem.thumbnailer";
|
||||
// let thumbnailer = Thumbnailer::load(Path::new(thumbnailer_path)).unwrap();
|
||||
// info!("thumb {:?}", thumbnailer);
|
||||
// info!("check {:?}", thumbnailer.check("audio/x-mp3"));
|
||||
// thumbnailer.gen(path.to_str().unwrap(), "/tmp/test.png", 256).expect("Generation failed");
|
||||
// gen.get_thumb(&path).expect("Get thumb failed");
|
||||
//
|
||||
// let ts = Thumbnailers::load_all().unwrap();
|
||||
// info!("Video thumbnailer: {:?}", ts.find("audio/x-mp3"));
|
||||
//
|
||||
// let encoders = Encoders::load().unwrap();
|
||||
// info!("Encoders {:?}", encoders);
|
||||
|
||||
let path: PathBuf = "/data/Anime/1977 Star Wars Collection/01 Star Wars Episode I The Phantom Menace - George Lucas 1999 Eng Subs 720p [H264-mp4].mp4".into();
|
||||
let mime = gen.get_mime(&path)
|
||||
.expect("Panic");
|
||||
info!("mime {mime}");
|
||||
let videos: Vec<PathBuf> = gen.list_video_files(&PathBuf::from("/data/Anime")).collect();
|
||||
videos.iter().for_each(|x| println!("Video found: {}", x.display()));
|
||||
println!("Length: {}", videos.len());
|
||||
|
||||
let thumbnailer_path = "/usr/share/thumbnailers/totem.thumbnailer";
|
||||
let thumbnailer = Thumbnailer::load(Path::new(thumbnailer_path)).unwrap();
|
||||
info!("thumb {:?}", thumbnailer);
|
||||
info!("check {:?}", thumbnailer.check("audio/x-mp3"));
|
||||
thumbnailer.gen(path.to_str().unwrap(), "/tmp/test.png", 256).expect("Generation failed");
|
||||
|
||||
let ts = Thumbnailers::load_all().unwrap();
|
||||
info!("Video thumbnailer: {:?}", ts.find("audio/x-mp3"))
|
||||
gen.encode_dir(&PathBuf::from("/data/Anime")).expect("Encoding failed");
|
||||
}
|
||||
|
||||
@@ -1,9 +1,8 @@
|
||||
use std::collections::HashSet;
|
||||
use std::fs;
|
||||
use std::path::Path;
|
||||
use std::process::Command;
|
||||
use anyhow::{bail, Result};
|
||||
use shlex::Shlex;
|
||||
use anyhow::{Result};
|
||||
use crate::utils::run_cmd;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct Thumbnailer {
|
||||
@@ -45,19 +44,11 @@ impl Thumbnailer {
|
||||
|
||||
/// Generate thumbnail
|
||||
pub fn gen(&self, orig: &str, new: &str, pixels: i32) -> Result<()> {
|
||||
let cmd = self.exec
|
||||
run_cmd(&*self.exec
|
||||
.replace("%s", &*format!("'{pixels}'"))
|
||||
.replace("%u", &shlex::quote(orig))
|
||||
.replace("%i", &shlex::quote(orig))
|
||||
.replace("%o", &shlex::quote(new));
|
||||
let args: Vec<String> = Shlex::new(&*cmd).collect();
|
||||
let out = Command::new(args[0].to_owned()).args(&args[1..]).output()?;
|
||||
if !out.status.success() {
|
||||
error!("Command failed: {cmd}");
|
||||
error!("Command output: {:?}", out);
|
||||
bail!(String::from_utf8(out.stderr)?);
|
||||
}
|
||||
debug!("Command output: {:?}", out);
|
||||
.replace("%o", &shlex::quote(new)))?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -1,5 +1,8 @@
|
||||
use std::{fs, io};
|
||||
use std::path::{PathBuf};
|
||||
use std::process::{Command, Output};
|
||||
use anyhow::{Result, bail};
|
||||
use shlex::Shlex;
|
||||
|
||||
pub fn write_sf<C: AsRef<[u8]>>(path: &PathBuf, contents: C) -> io::Result<()> {
|
||||
// Create parent if it has parent
|
||||
@@ -9,3 +12,16 @@ pub fn write_sf<C: AsRef<[u8]>>(path: &PathBuf, contents: C) -> io::Result<()> {
|
||||
|
||||
fs::write(path, contents)
|
||||
}
|
||||
|
||||
pub fn run_cmd(cmd: &str) -> Result<Output> {
|
||||
let args: Vec<String> = Shlex::new(&*cmd).collect();
|
||||
let out = Command::new(args[0].to_owned()).args(&args[1..]).output()?;
|
||||
if !out.status.success() {
|
||||
error!("Command failed: {cmd}");
|
||||
error!("Command output: {:?}", out);
|
||||
let msg = String::from_utf8_lossy(&out.stderr.to_owned()).to_string();
|
||||
bail!(msg);
|
||||
}
|
||||
// debug!("Command output: {:?}", out);
|
||||
Ok(out)
|
||||
}
|
||||
|
||||
+7
-2
@@ -1,13 +1,18 @@
|
||||
root $dir_path;
|
||||
|
||||
if ($deploy_path = "") {
|
||||
set $deploy_path "/";
|
||||
}
|
||||
|
||||
# The MeowIndex web app block
|
||||
location /__meowindex__ {
|
||||
alias /etc/nginx/MeowIndex/dist;
|
||||
|
||||
# Use sub_filter to configure the app
|
||||
sub_filter_types application/javascript;
|
||||
sub_filter_types application/javascript text/javascript;
|
||||
sub_filter_once off;
|
||||
sub_filter "{DEPLOY-PATH-PLACEHOLDER}" "/__meowindex__";
|
||||
sub_filter "{ASSETS-PATH-PLACEHOLDER}" "/__meowindex__";
|
||||
sub_filter "{DEPLOY-PATH-PLACEHOLDER}" $deploy_path;
|
||||
sub_filter "{HOST-PLACEHOLDER}" "/api";
|
||||
sub_filter "\"/assets" "\"/__meowindex__/assets";
|
||||
sub_filter "File Listing" $title;
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
"name": "meow-index",
|
||||
"version": "1.0.0",
|
||||
"description": "Modern Drop-in Replacement for Nginx AutoIndex / FancyIndex!",
|
||||
"homepage": "https://github.com/hykilpikonna/MeowIndex",
|
||||
"scripts": {
|
||||
"dev": "vite",
|
||||
"build": "vite build",
|
||||
|
||||
+23
-9
@@ -26,12 +26,14 @@ interface File {
|
||||
const getType = (f: File) => f.type ?? f.file_type
|
||||
|
||||
// Placeholder for nginx to replace
|
||||
let assetsPath = "{ASSETS-PATH-PLACEHOLDER}"
|
||||
let deployPath = "{DEPLOY-PATH-PLACEHOLDER}"
|
||||
let host = "{HOST-PLACEHOLDER}"
|
||||
|
||||
// Default deploy path and host for testing
|
||||
// Default paths and host for testing
|
||||
if (assetsPath.includes("-PLACEHOLDER")) assetsPath = "/"
|
||||
if (deployPath.includes("-PLACEHOLDER")) deployPath = "/"
|
||||
if (host.includes("-PLACEHOLDER")) host = "https://daisy.hydev.org/data/api"
|
||||
if (host.includes("-PLACEHOLDER")) host = undefined
|
||||
|
||||
// Compute path
|
||||
let fullPath = window.location.pathname
|
||||
@@ -47,19 +49,19 @@ const fetchApi = async () =>
|
||||
|
||||
function getIcon(f: File)
|
||||
{
|
||||
if (getType(f) == "directory") return urlJoin(deployPath, "mime/folder.svg")
|
||||
if (getType(f) == "directory") return urlJoin(assetsPath, "mime/folder.svg")
|
||||
|
||||
if (f.has_thumb) return urlJoin(host, filePath, f.name) + "?thumb=1"
|
||||
|
||||
const sp = f.name.split(".")
|
||||
const m = f.mime ?? mime.getType(sp[sp.length - 1])
|
||||
if (m) return urlJoin(deployPath, `mime/${m.replace("/", "-")}.svg`)
|
||||
else return urlJoin(deployPath, 'mime/application-blank.svg')
|
||||
if (m) return urlJoin(assetsPath, `mime/${m.replace("/", "-")}.svg`)
|
||||
else return urlJoin(assetsPath, 'mime/application-blank.svg')
|
||||
}
|
||||
|
||||
function getHref(f: File)
|
||||
{
|
||||
return getType(f) == "directory" ? urlJoin(fullPath, f.name) : urlJoin(host, filePath, f.name)
|
||||
return getType(f) == "directory" ? (urlJoin(fullPath, f.name) + "/") : urlJoin(host, filePath, f.name)
|
||||
// return urlJoin(fullPath, f.name)
|
||||
}
|
||||
|
||||
@@ -67,7 +69,7 @@ const alpNum = new Set("0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRST
|
||||
|
||||
export default function App() {
|
||||
const [api] = createResource(fetchApi)
|
||||
const paths = [window.location.host, ...filePath.split("/").filter(it => it)]
|
||||
const paths = [window.location.host, ...fullPath.split("/").filter(it => it)]
|
||||
|
||||
// Infinite Scroll
|
||||
const [scrollIndex, setScrollIndex] = createSignal(50)
|
||||
@@ -114,6 +116,10 @@ export default function App() {
|
||||
setBcLeft(Math.max(bcMax = Math.round(w.clientWidth - w.parentElement.clientWidth), 0))
|
||||
}, 100)
|
||||
|
||||
if (!host) return (<div class="min-h-full my-auto flex justify-center items-center">
|
||||
App is misconfigured. Please correct sub_filter.
|
||||
</div>)
|
||||
|
||||
return (
|
||||
// Full screen container
|
||||
<div class="p-2 text-sm bg-dark-800 color-main min-h-full lg:(p-10 text-base)">
|
||||
@@ -121,7 +127,15 @@ export default function App() {
|
||||
{/* Content container */}
|
||||
<div class="max-w-screen-md m-auto">
|
||||
|
||||
<p class="text-4xl color-emp text-center py-10">File Listing</p>
|
||||
<div class="heading color-emp text-center py-10">
|
||||
<p class="text-4xl">File Listing</p>
|
||||
<div class="flex justify-center opacity-50">
|
||||
<a class="flex gap-1" href="https://github.com/hykilpikonna/MeowIndex">
|
||||
Powered by Nginx MeowIndex
|
||||
<Icon class="text-lg" icon="mdi:github"/>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Breadcrumb slot */}
|
||||
<div id="breadcrumbs" class="flex bg-dark-600 p-2 px-5 mb-5 rounded-xl whitespace-nowrap relative z-30">
|
||||
@@ -142,7 +156,7 @@ export default function App() {
|
||||
<>
|
||||
<a class="breadcrumb-link ml-2 first:ml-0"
|
||||
classList={{active: i() + 1 == paths.length}}
|
||||
href={urlJoin(filePath, "../".repeat(paths.length - i() - 1))}>{decodeURIComponent(p)}</a>
|
||||
href={urlJoin(fullPath, "../".repeat(paths.length - i() - 1))}>{decodeURIComponent(p)}</a>
|
||||
<span class="color-subsub ml-2 last:hidden">/</span>
|
||||
</>
|
||||
}</For>
|
||||
|
||||
Reference in New Issue
Block a user