mecomp_tui/state/
audio.rsuse std::{sync::Arc, time::Duration};
use tokio::sync::{
broadcast,
mpsc::{unbounded_channel, UnboundedReceiver, UnboundedSender},
};
use mecomp_core::rpc::MusicPlayerClient;
use mecomp_core::state::StateAudio;
use crate::termination::Interrupted;
use super::action::{AudioAction, PlaybackAction, QueueAction, VolumeAction};
pub const TICK_RATE: Duration = Duration::from_millis(100);
#[derive(Debug, Clone)]
#[allow(clippy::module_name_repetitions)]
pub struct AudioState {
state_tx: UnboundedSender<StateAudio>,
}
impl AudioState {
#[must_use]
pub fn new() -> (Self, UnboundedReceiver<StateAudio>) {
let (state_tx, state_rx) = unbounded_channel::<StateAudio>();
(Self { state_tx }, state_rx)
}
pub async fn main_loop(
&self,
daemon: Arc<MusicPlayerClient>,
mut action_rx: UnboundedReceiver<AudioAction>,
mut interrupt_rx: broadcast::Receiver<Interrupted>,
) -> anyhow::Result<Interrupted> {
let mut state = get_state(daemon.clone()).await?;
self.state_tx.send(state.clone())?;
let mut ticker = tokio::time::interval(TICK_RATE);
let result = loop {
tokio::select! {
Some(action) = action_rx.recv() => {
self.handle_action(daemon.clone(), action).await?;
},
_ = ticker.tick() => {},
Ok(interrupted) = interrupt_rx.recv() => {
break interrupted;
}
}
state = get_state(daemon.clone()).await?;
self.state_tx.send(state.clone())?;
};
Ok(result)
}
async fn handle_action(
&self,
daemon: Arc<MusicPlayerClient>,
action: AudioAction,
) -> anyhow::Result<()> {
match action {
AudioAction::Playback(action) => handle_playback(daemon, action).await?,
AudioAction::Queue(action) => handle_queue(daemon, action).await?,
}
Ok(())
}
}
async fn get_state(daemon: Arc<MusicPlayerClient>) -> anyhow::Result<StateAudio> {
let ctx = tarpc::context::current();
Ok(daemon.state_audio(ctx).await?.unwrap_or_default())
}
async fn handle_playback(
daemon: Arc<MusicPlayerClient>,
action: PlaybackAction,
) -> anyhow::Result<()> {
let ctx = tarpc::context::current();
match action {
PlaybackAction::Toggle => daemon.playback_toggle(ctx).await?,
PlaybackAction::Next => daemon.playback_skip_forward(ctx, 1).await?,
PlaybackAction::Previous => daemon.playback_skip_backward(ctx, 1).await?,
PlaybackAction::Seek(seek_type, duration) => {
daemon.playback_seek(ctx, seek_type, duration).await?;
}
PlaybackAction::Volume(VolumeAction::Increase(amount)) => {
daemon.playback_volume_up(ctx, amount).await?;
}
PlaybackAction::Volume(VolumeAction::Decrease(amount)) => {
daemon.playback_volume_down(ctx, amount).await?;
}
PlaybackAction::ToggleMute => daemon.playback_volume_toggle_mute(ctx).await?,
}
Ok(())
}
async fn handle_queue(daemon: Arc<MusicPlayerClient>, action: QueueAction) -> anyhow::Result<()> {
let ctx = tarpc::context::current();
match action {
QueueAction::Add(ids) => daemon.queue_add_list(ctx, ids).await??,
QueueAction::Remove(index) => {
#[allow(clippy::range_plus_one)]
daemon.queue_remove_range(ctx, index..index + 1).await?;
}
QueueAction::SetPosition(index) => daemon.queue_set_index(ctx, index).await?,
QueueAction::Shuffle => daemon.playback_shuffle(ctx).await?,
QueueAction::Clear => daemon.playback_clear(ctx).await?,
QueueAction::SetRepeatMode(mode) => daemon.playback_repeat(ctx, mode).await?,
}
Ok(())
}