diff --git a/src/play.rs b/src/play.rs index d57a94b..0272c0c 100644 --- a/src/play.rs +++ b/src/play.rs @@ -1,5 +1,6 @@ //! Responsible for the basic initialization & shutdown of the audio server & frontend. +use std::env; use std::io::{stdout, IsTerminal}; use std::path::PathBuf; use std::sync::Arc; @@ -104,7 +105,7 @@ pub async fn play(args: Args) -> eyre::Result<()> { // Initialize the UI, as well as the internal communication channel. let (tx, rx) = mpsc::channel(8); - let ui = if stdout().is_terminal() { + let ui = if stdout().is_terminal() && !(env::var("LOWFI_DISABLE_UI") == Ok("1".to_owned())) { Some(task::spawn(ui::start( Arc::clone(&player), tx.clone(), @@ -118,7 +119,14 @@ pub async fn play(args: Args) -> eyre::Result<()> { tx.send(Messages::Init).await?; // Actually starts the player. - Player::play(Arc::clone(&player), tx.clone(), rx, args.buffer_size).await?; + Player::play( + Arc::clone(&player), + tx.clone(), + rx, + args.buffer_size, + args.debug, + ) + .await?; // Save the volume.txt file for the next session. PersistentVolume::save(player.sink.volume()).await?; diff --git a/src/player.rs b/src/player.rs index c099dc1..1a9cb4f 100644 --- a/src/player.rs +++ b/src/player.rs @@ -31,7 +31,7 @@ use mpris_server::{PlaybackStatus, PlayerInterface, Property}; use crate::{ messages::Messages, play::{PersistentVolume, SendableOutputStream}, - tracks::{self, bookmark, list::List}, + tracks::{self, bookmark, list::List, TrackError}, Args, }; @@ -193,7 +193,7 @@ impl Player { /// This will play the next track, as well as refilling the buffer in the background. /// /// This will also set `current` to the newly loaded song. - pub async fn next(&self) -> Result { + pub async fn next(&self) -> Result { // TODO: Consider replacing this with `unwrap_or_else` when async closures are stablized. let track = self.tracks.write().await.pop_front(); let track = if let Some(track) = track { @@ -209,7 +209,7 @@ impl Player { self.list.random(&self.client).await? }; - let decoded = track.decode().map_err(|_| false)?; + let decoded = track.decode().map_err(|x| TrackError::new_eyre(false, x))?; // Set the current track. self.set_current(decoded.info.clone()); @@ -229,6 +229,7 @@ impl Player { player: Arc, itx: Sender<()>, tx: Sender, + debug: bool, ) -> eyre::Result<()> { // Stop the sink. player.sink.stop(); @@ -247,8 +248,12 @@ impl Player { // Notify the audio server that the next song has actually been downloaded. tx.send(Messages::NewSong).await?; } - Err(timeout) => { - if !timeout { + Err(error) => { + if !error.timeout { + if debug { + panic!("{:?}", error) + } + sleep(TIMEOUT).await; } @@ -271,6 +276,7 @@ impl Player { tx: Sender, mut rx: Receiver, buf_size: usize, + debug: bool, ) -> eyre::Result<()> { // Initialize the mpris player. // @@ -287,7 +293,7 @@ impl Player { // `itx` is used to notify the `Downloader` when it needs to download new tracks. let downloader = Downloader::new(Arc::clone(&player), buf_size); - let (itx, downloader) = downloader.start(); + let (itx, downloader) = downloader.start(debug); // Start buffering tracks immediately. Downloader::notify(&itx).await?; @@ -343,6 +349,7 @@ impl Player { Arc::clone(&player), itx.clone(), tx.clone(), + debug, )); } Messages::Play => { diff --git a/src/player/downloader.rs b/src/player/downloader.rs index def9ff0..4a1a450 100644 --- a/src/player/downloader.rs +++ b/src/player/downloader.rs @@ -51,7 +51,7 @@ impl Downloader { } /// Actually starts & consumes the [Downloader]. - pub fn start(mut self) -> (Sender<()>, JoinHandle<()>) { + pub fn start(mut self, debug: bool) -> (Sender<()>, JoinHandle<()>) { ( self.tx, task::spawn(async move { @@ -62,8 +62,12 @@ impl Downloader { let data = self.player.list.random(&self.player.client).await; match data { Ok(track) => self.player.tracks.write().await.push_back(track), - Err(timeout) => { - if !timeout { + Err(error) => { + if !error.timeout { + if debug { + panic!("{}", error) + } + sleep(TIMEOUT).await; } } diff --git a/src/tracks.rs b/src/tracks.rs index 161b39d..cceb860 100644 --- a/src/tracks.rs +++ b/src/tracks.rs @@ -15,7 +15,7 @@ //! 2. [`Info`] created from decoded data. //! 3. [`Decoded`] made from [`Info`] and the original decoded data. -use std::{io::Cursor, time::Duration}; +use std::{error::Error, io::Cursor, time::Duration}; use bytes::Bytes; use eyre::OptionExt as _; @@ -186,3 +186,48 @@ impl Decoded { Ok(Self { info, data }) } } + +#[derive(Debug)] +pub struct TrackError { + pub timeout: bool, + inner: Option, +} + +impl<'a> std::fmt::Display for TrackError { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!( + f, + "TrackError (timeout: {}): {:?}", + self.timeout, self.inner + ) + } +} + +impl<'a> std::error::Error for TrackError { + fn source(&self) -> Option<&(dyn Error + 'static)> { + self.inner.as_ref().map(|e| e.as_ref()) + } +} + +impl TrackError { + pub fn new(timeout: bool, inner: T) -> Self { + Self { + inner: Some(eyre::eyre!(inner)), + timeout, + } + } + + pub fn new_eyre(timeout: bool, inner: eyre::Error) -> Self { + Self { + inner: Some(inner), + timeout, + } + } + + pub fn empty(timeout: bool) -> Self { + Self { + inner: None, + timeout, + } + } +} diff --git a/src/tracks/list.rs b/src/tracks/list.rs index e1c6e20..02a73e0 100644 --- a/src/tracks/list.rs +++ b/src/tracks/list.rs @@ -7,7 +7,7 @@ use rand::Rng as _; use reqwest::Client; use tokio::fs; -use crate::data_dir; +use crate::{data_dir, tracks::TrackError}; use super::Track; @@ -52,7 +52,7 @@ impl List { } /// Downloads a raw track, but doesn't decode it. - async fn download(&self, track: &str, client: &Client) -> Result<(Bytes, String), bool> { + async fn download(&self, track: &str, client: &Client) -> Result<(Bytes, String), TrackError> { // If the track has a protocol, then we should ignore the base for it. let full_path = if track.contains("://") { track.to_owned() @@ -62,23 +62,28 @@ impl List { let data: Bytes = if let Some(x) = full_path.strip_prefix("file://") { let path = if x.starts_with("~") { - let home_path = dirs::home_dir().ok_or(false)?; - let home = home_path.to_str().ok_or(false)?; + let home_path = dirs::home_dir().ok_or(TrackError::empty(false))?; + let home = home_path.to_str().ok_or(TrackError::empty(false))?; x.replace("~", home) } else { x.to_owned() }; - let result = tokio::fs::read(path).await.map_err(|_| false)?; + let result = tokio::fs::read(path) + .await + .map_err(|x| TrackError::new(false, x))?; result.into() } else { let response = client .get(full_path.clone()) .send() .await - .map_err(|x| x.is_timeout())?; - response.bytes().await.map_err(|_| false)? + .map_err(|x| TrackError::new(x.is_timeout(), x))?; + response + .bytes() + .await + .map_err(|x| TrackError::new(false, x))? }; Ok((data, full_path)) @@ -88,7 +93,7 @@ impl List { /// /// The Result's error is a bool, which is true if a timeout error occured, /// and false otherwise. This tells lowfi if it shouldn't wait to try again. - pub async fn random(&self, client: &Client) -> Result { + pub async fn random(&self, client: &Client) -> Result { let (path, custom_name) = self.random_path(); let (data, full_path) = self.download(&path, client).await?;