use std::thread;
use std::time::{Duration, Instant};
use anyhow::{anyhow, Context, Result};
use mpris::{PlaybackStatus, PlayerFinder};
use notify_rust::{Notification, Timeout};
use crate::config::Config;
use crate::filter::{filter_metadata, FilterResult};
use crate::player;
use crate::service::Service;
use crate::track::Track;
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);
const NOTIFICATION_TIMEOUT: Timeout = Timeout::Milliseconds(6000);
fn get_min_play_time(config: &Config, track_length: Duration) -> 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, services: Vec<Service>) -> Result<()> {
let finder = PlayerFinder::new()
.map_err(|err| anyhow!("{}", err))
.context("Failed to connect to D-Bus")?;
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_track = Track::default();
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!(
"----\n\
Player {} stopped, looking for a new MPRIS player...",
player.identity()
);
player = player::wait_for_player(&config, &finder);
println!("Found active player {}", player.identity());
previous_track.clear();
timer = Instant::now();
current_play_time = Duration::from_secs(0);
scrobbled_current_song = false;
}
let status = player
.get_playback_status()
.map_err(|err| anyhow!("{}", err))
.context("Failed to retrieve playback status");
match status {
Ok(PlaybackStatus::Playing) => {}
Ok(_) => {
thread::sleep(POLL_INTERVAL);
continue;
}
Err(err) => {
eprintln!("{:?}", err);
thread::sleep(POLL_INTERVAL);
continue;
}
}
let metadata = player
.get_metadata()
.map_err(|err| anyhow!("{}", err))
.context("Failed to get metadata");
let metadata = match metadata {
Ok(metadata) => metadata,
Err(err) => {
eprintln!("{:?}", err);
thread::sleep(POLL_INTERVAL);
continue;
}
};
let current_track = Track::from_metadata(&metadata);
let length = if let Some(length) = metadata.length() {
if length.is_zero() {
None
} else {
Some(length)
}
} else {
None
};
if current_track == previous_track {
if !scrobbled_current_song {
let min_play_time = get_min_play_time(&config, length.unwrap_or(MIN_LENGTH));
if length.map(|length| length > MIN_LENGTH).unwrap_or(true)
&& current_play_time > min_play_time
{
match filter_metadata(&config, current_track) {
Ok(FilterResult::Filtered(track))
| Ok(FilterResult::NotFiltered(track)) => {
for service in services.iter() {
match service.submit(&track) {
Ok(()) => {
println!("Track submitted to {} successfully", service)
}
Err(err) => eprintln!("{:?}", err),
}
}
}
Ok(FilterResult::Ignored) => {}
Err(err) => eprintln!("{:?}", err),
}
scrobbled_current_song = true;
}
} else if length
.map(|length| current_play_time >= length)
.unwrap_or(false)
{
current_play_time = Duration::from_secs(0);
scrobbled_current_song = false;
}
current_play_time += timer.elapsed();
timer = Instant::now();
} else {
previous_track.clone_from(¤t_track);
timer = Instant::now();
current_play_time = Duration::from_secs(0);
scrobbled_current_song = false;
if config.enable_notifications.unwrap_or(false) {
Notification::new()
.summary(current_track.title())
.body(&format!(
"{} - {}",
current_track.artist(),
current_track.album()
))
.timeout(NOTIFICATION_TIMEOUT)
.show()
.unwrap();
}
println!(
"----\n\
Now playing: {} - {} ({})",
current_track.artist(),
current_track.title(),
current_track.album()
);
match filter_metadata(&config, current_track) {
Ok(FilterResult::Filtered(track)) | Ok(FilterResult::NotFiltered(track)) => {
for service in services.iter() {
match service.now_playing(&track) {
Ok(()) => println!("Status updated on {} successfully", service),
Err(err) => eprintln!("{:?}", err),
}
}
}
Ok(FilterResult::Ignored) => println!("Track ignored"),
Err(err) => eprintln!("{:?}", err),
}
}
thread::sleep(POLL_INTERVAL);
}
}