use listenbrainz_rust::Listen;
use mpris::{PlaybackStatus, PlayerFinder};
use notify_rust::{Notification, Timeout};
use rustfm_scrobble::{Scrobble, Scrobbler};
use std::process;
use std::thread;
use std::time::{Duration, Instant};
use crate::config::Config;
use crate::filter::filter_metadata;
use crate::player;
const POLL_INTERVAL: Duration = Duration::from_millis(500);
const MIN_LENGTH: Duration = Duration::from_secs(30);
const MIN_PLAY_TIME: Duration = Duration::from_secs(4 * 60);
fn get_min_play_time(track_length: Duration, config: &Config) -> Duration {
config.min_play_time.unwrap_or_else(|| {
if (track_length / 2) < MIN_PLAY_TIME {
track_length / 2
} else {
MIN_PLAY_TIME
}
})
}
pub fn run(config: Config, scrobbler: Option<Scrobbler>) {
let finder = match PlayerFinder::new() {
Ok(finder) => finder,
Err(err) => {
eprintln!("Failed to connect to D-Bus: {}", err);
process::exit(1);
}
};
println!("Looking for an active MPRIS player...");
let mut player = player::wait_for_player(&config, &finder);
println!("Found active player {}", player.identity());
let mut previous_artist = String::new();
let mut previous_title = String::new();
let mut previous_album = String::new();
let mut timer = Instant::now();
let mut current_play_time = Duration::from_secs(0);
let mut scrobbled_current_song = false;
loop {
if !player::is_active(&player) {
println!(
"Player {} stopped, looking for a new MPRIS player...",
player.identity()
);
player = player::wait_for_player(&config, &finder);
println!("Found active player {}", player.identity());
previous_artist.clear();
previous_title.clear();
previous_album.clear();
timer = Instant::now();
current_play_time = Duration::from_secs(0);
scrobbled_current_song = false;
}
match player.get_playback_status() {
Ok(PlaybackStatus::Playing) => {}
Ok(_) => {
thread::sleep(POLL_INTERVAL);
continue;
}
Err(err) => {
eprintln!("Failed to retrieve playback status: {}", err);
thread::sleep(POLL_INTERVAL);
continue;
}
}
let metadata = match player.get_metadata() {
Ok(metadata) => metadata,
Err(err) => {
eprintln!("Failed to get metadata: {}", err);
thread::sleep(POLL_INTERVAL);
continue;
}
};
let artist = metadata
.artists()
.as_ref()
.and_then(|artists| artists.first())
.map(|&artist| artist)
.unwrap_or("");
let title = metadata.title().unwrap_or("");
let album = metadata.album_name().unwrap_or("");
let length = match metadata.length() {
Some(length) => length,
None => {
eprintln!("Failed to get track length");
thread::sleep(POLL_INTERVAL);
continue;
}
};
if artist == previous_artist && title == previous_title && album == previous_album {
if !scrobbled_current_song {
let min_play_time = get_min_play_time(length, &config);
if length > MIN_LENGTH && current_play_time > min_play_time {
let (filtered_artist, filtered_title, filtered_album) =
filter_metadata(&config, artist, title, album).unwrap_or_else(|| {
(artist.to_string(), title.to_string(), album.to_string())
});
let scrobble =
Scrobble::new(&filtered_artist, &filtered_title, &filtered_album);
if let Some(ref scrobbler) = scrobbler {
match scrobbler.scrobble(&scrobble) {
Ok(_) => println!("Track submitted to Last.fm successfully"),
Err(err) => eprintln!("Failed to submit track to Last.fm: {}", err),
}
}
if let Some(ref token) = config.listenbrainz_token {
let listen = Listen {
artist: &filtered_artist,
track: &filtered_title,
album: &filtered_album,
};
match listen.single(token) {
Ok(_) => println!("Track submitted to ListenBrainz successfully"),
Err(err) => {
eprintln!("Failed to submit track to ListenBrainz: {}", err)
}
}
}
scrobbled_current_song = true;
}
} else if current_play_time >= length {
current_play_time = Duration::from_secs(0);
scrobbled_current_song = false;
}
current_play_time += timer.elapsed();
timer = Instant::now();
} else {
previous_artist.clear();
previous_artist.push_str(artist);
previous_title.clear();
previous_title.push_str(title);
previous_album.clear();
previous_album.push_str(album);
let (filtered_artist, filtered_title, filtered_album) =
filter_metadata(&config, artist, title, album)
.unwrap_or_else(|| (artist.to_string(), title.to_string(), album.to_string()));
timer = Instant::now();
current_play_time = Duration::from_secs(0);
scrobbled_current_song = false;
println!("----");
println!(
"Now playing: {} - {} ({})",
filtered_artist, filtered_title, filtered_album
);
if config.enable_notifications.unwrap_or(false) {
Notification::new()
.summary(&filtered_title)
.body(&format!("{} - {}", filtered_artist, filtered_album))
.timeout(Timeout::Milliseconds(6000))
.show()
.unwrap();
}
let scrobble = Scrobble::new(&filtered_artist, &filtered_title, &filtered_album);
if let Some(ref scrobbler) = scrobbler {
match scrobbler.now_playing(&scrobble) {
Ok(_) => println!("Status updated on Last.fm successfully"),
Err(err) => eprintln!("Failed to update status on Last.fm: {}", err),
}
}
if let Some(ref token) = config.listenbrainz_token {
let listen = Listen {
artist: &filtered_artist,
track: &filtered_title,
album: &filtered_album,
};
match listen.playing_now(token) {
Ok(_) => println!("Status updated on ListenBrainz successfully"),
Err(err) => eprintln!("Failed to update status on ListenBrainz: {}", err),
}
}
}
thread::sleep(POLL_INTERVAL);
}
}