feat: initial commit

This commit is contained in:
Ashhhleyyy 2023-06-15 19:32:03 +01:00
commit 2b26392873
Signed by: ash
GPG key ID: 83B789081A0878FB
13 changed files with 1723 additions and 0 deletions

1
.envrc Normal file
View file

@ -0,0 +1 @@
use flake

5
.gitignore vendored Normal file
View file

@ -0,0 +1,5 @@
/target
/result
/.direnv
/out
*.nfo

789
Cargo.lock generated Normal file
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
}