use std::{io::Read, path::Path, sync::atomic::Ordering, thread, time::Duration};
use crossbeam_channel::{unbounded, Receiver};
use diskit::Diskit;
use id3::Tag;
use legacytranslate::MessageHandler;
use signal_hook::{
consts::{SIGINT, SIGTERM, SIGUSR1, SIGUSR2},
iterator::Signals,
};
use simple_logger::SimpleLogger;
use crate::{
api::ApiResponder,
audio::{AudioHandler, ChannelAudio},
conffile::Conffile,
config::Config,
err::Error,
helpers::take_error,
l10n::{messages::Message, Writer},
matcher::{main_match, BigAction},
songs::{Song, Songs},
threads::start_threads,
};
#[allow(clippy::needless_pass_by_value)]
fn handle_song<D>(
song: &mut Song,
api: &ApiResponder,
quit_request: Receiver<()>,
config: &mut Config,
diskit: D,
) -> BigAction
where
D: Diskit + Send + 'static,
{
let data_dir = &config.arc_config.conffile.data_dir;
let song_path = data_dir.join(song.name.clone());
let (tag, tag_option) = take_error(Tag::read_from_path(&song_path));
config.tag = Some(tag);
config.source = match ChannelAudio::new(&song_path, config.arc_config.clone(), diskit.clone())
{
Ok(source) => source,
Err(e) =>
{
config
.l10n
.write(Message::ReadingSongProblem(song.name.clone(), e));
if config.unsuccessful_tries == 255
{
config.l10n.write(Message::TooManyTries);
return BigAction::Quit;
}
config.unsuccessful_tries += 1;
config.l10n.write(Message::ChoosingNewSong);
return BigAction::Skip;
}
};
config.unsuccessful_tries = 0;
config.num = song.num;
config.loud = song.loud;
config
.audio_handler
.append(config.source.inner.take().unwrap());
config.audio_handler.set_volume(song.loud);
if let Ok(s) = data_dir
.join(song.name.clone())
.into_os_string()
.into_string()
{
config.l10n.write(Message::PlayingSong(s));
}
else
{
config.l10n.write(Message::PlayingSongUnknown);
}
config.l10n.write(Message::SongLikelihood(song.num));
config.arc_config.update_dbus.store(true, Ordering::SeqCst);
while !config.audio_handler.empty()
{
match main_match(config, diskit.clone())
{
BigAction::Nothing =>
{
song.num = config.num;
song.loud = config.loud;
}
x => return x,
}
if quit_request.try_recv().is_ok()
{
return BigAction::Quit;
}
if config.rx_control.try_recv().is_ok()
{
let _ = config.tx_paused.send(config.paused);
let _ = config.tx_path.send((song_path.clone(), tag_option.clone()));
}
if config.arc_config.reading_paused.load(Ordering::SeqCst)
{
config.l10n.write(Message::SignalPaused);
}
api.handle(config);
song.num = config.num;
song.loud = config.loud;
thread::sleep(Duration::from_micros(1));
}
BigAction::Nothing
}
fn handle_pausely(config: &mut Config) -> bool
{
if config.quit_after_song
{
return true;
}
if config.pause_after_song
{
config.l10n.write(Message::RequestedPause);
config.audio_handler.pause();
config.paused = true;
config.pause_after_song = false;
}
while config
.arc_config
.reading_paused
.load(std::sync::atomic::Ordering::SeqCst)
{
thread::sleep(Duration::from_millis(1));
}
false
}
#[allow(clippy::needless_pass_by_value)]
pub fn run<C, A, D, R>(
get_conffile: C,
writer: Writer,
reader: fn() -> R,
api: ApiResponder,
quit_request: Receiver<()>,
diskit: D,
) -> Result<(), Error>
where
C: Fn(&Path, D) -> Conffile,
A: AudioHandler,
D: Diskit + Send + 'static,
R: Read + 'static,
{
SimpleLogger::new().init().unwrap();
let (tx_control, rx_control) = unbounded();
let (tx_paused, rx_paused) = unbounded();
let (tx_path, rx_path) = unbounded();
let mut config = Config::new::<_, A, _>(
rx_control,
tx_paused,
tx_path,
get_conffile,
writer,
diskit.clone(),
)?;
let mut songs = Songs::read(config.arc_config.clone(), config.l10n, diskit.clone())?;
let l10n = config.l10n;
start_threads(
config.tx.clone(),
tx_control,
rx_paused,
rx_path,
Signals::new([SIGINT, SIGTERM, SIGUSR1, SIGUSR2])?,
reader,
config.arc_config.clone(),
config.l10n,
diskit.clone(),
);
loop
{
if handle_pausely(&mut config)
{
break;
}
config.arc_config.update_dbus.store(true, Ordering::SeqCst);
match songs.choose_random(
&mut config,
handle_song,
&api,
quit_request.clone(),
l10n,
diskit.clone(),
)
{
BigAction::Nothing | BigAction::Skip =>
{}
BigAction::Quit => break,
}
}
config
.l10n
.write(Message::TotalPlayingLikelihood(songs.total_likelihood()));
Ok(())
}