mirror of
https://github.com/talwat/lowfi
synced 2025-01-28 19:31:27 +00:00
feat: start work on frontend
This commit is contained in:
parent
e0d13792e2
commit
681889a268
@ -1,8 +1,8 @@
|
||||
use clap::{Parser, Subcommand};
|
||||
|
||||
mod player;
|
||||
mod scrape;
|
||||
mod tracks;
|
||||
mod player;
|
||||
|
||||
/// An extremely simple lofi player.
|
||||
#[derive(Parser)]
|
||||
@ -17,7 +17,7 @@ enum Commands {
|
||||
/// Scrapes the lofi girl website file server for mp3 files.
|
||||
Scrape,
|
||||
/// Starts the player.
|
||||
Play
|
||||
Play,
|
||||
}
|
||||
|
||||
#[tokio::main]
|
||||
@ -26,6 +26,6 @@ async fn main() -> eyre::Result<()> {
|
||||
|
||||
match cli.command {
|
||||
Commands::Scrape => scrape::scrape().await,
|
||||
Commands::Play => player::play().await
|
||||
Commands::Play => player::play().await,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,12 +1,18 @@
|
||||
use std::{collections::VecDeque, sync::Arc};
|
||||
use std::{collections::VecDeque, io::stderr, sync::Arc};
|
||||
|
||||
use crossterm::{
|
||||
cursor::{MoveDown, MoveToColumn, MoveToNextLine},
|
||||
style::Print,
|
||||
};
|
||||
use reqwest::Client;
|
||||
use rodio::{Decoder, OutputStream, Sink};
|
||||
use tokio::{
|
||||
select, sync::{
|
||||
select,
|
||||
sync::{
|
||||
mpsc::{self, Receiver},
|
||||
RwLock,
|
||||
}, task
|
||||
},
|
||||
task,
|
||||
};
|
||||
|
||||
/// The amount of songs to buffer up.
|
||||
@ -17,6 +23,7 @@ use crate::tracks::Track;
|
||||
/// Handles communication between the frontend & audio player.
|
||||
pub enum Messages {
|
||||
Skip,
|
||||
Die,
|
||||
}
|
||||
|
||||
/// Main struct responsible for queuing up tracks.
|
||||
@ -62,14 +69,14 @@ impl Queue {
|
||||
}
|
||||
|
||||
/// This is the main "audio server".
|
||||
///
|
||||
///
|
||||
/// `rx` is used to communicate with it, for example when to
|
||||
/// skip tracks or pause.
|
||||
pub async fn play(
|
||||
self,
|
||||
sink: Sink,
|
||||
client: Client,
|
||||
mut rx: Receiver<Messages>
|
||||
mut rx: Receiver<Messages>,
|
||||
) -> eyre::Result<()> {
|
||||
let sink = Arc::new(sink);
|
||||
|
||||
@ -89,11 +96,21 @@ impl Queue {
|
||||
let track = self.next(&client).await?;
|
||||
sink.append(Decoder::new(track.data)?);
|
||||
}
|
||||
Messages::Die => break,
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn gui() -> eyre::Result<()> {
|
||||
crossterm::execute!(stderr(), MoveToColumn(0), Print("hello!\r\n"))?;
|
||||
crossterm::execute!(stderr(), Print("next line!\r\n"))?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub async fn play() -> eyre::Result<()> {
|
||||
let queue = Queue::new().await;
|
||||
let (tx, rx) = mpsc::channel(8);
|
||||
@ -106,11 +123,15 @@ pub async fn play() -> eyre::Result<()> {
|
||||
|
||||
crossterm::terminal::enable_raw_mode()?;
|
||||
|
||||
gui().await?;
|
||||
|
||||
'a: loop {
|
||||
match crossterm::event::read()? {
|
||||
crossterm::event::Event::Key(event) => match event.code {
|
||||
crossterm::event::KeyCode::Char(x) => {
|
||||
if x == 'q' {
|
||||
tx.send(Messages::Die).await?;
|
||||
|
||||
break 'a;
|
||||
} else if x == 's' {
|
||||
tx.send(Messages::Skip).await?;
|
||||
|
@ -3,48 +3,58 @@ use std::sync::LazyLock;
|
||||
use futures::{stream::FuturesUnordered, StreamExt};
|
||||
use scraper::{Html, Selector};
|
||||
|
||||
static SELECTOR: LazyLock<Selector> = LazyLock::new(|| {
|
||||
Selector::parse("html > body > pre > a").unwrap()
|
||||
});
|
||||
static SELECTOR: LazyLock<Selector> =
|
||||
LazyLock::new(|| Selector::parse("html > body > pre > a").unwrap());
|
||||
|
||||
async fn parse(path: &str) -> eyre::Result<Vec<String>> {
|
||||
let response = reqwest::get(format!("https://lofigirl.com/wp-content/uploads/{}", path)).await?;
|
||||
let response =
|
||||
reqwest::get(format!("https://lofigirl.com/wp-content/uploads/{}", path)).await?;
|
||||
let document = response.text().await?;
|
||||
|
||||
let html = Html::parse_document(&document);
|
||||
Ok(html.select(&SELECTOR).skip(5).map(|x| String::from(x.attr("href").unwrap())).collect())
|
||||
Ok(html
|
||||
.select(&SELECTOR)
|
||||
.skip(5)
|
||||
.map(|x| String::from(x.attr("href").unwrap()))
|
||||
.collect())
|
||||
}
|
||||
|
||||
/// This function basically just scans the entire file server, and returns a list of paths to mp3 files.
|
||||
///
|
||||
///
|
||||
/// It's a bit hacky, and basically works by checking all of the years, then months, and then all of the files.
|
||||
/// This is done as a way to avoid recursion, since async rust really hates recursive functions.
|
||||
async fn scan() -> eyre::Result<Vec<String>> {
|
||||
let items = parse("").await?;
|
||||
|
||||
let years: Vec<u32> = items.iter().filter_map(|x| {
|
||||
let year = x.strip_suffix("/")?;
|
||||
year.parse().ok()
|
||||
}).collect();
|
||||
|
||||
let years: Vec<u32> = items
|
||||
.iter()
|
||||
.filter_map(|x| {
|
||||
let year = x.strip_suffix("/")?;
|
||||
year.parse().ok()
|
||||
})
|
||||
.collect();
|
||||
|
||||
// A little bit of async to run all of the months concurrently.
|
||||
let mut futures = FuturesUnordered::new();
|
||||
|
||||
|
||||
for year in years {
|
||||
let months = parse(&year.to_string()).await?;
|
||||
|
||||
|
||||
for month in months {
|
||||
futures.push(async move {
|
||||
let path = format!("{}/{}", year, month);
|
||||
|
||||
let items = parse(&path).await.unwrap();
|
||||
let items = items.into_iter().filter_map(|x| {
|
||||
if x.ends_with(".mp3") {
|
||||
Some(format!("{path}{x}"))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}).collect::<Vec<String>>();
|
||||
let items = items
|
||||
.into_iter()
|
||||
.filter_map(|x| {
|
||||
if x.ends_with(".mp3") {
|
||||
Some(format!("{path}{x}"))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
})
|
||||
.collect::<Vec<String>>();
|
||||
|
||||
items
|
||||
});
|
||||
|
@ -10,7 +10,7 @@ async fn download(track: &str, client: &Client) -> eyre::Result<Data> {
|
||||
let url = format!("https://lofigirl.com/wp-content/uploads/{}", track);
|
||||
let response = client.get(url).send().await?;
|
||||
let file = Cursor::new(response.bytes().await?);
|
||||
|
||||
|
||||
Ok(file)
|
||||
}
|
||||
|
||||
@ -37,4 +37,4 @@ impl Track {
|
||||
|
||||
Ok(Self { name, data })
|
||||
}
|
||||
}
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user