mirror of
https://github.com/talwat/lowfi
synced 2025-02-06 15:51: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};
|
use clap::{Parser, Subcommand};
|
||||||
|
|
||||||
|
mod player;
|
||||||
mod scrape;
|
mod scrape;
|
||||||
mod tracks;
|
mod tracks;
|
||||||
mod player;
|
|
||||||
|
|
||||||
/// An extremely simple lofi player.
|
/// An extremely simple lofi player.
|
||||||
#[derive(Parser)]
|
#[derive(Parser)]
|
||||||
@ -17,7 +17,7 @@ enum Commands {
|
|||||||
/// Scrapes the lofi girl website file server for mp3 files.
|
/// Scrapes the lofi girl website file server for mp3 files.
|
||||||
Scrape,
|
Scrape,
|
||||||
/// Starts the player.
|
/// Starts the player.
|
||||||
Play
|
Play,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[tokio::main]
|
#[tokio::main]
|
||||||
@ -26,6 +26,6 @@ async fn main() -> eyre::Result<()> {
|
|||||||
|
|
||||||
match cli.command {
|
match cli.command {
|
||||||
Commands::Scrape => scrape::scrape().await,
|
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 reqwest::Client;
|
||||||
use rodio::{Decoder, OutputStream, Sink};
|
use rodio::{Decoder, OutputStream, Sink};
|
||||||
use tokio::{
|
use tokio::{
|
||||||
select, sync::{
|
select,
|
||||||
|
sync::{
|
||||||
mpsc::{self, Receiver},
|
mpsc::{self, Receiver},
|
||||||
RwLock,
|
RwLock,
|
||||||
}, task
|
},
|
||||||
|
task,
|
||||||
};
|
};
|
||||||
|
|
||||||
/// The amount of songs to buffer up.
|
/// The amount of songs to buffer up.
|
||||||
@ -17,6 +23,7 @@ use crate::tracks::Track;
|
|||||||
/// Handles communication between the frontend & audio player.
|
/// Handles communication between the frontend & audio player.
|
||||||
pub enum Messages {
|
pub enum Messages {
|
||||||
Skip,
|
Skip,
|
||||||
|
Die,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Main struct responsible for queuing up tracks.
|
/// Main struct responsible for queuing up tracks.
|
||||||
@ -69,7 +76,7 @@ impl Queue {
|
|||||||
self,
|
self,
|
||||||
sink: Sink,
|
sink: Sink,
|
||||||
client: Client,
|
client: Client,
|
||||||
mut rx: Receiver<Messages>
|
mut rx: Receiver<Messages>,
|
||||||
) -> eyre::Result<()> {
|
) -> eyre::Result<()> {
|
||||||
let sink = Arc::new(sink);
|
let sink = Arc::new(sink);
|
||||||
|
|
||||||
@ -89,11 +96,21 @@ impl Queue {
|
|||||||
let track = self.next(&client).await?;
|
let track = self.next(&client).await?;
|
||||||
sink.append(Decoder::new(track.data)?);
|
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<()> {
|
pub async fn play() -> eyre::Result<()> {
|
||||||
let queue = Queue::new().await;
|
let queue = Queue::new().await;
|
||||||
let (tx, rx) = mpsc::channel(8);
|
let (tx, rx) = mpsc::channel(8);
|
||||||
@ -106,11 +123,15 @@ pub async fn play() -> eyre::Result<()> {
|
|||||||
|
|
||||||
crossterm::terminal::enable_raw_mode()?;
|
crossterm::terminal::enable_raw_mode()?;
|
||||||
|
|
||||||
|
gui().await?;
|
||||||
|
|
||||||
'a: loop {
|
'a: loop {
|
||||||
match crossterm::event::read()? {
|
match crossterm::event::read()? {
|
||||||
crossterm::event::Event::Key(event) => match event.code {
|
crossterm::event::Event::Key(event) => match event.code {
|
||||||
crossterm::event::KeyCode::Char(x) => {
|
crossterm::event::KeyCode::Char(x) => {
|
||||||
if x == 'q' {
|
if x == 'q' {
|
||||||
|
tx.send(Messages::Die).await?;
|
||||||
|
|
||||||
break 'a;
|
break 'a;
|
||||||
} else if x == 's' {
|
} else if x == 's' {
|
||||||
tx.send(Messages::Skip).await?;
|
tx.send(Messages::Skip).await?;
|
||||||
|
@ -3,16 +3,20 @@ use std::sync::LazyLock;
|
|||||||
use futures::{stream::FuturesUnordered, StreamExt};
|
use futures::{stream::FuturesUnordered, StreamExt};
|
||||||
use scraper::{Html, Selector};
|
use scraper::{Html, Selector};
|
||||||
|
|
||||||
static SELECTOR: LazyLock<Selector> = LazyLock::new(|| {
|
static SELECTOR: LazyLock<Selector> =
|
||||||
Selector::parse("html > body > pre > a").unwrap()
|
LazyLock::new(|| Selector::parse("html > body > pre > a").unwrap());
|
||||||
});
|
|
||||||
|
|
||||||
async fn parse(path: &str) -> eyre::Result<Vec<String>> {
|
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 document = response.text().await?;
|
||||||
|
|
||||||
let html = Html::parse_document(&document);
|
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.
|
/// This function basically just scans the entire file server, and returns a list of paths to mp3 files.
|
||||||
@ -22,10 +26,13 @@ async fn parse(path: &str) -> eyre::Result<Vec<String>> {
|
|||||||
async fn scan() -> eyre::Result<Vec<String>> {
|
async fn scan() -> eyre::Result<Vec<String>> {
|
||||||
let items = parse("").await?;
|
let items = parse("").await?;
|
||||||
|
|
||||||
let years: Vec<u32> = items.iter().filter_map(|x| {
|
let years: Vec<u32> = items
|
||||||
let year = x.strip_suffix("/")?;
|
.iter()
|
||||||
year.parse().ok()
|
.filter_map(|x| {
|
||||||
}).collect();
|
let year = x.strip_suffix("/")?;
|
||||||
|
year.parse().ok()
|
||||||
|
})
|
||||||
|
.collect();
|
||||||
|
|
||||||
// A little bit of async to run all of the months concurrently.
|
// A little bit of async to run all of the months concurrently.
|
||||||
let mut futures = FuturesUnordered::new();
|
let mut futures = FuturesUnordered::new();
|
||||||
@ -38,13 +45,16 @@ async fn scan() -> eyre::Result<Vec<String>> {
|
|||||||
let path = format!("{}/{}", year, month);
|
let path = format!("{}/{}", year, month);
|
||||||
|
|
||||||
let items = parse(&path).await.unwrap();
|
let items = parse(&path).await.unwrap();
|
||||||
let items = items.into_iter().filter_map(|x| {
|
let items = items
|
||||||
if x.ends_with(".mp3") {
|
.into_iter()
|
||||||
Some(format!("{path}{x}"))
|
.filter_map(|x| {
|
||||||
} else {
|
if x.ends_with(".mp3") {
|
||||||
None
|
Some(format!("{path}{x}"))
|
||||||
}
|
} else {
|
||||||
}).collect::<Vec<String>>();
|
None
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.collect::<Vec<String>>();
|
||||||
|
|
||||||
items
|
items
|
||||||
});
|
});
|
||||||
|
Loading…
x
Reference in New Issue
Block a user