fix: make the paused flag function properly

This commit is contained in:
Tal 2025-12-06 15:48:11 +01:00
parent f88b46ec56
commit cba68de6ff
6 changed files with 45 additions and 36 deletions

View File

@ -46,19 +46,26 @@ pub struct Downloader {
/// The [`reqwest`] client to use for downloads. /// The [`reqwest`] client to use for downloads.
client: Client, client: Client,
/// The timeout to use for both the client,
/// and also how long to wait between trying
/// again after a failed download.
timeout: Duration,
} }
impl Downloader { impl Downloader {
/// Initializes the downloader with a track list. /// Initializes the downloader with a track list.
/// ///
/// `tx` specifies the [`Sender`] to be notified with [`crate::Message::Loaded`]. /// `tx` specifies the [`Sender`] to be notified with [`crate::Message::Loaded`].
pub fn init(size: usize, tracks: tracks::List, tx: Sender<crate::Message>) -> Handle { pub fn init(
let client = Client::new(); size: usize,
timeout: u64,
tracks: tracks::List,
tx: Sender<crate::Message>,
) -> crate::Result<Handle> {
let client = Client::builder()
.user_agent(concat!(
env!("CARGO_PKG_NAME"),
"/",
env!("CARGO_PKG_VERSION")
))
.timeout(Duration::from_secs(timeout))
.build()?;
let (qtx, qrx) = mpsc::channel(size - 1); let (qtx, qrx) = mpsc::channel(size - 1);
let downloader = Self { let downloader = Self {
@ -66,19 +73,20 @@ impl Downloader {
tx, tx,
tracks, tracks,
client, client,
timeout: Duration::from_secs(1),
}; };
Handle { Ok(Handle {
queue: qrx, queue: qrx,
task: tokio::spawn(downloader.run()), task: tokio::spawn(downloader.run()),
} })
} }
/// Actually runs the downloader, consuming it and beginning /// Actually runs the downloader, consuming it and beginning
/// the cycle of downloading tracks and reporting to the /// the cycle of downloading tracks and reporting to the
/// rest of the program. /// rest of the program.
async fn run(self) -> crate::Result<()> { async fn run(self) -> crate::Result<()> {
const ERROR_TIMEOUT: Duration = Duration::from_secs(1);
loop { loop {
let result = self.tracks.random(&self.client, &PROGRESS).await; let result = self.tracks.random(&self.client, &PROGRESS).await;
match result { match result {
@ -93,7 +101,7 @@ impl Downloader {
Err(error) => { Err(error) => {
PROGRESS.store(0, atomic::Ordering::Relaxed); PROGRESS.store(0, atomic::Ordering::Relaxed);
if !error.timeout() { if !error.timeout() {
tokio::time::sleep(self.timeout).await; tokio::time::sleep(ERROR_TIMEOUT).await;
} }
} }
} }

View File

@ -49,7 +49,7 @@ pub struct Args {
fps: u8, fps: u8,
/// Timeout in seconds for music downloads. /// Timeout in seconds for music downloads.
#[clap(long, default_value_t = 3)] #[clap(long, default_value_t = 16)]
timeout: u64, timeout: u64,
/// Include ALSA & other logs. /// Include ALSA & other logs.
@ -117,10 +117,9 @@ async fn main() -> eyre::Result<()> {
} }
let stream = audio::stream()?; let stream = audio::stream()?;
let player = Player::init(args, stream.mixer()).await?; let mut player = Player::init(args, stream.mixer()).await?;
let environment = player.environment();
let result = player.run().await; let result = player.run().await;
environment.cleanup(result.is_ok())?; player.environment().cleanup(result.is_ok())?;
Ok(result?) Ok(result?)
} }

View File

@ -2,7 +2,8 @@
#[allow(dead_code, reason = "this code may not be dead depending on features")] #[allow(dead_code, reason = "this code may not be dead depending on features")]
#[derive(PartialEq, Debug, Clone)] #[derive(PartialEq, Debug, Clone)]
pub enum Message { pub enum Message {
/// Deliberate user request to go to the next song. /// Deliberate user request to go to the next song, also sent when the
/// song is over by the waiter.
Next, Next,
/// When a track is loaded after the caller previously being told to wait. /// When a track is loaded after the caller previously being told to wait.

View File

@ -117,6 +117,10 @@ impl Player {
/// to be driven via `run`. /// to be driven via `run`.
pub async fn init(args: crate::Args, mixer: &rodio::mixer::Mixer) -> crate::Result<Self> { pub async fn init(args: crate::Args, mixer: &rodio::mixer::Mixer) -> crate::Result<Self> {
let (tx, rx) = mpsc::channel(8); let (tx, rx) = mpsc::channel(8);
if args.paused {
tx.send(Message::Pause).await?;
}
tx.send(Message::Init).await?; tx.send(Message::Init).await?;
let (utx, urx) = broadcast::channel(8); let (utx, urx) = broadcast::channel(8);
@ -130,7 +134,12 @@ impl Player {
Ok(Self { Ok(Self {
ui: ui::Handle::init(tx.clone(), urx, state, &args).await?, ui: ui::Handle::init(tx.clone(), urx, state, &args).await?,
downloader: Downloader::init(args.buffer_size as usize, list, tx.clone()), downloader: Downloader::init(
args.buffer_size as usize,
args.timeout,
list,
tx.clone(),
)?,
waiter: waiter::Handle::new(Arc::clone(&sink), tx), waiter: waiter::Handle::new(Arc::clone(&sink), tx),
bookmarks: Bookmarks::load().await?, bookmarks: Bookmarks::load().await?,
current: Current::default(), current: Current::default(),
@ -159,10 +168,11 @@ impl Player {
Ok(()) Ok(())
} }
/// Drive the main message loop. This function consumes the `Player` and /// Drives the main message loop of the player.
/// will return when a `Message::Quit` is received. It handles commands ///
/// This will return when a `Message::Quit` is received. It handles commands
/// coming from the frontend and updates playback/UI state accordingly. /// coming from the frontend and updates playback/UI state accordingly.
pub async fn run(mut self) -> crate::Result<()> { pub async fn run(&mut self) -> crate::Result<()> {
while let Some(message) = self.rx.recv().await { while let Some(message) = self.rx.recv().await {
match message { match message {
Message::Next | Message::Init | Message::Loaded => { Message::Next | Message::Init | Message::Loaded => {
@ -175,9 +185,7 @@ impl Player {
download::Output::Loading(progress) => { download::Output::Loading(progress) => {
self.set_current(Current::Loading(progress))?; self.set_current(Current::Loading(progress))?;
} }
download::Output::Queued(queued) => { download::Output::Queued(queued) => self.play(queued)?,
self.play(queued)?;
}
} }
} }
Message::Play => { Message::Play => {

View File

@ -27,24 +27,24 @@ type Result<T> = std::result::Result<T, Error>;
/// that occur while drawing the UI or handling input. /// that occur while drawing the UI or handling input.
#[derive(Debug, thiserror::Error)] #[derive(Debug, thiserror::Error)]
pub enum Error { pub enum Error {
#[error("unable to convert number")] #[error("unable to convert number: {0}")]
Conversion(#[from] std::num::TryFromIntError), Conversion(#[from] std::num::TryFromIntError),
#[error("unable to write output")] #[error("unable to write output: {0}")]
Write(#[from] std::io::Error), Write(#[from] std::io::Error),
#[error("sending message to backend from ui failed")] #[error("sending message to backend from ui failed: {0}")]
CrateSend(#[from] tokio::sync::mpsc::error::SendError<crate::Message>), CrateSend(#[from] tokio::sync::mpsc::error::SendError<crate::Message>),
#[error("sharing state between backend and frontend failed")] #[error("sharing state between backend and frontend failed: {0}")]
Send(#[from] tokio::sync::broadcast::error::SendError<Update>), Send(#[from] tokio::sync::broadcast::error::SendError<Update>),
#[cfg(feature = "mpris")] #[cfg(feature = "mpris")]
#[error("mpris bus error")] #[error("mpris bus error: {0}")]
ZBus(#[from] mpris_server::zbus::Error), ZBus(#[from] mpris_server::zbus::Error),
#[cfg(feature = "mpris")] #[cfg(feature = "mpris")]
#[error("mpris fdo (zbus interface) error")] #[error("mpris fdo (zbus interface) error: {0}")]
Fdo(#[from] mpris_server::zbus::fdo::Error), Fdo(#[from] mpris_server::zbus::fdo::Error),
} }

View File

@ -77,13 +77,6 @@ impl PersistentVolume {
pub async fn save(volume: f32) -> Result<()> { pub async fn save(volume: f32) -> Result<()> {
let config = Self::config().await?; let config = Self::config().await?;
let path = config.join(PathBuf::from("volume.txt")); let path = config.join(PathBuf::from("volume.txt"));
// Already rounded & absolute, therefore this should be safe.
#[expect(
clippy::as_conversions,
clippy::cast_sign_loss,
clippy::cast_possible_truncation
)]
let percentage = (volume * 100.0).abs().round() as u16; let percentage = (volume * 100.0).abs().round() as u16;
fs::write(path, percentage.to_string()).await?; fs::write(path, percentage.to_string()).await?;