mirror of
https://github.com/talwat/lowfi
synced 2025-03-13 00:22:22 +00:00
fix: tackle lots of clippy lints, along with removing outputstream from player struct
This commit is contained in:
parent
a720e9d2cf
commit
ece88de1ae
2
Cargo.lock
generated
2
Cargo.lock
generated
@ -1453,7 +1453,7 @@ checksum = "a7a70ba024b9dc04c27ea2f0c0548feb474ec5c54bba33a7f72f873a39d07b24"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "lowfi"
|
name = "lowfi"
|
||||||
version = "1.6.0"
|
version = "1.6.1"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"Inflector",
|
"Inflector",
|
||||||
"arc-swap",
|
"arc-swap",
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "lowfi"
|
name = "lowfi"
|
||||||
version = "1.6.0"
|
version = "1.6.1"
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
description = "An extremely simple lofi player."
|
description = "An extremely simple lofi player."
|
||||||
license = "MIT"
|
license = "MIT"
|
||||||
|
44
src/main.rs
44
src/main.rs
@ -1,44 +1,6 @@
|
|||||||
//! An extremely simple lofi player.
|
//! An extremely simple lofi player.
|
||||||
|
|
||||||
#![warn(clippy::all, clippy::restriction, clippy::pedantic, clippy::nursery)]
|
#![warn(clippy::all, clippy::pedantic, clippy::nursery)]
|
||||||
#![allow(
|
|
||||||
clippy::single_call_fn,
|
|
||||||
clippy::struct_excessive_bools,
|
|
||||||
clippy::implicit_return,
|
|
||||||
clippy::question_mark_used,
|
|
||||||
clippy::shadow_reuse,
|
|
||||||
clippy::indexing_slicing,
|
|
||||||
clippy::arithmetic_side_effects,
|
|
||||||
clippy::std_instead_of_core,
|
|
||||||
clippy::print_stdout,
|
|
||||||
clippy::float_arithmetic,
|
|
||||||
clippy::integer_division_remainder_used,
|
|
||||||
clippy::used_underscore_binding,
|
|
||||||
clippy::print_stderr,
|
|
||||||
clippy::semicolon_outside_block,
|
|
||||||
clippy::non_send_fields_in_send_ty,
|
|
||||||
clippy::non_ascii_literal,
|
|
||||||
clippy::let_underscore_untyped,
|
|
||||||
clippy::let_underscore_must_use,
|
|
||||||
clippy::shadow_unrelated,
|
|
||||||
clippy::std_instead_of_alloc,
|
|
||||||
clippy::partial_pub_fields,
|
|
||||||
clippy::unseparated_literal_suffix,
|
|
||||||
clippy::self_named_module_files,
|
|
||||||
// TODO: Disallow these lints later.
|
|
||||||
clippy::unwrap_used,
|
|
||||||
clippy::pattern_type_mismatch,
|
|
||||||
clippy::tuple_array_conversions,
|
|
||||||
clippy::as_conversions,
|
|
||||||
clippy::cast_possible_truncation,
|
|
||||||
clippy::cast_precision_loss,
|
|
||||||
clippy::wildcard_enum_match_arm,
|
|
||||||
clippy::integer_division,
|
|
||||||
clippy::cast_sign_loss,
|
|
||||||
clippy::cast_lossless,
|
|
||||||
clippy::arbitrary_source_item_ordering,
|
|
||||||
clippy::unused_trait_names
|
|
||||||
)]
|
|
||||||
|
|
||||||
use clap::{Parser, Subcommand};
|
use clap::{Parser, Subcommand};
|
||||||
|
|
||||||
@ -52,6 +14,10 @@ mod scrape;
|
|||||||
/// An extremely simple lofi player.
|
/// An extremely simple lofi player.
|
||||||
#[derive(Parser)]
|
#[derive(Parser)]
|
||||||
#[command(about, version)]
|
#[command(about, version)]
|
||||||
|
#[allow(
|
||||||
|
clippy::struct_excessive_bools,
|
||||||
|
reason = "señor clippy, i assure you this is not a state machine"
|
||||||
|
)]
|
||||||
struct Args {
|
struct Args {
|
||||||
/// Whether to use an alternate terminal screen.
|
/// Whether to use an alternate terminal screen.
|
||||||
#[clap(long, short)]
|
#[clap(long, short)]
|
||||||
|
30
src/play.rs
30
src/play.rs
@ -23,7 +23,7 @@ impl PersistentVolume {
|
|||||||
/// Retrieves the config directory.
|
/// Retrieves the config directory.
|
||||||
async fn config() -> eyre::Result<PathBuf> {
|
async fn config() -> eyre::Result<PathBuf> {
|
||||||
let config = dirs::config_dir()
|
let config = dirs::config_dir()
|
||||||
.ok_or(eyre!("Couldn't find config directory"))?
|
.ok_or_else(|| eyre!("Couldn't find config directory"))?
|
||||||
.join(PathBuf::from("lowfi"));
|
.join(PathBuf::from("lowfi"));
|
||||||
|
|
||||||
if !config.exists() {
|
if !config.exists() {
|
||||||
@ -35,7 +35,7 @@ impl PersistentVolume {
|
|||||||
|
|
||||||
/// Returns the volume as a float from 0 to 1.
|
/// Returns the volume as a float from 0 to 1.
|
||||||
pub fn float(self) -> f32 {
|
pub fn float(self) -> f32 {
|
||||||
self.inner as f32 / 100.0
|
f32::from(self.inner) / 100.0
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Loads the [`PersistentVolume`] from [`dirs::config_dir()`].
|
/// Loads the [`PersistentVolume`] from [`dirs::config_dir()`].
|
||||||
@ -64,17 +64,38 @@ impl PersistentVolume {
|
|||||||
let config = Self::config().await?;
|
let config = Self::config().await?;
|
||||||
let path = config.join(PathBuf::from("volume.txt"));
|
let path = config.join(PathBuf::from("volume.txt"));
|
||||||
|
|
||||||
fs::write(path, ((volume * 100.0).abs().round() as u16).to_string()).await?;
|
#[expect(
|
||||||
|
clippy::as_conversions,
|
||||||
|
clippy::cast_sign_loss,
|
||||||
|
clippy::cast_possible_truncation,
|
||||||
|
reason = "already rounded & absolute, therefore this should be safe"
|
||||||
|
)]
|
||||||
|
let percentage = (volume * 100.0).abs().round() as u16;
|
||||||
|
|
||||||
|
fs::write(path, percentage.to_string()).await?;
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Wrapper around [`rodio::OutputStream`] to implement [Send], currently unsafely.
|
||||||
|
///
|
||||||
|
/// This is more of a temporary solution until cpal implements [Send] on it's output stream.
|
||||||
|
pub struct SendableOutputStream(pub rodio::OutputStream);
|
||||||
|
|
||||||
|
// SAFETY: This is necessary because [OutputStream] does not implement [Send],
|
||||||
|
// due to some limitation with Android's Audio API.
|
||||||
|
// I'm pretty sure nobody will use lowfi with android, so this is safe.
|
||||||
|
#[expect(clippy::non_send_fields_in_send_ty, reason = "yes")]
|
||||||
|
unsafe impl Send for SendableOutputStream {}
|
||||||
|
|
||||||
/// Initializes the audio server, and then safely stops
|
/// Initializes the audio server, and then safely stops
|
||||||
/// it when the frontend quits.
|
/// it when the frontend quits.
|
||||||
pub async fn play(args: Args) -> eyre::Result<()> {
|
pub async fn play(args: Args) -> eyre::Result<()> {
|
||||||
// Actually initializes the player.
|
// Actually initializes the player.
|
||||||
let player = Arc::new(Player::new(&args).await?);
|
// Stream kept here in the master thread to keep it alive.
|
||||||
|
let (player, stream) = Player::new(&args).await?;
|
||||||
|
let player = Arc::new(player);
|
||||||
|
|
||||||
// Initialize the UI, as well as the internal communication channel.
|
// Initialize the UI, as well as the internal communication channel.
|
||||||
let (tx, rx) = mpsc::channel(8);
|
let (tx, rx) = mpsc::channel(8);
|
||||||
@ -90,6 +111,7 @@ pub async fn play(args: Args) -> eyre::Result<()> {
|
|||||||
PersistentVolume::save(player.sink.volume()).await?;
|
PersistentVolume::save(player.sink.volume()).await?;
|
||||||
player.sink.stop();
|
player.sink.stop();
|
||||||
ui.abort();
|
ui.abort();
|
||||||
|
drop(stream.0);
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
@ -2,11 +2,10 @@
|
|||||||
//! This also has the code for the underlying
|
//! This also has the code for the underlying
|
||||||
//! audio server which adds new tracks.
|
//! audio server which adds new tracks.
|
||||||
|
|
||||||
use std::{collections::VecDeque, ffi::CString, sync::Arc, time::Duration};
|
use std::{collections::VecDeque, sync::Arc, time::Duration};
|
||||||
|
|
||||||
use arc_swap::ArcSwapOption;
|
use arc_swap::ArcSwapOption;
|
||||||
use downloader::Downloader;
|
use downloader::Downloader;
|
||||||
use libc::freopen;
|
|
||||||
use reqwest::Client;
|
use reqwest::Client;
|
||||||
use rodio::{OutputStream, OutputStreamHandle, Sink};
|
use rodio::{OutputStream, OutputStreamHandle, Sink};
|
||||||
use tokio::{
|
use tokio::{
|
||||||
@ -23,7 +22,7 @@ use tokio::{
|
|||||||
use mpris_server::{PlaybackStatus, PlayerInterface, Property};
|
use mpris_server::{PlaybackStatus, PlayerInterface, Property};
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
play::PersistentVolume,
|
play::{PersistentVolume, SendableOutputStream},
|
||||||
tracks::{self, list::List},
|
tracks::{self, list::List},
|
||||||
Args,
|
Args,
|
||||||
};
|
};
|
||||||
@ -53,6 +52,7 @@ pub enum Messages {
|
|||||||
Init,
|
Init,
|
||||||
|
|
||||||
/// Unpause the [Sink].
|
/// Unpause the [Sink].
|
||||||
|
#[allow(dead_code, reason = "this code may not be dead depending on features")]
|
||||||
Play,
|
Play,
|
||||||
|
|
||||||
/// Pauses the [Sink].
|
/// Pauses the [Sink].
|
||||||
@ -109,20 +109,8 @@ pub struct Player {
|
|||||||
/// playback, is for now unused and is here just to keep it
|
/// playback, is for now unused and is here just to keep it
|
||||||
/// alive so the playback can function properly.
|
/// alive so the playback can function properly.
|
||||||
_handle: OutputStreamHandle,
|
_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],
|
|
||||||
// due to some limitation with Android's Audio API.
|
|
||||||
// I'm pretty sure nobody will use lowfi with android, so this is safe.
|
|
||||||
unsafe impl Send for Player {}
|
|
||||||
|
|
||||||
// SAFETY: See implementation for [Send].
|
|
||||||
unsafe impl Sync for Player {}
|
|
||||||
|
|
||||||
impl Player {
|
impl Player {
|
||||||
/// This gets the output stream while also shutting up alsa with [libc].
|
/// This gets the output stream while also shutting up alsa with [libc].
|
||||||
/// Uses raw libc calls, and therefore is functional only on Linux.
|
/// Uses raw libc calls, and therefore is functional only on Linux.
|
||||||
@ -182,7 +170,7 @@ impl Player {
|
|||||||
/// Initializes the entire player, including audio devices & sink.
|
/// Initializes the entire player, including audio devices & sink.
|
||||||
///
|
///
|
||||||
/// This also will load the track list & persistent volume.
|
/// This also will load the track list & persistent volume.
|
||||||
pub async fn new(args: &Args) -> eyre::Result<Self> {
|
pub async fn new(args: &Args) -> eyre::Result<(Self, SendableOutputStream)> {
|
||||||
// Load the volume file.
|
// Load the volume file.
|
||||||
let volume = PersistentVolume::load().await?;
|
let volume = PersistentVolume::load().await?;
|
||||||
|
|
||||||
@ -199,7 +187,7 @@ impl Player {
|
|||||||
|
|
||||||
// If we're not on Linux, then there's no problem.
|
// If we're not on Linux, then there's no problem.
|
||||||
#[cfg(not(target_os = "linux"))]
|
#[cfg(not(target_os = "linux"))]
|
||||||
let (_stream, handle) = OutputStream::try_default()?;
|
let (stream, handle) = OutputStream::try_default()?;
|
||||||
|
|
||||||
let sink = Sink::try_new(&handle)?;
|
let sink = Sink::try_new(&handle)?;
|
||||||
if args.paused {
|
if args.paused {
|
||||||
@ -223,10 +211,9 @@ impl Player {
|
|||||||
volume,
|
volume,
|
||||||
list,
|
list,
|
||||||
_handle: handle,
|
_handle: handle,
|
||||||
_stream,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
Ok(player)
|
Ok((player, SendableOutputStream(stream)))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// This will play the next track, as well as refilling the buffer in the background.
|
/// This will play the next track, as well as refilling the buffer in the background.
|
||||||
|
@ -1,7 +1,15 @@
|
|||||||
//! The module which manages all user interface, including inputs.
|
//! The module which manages all user interface, including inputs.
|
||||||
|
|
||||||
|
#![allow(
|
||||||
|
clippy::as_conversions,
|
||||||
|
clippy::cast_sign_loss,
|
||||||
|
clippy::cast_precision_loss,
|
||||||
|
clippy::cast_possible_truncation,
|
||||||
|
reason = "the ui is full of these because of various layout & positioning aspects, and for a simple music player making all casts safe is not worth the effort"
|
||||||
|
)]
|
||||||
|
|
||||||
use std::{
|
use std::{
|
||||||
fmt::Write,
|
fmt::Write as _,
|
||||||
io::{stdout, Stdout},
|
io::{stdout, Stdout},
|
||||||
sync::{
|
sync::{
|
||||||
atomic::{AtomicUsize, Ordering},
|
atomic::{AtomicUsize, Ordering},
|
||||||
@ -15,7 +23,7 @@ use crate::Args;
|
|||||||
use crossterm::{
|
use crossterm::{
|
||||||
cursor::{Hide, MoveTo, MoveToColumn, MoveUp, Show},
|
cursor::{Hide, MoveTo, MoveToColumn, MoveUp, Show},
|
||||||
event::{KeyboardEnhancementFlags, PopKeyboardEnhancementFlags, PushKeyboardEnhancementFlags},
|
event::{KeyboardEnhancementFlags, PopKeyboardEnhancementFlags, PushKeyboardEnhancementFlags},
|
||||||
style::{Print, Stylize},
|
style::{Print, Stylize as _},
|
||||||
terminal::{self, Clear, ClearType, EnterAlternateScreen, LeaveAlternateScreen},
|
terminal::{self, Clear, ClearType, EnterAlternateScreen, LeaveAlternateScreen},
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -50,7 +58,7 @@ lazy_static! {
|
|||||||
|
|
||||||
/// Sets the volume timer to one, effectively flashing the audio display in lowfi's UI.
|
/// Sets the volume timer to one, effectively flashing the audio display in lowfi's UI.
|
||||||
///
|
///
|
||||||
/// The amount of frames the audio display is visible for is determined by [AUDIO_BAR_DURATION].
|
/// The amount of frames the audio display is visible for is determined by [`AUDIO_BAR_DURATION`].
|
||||||
pub fn flash_audio() {
|
pub fn flash_audio() {
|
||||||
VOLUME_TIMER.store(1, Ordering::Relaxed);
|
VOLUME_TIMER.store(1, Ordering::Relaxed);
|
||||||
}
|
}
|
||||||
@ -96,7 +104,7 @@ impl Window {
|
|||||||
|
|
||||||
/// Actually draws the window, with each element in `content` being on a new line.
|
/// Actually draws the window, with each element in `content` being on a new line.
|
||||||
pub fn draw(&mut self, content: Vec<String>) -> eyre::Result<()> {
|
pub fn draw(&mut self, content: Vec<String>) -> eyre::Result<()> {
|
||||||
let len = content.len() as u16;
|
let len: u16 = content.len().try_into()?;
|
||||||
|
|
||||||
// Note that this will have a trailing newline, which we use later.
|
// Note that this will have a trailing newline, which we use later.
|
||||||
let menu: String = content.into_iter().fold(String::new(), |mut output, x| {
|
let menu: String = content.into_iter().fold(String::new(), |mut output, x| {
|
||||||
|
@ -1,10 +1,10 @@
|
|||||||
//! Various different individual components that
|
//! Various different individual components that
|
||||||
//! appear in lowfi's UI, like the progress bar.
|
//! appear in lowfi's UI, like the progress bar.
|
||||||
|
|
||||||
use std::{ops::Deref, sync::Arc, time::Duration};
|
use std::{ops::Deref as _, sync::Arc, time::Duration};
|
||||||
|
|
||||||
use crossterm::style::Stylize;
|
use crossterm::style::Stylize as _;
|
||||||
use unicode_segmentation::UnicodeSegmentation;
|
use unicode_segmentation::UnicodeSegmentation as _;
|
||||||
|
|
||||||
use crate::{player::Player, tracks::Info};
|
use crate::{player::Player, tracks::Info};
|
||||||
|
|
||||||
|
@ -2,7 +2,7 @@
|
|||||||
//! using [`crossterm`].
|
//! using [`crossterm`].
|
||||||
|
|
||||||
use crossterm::event::{self, EventStream, KeyCode, KeyEventKind, KeyModifiers};
|
use crossterm::event::{self, EventStream, KeyCode, KeyEventKind, KeyModifiers};
|
||||||
use futures::{FutureExt, StreamExt};
|
use futures::{FutureExt as _, StreamExt as _};
|
||||||
use tokio::sync::mpsc::Sender;
|
use tokio::sync::mpsc::Sender;
|
||||||
|
|
||||||
use crate::player::{ui, Messages};
|
use crate::player::{ui, Messages};
|
||||||
|
@ -5,9 +5,10 @@
|
|||||||
use std::{io::Cursor, time::Duration};
|
use std::{io::Cursor, time::Duration};
|
||||||
|
|
||||||
use bytes::Bytes;
|
use bytes::Bytes;
|
||||||
use inflector::Inflector;
|
use eyre::OptionExt as _;
|
||||||
use rodio::{Decoder, Source};
|
use inflector::Inflector as _;
|
||||||
use unicode_width::UnicodeWidthStr;
|
use rodio::{Decoder, Source as _};
|
||||||
|
use unicode_width::UnicodeWidthStr as _;
|
||||||
use url::form_urlencoded;
|
use url::form_urlencoded;
|
||||||
|
|
||||||
pub mod list;
|
pub mod list;
|
||||||
@ -36,6 +37,10 @@ pub struct Info {
|
|||||||
impl Info {
|
impl Info {
|
||||||
/// Decodes a URL string into normal UTF-8.
|
/// Decodes a URL string into normal UTF-8.
|
||||||
fn decode_url(text: &str) -> String {
|
fn decode_url(text: &str) -> String {
|
||||||
|
#[expect(
|
||||||
|
clippy::tuple_array_conversions,
|
||||||
|
reason = "the tuple contains smart pointers, so it's not really practical to use `into()`"
|
||||||
|
)]
|
||||||
form_urlencoded::parse(text.as_bytes())
|
form_urlencoded::parse(text.as_bytes())
|
||||||
.map(|(key, val)| [key, val].concat())
|
.map(|(key, val)| [key, val].concat())
|
||||||
.collect()
|
.collect()
|
||||||
@ -44,11 +49,13 @@ impl Info {
|
|||||||
/// Formats a name with [Inflector].
|
/// Formats a name with [Inflector].
|
||||||
/// This will also strip the first few numbers that are
|
/// This will also strip the first few numbers that are
|
||||||
/// usually present on most lofi tracks.
|
/// usually present on most lofi tracks.
|
||||||
fn format_name(name: &str) -> String {
|
fn format_name(name: &str) -> eyre::Result<String> {
|
||||||
let split = name.split('/').last().unwrap();
|
let split = name
|
||||||
|
.split('/')
|
||||||
|
.last()
|
||||||
|
.ok_or_eyre("split is never supposed to return nothing")?;
|
||||||
|
|
||||||
let stripped = split.strip_suffix(".mp3").unwrap_or(split);
|
let stripped = split.strip_suffix(".mp3").unwrap_or(split);
|
||||||
|
|
||||||
let formatted = Self::decode_url(stripped)
|
let formatted = Self::decode_url(stripped)
|
||||||
.to_lowercase()
|
.to_lowercase()
|
||||||
.to_title_case()
|
.to_title_case()
|
||||||
@ -76,28 +83,28 @@ impl Info {
|
|||||||
|
|
||||||
// If the entire name of the track is a number, then just return it.
|
// If the entire name of the track is a number, then just return it.
|
||||||
if skip == formatted.len() {
|
if skip == formatted.len() {
|
||||||
formatted
|
Ok(formatted)
|
||||||
} else {
|
} else {
|
||||||
#[expect(
|
#[expect(
|
||||||
clippy::string_slice,
|
clippy::string_slice,
|
||||||
reason = "We've already checked before that the bound is at an ASCII digit."
|
reason = "We've already checked before that the bound is at an ASCII digit."
|
||||||
)]
|
)]
|
||||||
String::from(&formatted[skip..])
|
Ok(String::from(&formatted[skip..]))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Creates a new [`TrackInfo`] from a possibly raw name & decoded track data.
|
/// Creates a new [`TrackInfo`] from a possibly raw name & decoded track data.
|
||||||
pub fn new(name: TrackName, decoded: &DecodedData) -> Self {
|
pub fn new(name: TrackName, decoded: &DecodedData) -> eyre::Result<Self> {
|
||||||
let name = match name {
|
let name = match name {
|
||||||
TrackName::Raw(raw) => Self::format_name(&raw),
|
TrackName::Raw(raw) => Self::format_name(&raw)?,
|
||||||
TrackName::Formatted(formatted) => formatted,
|
TrackName::Formatted(formatted) => formatted,
|
||||||
};
|
};
|
||||||
|
|
||||||
Self {
|
Ok(Self {
|
||||||
duration: decoded.total_duration(),
|
duration: decoded.total_duration(),
|
||||||
width: name.width(),
|
width: name.width(),
|
||||||
name,
|
name,
|
||||||
}
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -116,7 +123,7 @@ impl Decoded {
|
|||||||
/// This is equivalent to [`Track::decode`].
|
/// This is equivalent to [`Track::decode`].
|
||||||
pub fn new(track: Track) -> eyre::Result<Self> {
|
pub fn new(track: Track) -> eyre::Result<Self> {
|
||||||
let data = Decoder::new(Cursor::new(track.data))?;
|
let data = Decoder::new(Cursor::new(track.data))?;
|
||||||
let info = Info::new(track.name, &data);
|
let info = Info::new(track.name, &data)?;
|
||||||
|
|
||||||
Ok(Self { info, data })
|
Ok(Self { info, data })
|
||||||
}
|
}
|
||||||
|
@ -2,8 +2,8 @@
|
|||||||
//! as well as obtaining track names & downloading the raw mp3 data.
|
//! as well as obtaining track names & downloading the raw mp3 data.
|
||||||
|
|
||||||
use bytes::Bytes;
|
use bytes::Bytes;
|
||||||
use eyre::OptionExt;
|
use eyre::OptionExt as _;
|
||||||
use rand::Rng;
|
use rand::Rng as _;
|
||||||
use reqwest::Client;
|
use reqwest::Client;
|
||||||
use tokio::fs;
|
use tokio::fs;
|
||||||
|
|
||||||
@ -15,6 +15,7 @@ use super::Track;
|
|||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
pub struct List {
|
pub struct List {
|
||||||
/// The "name" of the list, usually derived from a filename.
|
/// The "name" of the list, usually derived from a filename.
|
||||||
|
#[allow(dead_code, reason = "this code may not be dead depending on features")]
|
||||||
pub name: String,
|
pub name: String,
|
||||||
|
|
||||||
/// Just the raw file, but seperated by `/n` (newlines).
|
/// Just the raw file, but seperated by `/n` (newlines).
|
||||||
@ -90,7 +91,7 @@ impl List {
|
|||||||
if let Some(arg) = tracks {
|
if let Some(arg) = tracks {
|
||||||
// Check if the track is in ~/.local/share/lowfi, in which case we'll load that.
|
// Check if the track is in ~/.local/share/lowfi, in which case we'll load that.
|
||||||
let name = dirs::data_dir()
|
let name = dirs::data_dir()
|
||||||
.unwrap()
|
.ok_or_eyre("data directory not found, are you *really* running this on wasm?")?
|
||||||
.join("lowfi")
|
.join("lowfi")
|
||||||
.join(format!("{arg}.txt"));
|
.join(format!("{arg}.txt"));
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user