Watch config file changes
This commit is contained in:
parent
142d400b8b
commit
d555a2e5f0
6 changed files with 246 additions and 33 deletions
186
Cargo.lock
generated
186
Cargo.lock
generated
|
@ -443,6 +443,15 @@ dependencies = [
|
|||
"cfg-if",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "crossbeam-channel"
|
||||
version = "0.5.13"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "33480d6946193aa8033910124896ca395333cae7e2d1113d1fef6c3272217df2"
|
||||
dependencies = [
|
||||
"crossbeam-utils",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "crossbeam-deque"
|
||||
version = "0.8.5"
|
||||
|
@ -631,6 +640,27 @@ dependencies = [
|
|||
"serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "file-id"
|
||||
version = "0.2.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6584280525fb2059cba3db2c04abf947a1a29a45ddae89f3870f8281704fafc9"
|
||||
dependencies = [
|
||||
"windows-sys 0.48.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "filetime"
|
||||
version = "0.2.25"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "35c0522e981e68cbfa8c3f978441a5f34b30b96e146b33cd3359176b50fe8586"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"libc",
|
||||
"libredox",
|
||||
"windows-sys 0.59.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "flate2"
|
||||
version = "1.0.31"
|
||||
|
@ -656,6 +686,15 @@ dependencies = [
|
|||
"percent-encoding",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "fsevent-sys"
|
||||
version = "4.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "76ee7a02da4d231650c7cea31349b889be2f45ddb3ef3032d2ec8185f6313fd2"
|
||||
dependencies = [
|
||||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "futures"
|
||||
version = "0.3.30"
|
||||
|
@ -985,12 +1024,52 @@ dependencies = [
|
|||
"hashbrown",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "inotify"
|
||||
version = "0.9.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f8069d3ec154eb856955c1c0fbffefbf5f3c40a104ec912d4797314c1801abff"
|
||||
dependencies = [
|
||||
"bitflags 1.3.2",
|
||||
"inotify-sys",
|
||||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "inotify-sys"
|
||||
version = "0.1.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e05c02b5e89bff3b946cedeca278abc628fe811e604f027c45a8aa3cf793d0eb"
|
||||
dependencies = [
|
||||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "itoa"
|
||||
version = "1.0.11"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "49f1f14873335454500d59611f1cf4a4b0f786f9ac11f4312a78e4cf2566695b"
|
||||
|
||||
[[package]]
|
||||
name = "kqueue"
|
||||
version = "1.0.8"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7447f1ca1b7b563588a205fe93dea8df60fd981423a768bc1c0ded35ed147d0c"
|
||||
dependencies = [
|
||||
"kqueue-sys",
|
||||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "kqueue-sys"
|
||||
version = "1.0.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ed9625ffda8729b85e45cf04090035ac368927b8cebc34898e7c120f52e4838b"
|
||||
dependencies = [
|
||||
"bitflags 1.3.2",
|
||||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "lasso2"
|
||||
version = "0.8.2"
|
||||
|
@ -1024,12 +1103,33 @@ version = "0.2.155"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "97b3888a4aecf77e811145cadf6eef5901f4782c53886191b2f693f24761847c"
|
||||
|
||||
[[package]]
|
||||
name = "libredox"
|
||||
version = "0.1.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c0ff37bd590ca25063e35af745c343cb7a0271906fb7b37e4813e8f79f00268d"
|
||||
dependencies = [
|
||||
"bitflags 2.6.0",
|
||||
"libc",
|
||||
"redox_syscall",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "linux-raw-sys"
|
||||
version = "0.4.14"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "78b3ae25bc7c8c38cec158d1f2757ee79e9b3740fbc7ccf0e59e4b08d793fa89"
|
||||
|
||||
[[package]]
|
||||
name = "lock_api"
|
||||
version = "0.4.12"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "07af8b9cdd281b7915f413fa73f29ebd5d55d0d3f0155584dade1ff18cea1b17"
|
||||
dependencies = [
|
||||
"autocfg",
|
||||
"scopeguard",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "log"
|
||||
version = "0.4.22"
|
||||
|
@ -1094,6 +1194,18 @@ dependencies = [
|
|||
"simd-adler32",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "mio"
|
||||
version = "0.8.11"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a4a650543ca06a924e8b371db273b2756685faae30f8487da1b56505a8f78b0c"
|
||||
dependencies = [
|
||||
"libc",
|
||||
"log",
|
||||
"wasi",
|
||||
"windows-sys 0.48.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "mio"
|
||||
version = "1.0.1"
|
||||
|
@ -1212,6 +1324,38 @@ version = "0.2.0"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2bf50223579dc7cdcfb3bfcacf7069ff68243f8c363f62ffa99cf000a6b9c451"
|
||||
|
||||
[[package]]
|
||||
name = "notify"
|
||||
version = "6.1.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6205bd8bb1e454ad2e27422015fb5e4f2bcc7e08fa8f27058670d208324a4d2d"
|
||||
dependencies = [
|
||||
"bitflags 2.6.0",
|
||||
"crossbeam-channel",
|
||||
"filetime",
|
||||
"fsevent-sys",
|
||||
"inotify",
|
||||
"kqueue",
|
||||
"libc",
|
||||
"log",
|
||||
"mio 0.8.11",
|
||||
"walkdir",
|
||||
"windows-sys 0.48.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "notify-debouncer-full"
|
||||
version = "0.3.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "49f5dab59c348b9b50cf7f261960a20e389feb2713636399cd9082cd4b536154"
|
||||
dependencies = [
|
||||
"file-id",
|
||||
"log",
|
||||
"notify",
|
||||
"parking_lot",
|
||||
"walkdir",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "num-complex"
|
||||
version = "0.4.6"
|
||||
|
@ -1307,6 +1451,29 @@ dependencies = [
|
|||
"zeroize",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "parking_lot"
|
||||
version = "0.12.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f1bf18183cf54e8d6059647fc3063646a1801cf30896933ec2311622cc4b9a27"
|
||||
dependencies = [
|
||||
"lock_api",
|
||||
"parking_lot_core",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "parking_lot_core"
|
||||
version = "0.9.10"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1e401f977ab385c9e4e3ab30627d6f26d00e2c73eef317493c4ec6d468726cf8"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"libc",
|
||||
"redox_syscall",
|
||||
"smallvec",
|
||||
"windows-targets 0.52.6",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "password-hash"
|
||||
version = "0.4.2"
|
||||
|
@ -1413,6 +1580,8 @@ dependencies = [
|
|||
"native_db",
|
||||
"native_model",
|
||||
"nohash-hasher",
|
||||
"notify",
|
||||
"notify-debouncer-full",
|
||||
"num_cpus",
|
||||
"opus_headers",
|
||||
"pbkdf2",
|
||||
|
@ -1588,6 +1757,15 @@ dependencies = [
|
|||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "redox_syscall"
|
||||
version = "0.5.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9b6dfecf2c74bce2466cabf93f6664d6998a69eb21e39f4207930065b27b771f"
|
||||
dependencies = [
|
||||
"bitflags 2.6.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "regex"
|
||||
version = "1.10.6"
|
||||
|
@ -1754,6 +1932,12 @@ dependencies = [
|
|||
"winapi-util",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "scopeguard"
|
||||
version = "1.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49"
|
||||
|
||||
[[package]]
|
||||
name = "sd-notify"
|
||||
version = "0.4.2"
|
||||
|
@ -2272,7 +2456,7 @@ dependencies = [
|
|||
"backtrace",
|
||||
"bytes",
|
||||
"libc",
|
||||
"mio",
|
||||
"mio 1.0.1",
|
||||
"pin-project-lite",
|
||||
"socket2",
|
||||
"tokio-macros",
|
||||
|
|
|
@ -32,6 +32,8 @@ mp4ameta = "0.11.0"
|
|||
native_db = { git = "https://github.com/vincent-herlemont/native_db" }
|
||||
native_model = "0.4.19"
|
||||
nohash-hasher = "0.2.0"
|
||||
notify = { version = "6.1.1", default-features = false }
|
||||
notify-debouncer-full = { version = "0.3.1", default-features = false }
|
||||
num_cpus = "1.14.0"
|
||||
opus_headers = "0.1.2"
|
||||
pbkdf2 = "0.11"
|
||||
|
|
|
@ -30,6 +30,8 @@ pub enum Error {
|
|||
#[error("Filesystem error for `{0}`: `{1}`")]
|
||||
Io(PathBuf, std::io::Error),
|
||||
#[error(transparent)]
|
||||
FileWatch(#[from] notify::Error),
|
||||
#[error(transparent)]
|
||||
Ape(#[from] ape::Error),
|
||||
#[error("ID3 error in `{0}`: `{1}`")]
|
||||
Id3(PathBuf, id3::Error),
|
||||
|
|
|
@ -4,6 +4,9 @@ use std::{
|
|||
time::Duration,
|
||||
};
|
||||
|
||||
use log::{error, info};
|
||||
use notify::{RecommendedWatcher, RecursiveMode, Watcher};
|
||||
use notify_debouncer_full::{DebounceEventResult, Debouncer, FileIdMap};
|
||||
use regex::Regex;
|
||||
use tokio::sync::RwLock;
|
||||
|
||||
|
@ -70,42 +73,64 @@ pub struct Manager {
|
|||
config_file_path: PathBuf,
|
||||
config: Arc<tokio::sync::RwLock<Config>>,
|
||||
auth_secret: auth::Secret,
|
||||
#[allow(dead_code)]
|
||||
file_watcher: Arc<Debouncer<RecommendedWatcher, FileIdMap>>,
|
||||
}
|
||||
|
||||
impl Manager {
|
||||
pub async fn new(config_file_path: &Path, auth_secret: auth::Secret) -> Result<Self, Error> {
|
||||
let config = {
|
||||
if tokio::fs::try_exists(config_file_path)
|
||||
.await
|
||||
.map_err(|e| Error::Io(config_file_path.to_owned(), e))?
|
||||
{
|
||||
let config_content = tokio::fs::read_to_string(config_file_path)
|
||||
.await
|
||||
.map_err(|e| Error::Io(config_file_path.to_owned(), e))?;
|
||||
let config = toml::de::from_str::<storage::Config>(&config_content)
|
||||
.map_err(Error::ConfigDeserialization)?;
|
||||
config.try_into()?
|
||||
} else {
|
||||
Config::default()
|
||||
}
|
||||
};
|
||||
tokio::fs::File::create_new(config_file_path).await.ok();
|
||||
|
||||
let (sender, receiver) = std::sync::mpsc::channel::<DebounceEventResult>();
|
||||
let mut debouncer =
|
||||
notify_debouncer_full::new_debouncer(Duration::from_secs(1), None, sender)?;
|
||||
|
||||
debouncer
|
||||
.watcher()
|
||||
.watch(&config_file_path, RecursiveMode::NonRecursive)?;
|
||||
|
||||
let manager = Self {
|
||||
config_file_path: config_file_path.to_owned(),
|
||||
config: Arc::new(RwLock::new(config)),
|
||||
config: Arc::new(RwLock::new(Config::default())),
|
||||
auth_secret,
|
||||
file_watcher: Arc::new(debouncer),
|
||||
};
|
||||
|
||||
if !tokio::fs::try_exists(config_file_path)
|
||||
.await
|
||||
.map_err(|e| Error::Io(config_file_path.to_owned(), e))?
|
||||
{
|
||||
manager.save_config().await?;
|
||||
}
|
||||
tokio::task::spawn({
|
||||
let manager = manager.clone();
|
||||
async move {
|
||||
loop {
|
||||
match receiver.recv() {
|
||||
Err(_) => break,
|
||||
Ok(_) => {
|
||||
if let Err(e) = manager.reload_config().await {
|
||||
error!("Configuration error: {e}");
|
||||
} else {
|
||||
info!("Sucessfully applied configuration change");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
manager.reload_config().await?;
|
||||
|
||||
Ok(manager)
|
||||
}
|
||||
|
||||
async fn reload_config(&self) -> Result<(), Error> {
|
||||
let config = Self::read_config(&self.config_file_path).await?;
|
||||
self.apply_config(config).await
|
||||
}
|
||||
|
||||
async fn read_config(config_file_path: &Path) -> Result<storage::Config, Error> {
|
||||
let config_content = tokio::fs::read_to_string(config_file_path)
|
||||
.await
|
||||
.map_err(|e| Error::Io(config_file_path.to_owned(), e))?;
|
||||
toml::de::from_str::<storage::Config>(&config_content).map_err(Error::ConfigDeserialization)
|
||||
}
|
||||
|
||||
async fn save_config(&self) -> Result<(), Error> {
|
||||
let serialized = toml::ser::to_string_pretty::<storage::Config>(
|
||||
&self.config.read().await.clone().into(),
|
||||
|
@ -117,13 +142,10 @@ impl Manager {
|
|||
Ok(())
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
pub async fn apply(&self, config: storage::Config) -> Result<(), Error> {
|
||||
self.mutate_fallible(|c| {
|
||||
*c = config.try_into()?;
|
||||
Ok(())
|
||||
})
|
||||
.await
|
||||
pub async fn apply_config(&self, new_config: storage::Config) -> Result<(), Error> {
|
||||
let mut config = self.config.write().await;
|
||||
*config = new_config.try_into()?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn mutate<F: FnOnce(&mut Config)>(&self, op: F) -> Result<(), Error> {
|
||||
|
@ -138,8 +160,10 @@ impl Manager {
|
|||
&self,
|
||||
op: F,
|
||||
) -> Result<(), Error> {
|
||||
let mut config = self.config.write().await;
|
||||
op(&mut config)?;
|
||||
{
|
||||
let mut config = self.config.write().await;
|
||||
op(&mut config)?;
|
||||
}
|
||||
self.save_config().await?;
|
||||
Ok(())
|
||||
}
|
||||
|
|
|
@ -56,7 +56,7 @@ impl ContextBuilder {
|
|||
.unwrap();
|
||||
let playlist_manager = playlist::Manager::new(ndb_manager.clone());
|
||||
|
||||
config_manager.apply(self.config).await.unwrap();
|
||||
config_manager.apply_config(self.config).await.unwrap();
|
||||
|
||||
Context {
|
||||
index_manager,
|
||||
|
|
|
@ -92,6 +92,7 @@ impl From<app::Error> for APIError {
|
|||
app::Error::ThreadJoining(_) => APIError::Internal,
|
||||
|
||||
app::Error::Io(p, e) => APIError::Io(p, e),
|
||||
app::Error::FileWatch(_) => APIError::Internal,
|
||||
app::Error::Ape(_) => APIError::Internal,
|
||||
app::Error::Id3(p, e) => APIError::ThumbnailId3Decoding(p, e),
|
||||
app::Error::Metaflac(p, e) => APIError::ThumbnailFlacDecoding(p, e),
|
||||
|
|
Loading…
Add table
Reference in a new issue