mirror of
https://github.com/talwat/lowfi
synced 2025-12-09 16:34:12 +00:00
fix: error reporting
docs: fix typos
This commit is contained in:
parent
a26623f9c0
commit
e44a8b85c4
@ -3,11 +3,13 @@
|
|||||||
There are a few guidelines outlined here that will make it more likely for your PR to be accepted.
|
There are a few guidelines outlined here that will make it more likely for your PR to be accepted.
|
||||||
Only ones that are less obvious are going to be listed. If you need to ask, it's probably a no.
|
Only ones that are less obvious are going to be listed. If you need to ask, it's probably a no.
|
||||||
|
|
||||||
## 1. No AI
|
## 1. AI
|
||||||
|
|
||||||
You can use AI for searching and so on, and if there's something minor and tedious that you'd like
|
You can use AI for searching or if there's something minor and tedious (eg. tests) that you'd like
|
||||||
the AI to write then that's okay, but if it is noticable that you used AI then it is way too much.
|
to avoid having to do manually.
|
||||||
If you used AI, you aren't helping any maintainer by submitting your slop, it's just a hassle for them.
|
|
||||||
|
With that said, if it is noticeable that you used AI then it is way too much.
|
||||||
|
AI generated PR's do not help maintainers, it's just a hassle and frequently wastes their time.
|
||||||
|
|
||||||
## 2. Smaller is better
|
## 2. Smaller is better
|
||||||
|
|
||||||
|
|||||||
@ -1,7 +1,7 @@
|
|||||||
# Environment Variables
|
# Environment Variables
|
||||||
|
|
||||||
Lowfi has some more specific options, usually as a result of minor feature requests, which are only documented here.
|
Lowfi has some more specific options, usually as a result of minor feature requests, which are only documented here.
|
||||||
If you have some behaviour you'd like to change, which is quite specific, then see if one of these options suits you.
|
If you have some behavior you'd like to change, which is quite specific, then see if one of these options suits you.
|
||||||
|
|
||||||
* `LOWFI_FIXED_MPRIS_NAME` - Limits the number of lowfi instances to one, but ensures the player name is always `lowfi`.
|
* `LOWFI_FIXED_MPRIS_NAME` - Limits the number of lowfi instances to one, but ensures the player name is always `lowfi`.
|
||||||
* `LOWFI_DISABLE_UI` - Disables the UI.
|
* `LOWFI_DISABLE_UI` - Disables the UI.
|
||||||
|
|||||||
2
MUSIC.md
2
MUSIC.md
@ -58,7 +58,7 @@ I'm not making money on any of this, and I think degrading the experience for my
|
|||||||
fellow nerds who just want to listen to some lowfi without all the crap is not worth it.
|
fellow nerds who just want to listen to some lowfi without all the crap is not worth it.
|
||||||
|
|
||||||
At the end of the day, lowfi has a distinct UserAgent. Should chillhop ever take issue with
|
At the end of the day, lowfi has a distinct UserAgent. Should chillhop ever take issue with
|
||||||
it's behaviour, banning it is extremely simple. I don't want that to happen, but I
|
it's behavior, banning it is extremely simple. I don't want that to happen, but I
|
||||||
understand if it does.
|
understand if it does.
|
||||||
|
|
||||||
## Well, *I* Hate the Chillhop Music
|
## Well, *I* Hate the Chillhop Music
|
||||||
|
|||||||
20
README.md
20
README.md
@ -5,24 +5,6 @@ It'll do this as simply as it can: no albums, no ads, just lofi.
|
|||||||
|
|
||||||

|

|
||||||
|
|
||||||
## The Rewrite
|
|
||||||
|
|
||||||
This branch serves as a rewrite for lowfi. The main focus is to make the code more
|
|
||||||
maintainable. This includes such things as:
|
|
||||||
|
|
||||||
- Replacing `Mutex` & `Arc` with channels, massively improving readability and flow.
|
|
||||||
- More clearly handling tracks in different phases of loading, instead of having
|
|
||||||
a mess of different structs.
|
|
||||||
- Making the UI code cleaner and easier to follow.
|
|
||||||
- Rethinking input & control of the player, especially with MPRIS in mind.
|
|
||||||
- Making track loading simpler and more consistent.
|
|
||||||
|
|
||||||
This is an *internal rewrite*, and the goal is to retain every single feature.
|
|
||||||
If there is a feature present in the original version of lowfi that is not present
|
|
||||||
in the rewrite, then it is a bug and must be implemented.
|
|
||||||
|
|
||||||
Currently, it is in an extremely early and non-functional state.
|
|
||||||
|
|
||||||
## Disclaimer
|
## Disclaimer
|
||||||
|
|
||||||
As of the 1.7.0 version of lowfi, **all** of the audio files embedded
|
As of the 1.7.0 version of lowfi, **all** of the audio files embedded
|
||||||
@ -207,7 +189,7 @@ If you are dealing with the 1% using another audio format which is in
|
|||||||
|
|
||||||
> [!NOTE]
|
> [!NOTE]
|
||||||
> Some nice users, especially [danielwerg](https://github.com/danielwerg),
|
> Some nice users, especially [danielwerg](https://github.com/danielwerg),
|
||||||
> have aleady made alternative track lists located in the [data](https://github.com/talwat/lowfi/blob/main/data/)
|
> have already made alternative track lists located in the [data](https://github.com/talwat/lowfi/blob/main/data/)
|
||||||
> directory of this repo. You can use them with lowfi by using the `--track-list` flag.
|
> directory of this repo. You can use them with lowfi by using the `--track-list` flag.
|
||||||
>
|
>
|
||||||
> Feel free to contribute your own list with a PR.
|
> Feel free to contribute your own list with a PR.
|
||||||
|
|||||||
20
src/error.rs
20
src/error.rs
@ -15,39 +15,39 @@ pub type Result<T> = std::result::Result<T, Error>;
|
|||||||
#[derive(Debug, thiserror::Error)]
|
#[derive(Debug, thiserror::Error)]
|
||||||
pub enum Error {
|
pub enum Error {
|
||||||
/// Errors while loading or saving the persistent volume settings.
|
/// Errors while loading or saving the persistent volume settings.
|
||||||
#[error("unable to load/save the persistent volume: {0}")]
|
#[error("unable to load/save the persistent volume")]
|
||||||
PersistentVolume(#[from] volume::Error),
|
PersistentVolume(#[from] volume::Error),
|
||||||
|
|
||||||
/// Errors while loading or saving bookmarks.
|
/// Errors while loading or saving bookmarks.
|
||||||
#[error("unable to load/save bookmarks: {0}")]
|
#[error("unable to load/save bookmarks")]
|
||||||
Bookmarks(#[from] bookmark::Error),
|
Bookmarks(#[from] bookmark::Error),
|
||||||
|
|
||||||
/// Network request failures from `reqwest`.
|
/// Network request failures from `reqwest`.
|
||||||
#[error("unable to fetch data: {0}")]
|
#[error("unable to fetch data")]
|
||||||
Request(#[from] reqwest::Error),
|
Request(#[from] reqwest::Error),
|
||||||
|
|
||||||
/// Failure converting to/from a C string (FFI helpers).
|
/// Failure converting to/from a C string (FFI helpers).
|
||||||
#[error("C string null error: {0}")]
|
#[error("C string null error")]
|
||||||
FfiNull(#[from] std::ffi::NulError),
|
FfiNull(#[from] std::ffi::NulError),
|
||||||
|
|
||||||
/// Errors coming from the audio backend / stream handling.
|
/// Errors coming from the audio backend / stream handling.
|
||||||
#[error("audio playing error: {0}")]
|
#[error("audio playing error")]
|
||||||
Rodio(#[from] rodio::StreamError),
|
Rodio(#[from] rodio::StreamError),
|
||||||
|
|
||||||
/// Failure to send an internal `Message` over the mpsc channel.
|
/// Failure to send an internal `Message` over the mpsc channel.
|
||||||
#[error("couldn't send internal message: {0}")]
|
#[error("couldn't send internal message")]
|
||||||
Send(#[from] mpsc::error::SendError<crate::Message>),
|
Send(#[from] mpsc::error::SendError<crate::Message>),
|
||||||
|
|
||||||
/// Failure to enqueue a track into the queue channel.
|
/// Failure to enqueue a track into the queue channel.
|
||||||
#[error("couldn't add track to the queue: {0}")]
|
#[error("couldn't add track to the queue")]
|
||||||
Queue(#[from] mpsc::error::SendError<tracks::Queued>),
|
Queue(#[from] mpsc::error::SendError<tracks::Queued>),
|
||||||
|
|
||||||
/// Failure to broadcast UI updates.
|
/// Failure to broadcast UI updates.
|
||||||
#[error("couldn't update UI state: {0}")]
|
#[error("couldn't update UI state")]
|
||||||
Broadcast(#[from] broadcast::error::SendError<ui::Update>),
|
Broadcast(#[from] broadcast::error::SendError<ui::Update>),
|
||||||
|
|
||||||
/// Generic IO error.
|
/// Generic IO error.
|
||||||
#[error("io error: {0}")]
|
#[error("io error")]
|
||||||
Io(#[from] std::io::Error),
|
Io(#[from] std::io::Error),
|
||||||
|
|
||||||
/// Data directory was not found or could not be determined.
|
/// Data directory was not found or could not be determined.
|
||||||
@ -59,7 +59,7 @@ pub enum Error {
|
|||||||
Download,
|
Download,
|
||||||
|
|
||||||
/// Integer parsing errors.
|
/// Integer parsing errors.
|
||||||
#[error("couldn't parse integer: {0}")]
|
#[error("couldn't parse integer")]
|
||||||
Parse(#[from] std::num::ParseIntError),
|
Parse(#[from] std::num::ParseIntError),
|
||||||
|
|
||||||
/// Track subsystem error.
|
/// Track subsystem error.
|
||||||
|
|||||||
15
src/main.rs
15
src/main.rs
@ -96,6 +96,14 @@ pub fn data_dir() -> crate::Result<PathBuf> {
|
|||||||
Ok(dir)
|
Ok(dir)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async fn player(args: Args, environment: ui::Environment) -> crate::Result<()> {
|
||||||
|
let stream = audio::stream()?;
|
||||||
|
let mut player = Player::init(args, environment, stream.mixer()).await?;
|
||||||
|
player.run().await?;
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
/// Program entry point.
|
/// Program entry point.
|
||||||
///
|
///
|
||||||
/// Parses CLI arguments, initializes the audio stream and player, then
|
/// Parses CLI arguments, initializes the audio stream and player, then
|
||||||
@ -116,10 +124,9 @@ async fn main() -> eyre::Result<()> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let stream = audio::stream()?;
|
let environment = ui::Environment::ready(args.alternate)?;
|
||||||
let mut player = Player::init(args, stream.mixer()).await?;
|
let result = player(args, environment).await;
|
||||||
let result = player.run().await;
|
environment.cleanup(result.is_ok())?;
|
||||||
|
|
||||||
player.environment().cleanup(result.is_ok())?;
|
|
||||||
Ok(result?)
|
Ok(result?)
|
||||||
}
|
}
|
||||||
|
|||||||
@ -115,7 +115,11 @@ impl Player {
|
|||||||
/// This sets up the audio sink, UI, downloader, bookmarks and persistent
|
/// This sets up the audio sink, UI, downloader, bookmarks and persistent
|
||||||
/// volume state. The function returns a fully constructed `Player` ready
|
/// volume state. The function returns a fully constructed `Player` ready
|
||||||
/// 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,
|
||||||
|
environment: ui::Environment,
|
||||||
|
mixer: &rodio::mixer::Mixer,
|
||||||
|
) -> crate::Result<Self> {
|
||||||
let (tx, rx) = mpsc::channel(8);
|
let (tx, rx) = mpsc::channel(8);
|
||||||
if args.paused {
|
if args.paused {
|
||||||
tx.send(Message::Pause).await?;
|
tx.send(Message::Pause).await?;
|
||||||
@ -133,7 +137,7 @@ impl Player {
|
|||||||
sink.set_volume(volume.float());
|
sink.set_volume(volume.float());
|
||||||
|
|
||||||
Ok(Self {
|
Ok(Self {
|
||||||
ui: ui::Handle::init(tx.clone(), urx, state, &args).await?,
|
ui: ui::Handle::init(tx.clone(), environment, urx, state, &args).await?,
|
||||||
downloader: Downloader::init(
|
downloader: Downloader::init(
|
||||||
args.buffer_size as usize,
|
args.buffer_size as usize,
|
||||||
args.timeout,
|
args.timeout,
|
||||||
|
|||||||
48
src/ui.rs
48
src/ui.rs
@ -1,4 +1,4 @@
|
|||||||
use std::sync::Arc;
|
use std::{env, sync::Arc};
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
player::Current,
|
player::Current,
|
||||||
@ -39,6 +39,9 @@ pub enum Error {
|
|||||||
#[error("sharing state between backend and frontend failed: {0}")]
|
#[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>),
|
||||||
|
|
||||||
|
#[error("you can't disable the UI without MPRIS!")]
|
||||||
|
RejectedDisable,
|
||||||
|
|
||||||
#[cfg(feature = "mpris")]
|
#[cfg(feature = "mpris")]
|
||||||
#[error("mpris bus error: {0}")]
|
#[error("mpris bus error: {0}")]
|
||||||
ZBus(#[from] mpris_server::zbus::Error),
|
ZBus(#[from] mpris_server::zbus::Error),
|
||||||
@ -115,6 +118,27 @@ struct Tasks {
|
|||||||
input: JoinHandle<Result<()>>,
|
input: JoinHandle<Result<()>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl Tasks {
|
||||||
|
pub fn spawn(
|
||||||
|
tx: Sender<crate::Message>,
|
||||||
|
updater: broadcast::Receiver<ui::Update>,
|
||||||
|
state: State,
|
||||||
|
params: interface::Params,
|
||||||
|
) -> Self {
|
||||||
|
Self {
|
||||||
|
render: tokio::spawn(Handle::ui(updater, state, params)),
|
||||||
|
input: tokio::spawn(input::listen(tx)),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Drop for Tasks {
|
||||||
|
fn drop(&mut self) {
|
||||||
|
self.input.abort();
|
||||||
|
self.render.abort();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// The UI handle for controlling the state of the UI, as well as
|
/// The UI handle for controlling the state of the UI, as well as
|
||||||
/// updating MPRIS information and other small interfacing tasks.
|
/// updating MPRIS information and other small interfacing tasks.
|
||||||
pub struct Handle {
|
pub struct Handle {
|
||||||
@ -126,14 +150,7 @@ pub struct Handle {
|
|||||||
pub mpris: mpris::Server,
|
pub mpris: mpris::Server,
|
||||||
|
|
||||||
/// The UI's running tasks.
|
/// The UI's running tasks.
|
||||||
tasks: Tasks,
|
_tasks: Option<Tasks>,
|
||||||
}
|
|
||||||
|
|
||||||
impl Drop for Handle {
|
|
||||||
fn drop(&mut self) {
|
|
||||||
self.tasks.input.abort();
|
|
||||||
self.tasks.render.abort();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Handle {
|
impl Handle {
|
||||||
@ -174,19 +191,22 @@ impl Handle {
|
|||||||
#[allow(clippy::unused_async)]
|
#[allow(clippy::unused_async)]
|
||||||
pub async fn init(
|
pub async fn init(
|
||||||
tx: Sender<crate::Message>,
|
tx: Sender<crate::Message>,
|
||||||
|
environment: Environment,
|
||||||
updater: broadcast::Receiver<ui::Update>,
|
updater: broadcast::Receiver<ui::Update>,
|
||||||
state: State,
|
state: State,
|
||||||
args: &Args,
|
args: &Args,
|
||||||
) -> Result<Self> {
|
) -> Result<Self> {
|
||||||
let environment = Environment::ready(args.alternate)?;
|
let disabled = env::var("LOWFI_DISABLE_UI").is_ok_and(|x| x == "1");
|
||||||
|
if disabled && !cfg!(feature = "mpris") {
|
||||||
|
return Err(Error::RejectedDisable);
|
||||||
|
}
|
||||||
|
|
||||||
Ok(Self {
|
Ok(Self {
|
||||||
#[cfg(feature = "mpris")]
|
#[cfg(feature = "mpris")]
|
||||||
mpris: mpris::Server::new(state.clone(), tx.clone(), updater.resubscribe()).await?,
|
mpris: mpris::Server::new(state.clone(), tx.clone(), updater.resubscribe()).await?,
|
||||||
environment,
|
environment,
|
||||||
tasks: Tasks {
|
_tasks: (!disabled)
|
||||||
render: tokio::spawn(Self::ui(updater, state, interface::Params::from(args))),
|
.then(|| Tasks::spawn(tx, updater, state, interface::Params::from(args))),
|
||||||
input: tokio::spawn(input::listen(tx)),
|
|
||||||
},
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -127,7 +127,7 @@ impl PlayerInterface for Player {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async fn stop(&self) -> fdo::Result<()> {
|
async fn stop(&self) -> fdo::Result<()> {
|
||||||
self.pause().await
|
self.sender.send(Message::Quit).await
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn play(&self) -> fdo::Result<()> {
|
async fn play(&self) -> fdo::Result<()> {
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user