mirror of
				https://github.com/talwat/lowfi
				synced 2025-10-30 18:58:45 +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…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user