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.
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 {
/// Initializes the downloader with a track list.
///
/// `tx` specifies the [`Sender`] to be notified with [`crate::Message::Loaded`].
pub fn init(size: usize, tracks: tracks::List, tx: Sender<crate::Message>) -> Handle {
let client = Client::new();
pub fn init(
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 downloader = Self {
@ -66,19 +73,20 @@ impl Downloader {
tx,
tracks,
client,
timeout: Duration::from_secs(1),
};
Handle {
Ok(Handle {
queue: qrx,
task: tokio::spawn(downloader.run()),
}
})
}
/// Actually runs the downloader, consuming it and beginning
/// the cycle of downloading tracks and reporting to the
/// rest of the program.
async fn run(self) -> crate::Result<()> {
const ERROR_TIMEOUT: Duration = Duration::from_secs(1);
loop {
let result = self.tracks.random(&self.client, &PROGRESS).await;
match result {
@ -93,7 +101,7 @@ impl Downloader {
Err(error) => {
PROGRESS.store(0, atomic::Ordering::Relaxed);
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,
/// Timeout in seconds for music downloads.
#[clap(long, default_value_t = 3)]
#[clap(long, default_value_t = 16)]
timeout: u64,
/// Include ALSA & other logs.
@ -117,10 +117,9 @@ async fn main() -> eyre::Result<()> {
}
let stream = audio::stream()?;
let player = Player::init(args, stream.mixer()).await?;
let environment = player.environment();
let mut player = Player::init(args, stream.mixer()).await?;
let result = player.run().await;
environment.cleanup(result.is_ok())?;
player.environment().cleanup(result.is_ok())?;
Ok(result?)
}

View File

@ -2,7 +2,8 @@
#[allow(dead_code, reason = "this code may not be dead depending on features")]
#[derive(PartialEq, Debug, Clone)]
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,
/// 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`.
pub async fn init(args: crate::Args, mixer: &rodio::mixer::Mixer) -> crate::Result<Self> {
let (tx, rx) = mpsc::channel(8);
if args.paused {
tx.send(Message::Pause).await?;
}
tx.send(Message::Init).await?;
let (utx, urx) = broadcast::channel(8);
@ -130,7 +134,12 @@ impl Player {
Ok(Self {
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),
bookmarks: Bookmarks::load().await?,
current: Current::default(),
@ -159,10 +168,11 @@ impl Player {
Ok(())
}
/// Drive the main message loop. This function consumes the `Player` and
/// will return when a `Message::Quit` is received. It handles commands
/// Drives the main message loop of the player.
///
/// This will return when a `Message::Quit` is received. It handles commands
/// 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 {
match message {
Message::Next | Message::Init | Message::Loaded => {
@ -175,9 +185,7 @@ impl Player {
download::Output::Loading(progress) => {
self.set_current(Current::Loading(progress))?;
}
download::Output::Queued(queued) => {
self.play(queued)?;
}
download::Output::Queued(queued) => self.play(queued)?,
}
}
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.
#[derive(Debug, thiserror::Error)]
pub enum Error {
#[error("unable to convert number")]
#[error("unable to convert number: {0}")]
Conversion(#[from] std::num::TryFromIntError),
#[error("unable to write output")]
#[error("unable to write output: {0}")]
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>),
#[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>),
#[cfg(feature = "mpris")]
#[error("mpris bus error")]
#[error("mpris bus error: {0}")]
ZBus(#[from] mpris_server::zbus::Error),
#[cfg(feature = "mpris")]
#[error("mpris fdo (zbus interface) error")]
#[error("mpris fdo (zbus interface) error: {0}")]
Fdo(#[from] mpris_server::zbus::fdo::Error),
}

View File

@ -77,13 +77,6 @@ impl PersistentVolume {
pub async fn save(volume: f32) -> Result<()> {
let config = Self::config().await?;
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;
fs::write(path, percentage.to_string()).await?;