add support for getting genre from mpris (experimental)

master
Ondřej Hruška 5 years ago
parent ef78943940
commit 170b6ebc7b
Signed by: MightyPork
GPG Key ID: 2C5FD5035250423D
  1. 1
      Cargo.lock
  2. 1
      Cargo.toml
  3. 63
      src/brainz.rs
  4. 142
      src/main.rs

1
Cargo.lock generated

@ -956,6 +956,7 @@ dependencies = [
name = "rapblock" name = "rapblock"
version = "0.1.0" version = "0.1.0"
dependencies = [ dependencies = [
"dbus 0.6.4 (registry+https://github.com/rust-lang/crates.io-index)",
"env_logger 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)", "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)", "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)", "inotify 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)",

@ -17,3 +17,4 @@ log = "0.4"
env_logger = "0.6" env_logger = "0.6"
regex = "1.1.0" regex = "1.1.0"
inotify = "0.7.0" inotify = "0.7.0"
dbus = "0.6.4"

@ -30,6 +30,38 @@ struct MBArtist {
const VERSION: &'static str = env!("CARGO_PKG_VERSION"); const VERSION: &'static str = env!("CARGO_PKG_VERSION");
/// Check genre; returns true if it's OK
pub fn check_genre(config: &crate::Config, genre: &String) -> bool {
if config.whitelist.tag.contains(genre) {
info!("+ Whitelisted tag \"{}\"", genre);
return true;
}
if config.blacklist.tag.contains(genre) {
info!("- Blacklisted tag \"{}\"", genre);
return false;
} else {
for substr in config.blacklist.tag_partial.iter() {
let re = Regex::new(&format!(r"\b{}\b", regex::escape(substr)));
let matches = match re {
Ok(re) => re.is_match(genre),
Err(_) => genre.contains(substr),
};
if matches {
info!(
"- Blacklisted tag \"{}\" - due to substring \"{}\"",
genre, substr
);
return false;
}
}
}
true
}
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 query = utf8_percent_encode(&format!("\"{}\"", artist), DEFAULT_ENCODE_SET).to_string();
let url = format!( let url = format!(
@ -89,36 +121,7 @@ pub fn check_artist(config: &crate::Config, artist: &str) -> Result<bool, Error>
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 { 'tags: for tag in as_vec {
if config.whitelist.tag.contains(tag) { if !check_genre(config, tag) {
info!("+ Whitelisted tag \"{}\"", tag);
continue 'tags;
}
let mut blacklisted = false;
if config.blacklist.tag.contains(&tag) {
info!("- Blacklisted tag \"{}\"", tag);
blacklisted = true;
} else {
'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),
};
if matches {
info!(
"- Blacklisted tag \"{}\" - due to substring \"{}\"",
tag, t
);
blacklisted = true;
break 'blacklist;
}
}
}
if blacklisted {
confidence = true; confidence = true;
passed = false; passed = false;
break 'artists; break 'artists;

@ -11,6 +11,8 @@ use std::time::Duration;
use mpris::Event; use mpris::Event;
use std::collections::HashSet; use std::collections::HashSet;
use failure::Error; use failure::Error;
use dbus::arg::Variant;
use dbus::arg::RefArg;
mod brainz; mod brainz;
mod config_file; mod config_file;
@ -167,6 +169,9 @@ fn main() -> Result<(), Error> {
break 'event_loop; break 'event_loop;
} }
Event::TrackChanged(mut metadata) => { Event::TrackChanged(mut metadata) => {
::std::thread::sleep(Duration::from_millis(250));
metadata = player.get_metadata().unwrap_or(metadata);
let mut title = metadata.title().unwrap_or(""); let mut title = metadata.title().unwrap_or("");
info!("--- new track : {} ---", title); info!("--- new track : {} ---", title);
@ -185,71 +190,104 @@ fn main() -> Result<(), Error> {
} }
} }
let mut artists = HashSet::new(); let mut skip = !config.allow_by_default;
let mut confidence = false;
if let Some(aa) = metadata.artists() { debug!("MPRIS meta: {:#?}", metadata);
for a in aa {
artists.insert(a); // try to get genre from the 'rest' map in the metadata struct
} #[allow(deprecated)] // 2.0.0 will provide some nicer API, hopefully
} let rest = metadata.rest();
if let Some(aa) = metadata.album_artists() { let meta_genre : Option<&Variant<Box<RefArg>>> = rest.get("xesam:genre");
for a in aa { match meta_genre {
artists.insert(a); Some(variant) => {
let b : &RefArg = variant.0.as_ref();
if let Some(s) = b.as_str() {
info!("Using genre from MPRIS metadata: {}", s);
skip = !brainz::check_genre(&config, &s.to_string().to_lowercase());
confidence = true;
} else if let Some(list) = b.as_iter() {
debug!("MPRIS contains array of genres");
for item in list {
if let Some(s) = item.as_str() {
info!("Using genre from MPRIS metadata: {}", s);
skip = !brainz::check_genre(&config, &s.to_string().to_lowercase());
confidence = true;
}
}
}
},
None => {
debug!("No MPRIS genre");
} }
} };
let mut skip = false; if !confidence {
let mut confidence = false; let mut artists = HashSet::new();
'artists_loop: for (an, a) in artists
.iter() if let Some(aa) = metadata.artists() {
.take(config.max_artists_per_track as usize) for a in aa {
.enumerate() artists.insert(a);
{
info!("Checking artist #{}: {}", an + 1, a);
if let Some(resolution) = artist_cache.get(a.as_str()) {
confidence = true;
if !resolution {
info!("~ result cached: BAD");
skip = true;
break 'artists_loop;
} }
info!("~ result cached: GOOD");
continue 'artists_loop;
} }
if let Some(aa) = metadata.album_artists() {
if config.whitelist.artist.contains(a) { for a in aa {
info!("+ Whitelisted artist!"); artists.insert(a);
// there may be other co-artists that spoil the song -> don't break yet }
artist_cache.insert(a.to_string(), true);
confidence = true;
continue 'artists_loop;
} }
if config.blacklist.artist.contains(a) { let artists_e = artists
info!("- Blacklisted artist!"); .iter()
skip = true; .take(config.max_artists_per_track as usize)
artist_cache.insert(a.to_string(), false); .enumerate();
confidence = true;
break 'artists_loop;
}
let verdict = brainz::check_artist(&config, &a); 'artists_loop: for (an, a) in artists_e {
match verdict { info!("Checking artist #{}: {}", an + 1, a);
Ok(allow) => { if let Some(resolution) = artist_cache.get(*a) {
confidence = true; confidence = true;
artist_cache.insert(a.to_string(), allow); if !resolution {
if allow { info!("~ result cached: BAD");
info!("Artist passed");
} else {
skip = true; skip = true;
break 'artists_loop; break 'artists_loop;
} }
info!("~ result cached: GOOD");
continue 'artists_loop;
} }
Err(e) => {
warn!("Something went wrong: {}", e); if config.whitelist.artist.contains(a) {
// probably no tags, or not found - use the default action info!("+ Whitelisted artist!");
artist_cache // there may be other co-artists that spoil the song -> don't break yet
.insert(a.to_string(), config.allow_by_default); artist_cache.insert(a.to_string(), true);
confidence = true;
continue 'artists_loop;
}
if config.blacklist.artist.contains(a) {
info!("- Blacklisted artist!");
skip = true;
artist_cache.insert(a.to_string(), false);
confidence = true;
break 'artists_loop;
}
let verdict = brainz::check_artist(&config, &a);
match verdict {
Ok(allow) => {
confidence = true;
artist_cache.insert(a.to_string(), allow);
if allow {
info!("Artist passed");
} else {
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);
}
} }
} }
} }

Loading…
Cancel
Save