initial code
This commit is contained in:
@@ -0,0 +1,4 @@
|
||||
/target
|
||||
**/*.rs.bk
|
||||
.idea
|
||||
*.iml
|
||||
Generated
+1683
File diff suppressed because it is too large
Load Diff
+17
@@ -0,0 +1,17 @@
|
||||
[package]
|
||||
name = "rapblock"
|
||||
version = "0.1.0"
|
||||
authors = ["Ondřej Hruška <ondra@ondrovo.com>"]
|
||||
edition = "2018"
|
||||
|
||||
[dependencies]
|
||||
mpris = "^1.1"
|
||||
reqwest = "0.9.10"
|
||||
percent-encoding = "1.0.1"
|
||||
serde = "1.0.88"
|
||||
serde_derive = "1.0.88"
|
||||
serde_json = "1.0"
|
||||
failure = "0.1.5"
|
||||
lazy_static = "1.2.0"
|
||||
log = "0.4"
|
||||
env_logger = "0.6"
|
||||
@@ -0,0 +1,86 @@
|
||||
use percent_encoding::{utf8_percent_encode, DEFAULT_ENCODE_SET};
|
||||
use failure::Error;
|
||||
use std::io::Read;
|
||||
use std::time::Duration;
|
||||
|
||||
#[derive(Serialize, Deserialize, Debug)]
|
||||
struct MBArtistQueryResult {
|
||||
created : String,
|
||||
count: i32,
|
||||
offset: i32,
|
||||
artists: Option<Vec<MBArtist>>,
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Debug)]
|
||||
struct Tag {
|
||||
count: i32, // negative doesn't make sense, but it sometimes occurs
|
||||
name: String,
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Debug)]
|
||||
struct MBArtist {
|
||||
id: String,
|
||||
score: i32,
|
||||
name: String,
|
||||
#[serde(rename="sort-name")]
|
||||
sort_name: String,
|
||||
tags: Option<Vec<Tag>>
|
||||
}
|
||||
|
||||
lazy_static! {
|
||||
static ref BAD_GENRES : Vec<&'static str> = vec![
|
||||
"hip hop", "hip-hop", "hiphop", "rnb", "rap", "rapper",
|
||||
"hardcore hip hop", "new york hip hop", "east coast hip hop",
|
||||
"pop rap"
|
||||
];
|
||||
}
|
||||
|
||||
pub fn artist_sucks(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);
|
||||
|
||||
info!("Querying MusicBrainz for artist \"{}\", query URL: {}", artist, url);
|
||||
|
||||
let mut resp = String::new();
|
||||
|
||||
reqwest::Client::builder()
|
||||
.timeout(Duration::from_millis(2000))
|
||||
.build()?
|
||||
.get(&url)
|
||||
.header(reqwest::header::USER_AGENT, "Bad Spotify Song Skipper/0.0.1 ( ondra@ondrovo.com )")
|
||||
.header(reqwest::header::ACCEPT, "text/json")
|
||||
.send()?
|
||||
.read_to_string(&mut resp)?;
|
||||
|
||||
debug!("Resp: {}", resp);
|
||||
|
||||
let result : MBArtistQueryResult = serde_json::from_str(&resp)?;
|
||||
|
||||
info!("Response OK");
|
||||
debug!("{:#?}", result);
|
||||
|
||||
if result.count == 0 {
|
||||
// not found, let's hope it's OK
|
||||
warn!("No results!");
|
||||
return Err(failure::err_msg("Artist not found"));
|
||||
} else {
|
||||
info!("Got {} results", result.count);
|
||||
let artists = result.artists.as_ref().unwrap();
|
||||
if artists[0].tags.is_some() {
|
||||
let tags = artists[0].tags.as_ref().unwrap();
|
||||
let as_vec : Vec<&String> = tags.iter().map(|t| &t.name).collect();
|
||||
debug!("First artist has tags: {:?}", tags);
|
||||
for tag in as_vec {
|
||||
if BAD_GENRES.contains(&tag.as_str()) {
|
||||
info!("Found a bad tag: {}", tag);
|
||||
return Ok(true);
|
||||
}
|
||||
}
|
||||
info!("All tags OK");
|
||||
return Ok(false);
|
||||
} else {
|
||||
warn!("No tags, can't determine genre");
|
||||
return Err(failure::err_msg("Artist found, but has no tags"));
|
||||
}
|
||||
}
|
||||
}
|
||||
+109
@@ -0,0 +1,109 @@
|
||||
#[macro_use] extern crate serde_derive;
|
||||
#[macro_use] extern crate lazy_static;
|
||||
#[macro_use] extern crate log;
|
||||
|
||||
mod brainz;
|
||||
|
||||
use mpris::{PlayerFinder,Event};
|
||||
use failure::Error;
|
||||
use std::time::Duration;
|
||||
|
||||
const DELAY_FIND_PLAYER : Duration = Duration::from_millis(1000);
|
||||
|
||||
fn main() -> Result<(), Error> {
|
||||
let env = env_logger::Env::default().default_filter_or("info");
|
||||
env_logger::Builder::from_env(env).init();
|
||||
|
||||
'main_loop:
|
||||
loop {
|
||||
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 events = player.events();
|
||||
if events.is_err() {
|
||||
error!("Could not start event stream!");
|
||||
// add a delay so we don't run too hot here
|
||||
::std::thread::sleep(DELAY_FIND_PLAYER);
|
||||
continue 'main_loop;
|
||||
}
|
||||
|
||||
'event_loop:
|
||||
for event in events.unwrap() {
|
||||
match event {
|
||||
Ok(event) => {
|
||||
debug!("MPRIS event: {:#?}", event);
|
||||
match &event {
|
||||
Event::PlayerShutDown => {
|
||||
info!("Player shut down");
|
||||
break 'event_loop;
|
||||
},
|
||||
Event::TrackChanged(metadata) => {
|
||||
let title = metadata.title().unwrap_or("");
|
||||
info!("--- new track : {} ---", title);
|
||||
debug!("{:#?}", event);
|
||||
|
||||
if title.is_empty() {
|
||||
warn!("!!! Spotify is giving us garbage - empty metadata struct !!!");
|
||||
// wait for next event
|
||||
continue 'event_loop;
|
||||
}
|
||||
|
||||
'artists_loop:
|
||||
for ar in metadata.artists().into_iter().chain(metadata.artists()) {
|
||||
for a in ar {
|
||||
info!("Checking artist: {}", a);
|
||||
|
||||
let verdict = brainz::artist_sucks(&a);
|
||||
match verdict {
|
||||
Ok(verdict) => {
|
||||
if verdict {
|
||||
if player.can_go_next().unwrap_or(false) {
|
||||
info!(">>>>>> SKIP >>>>>>");
|
||||
if player.next().is_err() {
|
||||
break 'artists_loop;
|
||||
}
|
||||
} else {
|
||||
info!("<><><> STOP <><><>");
|
||||
if player.pause().is_err() {
|
||||
break 'artists_loop;
|
||||
}
|
||||
}
|
||||
// we add a delay here to prevent going insane on rap playlists
|
||||
::std::thread::sleep(Duration::from_millis(1000));
|
||||
} else {
|
||||
info!("Good artist, let it play");
|
||||
}
|
||||
},
|
||||
Err(e) => {
|
||||
error!("Something went wrong: {}", e);
|
||||
info!("Letting to play");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
_ => {
|
||||
debug!("Event not handled.");
|
||||
}
|
||||
}
|
||||
},
|
||||
Err(err) => {
|
||||
error!("D-Bus error: {}. Aborting.", err);
|
||||
break 'event_loop;
|
||||
}
|
||||
}
|
||||
}
|
||||
info!("Event stream ended - player likely shut down");
|
||||
} else {
|
||||
debug!("No player found, waiting...");
|
||||
}
|
||||
|
||||
::std::thread::sleep(DELAY_FIND_PLAYER);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user