added hot-reload of the config file

master
Ondřej Hruška 5 years ago
parent fa1979c2bb
commit 46c01c6c38
Signed by: MightyPork
GPG Key ID: 2C5FD5035250423D
  1. 107
      Cargo.lock
  2. 1
      Cargo.toml
  3. 52
      src/brainz.rs
  4. 150
      src/config_file.rs
  5. 128
      src/main.rs

107
Cargo.lock generated

@ -473,6 +473,29 @@ name = "indexmap"
version = "1.0.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "inotify"
version = "0.7.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"bitflags 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)",
"futures 0.1.25 (registry+https://github.com/rust-lang/crates.io-index)",
"inotify-sys 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)",
"libc 0.2.48 (registry+https://github.com/rust-lang/crates.io-index)",
"mio 0.6.16 (registry+https://github.com/rust-lang/crates.io-index)",
"tokio 0.1.15 (registry+https://github.com/rust-lang/crates.io-index)",
"tokio-io 0.1.11 (registry+https://github.com/rust-lang/crates.io-index)",
"tokio-reactor 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "inotify-sys"
version = "0.1.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"libc 0.2.48 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "iovec"
version = "0.1.2"
@ -598,6 +621,16 @@ dependencies = [
"winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "mio-uds"
version = "0.6.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"iovec 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)",
"libc 0.2.48 (registry+https://github.com/rust-lang/crates.io-index)",
"mio 0.6.16 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "miow"
version = "0.2.1"
@ -925,6 +958,7 @@ version = "0.1.0"
dependencies = [
"env_logger 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)",
"failure 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)",
"inotify 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)",
"log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)",
"mpris 1.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
"percent-encoding 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)",
@ -1257,12 +1291,28 @@ dependencies = [
"futures 0.1.25 (registry+https://github.com/rust-lang/crates.io-index)",
"mio 0.6.16 (registry+https://github.com/rust-lang/crates.io-index)",
"num_cpus 1.10.0 (registry+https://github.com/rust-lang/crates.io-index)",
"tokio-codec 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
"tokio-current-thread 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)",
"tokio-executor 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)",
"tokio-fs 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)",
"tokio-io 0.1.11 (registry+https://github.com/rust-lang/crates.io-index)",
"tokio-reactor 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)",
"tokio-sync 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)",
"tokio-tcp 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)",
"tokio-threadpool 0.1.11 (registry+https://github.com/rust-lang/crates.io-index)",
"tokio-timer 0.2.10 (registry+https://github.com/rust-lang/crates.io-index)",
"tokio-udp 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)",
"tokio-uds 0.2.5 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "tokio-codec"
version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"bytes 0.4.11 (registry+https://github.com/rust-lang/crates.io-index)",
"futures 0.1.25 (registry+https://github.com/rust-lang/crates.io-index)",
"tokio-io 0.1.11 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
@ -1283,6 +1333,16 @@ dependencies = [
"futures 0.1.25 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "tokio-fs"
version = "0.1.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"futures 0.1.25 (registry+https://github.com/rust-lang/crates.io-index)",
"tokio-io 0.1.11 (registry+https://github.com/rust-lang/crates.io-index)",
"tokio-threadpool 0.1.11 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "tokio-io"
version = "0.1.11"
@ -1310,6 +1370,14 @@ dependencies = [
"tokio-io 0.1.11 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "tokio-sync"
version = "0.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"futures 0.1.25 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "tokio-tcp"
version = "0.1.3"
@ -1351,6 +1419,37 @@ dependencies = [
"tokio-executor 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "tokio-udp"
version = "0.1.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"bytes 0.4.11 (registry+https://github.com/rust-lang/crates.io-index)",
"futures 0.1.25 (registry+https://github.com/rust-lang/crates.io-index)",
"log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)",
"mio 0.6.16 (registry+https://github.com/rust-lang/crates.io-index)",
"tokio-codec 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
"tokio-io 0.1.11 (registry+https://github.com/rust-lang/crates.io-index)",
"tokio-reactor 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "tokio-uds"
version = "0.2.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"bytes 0.4.11 (registry+https://github.com/rust-lang/crates.io-index)",
"futures 0.1.25 (registry+https://github.com/rust-lang/crates.io-index)",
"iovec 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)",
"libc 0.2.48 (registry+https://github.com/rust-lang/crates.io-index)",
"log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)",
"mio 0.6.16 (registry+https://github.com/rust-lang/crates.io-index)",
"mio-uds 0.6.7 (registry+https://github.com/rust-lang/crates.io-index)",
"tokio-codec 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
"tokio-io 0.1.11 (registry+https://github.com/rust-lang/crates.io-index)",
"tokio-reactor 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "try-lock"
version = "0.2.2"
@ -1562,6 +1661,8 @@ dependencies = [
"checksum ident_case 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "3c9826188e666f2ed92071d2dadef6edc430b11b158b5b2b3f4babbcc891eaaa"
"checksum idna 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "38f09e0f0b1fb55fdee1f17470ad800da77af5186a1a76c026b679358b7e844e"
"checksum indexmap 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "7e81a7c05f79578dbc15793d8b619db9ba32b4577003ef3af1a91c416798c58d"
"checksum inotify 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "24e40d6fd5d64e2082e0c796495c8ef5ad667a96d03e5aaa0becfd9d47bcbfb8"
"checksum inotify-sys 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "e74a1aa87c59aeff6ef2cc2fa62d41bc43f54952f55652656b18a02fd5e356c0"
"checksum iovec 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "dbe6e417e7d0975db6512b90796e8ce223145ac4e33c377e4a42882a0e88bb08"
"checksum itoa 0.4.3 (registry+https://github.com/rust-lang/crates.io-index)" = "1306f3464951f30e30d12373d31c79fbd52d236e5e896fd92f96ec7babbbe60b"
"checksum kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "7507624b29483431c0ba2d82aece8ca6cdba9382bff4ddd0f7490560c056098d"
@ -1578,6 +1679,7 @@ dependencies = [
"checksum mime 0.3.13 (registry+https://github.com/rust-lang/crates.io-index)" = "3e27ca21f40a310bd06d9031785f4801710d566c184a6e15bad4f1d9b65f9425"
"checksum mime_guess 2.0.0-alpha.6 (registry+https://github.com/rust-lang/crates.io-index)" = "30de2e4613efcba1ec63d8133f344076952090c122992a903359be5a4f99c3ed"
"checksum mio 0.6.16 (registry+https://github.com/rust-lang/crates.io-index)" = "71646331f2619b1026cc302f87a2b8b648d5c6dd6937846a16cc8ce0f347f432"
"checksum mio-uds 0.6.7 (registry+https://github.com/rust-lang/crates.io-index)" = "966257a94e196b11bb43aca423754d87429960a768de9414f3691d6957abf125"
"checksum miow 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "8c1f2f3b1cf331de6896aabf6e9d55dca90356cc9960cca7eaaf408a355ae919"
"checksum mpris 1.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "b8a00ae9537d355ab18532a7cf325c7177fb0dcb82851c82228fcaf9815f698d"
"checksum native-tls 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "ff8e08de0070bbf4c31f452ea2a70db092f36f6f2e4d897adf5674477d488fb2"
@ -1651,13 +1753,18 @@ dependencies = [
"checksum thread_local 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)" = "c6b53e329000edc2b34dbe8545fd20e55a333362d0a321909685a19bd28c3f1b"
"checksum time 0.1.42 (registry+https://github.com/rust-lang/crates.io-index)" = "db8dcfca086c1143c9270ac42a2bbd8a7ee477b78ac8e45b19abfb0cbede4b6f"
"checksum tokio 0.1.15 (registry+https://github.com/rust-lang/crates.io-index)" = "e0500b88064f08bebddd0c0bed39e19f5c567a5f30975bee52b0c0d3e2eeb38c"
"checksum tokio-codec 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "5c501eceaf96f0e1793cf26beb63da3d11c738c4a943fdf3746d81d64684c39f"
"checksum tokio-current-thread 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)" = "331c8acc267855ec06eb0c94618dcbbfea45bed2d20b77252940095273fb58f6"
"checksum tokio-executor 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)" = "30c6dbf2d1ad1de300b393910e8a3aa272b724a400b6531da03eed99e329fbf0"
"checksum tokio-fs 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "0e9cbbc8a3698b7ab652340f46633364f9eaa928ddaaee79d8b8f356dd79a09d"
"checksum tokio-io 0.1.11 (registry+https://github.com/rust-lang/crates.io-index)" = "b53aeb9d3f5ccf2ebb29e19788f96987fa1355f8fe45ea193928eaaaf3ae820f"
"checksum tokio-reactor 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)" = "afbcdb0f0d2a1e4c440af82d7bbf0bf91a8a8c0575bcd20c05d15be7e9d3a02f"
"checksum tokio-sync 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "c73850a5ad497d73ccfcfc0ffb494a4502d93f35cb475cfeef4fcf2916d26040"
"checksum tokio-tcp 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "1d14b10654be682ac43efee27401d792507e30fd8d26389e1da3b185de2e4119"
"checksum tokio-threadpool 0.1.11 (registry+https://github.com/rust-lang/crates.io-index)" = "c3fd86cb15547d02daa2b21aadaf4e37dee3368df38a526178a5afa3c034d2fb"
"checksum tokio-timer 0.2.10 (registry+https://github.com/rust-lang/crates.io-index)" = "2910970404ba6fa78c5539126a9ae2045d62e3713041e447f695f41405a120c6"
"checksum tokio-udp 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "66268575b80f4a4a710ef83d087fdfeeabdce9b74c797535fbac18a2cb906e92"
"checksum tokio-uds 0.2.5 (registry+https://github.com/rust-lang/crates.io-index)" = "037ffc3ba0e12a0ab4aca92e5234e0dedeb48fddf6ccd260f1f150a36a9f2445"
"checksum try-lock 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "e604eb7b43c06650e854be16a2a03155743d3752dd1c943f6829e26b7a36e382"
"checksum ucd-util 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "535c204ee4d8434478593480b8f86ab45ec9aae0e83c568ca81abf0fd0e88f86"
"checksum unicase 1.4.2 (registry+https://github.com/rust-lang/crates.io-index)" = "7f4765f83163b74f957c797ad9253caf97f103fb064d3999aea9568d09fc8a33"

@ -16,3 +16,4 @@ failure = "0.1.5"
log = "0.4"
env_logger = "0.6"
regex = "1.1.0"
inotify = "0.7.0"

@ -1,12 +1,12 @@
use percent_encoding::{utf8_percent_encode, DEFAULT_ENCODE_SET};
use failure::Error;
use percent_encoding::{utf8_percent_encode, DEFAULT_ENCODE_SET};
use regex::Regex;
use std::io::Read;
use std::time::Duration;
use regex::Regex;
#[derive(Serialize, Deserialize, Debug)]
struct MBArtistQueryResult {
created : String,
created: String,
count: i32,
offset: i32,
artists: Option<Vec<MBArtist>>,
@ -23,22 +23,31 @@ struct MBArtist {
id: String,
score: i32,
name: String,
#[serde(rename="sort-name")]
#[serde(rename = "sort-name")]
sort_name: String,
tags: Option<Vec<Tag>>
tags: Option<Vec<Tag>>,
}
const VERSION: &'static str = env!("CARGO_PKG_VERSION");
pub fn check_artist(config : &crate::Config, artist : &str) -> Result<bool, Error> {
pub fn check_artist(config: &crate::Config, artist: &str) -> Result<bool, Error> {
let query = utf8_percent_encode(&format!("\"{}\"", artist), DEFAULT_ENCODE_SET).to_string();
let url = format!("https://musicbrainz.org/ws/2/artist?query={}&fmt=json&inc=tags", query);
let url = format!(
"https://musicbrainz.org/ws/2/artist?query={}&fmt=json&inc=tags",
query
);
info!("Querying MusicBrainz for artist \"{}\", query URL: {}", artist, url);
info!(
"Querying MusicBrainz for artist \"{}\", query URL: {}",
artist, url
);
let mut resp = String::new();
let ua = format!("Bad Song Skipper / {v} ( https://git.ondrovo.com/MightyPork/rapblock )", v=VERSION);
let ua = format!(
"Bad Song Skipper / {v} ( https://git.ondrovo.com/MightyPork/rapblock )",
v = VERSION
);
debug!("Using UA: {}", ua);
@ -53,7 +62,7 @@ pub fn check_artist(config : &crate::Config, artist : &str) -> Result<bool, Erro
debug!("Resp: {}", resp);
let result : MBArtistQueryResult = serde_json::from_str(&resp)?;
let result: MBArtistQueryResult = serde_json::from_str(&resp)?;
info!("Response OK");
debug!("{:#?}", result);
@ -63,7 +72,10 @@ pub fn check_artist(config : &crate::Config, artist : &str) -> Result<bool, Erro
warn!("No results!");
return Err(failure::err_msg("Artist not found"));
} else {
info!("Got {} results, checking where score >= {}", result.count, config.artist_min_score);
info!(
"Got {} results, checking where score >= {}",
result.count, config.artist_min_score
);
let artists = result.artists.as_ref().unwrap();
let mut confidence = false;
@ -75,7 +87,7 @@ pub fn check_artist(config : &crate::Config, artist : &str) -> Result<bool, Erro
let tags = a.tags.as_ref().unwrap();
let as_vec: Vec<&String> = tags.iter().map(|t| &t.name).collect();
info!("Artist #{} - \"{}\" has tags: {:?}", an+1, a.name, as_vec);
info!("Artist #{} - \"{}\" has tags: {:?}", an + 1, a.name, as_vec);
'tags: for tag in as_vec {
if config.whitelist.tag.contains(tag) {
info!("+ Whitelisted tag \"{}\"", tag);
@ -87,17 +99,19 @@ pub fn check_artist(config : &crate::Config, artist : &str) -> Result<bool, Erro
info!("- Blacklisted tag \"{}\"", tag);
blacklisted = true;
} else {
'blacklist:
for t in config.blacklist.tag_partial.iter() {
'blacklist: for t in config.blacklist.tag_partial.iter() {
let re = Regex::new(&format!(r"\b{}\b", regex::escape(t)));
let matches = match re {
Ok(re) => re.is_match(tag),
Err(_) => tag.contains(t)
Err(_) => tag.contains(t),
};
if matches {
info!("- Blacklisted tag \"{}\" - due to substring \"{}\"", tag, t);
info!(
"- Blacklisted tag \"{}\" - due to substring \"{}\"",
tag, t
);
blacklisted = true;
break 'blacklist;
}
@ -113,7 +127,11 @@ pub fn check_artist(config : &crate::Config, artist : &str) -> Result<bool, Erro
info!("All tags OK");
confidence = true;
} else {
warn!("Artist #{} - \"{}\" has no tags, can't determine genre", an+1, a.name);
warn!(
"Artist #{} - \"{}\" has no tags, can't determine genre",
an + 1,
a.name
);
}
}
}

@ -0,0 +1,150 @@
use failure::Error;
use inotify::{Inotify, WatchMask, EventMask};
use serde::de::DeserializeOwned;
use std::fs::File;
use std::io::BufReader;
use std::path::Path;
use std::path::PathBuf;
/// Reloadable config file
pub struct ConfigFile<T> {
is_dummy : bool,
config: T,
path: PathBuf,
inotify: Option<Inotify>,
inotify_buffer: [u8; 1024],
}
impl<T> ConfigFile<T>
where
T: Default + std::fmt::Debug + DeserializeOwned,
{
pub fn borrow(&self) -> &T {
&self.config
}
/// Create a new reloadable config file
/// If loading fails, an error is printed and default values are be used
pub fn new<P: AsRef<Path>>(path: P) -> Result<Self, Error> {
let loaded = Self::load(&path);
if let Err(e) = &loaded {
eprintln!("Config file failed to load/parse: {}", e);
}
Ok(Self {
is_dummy: false,
config: loaded.unwrap_or(T::default()),
path: path.as_ref().to_owned(),
inotify: Self::setup_inotify(&path),
inotify_buffer: [0; 1024],
})
}
/// New without a backing file
#[allow(dead_code)]
pub fn new_from_defaults() -> Self {
Self {
is_dummy: true,
config: T::default(),
path: "".into(),
inotify: None,
inotify_buffer: [0; 1024]
}
}
/// Try to install inotify
fn setup_inotify<P: AsRef<Path>>(path: P) -> Option<Inotify> {
let inotify = Inotify::init();
if let Err(e) = inotify {
eprintln!("Failed to set up inotify for config file reload!\n{}", e);
return None;
}
let mut inotify = inotify.unwrap();
let rv = inotify.add_watch(&path, WatchMask::MODIFY | WatchMask::CLOSE);
match rv {
Err(e) => {
eprintln!("Failed to add inotify watch for config!\n{}", e);
None
}
Ok(_) => Some(inotify),
}
}
/// Update the config file if needed (there are inotify events)
/// - if inotify failed to init, or an error occurs trying to get events from it,
/// the 'def' value is used (true = reload if unsure)
pub fn update_if_needed(&mut self, def: bool) -> Result<bool, Error> {
if self.is_dummy {
debug!("Config is dummy, no path to reload.");
return Ok(false);
}
let needed = match self.inotify.as_mut() {
Some(ino) => {
let rv = ino.read_events(&mut self.inotify_buffer);
match rv {
Ok(evs) => {
let mut changed = false;
let mut need_reinstall = false;
for ev in evs {
if ev.mask.contains(EventMask::IGNORED) {
debug!("Inotify watcher got removed by OS??");
changed = true;
need_reinstall = true;
}
if ev.mask.contains(EventMask::MODIFY) {
changed = true;
}
}
if changed {
info!("Config file changes detected, reloading");
}
if need_reinstall {
debug!("Reinstalling inotify");
self.inotify = Self::setup_inotify(&self.path);
}
changed
}
_ => {
error!("Failed to get inotify events.");
def
}
}
}
None => def,
};
if needed {
self.update()?;
} else {
debug!("Config update not needed.");
}
Ok(needed)
}
/// load and parse the config file
fn load<P: AsRef<Path>>(path: P) -> Result<T, Error> {
info!("Loading config file @ {}", path.as_ref().to_str().unwrap_or("???"));
let file = File::open(&path)?;
let reader = BufReader::new(file);
let val : T = serde_json::from_reader(reader)?;
debug!("Loaded config:\n{:#?}", val);
Ok(val)
}
/// Reload unconditionally
pub fn update(&mut self) -> Result<(), Error> {
if self.is_dummy {
debug!("Config is dummy, no path to reload.");
return Ok(());
}
self.config = Self::load(&self.path)?;
Ok(())
}
}

@ -1,19 +1,19 @@
#[macro_use] extern crate serde_derive;
//#[macro_use] extern crate lazy_static;
#[macro_use] extern crate log;
#[macro_use] extern crate failure;
//#[macro_use] extern crate failure;
mod brainz;
use mpris::{PlayerFinder,Event};
use failure::Error;
use std::time::Duration;
use std::collections::HashSet;
use std::env;
use std::path::Path;
use std::fs::File;
use std::io::Read;
use crate::config_file::ConfigFile;
use std::collections::HashMap;
use mpris::PlayerFinder;
use std::time::Duration;
use mpris::Event;
use std::collections::HashSet;
use failure::Error;
mod brainz;
mod config_file;
#[derive(Serialize, Deserialize, Debug)]
#[serde(default)]
@ -35,7 +35,7 @@ impl Default for BlacklistConf {
"hip-hop".to_owned(),
"hip hop".to_owned(),
"hiphop".to_owned(),
"rap".to_owned()
"rap".to_owned(),
],
}
}
@ -54,7 +54,7 @@ impl Default for WhiteList {
fn default() -> Self {
Self {
tag: vec![],
artist: vec![]
artist: vec![],
}
}
}
@ -71,9 +71,9 @@ pub struct Config {
/// Whitelist (overrides blacklist)
pub whitelist: WhiteList,
/// Min MusicBrainz search score for artist look-up
pub artist_min_score : i32,
pub artist_min_score: i32,
/// Max nr of artists to check per track
pub max_artists_per_track : u64,
pub max_artists_per_track: u64,
/// Interval in which the daemon probes DBUS for open MPRIS channels
pub player_find_interval_ms: u64,
/// Delay after a skip or allow, e.g. to prevent infinite skip chain when someone starts a rap playlist
@ -96,71 +96,55 @@ impl Default for Config {
player_find_interval_ms: 2500,
cooldown_ms: 1000,
api_timeout_ms: 2000,
allow_by_default: true
allow_by_default: true,
}
}
}
const CONFIG_FILE: &'static str = "rapblock.json";
fn load_config() -> Result<Config, Error> {
let config_file_path = env::current_dir()?.join(CONFIG_FILE);
let buf = read_file(config_file_path)?;
let config : Config = serde_json::from_str(&buf)?;
// Validations
match config.logging.as_ref() {
"info" | "debug" | "trace" | "warning" | "error" => (),
_ => bail!("Invalid value for \"logging\""),
};
Ok(config)
}
pub fn read_file<P: AsRef<Path>>(path: P) -> Result<String, Error> {
let path = path.as_ref();
let mut file = File::open(path)?;
let mut buf = String::new();
file.read_to_string(&mut buf)?;
Ok(buf)
}
fn main() -> Result<(), Error> {
let config = match load_config() {
Ok(c) => c,
Err(e) => {
eprintln!("Could not load config from \"{}\": {}", CONFIG_FILE, e);
Config::default()
}
};
// let config = match load_config() {
// Ok(c) => c,
// Err(e) => {
// eprintln!("Could not load config from \"{}\": {}", CONFIG_FILE, e);
// Config::default()
// }
// };
let env = env_logger::Env::default().default_filter_or(&config.logging);
env_logger::Builder::from_env(env).init();
let config_file_path = env::current_dir()?.join(CONFIG_FILE);
let mut cfg: ConfigFile<Config> = config_file::ConfigFile::new(config_file_path)?;
debug!("Loaded config:\n{}", serde_json::to_string_pretty(&config)?);
let env = env_logger::Env::default().default_filter_or(&cfg.borrow().logging);
env_logger::Builder::from_env(env).init();
let mut artist_cache = HashMap::<String, bool>::new();
info!("Waiting for players...");
'main_loop:
loop {
'main_loop: loop {
// XXX this picks the first player, which isn't always ideal - see mpris/src/find.rs
let player = PlayerFinder::new()
.expect("Could not connect to D-Bus")
.find_active();
let _ = cfg.update_if_needed(false);
if let Ok(player) = player {
let config = cfg.borrow();
let player_name = player.bus_name().to_string();
if !config.allowed_players.is_empty() && !config.allowed_players.contains(&player_name) {
if !config.allowed_players.is_empty() && !config.allowed_players.contains(&player_name)
{
debug!("Ignoring player {}", player_name);
::std::thread::sleep(Duration::from_millis(config.player_find_interval_ms));
continue 'main_loop;
}
info!("Connected to player: {}{}", player_name, player.unique_name());
info!(
"Connected to player: {}{}",
player_name,
player.unique_name()
);
let events = player.events();
if events.is_err() {
@ -170,8 +154,10 @@ fn main() -> Result<(), Error> {
continue 'main_loop;
}
'event_loop:
for event in events.unwrap() {
'event_loop: for event in events.unwrap() {
let _ = cfg.update_if_needed(false);
let config = cfg.borrow();
match event {
Ok(event) => {
debug!("MPRIS event: {:#?}", event);
@ -179,7 +165,7 @@ fn main() -> Result<(), Error> {
Event::PlayerShutDown => {
info!("Player shut down");
break 'event_loop;
},
}
Event::TrackChanged(metadata) => {
let title = metadata.title().unwrap_or("");
info!("--- new track : {} ---", title);
@ -194,17 +180,24 @@ fn main() -> Result<(), Error> {
let mut artists = HashSet::new();
if let Some(aa) = metadata.artists() {
for a in aa { artists.insert(a); }
for a in aa {
artists.insert(a);
}
}
if let Some(aa) = metadata.album_artists() {
for a in aa { artists.insert(a); }
for a in aa {
artists.insert(a);
}
}
let mut skip = false;
let mut confidence = false;
'artists_loop:
for (an, a) in artists.iter().take(config.max_artists_per_track as usize).enumerate() {
info!("Checking artist #{}: {}", an+1, a);
'artists_loop: for (an, a) in artists
.iter()
.take(config.max_artists_per_track as usize)
.enumerate()
{
info!("Checking artist #{}: {}", an + 1, a);
if let Some(resolution) = artist_cache.get(a.as_str()) {
confidence = true;
if !resolution {
@ -243,11 +236,12 @@ fn main() -> Result<(), Error> {
skip = true;
break 'artists_loop;
}
},
}
Err(e) => {
warn!("Something went wrong: {}", e);
// probably no tags, or not found - use the default action
artist_cache.insert(a.to_string(), config.allow_by_default);
artist_cache
.insert(a.to_string(), config.allow_by_default);
}
}
}
@ -262,12 +256,12 @@ fn main() -> Result<(), Error> {
}
::std::thread::sleep(Duration::from_millis(config.cooldown_ms));
},
}
_ => {
debug!("Event not handled.");
}
}
},
}
Err(err) => {
error!("D-Bus error: {}. Aborting.", err);
break 'event_loop;
@ -279,8 +273,6 @@ fn main() -> Result<(), Error> {
debug!("No player found, waiting...");
}
::std::thread::sleep(Duration::from_millis(config.player_find_interval_ms));
::std::thread::sleep(Duration::from_millis(cfg.borrow().player_find_interval_ms));
}
}

Loading…
Cancel
Save