From 34577efe8f6a63796caf7dc3583cc4e1113e449e Mon Sep 17 00:00:00 2001 From: talwat <83217276+talwat@users.noreply.github.com> Date: Mon, 17 Mar 2025 16:32:06 +0100 Subject: [PATCH] feat: add buffer size option --- Cargo.lock | 2 +- Cargo.toml | 2 +- src/main.rs | 10 +++++++--- src/play.rs | 4 ++-- src/player.rs | 11 +++++------ src/player/downloader.rs | 16 ++++++++++++---- 6 files changed, 28 insertions(+), 17 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 3ab9620..99c2d0e 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1453,7 +1453,7 @@ checksum = "a7a70ba024b9dc04c27ea2f0c0548feb474ec5c54bba33a7f72f873a39d07b24" [[package]] name = "lowfi" -version = "1.6.3-dev" +version = "1.6.4-dev" dependencies = [ "Inflector", "arc-swap", diff --git a/Cargo.toml b/Cargo.toml index a734da8..c746f48 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "lowfi" -version = "1.6.3-dev" +version = "1.6.4-dev" edition = "2021" description = "An extremely simple lofi player." license = "MIT" diff --git a/src/main.rs b/src/main.rs index 68b1942..c454d6d 100644 --- a/src/main.rs +++ b/src/main.rs @@ -12,7 +12,7 @@ mod tracks; mod scrape; /// An extremely simple lofi player. -#[derive(Parser)] +#[derive(Parser, Clone)] #[command(about, version)] #[allow( clippy::struct_excessive_bools, @@ -45,7 +45,11 @@ struct Args { /// Use a custom track list #[clap(long, short, alias = "list", short_alias = 'l')] - tracklist: Option, + track_list: Option, + + /// Song buffer size. + #[clap(long, short = 's', alias = "buffer", default_value_t = 5)] + buffer_size: usize, /// The command that was ran. /// This is [None] if no command was specified. @@ -54,7 +58,7 @@ struct Args { } /// Defines all of the extra commands lowfi can run. -#[derive(Subcommand)] +#[derive(Subcommand, Clone)] enum Commands { /// Scrapes the lofi girl website file server for files. Scrape { diff --git a/src/play.rs b/src/play.rs index ae5f000..424be66 100644 --- a/src/play.rs +++ b/src/play.rs @@ -102,13 +102,13 @@ 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 = task::spawn(ui::start(Arc::clone(&player), tx.clone(), args)); + let ui = task::spawn(ui::start(Arc::clone(&player), tx.clone(), args.clone())); // Sends the player an "init" signal telling it to start playing a song straight away. tx.send(Messages::Init).await?; // Actually starts the player. - Player::play(Arc::clone(&player), tx.clone(), rx).await?; + Player::play(Arc::clone(&player), tx.clone(), rx, args.buffer_size).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 c3a2d0b..a75d98b 100644 --- a/src/player.rs +++ b/src/player.rs @@ -71,9 +71,6 @@ pub enum Messages { /// The time to wait in between errors. const TIMEOUT: Duration = Duration::from_secs(3); -/// The amount of songs to buffer up. -const BUFFER_SIZE: usize = 5; - /// Main struct responsible for queuing up & playing tracks. // TODO: Consider refactoring [Player] from being stored in an [Arc], into containing many smaller [Arc]s. // TODO: In other words, this would change the type from `Arc` to just `Player`. @@ -178,7 +175,7 @@ impl Player { let volume = PersistentVolume::load().await?; // Load the track list. - let list = List::load(args.tracklist.as_ref()).await?; + let list = List::load(args.track_list.as_ref()).await?; // We should only shut up alsa forcefully on Linux if we really have to. #[cfg(target_os = "linux")] @@ -207,7 +204,7 @@ impl Player { .build()?; let player = Self { - tracks: RwLock::new(VecDeque::with_capacity(5)), + tracks: RwLock::new(VecDeque::with_capacity(args.buffer_size)), current: ArcSwapOption::new(None), client, sink, @@ -294,10 +291,12 @@ impl Player { /// skip tracks or pause. /// /// This will also initialize a [Downloader] as well as an MPRIS server if enabled. + /// The [Downloader]s internal buffer size is determined by `buf_size`. pub async fn play( player: Arc, tx: Sender, mut rx: Receiver, + buf_size: usize, ) -> eyre::Result<()> { // Initialize the mpris player. // @@ -313,7 +312,7 @@ impl Player { })?; // `itx` is used to notify the `Downloader` when it needs to download new tracks. - let downloader = Downloader::new(Arc::clone(&player)); + let downloader = Downloader::new(Arc::clone(&player), buf_size); let (itx, downloader) = downloader.start(); // Start buffering tracks immediately. diff --git a/src/player/downloader.rs b/src/player/downloader.rs index 3fd53bf..def9ff0 100644 --- a/src/player/downloader.rs +++ b/src/player/downloader.rs @@ -8,7 +8,7 @@ use tokio::{ time::sleep, }; -use super::{Player, BUFFER_SIZE, TIMEOUT}; +use super::{Player, TIMEOUT}; /// This struct is responsible for downloading tracks in the background. /// @@ -24,6 +24,9 @@ pub struct Downloader { /// A copy of the internal sender, which can be useful for keeping /// track of it. tx: Sender<()>, + + /// The size of the internal download buffer. + buf_size: usize, } impl Downloader { @@ -37,9 +40,14 @@ impl Downloader { /// /// This also sends a [`Sender`] which can be used to notify /// when the downloader needs to begin downloading more tracks. - pub fn new(player: Arc) -> Self { + pub fn new(player: Arc, buf_size: usize) -> Self { let (tx, rx) = mpsc::channel(8); - Self { player, rx, tx } + Self { + player, + rx, + tx, + buf_size, + } } /// Actually starts & consumes the [Downloader]. @@ -50,7 +58,7 @@ impl Downloader { // Loop through each update notification. while self.rx.recv().await == Some(()) { // For each update notification, we'll push tracks until the buffer is completely full. - while self.player.tracks.read().await.len() < BUFFER_SIZE { + while self.player.tracks.read().await.len() < self.buf_size { let data = self.player.list.random(&self.player.client).await; match data { Ok(track) => self.player.tracks.write().await.push_back(track),