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",
 | 
					 "futures-lite 1.13.0",
 | 
				
			||||||
 "once_cell",
 | 
					 "once_cell",
 | 
				
			||||||
 "serde",
 | 
					 "serde",
 | 
				
			||||||
 "zbus",
 | 
					 "zbus 3.15.2",
 | 
				
			||||||
]
 | 
					]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
[[package]]
 | 
					[[package]]
 | 
				
			||||||
| 
						 | 
					@ -93,15 +93,6 @@ dependencies = [
 | 
				
			||||||
 "winit",
 | 
					 "winit",
 | 
				
			||||||
]
 | 
					]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
[[package]]
 | 
					 | 
				
			||||||
name = "addr2line"
 | 
					 | 
				
			||||||
version = "0.24.2"
 | 
					 | 
				
			||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
 | 
					 | 
				
			||||||
checksum = "dfbe277e56a376000877090da837660b4427aad530e3028d44e0bffe4f89a1c1"
 | 
					 | 
				
			||||||
dependencies = [
 | 
					 | 
				
			||||||
 "gimli",
 | 
					 | 
				
			||||||
]
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
[[package]]
 | 
					[[package]]
 | 
				
			||||||
name = "adler2"
 | 
					name = "adler2"
 | 
				
			||||||
version = "2.0.0"
 | 
					version = "2.0.0"
 | 
				
			||||||
| 
						 | 
					@ -292,6 +283,28 @@ dependencies = [
 | 
				
			||||||
 "libloading 0.7.4",
 | 
					 "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]]
 | 
					[[package]]
 | 
				
			||||||
name = "async-broadcast"
 | 
					name = "async-broadcast"
 | 
				
			||||||
version = "0.5.1"
 | 
					version = "0.5.1"
 | 
				
			||||||
| 
						 | 
					@ -302,6 +315,18 @@ dependencies = [
 | 
				
			||||||
 "futures-core",
 | 
					 "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]]
 | 
					[[package]]
 | 
				
			||||||
name = "async-channel"
 | 
					name = "async-channel"
 | 
				
			||||||
version = "2.3.1"
 | 
					version = "2.3.1"
 | 
				
			||||||
| 
						 | 
					@ -339,6 +364,17 @@ dependencies = [
 | 
				
			||||||
 "futures-lite 1.13.0",
 | 
					 "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]]
 | 
					[[package]]
 | 
				
			||||||
name = "async-io"
 | 
					name = "async-io"
 | 
				
			||||||
version = "1.13.0"
 | 
					version = "1.13.0"
 | 
				
			||||||
| 
						 | 
					@ -398,6 +434,17 @@ dependencies = [
 | 
				
			||||||
 "pin-project-lite",
 | 
					 "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]]
 | 
					[[package]]
 | 
				
			||||||
name = "async-once-cell"
 | 
					name = "async-once-cell"
 | 
				
			||||||
version = "0.5.4"
 | 
					version = "0.5.4"
 | 
				
			||||||
| 
						 | 
					@ -421,6 +468,25 @@ dependencies = [
 | 
				
			||||||
 "windows-sys 0.48.0",
 | 
					 "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]]
 | 
					[[package]]
 | 
				
			||||||
name = "async-recursion"
 | 
					name = "async-recursion"
 | 
				
			||||||
version = "1.1.1"
 | 
					version = "1.1.1"
 | 
				
			||||||
| 
						 | 
					@ -493,9 +559,9 @@ dependencies = [
 | 
				
			||||||
 "enumflags2",
 | 
					 "enumflags2",
 | 
				
			||||||
 "serde",
 | 
					 "serde",
 | 
				
			||||||
 "static_assertions",
 | 
					 "static_assertions",
 | 
				
			||||||
 "zbus",
 | 
					 "zbus 3.15.2",
 | 
				
			||||||
 "zbus_names",
 | 
					 "zbus_names 2.6.1",
 | 
				
			||||||
 "zvariant",
 | 
					 "zvariant 3.15.2",
 | 
				
			||||||
]
 | 
					]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
[[package]]
 | 
					[[package]]
 | 
				
			||||||
| 
						 | 
					@ -507,7 +573,7 @@ dependencies = [
 | 
				
			||||||
 "atspi-common",
 | 
					 "atspi-common",
 | 
				
			||||||
 "atspi-proxies",
 | 
					 "atspi-proxies",
 | 
				
			||||||
 "futures-lite 1.13.0",
 | 
					 "futures-lite 1.13.0",
 | 
				
			||||||
 "zbus",
 | 
					 "zbus 3.15.2",
 | 
				
			||||||
]
 | 
					]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
[[package]]
 | 
					[[package]]
 | 
				
			||||||
| 
						 | 
					@ -518,7 +584,7 @@ checksum = "6495661273703e7a229356dcbe8c8f38223d697aacfaf0e13590a9ac9977bb52"
 | 
				
			||||||
dependencies = [
 | 
					dependencies = [
 | 
				
			||||||
 "atspi-common",
 | 
					 "atspi-common",
 | 
				
			||||||
 "serde",
 | 
					 "serde",
 | 
				
			||||||
 "zbus",
 | 
					 "zbus 3.15.2",
 | 
				
			||||||
]
 | 
					]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
[[package]]
 | 
					[[package]]
 | 
				
			||||||
| 
						 | 
					@ -527,21 +593,6 @@ version = "1.4.0"
 | 
				
			||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
 | 
					source = "registry+https://github.com/rust-lang/crates.io-index"
 | 
				
			||||||
checksum = "ace50bade8e6234aa140d9a2f552bbee1db4d353f69b8217bc503490fc1a9f26"
 | 
					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]]
 | 
					[[package]]
 | 
				
			||||||
name = "base64"
 | 
					name = "base64"
 | 
				
			||||||
version = "0.21.7"
 | 
					version = "0.21.7"
 | 
				
			||||||
| 
						 | 
					@ -811,6 +862,12 @@ version = "0.1.1"
 | 
				
			||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
 | 
					source = "registry+https://github.com/rust-lang/crates.io-index"
 | 
				
			||||||
checksum = "fd16c4719339c4530435d38e511904438d07cce7950afa3718a84ac36c10e89e"
 | 
					checksum = "fd16c4719339c4530435d38e511904438d07cce7950afa3718a84ac36c10e89e"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					[[package]]
 | 
				
			||||||
 | 
					name = "cfg_aliases"
 | 
				
			||||||
 | 
					version = "0.2.1"
 | 
				
			||||||
 | 
					source = "registry+https://github.com/rust-lang/crates.io-index"
 | 
				
			||||||
 | 
					checksum = "613afe47fcd5fac7ccf1db93babcb082c5994d996f20b8b159f2ad1658eb5724"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
[[package]]
 | 
					[[package]]
 | 
				
			||||||
name = "cgl"
 | 
					name = "cgl"
 | 
				
			||||||
version = "0.3.2"
 | 
					version = "0.3.2"
 | 
				
			||||||
| 
						 | 
					@ -1212,15 +1269,6 @@ dependencies = [
 | 
				
			||||||
 "serde",
 | 
					 "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]]
 | 
					[[package]]
 | 
				
			||||||
name = "eframe"
 | 
					name = "eframe"
 | 
				
			||||||
version = "0.27.2"
 | 
					version = "0.27.2"
 | 
				
			||||||
| 
						 | 
					@ -1230,7 +1278,7 @@ dependencies = [
 | 
				
			||||||
 "bytemuck",
 | 
					 "bytemuck",
 | 
				
			||||||
 "cocoa",
 | 
					 "cocoa",
 | 
				
			||||||
 "document-features",
 | 
					 "document-features",
 | 
				
			||||||
 "egui 0.27.2",
 | 
					 "egui",
 | 
				
			||||||
 "egui-wgpu",
 | 
					 "egui-wgpu",
 | 
				
			||||||
 "egui-winit",
 | 
					 "egui-winit",
 | 
				
			||||||
 "egui_glow",
 | 
					 "egui_glow",
 | 
				
			||||||
| 
						 | 
					@ -1263,33 +1311,12 @@ checksum = "584c5d1bf9a67b25778a3323af222dbe1a1feb532190e103901187f92c7fe29a"
 | 
				
			||||||
dependencies = [
 | 
					dependencies = [
 | 
				
			||||||
 "accesskit",
 | 
					 "accesskit",
 | 
				
			||||||
 "ahash",
 | 
					 "ahash",
 | 
				
			||||||
 "epaint 0.27.2",
 | 
					 "epaint",
 | 
				
			||||||
 "log",
 | 
					 "log",
 | 
				
			||||||
 "nohash-hasher",
 | 
					 "nohash-hasher",
 | 
				
			||||||
 "serde",
 | 
					 "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]]
 | 
					[[package]]
 | 
				
			||||||
name = "egui-wgpu"
 | 
					name = "egui-wgpu"
 | 
				
			||||||
version = "0.27.2"
 | 
					version = "0.27.2"
 | 
				
			||||||
| 
						 | 
					@ -1298,8 +1325,8 @@ checksum = "469ff65843f88a702b731a1532b7d03b0e8e96d283e70f3a22b0e06c46cb9b37"
 | 
				
			||||||
dependencies = [
 | 
					dependencies = [
 | 
				
			||||||
 "bytemuck",
 | 
					 "bytemuck",
 | 
				
			||||||
 "document-features",
 | 
					 "document-features",
 | 
				
			||||||
 "egui 0.27.2",
 | 
					 "egui",
 | 
				
			||||||
 "epaint 0.27.2",
 | 
					 "epaint",
 | 
				
			||||||
 "log",
 | 
					 "log",
 | 
				
			||||||
 "thiserror",
 | 
					 "thiserror",
 | 
				
			||||||
 "type-map",
 | 
					 "type-map",
 | 
				
			||||||
| 
						 | 
					@ -1316,7 +1343,7 @@ checksum = "2e3da0cbe020f341450c599b35b92de4af7b00abde85624fd16f09c885573609"
 | 
				
			||||||
dependencies = [
 | 
					dependencies = [
 | 
				
			||||||
 "accesskit_winit",
 | 
					 "accesskit_winit",
 | 
				
			||||||
 "arboard",
 | 
					 "arboard",
 | 
				
			||||||
 "egui 0.27.2",
 | 
					 "egui",
 | 
				
			||||||
 "log",
 | 
					 "log",
 | 
				
			||||||
 "raw-window-handle 0.6.2",
 | 
					 "raw-window-handle 0.6.2",
 | 
				
			||||||
 "smithay-clipboard",
 | 
					 "smithay-clipboard",
 | 
				
			||||||
| 
						 | 
					@ -1331,7 +1358,7 @@ version = "0.27.2"
 | 
				
			||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
 | 
					source = "registry+https://github.com/rust-lang/crates.io-index"
 | 
				
			||||||
checksum = "1b78779f35ded1a853786c9ce0b43fe1053e10a21ea3b23ebea411805ce41593"
 | 
					checksum = "1b78779f35ded1a853786c9ce0b43fe1053e10a21ea3b23ebea411805ce41593"
 | 
				
			||||||
dependencies = [
 | 
					dependencies = [
 | 
				
			||||||
 "egui 0.27.2",
 | 
					 "egui",
 | 
				
			||||||
 "ehttp",
 | 
					 "ehttp",
 | 
				
			||||||
 "enum-map",
 | 
					 "enum-map",
 | 
				
			||||||
 "image",
 | 
					 "image",
 | 
				
			||||||
| 
						 | 
					@ -1348,7 +1375,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
 | 
				
			||||||
checksum = "e0e5d975f3c86edc3d35b1db88bb27c15dde7c55d3b5af164968ab5ede3f44ca"
 | 
					checksum = "e0e5d975f3c86edc3d35b1db88bb27c15dde7c55d3b5af164968ab5ede3f44ca"
 | 
				
			||||||
dependencies = [
 | 
					dependencies = [
 | 
				
			||||||
 "bytemuck",
 | 
					 "bytemuck",
 | 
				
			||||||
 "egui 0.27.2",
 | 
					 "egui",
 | 
				
			||||||
 "glow",
 | 
					 "glow",
 | 
				
			||||||
 "log",
 | 
					 "log",
 | 
				
			||||||
 "memoffset 0.9.1",
 | 
					 "memoffset 0.9.1",
 | 
				
			||||||
| 
						 | 
					@ -1387,12 +1414,6 @@ dependencies = [
 | 
				
			||||||
 "serde",
 | 
					 "serde",
 | 
				
			||||||
]
 | 
					]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
[[package]]
 | 
					 | 
				
			||||||
name = "emath"
 | 
					 | 
				
			||||||
version = "0.28.1"
 | 
					 | 
				
			||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
 | 
					 | 
				
			||||||
checksum = "0a6a21708405ea88f63d8309650b4d77431f4bc28fb9d8e6f77d3963b51249e6"
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
[[package]]
 | 
					[[package]]
 | 
				
			||||||
name = "encoding_rs"
 | 
					name = "encoding_rs"
 | 
				
			||||||
version = "0.8.35"
 | 
					version = "0.8.35"
 | 
				
			||||||
| 
						 | 
					@ -1402,6 +1423,12 @@ dependencies = [
 | 
				
			||||||
 "cfg-if",
 | 
					 "cfg-if",
 | 
				
			||||||
]
 | 
					]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					[[package]]
 | 
				
			||||||
 | 
					name = "endi"
 | 
				
			||||||
 | 
					version = "1.1.0"
 | 
				
			||||||
 | 
					source = "registry+https://github.com/rust-lang/crates.io-index"
 | 
				
			||||||
 | 
					checksum = "a3d8a32ae18130a3c84dd492d4215c3d913c3b07c6b63c2eb3eb7ff1101ab7bf"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
[[package]]
 | 
					[[package]]
 | 
				
			||||||
name = "enum-map"
 | 
					name = "enum-map"
 | 
				
			||||||
version = "2.7.3"
 | 
					version = "2.7.3"
 | 
				
			||||||
| 
						 | 
					@ -1487,28 +1514,14 @@ dependencies = [
 | 
				
			||||||
 "ab_glyph",
 | 
					 "ab_glyph",
 | 
				
			||||||
 "ahash",
 | 
					 "ahash",
 | 
				
			||||||
 "bytemuck",
 | 
					 "bytemuck",
 | 
				
			||||||
 "ecolor 0.27.2",
 | 
					 "ecolor",
 | 
				
			||||||
 "emath 0.27.2",
 | 
					 "emath",
 | 
				
			||||||
 "log",
 | 
					 "log",
 | 
				
			||||||
 "nohash-hasher",
 | 
					 "nohash-hasher",
 | 
				
			||||||
 "parking_lot",
 | 
					 "parking_lot",
 | 
				
			||||||
 "serde",
 | 
					 "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]]
 | 
					[[package]]
 | 
				
			||||||
name = "equivalent"
 | 
					name = "equivalent"
 | 
				
			||||||
version = "1.0.1"
 | 
					version = "1.0.1"
 | 
				
			||||||
| 
						 | 
					@ -1651,6 +1664,15 @@ dependencies = [
 | 
				
			||||||
 "percent-encoding",
 | 
					 "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]]
 | 
					[[package]]
 | 
				
			||||||
name = "futures-core"
 | 
					name = "futures-core"
 | 
				
			||||||
version = "0.3.31"
 | 
					version = "0.3.31"
 | 
				
			||||||
| 
						 | 
					@ -1691,6 +1713,17 @@ dependencies = [
 | 
				
			||||||
 "pin-project-lite",
 | 
					 "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]]
 | 
					[[package]]
 | 
				
			||||||
name = "futures-sink"
 | 
					name = "futures-sink"
 | 
				
			||||||
version = "0.3.31"
 | 
					version = "0.3.31"
 | 
				
			||||||
| 
						 | 
					@ -1711,6 +1744,7 @@ checksum = "9fa08315bb612088cc391249efdc3bc77536f16c91f6cf495e6fbe85b20a4a81"
 | 
				
			||||||
dependencies = [
 | 
					dependencies = [
 | 
				
			||||||
 "futures-core",
 | 
					 "futures-core",
 | 
				
			||||||
 "futures-io",
 | 
					 "futures-io",
 | 
				
			||||||
 | 
					 "futures-macro",
 | 
				
			||||||
 "futures-sink",
 | 
					 "futures-sink",
 | 
				
			||||||
 "futures-task",
 | 
					 "futures-task",
 | 
				
			||||||
 "memchr",
 | 
					 "memchr",
 | 
				
			||||||
| 
						 | 
					@ -1750,12 +1784,6 @@ dependencies = [
 | 
				
			||||||
 "wasi",
 | 
					 "wasi",
 | 
				
			||||||
]
 | 
					]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
[[package]]
 | 
					 | 
				
			||||||
name = "gimli"
 | 
					 | 
				
			||||||
version = "0.31.1"
 | 
					 | 
				
			||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
 | 
					 | 
				
			||||||
checksum = "07e28edb80900c19c28f1072f2e8aeca7fa06b23cd4169cefe1af5aa3260783f"
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
[[package]]
 | 
					[[package]]
 | 
				
			||||||
name = "gl_generator"
 | 
					name = "gl_generator"
 | 
				
			||||||
version = "0.14.0"
 | 
					version = "0.14.0"
 | 
				
			||||||
| 
						 | 
					@ -1792,7 +1820,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
 | 
				
			||||||
checksum = "18fcd4ae4e86d991ad1300b8f57166e5be0c95ef1f63f3f5b827f8a164548746"
 | 
					checksum = "18fcd4ae4e86d991ad1300b8f57166e5be0c95ef1f63f3f5b827f8a164548746"
 | 
				
			||||||
dependencies = [
 | 
					dependencies = [
 | 
				
			||||||
 "bitflags 2.6.0",
 | 
					 "bitflags 2.6.0",
 | 
				
			||||||
 "cfg_aliases",
 | 
					 "cfg_aliases 0.1.1",
 | 
				
			||||||
 "cgl",
 | 
					 "cgl",
 | 
				
			||||||
 "core-foundation",
 | 
					 "core-foundation",
 | 
				
			||||||
 "dispatch",
 | 
					 "dispatch",
 | 
				
			||||||
| 
						 | 
					@ -1815,7 +1843,7 @@ version = "0.4.2"
 | 
				
			||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
 | 
					source = "registry+https://github.com/rust-lang/crates.io-index"
 | 
				
			||||||
checksum = "1ebcdfba24f73b8412c5181e56f092b5eff16671c514ce896b258a0a64bd7735"
 | 
					checksum = "1ebcdfba24f73b8412c5181e56f092b5eff16671c514ce896b258a0a64bd7735"
 | 
				
			||||||
dependencies = [
 | 
					dependencies = [
 | 
				
			||||||
 "cfg_aliases",
 | 
					 "cfg_aliases 0.1.1",
 | 
				
			||||||
 "glutin",
 | 
					 "glutin",
 | 
				
			||||||
 "raw-window-handle 0.5.2",
 | 
					 "raw-window-handle 0.5.2",
 | 
				
			||||||
 "winit",
 | 
					 "winit",
 | 
				
			||||||
| 
						 | 
					@ -2327,18 +2355,6 @@ dependencies = [
 | 
				
			||||||
 "simd-adler32",
 | 
					 "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]]
 | 
					[[package]]
 | 
				
			||||||
name = "naga"
 | 
					name = "naga"
 | 
				
			||||||
version = "0.19.2"
 | 
					version = "0.19.2"
 | 
				
			||||||
| 
						 | 
					@ -2402,6 +2418,19 @@ dependencies = [
 | 
				
			||||||
 "memoffset 0.7.1",
 | 
					 "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]]
 | 
					[[package]]
 | 
				
			||||||
name = "nohash-hasher"
 | 
					name = "nohash-hasher"
 | 
				
			||||||
version = "0.2.0"
 | 
					version = "0.2.0"
 | 
				
			||||||
| 
						 | 
					@ -2581,6 +2610,7 @@ checksum = "0ee638a5da3799329310ad4cfa62fbf045d5f56e3ef5ba4149e7452dcf89d5a8"
 | 
				
			||||||
dependencies = [
 | 
					dependencies = [
 | 
				
			||||||
 "bitflags 2.6.0",
 | 
					 "bitflags 2.6.0",
 | 
				
			||||||
 "block2 0.5.1",
 | 
					 "block2 0.5.1",
 | 
				
			||||||
 | 
					 "dispatch",
 | 
				
			||||||
 "libc",
 | 
					 "libc",
 | 
				
			||||||
 "objc2 0.5.2",
 | 
					 "objc2 0.5.2",
 | 
				
			||||||
]
 | 
					]
 | 
				
			||||||
| 
						 | 
					@ -2619,15 +2649,6 @@ dependencies = [
 | 
				
			||||||
 "cc",
 | 
					 "cc",
 | 
				
			||||||
]
 | 
					]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
[[package]]
 | 
					 | 
				
			||||||
name = "object"
 | 
					 | 
				
			||||||
version = "0.36.5"
 | 
					 | 
				
			||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
 | 
					 | 
				
			||||||
checksum = "aedf0a2d09c573ed1d8d85b30c119153926a2b36dce0ab28322c09a117a4683e"
 | 
					 | 
				
			||||||
dependencies = [
 | 
					 | 
				
			||||||
 "memchr",
 | 
					 | 
				
			||||||
]
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
[[package]]
 | 
					[[package]]
 | 
				
			||||||
name = "oboe"
 | 
					name = "oboe"
 | 
				
			||||||
version = "0.6.1"
 | 
					version = "0.6.1"
 | 
				
			||||||
| 
						 | 
					@ -2820,6 +2841,12 @@ dependencies = [
 | 
				
			||||||
 "windows-sys 0.59.0",
 | 
					 "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]]
 | 
					[[package]]
 | 
				
			||||||
name = "ppv-lite86"
 | 
					name = "ppv-lite86"
 | 
				
			||||||
version = "0.2.20"
 | 
					version = "0.2.20"
 | 
				
			||||||
| 
						 | 
					@ -3013,6 +3040,28 @@ dependencies = [
 | 
				
			||||||
 "usvg",
 | 
					 "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]]
 | 
					[[package]]
 | 
				
			||||||
name = "rgb"
 | 
					name = "rgb"
 | 
				
			||||||
version = "0.8.50"
 | 
					version = "0.8.50"
 | 
				
			||||||
| 
						 | 
					@ -3056,12 +3105,6 @@ version = "0.19.0"
 | 
				
			||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
 | 
					source = "registry+https://github.com/rust-lang/crates.io-index"
 | 
				
			||||||
checksum = "3cd14fd5e3b777a7422cca79358c57a8f6e3a703d9ac187448d0daf220c2407f"
 | 
					checksum = "3cd14fd5e3b777a7422cca79358c57a8f6e3a703d9ac187448d0daf220c2407f"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
[[package]]
 | 
					 | 
				
			||||||
name = "rustc-demangle"
 | 
					 | 
				
			||||||
version = "0.1.24"
 | 
					 | 
				
			||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
 | 
					 | 
				
			||||||
checksum = "719b953e2095829ee67db738b3bfa9fa368c94900df327b3f07fe6e794d2fe1f"
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
[[package]]
 | 
					[[package]]
 | 
				
			||||||
name = "rustc-hash"
 | 
					name = "rustc-hash"
 | 
				
			||||||
version = "1.1.0"
 | 
					version = "1.1.0"
 | 
				
			||||||
| 
						 | 
					@ -3673,30 +3716,15 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
 | 
				
			||||||
checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20"
 | 
					checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
[[package]]
 | 
					[[package]]
 | 
				
			||||||
name = "tokio"
 | 
					name = "toml"
 | 
				
			||||||
version = "1.41.0"
 | 
					version = "0.7.8"
 | 
				
			||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
 | 
					source = "registry+https://github.com/rust-lang/crates.io-index"
 | 
				
			||||||
checksum = "145f3413504347a2be84393cc8a7d2fb4d863b375909ea59f2158261aa258bbb"
 | 
					checksum = "dd79e69d3b627db300ff956027cc6c3798cef26d22526befdfcd12feeb6d2257"
 | 
				
			||||||
dependencies = [
 | 
					dependencies = [
 | 
				
			||||||
 "backtrace",
 | 
					 "serde",
 | 
				
			||||||
 "bytes",
 | 
					 "serde_spanned",
 | 
				
			||||||
 "libc",
 | 
					 "toml_datetime",
 | 
				
			||||||
 "mio",
 | 
					 "toml_edit 0.19.15",
 | 
				
			||||||
 "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",
 | 
					 | 
				
			||||||
]
 | 
					]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
[[package]]
 | 
					[[package]]
 | 
				
			||||||
| 
						 | 
					@ -3727,6 +3755,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
 | 
				
			||||||
checksum = "1b5bb770da30e5cbfde35a2d7b9b8a2c4b8ef89548a7a6aeab5c9a576e3e7421"
 | 
					checksum = "1b5bb770da30e5cbfde35a2d7b9b8a2c4b8ef89548a7a6aeab5c9a576e3e7421"
 | 
				
			||||||
dependencies = [
 | 
					dependencies = [
 | 
				
			||||||
 "indexmap",
 | 
					 "indexmap",
 | 
				
			||||||
 | 
					 "serde",
 | 
				
			||||||
 | 
					 "serde_spanned",
 | 
				
			||||||
 "toml_datetime",
 | 
					 "toml_datetime",
 | 
				
			||||||
 "winnow 0.5.40",
 | 
					 "winnow 0.5.40",
 | 
				
			||||||
]
 | 
					]
 | 
				
			||||||
| 
						 | 
					@ -3886,6 +3916,12 @@ dependencies = [
 | 
				
			||||||
 "serde",
 | 
					 "serde",
 | 
				
			||||||
]
 | 
					]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					[[package]]
 | 
				
			||||||
 | 
					name = "urlencoding"
 | 
				
			||||||
 | 
					version = "2.1.3"
 | 
				
			||||||
 | 
					source = "registry+https://github.com/rust-lang/crates.io-index"
 | 
				
			||||||
 | 
					checksum = "daf8dba3b7eb870caf1ddeed7bc9d2a049f3cfdfae7cb521b087cc33ae4c49da"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
[[package]]
 | 
					[[package]]
 | 
				
			||||||
name = "usvg"
 | 
					name = "usvg"
 | 
				
			||||||
version = "0.37.0"
 | 
					version = "0.37.0"
 | 
				
			||||||
| 
						 | 
					@ -4229,7 +4265,7 @@ checksum = "cbd7311dbd2abcfebaabf1841a2824ed7c8be443a0f29166e5d3c6a53a762c01"
 | 
				
			||||||
dependencies = [
 | 
					dependencies = [
 | 
				
			||||||
 "arrayvec",
 | 
					 "arrayvec",
 | 
				
			||||||
 "cfg-if",
 | 
					 "cfg-if",
 | 
				
			||||||
 "cfg_aliases",
 | 
					 "cfg_aliases 0.1.1",
 | 
				
			||||||
 "js-sys",
 | 
					 "js-sys",
 | 
				
			||||||
 "log",
 | 
					 "log",
 | 
				
			||||||
 "parking_lot",
 | 
					 "parking_lot",
 | 
				
			||||||
| 
						 | 
					@ -4254,7 +4290,7 @@ dependencies = [
 | 
				
			||||||
 "arrayvec",
 | 
					 "arrayvec",
 | 
				
			||||||
 "bit-vec",
 | 
					 "bit-vec",
 | 
				
			||||||
 "bitflags 2.6.0",
 | 
					 "bitflags 2.6.0",
 | 
				
			||||||
 "cfg_aliases",
 | 
					 "cfg_aliases 0.1.1",
 | 
				
			||||||
 "codespan-reporting",
 | 
					 "codespan-reporting",
 | 
				
			||||||
 "indexmap",
 | 
					 "indexmap",
 | 
				
			||||||
 "log",
 | 
					 "log",
 | 
				
			||||||
| 
						 | 
					@ -4281,7 +4317,7 @@ dependencies = [
 | 
				
			||||||
 "arrayvec",
 | 
					 "arrayvec",
 | 
				
			||||||
 "ash",
 | 
					 "ash",
 | 
				
			||||||
 "bitflags 2.6.0",
 | 
					 "bitflags 2.6.0",
 | 
				
			||||||
 "cfg_aliases",
 | 
					 "cfg_aliases 0.1.1",
 | 
				
			||||||
 "core-graphics-types",
 | 
					 "core-graphics-types",
 | 
				
			||||||
 "glow",
 | 
					 "glow",
 | 
				
			||||||
 "glutin_wgl_sys",
 | 
					 "glutin_wgl_sys",
 | 
				
			||||||
| 
						 | 
					@ -4667,7 +4703,7 @@ dependencies = [
 | 
				
			||||||
 "bitflags 2.6.0",
 | 
					 "bitflags 2.6.0",
 | 
				
			||||||
 "bytemuck",
 | 
					 "bytemuck",
 | 
				
			||||||
 "calloop 0.12.4",
 | 
					 "calloop 0.12.4",
 | 
				
			||||||
 "cfg_aliases",
 | 
					 "cfg_aliases 0.1.1",
 | 
				
			||||||
 "core-foundation",
 | 
					 "core-foundation",
 | 
				
			||||||
 "core-graphics",
 | 
					 "core-graphics",
 | 
				
			||||||
 "cursor-icon",
 | 
					 "cursor-icon",
 | 
				
			||||||
| 
						 | 
					@ -4722,6 +4758,16 @@ dependencies = [
 | 
				
			||||||
 "memchr",
 | 
					 "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]]
 | 
					[[package]]
 | 
				
			||||||
name = "x11-dl"
 | 
					name = "x11-dl"
 | 
				
			||||||
version = "2.21.0"
 | 
					version = "2.21.0"
 | 
				
			||||||
| 
						 | 
					@ -4834,6 +4880,7 @@ dependencies = [
 | 
				
			||||||
 "clap",
 | 
					 "clap",
 | 
				
			||||||
 "env_logger",
 | 
					 "env_logger",
 | 
				
			||||||
 "log",
 | 
					 "log",
 | 
				
			||||||
 | 
					 "winresource",
 | 
				
			||||||
 "xmpd-cliargs",
 | 
					 "xmpd-cliargs",
 | 
				
			||||||
 "xmpd-gui",
 | 
					 "xmpd-gui",
 | 
				
			||||||
 "xmpd-manifest",
 | 
					 "xmpd-manifest",
 | 
				
			||||||
| 
						 | 
					@ -4846,17 +4893,18 @@ version = "2.0.0"
 | 
				
			||||||
dependencies = [
 | 
					dependencies = [
 | 
				
			||||||
 "anyhow",
 | 
					 "anyhow",
 | 
				
			||||||
 "camino",
 | 
					 "camino",
 | 
				
			||||||
 | 
					 "dirs",
 | 
				
			||||||
 "eframe",
 | 
					 "eframe",
 | 
				
			||||||
 "egui 0.27.2",
 | 
					 "egui",
 | 
				
			||||||
 "egui-aesthetix",
 | 
					 | 
				
			||||||
 "egui_extras",
 | 
					 "egui_extras",
 | 
				
			||||||
 "lazy_static",
 | 
					 "lazy_static",
 | 
				
			||||||
 "log",
 | 
					 "log",
 | 
				
			||||||
 "tokio",
 | 
					 "rfd",
 | 
				
			||||||
 "uuid",
 | 
					 "uuid",
 | 
				
			||||||
 "xmpd-cache",
 | 
					 "xmpd-cache",
 | 
				
			||||||
 "xmpd-cliargs",
 | 
					 "xmpd-cliargs",
 | 
				
			||||||
 "xmpd-manifest",
 | 
					 "xmpd-manifest",
 | 
				
			||||||
 | 
					 "xmpd-player",
 | 
				
			||||||
 "xmpd-settings",
 | 
					 "xmpd-settings",
 | 
				
			||||||
]
 | 
					]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -4867,14 +4915,20 @@ dependencies = [
 | 
				
			||||||
 "anyhow",
 | 
					 "anyhow",
 | 
				
			||||||
 "serde",
 | 
					 "serde",
 | 
				
			||||||
 "serde_json",
 | 
					 "serde_json",
 | 
				
			||||||
 | 
					 "toml 0.8.19",
 | 
				
			||||||
 "url",
 | 
					 "url",
 | 
				
			||||||
 "uuid",
 | 
					 "uuid",
 | 
				
			||||||
 | 
					 "xmpd-cliargs",
 | 
				
			||||||
 | 
					 "xmpd-settings",
 | 
				
			||||||
]
 | 
					]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
[[package]]
 | 
					[[package]]
 | 
				
			||||||
name = "xmpd-player"
 | 
					name = "xmpd-player"
 | 
				
			||||||
version = "2.0.0"
 | 
					version = "2.0.0"
 | 
				
			||||||
dependencies = [
 | 
					dependencies = [
 | 
				
			||||||
 | 
					 "anyhow",
 | 
				
			||||||
 | 
					 "lazy_static",
 | 
				
			||||||
 | 
					 "log",
 | 
				
			||||||
 "rodio",
 | 
					 "rodio",
 | 
				
			||||||
]
 | 
					]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -4884,10 +4938,10 @@ version = "2.0.0"
 | 
				
			||||||
dependencies = [
 | 
					dependencies = [
 | 
				
			||||||
 "anyhow",
 | 
					 "anyhow",
 | 
				
			||||||
 "camino",
 | 
					 "camino",
 | 
				
			||||||
 "egui 0.27.2",
 | 
					 "egui",
 | 
				
			||||||
 "lazy_static",
 | 
					 "lazy_static",
 | 
				
			||||||
 "serde",
 | 
					 "serde",
 | 
				
			||||||
 "toml",
 | 
					 "toml 0.8.19",
 | 
				
			||||||
]
 | 
					]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
[[package]]
 | 
					[[package]]
 | 
				
			||||||
| 
						 | 
					@ -4900,12 +4954,12 @@ version = "3.15.2"
 | 
				
			||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
 | 
					source = "registry+https://github.com/rust-lang/crates.io-index"
 | 
				
			||||||
checksum = "675d170b632a6ad49804c8cf2105d7c31eddd3312555cffd4b740e08e97c25e6"
 | 
					checksum = "675d170b632a6ad49804c8cf2105d7c31eddd3312555cffd4b740e08e97c25e6"
 | 
				
			||||||
dependencies = [
 | 
					dependencies = [
 | 
				
			||||||
 "async-broadcast",
 | 
					 "async-broadcast 0.5.1",
 | 
				
			||||||
 "async-executor",
 | 
					 "async-executor",
 | 
				
			||||||
 "async-fs",
 | 
					 "async-fs 1.6.0",
 | 
				
			||||||
 "async-io 1.13.0",
 | 
					 "async-io 1.13.0",
 | 
				
			||||||
 "async-lock 2.8.0",
 | 
					 "async-lock 2.8.0",
 | 
				
			||||||
 "async-process",
 | 
					 "async-process 1.8.1",
 | 
				
			||||||
 "async-recursion",
 | 
					 "async-recursion",
 | 
				
			||||||
 "async-task",
 | 
					 "async-task",
 | 
				
			||||||
 "async-trait",
 | 
					 "async-trait",
 | 
				
			||||||
| 
						 | 
					@ -4918,7 +4972,7 @@ dependencies = [
 | 
				
			||||||
 "futures-sink",
 | 
					 "futures-sink",
 | 
				
			||||||
 "futures-util",
 | 
					 "futures-util",
 | 
				
			||||||
 "hex",
 | 
					 "hex",
 | 
				
			||||||
 "nix",
 | 
					 "nix 0.26.4",
 | 
				
			||||||
 "once_cell",
 | 
					 "once_cell",
 | 
				
			||||||
 "ordered-stream",
 | 
					 "ordered-stream",
 | 
				
			||||||
 "rand",
 | 
					 "rand",
 | 
				
			||||||
| 
						 | 
					@ -4930,9 +4984,45 @@ dependencies = [
 | 
				
			||||||
 "uds_windows",
 | 
					 "uds_windows",
 | 
				
			||||||
 "winapi",
 | 
					 "winapi",
 | 
				
			||||||
 "xdg-home",
 | 
					 "xdg-home",
 | 
				
			||||||
 "zbus_macros",
 | 
					 "zbus_macros 3.15.2",
 | 
				
			||||||
 "zbus_names",
 | 
					 "zbus_names 2.6.1",
 | 
				
			||||||
 "zvariant",
 | 
					 "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]]
 | 
					[[package]]
 | 
				
			||||||
| 
						 | 
					@ -4946,7 +5036,22 @@ dependencies = [
 | 
				
			||||||
 "quote",
 | 
					 "quote",
 | 
				
			||||||
 "regex",
 | 
					 "regex",
 | 
				
			||||||
 "syn 1.0.109",
 | 
					 "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]]
 | 
					[[package]]
 | 
				
			||||||
| 
						 | 
					@ -4957,7 +5062,19 @@ checksum = "437d738d3750bed6ca9b8d423ccc7a8eb284f6b1d6d4e225a0e4e6258d864c8d"
 | 
				
			||||||
dependencies = [
 | 
					dependencies = [
 | 
				
			||||||
 "serde",
 | 
					 "serde",
 | 
				
			||||||
 "static_assertions",
 | 
					 "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]]
 | 
					[[package]]
 | 
				
			||||||
| 
						 | 
					@ -4998,7 +5115,23 @@ dependencies = [
 | 
				
			||||||
 "libc",
 | 
					 "libc",
 | 
				
			||||||
 "serde",
 | 
					 "serde",
 | 
				
			||||||
 "static_assertions",
 | 
					 "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]]
 | 
					[[package]]
 | 
				
			||||||
| 
						 | 
					@ -5011,7 +5144,20 @@ dependencies = [
 | 
				
			||||||
 "proc-macro2",
 | 
					 "proc-macro2",
 | 
				
			||||||
 "quote",
 | 
					 "quote",
 | 
				
			||||||
 "syn 1.0.109",
 | 
					 "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]]
 | 
					[[package]]
 | 
				
			||||||
| 
						 | 
					@ -5024,3 +5170,17 @@ dependencies = [
 | 
				
			||||||
 "quote",
 | 
					 "quote",
 | 
				
			||||||
 "syn 1.0.109",
 | 
					 "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]
 | 
					[workspace.dependencies]
 | 
				
			||||||
anstyle = "1.0.6"
 | 
					anstyle = "1.0.6"
 | 
				
			||||||
anyhow = "1.0.81"
 | 
					anyhow = "1.0.81"
 | 
				
			||||||
bitflags = { version = "2.6.0", features = ["serde"] }
 | 
					 | 
				
			||||||
camino = { version="1.1.6", features = ["serde1"] }
 | 
					camino = { version="1.1.6", features = ["serde1"] }
 | 
				
			||||||
clap = { version = "4.5.4", features = ["derive"] }
 | 
					clap = { version = "4.5.4", features = ["derive"] }
 | 
				
			||||||
eframe = "0.27.2"
 | 
					eframe = "0.27.2"
 | 
				
			||||||
egui = { version = "0.27.2", features = ["color-hex", "serde"] }
 | 
					egui = { version = "0.27.2", features = ["color-hex", "serde"] }
 | 
				
			||||||
egui_extras = { version = "0.27.2", features = ["all_loaders"] }
 | 
					egui_extras = { version = "0.27.2", features = ["all_loaders"] }
 | 
				
			||||||
env_logger = "0.11.3"
 | 
					env_logger = "0.11.3"
 | 
				
			||||||
futures = "0.3.30"
 | 
					 | 
				
			||||||
html-escape = "0.2.13"
 | 
					 | 
				
			||||||
lazy_static = "1.4.0"
 | 
					lazy_static = "1.4.0"
 | 
				
			||||||
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"
 | 
				
			||||||
regex = "1.11.0"
 | 
					# reqwest = { version = "0.12.3", features = ["blocking", "h2", "http2", "rustls-tls"], default-features = false }
 | 
				
			||||||
reqwest = { version = "0.12.3", features = ["blocking", "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"
 | 
					 | 
				
			||||||
tokio = { version = "1.37.0", features = ["macros", "rt-multi-thread", "process", "sync"] }
 | 
					 | 
				
			||||||
url = { version = "2.5.0", features = ["serde"] }
 | 
					url = { version = "2.5.0", features = ["serde"] }
 | 
				
			||||||
uuid = { version = "1.11.0", features = ["serde", "v4"] }
 | 
					uuid = { version = "1.11.0", features = ["serde", "v4"] }
 | 
				
			||||||
windows = { version = "0.56.0", features = ["Win32_Foundation", "Win32_Storage_FileSystem", "Win32_System_Console"] }
 | 
					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"
 | 
					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();
 | 
					                    let mut from = song_p.clone();
 | 
				
			||||||
                    from.pop();
 | 
					                    from.pop();
 | 
				
			||||||
                    from.push("{song_format}.{song_format}");
 | 
					                    from.push(format!("{song_format}.{song_format}"));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                    let mut to = song_p.clone();
 | 
					                    let mut to = song_p.clone();
 | 
				
			||||||
                    to.pop();
 | 
					                    to.pop();
 | 
				
			||||||
                    to.set_extension(&song_format);
 | 
					                    to.set_extension(&song_format);
 | 
				
			||||||
 | 
					                    log::debug!("from: {from:?} to: {to:?}");
 | 
				
			||||||
                    std::fs::copy(&from, &to).unwrap();
 | 
					                    std::fs::copy(&from, &to).unwrap();
 | 
				
			||||||
                    from.pop();
 | 
					                    from.pop();
 | 
				
			||||||
                    std::fs::remove_dir_all(from).unwrap();
 | 
					                    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 std::{collections::HashMap, str::FromStr, sync::{mpsc::{self, Receiver, Sender}, Arc, Mutex, MutexGuard}, time::Duration};
 | 
				
			||||||
use downloader::song::SongStatus;
 | 
					use downloader::song::SongStatus;
 | 
				
			||||||
use log::warn;
 | 
					 | 
				
			||||||
use xmpd_manifest::song::Song;
 | 
					use xmpd_manifest::song::Song;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
pub mod downloader;
 | 
					pub mod downloader;
 | 
				
			||||||
| 
						 | 
					@ -106,7 +105,6 @@ fn start_cache_mv_thread(tx: Sender<Message>) {
 | 
				
			||||||
                        song_p.push("songs");
 | 
					                        song_p.push("songs");
 | 
				
			||||||
                        song_p.push(sid.clone().to_string());
 | 
					                        song_p.push(sid.clone().to_string());
 | 
				
			||||||
                        let song_p = song_p.with_extension(&song_format);
 | 
					                        let song_p = song_p.with_extension(&song_format);
 | 
				
			||||||
                        log::debug!("Found done: {:?}: {}", song_p, song_p.exists());
 | 
					 | 
				
			||||||
                        if song_p.exists() {
 | 
					                        if song_p.exists() {
 | 
				
			||||||
                            let _ = tx.send(Message::DownloadDone(sid.clone()));
 | 
					                            let _ = tx.send(Message::DownloadDone(sid.clone()));
 | 
				
			||||||
                            cache.song_cache.insert(sid.clone(), DlStatus::Done(song_p)); 
 | 
					                            cache.song_cache.insert(sid.clone(), DlStatus::Done(song_p)); 
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -30,3 +30,6 @@ camino.workspace = true
 | 
				
			||||||
anyhow.workspace = true
 | 
					anyhow.workspace = true
 | 
				
			||||||
log.workspace = true
 | 
					log.workspace = true
 | 
				
			||||||
env_logger.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-settings.path = "../xmpd-settings"
 | 
				
			||||||
xmpd-cliargs.path = "../xmpd-cliargs"
 | 
					xmpd-cliargs.path = "../xmpd-cliargs"
 | 
				
			||||||
xmpd-cache.path = "../xmpd-cache"
 | 
					xmpd-cache.path = "../xmpd-cache"
 | 
				
			||||||
 | 
					xmpd-player.path = "../xmpd-player"
 | 
				
			||||||
egui.workspace = true
 | 
					egui.workspace = true
 | 
				
			||||||
eframe.workspace = true
 | 
					eframe.workspace = true
 | 
				
			||||||
tokio.workspace = true
 | 
					 | 
				
			||||||
anyhow.workspace = true
 | 
					anyhow.workspace = true
 | 
				
			||||||
lazy_static.workspace = true
 | 
					lazy_static.workspace = true
 | 
				
			||||||
log.workspace = true
 | 
					log.workspace = true
 | 
				
			||||||
egui_extras.workspace = true
 | 
					egui_extras.workspace = true
 | 
				
			||||||
egui-aesthetix = "0.2.4"
 | 
					 | 
				
			||||||
uuid.workspace = true
 | 
					uuid.workspace = true
 | 
				
			||||||
camino.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 xmpd_manifest::store::BaseStore;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
use super::{CompGetter, CompUi};
 | 
					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(
 | 
					        ui.add(
 | 
				
			||||||
            egui::Image::new(crate::data::NOTE_ICON)
 | 
					            egui::Image::new(crate::data::NOTE_ICON)
 | 
				
			||||||
                .tint(theme.accent_color)
 | 
					                .tint(theme.accent_color)
 | 
				
			||||||
 | 
					                .sense(Sense::click())
 | 
				
			||||||
                .fit_to_exact_size(egui::Vec2::new(32.0, 32.0))
 | 
					                .fit_to_exact_size(egui::Vec2::new(32.0, 32.0))
 | 
				
			||||||
        );
 | 
					        );
 | 
				
			||||||
        ui.vertical(|ui| {
 | 
					        ui.vertical(|ui| {
 | 
				
			||||||
| 
						 | 
					@ -83,9 +84,13 @@ fn add_playlist_tab(ui: &mut egui::Ui, pid: &Option<uuid::Uuid>, title: &str, au
 | 
				
			||||||
        });
 | 
					        });
 | 
				
			||||||
    }).response.rect;
 | 
					    }).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();
 | 
					        handle_error_ui!(LeftNav::get()).selected_playlist_id = pid.clone();
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					    if blob.hovered() {
 | 
				
			||||||
 | 
					        ui.output_mut(|o| o.cursor_icon = CursorIcon::PointingHand);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
    ui.separator();
 | 
					    ui.separator();
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1,19 +1,30 @@
 | 
				
			||||||
use egui::{Sense, Stroke, Vec2};
 | 
					use egui::{Sense, Stroke, Vec2};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
use super::{CompGetter, CompUi};
 | 
					use super::{song_list::SongList, CompGetter, CompUi};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#[derive(Debug, Default)]
 | 
					#[derive(Debug)]
 | 
				
			||||||
pub struct Player {
 | 
					pub struct Player {
 | 
				
			||||||
    slider_progress: usize,
 | 
					    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);
 | 
					component_register!(Player);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
impl CompUi for 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 theme = xmpd_settings::Settings::get()?.theme.clone();
 | 
				
			||||||
        let avail = ui.available_size();
 | 
					        let avail = ui.available_size();
 | 
				
			||||||
        ui.vertical_centered_justified(|ui| {
 | 
					        ui.vertical_centered_justified(|ui| {
 | 
				
			||||||
| 
						 | 
					@ -21,10 +32,7 @@ impl CompUi for Player {
 | 
				
			||||||
            ui.horizontal(|ui| {
 | 
					            ui.horizontal(|ui| {
 | 
				
			||||||
                {
 | 
					                {
 | 
				
			||||||
                    ui.add_space(avail.x * 0.05 / 2.0);
 | 
					                    ui.add_space(avail.x * 0.05 / 2.0);
 | 
				
			||||||
                    let mut slf = handle_error_ui!(Player::get());
 | 
					                    ui.style_mut().spacing.slider_width = avail.x * 0.87;
 | 
				
			||||||
                    let slider = egui::Slider::new(&mut slf.slider_progress, 0..=100)
 | 
					 | 
				
			||||||
                        .show_value(false);
 | 
					 | 
				
			||||||
                    ui.style_mut().spacing.slider_width = avail.x * 0.90;
 | 
					 | 
				
			||||||
                    let s = Stroke {
 | 
					                    let s = Stroke {
 | 
				
			||||||
                        color: theme.accent_color,
 | 
					                        color: theme.accent_color,
 | 
				
			||||||
                        width: 2.0
 | 
					                        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.inactive.fg_stroke = s;
 | 
				
			||||||
                    ui.style_mut().visuals.widgets.active.fg_stroke = s;
 | 
					                    ui.style_mut().visuals.widgets.active.fg_stroke = s;
 | 
				
			||||||
                    ui.style_mut().visuals.widgets.hovered.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.horizontal(|ui| {
 | 
				
			||||||
                ui.add_space((avail.x / 2.0) - 16.0 - 8.0 - ui.spacing().item_spacing.x);
 | 
					                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 {
 | 
					                let pp = if state.player.is_paused() {
 | 
				
			||||||
                    crate::data::PAUSE_ICON
 | 
					 | 
				
			||||||
                } else {
 | 
					 | 
				
			||||||
                    crate::data::PLAY_ICON
 | 
					                    crate::data::PLAY_ICON
 | 
				
			||||||
 | 
					                } else {
 | 
				
			||||||
 | 
					                    crate::data::PAUSE_ICON
 | 
				
			||||||
                };
 | 
					                };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					 | 
				
			||||||
                let prev = egui::Image::new(crate::data::PREV_ICON)
 | 
					                let prev = egui::Image::new(crate::data::PREV_ICON)
 | 
				
			||||||
                    .tint(theme.accent_color)
 | 
					                    .tint(theme.accent_color)
 | 
				
			||||||
                    .sense(Sense::click())
 | 
					                    .sense(Sense::click())
 | 
				
			||||||
| 
						 | 
					@ -57,12 +81,43 @@ impl CompUi for Player {
 | 
				
			||||||
                    .tint(theme.accent_color)
 | 
					                    .tint(theme.accent_color)
 | 
				
			||||||
                    .sense(Sense::click())
 | 
					                    .sense(Sense::click())
 | 
				
			||||||
                    .max_size(Vec2::new(16.0, 16.0));
 | 
					                    .max_size(Vec2::new(16.0, 16.0));
 | 
				
			||||||
                if ui.add(prev).clicked() {}
 | 
					                if ui.add(prev).clicked() {     
 | 
				
			||||||
                if ui.add(pp).clicked() {
 | 
					                    handle_error_ui!(handle_error_ui!(SongList::get()).play_prev(state));
 | 
				
			||||||
                    let mut slf = handle_error_ui!(Player::get());
 | 
					 | 
				
			||||||
                    slf.is_playing = !slf.is_playing;
 | 
					 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
                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);
 | 
					            ui.add_space(3.0);
 | 
				
			||||||
        });
 | 
					        });
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1,6 +1,4 @@
 | 
				
			||||||
use std::fmt::write;
 | 
					use egui::{Color32, CursorIcon, RichText, Sense, Vec2};
 | 
				
			||||||
 | 
					 | 
				
			||||||
use egui::{Color32, RichText, Sense, Vec2};
 | 
					 | 
				
			||||||
use song_list_nav::SearchType;
 | 
					use song_list_nav::SearchType;
 | 
				
			||||||
use xmpd_cache::DlStatus;
 | 
					use xmpd_cache::DlStatus;
 | 
				
			||||||
use xmpd_manifest::{song::Song, store::BaseStore};
 | 
					use xmpd_manifest::{song::Song, store::BaseStore};
 | 
				
			||||||
| 
						 | 
					@ -10,104 +8,155 @@ pub mod song_list_nav;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#[derive(Debug, Default)]
 | 
					#[derive(Debug, Default)]
 | 
				
			||||||
pub struct SongList {
 | 
					pub struct SongList {
 | 
				
			||||||
    selected_song_id: uuid::Uuid
 | 
					    selected_sid: uuid::Uuid,
 | 
				
			||||||
 | 
					    playable_songs: Vec<uuid::Uuid>,
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
component_register!(SongList);
 | 
					component_register!(SongList);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
impl CompUi for SongList {
 | 
					impl CompUi for SongList {
 | 
				
			||||||
    fn draw(ui: &mut egui::Ui, state: &mut crate::GuiState) -> crate::Result<()> {
 | 
					    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()
 | 
					        egui::ScrollArea::vertical()
 | 
				
			||||||
            .id_source("song_list")
 | 
					            .id_source("song_list")
 | 
				
			||||||
            .drag_to_scroll(false)
 | 
					            .drag_to_scroll(false)
 | 
				
			||||||
            .show(ui, |ui| {
 | 
					            .show(ui, |ui| {
 | 
				
			||||||
                ui.vertical(|ui| {
 | 
					                ui.vertical(|ui| {
 | 
				
			||||||
                    ui.add_space(3.0);
 | 
					                    ui.add_space(3.0);
 | 
				
			||||||
                    let pid = {handle_error_ui!(super::left_nav::LeftNav::get()).selected_playlist_id.clone()};
 | 
					                    for (sid, song) in disp_songs {
 | 
				
			||||||
                    match pid {
 | 
					                         handle_error_ui!(Self::display_song_tab(ui, state, &sid, &song));
 | 
				
			||||||
                        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);
 | 
					 | 
				
			||||||
                    }
 | 
					                    }
 | 
				
			||||||
                            }
 | 
					 | 
				
			||||||
                        }
 | 
					 | 
				
			||||||
                        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(())
 | 
					        Ok(())
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
fn display_song_tab(ui: &mut egui::Ui, sid: &uuid::Uuid, song: &Song) {
 | 
					impl SongList {
 | 
				
			||||||
    let theme = handle_error_ui!(xmpd_settings::Settings::get()).theme.clone();
 | 
					    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| {
 | 
					        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)
 | 
					                egui::Image::new(crate::data::NOTE_ICON)
 | 
				
			||||||
                    .tint(theme.accent_color)
 | 
					                    .tint(theme.accent_color)
 | 
				
			||||||
                    .sense(Sense::click())
 | 
					                    .sense(Sense::click())
 | 
				
			||||||
                    .fit_to_exact_size(Vec2::new(32.0, 32.0))
 | 
					                    .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| {
 | 
					            ui.vertical(|ui| {
 | 
				
			||||||
            let selected_song_id = {handle_error_ui!(SongList::get()).selected_song_id};
 | 
					                let slf = handle_error_ui!(SongList::get());
 | 
				
			||||||
            let label = if selected_song_id == *sid {
 | 
					                let label = if slf.selected_sid == *sid {
 | 
				
			||||||
                ui.label(
 | 
					 | 
				
			||||||
                    RichText::new(song.name())
 | 
					                    RichText::new(song.name())
 | 
				
			||||||
                        .color(theme.accent_color)
 | 
					                        .color(theme.accent_color)
 | 
				
			||||||
                )
 | 
					                } else if matches!(status, Some(DlStatus::Done(_))) {
 | 
				
			||||||
            } else {
 | 
					 | 
				
			||||||
                ui.label(
 | 
					 | 
				
			||||||
                    RichText::new(song.name())
 | 
					                    RichText::new(song.name())
 | 
				
			||||||
                        .color(theme.text_color)
 | 
					                        .color(theme.text_color)
 | 
				
			||||||
                )
 | 
					                } else {
 | 
				
			||||||
 | 
					                    RichText::new(song.name())
 | 
				
			||||||
 | 
					                        .color(theme.dim_text_color)
 | 
				
			||||||
                };
 | 
					                };
 | 
				
			||||||
 | 
					                let label = ui.label(label);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                if label.clicked() {
 | 
					                if label.clicked() {
 | 
				
			||||||
                    clicked = true;
 | 
					                    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(
 | 
					                ui.monospace(
 | 
				
			||||||
                    RichText::new(format!("By {}", song.author()))
 | 
					                    RichText::new(format!("By {}", song.author()))
 | 
				
			||||||
                        .color(theme.dim_text_color)
 | 
					                        .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.with_layout(egui::Layout::right_to_left(egui::Align::RIGHT), |ui| {
 | 
				
			||||||
                ui.add_space(3.0);
 | 
					                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| {
 | 
					                match status {
 | 
				
			||||||
                        ui.label(format!("Path: {p}"));
 | 
					                    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) => {
 | 
					                    Some(DlStatus::Downloading) => {
 | 
				
			||||||
                    ui.add(
 | 
					                        let spinner = egui::Spinner::new()
 | 
				
			||||||
                        egui::Spinner::new()
 | 
					 | 
				
			||||||
                                .color(theme.accent_color)
 | 
					                                .color(theme.accent_color)
 | 
				
			||||||
                            .size(16.0)
 | 
					                                .size(16.0);
 | 
				
			||||||
                    );
 | 
					                        ui.add(spinner);
 | 
				
			||||||
                    }
 | 
					                    }
 | 
				
			||||||
                    Some(DlStatus::Error(e)) => {
 | 
					                    Some(DlStatus::Error(e)) => {
 | 
				
			||||||
                    let img = ui.add(
 | 
					                        let img = egui::Image::new(crate::data::WARN_ICON)
 | 
				
			||||||
                        egui::Image::new(crate::data::WARN_ICON)
 | 
					 | 
				
			||||||
                                .tint(Color32::LIGHT_YELLOW)
 | 
					                                .tint(Color32::LIGHT_YELLOW)
 | 
				
			||||||
                                .sense(Sense::hover())
 | 
					                                .sense(Sense::hover())
 | 
				
			||||||
                            .fit_to_exact_size(Vec2::new(16.0, 16.0))
 | 
					                                .fit_to_exact_size(Vec2::new(16.0, 16.0));
 | 
				
			||||||
                    );
 | 
					
 | 
				
			||||||
                    img.on_hover_ui(|ui| {
 | 
					                        ui.add(img).on_hover_ui(|ui| {
 | 
				
			||||||
                            ui.label(e);
 | 
					                            ui.label(e);
 | 
				
			||||||
                        });
 | 
					                        });
 | 
				
			||||||
                    }
 | 
					                    }
 | 
				
			||||||
                    None => {
 | 
					                    None => {
 | 
				
			||||||
                    let img = ui.add(
 | 
					                        let img = egui::Image::new(crate::data::DL_ICON)
 | 
				
			||||||
                        egui::Image::new(crate::data::DL_ICON)
 | 
					 | 
				
			||||||
                                .tint(theme.accent_color)
 | 
					                                .tint(theme.accent_color)
 | 
				
			||||||
                                .sense(Sense::click())
 | 
					                                .sense(Sense::click())
 | 
				
			||||||
                            .fit_to_exact_size(Vec2::new(16.0, 16.0))
 | 
					                                .fit_to_exact_size(Vec2::new(16.0, 16.0));
 | 
				
			||||||
                    );
 | 
					
 | 
				
			||||||
                    if img.clicked() {
 | 
					                        if ui.add(img).clicked() {
 | 
				
			||||||
                            handle_error_ui!(xmpd_cache::Cache::get()).download_to_cache(sid.clone(), song.clone());
 | 
					                            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());
 | 
					                            let mut toast = handle_error_ui!(crate::components::toast::Toast::get());
 | 
				
			||||||
                            toast.show_toast(
 | 
					                            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();
 | 
					        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 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 crate::components::{left_nav::LeftNav, toast::ToastType, CompGetter, CompUi};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					use super::SongList;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#[derive(Debug, Clone)]
 | 
					#[derive(Debug, Clone)]
 | 
				
			||||||
pub enum SearchType {
 | 
					pub enum SearchType {
 | 
				
			||||||
    Name(String),
 | 
					    Name(String),
 | 
				
			||||||
| 
						 | 
					@ -53,8 +56,9 @@ impl CompUi for SongListNav {
 | 
				
			||||||
                        None => {
 | 
					                        None => {
 | 
				
			||||||
                            songs = state.manifest.store().get_songs().keys().cloned().collect();
 | 
					                            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) {
 | 
					                        if let Some(song) = state.manifest.store().get_song(&sid) {
 | 
				
			||||||
                            handle_error_ui!(xmpd_cache::Cache::get()).download_to_cache(sid.clone(), song.clone())
 | 
					                            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())
 | 
					            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 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};
 | 
					use super::{CompGetter, CompUi};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -43,19 +43,24 @@ impl CompUi for Toast {
 | 
				
			||||||
                        ToastType::Info => {
 | 
					                        ToastType::Info => {
 | 
				
			||||||
                            color = theme.accent_color;
 | 
					                            color = theme.accent_color;
 | 
				
			||||||
                            img = Image::new(crate::data::INFO_ICON)
 | 
					                            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);
 | 
					                                .tint(color);
 | 
				
			||||||
                        }
 | 
					                        }
 | 
				
			||||||
                        ToastType::Warn => {
 | 
					                        ToastType::Warn => {
 | 
				
			||||||
                            color = crate::data::C_WARN;
 | 
					                            color = crate::data::C_WARN;
 | 
				
			||||||
                            img = Image::new(crate::data::WARN_ICON)
 | 
					                            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);
 | 
					                                .tint(color);
 | 
				
			||||||
                        }
 | 
					                        }
 | 
				
			||||||
                        ToastType::Error => {
 | 
					                        ToastType::Error => {
 | 
				
			||||||
                            color = Color32::LIGHT_RED;
 | 
					                            color = Color32::LIGHT_RED;
 | 
				
			||||||
                            img = Image::new(crate::data::ERROR_ICON)
 | 
					                            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);
 | 
					                                .tint(color);
 | 
				
			||||||
                        }
 | 
					                        }
 | 
				
			||||||
                    }
 | 
					                    }
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1,9 +1,19 @@
 | 
				
			||||||
 | 
					use std::path::PathBuf;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					use egui::TextBuffer;
 | 
				
			||||||
 | 
					use xmpd_manifest::store::{JsonStore, TomlStore};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
use crate::windows::WindowId;
 | 
					use crate::windows::WindowId;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
use super::CompUi;
 | 
					use super::{CompGetter, CompUi};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#[derive(Debug, Default)]
 | 
					#[derive(Debug, Default)]
 | 
				
			||||||
pub struct TopNav;
 | 
					pub struct TopNav {
 | 
				
			||||||
 | 
					    // for dialog 
 | 
				
			||||||
 | 
					    manifest_path: Option<(PathBuf, String)>
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					component_register!(TopNav);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
impl CompUi for TopNav {
 | 
					impl CompUi for TopNav {
 | 
				
			||||||
    fn draw(ui: &mut egui::Ui, state: &mut crate::GuiState) -> crate::Result<()> {
 | 
					    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());
 | 
					                        handle_error_ui!(state.manifest.save());
 | 
				
			||||||
                        ui.close_menu();
 | 
					                        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| {
 | 
					                ui.menu_button("Help", |ui| {
 | 
				
			||||||
                    if ui.button("Source").clicked() {
 | 
					                    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(())
 | 
					        Ok(())
 | 
				
			||||||
     } 
 | 
					     } 
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -36,11 +36,13 @@ pub fn start() -> Result<()> {
 | 
				
			||||||
pub struct GuiState {
 | 
					pub struct GuiState {
 | 
				
			||||||
    pub manifest: Manifest<JsonStore>,
 | 
					    pub manifest: Manifest<JsonStore>,
 | 
				
			||||||
    pub windows: windows::Windows,
 | 
					    pub windows: windows::Windows,
 | 
				
			||||||
 | 
					    pub player: xmpd_player::Player,
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
impl GuiState {
 | 
					impl GuiState {
 | 
				
			||||||
    pub fn new() -> Result<Self> {
 | 
					    pub fn new() -> Result<Self> {
 | 
				
			||||||
        Ok(Self {
 | 
					        Ok(Self {
 | 
				
			||||||
 | 
					            player: xmpd_player::Player::new(),
 | 
				
			||||||
            manifest: Manifest::new(&xmpd_cliargs::CLIARGS.manifest_path())?,
 | 
					            manifest: Manifest::new(&xmpd_cliargs::CLIARGS.manifest_path())?,
 | 
				
			||||||
            windows: windows::Windows::new(),
 | 
					            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
 | 
					bench = false
 | 
				
			||||||
 | 
					
 | 
				
			||||||
[dependencies]
 | 
					[dependencies]
 | 
				
			||||||
 | 
					xmpd-cliargs.path = "../xmpd-cliargs"
 | 
				
			||||||
 | 
					xmpd-settings.path = "../xmpd-settings"
 | 
				
			||||||
anyhow.workspace = true
 | 
					anyhow.workspace = true
 | 
				
			||||||
uuid.workspace = true
 | 
					uuid.workspace = true
 | 
				
			||||||
serde.workspace = true
 | 
					serde.workspace = true
 | 
				
			||||||
serde_json.workspace = true
 | 
					serde_json.workspace = true
 | 
				
			||||||
url.workspace = true
 | 
					url.workspace = true
 | 
				
			||||||
 | 
					toml.workspace = true
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1,4 +1,4 @@
 | 
				
			||||||
use std::path::Path;
 | 
					use std::path::{Path, PathBuf};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#[cfg(test)]
 | 
					#[cfg(test)]
 | 
				
			||||||
| 
						 | 
					@ -43,6 +43,29 @@ impl<ST: store::BaseStore + Clone> Manifest<ST> {
 | 
				
			||||||
        self.store_mut().load_from(&p)?;
 | 
					        self.store_mut().load_from(&p)?;
 | 
				
			||||||
        Ok(())
 | 
					        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};
 | 
					use crate::{playlist::Playlist, song::Song};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
mod json;
 | 
					mod json;
 | 
				
			||||||
 | 
					mod toml;
 | 
				
			||||||
pub use json::JsonStore;
 | 
					pub use json::JsonStore;
 | 
				
			||||||
 | 
					pub use toml::TomlStore;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
pub trait BaseStore { 
 | 
					pub trait BaseStore { 
 | 
				
			||||||
    fn get_songs(&self) -> &HashMap<Uuid, Song>;
 | 
					    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
 | 
					authors.workspace = true
 | 
				
			||||||
 | 
					
 | 
				
			||||||
[dependencies]
 | 
					[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
 | 
					egui.workspace = true
 | 
				
			||||||
lazy_static.workspace = true
 | 
					lazy_static.workspace = true
 | 
				
			||||||
serde.workspace = true
 | 
					serde.workspace = true
 | 
				
			||||||
toml = "0.8.19"
 | 
					toml.workspace = true
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
		Loading…
	
		Reference in New Issue
	
	Block a user