docs: added lots of new internal documentation

This commit is contained in:
Tal 2024-09-27 14:21:49 +02:00
parent 93a668bae0
commit 8110b8418a
6 changed files with 75 additions and 16 deletions

View File

@ -1,3 +1,12 @@
#![warn(
clippy::all,
clippy::restriction,
clippy::pedantic,
clippy::nursery,
clippy::cargo
)]
#![allow(clippy::single_call_fn)]
use clap::{Parser, Subcommand};
mod play;

View File

@ -1,3 +1,5 @@
//! Responsible for the basic initialization & shutdown of the audio server & frontend.
use std::sync::Arc;
use tokio::{
@ -8,6 +10,8 @@ use tokio::{
use crate::player::Player;
use crate::player::{ui, Messages};
/// Initializes the audio server, and then safely stops
/// it when the frontend quits.
pub async fn play() -> eyre::Result<()> {
let (tx, rx) = mpsc::channel(8);

View File

@ -1,3 +1,7 @@
//! Responsible for playing & queueing audio.
//! This also has the code for the underlying
//! audio server which adds new tracks.
use std::{collections::VecDeque, sync::Arc};
use arc_swap::ArcSwapOption;
@ -18,8 +22,13 @@ pub mod ui;
/// Handles communication between the frontend & audio player.
pub enum Messages {
/// Notifies the audio server that it should update the track.
Next,
/// Similar to Next, but specific to the first track.
Init,
/// Pauses the [Sink]. This will also unpause it if it is paused.
Pause,
}
@ -28,15 +37,36 @@ const BUFFER_SIZE: usize = 5;
/// Main struct responsible for queuing up & playing tracks.
pub struct Player {
/// [rodio]'s [`Sink`] which can control playback.
pub sink: Sink,
/// The [`TrackInfo`] of the current track.
/// This is [`None`] when lowfi is buffering.
pub current: ArcSwapOption<TrackInfo>,
/// The tracks, which is a [VecDeque] that holds
/// *undecoded* [Track]s.
tracks: RwLock<VecDeque<Track>>,
/// The web client, which can contain a UserAgent & some
/// settings that help lowfi work more effectively.
client: Client,
/// The [OutputStreamHandle], which also can control some
/// playback, is for now unused and is here just to keep it
/// alive so the playback can function properly.
_handle: OutputStreamHandle,
/// The [OutputStream], which is just here to keep the playback
/// alive and functioning.
_stream: OutputStream,
}
/// SAFETY: This is necessary because [OutputStream] does not implement [Send],
/// SAFETY: even though it is perfectly possible.
unsafe impl Send for Player {}
/// SAFETY: See implementation for [Send].
unsafe impl Sync for Player {}
impl Player {
@ -55,6 +85,7 @@ impl Player {
})
}
/// Just a shorthand for setting `current`.
async fn set_current(&self, info: TrackInfo) -> eyre::Result<()> {
self.current.store(Some(Arc::new(info)));
@ -101,6 +132,7 @@ impl Player {
}
});
// Start buffering tracks immediately.
itx.send(()).await?;
loop {

View File

@ -23,18 +23,19 @@ fn format_duration(duration: &Duration) -> String {
format!("{:02}:{:02}", minutes, seconds)
}
enum Action {
/// This represents the main "action" bars state.
enum ActionBar {
Paused(TrackInfo),
Playing(TrackInfo),
Loading,
}
impl Action {
impl ActionBar {
fn format(&self) -> (String, usize) {
let (word, subject) = match self {
Action::Playing(x) => ("playing", Some(x.name.clone())),
Action::Paused(x) => ("paused", Some(x.name.clone())),
Action::Loading => ("loading", None),
ActionBar::Playing(x) => ("playing", Some(x.name.clone())),
ActionBar::Paused(x) => ("paused", Some(x.name.clone())),
ActionBar::Loading => ("loading", None),
};
if let Some(subject) = subject {
@ -43,11 +44,12 @@ impl Action {
word.len() + 1 + subject.len(),
)
} else {
(format!("{}", word), word.len())
(word.to_string(), word.len())
}
}
}
/// The code for the interface itself.
async fn interface(queue: Arc<Player>) -> eyre::Result<()> {
const WIDTH: usize = 27;
const PROGRESS_WIDTH: usize = WIDTH - 16;
@ -58,12 +60,12 @@ async fn interface(queue: Arc<Player>) -> eyre::Result<()> {
let name = (*x.clone()).clone();
if queue.sink.is_paused() {
Action::Paused(name)
ActionBar::Paused(name)
} else {
Action::Playing(name)
ActionBar::Playing(name)
}
}
None => Action::Loading,
None => ActionBar::Loading,
}
.format();
@ -88,7 +90,7 @@ async fn interface(queue: Arc<Player>) -> eyre::Result<()> {
let progress = format!(
" [{}{}] {}/{} ",
"/".repeat(filled as usize),
"/".repeat(filled),
" ".repeat(PROGRESS_WIDTH.saturating_sub(filled)),
format_duration(&elapsed),
format_duration(&duration),
@ -118,6 +120,7 @@ async fn interface(queue: Arc<Player>) -> eyre::Result<()> {
}
}
/// Initializes the UI, this will also start taking input from the user.
pub async fn start(queue: Arc<Player>, sender: Sender<Messages>) -> eyre::Result<()> {
crossterm::terminal::enable_raw_mode()?;
crossterm::execute!(stderr(), Hide)?;

View File

@ -1,9 +1,11 @@
//! Has all of the functions for the `scrape` command.
use std::sync::LazyLock;
use futures::{stream::FuturesUnordered, StreamExt};
use scraper::{Html, Selector};
const BASE_URL: &'static str = "https://lofigirl.com/wp-content/uploads/";
const BASE_URL: &str = "https://lofigirl.com/wp-content/uploads/";
static SELECTOR: LazyLock<Selector> =
LazyLock::new(|| Selector::parse("html > body > pre > a").unwrap());
@ -48,7 +50,7 @@ async fn scan(extention: &str, include_full: bool) -> eyre::Result<Vec<String>>
let path = format!("{}/{}", year, month);
let items = parse(&path).await.unwrap();
let items = items
items
.into_iter()
.filter_map(|x| {
if x.ends_with(extention) {
@ -61,9 +63,7 @@ async fn scan(extention: &str, include_full: bool) -> eyre::Result<Vec<String>>
None
}
})
.collect::<Vec<String>>();
items
.collect::<Vec<String>>()
});
}
}

View File

@ -1,3 +1,7 @@
//! Has all of the structs for managing the state
//! of tracks, as well as downloading them &
//! finding new ones.
use std::{io::Cursor, time::Duration};
use bytes::Bytes;
@ -70,7 +74,10 @@ impl TrackInfo {
/// This struct is seperate from [Track] since it is generated lazily from
/// a track, and not when the track is first downloaded.
pub struct DecodedTrack {
/// Has both the formatted name and some information from the decoded data.
pub info: TrackInfo,
/// The decoded data, which is able to be played by [rodio].
pub data: DecodedData,
}
@ -85,7 +92,11 @@ impl DecodedTrack {
/// The main track struct, which only includes data & the track name.
pub struct Track {
/// This name is not formatted, and also includes the month & year of the track.
pub name: &'static str,
/// The raw data of the track, which is not decoded and
/// therefore much more memory efficient.
pub data: Bytes,
}
@ -93,7 +104,7 @@ impl Track {
/// Fetches and downloads a random track from the tracklist.
pub async fn random(client: &Client) -> eyre::Result<Self> {
let name = random().await?;
let data = download(&name, client).await?;
let data = download(name, client).await?;
Ok(Self { data, name })
}