initial code

master
Ondřej Hruška 5 years ago
commit a872ea2f65
Signed by: MightyPork
GPG Key ID: 2C5FD5035250423D
  1. 4
      .gitignore
  2. 1683
      Cargo.lock
  3. 17
      Cargo.toml
  4. 86
      src/brainz.rs
  5. 109
      src/main.rs

4
.gitignore vendored

@ -0,0 +1,4 @@
/target
**/*.rs.bk
.idea
*.iml

1683
Cargo.lock generated

File diff suppressed because it is too large Load Diff

@ -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"));
}
}
}

@ -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);
}
}
Loading…
Cancel
Save