mirror of
https://github.com/csd4ni3l/soundboard.git
synced 2026-04-17 16:07:22 +02:00
Compare commits
18 Commits
6480c80cb4
...
main
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
ff947b57a8 | ||
|
|
8d21c268c2 | ||
|
|
dff8d5aeff | ||
|
|
4128566048 | ||
|
|
8d036bca08 | ||
|
|
9d3d027e45 | ||
|
|
4a21f60ee6 | ||
|
|
b241117813 | ||
|
|
dcf121c045 | ||
|
|
e7fbf8f4d8 | ||
|
|
815663b039 | ||
|
|
0ae204ed0f | ||
|
|
d78c3c22c9 | ||
|
|
5a4ebe3467 | ||
|
|
07c0457701 | ||
|
|
58deb34135 | ||
|
|
ff71f6f21a | ||
|
|
ca6fc96f99 |
1
.gitignore
vendored
1
.gitignore
vendored
@@ -1,2 +1,3 @@
|
|||||||
/target
|
/target
|
||||||
data.json
|
data.json
|
||||||
|
/bin
|
||||||
693
Cargo.lock
generated
693
Cargo.lock
generated
@@ -88,7 +88,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||||||
checksum = "5a15f179cd60c4584b8a8c596927aadc462e27f2ca70c04e0071964a73ba7a75"
|
checksum = "5a15f179cd60c4584b8a8c596927aadc462e27f2ca70c04e0071964a73ba7a75"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"cfg-if",
|
"cfg-if",
|
||||||
"getrandom",
|
"getrandom 0.3.4",
|
||||||
"once_cell",
|
"once_cell",
|
||||||
"version_check",
|
"version_check",
|
||||||
"zerocopy",
|
"zerocopy",
|
||||||
@@ -437,6 +437,28 @@ version = "1.5.0"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "c08606f8c3cbf4ce6ec8e28fb0014a2c086708fe954eaa885384a6165172e7e8"
|
checksum = "c08606f8c3cbf4ce6ec8e28fb0014a2c086708fe954eaa885384a6165172e7e8"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "aws-lc-rs"
|
||||||
|
version = "1.15.4"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "7b7b6141e96a8c160799cc2d5adecd5cbbe5054cb8c7c4af53da0f83bb7ad256"
|
||||||
|
dependencies = [
|
||||||
|
"aws-lc-sys",
|
||||||
|
"zeroize",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "aws-lc-sys"
|
||||||
|
version = "0.37.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "b092fe214090261288111db7a2b2c2118e5a7f30dc2569f1732c4069a6840549"
|
||||||
|
dependencies = [
|
||||||
|
"cc",
|
||||||
|
"cmake",
|
||||||
|
"dunce",
|
||||||
|
"fs_extra",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "base64"
|
name = "base64"
|
||||||
version = "0.22.1"
|
version = "0.22.1"
|
||||||
@@ -722,7 +744,7 @@ dependencies = [
|
|||||||
"crossbeam-channel",
|
"crossbeam-channel",
|
||||||
"egui",
|
"egui",
|
||||||
"encase",
|
"encase",
|
||||||
"getrandom",
|
"getrandom 0.3.4",
|
||||||
"image",
|
"image",
|
||||||
"itertools",
|
"itertools",
|
||||||
"js-sys",
|
"js-sys",
|
||||||
@@ -1023,7 +1045,7 @@ dependencies = [
|
|||||||
"critical-section",
|
"critical-section",
|
||||||
"foldhash 0.2.0",
|
"foldhash 0.2.0",
|
||||||
"futures-channel",
|
"futures-channel",
|
||||||
"getrandom",
|
"getrandom 0.3.4",
|
||||||
"hashbrown 0.16.1",
|
"hashbrown 0.16.1",
|
||||||
"js-sys",
|
"js-sys",
|
||||||
"portable-atomic",
|
"portable-atomic",
|
||||||
@@ -1630,6 +1652,15 @@ dependencies = [
|
|||||||
"error-code",
|
"error-code",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "cmake"
|
||||||
|
version = "0.1.57"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "75443c44cd6b379beb8c5b45d85d0773baf31cce901fe7bb252f4eff3008ef7d"
|
||||||
|
dependencies = [
|
||||||
|
"cc",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "codespan-reporting"
|
name = "codespan-reporting"
|
||||||
version = "0.12.0"
|
version = "0.12.0"
|
||||||
@@ -1801,7 +1832,7 @@ dependencies = [
|
|||||||
"fontdb",
|
"fontdb",
|
||||||
"log",
|
"log",
|
||||||
"rangemap",
|
"rangemap",
|
||||||
"rustc-hash",
|
"rustc-hash 1.1.0",
|
||||||
"rustybuzz",
|
"rustybuzz",
|
||||||
"self_cell",
|
"self_cell",
|
||||||
"smol_str",
|
"smol_str",
|
||||||
@@ -2008,6 +2039,12 @@ version = "0.1.2"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "d8b14ccef22fc6f5a8f4d7d768562a182c04ce9a3b3157b91390b52ddfdf1a76"
|
checksum = "d8b14ccef22fc6f5a8f4d7d768562a182c04ce9a3b3157b91390b52ddfdf1a76"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "dunce"
|
||||||
|
version = "1.0.5"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "92773504d58c093f6de2459af4af33faa518c13451eb8f2b5698ed3d36e7c813"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "ecolor"
|
name = "ecolor"
|
||||||
version = "0.33.3"
|
version = "0.33.3"
|
||||||
@@ -2268,6 +2305,12 @@ dependencies = [
|
|||||||
"miniz_oxide",
|
"miniz_oxide",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "fnv"
|
||||||
|
version = "1.0.7"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "foldhash"
|
name = "foldhash"
|
||||||
version = "0.1.5"
|
version = "0.1.5"
|
||||||
@@ -2348,6 +2391,12 @@ dependencies = [
|
|||||||
"percent-encoding",
|
"percent-encoding",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "fs_extra"
|
||||||
|
version = "1.3.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "42703706b716c37f96a77aea830392ad231f44c9e9a67872fa5548707e11b11c"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "futures-channel"
|
name = "futures-channel"
|
||||||
version = "0.3.31"
|
version = "0.3.31"
|
||||||
@@ -2355,6 +2404,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||||||
checksum = "2dff15bf788c671c1934e366d07e30c1814a8ef514e1af724a602e8a2fbe1b10"
|
checksum = "2dff15bf788c671c1934e366d07e30c1814a8ef514e1af724a602e8a2fbe1b10"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"futures-core",
|
"futures-core",
|
||||||
|
"futures-sink",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -2393,6 +2443,12 @@ dependencies = [
|
|||||||
"syn",
|
"syn",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "futures-sink"
|
||||||
|
version = "0.3.32"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "c39754e157331b013978ec91992bde1ac089843443c49cbc7f46150b0fad0893"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "futures-task"
|
name = "futures-task"
|
||||||
version = "0.3.31"
|
version = "0.3.31"
|
||||||
@@ -2408,6 +2464,7 @@ dependencies = [
|
|||||||
"futures-core",
|
"futures-core",
|
||||||
"futures-io",
|
"futures-io",
|
||||||
"futures-macro",
|
"futures-macro",
|
||||||
|
"futures-sink",
|
||||||
"futures-task",
|
"futures-task",
|
||||||
"memchr",
|
"memchr",
|
||||||
"pin-project-lite",
|
"pin-project-lite",
|
||||||
@@ -2425,6 +2482,19 @@ dependencies = [
|
|||||||
"windows-link 0.2.1",
|
"windows-link 0.2.1",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "getrandom"
|
||||||
|
version = "0.2.17"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "ff2abc00be7fca6ebc474524697ae276ad847ad0a6b3faa4bcb027e9a4614ad0"
|
||||||
|
dependencies = [
|
||||||
|
"cfg-if",
|
||||||
|
"js-sys",
|
||||||
|
"libc",
|
||||||
|
"wasi",
|
||||||
|
"wasm-bindgen",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "getrandom"
|
name = "getrandom"
|
||||||
version = "0.3.4"
|
version = "0.3.4"
|
||||||
@@ -2518,6 +2588,25 @@ dependencies = [
|
|||||||
"svg_fmt",
|
"svg_fmt",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "h2"
|
||||||
|
version = "0.4.13"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "2f44da3a8150a6703ed5d34e164b875fd14c2cdab9af1252a9a1020bde2bdc54"
|
||||||
|
dependencies = [
|
||||||
|
"atomic-waker",
|
||||||
|
"bytes",
|
||||||
|
"fnv",
|
||||||
|
"futures-core",
|
||||||
|
"futures-sink",
|
||||||
|
"http",
|
||||||
|
"indexmap",
|
||||||
|
"slab",
|
||||||
|
"tokio",
|
||||||
|
"tokio-util",
|
||||||
|
"tracing",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "half"
|
name = "half"
|
||||||
version = "2.7.1"
|
version = "2.7.1"
|
||||||
@@ -2599,6 +2688,108 @@ version = "0.2.1"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "dfa686283ad6dd069f105e5ab091b04c62850d3e4cf5d67debad1933f55023df"
|
checksum = "dfa686283ad6dd069f105e5ab091b04c62850d3e4cf5d67debad1933f55023df"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "http"
|
||||||
|
version = "1.4.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "e3ba2a386d7f85a81f119ad7498ebe444d2e22c2af0b86b069416ace48b3311a"
|
||||||
|
dependencies = [
|
||||||
|
"bytes",
|
||||||
|
"itoa",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "http-body"
|
||||||
|
version = "1.0.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "1efedce1fb8e6913f23e0c92de8e62cd5b772a67e7b3946df930a62566c93184"
|
||||||
|
dependencies = [
|
||||||
|
"bytes",
|
||||||
|
"http",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "http-body-util"
|
||||||
|
version = "0.1.3"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "b021d93e26becf5dc7e1b75b1bed1fd93124b374ceb73f43d4d4eafec896a64a"
|
||||||
|
dependencies = [
|
||||||
|
"bytes",
|
||||||
|
"futures-core",
|
||||||
|
"http",
|
||||||
|
"http-body",
|
||||||
|
"pin-project-lite",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "httparse"
|
||||||
|
version = "1.10.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "6dbf3de79e51f3d586ab4cb9d5c3e2c14aa28ed23d180cf89b4df0454a69cc87"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "hyper"
|
||||||
|
version = "1.8.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "2ab2d4f250c3d7b1c9fcdff1cece94ea4e2dfbec68614f7b87cb205f24ca9d11"
|
||||||
|
dependencies = [
|
||||||
|
"atomic-waker",
|
||||||
|
"bytes",
|
||||||
|
"futures-channel",
|
||||||
|
"futures-core",
|
||||||
|
"h2",
|
||||||
|
"http",
|
||||||
|
"http-body",
|
||||||
|
"httparse",
|
||||||
|
"itoa",
|
||||||
|
"pin-project-lite",
|
||||||
|
"pin-utils",
|
||||||
|
"smallvec",
|
||||||
|
"tokio",
|
||||||
|
"want",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "hyper-rustls"
|
||||||
|
version = "0.27.7"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "e3c93eb611681b207e1fe55d5a71ecf91572ec8a6705cdb6857f7d8d5242cf58"
|
||||||
|
dependencies = [
|
||||||
|
"http",
|
||||||
|
"hyper",
|
||||||
|
"hyper-util",
|
||||||
|
"rustls",
|
||||||
|
"rustls-pki-types",
|
||||||
|
"tokio",
|
||||||
|
"tokio-rustls",
|
||||||
|
"tower-service",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "hyper-util"
|
||||||
|
version = "0.1.20"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "96547c2556ec9d12fb1578c4eaf448b04993e7fb79cbaad930a656880a6bdfa0"
|
||||||
|
dependencies = [
|
||||||
|
"base64",
|
||||||
|
"bytes",
|
||||||
|
"futures-channel",
|
||||||
|
"futures-util",
|
||||||
|
"http",
|
||||||
|
"http-body",
|
||||||
|
"hyper",
|
||||||
|
"ipnet",
|
||||||
|
"libc",
|
||||||
|
"percent-encoding",
|
||||||
|
"pin-project-lite",
|
||||||
|
"socket2",
|
||||||
|
"system-configuration",
|
||||||
|
"tokio",
|
||||||
|
"tower-service",
|
||||||
|
"tracing",
|
||||||
|
"windows-registry",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "icu_collections"
|
name = "icu_collections"
|
||||||
version = "2.1.1"
|
version = "2.1.1"
|
||||||
@@ -2734,6 +2925,22 @@ dependencies = [
|
|||||||
"rustversion",
|
"rustversion",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "ipnet"
|
||||||
|
version = "2.11.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "469fb0b9cefa57e3ef31275ee7cacb78f2fdca44e4765491884a2b119d4eb130"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "iri-string"
|
||||||
|
version = "0.7.10"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "c91338f0783edbd6195decb37bae672fd3b165faffb89bf7b9e6942f8b1a731a"
|
||||||
|
dependencies = [
|
||||||
|
"memchr",
|
||||||
|
"serde",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "itertools"
|
name = "itertools"
|
||||||
version = "0.14.0"
|
version = "0.14.0"
|
||||||
@@ -2777,7 +2984,7 @@ version = "0.1.34"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "9afb3de4395d6b3e67a780b6de64b51c978ecf11cb9a462c66be7d4ca9039d33"
|
checksum = "9afb3de4395d6b3e67a780b6de64b51c978ecf11cb9a462c66be7d4ca9039d33"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"getrandom",
|
"getrandom 0.3.4",
|
||||||
"libc",
|
"libc",
|
||||||
]
|
]
|
||||||
|
|
||||||
@@ -2869,6 +3076,12 @@ version = "0.4.29"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "5e5032e24019045c762d3c0f28f5b6b8bbf38563a65908389bf7978758920897"
|
checksum = "5e5032e24019045c762d3c0f28f5b6b8bbf38563a65908389bf7978758920897"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "lru-slab"
|
||||||
|
version = "0.1.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "112b39cec0b298b6c1999fee3e31427f74f676e4cb9879ed1a121b43661a4154"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "mach2"
|
name = "mach2"
|
||||||
version = "0.4.3"
|
version = "0.4.3"
|
||||||
@@ -2935,6 +3148,12 @@ dependencies = [
|
|||||||
"paste",
|
"paste",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "mime"
|
||||||
|
version = "0.3.17"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "6877bb514081ee2a7ff5ef9de3281f14a4dd4bceac4c09388074a6b5df8a139a"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "miniz_oxide"
|
name = "miniz_oxide"
|
||||||
version = "0.8.9"
|
version = "0.8.9"
|
||||||
@@ -2945,6 +3164,17 @@ dependencies = [
|
|||||||
"simd-adler32",
|
"simd-adler32",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "mio"
|
||||||
|
version = "1.1.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "a69bcab0ad47271a0234d9422b131806bf3968021e5dc9328caf2d4cd58557fc"
|
||||||
|
dependencies = [
|
||||||
|
"libc",
|
||||||
|
"wasi",
|
||||||
|
"windows-sys 0.61.2",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "moxcms"
|
name = "moxcms"
|
||||||
version = "0.7.11"
|
version = "0.7.11"
|
||||||
@@ -2976,7 +3206,7 @@ dependencies = [
|
|||||||
"num-traits",
|
"num-traits",
|
||||||
"once_cell",
|
"once_cell",
|
||||||
"pp-rs",
|
"pp-rs",
|
||||||
"rustc-hash",
|
"rustc-hash 1.1.0",
|
||||||
"spirv",
|
"spirv",
|
||||||
"thiserror 2.0.17",
|
"thiserror 2.0.17",
|
||||||
"unicode-ident",
|
"unicode-ident",
|
||||||
@@ -2993,7 +3223,7 @@ dependencies = [
|
|||||||
"indexmap",
|
"indexmap",
|
||||||
"naga",
|
"naga",
|
||||||
"regex",
|
"regex",
|
||||||
"rustc-hash",
|
"rustc-hash 1.1.0",
|
||||||
"thiserror 2.0.17",
|
"thiserror 2.0.17",
|
||||||
"tracing",
|
"tracing",
|
||||||
"unicode-ident",
|
"unicode-ident",
|
||||||
@@ -3469,6 +3699,12 @@ version = "1.21.3"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "42f5e15c9953c5e4ccceeb2e7382a716482c34515315f7b03532b8b4e8393d2d"
|
checksum = "42f5e15c9953c5e4ccceeb2e7382a716482c34515315f7b03532b8b4e8393d2d"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "openssl-probe"
|
||||||
|
version = "0.2.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "7c87def4c32ab89d880effc9e097653c8da5d6ef28e6b539d313baaacfbafcbe"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "orbclient"
|
name = "orbclient"
|
||||||
version = "0.3.49"
|
version = "0.3.49"
|
||||||
@@ -3725,6 +3961,62 @@ dependencies = [
|
|||||||
"memchr",
|
"memchr",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "quinn"
|
||||||
|
version = "0.11.9"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "b9e20a958963c291dc322d98411f541009df2ced7b5a4f2bd52337638cfccf20"
|
||||||
|
dependencies = [
|
||||||
|
"bytes",
|
||||||
|
"cfg_aliases",
|
||||||
|
"pin-project-lite",
|
||||||
|
"quinn-proto",
|
||||||
|
"quinn-udp",
|
||||||
|
"rustc-hash 2.1.1",
|
||||||
|
"rustls",
|
||||||
|
"socket2",
|
||||||
|
"thiserror 2.0.17",
|
||||||
|
"tokio",
|
||||||
|
"tracing",
|
||||||
|
"web-time",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "quinn-proto"
|
||||||
|
version = "0.11.13"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "f1906b49b0c3bc04b5fe5d86a77925ae6524a19b816ae38ce1e426255f1d8a31"
|
||||||
|
dependencies = [
|
||||||
|
"aws-lc-rs",
|
||||||
|
"bytes",
|
||||||
|
"getrandom 0.3.4",
|
||||||
|
"lru-slab",
|
||||||
|
"rand",
|
||||||
|
"ring",
|
||||||
|
"rustc-hash 2.1.1",
|
||||||
|
"rustls",
|
||||||
|
"rustls-pki-types",
|
||||||
|
"slab",
|
||||||
|
"thiserror 2.0.17",
|
||||||
|
"tinyvec",
|
||||||
|
"tracing",
|
||||||
|
"web-time",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "quinn-udp"
|
||||||
|
version = "0.5.14"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "addec6a0dcad8a8d96a771f815f0eaf55f9d1805756410b39f5fa81332574cbd"
|
||||||
|
dependencies = [
|
||||||
|
"cfg_aliases",
|
||||||
|
"libc",
|
||||||
|
"once_cell",
|
||||||
|
"socket2",
|
||||||
|
"tracing",
|
||||||
|
"windows-sys 0.60.2",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "quote"
|
name = "quote"
|
||||||
version = "1.0.42"
|
version = "1.0.42"
|
||||||
@@ -3772,7 +4064,7 @@ version = "0.9.3"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "99d9a13982dcf210057a8a78572b2217b667c3beacbf3a0d8b454f6f82837d38"
|
checksum = "99d9a13982dcf210057a8a78572b2217b667c3beacbf3a0d8b454f6f82837d38"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"getrandom",
|
"getrandom 0.3.4",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -3881,6 +4173,46 @@ version = "1.1.0"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "19b30a45b0cd0bcca8037f3d0dc3421eaf95327a17cad11964fb8179b4fc4832"
|
checksum = "19b30a45b0cd0bcca8037f3d0dc3421eaf95327a17cad11964fb8179b4fc4832"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "reqwest"
|
||||||
|
version = "0.13.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "ab3f43e3283ab1488b624b44b0e988d0acea0b3214e694730a055cb6b2efa801"
|
||||||
|
dependencies = [
|
||||||
|
"base64",
|
||||||
|
"bytes",
|
||||||
|
"encoding_rs",
|
||||||
|
"futures-channel",
|
||||||
|
"futures-core",
|
||||||
|
"futures-util",
|
||||||
|
"h2",
|
||||||
|
"http",
|
||||||
|
"http-body",
|
||||||
|
"http-body-util",
|
||||||
|
"hyper",
|
||||||
|
"hyper-rustls",
|
||||||
|
"hyper-util",
|
||||||
|
"js-sys",
|
||||||
|
"log",
|
||||||
|
"mime",
|
||||||
|
"percent-encoding",
|
||||||
|
"pin-project-lite",
|
||||||
|
"quinn",
|
||||||
|
"rustls",
|
||||||
|
"rustls-pki-types",
|
||||||
|
"rustls-platform-verifier",
|
||||||
|
"sync_wrapper",
|
||||||
|
"tokio",
|
||||||
|
"tokio-rustls",
|
||||||
|
"tower",
|
||||||
|
"tower-http",
|
||||||
|
"tower-service",
|
||||||
|
"url",
|
||||||
|
"wasm-bindgen",
|
||||||
|
"wasm-bindgen-futures",
|
||||||
|
"web-sys",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "rfd"
|
name = "rfd"
|
||||||
version = "0.16.0"
|
version = "0.16.0"
|
||||||
@@ -3905,6 +4237,31 @@ dependencies = [
|
|||||||
"windows-sys 0.60.2",
|
"windows-sys 0.60.2",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "ring"
|
||||||
|
version = "0.17.14"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "a4689e6c2294d81e88dc6261c768b63bc4fcdb852be6d1352498b114f61383b7"
|
||||||
|
dependencies = [
|
||||||
|
"cc",
|
||||||
|
"cfg-if",
|
||||||
|
"getrandom 0.2.17",
|
||||||
|
"libc",
|
||||||
|
"untrusted",
|
||||||
|
"windows-sys 0.52.0",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "ringbuf"
|
||||||
|
version = "0.4.8"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "fe47b720588c8702e34b5979cb3271a8b1842c7cb6f57408efa70c779363488c"
|
||||||
|
dependencies = [
|
||||||
|
"crossbeam-utils",
|
||||||
|
"portable-atomic",
|
||||||
|
"portable-atomic-util",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "rodio"
|
name = "rodio"
|
||||||
version = "0.21.1"
|
version = "0.21.1"
|
||||||
@@ -3942,6 +4299,12 @@ version = "1.1.0"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2"
|
checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "rustc-hash"
|
||||||
|
version = "2.1.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "357703d41365b4b27c590e3ed91eabb1b663f07c4c084095e60cbed4362dff0d"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "rustc_version"
|
name = "rustc_version"
|
||||||
version = "0.4.1"
|
version = "0.4.1"
|
||||||
@@ -3977,6 +4340,81 @@ dependencies = [
|
|||||||
"windows-sys 0.61.2",
|
"windows-sys 0.61.2",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "rustls"
|
||||||
|
version = "0.23.36"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "c665f33d38cea657d9614f766881e4d510e0eda4239891eea56b4cadcf01801b"
|
||||||
|
dependencies = [
|
||||||
|
"aws-lc-rs",
|
||||||
|
"once_cell",
|
||||||
|
"rustls-pki-types",
|
||||||
|
"rustls-webpki",
|
||||||
|
"subtle",
|
||||||
|
"zeroize",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "rustls-native-certs"
|
||||||
|
version = "0.8.3"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "612460d5f7bea540c490b2b6395d8e34a953e52b491accd6c86c8164c5932a63"
|
||||||
|
dependencies = [
|
||||||
|
"openssl-probe",
|
||||||
|
"rustls-pki-types",
|
||||||
|
"schannel",
|
||||||
|
"security-framework",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "rustls-pki-types"
|
||||||
|
version = "1.14.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "be040f8b0a225e40375822a563fa9524378b9d63112f53e19ffff34df5d33fdd"
|
||||||
|
dependencies = [
|
||||||
|
"web-time",
|
||||||
|
"zeroize",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "rustls-platform-verifier"
|
||||||
|
version = "0.6.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "1d99feebc72bae7ab76ba994bb5e121b8d83d910ca40b36e0921f53becc41784"
|
||||||
|
dependencies = [
|
||||||
|
"core-foundation 0.10.1",
|
||||||
|
"core-foundation-sys",
|
||||||
|
"jni",
|
||||||
|
"log",
|
||||||
|
"once_cell",
|
||||||
|
"rustls",
|
||||||
|
"rustls-native-certs",
|
||||||
|
"rustls-platform-verifier-android",
|
||||||
|
"rustls-webpki",
|
||||||
|
"security-framework",
|
||||||
|
"security-framework-sys",
|
||||||
|
"webpki-root-certs",
|
||||||
|
"windows-sys 0.61.2",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "rustls-platform-verifier-android"
|
||||||
|
version = "0.1.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "f87165f0995f63a9fbeea62b64d10b4d9d8e78ec6d7d51fb2125fda7bb36788f"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "rustls-webpki"
|
||||||
|
version = "0.103.9"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "d7df23109aa6c1567d1c575b9952556388da57401e4ace1d15f79eedad0d8f53"
|
||||||
|
dependencies = [
|
||||||
|
"aws-lc-rs",
|
||||||
|
"ring",
|
||||||
|
"rustls-pki-types",
|
||||||
|
"untrusted",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "rustversion"
|
name = "rustversion"
|
||||||
version = "1.0.22"
|
version = "1.0.22"
|
||||||
@@ -4024,6 +4462,15 @@ dependencies = [
|
|||||||
"winapi-util",
|
"winapi-util",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "schannel"
|
||||||
|
version = "0.1.28"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "891d81b926048e76efe18581bf793546b4c0eaf8448d72be8de2bbee5fd166e1"
|
||||||
|
dependencies = [
|
||||||
|
"windows-sys 0.61.2",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "scoped-tls"
|
name = "scoped-tls"
|
||||||
version = "1.0.1"
|
version = "1.0.1"
|
||||||
@@ -4049,6 +4496,29 @@ dependencies = [
|
|||||||
"tiny-skia",
|
"tiny-skia",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "security-framework"
|
||||||
|
version = "3.6.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "d17b898a6d6948c3a8ee4372c17cb384f90d2e6e912ef00895b14fd7ab54ec38"
|
||||||
|
dependencies = [
|
||||||
|
"bitflags 2.10.0",
|
||||||
|
"core-foundation 0.10.1",
|
||||||
|
"core-foundation-sys",
|
||||||
|
"libc",
|
||||||
|
"security-framework-sys",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "security-framework-sys"
|
||||||
|
version = "2.16.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "321c8673b092a9a42605034a9879d73cb79101ed5fd117bc9a597b89b4e9e61a"
|
||||||
|
dependencies = [
|
||||||
|
"core-foundation-sys",
|
||||||
|
"libc",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "self_cell"
|
name = "self_cell"
|
||||||
version = "1.2.1"
|
version = "1.2.1"
|
||||||
@@ -4216,6 +4686,16 @@ dependencies = [
|
|||||||
"serde",
|
"serde",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "socket2"
|
||||||
|
version = "0.6.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "86f4aa3ad99f2088c990dfa82d367e19cb29268ed67c574d10d0a4bfe71f07e0"
|
||||||
|
dependencies = [
|
||||||
|
"libc",
|
||||||
|
"windows-sys 0.60.2",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "soundboard"
|
name = "soundboard"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
@@ -4223,7 +4703,9 @@ dependencies = [
|
|||||||
"bevy",
|
"bevy",
|
||||||
"bevy_egui",
|
"bevy_egui",
|
||||||
"rand",
|
"rand",
|
||||||
|
"reqwest",
|
||||||
"rfd",
|
"rfd",
|
||||||
|
"ringbuf",
|
||||||
"rodio",
|
"rodio",
|
||||||
"serde",
|
"serde",
|
||||||
"serde_json",
|
"serde_json",
|
||||||
@@ -4274,6 +4756,12 @@ version = "0.1.1"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "6637bab7722d379c8b41ba849228d680cc12d0a45ba1fa2b48f2a30577a06731"
|
checksum = "6637bab7722d379c8b41ba849228d680cc12d0a45ba1fa2b48f2a30577a06731"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "subtle"
|
||||||
|
version = "2.6.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "svg_fmt"
|
name = "svg_fmt"
|
||||||
version = "0.4.5"
|
version = "0.4.5"
|
||||||
@@ -4449,6 +4937,15 @@ dependencies = [
|
|||||||
"unicode-ident",
|
"unicode-ident",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "sync_wrapper"
|
||||||
|
version = "1.0.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "0bf256ce5efdfa370213c1dabab5935a12e49f2c58d15e9eac2870d3b4f27263"
|
||||||
|
dependencies = [
|
||||||
|
"futures-core",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "synstructure"
|
name = "synstructure"
|
||||||
version = "0.13.2"
|
version = "0.13.2"
|
||||||
@@ -4469,6 +4966,27 @@ dependencies = [
|
|||||||
"libc",
|
"libc",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "system-configuration"
|
||||||
|
version = "0.7.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "a13f3d0daba03132c0aa9767f98351b3488edc2c100cda2d2ec2b04f3d8d3c8b"
|
||||||
|
dependencies = [
|
||||||
|
"bitflags 2.10.0",
|
||||||
|
"core-foundation 0.9.4",
|
||||||
|
"system-configuration-sys",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "system-configuration-sys"
|
||||||
|
version = "0.6.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "8e1d1b10ced5ca923a1fcb8d03e96b8d3268065d724548c0211415ff6ac6bac4"
|
||||||
|
dependencies = [
|
||||||
|
"core-foundation-sys",
|
||||||
|
"libc",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "taffy"
|
name = "taffy"
|
||||||
version = "0.7.7"
|
version = "0.7.7"
|
||||||
@@ -4488,7 +5006,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||||||
checksum = "2d31c77bdf42a745371d260a26ca7163f1e0924b64afa0b688e61b5a9fa02f16"
|
checksum = "2d31c77bdf42a745371d260a26ca7163f1e0924b64afa0b688e61b5a9fa02f16"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"fastrand",
|
"fastrand",
|
||||||
"getrandom",
|
"getrandom 0.3.4",
|
||||||
"once_cell",
|
"once_cell",
|
||||||
"rustix 1.1.2",
|
"rustix 1.1.2",
|
||||||
"windows-sys 0.61.2",
|
"windows-sys 0.61.2",
|
||||||
@@ -4616,6 +5134,43 @@ version = "0.1.1"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20"
|
checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "tokio"
|
||||||
|
version = "1.49.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "72a2903cd7736441aac9df9d7688bd0ce48edccaadf181c3b90be801e81d3d86"
|
||||||
|
dependencies = [
|
||||||
|
"bytes",
|
||||||
|
"libc",
|
||||||
|
"mio",
|
||||||
|
"pin-project-lite",
|
||||||
|
"socket2",
|
||||||
|
"windows-sys 0.61.2",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "tokio-rustls"
|
||||||
|
version = "0.26.4"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "1729aa945f29d91ba541258c8df89027d5792d85a8841fb65e8bf0f4ede4ef61"
|
||||||
|
dependencies = [
|
||||||
|
"rustls",
|
||||||
|
"tokio",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "tokio-util"
|
||||||
|
version = "0.7.18"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "9ae9cec805b01e8fc3fd2fe289f89149a9b66dd16786abd8b19cfa7b48cb0098"
|
||||||
|
dependencies = [
|
||||||
|
"bytes",
|
||||||
|
"futures-core",
|
||||||
|
"futures-sink",
|
||||||
|
"pin-project-lite",
|
||||||
|
"tokio",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "toml_datetime"
|
name = "toml_datetime"
|
||||||
version = "0.7.5+spec-1.1.0"
|
version = "0.7.5+spec-1.1.0"
|
||||||
@@ -4646,6 +5201,51 @@ dependencies = [
|
|||||||
"winnow",
|
"winnow",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "tower"
|
||||||
|
version = "0.5.3"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "ebe5ef63511595f1344e2d5cfa636d973292adc0eec1f0ad45fae9f0851ab1d4"
|
||||||
|
dependencies = [
|
||||||
|
"futures-core",
|
||||||
|
"futures-util",
|
||||||
|
"pin-project-lite",
|
||||||
|
"sync_wrapper",
|
||||||
|
"tokio",
|
||||||
|
"tower-layer",
|
||||||
|
"tower-service",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "tower-http"
|
||||||
|
version = "0.6.8"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "d4e6559d53cc268e5031cd8429d05415bc4cb4aefc4aa5d6cc35fbf5b924a1f8"
|
||||||
|
dependencies = [
|
||||||
|
"bitflags 2.10.0",
|
||||||
|
"bytes",
|
||||||
|
"futures-util",
|
||||||
|
"http",
|
||||||
|
"http-body",
|
||||||
|
"iri-string",
|
||||||
|
"pin-project-lite",
|
||||||
|
"tower",
|
||||||
|
"tower-layer",
|
||||||
|
"tower-service",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "tower-layer"
|
||||||
|
version = "0.3.3"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "121c2a6cda46980bb0fcd1647ffaf6cd3fc79a013de288782836f6df9c48780e"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "tower-service"
|
||||||
|
version = "0.3.3"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "8df9b6e13f2d32c91b9bd719c00d1958837bc7dec474d94952798cc8e69eeec3"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "tracing"
|
name = "tracing"
|
||||||
version = "0.1.44"
|
version = "0.1.44"
|
||||||
@@ -4730,6 +5330,12 @@ dependencies = [
|
|||||||
"wasm-bindgen",
|
"wasm-bindgen",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "try-lock"
|
||||||
|
version = "0.2.5"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "e421abadd41a4225275504ea4d6566923418b7f05506fbc9c0fe86ba7396114b"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "ttf-parser"
|
name = "ttf-parser"
|
||||||
version = "0.20.0"
|
version = "0.20.0"
|
||||||
@@ -4837,6 +5443,12 @@ version = "0.2.6"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "ebc1c04c71510c7f702b52b7c350734c9ff1295c464a03335b00bb84fc54f853"
|
checksum = "ebc1c04c71510c7f702b52b7c350734c9ff1295c464a03335b00bb84fc54f853"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "untrusted"
|
||||||
|
version = "0.9.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "8ecb6da28b8a351d773b68d5825ac39017e680750f980f3a1a85cd8dd28a47c1"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "url"
|
name = "url"
|
||||||
version = "2.5.7"
|
version = "2.5.7"
|
||||||
@@ -4867,7 +5479,7 @@ version = "1.19.0"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "e2e054861b4bd027cd373e18e8d8d8e6548085000e41290d95ce0c373a654b4a"
|
checksum = "e2e054861b4bd027cd373e18e8d8d8e6548085000e41290d95ce0c373a654b4a"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"getrandom",
|
"getrandom 0.3.4",
|
||||||
"js-sys",
|
"js-sys",
|
||||||
"serde_core",
|
"serde_core",
|
||||||
"wasm-bindgen",
|
"wasm-bindgen",
|
||||||
@@ -4906,6 +5518,21 @@ dependencies = [
|
|||||||
"winapi-util",
|
"winapi-util",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "want"
|
||||||
|
version = "0.3.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "bfa7760aed19e106de2c7c0b581b509f2f25d3dacaf737cb82ac61bc6d760b0e"
|
||||||
|
dependencies = [
|
||||||
|
"try-lock",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "wasi"
|
||||||
|
version = "0.11.1+wasi-snapshot-preview1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "ccf3ec651a847eb01de73ccad15eb7d99f80485de043efb2f370cd654f4ea44b"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "wasip2"
|
name = "wasip2"
|
||||||
version = "1.0.1+wasi-0.2.4"
|
version = "1.0.1+wasi-0.2.4"
|
||||||
@@ -5117,6 +5744,15 @@ dependencies = [
|
|||||||
"web-sys",
|
"web-sys",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "webpki-root-certs"
|
||||||
|
version = "1.0.6"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "804f18a4ac2676ffb4e8b5b5fa9ae38af06df08162314f96a68d2a363e21a8ca"
|
||||||
|
dependencies = [
|
||||||
|
"rustls-pki-types",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "weezl"
|
name = "weezl"
|
||||||
version = "0.1.12"
|
version = "0.1.12"
|
||||||
@@ -5168,7 +5804,7 @@ dependencies = [
|
|||||||
"portable-atomic",
|
"portable-atomic",
|
||||||
"profiling",
|
"profiling",
|
||||||
"raw-window-handle",
|
"raw-window-handle",
|
||||||
"rustc-hash",
|
"rustc-hash 1.1.0",
|
||||||
"smallvec",
|
"smallvec",
|
||||||
"thiserror 2.0.17",
|
"thiserror 2.0.17",
|
||||||
"wgpu-core-deps-apple",
|
"wgpu-core-deps-apple",
|
||||||
@@ -5437,6 +6073,17 @@ dependencies = [
|
|||||||
"windows-link 0.1.3",
|
"windows-link 0.1.3",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "windows-registry"
|
||||||
|
version = "0.6.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "02752bf7fbdcce7f2a27a742f798510f3e5ad88dbe84871e5168e2120c3d5720"
|
||||||
|
dependencies = [
|
||||||
|
"windows-link 0.2.1",
|
||||||
|
"windows-result 0.4.1",
|
||||||
|
"windows-strings 0.5.1",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "windows-result"
|
name = "windows-result"
|
||||||
version = "0.1.2"
|
version = "0.1.2"
|
||||||
@@ -5464,6 +6111,15 @@ dependencies = [
|
|||||||
"windows-link 0.1.3",
|
"windows-link 0.1.3",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "windows-result"
|
||||||
|
version = "0.4.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "7781fa89eaf60850ac3d2da7af8e5242a5ea78d1a11c49bf2910bb5a73853eb5"
|
||||||
|
dependencies = [
|
||||||
|
"windows-link 0.2.1",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "windows-strings"
|
name = "windows-strings"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
@@ -5483,6 +6139,15 @@ dependencies = [
|
|||||||
"windows-link 0.1.3",
|
"windows-link 0.1.3",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "windows-strings"
|
||||||
|
version = "0.5.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "7837d08f69c77cf6b07689544538e017c1bfcf57e34b4c0ff58e6c2cd3b37091"
|
||||||
|
dependencies = [
|
||||||
|
"windows-link 0.2.1",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "windows-sys"
|
name = "windows-sys"
|
||||||
version = "0.45.0"
|
version = "0.45.0"
|
||||||
@@ -5990,6 +6655,12 @@ dependencies = [
|
|||||||
"synstructure",
|
"synstructure",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "zeroize"
|
||||||
|
version = "1.8.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "b97154e67e32c85465826e8bcc1c59429aaaf107c1e4a9e53c8d8ccd5eff88d0"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "zerotrie"
|
name = "zerotrie"
|
||||||
version = "0.2.3"
|
version = "0.2.3"
|
||||||
|
|||||||
@@ -6,7 +6,9 @@ edition = "2024"
|
|||||||
[dependencies]
|
[dependencies]
|
||||||
bevy_egui = "0.38.1"
|
bevy_egui = "0.38.1"
|
||||||
rand = "0.9.2"
|
rand = "0.9.2"
|
||||||
|
reqwest = { version = "0.13.2", features = ["blocking"] }
|
||||||
rfd = "0.16.0"
|
rfd = "0.16.0"
|
||||||
|
ringbuf = "0.4.8"
|
||||||
rodio = { version = "0.21.1", features = ["mp3", "wav", "flac", "vorbis"] }
|
rodio = { version = "0.21.1", features = ["mp3", "wav", "flac", "vorbis"] }
|
||||||
serde = "1.0.228"
|
serde = "1.0.228"
|
||||||
serde_json = "1.0.146"
|
serde_json = "1.0.146"
|
||||||
@@ -49,4 +51,4 @@ features = [
|
|||||||
"bevy_picking",
|
"bevy_picking",
|
||||||
"wayland",
|
"wayland",
|
||||||
"x11"
|
"x11"
|
||||||
]
|
]
|
||||||
|
|||||||
27
README.md
27
README.md
@@ -1,15 +1,18 @@
|
|||||||
Soundboard made in Rust & Bevy. My first Rust project.
|
# csd4ni3l Soundboard
|
||||||
|
|
||||||
# Support & Requirements
|
Cross-platform soundboard made in Rust & Bevy. My first Rust project.
|
||||||
- On all OSes, you need to still select the device inside the app you want to use it in.
|
You might ask, why? And my answer is why not? Also because i wanted to learn Rust and this was a good way.
|
||||||
|
|
||||||
## Linux
|
## Features & Requirements
|
||||||
- Needs the `mold` linker and `clang` to compile fast
|
|
||||||
- ALSA & PulseAudio/Pipewire-pulse is a requirement
|
|
||||||
|
|
||||||
## Windows
|
| Topic | Linux | Windows | MacOS & Other
|
||||||
- Needs the VB-Cable driver (https://vb-audio.com/Cable/)
|
| -------- | ------- | ------- | ------- |
|
||||||
|
| Requirements | ALSA & PulseAudio/Pipewire-pulse, optionally FFmpeg for youtube downloader | Needs the [VB-Cable driver](https://vb-audio.com/Cable), optionally FFmpeg for youtube downloader | Unknown (optionally FFmpeg for youtube downloader)|
|
||||||
## MacOS & Other
|
| Build Requirements | Rust, the `mold` linker and `clang` to compile fast | Rust, any C compiler | Unknown |
|
||||||
- Might work as a music player with the default output device.
|
| FFmpeg | Optionally for youtube downloader | Optional, Automatic install on Windows 11 (winget) | Optionally for youtube downloader |
|
||||||
- Not supported and not planned.
|
| Virtual Mic | Pulseaudio/Pipewire | VB-Cable | No |
|
||||||
|
| App Selection | Yes | No | No |
|
||||||
|
| Youtube Downloader support | Yes (ffmpeg required) | Yes (ffmpeg required) | Unknown (ffmpeg required) |
|
||||||
|
| Can others hear you? | Yes | Experimental | Unknown |
|
||||||
|
| Support | Best | Medium | None/Unknown |
|
||||||
|
| Download | [Download for Linux](https://github.com/csd4ni3l/soundboard/releases/download/latest/soundboard) | [Download for Windows](https://github.com/csd4ni3l/soundboard/releases/download/latest/soundboard.exe) | Build it yourself. |
|
||||||
182
src/linux_lib.rs
182
src/linux_lib.rs
@@ -1,7 +1,12 @@
|
|||||||
|
use rodio::{
|
||||||
use std::process::Command;
|
OutputStream, OutputStreamBuilder,
|
||||||
|
cpal::{self, traits::HostTrait},
|
||||||
|
};
|
||||||
use serde_json::Value;
|
use serde_json::Value;
|
||||||
use rodio::{OutputStream, OutputStreamBuilder, cpal::{self, traits::HostTrait}};
|
use std::process::Command;
|
||||||
|
|
||||||
|
const APPS_TO_EXCLUDE: [&str; 7] = ["plasmashell", "pavucontrol", "pipewire", "wireplumber", "kwin_wayland", "kwin_x11", "obs"];
|
||||||
|
const NODE_NAMES_TO_EXCLUDE: [&str; 2] = ["VirtualMicSource", "SoundboardSink"];
|
||||||
|
|
||||||
fn pactl_list(sink_type: &str) -> Value {
|
fn pactl_list(sink_type: &str) -> Value {
|
||||||
let command_output = Command::new("pactl")
|
let command_output = Command::new("pactl")
|
||||||
@@ -10,94 +15,167 @@ fn pactl_list(sink_type: &str) -> Value {
|
|||||||
.expect("Failed to execute process");
|
.expect("Failed to execute process");
|
||||||
|
|
||||||
if command_output.status.success() {
|
if command_output.status.success() {
|
||||||
serde_json::from_str(str::from_utf8(&command_output.stdout).expect("Failed to convert to string")).expect("Failed to parse sink JSON output")
|
serde_json::from_str(
|
||||||
}
|
str::from_utf8(&command_output.stdout).expect("Failed to convert to string"),
|
||||||
else {
|
)
|
||||||
Value::Null{}
|
.expect("Failed to parse sink JSON output")
|
||||||
|
} else {
|
||||||
|
Value::Null {}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_sink_by_index(sink_type: &str, index: String) -> Value {
|
pub fn get_soundboard_sink_index() -> String {
|
||||||
let sinks = pactl_list(sink_type);
|
let source_outputs = pactl_list("sinks");
|
||||||
|
source_outputs
|
||||||
|
.as_array()
|
||||||
|
.unwrap_or(&vec![])
|
||||||
|
.iter()
|
||||||
|
.find(|sink| sink["name"] == "SoundboardSink")
|
||||||
|
.and_then(|sink| {
|
||||||
|
Some(sink["index"].to_string())
|
||||||
|
})
|
||||||
|
.unwrap()
|
||||||
|
}
|
||||||
|
|
||||||
for sink in sinks.as_array().unwrap_or(&vec![]) {
|
pub fn get_default_source() -> String {
|
||||||
if sink["index"].as_u64().expect("sink index is not a number").to_string() == index {
|
let sources = pactl_list("sources");
|
||||||
return sink.clone();
|
|
||||||
}
|
let command = Command::new("pactl")
|
||||||
}
|
.args(&["get-default-source"])
|
||||||
|
.output()
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
return Value::Null{};
|
let default_source_name = String::from_utf8_lossy(&command.stdout).trim().to_string();
|
||||||
|
|
||||||
|
sources.as_array()
|
||||||
|
.unwrap()
|
||||||
|
.iter()
|
||||||
|
.find(|sink|{ sink["name"].as_str() == Some(&default_source_name) })
|
||||||
|
.and_then(|s|{ Some(s["index"].to_string()) })
|
||||||
|
.unwrap()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn find_soundboard_sinks() -> Vec<Value> {
|
fn find_soundboard_sinks() -> Vec<Value> {
|
||||||
let sink_inputs = pactl_list("sink-inputs");
|
let sink_inputs = pactl_list("sink-inputs");
|
||||||
sink_inputs.as_array()
|
sink_inputs
|
||||||
.unwrap_or(&vec![])
|
.as_array()
|
||||||
.iter()
|
.unwrap_or(&vec![])
|
||||||
.filter(|sink| {sink["properties"]["node.name"] == "alsa_playback.soundboard"})
|
.iter()
|
||||||
.cloned()
|
.filter(|sink| sink["properties"]["node.name"] == "alsa_playback.soundboard")
|
||||||
.collect()
|
.cloned()
|
||||||
}
|
.collect()
|
||||||
|
}
|
||||||
|
|
||||||
pub fn move_playback_to_sink() {
|
pub fn move_playback_to_sink() {
|
||||||
let soundboard_sinks = find_soundboard_sinks();
|
let soundboard_sinks = find_soundboard_sinks();
|
||||||
for sink in soundboard_sinks {
|
for sink in soundboard_sinks {
|
||||||
let index = sink["index"].as_u64().expect("sink index is not a number").to_string();
|
let index = sink["index"]
|
||||||
|
.as_u64()
|
||||||
|
.expect("sink index is not a number")
|
||||||
|
.to_string();
|
||||||
Command::new("pactl")
|
Command::new("pactl")
|
||||||
.args(&["move-sink-input", index.as_str(), "SoundboardSink"]) // as_str is needed here as you cannot instantly dereference a growing String (Rust...)
|
.args(&["move-sink-input", index.as_str(), "SoundboardSink"]) // as_str is needed here as you cannot instantly dereference a growing String (Rust...)
|
||||||
.output()
|
.output()
|
||||||
.expect("Failed to execute process");
|
.expect("Failed to execute process");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn list_outputs() -> Vec<(String, String)> {
|
pub fn list_outputs() -> Vec<(String, String)> {
|
||||||
let source_outputs = pactl_list("source-outputs");
|
let source_outputs = pactl_list("source-outputs");
|
||||||
return source_outputs.as_array().unwrap_or(&vec![]).iter().filter_map(|sink| {
|
return source_outputs
|
||||||
let app_name = sink["properties"]["application.name"].as_str()?;
|
.as_array()
|
||||||
let binary = sink["properties"]["application.process.binary"].as_str().unwrap_or("Unknown");
|
.unwrap_or(&vec![])
|
||||||
let index = sink["index"].as_u64().expect("sink index is not a number").to_string();
|
.iter()
|
||||||
Some((format!("{} ({})", app_name, binary), index))
|
.filter_map(|sink| {
|
||||||
}).collect();
|
let app_name = sink["properties"]["application.name"].as_str()?;
|
||||||
|
let node_name = sink["properties"]["node.name"].as_str()?;
|
||||||
|
let binary = sink["properties"]["application.process.binary"]
|
||||||
|
.as_str()
|
||||||
|
.unwrap_or("Unknown");
|
||||||
|
if APPS_TO_EXCLUDE.contains(&binary) || NODE_NAMES_TO_EXCLUDE.contains(&node_name) {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
let index = sink["index"]
|
||||||
|
.as_u64()
|
||||||
|
.expect("sink index is not a number")
|
||||||
|
.to_string();
|
||||||
|
Some((format!("{} ({})", app_name, binary), index))
|
||||||
|
})
|
||||||
|
.collect();
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn move_index_to_virtualmic(index: String) {
|
pub fn move_output_to_sink(output_index: String, sink_index: String) {
|
||||||
Command::new("pactl")
|
let _ = Command::new("pactl")
|
||||||
.args(&["move-source-output", index.as_str(), "VirtualMicSource"]) // as_str is needed here as you cannot instantly dereference a growing String (Rust...)
|
.args(&["move-source-output", output_index.as_str(), sink_index.as_str()]) // as_str is needed here as you cannot instantly dereference a growing String (Rust...)
|
||||||
.output()
|
.output()
|
||||||
.expect("Failed to execute process");
|
.expect("Failed to execute process");
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn create_virtual_mic_linux() -> OutputStream {
|
pub fn create_virtual_mic_linux() -> OutputStream {
|
||||||
Command::new("pactl")
|
Command::new("pactl")
|
||||||
.args(&["load-module", "module-null-sink", "sink_name=SoundboardSink", "sink_properties=device.description=\"Soundboard_Audio\""])
|
.args(&[
|
||||||
|
"load-module",
|
||||||
|
"module-null-sink",
|
||||||
|
"sink_name=SoundboardSink",
|
||||||
|
"sink_properties=device.description=\"Soundboard_Audio\"",
|
||||||
|
])
|
||||||
.output()
|
.output()
|
||||||
.expect("Failed to create SoundboardSink");
|
.expect("Failed to create SoundboardSink");
|
||||||
|
|
||||||
Command::new("pactl")
|
Command::new("pactl")
|
||||||
.args(&["load-module", "module-null-sink", "sink_name=VirtualMic", "sink_properties=device.description=\"Virtual_Microphone\""])
|
.args(&[
|
||||||
|
"load-module",
|
||||||
|
"module-null-sink",
|
||||||
|
"sink_name=VirtualMic",
|
||||||
|
"sink_properties=device.description=\"Virtual_Microphone\"",
|
||||||
|
])
|
||||||
.output()
|
.output()
|
||||||
.expect("Failed to create VirtualMic");
|
.expect("Failed to create VirtualMic");
|
||||||
|
|
||||||
Command::new("pactl")
|
Command::new("pactl")
|
||||||
.args(&["load-module", "module-remap-source", "master=VirtualMic.monitor", "source_name=VirtualMicSource", "source_properties=device.description=\"Virtual_Mic_Source\""])
|
.args(&[
|
||||||
|
"load-module",
|
||||||
|
"module-remap-source",
|
||||||
|
"master=VirtualMic.monitor",
|
||||||
|
"source_name=VirtualMicSource",
|
||||||
|
"source_properties=device.description=\"Virtual_Mic_Source\"",
|
||||||
|
])
|
||||||
.output()
|
.output()
|
||||||
.expect("Failed to create VirtualMicSource");
|
.expect("Failed to create VirtualMicSource");
|
||||||
|
|
||||||
// Soundboard audio -> speakers
|
// Soundboard audio -> speakers
|
||||||
Command::new("pactl")
|
Command::new("pactl")
|
||||||
.args(&["load-module", "module-loopback", "source=SoundboardSink.monitor", "sink=@DEFAULT_SINK@", "latency_msec=1"])
|
.args(&[
|
||||||
|
"load-module",
|
||||||
|
"module-loopback",
|
||||||
|
"source=SoundboardSink.monitor",
|
||||||
|
"sink=@DEFAULT_SINK@",
|
||||||
|
"latency_msec=1",
|
||||||
|
])
|
||||||
.output()
|
.output()
|
||||||
.expect("Failed to create soundboard to speakers loopback");
|
.expect("Failed to create soundboard to speakers loopback");
|
||||||
|
|
||||||
// Soundboard audio -> VirtualMic
|
// Soundboard audio -> VirtualMic
|
||||||
Command::new("pactl")
|
Command::new("pactl")
|
||||||
.args(&["load-module", "module-loopback", "source=SoundboardSink.monitor", "sink=VirtualMic", "latency_msec=1"])
|
.args(&[
|
||||||
|
"load-module",
|
||||||
|
"module-loopback",
|
||||||
|
"source=SoundboardSink.monitor",
|
||||||
|
"sink=VirtualMic",
|
||||||
|
"latency_msec=1",
|
||||||
|
])
|
||||||
.output()
|
.output()
|
||||||
.expect("Failed to create soundboard to VirtualMic loopback");
|
.expect("Failed to create soundboard to VirtualMic loopback");
|
||||||
|
|
||||||
// Microphone -> VirtualMic ONLY
|
// Microphone -> VirtualMic ONLY
|
||||||
Command::new("pactl")
|
Command::new("pactl")
|
||||||
.args(&["load-module", "module-loopback", "source=@DEFAULT_SOURCE@", "sink=VirtualMic", "latency_msec=1"])
|
.args(&[
|
||||||
|
"load-module",
|
||||||
|
"module-loopback",
|
||||||
|
"source=@DEFAULT_SOURCE@",
|
||||||
|
"sink=VirtualMic",
|
||||||
|
"latency_msec=1",
|
||||||
|
])
|
||||||
.output()
|
.output()
|
||||||
.expect("Failed to create microphone loopback");
|
.expect("Failed to create microphone loopback");
|
||||||
|
|
||||||
@@ -105,14 +183,16 @@ pub fn create_virtual_mic_linux() -> OutputStream {
|
|||||||
.args(&["set-sink-volume", "VirtualMic", "100%"])
|
.args(&["set-sink-volume", "VirtualMic", "100%"])
|
||||||
.output()
|
.output()
|
||||||
.expect("Failed to set volume");
|
.expect("Failed to set volume");
|
||||||
|
|
||||||
Command::new("pactl")
|
Command::new("pactl")
|
||||||
.args(&["set-sink-volume", "SoundboardSink", "100%"])
|
.args(&["set-sink-volume", "SoundboardSink", "100%"])
|
||||||
.output()
|
.output()
|
||||||
.expect("Failed to set soundboard volume");
|
.expect("Failed to set soundboard volume");
|
||||||
|
|
||||||
let host = cpal::host_from_id(cpal::HostId::Alsa).expect("Could not initialize ALSA");
|
let host = cpal::host_from_id(cpal::HostId::Alsa).expect("Could not initialize ALSA");
|
||||||
let device = host.default_output_device().expect("Could not get default output device");
|
let device = host
|
||||||
|
.default_output_device()
|
||||||
|
.expect("Could not get default output device");
|
||||||
|
|
||||||
let stream = OutputStreamBuilder::from_device(device)
|
let stream = OutputStreamBuilder::from_device(device)
|
||||||
.expect("Unable to open VirtualMic")
|
.expect("Unable to open VirtualMic")
|
||||||
@@ -143,4 +223,4 @@ pub fn reload_sound() {
|
|||||||
} else {
|
} else {
|
||||||
println!("Error: {}", String::from_utf8_lossy(&output.stderr));
|
println!("Error: {}", String::from_utf8_lossy(&output.stderr));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
425
src/main.rs
425
src/main.rs
@@ -1,12 +1,11 @@
|
|||||||
use bevy::{log::Level, prelude::*};
|
use bevy::{log::Level, prelude::*};
|
||||||
|
use bevy_egui::{EguiContextSettings, EguiContexts, EguiPrimaryContextPass, EguiStartupSet, egui::{self, Context, TextBuffer, Ui, ecolor::Color32}};
|
||||||
|
|
||||||
use std::{collections::HashMap, fs::File, io::BufReader, path::Path};
|
use std::{collections::HashMap, fs::{File, create_dir, exists, rename}, io::{BufReader, Read, Seek}, path::Path, process::{Command, Stdio}, sync::{Arc, Mutex}, thread, time::Instant};
|
||||||
|
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
use bevy_egui::{EguiContextSettings, EguiContexts, EguiPrimaryContextPass, EguiStartupSet, egui};
|
mod yt_dlp;
|
||||||
|
|
||||||
use egui::ecolor::Color32;
|
|
||||||
|
|
||||||
#[cfg(target_os = "linux")]
|
#[cfg(target_os = "linux")]
|
||||||
mod linux_lib;
|
mod linux_lib;
|
||||||
@@ -15,9 +14,12 @@ mod linux_lib;
|
|||||||
mod windows_lib;
|
mod windows_lib;
|
||||||
|
|
||||||
use rodio::{
|
use rodio::{
|
||||||
Decoder, OutputStream, Sink, Source, cpal::{self, traits::HostTrait}, OutputStreamBuilder
|
Decoder, OutputStream, OutputStreamBuilder, Sink, Source,
|
||||||
|
cpal::{self, traits::HostTrait},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
use crate::yt_dlp::*;
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize)]
|
#[derive(Serialize, Deserialize)]
|
||||||
struct JSONData {
|
struct JSONData {
|
||||||
tabs: Vec<String>,
|
tabs: Vec<String>,
|
||||||
@@ -28,15 +30,23 @@ struct PlayingSound {
|
|||||||
file_path: String,
|
file_path: String,
|
||||||
length: f32,
|
length: f32,
|
||||||
sink: Sink,
|
sink: Sink,
|
||||||
|
to_remove: bool,
|
||||||
#[cfg(target_os = "windows")]
|
#[cfg(target_os = "windows")]
|
||||||
normal_sink: Sink
|
normal_sink: Sink,
|
||||||
}
|
}
|
||||||
|
|
||||||
struct SoundSystem {
|
struct SoundSystem {
|
||||||
#[cfg(target_os = "windows")]
|
#[cfg(target_os = "windows")]
|
||||||
normal_output_stream: OutputStream,
|
normal_output_stream: OutputStream,
|
||||||
output_stream: OutputStream,
|
output_stream: OutputStream,
|
||||||
paused: bool,
|
}
|
||||||
|
|
||||||
|
struct YoutubeDownloaderState {
|
||||||
|
current_url: String,
|
||||||
|
current_filename: String,
|
||||||
|
download_directory: String,
|
||||||
|
yt_dlp_running: bool,
|
||||||
|
yt_dlp_stdout_text: Arc<Mutex<String>>
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Resource)]
|
#[derive(Resource)]
|
||||||
@@ -47,8 +57,10 @@ struct AppState {
|
|||||||
currently_playing: Vec<PlayingSound>,
|
currently_playing: Vec<PlayingSound>,
|
||||||
sound_system: SoundSystem,
|
sound_system: SoundSystem,
|
||||||
virt_outputs: Vec<(String, String)>,
|
virt_outputs: Vec<(String, String)>,
|
||||||
virt_output_index_switch: String,
|
is_virt_output_used: HashMap<String, bool>,
|
||||||
virt_output_index: String,
|
last_virt_output_update: Instant,
|
||||||
|
current_view: String,
|
||||||
|
youtube_downloader_state: YoutubeDownloaderState
|
||||||
}
|
}
|
||||||
|
|
||||||
const ALLOWED_FILE_EXTENSIONS: [&str; 4] = ["mp3", "wav", "flac", "ogg"];
|
const ALLOWED_FILE_EXTENSIONS: [&str; 4] = ["mp3", "wav", "flac", "ogg"];
|
||||||
@@ -60,7 +72,6 @@ fn create_virtual_mic() -> SoundSystem {
|
|||||||
return SoundSystem {
|
return SoundSystem {
|
||||||
output_stream: virtual_mic,
|
output_stream: virtual_mic,
|
||||||
normal_output_stream: normal,
|
normal_output_stream: normal,
|
||||||
paused: false,
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -68,20 +79,26 @@ fn create_virtual_mic() -> SoundSystem {
|
|||||||
{
|
{
|
||||||
return SoundSystem {
|
return SoundSystem {
|
||||||
output_stream: linux_lib::create_virtual_mic_linux(),
|
output_stream: linux_lib::create_virtual_mic_linux(),
|
||||||
paused: false,
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
#[allow(unreachable_code)]
|
#[allow(unreachable_code)]
|
||||||
{
|
{
|
||||||
let host = cpal::default_host();
|
let host = cpal::default_host();
|
||||||
let device = host.default_output_device().expect("Could not get default output device");
|
let device = host
|
||||||
|
.default_output_device()
|
||||||
|
.expect("Could not get default output device");
|
||||||
SoundSystem {
|
SoundSystem {
|
||||||
output_stream: OutputStreamBuilder::from_device(device)
|
output_stream: OutputStreamBuilder::from_device(device)
|
||||||
.expect("Unable to open device")
|
.expect("Unable to open device")
|
||||||
.open_stream()
|
.open_stream()
|
||||||
.expect("Failed to open stream"),
|
.expect("Failed to open stream"),
|
||||||
paused: false,
|
// this is actually not needed here, since windows would exit by far. But, cargo doesnt like SoundSystem not getting the normal_output stream so...
|
||||||
|
#[cfg(target_os = "windows")]
|
||||||
|
normal_output_stream: OutputStreamBuilder::from_device(device)
|
||||||
|
.expect("Unable to open device")
|
||||||
|
.open_stream()
|
||||||
|
.expect("Failed to open stream"),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -95,7 +112,7 @@ fn reload_sound() -> SoundSystem {
|
|||||||
|
|
||||||
fn list_outputs() -> Vec<(String, String)> {
|
fn list_outputs() -> Vec<(String, String)> {
|
||||||
#[cfg(target_os = "windows")]
|
#[cfg(target_os = "windows")]
|
||||||
return Vec::from([("Select in apps".to_string(), String::from("9999999"))]);
|
return Vec::from([("Select inside apps".to_string(), String::from("9999999"))]);
|
||||||
|
|
||||||
#[cfg(target_os = "linux")]
|
#[cfg(target_os = "linux")]
|
||||||
return linux_lib::list_outputs();
|
return linux_lib::list_outputs();
|
||||||
@@ -105,6 +122,13 @@ fn list_outputs() -> Vec<(String, String)> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
|
if !exists("bin").expect("Could not check existence of bin folder") {
|
||||||
|
let _ = create_dir("bin");
|
||||||
|
}
|
||||||
|
|
||||||
|
check_and_download_ffmpeg();
|
||||||
|
check_and_download_yt_dlp();
|
||||||
|
|
||||||
App::new()
|
App::new()
|
||||||
.insert_resource(ClearColor(Color::BLACK))
|
.insert_resource(ClearColor(Color::BLACK))
|
||||||
.add_plugins(
|
.add_plugins(
|
||||||
@@ -131,8 +155,16 @@ fn main() {
|
|||||||
currently_playing: Vec::new(),
|
currently_playing: Vec::new(),
|
||||||
sound_system: create_virtual_mic(),
|
sound_system: create_virtual_mic(),
|
||||||
virt_outputs: Vec::new(),
|
virt_outputs: Vec::new(),
|
||||||
virt_output_index_switch: String::from("0"),
|
is_virt_output_used: HashMap::new(),
|
||||||
virt_output_index: String::from("999"),
|
current_view: "main".to_string(),
|
||||||
|
last_virt_output_update: Instant::now(),
|
||||||
|
youtube_downloader_state: YoutubeDownloaderState {
|
||||||
|
current_url: String::new(),
|
||||||
|
current_filename: String::new(),
|
||||||
|
download_directory: String::new(),
|
||||||
|
yt_dlp_running: false,
|
||||||
|
yt_dlp_stdout_text: Arc::new(Mutex::new(String::new()))
|
||||||
|
}
|
||||||
})
|
})
|
||||||
.add_systems(
|
.add_systems(
|
||||||
PreStartup,
|
PreStartup,
|
||||||
@@ -141,28 +173,35 @@ fn main() {
|
|||||||
.add_systems(Startup, load_system)
|
.add_systems(Startup, load_system)
|
||||||
.add_systems(
|
.add_systems(
|
||||||
EguiPrimaryContextPass,
|
EguiPrimaryContextPass,
|
||||||
(ui_system, update_ui_scale_factor_system, update_virtualmic),
|
(draw, update_ui_scale_factor_system, update),
|
||||||
)
|
)
|
||||||
.run();
|
.run();
|
||||||
}
|
}
|
||||||
|
|
||||||
fn update_virtualmic(mut app_state: ResMut<AppState>) {
|
fn update(mut app_state: ResMut<AppState>) {
|
||||||
if app_state.virt_outputs.is_empty() {
|
#[cfg(target_os = "linux")] {
|
||||||
return;
|
if app_state.last_virt_output_update.elapsed().as_secs_f32() >= 1.5 {
|
||||||
}
|
app_state.last_virt_output_update = Instant::now();
|
||||||
|
app_state.virt_outputs = list_outputs();
|
||||||
|
let is_virt_output_used = app_state.is_virt_output_used.clone();
|
||||||
|
|
||||||
if app_state.virt_output_index != app_state.virt_output_index_switch {
|
for virt_output in &app_state.virt_outputs.clone() {
|
||||||
app_state.virt_output_index = app_state.virt_output_index_switch.clone();
|
if !is_virt_output_used.contains_key(&virt_output.1) {
|
||||||
#[cfg(target_os = "linux")]
|
app_state.is_virt_output_used.insert(virt_output.1.clone(), false);
|
||||||
linux_lib::move_index_to_virtualmic(app_state.virt_output_index_switch.clone());
|
}
|
||||||
|
|
||||||
|
if app_state.is_virt_output_used[&virt_output.1] {
|
||||||
|
linux_lib::move_output_to_sink(virt_output.1.clone(), linux_lib::get_soundboard_sink_index());
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
linux_lib::move_output_to_sink(virt_output.1.clone(), linux_lib::get_default_source());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn load_system(mut app_state: ResMut<AppState>) {
|
fn load_system(mut app_state: ResMut<AppState>) {
|
||||||
app_state.virt_outputs = list_outputs();
|
|
||||||
if !app_state.virt_outputs.is_empty() {
|
|
||||||
app_state.virt_output_index_switch = app_state.virt_outputs[0].1.clone();
|
|
||||||
}
|
|
||||||
load_data(&mut app_state);
|
load_data(&mut app_state);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -219,42 +258,84 @@ fn update_ui_scale_factor_system(egui_context: Single<(&mut EguiContextSettings,
|
|||||||
egui_settings.scale_factor = 1.5 / camera.target_scaling_factor().unwrap_or(1.5);
|
egui_settings.scale_factor = 1.5 / camera.target_scaling_factor().unwrap_or(1.5);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn get_duration<R>(decoder: &mut rodio::Decoder<R>) -> f32 // get_duration is needed cause some MP3 files dont provide duration metadata so we need to count
|
||||||
|
where
|
||||||
|
R: Read + Seek,
|
||||||
|
{
|
||||||
|
let mut total_samples: u32 = 0;
|
||||||
|
|
||||||
|
for _ in decoder.by_ref() {
|
||||||
|
total_samples += 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
let sample_rate = decoder.sample_rate() as u32;
|
||||||
|
let channels = decoder.channels() as u32;
|
||||||
|
|
||||||
|
total_samples as f32 / (sample_rate * channels) as f32
|
||||||
|
}
|
||||||
|
|
||||||
fn play_sound(file_path: String, app_state: &mut AppState) {
|
fn play_sound(file_path: String, app_state: &mut AppState) {
|
||||||
|
let file = File::open(&file_path).unwrap();
|
||||||
|
let mut src = Decoder::new(BufReader::new(file)).unwrap();
|
||||||
|
let length = get_duration(&mut src);
|
||||||
|
|
||||||
|
// need to recreate since get_duration seeks to the end and nothing is left
|
||||||
let file = File::open(&file_path).unwrap();
|
let file = File::open(&file_path).unwrap();
|
||||||
let src = Decoder::new(BufReader::new(file)).unwrap();
|
let src = Decoder::new(BufReader::new(file)).unwrap();
|
||||||
let sink = Sink::connect_new(&app_state.sound_system.output_stream.mixer());
|
let sink = Sink::connect_new(&app_state.sound_system.output_stream.mixer());
|
||||||
let length = src
|
|
||||||
.total_duration()
|
|
||||||
.expect("Could not get source duration")
|
|
||||||
.as_secs_f32();
|
|
||||||
sink.append(src);
|
sink.append(src);
|
||||||
|
|
||||||
#[cfg(target_os = "windows")]
|
|
||||||
{
|
|
||||||
let file2 = File::open(&file_path).unwrap();
|
|
||||||
let src2 = Decoder::new(BufReader::new(file2)).unwrap();
|
|
||||||
let normal_sink = Sink::connect_new(&app_state.sound_system.normal_output_stream.mixer());
|
|
||||||
sink2.append(src2);
|
|
||||||
}
|
|
||||||
|
|
||||||
sink.play();
|
sink.play();
|
||||||
|
|
||||||
app_state.currently_playing.push(PlayingSound {
|
let playing_sound = PlayingSound {
|
||||||
file_path: file_path.clone(),
|
file_path: file_path.clone(),
|
||||||
length,
|
length,
|
||||||
sink,
|
sink,
|
||||||
|
to_remove: false,
|
||||||
#[cfg(target_os = "windows")]
|
#[cfg(target_os = "windows")]
|
||||||
normal_sink
|
normal_sink: {
|
||||||
})
|
let file2 = File::open(&file_path).unwrap();
|
||||||
|
let src2 = Decoder::new(BufReader::new(file2)).unwrap();
|
||||||
|
let normal_sink =
|
||||||
|
Sink::connect_new(&app_state.sound_system.normal_output_stream.mixer());
|
||||||
|
normal_sink.append(src2);
|
||||||
|
normal_sink.play();
|
||||||
|
normal_sink
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
app_state.currently_playing.push(playing_sound);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn ui_system(mut contexts: EguiContexts, mut app_state: ResMut<AppState>) -> Result {
|
fn create_virtual_mic_ui(ui: &mut Ui, app_state: &mut ResMut<AppState>, available_width: f32, available_height: f32) {
|
||||||
let ctx = contexts.ctx_mut()?;
|
#[cfg(target_os = "linux")] {
|
||||||
|
if app_state.is_virt_output_used.len() != 0 {
|
||||||
|
let outputs = app_state.virt_outputs.clone();
|
||||||
|
for output in &outputs {
|
||||||
|
let current_value = *app_state.is_virt_output_used.get(&output.1).unwrap_or(&false);
|
||||||
|
if ui
|
||||||
|
.add_sized(
|
||||||
|
[available_width, available_height / 30.0],
|
||||||
|
egui::Button::new(format!("{} - {}", output.0.clone(), current_value)),
|
||||||
|
)
|
||||||
|
.clicked()
|
||||||
|
{
|
||||||
|
*app_state.is_virt_output_used.entry(output.1.clone()).or_insert(false) = !current_value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
ui.add(egui::Button::new("No apps found to use.".to_string()));
|
||||||
|
}
|
||||||
|
|
||||||
egui::TopBottomPanel::top("top_panel").show(ctx, |ui| {
|
return;
|
||||||
ui.heading("csd4ni3l Soundboard");
|
}
|
||||||
});
|
#[allow(unreachable_code)]
|
||||||
|
{
|
||||||
|
ui.add(egui::Button::new("Unsupported. Select inside apps.".to_string()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn main_ui(ctx: &Context, mut app_state: ResMut<AppState>) {
|
||||||
egui::SidePanel::right("tools").show(ctx, |ui| {
|
egui::SidePanel::right("tools").show(ctx, |ui| {
|
||||||
ui.heading("Tools");
|
ui.heading("Tools");
|
||||||
|
|
||||||
@@ -262,34 +343,8 @@ fn ui_system(mut contexts: EguiContexts, mut app_state: ResMut<AppState>) -> Res
|
|||||||
|
|
||||||
let available_width = ui.available_width();
|
let available_width = ui.available_width();
|
||||||
let available_height = ui.available_height();
|
let available_height = ui.available_height();
|
||||||
let outputs = app_state.virt_outputs.clone();
|
|
||||||
|
|
||||||
let mut mic_name = "Select inside apps".to_string();
|
|
||||||
|
|
||||||
#[cfg(target_os = "linux")]
|
|
||||||
{
|
|
||||||
let output_index = app_state.virt_output_index.clone();
|
|
||||||
let output_sink = linux_lib::get_sink_by_index("source-outputs", output_index);
|
|
||||||
if let Some(app_name) = output_sink["properties"]["application.name"].as_str() {
|
|
||||||
mic_name = app_name.to_string();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
ui.label("Virtual Mic Output");
|
ui.label("Virtual Mic Output");
|
||||||
|
create_virtual_mic_ui(ui, &mut app_state, available_width, available_height);
|
||||||
egui::ComboBox::from_id_salt("Virtual Mic Output")
|
|
||||||
.selected_text(mic_name)
|
|
||||||
.width(available_width)
|
|
||||||
.height(available_height / 15.0)
|
|
||||||
.show_ui(ui, |ui| {
|
|
||||||
for output in &outputs {
|
|
||||||
ui.selectable_value(
|
|
||||||
&mut app_state.virt_output_index_switch,
|
|
||||||
output.1.clone(),
|
|
||||||
output.0.clone(),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
if ui
|
if ui
|
||||||
.add_sized(
|
.add_sized(
|
||||||
@@ -300,7 +355,6 @@ fn ui_system(mut contexts: EguiContexts, mut app_state: ResMut<AppState>) -> Res
|
|||||||
{
|
{
|
||||||
if let Some(folder) = rfd::FileDialog::new().pick_folder() {
|
if let Some(folder) = rfd::FileDialog::new().pick_folder() {
|
||||||
if let Some(path_str) = folder.to_str() {
|
if let Some(path_str) = folder.to_str() {
|
||||||
println!("Selected: {}", path_str);
|
|
||||||
app_state.json_data.tabs.push(path_str.to_string());
|
app_state.json_data.tabs.push(path_str.to_string());
|
||||||
std::fs::write(
|
std::fs::write(
|
||||||
"data.json",
|
"data.json",
|
||||||
@@ -333,7 +387,7 @@ fn ui_system(mut contexts: EguiContexts, mut app_state: ResMut<AppState>) -> Res
|
|||||||
)
|
)
|
||||||
.clicked()
|
.clicked()
|
||||||
{
|
{
|
||||||
println!("Youtube downloader!");
|
app_state.current_view = "youtube_downloader".to_string();
|
||||||
}
|
}
|
||||||
|
|
||||||
if ui
|
if ui
|
||||||
@@ -349,27 +403,6 @@ fn ui_system(mut contexts: EguiContexts, mut app_state: ResMut<AppState>) -> Res
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
egui::TopBottomPanel::bottom("currently_playing").show(ctx, |ui| {
|
|
||||||
ui.horizontal(|ui| {
|
|
||||||
if app_state.sound_system.paused {
|
|
||||||
ui.heading("Paused");
|
|
||||||
} else {
|
|
||||||
ui.heading("Playing");
|
|
||||||
}
|
|
||||||
|
|
||||||
ui.vertical(|ui| {
|
|
||||||
for playing_sound in &app_state.currently_playing {
|
|
||||||
ui.label(format!(
|
|
||||||
"{} - {:.2} / {:.2}",
|
|
||||||
playing_sound.file_path,
|
|
||||||
playing_sound.sink.get_pos().as_secs_f32(),
|
|
||||||
playing_sound.length
|
|
||||||
));
|
|
||||||
}
|
|
||||||
})
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
egui::CentralPanel::default().show(ctx, |ui| {
|
egui::CentralPanel::default().show(ctx, |ui| {
|
||||||
let available_height = ui.available_height();
|
let available_height = ui.available_height();
|
||||||
ui.horizontal(|ui| {
|
ui.horizontal(|ui| {
|
||||||
@@ -424,10 +457,196 @@ fn ui_system(mut contexts: EguiContexts, mut app_state: ResMut<AppState>) -> Res
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
}
|
||||||
|
|
||||||
app_state.currently_playing.retain(|playing_sound| {
|
fn download_youtube_sound(app_state: &mut ResMut<AppState>) {
|
||||||
playing_sound.sink.get_pos().as_secs_f32() <= (playing_sound.length - 0.01) // 0.01 offset needed here because of floating point errors and so its not exact
|
let filename = app_state.youtube_downloader_state.current_filename.clone();
|
||||||
|
let download_directory = app_state.youtube_downloader_state.download_directory.clone();
|
||||||
|
let current_url = app_state.youtube_downloader_state.current_url.clone();
|
||||||
|
let stdout_text = Arc::clone(&app_state.youtube_downloader_state.yt_dlp_stdout_text);
|
||||||
|
|
||||||
|
app_state.youtube_downloader_state.yt_dlp_running = true;
|
||||||
|
|
||||||
|
thread::spawn(move || {
|
||||||
|
let mut command = Command::new(get_yt_dlp_path())
|
||||||
|
.args(&["-x", "--audio-format", "mp3", "-o", "sound.mp3", current_url.as_str()])
|
||||||
|
.stdout(Stdio::piped())
|
||||||
|
.stderr(Stdio::piped())
|
||||||
|
.spawn()
|
||||||
|
.expect("Failed to execute process");
|
||||||
|
|
||||||
|
if let Some(mut stdout) = command.stdout.take() {
|
||||||
|
let mut buffer = String::new();
|
||||||
|
loop {
|
||||||
|
let mut chunk = vec![0u8; 1024];
|
||||||
|
match stdout.read(&mut chunk) {
|
||||||
|
Ok(0) => break,
|
||||||
|
Ok(n) => {
|
||||||
|
if let Ok(text) = String::from_utf8(chunk[..n].to_vec()) {
|
||||||
|
buffer.push_str(&text);
|
||||||
|
if let Ok(mut locked) = stdout_text.lock() {
|
||||||
|
*locked = buffer.clone();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Err(_) => break,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
let _ = command.wait();
|
||||||
|
|
||||||
|
let path = Path::new(&download_directory).join(filename);
|
||||||
|
let _ = rename("sound.mp3", path.to_string_lossy().as_str());
|
||||||
});
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
fn youtube_downloader_ui(ctx: &Context, mut app_state: ResMut<AppState>) {
|
||||||
|
egui::CentralPanel::default().show(ctx, |ui| {
|
||||||
|
let available_width = ui.available_width();
|
||||||
|
let available_height = ui.available_height();
|
||||||
|
|
||||||
|
ui.with_layout(egui::Layout::top_down(egui::Align::Center), |ui| {
|
||||||
|
ui.heading("Directory");
|
||||||
|
egui::ComboBox::from_id_salt("Download Directory Selector")
|
||||||
|
.selected_text(app_state.youtube_downloader_state.download_directory.clone())
|
||||||
|
.width(available_width)
|
||||||
|
.height(available_height / 15.0)
|
||||||
|
.show_ui(ui, |ui| {
|
||||||
|
for directory in &app_state.loaded_files.keys().cloned().collect::<Vec<_>>() {
|
||||||
|
ui.selectable_value(
|
||||||
|
&mut app_state.youtube_downloader_state.download_directory,
|
||||||
|
directory.clone(),
|
||||||
|
directory,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
ui.heading("Filename");
|
||||||
|
ui.add_sized([available_width, available_height / 20.0], egui::TextEdit::singleline(&mut app_state.youtube_downloader_state.current_filename));
|
||||||
|
|
||||||
|
ui.heading("Youtube URL");
|
||||||
|
ui.add_sized([available_width, available_height / 20.0], egui::TextEdit::singleline(&mut app_state.youtube_downloader_state.current_url));
|
||||||
|
});
|
||||||
|
|
||||||
|
if let Ok(text) = app_state.youtube_downloader_state.yt_dlp_stdout_text.lock() {
|
||||||
|
ui.colored_label(Color32::GREEN, text.clone());
|
||||||
|
};
|
||||||
|
|
||||||
|
if ui
|
||||||
|
.add_sized(
|
||||||
|
[
|
||||||
|
available_width as f32,
|
||||||
|
available_height / 15.0,
|
||||||
|
],
|
||||||
|
egui::Button::new("Download Sound"),
|
||||||
|
)
|
||||||
|
.clicked()
|
||||||
|
{
|
||||||
|
download_youtube_sound(&mut app_state);
|
||||||
|
};
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
fn draw(mut contexts: EguiContexts, mut app_state: ResMut<AppState>) -> Result {
|
||||||
|
let ctx = contexts.ctx_mut()?;
|
||||||
|
|
||||||
|
egui::TopBottomPanel::top("top_panel").show(ctx, |ui| {
|
||||||
|
if app_state.current_view != "main" {
|
||||||
|
ui.with_layout(egui::Layout::left_to_right(egui::Align::Center), |ui| {
|
||||||
|
let available_width = ui.available_width();
|
||||||
|
let available_height = ui.available_height();
|
||||||
|
|
||||||
|
if ui
|
||||||
|
.add_sized(
|
||||||
|
[available_width / 25.0, available_height],
|
||||||
|
egui::Button::new("<--"),
|
||||||
|
)
|
||||||
|
.clicked()
|
||||||
|
{
|
||||||
|
app_state.current_view = "main".to_string();
|
||||||
|
}
|
||||||
|
|
||||||
|
ui.heading("csd4ni3l Soundboard");
|
||||||
|
});
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
ui.heading("csd4ni3l Soundboard");
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
let window_height = ctx.content_rect().height();
|
||||||
|
|
||||||
|
egui::TopBottomPanel::bottom("currently_playing")
|
||||||
|
.exact_height(window_height * 0.1)
|
||||||
|
.show(ctx, |ui| {
|
||||||
|
ui.vertical(|ui| {
|
||||||
|
for playing_sound in &mut app_state.currently_playing {
|
||||||
|
ui.horizontal(|ui| {
|
||||||
|
ui.label(format!(
|
||||||
|
"{} - {:.2} / {:.2}",
|
||||||
|
playing_sound.file_path,
|
||||||
|
playing_sound.sink.get_pos().as_secs_f32(),
|
||||||
|
playing_sound.length
|
||||||
|
));
|
||||||
|
let available_width = ui.available_width();
|
||||||
|
let available_height = ui.available_height();
|
||||||
|
if ui
|
||||||
|
.add_sized(
|
||||||
|
[
|
||||||
|
available_width / 2 as f32,
|
||||||
|
available_height,
|
||||||
|
],
|
||||||
|
egui::Button::new("Stop"),
|
||||||
|
)
|
||||||
|
.clicked()
|
||||||
|
{
|
||||||
|
playing_sound.to_remove = true;
|
||||||
|
};
|
||||||
|
if ui
|
||||||
|
.add_sized(
|
||||||
|
[
|
||||||
|
available_width / 2 as f32,
|
||||||
|
available_height,
|
||||||
|
],
|
||||||
|
egui::Button::new(if playing_sound.sink.is_paused() {"Resume"} else {"Pause"}),
|
||||||
|
)
|
||||||
|
.clicked()
|
||||||
|
{
|
||||||
|
if playing_sound.sink.is_paused() {
|
||||||
|
playing_sound.sink.play();
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
playing_sound.sink.pause();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
let available_width = ui.available_width();
|
||||||
|
let available_height = ui.available_height();
|
||||||
|
|
||||||
|
if ui
|
||||||
|
.add_sized(
|
||||||
|
[available_width, available_height / 15.0],
|
||||||
|
egui::Button::new("Stop all"),
|
||||||
|
)
|
||||||
|
.clicked()
|
||||||
|
{
|
||||||
|
app_state.currently_playing.clear();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
app_state.currently_playing.retain(|playing_sound| { // retains happen the next cycle, not in the current one because of borrowing and im lazy to fix
|
||||||
|
playing_sound.sink.get_pos().as_secs_f32() <= (playing_sound.length - 0.01) && !playing_sound.to_remove // 0.01 offset needed here because of floating point errors and so its not exact
|
||||||
|
});
|
||||||
|
|
||||||
|
if app_state.current_view == "main".to_string() {
|
||||||
|
main_ui(ctx, app_state);
|
||||||
|
}
|
||||||
|
else if app_state.current_view == "youtube_downloader".to_string() {
|
||||||
|
youtube_downloader_ui(ctx, app_state);
|
||||||
|
}
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,14 +1,89 @@
|
|||||||
use rodio::{OutputStream, OutputStreamBuilder, cpal::{self, traits::HostTrait, traits::DeviceTrait}};
|
use rodio::{
|
||||||
|
OutputStream, OutputStreamBuilder,
|
||||||
|
cpal::{self, traits::{DeviceTrait, StreamTrait, HostTrait}, StreamConfig, SampleRate},
|
||||||
|
};
|
||||||
|
use rfd::{MessageButtons, MessageDialog, MessageDialogResult};
|
||||||
|
use ringbuf::{traits::*, HeapRb};
|
||||||
|
use std::process;
|
||||||
|
|
||||||
pub fn create_virtual_mic_windows() -> (OutputStream, OutputStream) {
|
fn route_standard_to_virtual(host: &cpal::Host, virtual_mic: &cpal::Device) {
|
||||||
let host = cpal::host_from_id(cpal::HostId::Wasapi).expect("Could not initialize audio routing using WasAPI");
|
let standard_mic = host.default_input_device().expect("Could not get default input device.");
|
||||||
let virtual_mic = host.output_devices().expect("Could not list Output devices").find(|device| {
|
|
||||||
device.name().ok().map(|name|{
|
|
||||||
name.contains("CABLE Input") || name.contains("VB-Audio")
|
|
||||||
}).unwrap_or(false)
|
|
||||||
}).expect("Could not get default output device");
|
|
||||||
|
|
||||||
normal_output = host.default_output_device().expect("Could not get default output device");
|
|
||||||
|
|
||||||
return (OutputStreamBuilder::from_device(normal_output).expect("Unable to open default audio device").open_stream().expect("Failed to open stream"), OutputStreamBuilder::from_device(virtual_mic).expect("Unable to open default audio device").open_stream().expect("Failed to open stream"));
|
let config = StreamConfig {
|
||||||
|
channels: 2,
|
||||||
|
sample_rate: SampleRate(48_000),
|
||||||
|
buffer_size: cpal::BufferSize::Default,
|
||||||
|
};
|
||||||
|
let rb = HeapRb::<f32>::new(48_000 * 2);
|
||||||
|
let (mut producer, mut consumer) = rb.split();
|
||||||
|
|
||||||
|
let input_stream = standard_mic.build_input_stream(
|
||||||
|
&config,
|
||||||
|
move |data: &[f32], _| {
|
||||||
|
for &sample in data {
|
||||||
|
let _ = producer.try_push(sample);
|
||||||
|
let _ = producer.try_push(sample);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
move |err| eprintln!("Input stream error: {err}"),
|
||||||
|
None,
|
||||||
|
).expect("Could not build input stream for standard to virtual mic routing");
|
||||||
|
|
||||||
|
let output_stream = virtual_mic.build_output_stream(
|
||||||
|
&config,
|
||||||
|
move |data: &mut [f32], _| {
|
||||||
|
for sample in data {
|
||||||
|
*sample = consumer.try_pop().unwrap_or(0.0);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
move |err| eprintln!("Output stream error: {err}"),
|
||||||
|
None,
|
||||||
|
).expect("Could not build output stream for standard to virtual mic routing");
|
||||||
|
|
||||||
|
input_stream.play();
|
||||||
|
output_stream.play();
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn create_virtual_mic_windows() -> (OutputStream, OutputStream) {
|
||||||
|
let host = cpal::host_from_id(cpal::HostId::Wasapi)
|
||||||
|
.expect("Could not initialize audio routing using WasAPI");
|
||||||
|
|
||||||
|
let virtual_mic = host
|
||||||
|
.output_devices()
|
||||||
|
.expect("Could not list Output devices")
|
||||||
|
.find(|device| {
|
||||||
|
device
|
||||||
|
.name()
|
||||||
|
.ok()
|
||||||
|
.map(|name| name.contains("CABLE Input") || name.contains("VB-Audio"))
|
||||||
|
.unwrap_or(false)
|
||||||
|
});
|
||||||
|
|
||||||
|
if let Some(virtual_mic) = virtual_mic {
|
||||||
|
route_standard_to_virtual(&host, &virtual_mic);
|
||||||
|
|
||||||
|
let normal_output = host
|
||||||
|
.default_output_device()
|
||||||
|
.expect("Could not get default output device");
|
||||||
|
|
||||||
|
return (
|
||||||
|
OutputStreamBuilder::from_device(normal_output)
|
||||||
|
.expect("Unable to open default audio device")
|
||||||
|
.open_stream()
|
||||||
|
.expect("Failed to open stream"),
|
||||||
|
OutputStreamBuilder::from_device(virtual_mic)
|
||||||
|
.expect("Unable to open default audio device")
|
||||||
|
.open_stream()
|
||||||
|
.expect("Failed to open stream"),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
MessageDialog::new()
|
||||||
|
.set_title("VB Cable Driver not installed.")
|
||||||
|
.set_description("Could not access VB Cable output device. Is VB Cable Driver installed?")
|
||||||
|
.set_buttons(MessageButtons::Ok)
|
||||||
|
.show();
|
||||||
|
|
||||||
|
std::process::exit(1);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
82
src/yt_dlp.rs
Normal file
82
src/yt_dlp.rs
Normal file
@@ -0,0 +1,82 @@
|
|||||||
|
use std::{env::current_dir, fs::{File, exists}, io, process::Command};
|
||||||
|
use reqwest;
|
||||||
|
use rfd::{MessageButtons, MessageDialog, MessageDialogResult};
|
||||||
|
|
||||||
|
#[cfg(unix)]
|
||||||
|
use std::os::unix::fs::PermissionsExt;
|
||||||
|
|
||||||
|
pub fn get_yt_dlp_path() -> String {
|
||||||
|
if cfg!(target_os = "windows"){
|
||||||
|
current_dir().expect("Failed to get current working directory").join("bin").join("yt-dlp.exe").to_string_lossy().to_string()
|
||||||
|
}
|
||||||
|
else if cfg!(target_os = "macos"){
|
||||||
|
current_dir().expect("Failed to get current working directory").join("bin").join("yt-dlp_macos").to_string_lossy().to_string()
|
||||||
|
}
|
||||||
|
else if cfg!(target_os = "linux"){
|
||||||
|
current_dir().expect("Failed to get current working directory").join("bin").join("yt-dlp_linux").to_string_lossy().to_string()
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
"".to_string()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn check_and_download_yt_dlp() {
|
||||||
|
let url: &str;
|
||||||
|
|
||||||
|
if cfg!(target_os = "windows"){
|
||||||
|
url = "https://github.com/yt-dlp/yt-dlp/releases/latest/download/yt-dlp.exe";
|
||||||
|
}
|
||||||
|
else if cfg!(target_os = "macos"){
|
||||||
|
url = "https://github.com/yt-dlp/yt-dlp/releases/latest/download/yt-dlp_macos";
|
||||||
|
}
|
||||||
|
else if cfg!(target_os = "linux"){
|
||||||
|
url = "https://github.com/yt-dlp/yt-dlp/releases/latest/download/yt-dlp_linux";
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if exists(get_yt_dlp_path()).expect("Could not check existence of yt dlp executable.") {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut body = reqwest::blocking::get(url).expect("Could not download yt-dlp");
|
||||||
|
let mut out = File::create(get_yt_dlp_path()).expect("failed to create file");
|
||||||
|
io::copy(&mut body, &mut out).expect("failed to copy content");
|
||||||
|
|
||||||
|
#[cfg(unix)]
|
||||||
|
out.set_permissions(PermissionsExt::from_mode(0o755));
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn check_ffmpeg() -> bool{
|
||||||
|
return std::process::Command::new("ffmpeg").output().is_ok();
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn check_and_download_ffmpeg() {
|
||||||
|
if check_ffmpeg() {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if cfg!(target_os = "windows"){
|
||||||
|
let confirmed = MessageDialog::new()
|
||||||
|
.set_title("FFmpeg Download Optional.")
|
||||||
|
.set_description("The youtube downloader depends on FFmpeg for mp3 conversion. This app can auto-install FFmpeg with winget. Do you want to install FFmpeg?")
|
||||||
|
.set_buttons(MessageButtons::YesNo)
|
||||||
|
.show();
|
||||||
|
|
||||||
|
if confirmed == MessageDialogResult::Ok {
|
||||||
|
Command::new("winget")
|
||||||
|
.args(&["install", "BtbN.FFmpeg.GPL.Shared.8.0", "--source winget", "--accept-source-agreements", "--accept-package-agreements"]) // as_str is needed here as you cannot instantly dereference a growing String (Rust...)
|
||||||
|
.output()
|
||||||
|
.expect("Failed to execute process");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
MessageDialog::new()
|
||||||
|
.set_title("FFmpeg Download Optional.")
|
||||||
|
.set_description("The youtube downloader depends on FFmpeg for mp3 conversion. You are on a Linux or Darwin based OS. If you want to use the Youtube Downloader, you need to install FFmpeg and libavcodec shared libraries from your package manager to make sure it is in PATH.")
|
||||||
|
.set_buttons(MessageButtons::Ok)
|
||||||
|
.show();
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user