Merge pull request #454 from jb-alvarado/master
delete/update user, add silence detection for validation
This commit is contained in:
commit
05347169b5
231
Cargo.lock
generated
231
Cargo.lock
generated
@ -458,21 +458,34 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "81953c529336010edd6d8e358f886d9581267795c61b19475b71314bffa46d35"
|
||||
dependencies = [
|
||||
"concurrent-queue",
|
||||
"event-listener",
|
||||
"event-listener 2.5.3",
|
||||
"futures-core",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "async-executor"
|
||||
version = "1.6.0"
|
||||
name = "async-channel"
|
||||
version = "2.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4b0c4a4f319e45986f347ee47fef8bf5e81c9abc3f6f58dc2391439f30df65f0"
|
||||
checksum = "d37875bd9915b7d67c2f117ea2c30a0989874d0b2cb694fe25403c85763c0c9e"
|
||||
dependencies = [
|
||||
"async-lock",
|
||||
"concurrent-queue",
|
||||
"event-listener 3.1.0",
|
||||
"event-listener-strategy",
|
||||
"futures-core",
|
||||
"pin-project-lite",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "async-executor"
|
||||
version = "1.7.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "fc5ea910c42e5ab19012bab31f53cb4d63d54c3a27730f9a833a88efcf4bb52d"
|
||||
dependencies = [
|
||||
"async-lock 3.1.1",
|
||||
"async-task",
|
||||
"concurrent-queue",
|
||||
"fastrand 2.0.1",
|
||||
"futures-lite",
|
||||
"futures-lite 2.0.1",
|
||||
"slab",
|
||||
]
|
||||
|
||||
@ -482,12 +495,12 @@ version = "2.3.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f1b6f5d7df27bd294849f8eec66ecfc63d11814df7a4f5d74168a2394467b776"
|
||||
dependencies = [
|
||||
"async-channel",
|
||||
"async-channel 1.9.0",
|
||||
"async-executor",
|
||||
"async-io",
|
||||
"async-lock",
|
||||
"async-lock 2.8.0",
|
||||
"blocking",
|
||||
"futures-lite",
|
||||
"futures-lite 1.13.0",
|
||||
"once_cell",
|
||||
]
|
||||
|
||||
@ -497,11 +510,11 @@ version = "1.13.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0fc5b45d93ef0529756f812ca52e44c221b35341892d3dcc34132ac02f3dd2af"
|
||||
dependencies = [
|
||||
"async-lock",
|
||||
"async-lock 2.8.0",
|
||||
"autocfg",
|
||||
"cfg-if",
|
||||
"concurrent-queue",
|
||||
"futures-lite",
|
||||
"futures-lite 1.13.0",
|
||||
"log",
|
||||
"parking",
|
||||
"polling",
|
||||
@ -517,7 +530,18 @@ version = "2.8.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "287272293e9d8c41773cec55e365490fe034813a2f172f502d6ddcf75b2f582b"
|
||||
dependencies = [
|
||||
"event-listener",
|
||||
"event-listener 2.5.3",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "async-lock"
|
||||
version = "3.1.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "655b9c7fe787d3b25cc0f804a1a8401790f0c5bc395beb5a64dc77d8de079105"
|
||||
dependencies = [
|
||||
"event-listener 3.1.0",
|
||||
"event-listener-strategy",
|
||||
"pin-project-lite",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -527,15 +551,15 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "62565bb4402e926b29953c785397c6dc0391b7b446e45008b0049eb43cec6f5d"
|
||||
dependencies = [
|
||||
"async-attributes",
|
||||
"async-channel",
|
||||
"async-channel 1.9.0",
|
||||
"async-global-executor",
|
||||
"async-io",
|
||||
"async-lock",
|
||||
"async-lock 2.8.0",
|
||||
"crossbeam-utils",
|
||||
"futures-channel",
|
||||
"futures-core",
|
||||
"futures-io",
|
||||
"futures-lite",
|
||||
"futures-lite 1.13.0",
|
||||
"gloo-timers",
|
||||
"kv-log-macro",
|
||||
"log",
|
||||
@ -660,16 +684,16 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "blocking"
|
||||
version = "1.4.1"
|
||||
version = "1.5.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8c36a4d0d48574b3dd360b4b7d95cc651d2b6557b6402848a27d4b228a473e2a"
|
||||
checksum = "6a37913e8dc4ddcc604f0c6d3bf2887c995153af3611de9e23c352b44c1b9118"
|
||||
dependencies = [
|
||||
"async-channel",
|
||||
"async-lock",
|
||||
"async-channel 2.1.0",
|
||||
"async-lock 3.1.1",
|
||||
"async-task",
|
||||
"fastrand 2.0.1",
|
||||
"futures-io",
|
||||
"futures-lite",
|
||||
"futures-lite 2.0.1",
|
||||
"piper",
|
||||
"tracing",
|
||||
]
|
||||
@ -769,7 +793,7 @@ version = "0.9.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8eebd66744a15ded14960ab4ccdbfb51ad3b81f51f3f04a80adac98c985396c9"
|
||||
dependencies = [
|
||||
"hashbrown 0.14.2",
|
||||
"hashbrown",
|
||||
"stacker",
|
||||
]
|
||||
|
||||
@ -781,9 +805,9 @@ checksum = "cca491388666e04d7248af3f60f0c40cfb0991c72205595d7c396e3510207d1a"
|
||||
|
||||
[[package]]
|
||||
name = "clap"
|
||||
version = "4.4.7"
|
||||
version = "4.4.8"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ac495e00dcec98c83465d5ad66c5c4fabd652fd6686e7c6269b117e729a6f17b"
|
||||
checksum = "2275f18819641850fa26c89acc84d465c1bf91ce57bc2748b28c420473352f64"
|
||||
dependencies = [
|
||||
"clap_builder",
|
||||
"clap_derive",
|
||||
@ -791,9 +815,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "clap_builder"
|
||||
version = "4.4.7"
|
||||
version = "4.4.8"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c77ed9a32a62e6ca27175d00d29d05ca32e396ea1eb5fb01d8256b669cec7663"
|
||||
checksum = "07cdf1b148b25c1e1f7a42225e30a0d99a615cd4637eae7365548dd4529b95bc"
|
||||
dependencies = [
|
||||
"anstream",
|
||||
"anstyle",
|
||||
@ -1011,7 +1035,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "978747c1d849a7d2ee5e8adc0159961c48fb7e5db2f06af6723b80123bb53856"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"hashbrown 0.14.2",
|
||||
"hashbrown",
|
||||
"lock_api",
|
||||
"once_cell",
|
||||
"parking_lot_core",
|
||||
@ -1110,9 +1134,9 @@ checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5"
|
||||
|
||||
[[package]]
|
||||
name = "errno"
|
||||
version = "0.3.6"
|
||||
version = "0.3.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7c18ee0ed65a5f1f81cac6b1d213b69c35fa47d4252ad41f1486dbd8226fe36e"
|
||||
checksum = "f258a7194e7f7c2a7837a8913aeab7fd8c383457034fa20ce4dd3dcb813e8eb8"
|
||||
dependencies = [
|
||||
"libc",
|
||||
"windows-sys",
|
||||
@ -1135,6 +1159,27 @@ version = "2.5.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0206175f82b8d6bf6652ff7d71a1e27fd2e4efde587fd368662814d6ec1d9ce0"
|
||||
|
||||
[[package]]
|
||||
name = "event-listener"
|
||||
version = "3.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d93877bcde0eb80ca09131a08d23f0a5c18a620b01db137dba666d18cd9b30c2"
|
||||
dependencies = [
|
||||
"concurrent-queue",
|
||||
"parking",
|
||||
"pin-project-lite",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "event-listener-strategy"
|
||||
version = "0.3.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d96b852f1345da36d551b9473fa1e2b1eb5c5195585c6c018118bc92a8d91160"
|
||||
dependencies = [
|
||||
"event-listener 3.1.0",
|
||||
"pin-project-lite",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "faccess"
|
||||
version = "0.2.4"
|
||||
@ -1163,14 +1208,14 @@ checksum = "25cbce373ec4653f1a01a31e8a5e5ec0c622dc27ff9c4e6606eefef5cbbed4a5"
|
||||
|
||||
[[package]]
|
||||
name = "ffplayout"
|
||||
version = "0.20.0"
|
||||
version = "0.20.1"
|
||||
dependencies = [
|
||||
"chrono",
|
||||
"clap",
|
||||
"crossbeam-channel",
|
||||
"ffplayout-lib",
|
||||
"futures",
|
||||
"itertools",
|
||||
"itertools 0.12.0",
|
||||
"notify",
|
||||
"notify-debouncer-full",
|
||||
"rand",
|
||||
@ -1185,7 +1230,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "ffplayout-api"
|
||||
version = "0.20.0"
|
||||
version = "0.20.1"
|
||||
dependencies = [
|
||||
"actix-files",
|
||||
"actix-multipart",
|
||||
@ -1224,10 +1269,11 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "ffplayout-lib"
|
||||
version = "0.20.0"
|
||||
version = "0.20.1"
|
||||
dependencies = [
|
||||
"chrono",
|
||||
"crossbeam-channel",
|
||||
"derive_more",
|
||||
"ffprobe",
|
||||
"file-rotate",
|
||||
"lettre",
|
||||
@ -1413,6 +1459,20 @@ dependencies = [
|
||||
"waker-fn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "futures-lite"
|
||||
version = "2.0.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d3831c2651acb5177cbd83943f3d9c8912c5ad03c76afcc0e9511ba568ec5ebb"
|
||||
dependencies = [
|
||||
"fastrand 2.0.1",
|
||||
"futures-core",
|
||||
"futures-io",
|
||||
"memchr",
|
||||
"parking",
|
||||
"pin-project-lite",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "futures-macro"
|
||||
version = "0.3.29"
|
||||
@ -1501,9 +1561,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "h2"
|
||||
version = "0.3.21"
|
||||
version = "0.3.22"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "91fc23aa11be92976ef4729127f1a74adf36d8436f7816b185d18df956790833"
|
||||
checksum = "4d6250322ef6e60f93f9a2162799302cd6f68f79f6e5d85c8c16f14d1d958178"
|
||||
dependencies = [
|
||||
"bytes",
|
||||
"fnv",
|
||||
@ -1511,19 +1571,13 @@ dependencies = [
|
||||
"futures-sink",
|
||||
"futures-util",
|
||||
"http",
|
||||
"indexmap 1.9.3",
|
||||
"indexmap",
|
||||
"slab",
|
||||
"tokio",
|
||||
"tokio-util",
|
||||
"tracing",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "hashbrown"
|
||||
version = "0.12.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888"
|
||||
|
||||
[[package]]
|
||||
name = "hashbrown"
|
||||
version = "0.14.2"
|
||||
@ -1540,7 +1594,7 @@ version = "0.8.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e8094feaf31ff591f651a2664fb9cfd92bba7a60ce3197265e9482ebe753c8f7"
|
||||
dependencies = [
|
||||
"hashbrown 0.14.2",
|
||||
"hashbrown",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -1593,9 +1647,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "http"
|
||||
version = "0.2.9"
|
||||
version = "0.2.11"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bd6effc99afb63425aff9b05836f029929e345a6148a14b7ecd5ab67af944482"
|
||||
checksum = "8947b1a6fad4393052c7ba1f4cd97bed3e953a95c79c92ad9b051a04611d9fbb"
|
||||
dependencies = [
|
||||
"bytes",
|
||||
"fnv",
|
||||
@ -1708,16 +1762,6 @@ dependencies = [
|
||||
"unicode-normalization",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "indexmap"
|
||||
version = "1.9.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bd070e393353796e801d209ad339e89596eb4c8d430d18ede6a1cced8fafbd99"
|
||||
dependencies = [
|
||||
"autocfg",
|
||||
"hashbrown 0.12.3",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "indexmap"
|
||||
version = "2.1.0"
|
||||
@ -1725,7 +1769,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d530e1a18b1cb4c484e6e34556a0d948706958449fca0cab753d649f2bce3d1f"
|
||||
dependencies = [
|
||||
"equivalent",
|
||||
"hashbrown 0.14.2",
|
||||
"hashbrown",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -1783,6 +1827,15 @@ dependencies = [
|
||||
"either",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "itertools"
|
||||
version = "0.12.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "25db6b064527c5d482d0423354fcd07a89a2dfe07b67892e62411946db7f07b0"
|
||||
dependencies = [
|
||||
"either",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "itoa"
|
||||
version = "1.0.9"
|
||||
@ -2593,13 +2646,13 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "rpassword"
|
||||
version = "7.2.0"
|
||||
version = "7.3.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6678cf63ab3491898c0d021b493c94c9b221d91295294a2a5746eacbe5928322"
|
||||
checksum = "80472be3c897911d0137b2d2b9055faf6eeac5b14e324073d83bc17b191d7e3f"
|
||||
dependencies = [
|
||||
"libc",
|
||||
"rtoolbox",
|
||||
"winapi",
|
||||
"windows-sys",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -2624,12 +2677,12 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "rtoolbox"
|
||||
version = "0.0.1"
|
||||
version = "0.0.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "034e22c514f5c0cb8a10ff341b9b048b5ceb21591f31c8f44c43b960f9b3524a"
|
||||
checksum = "c247d24e63230cdb56463ae328478bd5eac8b8faa8c69461a77e8e323afac90e"
|
||||
dependencies = [
|
||||
"libc",
|
||||
"winapi",
|
||||
"windows-sys",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -2663,9 +2716,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "rustix"
|
||||
version = "0.38.21"
|
||||
version = "0.38.25"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2b426b0506e5d50a7d8dafcf2e81471400deb602392c7dd110815afb4eaf02a3"
|
||||
checksum = "dc99bc2d4f1fed22595588a013687477aedf3cdcfb26558c559edb67b4d9b22e"
|
||||
dependencies = [
|
||||
"bitflags 2.4.1",
|
||||
"errno",
|
||||
@ -2676,9 +2729,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "rustls"
|
||||
version = "0.21.8"
|
||||
version = "0.21.9"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "446e14c5cda4f3f30fe71863c34ec70f5ac79d6087097ad0bb433e1be5edf04c"
|
||||
checksum = "629648aced5775d558af50b2b4c7b02983a04b312126d45eeead26e7caa498b9"
|
||||
dependencies = [
|
||||
"log",
|
||||
"ring",
|
||||
@ -2688,9 +2741,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "rustls-pemfile"
|
||||
version = "1.0.3"
|
||||
version = "1.0.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2d3987094b1d07b653b7dfdc3f70ce9a1da9c51ac18c1b06b662e4f9a0e9f4b2"
|
||||
checksum = "1c74cae0a4cf6ccbbf5f359f08efdf8ee7e1dc532573bf0db71968cb56b1448c"
|
||||
dependencies = [
|
||||
"base64",
|
||||
]
|
||||
@ -2810,7 +2863,7 @@ version = "0.9.27"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3cc7a1570e38322cfe4154732e5110f887ea57e22b76f4bfd32b5bdd3368666c"
|
||||
dependencies = [
|
||||
"indexmap 2.1.0",
|
||||
"indexmap",
|
||||
"itoa",
|
||||
"ryu",
|
||||
"serde",
|
||||
@ -2887,9 +2940,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "signature"
|
||||
version = "2.1.0"
|
||||
version = "2.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5e1788eed21689f9cf370582dfc467ef36ed9c707f073528ddafa8d83e3b8500"
|
||||
checksum = "77549399552de45a898a580c1b41d445bf730df867cc44e6c0233bbc4b8329de"
|
||||
dependencies = [
|
||||
"digest",
|
||||
"rand_core",
|
||||
@ -2930,9 +2983,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "smallvec"
|
||||
version = "1.11.1"
|
||||
version = "1.11.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "942b4a808e05215192e39f4ab80813e599068285906cc91aa64f923db842bd5a"
|
||||
checksum = "4dccd0940a2dcdf68d092b8cbab7dc0ad8fa938bf95787e1b916b0e3d0e8e970"
|
||||
|
||||
[[package]]
|
||||
name = "socket2"
|
||||
@ -2985,7 +3038,7 @@ version = "0.2.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6b7b278788e7be4d0d29c0f39497a0eef3fba6bbc8e70d8bf7fde46edeaa9e85"
|
||||
dependencies = [
|
||||
"itertools",
|
||||
"itertools 0.11.0",
|
||||
"nom",
|
||||
"unicode_categories",
|
||||
]
|
||||
@ -3017,7 +3070,7 @@ dependencies = [
|
||||
"crossbeam-queue",
|
||||
"dotenvy",
|
||||
"either",
|
||||
"event-listener",
|
||||
"event-listener 2.5.3",
|
||||
"futures-channel",
|
||||
"futures-core",
|
||||
"futures-intrusive",
|
||||
@ -3025,7 +3078,7 @@ dependencies = [
|
||||
"futures-util",
|
||||
"hashlink",
|
||||
"hex",
|
||||
"indexmap 2.1.0",
|
||||
"indexmap",
|
||||
"log",
|
||||
"memchr",
|
||||
"once_cell",
|
||||
@ -3298,7 +3351,7 @@ dependencies = [
|
||||
"cfg-if",
|
||||
"fastrand 2.0.1",
|
||||
"redox_syscall 0.4.1",
|
||||
"rustix 0.38.21",
|
||||
"rustix 0.38.25",
|
||||
"windows-sys",
|
||||
]
|
||||
|
||||
@ -3313,7 +3366,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "tests"
|
||||
version = "0.20.0"
|
||||
version = "0.20.1"
|
||||
dependencies = [
|
||||
"chrono",
|
||||
"crossbeam-channel",
|
||||
@ -3416,9 +3469,9 @@ checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20"
|
||||
|
||||
[[package]]
|
||||
name = "tokio"
|
||||
version = "1.33.0"
|
||||
version = "1.34.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4f38200e3ef7995e5ef13baec2f432a6da0aa9ac495b2c0e8f3b7eec2c92d653"
|
||||
checksum = "d0c014766411e834f7af5b8f4cf46257aab4036ca95e9d2c144a10f59ad6f5b9"
|
||||
dependencies = [
|
||||
"backtrace",
|
||||
"bytes",
|
||||
@ -3435,9 +3488,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "tokio-macros"
|
||||
version = "2.1.0"
|
||||
version = "2.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "630bdcf245f78637c13ec01ffae6187cca34625e8c63150d424b59e55af2675e"
|
||||
checksum = "5b8a1e28f2deaa14e508979454cb3a223b10b938b45af148bc0986de36f1923b"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
@ -3602,9 +3655,9 @@ checksum = "711b9620af191e0cdc7468a8d14e709c3dcdb115b36f838e601583af800a370a"
|
||||
|
||||
[[package]]
|
||||
name = "uuid"
|
||||
version = "1.5.0"
|
||||
version = "1.6.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "88ad59a7560b41a70d191093a945f0b87bc1deeda46fb237479708a1d6b6cdfc"
|
||||
checksum = "5e395fcf16a7a3d8127ec99782007af141946b4795001f876d54fb0d55978560"
|
||||
dependencies = [
|
||||
"getrandom",
|
||||
]
|
||||
@ -3864,18 +3917,18 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "zerocopy"
|
||||
version = "0.7.25"
|
||||
version = "0.7.26"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8cd369a67c0edfef15010f980c3cbe45d7f651deac2cd67ce097cd801de16557"
|
||||
checksum = "e97e415490559a91254a2979b4829267a57d2fcd741a98eee8b722fb57289aa0"
|
||||
dependencies = [
|
||||
"zerocopy-derive",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "zerocopy-derive"
|
||||
version = "0.7.25"
|
||||
version = "0.7.26"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c2f140bda219a26ccc0cdb03dba58af72590c53b22642577d88a927bc5c87d6b"
|
||||
checksum = "dd7e48ccf166952882ca8bd778a43502c64f33bf94c12ebe2a7f08e5a0f6689f"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
@ -3884,9 +3937,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "zeroize"
|
||||
version = "1.6.0"
|
||||
version = "1.7.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2a0956f1ba7c7909bfb66c2e9e4124ab6f6482560f6628b5aaeba39207c9aad9"
|
||||
checksum = "525b4ec142c6b68a2d10f01f7bbf6755599ca3f81ea53b8431b7dd348f5fdb2d"
|
||||
|
||||
[[package]]
|
||||
name = "zeromq"
|
||||
|
@ -4,7 +4,7 @@ default-members = ["ffplayout-api", "ffplayout-engine", "tests"]
|
||||
resolver = "2"
|
||||
|
||||
[workspace.package]
|
||||
version = "0.20.0"
|
||||
version = "0.20.1"
|
||||
license = "GPL-3.0"
|
||||
repository = "https://github.com/ffplayout/ffplayout"
|
||||
authors = ["Jonathan Baecker <jonbae77@gmail.com>"]
|
||||
|
@ -35,6 +35,8 @@ logging:
|
||||
false will set log timestamps to UTC. Path to /var/log/ only if you run this
|
||||
program as daemon. 'level' can be DEBUG, INFO, WARNING, ERROR.
|
||||
'ffmpeg_level' can be info, warning, error.
|
||||
'detect_silence' logs an error message if the audio line is silent for 15
|
||||
seconds during the validation process.
|
||||
log_to_file: true
|
||||
backup_count: 7
|
||||
local_time: true
|
||||
@ -43,6 +45,7 @@ logging:
|
||||
level: DEBUG
|
||||
ffmpeg_level: error
|
||||
ingest_level: warning
|
||||
detect_silence: false
|
||||
|
||||
processing:
|
||||
help_text: Default processing for all clips, to have them unique. Mode can be playlist
|
||||
|
19
docs/api.md
19
docs/api.md
@ -38,6 +38,19 @@ curl -X GET 'http://127.0.0.1:8787/api/user' -H 'Content-Type: application/json'
|
||||
-H 'Authorization: Bearer <TOKEN>'
|
||||
```
|
||||
|
||||
**Get User by ID**
|
||||
|
||||
```BASH
|
||||
curl -X GET 'http://127.0.0.1:8787/api/user/2' -H 'Content-Type: application/json' \
|
||||
-H 'Authorization: Bearer <TOKEN>'
|
||||
```
|
||||
|
||||
|
||||
```BASH
|
||||
curl -X GET 'http://127.0.0.1:8787/api/users' -H 'Content-Type: application/json' \
|
||||
-H 'Authorization: Bearer <TOKEN>'
|
||||
```
|
||||
|
||||
**Update current User**
|
||||
|
||||
```BASH
|
||||
@ -53,6 +66,12 @@ curl -X POST 'http://127.0.0.1:8787/api/user/' -H 'Content-Type: application/jso
|
||||
-H 'Authorization: Bearer <TOKEN>'
|
||||
```
|
||||
|
||||
|
||||
```BASH
|
||||
curl -X GET 'http://127.0.0.1:8787/api/user/2' -H 'Content-Type: application/json' \
|
||||
-H 'Authorization: Bearer <TOKEN>'
|
||||
```
|
||||
|
||||
#### ffpapi Settings
|
||||
|
||||
**Get Settings from Channel**
|
||||
|
@ -232,6 +232,45 @@ async fn get_user(
|
||||
}
|
||||
}
|
||||
|
||||
/// **Get User by ID**
|
||||
///
|
||||
/// ```BASH
|
||||
/// curl -X GET 'http://127.0.0.1:8787/api/user/2' -H 'Content-Type: application/json' \
|
||||
/// -H 'Authorization: Bearer <TOKEN>'
|
||||
/// ```
|
||||
#[get("/user/{name}")]
|
||||
#[has_any_role("Role::Admin", type = "Role")]
|
||||
async fn get_user_by_name(
|
||||
pool: web::Data<Pool<Sqlite>>,
|
||||
name: web::Path<String>,
|
||||
) -> Result<impl Responder, ServiceError> {
|
||||
match handles::select_user(&pool.into_inner(), &name).await {
|
||||
Ok(user) => Ok(web::Json(user)),
|
||||
Err(e) => {
|
||||
error!("{e}");
|
||||
Err(ServiceError::InternalServerError)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// **Get all User**
|
||||
///
|
||||
/// ```BASH
|
||||
/// curl -X GET 'http://127.0.0.1:8787/api/users' -H 'Content-Type: application/json' \
|
||||
/// -H 'Authorization: Bearer <TOKEN>'
|
||||
/// ```
|
||||
#[get("/users")]
|
||||
#[has_any_role("Role::Admin", type = "Role")]
|
||||
async fn get_users(pool: web::Data<Pool<Sqlite>>) -> Result<impl Responder, ServiceError> {
|
||||
match handles::select_users(&pool.into_inner()).await {
|
||||
Ok(users) => Ok(web::Json(users)),
|
||||
Err(e) => {
|
||||
error!("{e}");
|
||||
Err(ServiceError::InternalServerError)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// **Update current User**
|
||||
///
|
||||
/// ```BASH
|
||||
@ -245,11 +284,16 @@ async fn update_user(
|
||||
id: web::Path<i32>,
|
||||
user: web::ReqData<LoginUser>,
|
||||
data: web::Json<User>,
|
||||
role: AuthDetails<Role>,
|
||||
) -> Result<impl Responder, ServiceError> {
|
||||
if id.into_inner() == user.id {
|
||||
if *id == user.id || role.has_role(&Role::Admin) {
|
||||
let mut fields = String::new();
|
||||
|
||||
if let Some(mail) = data.mail.clone() {
|
||||
if !fields.is_empty() {
|
||||
fields.push_str(", ");
|
||||
}
|
||||
|
||||
fields.push_str(format!("mail = '{mail}'").as_str());
|
||||
}
|
||||
|
||||
@ -266,7 +310,7 @@ async fn update_user(
|
||||
fields.push_str(format!("password = '{password_hash}', salt = '{salt}'").as_str());
|
||||
}
|
||||
|
||||
if handles::update_user(&pool.into_inner(), user.id, fields)
|
||||
if handles::update_user(&pool.into_inner(), *id, fields)
|
||||
.await
|
||||
.is_ok()
|
||||
{
|
||||
@ -301,6 +345,27 @@ async fn add_user(
|
||||
}
|
||||
}
|
||||
|
||||
// **Delete User**
|
||||
///
|
||||
/// ```BASH
|
||||
/// curl -X GET 'http://127.0.0.1:8787/api/user/2' -H 'Content-Type: application/json' \
|
||||
/// -H 'Authorization: Bearer <TOKEN>'
|
||||
/// ```
|
||||
#[delete("/user/{name}")]
|
||||
#[has_any_role("Role::Admin", type = "Role")]
|
||||
async fn remove_user(
|
||||
pool: web::Data<Pool<Sqlite>>,
|
||||
name: web::Path<String>,
|
||||
) -> Result<impl Responder, ServiceError> {
|
||||
match handles::delete_user(&pool.into_inner(), &name).await {
|
||||
Ok(_) => return Ok("Delete user success"),
|
||||
Err(e) => {
|
||||
error!("{e}");
|
||||
Err(ServiceError::InternalServerError)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// #### ffpapi Settings
|
||||
///
|
||||
/// **Get Settings from Channel**
|
||||
|
@ -228,6 +228,18 @@ pub async fn select_user(conn: &Pool<Sqlite>, user: &str) -> Result<User, sqlx::
|
||||
sqlx::query_as(query).bind(user).fetch_one(conn).await
|
||||
}
|
||||
|
||||
pub async fn select_user_by_id(conn: &Pool<Sqlite>, id: i32) -> Result<User, sqlx::Error> {
|
||||
let query = "SELECT id, mail, username, role_id FROM user WHERE id = $1";
|
||||
|
||||
sqlx::query_as(query).bind(id).fetch_one(conn).await
|
||||
}
|
||||
|
||||
pub async fn select_users(conn: &Pool<Sqlite>) -> Result<Vec<User>, sqlx::Error> {
|
||||
let query = "SELECT id, username FROM user";
|
||||
|
||||
sqlx::query_as(query).fetch_all(conn).await
|
||||
}
|
||||
|
||||
pub async fn insert_user(
|
||||
conn: &Pool<Sqlite>,
|
||||
user: User,
|
||||
@ -260,6 +272,15 @@ pub async fn update_user(
|
||||
sqlx::query(&query).bind(id).execute(conn).await
|
||||
}
|
||||
|
||||
pub async fn delete_user(
|
||||
conn: &Pool<Sqlite>,
|
||||
name: &str,
|
||||
) -> Result<SqliteQueryResult, sqlx::Error> {
|
||||
let query = "DELETE FROM user WHERE username = $1;";
|
||||
|
||||
sqlx::query(query).bind(name).execute(conn).await
|
||||
}
|
||||
|
||||
pub async fn select_presets(conn: &Pool<Sqlite>, id: i32) -> Result<Vec<TextPreset>, sqlx::Error> {
|
||||
let query = "SELECT * FROM presets WHERE channel_id = $1";
|
||||
|
||||
|
@ -10,6 +10,7 @@ pub struct User {
|
||||
#[serde(skip_deserializing)]
|
||||
pub id: i32,
|
||||
#[sqlx(default)]
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub mail: Option<String>,
|
||||
pub username: String,
|
||||
#[sqlx(default)]
|
||||
|
@ -109,6 +109,9 @@ async fn main() -> std::io::Result<()> {
|
||||
.wrap(auth)
|
||||
.service(add_user)
|
||||
.service(get_user)
|
||||
.service(get_user_by_name)
|
||||
.service(get_users)
|
||||
.service(remove_user)
|
||||
.service(get_playout_config)
|
||||
.service(update_playout_config)
|
||||
.service(add_preset)
|
||||
|
@ -16,7 +16,7 @@ chrono = { version = "0.4", default-features = false, features = ["clock", "std"
|
||||
clap = { version = "4.3", features = ["derive"] }
|
||||
crossbeam-channel = "0.5"
|
||||
futures = "0.3"
|
||||
itertools = "0.11"
|
||||
itertools = "0.12"
|
||||
notify = "6.0"
|
||||
notify-debouncer-full = { version = "*", default-features = false }
|
||||
rand = "0.8"
|
||||
|
@ -19,9 +19,9 @@ use ffplayout::{
|
||||
};
|
||||
|
||||
use ffplayout_lib::utils::{
|
||||
folder::fill_filler_list, generate_playlist, get_date, import::import_file, init_logging,
|
||||
is_remote, send_mail, test_tcp_port, validate_ffmpeg, validate_playlist, JsonPlaylist,
|
||||
OutputMode::*, PlayerControl, PlayoutStatus, ProcessControl,
|
||||
errors::ProcError, folder::fill_filler_list, generate_playlist, get_date, import::import_file,
|
||||
init_logging, is_remote, send_mail, test_tcp_port, validate_ffmpeg, validate_playlist,
|
||||
JsonPlaylist, OutputMode::*, PlayerControl, PlayoutStatus, ProcessControl,
|
||||
};
|
||||
|
||||
#[cfg(debug_assertions)]
|
||||
@ -44,7 +44,7 @@ struct StatusData {
|
||||
/// we save the time difference, so we stay in sync.
|
||||
///
|
||||
/// When file not exists we create it, and when it exists we get its values.
|
||||
fn status_file(stat_file: &str, playout_stat: &PlayoutStatus) {
|
||||
fn status_file(stat_file: &str, playout_stat: &PlayoutStatus) -> Result<(), ProcError> {
|
||||
debug!("Start ffplayout v{VERSION}, status file path: <b><magenta>{stat_file}</></b>");
|
||||
|
||||
if !PathBuf::from(stat_file).exists() {
|
||||
@ -53,23 +53,19 @@ fn status_file(stat_file: &str, playout_stat: &PlayoutStatus) {
|
||||
"date": String::new(),
|
||||
});
|
||||
|
||||
let json: String = serde_json::to_string(&data).expect("Serialize status data failed");
|
||||
let json: String = serde_json::to_string(&data)?;
|
||||
if let Err(e) = fs::write(stat_file, json) {
|
||||
error!("Unable to write to status file <b><magenta>{stat_file}</></b>: {e}");
|
||||
};
|
||||
} else {
|
||||
let stat_file = File::options()
|
||||
.read(true)
|
||||
.write(false)
|
||||
.open(stat_file)
|
||||
.expect("Could not open status file");
|
||||
|
||||
let data: StatusData =
|
||||
serde_json::from_reader(stat_file).expect("Could not read status file.");
|
||||
let stat_file = File::options().read(true).write(false).open(stat_file)?;
|
||||
let data: StatusData = serde_json::from_reader(stat_file)?;
|
||||
|
||||
*playout_stat.time_shift.lock().unwrap() = data.time_shift;
|
||||
*playout_stat.date.lock().unwrap() = data.date;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Set fake time for debugging.
|
||||
@ -88,14 +84,14 @@ fn fake_time(args: &Args) {
|
||||
/// Main function.
|
||||
/// Here we check the command line arguments and start the player.
|
||||
/// We also start a JSON RPC server if enabled.
|
||||
fn main() {
|
||||
fn main() -> Result<(), ProcError> {
|
||||
let args = get_args();
|
||||
|
||||
// use fake time function only in debugging mode
|
||||
#[cfg(debug_assertions)]
|
||||
fake_time(&args);
|
||||
|
||||
let mut config = get_config(args.clone());
|
||||
let mut config = get_config(args.clone())?;
|
||||
let play_control = PlayerControl::new();
|
||||
let playout_stat = PlayoutStatus::new();
|
||||
let proc_control = ProcessControl::new();
|
||||
@ -116,7 +112,7 @@ fn main() {
|
||||
}
|
||||
|
||||
let logging = init_logging(&config, Some(proc_ctl1), Some(messages.clone()));
|
||||
CombinedLogger::init(logging).unwrap();
|
||||
CombinedLogger::init(logging)?;
|
||||
|
||||
if let Err(e) = validate_ffmpeg(&mut config) {
|
||||
error!("{e}");
|
||||
@ -126,7 +122,7 @@ fn main() {
|
||||
let config_clone1 = config.clone();
|
||||
let config_clone2 = config.clone();
|
||||
|
||||
if ![2, 4, 6, 8].contains(&config.processing.audio_channels) {
|
||||
if !matches!(config.processing.audio_channels, 2 | 4 | 6 | 8) {
|
||||
error!(
|
||||
"Encoding {} channel(s) is not allowed. Only 2, 4, 6 and 8 channels are supported!",
|
||||
config.processing.audio_channels
|
||||
@ -179,16 +175,9 @@ fn main() {
|
||||
let f = File::options()
|
||||
.read(true)
|
||||
.write(false)
|
||||
.open(&playlist_path)
|
||||
.expect("Could not open json playlist file.");
|
||||
.open(&playlist_path)?;
|
||||
|
||||
let playlist: JsonPlaylist = match serde_json::from_reader(f) {
|
||||
Ok(p) => p,
|
||||
Err(e) => {
|
||||
error!("{e:?}");
|
||||
exit(1)
|
||||
}
|
||||
};
|
||||
let playlist: JsonPlaylist = serde_json::from_reader(f)?;
|
||||
|
||||
validate_playlist(playlist, Arc::new(AtomicBool::new(false)), config);
|
||||
|
||||
@ -205,7 +194,7 @@ fn main() {
|
||||
thread::spawn(move || run_server(config_clone1, play_ctl1, play_stat, proc_ctl2));
|
||||
}
|
||||
|
||||
status_file(&config.general.stat_file, &playout_stat);
|
||||
status_file(&config.general.stat_file, &playout_stat)?;
|
||||
|
||||
debug!(
|
||||
"Use config: <b><magenta>{}</></b>",
|
||||
@ -233,4 +222,6 @@ fn main() {
|
||||
}
|
||||
|
||||
drop(msg);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
@ -1,7 +1,6 @@
|
||||
use std::{
|
||||
fs::File,
|
||||
path::{Path, PathBuf},
|
||||
process::exit,
|
||||
};
|
||||
|
||||
use regex::Regex;
|
||||
@ -15,23 +14,22 @@ pub use arg_parse::Args;
|
||||
use ffplayout_lib::{
|
||||
filter::Filters,
|
||||
utils::{
|
||||
config::Template, get_sec, parse_log_level_filter, sec_to_time, time_to_sec, Media,
|
||||
OutputMode::*, PlayoutConfig, PlayoutStatus, ProcessMode::*,
|
||||
config::Template, errors::ProcError, get_sec, parse_log_level_filter, sec_to_time,
|
||||
time_to_sec, Media, OutputMode::*, PlayoutConfig, PlayoutStatus, ProcessMode::*,
|
||||
},
|
||||
vec_strings,
|
||||
};
|
||||
|
||||
/// Read command line arguments, and override the config with them.
|
||||
pub fn get_config(args: Args) -> PlayoutConfig {
|
||||
pub fn get_config(args: Args) -> Result<PlayoutConfig, ProcError> {
|
||||
let cfg_path = match args.channel {
|
||||
Some(c) => {
|
||||
let path = PathBuf::from(format!("/etc/ffplayout/{c}.yml"));
|
||||
|
||||
if !path.is_file() {
|
||||
println!(
|
||||
return Err(ProcError::Custom(format!(
|
||||
"Config file \"{c}\" under \"/etc/ffplayout/\" not found.\n\nCheck arguments!"
|
||||
);
|
||||
exit(1)
|
||||
)));
|
||||
}
|
||||
|
||||
Some(path)
|
||||
@ -53,17 +51,9 @@ pub fn get_config(args: Args) -> PlayoutConfig {
|
||||
let f = File::options()
|
||||
.read(true)
|
||||
.write(false)
|
||||
.open(template_file)
|
||||
.expect("JSON template file");
|
||||
.open(template_file)?;
|
||||
|
||||
let mut template: Template = match serde_json::from_reader(f) {
|
||||
Ok(p) => p,
|
||||
Err(e) => {
|
||||
error!("Template file not readable! {e}");
|
||||
|
||||
exit(1)
|
||||
}
|
||||
};
|
||||
let mut template: Template = serde_json::from_reader(f)?;
|
||||
|
||||
template.sources.sort_by(|d1, d2| d1.start.cmp(&d2.start));
|
||||
|
||||
@ -135,7 +125,7 @@ pub fn get_config(args: Args) -> PlayoutConfig {
|
||||
config.processing.volume = volume;
|
||||
}
|
||||
|
||||
config
|
||||
Ok(config)
|
||||
}
|
||||
|
||||
/// Format ingest and HLS logging output
|
||||
|
@ -1 +1 @@
|
||||
Subproject commit 03200acf1b426dc8b64dc702ab907e4184efd667
|
||||
Subproject commit e060cbe885a961f295fb4f6f12bc2c86b5f94192
|
@ -11,6 +11,7 @@ edition.workspace = true
|
||||
[dependencies]
|
||||
chrono = { version = "0.4", default-features = false, features = ["clock", "serde", "std"] }
|
||||
crossbeam-channel = "0.5"
|
||||
derive_more = "0.99"
|
||||
ffprobe = "0.3"
|
||||
file-rotate = "0.7"
|
||||
lettre = { version = "0.11", features = ["builder", "rustls-tls", "smtp-transport"], default-features = false }
|
||||
|
@ -222,6 +222,8 @@ pub struct Logging {
|
||||
pub level: LevelFilter,
|
||||
pub ffmpeg_level: String,
|
||||
pub ingest_level: Option<String>,
|
||||
#[serde(default)]
|
||||
pub detect_silence: bool,
|
||||
}
|
||||
|
||||
#[derive(Debug, Serialize, Deserialize, Clone)]
|
||||
|
47
lib/src/utils/errors.rs
Normal file
47
lib/src/utils/errors.rs
Normal file
@ -0,0 +1,47 @@
|
||||
use std::io;
|
||||
|
||||
use derive_more::Display;
|
||||
|
||||
#[derive(Debug, Display)]
|
||||
pub enum ProcError {
|
||||
#[display(fmt = "Failed to spawn ffmpeg/ffprobe. {}", _0)]
|
||||
CommandSpawn(io::Error),
|
||||
#[display(fmt = "Failed to read data from ffmpeg/ffprobe. {}", _0)]
|
||||
IO(io::Error),
|
||||
#[display(fmt = "{}", _0)]
|
||||
Custom(String),
|
||||
#[display(fmt = "Regex compile error {}", _0)]
|
||||
Regex(String),
|
||||
#[display(fmt = "Thread error {}", _0)]
|
||||
Thread(String),
|
||||
}
|
||||
|
||||
impl From<std::io::Error> for ProcError {
|
||||
fn from(err: std::io::Error) -> Self {
|
||||
Self::CommandSpawn(err)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<regex::Error> for ProcError {
|
||||
fn from(err: regex::Error) -> Self {
|
||||
Self::Regex(err.to_string())
|
||||
}
|
||||
}
|
||||
|
||||
impl From<log::SetLoggerError> for ProcError {
|
||||
fn from(err: log::SetLoggerError) -> Self {
|
||||
Self::Custom(err.to_string())
|
||||
}
|
||||
}
|
||||
|
||||
impl From<serde_json::Error> for ProcError {
|
||||
fn from(err: serde_json::Error) -> Self {
|
||||
Self::Custom(err.to_string())
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Box<dyn std::any::Any + std::marker::Send>> for ProcError {
|
||||
fn from(err: Box<dyn std::any::Any + std::marker::Send>) -> Self {
|
||||
Self::Thread(format!("{err:?}"))
|
||||
}
|
||||
}
|
@ -1,45 +1,60 @@
|
||||
use std::{
|
||||
io::{BufRead, BufReader, Error, ErrorKind},
|
||||
io::{BufRead, BufReader},
|
||||
process::{Command, Stdio},
|
||||
sync::{
|
||||
atomic::{AtomicBool, Ordering},
|
||||
Arc,
|
||||
},
|
||||
time::Instant,
|
||||
};
|
||||
|
||||
use regex::Regex;
|
||||
use simplelog::*;
|
||||
|
||||
use crate::filter::FilterType::Audio;
|
||||
use crate::utils::{
|
||||
loop_image, sec_to_time, seek_and_length, valid_source, vec_strings, JsonPlaylist, Media,
|
||||
OutputMode::Null, PlayoutConfig, FFMPEG_IGNORE_ERRORS, IMAGE_FORMAT,
|
||||
errors::ProcError, loop_image, sec_to_time, seek_and_length, valid_source, vec_strings,
|
||||
JsonPlaylist, Media, OutputMode::Null, PlayoutConfig, FFMPEG_IGNORE_ERRORS, IMAGE_FORMAT,
|
||||
};
|
||||
|
||||
/// check if ffmpeg can read the file and apply filter to it.
|
||||
/// Validate a single media file.
|
||||
///
|
||||
/// - Check if file exists
|
||||
/// - Check if ffmpeg can read the file
|
||||
/// - Check if Metadata exists
|
||||
/// - Check if the file is not silent
|
||||
fn check_media(
|
||||
mut node: Media,
|
||||
pos: usize,
|
||||
begin: f64,
|
||||
config: &PlayoutConfig,
|
||||
) -> Result<(), Error> {
|
||||
let mut enc_cmd = vec_strings!["-hide_banner", "-nostats", "-v", "level+error"];
|
||||
) -> Result<(), ProcError> {
|
||||
let mut enc_cmd = vec_strings!["-hide_banner", "-nostats", "-v", "level+info"];
|
||||
let mut error_list = vec![];
|
||||
let mut config = config.clone();
|
||||
config.out.mode = Null;
|
||||
|
||||
let mut process_length = 0.1;
|
||||
|
||||
if config.logging.detect_silence {
|
||||
process_length = 15.0;
|
||||
let seek = node.duration / 4.0;
|
||||
|
||||
// Seek in file, to prevent false silence detection on intros without sound.
|
||||
enc_cmd.append(&mut vec_strings!["-ss", seek]);
|
||||
}
|
||||
|
||||
node.add_probe();
|
||||
|
||||
if node.probe.clone().and_then(|p| p.format).is_none() {
|
||||
return Err(Error::new(
|
||||
ErrorKind::Other,
|
||||
format!(
|
||||
"No Metadata at position <yellow>{pos}</> {}, from file <b><magenta>\"{}\"</></b>",
|
||||
sec_to_time(begin),
|
||||
node.source
|
||||
),
|
||||
));
|
||||
return Err(ProcError::Custom(format!(
|
||||
"No Metadata at position <yellow>{pos}</> {}, from file <b><magenta>\"{}\"</></b>",
|
||||
sec_to_time(begin),
|
||||
node.source
|
||||
)));
|
||||
}
|
||||
|
||||
// take care, that no seek and length command is added.
|
||||
// Take care, that no seek and length command is added.
|
||||
node.seek = 0.0;
|
||||
node.out = node.duration;
|
||||
|
||||
@ -60,24 +75,30 @@ fn check_media(
|
||||
let mut filter = node.filter.unwrap_or_default();
|
||||
|
||||
if filter.cmd().len() > 1 {
|
||||
filter.cmd()[1] = filter.cmd()[1].replace("realtime=speed=1", "null")
|
||||
let re_clean = Regex::new(r"volume=[0-9.]+")?;
|
||||
|
||||
filter.audio_chain = re_clean
|
||||
.replace_all(&filter.audio_chain, "anull")
|
||||
.to_string();
|
||||
}
|
||||
|
||||
filter.add_filter("silencedetect=n=-30dB", 0, Audio);
|
||||
|
||||
enc_cmd.append(&mut node.cmd.unwrap_or_default());
|
||||
enc_cmd.append(&mut filter.cmd());
|
||||
enc_cmd.append(&mut filter.map());
|
||||
enc_cmd.append(&mut vec_strings!["-t", "0.1", "-f", "null", "-"]);
|
||||
enc_cmd.append(&mut vec_strings!["-t", process_length, "-f", "null", "-"]);
|
||||
|
||||
let mut enc_proc = match Command::new("ffmpeg")
|
||||
.args(enc_cmd.clone())
|
||||
let mut enc_proc = Command::new("ffmpeg")
|
||||
.args(enc_cmd)
|
||||
.stderr(Stdio::piped())
|
||||
.spawn()
|
||||
{
|
||||
Err(e) => return Err(e),
|
||||
Ok(proc) => proc,
|
||||
};
|
||||
.spawn()?;
|
||||
|
||||
let enc_err = BufReader::new(enc_proc.stderr.take().unwrap());
|
||||
let mut silence_start = 0.0;
|
||||
let mut silence_end = 0.0;
|
||||
let re_start = Regex::new(r"silence_start: ([0-9]+:)?([0-9.]+)")?;
|
||||
let re_end = Regex::new(r"silence_end: ([0-9]+:)?([0-9.]+)")?;
|
||||
|
||||
for line in enc_err.lines() {
|
||||
let line = line?;
|
||||
@ -91,11 +112,25 @@ fn check_media(
|
||||
error_list.push(log_line);
|
||||
}
|
||||
}
|
||||
|
||||
if config.logging.detect_silence {
|
||||
if let Some(start) = re_start.captures(&line).and_then(|c| c.get(2)) {
|
||||
silence_start = start.as_str().parse::<f32>().unwrap_or_default();
|
||||
}
|
||||
|
||||
if let Some(end) = re_end.captures(&line).and_then(|c| c.get(2)) {
|
||||
silence_end = end.as_str().parse::<f32>().unwrap_or_default() + 0.5;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if silence_end - silence_start > process_length {
|
||||
error_list.push("Audio is totally silent!".to_string());
|
||||
}
|
||||
|
||||
if !error_list.is_empty() {
|
||||
error!(
|
||||
"<bright black>[Validator]</> ffmpeg error on position <yellow>{pos}</> - {}: <b><magenta>{}</></b>:\n{}",
|
||||
"<bright black>[Validator]</> ffmpeg error on position <yellow>{pos}</> - {}: <b><magenta>{}</></b>: {}",
|
||||
sec_to_time(begin),
|
||||
node.source,
|
||||
error_list.join("\n")
|
||||
@ -136,6 +171,7 @@ pub fn validate_playlist(
|
||||
length += begin;
|
||||
|
||||
debug!("Validate playlist from: <yellow>{date}</>");
|
||||
let timer = Instant::now();
|
||||
|
||||
for (index, item) in playlist.program.iter().enumerate() {
|
||||
if is_terminated.load(Ordering::SeqCst) {
|
||||
@ -172,5 +208,5 @@ pub fn validate_playlist(
|
||||
);
|
||||
}
|
||||
|
||||
debug!("Validation done...");
|
||||
debug!("Validation done, in {:.3?} ...", timer.elapsed(),);
|
||||
}
|
||||
|
@ -23,6 +23,7 @@ use simplelog::*;
|
||||
|
||||
pub mod config;
|
||||
pub mod controller;
|
||||
pub mod errors;
|
||||
pub mod folder;
|
||||
pub mod generator;
|
||||
pub mod import;
|
||||
@ -373,7 +374,7 @@ pub fn modified_time(path: &str) -> Option<String> {
|
||||
|
||||
/// Convert a formatted time string to seconds.
|
||||
pub fn time_to_sec(time_str: &str) -> f64 {
|
||||
if ["now", "", "none"].contains(&time_str) || !time_str.contains(':') {
|
||||
if matches!(time_str, "now" | "" | "none") || !time_str.contains(':') {
|
||||
return get_sec();
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user