feat: extend scrape command options

This commit is contained in:
talwat 2024-09-25 23:54:55 +02:00
parent 2102564d04
commit 233b552343
4 changed files with 32 additions and 11 deletions

View File

@ -15,8 +15,16 @@ struct Args {
#[derive(Subcommand)] #[derive(Subcommand)]
enum Commands { enum Commands {
/// Scrapes the lofi girl website file server for mp3 files. /// Scrapes the lofi girl website file server for files.
Scrape, Scrape {
/// The file extention to search for, defaults to mp3.
#[clap(long, short, default_value = "mp3")]
extention: String,
/// Whether to include the full HTTP URL or just the distinguishing part.
#[clap(long, short)]
include_full: bool,
},
/// Starts the player. /// Starts the player.
Play, Play,
} }
@ -26,7 +34,10 @@ async fn main() -> eyre::Result<()> {
let cli = Args::parse(); let cli = Args::parse();
match cli.command { match cli.command {
Commands::Scrape => scrape::scrape().await, Commands::Scrape {
extention,
include_full,
} => scrape::scrape(extention, include_full).await,
Commands::Play => play::play().await, Commands::Play => play::play().await,
} }
} }

View File

@ -2,7 +2,7 @@ use std::{collections::VecDeque, sync::Arc};
use arc_swap::ArcSwapOption; use arc_swap::ArcSwapOption;
use reqwest::Client; use reqwest::Client;
use rodio::{Decoder, OutputStream, OutputStreamHandle, Sink}; use rodio::{OutputStream, OutputStreamHandle, Sink};
use tokio::{ use tokio::{
select, select,
sync::{ sync::{

View File

@ -3,12 +3,13 @@ use std::sync::LazyLock;
use futures::{stream::FuturesUnordered, StreamExt}; use futures::{stream::FuturesUnordered, StreamExt};
use scraper::{Html, Selector}; use scraper::{Html, Selector};
const BASE_URL: &'static str = "https://lofigirl.com/wp-content/uploads/";
static SELECTOR: LazyLock<Selector> = static SELECTOR: LazyLock<Selector> =
LazyLock::new(|| 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 = let response = reqwest::get(format!("{}{}", BASE_URL, path)).await?;
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);
@ -23,7 +24,9 @@ async fn parse(path: &str) -> eyre::Result<Vec<String>> {
/// ///
/// It's a bit hacky, and basically works by checking all of the years, then months, and then all of the 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. /// This is done as a way to avoid recursion, since async rust really hates recursive functions.
async fn scan() -> eyre::Result<Vec<String>> { async fn scan(extention: &str, include_full: bool) -> eyre::Result<Vec<String>> {
let extention = &format!(".{}", extention);
let items = parse("").await?; let items = parse("").await?;
let years: Vec<u32> = items let years: Vec<u32> = items
@ -48,8 +51,12 @@ async fn scan() -> eyre::Result<Vec<String>> {
let items = items let items = items
.into_iter() .into_iter()
.filter_map(|x| { .filter_map(|x| {
if x.ends_with(".mp3") { if x.ends_with(extention) {
Some(format!("{path}{x}")) if include_full {
Some(format!("{BASE_URL}{path}{x}"))
} else {
Some(format!("{path}{x}"))
}
} else { } else {
None None
} }
@ -69,8 +76,8 @@ async fn scan() -> eyre::Result<Vec<String>> {
eyre::Result::Ok(files) eyre::Result::Ok(files)
} }
pub async fn scrape() -> eyre::Result<()> { pub async fn scrape(extention: String, include_full: bool) -> eyre::Result<()> {
let files = scan().await?; let files = scan(&extention, include_full).await?;
for file in files { for file in files {
println!("{}", file); println!("{}", file);
} }

View File

@ -38,12 +38,15 @@ impl TrackInfo {
} }
} }
/// The main track struct, which includes the actual decoded file
/// as well as some basic information about it.
pub struct Track { pub struct Track {
pub info: TrackInfo, pub info: TrackInfo,
pub data: Data, pub data: Data,
} }
impl Track { impl Track {
/// Fetches, downloads, and decodes a random track from the tracklist.
pub async fn random(client: &Client) -> eyre::Result<Self> { pub async fn random(client: &Client) -> eyre::Result<Self> {
let name = random().await?; let name = random().await?;
let data = download(&name, client).await?; let data = download(&name, client).await?;