Compare commits
5 Commits
ft-dep-dl
...
f26fd97809
| Author | SHA1 | Date | |
|---|---|---|---|
|
f26fd97809
|
|||
|
2cde24e7a8
|
|||
|
33ca4502e4
|
|||
| 6e70d38f7f | |||
|
affafed441
|
2
.cargo/config.toml
Normal file
2
.cargo/config.toml
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
[target.aarch64-unknown-linux-gnu]
|
||||||
|
linker="aarch64-linux-gnu-gcc"
|
||||||
261
Cargo.lock
generated
261
Cargo.lock
generated
@@ -924,7 +924,7 @@ dependencies = [
|
|||||||
"cocoa-foundation",
|
"cocoa-foundation",
|
||||||
"core-foundation",
|
"core-foundation",
|
||||||
"core-graphics",
|
"core-graphics",
|
||||||
"foreign-types 0.5.0",
|
"foreign-types",
|
||||||
"libc",
|
"libc",
|
||||||
"objc",
|
"objc",
|
||||||
]
|
]
|
||||||
@@ -1046,7 +1046,7 @@ dependencies = [
|
|||||||
"bitflags 1.3.2",
|
"bitflags 1.3.2",
|
||||||
"core-foundation",
|
"core-foundation",
|
||||||
"core-graphics-types",
|
"core-graphics-types",
|
||||||
"foreign-types 0.5.0",
|
"foreign-types",
|
||||||
"libc",
|
"libc",
|
||||||
]
|
]
|
||||||
|
|
||||||
@@ -1315,15 +1315,6 @@ dependencies = [
|
|||||||
"serde",
|
"serde",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "encoding_rs"
|
|
||||||
version = "0.8.34"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "b45de904aa0b010bce2ab45264d0631681847fa7b6f2eaa7dab7619943bc4f59"
|
|
||||||
dependencies = [
|
|
||||||
"cfg-if",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "endi"
|
name = "endi"
|
||||||
version = "1.1.0"
|
version = "1.1.0"
|
||||||
@@ -1523,15 +1514,6 @@ version = "1.0.7"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1"
|
checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1"
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "foreign-types"
|
|
||||||
version = "0.3.2"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "f6f339eb8adc052cd2ca78910fda869aefa38d22d5cb648e6485e4d3fc06f3b1"
|
|
||||||
dependencies = [
|
|
||||||
"foreign-types-shared 0.1.1",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "foreign-types"
|
name = "foreign-types"
|
||||||
version = "0.5.0"
|
version = "0.5.0"
|
||||||
@@ -1539,7 +1521,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||||||
checksum = "d737d9aa519fb7b749cbc3b962edcf310a8dd1f4b67c91c4f83975dbdd17d965"
|
checksum = "d737d9aa519fb7b749cbc3b962edcf310a8dd1f4b67c91c4f83975dbdd17d965"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"foreign-types-macros",
|
"foreign-types-macros",
|
||||||
"foreign-types-shared 0.3.1",
|
"foreign-types-shared",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -1553,12 +1535,6 @@ dependencies = [
|
|||||||
"syn 2.0.77",
|
"syn 2.0.77",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "foreign-types-shared"
|
|
||||||
version = "0.1.1"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "00b0228411908ca8685dba7fc2cdd70ec9990a6e753e89b6ac91a84c40fbaf4b"
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "foreign-types-shared"
|
name = "foreign-types-shared"
|
||||||
version = "0.3.1"
|
version = "0.3.1"
|
||||||
@@ -2041,22 +2017,7 @@ dependencies = [
|
|||||||
"tokio",
|
"tokio",
|
||||||
"tokio-rustls",
|
"tokio-rustls",
|
||||||
"tower-service",
|
"tower-service",
|
||||||
]
|
"webpki-roots",
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "hyper-tls"
|
|
||||||
version = "0.6.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "70206fc6890eaca9fde8a0bf71caa2ddfc9fe045ac9e5c70df101a7dbde866e0"
|
|
||||||
dependencies = [
|
|
||||||
"bytes",
|
|
||||||
"http-body-util",
|
|
||||||
"hyper",
|
|
||||||
"hyper-util",
|
|
||||||
"native-tls",
|
|
||||||
"tokio",
|
|
||||||
"tokio-native-tls",
|
|
||||||
"tower-service",
|
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -2424,7 +2385,7 @@ dependencies = [
|
|||||||
"bitflags 2.6.0",
|
"bitflags 2.6.0",
|
||||||
"block",
|
"block",
|
||||||
"core-graphics-types",
|
"core-graphics-types",
|
||||||
"foreign-types 0.5.0",
|
"foreign-types",
|
||||||
"log",
|
"log",
|
||||||
"objc",
|
"objc",
|
||||||
"paste",
|
"paste",
|
||||||
@@ -2490,30 +2451,13 @@ dependencies = [
|
|||||||
"indexmap",
|
"indexmap",
|
||||||
"log",
|
"log",
|
||||||
"num-traits",
|
"num-traits",
|
||||||
"rustc-hash",
|
"rustc-hash 1.1.0",
|
||||||
"spirv",
|
"spirv",
|
||||||
"termcolor",
|
"termcolor",
|
||||||
"thiserror",
|
"thiserror",
|
||||||
"unicode-xid",
|
"unicode-xid",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "native-tls"
|
|
||||||
version = "0.2.12"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "a8614eb2c83d59d1c8cc974dd3f920198647674a0a035e1af1fa58707e317466"
|
|
||||||
dependencies = [
|
|
||||||
"libc",
|
|
||||||
"log",
|
|
||||||
"openssl",
|
|
||||||
"openssl-probe",
|
|
||||||
"openssl-sys",
|
|
||||||
"schannel",
|
|
||||||
"security-framework",
|
|
||||||
"security-framework-sys",
|
|
||||||
"tempfile",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "ndk"
|
name = "ndk"
|
||||||
version = "0.8.0"
|
version = "0.8.0"
|
||||||
@@ -2831,50 +2775,6 @@ dependencies = [
|
|||||||
"pathdiff",
|
"pathdiff",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "openssl"
|
|
||||||
version = "0.10.66"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "9529f4786b70a3e8c61e11179af17ab6188ad8d0ded78c5529441ed39d4bd9c1"
|
|
||||||
dependencies = [
|
|
||||||
"bitflags 2.6.0",
|
|
||||||
"cfg-if",
|
|
||||||
"foreign-types 0.3.2",
|
|
||||||
"libc",
|
|
||||||
"once_cell",
|
|
||||||
"openssl-macros",
|
|
||||||
"openssl-sys",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "openssl-macros"
|
|
||||||
version = "0.1.1"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "a948666b637a0f465e8564c73e89d4dde00d72d4d473cc972f390fc3dcee7d9c"
|
|
||||||
dependencies = [
|
|
||||||
"proc-macro2",
|
|
||||||
"quote",
|
|
||||||
"syn 2.0.77",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "openssl-probe"
|
|
||||||
version = "0.1.5"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "ff011a302c396a5197692431fc1948019154afc178baf7d8e37367442a4601cf"
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "openssl-sys"
|
|
||||||
version = "0.9.103"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "7f9e8deee91df40a943c71b917e5874b951d32a802526c85721ce3b776c929d6"
|
|
||||||
dependencies = [
|
|
||||||
"cc",
|
|
||||||
"libc",
|
|
||||||
"pkg-config",
|
|
||||||
"vcpkg",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "orbclient"
|
name = "orbclient"
|
||||||
version = "0.3.47"
|
version = "0.3.47"
|
||||||
@@ -3139,6 +3039,54 @@ dependencies = [
|
|||||||
"memchr",
|
"memchr",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "quinn"
|
||||||
|
version = "0.11.3"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "b22d8e7369034b9a7132bc2008cac12f2013c8132b45e0554e6e20e2617f2156"
|
||||||
|
dependencies = [
|
||||||
|
"bytes",
|
||||||
|
"pin-project-lite",
|
||||||
|
"quinn-proto",
|
||||||
|
"quinn-udp",
|
||||||
|
"rustc-hash 2.0.0",
|
||||||
|
"rustls",
|
||||||
|
"socket2 0.5.7",
|
||||||
|
"thiserror",
|
||||||
|
"tokio",
|
||||||
|
"tracing",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "quinn-proto"
|
||||||
|
version = "0.11.6"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "ba92fb39ec7ad06ca2582c0ca834dfeadcaf06ddfc8e635c80aa7e1c05315fdd"
|
||||||
|
dependencies = [
|
||||||
|
"bytes",
|
||||||
|
"rand",
|
||||||
|
"ring",
|
||||||
|
"rustc-hash 2.0.0",
|
||||||
|
"rustls",
|
||||||
|
"slab",
|
||||||
|
"thiserror",
|
||||||
|
"tinyvec",
|
||||||
|
"tracing",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "quinn-udp"
|
||||||
|
version = "0.5.4"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "8bffec3605b73c6f1754535084a85229fa8a30f86014e6c81aeec4abb68b0285"
|
||||||
|
dependencies = [
|
||||||
|
"libc",
|
||||||
|
"once_cell",
|
||||||
|
"socket2 0.5.7",
|
||||||
|
"tracing",
|
||||||
|
"windows-sys 0.52.0",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "quote"
|
name = "quote"
|
||||||
version = "1.0.37"
|
version = "1.0.37"
|
||||||
@@ -3271,7 +3219,6 @@ checksum = "f8f4955649ef5c38cc7f9e8aa41761d48fb9677197daea9984dc54f56aad5e63"
|
|||||||
dependencies = [
|
dependencies = [
|
||||||
"base64",
|
"base64",
|
||||||
"bytes",
|
"bytes",
|
||||||
"encoding_rs",
|
|
||||||
"futures-core",
|
"futures-core",
|
||||||
"futures-util",
|
"futures-util",
|
||||||
"h2",
|
"h2",
|
||||||
@@ -3280,29 +3227,30 @@ dependencies = [
|
|||||||
"http-body-util",
|
"http-body-util",
|
||||||
"hyper",
|
"hyper",
|
||||||
"hyper-rustls",
|
"hyper-rustls",
|
||||||
"hyper-tls",
|
|
||||||
"hyper-util",
|
"hyper-util",
|
||||||
"ipnet",
|
"ipnet",
|
||||||
"js-sys",
|
"js-sys",
|
||||||
"log",
|
"log",
|
||||||
"mime",
|
"mime",
|
||||||
"native-tls",
|
|
||||||
"once_cell",
|
"once_cell",
|
||||||
"percent-encoding",
|
"percent-encoding",
|
||||||
"pin-project-lite",
|
"pin-project-lite",
|
||||||
|
"quinn",
|
||||||
|
"rustls",
|
||||||
"rustls-pemfile",
|
"rustls-pemfile",
|
||||||
|
"rustls-pki-types",
|
||||||
"serde",
|
"serde",
|
||||||
"serde_json",
|
"serde_json",
|
||||||
"serde_urlencoded",
|
"serde_urlencoded",
|
||||||
"sync_wrapper",
|
"sync_wrapper",
|
||||||
"system-configuration",
|
|
||||||
"tokio",
|
"tokio",
|
||||||
"tokio-native-tls",
|
"tokio-rustls",
|
||||||
"tower-service",
|
"tower-service",
|
||||||
"url",
|
"url",
|
||||||
"wasm-bindgen",
|
"wasm-bindgen",
|
||||||
"wasm-bindgen-futures",
|
"wasm-bindgen-futures",
|
||||||
"web-sys",
|
"web-sys",
|
||||||
|
"webpki-roots",
|
||||||
"windows-registry",
|
"windows-registry",
|
||||||
]
|
]
|
||||||
|
|
||||||
@@ -3333,6 +3281,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.0.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "583034fd73374156e66797ed8e5b0d5690409c9226b22d87cb7f19821c05d152"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "rustix"
|
name = "rustix"
|
||||||
version = "0.37.27"
|
version = "0.37.27"
|
||||||
@@ -3367,6 +3321,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||||||
checksum = "f2dabaac7466917e566adb06783a81ca48944c6898a1b08b9374106dd671f4c8"
|
checksum = "f2dabaac7466917e566adb06783a81ca48944c6898a1b08b9374106dd671f4c8"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"once_cell",
|
"once_cell",
|
||||||
|
"ring",
|
||||||
"rustls-pki-types",
|
"rustls-pki-types",
|
||||||
"rustls-webpki",
|
"rustls-webpki",
|
||||||
"subtle",
|
"subtle",
|
||||||
@@ -3415,15 +3370,6 @@ dependencies = [
|
|||||||
"winapi-util",
|
"winapi-util",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "schannel"
|
|
||||||
version = "0.1.24"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "e9aaafd5a2b6e3d657ff009d82fbd630b6bd54dd4eb06f21693925cdf80f9b8b"
|
|
||||||
dependencies = [
|
|
||||||
"windows-sys 0.59.0",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "scoped-tls"
|
name = "scoped-tls"
|
||||||
version = "1.0.1"
|
version = "1.0.1"
|
||||||
@@ -3449,29 +3395,6 @@ dependencies = [
|
|||||||
"tiny-skia",
|
"tiny-skia",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "security-framework"
|
|
||||||
version = "2.11.1"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "897b2245f0b511c87893af39b033e5ca9cce68824c4d7e7630b5a1d339658d02"
|
|
||||||
dependencies = [
|
|
||||||
"bitflags 2.6.0",
|
|
||||||
"core-foundation",
|
|
||||||
"core-foundation-sys",
|
|
||||||
"libc",
|
|
||||||
"security-framework-sys",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "security-framework-sys"
|
|
||||||
version = "2.11.1"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "75da29fe9b9b08fe9d6b22b5b4bcbc75d8db3aa31e639aa56bb62e9d46bfceaf"
|
|
||||||
dependencies = [
|
|
||||||
"core-foundation-sys",
|
|
||||||
"libc",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "serde"
|
name = "serde"
|
||||||
version = "1.0.210"
|
version = "1.0.210"
|
||||||
@@ -3754,27 +3677,6 @@ dependencies = [
|
|||||||
"futures-core",
|
"futures-core",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "system-configuration"
|
|
||||||
version = "0.6.1"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "3c879d448e9d986b661742763247d3693ed13609438cf3d006f51f5368a5ba6b"
|
|
||||||
dependencies = [
|
|
||||||
"bitflags 2.6.0",
|
|
||||||
"core-foundation",
|
|
||||||
"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 = "tauri-winrt-notification"
|
name = "tauri-winrt-notification"
|
||||||
version = "0.2.1"
|
version = "0.2.1"
|
||||||
@@ -3915,16 +3817,6 @@ dependencies = [
|
|||||||
"syn 2.0.77",
|
"syn 2.0.77",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "tokio-native-tls"
|
|
||||||
version = "0.3.1"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "bbae76ab933c85776efabc971569dd6119c580d8f5d448769dec1764bf796ef2"
|
|
||||||
dependencies = [
|
|
||||||
"native-tls",
|
|
||||||
"tokio",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "tokio-rustls"
|
name = "tokio-rustls"
|
||||||
version = "0.26.0"
|
version = "0.26.0"
|
||||||
@@ -4053,7 +3945,7 @@ 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 = "deb68604048ff8fa93347f02441e4487594adc20bb8a084f9e564d2b827a0a9f"
|
checksum = "deb68604048ff8fa93347f02441e4487594adc20bb8a084f9e564d2b827a0a9f"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"rustc-hash",
|
"rustc-hash 1.1.0",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -4144,12 +4036,6 @@ version = "0.2.2"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821"
|
checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821"
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "vcpkg"
|
|
||||||
version = "0.2.15"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426"
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "version_check"
|
name = "version_check"
|
||||||
version = "0.9.5"
|
version = "0.9.5"
|
||||||
@@ -4425,6 +4311,15 @@ dependencies = [
|
|||||||
"web-sys",
|
"web-sys",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "webpki-roots"
|
||||||
|
version = "0.26.5"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "0bd24728e5af82c6c4ec1b66ac4844bdf8156257fccda846ec58b42cd0cdbe6a"
|
||||||
|
dependencies = [
|
||||||
|
"rustls-pki-types",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "wgpu"
|
name = "wgpu"
|
||||||
version = "0.19.4"
|
version = "0.19.4"
|
||||||
@@ -4467,7 +4362,7 @@ dependencies = [
|
|||||||
"parking_lot",
|
"parking_lot",
|
||||||
"profiling",
|
"profiling",
|
||||||
"raw-window-handle 0.6.2",
|
"raw-window-handle 0.6.2",
|
||||||
"rustc-hash",
|
"rustc-hash 1.1.0",
|
||||||
"smallvec",
|
"smallvec",
|
||||||
"thiserror",
|
"thiserror",
|
||||||
"web-sys",
|
"web-sys",
|
||||||
@@ -4507,7 +4402,7 @@ dependencies = [
|
|||||||
"profiling",
|
"profiling",
|
||||||
"raw-window-handle 0.6.2",
|
"raw-window-handle 0.6.2",
|
||||||
"renderdoc-sys",
|
"renderdoc-sys",
|
||||||
"rustc-hash",
|
"rustc-hash 1.1.0",
|
||||||
"smallvec",
|
"smallvec",
|
||||||
"thiserror",
|
"thiserror",
|
||||||
"wasm-bindgen",
|
"wasm-bindgen",
|
||||||
|
|||||||
@@ -20,7 +20,7 @@ libc = "0.2.153"
|
|||||||
log = "0.4.21"
|
log = "0.4.21"
|
||||||
notify-rust = "4.11.3"
|
notify-rust = "4.11.3"
|
||||||
open = "5.3.0"
|
open = "5.3.0"
|
||||||
reqwest = "0.12.3"
|
reqwest = { version = "0.12.3", features = ["h2", "http2", "rustls-tls"], default-features = false }
|
||||||
serde = { version = "1.0.197", features = ["derive"] }
|
serde = { version = "1.0.197", features = ["derive"] }
|
||||||
serde_json = "1.0.115"
|
serde_json = "1.0.115"
|
||||||
# serde_traitobject = "0.2.8"
|
# serde_traitobject = "0.2.8"
|
||||||
|
|||||||
31
\
Normal file
31
\
Normal file
@@ -0,0 +1,31 @@
|
|||||||
|
use std::collections::HashMap;
|
||||||
|
|
||||||
|
use super::Gui;
|
||||||
|
|
||||||
|
mod song_edit;
|
||||||
|
|
||||||
|
trait Window {
|
||||||
|
fn show(&mut self, gui: &mut Gui, ctx: &egui::Context, open: &mut bool) -> anyhow::Result<()>;
|
||||||
|
fn name(&self) -> &'static str;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub enum WindowIndex {
|
||||||
|
SongEdit
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
pub struct WindowManager {
|
||||||
|
windows: HashMap<WindowIndex, Box<dyn Window>>
|
||||||
|
}
|
||||||
|
|
||||||
|
impl WindowManager {
|
||||||
|
pub fn new() -> Self {
|
||||||
|
let mut windows = HashMap::new();
|
||||||
|
windows.instert(WindowIndex::SongEdit, Box::<song_edit::GuiSongEditor>::default());
|
||||||
|
Self {
|
||||||
|
windows: HashMap::from_iter([
|
||||||
|
(WindowIndex::SongEdit, Box::<song_edit::GuiSongEditor>::default())
|
||||||
|
])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
18
scripts/build-release.sh
Executable file
18
scripts/build-release.sh
Executable file
@@ -0,0 +1,18 @@
|
|||||||
|
#!/usr/bin/bash
|
||||||
|
|
||||||
|
if [[ -z "$1" ]]; then
|
||||||
|
echo "Please suppy a version: 0.0.0[a | b | rc-0]"
|
||||||
|
exit
|
||||||
|
fi
|
||||||
|
|
||||||
|
set -e
|
||||||
|
|
||||||
|
cargo build --release --target x86_64-pc-windows-gnu
|
||||||
|
cargo build --release --target x86_64-unknown-linux-gnu
|
||||||
|
cargo build --release --target aarch64-unknown-linux-gnu
|
||||||
|
|
||||||
|
strip --strip-unneeded ./target/x86_64-pc-windows-gnu/release/mcmg.exe -o ./target/mcmg_win32.exe
|
||||||
|
strip --strip-unneeded ./target/x86_64-unknown-linux-gnu/release/mcmg -o ./target/mcmg_linux_x86_64
|
||||||
|
aarch64-linux-gnu-strip --strip-unneeded ./target/aarch64-unknown-linux-gnu/release/mcmg -o ./target/mcmg_linux_aarch64
|
||||||
|
cp ./scripts/setup-template.sh "./target/mcmg-setup-$1.sh"
|
||||||
|
cp ./scripts/setup-template.ps1 "./target/mcmg-setup-$1.ps1"
|
||||||
23
scripts/setup-template.ps1
Normal file
23
scripts/setup-template.ps1
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
|
||||||
|
$MyInvocation.MyCommand.Name -match '([0-9]+\.[0-9]+\.[0-9]+([ab]|(rc[-]*[0-9]*)))'
|
||||||
|
$Ver = $Matches[1]
|
||||||
|
|
||||||
|
|
||||||
|
if (-not (Get-Command ffmpeg -ErrorAction SilentlyContinue)) {
|
||||||
|
winget install Gyan.FFmpeg
|
||||||
|
}
|
||||||
|
|
||||||
|
if (-not (Get-Command "yt-dlp" -ErrorAction SilentlyContinue)) {
|
||||||
|
winget install "yt-dlp.yt-dlp"
|
||||||
|
}
|
||||||
|
|
||||||
|
if (-not (Get-Command spotdl -ErrorAction SilentlyContinue)) {
|
||||||
|
if (-not (Get-Command python -ErrorAction SilentlyContinue)) {
|
||||||
|
winget install "Python.Python.3.12"
|
||||||
|
}
|
||||||
|
python -m pip install spotdl
|
||||||
|
}
|
||||||
|
|
||||||
|
$url = "https://git.mcorangehq.xyz/XOR64/music/releases/download/$Ver/mcmg_win32.exe"
|
||||||
|
|
||||||
|
Invoke-WebRequest -Uri $url -OutFile "mcmg.exe"
|
||||||
42
scripts/setup-template.sh
Executable file
42
scripts/setup-template.sh
Executable file
@@ -0,0 +1,42 @@
|
|||||||
|
#!/usr/bin/bash
|
||||||
|
|
||||||
|
set -e
|
||||||
|
|
||||||
|
PROG_VER=$(echo $0 | grep -o -E "[0-9]+\.[0-9]+\.[0-9]+([ab]|(rc[-]*[0-9]*))")
|
||||||
|
|
||||||
|
echo $PROG_VER
|
||||||
|
|
||||||
|
function cmd_exists() {
|
||||||
|
if ! command -v $1 &> /dev/null
|
||||||
|
then
|
||||||
|
return 1
|
||||||
|
else
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
if cmd_exists "pacman"; then
|
||||||
|
if cmd_exists "yay"; then
|
||||||
|
yay -Sy --needed ffmpeg yt-dlp spotdl curl
|
||||||
|
else
|
||||||
|
sudo pacman -Sy --needed ffmpeg yt-dlp python python-pip python-pipx curl
|
||||||
|
pipx install spotdl
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
if cmd_exists "apt"; then
|
||||||
|
sudo apt-get update
|
||||||
|
sudo apt-get install python3 python3-pip ffmpeg curl
|
||||||
|
|
||||||
|
# updates all python packages, uncomment if you get errors for packages
|
||||||
|
# pip3 freeze | grep -v '^\-e' | cut -d = -f 1 | xargs -n1 pip3 install -U
|
||||||
|
python3 -m pip install --upgrade pip
|
||||||
|
python3 -m pip install spotdl
|
||||||
|
python3 -m pip install yt-dlp
|
||||||
|
fi
|
||||||
|
|
||||||
|
curl "https://git.mcorangehq.xyz/XOR64/music/releases/download/${PROG_VER}/mcmg_linux_x86_64" -o mcmg
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@@ -1,254 +0,0 @@
|
|||||||
mod nav_bar;
|
|
||||||
mod song_edit_window;
|
|
||||||
|
|
||||||
|
|
||||||
use std::collections::HashSet;
|
|
||||||
|
|
||||||
use egui::{Button, Color32, Label, RichText, Sense};
|
|
||||||
use egui_extras::{Column, TableBuilder};
|
|
||||||
use song_edit_window::{GuiError, GuiImportPlaylist, GuiNewSong};
|
|
||||||
|
|
||||||
use crate::{config::{Config, ConfigWrapper}, downloader::Downloader, manifest::{song::{Song, SongType}, Manifest}};
|
|
||||||
|
|
||||||
use self::song_edit_window::GuiSongEditor;
|
|
||||||
|
|
||||||
#[derive(Debug, Default)]
|
|
||||||
pub struct Gui {
|
|
||||||
manifest: Manifest,
|
|
||||||
song_edit_w: GuiSongEditor,
|
|
||||||
new_song_w: GuiNewSong,
|
|
||||||
import_playlist_w: GuiImportPlaylist,
|
|
||||||
error_w: GuiError,
|
|
||||||
filter: String,
|
|
||||||
downloader: Downloader,
|
|
||||||
cfg: ConfigWrapper,
|
|
||||||
downloading: bool,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Gui {
|
|
||||||
fn new(_: &eframe::CreationContext<'_>, manifest: Manifest, downloader: Downloader, cfg: ConfigWrapper) -> Self {
|
|
||||||
Self {
|
|
||||||
manifest,
|
|
||||||
downloader,
|
|
||||||
cfg,
|
|
||||||
..Default::default()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn start(manifest: Manifest, downloader: Downloader, cfg: ConfigWrapper) -> anyhow::Result<()> {
|
|
||||||
let native_options = eframe::NativeOptions {
|
|
||||||
viewport: egui::ViewportBuilder::default()
|
|
||||||
.with_inner_size([400.0, 300.0])
|
|
||||||
.with_min_inner_size([300.0, 220.0]),
|
|
||||||
// .with_icon(
|
|
||||||
// // NOTE: Adding an icon is optional
|
|
||||||
// eframe::icon_data::from_png_bytes(&include_bytes!("../assets/icon-256.png")[..])
|
|
||||||
// .expect("Failed to load icon"),
|
|
||||||
// ),
|
|
||||||
..Default::default()
|
|
||||||
};
|
|
||||||
|
|
||||||
if let Err(e) = eframe::run_native(
|
|
||||||
"eframe template",
|
|
||||||
native_options,
|
|
||||||
Box::new(|cc| Box::new(Gui::new(cc, manifest, downloader, cfg))),
|
|
||||||
) {
|
|
||||||
log::error!("Failed to create window: {e}");
|
|
||||||
};
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
pub fn throw_error(&mut self, text: impl ToString) {
|
|
||||||
self.error_w.is_open = true;
|
|
||||||
self.error_w.text = text.to_string();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl eframe::App for Gui {
|
|
||||||
fn update(&mut self, ctx: &egui::Context, frame: &mut eframe::Frame) {
|
|
||||||
self.draw_nav(ctx, frame);
|
|
||||||
self.draw_song_edit_window(ctx, frame);
|
|
||||||
self.draw_new_song_window(ctx, frame);
|
|
||||||
self.draw_import_playlist_window(ctx, frame);
|
|
||||||
self.draw_error_window(ctx, frame);
|
|
||||||
|
|
||||||
egui::CentralPanel::default().show(ctx, |ui| {
|
|
||||||
// The central panel the region left after adding TopPanel's and SidePanel's
|
|
||||||
//ui.heading(format!("Songs ({})", self.manifest.get_song_count()));
|
|
||||||
|
|
||||||
let fltr_by;
|
|
||||||
let filter_clean;
|
|
||||||
if self.filter.starts_with("playlist:") {
|
|
||||||
fltr_by = "playlist";
|
|
||||||
filter_clean = self.filter.strip_prefix("playlist:").unwrap_or("").to_string().to_lowercase();
|
|
||||||
} else if self.filter.starts_with("source:") {
|
|
||||||
fltr_by = "source";
|
|
||||||
filter_clean = self.filter.strip_prefix("source:").unwrap_or("").to_string().to_lowercase();
|
|
||||||
} else if self.filter.starts_with("url:") {
|
|
||||||
fltr_by = "url";
|
|
||||||
filter_clean = self.filter.strip_prefix("url:").unwrap_or("").to_string();
|
|
||||||
} else {
|
|
||||||
fltr_by = "";
|
|
||||||
filter_clean = self.filter.clone();
|
|
||||||
}
|
|
||||||
|
|
||||||
ui.vertical(|ui| {
|
|
||||||
ui.horizontal(|ui| {
|
|
||||||
ui.colored_label(Color32::from_hex("#4444aa").unwrap(), "Filter: ");
|
|
||||||
ui.text_edit_singleline(&mut self.filter);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
ui.vertical(|ui| {
|
|
||||||
let available_height = ui.available_height();
|
|
||||||
let table = TableBuilder::new(ui)
|
|
||||||
.striped(true)
|
|
||||||
.cell_layout(egui::Layout::left_to_right(egui::Align::Center))
|
|
||||||
.resizable(true)
|
|
||||||
//.column(Column::auto())
|
|
||||||
.column(Column::auto())
|
|
||||||
//.column(
|
|
||||||
// Column::remainder()
|
|
||||||
// .at_least(40.0)
|
|
||||||
// .clip(true)
|
|
||||||
// .resizable(true),
|
|
||||||
//)
|
|
||||||
.column(Column::auto())
|
|
||||||
.column(Column::remainder())
|
|
||||||
//.column(Column::remainder())
|
|
||||||
.min_scrolled_height(0.0)
|
|
||||||
.max_scroll_height(available_height)
|
|
||||||
.sense(egui::Sense::click());
|
|
||||||
|
|
||||||
let playlists = self.manifest.get_playlists().clone();
|
|
||||||
|
|
||||||
let songs = {
|
|
||||||
let mut songs = Vec::new();
|
|
||||||
for (pname, p) in playlists {
|
|
||||||
for (sname, s) in p {
|
|
||||||
songs.push((pname.clone(), sname, s))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
songs
|
|
||||||
};
|
|
||||||
|
|
||||||
table.header(20.0, |mut header| {
|
|
||||||
// header.col(|_|{});
|
|
||||||
header.col(|ui| {
|
|
||||||
ui.strong("Playlist");
|
|
||||||
});
|
|
||||||
header.col(|ui| {
|
|
||||||
ui.strong("Source");
|
|
||||||
});
|
|
||||||
header.col(|ui| {
|
|
||||||
ui.strong("Name");
|
|
||||||
});
|
|
||||||
}).body(|mut body| {
|
|
||||||
for (pname, sname, s) in songs {
|
|
||||||
if fltr_by == "playlist" && !filter_clean.is_empty() {
|
|
||||||
if !pname.to_lowercase().contains(&filter_clean) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
} else if fltr_by == "type" && !filter_clean.is_empty(){
|
|
||||||
if !s.get_type().to_string().to_lowercase().contains(&filter_clean) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
} else if fltr_by == "url" && !filter_clean.is_empty(){
|
|
||||||
if !s.get_url_str().contains(&filter_clean) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
} else if !filter_clean.is_empty() {
|
|
||||||
if !sname.to_lowercase().contains(&filter_clean) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
body.row(18.0, |mut row| {
|
|
||||||
|
|
||||||
row.col(|ui| {
|
|
||||||
ui.label(pname.clone())
|
|
||||||
.context_menu(|ui| context_menu(self, ui, &pname, &sname, &s));
|
|
||||||
});
|
|
||||||
row.col(|ui| {
|
|
||||||
let color =
|
|
||||||
match s.get_type() {
|
|
||||||
SongType::Youtube => Color32::from_hex("#FF0000").unwrap(),
|
|
||||||
SongType::Spotify => Color32::from_hex("#1db954").unwrap(),
|
|
||||||
SongType::Soundcloud => Color32::from_hex("#F26F23").unwrap()
|
|
||||||
};
|
|
||||||
|
|
||||||
ui.colored_label(color, s.get_type().to_string())
|
|
||||||
.context_menu(|ui| context_menu(self, ui, &pname, &sname, &s));
|
|
||||||
});
|
|
||||||
row.col(|ui| {
|
|
||||||
ui.hyperlink_to(sname.clone(), s.get_url_str())
|
|
||||||
.context_menu(|ui| context_menu(self, ui, &pname, &sname, &s));
|
|
||||||
});
|
|
||||||
|
|
||||||
row.response()
|
|
||||||
.context_menu(|ui| context_menu(self, ui, &pname, &sname, &s));
|
|
||||||
|
|
||||||
fn context_menu(this: &mut Gui, ui: &mut egui::Ui, pname: &String, sname: &String, song: &Song) {
|
|
||||||
if ui.button("Edit").clicked() {
|
|
||||||
this.song_edit_w.song = (
|
|
||||||
pname.clone(),
|
|
||||||
sname.clone(),
|
|
||||||
);
|
|
||||||
this.song_edit_w.is_open = true;
|
|
||||||
this.song_edit_w.ed_name = sname.clone();
|
|
||||||
this.song_edit_w.ed_url = song.get_url_str().clone();
|
|
||||||
ui.close_menu()
|
|
||||||
}
|
|
||||||
|
|
||||||
if ui.button("Download").clicked() {
|
|
||||||
if let Err(e) = this.downloader.download_song_nb(&this.cfg, pname, sname, song, this.manifest.get_format()) {
|
|
||||||
log::error!("{e}");
|
|
||||||
this.throw_error(format!("Failed to download song {sname}: {e}"));
|
|
||||||
}
|
|
||||||
ui.close_menu()
|
|
||||||
}
|
|
||||||
|
|
||||||
if ui.button("Open Source").clicked() {
|
|
||||||
if let Err(e) = open::that(song.get_url_str()) {
|
|
||||||
log::error!("{e}");
|
|
||||||
this.throw_error(format!("Failed to open song source: {e}"));
|
|
||||||
}
|
|
||||||
ui.close_menu()
|
|
||||||
}
|
|
||||||
if ui.button("Play").clicked() {
|
|
||||||
let p = crate::util::get_song_path(pname, sname, this.manifest.get_format());
|
|
||||||
|
|
||||||
if !p.exists() {
|
|
||||||
this.throw_error(format!("Song does not exist on disk"));
|
|
||||||
} else if let Err(e) = open::that(p) {
|
|
||||||
log::error!("{e}");
|
|
||||||
this.throw_error(format!("Failed to play song: {e}"));
|
|
||||||
}
|
|
||||||
ui.close_menu()
|
|
||||||
}
|
|
||||||
if ui.button("Delete from disk").clicked() {
|
|
||||||
let p = crate::util::get_song_path(pname, sname, this.manifest.get_format());
|
|
||||||
if p.exists() {
|
|
||||||
if let Err(e) = std::fs::remove_file(p) {
|
|
||||||
this.throw_error(format!("Failed to delete file: {e}"));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
ui.close_menu();
|
|
||||||
}
|
|
||||||
if ui.button(RichText::new("Delete").color(Color32::RED)).clicked() {
|
|
||||||
this.throw_error("TODO");
|
|
||||||
ui.close_menu()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
});
|
|
||||||
});
|
|
||||||
ui.separator();
|
|
||||||
|
|
||||||
|
|
||||||
ui.with_layout(egui::Layout::bottom_up(egui::Align::LEFT), |ui| {
|
|
||||||
egui::warn_if_debug_build(ui);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,226 +0,0 @@
|
|||||||
|
|
||||||
use std::sync::Arc;
|
|
||||||
|
|
||||||
use egui::{text::{LayoutJob, TextWrapping}, Color32, Label, RichText, Style, TextBuffer, TextFormat, TextStyle};
|
|
||||||
|
|
||||||
|
|
||||||
use crate::manifest::{playlist::Playlist, song::{Song, SongType}};
|
|
||||||
|
|
||||||
use super::Gui;
|
|
||||||
|
|
||||||
#[derive(Debug, Default)]
|
|
||||||
pub struct GuiSongEditor {
|
|
||||||
pub is_open: bool,
|
|
||||||
pub song: (String, String),
|
|
||||||
pub ed_url: String,
|
|
||||||
pub ed_name: String,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Default)]
|
|
||||||
pub struct GuiNewSong {
|
|
||||||
pub is_open: bool,
|
|
||||||
ed_type: SongType,
|
|
||||||
ed_name: String,
|
|
||||||
ed_playlist: Option<String>,
|
|
||||||
ed_url: String,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Default)]
|
|
||||||
pub struct GuiImportPlaylist {
|
|
||||||
pub is_open: bool,
|
|
||||||
ed_name: String,
|
|
||||||
ed_url: String,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Default)]
|
|
||||||
pub struct GuiError {
|
|
||||||
pub is_open: bool,
|
|
||||||
pub text: String,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Gui {
|
|
||||||
pub fn draw_song_edit_window(&mut self, ctx: &egui::Context, _: &mut eframe::Frame) {
|
|
||||||
let mut save = false;
|
|
||||||
|
|
||||||
let (playlist, song_name) = self.song_edit_w.song.clone();
|
|
||||||
|
|
||||||
let Some(song) = self.manifest.get_song(&playlist, &song_name) else {
|
|
||||||
return;
|
|
||||||
};
|
|
||||||
let song = song.clone();
|
|
||||||
|
|
||||||
egui::Window::new("Song editor")
|
|
||||||
.open(&mut self.song_edit_w.is_open)
|
|
||||||
.show(ctx,
|
|
||||||
|ui| {
|
|
||||||
|
|
||||||
ui.horizontal(|ui| {
|
|
||||||
ui.spacing_mut().item_spacing.x = 0.0;
|
|
||||||
ui.label("[");
|
|
||||||
ui.hyperlink_to("link", song.get_url().unwrap());
|
|
||||||
ui.label("] ");
|
|
||||||
ui.colored_label(Color32::LIGHT_BLUE, &playlist);
|
|
||||||
ui.label(": ");
|
|
||||||
ui.label(&song_name)
|
|
||||||
});
|
|
||||||
|
|
||||||
ui.horizontal(|ui| {
|
|
||||||
ui.label("Type: ");
|
|
||||||
ui.label(&song.get_type().to_string());
|
|
||||||
});
|
|
||||||
|
|
||||||
ui.horizontal(|ui| {
|
|
||||||
ui.label("Name: ");
|
|
||||||
ui.text_edit_singleline(&mut self.song_edit_w.ed_name);
|
|
||||||
});
|
|
||||||
ui.horizontal(|ui| {
|
|
||||||
ui.label("Url: ");
|
|
||||||
ui.text_edit_singleline(&mut self.song_edit_w.ed_url);
|
|
||||||
});
|
|
||||||
|
|
||||||
if ui.button("Save").clicked() {
|
|
||||||
save = true;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
if save {
|
|
||||||
{
|
|
||||||
let Some(song) = self.manifest.get_song_mut(&playlist, &song_name) else {
|
|
||||||
return;
|
|
||||||
};
|
|
||||||
|
|
||||||
*song.get_url_str_mut() = self.song_edit_w.ed_url.clone();
|
|
||||||
}
|
|
||||||
|
|
||||||
let Some(playlist) = self.manifest.get_playlist_mut(&playlist) else {
|
|
||||||
return;
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
playlist.remove_song(&song_name);
|
|
||||||
playlist.add_song(self.song_edit_w.ed_name.clone(), song);
|
|
||||||
self.song_edit_w.is_open = false;
|
|
||||||
let _ = self.manifest.save(None);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn draw_new_song_window(&mut self, ctx: &egui::Context, _: &mut eframe::Frame) {
|
|
||||||
let mut save = false;
|
|
||||||
egui::Window::new("New song")
|
|
||||||
.open(&mut self.new_song_w.is_open)
|
|
||||||
.show(ctx, |ui| {
|
|
||||||
ui.horizontal(|ui| {
|
|
||||||
ui.label("Type: ");
|
|
||||||
egui::ComboBox::from_id_source("new_song_window_type")
|
|
||||||
.selected_text(format!("{:?}", self.new_song_w.ed_type))
|
|
||||||
.show_ui(ui, |ui| {
|
|
||||||
ui.selectable_value(&mut self.new_song_w.ed_type, SongType::Youtube, "Youtube");
|
|
||||||
ui.selectable_value(&mut self.new_song_w.ed_type, SongType::Spotify, "Spotify");
|
|
||||||
ui.selectable_value(&mut self.new_song_w.ed_type, SongType::Soundcloud, "Soundcloud");
|
|
||||||
}
|
|
||||||
);
|
|
||||||
});
|
|
||||||
|
|
||||||
ui.horizontal(|ui| {
|
|
||||||
ui.label("Name: ");
|
|
||||||
ui.text_edit_singleline(&mut self.new_song_w.ed_name);
|
|
||||||
});
|
|
||||||
ui.horizontal(|ui| {
|
|
||||||
ui.label("Playlist: ");
|
|
||||||
egui::ComboBox::from_id_source("new_song_window_playlist")
|
|
||||||
.selected_text(format!("{}", self.new_song_w.ed_playlist.clone().unwrap_or("".to_string())))
|
|
||||||
.show_ui(ui, |ui| {
|
|
||||||
for p in self.manifest.get_playlists().keys() {
|
|
||||||
ui.selectable_value(&mut self.new_song_w.ed_playlist, Option::Some(p.clone()), p.as_str());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
);
|
|
||||||
});
|
|
||||||
ui.horizontal(|ui| {
|
|
||||||
ui.label("Url: ");
|
|
||||||
ui.text_edit_singleline(&mut self.new_song_w.ed_url);
|
|
||||||
});
|
|
||||||
|
|
||||||
if ui.button("Save").clicked() {
|
|
||||||
save = true;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
if save {
|
|
||||||
let Some(playlist) = self.manifest.get_playlist_mut(&self.new_song_w.ed_playlist.clone().unwrap()) else {
|
|
||||||
panic!("couldnt find playlist from a preset playlist list????????????");
|
|
||||||
};
|
|
||||||
|
|
||||||
playlist.add_song(
|
|
||||||
self.new_song_w.ed_name.clone(),
|
|
||||||
Song::from_url_str(self.new_song_w.ed_url.clone()).unwrap().set_type(self.new_song_w.ed_type.clone()).clone()
|
|
||||||
);
|
|
||||||
|
|
||||||
|
|
||||||
let _ = self.manifest.save(None);
|
|
||||||
self.new_song_w.is_open = false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn draw_import_playlist_window(&mut self, ctx: &egui::Context, _: &mut eframe::Frame) {
|
|
||||||
let mut save = false;
|
|
||||||
egui::Window::new("Import Playlist")
|
|
||||||
.open(&mut self.import_playlist_w.is_open)
|
|
||||||
.show(ctx, |ui| {
|
|
||||||
ui.horizontal(|ui| {
|
|
||||||
ui.label("Type: Youtube");
|
|
||||||
});
|
|
||||||
|
|
||||||
ui.horizontal(|ui| {
|
|
||||||
ui.label("Name: ");
|
|
||||||
ui.text_edit_singleline(&mut self.import_playlist_w.ed_name);
|
|
||||||
});
|
|
||||||
ui.horizontal(|ui| {
|
|
||||||
ui.label("Url: ");
|
|
||||||
ui.text_edit_singleline(&mut self.import_playlist_w.ed_url);
|
|
||||||
});
|
|
||||||
|
|
||||||
if ui.button("Import").clicked() {
|
|
||||||
save = true;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
if save {
|
|
||||||
let name = self.import_playlist_w.ed_name.clone();
|
|
||||||
let url = self.import_playlist_w.ed_url.clone();
|
|
||||||
|
|
||||||
if self.manifest.get_playlist(&name).is_some() {
|
|
||||||
log::error!("Playlist {name} already exists");
|
|
||||||
self.throw_error(format!("Playlist {name} already exists"));
|
|
||||||
}
|
|
||||||
|
|
||||||
let songs = self.downloader.download_playlist_nb(&self.cfg, &url, &name, &self.manifest.get_format()).unwrap();
|
|
||||||
self.manifest.add_playlist(name.clone());
|
|
||||||
|
|
||||||
let playlist = self.manifest.get_playlist_mut(&name).expect("Unreachable");
|
|
||||||
|
|
||||||
for (sname, song) in songs {
|
|
||||||
log::info!("Added: {sname}");
|
|
||||||
playlist.add_song(sname, song);
|
|
||||||
}
|
|
||||||
let _ = self.manifest.save(None);
|
|
||||||
self.import_playlist_w.is_open = false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn draw_error_window(&mut self, ctx: &egui::Context, _: &mut eframe::Frame) {
|
|
||||||
egui::Window::new("ERROR!!!! D:")
|
|
||||||
.open(&mut self.error_w.is_open)
|
|
||||||
.show(ctx, |ui| {
|
|
||||||
ui.vertical(|ui| {
|
|
||||||
ui.label(RichText::new("Error:").size(30.0).color(Color32::RED));
|
|
||||||
ui.horizontal(|ui| {
|
|
||||||
ui.add(Label::new(self.error_w.text.clone()).wrap(true));
|
|
||||||
})
|
|
||||||
})
|
|
||||||
});
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -18,7 +18,7 @@ lazy_static!(
|
|||||||
static ref PROCESSES: Mutex<RwLock<HashMap<usize, Proc>>> = Mutex::new(RwLock::new(HashMap::new()));
|
static ref PROCESSES: Mutex<RwLock<HashMap<usize, Proc>>> = Mutex::new(RwLock::new(HashMap::new()));
|
||||||
);
|
);
|
||||||
|
|
||||||
#[derive(Debug, Default)]
|
#[derive(Debug, Default, Clone)]
|
||||||
pub struct Downloader {
|
pub struct Downloader {
|
||||||
count: usize,
|
count: usize,
|
||||||
nb_initial_song_count: usize,
|
nb_initial_song_count: usize,
|
||||||
|
|||||||
@@ -1,3 +1,5 @@
|
|||||||
|
#![feature(downcast_unchecked)]
|
||||||
|
|
||||||
use config::ConfigWrapper;
|
use config::ConfigWrapper;
|
||||||
|
|
||||||
|
|
||||||
@@ -6,11 +8,11 @@ mod manifest;
|
|||||||
mod logger;
|
mod logger;
|
||||||
mod downloader;
|
mod downloader;
|
||||||
mod util;
|
mod util;
|
||||||
mod commands;
|
|
||||||
mod prompt;
|
mod prompt;
|
||||||
mod config;
|
mod config;
|
||||||
mod constants;
|
mod constants;
|
||||||
mod process_manager;
|
mod process_manager;
|
||||||
|
mod ui;
|
||||||
|
|
||||||
#[tokio::main]
|
#[tokio::main]
|
||||||
async fn main() {
|
async fn main() {
|
||||||
@@ -27,5 +29,5 @@ async fn main() {
|
|||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
let _ = commands::command_run(&cfg, &mut manifest).await;
|
let _ = ui::cli::command_run(&cfg, &mut manifest).await;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -13,8 +13,6 @@ use serde::{Deserialize, Serialize};
|
|||||||
const DEFAULT_MANIFEST: &'static str = include_str!("../../manifest.default.json");
|
const DEFAULT_MANIFEST: &'static str = include_str!("../../manifest.default.json");
|
||||||
|
|
||||||
|
|
||||||
pub type SongName = String;
|
|
||||||
pub type Genre = HashMap<SongName, song::Song>;
|
|
||||||
|
|
||||||
#[allow(non_camel_case_types)]
|
#[allow(non_camel_case_types)]
|
||||||
#[derive(Debug, Serialize, Deserialize, Clone, Default)]
|
#[derive(Debug, Serialize, Deserialize, Clone, Default)]
|
||||||
@@ -43,7 +41,7 @@ impl Manifest {
|
|||||||
pub fn get_format(&self) -> &Format {
|
pub fn get_format(&self) -> &Format {
|
||||||
&self.format
|
&self.format
|
||||||
}
|
}
|
||||||
pub fn add_song(&mut self, playlist_name: &String, name: SongName, song: Song) -> Option<Song> {
|
pub fn add_song(&mut self, playlist_name: &String, name: String, song: Song) -> Option<Song> {
|
||||||
self.get_playlist_mut(playlist_name)?.add_song(name, song)
|
self.get_playlist_mut(playlist_name)?.add_song(name, song)
|
||||||
}
|
}
|
||||||
pub fn get_song(&self, playlist_name: &String, name: &String) -> Option<&Song> {
|
pub fn get_song(&self, playlist_name: &String, name: &String) -> Option<&Song> {
|
||||||
|
|||||||
@@ -13,9 +13,6 @@ pub struct Playlist {
|
|||||||
|
|
||||||
|
|
||||||
impl Playlist {
|
impl Playlist {
|
||||||
pub fn new() -> Self {
|
|
||||||
Self { ..Default::default() }
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn add_song(&mut self, name: String, song: Song) -> Option<Song> {
|
pub fn add_song(&mut self, name: String, song: Song) -> Option<Song> {
|
||||||
self.songs.insert(name, song)
|
self.songs.insert(name, song)
|
||||||
@@ -33,6 +30,7 @@ impl Playlist {
|
|||||||
&self.songs
|
&self.songs
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[allow(dead_code)]
|
||||||
pub fn get_songs_mut(&mut self) -> &mut HashMap<String, Song> {
|
pub fn get_songs_mut(&mut self) -> &mut HashMap<String, Song> {
|
||||||
&mut self.songs
|
&mut self.songs
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,22 +1,21 @@
|
|||||||
use std::{collections::HashMap, io::Write};
|
use std::{collections::HashMap, io::Write};
|
||||||
|
|
||||||
|
|
||||||
|
//pub(crate) fn simple_prompt(p: &str) -> String {
|
||||||
pub(crate) fn simple_prompt(p: &str) -> String {
|
//
|
||||||
|
// print!("{c}prompt{r}: {p} > ",
|
||||||
print!("{c}prompt{r}: {p} > ",
|
// c=anstyle::AnsiColor::Cyan.render_fg(),
|
||||||
c=anstyle::AnsiColor::Cyan.render_fg(),
|
// r=anstyle::Reset.render()
|
||||||
r=anstyle::Reset.render()
|
// );
|
||||||
);
|
//
|
||||||
|
// // I dont care if it fails
|
||||||
// I dont care if it fails
|
// let _ = std::io::stdout().flush();
|
||||||
let _ = std::io::stdout().flush();
|
//
|
||||||
|
// let mut buf = String::new();
|
||||||
let mut buf = String::new();
|
// let _ = std::io::stdin().read_line(&mut buf);
|
||||||
let _ = std::io::stdin().read_line(&mut buf);
|
//
|
||||||
|
// buf.trim().to_string()
|
||||||
buf.trim().to_string()
|
//}
|
||||||
}
|
|
||||||
|
|
||||||
#[allow(dead_code)]
|
#[allow(dead_code)]
|
||||||
pub(crate) fn prompt_with_list(p: &str, options: &[&str]) -> usize {
|
pub(crate) fn prompt_with_list(p: &str, options: &[&str]) -> usize {
|
||||||
@@ -47,33 +46,33 @@ pub(crate) fn prompt_with_list(p: &str, options: &[&str]) -> usize {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn prompt_with_list_or_str(p: &str, options: &[String]) -> String {
|
// pub(crate) fn prompt_with_list_or_str(p: &str, options: &[String]) -> String {
|
||||||
println!("{c}prompt{r}: {p} (select with number or input text)",
|
// println!("{c}prompt{r}: {p} (select with number or input text)",
|
||||||
c=anstyle::AnsiColor::Cyan.render_fg(),
|
// c=anstyle::AnsiColor::Cyan.render_fg(),
|
||||||
r=anstyle::Reset.render()
|
// r=anstyle::Reset.render()
|
||||||
);
|
// );
|
||||||
|
//
|
||||||
for (i, op) in options.iter().enumerate() {
|
// for (i, op) in options.iter().enumerate() {
|
||||||
println!(" - {}: {}", i, op);
|
// println!(" - {}: {}", i, op);
|
||||||
}
|
// }
|
||||||
|
//
|
||||||
print!("> ");
|
// print!("> ");
|
||||||
// I dont care if it fails
|
// // I dont care if it fails
|
||||||
let _ = std::io::stdout().flush();
|
// let _ = std::io::stdout().flush();
|
||||||
|
//
|
||||||
let mut buf = String::new();
|
// let mut buf = String::new();
|
||||||
let _ = std::io::stdin().read_line(&mut buf);
|
// let _ = std::io::stdin().read_line(&mut buf);
|
||||||
|
//
|
||||||
if let Ok(num) = buf.trim().parse::<usize>() {
|
// if let Ok(num) = buf.trim().parse::<usize>() {
|
||||||
if let Some(g) = options.get(num) {
|
// if let Some(g) = options.get(num) {
|
||||||
return g.clone();
|
// return g.clone();
|
||||||
} else {
|
// } else {
|
||||||
return prompt_with_list_or_str(p, options);
|
// return prompt_with_list_or_str(p, options);
|
||||||
}
|
// }
|
||||||
} else {
|
// } else {
|
||||||
return buf.trim().to_string();
|
// return buf.trim().to_string();
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
|
|
||||||
|
|
||||||
#[allow(dead_code)]
|
#[allow(dead_code)]
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
mod add;
|
mod add;
|
||||||
pub mod gui;
|
|
||||||
|
|
||||||
use crate::{config::{cli::CliCommand, ConfigWrapper}, downloader::Downloader, manifest::Manifest};
|
use crate::{config::{cli::CliCommand, ConfigWrapper}, downloader::Downloader, manifest::Manifest, ui::gui};
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
57
src/ui/gui/components/context_menu.rs
Normal file
57
src/ui/gui/components/context_menu.rs
Normal file
@@ -0,0 +1,57 @@
|
|||||||
|
use egui::{Color32, RichText};
|
||||||
|
use crate::{manifest::song::Song, ui::gui::windows::{song_edit::GuiSongEditor, WindowIndex}};
|
||||||
|
|
||||||
|
pub struct ContextMenu;
|
||||||
|
|
||||||
|
// NOTE: This should be a component but theres no easy way to do that, so we just make it folow the
|
||||||
|
// trait manually, ish, more like a convention
|
||||||
|
impl /* ComponentUi for */ ContextMenu {
|
||||||
|
pub fn ui(gui: &mut crate::ui::gui::Gui, ui: &mut egui::Ui, pname: &String, sname: &String, song: &Song) {
|
||||||
|
if ui.button("Edit").clicked() {
|
||||||
|
let w = gui.windows.get_window::<GuiSongEditor>(WindowIndex::SongEdit);
|
||||||
|
w.set_active_song(pname, sname, song.get_url_str());
|
||||||
|
gui.windows.open(WindowIndex::SongEdit, true);
|
||||||
|
ui.close_menu()
|
||||||
|
}
|
||||||
|
|
||||||
|
if ui.button("Download").clicked() {
|
||||||
|
if let Err(e) = gui.downloader.download_song_nb(&gui.cfg, pname, sname, song, gui.manifest.get_format()) {
|
||||||
|
log::error!("{e}");
|
||||||
|
gui.throw_error(format!("Failed to download song {sname}: {e}"));
|
||||||
|
}
|
||||||
|
ui.close_menu()
|
||||||
|
}
|
||||||
|
|
||||||
|
if ui.button("Open Source").clicked() {
|
||||||
|
if let Err(e) = open::that(song.get_url_str()) {
|
||||||
|
log::error!("{e}");
|
||||||
|
gui.throw_error(format!("Failed to open song source: {e}"));
|
||||||
|
}
|
||||||
|
ui.close_menu()
|
||||||
|
}
|
||||||
|
if ui.button("Play").clicked() {
|
||||||
|
let p = crate::util::get_song_path(pname, sname, gui.manifest.get_format());
|
||||||
|
|
||||||
|
if !p.exists() {
|
||||||
|
gui.throw_error(format!("Song does not exist on disk"));
|
||||||
|
} else if let Err(e) = open::that(p) {
|
||||||
|
log::error!("{e}");
|
||||||
|
gui.throw_error(format!("Failed to play song: {e}"));
|
||||||
|
}
|
||||||
|
ui.close_menu()
|
||||||
|
}
|
||||||
|
if ui.button("Delete from disk").clicked() {
|
||||||
|
let p = crate::util::get_song_path(pname, sname, gui.manifest.get_format());
|
||||||
|
if p.exists() {
|
||||||
|
if let Err(e) = std::fs::remove_file(p) {
|
||||||
|
gui.throw_error(format!("Failed to delete file: {e}"));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ui.close_menu();
|
||||||
|
}
|
||||||
|
if ui.button(RichText::new("Delete").color(Color32::RED)).clicked() {
|
||||||
|
gui.throw_error("TODO");
|
||||||
|
ui.close_menu()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
14
src/ui/gui/components/mod.rs
Normal file
14
src/ui/gui/components/mod.rs
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
use super::Gui;
|
||||||
|
|
||||||
|
|
||||||
|
pub mod nav;
|
||||||
|
pub mod song_list;
|
||||||
|
pub mod context_menu;
|
||||||
|
|
||||||
|
pub trait Component {
|
||||||
|
fn ui(gui: &mut Gui, ctx: &egui::Context);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub trait ComponentUi {
|
||||||
|
fn ui(gui: &mut Gui, ui: &mut egui::Ui);
|
||||||
|
}
|
||||||
@@ -1,11 +1,13 @@
|
|||||||
use egui::Hyperlink;
|
use crate::ui::gui::{windows::WindowIndex, Gui};
|
||||||
|
|
||||||
use super::Gui;
|
use super::Component;
|
||||||
|
|
||||||
|
|
||||||
impl Gui {
|
|
||||||
|
|
||||||
pub fn draw_nav(&mut self, ctx: &egui::Context, _: &mut eframe::Frame) {
|
pub struct NavBar;
|
||||||
|
|
||||||
|
impl Component for NavBar {
|
||||||
|
fn ui(gui: &mut Gui, ctx: &egui::Context) {
|
||||||
egui::TopBottomPanel::top("top_panel").show(ctx, |ui| {
|
egui::TopBottomPanel::top("top_panel").show(ctx, |ui| {
|
||||||
// The top panel is often a good place for a menu bar:
|
// The top panel is often a good place for a menu bar:
|
||||||
egui::menu::bar(ui, |ui| {
|
egui::menu::bar(ui, |ui| {
|
||||||
@@ -14,7 +16,7 @@ impl Gui {
|
|||||||
ctx.open_url(egui::OpenUrl::new_tab("https://git.mcorangehq.xyz/XOR64/music"));
|
ctx.open_url(egui::OpenUrl::new_tab("https://git.mcorangehq.xyz/XOR64/music"));
|
||||||
}
|
}
|
||||||
if ui.button("Save").clicked() {
|
if ui.button("Save").clicked() {
|
||||||
if let Err(e) = self.manifest.save(None) {
|
if let Err(e) = gui.manifest.save(None) {
|
||||||
log::error!("Failed to save manifest: {e}");
|
log::error!("Failed to save manifest: {e}");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -25,19 +27,19 @@ impl Gui {
|
|||||||
|
|
||||||
ui.menu_button("Song", |ui| {
|
ui.menu_button("Song", |ui| {
|
||||||
if ui.button("Add New").clicked() {
|
if ui.button("Add New").clicked() {
|
||||||
self.new_song_w.is_open = true;
|
gui.windows.open(WindowIndex::SongNew, true);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
ui.menu_button("Playlist", |ui| {
|
ui.menu_button("Playlist", |ui| {
|
||||||
if ui.button("Import").clicked() {
|
if ui.button("Import").clicked() {
|
||||||
self.import_playlist_w.is_open = true;
|
gui.windows.open(WindowIndex::ImportPlaylist, true);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
ui.menu_button("Downloader", |ui| {
|
ui.menu_button("Downloader", |ui| {
|
||||||
if ui.button("Download All").clicked() {
|
if ui.button("Download All").clicked() {
|
||||||
if let Err(e) = self.downloader.download_all_nb(&self.manifest, &self.cfg) {
|
if let Err(e) = gui.downloader.download_all_nb(&gui.manifest, &gui.cfg) {
|
||||||
log::error!("Err: {e}");
|
log::error!("Err: {e}");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -45,21 +47,22 @@ impl Gui {
|
|||||||
ui.add_space(16.0);
|
ui.add_space(16.0);
|
||||||
ui.with_layout(egui::Layout::bottom_up(egui::Align::RIGHT), |ui| {
|
ui.with_layout(egui::Layout::bottom_up(egui::Align::RIGHT), |ui| {
|
||||||
ui.horizontal(|ui| {
|
ui.horizontal(|ui| {
|
||||||
if self.downloader.get_songs_left_nb() > 0 {
|
if gui.downloader.get_songs_left_nb() > 0 {
|
||||||
self.downloading = true;
|
gui.downloading = true;
|
||||||
ui.label(format!("Downloading: {}/{}", self.downloader.get_songs_left_nb(), self.downloader.get_initial_song_count_nb()));
|
ui.label(format!("Downloading: {}/{}", gui.downloader.get_songs_left_nb(), gui.downloader.get_initial_song_count_nb()));
|
||||||
} else if self.downloading {
|
} else if gui.downloading {
|
||||||
let _ = notify_rust::Notification::new()
|
let _ = notify_rust::Notification::new()
|
||||||
.summary("Done downloading")
|
.summary("Done downloading")
|
||||||
.body("Your music has been downloaded")
|
.body("Your music has been downloaded")
|
||||||
.show();
|
.show();
|
||||||
self.downloading = false;
|
gui.downloading = false;
|
||||||
}
|
}
|
||||||
let _ = self.downloader.download_all_nb_poll(&self.cfg);
|
let _ = gui.downloader.download_all_nb_poll(&gui.cfg);
|
||||||
egui::widgets::global_dark_light_mode_buttons(ui);
|
egui::widgets::global_dark_light_mode_buttons(ui);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
130
src/ui/gui/components/song_list.rs
Normal file
130
src/ui/gui/components/song_list.rs
Normal file
@@ -0,0 +1,130 @@
|
|||||||
|
use egui::{Color32, RichText};
|
||||||
|
use egui_extras::{Column, TableBuilder};
|
||||||
|
|
||||||
|
use crate::manifest::song::SongType;
|
||||||
|
|
||||||
|
use super::{context_menu::ContextMenu, ComponentUi};
|
||||||
|
|
||||||
|
|
||||||
|
pub struct SongList;
|
||||||
|
|
||||||
|
impl ComponentUi for SongList {
|
||||||
|
fn ui(gui: &mut crate::ui::gui::Gui, ui: &mut egui::Ui) {
|
||||||
|
let fltr_by;
|
||||||
|
let filter_clean;
|
||||||
|
if gui.filter.starts_with("playlist:") {
|
||||||
|
fltr_by = "playlist";
|
||||||
|
filter_clean = gui.filter.strip_prefix("playlist:").unwrap_or("").to_string().to_lowercase();
|
||||||
|
} else if gui.filter.starts_with("source:") {
|
||||||
|
fltr_by = "source";
|
||||||
|
filter_clean = gui.filter.strip_prefix("source:").unwrap_or("").to_string().to_lowercase();
|
||||||
|
} else if gui.filter.starts_with("url:") {
|
||||||
|
fltr_by = "url";
|
||||||
|
filter_clean = gui.filter.strip_prefix("url:").unwrap_or("").to_string();
|
||||||
|
} else {
|
||||||
|
fltr_by = "";
|
||||||
|
filter_clean = gui.filter.clone();
|
||||||
|
}
|
||||||
|
|
||||||
|
ui.vertical(|ui| {
|
||||||
|
ui.horizontal(|ui| {
|
||||||
|
ui.colored_label(Color32::from_hex("#4444aa").unwrap(), "Filter: ");
|
||||||
|
ui.text_edit_singleline(&mut gui.filter);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
ui.vertical(|ui| {
|
||||||
|
let available_height = ui.available_height();
|
||||||
|
let table = TableBuilder::new(ui)
|
||||||
|
.striped(true)
|
||||||
|
.cell_layout(egui::Layout::left_to_right(egui::Align::Center))
|
||||||
|
.resizable(true)
|
||||||
|
//.column(Column::auto())
|
||||||
|
.column(Column::auto())
|
||||||
|
//.column(
|
||||||
|
// Column::remainder()
|
||||||
|
// .at_least(40.0)
|
||||||
|
// .clip(true)
|
||||||
|
// .resizable(true),
|
||||||
|
//)
|
||||||
|
.column(Column::auto())
|
||||||
|
.column(Column::remainder())
|
||||||
|
//.column(Column::remainder())
|
||||||
|
.min_scrolled_height(0.0)
|
||||||
|
.max_scroll_height(available_height)
|
||||||
|
.sense(egui::Sense::click());
|
||||||
|
|
||||||
|
let playlists = gui.manifest.get_playlists().clone();
|
||||||
|
|
||||||
|
let songs = {
|
||||||
|
let mut songs = Vec::new();
|
||||||
|
for (pname, p) in playlists {
|
||||||
|
for (sname, s) in p {
|
||||||
|
songs.push((pname.clone(), sname, s))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
songs
|
||||||
|
};
|
||||||
|
|
||||||
|
table.header(20.0, |mut header| {
|
||||||
|
// header.col(|_|{});
|
||||||
|
header.col(|ui| {
|
||||||
|
ui.strong("Playlist");
|
||||||
|
});
|
||||||
|
header.col(|ui| {
|
||||||
|
ui.strong("Source");
|
||||||
|
});
|
||||||
|
header.col(|ui| {
|
||||||
|
ui.strong("Name");
|
||||||
|
});
|
||||||
|
}).body(|mut body| {
|
||||||
|
for (pname, sname, s) in songs {
|
||||||
|
if fltr_by == "playlist" && !filter_clean.is_empty() {
|
||||||
|
if !pname.to_lowercase().contains(&filter_clean) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
} else if fltr_by == "type" && !filter_clean.is_empty(){
|
||||||
|
if !s.get_type().to_string().to_lowercase().contains(&filter_clean) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
} else if fltr_by == "url" && !filter_clean.is_empty(){
|
||||||
|
if !s.get_url_str().contains(&filter_clean) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
} else if !filter_clean.is_empty() {
|
||||||
|
if !sname.to_lowercase().contains(&filter_clean) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
body.row(18.0, |mut row| {
|
||||||
|
|
||||||
|
row.col(|ui| {
|
||||||
|
ui.label(pname.clone())
|
||||||
|
.context_menu(|ui| ContextMenu::ui(gui, ui, &pname, &sname, &s));
|
||||||
|
});
|
||||||
|
row.col(|ui| {
|
||||||
|
let color =
|
||||||
|
match s.get_type() {
|
||||||
|
SongType::Youtube => Color32::from_hex("#FF0000").unwrap(),
|
||||||
|
SongType::Spotify => Color32::from_hex("#1db954").unwrap(),
|
||||||
|
SongType::Soundcloud => Color32::from_hex("#F26F23").unwrap()
|
||||||
|
};
|
||||||
|
|
||||||
|
ui.colored_label(color, s.get_type().to_string())
|
||||||
|
.context_menu(|ui| ContextMenu::ui(gui, ui, &pname, &sname, &s));
|
||||||
|
});
|
||||||
|
row.col(|ui| {
|
||||||
|
ui.hyperlink_to(sname.clone(), s.get_url_str())
|
||||||
|
.context_menu(|ui| ContextMenu::ui(gui, ui, &pname, &sname, &s));
|
||||||
|
});
|
||||||
|
|
||||||
|
row.response()
|
||||||
|
.context_menu(|ui| ContextMenu::ui(gui, ui, &pname, &sname, &s));
|
||||||
|
|
||||||
|
})
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
86
src/ui/gui/mod.rs
Normal file
86
src/ui/gui/mod.rs
Normal file
@@ -0,0 +1,86 @@
|
|||||||
|
mod windows;
|
||||||
|
mod components;
|
||||||
|
|
||||||
|
use components::{Component, ComponentUi};
|
||||||
|
use windows::{State, WindowIndex, WindowManager};
|
||||||
|
use crate::{config::ConfigWrapper, downloader::Downloader, manifest::Manifest};
|
||||||
|
|
||||||
|
|
||||||
|
#[derive(Debug, Default)]
|
||||||
|
pub struct Gui {
|
||||||
|
windows: WindowManager,
|
||||||
|
manifest: Manifest,
|
||||||
|
filter: String,
|
||||||
|
downloader: Downloader,
|
||||||
|
cfg: ConfigWrapper,
|
||||||
|
downloading: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Gui {
|
||||||
|
fn new(_: &eframe::CreationContext<'_>, manifest: Manifest, downloader: Downloader, cfg: ConfigWrapper) -> Self {
|
||||||
|
Self {
|
||||||
|
manifest,
|
||||||
|
downloader,
|
||||||
|
cfg,
|
||||||
|
windows: windows::WindowManager::new(),
|
||||||
|
..Default::default()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn start(manifest: Manifest, downloader: Downloader, cfg: ConfigWrapper) -> anyhow::Result<()> {
|
||||||
|
let native_options = eframe::NativeOptions {
|
||||||
|
viewport: egui::ViewportBuilder::default()
|
||||||
|
.with_inner_size([400.0, 300.0])
|
||||||
|
.with_min_inner_size([300.0, 220.0]),
|
||||||
|
// .with_icon(
|
||||||
|
// // NOTE: Adding an icon is optional
|
||||||
|
// eframe::icon_data::from_png_bytes(&include_bytes!("../assets/icon-256.png")[..])
|
||||||
|
// .expect("Failed to load icon"),
|
||||||
|
// ),
|
||||||
|
..Default::default()
|
||||||
|
};
|
||||||
|
|
||||||
|
if let Err(e) = eframe::run_native(
|
||||||
|
"eframe template",
|
||||||
|
native_options,
|
||||||
|
Box::new(|cc| Box::new(Gui::new(cc, manifest, downloader, cfg))),
|
||||||
|
) {
|
||||||
|
log::error!("Failed to create window: {e}");
|
||||||
|
};
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
pub fn throw_error(&mut self, text: impl ToString) {
|
||||||
|
let w = self.windows.get_window::<windows::error::GuiError>(WindowIndex::Error);
|
||||||
|
w.set_error_message(text);
|
||||||
|
self.windows.open(WindowIndex::Error, true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl eframe::App for Gui {
|
||||||
|
fn update(&mut self, ctx: &egui::Context, _: &mut eframe::Frame) {
|
||||||
|
components::nav::NavBar::ui(self, ctx);
|
||||||
|
{
|
||||||
|
let mut state = State {
|
||||||
|
cfg: self.cfg.clone(),
|
||||||
|
downloader: self.downloader.clone(),
|
||||||
|
manifest: self.manifest.clone(),
|
||||||
|
};
|
||||||
|
self.windows.ui(&mut state, ctx).unwrap();
|
||||||
|
self.cfg = state.cfg;
|
||||||
|
self.downloader = state.downloader;
|
||||||
|
self.manifest = state.manifest;
|
||||||
|
}
|
||||||
|
|
||||||
|
egui::CentralPanel::default().show(ctx, |ui| {
|
||||||
|
// The central panel the region left after adding TopPanel's and SidePanel's
|
||||||
|
//ui.heading(format!("Songs ({})", self.manifest.get_song_count()));
|
||||||
|
components::song_list::SongList::ui(self, ui);
|
||||||
|
ui.separator();
|
||||||
|
|
||||||
|
ui.with_layout(egui::Layout::bottom_up(egui::Align::LEFT), |ui| {
|
||||||
|
egui::warn_if_debug_build(ui);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
32
src/ui/gui/windows/error.rs
Normal file
32
src/ui/gui/windows/error.rs
Normal file
@@ -0,0 +1,32 @@
|
|||||||
|
use egui::{Color32, Label, RichText};
|
||||||
|
|
||||||
|
use super::{State, Window};
|
||||||
|
|
||||||
|
|
||||||
|
#[derive(Debug, Default)]
|
||||||
|
pub struct GuiError {
|
||||||
|
text: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
impl Window for GuiError {
|
||||||
|
fn ui(&mut self, _: &mut State, ctx: &egui::Context, open: &mut bool) -> anyhow::Result<()> {
|
||||||
|
egui::Window::new("ERROR!!!! D:")
|
||||||
|
.open(open)
|
||||||
|
.show(ctx, |ui| {
|
||||||
|
ui.vertical(|ui| {
|
||||||
|
ui.label(RichText::new("Error:").size(30.0).color(Color32::RED));
|
||||||
|
ui.horizontal(|ui| {
|
||||||
|
ui.add(Label::new(self.text.clone()).wrap(true));
|
||||||
|
})
|
||||||
|
})
|
||||||
|
});
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl GuiError {
|
||||||
|
pub fn set_error_message<S: ToString>(&mut self, text: S) {
|
||||||
|
self.text = text.to_string();
|
||||||
|
}
|
||||||
|
}
|
||||||
59
src/ui/gui/windows/import_playlist.rs
Normal file
59
src/ui/gui/windows/import_playlist.rs
Normal file
@@ -0,0 +1,59 @@
|
|||||||
|
use super::{State, Window};
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
#[derive(Debug, Default)]
|
||||||
|
pub struct GuiImportPlaylist {
|
||||||
|
ed_name: String,
|
||||||
|
ed_url: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
impl Window for GuiImportPlaylist {
|
||||||
|
fn ui(&mut self, state: &mut State, ctx: &egui::Context, open: &mut bool) -> anyhow::Result<()> {
|
||||||
|
let mut save = false;
|
||||||
|
egui::Window::new("Import Playlist")
|
||||||
|
.open(open)
|
||||||
|
.show(ctx, |ui| {
|
||||||
|
ui.horizontal(|ui| {
|
||||||
|
ui.label("Type: Youtube");
|
||||||
|
});
|
||||||
|
|
||||||
|
ui.horizontal(|ui| {
|
||||||
|
ui.label("Name: ");
|
||||||
|
ui.text_edit_singleline(&mut self.ed_name);
|
||||||
|
});
|
||||||
|
ui.horizontal(|ui| {
|
||||||
|
ui.label("Url: ");
|
||||||
|
ui.text_edit_singleline(&mut self.ed_url);
|
||||||
|
});
|
||||||
|
|
||||||
|
if ui.button("Import").clicked() {
|
||||||
|
save = true;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
if save {
|
||||||
|
let name = self.ed_name.clone();
|
||||||
|
let url = self.ed_url.clone();
|
||||||
|
|
||||||
|
if state.manifest.get_playlist(&name).is_some() {
|
||||||
|
log::error!("Playlist {name} already exists");
|
||||||
|
}
|
||||||
|
|
||||||
|
let songs = state.downloader.download_playlist_nb(&state.cfg, &url, &name, &state.manifest.get_format()).unwrap();
|
||||||
|
state.manifest.add_playlist(name.clone());
|
||||||
|
|
||||||
|
let playlist = state.manifest.get_playlist_mut(&name).expect("Unreachable");
|
||||||
|
|
||||||
|
for (sname, song) in songs {
|
||||||
|
log::info!("Added: {sname}");
|
||||||
|
playlist.add_song(sname, song);
|
||||||
|
}
|
||||||
|
let _ = state.manifest.save(None);
|
||||||
|
*open = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
76
src/ui/gui/windows/mod.rs
Normal file
76
src/ui/gui/windows/mod.rs
Normal file
@@ -0,0 +1,76 @@
|
|||||||
|
use std::collections::HashMap;
|
||||||
|
use crate::{config::ConfigWrapper, downloader::Downloader, manifest::Manifest};
|
||||||
|
|
||||||
|
pub mod song_edit;
|
||||||
|
pub mod error;
|
||||||
|
pub mod import_playlist;
|
||||||
|
pub mod song_new;
|
||||||
|
|
||||||
|
pub trait Window: std::fmt::Debug {
|
||||||
|
fn ui(&mut self, state: &mut State, ctx: &egui::Context, open: &mut bool) -> anyhow::Result<()>;
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Hash, Eq, PartialEq, Clone, Copy)]
|
||||||
|
pub enum WindowIndex {
|
||||||
|
Error,
|
||||||
|
ImportPlaylist,
|
||||||
|
SongEdit,
|
||||||
|
SongNew,
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
#[derive(Debug,Default)]
|
||||||
|
pub struct WindowManager {
|
||||||
|
opened: HashMap<WindowIndex, bool>,
|
||||||
|
windows: HashMap<WindowIndex, Box<dyn Window>>
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct State {
|
||||||
|
pub downloader: Downloader,
|
||||||
|
pub manifest: Manifest,
|
||||||
|
pub cfg: ConfigWrapper,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl WindowManager {
|
||||||
|
pub fn new() -> Self {
|
||||||
|
let mut windows: HashMap<WindowIndex, Box<dyn Window>> = HashMap::new();
|
||||||
|
windows.insert(WindowIndex::Error, Box::<error::GuiError>::default());
|
||||||
|
windows.insert(WindowIndex::ImportPlaylist, Box::<import_playlist::GuiImportPlaylist>::default());
|
||||||
|
windows.insert(WindowIndex::SongEdit, Box::<song_edit::GuiSongEditor>::default());
|
||||||
|
windows.insert(WindowIndex::SongNew, Box::<song_new::GuiNewSong>::default());
|
||||||
|
Self {
|
||||||
|
windows,
|
||||||
|
..Default::default()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[allow(dead_code)]
|
||||||
|
pub fn is_open(&self, id: &WindowIndex) -> bool {
|
||||||
|
*self.opened.get(id).unwrap()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn open(&mut self, id: WindowIndex, open: bool) {
|
||||||
|
self.opened.insert(id, open);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn ui(&mut self, state: &mut State, ctx: &egui::Context) -> anyhow::Result<()> {
|
||||||
|
for (id, window) in &mut self.windows {
|
||||||
|
if !self.opened.contains_key(&id) {
|
||||||
|
self.opened.insert(*id, false);
|
||||||
|
}
|
||||||
|
let open = self.opened.get_mut(id).unwrap();
|
||||||
|
if let Err(e) = window.ui(state, ctx, open) {
|
||||||
|
log::error!("Window {id:?} errored: {e}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_window<T: Window + 'static>(&mut self, id: WindowIndex) -> &mut Box<T> {
|
||||||
|
let w = self.windows.get_mut(&id).unwrap();
|
||||||
|
unsafe {
|
||||||
|
crate::util::as_any_mut(w).downcast_mut_unchecked()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
95
src/ui/gui/windows/song_edit.rs
Normal file
95
src/ui/gui/windows/song_edit.rs
Normal file
@@ -0,0 +1,95 @@
|
|||||||
|
use anyhow::{Result, bail};
|
||||||
|
use egui::Color32;
|
||||||
|
use crate::ui::gui::Gui;
|
||||||
|
|
||||||
|
use super::{State, Window};
|
||||||
|
|
||||||
|
|
||||||
|
#[derive(Debug, Default)]
|
||||||
|
pub struct GuiSongEditor {
|
||||||
|
song: (String, String),
|
||||||
|
ed_url: String,
|
||||||
|
ed_name: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
impl Window for GuiSongEditor {
|
||||||
|
fn ui(&mut self, state: &mut State, ctx: &egui::Context, open: &mut bool) -> anyhow::Result<()> {
|
||||||
|
let mut save = false;
|
||||||
|
let (playlist, song_name) = self.song.clone();
|
||||||
|
|
||||||
|
if playlist.is_empty() {
|
||||||
|
return Ok(());
|
||||||
|
}
|
||||||
|
|
||||||
|
let Some(song) = state.manifest.get_song(&playlist, &song_name) else {
|
||||||
|
bail!("Failed to get song (1)");
|
||||||
|
};
|
||||||
|
let song = song.clone();
|
||||||
|
|
||||||
|
egui::Window::new("Song editor")
|
||||||
|
.open(open)
|
||||||
|
.show(ctx,
|
||||||
|
|ui| {
|
||||||
|
|
||||||
|
ui.horizontal(|ui| {
|
||||||
|
ui.spacing_mut().item_spacing.x = 0.0;
|
||||||
|
ui.label("[");
|
||||||
|
ui.hyperlink_to("link", song.get_url().unwrap());
|
||||||
|
ui.label("] ");
|
||||||
|
ui.colored_label(Color32::LIGHT_BLUE, &playlist);
|
||||||
|
ui.label(": ");
|
||||||
|
ui.label(&song_name)
|
||||||
|
});
|
||||||
|
|
||||||
|
ui.horizontal(|ui| {
|
||||||
|
ui.label("Type: ");
|
||||||
|
ui.label(&song.get_type().to_string());
|
||||||
|
});
|
||||||
|
|
||||||
|
ui.horizontal(|ui| {
|
||||||
|
ui.label("Name: ");
|
||||||
|
ui.text_edit_singleline(&mut self.ed_name);
|
||||||
|
});
|
||||||
|
ui.horizontal(|ui| {
|
||||||
|
ui.label("Url: ");
|
||||||
|
ui.text_edit_singleline(&mut self.ed_url);
|
||||||
|
});
|
||||||
|
|
||||||
|
if ui.button("Save").clicked() {
|
||||||
|
save = true;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
if save {
|
||||||
|
{
|
||||||
|
let Some(song) = state.manifest.get_song_mut(&playlist, &song_name) else {
|
||||||
|
bail!("Failed to get song (2)");
|
||||||
|
};
|
||||||
|
|
||||||
|
*song.get_url_str_mut() = self.ed_url.clone();
|
||||||
|
}
|
||||||
|
|
||||||
|
let Some(playlist) = state.manifest.get_playlist_mut(&playlist) else {
|
||||||
|
bail!("Failed to get playlist");
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
playlist.remove_song(&song_name);
|
||||||
|
playlist.add_song(self.ed_name.clone(), song);
|
||||||
|
*open = false;
|
||||||
|
let _ = state.manifest.save(None);
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl GuiSongEditor {
|
||||||
|
pub fn set_active_song(&mut self, pname: &String, sname: &String, url: &String) {
|
||||||
|
self.song.0 = pname.clone();
|
||||||
|
self.song.1 = sname.clone();
|
||||||
|
self.ed_name = sname.clone();
|
||||||
|
self.ed_url = url.clone();
|
||||||
|
}
|
||||||
|
}
|
||||||
72
src/ui/gui/windows/song_new.rs
Normal file
72
src/ui/gui/windows/song_new.rs
Normal file
@@ -0,0 +1,72 @@
|
|||||||
|
use crate::manifest::song::{Song, SongType};
|
||||||
|
use super::{State, Window};
|
||||||
|
|
||||||
|
#[derive(Debug, Default)]
|
||||||
|
pub struct GuiNewSong {
|
||||||
|
ed_type: SongType,
|
||||||
|
ed_name: String,
|
||||||
|
ed_playlist: Option<String>,
|
||||||
|
ed_url: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Window for GuiNewSong {
|
||||||
|
fn ui(&mut self, state: &mut State, ctx: &egui::Context, open: &mut bool) -> anyhow::Result<()> {
|
||||||
|
let mut save = false;
|
||||||
|
egui::Window::new("New song")
|
||||||
|
.open(open)
|
||||||
|
.show(ctx, |ui| {
|
||||||
|
ui.horizontal(|ui| {
|
||||||
|
ui.label("Type: ");
|
||||||
|
egui::ComboBox::from_id_source("new_song_window_type")
|
||||||
|
.selected_text(format!("{:?}", self.ed_type))
|
||||||
|
.show_ui(ui, |ui| {
|
||||||
|
ui.selectable_value(&mut self.ed_type, SongType::Youtube, "Youtube");
|
||||||
|
ui.selectable_value(&mut self.ed_type, SongType::Spotify, "Spotify");
|
||||||
|
ui.selectable_value(&mut self.ed_type, SongType::Soundcloud, "Soundcloud");
|
||||||
|
}
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
ui.horizontal(|ui| {
|
||||||
|
ui.label("Name: ");
|
||||||
|
ui.text_edit_singleline(&mut self.ed_name);
|
||||||
|
});
|
||||||
|
ui.horizontal(|ui| {
|
||||||
|
ui.label("Playlist: ");
|
||||||
|
egui::ComboBox::from_id_source("new_song_window_playlist")
|
||||||
|
.selected_text(format!("{}", self.ed_playlist.clone().unwrap_or("".to_string())))
|
||||||
|
.show_ui(ui, |ui| {
|
||||||
|
for p in state.manifest.get_playlists().keys() {
|
||||||
|
ui.selectable_value(&mut self.ed_playlist, Option::Some(p.clone()), p.as_str());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
);
|
||||||
|
});
|
||||||
|
ui.horizontal(|ui| {
|
||||||
|
ui.label("Url: ");
|
||||||
|
ui.text_edit_singleline(&mut self.ed_url);
|
||||||
|
});
|
||||||
|
|
||||||
|
if ui.button("Save").clicked() {
|
||||||
|
save = true;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
if save {
|
||||||
|
let Some(playlist) = state.manifest.get_playlist_mut(&self.ed_playlist.clone().unwrap()) else {
|
||||||
|
panic!("couldnt find playlist from a preset playlist list????????????");
|
||||||
|
};
|
||||||
|
|
||||||
|
playlist.add_song(
|
||||||
|
self.ed_name.clone(),
|
||||||
|
Song::from_url_str(self.ed_url.clone()).unwrap().set_type(self.ed_type.clone()).clone()
|
||||||
|
);
|
||||||
|
|
||||||
|
|
||||||
|
let _ = state.manifest.save(None);
|
||||||
|
*open = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
2
src/ui/mod.rs
Normal file
2
src/ui/mod.rs
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
pub mod gui;
|
||||||
|
pub mod cli;
|
||||||
12
src/util.rs
12
src/util.rs
@@ -52,21 +52,11 @@ pub(crate) fn isatty() -> bool {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[allow(dead_code)]
|
||||||
pub fn as_any_mut<T: Any>(val: &mut T) -> &mut dyn Any {
|
pub fn as_any_mut<T: Any>(val: &mut T) -> &mut dyn Any {
|
||||||
val as &mut dyn Any
|
val as &mut dyn Any
|
||||||
}
|
}
|
||||||
|
|
||||||
// pub async fn dl_to_file(url: &str, p: PathBuf) -> anyhow::Result<()> {
|
|
||||||
// log::info!("Downloading {} -> {:?}", url, p);
|
|
||||||
// let ytdlp_req = reqwest::get(url).await?.bytes().await?;
|
|
||||||
// log::debug!("Downloading {:?} finished, writing to file", p);
|
|
||||||
// let mut fd = std::fs::File::create(&p)?;
|
|
||||||
// fd.write(&ytdlp_req)?;
|
|
||||||
// log::debug!("Finished writing {:?}", p);
|
|
||||||
// Ok(())
|
|
||||||
// }
|
|
||||||
|
|
||||||
pub fn get_song_path/*<P: TryInto<PathBuf>>*/(/*basepath: Option<P>,*/ pname: &String, sname: &String, format: &Format) -> PathBuf {
|
pub fn get_song_path/*<P: TryInto<PathBuf>>*/(/*basepath: Option<P>,*/ pname: &String, sname: &String, format: &Format) -> PathBuf {
|
||||||
// let mut path: PathBuf;
|
// let mut path: PathBuf;
|
||||||
/*if let Some(bp) = basepath {
|
/*if let Some(bp) = basepath {
|
||||||
|
|||||||
Reference in New Issue
Block a user