Actually usable????????//
This commit is contained in:
parent
b1c8417b0f
commit
4ee4ca1add
492
Cargo.lock
generated
492
Cargo.lock
generated
|
@ -63,7 +63,7 @@ dependencies = [
|
|||
"futures-lite 1.13.0",
|
||||
"once_cell",
|
||||
"serde",
|
||||
"zbus",
|
||||
"zbus 3.15.2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -93,15 +93,6 @@ dependencies = [
|
|||
"winit",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "addr2line"
|
||||
version = "0.24.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "dfbe277e56a376000877090da837660b4427aad530e3028d44e0bffe4f89a1c1"
|
||||
dependencies = [
|
||||
"gimli",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "adler2"
|
||||
version = "2.0.0"
|
||||
|
@ -292,6 +283,28 @@ dependencies = [
|
|||
"libloading 0.7.4",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ashpd"
|
||||
version = "0.10.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e9c39d707614dbcc6bed00015539f488d8e3fe3e66ed60961efc0c90f4b380b3"
|
||||
dependencies = [
|
||||
"async-fs 2.1.2",
|
||||
"async-net",
|
||||
"enumflags2",
|
||||
"futures-channel",
|
||||
"futures-util",
|
||||
"rand",
|
||||
"raw-window-handle 0.6.2",
|
||||
"serde",
|
||||
"serde_repr",
|
||||
"url",
|
||||
"wayland-backend",
|
||||
"wayland-client",
|
||||
"wayland-protocols 0.32.5",
|
||||
"zbus 5.1.1",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "async-broadcast"
|
||||
version = "0.5.1"
|
||||
|
@ -302,6 +315,18 @@ dependencies = [
|
|||
"futures-core",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "async-broadcast"
|
||||
version = "0.7.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "20cd0e2e25ea8e5f7e9df04578dc6cf5c83577fd09b1a46aaf5c85e1c33f2a7e"
|
||||
dependencies = [
|
||||
"event-listener 5.3.1",
|
||||
"event-listener-strategy",
|
||||
"futures-core",
|
||||
"pin-project-lite",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "async-channel"
|
||||
version = "2.3.1"
|
||||
|
@ -339,6 +364,17 @@ dependencies = [
|
|||
"futures-lite 1.13.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "async-fs"
|
||||
version = "2.1.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ebcd09b382f40fcd159c2d695175b2ae620ffa5f3bd6f664131efff4e8b9e04a"
|
||||
dependencies = [
|
||||
"async-lock 3.4.0",
|
||||
"blocking",
|
||||
"futures-lite 2.4.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "async-io"
|
||||
version = "1.13.0"
|
||||
|
@ -398,6 +434,17 @@ dependencies = [
|
|||
"pin-project-lite",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "async-net"
|
||||
version = "2.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b948000fad4873c1c9339d60f2623323a0cfd3816e5181033c6a5cb68b2accf7"
|
||||
dependencies = [
|
||||
"async-io 2.3.4",
|
||||
"blocking",
|
||||
"futures-lite 2.4.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "async-once-cell"
|
||||
version = "0.5.4"
|
||||
|
@ -421,6 +468,25 @@ dependencies = [
|
|||
"windows-sys 0.48.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "async-process"
|
||||
version = "2.3.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "63255f1dc2381611000436537bbedfe83183faa303a5a0edaf191edef06526bb"
|
||||
dependencies = [
|
||||
"async-channel",
|
||||
"async-io 2.3.4",
|
||||
"async-lock 3.4.0",
|
||||
"async-signal",
|
||||
"async-task",
|
||||
"blocking",
|
||||
"cfg-if",
|
||||
"event-listener 5.3.1",
|
||||
"futures-lite 2.4.0",
|
||||
"rustix 0.38.39",
|
||||
"tracing",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "async-recursion"
|
||||
version = "1.1.1"
|
||||
|
@ -493,9 +559,9 @@ dependencies = [
|
|||
"enumflags2",
|
||||
"serde",
|
||||
"static_assertions",
|
||||
"zbus",
|
||||
"zbus_names",
|
||||
"zvariant",
|
||||
"zbus 3.15.2",
|
||||
"zbus_names 2.6.1",
|
||||
"zvariant 3.15.2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -507,7 +573,7 @@ dependencies = [
|
|||
"atspi-common",
|
||||
"atspi-proxies",
|
||||
"futures-lite 1.13.0",
|
||||
"zbus",
|
||||
"zbus 3.15.2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -518,7 +584,7 @@ checksum = "6495661273703e7a229356dcbe8c8f38223d697aacfaf0e13590a9ac9977bb52"
|
|||
dependencies = [
|
||||
"atspi-common",
|
||||
"serde",
|
||||
"zbus",
|
||||
"zbus 3.15.2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -527,21 +593,6 @@ version = "1.4.0"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ace50bade8e6234aa140d9a2f552bbee1db4d353f69b8217bc503490fc1a9f26"
|
||||
|
||||
[[package]]
|
||||
name = "backtrace"
|
||||
version = "0.3.74"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8d82cb332cdfaed17ae235a638438ac4d4839913cc2af585c3c6746e8f8bee1a"
|
||||
dependencies = [
|
||||
"addr2line",
|
||||
"cfg-if",
|
||||
"libc",
|
||||
"miniz_oxide",
|
||||
"object",
|
||||
"rustc-demangle",
|
||||
"windows-targets 0.52.6",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "base64"
|
||||
version = "0.21.7"
|
||||
|
@ -811,6 +862,12 @@ version = "0.1.1"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "fd16c4719339c4530435d38e511904438d07cce7950afa3718a84ac36c10e89e"
|
||||
|
||||
[[package]]
|
||||
name = "cfg_aliases"
|
||||
version = "0.2.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "613afe47fcd5fac7ccf1db93babcb082c5994d996f20b8b159f2ad1658eb5724"
|
||||
|
||||
[[package]]
|
||||
name = "cgl"
|
||||
version = "0.3.2"
|
||||
|
@ -1212,15 +1269,6 @@ dependencies = [
|
|||
"serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ecolor"
|
||||
version = "0.28.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2e6b451ff1143f6de0f33fc7f1b68fecfd2c7de06e104de96c4514de3f5396f8"
|
||||
dependencies = [
|
||||
"emath 0.28.1",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "eframe"
|
||||
version = "0.27.2"
|
||||
|
@ -1230,7 +1278,7 @@ dependencies = [
|
|||
"bytemuck",
|
||||
"cocoa",
|
||||
"document-features",
|
||||
"egui 0.27.2",
|
||||
"egui",
|
||||
"egui-wgpu",
|
||||
"egui-winit",
|
||||
"egui_glow",
|
||||
|
@ -1263,33 +1311,12 @@ checksum = "584c5d1bf9a67b25778a3323af222dbe1a1feb532190e103901187f92c7fe29a"
|
|||
dependencies = [
|
||||
"accesskit",
|
||||
"ahash",
|
||||
"epaint 0.27.2",
|
||||
"epaint",
|
||||
"log",
|
||||
"nohash-hasher",
|
||||
"serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "egui"
|
||||
version = "0.28.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "20c97e70a2768de630f161bb5392cbd3874fcf72868f14df0e002e82e06cb798"
|
||||
dependencies = [
|
||||
"ahash",
|
||||
"emath 0.28.1",
|
||||
"epaint 0.28.1",
|
||||
"nohash-hasher",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "egui-aesthetix"
|
||||
version = "0.2.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c0b91e1b07193fa9bc71849e39a76d30ffd6a2f57bfc54552e3a2df263da577b"
|
||||
dependencies = [
|
||||
"egui 0.28.1",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "egui-wgpu"
|
||||
version = "0.27.2"
|
||||
|
@ -1298,8 +1325,8 @@ checksum = "469ff65843f88a702b731a1532b7d03b0e8e96d283e70f3a22b0e06c46cb9b37"
|
|||
dependencies = [
|
||||
"bytemuck",
|
||||
"document-features",
|
||||
"egui 0.27.2",
|
||||
"epaint 0.27.2",
|
||||
"egui",
|
||||
"epaint",
|
||||
"log",
|
||||
"thiserror",
|
||||
"type-map",
|
||||
|
@ -1316,7 +1343,7 @@ checksum = "2e3da0cbe020f341450c599b35b92de4af7b00abde85624fd16f09c885573609"
|
|||
dependencies = [
|
||||
"accesskit_winit",
|
||||
"arboard",
|
||||
"egui 0.27.2",
|
||||
"egui",
|
||||
"log",
|
||||
"raw-window-handle 0.6.2",
|
||||
"smithay-clipboard",
|
||||
|
@ -1331,7 +1358,7 @@ version = "0.27.2"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1b78779f35ded1a853786c9ce0b43fe1053e10a21ea3b23ebea411805ce41593"
|
||||
dependencies = [
|
||||
"egui 0.27.2",
|
||||
"egui",
|
||||
"ehttp",
|
||||
"enum-map",
|
||||
"image",
|
||||
|
@ -1348,7 +1375,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||
checksum = "e0e5d975f3c86edc3d35b1db88bb27c15dde7c55d3b5af164968ab5ede3f44ca"
|
||||
dependencies = [
|
||||
"bytemuck",
|
||||
"egui 0.27.2",
|
||||
"egui",
|
||||
"glow",
|
||||
"log",
|
||||
"memoffset 0.9.1",
|
||||
|
@ -1387,12 +1414,6 @@ dependencies = [
|
|||
"serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "emath"
|
||||
version = "0.28.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0a6a21708405ea88f63d8309650b4d77431f4bc28fb9d8e6f77d3963b51249e6"
|
||||
|
||||
[[package]]
|
||||
name = "encoding_rs"
|
||||
version = "0.8.35"
|
||||
|
@ -1402,6 +1423,12 @@ dependencies = [
|
|||
"cfg-if",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "endi"
|
||||
version = "1.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a3d8a32ae18130a3c84dd492d4215c3d913c3b07c6b63c2eb3eb7ff1101ab7bf"
|
||||
|
||||
[[package]]
|
||||
name = "enum-map"
|
||||
version = "2.7.3"
|
||||
|
@ -1487,28 +1514,14 @@ dependencies = [
|
|||
"ab_glyph",
|
||||
"ahash",
|
||||
"bytemuck",
|
||||
"ecolor 0.27.2",
|
||||
"emath 0.27.2",
|
||||
"ecolor",
|
||||
"emath",
|
||||
"log",
|
||||
"nohash-hasher",
|
||||
"parking_lot",
|
||||
"serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "epaint"
|
||||
version = "0.28.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3f0dcc0a0771e7500e94cd1cb797bd13c9f23b9409bdc3c824e2cbc562b7fa01"
|
||||
dependencies = [
|
||||
"ab_glyph",
|
||||
"ahash",
|
||||
"ecolor 0.28.1",
|
||||
"emath 0.28.1",
|
||||
"nohash-hasher",
|
||||
"parking_lot",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "equivalent"
|
||||
version = "1.0.1"
|
||||
|
@ -1651,6 +1664,15 @@ dependencies = [
|
|||
"percent-encoding",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "futures-channel"
|
||||
version = "0.3.31"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2dff15bf788c671c1934e366d07e30c1814a8ef514e1af724a602e8a2fbe1b10"
|
||||
dependencies = [
|
||||
"futures-core",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "futures-core"
|
||||
version = "0.3.31"
|
||||
|
@ -1691,6 +1713,17 @@ dependencies = [
|
|||
"pin-project-lite",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "futures-macro"
|
||||
version = "0.3.31"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "162ee34ebcb7c64a8abebc059ce0fee27c2262618d7b60ed8faf72fef13c3650"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.87",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "futures-sink"
|
||||
version = "0.3.31"
|
||||
|
@ -1711,6 +1744,7 @@ checksum = "9fa08315bb612088cc391249efdc3bc77536f16c91f6cf495e6fbe85b20a4a81"
|
|||
dependencies = [
|
||||
"futures-core",
|
||||
"futures-io",
|
||||
"futures-macro",
|
||||
"futures-sink",
|
||||
"futures-task",
|
||||
"memchr",
|
||||
|
@ -1750,12 +1784,6 @@ dependencies = [
|
|||
"wasi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "gimli"
|
||||
version = "0.31.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "07e28edb80900c19c28f1072f2e8aeca7fa06b23cd4169cefe1af5aa3260783f"
|
||||
|
||||
[[package]]
|
||||
name = "gl_generator"
|
||||
version = "0.14.0"
|
||||
|
@ -1792,7 +1820,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||
checksum = "18fcd4ae4e86d991ad1300b8f57166e5be0c95ef1f63f3f5b827f8a164548746"
|
||||
dependencies = [
|
||||
"bitflags 2.6.0",
|
||||
"cfg_aliases",
|
||||
"cfg_aliases 0.1.1",
|
||||
"cgl",
|
||||
"core-foundation",
|
||||
"dispatch",
|
||||
|
@ -1815,7 +1843,7 @@ version = "0.4.2"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1ebcdfba24f73b8412c5181e56f092b5eff16671c514ce896b258a0a64bd7735"
|
||||
dependencies = [
|
||||
"cfg_aliases",
|
||||
"cfg_aliases 0.1.1",
|
||||
"glutin",
|
||||
"raw-window-handle 0.5.2",
|
||||
"winit",
|
||||
|
@ -2327,18 +2355,6 @@ dependencies = [
|
|||
"simd-adler32",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "mio"
|
||||
version = "1.0.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "80e04d1dcff3aae0704555fe5fee3bcfaf3d1fdf8a7e521d5b9d2b42acb52cec"
|
||||
dependencies = [
|
||||
"hermit-abi 0.3.9",
|
||||
"libc",
|
||||
"wasi",
|
||||
"windows-sys 0.52.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "naga"
|
||||
version = "0.19.2"
|
||||
|
@ -2402,6 +2418,19 @@ dependencies = [
|
|||
"memoffset 0.7.1",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "nix"
|
||||
version = "0.29.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "71e2746dc3a24dd78b3cfcb7be93368c6de9963d30f43a6a73998a9cf4b17b46"
|
||||
dependencies = [
|
||||
"bitflags 2.6.0",
|
||||
"cfg-if",
|
||||
"cfg_aliases 0.2.1",
|
||||
"libc",
|
||||
"memoffset 0.9.1",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "nohash-hasher"
|
||||
version = "0.2.0"
|
||||
|
@ -2581,6 +2610,7 @@ checksum = "0ee638a5da3799329310ad4cfa62fbf045d5f56e3ef5ba4149e7452dcf89d5a8"
|
|||
dependencies = [
|
||||
"bitflags 2.6.0",
|
||||
"block2 0.5.1",
|
||||
"dispatch",
|
||||
"libc",
|
||||
"objc2 0.5.2",
|
||||
]
|
||||
|
@ -2619,15 +2649,6 @@ dependencies = [
|
|||
"cc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "object"
|
||||
version = "0.36.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "aedf0a2d09c573ed1d8d85b30c119153926a2b36dce0ab28322c09a117a4683e"
|
||||
dependencies = [
|
||||
"memchr",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "oboe"
|
||||
version = "0.6.1"
|
||||
|
@ -2820,6 +2841,12 @@ dependencies = [
|
|||
"windows-sys 0.59.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "pollster"
|
||||
version = "0.3.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "22686f4785f02a4fcc856d3b3bb19bf6c8160d103f7a99cc258bddd0251dc7f2"
|
||||
|
||||
[[package]]
|
||||
name = "ppv-lite86"
|
||||
version = "0.2.20"
|
||||
|
@ -3013,6 +3040,28 @@ dependencies = [
|
|||
"usvg",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rfd"
|
||||
version = "0.15.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "46f6f80a9b882647d9014673ca9925d30ffc9750f2eed2b4490e189eaebd01e8"
|
||||
dependencies = [
|
||||
"ashpd",
|
||||
"block2 0.5.1",
|
||||
"js-sys",
|
||||
"log",
|
||||
"objc2 0.5.2",
|
||||
"objc2-app-kit",
|
||||
"objc2-foundation",
|
||||
"pollster",
|
||||
"raw-window-handle 0.6.2",
|
||||
"urlencoding",
|
||||
"wasm-bindgen",
|
||||
"wasm-bindgen-futures",
|
||||
"web-sys",
|
||||
"windows-sys 0.48.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rgb"
|
||||
version = "0.8.50"
|
||||
|
@ -3056,12 +3105,6 @@ version = "0.19.0"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3cd14fd5e3b777a7422cca79358c57a8f6e3a703d9ac187448d0daf220c2407f"
|
||||
|
||||
[[package]]
|
||||
name = "rustc-demangle"
|
||||
version = "0.1.24"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "719b953e2095829ee67db738b3bfa9fa368c94900df327b3f07fe6e794d2fe1f"
|
||||
|
||||
[[package]]
|
||||
name = "rustc-hash"
|
||||
version = "1.1.0"
|
||||
|
@ -3673,30 +3716,15 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||
checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20"
|
||||
|
||||
[[package]]
|
||||
name = "tokio"
|
||||
version = "1.41.0"
|
||||
name = "toml"
|
||||
version = "0.7.8"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "145f3413504347a2be84393cc8a7d2fb4d863b375909ea59f2158261aa258bbb"
|
||||
checksum = "dd79e69d3b627db300ff956027cc6c3798cef26d22526befdfcd12feeb6d2257"
|
||||
dependencies = [
|
||||
"backtrace",
|
||||
"bytes",
|
||||
"libc",
|
||||
"mio",
|
||||
"pin-project-lite",
|
||||
"signal-hook-registry",
|
||||
"tokio-macros",
|
||||
"windows-sys 0.52.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tokio-macros"
|
||||
version = "2.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "693d596312e88961bc67d7f1f97af8a70227d9f90c31bba5806eec004978d752"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.87",
|
||||
"serde",
|
||||
"serde_spanned",
|
||||
"toml_datetime",
|
||||
"toml_edit 0.19.15",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -3727,6 +3755,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||
checksum = "1b5bb770da30e5cbfde35a2d7b9b8a2c4b8ef89548a7a6aeab5c9a576e3e7421"
|
||||
dependencies = [
|
||||
"indexmap",
|
||||
"serde",
|
||||
"serde_spanned",
|
||||
"toml_datetime",
|
||||
"winnow 0.5.40",
|
||||
]
|
||||
|
@ -3886,6 +3916,12 @@ dependencies = [
|
|||
"serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "urlencoding"
|
||||
version = "2.1.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "daf8dba3b7eb870caf1ddeed7bc9d2a049f3cfdfae7cb521b087cc33ae4c49da"
|
||||
|
||||
[[package]]
|
||||
name = "usvg"
|
||||
version = "0.37.0"
|
||||
|
@ -4229,7 +4265,7 @@ checksum = "cbd7311dbd2abcfebaabf1841a2824ed7c8be443a0f29166e5d3c6a53a762c01"
|
|||
dependencies = [
|
||||
"arrayvec",
|
||||
"cfg-if",
|
||||
"cfg_aliases",
|
||||
"cfg_aliases 0.1.1",
|
||||
"js-sys",
|
||||
"log",
|
||||
"parking_lot",
|
||||
|
@ -4254,7 +4290,7 @@ dependencies = [
|
|||
"arrayvec",
|
||||
"bit-vec",
|
||||
"bitflags 2.6.0",
|
||||
"cfg_aliases",
|
||||
"cfg_aliases 0.1.1",
|
||||
"codespan-reporting",
|
||||
"indexmap",
|
||||
"log",
|
||||
|
@ -4281,7 +4317,7 @@ dependencies = [
|
|||
"arrayvec",
|
||||
"ash",
|
||||
"bitflags 2.6.0",
|
||||
"cfg_aliases",
|
||||
"cfg_aliases 0.1.1",
|
||||
"core-graphics-types",
|
||||
"glow",
|
||||
"glutin_wgl_sys",
|
||||
|
@ -4667,7 +4703,7 @@ dependencies = [
|
|||
"bitflags 2.6.0",
|
||||
"bytemuck",
|
||||
"calloop 0.12.4",
|
||||
"cfg_aliases",
|
||||
"cfg_aliases 0.1.1",
|
||||
"core-foundation",
|
||||
"core-graphics",
|
||||
"cursor-icon",
|
||||
|
@ -4722,6 +4758,16 @@ dependencies = [
|
|||
"memchr",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "winresource"
|
||||
version = "0.1.17"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "77e2aaaf8cfa92078c0c0375423d631f82f2f57979c2884fdd5f604a11e45329"
|
||||
dependencies = [
|
||||
"toml 0.7.8",
|
||||
"version_check",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "x11-dl"
|
||||
version = "2.21.0"
|
||||
|
@ -4834,6 +4880,7 @@ dependencies = [
|
|||
"clap",
|
||||
"env_logger",
|
||||
"log",
|
||||
"winresource",
|
||||
"xmpd-cliargs",
|
||||
"xmpd-gui",
|
||||
"xmpd-manifest",
|
||||
|
@ -4846,17 +4893,18 @@ version = "2.0.0"
|
|||
dependencies = [
|
||||
"anyhow",
|
||||
"camino",
|
||||
"dirs",
|
||||
"eframe",
|
||||
"egui 0.27.2",
|
||||
"egui-aesthetix",
|
||||
"egui",
|
||||
"egui_extras",
|
||||
"lazy_static",
|
||||
"log",
|
||||
"tokio",
|
||||
"rfd",
|
||||
"uuid",
|
||||
"xmpd-cache",
|
||||
"xmpd-cliargs",
|
||||
"xmpd-manifest",
|
||||
"xmpd-player",
|
||||
"xmpd-settings",
|
||||
]
|
||||
|
||||
|
@ -4867,14 +4915,20 @@ dependencies = [
|
|||
"anyhow",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"toml 0.8.19",
|
||||
"url",
|
||||
"uuid",
|
||||
"xmpd-cliargs",
|
||||
"xmpd-settings",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "xmpd-player"
|
||||
version = "2.0.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"lazy_static",
|
||||
"log",
|
||||
"rodio",
|
||||
]
|
||||
|
||||
|
@ -4884,10 +4938,10 @@ version = "2.0.0"
|
|||
dependencies = [
|
||||
"anyhow",
|
||||
"camino",
|
||||
"egui 0.27.2",
|
||||
"egui",
|
||||
"lazy_static",
|
||||
"serde",
|
||||
"toml",
|
||||
"toml 0.8.19",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -4900,12 +4954,12 @@ version = "3.15.2"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "675d170b632a6ad49804c8cf2105d7c31eddd3312555cffd4b740e08e97c25e6"
|
||||
dependencies = [
|
||||
"async-broadcast",
|
||||
"async-broadcast 0.5.1",
|
||||
"async-executor",
|
||||
"async-fs",
|
||||
"async-fs 1.6.0",
|
||||
"async-io 1.13.0",
|
||||
"async-lock 2.8.0",
|
||||
"async-process",
|
||||
"async-process 1.8.1",
|
||||
"async-recursion",
|
||||
"async-task",
|
||||
"async-trait",
|
||||
|
@ -4918,7 +4972,7 @@ dependencies = [
|
|||
"futures-sink",
|
||||
"futures-util",
|
||||
"hex",
|
||||
"nix",
|
||||
"nix 0.26.4",
|
||||
"once_cell",
|
||||
"ordered-stream",
|
||||
"rand",
|
||||
|
@ -4930,9 +4984,45 @@ dependencies = [
|
|||
"uds_windows",
|
||||
"winapi",
|
||||
"xdg-home",
|
||||
"zbus_macros",
|
||||
"zbus_names",
|
||||
"zvariant",
|
||||
"zbus_macros 3.15.2",
|
||||
"zbus_names 2.6.1",
|
||||
"zvariant 3.15.2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "zbus"
|
||||
version = "5.1.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1162094dc63b1629fcc44150bcceeaa80798cd28bcbe7fa987b65a034c258608"
|
||||
dependencies = [
|
||||
"async-broadcast 0.7.1",
|
||||
"async-executor",
|
||||
"async-fs 2.1.2",
|
||||
"async-io 2.3.4",
|
||||
"async-lock 3.4.0",
|
||||
"async-process 2.3.0",
|
||||
"async-recursion",
|
||||
"async-task",
|
||||
"async-trait",
|
||||
"blocking",
|
||||
"enumflags2",
|
||||
"event-listener 5.3.1",
|
||||
"futures-core",
|
||||
"futures-util",
|
||||
"hex",
|
||||
"nix 0.29.0",
|
||||
"ordered-stream",
|
||||
"serde",
|
||||
"serde_repr",
|
||||
"static_assertions",
|
||||
"tracing",
|
||||
"uds_windows",
|
||||
"windows-sys 0.59.0",
|
||||
"winnow 0.6.20",
|
||||
"xdg-home",
|
||||
"zbus_macros 5.1.1",
|
||||
"zbus_names 4.1.0",
|
||||
"zvariant 5.1.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -4946,7 +5036,22 @@ dependencies = [
|
|||
"quote",
|
||||
"regex",
|
||||
"syn 1.0.109",
|
||||
"zvariant_utils",
|
||||
"zvariant_utils 1.0.1",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "zbus_macros"
|
||||
version = "5.1.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2cd2dcdce3e2727f7d74b7e33b5a89539b3cc31049562137faf7ae4eb86cd16d"
|
||||
dependencies = [
|
||||
"proc-macro-crate 3.2.0",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.87",
|
||||
"zbus_names 4.1.0",
|
||||
"zvariant 5.1.0",
|
||||
"zvariant_utils 3.0.2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -4957,7 +5062,19 @@ checksum = "437d738d3750bed6ca9b8d423ccc7a8eb284f6b1d6d4e225a0e4e6258d864c8d"
|
|||
dependencies = [
|
||||
"serde",
|
||||
"static_assertions",
|
||||
"zvariant",
|
||||
"zvariant 3.15.2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "zbus_names"
|
||||
version = "4.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "856b7a38811f71846fd47856ceee8bccaec8399ff53fb370247e66081ace647b"
|
||||
dependencies = [
|
||||
"serde",
|
||||
"static_assertions",
|
||||
"winnow 0.6.20",
|
||||
"zvariant 5.1.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -4998,7 +5115,23 @@ dependencies = [
|
|||
"libc",
|
||||
"serde",
|
||||
"static_assertions",
|
||||
"zvariant_derive",
|
||||
"zvariant_derive 3.15.2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "zvariant"
|
||||
version = "5.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a1200ee6ac32f1e5a312e455a949a4794855515d34f9909f4a3e082d14e1a56f"
|
||||
dependencies = [
|
||||
"endi",
|
||||
"enumflags2",
|
||||
"serde",
|
||||
"static_assertions",
|
||||
"url",
|
||||
"winnow 0.6.20",
|
||||
"zvariant_derive 5.1.0",
|
||||
"zvariant_utils 3.0.2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -5011,7 +5144,20 @@ dependencies = [
|
|||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 1.0.109",
|
||||
"zvariant_utils",
|
||||
"zvariant_utils 1.0.1",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "zvariant_derive"
|
||||
version = "5.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "687e3b97fae6c9104fbbd36c73d27d149abf04fb874e2efbd84838763daa8916"
|
||||
dependencies = [
|
||||
"proc-macro-crate 3.2.0",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.87",
|
||||
"zvariant_utils 3.0.2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -5024,3 +5170,17 @@ dependencies = [
|
|||
"quote",
|
||||
"syn 1.0.109",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "zvariant_utils"
|
||||
version = "3.0.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "20d1d011a38f12360e5fcccceeff5e2c42a8eb7f27f0dcba97a0862ede05c9c6"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"serde",
|
||||
"static_assertions",
|
||||
"syn 2.0.87",
|
||||
"winnow 0.6.20",
|
||||
]
|
||||
|
|
19
Cargo.toml
19
Cargo.toml
|
@ -25,28 +25,25 @@ authors=[
|
|||
[workspace.dependencies]
|
||||
anstyle = "1.0.6"
|
||||
anyhow = "1.0.81"
|
||||
bitflags = { version = "2.6.0", features = ["serde"] }
|
||||
camino = { version="1.1.6", features = ["serde1"] }
|
||||
clap = { version = "4.5.4", features = ["derive"] }
|
||||
eframe = "0.27.2"
|
||||
egui = { version = "0.27.2", features = ["color-hex", "serde"] }
|
||||
egui_extras = { version = "0.27.2", features = ["all_loaders"] }
|
||||
env_logger = "0.11.3"
|
||||
futures = "0.3.30"
|
||||
html-escape = "0.2.13"
|
||||
lazy_static = "1.4.0"
|
||||
libc = "0.2.153"
|
||||
log = "0.4.21"
|
||||
notify-rust = "4.11.3"
|
||||
open = "5.3.0"
|
||||
regex = "1.11.0"
|
||||
reqwest = { version = "0.12.3", features = ["blocking", "h2", "http2", "rustls-tls"], default-features = false }
|
||||
# notify-rust = "4.11.3"
|
||||
# open = "5.3.0"
|
||||
# reqwest = { version = "0.12.3", features = ["blocking", "h2", "http2", "rustls-tls"], default-features = false }
|
||||
serde = { version = "1.0.197", features = ["derive"] }
|
||||
serde_json = "1.0.115"
|
||||
# serde_traitobject = "0.2.8"
|
||||
tokio = { version = "1.37.0", features = ["macros", "rt-multi-thread", "process", "sync"] }
|
||||
url = { version = "2.5.0", features = ["serde"] }
|
||||
uuid = { version = "1.11.0", features = ["serde", "v4"] }
|
||||
windows = { version = "0.56.0", features = ["Win32_Foundation", "Win32_Storage_FileSystem", "Win32_System_Console"] }
|
||||
zip-extensions = "0.6.20"
|
||||
# zip-extensions = "0.6.20"
|
||||
dirs="5.0.1"
|
||||
winresource = "0.1.17"
|
||||
toml = "0.8.19"
|
||||
rfd = "0.15.1"
|
||||
rodio = { version = "0.20.1", features = ["symphonia-all"] }
|
||||
|
|
BIN
assets/icon.ico
Normal file
BIN
assets/icon.ico
Normal file
Binary file not shown.
After Width: | Height: | Size: 15 KiB |
|
@ -1 +1 @@
|
|||
<svg xmlns="http://www.w3.org/2000/svg" height="24px" viewBox="0 -960 960 960" width="24px" fill="#FFFFFF"><path d="M440-280h80v-240h-80v240Zm40-320q17 0 28.5-11.5T520-640q0-17-11.5-28.5T480-680q-17 0-28.5 11.5T440-640q0 17 11.5 28.5T480-600Zm0 520q-83 0-156-31.5T197-197q-54-54-85.5-127T80-480q0-83 31.5-156T197-763q54-54 127-85.5T480-880q83 0 156 31.5T763-763q54 54 85.5 127T880-480q0 83-31.5 156T763-197q-54 54-127 85.5T480-80Zm0-80q134 0 227-93t93-227q0-134-93-227t-227-93q-134 0-227 93t-93 227q0 134 93 227t227 93Zm0-320Z"/></svg>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" height="16px" viewBox="0 -960 960 960" width="16px" fill="#FFFFFF"><path d="M440-280h80v-240h-80v240Zm40-320q17 0 28.5-11.5T520-640q0-17-11.5-28.5T480-680q-17 0-28.5 11.5T440-640q0 17 11.5 28.5T480-600Zm0 520q-83 0-156-31.5T197-197q-54-54-85.5-127T80-480q0-83 31.5-156T197-763q54-54 127-85.5T480-880q83 0 156 31.5T763-763q54 54 85.5 127T880-480q0 83-31.5 156T763-197q-54 54-127 85.5T480-80Zm0-80q134 0 227-93t93-227q0-134-93-227t-227-93q-134 0-227 93t-93 227q0 134 93 227t227 93Zm0-320Z"/></svg>
|
||||
|
|
Before Width: | Height: | Size: 536 B After Width: | Height: | Size: 536 B |
1373
manifest.toml
Normal file
1373
manifest.toml
Normal file
File diff suppressed because it is too large
Load Diff
|
@ -108,11 +108,12 @@ impl SongCacheDl {
|
|||
}
|
||||
let mut from = song_p.clone();
|
||||
from.pop();
|
||||
from.push("{song_format}.{song_format}");
|
||||
from.push(format!("{song_format}.{song_format}"));
|
||||
|
||||
let mut to = song_p.clone();
|
||||
to.pop();
|
||||
to.set_extension(&song_format);
|
||||
log::debug!("from: {from:?} to: {to:?}");
|
||||
std::fs::copy(&from, &to).unwrap();
|
||||
from.pop();
|
||||
std::fs::remove_dir_all(from).unwrap();
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
use std::{collections::HashMap, str::FromStr, sync::{mpsc::{self, Receiver, Sender}, Arc, Mutex, MutexGuard}, time::Duration};
|
||||
use downloader::song::SongStatus;
|
||||
use log::warn;
|
||||
use xmpd_manifest::song::Song;
|
||||
|
||||
pub mod downloader;
|
||||
|
@ -106,7 +105,6 @@ fn start_cache_mv_thread(tx: Sender<Message>) {
|
|||
song_p.push("songs");
|
||||
song_p.push(sid.clone().to_string());
|
||||
let song_p = song_p.with_extension(&song_format);
|
||||
log::debug!("Found done: {:?}: {}", song_p, song_p.exists());
|
||||
if song_p.exists() {
|
||||
let _ = tx.send(Message::DownloadDone(sid.clone()));
|
||||
cache.song_cache.insert(sid.clone(), DlStatus::Done(song_p));
|
||||
|
|
|
@ -30,3 +30,6 @@ camino.workspace = true
|
|||
anyhow.workspace = true
|
||||
log.workspace = true
|
||||
env_logger.workspace = true
|
||||
|
||||
[build-dependencies]
|
||||
winresource.workspace = true
|
||||
|
|
11
xmpd-core/build.rs
Normal file
11
xmpd-core/build.rs
Normal file
|
@ -0,0 +1,11 @@
|
|||
use winresource::WindowsResource;
|
||||
|
||||
fn main() -> std::io::Result<()> {
|
||||
if std::env::var_os("CARGO_CFG_WINDOWS").is_some() {
|
||||
WindowsResource::new()
|
||||
// This path can be absolute, or relative to your crate root.
|
||||
.set_icon("../assets/icon.ico")
|
||||
.compile()?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
|
@ -22,13 +22,14 @@ xmpd-manifest.path = "../xmpd-manifest"
|
|||
xmpd-settings.path = "../xmpd-settings"
|
||||
xmpd-cliargs.path = "../xmpd-cliargs"
|
||||
xmpd-cache.path = "../xmpd-cache"
|
||||
xmpd-player.path = "../xmpd-player"
|
||||
egui.workspace = true
|
||||
eframe.workspace = true
|
||||
tokio.workspace = true
|
||||
anyhow.workspace = true
|
||||
lazy_static.workspace = true
|
||||
log.workspace = true
|
||||
egui_extras.workspace = true
|
||||
egui-aesthetix = "0.2.4"
|
||||
uuid.workspace = true
|
||||
camino.workspace = true
|
||||
rfd.workspace = true
|
||||
dirs.workspace = true
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
use egui::RichText;
|
||||
use egui::{CursorIcon, RichText, Sense};
|
||||
use xmpd_manifest::store::BaseStore;
|
||||
|
||||
use super::{CompGetter, CompUi};
|
||||
|
@ -50,6 +50,7 @@ fn add_playlist_tab(ui: &mut egui::Ui, pid: &Option<uuid::Uuid>, title: &str, au
|
|||
ui.add(
|
||||
egui::Image::new(crate::data::NOTE_ICON)
|
||||
.tint(theme.accent_color)
|
||||
.sense(Sense::click())
|
||||
.fit_to_exact_size(egui::Vec2::new(32.0, 32.0))
|
||||
);
|
||||
ui.vertical(|ui| {
|
||||
|
@ -83,9 +84,13 @@ fn add_playlist_tab(ui: &mut egui::Ui, pid: &Option<uuid::Uuid>, title: &str, au
|
|||
});
|
||||
}).response.rect;
|
||||
|
||||
if ui.interact(wdg_rect, format!("left_nav_playlist_{pid:?}").into(), egui::Sense::click()).clicked() {
|
||||
let blob = ui.interact(wdg_rect, format!("left_nav_playlist_{pid:?}").into(), egui::Sense::click());
|
||||
if blob.clicked() {
|
||||
handle_error_ui!(LeftNav::get()).selected_playlist_id = pid.clone();
|
||||
}
|
||||
if blob.hovered() {
|
||||
ui.output_mut(|o| o.cursor_icon = CursorIcon::PointingHand);
|
||||
}
|
||||
ui.separator();
|
||||
}
|
||||
|
||||
|
|
|
@ -1,19 +1,30 @@
|
|||
use egui::{Sense, Stroke, Vec2};
|
||||
|
||||
use super::{CompGetter, CompUi};
|
||||
use super::{song_list::SongList, CompGetter, CompUi};
|
||||
|
||||
|
||||
#[derive(Debug, Default)]
|
||||
#[derive(Debug)]
|
||||
pub struct Player {
|
||||
slider_progress: usize,
|
||||
is_playing: bool,
|
||||
old_slider_progress: usize,
|
||||
volume_slider: f64,
|
||||
}
|
||||
|
||||
impl Default for Player {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
volume_slider: 1.0,
|
||||
old_slider_progress: 0,
|
||||
slider_progress: 0
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
component_register!(Player);
|
||||
|
||||
|
||||
impl CompUi for Player {
|
||||
fn draw(ui: &mut egui::Ui, _: &mut crate::GuiState) -> crate::Result<()> {
|
||||
fn draw(ui: &mut egui::Ui, state: &mut crate::GuiState) -> crate::Result<()> {
|
||||
let theme = xmpd_settings::Settings::get()?.theme.clone();
|
||||
let avail = ui.available_size();
|
||||
ui.vertical_centered_justified(|ui| {
|
||||
|
@ -21,10 +32,7 @@ impl CompUi for Player {
|
|||
ui.horizontal(|ui| {
|
||||
{
|
||||
ui.add_space(avail.x * 0.05 / 2.0);
|
||||
let mut slf = handle_error_ui!(Player::get());
|
||||
let slider = egui::Slider::new(&mut slf.slider_progress, 0..=100)
|
||||
.show_value(false);
|
||||
ui.style_mut().spacing.slider_width = avail.x * 0.90;
|
||||
ui.style_mut().spacing.slider_width = avail.x * 0.87;
|
||||
let s = Stroke {
|
||||
color: theme.accent_color,
|
||||
width: 2.0
|
||||
|
@ -32,19 +40,35 @@ impl CompUi for Player {
|
|||
ui.style_mut().visuals.widgets.inactive.fg_stroke = s;
|
||||
ui.style_mut().visuals.widgets.active.fg_stroke = s;
|
||||
ui.style_mut().visuals.widgets.hovered.fg_stroke = s;
|
||||
ui.add(slider);
|
||||
ui.label("00:00");
|
||||
|
||||
let mut slf = handle_error_ui!(Player::get());
|
||||
ui.add(
|
||||
egui::Slider::new(&mut slf.slider_progress, 0..=100)
|
||||
.show_value(false)
|
||||
);
|
||||
if slf.slider_progress == slf.old_slider_progress {
|
||||
slf.slider_progress = (state.player.get_played_f() * 100.0) as usize;
|
||||
slf.old_slider_progress = slf.slider_progress;
|
||||
} else {
|
||||
handle_error_ui!(state.player.seek_to_f(slf.slider_progress as f64 / 100.0 ));
|
||||
slf.old_slider_progress = slf.slider_progress;
|
||||
}
|
||||
let secs_left = state.player.get_ms_left() as f64 / 1000.0;
|
||||
let h = (secs_left/60.0/60.0).floor();
|
||||
let m = ((secs_left - h * 60.0)/60.0).floor();
|
||||
let s = (secs_left - m * 60.0).floor();
|
||||
|
||||
ui.label(format!("{h:02}:{m:02}:{s:02}"));
|
||||
}
|
||||
});
|
||||
ui.horizontal(|ui| {
|
||||
ui.add_space((avail.x / 2.0) - 16.0 - 8.0 - ui.spacing().item_spacing.x);
|
||||
let pp = if handle_error_ui!(Player::get()).is_playing {
|
||||
crate::data::PAUSE_ICON
|
||||
} else {
|
||||
let pp = if state.player.is_paused() {
|
||||
crate::data::PLAY_ICON
|
||||
} else {
|
||||
crate::data::PAUSE_ICON
|
||||
};
|
||||
|
||||
|
||||
let prev = egui::Image::new(crate::data::PREV_ICON)
|
||||
.tint(theme.accent_color)
|
||||
.sense(Sense::click())
|
||||
|
@ -57,12 +81,43 @@ impl CompUi for Player {
|
|||
.tint(theme.accent_color)
|
||||
.sense(Sense::click())
|
||||
.max_size(Vec2::new(16.0, 16.0));
|
||||
if ui.add(prev).clicked() {}
|
||||
if ui.add(pp).clicked() {
|
||||
let mut slf = handle_error_ui!(Player::get());
|
||||
slf.is_playing = !slf.is_playing;
|
||||
if ui.add(prev).clicked() {
|
||||
handle_error_ui!(handle_error_ui!(SongList::get()).play_prev(state));
|
||||
}
|
||||
if ui.add(next).clicked() {}
|
||||
if ui.add(pp).clicked() {
|
||||
if state.player.is_paused() {
|
||||
state.player.play();
|
||||
} else {
|
||||
state.player.pause();
|
||||
}
|
||||
}
|
||||
if ui.add(next).clicked() || state.player.just_stopped() {
|
||||
handle_error_ui!(handle_error_ui!(SongList::get()).play_next(state));
|
||||
}
|
||||
|
||||
|
||||
|
||||
ui.with_layout(egui::Layout::right_to_left(egui::Align::RIGHT), |ui| {
|
||||
ui.add_space(15.0);
|
||||
ui.style_mut().spacing.slider_width = avail.x * 0.15;
|
||||
let s = Stroke {
|
||||
color: theme.accent_color,
|
||||
width: 1.0
|
||||
};
|
||||
ui.style_mut().visuals.widgets.inactive.fg_stroke = s;
|
||||
ui.style_mut().visuals.widgets.active.fg_stroke = s;
|
||||
ui.style_mut().visuals.widgets.hovered.fg_stroke = s;
|
||||
|
||||
let mut slf = handle_error_ui!(Player::get());
|
||||
let slider =ui.add(
|
||||
egui::Slider::new(&mut slf.volume_slider, 0.0..=1.0)
|
||||
.show_value(false)
|
||||
);
|
||||
|
||||
if slider.changed() {
|
||||
state.player.set_volume(slf.volume_slider);
|
||||
}
|
||||
});
|
||||
});
|
||||
ui.add_space(3.0);
|
||||
});
|
||||
|
|
|
@ -1,6 +1,4 @@
|
|||
use std::fmt::write;
|
||||
|
||||
use egui::{Color32, RichText, Sense, Vec2};
|
||||
use egui::{Color32, CursorIcon, RichText, Sense, Vec2};
|
||||
use song_list_nav::SearchType;
|
||||
use xmpd_cache::DlStatus;
|
||||
use xmpd_manifest::{song::Song, store::BaseStore};
|
||||
|
@ -10,104 +8,155 @@ pub mod song_list_nav;
|
|||
|
||||
#[derive(Debug, Default)]
|
||||
pub struct SongList {
|
||||
selected_song_id: uuid::Uuid
|
||||
selected_sid: uuid::Uuid,
|
||||
playable_songs: Vec<uuid::Uuid>,
|
||||
}
|
||||
|
||||
component_register!(SongList);
|
||||
|
||||
impl CompUi for SongList {
|
||||
fn draw(ui: &mut egui::Ui, state: &mut crate::GuiState) -> crate::Result<()> {
|
||||
let songs = Self::get_and_sort_songs(state)?;
|
||||
let disp_songs = Self::get_songs_to_display(&songs)?;
|
||||
{
|
||||
let mut sl = SongList::get()?;
|
||||
sl.playable_songs = Self::get_playable_songs(&songs)?;
|
||||
if let Some((sid, _)) = songs.first() {
|
||||
if sl.selected_sid == Default::default() {
|
||||
sl.selected_sid = sid.clone();
|
||||
}
|
||||
}
|
||||
}
|
||||
egui::ScrollArea::vertical()
|
||||
.id_source("song_list")
|
||||
.drag_to_scroll(false)
|
||||
.show(ui, |ui| {
|
||||
ui.vertical(|ui| {
|
||||
ui.add_space(3.0);
|
||||
let pid = {handle_error_ui!(super::left_nav::LeftNav::get()).selected_playlist_id.clone()};
|
||||
match pid {
|
||||
None => {
|
||||
let mut songs: Vec<_> = state.manifest.store().get_songs().iter().collect();
|
||||
songs.sort_by(|a, b| {
|
||||
let a = a.1.name().to_lowercase();
|
||||
let b = b.1.name().to_lowercase();
|
||||
a.cmp(&b)
|
||||
});
|
||||
|
||||
for (sid, song) in songs {
|
||||
let query = {handle_error_ui!(song_list_nav::SongListNav::get()).parse_search()}.clone();
|
||||
let should_display = match query {
|
||||
SearchType::Source(s) if !s.is_empty() => format!("{:?}", &song.source_type()).to_lowercase().contains(&s),
|
||||
SearchType::Author(s) if !s.is_empty() => song.author().to_lowercase().contains(&s),
|
||||
SearchType::Name(s) if !s.is_empty() => song.name().to_owned().contains(&s),
|
||||
_ => true
|
||||
};
|
||||
if should_display {
|
||||
display_song_tab(ui, sid, song);
|
||||
for (sid, song) in disp_songs {
|
||||
handle_error_ui!(Self::display_song_tab(ui, state, &sid, &song));
|
||||
}
|
||||
}
|
||||
}
|
||||
Some(pid) => {
|
||||
if let Some(playlist) = state.manifest.store().get_playlist(&pid) {
|
||||
let mut songs = Vec::new();
|
||||
for sid in playlist.songs() {
|
||||
if let Some(song) = state.manifest.store().get_song(&sid) {
|
||||
songs.push((sid, song));
|
||||
}
|
||||
}
|
||||
songs.sort_by(|a, b| {
|
||||
let a = a.1.name().to_lowercase();
|
||||
let b = b.1.name().to_lowercase();
|
||||
a.cmp(&b)
|
||||
});
|
||||
let query = {handle_error_ui!(song_list_nav::SongListNav::get()).parse_search()}.clone();
|
||||
for (sid, song) in songs {
|
||||
let should_display = match query {
|
||||
SearchType::Source(ref s) if !s.is_empty() => format!("{:?}", &song.source_type()).to_lowercase().contains(s),
|
||||
SearchType::Author(ref s) if !s.is_empty() => song.author().to_lowercase().contains(s),
|
||||
SearchType::Name(ref s) if !s.is_empty() => song.name().to_owned().contains(s),
|
||||
_ => true
|
||||
};
|
||||
if should_display {
|
||||
display_song_tab(ui, sid, song);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
});
|
||||
});
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
fn display_song_tab(ui: &mut egui::Ui, sid: &uuid::Uuid, song: &Song) {
|
||||
let theme = handle_error_ui!(xmpd_settings::Settings::get()).theme.clone();
|
||||
impl SongList {
|
||||
fn get_and_sort_songs(state: &mut crate::GuiState) -> crate::Result<Vec<(uuid::Uuid, Song)>> {
|
||||
let pid = super::left_nav::LeftNav::get()?.selected_playlist_id.clone();
|
||||
match pid {
|
||||
None => {
|
||||
let songs = state.manifest.store().get_songs().clone().into_iter();
|
||||
let mut songs: Vec<_> = songs.collect();
|
||||
songs.sort_by(|a, b| {
|
||||
let a = a.1.name().to_lowercase();
|
||||
let b = b.1.name().to_lowercase();
|
||||
a.cmp(&b)
|
||||
});
|
||||
Ok(songs)
|
||||
}
|
||||
Some(pid) => {
|
||||
let Some(playlist) = state.manifest.store().get_playlist(&pid) else {
|
||||
anyhow::bail!("Couldnt find playlist (corruption?)");
|
||||
};
|
||||
let mut songs = Vec::new();
|
||||
for sid in playlist.songs() {
|
||||
if let Some(song) = state.manifest.store().get_song(&sid) {
|
||||
songs.push((sid.clone(), song.clone()));
|
||||
}
|
||||
}
|
||||
songs.sort_by(|a, b| {
|
||||
let a = a.1.name().to_lowercase();
|
||||
let b = b.1.name().to_lowercase();
|
||||
a.cmp(&b)
|
||||
});
|
||||
Ok(songs)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn get_songs_to_display(songs: &Vec<(uuid::Uuid, Song)>) -> crate::Result<Vec<(uuid::Uuid, Song)>>{
|
||||
let mut to_display = Vec::new();
|
||||
let query = {song_list_nav::SongListNav::get()?.parse_search()}.clone();
|
||||
for (sid, song) in songs {
|
||||
let should_display = match &query {
|
||||
SearchType::Name(s) |
|
||||
SearchType::Author(s) |
|
||||
SearchType::Source(s) if s.is_empty() => true,
|
||||
|
||||
SearchType::Source(s) => {
|
||||
song.source_type().to_string()
|
||||
.to_lowercase()
|
||||
.contains(s)
|
||||
},
|
||||
SearchType::Author(s) => {
|
||||
song.author()
|
||||
.to_lowercase()
|
||||
.contains(s)
|
||||
},
|
||||
SearchType::Name(s) => {
|
||||
song.name()
|
||||
.to_lowercase()
|
||||
.contains(s)
|
||||
},
|
||||
};
|
||||
|
||||
if should_display {
|
||||
to_display.push((sid.clone(), song.clone()));
|
||||
}
|
||||
}
|
||||
Ok(to_display)
|
||||
}
|
||||
|
||||
fn display_song_tab(ui: &mut egui::Ui, state: &mut crate::GuiState, sid: &uuid::Uuid, song: &Song) -> crate::Result<()> {
|
||||
let mut clicked = false;
|
||||
ui.horizontal(|ui| {
|
||||
let mut clicked = ui.add(
|
||||
let theme = handle_error_ui!(xmpd_settings::Settings::get()).theme.clone();
|
||||
let img = ui.add(
|
||||
egui::Image::new(crate::data::NOTE_ICON)
|
||||
.tint(theme.accent_color)
|
||||
.sense(Sense::click())
|
||||
.fit_to_exact_size(Vec2::new(32.0, 32.0))
|
||||
).clicked();
|
||||
);
|
||||
let status = {
|
||||
handle_error_ui!(xmpd_cache::Cache::get()).get_cached_song_status(&sid).clone()
|
||||
};
|
||||
if img.clicked() {
|
||||
clicked = true;
|
||||
}
|
||||
if img.hovered() {
|
||||
if matches!(status, Some(DlStatus::Done(_))) {
|
||||
ui.output_mut(|o| o.cursor_icon = CursorIcon::PointingHand);
|
||||
} else {
|
||||
ui.output_mut(|o| o.cursor_icon = CursorIcon::Default);
|
||||
}
|
||||
}
|
||||
|
||||
ui.vertical(|ui| {
|
||||
let selected_song_id = {handle_error_ui!(SongList::get()).selected_song_id};
|
||||
let label = if selected_song_id == *sid {
|
||||
ui.label(
|
||||
let slf = handle_error_ui!(SongList::get());
|
||||
let label = if slf.selected_sid == *sid {
|
||||
RichText::new(song.name())
|
||||
.color(theme.accent_color)
|
||||
)
|
||||
} else {
|
||||
ui.label(
|
||||
} else if matches!(status, Some(DlStatus::Done(_))) {
|
||||
RichText::new(song.name())
|
||||
.color(theme.text_color)
|
||||
)
|
||||
} else {
|
||||
RichText::new(song.name())
|
||||
.color(theme.dim_text_color)
|
||||
};
|
||||
let label = ui.label(label);
|
||||
|
||||
if label.clicked() {
|
||||
clicked = true;
|
||||
}
|
||||
if label.hovered() {
|
||||
if matches!(status, Some(DlStatus::Done(_))) {
|
||||
ui.output_mut(|o| o.cursor_icon = CursorIcon::PointingHand);
|
||||
} else {
|
||||
ui.output_mut(|o| o.cursor_icon = CursorIcon::Default);
|
||||
}
|
||||
}
|
||||
ui.monospace(
|
||||
RichText::new(format!("By {}", song.author()))
|
||||
.color(theme.dim_text_color)
|
||||
|
@ -118,48 +167,41 @@ fn display_song_tab(ui: &mut egui::Ui, sid: &uuid::Uuid, song: &Song) {
|
|||
|
||||
ui.with_layout(egui::Layout::right_to_left(egui::Align::RIGHT), |ui| {
|
||||
ui.add_space(3.0);
|
||||
let status = {
|
||||
handle_error_ui!(xmpd_cache::Cache::get()).get_cached_song_status(&sid).clone()
|
||||
};
|
||||
match status {
|
||||
Some(DlStatus::Done(p)) => {
|
||||
let img = ui.add(
|
||||
egui::Image::new(crate::data::CHECK_ICON)
|
||||
.tint(Color32::LIGHT_GREEN)
|
||||
.sense(Sense::hover())
|
||||
.fit_to_exact_size(Vec2::new(16.0, 16.0))
|
||||
);
|
||||
|
||||
img.on_hover_ui(|ui| {
|
||||
ui.label(format!("Path: {p}"));
|
||||
});
|
||||
match status {
|
||||
Some(DlStatus::Done(_p)) => {
|
||||
//let img = egui::Image::new(crate::data::CHECK_ICON)
|
||||
// .tint(Color32::LIGHT_GREEN)
|
||||
// .sense(Sense::hover())
|
||||
// .fit_to_exact_size(Vec2::new(16.0, 16.0));
|
||||
|
||||
//ui.add(img).on_hover_ui(|ui| {
|
||||
// ui.label(format!("Path: {p}"));
|
||||
//});
|
||||
}
|
||||
Some(DlStatus::Downloading) => {
|
||||
ui.add(
|
||||
egui::Spinner::new()
|
||||
let spinner = egui::Spinner::new()
|
||||
.color(theme.accent_color)
|
||||
.size(16.0)
|
||||
);
|
||||
.size(16.0);
|
||||
ui.add(spinner);
|
||||
}
|
||||
Some(DlStatus::Error(e)) => {
|
||||
let img = ui.add(
|
||||
egui::Image::new(crate::data::WARN_ICON)
|
||||
let img = egui::Image::new(crate::data::WARN_ICON)
|
||||
.tint(Color32::LIGHT_YELLOW)
|
||||
.sense(Sense::hover())
|
||||
.fit_to_exact_size(Vec2::new(16.0, 16.0))
|
||||
);
|
||||
img.on_hover_ui(|ui| {
|
||||
.fit_to_exact_size(Vec2::new(16.0, 16.0));
|
||||
|
||||
ui.add(img).on_hover_ui(|ui| {
|
||||
ui.label(e);
|
||||
});
|
||||
}
|
||||
None => {
|
||||
let img = ui.add(
|
||||
egui::Image::new(crate::data::DL_ICON)
|
||||
let img = egui::Image::new(crate::data::DL_ICON)
|
||||
.tint(theme.accent_color)
|
||||
.sense(Sense::click())
|
||||
.fit_to_exact_size(Vec2::new(16.0, 16.0))
|
||||
);
|
||||
if img.clicked() {
|
||||
.fit_to_exact_size(Vec2::new(16.0, 16.0));
|
||||
|
||||
if ui.add(img).clicked() {
|
||||
handle_error_ui!(xmpd_cache::Cache::get()).download_to_cache(sid.clone(), song.clone());
|
||||
let mut toast = handle_error_ui!(crate::components::toast::Toast::get());
|
||||
toast.show_toast(
|
||||
|
@ -171,10 +213,59 @@ fn display_song_tab(ui: &mut egui::Ui, sid: &uuid::Uuid, song: &Song) {
|
|||
}
|
||||
}
|
||||
});
|
||||
if clicked {
|
||||
handle_error_ui!(SongList::get()).selected_song_id = sid.clone();
|
||||
}
|
||||
});
|
||||
|
||||
ui.separator();
|
||||
if clicked {
|
||||
let mut sl = SongList::get()?;
|
||||
sl.play_song(sid.clone(), state)?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
pub fn play_song(&mut self, sid: uuid::Uuid, state: &mut crate::GuiState) -> crate::Result<()> {
|
||||
if self.playable_songs.contains(&sid) {
|
||||
self.selected_sid = sid.clone();
|
||||
let path = state.manifest.get_song_as_path(sid)?;
|
||||
state.player.play_song(&path)?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
pub fn play_prev(&mut self, state: &mut crate::GuiState) -> crate::Result<()> {
|
||||
let Some(mut prev) = self.playable_songs.last().cloned() else {
|
||||
anyhow::bail!("Trying to play a song in an empty playlist (impossible)")
|
||||
};
|
||||
for sid in self.playable_songs.clone() {
|
||||
if sid == self.selected_sid {
|
||||
self.play_song(prev, state)?;
|
||||
}
|
||||
prev = sid;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
pub fn play_next(&mut self, state: &mut crate::GuiState) -> crate::Result<()> {
|
||||
let Some(mut next) = self.playable_songs.first().cloned() else {
|
||||
anyhow::bail!("Trying to play a song in an empty playlist (impossible)")
|
||||
};
|
||||
let mut found = false;
|
||||
for sid in self.playable_songs.clone() {
|
||||
if sid == self.selected_sid {
|
||||
found = true;
|
||||
} else if found {
|
||||
next = sid;
|
||||
break;
|
||||
}
|
||||
}
|
||||
self.play_song(next, state)?;
|
||||
Ok(())
|
||||
}
|
||||
fn get_playable_songs(songs: &Vec<(uuid::Uuid, Song)>) -> crate::Result<Vec<uuid::Uuid>> {
|
||||
let mut playable_songs = Vec::new();
|
||||
|
||||
for (sid, _) in songs {
|
||||
if let Some(DlStatus::Done(_)) = xmpd_cache::Cache::get()?.get_cached_song_status(&sid) {
|
||||
playable_songs.push(sid.clone());
|
||||
}
|
||||
}
|
||||
Ok(playable_songs)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,8 +1,11 @@
|
|||
use uuid::Uuid;
|
||||
use xmpd_manifest::store::BaseStore;
|
||||
use xmpd_cache::DlStatus;
|
||||
use xmpd_manifest::{song::Song, store::BaseStore};
|
||||
|
||||
use crate::components::{left_nav::LeftNav, toast::ToastType, CompGetter, CompUi};
|
||||
|
||||
use super::SongList;
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub enum SearchType {
|
||||
Name(String),
|
||||
|
@ -53,8 +56,9 @@ impl CompUi for SongListNav {
|
|||
None => {
|
||||
songs = state.manifest.store().get_songs().keys().cloned().collect();
|
||||
}
|
||||
|
||||
}
|
||||
for sid in &songs {
|
||||
for sid in handle_error_ui!(Self::get_songs_to_download(&songs)) {
|
||||
if let Some(song) = state.manifest.store().get_song(&sid) {
|
||||
handle_error_ui!(xmpd_cache::Cache::get()).download_to_cache(sid.clone(), song.clone())
|
||||
}
|
||||
|
@ -86,6 +90,16 @@ impl SongListNav {
|
|||
i @ _ => SearchType::Name(i.to_string().to_lowercase())
|
||||
}
|
||||
}
|
||||
fn get_songs_to_download(songs: &Vec<uuid::Uuid>) -> crate::Result<Vec<uuid::Uuid>> {
|
||||
let mut songs2 = Vec::new();
|
||||
|
||||
for sid in songs {
|
||||
if let None = xmpd_cache::Cache::get()?.get_cached_song_status(&sid) {
|
||||
songs2.push(sid.clone());
|
||||
}
|
||||
}
|
||||
Ok(songs2)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
|
||||
use std::{collections::VecDeque, time::SystemTime};
|
||||
|
||||
use egui::{epaint::Shadow, load::TexturePoll, Align2, Color32, Frame, Image, Margin, Pos2, Rect, RichText, Rounding, Stroke, Style, Vec2};
|
||||
use egui::{epaint::Shadow, load::TexturePoll, Align2, Color32, Frame, Image, ImageSource, Margin, Pos2, Rect, RichText, Rounding, Stroke, Style, TextureFilter, TextureOptions, TextureWrapMode, Vec2};
|
||||
|
||||
use super::{CompGetter, CompUi};
|
||||
|
||||
|
@ -43,19 +43,24 @@ impl CompUi for Toast {
|
|||
ToastType::Info => {
|
||||
color = theme.accent_color;
|
||||
img = Image::new(crate::data::INFO_ICON)
|
||||
.max_size(Vec2::new(16.0, 16.0))
|
||||
.fit_to_exact_size(Vec2::new(16.0, 16.0))
|
||||
.tint(color);
|
||||
}
|
||||
ToastType::Warn => {
|
||||
color = crate::data::C_WARN;
|
||||
img = Image::new(crate::data::WARN_ICON)
|
||||
.max_size(Vec2::new(16.0, 16.0))
|
||||
.fit_to_exact_size(Vec2::new(16.0, 16.0))
|
||||
.texture_options(TextureOptions {
|
||||
magnification: TextureFilter::Linear,
|
||||
minification: TextureFilter::Linear,
|
||||
wrap_mode: TextureWrapMode::ClampToEdge,
|
||||
})
|
||||
.tint(color);
|
||||
}
|
||||
ToastType::Error => {
|
||||
color = Color32::LIGHT_RED;
|
||||
img = Image::new(crate::data::ERROR_ICON)
|
||||
.max_size(Vec2::new(16.0, 16.0))
|
||||
.fit_to_exact_size(Vec2::new(16.0, 16.0))
|
||||
.tint(color);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,9 +1,19 @@
|
|||
use std::path::PathBuf;
|
||||
|
||||
use egui::TextBuffer;
|
||||
use xmpd_manifest::store::{JsonStore, TomlStore};
|
||||
|
||||
use crate::windows::WindowId;
|
||||
|
||||
use super::CompUi;
|
||||
use super::{CompGetter, CompUi};
|
||||
|
||||
#[derive(Debug, Default)]
|
||||
pub struct TopNav;
|
||||
pub struct TopNav {
|
||||
// for dialog
|
||||
manifest_path: Option<(PathBuf, String)>
|
||||
}
|
||||
|
||||
component_register!(TopNav);
|
||||
|
||||
impl CompUi for TopNav {
|
||||
fn draw(ui: &mut egui::Ui, state: &mut crate::GuiState) -> crate::Result<()> {
|
||||
|
@ -22,6 +32,25 @@ impl CompUi for TopNav {
|
|||
handle_error_ui!(state.manifest.save());
|
||||
ui.close_menu();
|
||||
}
|
||||
if ui.button("Save As").clicked() {
|
||||
std::thread::spawn(|| -> crate::Result<()> {
|
||||
let mut dialog = rfd::FileDialog::new()
|
||||
.add_filter("Json", &["json"])
|
||||
.add_filter("Toml", &["toml"])
|
||||
.set_title("Save Manifest As")
|
||||
.set_can_create_directories(true)
|
||||
.set_file_name("manifest");
|
||||
if let Some(home_dir) = dirs::home_dir() {
|
||||
dialog = dialog.set_directory(home_dir);
|
||||
}
|
||||
if let Some(path) = dialog.save_file() {
|
||||
if let Some(ext) = path.extension() {
|
||||
TopNav::get()?.manifest_path = Some((path.clone(), ext.to_string_lossy().to_string()))
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
});
|
||||
}
|
||||
});
|
||||
ui.menu_button("Help", |ui| {
|
||||
if ui.button("Source").clicked() {
|
||||
|
@ -39,7 +68,20 @@ impl CompUi for TopNav {
|
|||
|
||||
});
|
||||
});
|
||||
|
||||
let mut used = false;
|
||||
if let Some((path, ext)) = &TopNav::get()?.manifest_path {
|
||||
match ext.as_str() {
|
||||
"json" => state.manifest.convert_and_save_to::<JsonStore>(&path)?,
|
||||
"toml" => state.manifest.convert_and_save_to::<TomlStore>(&path)?,
|
||||
_ => ()
|
||||
}
|
||||
used = true;
|
||||
}
|
||||
if used {
|
||||
TopNav::get()?.manifest_path = None;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -36,11 +36,13 @@ pub fn start() -> Result<()> {
|
|||
pub struct GuiState {
|
||||
pub manifest: Manifest<JsonStore>,
|
||||
pub windows: windows::Windows,
|
||||
pub player: xmpd_player::Player,
|
||||
}
|
||||
|
||||
impl GuiState {
|
||||
pub fn new() -> Result<Self> {
|
||||
Ok(Self {
|
||||
player: xmpd_player::Player::new(),
|
||||
manifest: Manifest::new(&xmpd_cliargs::CLIARGS.manifest_path())?,
|
||||
windows: windows::Windows::new(),
|
||||
})
|
||||
|
|
|
@ -35,3 +35,14 @@ macro_rules! handle_error_ui {
|
|||
}
|
||||
};
|
||||
}
|
||||
|
||||
macro_rules! handle_option {
|
||||
($reason:expr, $val:expr) => {
|
||||
if let Some(v) = $val {
|
||||
v
|
||||
} else {
|
||||
handle_error_ui!(Err(anyhow::anyhow!($reason)));
|
||||
return;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
|
|
@ -18,8 +18,11 @@ crate-type = ["rlib"]
|
|||
bench = false
|
||||
|
||||
[dependencies]
|
||||
xmpd-cliargs.path = "../xmpd-cliargs"
|
||||
xmpd-settings.path = "../xmpd-settings"
|
||||
anyhow.workspace = true
|
||||
uuid.workspace = true
|
||||
serde.workspace = true
|
||||
serde_json.workspace = true
|
||||
url.workspace = true
|
||||
toml.workspace = true
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
use std::path::Path;
|
||||
use std::path::{Path, PathBuf};
|
||||
|
||||
|
||||
#[cfg(test)]
|
||||
|
@ -43,6 +43,29 @@ impl<ST: store::BaseStore + Clone> Manifest<ST> {
|
|||
self.store_mut().load_from(&p)?;
|
||||
Ok(())
|
||||
}
|
||||
pub fn convert_to<ST2: store::BaseStore>(&self) -> ST2 {
|
||||
let songs = self.store().get_songs().clone();
|
||||
let playlists = self.store().get_playlists().clone();
|
||||
let mut st2 = ST2::empty();
|
||||
*st2.get_songs_mut() = songs;
|
||||
*st2.get_playlists_mut() = playlists;
|
||||
st2
|
||||
}
|
||||
|
||||
pub fn convert_and_save_to<ST2: store::BaseStore>(&self, path: &Path) -> Result<()> {
|
||||
let st2 = self.convert_to::<ST2>();
|
||||
std::fs::write(path, st2.to_bytes()?)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn get_song_as_path(&self, sid: uuid::Uuid) -> Result<PathBuf> {
|
||||
let ext = &xmpd_settings::Settings::get()?.tooling.song_format;
|
||||
let mut p = xmpd_cliargs::CLIARGS.cache_path().into_std_path_buf();
|
||||
p.push("songs");
|
||||
p.push(sid.to_string());
|
||||
p.set_extension(ext);
|
||||
Ok(p)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
use std::{path::PathBuf, str::FromStr};
|
||||
use std::{fmt::Display, path::PathBuf, str::FromStr};
|
||||
|
||||
|
||||
|
||||
|
@ -72,3 +72,14 @@ impl SourceType {
|
|||
}
|
||||
}
|
||||
|
||||
impl Display for SourceType {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
match self {
|
||||
Self::Youtube => write!(f, "YouTube"),
|
||||
Self::Soundcloud => write!(f, "SoundCloud"),
|
||||
Self::Spotify => write!(f, "Spotify"),
|
||||
s => write!(f, "{s:?}"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -5,7 +5,9 @@ use uuid::Uuid;
|
|||
use crate::{playlist::Playlist, song::Song};
|
||||
|
||||
mod json;
|
||||
mod toml;
|
||||
pub use json::JsonStore;
|
||||
pub use toml::TomlStore;
|
||||
|
||||
pub trait BaseStore {
|
||||
fn get_songs(&self) -> &HashMap<Uuid, Song>;
|
||||
|
|
71
xmpd-manifest/src/store/toml.rs
Normal file
71
xmpd-manifest/src/store/toml.rs
Normal file
|
@ -0,0 +1,71 @@
|
|||
use std::{collections::HashMap, path::PathBuf};
|
||||
use uuid::Uuid;
|
||||
use crate::{playlist::Playlist, song::Song};
|
||||
|
||||
const DEFAULT_TEXT: &str = r#"{
|
||||
"songs": {},
|
||||
"playlists": {}
|
||||
}"#;
|
||||
|
||||
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize, PartialEq)]
|
||||
pub struct TomlStore {
|
||||
#[serde(skip)]
|
||||
original_path: PathBuf,
|
||||
songs: HashMap<Uuid, Song>,
|
||||
playlists: HashMap<Uuid, Playlist>
|
||||
}
|
||||
|
||||
impl super::BaseStore for TomlStore {
|
||||
fn get_default_file_contents() -> &'static str {
|
||||
&DEFAULT_TEXT
|
||||
}
|
||||
fn get_file_extension() -> &'static str {
|
||||
"toml"
|
||||
}
|
||||
fn empty() -> Self where Self: Sized {
|
||||
Self {
|
||||
original_path: PathBuf::new(),
|
||||
songs: HashMap::default(),
|
||||
playlists: HashMap::default(),
|
||||
}
|
||||
}
|
||||
fn to_bytes(&self) -> crate::Result<Vec<u8>> {
|
||||
let s: Vec<u8> = toml::to_string_pretty(self)?.chars().map(|c| c as u8).collect();
|
||||
Ok(s)
|
||||
}
|
||||
fn from_bytes(s: &[u8]) -> crate::Result<Self> where Self: Sized {
|
||||
let s: Self = toml::from_str(&String::from_utf8(s.to_vec())?)?;
|
||||
Ok(s)
|
||||
}
|
||||
fn get_songs(&self) -> &HashMap<Uuid, Song> {
|
||||
&self.songs
|
||||
}
|
||||
fn get_songs_mut(&mut self) -> &mut HashMap<Uuid, Song> {
|
||||
&mut self.songs
|
||||
}
|
||||
fn get_song(&self, id: &Uuid) -> Option<&Song> {
|
||||
self.songs.get(id)
|
||||
}
|
||||
fn get_song_mut(&mut self, id: &Uuid) -> Option<&mut Song> {
|
||||
self.songs.get_mut(id)
|
||||
}
|
||||
fn get_playlists(&self) -> &HashMap<Uuid, Playlist> {
|
||||
&self.playlists
|
||||
}
|
||||
fn get_playlists_mut(&mut self) -> &mut HashMap<Uuid, Playlist> {
|
||||
&mut self.playlists
|
||||
}
|
||||
fn get_playlist(&self, id: &Uuid) -> Option<&Playlist> {
|
||||
self.playlists.get(id)
|
||||
}
|
||||
fn get_playlist_mut(&mut self, id: &Uuid) -> Option<&mut Playlist> {
|
||||
self.playlists.get_mut(id)
|
||||
}
|
||||
fn save_original_path(&mut self, p: &std::path::Path) {
|
||||
self.original_path = p.to_path_buf();
|
||||
}
|
||||
fn get_original_path(&self) -> &std::path::Path {
|
||||
&self.original_path
|
||||
}
|
||||
}
|
||||
|
|
@ -7,4 +7,7 @@ license.workspace = true
|
|||
authors.workspace = true
|
||||
|
||||
[dependencies]
|
||||
rodio = { version = "0.20.1", features = ["symphonia-all"] }
|
||||
rodio.workspace = true
|
||||
lazy_static.workspace = true
|
||||
anyhow.workspace = true
|
||||
log.workspace = true
|
||||
|
|
|
@ -0,0 +1,101 @@
|
|||
use std::{path::Path, time::Duration};
|
||||
|
||||
use rodio::{Decoder, OutputStream, OutputStreamHandle, Sink, Source};
|
||||
|
||||
type Result<T> = anyhow::Result<T>;
|
||||
|
||||
pub struct Player {
|
||||
_stream: OutputStream,
|
||||
_stream_handle: OutputStreamHandle,
|
||||
sink: Sink,
|
||||
source_len: Duration,
|
||||
tested_for_song_end: bool,
|
||||
}
|
||||
|
||||
impl Player {
|
||||
pub fn new() -> Self {
|
||||
let (_stream, _stream_handle) = OutputStream::try_default().unwrap();
|
||||
let sink = Sink::try_new(&_stream_handle).unwrap();
|
||||
sink.pause();
|
||||
Self {
|
||||
_stream,
|
||||
_stream_handle,
|
||||
sink,
|
||||
source_len: Default::default(),
|
||||
tested_for_song_end: true,
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
pub fn play_song(&mut self, path: &Path) -> Result<()> {
|
||||
let file = std::io::BufReader::new(std::fs::File::open(path)?);
|
||||
let source = Decoder::new(file)?;
|
||||
self.tested_for_song_end = false;
|
||||
self.source_len = source.total_duration().unwrap();
|
||||
self.sink.clear();
|
||||
self.sink.append(source);
|
||||
self.sink.play();
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn get_played_f(&self) -> f64 {
|
||||
let source_len = self.source_len.as_millis();
|
||||
let curr_len = self.sink.get_pos().as_millis();
|
||||
if source_len == 0 || curr_len == 0 {
|
||||
return 0.0;
|
||||
}
|
||||
curr_len as f64 / source_len as f64
|
||||
}
|
||||
|
||||
pub fn get_played_len(&self) -> Duration {
|
||||
self.sink.get_pos()
|
||||
}
|
||||
|
||||
pub fn get_ms_left(&self) -> u64 {
|
||||
let source_len = self.source_len.as_millis();
|
||||
let curr_len = self.sink.get_pos().as_millis();
|
||||
if source_len < curr_len {
|
||||
return 0;
|
||||
}
|
||||
(source_len - curr_len) as u64
|
||||
}
|
||||
|
||||
pub fn just_stopped(&mut self) -> bool {
|
||||
if self.sink.len() == 0 && !self.tested_for_song_end {
|
||||
self.tested_for_song_end = true;
|
||||
true
|
||||
} else {
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
pub fn play(&self) {
|
||||
self.sink.play()
|
||||
}
|
||||
|
||||
pub fn pause(&self) {
|
||||
self.sink.pause()
|
||||
}
|
||||
|
||||
pub fn seek_to_f(&self, f: f64) -> Result<()> {
|
||||
let dur = self.source_len.as_millis() as f64 * f.clamp(0.0, 1.0);
|
||||
self.seek(Duration::from_millis(dur as u64))?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn seek(&self, d: Duration) -> Result<()> {
|
||||
if let Err(e) = self.sink.try_seek(d) {
|
||||
anyhow::bail!("{e:?}");
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn is_paused(&self) -> bool {
|
||||
self.sink.is_paused()
|
||||
}
|
||||
|
||||
pub fn set_volume(&self, vol: f64) {
|
||||
// clamped for the safety of my, and your ears
|
||||
self.sink.set_volume(vol.clamp(0.0, 1.0) as f32);
|
||||
}
|
||||
}
|
|
@ -12,4 +12,4 @@ camino.workspace = true
|
|||
egui.workspace = true
|
||||
lazy_static.workspace = true
|
||||
serde.workspace = true
|
||||
toml = "0.8.19"
|
||||
toml.workspace = true
|
||||
|
|
Loading…
Reference in New Issue
Block a user