diff --git a/README.md b/README.md index f4536c5..9cabd96 100644 --- a/README.md +++ b/README.md @@ -14,5 +14,68 @@ in the TrackChanged event metadata. Customization ------------- -You can easily modify this to skip e.g. folk, country or any other genre. Just change the -`BAD_GENRES` list in `src/brainz.rs`. \ No newline at end of file +The daemon loads its config from a JSON file, an example of which is provided as +`rapblock.example.json`. Copy this file (removing `.example`) and customize as desired. + +Defaults will be loaded in absence of the config file. + +Here is the structure explained (please note that JSON doesn't allow comments, +this is just for demonstration): + +``` +{ + // Blacklist config + "blacklist": { + // Literal tags to reject + "tag": [], + + // Tag sub-strings to reject (must be a whole word) + "tag_partial": ["hip-hop", "hip hop", "rap"], + + // Artists to reject without even checking tags + "artist": ["Higher Brothers"] + }, + + // Whitelist config + "whitelist": { + // Tags to allow despite e.g. a substring match + "tag": [], + + // Artists to allow unconditionally + "artist": [] + }, + + // Logging - trace, debug, (info), warning, error + "logging": "info", + + // Player names to allow (e.g. when we want to block only Spotify songs) + // This does not support multiple players playing at once - always only the first is + // handled. + "allowed_players": [ + // spotify player name, as copied from the debug log + // -> Leave this list empty to allow all players! + "org.mpris.MediaPlayer2.spotify" + ], + + // Min MusicBrainz search score for artist look-up + "artist_min_score": 95, + + // Max nr of artists to check per track (limits MusicBrainz request count) + "max_artists_per_track": 3, + + // Interval in which the daemon probes DBUS for open MPRIS channels + "player_find_interval_ms": 2500, + + // Delay after a skip or allow, e.g. to prevent infinite skip + // chain when someone starts a rap playlist and we block the 'rap' tag + "cooldown_ms": 500, + + // Music Brainz API access timeout + "api_timeout_ms": 2000, + + // Action to take when the daemon can't figure out if a song is good or not. + // Default is true; change to false if you REALLY hate rap, but it will + // also block good songs by less known artists. + "allow_by_default": true +} +``` diff --git a/rapblock.example.json b/rapblock.example.json index 8d22139..88cf379 100644 --- a/rapblock.example.json +++ b/rapblock.example.json @@ -1,5 +1,4 @@ { - "logging": "info", "blacklist": { "tag": [], "tag_partial": ["hip-hop", "hip hop", "rap"], @@ -9,9 +8,12 @@ "tag": [], "artist": [] }, + "logging": "info", + "allowed_players": [], "artist_min_score": 95, "max_artists_per_track": 3, "player_find_interval_ms": 2500, "cooldown_ms": 500, - "api_timeout_ms": 2000 + "api_timeout_ms": 2000, + "allow_by_default": true } diff --git a/src/main.rs b/src/main.rs index 26dc17c..1d761dd 100644 --- a/src/main.rs +++ b/src/main.rs @@ -18,8 +18,11 @@ use std::collections::HashMap; #[derive(Serialize, Deserialize, Debug)] #[serde(default)] pub struct BlacklistConf { + /// Literal tags to reject pub tag: Vec, + /// Tag sub-strings to reject (must be a whole word) pub tag_partial: Vec, + /// Artists to reject pub artist: Vec, } @@ -41,7 +44,9 @@ impl Default for BlacklistConf { #[derive(Serialize, Deserialize, Debug)] #[serde(default)] pub struct WhiteList { + /// Tags to allow despite e.g. a substring match pub tag: Vec, + /// Artists to allow pub artist: Vec, } @@ -59,6 +64,8 @@ impl Default for WhiteList { pub struct Config { /// Logging - trace, debug, (info), warning, error pub logging: String, + /// Players to handle (empty = all) + pub allowed_players: Vec, /// Blacklists pub blacklist: BlacklistConf, /// Whitelist (overrides blacklist) @@ -81,6 +88,7 @@ impl Default for Config { fn default() -> Self { Self { logging: "info".to_owned(), + allowed_players: vec![], blacklist: BlacklistConf::default(), whitelist: WhiteList::default(), artist_min_score: 95, @@ -137,12 +145,20 @@ fn main() -> Result<(), Error> { '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(); if let Ok(player) = player { - info!("Connected to player: {}{}", player.bus_name().to_string(), player.unique_name()); + let player_name = player.bus_name().to_string(); + 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()); let events = player.events(); if events.is_err() {