|
|
|
@ -11,6 +11,8 @@ use std::time::Duration; |
|
|
|
|
use mpris::Event; |
|
|
|
|
use std::collections::HashSet; |
|
|
|
|
use failure::Error; |
|
|
|
|
use dbus::arg::Variant; |
|
|
|
|
use dbus::arg::RefArg; |
|
|
|
|
|
|
|
|
|
mod brainz; |
|
|
|
|
mod config_file; |
|
|
|
@ -167,6 +169,9 @@ fn main() -> Result<(), Error> { |
|
|
|
|
break 'event_loop; |
|
|
|
|
} |
|
|
|
|
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(""); |
|
|
|
|
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() { |
|
|
|
|
for a in aa { |
|
|
|
|
artists.insert(a); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
if let Some(aa) = metadata.album_artists() { |
|
|
|
|
for a in aa { |
|
|
|
|
artists.insert(a); |
|
|
|
|
debug!("MPRIS meta: {:#?}", metadata); |
|
|
|
|
|
|
|
|
|
// 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(); |
|
|
|
|
let meta_genre : Option<&Variant<Box<RefArg>>> = rest.get("xesam:genre"); |
|
|
|
|
match meta_genre { |
|
|
|
|
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; |
|
|
|
|
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); |
|
|
|
|
if let Some(resolution) = artist_cache.get(a.as_str()) { |
|
|
|
|
confidence = true; |
|
|
|
|
if !resolution { |
|
|
|
|
info!("~ result cached: BAD"); |
|
|
|
|
skip = true; |
|
|
|
|
break 'artists_loop; |
|
|
|
|
if !confidence { |
|
|
|
|
let mut artists = HashSet::new(); |
|
|
|
|
|
|
|
|
|
if let Some(aa) = metadata.artists() { |
|
|
|
|
for a in aa { |
|
|
|
|
artists.insert(a); |
|
|
|
|
} |
|
|
|
|
info!("~ result cached: GOOD"); |
|
|
|
|
continue 'artists_loop; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
if config.whitelist.artist.contains(a) { |
|
|
|
|
info!("+ Whitelisted artist!"); |
|
|
|
|
// 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 let Some(aa) = metadata.album_artists() { |
|
|
|
|
for a in aa { |
|
|
|
|
artists.insert(a); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
if config.blacklist.artist.contains(a) { |
|
|
|
|
info!("- Blacklisted artist!"); |
|
|
|
|
skip = true; |
|
|
|
|
artist_cache.insert(a.to_string(), false); |
|
|
|
|
confidence = true; |
|
|
|
|
break 'artists_loop; |
|
|
|
|
} |
|
|
|
|
let artists_e = artists |
|
|
|
|
.iter() |
|
|
|
|
.take(config.max_artists_per_track as usize) |
|
|
|
|
.enumerate(); |
|
|
|
|
|
|
|
|
|
let verdict = brainz::check_artist(&config, &a); |
|
|
|
|
match verdict { |
|
|
|
|
Ok(allow) => { |
|
|
|
|
'artists_loop: for (an, a) in artists_e { |
|
|
|
|
info!("Checking artist #{}: {}", an + 1, a); |
|
|
|
|
if let Some(resolution) = artist_cache.get(*a) { |
|
|
|
|
confidence = true; |
|
|
|
|
artist_cache.insert(a.to_string(), allow); |
|
|
|
|
if allow { |
|
|
|
|
info!("Artist passed"); |
|
|
|
|
} else { |
|
|
|
|
if !resolution { |
|
|
|
|
info!("~ result cached: BAD"); |
|
|
|
|
skip = true; |
|
|
|
|
break 'artists_loop; |
|
|
|
|
} |
|
|
|
|
info!("~ result cached: GOOD"); |
|
|
|
|
continue '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); |
|
|
|
|
|
|
|
|
|
if config.whitelist.artist.contains(a) { |
|
|
|
|
info!("+ Whitelisted artist!"); |
|
|
|
|
// 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) { |
|
|
|
|
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); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|