mirror of
https://github.com/euzu/tuliprox.git
synced 2026-10-31 03:02:24 +01:00
Merge pull request #307 from euzu/feature/refactor_config
Feature/refactor config
This commit is contained in:
@@ -1,3 +1,4 @@
|
||||
dist
|
||||
/resources/*.ts
|
||||
/release
|
||||
/target
|
||||
|
||||
Generated
+64
-58
@@ -143,9 +143,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "async-compression"
|
||||
version = "0.4.24"
|
||||
version = "0.4.25"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d615619615a650c571269c00dca41db04b9210037fa76ed8239f70404ab56985"
|
||||
checksum = "40f6024f3f856663b45fd0c9b6f2024034a702f453549449e0d84a305900dad4"
|
||||
dependencies = [
|
||||
"brotli",
|
||||
"flate2",
|
||||
@@ -165,9 +165,9 @@ checksum = "1505bd5d3d116872e7271a6d4e16d81d0c8570876c8de68093a09ac269d8aac0"
|
||||
|
||||
[[package]]
|
||||
name = "autocfg"
|
||||
version = "1.4.0"
|
||||
version = "1.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ace50bade8e6234aa140d9a2f552bbee1db4d353f69b8217bc503490fc1a9f26"
|
||||
checksum = "c08606f8c3cbf4ce6ec8e28fb0014a2c086708fe954eaa885384a6165172e7e8"
|
||||
|
||||
[[package]]
|
||||
name = "axum"
|
||||
@@ -368,9 +368,9 @@ checksum = "a2698f953def977c68f935bb0dfa959375ad4638570e969e2f1e9f433cbf1af6"
|
||||
|
||||
[[package]]
|
||||
name = "cc"
|
||||
version = "1.2.26"
|
||||
version = "1.2.27"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "956a5e21988b87f372569b66183b78babf23ebc2e744b733e4350a752c4dafac"
|
||||
checksum = "d487aa071b5f64da6f19a3e848e3578944b726ee5a4854b82172f02aa876bfdc"
|
||||
dependencies = [
|
||||
"jobserver",
|
||||
"libc",
|
||||
@@ -995,7 +995,7 @@ dependencies = [
|
||||
"cfg-if",
|
||||
"js-sys",
|
||||
"libc",
|
||||
"wasi 0.11.0+wasi-snapshot-preview1",
|
||||
"wasi 0.11.1+wasi-snapshot-preview1",
|
||||
"wasm-bindgen",
|
||||
]
|
||||
|
||||
@@ -1457,9 +1457,9 @@ checksum = "4a5f13b858c8d314ee3e8f639011f7ccefe71f97f96e50151fb991f267928e2c"
|
||||
|
||||
[[package]]
|
||||
name = "jiff"
|
||||
version = "0.2.14"
|
||||
version = "0.2.15"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a194df1107f33c79f4f93d02c80798520551949d59dfad22b6157048a88cca93"
|
||||
checksum = "be1f93b8b1eb69c77f24bbb0afdf66f54b632ee39af40ca21c4365a1d7347e49"
|
||||
dependencies = [
|
||||
"jiff-static",
|
||||
"log",
|
||||
@@ -1470,9 +1470,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "jiff-static"
|
||||
version = "0.2.14"
|
||||
version = "0.2.15"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6c6e1db7ed32c6c71b759497fae34bf7933636f75a251b9e736555da426f6442"
|
||||
checksum = "03343451ff899767262ec32146f6d559dd759fdadf42ff0e227c7c48f72594b4"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
@@ -1542,9 +1542,9 @@ checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe"
|
||||
|
||||
[[package]]
|
||||
name = "libc"
|
||||
version = "0.2.172"
|
||||
version = "0.2.174"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d750af042f7ef4f724306de029d18836c26c1765a54a6a3f094cbd23a7267ffa"
|
||||
checksum = "1171693293099992e19cddea4e8b849964e9846f4acee11b3948bcc337be8776"
|
||||
|
||||
[[package]]
|
||||
name = "libnghttp2-sys"
|
||||
@@ -1627,9 +1627,9 @@ checksum = "47e1ffaa40ddd1f3ed91f717a33c8c0ee23fff369e3aa8772b9605cc1d22f4c3"
|
||||
|
||||
[[package]]
|
||||
name = "memchr"
|
||||
version = "2.7.4"
|
||||
version = "2.7.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3"
|
||||
checksum = "32a282da65faaf38286cf3be983213fcf1d2e2a58700e808f83f4ea9a4804bc0"
|
||||
|
||||
[[package]]
|
||||
name = "mime"
|
||||
@@ -1664,7 +1664,7 @@ checksum = "78bed444cc8a2160f01cbcf811ef18cac863ad68ae8ca62092e8db51d51c761c"
|
||||
dependencies = [
|
||||
"libc",
|
||||
"log",
|
||||
"wasi 0.11.0+wasi-snapshot-preview1",
|
||||
"wasi 0.11.1+wasi-snapshot-preview1",
|
||||
"windows-sys 0.59.0",
|
||||
]
|
||||
|
||||
@@ -1914,9 +1914,9 @@ checksum = "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e"
|
||||
|
||||
[[package]]
|
||||
name = "pest"
|
||||
version = "2.8.0"
|
||||
version = "2.8.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "198db74531d58c70a361c42201efde7e2591e976d518caf7662a47dc5720e7b6"
|
||||
checksum = "1db05f56d34358a8b1066f67cbb203ee3e7ed2ba674a6263a1d5ec6db2204323"
|
||||
dependencies = [
|
||||
"memchr",
|
||||
"thiserror 2.0.12",
|
||||
@@ -1925,9 +1925,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "pest_derive"
|
||||
version = "2.8.0"
|
||||
version = "2.8.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d725d9cfd79e87dccc9341a2ef39d1b6f6353d68c4b33c177febbe1a402c97c5"
|
||||
checksum = "bb056d9e8ea77922845ec74a1c4e8fb17e7c218cc4fc11a15c5d25e189aa40bc"
|
||||
dependencies = [
|
||||
"pest",
|
||||
"pest_generator",
|
||||
@@ -1935,9 +1935,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "pest_generator"
|
||||
version = "2.8.0"
|
||||
version = "2.8.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "db7d01726be8ab66ab32f9df467ae8b1148906685bbe75c82d1e65d7f5b3f841"
|
||||
checksum = "87e404e638f781eb3202dc82db6760c8ae8a1eeef7fb3fa8264b2ef280504966"
|
||||
dependencies = [
|
||||
"pest",
|
||||
"pest_meta",
|
||||
@@ -1948,11 +1948,10 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "pest_meta"
|
||||
version = "2.8.0"
|
||||
version = "2.8.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7f9f832470494906d1fca5329f8ab5791cc60beb230c74815dff541cbd2b5ca0"
|
||||
checksum = "edd1101f170f5903fde0914f899bb503d9ff5271d7ba76bbb70bea63690cc0d5"
|
||||
dependencies = [
|
||||
"once_cell",
|
||||
"pest",
|
||||
"sha2",
|
||||
]
|
||||
@@ -2061,15 +2060,15 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "quanta"
|
||||
version = "0.12.5"
|
||||
version = "0.12.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3bd1fe6824cea6538803de3ff1bc0cf3949024db3d43c9643024bfb33a807c0e"
|
||||
checksum = "f3ab5a9d756f0d97bdc89019bd2e4ea098cf9cde50ee7564dde6b81ccc8f06c7"
|
||||
dependencies = [
|
||||
"crossbeam-utils",
|
||||
"libc",
|
||||
"once_cell",
|
||||
"raw-cpuid",
|
||||
"wasi 0.11.0+wasi-snapshot-preview1",
|
||||
"wasi 0.11.1+wasi-snapshot-preview1",
|
||||
"web-sys",
|
||||
"winapi",
|
||||
]
|
||||
@@ -2126,9 +2125,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "quinn-udp"
|
||||
version = "0.5.12"
|
||||
version = "0.5.13"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ee4e529991f949c5e25755532370b8af5d114acae52326361d68d47af64aa842"
|
||||
checksum = "fcebb1209ee276352ef14ff8732e24cc2b02bbac986cd74a4c81bcb2f9881970"
|
||||
dependencies = [
|
||||
"cfg_aliases",
|
||||
"libc",
|
||||
@@ -2149,9 +2148,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "r-efi"
|
||||
version = "5.2.0"
|
||||
version = "5.3.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "74765f6d916ee2faa39bc8e68e4f3ed8949b48cccdac59983d287a7cb71ce9c5"
|
||||
checksum = "69cdb34c158ceb288df11e18b4bd39de994f6657d83847bdffdbd7f346754b0f"
|
||||
|
||||
[[package]]
|
||||
name = "rand"
|
||||
@@ -2213,9 +2212,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "redox_syscall"
|
||||
version = "0.5.12"
|
||||
version = "0.5.13"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "928fca9cf2aa042393a8325b9ead81d2f0df4cb12e1e24cef072922ccd99c5af"
|
||||
checksum = "0d04b7d0ee6b4a0207a0a7adb104d23ecb0b47d6beae7152d0fa34b692b29fd6"
|
||||
dependencies = [
|
||||
"bitflags 2.9.1",
|
||||
]
|
||||
@@ -2251,9 +2250,9 @@ checksum = "2b15c43186be67a4fd63bee50d0303afffcef381492ebe2c5d87f324e1b8815c"
|
||||
|
||||
[[package]]
|
||||
name = "reqwest"
|
||||
version = "0.12.19"
|
||||
version = "0.12.20"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a2f8e5513d63f2e5b386eb5106dc67eaf3f84e95258e210489136b8b92ad6119"
|
||||
checksum = "eabf4c97d9130e2bf606614eb937e86edac8292eaa6f422f995d7e8de1eb1813"
|
||||
dependencies = [
|
||||
"base64 0.22.1",
|
||||
"bytes",
|
||||
@@ -2269,12 +2268,10 @@ dependencies = [
|
||||
"hyper-rustls",
|
||||
"hyper-tls",
|
||||
"hyper-util",
|
||||
"ipnet",
|
||||
"js-sys",
|
||||
"log",
|
||||
"mime",
|
||||
"native-tls",
|
||||
"once_cell",
|
||||
"percent-encoding",
|
||||
"pin-project-lite",
|
||||
"quinn",
|
||||
@@ -2400,9 +2397,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "rustls"
|
||||
version = "0.23.27"
|
||||
version = "0.23.28"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "730944ca083c1c233a75c09f199e973ca499344a2b7ba9e755c457e86fb4a321"
|
||||
checksum = "7160e3e10bf4535308537f3c4e1641468cd0e485175d6163087c0393c7d46643"
|
||||
dependencies = [
|
||||
"once_cell",
|
||||
"ring",
|
||||
@@ -2579,6 +2576,17 @@ dependencies = [
|
||||
"digest",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "shared"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"bitflags 2.9.1",
|
||||
"enum-iterator",
|
||||
"rand",
|
||||
"regex",
|
||||
"serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "shlex"
|
||||
version = "1.3.0"
|
||||
@@ -2599,12 +2607,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "slab"
|
||||
version = "0.4.9"
|
||||
version = "0.4.10"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8f92a496fb766b417c996b9c5e57daf2f7ad3b0bebe1ccfca4856390e3d3bb67"
|
||||
dependencies = [
|
||||
"autocfg",
|
||||
]
|
||||
checksum = "04dc19736151f35336d325007ac991178d504a119863a2fcb3758cdb5e52c50d"
|
||||
|
||||
[[package]]
|
||||
name = "sluice"
|
||||
@@ -2662,9 +2667,9 @@ checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292"
|
||||
|
||||
[[package]]
|
||||
name = "syn"
|
||||
version = "2.0.102"
|
||||
version = "2.0.103"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f6397daf94fa90f058bd0fd88429dd9e5738999cca8d701813c80723add80462"
|
||||
checksum = "e4307e30089d6fd6aff212f2da3a1f9e32f3223b1f010fb09b7c95f90f3ca1e8"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
@@ -2984,9 +2989,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "tracing-attributes"
|
||||
version = "0.1.29"
|
||||
version = "0.1.30"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1b1ffbcf9c6f6b99d386e7444eb608ba646ae452a36b39737deb9663b610f662"
|
||||
checksum = "81383ab64e72a7a8b8e13130c49e3dab29def6d0c7d76a03087b3cf71c5c6903"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
@@ -3024,7 +3029,7 @@ version = "3.1.4"
|
||||
dependencies = [
|
||||
"arc-swap",
|
||||
"axum",
|
||||
"base64 0.21.7",
|
||||
"base64 0.22.1",
|
||||
"bincode",
|
||||
"bitflags 2.9.1",
|
||||
"blake3",
|
||||
@@ -3065,6 +3070,7 @@ dependencies = [
|
||||
"serde",
|
||||
"serde_json",
|
||||
"serde_yaml",
|
||||
"shared",
|
||||
"socket2",
|
||||
"strsim",
|
||||
"tempfile",
|
||||
@@ -3220,9 +3226,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "wasi"
|
||||
version = "0.11.0+wasi-snapshot-preview1"
|
||||
version = "0.11.1+wasi-snapshot-preview1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423"
|
||||
checksum = "ccf3ec651a847eb01de73ccad15eb7d99f80485de043efb2f370cd654f4ea44b"
|
||||
|
||||
[[package]]
|
||||
name = "wasi"
|
||||
@@ -3414,9 +3420,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "windows-link"
|
||||
version = "0.1.1"
|
||||
version = "0.1.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "76840935b766e1b0a05c0066835fb9ec80071d4c09a16f6bd5f7e655e3c14c38"
|
||||
checksum = "5e6ad25900d524eaabdbbb96d20b4311e1e7ae1699af4fb28c17ae66c80d798a"
|
||||
|
||||
[[package]]
|
||||
name = "windows-registry"
|
||||
@@ -3645,18 +3651,18 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "zerocopy"
|
||||
version = "0.8.25"
|
||||
version = "0.8.26"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a1702d9583232ddb9174e01bb7c15a2ab8fb1bc6f227aa1233858c351a3ba0cb"
|
||||
checksum = "1039dd0d3c310cf05de012d8a39ff557cb0d23087fd44cad61df08fc31907a2f"
|
||||
dependencies = [
|
||||
"zerocopy-derive",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "zerocopy-derive"
|
||||
version = "0.8.25"
|
||||
version = "0.8.26"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "28a6e20d751156648aa063f3800b706ee209a32c0b4d9f24be3d980b01be55ef"
|
||||
checksum = "9ecf5b4cc5364572d7f4c329661bcc82724222973f2cab6f050a4e5c22f75181"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
|
||||
+3
-69
@@ -1,7 +1,6 @@
|
||||
[package]
|
||||
name = "tuliprox"
|
||||
version = "3.1.4"
|
||||
edition = "2021"
|
||||
[workspace]
|
||||
members = ["backend", "shared"]
|
||||
resolver = "2"
|
||||
|
||||
[profile.release]
|
||||
debug = false
|
||||
@@ -11,68 +10,3 @@ codegen-units = 1 # Reduce number of codegen units to increase optimizations.
|
||||
panic = 'abort' # Abort on panic
|
||||
strip = true
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[dependencies]
|
||||
serde = { version = "1.0", features = ["derive", "rc"] }
|
||||
serde_yaml = "0.9"
|
||||
serde_json = { version = "1", features = ["arbitrary_precision"] }
|
||||
quick-xml = "0.37"
|
||||
regex = "1.11"
|
||||
clap = { version = "4", features = ["derive"] }
|
||||
url = "2.5"
|
||||
reqwest = { version = "0", features = ["blocking", "json", "stream", "rustls-tls"] }
|
||||
chrono = "0.4"
|
||||
cron = "0.15"
|
||||
axum = { version = "0" , features = ["macros", "default"]}
|
||||
tower = "0"
|
||||
tower-http = { version = "0", features = ["cors", "auth", "fs", "compression-full"]}
|
||||
tower_governor = { version = "0.7", features = ["axum"] }
|
||||
jsonwebtoken = "9.3"
|
||||
rust-argon2 = "2.1"
|
||||
futures = "0.3"
|
||||
path-clean = "1"
|
||||
pest = "2.8"
|
||||
pest_derive = "2.8"
|
||||
enum-iterator = "2"
|
||||
openssl = { version = "*", features = ["vendored"] } #https://docs.rs/openssl/0.10.34/openssl/#vendored
|
||||
deunicode = "1.6.2"
|
||||
mime = "0.3"
|
||||
log = "0.4"
|
||||
env_logger = "0.11"
|
||||
rustelebot = "0.3"
|
||||
bincode = { version = "2.0.1", features = ["std", "serde"] }
|
||||
rand = "0.9"
|
||||
rpassword = "7.4"
|
||||
flate2 = "1"
|
||||
blake3 = "1.8"
|
||||
bytes = "1.10"
|
||||
tokio-stream = { version = "0.1", features = ["sync"] }
|
||||
tokio = { version = "1.45", features = ["rt-multi-thread", "parking_lot", "fs"] }
|
||||
tokio-util = "0.7"
|
||||
tempfile = "3.20"
|
||||
ruzstd = "0"
|
||||
filetime = "0.2"
|
||||
zeroize = "1.8"
|
||||
strsim = "0.11"
|
||||
rphonetic = "3"
|
||||
rayon = "1.10"
|
||||
base64 = "0"
|
||||
paste = "1"
|
||||
bitflags = "2"
|
||||
indexmap = "2"
|
||||
notify = "8"
|
||||
arc-swap = "1"
|
||||
dashmap = "6"
|
||||
hyper = "1"
|
||||
hyper-util = "0"
|
||||
socket2 = "0"
|
||||
#[cfg(target_os = "macos")]
|
||||
libc = "0"
|
||||
#[cfg(target_os = "windows")]
|
||||
winapi = { version = "0", features = ["psapi", "minwindef", "processthreadsapi"] }
|
||||
# [target.'cfg(not(target_env = "msvc"))'.dependencies]
|
||||
# tikv-jemallocator = { version = "*", features = ["profiling", "unprefixed_malloc_on_supported_platforms"] }
|
||||
|
||||
[build-dependencies]
|
||||
vergen = { version = "9", features = ["build"] }
|
||||
|
||||
@@ -1516,7 +1516,7 @@ Ease way to compile is a docker toolchain `cross`
|
||||
|
||||
```shell
|
||||
rust install cross
|
||||
env RUSTFLAGS="--remap-path-prefix $HOME=~" cross build --release --target x86_64-unknown-linux-musl
|
||||
env RUSTFLAGS="--remap-path-prefix $HOME=~" cross build -p tuliprox --release --target x86_64-unknown-linux-musl
|
||||
```
|
||||
|
||||
#### Manual compile - install prerequisites
|
||||
@@ -1527,7 +1527,7 @@ rustup target add x86_64-unknown-linux-musl
|
||||
```
|
||||
#### Build statically linked binary
|
||||
```shell
|
||||
cargo build --target x86_64-unknown-linux-musl --release
|
||||
cargo build -p tuliprox --target x86_64-unknown-linux-musl --release
|
||||
```
|
||||
#### Dockerize
|
||||
There is a Dockerfile in `docker` directory.
|
||||
@@ -1645,7 +1645,7 @@ rustup toolchain install stable-x86_64-pc-windows-gnu
|
||||
|
||||
Compile it with:
|
||||
```shell
|
||||
cargo build --release --target x86_64-pc-windows-gnu
|
||||
cargo build -p tuliprox --release --target x86_64-pc-windows-gnu
|
||||
```
|
||||
|
||||
### Cross compile for raspberry pi 2/3/4
|
||||
@@ -1654,7 +1654,7 @@ Ease way to compile is a docker toolchain `cross`
|
||||
|
||||
```shell
|
||||
rust install cross
|
||||
env RUSTFLAGS="--remap-path-prefix $HOME=~" cross build --release --target armv7-unknown-linux-musleabihf
|
||||
env RUSTFLAGS="--remap-path-prefix $HOME=~" cross build -p tuliprox --release --target armv7-unknown-linux-musleabihf
|
||||
```
|
||||
|
||||
# Different Scenarios
|
||||
|
||||
Generated
+3752
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,71 @@
|
||||
[package]
|
||||
name = "tuliprox"
|
||||
version = "3.1.4"
|
||||
edition = "2021"
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[dependencies]
|
||||
shared = {version = "0", path = "../shared" }
|
||||
serde = { version = "1.0", features = ["derive", "rc"] }
|
||||
serde_yaml = "0.9"
|
||||
serde_json = { version = "1", features = ["arbitrary_precision"] }
|
||||
quick-xml = "0.37"
|
||||
regex = "1.11"
|
||||
clap = { version = "4", features = ["derive"] }
|
||||
url = "2.5"
|
||||
reqwest = { version = "0", features = ["blocking", "json", "stream", "rustls-tls"] }
|
||||
chrono = "0.4"
|
||||
cron = "0.15"
|
||||
axum = { version = "0" , features = ["macros", "default"]}
|
||||
tower = "0"
|
||||
tower-http = { version = "0", features = ["cors", "auth", "fs", "compression-full"]}
|
||||
tower_governor = { version = "0.7", features = ["axum"] }
|
||||
jsonwebtoken = "9.3"
|
||||
rust-argon2 = "2.1"
|
||||
futures = "0.3"
|
||||
path-clean = "1"
|
||||
pest = "2.8"
|
||||
pest_derive = "2.8"
|
||||
enum-iterator = "2"
|
||||
openssl = { version = "*", features = ["vendored"] } #https://docs.rs/openssl/0.10.34/openssl/#vendored
|
||||
deunicode = "1.6.2"
|
||||
mime = "0.3"
|
||||
log = "0.4"
|
||||
env_logger = "0.11"
|
||||
rustelebot = "0.3"
|
||||
bincode = { version = "2.0.1", features = ["std", "serde"] }
|
||||
rand = "0.9"
|
||||
rpassword = "7.4"
|
||||
flate2 = "1"
|
||||
blake3 = "1.8"
|
||||
bytes = "1.10"
|
||||
tokio-stream = { version = "0.1", features = ["sync"] }
|
||||
tokio = { version = "1.45", features = ["rt-multi-thread", "parking_lot", "fs"] }
|
||||
tokio-util = "0.7"
|
||||
tempfile = "3.20"
|
||||
ruzstd = "0"
|
||||
filetime = "0.2"
|
||||
zeroize = "1.8"
|
||||
strsim = "0.11"
|
||||
rphonetic = "3"
|
||||
rayon = "1.10"
|
||||
base64 = "0"
|
||||
paste = "1"
|
||||
bitflags = "2"
|
||||
indexmap = "2"
|
||||
notify = "8"
|
||||
arc-swap = "1"
|
||||
dashmap = "6"
|
||||
hyper = "1"
|
||||
hyper-util = "0"
|
||||
socket2 = "0"
|
||||
#[cfg(target_os = "macos")]
|
||||
libc = "0"
|
||||
#[cfg(target_os = "windows")]
|
||||
winapi = { version = "0", features = ["psapi", "minwindef", "processthreadsapi"] }
|
||||
# [target.'cfg(not(target_env = "msvc"))'.dependencies]
|
||||
# tikv-jemallocator = { version = "*", features = ["profiling", "unprefixed_malloc_on_supported_platforms"] }
|
||||
|
||||
[build-dependencies]
|
||||
vergen = { version = "9", features = ["build"] }
|
||||
@@ -12,17 +12,16 @@ use crate::api::model::streams::provider_stream_factory::{create_provider_stream
|
||||
use crate::api::model::streams::shared_stream_manager::SharedStreamManager;
|
||||
use crate::api::model::streams::throttled_stream::ThrottledStream;
|
||||
use crate::auth::Claims;
|
||||
use crate::model::{ProxyUserCredentials, UserConnectionPermission};
|
||||
use crate::model::{ConfigInput, ConfigTarget, InputFetchMethod, TargetType};
|
||||
use crate::model::{PlaylistEntry, PlaylistItemType, XtreamCluster};
|
||||
use crate::model::{ConfigTarget, ProxyUserCredentials};
|
||||
use crate::model::{ConfigInput, InputFetchMethod};
|
||||
use shared::model::{PlaylistEntry, PlaylistItemType, TargetType, UserConnectionPermission, XtreamCluster};
|
||||
use crate::tools::atomic_once_flag::AtomicOnceFlag;
|
||||
use crate::tools::lru_cache::LRUResourceCache;
|
||||
use crate::utils::{DASH_EXT, HLS_EXT};
|
||||
use crate::utils::default_grace_period_millis;
|
||||
use shared::utils::{DASH_EXT, HLS_EXT};
|
||||
use shared::utils::{default_grace_period_millis, human_readable_byte_size};
|
||||
use crate::utils::create_new_file_for_write;
|
||||
use crate::utils::request;
|
||||
use crate::utils::request::{extract_extension_from_url, replace_url_extension, sanitize_sensitive_info};
|
||||
use crate::utils::human_readable_byte_size;
|
||||
use crate::utils::{debug_if_enabled, trace_if_enabled};
|
||||
use crate::{BUILD_TIMESTAMP};
|
||||
use axum::body::Body;
|
||||
@@ -1,5 +1,5 @@
|
||||
use crate::api::model::app_state::AppState;
|
||||
use crate::tuliprox_error::{TuliproxError, TuliproxErrorKind};
|
||||
use shared::error::{TuliproxError, TuliproxErrorKind};
|
||||
use crate::utils;
|
||||
use crate::utils::is_directory;
|
||||
use log::{debug, error, info};
|
||||
+3
-2
@@ -1,5 +1,6 @@
|
||||
use crate::model::{Config, ConfigInput, ConfigTarget, InputType, TargetType};
|
||||
use crate::model::{M3uPlaylistItem, PlaylistGroup, PlaylistItemType, XtreamCluster};
|
||||
use crate::model::{Config, ConfigInput, ConfigTarget, InputType};
|
||||
use crate::model::{M3uPlaylistItem, PlaylistGroup};
|
||||
use shared::model::{PlaylistItemType, TargetType, XtreamCluster};
|
||||
use crate::repository::{m3u_repository, xtream_repository};
|
||||
use crate::utils::{m3u, xtream};
|
||||
use axum::response::IntoResponse;
|
||||
@@ -12,7 +12,8 @@ use std::ops::Deref;
|
||||
use std::sync::Arc;
|
||||
use std::{fs};
|
||||
use axum::response::IntoResponse;
|
||||
use crate::tuliprox_error::to_io_error;
|
||||
use shared::utils::bytes_to_megabytes;
|
||||
use shared::error::to_io_error;
|
||||
use crate::utils::request::create_client;
|
||||
|
||||
async fn download_file(active: Arc<RwLock<Option<FileDownload>>>, client: &reqwest::Client) -> Result<(), String> {
|
||||
@@ -39,7 +40,7 @@ async fn download_file(active: Arc<RwLock<Option<FileDownload>>>, client: &reqwe
|
||||
Err(err) => return Err(format!("Error while writing to file: {file_path_str} {err}"))
|
||||
}
|
||||
} else {
|
||||
let megabytes = request::bytes_to_megabytes(downloaded);
|
||||
let megabytes = bytes_to_megabytes(downloaded);
|
||||
info!("Downloaded {file_path_str}, filesize: {megabytes}MB");
|
||||
active.write().await.as_mut().unwrap().size = downloaded;
|
||||
return Ok(());
|
||||
@@ -1,8 +1,9 @@
|
||||
use crate::api::model::app_state::HdHomerunAppState;
|
||||
use crate::auth::AuthBasic;
|
||||
use crate::model::ProxyUserCredentials;
|
||||
use crate::model::{Config, ConfigTarget, TargetType};
|
||||
use crate::model::{M3uPlaylistItem, PlaylistItemType, XtreamCluster, XtreamPlaylistItem};
|
||||
use crate::model::{ConfigTarget, ProxyUserCredentials};
|
||||
use crate::model::{Config};
|
||||
use crate::model::{M3uPlaylistItem, XtreamPlaylistItem};
|
||||
use shared::model::{PlaylistItemType, TargetType, XtreamCluster};
|
||||
use crate::processing::parser::xtream::get_xtream_url;
|
||||
use crate::repository::m3u_playlist_iterator::M3uPlaylistIterator;
|
||||
use crate::repository::xtream_playlist_iterator::XtreamPlaylistIterator;
|
||||
@@ -2,11 +2,11 @@ use crate::api::api_utils::{force_provider_stream_response, get_stream_alternati
|
||||
use crate::api::api_utils::{try_option_bad_request};
|
||||
use crate::api::model::app_state::AppState;
|
||||
use crate::api::model::streams::provider_stream::{create_custom_video_stream_response, CustomVideoStreamType};
|
||||
use crate::model::{ProxyUserCredentials, UserConnectionPermission};
|
||||
use crate::model::{ProxyUserCredentials};
|
||||
use crate::model::ConfigInput;
|
||||
use crate::model::{PlaylistItemType, XtreamCluster};
|
||||
use shared::model::{PlaylistItemType, UserConnectionPermission, XtreamCluster};
|
||||
use crate::processing::parser::hls::{get_hls_session_token_and_url_from_token, rewrite_hls, RewriteHlsProps};
|
||||
use crate::utils::HLS_EXT;
|
||||
use shared::utils::HLS_EXT;
|
||||
use crate::utils::request;
|
||||
use crate::utils::request::{is_hls_url, replace_url_extension, sanitize_sensitive_info};
|
||||
use axum::response::IntoResponse;
|
||||
@@ -4,13 +4,11 @@ use crate::api::endpoints::xtream_api::{ApiStreamContext, ApiStreamRequest};
|
||||
use crate::api::model::app_state::AppState;
|
||||
use crate::api::model::request::UserApiRequest;
|
||||
use crate::api::model::streams::provider_stream::{create_custom_video_stream_response, CustomVideoStreamType};
|
||||
use crate::model::TargetType;
|
||||
use crate::model::UserConnectionPermission;
|
||||
use crate::model::{FieldGetAccessor, PlaylistEntry, PlaylistItemType, XtreamCluster};
|
||||
use shared::model::{FieldGetAccessor, PlaylistEntry, PlaylistItemType, TargetType, UserConnectionPermission, XtreamCluster};
|
||||
use crate::repository::m3u_repository::{m3u_get_item_for_stream_id, m3u_load_rewrite_playlist};
|
||||
use crate::repository::storage_const;
|
||||
use crate::utils::request::{extract_extension_from_url, sanitize_sensitive_info};
|
||||
use crate::utils::HLS_EXT;
|
||||
use shared::utils::HLS_EXT;
|
||||
use axum::http::{HeaderMap, StatusCode};
|
||||
use axum::response::IntoResponse;
|
||||
use bytes::Bytes;
|
||||
@@ -1,8 +1,8 @@
|
||||
use crate::api::api_utils::{get_user_target_by_username, get_username_from_auth_header};
|
||||
use crate::api::model::app_state::AppState;
|
||||
use crate::auth::validator_user;
|
||||
use crate::model::{Config, ConfigTarget, TargetType};
|
||||
use crate::model::XtreamCluster;
|
||||
use crate::model::{Config, ConfigTarget};
|
||||
use shared::model::{TargetType, XtreamCluster};
|
||||
use crate::model::PlaylistBouquetDto;
|
||||
use crate::model::PlaylistXtreamCategory;
|
||||
use crate::repository::user_repository::{load_user_bouquet_as_json, save_user_bouquet};
|
||||
@@ -6,10 +6,10 @@ use crate::api::model::config::{ServerConfig, ServerInputConfig, ServerSourceCon
|
||||
use crate::api::model::request::{PlaylistRequest, PlaylistRequestType};
|
||||
use crate::auth::create_access_token;
|
||||
use crate::auth::validator_admin;
|
||||
use crate::tuliprox_error::TuliproxError;
|
||||
use crate::model::StatusCheck;
|
||||
use shared::error::TuliproxError;
|
||||
use crate::model::{ConfigTarget, StatusCheck};
|
||||
use crate::model::XtreamPlaylistItem;
|
||||
use crate::model::{Config, ConfigDto, ConfigInput, ConfigInputOptions, ConfigSource, ConfigTarget, InputType};
|
||||
use crate::model::{Config, ConfigInput, ConfigInputOptions, ConfigSource, InputType};
|
||||
use crate::model::{ApiProxyConfig, ApiProxyServerInfo, ProxyUserCredentials, TargetUser};
|
||||
use crate::processing::processor::playlist;
|
||||
use crate::repository::user_repository::store_api_user;
|
||||
@@ -21,6 +21,7 @@ use log::error;
|
||||
use serde_json::json;
|
||||
use std::collections::{BTreeMap, HashSet};
|
||||
use std::sync::Arc;
|
||||
use shared::model::ConfigDto;
|
||||
|
||||
fn intern_save_config_api_proxy(backup_dir: &str, api_proxy: &ApiProxyConfig, file_path: &str) -> Option<TuliproxError> {
|
||||
match utils::save_api_proxy(file_path, backup_dir, api_proxy) {
|
||||
@@ -8,7 +8,7 @@ use std::collections::HashMap;
|
||||
use std::path::{Path, PathBuf};
|
||||
use std::sync::{Arc};
|
||||
use tower::Service;
|
||||
use crate::utils::CONSTANTS;
|
||||
use shared::utils::CONSTANTS;
|
||||
|
||||
fn no_web_auth_token() -> impl axum::response::IntoResponse + Send {
|
||||
axum::Json(HashMap::from([("token", "authorized")])).into_response()
|
||||
@@ -12,8 +12,8 @@ use std::sync::Arc;
|
||||
use crate::api::api_utils::{get_user_target, serve_file};
|
||||
use crate::api::model::app_state::AppState;
|
||||
use crate::api::model::request::UserApiRequest;
|
||||
use crate::model::ProxyUserCredentials;
|
||||
use crate::model::{Config, ConfigTarget, TargetOutput};
|
||||
use crate::model::{ConfigTarget, ProxyUserCredentials, TargetOutput};
|
||||
use crate::model::{Config};
|
||||
use crate::repository::m3u_repository::m3u_get_epg_file_path;
|
||||
use crate::repository::storage::get_target_storage_path;
|
||||
use crate::repository::xtream_repository::{xtream_get_epg_file_path, xtream_get_storage_path};
|
||||
@@ -9,22 +9,21 @@ use crate::api::model::app_state::AppState;
|
||||
use crate::api::model::request::UserApiRequest;
|
||||
use crate::api::model::streams::provider_stream::{create_custom_video_stream_response, CustomVideoStreamType};
|
||||
use crate::api::model::xtream::XtreamAuthorizationResponse;
|
||||
use crate::model::TargetType;
|
||||
use crate::model::{get_backdrop_path_value, FieldGetAccessor, PlaylistEntry, PlaylistItemType, XtreamCluster, XtreamPlaylistItem};
|
||||
use crate::model::{Config, ConfigInput, ConfigTarget};
|
||||
use crate::model::{ProxyType, ProxyUserCredentials, UserConnectionPermission};
|
||||
use crate::model::{get_backdrop_path_value, ConfigTarget, XtreamPlaylistItem};
|
||||
use crate::model::{Config, ConfigInput};
|
||||
use crate::model::{ProxyUserCredentials};
|
||||
use crate::repository::playlist_repository::get_target_id_mapping;
|
||||
use crate::repository::storage::{get_target_storage_path, hex_encode};
|
||||
use crate::repository::storage::{get_target_storage_path};
|
||||
use crate::repository::{storage_const, user_repository, xtream_repository};
|
||||
use crate::tuliprox_error::create_tuliprox_error_result;
|
||||
use crate::tuliprox_error::info_err;
|
||||
use crate::tuliprox_error::{str_to_io_error, TuliproxError, TuliproxErrorKind};
|
||||
use crate::utils::generate_playlist_uuid;
|
||||
use shared::error::create_tuliprox_error_result;
|
||||
use shared::error::info_err;
|
||||
use shared::error::{str_to_io_error, TuliproxError, TuliproxErrorKind};
|
||||
use crate::utils::{generate_playlist_uuid, hex_encode};
|
||||
use crate::utils::get_u32_from_serde_value;
|
||||
use crate::utils::request::{extract_extension_from_url, sanitize_sensitive_info};
|
||||
use crate::utils::trace_if_enabled;
|
||||
use crate::utils::xtream::create_vod_info_from_item;
|
||||
use crate::utils::HLS_EXT;
|
||||
use shared::utils::HLS_EXT;
|
||||
use crate::utils::{request, xtream};
|
||||
use crate::auth::Fingerprint;
|
||||
use axum::http::{HeaderMap, StatusCode};
|
||||
@@ -41,6 +40,7 @@ use std::fmt::{Display, Formatter};
|
||||
use std::path::Path;
|
||||
use std::str::FromStr;
|
||||
use std::sync::Arc;
|
||||
use shared::model::{PlaylistItemType, XtreamCluster, FieldGetAccessor, PlaylistEntry, TargetType, UserConnectionPermission, ProxyType};
|
||||
|
||||
#[derive(Serialize, Deserialize, Debug, Copy, Clone, Eq, PartialEq)]
|
||||
pub enum ApiStreamContext {
|
||||
+1
-1
@@ -6,7 +6,7 @@ use std::sync::atomic::{AtomicUsize, Ordering};
|
||||
use std::sync::Arc;
|
||||
use tokio::sync::{Mutex, RwLock};
|
||||
use crate::api::model::provider_config::{ProviderConfig, ProviderConfigWrapper};
|
||||
use crate::utils::{default_grace_period_millis, default_grace_period_timeout_secs};
|
||||
use shared::utils::{default_grace_period_millis, default_grace_period_timeout_secs};
|
||||
|
||||
pub struct ProviderConnectionGuard {
|
||||
// manager: Arc<ActiveProviderManager>,
|
||||
@@ -1,13 +1,14 @@
|
||||
use crate::model::Config;
|
||||
use crate::model::{ProxyUserCredentials, UserConnectionPermission};
|
||||
use crate::model::{ProxyUserCredentials};
|
||||
use crate::utils::request::sanitize_sensitive_info;
|
||||
use crate::utils::{current_time_secs, default_grace_period_millis, default_grace_period_timeout_secs};
|
||||
use shared::utils::{current_time_secs, default_grace_period_millis, default_grace_period_timeout_secs};
|
||||
use jsonwebtoken::get_current_timestamp;
|
||||
use log::{debug, info};
|
||||
use std::collections::HashMap;
|
||||
use std::sync::atomic::{AtomicU64, Ordering};
|
||||
use std::sync::Arc;
|
||||
use tokio::sync::RwLock;
|
||||
use shared::model::UserConnectionPermission;
|
||||
|
||||
const USER_CON_TTL: u64 = 10_800; // 3 hours
|
||||
const USER_SESSION_LIMIT: usize = 50;
|
||||
@@ -1,10 +1,11 @@
|
||||
use tokio::sync::{Mutex};
|
||||
use std::sync::Arc;
|
||||
use shared::model::UserConnectionPermission;
|
||||
use crate::api::model::active_provider_manager::ActiveProviderManager;
|
||||
use crate::api::model::active_user_manager::ActiveUserManager;
|
||||
use crate::api::model::download::DownloadQueue;
|
||||
use crate::api::model::streams::shared_stream_manager::SharedStreamManager;
|
||||
use crate::model::{Config, UserConnectionPermission, HdHomeRunDeviceConfig};
|
||||
use crate::model::{Config, HdHomeRunDeviceConfig};
|
||||
use crate::tools::lru_cache::LRUResourceCache;
|
||||
|
||||
#[derive(Clone)]
|
||||
@@ -1,6 +1,6 @@
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
use crate::model::{ApiProxyConfig, ConfigApi, InputType, LogConfig, MessagingConfig, ProcessingOrder, ReverseProxyConfig, ScheduleConfig, VideoConfig, ConfigRename, ConfigSort, ConfigTargetOptions, TargetOutput, WebUiConfig, ProxyConfig, IpCheckConfig};
|
||||
use shared::model::ProcessingOrder;
|
||||
use crate::model::{ApiProxyConfig, ConfigApi, InputType, LogConfig, MessagingConfig, ReverseProxyConfig, ScheduleConfig, VideoConfig, ConfigSort, WebUiConfig, ProxyConfig, IpCheckConfig, ConfigTargetOptions, TargetOutput, ConfigRename};
|
||||
|
||||
#[derive(Deserialize, Serialize, Debug, Clone)]
|
||||
pub struct ServerInputConfig {
|
||||
@@ -7,8 +7,8 @@ use deunicode::deunicode;
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
use crate::model::{VideoDownloadConfig};
|
||||
use crate::repository::storage::hash_string_as_hex;
|
||||
use crate::utils::{CONSTANTS, FILENAME_TRIM_PATTERNS};
|
||||
use crate::utils::hash_string_as_hex;
|
||||
use shared::utils::{CONSTANTS, FILENAME_TRIM_PATTERNS};
|
||||
|
||||
/// File-Download information.
|
||||
#[derive(Clone)]
|
||||
@@ -2,7 +2,7 @@ use reqwest::{StatusCode};
|
||||
use std::collections::{HashSet};
|
||||
use std::str::FromStr;
|
||||
use reqwest::header::HeaderMap;
|
||||
use crate::utils::{filter_response_header};
|
||||
use shared::utils::{filter_response_header};
|
||||
|
||||
pub fn get_response_headers(headers: &HeaderMap) -> Vec<(String, String)> {
|
||||
let mut response_headers: Vec<(String, String)> = headers.iter()
|
||||
+2
-1
@@ -6,7 +6,7 @@ use crate::api::model::app_state::AppState;
|
||||
use crate::api::model::stream::BoxedProviderStream;
|
||||
use crate::api::model::stream_error::StreamError;
|
||||
use crate::api::model::streams::transport_stream_buffer::TransportStreamBuffer;
|
||||
use crate::model::{ProxyUserCredentials, UserConnectionPermission};
|
||||
use crate::model::{ProxyUserCredentials};
|
||||
use bytes::Bytes;
|
||||
use futures::Stream;
|
||||
use log::{error, info};
|
||||
@@ -16,6 +16,7 @@ use std::sync::{Arc, Mutex};
|
||||
use std::task::{Poll, Waker};
|
||||
use crate::api::model::streams::timed_client_stream::TimedClientStream;
|
||||
use futures::{StreamExt};
|
||||
use shared::model::UserConnectionPermission;
|
||||
|
||||
const INNER_STREAM: u8 = 0_u8;
|
||||
const GRACE_BLOCK_STREAM: u8 = 1_u8;
|
||||
+1
-1
@@ -1,7 +1,7 @@
|
||||
use crate::api::api_utils::{HeaderFilter};
|
||||
use crate::api::model::streams::custom_video_stream::CustomVideoStream;
|
||||
use crate::model::{Config};
|
||||
use crate::model::PlaylistItemType;
|
||||
use shared::model::PlaylistItemType;
|
||||
use log::{trace};
|
||||
use reqwest::StatusCode;
|
||||
use axum::response::IntoResponse;
|
||||
+3
-2
@@ -6,11 +6,12 @@ use crate::api::model::streams::buffered_stream::BufferedStream;
|
||||
use crate::api::model::streams::client_stream::ClientStream;
|
||||
use crate::api::model::streams::provider_stream::{create_channel_unavailable_stream, get_header_filter_for_item_type};
|
||||
use crate::api::model::streams::timed_client_stream::TimedClientStream;
|
||||
use crate::model::PlaylistItemType;
|
||||
use shared::model::PlaylistItemType;
|
||||
use crate::model::{Config, DEFAULT_USER_AGENT};
|
||||
use crate::tools::atomic_once_flag::AtomicOnceFlag;
|
||||
use crate::utils::request::{classify_content_type, get_request_headers, sanitize_sensitive_info, MimeCategory};
|
||||
use crate::utils::{debug_if_enabled, filter_request_header};
|
||||
use crate::utils::{debug_if_enabled};
|
||||
use shared::utils::{filter_request_header};
|
||||
use futures::stream::{self};
|
||||
use futures::{StreamExt, TryStreamExt};
|
||||
use log::{debug, log_enabled, warn};
|
||||
@@ -1,7 +1,8 @@
|
||||
use crate::model::{ApiProxyServerInfo, ProxyUserCredentials, ProxyUserStatus};
|
||||
use crate::model::{ApiProxyServerInfo, ProxyUserCredentials};
|
||||
use chrono::{Duration, Local};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use crate::utils::CONSTANTS;
|
||||
use shared::model::ProxyUserStatus;
|
||||
use shared::utils::CONSTANTS;
|
||||
|
||||
#[derive(Serialize, Deserialize, Clone)]
|
||||
pub struct XtreamUserInfoResponse {
|
||||
@@ -1,4 +1,4 @@
|
||||
use crate::repository::storage::{hex_decode, hex_encode};
|
||||
use crate::utils::{hex_decode, hex_encode};
|
||||
use chrono::Utc;
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
@@ -5,7 +5,7 @@ use crate::api::api_utils::get_username_from_auth_header;
|
||||
use crate::model::WebAuthConfig;
|
||||
use crate::api::model::app_state::AppState;
|
||||
use crate::auth::AuthBearer;
|
||||
use crate::tuliprox_error::to_io_error;
|
||||
use shared::error::to_io_error;
|
||||
|
||||
const ROLE_ADMIN: &str = "ADMIN";
|
||||
const ROLE_USER: &str = "USER";
|
||||
@@ -1,6 +1,6 @@
|
||||
use rand::{Rng};
|
||||
use rand::distr::Alphanumeric;
|
||||
use crate::tuliprox_error::str_to_io_error;
|
||||
use shared::error::str_to_io_error;
|
||||
|
||||
fn generate_salt(length: usize) -> String {
|
||||
let salt: String = rand::rng()
|
||||
@@ -8,12 +8,12 @@ use pest::iterators::Pair;
|
||||
use pest::Parser;
|
||||
use std::cmp::Ordering;
|
||||
use std::collections::HashMap;
|
||||
use crate::model::{FieldGetAccessor, FieldSetAccessor, ItemField};
|
||||
use crate::model::{PlaylistItem, PlaylistItemType};
|
||||
use shared::model::{FieldGetAccessor, FieldSetAccessor, ItemField, PlaylistItemType};
|
||||
use crate::model::{PlaylistItem};
|
||||
use crate::tools::directed_graph::DirectedGraph;
|
||||
use crate::tuliprox_error::{create_tuliprox_error_result, info_err};
|
||||
use crate::tuliprox_error::{TuliproxError, TuliproxErrorKind};
|
||||
use crate::utils::CONSTANTS;
|
||||
use shared::error::{create_tuliprox_error_result, info_err};
|
||||
use shared::error::{TuliproxError, TuliproxErrorKind};
|
||||
use shared::utils::CONSTANTS;
|
||||
|
||||
pub fn get_field_value(pli: &PlaylistItem, field: ItemField) -> String {
|
||||
let header = &pli.header;
|
||||
@@ -2,8 +2,8 @@
|
||||
|
||||
use crate::foundation::filter::{PatternTemplate, TemplateValue, ValueAccessor};
|
||||
use crate::foundation::mapper::EvalResult::{AnyValue, Failure, Named, Number, Undefined, Value};
|
||||
use crate::tuliprox_error::{create_tuliprox_error_result, info_err, TuliproxError, TuliproxErrorKind};
|
||||
use crate::utils::Capitalize;
|
||||
use shared::error::{create_tuliprox_error_result, info_err, TuliproxError, TuliproxErrorKind};
|
||||
use shared::utils::Capitalize;
|
||||
use log::{debug, trace};
|
||||
use pest::iterators::{Pair, Pairs};
|
||||
use pest::Parser;
|
||||
@@ -10,7 +10,7 @@ mod modules;
|
||||
include_modules!();
|
||||
|
||||
use crate::auth::generate_password;
|
||||
use crate::model::{Config, HealthcheckConfig, Healthcheck, ProcessTargets};
|
||||
use crate::model::{Config, Healthcheck, HealthcheckConfig, ProcessTargets};
|
||||
use crate::processing::processor::playlist;
|
||||
use crate::utils::{config_file_reader, resolve_env_var};
|
||||
use crate::utils::request::{create_client, set_sanitize_sensitive_info};
|
||||
@@ -20,7 +20,7 @@ use log::{error, info};
|
||||
use std::fs::File;
|
||||
use std::path::{Path, PathBuf};
|
||||
use std::sync::Arc;
|
||||
use crate::utils::logging::init_logger;
|
||||
use crate::utils::init_logger;
|
||||
|
||||
#[derive(Parser)]
|
||||
#[command(name = "tuliprox")]
|
||||
@@ -2,18 +2,7 @@ use std::sync::Arc;
|
||||
use crate::model::{MessagingConfig};
|
||||
use log::{debug, error};
|
||||
use reqwest::{header};
|
||||
|
||||
#[derive(Debug, Copy, Clone, serde::Serialize, serde::Deserialize, PartialEq, Eq)]
|
||||
pub enum MsgKind {
|
||||
#[serde(rename = "info")]
|
||||
Info,
|
||||
#[serde(rename = "stats")]
|
||||
Stats,
|
||||
#[serde(rename = "error")]
|
||||
Error,
|
||||
#[serde(rename = "watch")]
|
||||
Watch,
|
||||
}
|
||||
use shared::model::MsgKind;
|
||||
|
||||
fn is_enabled(kind: MsgKind, cfg: &MessagingConfig) -> bool {
|
||||
cfg.notify_on.contains(&kind)
|
||||
@@ -1,190 +1,17 @@
|
||||
use crate::api::model::app_state::AppState;
|
||||
use crate::tuliprox_error::{create_tuliprox_error_result, info_err, TuliproxError, TuliproxErrorKind};
|
||||
use crate::model::{Config, ClusterFlags};
|
||||
use shared::error::{info_err, TuliproxError, TuliproxErrorKind};
|
||||
use crate::model::{Config};
|
||||
use crate::repository::user_repository::{backup_api_user_db_file, get_api_user_db_path, load_api_user, merge_api_user};
|
||||
use crate::utils::{default_as_true, save_api_proxy};
|
||||
use crate::utils::{save_api_proxy};
|
||||
use shared::utils::{default_as_true};
|
||||
use chrono::Local;
|
||||
use enum_iterator::Sequence;
|
||||
use log::debug;
|
||||
use std::cmp::PartialEq;
|
||||
use std::collections::HashSet;
|
||||
use std::fmt::Display;
|
||||
use std::fs;
|
||||
use std::str::FromStr;
|
||||
use serde::{Deserialize, Deserializer, Serialize, Serializer};
|
||||
use crate::model::PlaylistItemType;
|
||||
use shared::model::{ProxyType, ProxyUserStatus, UserConnectionPermission};
|
||||
use crate::utils;
|
||||
|
||||
#[derive(Debug, Copy, Clone, serde::Serialize, serde::Deserialize, PartialEq, Eq)]
|
||||
pub enum UserConnectionPermission {
|
||||
Exhausted,
|
||||
Allowed,
|
||||
GracePeriod,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
pub enum ProxyType {
|
||||
Reverse(Option<ClusterFlags>),
|
||||
Redirect,
|
||||
}
|
||||
|
||||
impl Default for ProxyType {
|
||||
fn default() -> Self {
|
||||
Self::Redirect
|
||||
}
|
||||
}
|
||||
|
||||
impl ProxyType {
|
||||
const REVERSE: &'static str = "reverse";
|
||||
const REDIRECT: &'static str = "redirect";
|
||||
|
||||
pub fn is_redirect(&self, item_type: PlaylistItemType) -> bool {
|
||||
match self {
|
||||
ProxyType::Reverse(Some(flags)) => {
|
||||
if flags.is_empty() {
|
||||
return false;
|
||||
}
|
||||
if flags.has_cluster(item_type) {
|
||||
return false;
|
||||
}
|
||||
true
|
||||
},
|
||||
ProxyType::Reverse(None) => false,
|
||||
ProxyType::Redirect => true
|
||||
}
|
||||
}
|
||||
|
||||
pub fn is_reverse(&self, item_type: PlaylistItemType) -> bool {
|
||||
!self.is_redirect(item_type)
|
||||
}
|
||||
}
|
||||
|
||||
impl Display for ProxyType {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
match self {
|
||||
Self::Reverse(force_redirect) => {
|
||||
if let Some(force) = force_redirect {
|
||||
write!(f, "{}{force:?}", Self::REVERSE)
|
||||
} else {
|
||||
write!(f, "{}", Self::REVERSE)
|
||||
}
|
||||
}
|
||||
Self::Redirect => write!(f, "{}", Self::REDIRECT),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl FromStr for ProxyType {
|
||||
type Err = TuliproxError;
|
||||
|
||||
fn from_str(s: &str) -> Result<Self, TuliproxError> {
|
||||
if s == Self::REDIRECT {
|
||||
return Ok(Self::Redirect);
|
||||
}
|
||||
if s == Self::REVERSE {
|
||||
return Ok(Self::Reverse(None));
|
||||
}
|
||||
|
||||
if s.starts_with(Self::REVERSE) {
|
||||
let suffix = s.strip_prefix(Self::REVERSE).unwrap();
|
||||
if let Ok(force_redirect) = ClusterFlags::try_from(suffix) {
|
||||
if force_redirect.has_full_flags() {
|
||||
return Ok(ProxyType::Reverse(None));
|
||||
}
|
||||
return Ok(Self::Reverse(Some(force_redirect)));
|
||||
}
|
||||
}
|
||||
|
||||
create_tuliprox_error_result!(TuliproxErrorKind::Info, "Unknown ProxyType: {}", s)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'de> Deserialize<'de> for ProxyType {
|
||||
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
|
||||
where
|
||||
D: Deserializer<'de>,
|
||||
{
|
||||
let raw: String = Deserialize::deserialize(deserializer)?;
|
||||
if raw == ProxyType::REDIRECT {
|
||||
return Ok(ProxyType::Redirect);
|
||||
} else if raw.starts_with(ProxyType::REVERSE) {
|
||||
return ProxyType::from_str(raw.as_str()).map_err(serde::de::Error::custom);
|
||||
}
|
||||
Err(serde::de::Error::custom("Unknown proxy type"))
|
||||
}
|
||||
}
|
||||
|
||||
impl Serialize for ProxyType {
|
||||
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
|
||||
where
|
||||
S: Serializer,
|
||||
{
|
||||
match *self {
|
||||
ProxyType::Redirect => serializer.serialize_str(ProxyType::REDIRECT),
|
||||
ProxyType::Reverse(None) => serializer.serialize_str(ProxyType::REVERSE),
|
||||
ProxyType::Reverse(Some(ref force_redirect)) => {
|
||||
serializer.serialize_str(&format!("{}{}", ProxyType::REVERSE, force_redirect))
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#[derive(Debug, Copy, Clone, serde::Serialize, serde::Deserialize, Sequence, PartialEq, Eq)]
|
||||
pub enum ProxyUserStatus {
|
||||
Active, // The account is in good standing and can stream content
|
||||
Expired, // The account can no longer access content unless it is renewed.
|
||||
Banned, // The account is temporarily or permanently disabled. Typically used for users who violate terms of service or abuse the system.
|
||||
Trial, // The account is marked as a trial account.
|
||||
Disabled, // The account is inactive or deliberately disabled by the administrator.
|
||||
Pending,
|
||||
}
|
||||
|
||||
|
||||
impl Default for ProxyUserStatus {
|
||||
fn default() -> Self {
|
||||
Self::Active
|
||||
}
|
||||
}
|
||||
|
||||
impl ProxyUserStatus {
|
||||
const ACTIVE: &'static str = "Active";
|
||||
const EXPIRED: &'static str = "Expired";
|
||||
const BANNED: &'static str = "Banned";
|
||||
const TRIAL: &'static str = "Trial";
|
||||
const DISABLED: &'static str = "Disabled";
|
||||
const PENDING: &'static str = "Pending";
|
||||
}
|
||||
|
||||
impl Display for ProxyUserStatus {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
write!(f, "{}", match self {
|
||||
Self::Active => Self::ACTIVE,
|
||||
Self::Expired => Self::EXPIRED,
|
||||
Self::Banned => Self::BANNED,
|
||||
Self::Trial => Self::TRIAL,
|
||||
Self::Disabled => Self::DISABLED,
|
||||
Self::Pending => Self::PENDING,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl FromStr for ProxyUserStatus {
|
||||
type Err = TuliproxError;
|
||||
|
||||
fn from_str(s: &str) -> Result<Self, TuliproxError> {
|
||||
match s {
|
||||
Self::ACTIVE => Ok(Self::Active),
|
||||
Self::EXPIRED => Ok(Self::Expired),
|
||||
Self::BANNED => Ok(Self::Banned),
|
||||
Self::TRIAL => Ok(Self::Trial),
|
||||
Self::DISABLED => Ok(Self::Disabled),
|
||||
Self::PENDING => Ok(Self::Pending),
|
||||
_ => create_tuliprox_error_result!(TuliproxErrorKind::Info, "Unknown ProxyUserStatus: {}", s)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
|
||||
#[serde(deny_unknown_fields)]
|
||||
pub struct ProxyUserCredentials {
|
||||
@@ -1,25 +1,18 @@
|
||||
use arc_swap::{ArcSwapOption};
|
||||
use enum_iterator::Sequence;
|
||||
use std::collections::{HashSet};
|
||||
use std::fmt::Display;
|
||||
use std::fs::File;
|
||||
use std::io::Read;
|
||||
use std::path::{Path, PathBuf};
|
||||
use std::str::FromStr;
|
||||
use std::sync::Arc;
|
||||
|
||||
use log::{debug, error, warn};
|
||||
use log::{debug, error};
|
||||
use path_clean::PathClean;
|
||||
use rand::Rng;
|
||||
|
||||
use crate::model::{ApiProxyConfig, ApiProxyServerInfo, Mappings, ProxyUserCredentials, SourcesConfig};
|
||||
use crate::model::{ApiProxyConfig, ApiProxyServerInfo, CustomStreamResponse, Mappings, ProxyUserCredentials, ReverseProxyConfig, ScheduleConfig, SourcesConfig};
|
||||
use crate::model::{ConfigInput, ConfigInputOptions, ConfigTarget, HdHomeRunConfig, IpCheckConfig, LogConfig, MessagingConfig, ProxyConfig, TargetOutput, VideoConfig, WebUiConfig};
|
||||
use crate::tuliprox_error::{create_tuliprox_error_result, TuliproxError, TuliproxErrorKind};
|
||||
use crate::utils::{default_connect_timeout_secs, default_grace_period_millis, default_grace_period_timeout_secs};
|
||||
use crate::utils::{parse_to_kbps};
|
||||
|
||||
const STREAM_QUEUE_SIZE: usize = 1024; // mpsc channel holding messages. with 8192byte chunks and 2Mbit/s approx 8MB
|
||||
|
||||
use shared::error::{create_tuliprox_error_result, TuliproxError, TuliproxErrorKind};
|
||||
use shared::utils::{default_connect_timeout_secs};
|
||||
|
||||
const CHANNEL_UNAVAILABLE: &str = "channel_unavailable.ts";
|
||||
const USER_CONNECTIONS_EXHAUSTED: &str = "user_connections_exhausted.ts";
|
||||
@@ -41,87 +34,8 @@ macro_rules! valid_property {
|
||||
}
|
||||
pub use valid_property;
|
||||
use crate::api::model::streams::transport_stream_buffer::TransportStreamBuffer;
|
||||
use crate::model::config_cache::CacheConfig;
|
||||
use crate::utils;
|
||||
|
||||
#[derive(Debug, Copy, Clone, serde::Serialize, serde::Deserialize, Sequence, Eq, PartialEq)]
|
||||
pub enum ItemField {
|
||||
#[serde(rename = "group")]
|
||||
Group,
|
||||
#[serde(rename = "name")]
|
||||
Name,
|
||||
#[serde(rename = "title")]
|
||||
Title,
|
||||
#[serde(rename = "url")]
|
||||
Url,
|
||||
#[serde(rename = "input")]
|
||||
Input,
|
||||
#[serde(rename = "type")]
|
||||
Type,
|
||||
#[serde(rename = "caption")]
|
||||
Caption,
|
||||
}
|
||||
|
||||
impl ItemField {
|
||||
const GROUP: &'static str = "Group";
|
||||
const NAME: &'static str = "Name";
|
||||
const TITLE: &'static str = "Title";
|
||||
const URL: &'static str = "Url";
|
||||
const INPUT: &'static str = "Input";
|
||||
const TYPE: &'static str = "Type";
|
||||
const CAPTION: &'static str = "Caption";
|
||||
|
||||
pub fn as_str(&self) -> &'static str {
|
||||
match *self {
|
||||
Self::Group => Self::GROUP,
|
||||
Self::Name => Self::NAME,
|
||||
Self::Title => Self::TITLE,
|
||||
Self::Url => Self::URL,
|
||||
Self::Input => Self::INPUT,
|
||||
Self::Type => Self::TYPE,
|
||||
Self::Caption => Self::CAPTION,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Display for ItemField {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
|
||||
write!(f, "{}", match *self {
|
||||
Self::Group => Self::GROUP,
|
||||
Self::Name => Self::NAME,
|
||||
Self::Title => Self::TITLE,
|
||||
Self::Url => Self::URL,
|
||||
Self::Input => Self::INPUT,
|
||||
Self::Type => Self::TYPE,
|
||||
Self::Caption => Self::CAPTION,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl FromStr for ItemField {
|
||||
type Err = TuliproxError;
|
||||
|
||||
fn from_str(s: &str) -> Result<Self, TuliproxError> {
|
||||
if s.eq_ignore_ascii_case(Self::GROUP) {
|
||||
Ok(Self::Group)
|
||||
} else if s.eq_ignore_ascii_case(Self::NAME) {
|
||||
Ok(Self::Name)
|
||||
} else if s.eq_ignore_ascii_case(Self::TITLE) {
|
||||
Ok(Self::Title)
|
||||
} else if s.eq_ignore_ascii_case(Self::CAPTION) {
|
||||
Ok(Self::Caption)
|
||||
} else if s.eq_ignore_ascii_case(Self::URL) {
|
||||
Ok(Self::Url)
|
||||
} else if s.eq_ignore_ascii_case(Self::INPUT) {
|
||||
Ok(Self::Input)
|
||||
} else if s.eq_ignore_ascii_case(Self::TYPE) {
|
||||
Ok(Self::Type)
|
||||
} else {
|
||||
create_tuliprox_error_result!(TuliproxErrorKind::Info, "Unknown InputType: {}", s)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#[derive(Debug, Copy, Clone, serde::Serialize, serde::Deserialize)]
|
||||
pub enum FilterMode {
|
||||
@@ -148,213 +62,6 @@ impl ConfigApi {
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(clippy::struct_excessive_bools)]
|
||||
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize, Default)]
|
||||
#[serde(deny_unknown_fields)]
|
||||
pub struct ConfigDto {
|
||||
#[serde(default)]
|
||||
pub threads: u8,
|
||||
pub api: ConfigApi,
|
||||
#[serde(default)]
|
||||
pub working_dir: String,
|
||||
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||
pub backup_dir: Option<String>,
|
||||
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||
pub user_config_dir: Option<String>,
|
||||
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||
pub mapping_path: Option<String>,
|
||||
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||
pub custom_stream_response_path: Option<String>,
|
||||
#[serde(default)]
|
||||
pub user_access_control: bool,
|
||||
#[serde(default = "default_connect_timeout_secs")]
|
||||
pub connect_timeout_secs: u32,
|
||||
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||
pub sleep_timer_mins: Option<u32>,
|
||||
#[serde(default)]
|
||||
pub config_hot_reload: bool,
|
||||
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||
pub video: Option<VideoConfig>,
|
||||
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||
pub schedules: Option<Vec<ScheduleConfig>>,
|
||||
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||
pub messaging: Option<MessagingConfig>,
|
||||
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||
pub log: Option<LogConfig>,
|
||||
#[serde(default)]
|
||||
pub update_on_boot: bool,
|
||||
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||
pub web_ui: Option<WebUiConfig>,
|
||||
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||
pub reverse_proxy: Option<ReverseProxyConfig>,
|
||||
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||
pub proxy: Option<ProxyConfig>,
|
||||
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||
pub ipcheck: Option<IpCheckConfig>,
|
||||
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||
pub hdhomerun: Option<HdHomeRunConfig>,
|
||||
|
||||
}
|
||||
|
||||
impl ConfigDto {
|
||||
pub fn is_valid(&self) -> bool {
|
||||
if self.api.host.is_empty() {
|
||||
return false;
|
||||
}
|
||||
|
||||
if let Some(video) = &self.video {
|
||||
if let Some(download) = &video.download {
|
||||
if let Some(episode_pattern) = &download.episode_pattern {
|
||||
if !episode_pattern.is_empty() {
|
||||
let re = regex::Regex::new(episode_pattern);
|
||||
if re.is_err() {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
true
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize, Default)]
|
||||
#[serde(deny_unknown_fields)]
|
||||
pub struct ScheduleConfig {
|
||||
#[serde(default)]
|
||||
pub schedule: String,
|
||||
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||
pub targets: Option<Vec<String>>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize, Default)]
|
||||
#[serde(deny_unknown_fields)]
|
||||
pub struct StreamBufferConfig {
|
||||
#[serde(default)]
|
||||
pub enabled: bool,
|
||||
#[serde(default)]
|
||||
pub size: usize,
|
||||
}
|
||||
|
||||
impl StreamBufferConfig {
|
||||
fn prepare(&mut self) {
|
||||
if self.enabled && self.size == 0 {
|
||||
self.size = STREAM_QUEUE_SIZE;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize, Default)]
|
||||
#[serde(deny_unknown_fields)]
|
||||
pub struct StreamConfig {
|
||||
#[serde(default)]
|
||||
pub retry: bool,
|
||||
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||
pub buffer: Option<StreamBufferConfig>,
|
||||
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||
pub throttle: Option<String>,
|
||||
#[serde(default = "default_grace_period_millis")]
|
||||
pub grace_period_millis: u64,
|
||||
#[serde(default = "default_grace_period_timeout_secs")]
|
||||
pub grace_period_timeout_secs: u64,
|
||||
#[serde(default)]
|
||||
pub forced_retry_interval_secs: u32,
|
||||
#[serde(default, skip)]
|
||||
pub throttle_kbps: u64,
|
||||
}
|
||||
|
||||
impl StreamConfig {
|
||||
fn prepare(&mut self) -> Result<(), TuliproxError> {
|
||||
if let Some(buffer) = self.buffer.as_mut() {
|
||||
buffer.prepare();
|
||||
}
|
||||
if let Some(throttle) = &self.throttle {
|
||||
self.throttle_kbps = parse_to_kbps(throttle).map_err(|err| TuliproxError::new(TuliproxErrorKind::Info, err))?;
|
||||
}
|
||||
|
||||
if self.grace_period_millis > 0 {
|
||||
if self.grace_period_timeout_secs == 0 {
|
||||
let triple_ms = self.grace_period_millis * 3;
|
||||
self.grace_period_timeout_secs = std::cmp::max(1, triple_ms.div_ceil(1000));
|
||||
} else if self.grace_period_millis / 1000 > self.grace_period_timeout_secs {
|
||||
return Err(TuliproxError::new(TuliproxErrorKind::Info, format!("Grace time period timeout {} sec should be more than grace time period {} ms", self.grace_period_timeout_secs, self.grace_period_millis)));
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize, Default)]
|
||||
#[serde(deny_unknown_fields)]
|
||||
pub struct RateLimitConfig {
|
||||
pub enabled: bool,
|
||||
pub period_millis: u64,
|
||||
pub burst_size: u32,
|
||||
}
|
||||
|
||||
impl RateLimitConfig {
|
||||
fn prepare(&self) -> Result<(), TuliproxError> {
|
||||
if self.period_millis == 0 {
|
||||
return Err(TuliproxError::new(TuliproxErrorKind::Info, "Rate limiter period can't be 0".to_string()));
|
||||
}
|
||||
if self.burst_size == 0 {
|
||||
return Err(TuliproxError::new(TuliproxErrorKind::Info, "Rate limiter bust can't be 0".to_string()));
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize, Default)]
|
||||
#[serde(deny_unknown_fields)]
|
||||
pub struct ReverseProxyConfig {
|
||||
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||
pub stream: Option<StreamConfig>,
|
||||
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||
pub cache: Option<CacheConfig>,
|
||||
#[serde(default)]
|
||||
pub resource_rewrite_disabled: bool,
|
||||
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||
pub rate_limit: Option<RateLimitConfig>,
|
||||
#[serde(default)]
|
||||
pub disable_referer_header: bool,
|
||||
}
|
||||
|
||||
impl ReverseProxyConfig {
|
||||
fn prepare(&mut self, working_dir: &str) -> Result<(), TuliproxError> {
|
||||
if let Some(stream) = self.stream.as_mut() {
|
||||
stream.prepare()?;
|
||||
}
|
||||
if let Some(cache) = self.cache.as_mut() {
|
||||
if cache.enabled && self.resource_rewrite_disabled {
|
||||
warn!("The cache is disabled because resource rewrite is disabled");
|
||||
cache.enabled = false;
|
||||
}
|
||||
cache.prepare(working_dir)?;
|
||||
}
|
||||
|
||||
if let Some(rate_limit) = self.rate_limit.as_mut() {
|
||||
if rate_limit.enabled {
|
||||
rate_limit.prepare()?;
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize, Default)]
|
||||
#[serde(deny_unknown_fields)]
|
||||
pub struct CustomStreamResponse {
|
||||
#[serde(default, skip)]
|
||||
pub channel_unavailable: Option<TransportStreamBuffer>,
|
||||
#[serde(default, skip)]
|
||||
pub user_connections_exhausted: Option<TransportStreamBuffer>, // user has no more connections
|
||||
#[serde(default, skip)]
|
||||
pub provider_connections_exhausted: Option<TransportStreamBuffer>, // provider limit reached, has no more connections
|
||||
#[serde(default, skip)]
|
||||
pub user_account_expired: Option<TransportStreamBuffer>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize, Default)]
|
||||
#[serde(deny_unknown_fields)]
|
||||
pub struct Config {
|
||||
@@ -1,8 +1,8 @@
|
||||
use std::path::PathBuf;
|
||||
use log::error;
|
||||
use path_clean::PathClean;
|
||||
use crate::tuliprox_error::{info_err, TuliproxError, TuliproxErrorKind};
|
||||
use crate::utils::parse_size_base_2;
|
||||
use shared::error::{info_err, TuliproxError, TuliproxErrorKind};
|
||||
use shared::utils::parse_size_base_2;
|
||||
|
||||
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize, Default)]
|
||||
#[serde(deny_unknown_fields)]
|
||||
@@ -1,5 +1,5 @@
|
||||
use crate::tuliprox_error::{create_tuliprox_error_result, info_err, TuliproxError, TuliproxErrorKind};
|
||||
use crate::utils::CONSTANTS;
|
||||
use shared::error::{create_tuliprox_error_result, info_err, TuliproxError, TuliproxErrorKind};
|
||||
use shared::utils::CONSTANTS;
|
||||
use log::warn;
|
||||
use regex::Regex;
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
use std::collections::HashSet;
|
||||
use log::warn;
|
||||
use crate::tuliprox_error::{TuliproxError, TuliproxErrorKind, create_tuliprox_error_result};
|
||||
use shared::error::{TuliproxError, TuliproxErrorKind, create_tuliprox_error_result};
|
||||
fn default_friendly_name() -> String { String::from("TuliproxTV") }
|
||||
fn default_manufacturer() -> String { String::from("Silicondust") }
|
||||
fn default_model_name() -> String { String::from("HDTC-2US") }
|
||||
@@ -1,7 +1,7 @@
|
||||
use crate::tuliprox_error::{create_tuliprox_error_result, handle_tuliprox_error_result_list, info_err, TuliproxError, TuliproxErrorKind};
|
||||
use shared::error::{create_tuliprox_error_result, handle_tuliprox_error_result_list, info_err, TuliproxError, TuliproxErrorKind};
|
||||
use crate::model::{EpgConfig};
|
||||
use crate::utils::default_as_true;
|
||||
use crate::utils::get_trimmed_string;
|
||||
use shared::utils::default_as_true;
|
||||
use shared::utils::get_trimmed_string;
|
||||
use crate::utils::request::{get_base_url_from_str, get_credentials_from_url, get_credentials_from_url_str, sanitize_sensitive_info};
|
||||
use enum_iterator::Sequence;
|
||||
use log::{debug};
|
||||
@@ -1,5 +1,5 @@
|
||||
use regex::Regex;
|
||||
use crate::tuliprox_error::{TuliproxError, TuliproxErrorKind};
|
||||
use shared::error::{TuliproxError, TuliproxErrorKind};
|
||||
|
||||
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize, Default)]
|
||||
#[serde(deny_unknown_fields)]
|
||||
@@ -1,5 +1,5 @@
|
||||
|
||||
use crate::utils::default_as_true;
|
||||
use shared::utils::default_as_true;
|
||||
|
||||
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize, Default)]
|
||||
#[serde(deny_unknown_fields)]
|
||||
@@ -1,4 +1,4 @@
|
||||
use crate::messaging::MsgKind;
|
||||
use shared::model::MsgKind;
|
||||
|
||||
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
|
||||
#[serde(deny_unknown_fields)]
|
||||
@@ -0,0 +1,47 @@
|
||||
mod trakt;
|
||||
mod base;
|
||||
mod webui;
|
||||
mod web_auth;
|
||||
mod messaging;
|
||||
mod hdhomerun;
|
||||
mod ip_check;
|
||||
mod source;
|
||||
mod target;
|
||||
mod sort;
|
||||
mod video_download;
|
||||
mod log;
|
||||
mod input;
|
||||
mod stream;
|
||||
mod epg;
|
||||
mod reverse_proxy;
|
||||
mod cache;
|
||||
mod rate_limit;
|
||||
mod proxy;
|
||||
mod schedule;
|
||||
mod api_proxy;
|
||||
mod rename;
|
||||
|
||||
mod healthcheck;
|
||||
|
||||
pub use base::*;
|
||||
pub use api_proxy::*;
|
||||
pub use webui::*;
|
||||
pub use web_auth::*;
|
||||
pub use messaging::*;
|
||||
pub use hdhomerun::*;
|
||||
pub use ip_check::*;
|
||||
pub use source::*;
|
||||
pub use target::*;
|
||||
pub use sort::*;
|
||||
pub use video_download::*;
|
||||
pub use schedule::*;
|
||||
pub use log::*;
|
||||
pub use input::*;
|
||||
pub use stream::*;
|
||||
pub use epg::*;
|
||||
pub use rate_limit::*;
|
||||
pub use reverse_proxy::*;
|
||||
pub use proxy::*;
|
||||
pub use rename::*;
|
||||
pub use trakt::*;
|
||||
pub use healthcheck::*;
|
||||
@@ -1,4 +1,4 @@
|
||||
use crate::tuliprox_error::{TuliproxError, TuliproxErrorKind};
|
||||
use shared::error::{TuliproxError, TuliproxErrorKind};
|
||||
|
||||
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize, Default)]
|
||||
#[serde(deny_unknown_fields)]
|
||||
@@ -0,0 +1,21 @@
|
||||
use shared::error::{TuliproxError, TuliproxErrorKind};
|
||||
|
||||
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize, Default)]
|
||||
#[serde(deny_unknown_fields)]
|
||||
pub struct RateLimitConfig {
|
||||
pub enabled: bool,
|
||||
pub period_millis: u64,
|
||||
pub burst_size: u32,
|
||||
}
|
||||
|
||||
impl RateLimitConfig {
|
||||
pub(crate) fn prepare(&self) -> Result<(), TuliproxError> {
|
||||
if self.period_millis == 0 {
|
||||
return Err(TuliproxError::new(TuliproxErrorKind::Info, "Rate limiter period can't be 0".to_string()));
|
||||
}
|
||||
if self.burst_size == 0 {
|
||||
return Err(TuliproxError::new(TuliproxErrorKind::Info, "Rate limiter burst can't be 0".to_string()));
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
use shared::model::ItemField;
|
||||
use crate::foundation::filter::{apply_templates_to_pattern_single, PatternTemplate};
|
||||
use crate::tuliprox_error::{TuliproxError, TuliproxErrorKind, create_tuliprox_error_result};
|
||||
use crate::model::ItemField;
|
||||
use shared::error::{TuliproxError, TuliproxErrorKind, create_tuliprox_error_result};
|
||||
|
||||
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
|
||||
#[serde(deny_unknown_fields)]
|
||||
@@ -0,0 +1,42 @@
|
||||
use log::warn;
|
||||
use shared::error::TuliproxError;
|
||||
use crate::model::config::cache::CacheConfig;
|
||||
use crate::model::{RateLimitConfig, StreamConfig};
|
||||
|
||||
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize, Default)]
|
||||
#[serde(deny_unknown_fields)]
|
||||
pub struct ReverseProxyConfig {
|
||||
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||
pub stream: Option<StreamConfig>,
|
||||
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||
pub cache: Option<CacheConfig>,
|
||||
#[serde(default)]
|
||||
pub resource_rewrite_disabled: bool,
|
||||
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||
pub rate_limit: Option<RateLimitConfig>,
|
||||
#[serde(default)]
|
||||
pub disable_referer_header: bool,
|
||||
}
|
||||
|
||||
|
||||
impl ReverseProxyConfig {
|
||||
pub(crate) fn prepare(&mut self, working_dir: &str) -> Result<(), TuliproxError> {
|
||||
if let Some(stream) = self.stream.as_mut() {
|
||||
stream.prepare()?;
|
||||
}
|
||||
if let Some(cache) = self.cache.as_mut() {
|
||||
if cache.enabled && self.resource_rewrite_disabled {
|
||||
warn!("The cache is disabled because resource rewrite is disabled");
|
||||
cache.enabled = false;
|
||||
}
|
||||
cache.prepare(working_dir)?;
|
||||
}
|
||||
|
||||
if let Some(rate_limit) = self.rate_limit.as_mut() {
|
||||
if rate_limit.enabled {
|
||||
rate_limit.prepare()?;
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
|
||||
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize, Default)]
|
||||
#[serde(deny_unknown_fields)]
|
||||
pub struct ScheduleConfig {
|
||||
#[serde(default)]
|
||||
pub schedule: String,
|
||||
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||
pub targets: Option<Vec<String>>,
|
||||
}
|
||||
@@ -1,7 +1,7 @@
|
||||
use regex::Regex;
|
||||
use crate::foundation::filter::{apply_templates_to_pattern, apply_templates_to_pattern_single, PatternTemplate, TemplateValue};
|
||||
use crate::tuliprox_error::{TuliproxError, TuliproxErrorKind, create_tuliprox_error, handle_tuliprox_error_result_list};
|
||||
use crate::model::{ItemField};
|
||||
use shared::error::{TuliproxError, TuliproxErrorKind, create_tuliprox_error, handle_tuliprox_error_result_list};
|
||||
use shared::model::ItemField;
|
||||
|
||||
#[derive(Debug, Copy, Clone, serde::Serialize, serde::Deserialize)]
|
||||
pub enum SortOrder {
|
||||
@@ -1,9 +1,8 @@
|
||||
use std::collections::{HashMap, HashSet};
|
||||
use crate::foundation::filter::{prepare_templates, PatternTemplate};
|
||||
use crate::tuliprox_error::{TuliproxError, TuliproxErrorKind, handle_tuliprox_error_result_list, create_tuliprox_error_result};
|
||||
use crate::model::{ConfigInput, ProcessTargets};
|
||||
use crate::model::config_target::ConfigTarget;
|
||||
use crate::utils::default_as_default;
|
||||
use shared::error::{TuliproxError, TuliproxErrorKind, handle_tuliprox_error_result_list, create_tuliprox_error_result};
|
||||
use crate::model::{ConfigInput, ConfigTarget, ProcessTargets};
|
||||
use shared::utils::default_as_default;
|
||||
|
||||
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
|
||||
#[serde(deny_unknown_fields)]
|
||||
@@ -0,0 +1,80 @@
|
||||
use shared::utils::default_grace_period_millis;
|
||||
use shared::utils::default_grace_period_timeout_secs;
|
||||
use shared::error::{TuliproxError, TuliproxErrorKind};
|
||||
use shared::info_err;
|
||||
use shared::utils::parse_to_kbps;
|
||||
use crate::api::model::streams::transport_stream_buffer::TransportStreamBuffer;
|
||||
|
||||
const STREAM_QUEUE_SIZE: usize = 1024; // mpsc channel holding messages. with 8192byte chunks and 2Mbit/s approx 8MB
|
||||
|
||||
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize, Default)]
|
||||
#[serde(deny_unknown_fields)]
|
||||
pub struct StreamBufferConfig {
|
||||
#[serde(default)]
|
||||
pub enabled: bool,
|
||||
#[serde(default)]
|
||||
pub size: usize,
|
||||
}
|
||||
|
||||
impl StreamBufferConfig {
|
||||
fn prepare(&mut self) {
|
||||
if self.enabled && self.size == 0 {
|
||||
self.size = STREAM_QUEUE_SIZE;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize, Default)]
|
||||
#[serde(deny_unknown_fields)]
|
||||
pub struct StreamConfig {
|
||||
#[serde(default)]
|
||||
pub retry: bool,
|
||||
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||
pub buffer: Option<StreamBufferConfig>,
|
||||
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||
pub throttle: Option<String>,
|
||||
#[serde(default = "default_grace_period_millis")]
|
||||
pub grace_period_millis: u64,
|
||||
#[serde(default = "default_grace_period_timeout_secs")]
|
||||
pub grace_period_timeout_secs: u64,
|
||||
#[serde(default)]
|
||||
pub forced_retry_interval_secs: u32,
|
||||
#[serde(default, skip)]
|
||||
pub throttle_kbps: u64,
|
||||
}
|
||||
|
||||
impl StreamConfig {
|
||||
pub(crate) fn prepare(&mut self) -> Result<(), TuliproxError> {
|
||||
if let Some(buffer) = self.buffer.as_mut() {
|
||||
buffer.prepare();
|
||||
}
|
||||
if let Some(throttle) = &self.throttle {
|
||||
self.throttle_kbps = parse_to_kbps(throttle).map_err(|err| TuliproxError::new(TuliproxErrorKind::Info, err))?;
|
||||
}
|
||||
|
||||
if self.grace_period_millis > 0 {
|
||||
if self.grace_period_timeout_secs == 0 {
|
||||
let triple_ms = self.grace_period_millis.saturating_mul(3);
|
||||
self.grace_period_timeout_secs = std::cmp::max(1, triple_ms.div_ceil(1000));
|
||||
} else if self.grace_period_millis / 1000 > self.grace_period_timeout_secs {
|
||||
return Err(info_err!(format!("Grace time period timeout {} sec should be more than grace time period {} ms", self.grace_period_timeout_secs, self.grace_period_millis)));
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize, Default)]
|
||||
#[serde(deny_unknown_fields)]
|
||||
pub struct CustomStreamResponse {
|
||||
#[serde(default, skip)]
|
||||
pub channel_unavailable: Option<TransportStreamBuffer>,
|
||||
#[serde(default, skip)]
|
||||
pub user_connections_exhausted: Option<TransportStreamBuffer>, // user has no more connections
|
||||
#[serde(default, skip)]
|
||||
pub provider_connections_exhausted: Option<TransportStreamBuffer>, // provider limit reached, has no more connections
|
||||
#[serde(default, skip)]
|
||||
pub user_account_expired: Option<TransportStreamBuffer>,
|
||||
}
|
||||
@@ -1,80 +1,14 @@
|
||||
use crate::foundation::filter::{get_filter, Filter, PatternTemplate, ValueProvider};
|
||||
use crate::tuliprox_error::{create_tuliprox_error_result, handle_tuliprox_error_result_list, info_err, TuliproxError, TuliproxErrorKind};
|
||||
use crate::model::cluster_flags::ClusterFlags;
|
||||
use crate::model::config_rename::ConfigRename;
|
||||
use crate::model::config_sort::ConfigSort;
|
||||
use crate::model::mapping::Mapping;
|
||||
use crate::model::processing_order::ProcessingOrder;
|
||||
use crate::model::PlaylistItemType;
|
||||
use crate::model::trakt::TraktConfig;
|
||||
use crate::utils::{default_as_default, default_as_true, default_resolve_delay_secs};
|
||||
use enum_iterator::Sequence;
|
||||
use std::fmt::Display;
|
||||
use crate::model::config::trakt::TraktConfig;
|
||||
use shared::error::{create_tuliprox_error_result, handle_tuliprox_error_result_list, info_err, TuliproxError, TuliproxErrorKind};
|
||||
use shared::utils::{default_as_default, default_as_true, default_resolve_delay_secs};
|
||||
use arc_swap::ArcSwapOption;
|
||||
use shared::model::{ClusterFlags, ProcessingOrder, StrmExportStyle, TargetType};
|
||||
use shared::model::PlaylistItemType;
|
||||
use std::sync::Arc;
|
||||
use arc_swap::{ArcSwapOption};
|
||||
use crate::model::{ConfigRename, ConfigSort};
|
||||
|
||||
#[derive(Debug, Copy, Clone, serde::Serialize, serde::Deserialize, Sequence, PartialEq, Eq, Hash, Default)]
|
||||
pub enum ExportStyle {
|
||||
#[serde(rename = "kodi")]
|
||||
#[default]
|
||||
Kodi,
|
||||
#[serde(rename = "plex")]
|
||||
Plex,
|
||||
#[serde(rename = "emby")]
|
||||
Emby,
|
||||
#[serde(rename = "jellyfin")]
|
||||
Jellyfin,
|
||||
}
|
||||
|
||||
#[derive(Debug, Copy, Clone, serde::Serialize, serde::Deserialize, Sequence, PartialEq, Eq, Hash)]
|
||||
pub enum TargetType {
|
||||
#[serde(rename = "m3u")]
|
||||
M3u,
|
||||
#[serde(rename = "xtream")]
|
||||
Xtream,
|
||||
#[serde(rename = "strm")]
|
||||
Strm,
|
||||
#[serde(rename = "hdhomerun")]
|
||||
HdHomeRun,
|
||||
}
|
||||
|
||||
impl TargetType {
|
||||
const M3U: &'static str = "M3u";
|
||||
const XTREAM: &'static str = "Xtream";
|
||||
const STRM: &'static str = "Strm";
|
||||
const HDHOMERUN: &'static str = "HdHomeRun";
|
||||
}
|
||||
|
||||
impl Display for TargetType {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
|
||||
write!(f, "{}", match *self {
|
||||
Self::M3u => Self::M3U,
|
||||
Self::Xtream => Self::XTREAM,
|
||||
Self::Strm => Self::STRM,
|
||||
Self::HdHomeRun => Self::HDHOMERUN,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize, Sequence, PartialEq, Eq, Hash)]
|
||||
enum HdHomeRunUseTargetType {
|
||||
#[serde(rename = "m3u")]
|
||||
M3u,
|
||||
#[serde(rename = "xtream")]
|
||||
Xtream,
|
||||
}
|
||||
|
||||
impl TryFrom<TargetType> for HdHomeRunUseTargetType {
|
||||
type Error = &'static str;
|
||||
|
||||
fn try_from(value: TargetType) -> Result<Self, Self::Error> {
|
||||
match value {
|
||||
TargetType::Xtream => Ok(Self::Xtream),
|
||||
TargetType::M3u => Ok(Self::M3u),
|
||||
_ => Err("Not allowed!"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct ProcessTargets {
|
||||
@@ -154,7 +88,7 @@ pub struct StrmTargetOutput {
|
||||
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||
pub username: Option<String>,
|
||||
#[serde(default)]
|
||||
pub style: ExportStyle,
|
||||
pub style: StrmExportStyle,
|
||||
#[serde(default)]
|
||||
pub flat: bool,
|
||||
#[serde(default)]
|
||||
@@ -184,7 +118,7 @@ pub enum TargetOutput {
|
||||
|
||||
impl TargetOutput {
|
||||
pub fn prepare(&mut self) {
|
||||
match self {
|
||||
match self {
|
||||
TargetOutput::Xtream(output) => output.prepare(),
|
||||
TargetOutput::M3u(_)
|
||||
| TargetOutput::Strm(_)
|
||||
@@ -257,7 +191,7 @@ impl ConfigTarget {
|
||||
m3u_output.filename = m3u_output.filename.as_ref().map(|s| s.trim().to_string());
|
||||
}
|
||||
TargetOutput::Strm(strm_output) => {
|
||||
strm_cnt +=1;
|
||||
strm_cnt += 1;
|
||||
strm_output.directory = strm_output.directory.trim().to_string();
|
||||
if strm_output.directory.trim().is_empty() {
|
||||
return create_tuliprox_error_result!(TuliproxErrorKind::Info, "directory is required for strm type: {}", self.name);
|
||||
@@ -1,6 +1,6 @@
|
||||
use serde::{Deserialize, Serialize};
|
||||
use crate::model::{PlaylistItem};
|
||||
use crate::utils::trakt::normalize_title_for_matching;
|
||||
use crate::utils::normalize_title_for_matching;
|
||||
|
||||
const TRAKT_API_KEY: &str = "0183a05ad97098d87287fe46da4ae286f434f32e8e951caad4cc147c947d79a3";
|
||||
const TRAKT_API_VERSION: &str = "2";
|
||||
@@ -1,4 +1,4 @@
|
||||
use crate::tuliprox_error::{create_tuliprox_error_result, TuliproxError, TuliproxErrorKind};
|
||||
use shared::error::{create_tuliprox_error_result, TuliproxError, TuliproxErrorKind};
|
||||
use regex::Regex;
|
||||
use std::borrow::BorrowMut;
|
||||
use std::collections::HashMap;
|
||||
@@ -1,9 +1,9 @@
|
||||
use crate::utils::default_as_true;
|
||||
use shared::utils::default_as_true;
|
||||
use std::fs::File;
|
||||
use std::io::BufRead;
|
||||
use std::path::PathBuf;
|
||||
use crate::auth::UserCredential;
|
||||
use crate::tuliprox_error::{TuliproxError, TuliproxErrorKind, create_tuliprox_error_result};
|
||||
use shared::error::{TuliproxError, TuliproxErrorKind, create_tuliprox_error_result};
|
||||
use crate::utils;
|
||||
|
||||
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
|
||||
@@ -1,5 +1,5 @@
|
||||
use crate::utils::default_as_true;
|
||||
use crate::tuliprox_error::{TuliproxError, TuliproxErrorKind};
|
||||
use shared::utils::default_as_true;
|
||||
use shared::error::{TuliproxError, TuliproxErrorKind};
|
||||
use crate::model::WebAuthConfig;
|
||||
|
||||
const RESERVED_PATHS: &[&str] = &[
|
||||
@@ -7,8 +7,8 @@ use log::{trace};
|
||||
use crate::foundation::filter::{apply_templates_to_pattern_single, get_filter, prepare_templates, Filter, PatternTemplate};
|
||||
use crate::foundation::mapper::MapperScript;
|
||||
use crate::model::valid_property;
|
||||
use crate::tuliprox_error::{create_tuliprox_error_result, info_err};
|
||||
use crate::tuliprox_error::{TuliproxError, TuliproxErrorKind};
|
||||
use shared::error::{create_tuliprox_error_result, info_err};
|
||||
use shared::error::{TuliproxError, TuliproxErrorKind};
|
||||
|
||||
pub const COUNTER_FIELDS: &[&str] = &["name", "title", "caption", "chno"];
|
||||
|
||||
@@ -0,0 +1,19 @@
|
||||
mod playlist;
|
||||
mod mapping;
|
||||
mod stats;
|
||||
mod xmltv;
|
||||
mod xtream;
|
||||
mod healthcheck;
|
||||
mod playlist_categories;
|
||||
mod xtream_const;
|
||||
mod config;
|
||||
|
||||
pub use self::playlist::*;
|
||||
pub use self::mapping::*;
|
||||
pub use self::stats::*;
|
||||
pub use self::xmltv::*;
|
||||
pub use self::xtream::*;
|
||||
pub use self::healthcheck::*;
|
||||
pub use self::playlist_categories::*;
|
||||
pub use self::xtream_const::*;
|
||||
pub use self::config::*;
|
||||
@@ -1,28 +1,16 @@
|
||||
use crate::model::xtream::{xtream_playlistitem_to_document, XtreamMappingOptions};
|
||||
use crate::model::xtream_const;
|
||||
use crate::model::ProxyUserCredentials;
|
||||
use crate::model::TVGuide;
|
||||
use crate::model::{ConfigInput, ConfigTargetOptions};
|
||||
use crate::model::{TVGuide, ProxyUserCredentials, ConfigInput, ConfigTargetOptions};
|
||||
use crate::utils::request::extract_extension_from_url;
|
||||
use crate::utils::{generate_playlist_uuid, get_provider_id};
|
||||
use crate::utils::{get_string_from_serde_value, get_u64_from_serde_value};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use serde_json::{Map, Value};
|
||||
use std::borrow::Cow;
|
||||
use std::cmp::PartialEq;
|
||||
use std::fmt::{Display, Formatter};
|
||||
use std::str::FromStr;
|
||||
use shared::model::{PlaylistEntry, PlaylistItemType, UUIDType, XtreamCluster};
|
||||
// https://de.wikipedia.org/wiki/M3U
|
||||
// https://siptv.eu/howto/playlist.html
|
||||
|
||||
pub trait PlaylistEntry: Send + Sync {
|
||||
fn get_virtual_id(&self) -> u32;
|
||||
fn get_provider_id(&self) -> Option<u32>;
|
||||
fn get_category_id(&self) -> Option<u32>;
|
||||
fn get_provider_url(&self) -> String;
|
||||
fn get_uuid(&self) -> UUIDType;
|
||||
fn get_item_type(&self) -> PlaylistItemType;
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct FetchedPlaylist<'a> { // Contains playlist for one input
|
||||
@@ -42,120 +30,6 @@ impl FetchedPlaylist<'_> {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Copy, Clone, Eq, Hash, PartialEq, Serialize, Deserialize, Default)]
|
||||
#[repr(u8)]
|
||||
pub enum XtreamCluster {
|
||||
#[default]
|
||||
Live = 1,
|
||||
Video = 2,
|
||||
Series = 3,
|
||||
}
|
||||
|
||||
impl XtreamCluster {
|
||||
pub const fn as_str(&self) -> &str {
|
||||
match self {
|
||||
Self::Live => "Live",
|
||||
Self::Video => "Video",
|
||||
Self::Series => "Series",
|
||||
}
|
||||
}
|
||||
pub const fn as_stream_type(&self) -> &str {
|
||||
match self {
|
||||
Self::Live => "live",
|
||||
Self::Video => "movie",
|
||||
Self::Series => "series",
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Display for XtreamCluster {
|
||||
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
|
||||
write!(f, "{}", self.as_str())
|
||||
}
|
||||
}
|
||||
|
||||
impl TryFrom<PlaylistItemType> for XtreamCluster {
|
||||
type Error = String;
|
||||
fn try_from(item_type: PlaylistItemType) -> Result<Self, Self::Error> {
|
||||
match item_type {
|
||||
PlaylistItemType::Live | PlaylistItemType::LiveHls | PlaylistItemType::LiveDash | PlaylistItemType::LiveUnknown => Ok(Self::Live),
|
||||
PlaylistItemType::Catchup | PlaylistItemType::Video => Ok(Self::Video),
|
||||
PlaylistItemType::Series | PlaylistItemType::SeriesInfo => Ok(Self::Series),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Copy, Clone, Eq, Hash, PartialEq, Serialize, Deserialize, Default)]
|
||||
#[repr(u8)]
|
||||
pub enum PlaylistItemType {
|
||||
#[default]
|
||||
Live = 1,
|
||||
Video = 2,
|
||||
Series = 3, // xtream series description
|
||||
SeriesInfo = 4, // xtream series info fetched for series description
|
||||
Catchup = 5,
|
||||
LiveUnknown = 6, // No Provider id
|
||||
LiveHls = 7, // m3u8 entry
|
||||
LiveDash = 8, // mpd
|
||||
}
|
||||
|
||||
impl From<XtreamCluster> for PlaylistItemType {
|
||||
fn from(xtream_cluster: XtreamCluster) -> Self {
|
||||
match xtream_cluster {
|
||||
XtreamCluster::Live => Self::Live,
|
||||
XtreamCluster::Video => Self::Video,
|
||||
XtreamCluster::Series => Self::SeriesInfo,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
impl FromStr for PlaylistItemType {
|
||||
type Err = String;
|
||||
|
||||
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
||||
match s {
|
||||
"Live" => Ok(PlaylistItemType::Live),
|
||||
"Video" => Ok(PlaylistItemType::Video),
|
||||
"Series" => Ok(PlaylistItemType::Series),
|
||||
"SeriesInfo" => Ok(PlaylistItemType::SeriesInfo),
|
||||
"Catchup" => Ok(PlaylistItemType::Catchup),
|
||||
"LiveUnknown" => Ok(PlaylistItemType::LiveUnknown),
|
||||
"LiveHls" => Ok(PlaylistItemType::LiveHls),
|
||||
"LiveDash" => Ok(PlaylistItemType::LiveDash),
|
||||
_ => Err(format!("Invalid PlaylistItemType: {s}")),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl PlaylistItemType {
|
||||
const LIVE: &'static str = "live";
|
||||
const VIDEO: &'static str = "video";
|
||||
const SERIES: &'static str = "series";
|
||||
const SERIES_INFO: &'static str = "series-info";
|
||||
const CATCHUP: &'static str = "catchup";
|
||||
}
|
||||
|
||||
impl Display for PlaylistItemType {
|
||||
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
|
||||
write!(f, "{}", match self {
|
||||
Self::Live | Self::LiveHls | Self::LiveDash | Self::LiveUnknown => Self::LIVE,
|
||||
Self::Video => Self::VIDEO,
|
||||
Self::Series => Self::SERIES,
|
||||
Self::SeriesInfo => Self::SERIES_INFO,
|
||||
Self::Catchup => Self::CATCHUP,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
pub trait FieldGetAccessor {
|
||||
fn get_field(&self, field: &str) -> Option<Cow<str>>;
|
||||
}
|
||||
pub trait FieldSetAccessor {
|
||||
fn set_field(&mut self, field: &str, value: &str) -> bool;
|
||||
}
|
||||
|
||||
pub type UUIDType = [u8; 32];
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, Default)]
|
||||
pub struct PlaylistItemHeader {
|
||||
@@ -252,7 +126,7 @@ macro_rules! to_m3u_resource_non_empty_fields {
|
||||
|
||||
macro_rules! generate_field_accessor_impl_for_playlist_item_header {
|
||||
($($prop:ident),*;) => {
|
||||
impl FieldGetAccessor for PlaylistItemHeader {
|
||||
impl shared::model::FieldGetAccessor for PlaylistItemHeader {
|
||||
fn get_field(&self, field: &str) -> Option<Cow<str>> {
|
||||
let field = field.to_lowercase();
|
||||
match field.as_str() {
|
||||
@@ -267,7 +141,7 @@ macro_rules! generate_field_accessor_impl_for_playlist_item_header {
|
||||
}
|
||||
}
|
||||
}
|
||||
impl FieldSetAccessor for PlaylistItemHeader {
|
||||
impl shared::model::FieldSetAccessor for PlaylistItemHeader {
|
||||
fn set_field(&mut self, field: &str, value: &str) -> bool {
|
||||
let field = field.to_lowercase();
|
||||
let val = String::from(value);
|
||||
@@ -379,7 +253,7 @@ impl PlaylistEntry for M3uPlaylistItem {
|
||||
|
||||
macro_rules! generate_field_accessor_impl_for_m3u_playlist_item {
|
||||
($($prop:ident),*;) => {
|
||||
impl FieldGetAccessor for M3uPlaylistItem {
|
||||
impl shared::model::FieldGetAccessor for M3uPlaylistItem {
|
||||
fn get_field(&self, field: &str) -> Option<Cow<str>> {
|
||||
let field = field.to_lowercase();
|
||||
match field.as_str() {
|
||||
@@ -490,7 +364,7 @@ pub fn get_backdrop_path_value<'a>(field: &'a str, value: Option<&'a Value>) ->
|
||||
|
||||
macro_rules! generate_field_accessor_impl_for_xtream_playlist_item {
|
||||
($($prop:ident),*;) => {
|
||||
impl FieldGetAccessor for XtreamPlaylistItem {
|
||||
impl shared::model::FieldGetAccessor for XtreamPlaylistItem {
|
||||
fn get_field(&self, field: &str) -> Option<Cow<str>> {
|
||||
let field = field.to_lowercase();
|
||||
match field.as_str() {
|
||||
@@ -1,14 +1,15 @@
|
||||
use crate::model::{ProxyUserCredentials};
|
||||
use crate::model::{Config, ConfigTarget, ClusterFlags, XtreamTargetOutput};
|
||||
use crate::model::{PlaylistEntry, PlaylistItem, XtreamCluster, XtreamPlaylistItem};
|
||||
use crate::model::serde_utils::{deserialize_as_option_rc_string, deserialize_as_rc_string, deserialize_as_string_array, deserialize_number_from_string};
|
||||
use crate::model::{Config, ConfigTarget, XtreamTargetOutput};
|
||||
use crate::model::{PlaylistItem,XtreamPlaylistItem};
|
||||
use crate::utils::{deserialize_as_option_rc_string, deserialize_as_rc_string, deserialize_as_string_array, deserialize_number_from_string};
|
||||
use crate::model::xtream_const;
|
||||
use crate::utils::{get_non_empty_str, opt_string_or_number_u32, string_default_on_null, string_or_number_f64, string_or_number_u32};
|
||||
use crate::utils::{opt_string_or_number_u32, string_default_on_null, string_or_number_f64, string_or_number_u32};
|
||||
use shared::utils::{get_non_empty_str};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use serde_json::{Map, Value};
|
||||
use std::collections::HashMap;
|
||||
use std::iter::FromIterator;
|
||||
|
||||
use shared::model::{ClusterFlags, PlaylistEntry, XtreamCluster};
|
||||
|
||||
#[derive(Deserialize, Default)]
|
||||
pub struct XtreamCategory {
|
||||
@@ -8,7 +8,6 @@ macro_rules! include_modules {
|
||||
extern crate pest_derive;
|
||||
pub mod api;
|
||||
pub mod auth;
|
||||
pub mod tuliprox_error;
|
||||
pub mod messaging;
|
||||
pub mod model;
|
||||
pub mod processing;
|
||||
@@ -1,5 +1,5 @@
|
||||
use crate::model::ProxyUserCredentials;
|
||||
use crate::utils::{CONSTANTS, HLS_PREFIX};
|
||||
use shared::utils::{CONSTANTS, HLS_PREFIX};
|
||||
use crate::utils::{deobfuscate_text, obfuscate_text};
|
||||
use std::str;
|
||||
|
||||
@@ -1,7 +1,24 @@
|
||||
use std::borrow::BorrowMut;
|
||||
use shared::model::{PlaylistItemType, XtreamCluster};
|
||||
use crate::model::{Config, ConfigInput};
|
||||
use crate::model::{PlaylistGroup, PlaylistItem, PlaylistItemHeader, PlaylistItemType, XtreamCluster};
|
||||
use crate::utils::{extract_id_from_url, get_title_group};
|
||||
use crate::model::{PlaylistGroup, PlaylistItem, PlaylistItemHeader};
|
||||
use crate::utils::{extract_id_from_url};
|
||||
|
||||
|
||||
// other implementations like calculating text_distance on all titles took too much time
|
||||
// we keep it now as simple as possible and less memory intensive.
|
||||
fn get_title_group(text: &str) -> String {
|
||||
let alphabetic_only: String = text.chars().map(|c| if c.is_alphanumeric() { c } else { ' ' }).collect();
|
||||
let parts = alphabetic_only.split_whitespace();
|
||||
let mut combination = String::new();
|
||||
for p in parts {
|
||||
combination = format!("{combination} {p}").trim().to_string();
|
||||
if combination.len() > 2 {
|
||||
return combination;
|
||||
}
|
||||
}
|
||||
text.to_string()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn token_value(stack: &mut String, it: &mut std::str::Chars) -> String {
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user