mirror of
https://github.com/talwat/lowfi
synced 2025-06-07 12:02:36 +00:00
fix: begin work of improving error handling
This commit is contained in:
parent
e8b4b17f98
commit
1af976ad77
12
src/play.rs
12
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?;
|
||||
|
@ -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<tracks::Decoded, bool> {
|
||||
pub async fn next(&self) -> Result<tracks::Decoded, tracks::TrackError> {
|
||||
// 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<Self>,
|
||||
itx: Sender<()>,
|
||||
tx: Sender<Messages>,
|
||||
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<Messages>,
|
||||
mut rx: Receiver<Messages>,
|
||||
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 => {
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
@ -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<eyre::Error>,
|
||||
}
|
||||
|
||||
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<T: Error + 'static + Send + Sync>(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,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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<Track, bool> {
|
||||
pub async fn random(&self, client: &Client) -> Result<Track, TrackError> {
|
||||
let (path, custom_name) = self.random_path();
|
||||
let (data, full_path) = self.download(&path, client).await?;
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user