43 Commits

Author SHA1 Message Date
Azalea 4c566b9dc9 [F] Fix github actions
Docker / build (push) Has been cancelled
Nightly Build / build (push) Has been cancelled
2023-12-20 23:05:47 -08:00
Azalea Gui f707f6d4ae [F] Fix content type after nginx update 2023-09-12 09:37:17 -04:00
Azalea b5c2b36adc [U] Add homepage in package.json
Closes #2
2023-06-12 01:05:39 -04:00
Azalea Gui 4418c9c8bd [F] Fix deploy subpath breadcrumbs 2023-03-20 17:15:50 -04:00
Azalea 0018b035bd [U] Add demo pages 2023-03-08 18:13:22 -05:00
Azalea Gui 2ca68b4efc [+] Add "Powered by Nginx MeowIndex" 2023-03-05 21:45:55 -05:00
Azalea Gui 741ca48f9c [F] Fix directory path 2023-03-05 21:40:21 -05:00
Azalea fbc0e99f5a [U] Add todo 2023-03-03 19:17:34 -05:00
Azalea 4b44ea494f [U] Update readme 2023-03-03 19:14:20 -05:00
Azalea 21b31fe20e [U] Update changelog 2023-03-03 19:12:47 -05:00
Azalea Gui f6202b1837 [+] Show progress bar 2023-02-25 10:54:29 -05:00
Azalea Gui 78a7b2d1bb [F] Fix UTF8 encoding error 2023-02-25 10:54:18 -05:00
Azalea Gui d9ac9c9031 [+] Add indicatif dependency 2023-02-25 10:54:06 -05:00
Azalea Gui ba13e8fd00 [F] Fix encoder output typo 2023-02-25 10:53:51 -05:00
Azalea Gui fbceba8320 [-] Remove debug command output 2023-02-24 20:15:36 -05:00
Azalea Gui 833e95e9d7 [O] Set number of threads 2023-02-24 20:14:52 -05:00
Azalea Gui 1e4205b311 [O] Multi-threaded encoding 2023-02-24 12:52:57 -05:00
Azalea Gui 99152982f1 [F] Fix path not found 2023-02-24 12:52:44 -05:00
Azalea Gui 889d4b4924 [+] Rayon dependency 2023-02-24 12:52:35 -05:00
Azalea Gui 215564523c [+] encode_dir 2023-02-24 12:30:18 -05:00
Azalea Gui ee67670a89 [+] Encoder::exec_all 2023-02-24 12:22:35 -05:00
Azalea Gui b39f28fab6 [+] Add tempdir dependency 2023-02-24 12:22:22 -05:00
Azalea Gui 31842ef5a0 [O] Return iterator instead 2023-02-24 12:10:23 -05:00
Azalea Gui c32055f213 [T] Test list_video_files 2023-02-24 12:02:39 -05:00
Azalea Gui 8e4fcce86e [+] list_video_files function 2023-02-24 12:02:29 -05:00
Azalea Gui cbd26a619c [+] Video extensions 2023-02-24 12:02:05 -05:00
Azalea Gui 7ace885a61 [+] Walkdir dependency 2023-02-24 12:01:10 -05:00
Azalea Gui acd0b2bcd5 [+] Add encoder to generator 2023-02-24 10:47:56 -05:00
Azalea Gui d9ce7263f3 [+] Encoding::execute 2023-02-24 10:46:54 -05:00
Azalea Gui 18a7018a83 [O] Encapsulate run_cmd function 2023-02-24 10:44:35 -05:00
Azalea Gui 4f17cc7d4f [+] Add suffix to encoders 2023-02-24 10:36:54 -05:00
Azalea Gui 12fb9c35c2 [T] Test loading encoders 2023-02-24 10:32:49 -05:00
Azalea Gui 5a96d32af4 [+] Load encoders 2023-02-24 10:32:39 -05:00
Azalea Gui 86d732d463 [+] Encoders 2023-02-24 10:32:32 -05:00
Azalea Gui 1eb5b8bfef [+] toml dependency 2023-02-24 10:32:23 -05:00
Azalea Gui 223ca959ce [F] Try to fix github actions 2023-02-23 21:15:43 -05:00
Azalea Gui 7c77c5605d [F] Try to fix github actions 2023-02-23 21:14:38 -05:00
Azalea Gui 94ec4ebc6d [F] Try to fix github actions 2023-02-23 21:10:20 -05:00
Azalea Gui 8bbbaa1408 [F] Try to fix github actions 2023-02-23 21:02:31 -05:00
Azalea Gui 6794a9a765 [F] Try to fix github actions 2023-02-23 20:58:57 -05:00
Azalea Gui 90c1ea7662 [O] Reorder docker layers 2023-02-23 20:56:57 -05:00
Azalea Gui ebbf794a6c Merge branch 'staging' 2023-02-23 20:54:58 -05:00
Hykilpikonna a7f69c55fb [PR] Merge pull request #1 from hykilpikonna/staging 2023-02-23 20:45:57 -05:00
16 changed files with 521 additions and 84 deletions
+7 -3
View File
@@ -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
+1 -1
View File
@@ -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:
+15 -19
View File
@@ -4,6 +4,11 @@ A cute, feature-rich file listing module to replace nginx's autoindex / fancyind
![image](https://user-images.githubusercontent.com/22280294/219513952-736182cb-a38a-4a49-b9ea-f9160399987c.png)
## 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
+275
View File
@@ -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"
+5
View File
@@ -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
View File
@@ -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"]
+19
View File
@@ -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"
+68
View File
@@ -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
View File
@@ -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
}
}
+1
View File
@@ -3,6 +3,7 @@ mod macros;
mod generator;
mod utils;
mod thumbnailer;
mod encoder;
use generator::*;
use macros::*;
+25 -12
View File
@@ -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");
}
+4 -13
View File
@@ -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(())
}
+16
View File
@@ -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
View File
@@ -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;
+1
View File
@@ -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
View File
@@ -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>