Merge pull request #239 from jb-alvarado/master
This commit is contained in:
commit
dc3a8b68e2
132
Cargo.lock
generated
132
Cargo.lock
generated
@ -217,7 +217,7 @@ dependencies = [
|
|||||||
"serde_urlencoded",
|
"serde_urlencoded",
|
||||||
"smallvec",
|
"smallvec",
|
||||||
"socket2",
|
"socket2",
|
||||||
"time 0.3.16",
|
"time 0.3.17",
|
||||||
"url",
|
"url",
|
||||||
]
|
]
|
||||||
|
|
||||||
@ -341,22 +341,22 @@ version = "1.7.1"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "e14485364214912d3b19cc3435dde4df66065127f05fa0d75c712f36f12c2f28"
|
checksum = "e14485364214912d3b19cc3435dde4df66065127f05fa0d75c712f36f12c2f28"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"concurrent-queue",
|
"concurrent-queue 1.2.4",
|
||||||
"event-listener",
|
"event-listener",
|
||||||
"futures-core",
|
"futures-core",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "async-executor"
|
name = "async-executor"
|
||||||
version = "1.4.1"
|
version = "1.5.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "871f9bb5e0a22eeb7e8cf16641feb87c9dc67032ccf8ff49e772eb9941d3a965"
|
checksum = "17adb73da160dfb475c183343c8cccd80721ea5a605d3eb57125f0a7b7a92d0b"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
|
"async-lock",
|
||||||
"async-task",
|
"async-task",
|
||||||
"concurrent-queue",
|
"concurrent-queue 2.0.0",
|
||||||
"fastrand",
|
"fastrand",
|
||||||
"futures-lite",
|
"futures-lite",
|
||||||
"once_cell",
|
|
||||||
"slab",
|
"slab",
|
||||||
]
|
]
|
||||||
|
|
||||||
@ -383,7 +383,7 @@ checksum = "e8121296a9f05be7f34aa4196b1747243b3b62e048bb7906f644f3fbfc490cf7"
|
|||||||
dependencies = [
|
dependencies = [
|
||||||
"async-lock",
|
"async-lock",
|
||||||
"autocfg",
|
"autocfg",
|
||||||
"concurrent-queue",
|
"concurrent-queue 1.2.4",
|
||||||
"futures-lite",
|
"futures-lite",
|
||||||
"libc",
|
"libc",
|
||||||
"log",
|
"log",
|
||||||
@ -451,9 +451,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "asynchronous-codec"
|
name = "asynchronous-codec"
|
||||||
version = "0.6.0"
|
version = "0.6.1"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "f0de5164e5edbf51c45fb8c2d9664ae1c095cce1b265ecf7569093c0d66ef690"
|
checksum = "06a0daa378f5fd10634e44b0a29b2a87b890657658e072a30d6f26e57ddee182"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bytes",
|
"bytes",
|
||||||
"futures-sink",
|
"futures-sink",
|
||||||
@ -514,9 +514,9 @@ checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "blake2"
|
name = "blake2"
|
||||||
version = "0.10.4"
|
version = "0.10.5"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "b9cf849ee05b2ee5fba5e36f97ff8ec2533916700fc0758d40d92136a42f3388"
|
checksum = "b12e5fd123190ce1c2e559308a94c9bacad77907d4c6005d9e58fe1a0689e55e"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"digest",
|
"digest",
|
||||||
]
|
]
|
||||||
@ -594,9 +594,9 @@ checksum = "ec8a7b6a70fde80372154c65702f00a0f56f3e1c36abbc6c440484be248856db"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "bytestring"
|
name = "bytestring"
|
||||||
version = "1.1.0"
|
version = "1.2.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "86b6a75fd3048808ef06af5cd79712be8111960adaf89d90250974b38fc3928a"
|
checksum = "f7f83e57d9154148e355404702e2694463241880b939570d7c97c014da7a69a1"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bytes",
|
"bytes",
|
||||||
]
|
]
|
||||||
@ -609,9 +609,9 @@ checksum = "c1db59621ec70f09c5e9b597b220c7a2b43611f4710dc03ceb8748637775692c"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "cc"
|
name = "cc"
|
||||||
version = "1.0.74"
|
version = "1.0.76"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "581f5dba903aac52ea3feb5ec4810848460ee833876f1f9b0fdeab1f19091574"
|
checksum = "76a284da2e6fe2092f2353e51713435363112dfd60030e22add80be333fb928f"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"jobserver",
|
"jobserver",
|
||||||
]
|
]
|
||||||
@ -630,9 +630,9 @@ checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "chrono"
|
name = "chrono"
|
||||||
version = "0.4.22"
|
version = "0.4.23"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "bfd4d1b31faaa3a89d7934dbded3111da0d2ef28e3ebccdb4f0179f5929d1ef1"
|
checksum = "16b0a3d9ed01224b22057780a37bb8c5dbfe1be8ba48678e7bf57ec4b385411f"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"iana-time-zone",
|
"iana-time-zone",
|
||||||
"js-sys",
|
"js-sys",
|
||||||
@ -701,6 +701,15 @@ dependencies = [
|
|||||||
"cache-padded",
|
"cache-padded",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "concurrent-queue"
|
||||||
|
version = "2.0.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "bd7bef69dc86e3c610e4e7aed41035e2a7ed12e72dd7530f61327a6579a4390b"
|
||||||
|
dependencies = [
|
||||||
|
"crossbeam-utils",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "convert_case"
|
name = "convert_case"
|
||||||
version = "0.4.0"
|
version = "0.4.0"
|
||||||
@ -714,7 +723,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||||||
checksum = "344adc371239ef32293cb1c4fe519592fcf21206c79c02854320afcdf3ab4917"
|
checksum = "344adc371239ef32293cb1c4fe519592fcf21206c79c02854320afcdf3ab4917"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"percent-encoding",
|
"percent-encoding",
|
||||||
"time 0.3.16",
|
"time 0.3.17",
|
||||||
"version_check",
|
"version_check",
|
||||||
]
|
]
|
||||||
|
|
||||||
@ -818,9 +827,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "cxx"
|
name = "cxx"
|
||||||
version = "1.0.80"
|
version = "1.0.82"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "6b7d4e43b25d3c994662706a1d4fcfc32aaa6afd287502c111b237093bb23f3a"
|
checksum = "d4a41a86530d0fe7f5d9ea779916b7cadd2d4f9add748b99c2c029cbbdfaf453"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"cc",
|
"cc",
|
||||||
"cxxbridge-flags",
|
"cxxbridge-flags",
|
||||||
@ -830,9 +839,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "cxx-build"
|
name = "cxx-build"
|
||||||
version = "1.0.80"
|
version = "1.0.82"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "84f8829ddc213e2c1368e51a2564c552b65a8cb6a28f31e576270ac81d5e5827"
|
checksum = "06416d667ff3e3ad2df1cd8cd8afae5da26cf9cec4d0825040f88b5ca659a2f0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"cc",
|
"cc",
|
||||||
"codespan-reporting",
|
"codespan-reporting",
|
||||||
@ -845,15 +854,15 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "cxxbridge-flags"
|
name = "cxxbridge-flags"
|
||||||
version = "1.0.80"
|
version = "1.0.82"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "e72537424b474af1460806647c41d4b6d35d09ef7fe031c5c2fa5766047cc56a"
|
checksum = "820a9a2af1669deeef27cb271f476ffd196a2c4b6731336011e0ba63e2c7cf71"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "cxxbridge-macro"
|
name = "cxxbridge-macro"
|
||||||
version = "1.0.80"
|
version = "1.0.82"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "309e4fb93eed90e1e14bea0da16b209f81813ba9fc7830c20ed151dd7bc0a4d7"
|
checksum = "a08a6e2fcc370a089ad3b4aaf54db3b1b4cee38ddabce5896b33eb693275f470"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
@ -888,9 +897,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "digest"
|
name = "digest"
|
||||||
version = "0.10.5"
|
version = "0.10.6"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "adfbc57365a37acbd2ebf2b64d7e69bb766e2fea813521ed536f5d0520dcf86c"
|
checksum = "8168378f4e5023e7218c89c891c0fd8ecdb5e5e4f18cb78f38cf245dd021e76f"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"block-buffer",
|
"block-buffer",
|
||||||
"crypto-common",
|
"crypto-common",
|
||||||
@ -962,7 +971,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "ffplayout"
|
name = "ffplayout"
|
||||||
version = "0.16.3"
|
version = "0.16.4"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"chrono",
|
"chrono",
|
||||||
"clap",
|
"clap",
|
||||||
@ -982,7 +991,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "ffplayout-api"
|
name = "ffplayout-api"
|
||||||
version = "0.7.1"
|
version = "0.8.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"actix-files",
|
"actix-files",
|
||||||
"actix-multipart",
|
"actix-multipart",
|
||||||
@ -999,6 +1008,7 @@ dependencies = [
|
|||||||
"jsonwebtoken",
|
"jsonwebtoken",
|
||||||
"once_cell",
|
"once_cell",
|
||||||
"rand",
|
"rand",
|
||||||
|
"regex",
|
||||||
"relative-path",
|
"relative-path",
|
||||||
"reqwest",
|
"reqwest",
|
||||||
"rpassword",
|
"rpassword",
|
||||||
@ -1012,7 +1022,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "ffplayout-lib"
|
name = "ffplayout-lib"
|
||||||
version = "0.16.3"
|
version = "0.16.4"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"chrono",
|
"chrono",
|
||||||
"crossbeam-channel",
|
"crossbeam-channel",
|
||||||
@ -1031,7 +1041,7 @@ dependencies = [
|
|||||||
"serde_yaml",
|
"serde_yaml",
|
||||||
"shlex",
|
"shlex",
|
||||||
"simplelog",
|
"simplelog",
|
||||||
"time 0.3.16",
|
"time 0.3.17",
|
||||||
"walkdir",
|
"walkdir",
|
||||||
"winapi 0.3.9",
|
"winapi 0.3.9",
|
||||||
]
|
]
|
||||||
@ -1430,9 +1440,9 @@ checksum = "c4a1e36c821dbe04574f602848a19f742f4fb3c98d40449f11bcad18d6b17421"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "hyper"
|
name = "hyper"
|
||||||
version = "0.14.22"
|
version = "0.14.23"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "abfba89e19b959ca163c7752ba59d737c1ceea53a5d31a149c805446fc958064"
|
checksum = "034711faac9d2166cb1baf1a2fb0b60b1f277f8492fd72176c17f3515e1abd3c"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bytes",
|
"bytes",
|
||||||
"futures-channel",
|
"futures-channel",
|
||||||
@ -1512,9 +1522,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "indexmap"
|
name = "indexmap"
|
||||||
version = "1.9.1"
|
version = "1.9.2"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "10a35a97730320ffe8e2d410b5d3b69279b98d2c14bdb8b70ea89ecf7888d41e"
|
checksum = "1885e79c1fc4b10f0e172c475f458b7f7b93061064d98c3293e98c5ba0c8b399"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"autocfg",
|
"autocfg",
|
||||||
"hashbrown",
|
"hashbrown",
|
||||||
@ -1560,9 +1570,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "ipnet"
|
name = "ipnet"
|
||||||
version = "2.5.0"
|
version = "2.5.1"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "879d54834c8c76457ef4293a689b2a8c59b076067ad77b15efafbb05f92a592b"
|
checksum = "f88c5561171189e69df9d98bcf18fd5f9558300f7ea7b801eb8a0fd748bd8745"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "itertools"
|
name = "itertools"
|
||||||
@ -2062,9 +2072,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "os_str_bytes"
|
name = "os_str_bytes"
|
||||||
version = "6.3.1"
|
version = "6.4.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "3baf96e39c5359d2eb0dd6ccb42c62b91d9678aa68160d261b9e0ccbf9e9dea9"
|
checksum = "7b5bf27447411e9ee3ff51186bf7a08e16c341efdde93f4d823e8844429bed7e"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "paris"
|
name = "paris"
|
||||||
@ -2212,9 +2222,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "ppv-lite86"
|
name = "ppv-lite86"
|
||||||
version = "0.2.16"
|
version = "0.2.17"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "eb9f9e6e233e5c4a35559a617bf40a4ec447db2e84c20b55a6f83167b7e57872"
|
checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "proc-macro-error"
|
name = "proc-macro-error"
|
||||||
@ -2305,9 +2315,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "regex"
|
name = "regex"
|
||||||
version = "1.6.0"
|
version = "1.7.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "4c4eb3267174b8c6c2f654116623910a0fef09c4753f8dd83db29c48a0df988b"
|
checksum = "e076559ef8e241f2ae3479e36f97bd5741c0330689e217ad51ce2c76808b868a"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"aho-corasick",
|
"aho-corasick",
|
||||||
"memchr",
|
"memchr",
|
||||||
@ -2316,9 +2326,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "regex-syntax"
|
name = "regex-syntax"
|
||||||
version = "0.6.27"
|
version = "0.6.28"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "a3f87b73ce11b1619a3c6332f45341e0047173771e8b8b73f87bfeefb7b56244"
|
checksum = "456c603be3e8d448b072f410900c09faf164fbce2d480456f50eea6e25f9c848"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "relative-path"
|
name = "relative-path"
|
||||||
@ -2337,9 +2347,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "reqwest"
|
name = "reqwest"
|
||||||
version = "0.11.12"
|
version = "0.11.13"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "431949c384f4e2ae07605ccaa56d1d9d2ecdb5cadd4f9577ccfab29f2e5149fc"
|
checksum = "68cc60575865c7831548863cc02356512e3f1dc2f3f82cb837d7fc4cc8f3c97c"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"base64",
|
"base64",
|
||||||
"bytes",
|
"bytes",
|
||||||
@ -2506,9 +2516,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "serde_json"
|
name = "serde_json"
|
||||||
version = "1.0.87"
|
version = "1.0.88"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "6ce777b7b150d76b9cf60d28b55f5847135a003f7d7350c6be7a773508ce7d45"
|
checksum = "8e8b3801309262e8184d9687fb697586833e939767aea0dda89f5a8e650e8bd7"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"itoa",
|
"itoa",
|
||||||
"ryu",
|
"ryu",
|
||||||
@ -2585,7 +2595,7 @@ dependencies = [
|
|||||||
"num-bigint",
|
"num-bigint",
|
||||||
"num-traits",
|
"num-traits",
|
||||||
"thiserror",
|
"thiserror",
|
||||||
"time 0.3.16",
|
"time 0.3.17",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@ -2597,7 +2607,7 @@ dependencies = [
|
|||||||
"log",
|
"log",
|
||||||
"paris",
|
"paris",
|
||||||
"termcolor",
|
"termcolor",
|
||||||
"time 0.3.16",
|
"time 0.3.17",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@ -2813,7 +2823,7 @@ dependencies = [
|
|||||||
"serde_yaml",
|
"serde_yaml",
|
||||||
"shlex",
|
"shlex",
|
||||||
"simplelog",
|
"simplelog",
|
||||||
"time 0.3.16",
|
"time 0.3.17",
|
||||||
"walkdir",
|
"walkdir",
|
||||||
]
|
]
|
||||||
|
|
||||||
@ -2856,9 +2866,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "time"
|
name = "time"
|
||||||
version = "0.3.16"
|
version = "0.3.17"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "0fab5c8b9980850e06d92ddbe3ab839c062c801f3927c0fb8abd6fc8e918fbca"
|
checksum = "a561bf4617eebd33bca6434b988f39ed798e527f51a1e797d0ee4f61c0a38376"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"itoa",
|
"itoa",
|
||||||
"libc",
|
"libc",
|
||||||
@ -2876,9 +2886,9 @@ checksum = "2e153e1f1acaef8acc537e68b44906d2db6436e2b35ac2c6b42640fff91f00fd"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "time-macros"
|
name = "time-macros"
|
||||||
version = "0.2.5"
|
version = "0.2.6"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "65bb801831d812c562ae7d2bfb531f26e66e4e1f6b17307ba4149c5064710e5b"
|
checksum = "d967f99f534ca7e495c575c62638eebc2898a8c84c119b89e250477bc4ba16b2"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"time-core",
|
"time-core",
|
||||||
]
|
]
|
||||||
@ -2900,9 +2910,9 @@ checksum = "cda74da7e1a664f795bb1f8a87ec406fb89a02522cf6e50620d016add6dbbf5c"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "tokio"
|
name = "tokio"
|
||||||
version = "1.21.2"
|
version = "1.22.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "a9e03c497dc955702ba729190dc4aac6f2a0ce97f913e5b1b5912fc5039d9099"
|
checksum = "d76ce4a75fb488c605c54bf610f221cea8b0dafb53333c1a67e8ee199dcd2ae3"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"autocfg",
|
"autocfg",
|
||||||
"bytes",
|
"bytes",
|
||||||
@ -3088,9 +3098,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "uuid"
|
name = "uuid"
|
||||||
version = "1.2.1"
|
version = "1.2.2"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "feb41e78f93363bb2df8b0e86a2ca30eed7806ea16ea0c790d757cf93f79be83"
|
checksum = "422ee0de9031b5b948b97a8fc04e3aa35230001a722ddd27943e0be31564ce4c"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"getrandom",
|
"getrandom",
|
||||||
]
|
]
|
||||||
|
@ -20,7 +20,7 @@ Check the [releases](https://github.com/ffplayout/ffplayout/releases/latest) for
|
|||||||
- send emails with error message
|
- send emails with error message
|
||||||
- overlay a logo
|
- overlay a logo
|
||||||
- overlay text, controllable through [ffplayout-frontend](https://github.com/ffplayout/ffplayout-frontend) (needs ffmpeg with libzmq and enabled JSON RPC server)
|
- overlay text, controllable through [ffplayout-frontend](https://github.com/ffplayout/ffplayout-frontend) (needs ffmpeg with libzmq and enabled JSON RPC server)
|
||||||
- EBU R128 loudness normalization (single pass)
|
- EBU R128 loudness normalization (single pass) (experimental *)
|
||||||
- loop playlist infinitely
|
- loop playlist infinitely
|
||||||
- [remote source](/docs/remote_source.md)
|
- [remote source](/docs/remote_source.md)
|
||||||
- trim and fade the last clip, to get full 24 hours
|
- trim and fade the last clip, to get full 24 hours
|
||||||
|
30
docs/api.md
30
docs/api.md
@ -284,7 +284,7 @@ curl -X DELETE http://127.0.0.1:8787/api/playlist/1/2022-06-20
|
|||||||
**Read Log Life**
|
**Read Log Life**
|
||||||
|
|
||||||
```BASH
|
```BASH
|
||||||
curl -X Get http://127.0.0.1:8787/api/log/1
|
curl -X GET http://127.0.0.1:8787/api/log/1
|
||||||
-H 'Content-Type: application/json' -H 'Authorization: Bearer <TOKEN>'
|
-H 'Content-Type: application/json' -H 'Authorization: Bearer <TOKEN>'
|
||||||
```
|
```
|
||||||
|
|
||||||
@ -321,7 +321,7 @@ curl -X POST http://127.0.0.1:8787/api/file/1/remove/ -H 'Content-Type: applicat
|
|||||||
**Upload File**
|
**Upload File**
|
||||||
|
|
||||||
```BASH
|
```BASH
|
||||||
curl -X POST http://127.0.0.1:8787/api/file/1/upload/ -H 'Authorization: Bearer <TOKEN>'
|
curl -X PUT http://127.0.0.1:8787/api/file/1/upload/ -H 'Authorization: Bearer <TOKEN>'
|
||||||
-F "file=@file.mp4"
|
-F "file=@file.mp4"
|
||||||
```
|
```
|
||||||
|
|
||||||
@ -331,6 +331,30 @@ Import text/m3u file and convert it to a playlist
|
|||||||
lines with leading "#" will be ignore
|
lines with leading "#" will be ignore
|
||||||
|
|
||||||
```BASH
|
```BASH
|
||||||
curl -X POST http://127.0.0.1:8787/api/file/1/import/ -H 'Authorization: Bearer <TOKEN>'
|
curl -X PUT http://127.0.0.1:8787/api/file/1/import/ -H 'Authorization: Bearer <TOKEN>'
|
||||||
-F "file=@list.m3u"
|
-F "file=@list.m3u"
|
||||||
```
|
```
|
||||||
|
|
||||||
|
**Program info**
|
||||||
|
|
||||||
|
Get program infos about given date, or current day
|
||||||
|
|
||||||
|
Examples:
|
||||||
|
|
||||||
|
* get program from current day
|
||||||
|
```BASH
|
||||||
|
curl -X GET http://127.0.0.1:8787/api/program/1/ -H 'Authorization: Bearer <TOKEN>'
|
||||||
|
```
|
||||||
|
|
||||||
|
* get a program range between two dates
|
||||||
|
```BASH
|
||||||
|
curl -X GET http://127.0.0.1:8787/api/program/1/?start_after=2022-11-13T12:00:00&start_before=2022-11-20T11:59:59 \
|
||||||
|
-H 'Authorization: Bearer <TOKEN>'
|
||||||
|
```
|
||||||
|
|
||||||
|
* get program from give day
|
||||||
|
```BASH
|
||||||
|
curl -X GET http://127.0.0.1:8787/api/program/1/?start_after=2022-11-13T10:00:00 \
|
||||||
|
-H 'Authorization: Bearer <TOKEN>'
|
||||||
|
```
|
||||||
|
|
||||||
|
@ -4,7 +4,7 @@ description = "Rest API for ffplayout"
|
|||||||
license = "GPL-3.0"
|
license = "GPL-3.0"
|
||||||
authors = ["Jonathan Baecker jonbae77@gmail.com"]
|
authors = ["Jonathan Baecker jonbae77@gmail.com"]
|
||||||
readme = "README.md"
|
readme = "README.md"
|
||||||
version = "0.7.1"
|
version = "0.8.0"
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
@ -23,6 +23,7 @@ futures-util = { version = "0.3", default-features = false, features = ["std"] }
|
|||||||
jsonwebtoken = "8"
|
jsonwebtoken = "8"
|
||||||
once_cell = "1.10"
|
once_cell = "1.10"
|
||||||
rand = "0.8"
|
rand = "0.8"
|
||||||
|
regex = "1"
|
||||||
relative-path = "1.6"
|
relative-path = "1.6"
|
||||||
reqwest = { version = "0.11", features = ["blocking", "json"] }
|
reqwest = { version = "0.11", features = ["blocking", "json"] }
|
||||||
rpassword = "6.0"
|
rpassword = "6.0"
|
||||||
|
@ -17,8 +17,11 @@ use argon2::{
|
|||||||
password_hash::{rand_core::OsRng, PasswordHash, SaltString},
|
password_hash::{rand_core::OsRng, PasswordHash, SaltString},
|
||||||
Argon2, PasswordHasher, PasswordVerifier,
|
Argon2, PasswordHasher, PasswordVerifier,
|
||||||
};
|
};
|
||||||
|
use chrono::{DateTime, Datelike, Duration, Local, NaiveDateTime, TimeZone, Utc};
|
||||||
|
use regex::Regex;
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use simplelog::*;
|
use simplelog::*;
|
||||||
|
use sqlx::{Pool, Sqlite};
|
||||||
|
|
||||||
use crate::auth::{create_jwt, Claims};
|
use crate::auth::{create_jwt, Claims};
|
||||||
use crate::db::{
|
use crate::db::{
|
||||||
@ -33,10 +36,16 @@ use crate::utils::{
|
|||||||
browser, create_directory, remove_file_or_folder, rename_file, upload, MoveObject,
|
browser, create_directory, remove_file_or_folder, rename_file, upload, MoveObject,
|
||||||
PathObject,
|
PathObject,
|
||||||
},
|
},
|
||||||
|
naive_date_time_from_str,
|
||||||
playlist::{delete_playlist, generate_playlist, read_playlist, write_playlist},
|
playlist::{delete_playlist, generate_playlist, read_playlist, write_playlist},
|
||||||
playout_config, read_log_file, read_playout_config, Role,
|
playout_config, read_log_file, read_playout_config, Role,
|
||||||
};
|
};
|
||||||
use ffplayout_lib::utils::{import::import_file, JsonPlaylist, PlayoutConfig};
|
use ffplayout_lib::{
|
||||||
|
utils::{
|
||||||
|
get_date_range, import::import_file, sec_to_time, time_to_sec, JsonPlaylist, PlayoutConfig,
|
||||||
|
},
|
||||||
|
vec_strings,
|
||||||
|
};
|
||||||
|
|
||||||
#[derive(Serialize)]
|
#[derive(Serialize)]
|
||||||
struct ResponseObj<T> {
|
struct ResponseObj<T> {
|
||||||
@ -71,6 +80,42 @@ pub struct ImportObj {
|
|||||||
date: String,
|
date: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Deserialize, Clone)]
|
||||||
|
pub struct ProgramObj {
|
||||||
|
#[serde(default = "time_after", deserialize_with = "naive_date_time_from_str")]
|
||||||
|
start_after: NaiveDateTime,
|
||||||
|
#[serde(default = "time_before", deserialize_with = "naive_date_time_from_str")]
|
||||||
|
start_before: NaiveDateTime,
|
||||||
|
}
|
||||||
|
|
||||||
|
fn time_after() -> NaiveDateTime {
|
||||||
|
let today = Utc::now();
|
||||||
|
|
||||||
|
chrono::Local
|
||||||
|
.with_ymd_and_hms(today.year(), today.month(), today.day(), 0, 0, 0)
|
||||||
|
.unwrap()
|
||||||
|
.naive_local()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn time_before() -> NaiveDateTime {
|
||||||
|
let today = Utc::now();
|
||||||
|
|
||||||
|
chrono::Local
|
||||||
|
.with_ymd_and_hms(today.year(), today.month(), today.day(), 23, 59, 59)
|
||||||
|
.unwrap()
|
||||||
|
.naive_local()
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Serialize)]
|
||||||
|
struct ProgramItem {
|
||||||
|
source: String,
|
||||||
|
start: String,
|
||||||
|
r#in: f64,
|
||||||
|
out: f64,
|
||||||
|
duration: f64,
|
||||||
|
category: String,
|
||||||
|
}
|
||||||
|
|
||||||
/// #### User Handling
|
/// #### User Handling
|
||||||
///
|
///
|
||||||
/// **Login**
|
/// **Login**
|
||||||
@ -90,8 +135,9 @@ pub struct ImportObj {
|
|||||||
/// }
|
/// }
|
||||||
/// ```
|
/// ```
|
||||||
#[post("/auth/login/")]
|
#[post("/auth/login/")]
|
||||||
pub async fn login(credentials: web::Json<User>) -> impl Responder {
|
pub async fn login(pool: web::Data<Pool<Sqlite>>, credentials: web::Json<User>) -> impl Responder {
|
||||||
match handles::select_login(&credentials.username).await {
|
let conn = pool.into_inner();
|
||||||
|
match handles::select_login(&conn, &credentials.username).await {
|
||||||
Ok(mut user) => {
|
Ok(mut user) => {
|
||||||
let pass = user.password.clone();
|
let pass = user.password.clone();
|
||||||
let hash = PasswordHash::new(&pass).unwrap();
|
let hash = PasswordHash::new(&pass).unwrap();
|
||||||
@ -102,7 +148,7 @@ pub async fn login(credentials: web::Json<User>) -> impl Responder {
|
|||||||
.verify_password(credentials.password.as_bytes(), &hash)
|
.verify_password(credentials.password.as_bytes(), &hash)
|
||||||
.is_ok()
|
.is_ok()
|
||||||
{
|
{
|
||||||
let role = handles::select_role(&user.role_id.unwrap_or_default())
|
let role = handles::select_role(&conn, &user.role_id.unwrap_or_default())
|
||||||
.await
|
.await
|
||||||
.unwrap_or_else(|_| "guest".to_string());
|
.unwrap_or_else(|_| "guest".to_string());
|
||||||
let claims = Claims::new(user.id, user.username.clone(), role.clone());
|
let claims = Claims::new(user.id, user.username.clone(), role.clone());
|
||||||
@ -152,8 +198,11 @@ pub async fn login(credentials: web::Json<User>) -> impl Responder {
|
|||||||
/// ```
|
/// ```
|
||||||
#[get("/user")]
|
#[get("/user")]
|
||||||
#[has_any_role("Role::Admin", "Role::User", type = "Role")]
|
#[has_any_role("Role::Admin", "Role::User", type = "Role")]
|
||||||
async fn get_user(user: web::ReqData<LoginUser>) -> Result<impl Responder, ServiceError> {
|
async fn get_user(
|
||||||
match handles::select_user(&user.username).await {
|
pool: web::Data<Pool<Sqlite>>,
|
||||||
|
user: web::ReqData<LoginUser>,
|
||||||
|
) -> Result<impl Responder, ServiceError> {
|
||||||
|
match handles::select_user(&pool.into_inner(), &user.username).await {
|
||||||
Ok(user) => Ok(web::Json(user)),
|
Ok(user) => Ok(web::Json(user)),
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
error!("{e}");
|
error!("{e}");
|
||||||
@ -171,6 +220,7 @@ async fn get_user(user: web::ReqData<LoginUser>) -> Result<impl Responder, Servi
|
|||||||
#[put("/user/{id}")]
|
#[put("/user/{id}")]
|
||||||
#[has_any_role("Role::Admin", "Role::User", type = "Role")]
|
#[has_any_role("Role::Admin", "Role::User", type = "Role")]
|
||||||
async fn update_user(
|
async fn update_user(
|
||||||
|
pool: web::Data<Pool<Sqlite>>,
|
||||||
id: web::Path<i32>,
|
id: web::Path<i32>,
|
||||||
user: web::ReqData<LoginUser>,
|
user: web::ReqData<LoginUser>,
|
||||||
data: web::Json<User>,
|
data: web::Json<User>,
|
||||||
@ -195,7 +245,10 @@ async fn update_user(
|
|||||||
fields.push_str(format!("password = '{}', salt = '{salt}'", password_hash).as_str());
|
fields.push_str(format!("password = '{}', salt = '{salt}'", password_hash).as_str());
|
||||||
}
|
}
|
||||||
|
|
||||||
if handles::update_user(user.id, fields).await.is_ok() {
|
if handles::update_user(&pool.into_inner(), user.id, fields)
|
||||||
|
.await
|
||||||
|
.is_ok()
|
||||||
|
{
|
||||||
return Ok("Update Success");
|
return Ok("Update Success");
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -214,8 +267,11 @@ async fn update_user(
|
|||||||
/// ```
|
/// ```
|
||||||
#[post("/user/")]
|
#[post("/user/")]
|
||||||
#[has_any_role("Role::Admin", type = "Role")]
|
#[has_any_role("Role::Admin", type = "Role")]
|
||||||
async fn add_user(data: web::Json<User>) -> Result<impl Responder, ServiceError> {
|
async fn add_user(
|
||||||
match handles::insert_user(data.into_inner()).await {
|
pool: web::Data<Pool<Sqlite>>,
|
||||||
|
data: web::Json<User>,
|
||||||
|
) -> Result<impl Responder, ServiceError> {
|
||||||
|
match handles::insert_user(&pool.into_inner(), data.into_inner()).await {
|
||||||
Ok(_) => Ok("Add User Success"),
|
Ok(_) => Ok("Add User Success"),
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
error!("{e}");
|
error!("{e}");
|
||||||
@ -247,8 +303,11 @@ async fn add_user(data: web::Json<User>) -> Result<impl Responder, ServiceError>
|
|||||||
/// ```
|
/// ```
|
||||||
#[get("/channel/{id}")]
|
#[get("/channel/{id}")]
|
||||||
#[has_any_role("Role::Admin", "Role::User", type = "Role")]
|
#[has_any_role("Role::Admin", "Role::User", type = "Role")]
|
||||||
async fn get_channel(id: web::Path<i32>) -> Result<impl Responder, ServiceError> {
|
async fn get_channel(
|
||||||
if let Ok(channel) = handles::select_channel(&id).await {
|
pool: web::Data<Pool<Sqlite>>,
|
||||||
|
id: web::Path<i32>,
|
||||||
|
) -> Result<impl Responder, ServiceError> {
|
||||||
|
if let Ok(channel) = handles::select_channel(&pool.into_inner(), &id).await {
|
||||||
return Ok(web::Json(channel));
|
return Ok(web::Json(channel));
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -262,8 +321,8 @@ async fn get_channel(id: web::Path<i32>) -> Result<impl Responder, ServiceError>
|
|||||||
/// ```
|
/// ```
|
||||||
#[get("/channels")]
|
#[get("/channels")]
|
||||||
#[has_any_role("Role::Admin", type = "Role")]
|
#[has_any_role("Role::Admin", type = "Role")]
|
||||||
async fn get_all_channels() -> Result<impl Responder, ServiceError> {
|
async fn get_all_channels(pool: web::Data<Pool<Sqlite>>) -> Result<impl Responder, ServiceError> {
|
||||||
if let Ok(channel) = handles::select_all_channels().await {
|
if let Ok(channel) = handles::select_all_channels(&pool.into_inner()).await {
|
||||||
return Ok(web::Json(channel));
|
return Ok(web::Json(channel));
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -281,10 +340,11 @@ async fn get_all_channels() -> Result<impl Responder, ServiceError> {
|
|||||||
#[patch("/channel/{id}")]
|
#[patch("/channel/{id}")]
|
||||||
#[has_any_role("Role::Admin", type = "Role")]
|
#[has_any_role("Role::Admin", type = "Role")]
|
||||||
async fn patch_channel(
|
async fn patch_channel(
|
||||||
|
pool: web::Data<Pool<Sqlite>>,
|
||||||
id: web::Path<i32>,
|
id: web::Path<i32>,
|
||||||
data: web::Json<Channel>,
|
data: web::Json<Channel>,
|
||||||
) -> Result<impl Responder, ServiceError> {
|
) -> Result<impl Responder, ServiceError> {
|
||||||
if handles::update_channel(*id, data.into_inner())
|
if handles::update_channel(&pool.into_inner(), *id, data.into_inner())
|
||||||
.await
|
.await
|
||||||
.is_ok()
|
.is_ok()
|
||||||
{
|
{
|
||||||
@ -305,8 +365,11 @@ async fn patch_channel(
|
|||||||
/// ```
|
/// ```
|
||||||
#[post("/channel/")]
|
#[post("/channel/")]
|
||||||
#[has_any_role("Role::Admin", type = "Role")]
|
#[has_any_role("Role::Admin", type = "Role")]
|
||||||
async fn add_channel(data: web::Json<Channel>) -> Result<impl Responder, ServiceError> {
|
async fn add_channel(
|
||||||
match create_channel(data.into_inner()).await {
|
pool: web::Data<Pool<Sqlite>>,
|
||||||
|
data: web::Json<Channel>,
|
||||||
|
) -> Result<impl Responder, ServiceError> {
|
||||||
|
match create_channel(&pool.into_inner(), data.into_inner()).await {
|
||||||
Ok(c) => Ok(web::Json(c)),
|
Ok(c) => Ok(web::Json(c)),
|
||||||
Err(e) => Err(e),
|
Err(e) => Err(e),
|
||||||
}
|
}
|
||||||
@ -319,8 +382,11 @@ async fn add_channel(data: web::Json<Channel>) -> Result<impl Responder, Service
|
|||||||
/// ```
|
/// ```
|
||||||
#[delete("/channel/{id}")]
|
#[delete("/channel/{id}")]
|
||||||
#[has_any_role("Role::Admin", type = "Role")]
|
#[has_any_role("Role::Admin", type = "Role")]
|
||||||
async fn remove_channel(id: web::Path<i32>) -> Result<impl Responder, ServiceError> {
|
async fn remove_channel(
|
||||||
if delete_channel(*id).await.is_ok() {
|
pool: web::Data<Pool<Sqlite>>,
|
||||||
|
id: web::Path<i32>,
|
||||||
|
) -> Result<impl Responder, ServiceError> {
|
||||||
|
if delete_channel(&pool.into_inner(), *id).await.is_ok() {
|
||||||
return Ok("Delete Channel Success");
|
return Ok("Delete Channel Success");
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -339,10 +405,11 @@ async fn remove_channel(id: web::Path<i32>) -> Result<impl Responder, ServiceErr
|
|||||||
#[get("/playout/config/{id}")]
|
#[get("/playout/config/{id}")]
|
||||||
#[has_any_role("Role::Admin", "Role::User", type = "Role")]
|
#[has_any_role("Role::Admin", "Role::User", type = "Role")]
|
||||||
async fn get_playout_config(
|
async fn get_playout_config(
|
||||||
|
pool: web::Data<Pool<Sqlite>>,
|
||||||
id: web::Path<i32>,
|
id: web::Path<i32>,
|
||||||
_details: AuthDetails<Role>,
|
_details: AuthDetails<Role>,
|
||||||
) -> Result<impl Responder, ServiceError> {
|
) -> Result<impl Responder, ServiceError> {
|
||||||
if let Ok(channel) = handles::select_channel(&id).await {
|
if let Ok(channel) = handles::select_channel(&pool.into_inner(), &id).await {
|
||||||
if let Ok(config) = read_playout_config(&channel.config_path) {
|
if let Ok(config) = read_playout_config(&channel.config_path) {
|
||||||
return Ok(web::Json(config));
|
return Ok(web::Json(config));
|
||||||
}
|
}
|
||||||
@ -360,10 +427,11 @@ async fn get_playout_config(
|
|||||||
#[put("/playout/config/{id}")]
|
#[put("/playout/config/{id}")]
|
||||||
#[has_any_role("Role::Admin", type = "Role")]
|
#[has_any_role("Role::Admin", type = "Role")]
|
||||||
async fn update_playout_config(
|
async fn update_playout_config(
|
||||||
|
pool: web::Data<Pool<Sqlite>>,
|
||||||
id: web::Path<i32>,
|
id: web::Path<i32>,
|
||||||
data: web::Json<PlayoutConfig>,
|
data: web::Json<PlayoutConfig>,
|
||||||
) -> Result<impl Responder, ServiceError> {
|
) -> Result<impl Responder, ServiceError> {
|
||||||
if let Ok(channel) = handles::select_channel(&id).await {
|
if let Ok(channel) = handles::select_channel(&pool.into_inner(), &id).await {
|
||||||
if let Ok(f) = std::fs::OpenOptions::new()
|
if let Ok(f) = std::fs::OpenOptions::new()
|
||||||
.write(true)
|
.write(true)
|
||||||
.truncate(true)
|
.truncate(true)
|
||||||
@ -392,8 +460,11 @@ async fn update_playout_config(
|
|||||||
/// ```
|
/// ```
|
||||||
#[get("/presets/{id}")]
|
#[get("/presets/{id}")]
|
||||||
#[has_any_role("Role::Admin", "Role::User", type = "Role")]
|
#[has_any_role("Role::Admin", "Role::User", type = "Role")]
|
||||||
async fn get_presets(id: web::Path<i32>) -> Result<impl Responder, ServiceError> {
|
async fn get_presets(
|
||||||
if let Ok(presets) = handles::select_presets(*id).await {
|
pool: web::Data<Pool<Sqlite>>,
|
||||||
|
id: web::Path<i32>,
|
||||||
|
) -> Result<impl Responder, ServiceError> {
|
||||||
|
if let Ok(presets) = handles::select_presets(&pool.into_inner(), *id).await {
|
||||||
return Ok(web::Json(presets));
|
return Ok(web::Json(presets));
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -411,10 +482,14 @@ async fn get_presets(id: web::Path<i32>) -> Result<impl Responder, ServiceError>
|
|||||||
#[put("/presets/{id}")]
|
#[put("/presets/{id}")]
|
||||||
#[has_any_role("Role::Admin", "Role::User", type = "Role")]
|
#[has_any_role("Role::Admin", "Role::User", type = "Role")]
|
||||||
async fn update_preset(
|
async fn update_preset(
|
||||||
|
pool: web::Data<Pool<Sqlite>>,
|
||||||
id: web::Path<i32>,
|
id: web::Path<i32>,
|
||||||
data: web::Json<TextPreset>,
|
data: web::Json<TextPreset>,
|
||||||
) -> Result<impl Responder, ServiceError> {
|
) -> Result<impl Responder, ServiceError> {
|
||||||
if handles::update_preset(&id, data.into_inner()).await.is_ok() {
|
if handles::update_preset(&pool.into_inner(), &id, data.into_inner())
|
||||||
|
.await
|
||||||
|
.is_ok()
|
||||||
|
{
|
||||||
return Ok("Update Success");
|
return Ok("Update Success");
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -431,8 +506,14 @@ async fn update_preset(
|
|||||||
/// ```
|
/// ```
|
||||||
#[post("/presets/")]
|
#[post("/presets/")]
|
||||||
#[has_any_role("Role::Admin", "Role::User", type = "Role")]
|
#[has_any_role("Role::Admin", "Role::User", type = "Role")]
|
||||||
async fn add_preset(data: web::Json<TextPreset>) -> Result<impl Responder, ServiceError> {
|
async fn add_preset(
|
||||||
if handles::insert_preset(data.into_inner()).await.is_ok() {
|
pool: web::Data<Pool<Sqlite>>,
|
||||||
|
data: web::Json<TextPreset>,
|
||||||
|
) -> Result<impl Responder, ServiceError> {
|
||||||
|
if handles::insert_preset(&pool.into_inner(), data.into_inner())
|
||||||
|
.await
|
||||||
|
.is_ok()
|
||||||
|
{
|
||||||
return Ok("Add preset Success");
|
return Ok("Add preset Success");
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -447,8 +528,14 @@ async fn add_preset(data: web::Json<TextPreset>) -> Result<impl Responder, Servi
|
|||||||
/// ```
|
/// ```
|
||||||
#[delete("/presets/{id}")]
|
#[delete("/presets/{id}")]
|
||||||
#[has_any_role("Role::Admin", "Role::User", type = "Role")]
|
#[has_any_role("Role::Admin", "Role::User", type = "Role")]
|
||||||
async fn delete_preset(id: web::Path<i32>) -> Result<impl Responder, ServiceError> {
|
async fn delete_preset(
|
||||||
if handles::delete_preset(&id).await.is_ok() {
|
pool: web::Data<Pool<Sqlite>>,
|
||||||
|
id: web::Path<i32>,
|
||||||
|
) -> Result<impl Responder, ServiceError> {
|
||||||
|
if handles::delete_preset(&pool.into_inner(), &id)
|
||||||
|
.await
|
||||||
|
.is_ok()
|
||||||
|
{
|
||||||
return Ok("Delete preset Success");
|
return Ok("Delete preset Success");
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -475,10 +562,11 @@ async fn delete_preset(id: web::Path<i32>) -> Result<impl Responder, ServiceErro
|
|||||||
#[post("/control/{id}/text/")]
|
#[post("/control/{id}/text/")]
|
||||||
#[has_any_role("Role::Admin", "Role::User", type = "Role")]
|
#[has_any_role("Role::Admin", "Role::User", type = "Role")]
|
||||||
pub async fn send_text_message(
|
pub async fn send_text_message(
|
||||||
|
pool: web::Data<Pool<Sqlite>>,
|
||||||
id: web::Path<i32>,
|
id: web::Path<i32>,
|
||||||
data: web::Json<HashMap<String, String>>,
|
data: web::Json<HashMap<String, String>>,
|
||||||
) -> Result<impl Responder, ServiceError> {
|
) -> Result<impl Responder, ServiceError> {
|
||||||
match send_message(*id, data.into_inner()).await {
|
match send_message(&pool.into_inner(), *id, data.into_inner()).await {
|
||||||
Ok(res) => Ok(res.text().await.unwrap_or_else(|_| "Success".into())),
|
Ok(res) => Ok(res.text().await.unwrap_or_else(|_| "Success".into())),
|
||||||
Err(e) => Err(e),
|
Err(e) => Err(e),
|
||||||
}
|
}
|
||||||
@ -497,10 +585,11 @@ pub async fn send_text_message(
|
|||||||
#[post("/control/{id}/playout/")]
|
#[post("/control/{id}/playout/")]
|
||||||
#[has_any_role("Role::Admin", "Role::User", type = "Role")]
|
#[has_any_role("Role::Admin", "Role::User", type = "Role")]
|
||||||
pub async fn control_playout(
|
pub async fn control_playout(
|
||||||
|
pool: web::Data<Pool<Sqlite>>,
|
||||||
id: web::Path<i32>,
|
id: web::Path<i32>,
|
||||||
control: web::Json<Process>,
|
control: web::Json<Process>,
|
||||||
) -> Result<impl Responder, ServiceError> {
|
) -> Result<impl Responder, ServiceError> {
|
||||||
match control_state(*id, control.command.clone()).await {
|
match control_state(&pool.into_inner(), *id, control.command.clone()).await {
|
||||||
Ok(res) => Ok(res.text().await.unwrap_or_else(|_| "Success".into())),
|
Ok(res) => Ok(res.text().await.unwrap_or_else(|_| "Success".into())),
|
||||||
Err(e) => Err(e),
|
Err(e) => Err(e),
|
||||||
}
|
}
|
||||||
@ -538,8 +627,11 @@ pub async fn control_playout(
|
|||||||
/// ```
|
/// ```
|
||||||
#[get("/control/{id}/media/current")]
|
#[get("/control/{id}/media/current")]
|
||||||
#[has_any_role("Role::Admin", "Role::User", type = "Role")]
|
#[has_any_role("Role::Admin", "Role::User", type = "Role")]
|
||||||
pub async fn media_current(id: web::Path<i32>) -> Result<impl Responder, ServiceError> {
|
pub async fn media_current(
|
||||||
match media_info(*id, "current".into()).await {
|
pool: web::Data<Pool<Sqlite>>,
|
||||||
|
id: web::Path<i32>,
|
||||||
|
) -> Result<impl Responder, ServiceError> {
|
||||||
|
match media_info(&pool.into_inner(), *id, "current".into()).await {
|
||||||
Ok(res) => Ok(res.text().await.unwrap_or_else(|_| "Success".into())),
|
Ok(res) => Ok(res.text().await.unwrap_or_else(|_| "Success".into())),
|
||||||
Err(e) => Err(e),
|
Err(e) => Err(e),
|
||||||
}
|
}
|
||||||
@ -552,8 +644,11 @@ pub async fn media_current(id: web::Path<i32>) -> Result<impl Responder, Service
|
|||||||
/// ```
|
/// ```
|
||||||
#[get("/control/{id}/media/next")]
|
#[get("/control/{id}/media/next")]
|
||||||
#[has_any_role("Role::Admin", "Role::User", type = "Role")]
|
#[has_any_role("Role::Admin", "Role::User", type = "Role")]
|
||||||
pub async fn media_next(id: web::Path<i32>) -> Result<impl Responder, ServiceError> {
|
pub async fn media_next(
|
||||||
match media_info(*id, "next".into()).await {
|
pool: web::Data<Pool<Sqlite>>,
|
||||||
|
id: web::Path<i32>,
|
||||||
|
) -> Result<impl Responder, ServiceError> {
|
||||||
|
match media_info(&pool.into_inner(), *id, "next".into()).await {
|
||||||
Ok(res) => Ok(res.text().await.unwrap_or_else(|_| "Success".into())),
|
Ok(res) => Ok(res.text().await.unwrap_or_else(|_| "Success".into())),
|
||||||
Err(e) => Err(e),
|
Err(e) => Err(e),
|
||||||
}
|
}
|
||||||
@ -567,8 +662,11 @@ pub async fn media_next(id: web::Path<i32>) -> Result<impl Responder, ServiceErr
|
|||||||
/// ```
|
/// ```
|
||||||
#[get("/control/{id}/media/last")]
|
#[get("/control/{id}/media/last")]
|
||||||
#[has_any_role("Role::Admin", "Role::User", type = "Role")]
|
#[has_any_role("Role::Admin", "Role::User", type = "Role")]
|
||||||
pub async fn media_last(id: web::Path<i32>) -> Result<impl Responder, ServiceError> {
|
pub async fn media_last(
|
||||||
match media_info(*id, "last".into()).await {
|
pool: web::Data<Pool<Sqlite>>,
|
||||||
|
id: web::Path<i32>,
|
||||||
|
) -> Result<impl Responder, ServiceError> {
|
||||||
|
match media_info(&pool.into_inner(), *id, "last".into()).await {
|
||||||
Ok(res) => Ok(res.text().await.unwrap_or_else(|_| "Success".into())),
|
Ok(res) => Ok(res.text().await.unwrap_or_else(|_| "Success".into())),
|
||||||
Err(e) => Err(e),
|
Err(e) => Err(e),
|
||||||
}
|
}
|
||||||
@ -590,10 +688,11 @@ pub async fn media_last(id: web::Path<i32>) -> Result<impl Responder, ServiceErr
|
|||||||
#[post("/control/{id}/process/")]
|
#[post("/control/{id}/process/")]
|
||||||
#[has_any_role("Role::Admin", "Role::User", type = "Role")]
|
#[has_any_role("Role::Admin", "Role::User", type = "Role")]
|
||||||
pub async fn process_control(
|
pub async fn process_control(
|
||||||
|
pool: web::Data<Pool<Sqlite>>,
|
||||||
id: web::Path<i32>,
|
id: web::Path<i32>,
|
||||||
proc: web::Json<Process>,
|
proc: web::Json<Process>,
|
||||||
) -> Result<impl Responder, ServiceError> {
|
) -> Result<impl Responder, ServiceError> {
|
||||||
control_service(*id, &proc.command).await
|
control_service(&pool.into_inner(), *id, &proc.command).await
|
||||||
}
|
}
|
||||||
|
|
||||||
/// #### ffplayout Playlist Operations
|
/// #### ffplayout Playlist Operations
|
||||||
@ -607,10 +706,11 @@ pub async fn process_control(
|
|||||||
#[get("/playlist/{id}")]
|
#[get("/playlist/{id}")]
|
||||||
#[has_any_role("Role::Admin", "Role::User", type = "Role")]
|
#[has_any_role("Role::Admin", "Role::User", type = "Role")]
|
||||||
pub async fn get_playlist(
|
pub async fn get_playlist(
|
||||||
|
pool: web::Data<Pool<Sqlite>>,
|
||||||
id: web::Path<i32>,
|
id: web::Path<i32>,
|
||||||
obj: web::Query<DateObj>,
|
obj: web::Query<DateObj>,
|
||||||
) -> Result<impl Responder, ServiceError> {
|
) -> Result<impl Responder, ServiceError> {
|
||||||
match read_playlist(*id, obj.date.clone()).await {
|
match read_playlist(&pool.into_inner(), *id, obj.date.clone()).await {
|
||||||
Ok(playlist) => Ok(web::Json(playlist)),
|
Ok(playlist) => Ok(web::Json(playlist)),
|
||||||
Err(e) => Err(e),
|
Err(e) => Err(e),
|
||||||
}
|
}
|
||||||
@ -626,10 +726,11 @@ pub async fn get_playlist(
|
|||||||
#[post("/playlist/{id}/")]
|
#[post("/playlist/{id}/")]
|
||||||
#[has_any_role("Role::Admin", "Role::User", type = "Role")]
|
#[has_any_role("Role::Admin", "Role::User", type = "Role")]
|
||||||
pub async fn save_playlist(
|
pub async fn save_playlist(
|
||||||
|
pool: web::Data<Pool<Sqlite>>,
|
||||||
id: web::Path<i32>,
|
id: web::Path<i32>,
|
||||||
data: web::Json<JsonPlaylist>,
|
data: web::Json<JsonPlaylist>,
|
||||||
) -> Result<impl Responder, ServiceError> {
|
) -> Result<impl Responder, ServiceError> {
|
||||||
match write_playlist(*id, data.into_inner()).await {
|
match write_playlist(&pool.into_inner(), *id, data.into_inner()).await {
|
||||||
Ok(res) => Ok(res),
|
Ok(res) => Ok(res),
|
||||||
Err(e) => Err(e),
|
Err(e) => Err(e),
|
||||||
}
|
}
|
||||||
@ -646,9 +747,10 @@ pub async fn save_playlist(
|
|||||||
#[get("/playlist/{id}/generate/{date}")]
|
#[get("/playlist/{id}/generate/{date}")]
|
||||||
#[has_any_role("Role::Admin", "Role::User", type = "Role")]
|
#[has_any_role("Role::Admin", "Role::User", type = "Role")]
|
||||||
pub async fn gen_playlist(
|
pub async fn gen_playlist(
|
||||||
|
pool: web::Data<Pool<Sqlite>>,
|
||||||
params: web::Path<(i32, String)>,
|
params: web::Path<(i32, String)>,
|
||||||
) -> Result<impl Responder, ServiceError> {
|
) -> Result<impl Responder, ServiceError> {
|
||||||
match generate_playlist(params.0, params.1.clone()).await {
|
match generate_playlist(&pool.into_inner(), params.0, params.1.clone()).await {
|
||||||
Ok(playlist) => Ok(web::Json(playlist)),
|
Ok(playlist) => Ok(web::Json(playlist)),
|
||||||
Err(e) => Err(e),
|
Err(e) => Err(e),
|
||||||
}
|
}
|
||||||
@ -663,9 +765,10 @@ pub async fn gen_playlist(
|
|||||||
#[delete("/playlist/{id}/{date}")]
|
#[delete("/playlist/{id}/{date}")]
|
||||||
#[has_any_role("Role::Admin", "Role::User", type = "Role")]
|
#[has_any_role("Role::Admin", "Role::User", type = "Role")]
|
||||||
pub async fn del_playlist(
|
pub async fn del_playlist(
|
||||||
|
pool: web::Data<Pool<Sqlite>>,
|
||||||
params: web::Path<(i32, String)>,
|
params: web::Path<(i32, String)>,
|
||||||
) -> Result<impl Responder, ServiceError> {
|
) -> Result<impl Responder, ServiceError> {
|
||||||
match delete_playlist(params.0, ¶ms.1).await {
|
match delete_playlist(&pool.into_inner(), params.0, ¶ms.1).await {
|
||||||
Ok(_) => Ok(format!("Delete playlist from {} success!", params.1)),
|
Ok(_) => Ok(format!("Delete playlist from {} success!", params.1)),
|
||||||
Err(e) => Err(e),
|
Err(e) => Err(e),
|
||||||
}
|
}
|
||||||
@ -676,16 +779,17 @@ pub async fn del_playlist(
|
|||||||
/// **Read Log Life**
|
/// **Read Log Life**
|
||||||
///
|
///
|
||||||
/// ```BASH
|
/// ```BASH
|
||||||
/// curl -X Get http://127.0.0.1:8787/api/log/1
|
/// curl -X GET http://127.0.0.1:8787/api/log/1
|
||||||
/// -H 'Content-Type: application/json' -H 'Authorization: Bearer <TOKEN>'
|
/// -H 'Content-Type: application/json' -H 'Authorization: Bearer <TOKEN>'
|
||||||
/// ```
|
/// ```
|
||||||
#[get("/log/{id}")]
|
#[get("/log/{id}")]
|
||||||
#[has_any_role("Role::Admin", "Role::User", type = "Role")]
|
#[has_any_role("Role::Admin", "Role::User", type = "Role")]
|
||||||
pub async fn get_log(
|
pub async fn get_log(
|
||||||
|
pool: web::Data<Pool<Sqlite>>,
|
||||||
id: web::Path<i32>,
|
id: web::Path<i32>,
|
||||||
log: web::Query<DateObj>,
|
log: web::Query<DateObj>,
|
||||||
) -> Result<impl Responder, ServiceError> {
|
) -> Result<impl Responder, ServiceError> {
|
||||||
read_log_file(&id, &log.date).await
|
read_log_file(&pool.into_inner(), &id, &log.date).await
|
||||||
}
|
}
|
||||||
|
|
||||||
/// ### File Operations
|
/// ### File Operations
|
||||||
@ -699,10 +803,11 @@ pub async fn get_log(
|
|||||||
#[post("/file/{id}/browse/")]
|
#[post("/file/{id}/browse/")]
|
||||||
#[has_any_role("Role::Admin", "Role::User", type = "Role")]
|
#[has_any_role("Role::Admin", "Role::User", type = "Role")]
|
||||||
pub async fn file_browser(
|
pub async fn file_browser(
|
||||||
|
pool: web::Data<Pool<Sqlite>>,
|
||||||
id: web::Path<i32>,
|
id: web::Path<i32>,
|
||||||
data: web::Json<PathObject>,
|
data: web::Json<PathObject>,
|
||||||
) -> Result<impl Responder, ServiceError> {
|
) -> Result<impl Responder, ServiceError> {
|
||||||
match browser(*id, &data.into_inner()).await {
|
match browser(&pool.into_inner(), *id, &data.into_inner()).await {
|
||||||
Ok(obj) => Ok(web::Json(obj)),
|
Ok(obj) => Ok(web::Json(obj)),
|
||||||
Err(e) => Err(e),
|
Err(e) => Err(e),
|
||||||
}
|
}
|
||||||
@ -717,10 +822,11 @@ pub async fn file_browser(
|
|||||||
#[post("/file/{id}/create-folder/")]
|
#[post("/file/{id}/create-folder/")]
|
||||||
#[has_any_role("Role::Admin", "Role::User", type = "Role")]
|
#[has_any_role("Role::Admin", "Role::User", type = "Role")]
|
||||||
pub async fn add_dir(
|
pub async fn add_dir(
|
||||||
|
pool: web::Data<Pool<Sqlite>>,
|
||||||
id: web::Path<i32>,
|
id: web::Path<i32>,
|
||||||
data: web::Json<PathObject>,
|
data: web::Json<PathObject>,
|
||||||
) -> Result<HttpResponse, ServiceError> {
|
) -> Result<HttpResponse, ServiceError> {
|
||||||
create_directory(*id, &data.into_inner()).await
|
create_directory(&pool.into_inner(), *id, &data.into_inner()).await
|
||||||
}
|
}
|
||||||
|
|
||||||
/// **Rename File**
|
/// **Rename File**
|
||||||
@ -732,10 +838,11 @@ pub async fn add_dir(
|
|||||||
#[post("/file/{id}/rename/")]
|
#[post("/file/{id}/rename/")]
|
||||||
#[has_any_role("Role::Admin", "Role::User", type = "Role")]
|
#[has_any_role("Role::Admin", "Role::User", type = "Role")]
|
||||||
pub async fn move_rename(
|
pub async fn move_rename(
|
||||||
|
pool: web::Data<Pool<Sqlite>>,
|
||||||
id: web::Path<i32>,
|
id: web::Path<i32>,
|
||||||
data: web::Json<MoveObject>,
|
data: web::Json<MoveObject>,
|
||||||
) -> Result<impl Responder, ServiceError> {
|
) -> Result<impl Responder, ServiceError> {
|
||||||
match rename_file(*id, &data.into_inner()).await {
|
match rename_file(&pool.into_inner(), *id, &data.into_inner()).await {
|
||||||
Ok(obj) => Ok(web::Json(obj)),
|
Ok(obj) => Ok(web::Json(obj)),
|
||||||
Err(e) => Err(e),
|
Err(e) => Err(e),
|
||||||
}
|
}
|
||||||
@ -750,10 +857,11 @@ pub async fn move_rename(
|
|||||||
#[post("/file/{id}/remove/")]
|
#[post("/file/{id}/remove/")]
|
||||||
#[has_any_role("Role::Admin", "Role::User", type = "Role")]
|
#[has_any_role("Role::Admin", "Role::User", type = "Role")]
|
||||||
pub async fn remove(
|
pub async fn remove(
|
||||||
|
pool: web::Data<Pool<Sqlite>>,
|
||||||
id: web::Path<i32>,
|
id: web::Path<i32>,
|
||||||
data: web::Json<PathObject>,
|
data: web::Json<PathObject>,
|
||||||
) -> Result<impl Responder, ServiceError> {
|
) -> Result<impl Responder, ServiceError> {
|
||||||
match remove_file_or_folder(*id, &data.into_inner().source).await {
|
match remove_file_or_folder(&pool.into_inner(), *id, &data.into_inner().source).await {
|
||||||
Ok(obj) => Ok(web::Json(obj)),
|
Ok(obj) => Ok(web::Json(obj)),
|
||||||
Err(e) => Err(e),
|
Err(e) => Err(e),
|
||||||
}
|
}
|
||||||
@ -762,17 +870,18 @@ pub async fn remove(
|
|||||||
/// **Upload File**
|
/// **Upload File**
|
||||||
///
|
///
|
||||||
/// ```BASH
|
/// ```BASH
|
||||||
/// curl -X POST http://127.0.0.1:8787/api/file/1/upload/ -H 'Authorization: Bearer <TOKEN>'
|
/// curl -X PUT http://127.0.0.1:8787/api/file/1/upload/ -H 'Authorization: Bearer <TOKEN>'
|
||||||
/// -F "file=@file.mp4"
|
/// -F "file=@file.mp4"
|
||||||
/// ```
|
/// ```
|
||||||
#[put("/file/{id}/upload/")]
|
#[put("/file/{id}/upload/")]
|
||||||
#[has_any_role("Role::Admin", "Role::User", type = "Role")]
|
#[has_any_role("Role::Admin", "Role::User", type = "Role")]
|
||||||
async fn save_file(
|
async fn save_file(
|
||||||
|
pool: web::Data<Pool<Sqlite>>,
|
||||||
id: web::Path<i32>,
|
id: web::Path<i32>,
|
||||||
payload: Multipart,
|
payload: Multipart,
|
||||||
obj: web::Query<FileObj>,
|
obj: web::Query<FileObj>,
|
||||||
) -> Result<HttpResponse, ServiceError> {
|
) -> Result<HttpResponse, ServiceError> {
|
||||||
upload(*id, payload, &obj.path, false).await
|
upload(&pool.into_inner(), *id, payload, &obj.path, false).await
|
||||||
}
|
}
|
||||||
|
|
||||||
/// **Import playlist**
|
/// **Import playlist**
|
||||||
@ -781,25 +890,126 @@ async fn save_file(
|
|||||||
/// lines with leading "#" will be ignore
|
/// lines with leading "#" will be ignore
|
||||||
///
|
///
|
||||||
/// ```BASH
|
/// ```BASH
|
||||||
/// curl -X POST http://127.0.0.1:8787/api/file/1/import/ -H 'Authorization: Bearer <TOKEN>'
|
/// curl -X PUT http://127.0.0.1:8787/api/file/1/import/ -H 'Authorization: Bearer <TOKEN>'
|
||||||
/// -F "file=@list.m3u"
|
/// -F "file=@list.m3u"
|
||||||
/// ```
|
/// ```
|
||||||
#[put("/file/{id}/import/")]
|
#[put("/file/{id}/import/")]
|
||||||
#[has_any_role("Role::Admin", "Role::User", type = "Role")]
|
#[has_any_role("Role::Admin", "Role::User", type = "Role")]
|
||||||
async fn import_playlist(
|
async fn import_playlist(
|
||||||
|
pool: web::Data<Pool<Sqlite>>,
|
||||||
id: web::Path<i32>,
|
id: web::Path<i32>,
|
||||||
payload: Multipart,
|
payload: Multipart,
|
||||||
obj: web::Query<ImportObj>,
|
obj: web::Query<ImportObj>,
|
||||||
) -> Result<HttpResponse, ServiceError> {
|
) -> Result<HttpResponse, ServiceError> {
|
||||||
let file = Path::new(&obj.file).file_name().unwrap_or_default();
|
let file = Path::new(&obj.file).file_name().unwrap_or_default();
|
||||||
let path = env::temp_dir().join(file).to_string_lossy().to_string();
|
let path = env::temp_dir().join(file).to_string_lossy().to_string();
|
||||||
let (config, _) = playout_config(&id).await?;
|
let (config, _) = playout_config(&pool.clone().into_inner(), &id).await?;
|
||||||
let channel = handles::select_channel(&id).await?;
|
let channel = handles::select_channel(&pool.clone().into_inner(), &id).await?;
|
||||||
|
|
||||||
upload(*id, payload, &path, true).await?;
|
upload(&pool.into_inner(), *id, payload, &path, true).await?;
|
||||||
import_file(&config, &obj.date, Some(channel.name), &path)?;
|
import_file(&config, &obj.date, Some(channel.name), &path)?;
|
||||||
|
|
||||||
fs::remove_file(path)?;
|
fs::remove_file(path)?;
|
||||||
|
|
||||||
Ok(HttpResponse::Ok().into())
|
Ok(HttpResponse::Ok().into())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// **Program info**
|
||||||
|
///
|
||||||
|
/// Get program infos about given date, or current day
|
||||||
|
///
|
||||||
|
/// Examples:
|
||||||
|
///
|
||||||
|
/// * get program from current day
|
||||||
|
/// ```BASH
|
||||||
|
/// curl -X GET http://127.0.0.1:8787/api/program/1/ -H 'Authorization: Bearer <TOKEN>'
|
||||||
|
/// ```
|
||||||
|
///
|
||||||
|
/// * get a program range between two dates
|
||||||
|
/// ```BASH
|
||||||
|
/// curl -X GET http://127.0.0.1:8787/api/program/1/?start_after=2022-11-13T12:00:00&start_before=2022-11-20T11:59:59 \
|
||||||
|
/// -H 'Authorization: Bearer <TOKEN>'
|
||||||
|
/// ```
|
||||||
|
///
|
||||||
|
/// * get program from give day
|
||||||
|
/// ```BASH
|
||||||
|
/// curl -X GET http://127.0.0.1:8787/api/program/1/?start_after=2022-11-13T10:00:00 \
|
||||||
|
/// -H 'Authorization: Bearer <TOKEN>'
|
||||||
|
/// ```
|
||||||
|
#[get("/program/{id}/")]
|
||||||
|
#[has_any_role("Role::Admin", "Role::User", type = "Role")]
|
||||||
|
async fn get_program(
|
||||||
|
pool: web::Data<Pool<Sqlite>>,
|
||||||
|
id: web::Path<i32>,
|
||||||
|
obj: web::Query<ProgramObj>,
|
||||||
|
) -> Result<impl Responder, ServiceError> {
|
||||||
|
let (config, _) = playout_config(&pool.clone().into_inner(), &id).await?;
|
||||||
|
let start_sec = config.playlist.start_sec.unwrap();
|
||||||
|
let mut days = 0;
|
||||||
|
let mut program = vec![];
|
||||||
|
let after = obj.start_after;
|
||||||
|
let mut before = obj.start_before;
|
||||||
|
|
||||||
|
if after > before {
|
||||||
|
before = chrono::Local
|
||||||
|
.with_ymd_and_hms(after.year(), after.month(), after.day(), 23, 59, 59)
|
||||||
|
.unwrap()
|
||||||
|
.naive_local()
|
||||||
|
}
|
||||||
|
|
||||||
|
if start_sec > time_to_sec(&after.format("%H:%M:%S").to_string()) {
|
||||||
|
days = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
let date_range = get_date_range(&vec_strings![
|
||||||
|
(after - Duration::days(days)).format("%Y-%m-%d"),
|
||||||
|
"-",
|
||||||
|
before.format("%Y-%m-%d")
|
||||||
|
]);
|
||||||
|
|
||||||
|
for date in date_range {
|
||||||
|
let conn = pool.clone().into_inner();
|
||||||
|
let mut naive = NaiveDateTime::parse_from_str(
|
||||||
|
&format!("{date} {}", sec_to_time(start_sec)),
|
||||||
|
"%Y-%m-%d %H:%M:%S%.3f",
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
let playlist = match read_playlist(&conn, *id, date.clone()).await {
|
||||||
|
Ok(p) => p,
|
||||||
|
Err(e) => {
|
||||||
|
error!("Error in Playlist from {date}: {e}");
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
for item in playlist.program {
|
||||||
|
let start: DateTime<Local> = Local.from_local_datetime(&naive).unwrap();
|
||||||
|
|
||||||
|
let source = match Regex::new(&config.text.regex)
|
||||||
|
.ok()
|
||||||
|
.and_then(|r| r.captures(&item.source))
|
||||||
|
{
|
||||||
|
Some(t) => t[1].to_string(),
|
||||||
|
None => item.source,
|
||||||
|
};
|
||||||
|
|
||||||
|
let p_item = ProgramItem {
|
||||||
|
source,
|
||||||
|
start: start.format("%Y-%m-%d %H:%M:%S%.3f%:z").to_string(),
|
||||||
|
r#in: item.seek,
|
||||||
|
out: item.out,
|
||||||
|
duration: item.duration,
|
||||||
|
category: item.category,
|
||||||
|
};
|
||||||
|
|
||||||
|
if naive >= after && naive <= before {
|
||||||
|
program.push(p_item);
|
||||||
|
}
|
||||||
|
|
||||||
|
naive += Duration::milliseconds(((item.out - item.seek) * 1000.0) as i64);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(web::Json(program))
|
||||||
|
}
|
||||||
|
@ -5,7 +5,7 @@ use argon2::{
|
|||||||
|
|
||||||
use rand::{distributions::Alphanumeric, Rng};
|
use rand::{distributions::Alphanumeric, Rng};
|
||||||
use simplelog::*;
|
use simplelog::*;
|
||||||
use sqlx::{migrate::MigrateDatabase, sqlite::SqliteQueryResult, Pool, Sqlite, SqlitePool};
|
use sqlx::{migrate::MigrateDatabase, sqlite::SqliteQueryResult, Pool, Sqlite};
|
||||||
|
|
||||||
use crate::db::models::{Channel, TextPreset, User};
|
use crate::db::models::{Channel, TextPreset, User};
|
||||||
use crate::utils::{db_path, local_utc_offset, GlobalSettings};
|
use crate::utils::{db_path, local_utc_offset, GlobalSettings};
|
||||||
@ -15,8 +15,7 @@ struct Role {
|
|||||||
name: String,
|
name: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn create_schema() -> Result<SqliteQueryResult, sqlx::Error> {
|
async fn create_schema(conn: &Pool<Sqlite>) -> Result<SqliteQueryResult, sqlx::Error> {
|
||||||
let conn = connection().await?;
|
|
||||||
let query = "PRAGMA foreign_keys = ON;
|
let query = "PRAGMA foreign_keys = ON;
|
||||||
CREATE TABLE IF NOT EXISTS global
|
CREATE TABLE IF NOT EXISTS global
|
||||||
(
|
(
|
||||||
@ -71,18 +70,19 @@ async fn create_schema() -> Result<SqliteQueryResult, sqlx::Error> {
|
|||||||
FOREIGN KEY (channel_id) REFERENCES channels (id) ON UPDATE SET NULL ON DELETE SET NULL,
|
FOREIGN KEY (channel_id) REFERENCES channels (id) ON UPDATE SET NULL ON DELETE SET NULL,
|
||||||
UNIQUE(mail, username)
|
UNIQUE(mail, username)
|
||||||
);";
|
);";
|
||||||
let result = sqlx::query(query).execute(&conn).await;
|
|
||||||
conn.close().await;
|
|
||||||
|
|
||||||
result
|
sqlx::query(query).execute(conn).await
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn db_init(domain: Option<String>) -> Result<&'static str, Box<dyn std::error::Error>> {
|
pub async fn db_init(
|
||||||
|
conn: &Pool<Sqlite>,
|
||||||
|
domain: Option<String>,
|
||||||
|
) -> Result<&'static str, Box<dyn std::error::Error>> {
|
||||||
let db_path = db_path()?;
|
let db_path = db_path()?;
|
||||||
|
|
||||||
if !Sqlite::database_exists(&db_path).await.unwrap_or(false) {
|
if !Sqlite::database_exists(&db_path).await.unwrap_or(false) {
|
||||||
Sqlite::create_database(&db_path).await.unwrap();
|
Sqlite::create_database(&db_path).await.unwrap();
|
||||||
match create_schema().await {
|
match create_schema(conn).await {
|
||||||
Ok(_) => info!("Database created Successfully"),
|
Ok(_) => info!("Database created Successfully"),
|
||||||
Err(e) => panic!("{e}"),
|
Err(e) => panic!("{e}"),
|
||||||
}
|
}
|
||||||
@ -93,8 +93,6 @@ pub async fn db_init(domain: Option<String>) -> Result<&'static str, Box<dyn std
|
|||||||
.map(char::from)
|
.map(char::from)
|
||||||
.collect();
|
.collect();
|
||||||
|
|
||||||
let instances = connection().await?;
|
|
||||||
|
|
||||||
let url = match domain {
|
let url = match domain {
|
||||||
Some(d) => format!("http://{d}/live/stream.m3u8"),
|
Some(d) => format!("http://{d}/live/stream.m3u8"),
|
||||||
None => "http://localhost/live/stream.m3u8".to_string(),
|
None => "http://localhost/live/stream.m3u8".to_string(),
|
||||||
@ -120,45 +118,30 @@ pub async fn db_init(domain: Option<String>) -> Result<&'static str, Box<dyn std
|
|||||||
sqlx::query(query)
|
sqlx::query(query)
|
||||||
.bind(secret)
|
.bind(secret)
|
||||||
.bind(url)
|
.bind(url)
|
||||||
.execute(&instances)
|
.execute(conn)
|
||||||
.await?;
|
.await?;
|
||||||
instances.close().await;
|
|
||||||
|
|
||||||
Ok("Database initialized!")
|
Ok("Database initialized!")
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn connection() -> Result<Pool<Sqlite>, sqlx::Error> {
|
pub async fn select_global(conn: &Pool<Sqlite>) -> Result<GlobalSettings, sqlx::Error> {
|
||||||
let db_path = db_path().unwrap();
|
|
||||||
let conn = SqlitePool::connect(&db_path).await?;
|
|
||||||
|
|
||||||
Ok(conn)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub async fn select_global() -> Result<GlobalSettings, sqlx::Error> {
|
|
||||||
let conn = connection().await?;
|
|
||||||
let query = "SELECT secret FROM global WHERE id = 1";
|
let query = "SELECT secret FROM global WHERE id = 1";
|
||||||
let result: GlobalSettings = sqlx::query_as(query).fetch_one(&conn).await?;
|
|
||||||
conn.close().await;
|
|
||||||
|
|
||||||
Ok(result)
|
sqlx::query_as(query).fetch_one(conn).await
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn select_channel(id: &i32) -> Result<Channel, sqlx::Error> {
|
pub async fn select_channel(conn: &Pool<Sqlite>, id: &i32) -> Result<Channel, sqlx::Error> {
|
||||||
let conn = connection().await?;
|
|
||||||
let query = "SELECT * FROM channels WHERE id = $1";
|
let query = "SELECT * FROM channels WHERE id = $1";
|
||||||
let mut result: Channel = sqlx::query_as(query).bind(id).fetch_one(&conn).await?;
|
let mut result: Channel = sqlx::query_as(query).bind(id).fetch_one(conn).await?;
|
||||||
conn.close().await;
|
|
||||||
|
|
||||||
result.utc_offset = local_utc_offset();
|
result.utc_offset = local_utc_offset();
|
||||||
|
|
||||||
Ok(result)
|
Ok(result)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn select_all_channels() -> Result<Vec<Channel>, sqlx::Error> {
|
pub async fn select_all_channels(conn: &Pool<Sqlite>) -> Result<Vec<Channel>, sqlx::Error> {
|
||||||
let conn = connection().await?;
|
|
||||||
let query = "SELECT * FROM channels";
|
let query = "SELECT * FROM channels";
|
||||||
let mut results: Vec<Channel> = sqlx::query_as(query).fetch_all(&conn).await?;
|
let mut results: Vec<Channel> = sqlx::query_as(query).fetch_all(conn).await?;
|
||||||
conn.close().await;
|
|
||||||
|
|
||||||
for result in results.iter_mut() {
|
for result in results.iter_mut() {
|
||||||
result.utc_offset = local_utc_offset();
|
result.utc_offset = local_utc_offset();
|
||||||
@ -167,26 +150,24 @@ pub async fn select_all_channels() -> Result<Vec<Channel>, sqlx::Error> {
|
|||||||
Ok(results)
|
Ok(results)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn update_channel(id: i32, channel: Channel) -> Result<SqliteQueryResult, sqlx::Error> {
|
pub async fn update_channel(
|
||||||
let conn = connection().await?;
|
conn: &Pool<Sqlite>,
|
||||||
|
id: i32,
|
||||||
|
channel: Channel,
|
||||||
|
) -> Result<SqliteQueryResult, sqlx::Error> {
|
||||||
let query = "UPDATE channels SET name = $2, preview_url = $3, config_path = $4, extra_extensions = $5 WHERE id = $1";
|
let query = "UPDATE channels SET name = $2, preview_url = $3, config_path = $4, extra_extensions = $5 WHERE id = $1";
|
||||||
let result: SqliteQueryResult = sqlx::query(query)
|
|
||||||
|
sqlx::query(query)
|
||||||
.bind(id)
|
.bind(id)
|
||||||
.bind(channel.name)
|
.bind(channel.name)
|
||||||
.bind(channel.preview_url)
|
.bind(channel.preview_url)
|
||||||
.bind(channel.config_path)
|
.bind(channel.config_path)
|
||||||
.bind(channel.extra_extensions)
|
.bind(channel.extra_extensions)
|
||||||
.execute(&conn)
|
.execute(conn)
|
||||||
.await?;
|
.await
|
||||||
conn.close().await;
|
|
||||||
|
|
||||||
Ok(result)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn insert_channel(channel: Channel) -> Result<Channel, sqlx::Error> {
|
pub async fn insert_channel(conn: &Pool<Sqlite>, channel: Channel) -> Result<Channel, sqlx::Error> {
|
||||||
let conn = connection().await?;
|
|
||||||
|
|
||||||
let query = "INSERT INTO channels (name, preview_url, config_path, extra_extensions, service) VALUES($1, $2, $3, $4, $5)";
|
let query = "INSERT INTO channels (name, preview_url, config_path, extra_extensions, service) VALUES($1, $2, $3, $4, $5)";
|
||||||
let result = sqlx::query(query)
|
let result = sqlx::query(query)
|
||||||
.bind(channel.name)
|
.bind(channel.name)
|
||||||
@ -194,56 +175,47 @@ pub async fn insert_channel(channel: Channel) -> Result<Channel, sqlx::Error> {
|
|||||||
.bind(channel.config_path)
|
.bind(channel.config_path)
|
||||||
.bind(channel.extra_extensions)
|
.bind(channel.extra_extensions)
|
||||||
.bind(channel.service)
|
.bind(channel.service)
|
||||||
.execute(&conn)
|
.execute(conn)
|
||||||
.await?;
|
.await?;
|
||||||
let new_channel: Channel = sqlx::query_as("SELECT * FROM channels WHERE id = $1")
|
|
||||||
|
sqlx::query_as("SELECT * FROM channels WHERE id = $1")
|
||||||
.bind(result.last_insert_rowid())
|
.bind(result.last_insert_rowid())
|
||||||
.fetch_one(&conn)
|
.fetch_one(conn)
|
||||||
.await?;
|
.await
|
||||||
conn.close().await;
|
|
||||||
|
|
||||||
Ok(new_channel)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn delete_channel(id: &i32) -> Result<SqliteQueryResult, sqlx::Error> {
|
pub async fn delete_channel(
|
||||||
let conn = connection().await?;
|
conn: &Pool<Sqlite>,
|
||||||
|
id: &i32,
|
||||||
|
) -> Result<SqliteQueryResult, sqlx::Error> {
|
||||||
let query = "DELETE FROM channels WHERE id = $1";
|
let query = "DELETE FROM channels WHERE id = $1";
|
||||||
let result: SqliteQueryResult = sqlx::query(query).bind(id).execute(&conn).await?;
|
|
||||||
conn.close().await;
|
|
||||||
|
|
||||||
Ok(result)
|
sqlx::query(query).bind(id).execute(conn).await
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn select_role(id: &i32) -> Result<String, sqlx::Error> {
|
pub async fn select_role(conn: &Pool<Sqlite>, id: &i32) -> Result<String, sqlx::Error> {
|
||||||
let conn = connection().await?;
|
|
||||||
let query = "SELECT name FROM roles WHERE id = $1";
|
let query = "SELECT name FROM roles WHERE id = $1";
|
||||||
let result: Role = sqlx::query_as(query).bind(id).fetch_one(&conn).await?;
|
let result: Role = sqlx::query_as(query).bind(id).fetch_one(conn).await?;
|
||||||
conn.close().await;
|
|
||||||
|
|
||||||
Ok(result.name)
|
Ok(result.name)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn select_login(user: &str) -> Result<User, sqlx::Error> {
|
pub async fn select_login(conn: &Pool<Sqlite>, user: &str) -> Result<User, sqlx::Error> {
|
||||||
let conn = connection().await?;
|
|
||||||
let query = "SELECT id, mail, username, password, salt, role_id FROM user WHERE username = $1";
|
let query = "SELECT id, mail, username, password, salt, role_id FROM user WHERE username = $1";
|
||||||
let result: User = sqlx::query_as(query).bind(user).fetch_one(&conn).await?;
|
|
||||||
conn.close().await;
|
|
||||||
|
|
||||||
Ok(result)
|
sqlx::query_as(query).bind(user).fetch_one(conn).await
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn select_user(user: &str) -> Result<User, sqlx::Error> {
|
pub async fn select_user(conn: &Pool<Sqlite>, user: &str) -> Result<User, sqlx::Error> {
|
||||||
let conn = connection().await?;
|
|
||||||
let query = "SELECT id, mail, username, role_id FROM user WHERE username = $1";
|
let query = "SELECT id, mail, username, role_id FROM user WHERE username = $1";
|
||||||
let result: User = sqlx::query_as(query).bind(user).fetch_one(&conn).await?;
|
|
||||||
conn.close().await;
|
|
||||||
|
|
||||||
Ok(result)
|
sqlx::query_as(query).bind(user).fetch_one(conn).await
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn insert_user(user: User) -> Result<SqliteQueryResult, sqlx::Error> {
|
pub async fn insert_user(
|
||||||
let conn = connection().await?;
|
conn: &Pool<Sqlite>,
|
||||||
|
user: User,
|
||||||
|
) -> Result<SqliteQueryResult, sqlx::Error> {
|
||||||
let salt = SaltString::generate(&mut OsRng);
|
let salt = SaltString::generate(&mut OsRng);
|
||||||
let password_hash = Argon2::default()
|
let password_hash = Argon2::default()
|
||||||
.hash_password(user.password.clone().as_bytes(), &salt)
|
.hash_password(user.password.clone().as_bytes(), &salt)
|
||||||
@ -251,43 +223,43 @@ pub async fn insert_user(user: User) -> Result<SqliteQueryResult, sqlx::Error> {
|
|||||||
|
|
||||||
let query =
|
let query =
|
||||||
"INSERT INTO user (mail, username, password, salt, role_id) VALUES($1, $2, $3, $4, $5)";
|
"INSERT INTO user (mail, username, password, salt, role_id) VALUES($1, $2, $3, $4, $5)";
|
||||||
let result = sqlx::query(query)
|
|
||||||
|
sqlx::query(query)
|
||||||
.bind(user.mail)
|
.bind(user.mail)
|
||||||
.bind(user.username)
|
.bind(user.username)
|
||||||
.bind(password_hash.to_string())
|
.bind(password_hash.to_string())
|
||||||
.bind(salt.to_string())
|
.bind(salt.to_string())
|
||||||
.bind(user.role_id)
|
.bind(user.role_id)
|
||||||
.execute(&conn)
|
.execute(conn)
|
||||||
.await?;
|
.await
|
||||||
conn.close().await;
|
|
||||||
|
|
||||||
Ok(result)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn update_user(id: i32, fields: String) -> Result<SqliteQueryResult, sqlx::Error> {
|
pub async fn update_user(
|
||||||
let conn = connection().await?;
|
conn: &Pool<Sqlite>,
|
||||||
|
id: i32,
|
||||||
|
fields: String,
|
||||||
|
) -> Result<SqliteQueryResult, sqlx::Error> {
|
||||||
let query = format!("UPDATE user SET {fields} WHERE id = $1");
|
let query = format!("UPDATE user SET {fields} WHERE id = $1");
|
||||||
let result: SqliteQueryResult = sqlx::query(&query).bind(id).execute(&conn).await?;
|
|
||||||
conn.close().await;
|
|
||||||
|
|
||||||
Ok(result)
|
sqlx::query(&query).bind(id).execute(conn).await
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn select_presets(id: i32) -> Result<Vec<TextPreset>, sqlx::Error> {
|
pub async fn select_presets(conn: &Pool<Sqlite>, id: i32) -> Result<Vec<TextPreset>, sqlx::Error> {
|
||||||
let conn = connection().await?;
|
|
||||||
let query = "SELECT * FROM presets WHERE channel_id = $1";
|
let query = "SELECT * FROM presets WHERE channel_id = $1";
|
||||||
let result: Vec<TextPreset> = sqlx::query_as(query).bind(id).fetch_all(&conn).await?;
|
|
||||||
conn.close().await;
|
|
||||||
|
|
||||||
Ok(result)
|
sqlx::query_as(query).bind(id).fetch_all(conn).await
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn update_preset(id: &i32, preset: TextPreset) -> Result<SqliteQueryResult, sqlx::Error> {
|
pub async fn update_preset(
|
||||||
let conn = connection().await?;
|
conn: &Pool<Sqlite>,
|
||||||
|
id: &i32,
|
||||||
|
preset: TextPreset,
|
||||||
|
) -> Result<SqliteQueryResult, sqlx::Error> {
|
||||||
let query =
|
let query =
|
||||||
"UPDATE presets SET name = $1, text = $2, x = $3, y = $4, fontsize = $5, line_spacing = $6,
|
"UPDATE presets SET name = $1, text = $2, x = $3, y = $4, fontsize = $5, line_spacing = $6,
|
||||||
fontcolor = $7, alpha = $8, box = $9, boxcolor = $10, boxborderw = 11 WHERE id = $12";
|
fontcolor = $7, alpha = $8, box = $9, boxcolor = $10, boxborderw = 11 WHERE id = $12";
|
||||||
let result: SqliteQueryResult = sqlx::query(query)
|
|
||||||
|
sqlx::query(query)
|
||||||
.bind(preset.name)
|
.bind(preset.name)
|
||||||
.bind(preset.text)
|
.bind(preset.text)
|
||||||
.bind(preset.x)
|
.bind(preset.x)
|
||||||
@ -300,19 +272,19 @@ pub async fn update_preset(id: &i32, preset: TextPreset) -> Result<SqliteQueryRe
|
|||||||
.bind(preset.boxcolor)
|
.bind(preset.boxcolor)
|
||||||
.bind(preset.boxborderw)
|
.bind(preset.boxborderw)
|
||||||
.bind(id)
|
.bind(id)
|
||||||
.execute(&conn)
|
.execute(conn)
|
||||||
.await?;
|
.await
|
||||||
conn.close().await;
|
|
||||||
|
|
||||||
Ok(result)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn insert_preset(preset: TextPreset) -> Result<SqliteQueryResult, sqlx::Error> {
|
pub async fn insert_preset(
|
||||||
let conn = connection().await?;
|
conn: &Pool<Sqlite>,
|
||||||
|
preset: TextPreset,
|
||||||
|
) -> Result<SqliteQueryResult, sqlx::Error> {
|
||||||
let query =
|
let query =
|
||||||
"INSERT INTO presets (channel_id, name, text, x, y, fontsize, line_spacing, fontcolor, alpha, box, boxcolor, boxborderw)
|
"INSERT INTO presets (channel_id, name, text, x, y, fontsize, line_spacing, fontcolor, alpha, box, boxcolor, boxborderw)
|
||||||
VALUES($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12)";
|
VALUES($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12)";
|
||||||
let result: SqliteQueryResult = sqlx::query(query)
|
|
||||||
|
sqlx::query(query)
|
||||||
.bind(preset.channel_id)
|
.bind(preset.channel_id)
|
||||||
.bind(preset.name)
|
.bind(preset.name)
|
||||||
.bind(preset.text)
|
.bind(preset.text)
|
||||||
@ -325,18 +297,15 @@ pub async fn insert_preset(preset: TextPreset) -> Result<SqliteQueryResult, sqlx
|
|||||||
.bind(preset.r#box)
|
.bind(preset.r#box)
|
||||||
.bind(preset.boxcolor)
|
.bind(preset.boxcolor)
|
||||||
.bind(preset.boxborderw)
|
.bind(preset.boxborderw)
|
||||||
.execute(&conn)
|
.execute(conn)
|
||||||
.await?;
|
.await
|
||||||
conn.close().await;
|
|
||||||
|
|
||||||
Ok(result)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn delete_preset(id: &i32) -> Result<SqliteQueryResult, sqlx::Error> {
|
pub async fn delete_preset(
|
||||||
let conn = connection().await?;
|
conn: &Pool<Sqlite>,
|
||||||
|
id: &i32,
|
||||||
|
) -> Result<SqliteQueryResult, sqlx::Error> {
|
||||||
let query = "DELETE FROM presets WHERE id = $1;";
|
let query = "DELETE FROM presets WHERE id = $1;";
|
||||||
let result: SqliteQueryResult = sqlx::query(query).bind(id).execute(&conn).await?;
|
|
||||||
conn.close().await;
|
|
||||||
|
|
||||||
Ok(result)
|
sqlx::query(query).bind(id).execute(conn).await
|
||||||
}
|
}
|
||||||
|
@ -1,2 +1,13 @@
|
|||||||
|
use sqlx::{Pool, Sqlite, SqlitePool};
|
||||||
|
|
||||||
pub mod handles;
|
pub mod handles;
|
||||||
pub mod models;
|
pub mod models;
|
||||||
|
|
||||||
|
use crate::utils::db_path;
|
||||||
|
|
||||||
|
pub async fn db_pool() -> Result<Pool<Sqlite>, sqlx::Error> {
|
||||||
|
let db_path = db_path().unwrap();
|
||||||
|
let conn = SqlitePool::connect(&db_path).await?;
|
||||||
|
|
||||||
|
Ok(conn)
|
||||||
|
}
|
||||||
|
@ -18,13 +18,13 @@ use api::{
|
|||||||
routes::{
|
routes::{
|
||||||
add_channel, add_dir, add_preset, add_user, control_playout, del_playlist, delete_preset,
|
add_channel, add_dir, add_preset, add_user, control_playout, del_playlist, delete_preset,
|
||||||
file_browser, gen_playlist, get_all_channels, get_channel, get_log, get_playlist,
|
file_browser, gen_playlist, get_all_channels, get_channel, get_log, get_playlist,
|
||||||
get_playout_config, get_presets, get_user, import_playlist, login, media_current,
|
get_playout_config, get_presets, get_program, get_user, import_playlist, login,
|
||||||
media_last, media_next, move_rename, patch_channel, process_control, remove,
|
media_current, media_last, media_next, move_rename, patch_channel, process_control, remove,
|
||||||
remove_channel, save_file, save_playlist, send_text_message, update_playout_config,
|
remove_channel, save_file, save_playlist, send_text_message, update_playout_config,
|
||||||
update_preset, update_user,
|
update_preset, update_user,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
use db::models::LoginUser;
|
use db::{db_pool, models::LoginUser};
|
||||||
use utils::{args_parse::Args, db_path, init_config, run_args, Role};
|
use utils::{args_parse::Args, db_path, init_config, run_args, Role};
|
||||||
|
|
||||||
use ffplayout_lib::utils::{init_logging, PlayoutConfig};
|
use ffplayout_lib::utils::{init_logging, PlayoutConfig};
|
||||||
@ -64,7 +64,15 @@ async fn main() -> std::io::Result<()> {
|
|||||||
let logging = init_logging(&config, None, None);
|
let logging = init_logging(&config, None, None);
|
||||||
CombinedLogger::init(logging).unwrap();
|
CombinedLogger::init(logging).unwrap();
|
||||||
|
|
||||||
if let Err(c) = run_args(args.clone()).await {
|
let pool = match db_pool().await {
|
||||||
|
Ok(p) => p,
|
||||||
|
Err(e) => {
|
||||||
|
error!("{e}");
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
if let Err(c) = run_args(&pool, args.clone()).await {
|
||||||
exit(c);
|
exit(c);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -75,7 +83,7 @@ async fn main() -> std::io::Result<()> {
|
|||||||
exit(1);
|
exit(1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
init_config().await;
|
init_config(&pool).await;
|
||||||
let ip_port = conn.split(':').collect::<Vec<&str>>();
|
let ip_port = conn.split(':').collect::<Vec<&str>>();
|
||||||
let addr = ip_port[0];
|
let addr = ip_port[0];
|
||||||
let port = ip_port[1].parse::<u16>().unwrap();
|
let port = ip_port[1].parse::<u16>().unwrap();
|
||||||
@ -85,7 +93,10 @@ async fn main() -> std::io::Result<()> {
|
|||||||
// no allow origin here, give it to the reverse proxy
|
// no allow origin here, give it to the reverse proxy
|
||||||
HttpServer::new(move || {
|
HttpServer::new(move || {
|
||||||
let auth = HttpAuthentication::bearer(validator);
|
let auth = HttpAuthentication::bearer(validator);
|
||||||
|
let db_pool = web::Data::new(pool.clone());
|
||||||
|
|
||||||
App::new()
|
App::new()
|
||||||
|
.app_data(db_pool)
|
||||||
.wrap(middleware::Logger::default())
|
.wrap(middleware::Logger::default())
|
||||||
.service(login)
|
.service(login)
|
||||||
.service(
|
.service(
|
||||||
@ -121,7 +132,8 @@ async fn main() -> std::io::Result<()> {
|
|||||||
.service(move_rename)
|
.service(move_rename)
|
||||||
.service(remove)
|
.service(remove)
|
||||||
.service(save_file)
|
.service(save_file)
|
||||||
.service(import_playlist),
|
.service(import_playlist)
|
||||||
|
.service(get_program),
|
||||||
)
|
)
|
||||||
.service(Files::new("/", public_path()).index_file("index.html"))
|
.service(Files::new("/", public_path()).index_file("index.html"))
|
||||||
})
|
})
|
||||||
|
@ -1,12 +1,16 @@
|
|||||||
use std::fs;
|
use std::fs;
|
||||||
|
|
||||||
use simplelog::*;
|
use simplelog::*;
|
||||||
|
use sqlx::{Pool, Sqlite};
|
||||||
|
|
||||||
use crate::utils::{control::control_service, errors::ServiceError};
|
use crate::utils::{control::control_service, errors::ServiceError};
|
||||||
|
|
||||||
use crate::db::{handles, models::Channel};
|
use crate::db::{handles, models::Channel};
|
||||||
|
|
||||||
pub async fn create_channel(target_channel: Channel) -> Result<Channel, ServiceError> {
|
pub async fn create_channel(
|
||||||
|
conn: &Pool<Sqlite>,
|
||||||
|
target_channel: Channel,
|
||||||
|
) -> Result<Channel, ServiceError> {
|
||||||
if !target_channel.service.starts_with("ffplayout@") {
|
if !target_channel.service.starts_with("ffplayout@") {
|
||||||
return Err(ServiceError::BadRequest("Bad service name!".to_string()));
|
return Err(ServiceError::BadRequest("Bad service name!".to_string()));
|
||||||
}
|
}
|
||||||
@ -20,22 +24,22 @@ pub async fn create_channel(target_channel: Channel) -> Result<Channel, ServiceE
|
|||||||
&target_channel.config_path,
|
&target_channel.config_path,
|
||||||
)?;
|
)?;
|
||||||
|
|
||||||
let new_channel = handles::insert_channel(target_channel).await?;
|
let new_channel = handles::insert_channel(conn, target_channel).await?;
|
||||||
control_service(new_channel.id, "enable").await?;
|
control_service(conn, new_channel.id, "enable").await?;
|
||||||
|
|
||||||
Ok(new_channel)
|
Ok(new_channel)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn delete_channel(id: i32) -> Result<(), ServiceError> {
|
pub async fn delete_channel(conn: &Pool<Sqlite>, id: i32) -> Result<(), ServiceError> {
|
||||||
let channel = handles::select_channel(&id).await?;
|
let channel = handles::select_channel(conn, &id).await?;
|
||||||
control_service(channel.id, "stop").await?;
|
control_service(conn, channel.id, "stop").await?;
|
||||||
control_service(channel.id, "disable").await?;
|
control_service(conn, channel.id, "disable").await?;
|
||||||
|
|
||||||
if let Err(e) = fs::remove_file(channel.config_path) {
|
if let Err(e) = fs::remove_file(channel.config_path) {
|
||||||
error!("{e}");
|
error!("{e}");
|
||||||
};
|
};
|
||||||
|
|
||||||
handles::delete_channel(&id).await?;
|
handles::delete_channel(conn, &id).await?;
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
@ -5,6 +5,7 @@ use reqwest::{
|
|||||||
Client, Response,
|
Client, Response,
|
||||||
};
|
};
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
use sqlx::{Pool, Sqlite};
|
||||||
|
|
||||||
use crate::db::handles::select_channel;
|
use crate::db::handles::select_channel;
|
||||||
use crate::utils::{errors::ServiceError, playout_config};
|
use crate::utils::{errors::ServiceError, playout_config};
|
||||||
@ -56,8 +57,8 @@ struct SystemD {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl SystemD {
|
impl SystemD {
|
||||||
async fn new(id: i32) -> Result<Self, ServiceError> {
|
async fn new(conn: &Pool<Sqlite>, id: i32) -> Result<Self, ServiceError> {
|
||||||
let channel = select_channel(&id).await?;
|
let channel = select_channel(conn, &id).await?;
|
||||||
|
|
||||||
Ok(Self {
|
Ok(Self {
|
||||||
service: channel.service,
|
service: channel.service,
|
||||||
@ -130,11 +131,15 @@ fn create_header(auth: &str) -> HeaderMap {
|
|||||||
headers
|
headers
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn post_request<T>(id: i32, obj: RpcObj<T>) -> Result<Response, ServiceError>
|
async fn post_request<T>(
|
||||||
|
conn: &Pool<Sqlite>,
|
||||||
|
id: i32,
|
||||||
|
obj: RpcObj<T>,
|
||||||
|
) -> Result<Response, ServiceError>
|
||||||
where
|
where
|
||||||
T: Serialize,
|
T: Serialize,
|
||||||
{
|
{
|
||||||
let (config, _) = playout_config(&id).await?;
|
let (config, _) = playout_config(conn, &id).await?;
|
||||||
let url = format!("http://{}", config.rpc_server.address);
|
let url = format!("http://{}", config.rpc_server.address);
|
||||||
let client = Client::new();
|
let client = Client::new();
|
||||||
|
|
||||||
@ -151,6 +156,7 @@ where
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub async fn send_message(
|
pub async fn send_message(
|
||||||
|
conn: &Pool<Sqlite>,
|
||||||
id: i32,
|
id: i32,
|
||||||
message: HashMap<String, String>,
|
message: HashMap<String, String>,
|
||||||
) -> Result<Response, ServiceError> {
|
) -> Result<Response, ServiceError> {
|
||||||
@ -163,23 +169,35 @@ pub async fn send_message(
|
|||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
post_request(id, json_obj).await
|
post_request(conn, id, json_obj).await
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn control_state(id: i32, command: String) -> Result<Response, ServiceError> {
|
pub async fn control_state(
|
||||||
|
conn: &Pool<Sqlite>,
|
||||||
|
id: i32,
|
||||||
|
command: String,
|
||||||
|
) -> Result<Response, ServiceError> {
|
||||||
let json_obj = RpcObj::new(id, "player".into(), ControlParams { control: command });
|
let json_obj = RpcObj::new(id, "player".into(), ControlParams { control: command });
|
||||||
|
|
||||||
post_request(id, json_obj).await
|
post_request(conn, id, json_obj).await
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn media_info(id: i32, command: String) -> Result<Response, ServiceError> {
|
pub async fn media_info(
|
||||||
|
conn: &Pool<Sqlite>,
|
||||||
|
id: i32,
|
||||||
|
command: String,
|
||||||
|
) -> Result<Response, ServiceError> {
|
||||||
let json_obj = RpcObj::new(id, "player".into(), MediaParams { media: command });
|
let json_obj = RpcObj::new(id, "player".into(), MediaParams { media: command });
|
||||||
|
|
||||||
post_request(id, json_obj).await
|
post_request(conn, id, json_obj).await
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn control_service(id: i32, command: &str) -> Result<String, ServiceError> {
|
pub async fn control_service(
|
||||||
let system_d = SystemD::new(id).await?;
|
conn: &Pool<Sqlite>,
|
||||||
|
id: i32,
|
||||||
|
command: &str,
|
||||||
|
) -> Result<String, ServiceError> {
|
||||||
|
let system_d = SystemD::new(conn, id).await?;
|
||||||
|
|
||||||
match command {
|
match command {
|
||||||
"enable" => system_d.enable(),
|
"enable" => system_d.enable(),
|
||||||
|
@ -6,6 +6,7 @@ use futures_util::TryStreamExt as _;
|
|||||||
use rand::{distributions::Alphanumeric, Rng};
|
use rand::{distributions::Alphanumeric, Rng};
|
||||||
use relative_path::RelativePath;
|
use relative_path::RelativePath;
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
use sqlx::{Pool, Sqlite};
|
||||||
|
|
||||||
use simplelog::*;
|
use simplelog::*;
|
||||||
|
|
||||||
@ -87,8 +88,12 @@ fn norm_abs_path(root_path: &str, input_path: &str) -> (PathBuf, String, String)
|
|||||||
/// Take input path and give file and folder list from it back.
|
/// Take input path and give file and folder list from it back.
|
||||||
/// Input should be a relative path segment, but when it is a absolut path, the norm_abs_path function
|
/// Input should be a relative path segment, but when it is a absolut path, the norm_abs_path function
|
||||||
/// will take care, that user can not break out from given storage path in config.
|
/// will take care, that user can not break out from given storage path in config.
|
||||||
pub async fn browser(id: i32, path_obj: &PathObject) -> Result<PathObject, ServiceError> {
|
pub async fn browser(
|
||||||
let (config, _) = playout_config(&id).await?;
|
conn: &Pool<Sqlite>,
|
||||||
|
id: i32,
|
||||||
|
path_obj: &PathObject,
|
||||||
|
) -> Result<PathObject, ServiceError> {
|
||||||
|
let (config, _) = playout_config(conn, &id).await?;
|
||||||
let extensions = config.storage.extensions;
|
let extensions = config.storage.extensions;
|
||||||
let (path, parent, path_component) = norm_abs_path(&config.storage.path, &path_obj.source);
|
let (path, parent, path_component) = norm_abs_path(&config.storage.path, &path_obj.source);
|
||||||
let mut obj = PathObject::new(path_component, Some(parent));
|
let mut obj = PathObject::new(path_component, Some(parent));
|
||||||
@ -143,10 +148,11 @@ pub async fn browser(id: i32, path_obj: &PathObject) -> Result<PathObject, Servi
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub async fn create_directory(
|
pub async fn create_directory(
|
||||||
|
conn: &Pool<Sqlite>,
|
||||||
id: i32,
|
id: i32,
|
||||||
path_obj: &PathObject,
|
path_obj: &PathObject,
|
||||||
) -> Result<HttpResponse, ServiceError> {
|
) -> Result<HttpResponse, ServiceError> {
|
||||||
let (config, _) = playout_config(&id).await?;
|
let (config, _) = playout_config(conn, &id).await?;
|
||||||
let (path, _, _) = norm_abs_path(&config.storage.path, &path_obj.source);
|
let (path, _, _) = norm_abs_path(&config.storage.path, &path_obj.source);
|
||||||
|
|
||||||
if let Err(e) = fs::create_dir_all(&path) {
|
if let Err(e) = fs::create_dir_all(&path) {
|
||||||
@ -198,8 +204,12 @@ fn rename(source: &PathBuf, target: &PathBuf) -> Result<MoveObject, ServiceError
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn rename_file(id: i32, move_object: &MoveObject) -> Result<MoveObject, ServiceError> {
|
pub async fn rename_file(
|
||||||
let (config, _) = playout_config(&id).await?;
|
conn: &Pool<Sqlite>,
|
||||||
|
id: i32,
|
||||||
|
move_object: &MoveObject,
|
||||||
|
) -> Result<MoveObject, ServiceError> {
|
||||||
|
let (config, _) = playout_config(conn, &id).await?;
|
||||||
let (source_path, _, _) = norm_abs_path(&config.storage.path, &move_object.source);
|
let (source_path, _, _) = norm_abs_path(&config.storage.path, &move_object.source);
|
||||||
let (mut target_path, _, _) = norm_abs_path(&config.storage.path, &move_object.target);
|
let (mut target_path, _, _) = norm_abs_path(&config.storage.path, &move_object.target);
|
||||||
|
|
||||||
@ -229,8 +239,12 @@ pub async fn rename_file(id: i32, move_object: &MoveObject) -> Result<MoveObject
|
|||||||
Err(ServiceError::InternalServerError)
|
Err(ServiceError::InternalServerError)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn remove_file_or_folder(id: i32, source_path: &str) -> Result<(), ServiceError> {
|
pub async fn remove_file_or_folder(
|
||||||
let (config, _) = playout_config(&id).await?;
|
conn: &Pool<Sqlite>,
|
||||||
|
id: i32,
|
||||||
|
source_path: &str,
|
||||||
|
) -> Result<(), ServiceError> {
|
||||||
|
let (config, _) = playout_config(conn, &id).await?;
|
||||||
let (source, _, _) = norm_abs_path(&config.storage.path, source_path);
|
let (source, _, _) = norm_abs_path(&config.storage.path, source_path);
|
||||||
|
|
||||||
if !source.exists() {
|
if !source.exists() {
|
||||||
@ -262,8 +276,8 @@ pub async fn remove_file_or_folder(id: i32, source_path: &str) -> Result<(), Ser
|
|||||||
Err(ServiceError::InternalServerError)
|
Err(ServiceError::InternalServerError)
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn valid_path(id: i32, path: &str) -> Result<PathBuf, ServiceError> {
|
async fn valid_path(conn: &Pool<Sqlite>, id: i32, path: &str) -> Result<PathBuf, ServiceError> {
|
||||||
let (config, _) = playout_config(&id).await?;
|
let (config, _) = playout_config(conn, &id).await?;
|
||||||
let (test_path, _, _) = norm_abs_path(&config.storage.path, path);
|
let (test_path, _, _) = norm_abs_path(&config.storage.path, path);
|
||||||
|
|
||||||
if !test_path.is_dir() {
|
if !test_path.is_dir() {
|
||||||
@ -274,6 +288,7 @@ async fn valid_path(id: i32, path: &str) -> Result<PathBuf, ServiceError> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub async fn upload(
|
pub async fn upload(
|
||||||
|
conn: &Pool<Sqlite>,
|
||||||
id: i32,
|
id: i32,
|
||||||
mut payload: Multipart,
|
mut payload: Multipart,
|
||||||
path: &str,
|
path: &str,
|
||||||
@ -296,7 +311,7 @@ pub async fn upload(
|
|||||||
if abs_path {
|
if abs_path {
|
||||||
filepath = PathBuf::from(path);
|
filepath = PathBuf::from(path);
|
||||||
} else {
|
} else {
|
||||||
let target_path = valid_path(id, path).await?;
|
let target_path = valid_path(conn, id, path).await?;
|
||||||
filepath = target_path.join(filename);
|
filepath = target_path.join(filename);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -5,11 +5,13 @@ use std::{
|
|||||||
path::Path,
|
path::Path,
|
||||||
};
|
};
|
||||||
|
|
||||||
use chrono::prelude::*;
|
use chrono::{format::ParseErrorKind, prelude::*};
|
||||||
use faccess::PathExt;
|
use faccess::PathExt;
|
||||||
use once_cell::sync::OnceCell;
|
use once_cell::sync::OnceCell;
|
||||||
use rpassword::read_password;
|
use rpassword::read_password;
|
||||||
|
use serde::{de, Deserialize, Deserializer};
|
||||||
use simplelog::*;
|
use simplelog::*;
|
||||||
|
use sqlx::{Pool, Sqlite};
|
||||||
|
|
||||||
pub mod args_parse;
|
pub mod args_parse;
|
||||||
pub mod channels;
|
pub mod channels;
|
||||||
@ -23,7 +25,7 @@ use crate::db::{
|
|||||||
models::{Channel, User},
|
models::{Channel, User},
|
||||||
};
|
};
|
||||||
use crate::utils::{args_parse::Args, errors::ServiceError};
|
use crate::utils::{args_parse::Args, errors::ServiceError};
|
||||||
use ffplayout_lib::utils::PlayoutConfig;
|
use ffplayout_lib::utils::{time_to_sec, PlayoutConfig};
|
||||||
|
|
||||||
#[derive(Clone, Eq, PartialEq)]
|
#[derive(Clone, Eq, PartialEq)]
|
||||||
pub enum Role {
|
pub enum Role {
|
||||||
@ -48,8 +50,8 @@ pub struct GlobalSettings {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl GlobalSettings {
|
impl GlobalSettings {
|
||||||
async fn new() -> Self {
|
async fn new(conn: &Pool<Sqlite>) -> Self {
|
||||||
let global_settings = select_global();
|
let global_settings = select_global(conn);
|
||||||
|
|
||||||
match global_settings.await {
|
match global_settings.await {
|
||||||
Ok(g) => g,
|
Ok(g) => g,
|
||||||
@ -66,8 +68,8 @@ impl GlobalSettings {
|
|||||||
|
|
||||||
static INSTANCE: OnceCell<GlobalSettings> = OnceCell::new();
|
static INSTANCE: OnceCell<GlobalSettings> = OnceCell::new();
|
||||||
|
|
||||||
pub async fn init_config() {
|
pub async fn init_config(conn: &Pool<Sqlite>) {
|
||||||
let config = GlobalSettings::new().await;
|
let config = GlobalSettings::new(conn).await;
|
||||||
INSTANCE.set(config).unwrap();
|
INSTANCE.set(config).unwrap();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -88,7 +90,7 @@ pub fn db_path() -> Result<String, Box<dyn std::error::Error>> {
|
|||||||
Ok(db_path)
|
Ok(db_path)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn run_args(mut args: Args) -> Result<(), i32> {
|
pub async fn run_args(conn: &Pool<Sqlite>, mut args: Args) -> Result<(), i32> {
|
||||||
if !args.init && args.listen.is_none() && !args.ask && args.username.is_none() {
|
if !args.init && args.listen.is_none() && !args.ask && args.username.is_none() {
|
||||||
error!("Wrong number of arguments! Run ffpapi --help for more information.");
|
error!("Wrong number of arguments! Run ffpapi --help for more information.");
|
||||||
|
|
||||||
@ -96,7 +98,7 @@ pub async fn run_args(mut args: Args) -> Result<(), i32> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if args.init {
|
if args.init {
|
||||||
if let Err(e) = db_init(args.domain).await {
|
if let Err(e) = db_init(conn, args.domain).await {
|
||||||
panic!("{e}");
|
panic!("{e}");
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -160,7 +162,7 @@ pub async fn run_args(mut args: Args) -> Result<(), i32> {
|
|||||||
token: None,
|
token: None,
|
||||||
};
|
};
|
||||||
|
|
||||||
if let Err(e) = insert_user(user).await {
|
if let Err(e) = insert_user(conn, user).await {
|
||||||
error!("{e}");
|
error!("{e}");
|
||||||
return Err(1);
|
return Err(1);
|
||||||
};
|
};
|
||||||
@ -175,13 +177,18 @@ pub async fn run_args(mut args: Args) -> Result<(), i32> {
|
|||||||
|
|
||||||
pub fn read_playout_config(path: &str) -> Result<PlayoutConfig, Box<dyn Error>> {
|
pub fn read_playout_config(path: &str) -> Result<PlayoutConfig, Box<dyn Error>> {
|
||||||
let file = File::open(path)?;
|
let file = File::open(path)?;
|
||||||
let config: PlayoutConfig = serde_yaml::from_reader(file)?;
|
let mut config: PlayoutConfig = serde_yaml::from_reader(file)?;
|
||||||
|
|
||||||
|
config.playlist.start_sec = Some(time_to_sec(&config.playlist.day_start));
|
||||||
|
|
||||||
Ok(config)
|
Ok(config)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn playout_config(channel_id: &i32) -> Result<(PlayoutConfig, Channel), ServiceError> {
|
pub async fn playout_config(
|
||||||
if let Ok(channel) = select_channel(channel_id).await {
|
conn: &Pool<Sqlite>,
|
||||||
|
channel_id: &i32,
|
||||||
|
) -> Result<(PlayoutConfig, Channel), ServiceError> {
|
||||||
|
if let Ok(channel) = select_channel(conn, channel_id).await {
|
||||||
if let Ok(config) = read_playout_config(&channel.config_path.clone()) {
|
if let Ok(config) = read_playout_config(&channel.config_path.clone()) {
|
||||||
return Ok((config, channel));
|
return Ok((config, channel));
|
||||||
}
|
}
|
||||||
@ -192,8 +199,12 @@ pub async fn playout_config(channel_id: &i32) -> Result<(PlayoutConfig, Channel)
|
|||||||
))
|
))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn read_log_file(channel_id: &i32, date: &str) -> Result<String, ServiceError> {
|
pub async fn read_log_file(
|
||||||
if let Ok(channel) = select_channel(channel_id).await {
|
conn: &Pool<Sqlite>,
|
||||||
|
channel_id: &i32,
|
||||||
|
date: &str,
|
||||||
|
) -> Result<String, ServiceError> {
|
||||||
|
if let Ok(channel) = select_channel(conn, channel_id).await {
|
||||||
let mut date_str = "".to_string();
|
let mut date_str = "".to_string();
|
||||||
|
|
||||||
if !date.is_empty() {
|
if !date.is_empty() {
|
||||||
@ -234,3 +245,22 @@ pub fn local_utc_offset() -> i32 {
|
|||||||
|
|
||||||
utc_offset
|
utc_offset
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn naive_date_time_from_str<'de, D>(deserializer: D) -> Result<NaiveDateTime, D::Error>
|
||||||
|
where
|
||||||
|
D: Deserializer<'de>,
|
||||||
|
{
|
||||||
|
let s: String = Deserialize::deserialize(deserializer)?;
|
||||||
|
|
||||||
|
match NaiveDateTime::parse_from_str(&s, "%Y-%m-%dT%H:%M:%S") {
|
||||||
|
Ok(date_time) => Ok(date_time),
|
||||||
|
Err(e) => {
|
||||||
|
if e.kind() == ParseErrorKind::TooShort {
|
||||||
|
NaiveDateTime::parse_from_str(&format!("{s}T00:00:00"), "%Y-%m-%dT%H:%M:%S")
|
||||||
|
.map_err(de::Error::custom)
|
||||||
|
} else {
|
||||||
|
NaiveDateTime::parse_from_str(&s, "%Y-%m-%dT%H:%M:%S%#z").map_err(de::Error::custom)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -1,14 +1,19 @@
|
|||||||
use std::{fs, path::PathBuf};
|
use std::{fs, path::PathBuf};
|
||||||
|
|
||||||
use simplelog::*;
|
use simplelog::*;
|
||||||
|
use sqlx::{Pool, Sqlite};
|
||||||
|
|
||||||
use crate::utils::{errors::ServiceError, playout_config};
|
use crate::utils::{errors::ServiceError, playout_config};
|
||||||
use ffplayout_lib::utils::{
|
use ffplayout_lib::utils::{
|
||||||
generate_playlist as playlist_generator, json_reader, json_writer, JsonPlaylist,
|
generate_playlist as playlist_generator, json_reader, json_writer, JsonPlaylist,
|
||||||
};
|
};
|
||||||
|
|
||||||
pub async fn read_playlist(id: i32, date: String) -> Result<JsonPlaylist, ServiceError> {
|
pub async fn read_playlist(
|
||||||
let (config, _) = playout_config(&id).await?;
|
conn: &Pool<Sqlite>,
|
||||||
|
id: i32,
|
||||||
|
date: String,
|
||||||
|
) -> Result<JsonPlaylist, ServiceError> {
|
||||||
|
let (config, _) = playout_config(conn, &id).await?;
|
||||||
let mut playlist_path = PathBuf::from(&config.playlist.path);
|
let mut playlist_path = PathBuf::from(&config.playlist.path);
|
||||||
let d: Vec<&str> = date.split('-').collect();
|
let d: Vec<&str> = date.split('-').collect();
|
||||||
playlist_path = playlist_path
|
playlist_path = playlist_path
|
||||||
@ -23,8 +28,12 @@ pub async fn read_playlist(id: i32, date: String) -> Result<JsonPlaylist, Servic
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn write_playlist(id: i32, json_data: JsonPlaylist) -> Result<String, ServiceError> {
|
pub async fn write_playlist(
|
||||||
let (config, _) = playout_config(&id).await?;
|
conn: &Pool<Sqlite>,
|
||||||
|
id: i32,
|
||||||
|
json_data: JsonPlaylist,
|
||||||
|
) -> Result<String, ServiceError> {
|
||||||
|
let (config, _) = playout_config(conn, &id).await?;
|
||||||
let date = json_data.date.clone();
|
let date = json_data.date.clone();
|
||||||
let mut playlist_path = PathBuf::from(&config.playlist.path);
|
let mut playlist_path = PathBuf::from(&config.playlist.path);
|
||||||
let d: Vec<&str> = date.split('-').collect();
|
let d: Vec<&str> = date.split('-').collect();
|
||||||
@ -68,8 +77,12 @@ pub async fn write_playlist(id: i32, json_data: JsonPlaylist) -> Result<String,
|
|||||||
Err(ServiceError::InternalServerError)
|
Err(ServiceError::InternalServerError)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn generate_playlist(id: i32, date: String) -> Result<JsonPlaylist, ServiceError> {
|
pub async fn generate_playlist(
|
||||||
let (mut config, channel) = playout_config(&id).await?;
|
conn: &Pool<Sqlite>,
|
||||||
|
id: i32,
|
||||||
|
date: String,
|
||||||
|
) -> Result<JsonPlaylist, ServiceError> {
|
||||||
|
let (mut config, channel) = playout_config(conn, &id).await?;
|
||||||
config.general.generate = Some(vec![date.clone()]);
|
config.general.generate = Some(vec![date.clone()]);
|
||||||
|
|
||||||
match playlist_generator(&config, Some(channel.name)) {
|
match playlist_generator(&config, Some(channel.name)) {
|
||||||
@ -89,8 +102,8 @@ pub async fn generate_playlist(id: i32, date: String) -> Result<JsonPlaylist, Se
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn delete_playlist(id: i32, date: &str) -> Result<(), ServiceError> {
|
pub async fn delete_playlist(conn: &Pool<Sqlite>, id: i32, date: &str) -> Result<(), ServiceError> {
|
||||||
let (config, _) = playout_config(&id).await?;
|
let (config, _) = playout_config(conn, &id).await?;
|
||||||
let mut playlist_path = PathBuf::from(&config.playlist.path);
|
let mut playlist_path = PathBuf::from(&config.playlist.path);
|
||||||
let d: Vec<&str> = date.split('-').collect();
|
let d: Vec<&str> = date.split('-').collect();
|
||||||
playlist_path = playlist_path
|
playlist_path = playlist_path
|
||||||
|
@ -4,7 +4,7 @@ description = "24/7 playout based on rust and ffmpeg"
|
|||||||
license = "GPL-3.0"
|
license = "GPL-3.0"
|
||||||
authors = ["Jonathan Baecker jonbae77@gmail.com"]
|
authors = ["Jonathan Baecker jonbae77@gmail.com"]
|
||||||
readme = "README.md"
|
readme = "README.md"
|
||||||
version = "0.16.3"
|
version = "0.16.4"
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
default-run = "ffplayout"
|
default-run = "ffplayout"
|
||||||
|
|
||||||
|
@ -1 +1 @@
|
|||||||
Subproject commit 535624d81bc58721314e7c392eafe8614b0384f8
|
Subproject commit 269d3f0306a230805f7a19477163631c07bbd0a3
|
@ -4,7 +4,7 @@ description = "Library for ffplayout"
|
|||||||
license = "GPL-3.0"
|
license = "GPL-3.0"
|
||||||
authors = ["Jonathan Baecker jonbae77@gmail.com"]
|
authors = ["Jonathan Baecker jonbae77@gmail.com"]
|
||||||
readme = "README.md"
|
readme = "README.md"
|
||||||
version = "0.16.3"
|
version = "0.16.4"
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
|
@ -14,41 +14,12 @@ use std::{
|
|||||||
sync::{atomic::AtomicUsize, Arc, Mutex},
|
sync::{atomic::AtomicUsize, Arc, Mutex},
|
||||||
};
|
};
|
||||||
|
|
||||||
use chrono::{Duration, NaiveDate};
|
|
||||||
use simplelog::*;
|
use simplelog::*;
|
||||||
|
|
||||||
use super::folder::FolderSource;
|
use super::folder::FolderSource;
|
||||||
use crate::utils::{json_serializer::JsonPlaylist, time_to_sec, Media, PlayoutConfig};
|
use crate::utils::{
|
||||||
|
get_date_range, json_serializer::JsonPlaylist, time_to_sec, Media, PlayoutConfig,
|
||||||
/// Generate a vector with dates, from given range.
|
};
|
||||||
fn get_date_range(date_range: &[String]) -> Vec<String> {
|
|
||||||
let mut range = vec![];
|
|
||||||
|
|
||||||
let start = match NaiveDate::parse_from_str(&date_range[0], "%Y-%m-%d") {
|
|
||||||
Ok(s) => s,
|
|
||||||
Err(_) => {
|
|
||||||
error!("date format error in: <yellow>{:?}</>", date_range[0]);
|
|
||||||
exit(1);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
let end = match NaiveDate::parse_from_str(&date_range[2], "%Y-%m-%d") {
|
|
||||||
Ok(e) => e,
|
|
||||||
Err(_) => {
|
|
||||||
error!("date format error in: <yellow>{:?}</>", date_range[2]);
|
|
||||||
exit(1);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
let duration = end.signed_duration_since(start);
|
|
||||||
let days = duration.num_days() + 1;
|
|
||||||
|
|
||||||
for day in 0..days {
|
|
||||||
range.push((start + Duration::days(day)).format("%Y-%m-%d").to_string());
|
|
||||||
}
|
|
||||||
|
|
||||||
range
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Generate playlists
|
/// Generate playlists
|
||||||
pub fn generate_playlist(
|
pub fn generate_playlist(
|
||||||
|
@ -797,6 +797,36 @@ pub fn test_tcp_port(url: &str) -> bool {
|
|||||||
false
|
false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Generate a vector with dates, from given range.
|
||||||
|
pub fn get_date_range(date_range: &[String]) -> Vec<String> {
|
||||||
|
let mut range = vec![];
|
||||||
|
|
||||||
|
let start = match NaiveDate::parse_from_str(&date_range[0], "%Y-%m-%d") {
|
||||||
|
Ok(s) => s,
|
||||||
|
Err(_) => {
|
||||||
|
error!("date format error in: <yellow>{:?}</>", date_range[0]);
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
let end = match NaiveDate::parse_from_str(&date_range[2], "%Y-%m-%d") {
|
||||||
|
Ok(e) => e,
|
||||||
|
Err(_) => {
|
||||||
|
error!("date format error in: <yellow>{:?}</>", date_range[2]);
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
let duration = end.signed_duration_since(start);
|
||||||
|
let days = duration.num_days() + 1;
|
||||||
|
|
||||||
|
for day in 0..days {
|
||||||
|
range.push((start + Duration::days(day)).format("%Y-%m-%d").to_string());
|
||||||
|
}
|
||||||
|
|
||||||
|
range
|
||||||
|
}
|
||||||
|
|
||||||
pub fn home_dir() -> Option<PathBuf> {
|
pub fn home_dir() -> Option<PathBuf> {
|
||||||
home_dir_inner()
|
home_dir_inner()
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user