feat: initial commit
This commit is contained in:
commit
2b26392873
13 changed files with 1723 additions and 0 deletions
1
.envrc
Normal file
1
.envrc
Normal file
|
@ -0,0 +1 @@
|
|||
use flake
|
5
.gitignore
vendored
Normal file
5
.gitignore
vendored
Normal file
|
@ -0,0 +1,5 @@
|
|||
/target
|
||||
/result
|
||||
/.direnv
|
||||
/out
|
||||
*.nfo
|
789
Cargo.lock
generated
Normal file
789
Cargo.lock
generated
Normal file
|
@ -0,0 +1,789 @@
|
|||
# This file is automatically @generated by Cargo.
|
||||
# It is not intended for manual editing.
|
||||
version = 3
|
||||
|
||||
[[package]]
|
||||
name = "addr2line"
|
||||
version = "0.19.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a76fd60b23679b7d19bd066031410fb7e458ccc5e958eb5c325888ce4baedc97"
|
||||
dependencies = [
|
||||
"gimli",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "adler"
|
||||
version = "1.0.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe"
|
||||
|
||||
[[package]]
|
||||
name = "anstream"
|
||||
version = "0.3.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0ca84f3628370c59db74ee214b3263d58f9aadd9b4fe7e711fd87dc452b7f163"
|
||||
dependencies = [
|
||||
"anstyle",
|
||||
"anstyle-parse",
|
||||
"anstyle-query",
|
||||
"anstyle-wincon",
|
||||
"colorchoice",
|
||||
"is-terminal",
|
||||
"utf8parse",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "anstyle"
|
||||
version = "1.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "41ed9a86bf92ae6580e0a31281f65a1b1d867c0cc68d5346e2ae128dddfa6a7d"
|
||||
|
||||
[[package]]
|
||||
name = "anstyle-parse"
|
||||
version = "0.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e765fd216e48e067936442276d1d57399e37bce53c264d6fefbe298080cb57ee"
|
||||
dependencies = [
|
||||
"utf8parse",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "anstyle-query"
|
||||
version = "1.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5ca11d4be1bab0c8bc8734a9aa7bf4ee8316d462a08c6ac5052f888fef5b494b"
|
||||
dependencies = [
|
||||
"windows-sys",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "anstyle-wincon"
|
||||
version = "1.0.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "180abfa45703aebe0093f79badacc01b8fd4ea2e35118747e5811127f926e188"
|
||||
dependencies = [
|
||||
"anstyle",
|
||||
"windows-sys",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "backtrace"
|
||||
version = "0.3.67"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "233d376d6d185f2a3093e58f283f60f880315b6c60075b01f36b3b85154564ca"
|
||||
dependencies = [
|
||||
"addr2line",
|
||||
"cc",
|
||||
"cfg-if",
|
||||
"libc",
|
||||
"miniz_oxide",
|
||||
"object",
|
||||
"rustc-demangle",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "bitflags"
|
||||
version = "1.3.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a"
|
||||
|
||||
[[package]]
|
||||
name = "cc"
|
||||
version = "1.0.79"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "50d30906286121d95be3d479533b458f87493b30a4b5f79a607db8f5d11aa91f"
|
||||
|
||||
[[package]]
|
||||
name = "cfg-if"
|
||||
version = "1.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
|
||||
|
||||
[[package]]
|
||||
name = "clap"
|
||||
version = "4.3.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "93aae7a4192245f70fe75dd9157fc7b4a5bf53e88d30bd4396f7d8f9284d5acc"
|
||||
dependencies = [
|
||||
"clap_builder",
|
||||
"clap_derive",
|
||||
"once_cell",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "clap_builder"
|
||||
version = "4.3.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4f423e341edefb78c9caba2d9c7f7687d0e72e89df3ce3394554754393ac3990"
|
||||
dependencies = [
|
||||
"anstream",
|
||||
"anstyle",
|
||||
"bitflags",
|
||||
"clap_lex",
|
||||
"strsim",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "clap_derive"
|
||||
version = "4.3.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "191d9573962933b4027f932c600cd252ce27a8ad5979418fe78e43c07996f27b"
|
||||
dependencies = [
|
||||
"heck",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.16",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "clap_lex"
|
||||
version = "0.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2da6da31387c7e4ef160ffab6d5e7f00c42626fe39aea70a7b0f1773f7dd6c1b"
|
||||
|
||||
[[package]]
|
||||
name = "color-eyre"
|
||||
version = "0.6.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5a667583cca8c4f8436db8de46ea8233c42a7d9ae424a82d338f2e4675229204"
|
||||
dependencies = [
|
||||
"backtrace",
|
||||
"color-spantrace",
|
||||
"eyre",
|
||||
"indenter",
|
||||
"once_cell",
|
||||
"owo-colors",
|
||||
"tracing-error",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "color-spantrace"
|
||||
version = "0.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1ba75b3d9449ecdccb27ecbc479fdc0b87fa2dd43d2f8298f9bf0e59aacc8dce"
|
||||
dependencies = [
|
||||
"once_cell",
|
||||
"owo-colors",
|
||||
"tracing-core",
|
||||
"tracing-error",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "colorchoice"
|
||||
version = "1.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "acbf1af155f9b9ef647e42cdc158db4b64a1b61f743629225fde6f3e0be2a7c7"
|
||||
|
||||
[[package]]
|
||||
name = "errno"
|
||||
version = "0.3.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4bcfec3a70f97c962c307b2d2c56e358cf1d00b558d74262b5f929ee8cc7e73a"
|
||||
dependencies = [
|
||||
"errno-dragonfly",
|
||||
"libc",
|
||||
"windows-sys",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "errno-dragonfly"
|
||||
version = "0.1.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "aa68f1b12764fab894d2755d2518754e71b4fd80ecfb822714a1206c2aab39bf"
|
||||
dependencies = [
|
||||
"cc",
|
||||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "eyre"
|
||||
version = "0.6.8"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4c2b6b5a29c02cdc822728b7d7b8ae1bab3e3b05d44522770ddd49722eeac7eb"
|
||||
dependencies = [
|
||||
"indenter",
|
||||
"once_cell",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "form_urlencoded"
|
||||
version = "1.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a9c384f161156f5260c24a097c56119f9be8c798586aecc13afbcbe7b7e26bf8"
|
||||
dependencies = [
|
||||
"percent-encoding",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "gimli"
|
||||
version = "0.27.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ad0a93d233ebf96623465aad4046a8d3aa4da22d4f4beba5388838c8a434bbb4"
|
||||
|
||||
[[package]]
|
||||
name = "heck"
|
||||
version = "0.4.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8"
|
||||
|
||||
[[package]]
|
||||
name = "hermit-abi"
|
||||
version = "0.3.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "fed44880c466736ef9a5c5b5facefb5ed0785676d0c02d612db14e54f0d84286"
|
||||
|
||||
[[package]]
|
||||
name = "idna"
|
||||
version = "0.3.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e14ddfc70884202db2244c223200c204c2bda1bc6e0998d11b5e024d657209e6"
|
||||
dependencies = [
|
||||
"unicode-bidi",
|
||||
"unicode-normalization",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "indenter"
|
||||
version = "0.3.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ce23b50ad8242c51a442f3ff322d56b02f08852c77e4c0b4d3fd684abc89c683"
|
||||
|
||||
[[package]]
|
||||
name = "io-lifetimes"
|
||||
version = "1.0.11"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "eae7b9aee968036d54dce06cebaefd919e4472e753296daccd6d344e3e2df0c2"
|
||||
dependencies = [
|
||||
"hermit-abi",
|
||||
"libc",
|
||||
"windows-sys",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "is-terminal"
|
||||
version = "0.4.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "adcf93614601c8129ddf72e2d5633df827ba6551541c6d8c59520a371475be1f"
|
||||
dependencies = [
|
||||
"hermit-abi",
|
||||
"io-lifetimes",
|
||||
"rustix",
|
||||
"windows-sys",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "itoa"
|
||||
version = "1.0.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "453ad9f582a441959e5f0d088b02ce04cfe8d51a8eaf077f12ac6d3e94164ca6"
|
||||
|
||||
[[package]]
|
||||
name = "lazy_static"
|
||||
version = "1.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646"
|
||||
|
||||
[[package]]
|
||||
name = "libc"
|
||||
version = "0.2.144"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2b00cc1c228a6782d0f076e7b232802e0c5689d41bb5df366f2a6b6621cfdfe1"
|
||||
|
||||
[[package]]
|
||||
name = "linux-raw-sys"
|
||||
version = "0.3.8"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ef53942eb7bf7ff43a617b3e2c1c4a5ecf5944a7c1bc12d7ee39bbb15e5c1519"
|
||||
|
||||
[[package]]
|
||||
name = "litefin"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"clap",
|
||||
"color-eyre",
|
||||
"maud",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"url",
|
||||
"walkdir",
|
||||
"yaserde",
|
||||
"yaserde_derive",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "log"
|
||||
version = "0.4.17"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "abb12e687cfb44aa40f41fc3978ef76448f9b6038cad6aef4259d3c095a2382e"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "maud"
|
||||
version = "0.25.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b0bab19cef8a7fe1c18a43e881793bfc9d4ea984befec3ae5bd0415abf3ecf00"
|
||||
dependencies = [
|
||||
"itoa",
|
||||
"maud_macros",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "maud_macros"
|
||||
version = "0.25.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0be95d66c3024ffce639216058e5bae17a83ecaf266ffc6e4d060ad447c9eed2"
|
||||
dependencies = [
|
||||
"proc-macro-error",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 1.0.109",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "memchr"
|
||||
version = "2.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d"
|
||||
|
||||
[[package]]
|
||||
name = "miniz_oxide"
|
||||
version = "0.6.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b275950c28b37e794e8c55d88aeb5e139d0ce23fdbbeda68f8d7174abdf9e8fa"
|
||||
dependencies = [
|
||||
"adler",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "object"
|
||||
version = "0.30.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ea86265d3d3dcb6a27fc51bd29a4bf387fae9d2986b823079d4986af253eb439"
|
||||
dependencies = [
|
||||
"memchr",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "once_cell"
|
||||
version = "1.17.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b7e5500299e16ebb147ae15a00a942af264cf3688f47923b8fc2cd5858f23ad3"
|
||||
|
||||
[[package]]
|
||||
name = "owo-colors"
|
||||
version = "3.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c1b04fb49957986fdce4d6ee7a65027d55d4b6d2265e5848bbb507b58ccfdb6f"
|
||||
|
||||
[[package]]
|
||||
name = "percent-encoding"
|
||||
version = "2.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "478c572c3d73181ff3c2539045f6eb99e5491218eae919370993b890cdbdd98e"
|
||||
|
||||
[[package]]
|
||||
name = "pin-project-lite"
|
||||
version = "0.2.9"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e0a7ae3ac2f1173085d398531c705756c94a4c56843785df85a60c1a0afac116"
|
||||
|
||||
[[package]]
|
||||
name = "proc-macro-error"
|
||||
version = "1.0.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "da25490ff9892aab3fcf7c36f08cfb902dd3e71ca0f9f9517bea02a73a5ce38c"
|
||||
dependencies = [
|
||||
"proc-macro-error-attr",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 1.0.109",
|
||||
"version_check",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "proc-macro-error-attr"
|
||||
version = "1.0.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a1be40180e52ecc98ad80b184934baf3d0d29f979574e439af5a55274b35f869"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"version_check",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "proc-macro2"
|
||||
version = "1.0.58"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "fa1fb82fc0c281dd9671101b66b771ebbe1eaf967b96ac8740dcba4b70005ca8"
|
||||
dependencies = [
|
||||
"unicode-ident",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "quote"
|
||||
version = "1.0.27"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8f4f29d145265ec1c483c7c654450edde0bfe043d3938d6972630663356d9500"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rustc-demangle"
|
||||
version = "0.1.23"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d626bb9dae77e28219937af045c257c28bfd3f69333c512553507f5f9798cb76"
|
||||
|
||||
[[package]]
|
||||
name = "rustix"
|
||||
version = "0.37.19"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "acf8729d8542766f1b2cf77eb034d52f40d375bb8b615d0b147089946e16613d"
|
||||
dependencies = [
|
||||
"bitflags",
|
||||
"errno",
|
||||
"io-lifetimes",
|
||||
"libc",
|
||||
"linux-raw-sys",
|
||||
"windows-sys",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ryu"
|
||||
version = "1.0.13"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f91339c0467de62360649f8d3e185ca8de4224ff281f66000de5eb2a77a79041"
|
||||
|
||||
[[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 = "serde"
|
||||
version = "1.0.163"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2113ab51b87a539ae008b5c6c02dc020ffa39afd2d83cffcb3f4eb2722cebec2"
|
||||
dependencies = [
|
||||
"serde_derive",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serde_derive"
|
||||
version = "1.0.163"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8c805777e3930c8883389c602315a24224bcc738b63905ef87cd1420353ea93e"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.16",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serde_json"
|
||||
version = "1.0.96"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "057d394a50403bcac12672b2b18fb387ab6d289d957dab67dd201875391e52f1"
|
||||
dependencies = [
|
||||
"itoa",
|
||||
"ryu",
|
||||
"serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "sharded-slab"
|
||||
version = "0.1.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "900fba806f70c630b0a382d0d825e17a0f19fcd059a2ade1ff237bcddf446b31"
|
||||
dependencies = [
|
||||
"lazy_static",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "strsim"
|
||||
version = "0.10.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623"
|
||||
|
||||
[[package]]
|
||||
name = "syn"
|
||||
version = "1.0.109"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"unicode-ident",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "syn"
|
||||
version = "2.0.16"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a6f671d4b5ffdb8eadec19c0ae67fe2639df8684bd7bc4b83d986b8db549cf01"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"unicode-ident",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "thread_local"
|
||||
version = "1.1.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3fdd6f064ccff2d6567adcb3873ca630700f00b5ad3f060c25b5dcfd9a4ce152"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"once_cell",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tinyvec"
|
||||
version = "1.6.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "87cc5ceb3875bb20c2890005a4e226a4651264a5c75edb2421b52861a0a0cb50"
|
||||
dependencies = [
|
||||
"tinyvec_macros",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tinyvec_macros"
|
||||
version = "0.1.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20"
|
||||
|
||||
[[package]]
|
||||
name = "tracing"
|
||||
version = "0.1.37"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8ce8c33a8d48bd45d624a6e523445fd21ec13d3653cd51f681abf67418f54eb8"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"pin-project-lite",
|
||||
"tracing-core",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tracing-core"
|
||||
version = "0.1.31"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0955b8137a1df6f1a2e9a37d8a6656291ff0297c1a97c24e0d8425fe2312f79a"
|
||||
dependencies = [
|
||||
"once_cell",
|
||||
"valuable",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tracing-error"
|
||||
version = "0.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d686ec1c0f384b1277f097b2f279a2ecc11afe8c133c1aabf036a27cb4cd206e"
|
||||
dependencies = [
|
||||
"tracing",
|
||||
"tracing-subscriber",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tracing-subscriber"
|
||||
version = "0.3.17"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "30a651bc37f915e81f087d86e62a18eec5f79550c7faff886f7090b4ea757c77"
|
||||
dependencies = [
|
||||
"sharded-slab",
|
||||
"thread_local",
|
||||
"tracing-core",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "unicode-bidi"
|
||||
version = "0.3.13"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "92888ba5573ff080736b3648696b70cafad7d250551175acbaa4e0385b3e1460"
|
||||
|
||||
[[package]]
|
||||
name = "unicode-ident"
|
||||
version = "1.0.8"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e5464a87b239f13a63a501f2701565754bae92d243d4bb7eb12f6d57d2269bf4"
|
||||
|
||||
[[package]]
|
||||
name = "unicode-normalization"
|
||||
version = "0.1.22"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5c5713f0fc4b5db668a2ac63cdb7bb4469d8c9fed047b1d0292cc7b0ce2ba921"
|
||||
dependencies = [
|
||||
"tinyvec",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "url"
|
||||
version = "2.3.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0d68c799ae75762b8c3fe375feb6600ef5602c883c5d21eb51c09f22b83c4643"
|
||||
dependencies = [
|
||||
"form_urlencoded",
|
||||
"idna",
|
||||
"percent-encoding",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "utf8parse"
|
||||
version = "0.2.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "711b9620af191e0cdc7468a8d14e709c3dcdb115b36f838e601583af800a370a"
|
||||
|
||||
[[package]]
|
||||
name = "valuable"
|
||||
version = "0.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "830b7e5d4d90034032940e4ace0d9a9a057e7a45cd94e6c007832e39edb82f6d"
|
||||
|
||||
[[package]]
|
||||
name = "version_check"
|
||||
version = "0.9.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f"
|
||||
|
||||
[[package]]
|
||||
name = "walkdir"
|
||||
version = "2.3.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "36df944cda56c7d8d8b7496af378e6b16de9284591917d307c9b4d313c44e698"
|
||||
dependencies = [
|
||||
"same-file",
|
||||
"winapi-util",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "winapi"
|
||||
version = "0.3.9"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419"
|
||||
dependencies = [
|
||||
"winapi-i686-pc-windows-gnu",
|
||||
"winapi-x86_64-pc-windows-gnu",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "winapi-i686-pc-windows-gnu"
|
||||
version = "0.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6"
|
||||
|
||||
[[package]]
|
||||
name = "winapi-util"
|
||||
version = "0.1.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "70ec6ce85bb158151cae5e5c87f95a8e97d2c0c4b001223f33a334e3ce5de178"
|
||||
dependencies = [
|
||||
"winapi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "winapi-x86_64-pc-windows-gnu"
|
||||
version = "0.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
|
||||
|
||||
[[package]]
|
||||
name = "windows-sys"
|
||||
version = "0.48.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9"
|
||||
dependencies = [
|
||||
"windows-targets",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows-targets"
|
||||
version = "0.48.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7b1eb6f0cd7c80c79759c929114ef071b87354ce476d9d94271031c0497adfd5"
|
||||
dependencies = [
|
||||
"windows_aarch64_gnullvm",
|
||||
"windows_aarch64_msvc",
|
||||
"windows_i686_gnu",
|
||||
"windows_i686_msvc",
|
||||
"windows_x86_64_gnu",
|
||||
"windows_x86_64_gnullvm",
|
||||
"windows_x86_64_msvc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows_aarch64_gnullvm"
|
||||
version = "0.48.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "91ae572e1b79dba883e0d315474df7305d12f569b400fcf90581b06062f7e1bc"
|
||||
|
||||
[[package]]
|
||||
name = "windows_aarch64_msvc"
|
||||
version = "0.48.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b2ef27e0d7bdfcfc7b868b317c1d32c641a6fe4629c171b8928c7b08d98d7cf3"
|
||||
|
||||
[[package]]
|
||||
name = "windows_i686_gnu"
|
||||
version = "0.48.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "622a1962a7db830d6fd0a69683c80a18fda201879f0f447f065a3b7467daa241"
|
||||
|
||||
[[package]]
|
||||
name = "windows_i686_msvc"
|
||||
version = "0.48.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4542c6e364ce21bf45d69fdd2a8e455fa38d316158cfd43b3ac1c5b1b19f8e00"
|
||||
|
||||
[[package]]
|
||||
name = "windows_x86_64_gnu"
|
||||
version = "0.48.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ca2b8a661f7628cbd23440e50b05d705db3686f894fc9580820623656af974b1"
|
||||
|
||||
[[package]]
|
||||
name = "windows_x86_64_gnullvm"
|
||||
version = "0.48.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7896dbc1f41e08872e9d5e8f8baa8fdd2677f29468c4e156210174edc7f7b953"
|
||||
|
||||
[[package]]
|
||||
name = "windows_x86_64_msvc"
|
||||
version = "0.48.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1a515f5799fe4961cb532f983ce2b23082366b898e52ffbce459c86f67c8378a"
|
||||
|
||||
[[package]]
|
||||
name = "xml-rs"
|
||||
version = "0.8.11"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1690519550bfa95525229b9ca2350c63043a4857b3b0013811b2ccf4a2420b01"
|
||||
|
||||
[[package]]
|
||||
name = "yaserde"
|
||||
version = "0.8.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4bf52af554a50b866aaad63d7eabd6fca298db3dfe49afd50b7ba5a33dfa0582"
|
||||
dependencies = [
|
||||
"log",
|
||||
"xml-rs",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "yaserde_derive"
|
||||
version = "0.8.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7ab8bd5c76eebb8380b26833d30abddbdd885b00dd06178412e0d51d5bfc221f"
|
||||
dependencies = [
|
||||
"heck",
|
||||
"log",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 1.0.109",
|
||||
"xml-rs",
|
||||
]
|
17
Cargo.toml
Normal file
17
Cargo.toml
Normal file
|
@ -0,0 +1,17 @@
|
|||
[package]
|
||||
name = "litefin"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[dependencies]
|
||||
clap = { version = "4.3.0", features = ["derive"] }
|
||||
color-eyre = "0.6.2"
|
||||
maud = "0.25.0"
|
||||
serde = { version = "1.0.163", features = ["derive"] }
|
||||
serde_json = "1.0.96"
|
||||
url = "2.3.1"
|
||||
walkdir = "2.3.3"
|
||||
yaserde = "0.8.0"
|
||||
yaserde_derive = "0.8.0"
|
13
README.md
Normal file
13
README.md
Normal file
|
@ -0,0 +1,13 @@
|
|||
# LiteFin
|
||||
|
||||
Lightweight media player using Jellyfin as a base.
|
||||
|
||||
## Setup
|
||||
|
||||
Import your media library into Jellyfin, and ensure that the `Nfo` "Metadata saver" is enabled when importing; this makes Jellyfin write information about series into the source folder alongside the media files.
|
||||
|
||||
To generate the static HTML pages, use `cargo run -- --base-url <base url> --rewrite-root /media/ --output ./out <library directory>`
|
||||
|
||||
`<base url>` should be replaced with the browser-accessible URL of the `<library directory>`. `--rewrite-root` is used to convert the image paths in the metadata files to the corresponding browser URL.
|
||||
|
||||
Once the command has completed, you can copy the contents of the output directory to your webserver, to allow the library to easily be browsed.
|
165
flake.lock
Normal file
165
flake.lock
Normal file
|
@ -0,0 +1,165 @@
|
|||
{
|
||||
"nodes": {
|
||||
"crane": {
|
||||
"inputs": {
|
||||
"flake-compat": "flake-compat",
|
||||
"flake-utils": "flake-utils",
|
||||
"nixpkgs": [
|
||||
"nixpkgs"
|
||||
],
|
||||
"rust-overlay": "rust-overlay"
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1680584903,
|
||||
"narHash": "sha256-uraq+D3jcLzw/UVk0xMHcnfILfIMa0DLrtAEq2nNlxU=",
|
||||
"owner": "ipetkov",
|
||||
"repo": "crane",
|
||||
"rev": "65d3f6a3970cd46bef5eedfd458300f72c56b3c5",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "ipetkov",
|
||||
"repo": "crane",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"flake-compat": {
|
||||
"flake": false,
|
||||
"locked": {
|
||||
"lastModified": 1673956053,
|
||||
"narHash": "sha256-4gtG9iQuiKITOjNQQeQIpoIB6b16fm+504Ch3sNKLd8=",
|
||||
"owner": "edolstra",
|
||||
"repo": "flake-compat",
|
||||
"rev": "35bb57c0c8d8b62bbfd284272c928ceb64ddbde9",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "edolstra",
|
||||
"repo": "flake-compat",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"flake-utils": {
|
||||
"locked": {
|
||||
"lastModified": 1678901627,
|
||||
"narHash": "sha256-U02riOqrKKzwjsxc/400XnElV+UtPUQWpANPlyazjH0=",
|
||||
"owner": "numtide",
|
||||
"repo": "flake-utils",
|
||||
"rev": "93a2b84fc4b70d9e089d029deacc3583435c2ed6",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "numtide",
|
||||
"repo": "flake-utils",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"flake-utils_2": {
|
||||
"inputs": {
|
||||
"systems": "systems"
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1681037374,
|
||||
"narHash": "sha256-XL6X3VGbEFJZDUouv2xpKg2Aljzu/etPLv5e1FPt1q0=",
|
||||
"owner": "numtide",
|
||||
"repo": "flake-utils",
|
||||
"rev": "033b9f258ca96a10e543d4442071f614dc3f8412",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "numtide",
|
||||
"repo": "flake-utils",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"nixpkgs": {
|
||||
"locked": {
|
||||
"lastModified": 1681064623,
|
||||
"narHash": "sha256-UngFykv8KTrjxFeu4ZMvsOwFrxsa0A3ZPwyLhxb0Rrs=",
|
||||
"owner": "NixOS",
|
||||
"repo": "nixpkgs",
|
||||
"rev": "da0b0bc6a5d699a8a9ffbf9e1b19e8642307062a",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "NixOS",
|
||||
"ref": "nixpkgs-unstable",
|
||||
"repo": "nixpkgs",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"root": {
|
||||
"inputs": {
|
||||
"crane": "crane",
|
||||
"flake-utils": "flake-utils_2",
|
||||
"nixpkgs": "nixpkgs",
|
||||
"rust-overlay": "rust-overlay_2"
|
||||
}
|
||||
},
|
||||
"rust-overlay": {
|
||||
"inputs": {
|
||||
"flake-utils": [
|
||||
"crane",
|
||||
"flake-utils"
|
||||
],
|
||||
"nixpkgs": [
|
||||
"crane",
|
||||
"nixpkgs"
|
||||
]
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1680488274,
|
||||
"narHash": "sha256-0vYMrZDdokVmPQQXtFpnqA2wEgCCUXf5a3dDuDVshn0=",
|
||||
"owner": "oxalica",
|
||||
"repo": "rust-overlay",
|
||||
"rev": "7ec2ff598a172c6e8584457167575b3a1a5d80d8",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "oxalica",
|
||||
"repo": "rust-overlay",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"rust-overlay_2": {
|
||||
"inputs": {
|
||||
"flake-utils": [
|
||||
"flake-utils"
|
||||
],
|
||||
"nixpkgs": [
|
||||
"nixpkgs"
|
||||
]
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1681093076,
|
||||
"narHash": "sha256-6uLZNeuP5jDDGlFkXgcoAxsJhTKy8yUTw25zdLHzdxE=",
|
||||
"owner": "oxalica",
|
||||
"repo": "rust-overlay",
|
||||
"rev": "45c2ed9dd1397526dad35fc867c43955d87f9f3f",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "oxalica",
|
||||
"repo": "rust-overlay",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"systems": {
|
||||
"locked": {
|
||||
"lastModified": 1681028828,
|
||||
"narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=",
|
||||
"owner": "nix-systems",
|
||||
"repo": "default",
|
||||
"rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "nix-systems",
|
||||
"repo": "default",
|
||||
"type": "github"
|
||||
}
|
||||
}
|
||||
},
|
||||
"root": "root",
|
||||
"version": 7
|
||||
}
|
90
flake.nix
Normal file
90
flake.nix
Normal file
|
@ -0,0 +1,90 @@
|
|||
{
|
||||
inputs = {
|
||||
nixpkgs.url = "github:NixOS/nixpkgs/nixpkgs-unstable";
|
||||
crane.url = "github:ipetkov/crane";
|
||||
crane.inputs.nixpkgs.follows = "nixpkgs";
|
||||
flake-utils.url = "github:numtide/flake-utils";
|
||||
rust-overlay = {
|
||||
url = "github:oxalica/rust-overlay";
|
||||
inputs = {
|
||||
nixpkgs.follows = "nixpkgs";
|
||||
flake-utils.follows = "flake-utils";
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
outputs = { self, nixpkgs, crane, rust-overlay, flake-utils }:
|
||||
flake-utils.lib.eachDefaultSystem (system:
|
||||
let
|
||||
overlays = [ (import rust-overlay) ];
|
||||
pkgs = import nixpkgs {
|
||||
inherit system overlays;
|
||||
};
|
||||
inherit (pkgs) lib;
|
||||
|
||||
rust-toolchain = pkgs.rust-bin.stable.latest.default;
|
||||
rust-dev-toolchain = pkgs.rust-bin.stable.latest.default.override {
|
||||
extensions = [ "rust-src" "rust-analyzer" ];
|
||||
};
|
||||
|
||||
craneLib = (crane.mkLib pkgs).overrideToolchain rust-toolchain;
|
||||
|
||||
cssFilter = path: _type: builtins.match ".*css$" path != null;
|
||||
cssOrCargo = path: type: (cssFilter path type) || (craneLib.filterCargoSources path type);
|
||||
src = lib.cleanSourceWith {
|
||||
src = craneLib.path ./.;
|
||||
filter = cssOrCargo;
|
||||
};
|
||||
|
||||
buildInputs = [
|
||||
pkgs.bintools
|
||||
] ++ lib.optionals pkgs.stdenv.isDarwin [
|
||||
# Additional darwin specific inputs can be set here
|
||||
pkgs.libiconv
|
||||
];
|
||||
|
||||
cargoArtifacts = craneLib.buildDepsOnly {
|
||||
inherit src buildInputs;
|
||||
};
|
||||
|
||||
# Build the actual crate itself, reusing the dependency
|
||||
# artifacts from above.
|
||||
litefin = craneLib.buildPackage {
|
||||
inherit cargoArtifacts src buildInputs;
|
||||
};
|
||||
in
|
||||
{
|
||||
checks = {
|
||||
# Build the crate as part of `nix flake check` for convenience
|
||||
inherit litefin;
|
||||
|
||||
# Run clippy (and deny all warnings) on the crate source,
|
||||
# again, resuing the dependency artifacts from above.
|
||||
#
|
||||
# Note that this is done as a separate derivation so that
|
||||
# we can block the CI if there are issues here, but not
|
||||
# prevent downstream consumers from building our crate by itself.
|
||||
litefin-clippy = craneLib.cargoClippy {
|
||||
inherit cargoArtifacts src buildInputs;
|
||||
cargoClippyExtraArgs = "--all-targets -- --deny warnings";
|
||||
};
|
||||
|
||||
# Check formatting
|
||||
litefin-fmt = craneLib.cargoFmt {
|
||||
inherit src;
|
||||
};
|
||||
};
|
||||
|
||||
packages.default = litefin;
|
||||
|
||||
devShells.default = pkgs.mkShell {
|
||||
inputsFrom = builtins.attrValues self.checks;
|
||||
|
||||
# Extra inputs can be added here
|
||||
nativeBuildInputs = with pkgs; [
|
||||
rust-dev-toolchain
|
||||
cargo-watch
|
||||
];
|
||||
};
|
||||
});
|
||||
}
|
36
src/main.rs
Normal file
36
src/main.rs
Normal file
|
@ -0,0 +1,36 @@
|
|||
use std::path::PathBuf;
|
||||
|
||||
use clap::Parser;
|
||||
use color_eyre::Result;
|
||||
|
||||
mod nfo;
|
||||
mod templates;
|
||||
mod util;
|
||||
mod walker;
|
||||
|
||||
#[derive(clap::Parser)]
|
||||
#[command(version, about)]
|
||||
pub struct Args {
|
||||
/// Root media directory to walk
|
||||
dir: PathBuf,
|
||||
|
||||
/// Root directory that media files are contained in, used to rewrite paths in nfo files.
|
||||
#[clap(short, long)]
|
||||
rewrite_root: String,
|
||||
|
||||
/// URL pointing to the root `dir`
|
||||
#[clap(short, long)]
|
||||
base_url: String,
|
||||
|
||||
/// Path to write the generated HTML files to.
|
||||
#[clap(short, long)]
|
||||
output: PathBuf,
|
||||
}
|
||||
|
||||
fn main() -> Result<()> {
|
||||
let args = Args::parse();
|
||||
|
||||
walker::walk_metadata(&args)?;
|
||||
|
||||
Ok(())
|
||||
}
|
126
src/nfo.rs
Normal file
126
src/nfo.rs
Normal file
|
@ -0,0 +1,126 @@
|
|||
use yaserde_derive::YaDeserialize;
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct ItemMeta {
|
||||
pub url: String,
|
||||
pub ty: ContentType,
|
||||
pub data: ItemData,
|
||||
pub parent: Option<ItemData>,
|
||||
}
|
||||
|
||||
impl ItemMeta {
|
||||
pub fn fanart_url(&self) -> Option<&str> {
|
||||
if let Some(fanart) = &self.data.art.fanart {
|
||||
Some(fanart)
|
||||
} else if let Some(parent) = &self.parent {
|
||||
if let Some(fanart) = &parent.art.fanart {
|
||||
Some(fanart)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub enum ContentType {
|
||||
TvShow,
|
||||
Season,
|
||||
Episode,
|
||||
}
|
||||
|
||||
impl ContentType {
|
||||
pub fn is_season(&self) -> bool {
|
||||
match self {
|
||||
ContentType::Season => true,
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn is_episode(&self) -> bool {
|
||||
match self {
|
||||
ContentType::Episode => true,
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Default, YaDeserialize)]
|
||||
pub struct ItemData {
|
||||
pub plot: Option<String>,
|
||||
pub outline: Option<String>,
|
||||
pub lockdata: bool,
|
||||
#[yaserde(rename = "dateadded")]
|
||||
pub date_added: String,
|
||||
pub title: String,
|
||||
#[yaserde(rename = "originaltitle")]
|
||||
pub original_title: Option<String>,
|
||||
pub trailer: Vec<String>,
|
||||
pub rating: f64,
|
||||
pub year: u32,
|
||||
pub mpaa: Option<String>,
|
||||
#[yaserde(rename = "imdbid")]
|
||||
pub imdb_id: Option<String>,
|
||||
#[yaserde(rename = "tmdbid")]
|
||||
pub tmdb_id: Option<String>,
|
||||
#[yaserde(rename = "tvdbid")]
|
||||
pub tvdb_id: String,
|
||||
pub premiered: Option<String>,
|
||||
#[yaserde(rename = "releasedate")]
|
||||
pub release_date: Option<String>,
|
||||
#[yaserde(rename = "enddate")]
|
||||
pub end_date: Option<String>,
|
||||
pub runtime: u32,
|
||||
#[yaserde(rename = "genre")]
|
||||
pub genres: Vec<String>,
|
||||
pub studio: Option<String>,
|
||||
#[yaserde(rename = "tag")]
|
||||
pub tags: Vec<String>,
|
||||
pub art: Art,
|
||||
#[yaserde(rename = "actor")]
|
||||
pub actors: Vec<Actor>,
|
||||
pub id: Option<String>,
|
||||
pub status: Option<String>,
|
||||
#[yaserde(rename = "seasonnumber")]
|
||||
pub season_number: Option<i32>,
|
||||
pub episode: Option<i32>,
|
||||
}
|
||||
|
||||
impl ItemData {
|
||||
pub fn description(&self) -> &str {
|
||||
if let Some(outline) = &self.outline {
|
||||
outline
|
||||
} else if let Some(plot) = &self.plot {
|
||||
plot
|
||||
} else {
|
||||
""
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Default, YaDeserialize)]
|
||||
#[yaserde(rename = "art")]
|
||||
pub struct Art {
|
||||
pub poster: Option<String>,
|
||||
pub fanart: Option<String>,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Default, YaDeserialize)]
|
||||
#[yaserde(rename = "actor")]
|
||||
pub struct Actor {
|
||||
pub name: String,
|
||||
pub role: String,
|
||||
#[yaserde(rename = "type")]
|
||||
pub ty: String,
|
||||
#[yaserde(rename = "sortorder")]
|
||||
pub sort_order: i32,
|
||||
pub thumb: Option<String>,
|
||||
}
|
||||
|
||||
pub struct SubtitleTrack {
|
||||
pub url: String,
|
||||
pub label: String,
|
||||
pub lang: String,
|
||||
}
|
118
src/style.css
Normal file
118
src/style.css
Normal file
|
@ -0,0 +1,118 @@
|
|||
/* AshCSS */
|
||||
:root {
|
||||
--background: #13092b;
|
||||
--background-transparent: #140a2bee;
|
||||
--background-2: #090f2b;
|
||||
--foreground: #ddd;
|
||||
--foreground-bright: #fff;
|
||||
--foreground-dim: #aaa;
|
||||
--error: orange;
|
||||
--accent: #f9027a;
|
||||
--accent-dim: hsl(331, 50%, 49%);
|
||||
}
|
||||
|
||||
html, body {
|
||||
font-family: 'Poppins', sans-serif;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
background-color: var(--background);
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
* {
|
||||
box-sizing: inherit;
|
||||
}
|
||||
|
||||
h1, h2, h3, h4, h5, h6 {
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
a {
|
||||
color: white;
|
||||
}
|
||||
|
||||
.fadein {
|
||||
transition: opacity 500ms ease;
|
||||
}
|
||||
|
||||
.poster-body {
|
||||
min-width: 100%;
|
||||
min-height: 100vh;
|
||||
/* background-size: cover;
|
||||
background-position: center; */
|
||||
}
|
||||
|
||||
.poster-image {
|
||||
width: 100%;
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
max-height: 100vh;
|
||||
object-fit: cover;
|
||||
}
|
||||
|
||||
.poster-details {
|
||||
position: absolute;
|
||||
width: 100%;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
color: var(--foreground);
|
||||
background: var(--background-transparent);
|
||||
background: linear-gradient(0deg, var(--background-transparent) 50%, rgba(0,0,0,0) 100%);
|
||||
}
|
||||
|
||||
.poster-details-gradient {
|
||||
width: 100%;
|
||||
height: 256px;
|
||||
}
|
||||
|
||||
.poster-details-content {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
padding: 16px;
|
||||
max-width: 100vw;
|
||||
gap: 16px;
|
||||
}
|
||||
|
||||
.poster-details-content img {
|
||||
/* display: inline-block; */
|
||||
height: 100%;
|
||||
max-height: 24rem;
|
||||
}
|
||||
|
||||
.metadata {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
gap: 16px;
|
||||
align-items: flex-end;
|
||||
}
|
||||
|
||||
.metadata-head {
|
||||
max-width: 720px;
|
||||
}
|
||||
|
||||
.seasons {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
gap: 16px;
|
||||
padding-left: 16px;
|
||||
padding-right: 16px;
|
||||
max-width: 100%;
|
||||
overflow: auto;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.season {
|
||||
transform-origin: center;
|
||||
transition: transform 300ms ease;
|
||||
text-align: center;
|
||||
margin-top: 1rem;
|
||||
}
|
||||
|
||||
.season:hover {
|
||||
transform: translateY(-1rem) scale(1.05);
|
||||
}
|
||||
|
||||
.season img {
|
||||
height: 12rem;
|
||||
}
|
201
src/templates.rs
Normal file
201
src/templates.rs
Normal file
|
@ -0,0 +1,201 @@
|
|||
use color_eyre::Result;
|
||||
|
||||
use crate::{
|
||||
nfo::{ItemData, ItemMeta, SubtitleTrack},
|
||||
util::get_url,
|
||||
Args,
|
||||
};
|
||||
|
||||
fn base_template(
|
||||
title: impl maud::Render,
|
||||
body: impl maud::Render,
|
||||
extra_head: Option<impl maud::Render>,
|
||||
) -> maud::Markup {
|
||||
maud::html! {
|
||||
(maud::DOCTYPE)
|
||||
html {
|
||||
head {
|
||||
meta charset="utf-8";
|
||||
meta name="viewport" content="width=device-width, initial-scale=1.0";
|
||||
title { (title) }
|
||||
link rel="stylesheet" href="https://cdn.ashhhleyyy.dev/file/ashhhleyyy-assets/css/addf63c63146a6d5.css";
|
||||
style { (include_str!("style.css")) }
|
||||
@if let Some(extra_head) = extra_head {
|
||||
(extra_head)
|
||||
}
|
||||
}
|
||||
body {
|
||||
(body)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn player(
|
||||
title: &str,
|
||||
video_url: &str,
|
||||
poster: &str,
|
||||
subtitles: &[SubtitleTrack],
|
||||
args: &Args,
|
||||
) -> Result<maud::Markup> {
|
||||
Ok(base_template(
|
||||
title,
|
||||
maud::html! {
|
||||
media-player
|
||||
title=(title)
|
||||
src=(video_url)
|
||||
poster=(get_url(poster, args)?)
|
||||
aspect-ratio="16/9"
|
||||
style="height: 100vh"
|
||||
crossorigin {
|
||||
media-outlet {
|
||||
media-poster alt=(title) {}
|
||||
@for subtitle_track in subtitles {
|
||||
track src=(get_url(&subtitle_track.url, args)?)
|
||||
label=(subtitle_track.label)
|
||||
srclang=(subtitle_track.lang)
|
||||
kind="subtitles"
|
||||
default;
|
||||
}
|
||||
}
|
||||
media-community-skin {}
|
||||
}
|
||||
},
|
||||
Some(maud::html! {
|
||||
link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/vidstack/styles/defaults.min.css";
|
||||
link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/vidstack/styles/community-skin/video.min.css";
|
||||
script type="module" src="https://cdn.jsdelivr.net/npm/vidstack/dist/cdn/dev.js" {}
|
||||
}),
|
||||
))
|
||||
}
|
||||
|
||||
pub fn tv_show(details: &ItemMeta, args: &Args, children: &[ItemMeta]) -> Result<maud::Markup> {
|
||||
let fanart_url = details.fanart_url().map(|s| get_url(s, args)).transpose()?;
|
||||
let poster_url = details
|
||||
.data
|
||||
.art
|
||||
.poster
|
||||
.as_ref()
|
||||
.map(|s| get_url(s, args))
|
||||
.transpose()?;
|
||||
Ok(base_template(
|
||||
&details.data.title,
|
||||
maud::html! {
|
||||
.poster-body {
|
||||
@if let Some(fanart_url) = fanart_url {
|
||||
(fade_image(&fanart_url, None, None, None, Some("poster-image")))
|
||||
// (fade_image("https://placehold.co/1920x1080", None, None, None, Some("poster-image")))
|
||||
}
|
||||
.poster-details {
|
||||
.poster-details-gradient {}
|
||||
.poster-details-content {
|
||||
.metadata {
|
||||
@if let Some(poster_url) = poster_url {
|
||||
(fade_image(&poster_url, None, None, None, None))
|
||||
// (fade_image("https://placehold.co/1364x2042", None, None, None, None))
|
||||
}
|
||||
.metadata-head {
|
||||
h1 { (details.data.title) }
|
||||
p.description { (details.data.description()) }
|
||||
}
|
||||
}
|
||||
.seasons {
|
||||
@for child in children {
|
||||
@if child.ty.is_season() {
|
||||
@match season_card(&child.data, &child.url, args) {
|
||||
Ok(s) => (s),
|
||||
Err(_) => {},
|
||||
}
|
||||
} @else if child.ty.is_episode() {
|
||||
@match episode_card(&child.data, &child.url, args) {
|
||||
Ok(s) => (s),
|
||||
Err(_) => {},
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
None::<maud::Markup>,
|
||||
))
|
||||
}
|
||||
|
||||
fn season_card(info: &ItemData, url: &str, args: &Args) -> Result<maud::Markup> {
|
||||
let poster_url = get_url(&info.art.poster.clone().unwrap(), args)?;
|
||||
Ok(maud::html! {
|
||||
.season {
|
||||
a href=(url) {
|
||||
(fade_image(&poster_url, None, None, None, None))
|
||||
// (fade_image("https://placehold.co/300x200", None, None, None, None))
|
||||
.h2 { (info.title) }
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
fn episode_card(info: &ItemData, url: &str, args: &Args) -> Result<maud::Markup> {
|
||||
let poster_url = get_url(&info.art.poster.clone().unwrap(), args)?;
|
||||
Ok(maud::html! {
|
||||
.season {
|
||||
a href=(url) {
|
||||
(fade_image(&poster_url, None, None, None, None))
|
||||
// (fade_image("https://placehold.co/300x200", None, None, None, None))
|
||||
.h2 { (info.title) }
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
fn fade_image(
|
||||
url: &str,
|
||||
alt: Option<&str>,
|
||||
width: Option<u32>,
|
||||
height: Option<u32>,
|
||||
class: Option<&str>,
|
||||
) -> maud::Markup {
|
||||
#[derive(serde::Serialize)]
|
||||
struct ImageInfo<'a> {
|
||||
url: &'a str,
|
||||
alt: Option<&'a str>,
|
||||
width: Option<u32>,
|
||||
height: Option<u32>,
|
||||
class: Option<&'a str>,
|
||||
}
|
||||
|
||||
let data = serde_json::to_string(&ImageInfo {
|
||||
url,
|
||||
alt,
|
||||
width,
|
||||
height,
|
||||
class,
|
||||
})
|
||||
.expect("failed to serialise");
|
||||
|
||||
maud::html! {
|
||||
noscript {
|
||||
img src=(url) loading="lazy" width=[width] height=[height] alt=[alt] class=[class];
|
||||
}
|
||||
|
||||
script {
|
||||
(maud::PreEscaped(format!(r#"
|
||||
(function() {{
|
||||
const data = {data};
|
||||
const img = document.createElement('img');
|
||||
img.src = data.url;
|
||||
if (data.alt) img.alt = data.alt;
|
||||
if (data.width) img.width = data.width;
|
||||
if (data.height) img.height = data.height;
|
||||
if (data.class) img.className = data.class;
|
||||
img.classList.add('fadein');
|
||||
img.style.opacity = 0;
|
||||
img.onload = () => {{
|
||||
img.style.opacity = 1;
|
||||
}};
|
||||
document.currentScript.parentElement.insertBefore(img, document.currentScript);
|
||||
}})()
|
||||
"#)))
|
||||
}
|
||||
}
|
||||
}
|
23
src/util.rs
Normal file
23
src/util.rs
Normal file
|
@ -0,0 +1,23 @@
|
|||
use color_eyre::Result;
|
||||
use url::Url;
|
||||
|
||||
use crate::Args;
|
||||
|
||||
pub(crate) fn get_url(path: &str, args: &Args) -> Result<String> {
|
||||
let prefix = if args.rewrite_root.ends_with('/') {
|
||||
args.rewrite_root.clone()
|
||||
} else {
|
||||
format!("{}/", args.rewrite_root)
|
||||
};
|
||||
let path = match path.strip_prefix(&prefix) {
|
||||
Some(path) => path,
|
||||
None => path,
|
||||
};
|
||||
let url = if args.base_url.ends_with('/') {
|
||||
format!("{}{path}", args.base_url)
|
||||
} else {
|
||||
format!("{}/{path}", args.base_url)
|
||||
};
|
||||
let url = Url::parse(&url)?;
|
||||
Ok(url.to_string())
|
||||
}
|
139
src/walker.rs
Normal file
139
src/walker.rs
Normal file
|
@ -0,0 +1,139 @@
|
|||
use std::{path::{Path, PathBuf}, fs::File, collections::HashMap};
|
||||
|
||||
use color_eyre::{Result, eyre::eyre};
|
||||
|
||||
use crate::{nfo::{ContentType, ItemMeta, ItemData}, Args, templates, util::get_url};
|
||||
|
||||
pub fn walk_metadata(args: &Args) -> Result<()> {
|
||||
let mut path_cache = HashMap::<PathBuf, ItemMeta>::new();
|
||||
|
||||
let walk = walkdir::WalkDir::new(&args.dir)
|
||||
.contents_first(true);
|
||||
|
||||
for entry in walk {
|
||||
let entry = entry?;
|
||||
if !entry.file_type().is_dir() { continue; }
|
||||
let path = entry.path();
|
||||
let read = std::fs::read_dir(path)?;
|
||||
let mut entries = Vec::with_capacity(read.size_hint().0);
|
||||
for e in read {
|
||||
entries.push(e?.path());
|
||||
}
|
||||
let content_type = determine_contents(path, &entries);
|
||||
if let Some(content_type) = content_type {
|
||||
let info = load_contents(path, content_type, args)?;
|
||||
let output_dir = path.strip_prefix(&args.dir)?;
|
||||
let output_dir = args.output.join(output_dir);
|
||||
std::fs::create_dir_all(&output_dir)?;
|
||||
let output_path = output_dir.join("index.html");
|
||||
match info.ty {
|
||||
ContentType::TvShow => {
|
||||
let children = find_tv_show_children(&path_cache, &entries);
|
||||
let rendered = templates::tv_show(&info, args, &children)?;
|
||||
std::fs::write(output_path, rendered.0)?;
|
||||
},
|
||||
ContentType::Season => {
|
||||
let mut children = find_season_children(&path_cache, &entries, &info.data, args)?;
|
||||
|
||||
for child in &mut children {
|
||||
let rendered = templates::player(
|
||||
&child.data.title,
|
||||
&child.url,
|
||||
child.data.art.poster.as_ref().unwrap(),
|
||||
&[],
|
||||
args,
|
||||
)?;
|
||||
let filename = format!("Episode {}.html", child.data.episode.unwrap());
|
||||
std::fs::write(output_dir.join(&filename), rendered.0)?;
|
||||
child.url = filename;
|
||||
}
|
||||
|
||||
let rendered = templates::tv_show(&info, args, &children)?;
|
||||
std::fs::write(output_path, rendered.0)?;
|
||||
},
|
||||
ContentType::Episode => unreachable!(),
|
||||
}
|
||||
println!("{path:?}: {:?}", info.ty);
|
||||
path_cache.insert(path.to_owned(), info);
|
||||
} else {
|
||||
println!("{path:?}: no content type");
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn find_tv_show_children(path_cache: &HashMap<PathBuf, ItemMeta>, entries: &[PathBuf]) -> Vec<ItemMeta> {
|
||||
let mut children = vec![];
|
||||
for entry in entries {
|
||||
if let Some(info) = path_cache.get(entry) {
|
||||
let mut info = info.clone();
|
||||
info.url = entry.file_name().unwrap().to_string_lossy().to_string();
|
||||
children.push(info);
|
||||
}
|
||||
}
|
||||
children.sort_by_key(|c| c.data.season_number);
|
||||
children
|
||||
}
|
||||
|
||||
fn find_season_children(_path_cache: &HashMap<PathBuf, ItemMeta>, entries: &[PathBuf], parent: &ItemData, args: &Args) -> Result<Vec<ItemMeta>> {
|
||||
let mut children = vec![];
|
||||
for entry in entries {
|
||||
if let (Some(stem), Some(ext)) = (entry.file_stem(), entry.extension()) {
|
||||
if let (Some(stem), Some(ext)) = (stem.to_str(), ext.to_str()) {
|
||||
if stem.contains("Episode") && ext == "nfo" {
|
||||
let data = read_data(entry)?;
|
||||
let video = entry.with_extension("mp4");
|
||||
let url = get_url(&video.to_string_lossy().replace("/stuff/ash/Series", "/media/Shows"), args)?;
|
||||
children.push(ItemMeta {
|
||||
url,
|
||||
ty: ContentType::Episode,
|
||||
data,
|
||||
parent: Some(parent.clone()),
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
children.sort_by_key(|c| c.data.episode);
|
||||
Ok(children)
|
||||
}
|
||||
|
||||
fn read_data(path: &Path) -> Result<ItemData> {
|
||||
let mut f = File::open(path)?;
|
||||
let data: ItemData = yaserde::de::from_reader(&mut f).map_err(|e| eyre!("failed to parse directory meta: {e}"))?;
|
||||
Ok(data)
|
||||
}
|
||||
|
||||
fn load_contents(path: &Path, content_type: ContentType, args: &Args) -> Result<ItemMeta> {
|
||||
let meta_file = match content_type {
|
||||
ContentType::TvShow => "tvshow.nfo",
|
||||
ContentType::Season => "season.nfo",
|
||||
ContentType::Episode => unreachable!(),
|
||||
};
|
||||
let data = read_data(&path.join(meta_file))?;
|
||||
let url = get_url(&path.to_string_lossy().to_owned(), args)?;
|
||||
let parent = match content_type {
|
||||
ContentType::TvShow => None,
|
||||
ContentType::Season => Some(read_data(&path.parent().unwrap().join("tvshow.nfo"))?),
|
||||
ContentType::Episode => unreachable!(),
|
||||
};
|
||||
Ok(ItemMeta {
|
||||
ty: content_type,
|
||||
data,
|
||||
url,
|
||||
parent,
|
||||
})
|
||||
}
|
||||
|
||||
fn determine_contents(_path: &Path, entries: &[PathBuf]) -> Option<ContentType> {
|
||||
for entry in entries {
|
||||
if entry.ends_with("tvshow.nfo") {
|
||||
return Some(ContentType::TvShow);
|
||||
}
|
||||
if entry.ends_with("season.nfo") {
|
||||
return Some(ContentType::Season);
|
||||
}
|
||||
}
|
||||
None
|
||||
}
|
Loading…
Reference in a new issue