chore: eliminate public statics

This commit is contained in:
Tal 2026-01-03 13:25:21 +01:00
parent 9acfcdcf9d
commit 6f6a552de5
13 changed files with 67 additions and 31 deletions

View File

@ -1,3 +1,5 @@
//! Some simple audio related utilities.
pub mod waiter;
/// This gets the output stream while also shutting up alsa with [libc].

View File

@ -1,3 +1,5 @@
//! All of the logic and state relating to the downloader.
use std::{
sync::atomic::{self, AtomicBool, AtomicU8},
time::Duration,
@ -13,14 +15,40 @@ use tokio::sync::mpsc;
/// indicate to the UI that a download is in progress.
static LOADING: AtomicBool = AtomicBool::new(false);
/// Global download progress in the range 0..=100 updated atomically.
/// Global download progress as an integer updated atomically.
///
/// The UI can read this `AtomicU8` to render a global progress indicator
/// when there isn't an immediately queued track available.
pub(crate) static PROGRESS: AtomicU8 = AtomicU8::new(0);
/// This is just a [`AtomicU8`] from 0 to 255, really representing
/// a progress percentage as just a simple integer. For instance,
/// 0.5 would be represented here as 127.
static PROGRESS: AtomicU8 = AtomicU8::new(0);
/// A convenient alias for the progress `AtomicU8` pointer type.
pub type Progress = &'static AtomicU8;
/// A convenient wrapper for the global progress. This is updated by the downloader,
/// and then accessed by the UI to display progress when there isn't an available
/// queued track.
#[derive(Clone, Copy, Debug)]
pub struct Progress(&'static AtomicU8);
impl Progress {
/// Creates a new handle to the global progress.
pub fn new() -> Self {
Self(&PROGRESS)
}
/// Sets the global progress.
///
/// `value` must be between 0 and 1.
pub fn set(&self, value: f32) {
self.0.store(
(value * f32::from(u8::MAX)).round() as u8,
atomic::Ordering::Relaxed,
);
}
/// Returns the global progress as a [`f32`] between 0 and 1.
pub fn get(&self) -> f32 {
f32::from(self.0.load(atomic::Ordering::Relaxed)) / f32::from(u8::MAX)
}
}
/// The downloader, which has all of the state necessary
/// to download tracks and add them to the queue.
@ -57,7 +85,7 @@ impl Downloader {
loop {
let result = self
.tracks
.random(&self.client, &PROGRESS, &mut self.rng)
.random(&self.client, Progress::new(), &mut self.rng)
.await;
match result {
@ -106,7 +134,7 @@ impl Handle {
pub fn track(&mut self) -> Output {
self.queue.try_recv().map_or_else(|_| {
LOADING.store(true, atomic::Ordering::Relaxed);
Output::Loading(Some(&PROGRESS))
Output::Loading(Some(Progress::new()))
}, Output::Queued,
)
}

View File

@ -1,11 +1,12 @@
//! 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 downloader;
pub mod error;
pub mod message;
pub mod player;

View File

@ -1,11 +1,12 @@
use std::sync::Arc;
//! The player, which contains all of the core logic behind the music player.
use std::sync::Arc;
use tokio::sync::mpsc::{self, Receiver};
use crate::{
audio::waiter,
bookmark::Bookmarks,
download,
downloader,
tracks::{self, List},
ui,
volume::PersistentVolume,
@ -20,7 +21,7 @@ use crate::{
pub enum Current {
/// Waiting for a track to arrive. The optional `Progress` is used to
/// indicate global download progress when present.
Loading(Option<download::Progress>),
Loading(Option<downloader::Progress>),
/// A decoded track that can be played; contains the track `Info`.
Track(tracks::Info),
@ -52,7 +53,7 @@ pub struct Player {
current: Current,
/// Background downloader that fills the internal queue.
downloader: download::Handle,
downloader: downloader::Handle,
/// Receiver for incoming `Message` commands.
rx: Receiver<crate::Message>,
@ -158,10 +159,10 @@ impl Player {
self.sink.stop();
match self.downloader.track() {
download::Output::Loading(progress) => {
downloader::Output::Loading(progress) => {
self.set_current(Current::Loading(progress))?;
}
download::Output::Queued(queued) => self.play(queued)?,
downloader::Output::Queued(queued) => self.play(queued)?,
}
}
Message::Play => {

View File

@ -1,3 +1,4 @@
//! Built in web scraping, which isn't guaranteed to have a unified UI.
#![allow(clippy::all)]
use std::path::{Path, PathBuf};

View File

@ -1,7 +1,7 @@
//! Task management.
//!
//! This file aims to abstract a lot of annoying Rust async logic, which may be subject to change.
//! For those who are not intimately familiar with async rust, this will be very confusing.
//! This file aims to abstract a lot of potentially annoying Rust async logic, which may be
//! subject to change.
use futures_util::TryFutureExt;
use std::future::Future;

View File

@ -108,7 +108,7 @@ mod decoded {
#[cfg(test)]
mod list {
use crate::{download::PROGRESS, tracks::List};
use crate::{downloader::Progress, tracks::List};
use reqwest::Client;
#[test]
@ -172,7 +172,7 @@ mod list {
let client = Client::new();
let track = list
.random(&client, &PROGRESS, &mut fastrand::Rng::new())
.random(&client, Progress::new(), &mut fastrand::Rng::new())
.await
.unwrap();
assert_eq!(track.display, "Apple Juice");

View File

@ -125,7 +125,7 @@ mod interface {
use tokio::time::Instant;
use crate::{
download::PROGRESS,
downloader::Progress,
player::Current,
tracks,
ui::{Interface, State},
@ -175,9 +175,9 @@ mod interface {
#[tokio::test]
async fn progress() {
let sink = Arc::new(rodio::Sink::new().0);
PROGRESS.store(50, std::sync::atomic::Ordering::Relaxed);
Progress::new().set(0.5);
let mut state = State::initial(sink, String::from("test"));
state.current = Current::Loading(Some(&PROGRESS));
state.current = Current::Loading(Some(Progress::new()));
let menu = Interface::default().menu(&mut state);

View File

@ -1,10 +1,7 @@
//! The module containing all of the logic behind track lists,
//! as well as obtaining track names & downloading the raw audio data
use std::{
cmp::min,
sync::atomic::{AtomicU8, Ordering},
};
use std::cmp::min;
use bytes::{BufMut as _, Bytes, BytesMut};
use futures_util::StreamExt as _;
@ -13,6 +10,7 @@ use tokio::fs;
use crate::{
data_dir,
downloader::Progress,
tracks::{
self,
error::{self, WithTrackContext as _},
@ -70,7 +68,7 @@ impl List {
&self,
track: &str,
client: &Client,
progress: Option<&AtomicU8>,
progress: Option<Progress>,
) -> tracks::Result<(Bytes, String)> {
// If the track has a protocol, then we should ignore the base for it.
let path = if track.contains("://") {
@ -114,8 +112,7 @@ impl List {
while let Some(item) = stream.next().await {
let chunk = item.track(track)?;
downloaded = min(downloaded + (chunk.len() as u64), total);
let rounded = ((downloaded as f64) / (total as f64) * 100.0).round() as u8;
progress.store(rounded, Ordering::Relaxed);
progress.set(downloaded as f32 / total as f32);
bytes.put(chunk);
}
@ -133,7 +130,7 @@ impl List {
pub async fn random(
&self,
client: &Client,
progress: &AtomicU8,
progress: Progress,
rng: &mut fastrand::Rng,
) -> tracks::Result<Queued> {
let (path, display) = self.random_path(rng);

View File

@ -1,3 +1,5 @@
//! Everything which has to do with the user interface, including both input and output.
use std::sync::Arc;
use crate::player::Current;

View File

@ -1,3 +1,5 @@
//! Contains the initialization and other handling of the terminal environment.
use std::{io::stdout, panic};
use crossterm::{

View File

@ -1,3 +1,5 @@
//! Responsible for the actual graphical interface of lowfi.
use crate::{
ui::{self, State},
Args,

View File

@ -111,7 +111,7 @@ impl ActionBar {
pub fn action(state: &ui::State, width: usize) -> String {
let action = match state.current.clone() {
Current::Loading(progress) => {
ActionBar::Loading(progress.map(|x| x.load(std::sync::atomic::Ordering::Relaxed)))
ActionBar::Loading(progress.map(|x| (x.get() * 100.0).round() as u8))
}
Current::Track(info) => {
if state.sink.volume() < 0.01 {