mirror of
https://github.com/talwat/lowfi
synced 2024-12-26 11:11:54 +00:00
docs: added lots of new internal documentation
This commit is contained in:
parent
93a668bae0
commit
8110b8418a
@ -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;
|
||||
|
@ -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);
|
||||
|
||||
|
@ -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 {
|
||||
|
@ -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)?;
|
||||
|
@ -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>>()
|
||||
});
|
||||
}
|
||||
}
|
||||
|
@ -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 })
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user