lowfi/src/main.rs
Tal 6802db1a1e
feat: better task management (#113)
* docs: attempt to explain tasks.rs

* fix: switch to using a joinset

* chore: remove unused tokio-utils import

* style: reorganize code
2025-12-28 17:12:40 +01:00

135 lines
3.6 KiB
Rust

//! An extremely simple lofi player.
use crate::player::Player;
use clap::{Parser, Subcommand};
use std::path::PathBuf;
pub mod audio;
pub mod bookmark;
pub mod download;
pub mod error;
pub mod message;
pub mod player;
#[cfg(feature = "scrape")]
mod scrapers;
pub mod tasks;
mod tests;
pub mod tracks;
pub mod ui;
pub mod volume;
#[cfg(feature = "scrape")]
use crate::scrapers::Source;
pub use error::{Error, Result};
pub use message::Message;
pub use tasks::Tasks;
/// An extremely simple lofi player.
#[derive(Parser, Clone)]
#[command(about, version)]
#[allow(clippy::struct_excessive_bools)]
pub struct Args {
/// Use an alternate terminal screen.
#[clap(long, short)]
alternate: bool,
/// Hide the bottom control bar.
#[clap(long, short)]
minimalist: bool,
/// Exclude window borders.
#[clap(long, short)]
borderless: bool,
/// Include a clock.
#[clap(long, short)]
clock: bool,
/// Start lowfi paused.
#[clap(long, short)]
paused: bool,
/// FPS of the UI.
#[clap(long, short, default_value_t = 12)]
fps: u8,
/// Timeout in seconds for music downloads.
#[clap(long, default_value_t = 16)]
timeout: u64,
/// Include ALSA & other logs.
#[clap(long, short)]
debug: bool,
/// Width of the player, from 0 to 32.
#[clap(long, short, default_value_t = 3)]
width: usize,
/// Track list to play music from
#[clap(long, short, alias = "list", alias = "tracks", short_alias = 'l', default_value_t = String::from("chillhop"))]
track_list: String,
/// Internal song buffer size.
#[clap(long, short = 's', alias = "buffer", default_value_t = 5, value_parser = clap::value_parser!(u32).range(2..))]
buffer_size: u32,
/// The command that was ran.
/// This is [None] if no command was specified.
#[command(subcommand)]
command: Option<Commands>,
}
/// Defines all of the extra commands lowfi can run.
#[derive(Subcommand, Clone)]
enum Commands {
/// Scrapes a music source for files.
#[cfg(feature = "scrape")]
Scrape {
// The source to scrape from.
source: scrapers::Source,
},
}
/// Returns the application data directory used for persistency.
///
/// The function returns the platform-specific user data directory with
/// a `lowfi` subfolder. Callers may use this path to store config,
/// bookmarks, and other persistent files.
pub fn data_dir() -> crate::Result<PathBuf> {
let dir = dirs::data_dir().unwrap().join("lowfi");
Ok(dir)
}
/// Program entry point.
///
/// Parses CLI arguments, initializes the audio stream and player, then
/// runs the main event loop. On exit it performs cleanup of the UI and
/// returns the inner result.
#[tokio::main(flavor = "current_thread")]
async fn main() -> eyre::Result<()> {
let args = Args::parse();
#[cfg(feature = "scrape")]
if let Some(command) = &args.command {
return match command {
Commands::Scrape { source } => match source {
Source::Archive => scrapers::archive::scrape().await,
Source::Lofigirl => scrapers::lofigirl::scrape().await,
Source::Chillhop => scrapers::chillhop::scrape().await,
},
};
}
let stream = audio::stream()?;
let environment = ui::Environment::ready(args.alternate)?;
let (mut player, mut tasks) = Player::init(args, stream.mixer())
.await
.inspect_err(|_| environment.cleanup(false).unwrap())?;
let result = tasks.wait(player.run()).await;
environment.cleanup(result.is_ok())?;
player.close().await?;
Ok(result?)
}