mirror of
https://github.com/talwat/lowfi
synced 2025-09-27 10:50:02 +00:00
feat: the 1.7.0 release (#97)
* docs: update to be relevant to the current version * chore: bump version * fix: change default progress to 0 why it was ever -1.0 is a mystery to me, it doesn't make any logical sense... * fix: switch from rand to fastrand * feat: prepare for 1.7.0 release docs: explain music situation docs: more internal documentation feat: make timeout configurable chore: clean up some sections of code * fix: use boring fs functions for bookmark loading and writing * chore: remove useless internal doc * chore: bump version
This commit is contained in:
parent
4d4f5e0920
commit
0162421db4
@ -1,5 +1,10 @@
|
|||||||
# Using the chillhop list
|
# Using the chillhop list
|
||||||
|
|
||||||
|
> [!WARNING]
|
||||||
|
> As of lowfi 1.7.0, the chillhop list is included by default. For a more
|
||||||
|
> detailed explanation, see [MUSIC.md](MUSIC.md). This document is included
|
||||||
|
> to preserve any old links or references. The instructions are still valid.
|
||||||
|
|
||||||
## Linux
|
## Linux
|
||||||
|
|
||||||
```sh
|
```sh
|
||||||
|
210
Cargo.lock
generated
210
Cargo.lock
generated
@ -46,7 +46,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||||||
checksum = "ed7572b7ba83a31e20d1b48970ee402d2e3e0537dcfe0a3ff4d6eb7508617d43"
|
checksum = "ed7572b7ba83a31e20d1b48970ee402d2e3e0537dcfe0a3ff4d6eb7508617d43"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"alsa-sys",
|
"alsa-sys",
|
||||||
"bitflags 2.6.0",
|
"bitflags 2.9.3",
|
||||||
"cfg-if",
|
"cfg-if",
|
||||||
"libc",
|
"libc",
|
||||||
]
|
]
|
||||||
@ -183,7 +183,7 @@ dependencies = [
|
|||||||
"futures-lite",
|
"futures-lite",
|
||||||
"parking",
|
"parking",
|
||||||
"polling",
|
"polling",
|
||||||
"rustix",
|
"rustix 0.38.42",
|
||||||
"slab",
|
"slab",
|
||||||
"tracing",
|
"tracing",
|
||||||
"windows-sys 0.59.0",
|
"windows-sys 0.59.0",
|
||||||
@ -215,7 +215,7 @@ dependencies = [
|
|||||||
"cfg-if",
|
"cfg-if",
|
||||||
"event-listener",
|
"event-listener",
|
||||||
"futures-lite",
|
"futures-lite",
|
||||||
"rustix",
|
"rustix 0.38.42",
|
||||||
"tracing",
|
"tracing",
|
||||||
]
|
]
|
||||||
|
|
||||||
@ -242,7 +242,7 @@ dependencies = [
|
|||||||
"cfg-if",
|
"cfg-if",
|
||||||
"futures-core",
|
"futures-core",
|
||||||
"futures-io",
|
"futures-io",
|
||||||
"rustix",
|
"rustix 0.38.42",
|
||||||
"signal-hook-registry",
|
"signal-hook-registry",
|
||||||
"slab",
|
"slab",
|
||||||
"windows-sys 0.59.0",
|
"windows-sys 0.59.0",
|
||||||
@ -312,9 +312,9 @@ checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "bitflags"
|
name = "bitflags"
|
||||||
version = "2.6.0"
|
version = "2.9.3"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "b048fb63fd8b5923fc5aa7b340d8e156aec7ec02f0c78fa8a6ddc2613f6f71de"
|
checksum = "34efbcccd345379ca2868b2b2c9d3782e9cc58ba87bc7d79d5b53d9c9ae6f25d"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "block-buffer"
|
name = "block-buffer"
|
||||||
@ -480,6 +480,15 @@ dependencies = [
|
|||||||
"windows-sys 0.60.2",
|
"windows-sys 0.60.2",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "convert_case"
|
||||||
|
version = "0.7.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "bb402b8d4c85569410425650ce3eddc7d698ed96d39a73f941b08fb63082f1e7"
|
||||||
|
dependencies = [
|
||||||
|
"unicode-segmentation",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "convert_case"
|
name = "convert_case"
|
||||||
version = "0.8.0"
|
version = "0.8.0"
|
||||||
@ -562,16 +571,18 @@ checksum = "d0a5c400df2834b80a4c3327b3aad3a4c4cd4de0629063962b03235697506a28"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "crossterm"
|
name = "crossterm"
|
||||||
version = "0.28.1"
|
version = "0.29.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "829d955a0bb380ef178a640b91779e3987da38c9aea133b20614cfed8cdea9c6"
|
checksum = "d8b9f2e4c67f833b660cdb0a3523065869fb35570177239812ed4c905aeff87b"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bitflags 2.6.0",
|
"bitflags 2.9.3",
|
||||||
"crossterm_winapi",
|
"crossterm_winapi",
|
||||||
|
"derive_more 2.0.1",
|
||||||
|
"document-features",
|
||||||
"futures-core",
|
"futures-core",
|
||||||
"mio",
|
"mio",
|
||||||
"parking_lot",
|
"parking_lot",
|
||||||
"rustix",
|
"rustix 1.0.8",
|
||||||
"signal-hook",
|
"signal-hook",
|
||||||
"signal-hook-mio",
|
"signal-hook-mio",
|
||||||
"winapi",
|
"winapi",
|
||||||
@ -636,6 +647,27 @@ dependencies = [
|
|||||||
"syn",
|
"syn",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "derive_more"
|
||||||
|
version = "2.0.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "093242cf7570c207c83073cf82f79706fe7b8317e98620a47d5be7c3d8497678"
|
||||||
|
dependencies = [
|
||||||
|
"derive_more-impl",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "derive_more-impl"
|
||||||
|
version = "2.0.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "bda628edc44c4bb645fbe0f758797143e4e07926f7ebf4e9bdfbd3d2ce621df3"
|
||||||
|
dependencies = [
|
||||||
|
"convert_case 0.7.1",
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"syn",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "digest"
|
name = "digest"
|
||||||
version = "0.10.7"
|
version = "0.10.7"
|
||||||
@ -648,23 +680,23 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "dirs"
|
name = "dirs"
|
||||||
version = "5.0.1"
|
version = "6.0.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "44c45a9d03d6676652bcb5e724c7e988de1acad23a711b5217ab9cbecbec2225"
|
checksum = "c3e8aa94d75141228480295a7d0e7feb620b1a5ad9f12bc40be62411e38cce4e"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"dirs-sys",
|
"dirs-sys",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "dirs-sys"
|
name = "dirs-sys"
|
||||||
version = "0.4.1"
|
version = "0.5.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "520f05a5cbd335fae5a99ff7a6ab8627577660ee5cfd6a94a6a929b52ff0321c"
|
checksum = "e01a3366d27ee9890022452ee61b2b63a67e6f13f58900b651ff5665f0bb1fab"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"libc",
|
"libc",
|
||||||
"option-ext",
|
"option-ext",
|
||||||
"redox_users",
|
"redox_users",
|
||||||
"windows-sys 0.48.0",
|
"windows-sys 0.60.2",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@ -673,7 +705,7 @@ version = "0.3.0"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "89a09f22a6c6069a18470eb92d2298acf25463f14256d24778e1230d789a2aec"
|
checksum = "89a09f22a6c6069a18470eb92d2298acf25463f14256d24778e1230d789a2aec"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bitflags 2.6.0",
|
"bitflags 2.9.3",
|
||||||
"objc2",
|
"objc2",
|
||||||
]
|
]
|
||||||
|
|
||||||
@ -688,6 +720,15 @@ dependencies = [
|
|||||||
"syn",
|
"syn",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "document-features"
|
||||||
|
version = "0.2.11"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "95249b50c6c185bee49034bcb378a49dc2b5dff0be90ff6616d31d64febab05d"
|
||||||
|
dependencies = [
|
||||||
|
"litrs",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "dtoa"
|
name = "dtoa"
|
||||||
version = "1.0.10"
|
version = "1.0.10"
|
||||||
@ -1421,7 +1462,7 @@ version = "0.1.3"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "c0ff37bd590ca25063e35af745c343cb7a0271906fb7b37e4813e8f79f00268d"
|
checksum = "c0ff37bd590ca25063e35af745c343cb7a0271906fb7b37e4813e8f79f00268d"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bitflags 2.6.0",
|
"bitflags 2.9.3",
|
||||||
"libc",
|
"libc",
|
||||||
]
|
]
|
||||||
|
|
||||||
@ -1431,12 +1472,24 @@ version = "0.4.14"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "78b3ae25bc7c8c38cec158d1f2757ee79e9b3740fbc7ccf0e59e4b08d793fa89"
|
checksum = "78b3ae25bc7c8c38cec158d1f2757ee79e9b3740fbc7ccf0e59e4b08d793fa89"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "linux-raw-sys"
|
||||||
|
version = "0.9.4"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "cd945864f07fe9f5371a27ad7b52a172b4b499999f1d97574c9fa68373937e12"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "litemap"
|
name = "litemap"
|
||||||
version = "0.7.4"
|
version = "0.7.4"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "4ee93343901ab17bd981295f2cf0026d4ad018c7c31ba84549a4ddbb47a45104"
|
checksum = "4ee93343901ab17bd981295f2cf0026d4ad018c7c31ba84549a4ddbb47a45104"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "litrs"
|
||||||
|
version = "0.4.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "f5e54036fe321fd421e10d732f155734c4e4afd610dd556d9a82833ab3ee0bed"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "lock_api"
|
name = "lock_api"
|
||||||
version = "0.4.12"
|
version = "0.4.12"
|
||||||
@ -1455,24 +1508,24 @@ checksum = "a7a70ba024b9dc04c27ea2f0c0548feb474ec5c54bba33a7f72f873a39d07b24"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "lowfi"
|
name = "lowfi"
|
||||||
version = "1.7.0-dev"
|
version = "1.7.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"arc-swap",
|
"arc-swap",
|
||||||
"atomic_float",
|
"atomic_float",
|
||||||
"bytes",
|
"bytes",
|
||||||
"clap",
|
"clap",
|
||||||
"color-eyre",
|
"color-eyre",
|
||||||
"convert_case",
|
"convert_case 0.8.0",
|
||||||
"crossterm",
|
"crossterm",
|
||||||
"dirs",
|
"dirs",
|
||||||
"eyre",
|
"eyre",
|
||||||
|
"fastrand",
|
||||||
"futures",
|
"futures",
|
||||||
"html-escape",
|
"html-escape",
|
||||||
"indicatif",
|
"indicatif",
|
||||||
"lazy_static",
|
"lazy_static",
|
||||||
"libc",
|
"libc",
|
||||||
"mpris-server",
|
"mpris-server",
|
||||||
"rand",
|
|
||||||
"regex",
|
"regex",
|
||||||
"reqwest",
|
"reqwest",
|
||||||
"rodio",
|
"rodio",
|
||||||
@ -1603,7 +1656,7 @@ version = "0.9.0"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "c3f42e7bbe13d351b6bead8286a43aac9534b82bd3cc43e47037f012ebfd62d4"
|
checksum = "c3f42e7bbe13d351b6bead8286a43aac9534b82bd3cc43e47037f012ebfd62d4"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bitflags 2.6.0",
|
"bitflags 2.9.3",
|
||||||
"jni-sys",
|
"jni-sys",
|
||||||
"log",
|
"log",
|
||||||
"ndk-sys",
|
"ndk-sys",
|
||||||
@ -1638,7 +1691,7 @@ version = "0.29.0"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "71e2746dc3a24dd78b3cfcb7be93368c6de9963d30f43a6a73998a9cf4b17b46"
|
checksum = "71e2746dc3a24dd78b3cfcb7be93368c6de9963d30f43a6a73998a9cf4b17b46"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bitflags 2.6.0",
|
"bitflags 2.9.3",
|
||||||
"cfg-if",
|
"cfg-if",
|
||||||
"cfg_aliases",
|
"cfg_aliases",
|
||||||
"libc",
|
"libc",
|
||||||
@ -1732,7 +1785,7 @@ version = "0.3.1"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "10cbe18d879e20a4aea544f8befe38bcf52255eb63d3f23eca2842f3319e4c07"
|
checksum = "10cbe18d879e20a4aea544f8befe38bcf52255eb63d3f23eca2842f3319e4c07"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bitflags 2.6.0",
|
"bitflags 2.9.3",
|
||||||
"libc",
|
"libc",
|
||||||
"objc2",
|
"objc2",
|
||||||
"objc2-core-audio",
|
"objc2-core-audio",
|
||||||
@ -1759,7 +1812,7 @@ version = "0.3.1"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "c0f1cc99bb07ad2ddb6527ddf83db6a15271bb036b3eb94b801cd44fdc666ee1"
|
checksum = "c0f1cc99bb07ad2ddb6527ddf83db6a15271bb036b3eb94b801cd44fdc666ee1"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bitflags 2.6.0",
|
"bitflags 2.9.3",
|
||||||
"objc2",
|
"objc2",
|
||||||
]
|
]
|
||||||
|
|
||||||
@ -1769,7 +1822,7 @@ version = "0.3.1"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "1c10c2894a6fed806ade6027bcd50662746363a9589d3ec9d9bef30a4e4bc166"
|
checksum = "1c10c2894a6fed806ade6027bcd50662746363a9589d3ec9d9bef30a4e4bc166"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bitflags 2.6.0",
|
"bitflags 2.9.3",
|
||||||
"dispatch2",
|
"dispatch2",
|
||||||
"objc2",
|
"objc2",
|
||||||
]
|
]
|
||||||
@ -1810,7 +1863,7 @@ version = "0.10.68"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "6174bc48f102d208783c2c84bf931bb75927a617866870de8a4ea85597f871f5"
|
checksum = "6174bc48f102d208783c2c84bf931bb75927a617866870de8a4ea85597f871f5"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bitflags 2.6.0",
|
"bitflags 2.9.3",
|
||||||
"cfg-if",
|
"cfg-if",
|
||||||
"foreign-types",
|
"foreign-types",
|
||||||
"libc",
|
"libc",
|
||||||
@ -1996,7 +2049,7 @@ dependencies = [
|
|||||||
"concurrent-queue",
|
"concurrent-queue",
|
||||||
"hermit-abi",
|
"hermit-abi",
|
||||||
"pin-project-lite",
|
"pin-project-lite",
|
||||||
"rustix",
|
"rustix 0.38.42",
|
||||||
"tracing",
|
"tracing",
|
||||||
"windows-sys 0.59.0",
|
"windows-sys 0.59.0",
|
||||||
]
|
]
|
||||||
@ -2091,18 +2144,18 @@ version = "0.5.8"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "03a862b389f93e68874fbf580b9de08dd02facb9a788ebadaf4a3fd33cf58834"
|
checksum = "03a862b389f93e68874fbf580b9de08dd02facb9a788ebadaf4a3fd33cf58834"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bitflags 2.6.0",
|
"bitflags 2.9.3",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "redox_users"
|
name = "redox_users"
|
||||||
version = "0.4.6"
|
version = "0.5.2"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "ba009ff324d1fc1b900bd1fdb31564febe58a8ccc8a6fdbb93b543d33b13ca43"
|
checksum = "a4e608c6638b9c18977b00b475ac1f28d14e84b27d8d42f70e0bf1e3dec127ac"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"getrandom 0.2.15",
|
"getrandom 0.2.15",
|
||||||
"libredox",
|
"libredox",
|
||||||
"thiserror 1.0.69",
|
"thiserror 2.0.12",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@ -2219,13 +2272,26 @@ version = "0.38.42"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "f93dc38ecbab2eb790ff964bb77fa94faf256fd3e73285fd7ba0903b76bedb85"
|
checksum = "f93dc38ecbab2eb790ff964bb77fa94faf256fd3e73285fd7ba0903b76bedb85"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bitflags 2.6.0",
|
"bitflags 2.9.3",
|
||||||
"errno",
|
"errno",
|
||||||
"libc",
|
"libc",
|
||||||
"linux-raw-sys",
|
"linux-raw-sys 0.4.14",
|
||||||
"windows-sys 0.59.0",
|
"windows-sys 0.59.0",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "rustix"
|
||||||
|
version = "1.0.8"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "11181fbabf243db407ef8df94a6ce0b2f9a733bd8be4ad02b4eda9602296cac8"
|
||||||
|
dependencies = [
|
||||||
|
"bitflags 2.9.3",
|
||||||
|
"errno",
|
||||||
|
"libc",
|
||||||
|
"linux-raw-sys 0.9.4",
|
||||||
|
"windows-sys 0.60.2",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "rustls"
|
name = "rustls"
|
||||||
version = "0.23.20"
|
version = "0.23.20"
|
||||||
@ -2323,7 +2389,7 @@ version = "2.11.1"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "897b2245f0b511c87893af39b033e5ca9cce68824c4d7e7630b5a1d339658d02"
|
checksum = "897b2245f0b511c87893af39b033e5ca9cce68824c4d7e7630b5a1d339658d02"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bitflags 2.6.0",
|
"bitflags 2.9.3",
|
||||||
"core-foundation",
|
"core-foundation",
|
||||||
"core-foundation-sys",
|
"core-foundation-sys",
|
||||||
"libc",
|
"libc",
|
||||||
@ -2346,9 +2412,9 @@ version = "0.26.0"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "fd568a4c9bb598e291a08244a5c1f5a8a6650bee243b5b0f8dbb3d9cc1d87fe8"
|
checksum = "fd568a4c9bb598e291a08244a5c1f5a8a6650bee243b5b0f8dbb3d9cc1d87fe8"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bitflags 2.6.0",
|
"bitflags 2.9.3",
|
||||||
"cssparser",
|
"cssparser",
|
||||||
"derive_more",
|
"derive_more 0.99.20",
|
||||||
"fxhash",
|
"fxhash",
|
||||||
"log",
|
"log",
|
||||||
"new_debug_unreachable",
|
"new_debug_unreachable",
|
||||||
@ -2740,7 +2806,7 @@ version = "0.6.1"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "3c879d448e9d986b661742763247d3693ed13609438cf3d006f51f5368a5ba6b"
|
checksum = "3c879d448e9d986b661742763247d3693ed13609438cf3d006f51f5368a5ba6b"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bitflags 2.6.0",
|
"bitflags 2.9.3",
|
||||||
"core-foundation",
|
"core-foundation",
|
||||||
"system-configuration-sys",
|
"system-configuration-sys",
|
||||||
]
|
]
|
||||||
@ -2765,7 +2831,7 @@ dependencies = [
|
|||||||
"fastrand",
|
"fastrand",
|
||||||
"getrandom 0.2.15",
|
"getrandom 0.2.15",
|
||||||
"once_cell",
|
"once_cell",
|
||||||
"rustix",
|
"rustix 0.38.42",
|
||||||
"windows-sys 0.59.0",
|
"windows-sys 0.59.0",
|
||||||
]
|
]
|
||||||
|
|
||||||
@ -3321,15 +3387,6 @@ dependencies = [
|
|||||||
"windows-targets 0.42.2",
|
"windows-targets 0.42.2",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "windows-sys"
|
|
||||||
version = "0.48.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9"
|
|
||||||
dependencies = [
|
|
||||||
"windows-targets 0.48.5",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "windows-sys"
|
name = "windows-sys"
|
||||||
version = "0.52.0"
|
version = "0.52.0"
|
||||||
@ -3372,21 +3429,6 @@ dependencies = [
|
|||||||
"windows_x86_64_msvc 0.42.2",
|
"windows_x86_64_msvc 0.42.2",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "windows-targets"
|
|
||||||
version = "0.48.5"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "9a2fa6e2155d7247be68c096456083145c183cbbbc2764150dda45a87197940c"
|
|
||||||
dependencies = [
|
|
||||||
"windows_aarch64_gnullvm 0.48.5",
|
|
||||||
"windows_aarch64_msvc 0.48.5",
|
|
||||||
"windows_i686_gnu 0.48.5",
|
|
||||||
"windows_i686_msvc 0.48.5",
|
|
||||||
"windows_x86_64_gnu 0.48.5",
|
|
||||||
"windows_x86_64_gnullvm 0.48.5",
|
|
||||||
"windows_x86_64_msvc 0.48.5",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "windows-targets"
|
name = "windows-targets"
|
||||||
version = "0.52.6"
|
version = "0.52.6"
|
||||||
@ -3426,12 +3468,6 @@ version = "0.42.2"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "597a5118570b68bc08d8d59125332c54f1ba9d9adeedeef5b99b02ba2b0698f8"
|
checksum = "597a5118570b68bc08d8d59125332c54f1ba9d9adeedeef5b99b02ba2b0698f8"
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "windows_aarch64_gnullvm"
|
|
||||||
version = "0.48.5"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8"
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "windows_aarch64_gnullvm"
|
name = "windows_aarch64_gnullvm"
|
||||||
version = "0.52.6"
|
version = "0.52.6"
|
||||||
@ -3450,12 +3486,6 @@ version = "0.42.2"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "e08e8864a60f06ef0d0ff4ba04124db8b0fb3be5776a5cd47641e942e58c4d43"
|
checksum = "e08e8864a60f06ef0d0ff4ba04124db8b0fb3be5776a5cd47641e942e58c4d43"
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "windows_aarch64_msvc"
|
|
||||||
version = "0.48.5"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc"
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "windows_aarch64_msvc"
|
name = "windows_aarch64_msvc"
|
||||||
version = "0.52.6"
|
version = "0.52.6"
|
||||||
@ -3474,12 +3504,6 @@ version = "0.42.2"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "c61d927d8da41da96a81f029489353e68739737d3beca43145c8afec9a31a84f"
|
checksum = "c61d927d8da41da96a81f029489353e68739737d3beca43145c8afec9a31a84f"
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "windows_i686_gnu"
|
|
||||||
version = "0.48.5"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e"
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "windows_i686_gnu"
|
name = "windows_i686_gnu"
|
||||||
version = "0.52.6"
|
version = "0.52.6"
|
||||||
@ -3510,12 +3534,6 @@ version = "0.42.2"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "44d840b6ec649f480a41c8d80f9c65108b92d89345dd94027bfe06ac444d1060"
|
checksum = "44d840b6ec649f480a41c8d80f9c65108b92d89345dd94027bfe06ac444d1060"
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "windows_i686_msvc"
|
|
||||||
version = "0.48.5"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406"
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "windows_i686_msvc"
|
name = "windows_i686_msvc"
|
||||||
version = "0.52.6"
|
version = "0.52.6"
|
||||||
@ -3534,12 +3552,6 @@ version = "0.42.2"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "8de912b8b8feb55c064867cf047dda097f92d51efad5b491dfb98f6bbb70cb36"
|
checksum = "8de912b8b8feb55c064867cf047dda097f92d51efad5b491dfb98f6bbb70cb36"
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "windows_x86_64_gnu"
|
|
||||||
version = "0.48.5"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e"
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "windows_x86_64_gnu"
|
name = "windows_x86_64_gnu"
|
||||||
version = "0.52.6"
|
version = "0.52.6"
|
||||||
@ -3558,12 +3570,6 @@ version = "0.42.2"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "26d41b46a36d453748aedef1486d5c7a85db22e56aff34643984ea85514e94a3"
|
checksum = "26d41b46a36d453748aedef1486d5c7a85db22e56aff34643984ea85514e94a3"
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "windows_x86_64_gnullvm"
|
|
||||||
version = "0.48.5"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc"
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "windows_x86_64_gnullvm"
|
name = "windows_x86_64_gnullvm"
|
||||||
version = "0.52.6"
|
version = "0.52.6"
|
||||||
@ -3582,12 +3588,6 @@ version = "0.42.2"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "9aec5da331524158c6d1a4ac0ab1541149c0b9505fde06423b02f5ef0106b9f0"
|
checksum = "9aec5da331524158c6d1a4ac0ab1541149c0b9505fde06423b02f5ef0106b9f0"
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "windows_x86_64_msvc"
|
|
||||||
version = "0.48.5"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538"
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "windows_x86_64_msvc"
|
name = "windows_x86_64_msvc"
|
||||||
version = "0.52.6"
|
version = "0.52.6"
|
||||||
@ -3615,7 +3615,7 @@ version = "0.39.0"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "6f42320e61fe2cfd34354ecb597f86f413484a798ba44a8ca1165c58d42da6c1"
|
checksum = "6f42320e61fe2cfd34354ecb597f86f413484a798ba44a8ca1165c58d42da6c1"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bitflags 2.6.0",
|
"bitflags 2.9.3",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "lowfi"
|
name = "lowfi"
|
||||||
version = "1.7.0-dev"
|
version = "1.7.0"
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
description = "An extremely simple lofi player."
|
description = "An extremely simple lofi player."
|
||||||
license = "MIT"
|
license = "MIT"
|
||||||
@ -25,7 +25,7 @@ scrape = ["dep:serde", "dep:serde_json", "dep:html-escape", "dep:scraper", "dep:
|
|||||||
# Basics
|
# Basics
|
||||||
clap = { version = "4.5.21", features = ["derive", "cargo"] }
|
clap = { version = "4.5.21", features = ["derive", "cargo"] }
|
||||||
eyre = "0.6.12"
|
eyre = "0.6.12"
|
||||||
rand = "0.8.5"
|
fastrand = "2.3.0"
|
||||||
thiserror = "2.0.12"
|
thiserror = "2.0.12"
|
||||||
color-eyre = { version = "0.6.5", default-features = false }
|
color-eyre = { version = "0.6.5", default-features = false }
|
||||||
|
|
||||||
@ -39,10 +39,10 @@ reqwest = { version = "0.12.9", features = ["stream"] }
|
|||||||
bytes = "1.9.0"
|
bytes = "1.9.0"
|
||||||
|
|
||||||
# I/O
|
# I/O
|
||||||
crossterm = { version = "0.28.1", features = ["event-stream"] }
|
crossterm = { version = "0.29.0", features = ["event-stream"] }
|
||||||
rodio = { version = "0.21.1", features = ["symphonia-mp3", "playback"], default-features = false }
|
rodio = { version = "0.21.1", features = ["symphonia-mp3", "playback"], default-features = false }
|
||||||
mpris-server = { version = "0.8.1", optional = true }
|
mpris-server = { version = "0.8.1", optional = true }
|
||||||
dirs = "5.0.1"
|
dirs = "6.0.0"
|
||||||
|
|
||||||
# Misc
|
# Misc
|
||||||
convert_case = "0.8.0"
|
convert_case = "0.8.0"
|
||||||
|
75
MUSIC.md
Normal file
75
MUSIC.md
Normal file
@ -0,0 +1,75 @@
|
|||||||
|
# The State of Lowfi's Music
|
||||||
|
|
||||||
|
> [!WARNING]
|
||||||
|
> This document will be a bit long and has almost nothing to do with the actual
|
||||||
|
> usage of lowfi, just the music embedded by default.
|
||||||
|
|
||||||
|
Before that though, some context. lowfi includes an extensive track list
|
||||||
|
embedded into the software, so you can download it and have it "just work"
|
||||||
|
out of the box.
|
||||||
|
|
||||||
|
I always hated apps that required extensive configuration just to be usable.
|
||||||
|
Sometimes it's justified, but often, it's just pointless when most will end up
|
||||||
|
with the same set of "defaults" that aren't really defaults.
|
||||||
|
|
||||||
|
Lowfi is so nice and simple because of the "plug and play" aspect,
|
||||||
|
but it's become a lot harder to continue it as of late.
|
||||||
|
|
||||||
|
## The Lofi Girl List
|
||||||
|
|
||||||
|
Originally, it was planned that lowfi would use music scraped from Lofi Girl's own
|
||||||
|
website. The scraper actually came before the rest of the program, believe it or not.
|
||||||
|
|
||||||
|
However, after a long period of downtime, the Lofi Girl website was redone without the
|
||||||
|
mp3 track files. Those are now pretty much inaccessible aside from paying for individual
|
||||||
|
albums on bandcamp which gets very expensive very quickly.
|
||||||
|
|
||||||
|
Doing this was never actually disallowed, but it is now simply impossible. So, the question was,
|
||||||
|
what to do next after losing lowfi's primary source of music?
|
||||||
|
|
||||||
|
## Tracklists
|
||||||
|
|
||||||
|
I was originally against the idea of custom tracklists, because of my almost purist
|
||||||
|
ideals of a 100% no config at all vision for lowfi. But eventually, I gave in, which proved
|
||||||
|
to be a very good decision in hindsight. Now, regardless of what choices I make on the music
|
||||||
|
which is embedded, all may opt out of that and choose whatever they like.
|
||||||
|
|
||||||
|
This culminated in a few templates located in the `data` directory of this repository
|
||||||
|
which included a handful of tracklists, and in particular, the chillhop list by user
|
||||||
|
[danielwerg](https://github.com/danielwerg).
|
||||||
|
|
||||||
|
## The Switch
|
||||||
|
|
||||||
|
After `lofigirl.com` went down, I thought a bit and eventually decided
|
||||||
|
to just bite the bullet and switch to the chillhop list. This was despite the fact
|
||||||
|
that chillhop entirely bans third party players in their TOS. They also ban
|
||||||
|
scrapers, which I only learned after writing one.
|
||||||
|
|
||||||
|
So, is lowfi really going to have to violate the TOS of it's own music provider?
|
||||||
|
Well, yes. I thought about it, and came to the conclusion that lowfi is probably
|
||||||
|
not much of a threat for a few reasons.
|
||||||
|
|
||||||
|
Firstly, it emulates exactly the behavior of chillhop's own radio player.
|
||||||
|
The only difference is that one shoves you into a web browser, and the other,
|
||||||
|
into a nice terminal window.
|
||||||
|
|
||||||
|
Then, I also realize that lowfi is just a small program used by few.
|
||||||
|
I'm not making money on any of this, and I think degrading the experience for my
|
||||||
|
fellow nerds who just want to listen to some lowfi without all the crap is not worth it.
|
||||||
|
|
||||||
|
At the end of the day, lowfi has a distinct UserAgent. Should chillhop ever take issue with
|
||||||
|
it's behaviour, banning it is extremely simple. I don't want that to happen, but I
|
||||||
|
understand if it does.
|
||||||
|
|
||||||
|
## Well, *I* Hate the Chillhop Music
|
||||||
|
|
||||||
|
It's not as "lofi". It is almost certainly a compromise, that much I cannot even pretend to
|
||||||
|
deny. I find myself hitting the skip button almost three times as often with chillhop.
|
||||||
|
|
||||||
|
If you are undeterred enough by TOS's to read this far, then you can use the `archive.txt`
|
||||||
|
list in the `data` folder. The list is a product of me worrying that the tracks on `lofigirl.com`
|
||||||
|
could've possibly been lost somehow, relating to the website going down.
|
||||||
|
|
||||||
|
It's hosted on `archive.org`, and could be taken down at any point for any reason.
|
||||||
|
Being derived from my own local archive, it retains ~2700 out of the ~3700 tracks.
|
||||||
|
That's not perfect, the organization is also *bad*, but it exists.
|
76
README.md
76
README.md
@ -7,18 +7,9 @@ It'll do this as simply as it can: no albums, no ads, just lofi.
|
|||||||
|
|
||||||
## Disclaimer
|
## Disclaimer
|
||||||
|
|
||||||
> [!NOTE]
|
As of the 1.7.0 version of lowfi, **all** of the audio files embedded
|
||||||
>
|
by default are from [chillhop](https://chillhop.com/). Read
|
||||||
> As of the 19th of July 2025, lofigirl.com is temporarily down. If your lowfi is up to date,
|
[MUSIC.md] for more information.
|
||||||
> you can follow the [quick instructions](CHILLHOP.md) for using the [chillhop](https://chillhop.com/) alternative track list.
|
|
||||||
>
|
|
||||||
> Apologies for the inconvenience, it's out of lowfi's control.
|
|
||||||
|
|
||||||
**All** of the audio files embedded into in lowfi by default are from [Lofi Girl's](https://lofigirl.com/) website,
|
|
||||||
under their [licensing guidelines](https://form.lofigirl.com/CommercialLicense).
|
|
||||||
|
|
||||||
If, god forbid, you're planning to use lowfi in a commercial setting, please
|
|
||||||
follow their rules.
|
|
||||||
|
|
||||||
## Why?
|
## Why?
|
||||||
|
|
||||||
@ -135,6 +126,7 @@ Yeah, that's it.
|
|||||||
| `-`, `_`, `j`, `↓` | Volume Down 10% |
|
| `-`, `_`, `j`, `↓` | Volume Down 10% |
|
||||||
| `←` | Volume Down 1% |
|
| `←` | Volume Down 1% |
|
||||||
| `q`, CTRL+C | Quit |
|
| `q`, CTRL+C | Quit |
|
||||||
|
| `b` | Bookmark |
|
||||||
|
|
||||||
> [!NOTE]
|
> [!NOTE]
|
||||||
> Besides its regular controls, lowfi offers compatibility with Media Keys
|
> Besides its regular controls, lowfi offers compatibility with Media Keys
|
||||||
@ -162,56 +154,54 @@ slightly tweak the UI or behaviour of the menu. The flags can be viewed with `lo
|
|||||||
|
|
||||||
### Scraping
|
### Scraping
|
||||||
|
|
||||||
lowfi also has a `scrape` command which is usually not relevant, but
|
lowfi also has an optional `scrape` command enabled by the `scrape` feature.
|
||||||
if you're trying to download some files from Lofi Girls' website,
|
It's usually not very useful, but is included for transparency's sake.
|
||||||
it can be useful.
|
|
||||||
|
|
||||||
An example of scrape is as follows,
|
More information can be found by running `lowfi help scrape`.
|
||||||
|
|
||||||
`lowfi scrape --extension zip --include-full`
|
|
||||||
|
|
||||||
where more information can be found by running `lowfi help scrape`.
|
|
||||||
|
|
||||||
### Custom Track Lists
|
### Custom Track Lists
|
||||||
|
|
||||||
Some nice users, especially [danielwerg](https://github.com/danielwerg),
|
> [!NOTE]
|
||||||
have aleady made alternative track lists located in the [data](https://github.com/talwat/lowfi/blob/main/data/)
|
> Some nice users, especially [danielwerg](https://github.com/danielwerg),
|
||||||
directory of this repo. You can use them with lowfi by using the `--track-list` flag.
|
> have aleady made alternative track lists located in the [data](https://github.com/talwat/lowfi/blob/main/data/)
|
||||||
|
> directory of this repo. You can use them with lowfi by using the `--track-list` flag.
|
||||||
Feel free to contribute your own list with a PR.
|
|
||||||
|
|
||||||
> [!WARNING]
|
|
||||||
>
|
>
|
||||||
> Custom track lists are going to be pretty particular.
|
> Feel free to contribute your own list with a PR.
|
||||||
> This is because I still want to keep `lowfi` as simple as possible,
|
|
||||||
> so custom lists will be very similar to how the built in list functions.
|
|
||||||
>
|
|
||||||
> This also means that there will be no added flexibility to these lists,
|
|
||||||
> so you'll have to work that out on your own.
|
|
||||||
|
|
||||||
lowfi also supports custom track lists, although the default one from Lofi Girl
|
lowfi also supports custom track lists, although the default one from Lofi Girl
|
||||||
is embedded into the binary.
|
is embedded into the binary.
|
||||||
|
|
||||||
To use a custom list, use the `--track-list` flag. This can either be a path to some file,
|
To use a custom list, use the `--track-list` flag. This can either be a path to some file,
|
||||||
or it could also be the name of a file (without the `.txt` extension) in the data
|
or it could also be the name of a file (without the `.txt` extension) in the data
|
||||||
directory, so on Linux it's `~/.local/share/lowfi`.
|
directory.
|
||||||
|
|
||||||
|
> [!NOTE]
|
||||||
|
> Data directories:
|
||||||
|
>
|
||||||
|
> - Linux - `~/.local/share/lowfi`
|
||||||
|
> - macOS - `~/Library/Application Support/lowfi`
|
||||||
|
> - Windows - `%appdata%\Roaming\lowfi`
|
||||||
|
|
||||||
For example, `lowfi --track-list minipop` would load `~/.local/share/lowfi/minipop.txt`.
|
For example, `lowfi --track-list minipop` would load `~/.local/share/lowfi/minipop.txt`.
|
||||||
Whereas if you did `lowfi --track-list ~/Music/minipop.txt` it would load from that
|
Whereas if you did `lowfi --track-list ~/Music/minipop.txt` it would load from that
|
||||||
specified directory.
|
specified directory.
|
||||||
|
|
||||||
|
All tracks must be in the MP3 format, unless lowfi has been compiled with the
|
||||||
|
`extra-audio-formats` feature which includes support for some others.
|
||||||
|
|
||||||
#### The Format
|
#### The Format
|
||||||
|
|
||||||
In lists, the first line should be the base URL, followed by the rest of the tracks.
|
In lists, the first line is what's known as the header, followed by the rest of the tracks.
|
||||||
This is also known as the "header", because it comes first.
|
Each track will be first appended to the header, and then use that to download
|
||||||
|
the track.
|
||||||
|
|
||||||
Each track will be first appended to the base URL, and then the result use to download
|
> [!NOTE]
|
||||||
the track. All tracks must be in the MP3 format, as lowfi doesn't support any others currently.
|
> lowfi _will not_ put a `/` between the base & track for added flexibility,
|
||||||
|
> so for most cases you should have a trailing `/` in your header.
|
||||||
|
|
||||||
Additionally, lowfi _won't_ put a `/` between the base & track for added flexibility,
|
The exception to this is if the track name begins with a protocol like `https://`,
|
||||||
so for most cases you should have a trailing `/` in your base url.
|
in which case the base will not be prepended to it. If all of your tracks are like this,
|
||||||
The exception to this is if the track name begins with something like `https://`,
|
then you can put `noheader` as the first line and not have a header at all.
|
||||||
where in that case the base will not be prepended to it.
|
|
||||||
|
|
||||||
For example, in this list:
|
For example, in this list:
|
||||||
|
|
||||||
@ -243,7 +233,7 @@ This is useful if you want to use a local file as the base URL, such as:
|
|||||||
```txt
|
```txt
|
||||||
file:///home/user/Music/
|
file:///home/user/Music/
|
||||||
file.mp3
|
file.mp3
|
||||||
file:///home/user/Music/second-file.mp3
|
file:///home/user/Other Music/second-file.mp3
|
||||||
```
|
```
|
||||||
|
|
||||||
Further examples can be found in the [data](https://github.com/talwat/lowfi/tree/main/data) folder.
|
Further examples can be found in the [data](https://github.com/talwat/lowfi/tree/main/data) folder.
|
||||||
|
@ -1,2 +1,2 @@
|
|||||||
file:///home/user/Music/
|
file:///home/user/Music/
|
||||||
Anomaly.mp3
|
Test.mp3
|
2
data/noheader.txt
Normal file
2
data/noheader.txt
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
noheader
|
||||||
|
https://stream.chillhop.com/mp3/9476
|
@ -1,4 +1,4 @@
|
|||||||
https://lofigirl.com/wp-content/uploads/
|
https://lofigirl.com/wp-content/uploads/
|
||||||
2023/06/Foudroie-Finding-The-Edge-V2.mp3
|
2023/06/Foudroie-Finding-The-Edge-V2.mp3
|
||||||
2023/04/2-In-Front-Of-Me.mp3
|
2023/04/2-In-Front-Of-Me.mp3
|
||||||
https://file-examples.com/storage/fe85f7a43b689349d9c8f18/2017/11/file_example_MP3_1MG.mp3
|
https://stream.chillhop.com/mp3/9476
|
@ -16,6 +16,7 @@ mod scrapers;
|
|||||||
|
|
||||||
#[cfg(feature = "scrape")]
|
#[cfg(feature = "scrape")]
|
||||||
use crate::scrapers::Source;
|
use crate::scrapers::Source;
|
||||||
|
|
||||||
/// An extremely simple lofi player.
|
/// An extremely simple lofi player.
|
||||||
#[derive(Parser, Clone)]
|
#[derive(Parser, Clone)]
|
||||||
#[command(about, version)]
|
#[command(about, version)]
|
||||||
@ -41,6 +42,10 @@ struct Args {
|
|||||||
#[clap(long, short, default_value_t = 12)]
|
#[clap(long, short, default_value_t = 12)]
|
||||||
fps: u8,
|
fps: u8,
|
||||||
|
|
||||||
|
/// Timeout in seconds for music downloads.
|
||||||
|
#[clap(long, default_value_t = 3)]
|
||||||
|
timeout: u64,
|
||||||
|
|
||||||
/// Include ALSA & other logs.
|
/// Include ALSA & other logs.
|
||||||
#[clap(long, short)]
|
#[clap(long, short)]
|
||||||
debug: bool,
|
debug: bool,
|
||||||
@ -50,7 +55,7 @@ struct Args {
|
|||||||
width: usize,
|
width: usize,
|
||||||
|
|
||||||
/// Use a custom track list
|
/// Use a custom track list
|
||||||
#[clap(long, short, alias = "list", short_alias = 'l')]
|
#[clap(long, short, alias = "list", alias = "tracks", short_alias = 'l')]
|
||||||
track_list: Option<String>,
|
track_list: Option<String>,
|
||||||
|
|
||||||
/// Internal song buffer size.
|
/// Internal song buffer size.
|
||||||
@ -70,6 +75,7 @@ enum Commands {
|
|||||||
#[cfg(feature = "scrape")]
|
#[cfg(feature = "scrape")]
|
||||||
Scrape {
|
Scrape {
|
||||||
// The source to scrape from.
|
// The source to scrape from.
|
||||||
|
#[clap(long, short)]
|
||||||
source: scrapers::Source,
|
source: scrapers::Source,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
@ -91,7 +97,6 @@ async fn main() -> eyre::Result<()> {
|
|||||||
|
|
||||||
if let Some(command) = cli.command {
|
if let Some(command) = cli.command {
|
||||||
match command {
|
match command {
|
||||||
// TODO: Actually distinguish between sources.
|
|
||||||
#[cfg(feature = "scrape")]
|
#[cfg(feature = "scrape")]
|
||||||
Commands::Scrape { source } => match source {
|
Commands::Scrape { source } => match source {
|
||||||
Source::Archive => scrapers::archive::scrape().await?,
|
Source::Archive => scrapers::archive::scrape().await?,
|
||||||
|
@ -70,7 +70,9 @@ pub async fn play(args: Args) -> eyre::Result<(), player::Error> {
|
|||||||
|
|
||||||
drop(stream);
|
drop(stream);
|
||||||
player.sink.stop();
|
player.sink.stop();
|
||||||
ui.map(|x| x.abort());
|
if let Some(x) = ui {
|
||||||
|
x.abort();
|
||||||
|
}
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
@ -41,10 +41,6 @@ pub use error::Error;
|
|||||||
#[cfg(feature = "mpris")]
|
#[cfg(feature = "mpris")]
|
||||||
pub mod mpris;
|
pub mod mpris;
|
||||||
|
|
||||||
/// The time to wait in between errors.
|
|
||||||
/// TODO: Make this configurable.
|
|
||||||
const TIMEOUT: Duration = Duration::from_secs(3);
|
|
||||||
|
|
||||||
/// Main struct responsible for queuing up & playing tracks.
|
/// Main struct responsible for queuing up & playing tracks.
|
||||||
// TODO: Consider refactoring [Player] from being stored in an [Arc], into containing many smaller [Arc]s.
|
// TODO: Consider refactoring [Player] from being stored in an [Arc], into containing many smaller [Arc]s.
|
||||||
// TODO: In other words, this would change the type from `Arc<Player>` to just `Player`.
|
// TODO: In other words, this would change the type from `Arc<Player>` to just `Player`.
|
||||||
@ -76,6 +72,9 @@ pub struct Player {
|
|||||||
/// The bookmarks, which are saved on quit.
|
/// The bookmarks, which are saved on quit.
|
||||||
pub bookmarks: Bookmarks,
|
pub bookmarks: Bookmarks,
|
||||||
|
|
||||||
|
/// The timeout for track downloads, as a [Duration].
|
||||||
|
timeout: Duration,
|
||||||
|
|
||||||
/// The actual list of tracks to be played.
|
/// The actual list of tracks to be played.
|
||||||
list: List,
|
list: List,
|
||||||
|
|
||||||
@ -144,14 +143,15 @@ impl Player {
|
|||||||
"/",
|
"/",
|
||||||
env!("CARGO_PKG_VERSION")
|
env!("CARGO_PKG_VERSION")
|
||||||
))
|
))
|
||||||
.timeout(TIMEOUT * 5)
|
.timeout(Duration::from_secs(args.timeout * 5))
|
||||||
.build()?;
|
.build()?;
|
||||||
|
|
||||||
let player = Self {
|
let player = Self {
|
||||||
tracks: RwLock::new(VecDeque::with_capacity(args.buffer_size)),
|
tracks: RwLock::new(VecDeque::with_capacity(args.buffer_size)),
|
||||||
buffer_size: args.buffer_size,
|
buffer_size: args.buffer_size,
|
||||||
current: ArcSwapOption::new(None),
|
current: ArcSwapOption::new(None),
|
||||||
progress: AtomicF32::new(-1.0),
|
progress: AtomicF32::new(0.0),
|
||||||
|
timeout: Duration::from_secs(args.timeout),
|
||||||
bookmarks,
|
bookmarks,
|
||||||
client,
|
client,
|
||||||
sink,
|
sink,
|
||||||
@ -301,7 +301,7 @@ impl Player {
|
|||||||
let current = player.current.load();
|
let current = player.current.load();
|
||||||
let current = current.as_ref().unwrap();
|
let current = current.as_ref().unwrap();
|
||||||
|
|
||||||
player.bookmarks.bookmark(&¤t).await?;
|
player.bookmarks.bookmark(current).await?;
|
||||||
}
|
}
|
||||||
Message::Quit => break,
|
Message::Quit => break,
|
||||||
}
|
}
|
||||||
|
@ -1,12 +1,15 @@
|
|||||||
use std::io::SeekFrom;
|
//! Module for handling saving, loading, and adding
|
||||||
|
//! bookmarks.
|
||||||
|
|
||||||
|
use std::path::PathBuf;
|
||||||
use std::sync::atomic::AtomicBool;
|
use std::sync::atomic::AtomicBool;
|
||||||
|
|
||||||
use tokio::fs::{create_dir_all, File, OpenOptions};
|
|
||||||
use tokio::io::{self, AsyncReadExt, AsyncSeekExt, AsyncWriteExt};
|
|
||||||
use tokio::sync::RwLock;
|
use tokio::sync::RwLock;
|
||||||
|
use tokio::{fs, io};
|
||||||
|
|
||||||
use crate::{data_dir, tracks};
|
use crate::{data_dir, tracks};
|
||||||
|
|
||||||
|
/// Errors that might occur while managing bookmarks.
|
||||||
#[derive(Debug, thiserror::Error)]
|
#[derive(Debug, thiserror::Error)]
|
||||||
pub enum BookmarkError {
|
pub enum BookmarkError {
|
||||||
#[error("data directory not found")]
|
#[error("data directory not found")]
|
||||||
@ -18,57 +21,51 @@ pub enum BookmarkError {
|
|||||||
|
|
||||||
/// Manages the bookmarks in the current player.
|
/// Manages the bookmarks in the current player.
|
||||||
pub struct Bookmarks {
|
pub struct Bookmarks {
|
||||||
|
/// The different entries in the bookmarks file.
|
||||||
entries: RwLock<Vec<String>>,
|
entries: RwLock<Vec<String>>,
|
||||||
file: RwLock<File>,
|
|
||||||
|
/// The internal bookmarked register, which keeps track
|
||||||
|
/// of whether a track is bookmarked or not.
|
||||||
|
///
|
||||||
|
/// This is much more efficient than checking every single frame.
|
||||||
bookmarked: AtomicBool,
|
bookmarked: AtomicBool,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Bookmarks {
|
impl Bookmarks {
|
||||||
pub async fn load() -> eyre::Result<Self, BookmarkError> {
|
/// Gets the path of the bookmarks file.
|
||||||
|
pub async fn path() -> eyre::Result<PathBuf, BookmarkError> {
|
||||||
let data_dir = data_dir().map_err(|_| BookmarkError::DataDir)?;
|
let data_dir = data_dir().map_err(|_| BookmarkError::DataDir)?;
|
||||||
create_dir_all(data_dir.clone()).await?;
|
fs::create_dir_all(data_dir.clone()).await?;
|
||||||
|
|
||||||
let mut file = OpenOptions::new()
|
Ok(data_dir.join("bookmarks.txt"))
|
||||||
.create(true)
|
}
|
||||||
.write(true)
|
/// Loads bookmarks from the `bookmarks.txt` file.
|
||||||
.read(true)
|
pub async fn load() -> eyre::Result<Self, BookmarkError> {
|
||||||
.append(false)
|
let text = fs::read_to_string(Self::path().await?).await?;
|
||||||
.truncate(false)
|
|
||||||
.open(data_dir.join("bookmarks.txt"))
|
|
||||||
.await?;
|
|
||||||
|
|
||||||
let mut text = String::new();
|
|
||||||
file.read_to_string(&mut text).await?;
|
|
||||||
|
|
||||||
let lines: Vec<String> = text
|
let lines: Vec<String> = text
|
||||||
.trim_start_matches("noheader")
|
.trim_start_matches("noheader")
|
||||||
.trim()
|
.trim()
|
||||||
.lines()
|
.lines()
|
||||||
.filter_map(|x| {
|
.filter_map(|x| {
|
||||||
if !x.is_empty() {
|
if x.is_empty() {
|
||||||
Some(x.to_string())
|
|
||||||
} else {
|
|
||||||
None
|
None
|
||||||
|
} else {
|
||||||
|
Some(x.to_string())
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.collect();
|
.collect();
|
||||||
|
|
||||||
Ok(Self {
|
Ok(Self {
|
||||||
entries: RwLock::new(lines),
|
entries: RwLock::new(lines),
|
||||||
file: RwLock::new(file),
|
|
||||||
bookmarked: AtomicBool::new(false),
|
bookmarked: AtomicBool::new(false),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Saves the bookmarks to the `bookmarks.txt` file.
|
||||||
pub async fn save(&self) -> eyre::Result<(), BookmarkError> {
|
pub async fn save(&self) -> eyre::Result<(), BookmarkError> {
|
||||||
let text = format!("noheader\n{}", self.entries.read().await.join("\n"));
|
let text = format!("noheader\n{}", self.entries.read().await.join("\n"));
|
||||||
|
fs::write(Self::path().await?, text).await?;
|
||||||
let mut lock = self.file.write().await;
|
|
||||||
lock.seek(SeekFrom::Start(0)).await?;
|
|
||||||
lock.set_len(0).await?;
|
|
||||||
lock.write_all(text.as_bytes()).await?;
|
|
||||||
lock.flush().await?;
|
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -91,10 +88,14 @@ impl Bookmarks {
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Returns whether a track is bookmarked or not by using the internal
|
||||||
|
/// bookmarked register.
|
||||||
pub fn bookmarked(&self) -> bool {
|
pub fn bookmarked(&self) -> bool {
|
||||||
self.bookmarked.load(std::sync::atomic::Ordering::Relaxed)
|
self.bookmarked.load(std::sync::atomic::Ordering::Relaxed)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Sets the internal bookmarked register by checking against
|
||||||
|
/// the current track's info.
|
||||||
pub async fn set_bookmarked(&self, track: &tracks::Info) {
|
pub async fn set_bookmarked(&self, track: &tracks::Info) {
|
||||||
let val = self.entries.read().await.contains(&track.to_entry());
|
let val = self.entries.read().await.contains(&track.to_entry());
|
||||||
self.bookmarked
|
self.bookmarked
|
||||||
|
@ -8,7 +8,7 @@ use tokio::{
|
|||||||
time::sleep,
|
time::sleep,
|
||||||
};
|
};
|
||||||
|
|
||||||
use super::{Player, TIMEOUT};
|
use super::Player;
|
||||||
|
|
||||||
/// This struct is responsible for downloading tracks in the background.
|
/// This struct is responsible for downloading tracks in the background.
|
||||||
///
|
///
|
||||||
@ -53,7 +53,7 @@ impl Downloader {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if !error.is_timeout() {
|
if !error.is_timeout() {
|
||||||
sleep(TIMEOUT).await;
|
sleep(self.player.timeout).await;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -6,7 +6,7 @@ use tokio::{sync::mpsc::Sender, time::sleep};
|
|||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
messages::Message,
|
messages::Message,
|
||||||
player::{downloader::Downloader, Player, TIMEOUT},
|
player::{downloader::Downloader, Player},
|
||||||
tracks,
|
tracks,
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -76,7 +76,7 @@ impl Player {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if !error.is_timeout() {
|
if !error.is_timeout() {
|
||||||
sleep(TIMEOUT).await;
|
sleep(player.timeout).await;
|
||||||
}
|
}
|
||||||
|
|
||||||
tx.send(Message::TryAgain).await?;
|
tx.send(Message::TryAgain).await?;
|
||||||
|
@ -159,8 +159,6 @@ impl Window {
|
|||||||
/// The code for the terminal interface itself.
|
/// The code for the terminal interface itself.
|
||||||
///
|
///
|
||||||
/// * `minimalist` - All this does is hide the bottom control bar.
|
/// * `minimalist` - All this does is hide the bottom control bar.
|
||||||
/// * `borderless` - Whether to include borders or not.
|
|
||||||
/// * `width` - The width of player
|
|
||||||
async fn interface(
|
async fn interface(
|
||||||
player: Arc<Player>,
|
player: Arc<Player>,
|
||||||
minimalist: bool,
|
minimalist: bool,
|
||||||
|
@ -87,7 +87,7 @@ impl ActionBar {
|
|||||||
Self::Muted => {
|
Self::Muted => {
|
||||||
let msg = "+ to increase volume";
|
let msg = "+ to increase volume";
|
||||||
|
|
||||||
("muted", Some((String::from(msg), msg.len())))
|
("muted,", Some((String::from(msg), msg.len())))
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -12,6 +12,7 @@ pub mod archive;
|
|||||||
pub mod chillhop;
|
pub mod chillhop;
|
||||||
pub mod lofigirl;
|
pub mod lofigirl;
|
||||||
|
|
||||||
|
/// Represents the different sources which can be scraped.
|
||||||
#[derive(Clone, Copy, PartialEq, Eq, Debug, ValueEnum)]
|
#[derive(Clone, Copy, PartialEq, Eq, Debug, ValueEnum)]
|
||||||
pub enum Source {
|
pub enum Source {
|
||||||
Lofigirl,
|
Lofigirl,
|
||||||
@ -20,6 +21,7 @@ pub enum Source {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl Source {
|
impl Source {
|
||||||
|
/// Gets the cache directory name, for example, `chillhop`.
|
||||||
pub fn cache_dir(&self) -> &'static str {
|
pub fn cache_dir(&self) -> &'static str {
|
||||||
match self {
|
match self {
|
||||||
Source::Lofigirl => "lofigirl",
|
Source::Lofigirl => "lofigirl",
|
||||||
@ -28,6 +30,7 @@ impl Source {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Gets the full root URL of the source.
|
||||||
pub fn url(&self) -> &'static str {
|
pub fn url(&self) -> &'static str {
|
||||||
match self {
|
match self {
|
||||||
Source::Chillhop => "https://chillhop.com",
|
Source::Chillhop => "https://chillhop.com",
|
||||||
|
@ -135,7 +135,7 @@ impl Info {
|
|||||||
.collect()
|
.collect()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Formats a name with [convert_case].
|
/// Formats a name with [`convert_case`].
|
||||||
///
|
///
|
||||||
/// 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 and do some other
|
/// usually present on most lofi tracks and do some other
|
||||||
|
@ -44,7 +44,7 @@ where
|
|||||||
Kind: From<E>,
|
Kind: From<E>,
|
||||||
{
|
{
|
||||||
fn from((track, err): (T, E)) -> Self {
|
fn from((track, err): (T, E)) -> Self {
|
||||||
Error {
|
Self {
|
||||||
track: track.into(),
|
track: track.into(),
|
||||||
kind: Kind::from(err),
|
kind: Kind::from(err),
|
||||||
}
|
}
|
||||||
|
@ -7,7 +7,6 @@ use atomic_float::AtomicF32;
|
|||||||
use bytes::{BufMut, Bytes, BytesMut};
|
use bytes::{BufMut, Bytes, BytesMut};
|
||||||
use eyre::OptionExt as _;
|
use eyre::OptionExt as _;
|
||||||
use futures::StreamExt;
|
use futures::StreamExt;
|
||||||
use rand::Rng as _;
|
|
||||||
use reqwest::Client;
|
use reqwest::Client;
|
||||||
use tokio::fs;
|
use tokio::fs;
|
||||||
|
|
||||||
@ -52,7 +51,7 @@ impl List {
|
|||||||
// We're also not pre-trimming `self.lines` into `base` & `tracks` due to
|
// We're also not pre-trimming `self.lines` into `base` & `tracks` due to
|
||||||
// how rust vectors work, since it is slower to drain only a single element from
|
// how rust vectors work, since it is slower to drain only a single element from
|
||||||
// the start, so it's faster to just keep it in & work around it.
|
// the start, so it's faster to just keep it in & work around it.
|
||||||
let random = rand::thread_rng().gen_range(1..self.lines.len());
|
let random = fastrand::usize(1..self.lines.len());
|
||||||
let line = self.lines[random].clone();
|
let line = self.lines[random].clone();
|
||||||
|
|
||||||
if let Some((first, second)) = line.split_once('!') {
|
if let Some((first, second)) = line.split_once('!') {
|
||||||
@ -134,7 +133,7 @@ impl List {
|
|||||||
|
|
||||||
let name = custom_name.map_or_else(
|
let name = custom_name.map_or_else(
|
||||||
|| super::TrackName::Raw(path.clone()),
|
|| super::TrackName::Raw(path.clone()),
|
||||||
|formatted| super::TrackName::Formatted(formatted),
|
super::TrackName::Formatted,
|
||||||
);
|
);
|
||||||
|
|
||||||
Ok(QueuedTrack {
|
Ok(QueuedTrack {
|
||||||
@ -154,7 +153,7 @@ impl List {
|
|||||||
|
|
||||||
Self {
|
Self {
|
||||||
lines,
|
lines,
|
||||||
path: path.map(|s| s.to_owned()),
|
path: path.map(ToOwned::to_owned),
|
||||||
name: name.to_owned(),
|
name: name.to_owned(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -169,11 +168,9 @@ impl List {
|
|||||||
let raw = fs::read_to_string(path.clone()).await?;
|
let raw = fs::read_to_string(path.clone()).await?;
|
||||||
|
|
||||||
// Get rid of special noheader case for tracklists without a header.
|
// Get rid of special noheader case for tracklists without a header.
|
||||||
let raw = if let Some(stripped) = raw.strip_prefix("noheader") {
|
let raw = raw
|
||||||
stripped
|
.strip_prefix("noheader")
|
||||||
} else {
|
.map_or(raw.as_ref(), |stripped| stripped);
|
||||||
&raw
|
|
||||||
};
|
|
||||||
|
|
||||||
let name = path
|
let name = path
|
||||||
.file_stem()
|
.file_stem()
|
||||||
|
Loading…
x
Reference in New Issue
Block a user