diff --git a/Cargo.lock b/Cargo.lock
index c017459..90d2631 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -63,7 +63,7 @@ dependencies = [
"futures-lite 1.13.0",
"once_cell",
"serde",
- "zbus",
+ "zbus 3.15.2",
]
[[package]]
@@ -93,15 +93,6 @@ dependencies = [
"winit",
]
-[[package]]
-name = "addr2line"
-version = "0.24.2"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "dfbe277e56a376000877090da837660b4427aad530e3028d44e0bffe4f89a1c1"
-dependencies = [
- "gimli",
-]
-
[[package]]
name = "adler2"
version = "2.0.0"
@@ -292,6 +283,28 @@ dependencies = [
"libloading 0.7.4",
]
+[[package]]
+name = "ashpd"
+version = "0.10.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e9c39d707614dbcc6bed00015539f488d8e3fe3e66ed60961efc0c90f4b380b3"
+dependencies = [
+ "async-fs 2.1.2",
+ "async-net",
+ "enumflags2",
+ "futures-channel",
+ "futures-util",
+ "rand",
+ "raw-window-handle 0.6.2",
+ "serde",
+ "serde_repr",
+ "url",
+ "wayland-backend",
+ "wayland-client",
+ "wayland-protocols 0.32.5",
+ "zbus 5.1.1",
+]
+
[[package]]
name = "async-broadcast"
version = "0.5.1"
@@ -302,6 +315,18 @@ dependencies = [
"futures-core",
]
+[[package]]
+name = "async-broadcast"
+version = "0.7.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "20cd0e2e25ea8e5f7e9df04578dc6cf5c83577fd09b1a46aaf5c85e1c33f2a7e"
+dependencies = [
+ "event-listener 5.3.1",
+ "event-listener-strategy",
+ "futures-core",
+ "pin-project-lite",
+]
+
[[package]]
name = "async-channel"
version = "2.3.1"
@@ -339,6 +364,17 @@ dependencies = [
"futures-lite 1.13.0",
]
+[[package]]
+name = "async-fs"
+version = "2.1.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ebcd09b382f40fcd159c2d695175b2ae620ffa5f3bd6f664131efff4e8b9e04a"
+dependencies = [
+ "async-lock 3.4.0",
+ "blocking",
+ "futures-lite 2.4.0",
+]
+
[[package]]
name = "async-io"
version = "1.13.0"
@@ -398,6 +434,17 @@ dependencies = [
"pin-project-lite",
]
+[[package]]
+name = "async-net"
+version = "2.0.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b948000fad4873c1c9339d60f2623323a0cfd3816e5181033c6a5cb68b2accf7"
+dependencies = [
+ "async-io 2.3.4",
+ "blocking",
+ "futures-lite 2.4.0",
+]
+
[[package]]
name = "async-once-cell"
version = "0.5.4"
@@ -421,6 +468,25 @@ dependencies = [
"windows-sys 0.48.0",
]
+[[package]]
+name = "async-process"
+version = "2.3.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "63255f1dc2381611000436537bbedfe83183faa303a5a0edaf191edef06526bb"
+dependencies = [
+ "async-channel",
+ "async-io 2.3.4",
+ "async-lock 3.4.0",
+ "async-signal",
+ "async-task",
+ "blocking",
+ "cfg-if",
+ "event-listener 5.3.1",
+ "futures-lite 2.4.0",
+ "rustix 0.38.39",
+ "tracing",
+]
+
[[package]]
name = "async-recursion"
version = "1.1.1"
@@ -493,9 +559,9 @@ dependencies = [
"enumflags2",
"serde",
"static_assertions",
- "zbus",
- "zbus_names",
- "zvariant",
+ "zbus 3.15.2",
+ "zbus_names 2.6.1",
+ "zvariant 3.15.2",
]
[[package]]
@@ -507,7 +573,7 @@ dependencies = [
"atspi-common",
"atspi-proxies",
"futures-lite 1.13.0",
- "zbus",
+ "zbus 3.15.2",
]
[[package]]
@@ -518,7 +584,7 @@ checksum = "6495661273703e7a229356dcbe8c8f38223d697aacfaf0e13590a9ac9977bb52"
dependencies = [
"atspi-common",
"serde",
- "zbus",
+ "zbus 3.15.2",
]
[[package]]
@@ -527,21 +593,6 @@ version = "1.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ace50bade8e6234aa140d9a2f552bbee1db4d353f69b8217bc503490fc1a9f26"
-[[package]]
-name = "backtrace"
-version = "0.3.74"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "8d82cb332cdfaed17ae235a638438ac4d4839913cc2af585c3c6746e8f8bee1a"
-dependencies = [
- "addr2line",
- "cfg-if",
- "libc",
- "miniz_oxide",
- "object",
- "rustc-demangle",
- "windows-targets 0.52.6",
-]
-
[[package]]
name = "base64"
version = "0.21.7"
@@ -811,6 +862,12 @@ version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fd16c4719339c4530435d38e511904438d07cce7950afa3718a84ac36c10e89e"
+[[package]]
+name = "cfg_aliases"
+version = "0.2.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "613afe47fcd5fac7ccf1db93babcb082c5994d996f20b8b159f2ad1658eb5724"
+
[[package]]
name = "cgl"
version = "0.3.2"
@@ -1212,15 +1269,6 @@ dependencies = [
"serde",
]
-[[package]]
-name = "ecolor"
-version = "0.28.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "2e6b451ff1143f6de0f33fc7f1b68fecfd2c7de06e104de96c4514de3f5396f8"
-dependencies = [
- "emath 0.28.1",
-]
-
[[package]]
name = "eframe"
version = "0.27.2"
@@ -1230,7 +1278,7 @@ dependencies = [
"bytemuck",
"cocoa",
"document-features",
- "egui 0.27.2",
+ "egui",
"egui-wgpu",
"egui-winit",
"egui_glow",
@@ -1263,33 +1311,12 @@ checksum = "584c5d1bf9a67b25778a3323af222dbe1a1feb532190e103901187f92c7fe29a"
dependencies = [
"accesskit",
"ahash",
- "epaint 0.27.2",
+ "epaint",
"log",
"nohash-hasher",
"serde",
]
-[[package]]
-name = "egui"
-version = "0.28.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "20c97e70a2768de630f161bb5392cbd3874fcf72868f14df0e002e82e06cb798"
-dependencies = [
- "ahash",
- "emath 0.28.1",
- "epaint 0.28.1",
- "nohash-hasher",
-]
-
-[[package]]
-name = "egui-aesthetix"
-version = "0.2.4"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c0b91e1b07193fa9bc71849e39a76d30ffd6a2f57bfc54552e3a2df263da577b"
-dependencies = [
- "egui 0.28.1",
-]
-
[[package]]
name = "egui-wgpu"
version = "0.27.2"
@@ -1298,8 +1325,8 @@ checksum = "469ff65843f88a702b731a1532b7d03b0e8e96d283e70f3a22b0e06c46cb9b37"
dependencies = [
"bytemuck",
"document-features",
- "egui 0.27.2",
- "epaint 0.27.2",
+ "egui",
+ "epaint",
"log",
"thiserror",
"type-map",
@@ -1316,7 +1343,7 @@ checksum = "2e3da0cbe020f341450c599b35b92de4af7b00abde85624fd16f09c885573609"
dependencies = [
"accesskit_winit",
"arboard",
- "egui 0.27.2",
+ "egui",
"log",
"raw-window-handle 0.6.2",
"smithay-clipboard",
@@ -1331,7 +1358,7 @@ version = "0.27.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1b78779f35ded1a853786c9ce0b43fe1053e10a21ea3b23ebea411805ce41593"
dependencies = [
- "egui 0.27.2",
+ "egui",
"ehttp",
"enum-map",
"image",
@@ -1348,7 +1375,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e0e5d975f3c86edc3d35b1db88bb27c15dde7c55d3b5af164968ab5ede3f44ca"
dependencies = [
"bytemuck",
- "egui 0.27.2",
+ "egui",
"glow",
"log",
"memoffset 0.9.1",
@@ -1387,12 +1414,6 @@ dependencies = [
"serde",
]
-[[package]]
-name = "emath"
-version = "0.28.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "0a6a21708405ea88f63d8309650b4d77431f4bc28fb9d8e6f77d3963b51249e6"
-
[[package]]
name = "encoding_rs"
version = "0.8.35"
@@ -1402,6 +1423,12 @@ dependencies = [
"cfg-if",
]
+[[package]]
+name = "endi"
+version = "1.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a3d8a32ae18130a3c84dd492d4215c3d913c3b07c6b63c2eb3eb7ff1101ab7bf"
+
[[package]]
name = "enum-map"
version = "2.7.3"
@@ -1487,28 +1514,14 @@ dependencies = [
"ab_glyph",
"ahash",
"bytemuck",
- "ecolor 0.27.2",
- "emath 0.27.2",
+ "ecolor",
+ "emath",
"log",
"nohash-hasher",
"parking_lot",
"serde",
]
-[[package]]
-name = "epaint"
-version = "0.28.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "3f0dcc0a0771e7500e94cd1cb797bd13c9f23b9409bdc3c824e2cbc562b7fa01"
-dependencies = [
- "ab_glyph",
- "ahash",
- "ecolor 0.28.1",
- "emath 0.28.1",
- "nohash-hasher",
- "parking_lot",
-]
-
[[package]]
name = "equivalent"
version = "1.0.1"
@@ -1651,6 +1664,15 @@ dependencies = [
"percent-encoding",
]
+[[package]]
+name = "futures-channel"
+version = "0.3.31"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2dff15bf788c671c1934e366d07e30c1814a8ef514e1af724a602e8a2fbe1b10"
+dependencies = [
+ "futures-core",
+]
+
[[package]]
name = "futures-core"
version = "0.3.31"
@@ -1691,6 +1713,17 @@ dependencies = [
"pin-project-lite",
]
+[[package]]
+name = "futures-macro"
+version = "0.3.31"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "162ee34ebcb7c64a8abebc059ce0fee27c2262618d7b60ed8faf72fef13c3650"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn 2.0.87",
+]
+
[[package]]
name = "futures-sink"
version = "0.3.31"
@@ -1711,6 +1744,7 @@ checksum = "9fa08315bb612088cc391249efdc3bc77536f16c91f6cf495e6fbe85b20a4a81"
dependencies = [
"futures-core",
"futures-io",
+ "futures-macro",
"futures-sink",
"futures-task",
"memchr",
@@ -1750,12 +1784,6 @@ dependencies = [
"wasi",
]
-[[package]]
-name = "gimli"
-version = "0.31.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "07e28edb80900c19c28f1072f2e8aeca7fa06b23cd4169cefe1af5aa3260783f"
-
[[package]]
name = "gl_generator"
version = "0.14.0"
@@ -1792,7 +1820,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "18fcd4ae4e86d991ad1300b8f57166e5be0c95ef1f63f3f5b827f8a164548746"
dependencies = [
"bitflags 2.6.0",
- "cfg_aliases",
+ "cfg_aliases 0.1.1",
"cgl",
"core-foundation",
"dispatch",
@@ -1815,7 +1843,7 @@ version = "0.4.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1ebcdfba24f73b8412c5181e56f092b5eff16671c514ce896b258a0a64bd7735"
dependencies = [
- "cfg_aliases",
+ "cfg_aliases 0.1.1",
"glutin",
"raw-window-handle 0.5.2",
"winit",
@@ -2327,18 +2355,6 @@ dependencies = [
"simd-adler32",
]
-[[package]]
-name = "mio"
-version = "1.0.2"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "80e04d1dcff3aae0704555fe5fee3bcfaf3d1fdf8a7e521d5b9d2b42acb52cec"
-dependencies = [
- "hermit-abi 0.3.9",
- "libc",
- "wasi",
- "windows-sys 0.52.0",
-]
-
[[package]]
name = "naga"
version = "0.19.2"
@@ -2402,6 +2418,19 @@ dependencies = [
"memoffset 0.7.1",
]
+[[package]]
+name = "nix"
+version = "0.29.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "71e2746dc3a24dd78b3cfcb7be93368c6de9963d30f43a6a73998a9cf4b17b46"
+dependencies = [
+ "bitflags 2.6.0",
+ "cfg-if",
+ "cfg_aliases 0.2.1",
+ "libc",
+ "memoffset 0.9.1",
+]
+
[[package]]
name = "nohash-hasher"
version = "0.2.0"
@@ -2581,6 +2610,7 @@ checksum = "0ee638a5da3799329310ad4cfa62fbf045d5f56e3ef5ba4149e7452dcf89d5a8"
dependencies = [
"bitflags 2.6.0",
"block2 0.5.1",
+ "dispatch",
"libc",
"objc2 0.5.2",
]
@@ -2619,15 +2649,6 @@ dependencies = [
"cc",
]
-[[package]]
-name = "object"
-version = "0.36.5"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "aedf0a2d09c573ed1d8d85b30c119153926a2b36dce0ab28322c09a117a4683e"
-dependencies = [
- "memchr",
-]
-
[[package]]
name = "oboe"
version = "0.6.1"
@@ -2820,6 +2841,12 @@ dependencies = [
"windows-sys 0.59.0",
]
+[[package]]
+name = "pollster"
+version = "0.3.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "22686f4785f02a4fcc856d3b3bb19bf6c8160d103f7a99cc258bddd0251dc7f2"
+
[[package]]
name = "ppv-lite86"
version = "0.2.20"
@@ -3013,6 +3040,28 @@ dependencies = [
"usvg",
]
+[[package]]
+name = "rfd"
+version = "0.15.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "46f6f80a9b882647d9014673ca9925d30ffc9750f2eed2b4490e189eaebd01e8"
+dependencies = [
+ "ashpd",
+ "block2 0.5.1",
+ "js-sys",
+ "log",
+ "objc2 0.5.2",
+ "objc2-app-kit",
+ "objc2-foundation",
+ "pollster",
+ "raw-window-handle 0.6.2",
+ "urlencoding",
+ "wasm-bindgen",
+ "wasm-bindgen-futures",
+ "web-sys",
+ "windows-sys 0.48.0",
+]
+
[[package]]
name = "rgb"
version = "0.8.50"
@@ -3056,12 +3105,6 @@ version = "0.19.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3cd14fd5e3b777a7422cca79358c57a8f6e3a703d9ac187448d0daf220c2407f"
-[[package]]
-name = "rustc-demangle"
-version = "0.1.24"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "719b953e2095829ee67db738b3bfa9fa368c94900df327b3f07fe6e794d2fe1f"
-
[[package]]
name = "rustc-hash"
version = "1.1.0"
@@ -3673,30 +3716,15 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20"
[[package]]
-name = "tokio"
-version = "1.41.0"
+name = "toml"
+version = "0.7.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "145f3413504347a2be84393cc8a7d2fb4d863b375909ea59f2158261aa258bbb"
+checksum = "dd79e69d3b627db300ff956027cc6c3798cef26d22526befdfcd12feeb6d2257"
dependencies = [
- "backtrace",
- "bytes",
- "libc",
- "mio",
- "pin-project-lite",
- "signal-hook-registry",
- "tokio-macros",
- "windows-sys 0.52.0",
-]
-
-[[package]]
-name = "tokio-macros"
-version = "2.4.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "693d596312e88961bc67d7f1f97af8a70227d9f90c31bba5806eec004978d752"
-dependencies = [
- "proc-macro2",
- "quote",
- "syn 2.0.87",
+ "serde",
+ "serde_spanned",
+ "toml_datetime",
+ "toml_edit 0.19.15",
]
[[package]]
@@ -3727,6 +3755,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1b5bb770da30e5cbfde35a2d7b9b8a2c4b8ef89548a7a6aeab5c9a576e3e7421"
dependencies = [
"indexmap",
+ "serde",
+ "serde_spanned",
"toml_datetime",
"winnow 0.5.40",
]
@@ -3886,6 +3916,12 @@ dependencies = [
"serde",
]
+[[package]]
+name = "urlencoding"
+version = "2.1.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "daf8dba3b7eb870caf1ddeed7bc9d2a049f3cfdfae7cb521b087cc33ae4c49da"
+
[[package]]
name = "usvg"
version = "0.37.0"
@@ -4229,7 +4265,7 @@ checksum = "cbd7311dbd2abcfebaabf1841a2824ed7c8be443a0f29166e5d3c6a53a762c01"
dependencies = [
"arrayvec",
"cfg-if",
- "cfg_aliases",
+ "cfg_aliases 0.1.1",
"js-sys",
"log",
"parking_lot",
@@ -4254,7 +4290,7 @@ dependencies = [
"arrayvec",
"bit-vec",
"bitflags 2.6.0",
- "cfg_aliases",
+ "cfg_aliases 0.1.1",
"codespan-reporting",
"indexmap",
"log",
@@ -4281,7 +4317,7 @@ dependencies = [
"arrayvec",
"ash",
"bitflags 2.6.0",
- "cfg_aliases",
+ "cfg_aliases 0.1.1",
"core-graphics-types",
"glow",
"glutin_wgl_sys",
@@ -4667,7 +4703,7 @@ dependencies = [
"bitflags 2.6.0",
"bytemuck",
"calloop 0.12.4",
- "cfg_aliases",
+ "cfg_aliases 0.1.1",
"core-foundation",
"core-graphics",
"cursor-icon",
@@ -4722,6 +4758,16 @@ dependencies = [
"memchr",
]
+[[package]]
+name = "winresource"
+version = "0.1.17"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "77e2aaaf8cfa92078c0c0375423d631f82f2f57979c2884fdd5f604a11e45329"
+dependencies = [
+ "toml 0.7.8",
+ "version_check",
+]
+
[[package]]
name = "x11-dl"
version = "2.21.0"
@@ -4834,6 +4880,7 @@ dependencies = [
"clap",
"env_logger",
"log",
+ "winresource",
"xmpd-cliargs",
"xmpd-gui",
"xmpd-manifest",
@@ -4846,17 +4893,18 @@ version = "2.0.0"
dependencies = [
"anyhow",
"camino",
+ "dirs",
"eframe",
- "egui 0.27.2",
- "egui-aesthetix",
+ "egui",
"egui_extras",
"lazy_static",
"log",
- "tokio",
+ "rfd",
"uuid",
"xmpd-cache",
"xmpd-cliargs",
"xmpd-manifest",
+ "xmpd-player",
"xmpd-settings",
]
@@ -4867,14 +4915,20 @@ dependencies = [
"anyhow",
"serde",
"serde_json",
+ "toml 0.8.19",
"url",
"uuid",
+ "xmpd-cliargs",
+ "xmpd-settings",
]
[[package]]
name = "xmpd-player"
version = "2.0.0"
dependencies = [
+ "anyhow",
+ "lazy_static",
+ "log",
"rodio",
]
@@ -4884,10 +4938,10 @@ version = "2.0.0"
dependencies = [
"anyhow",
"camino",
- "egui 0.27.2",
+ "egui",
"lazy_static",
"serde",
- "toml",
+ "toml 0.8.19",
]
[[package]]
@@ -4900,12 +4954,12 @@ version = "3.15.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "675d170b632a6ad49804c8cf2105d7c31eddd3312555cffd4b740e08e97c25e6"
dependencies = [
- "async-broadcast",
+ "async-broadcast 0.5.1",
"async-executor",
- "async-fs",
+ "async-fs 1.6.0",
"async-io 1.13.0",
"async-lock 2.8.0",
- "async-process",
+ "async-process 1.8.1",
"async-recursion",
"async-task",
"async-trait",
@@ -4918,7 +4972,7 @@ dependencies = [
"futures-sink",
"futures-util",
"hex",
- "nix",
+ "nix 0.26.4",
"once_cell",
"ordered-stream",
"rand",
@@ -4930,9 +4984,45 @@ dependencies = [
"uds_windows",
"winapi",
"xdg-home",
- "zbus_macros",
- "zbus_names",
- "zvariant",
+ "zbus_macros 3.15.2",
+ "zbus_names 2.6.1",
+ "zvariant 3.15.2",
+]
+
+[[package]]
+name = "zbus"
+version = "5.1.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1162094dc63b1629fcc44150bcceeaa80798cd28bcbe7fa987b65a034c258608"
+dependencies = [
+ "async-broadcast 0.7.1",
+ "async-executor",
+ "async-fs 2.1.2",
+ "async-io 2.3.4",
+ "async-lock 3.4.0",
+ "async-process 2.3.0",
+ "async-recursion",
+ "async-task",
+ "async-trait",
+ "blocking",
+ "enumflags2",
+ "event-listener 5.3.1",
+ "futures-core",
+ "futures-util",
+ "hex",
+ "nix 0.29.0",
+ "ordered-stream",
+ "serde",
+ "serde_repr",
+ "static_assertions",
+ "tracing",
+ "uds_windows",
+ "windows-sys 0.59.0",
+ "winnow 0.6.20",
+ "xdg-home",
+ "zbus_macros 5.1.1",
+ "zbus_names 4.1.0",
+ "zvariant 5.1.0",
]
[[package]]
@@ -4946,7 +5036,22 @@ dependencies = [
"quote",
"regex",
"syn 1.0.109",
- "zvariant_utils",
+ "zvariant_utils 1.0.1",
+]
+
+[[package]]
+name = "zbus_macros"
+version = "5.1.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2cd2dcdce3e2727f7d74b7e33b5a89539b3cc31049562137faf7ae4eb86cd16d"
+dependencies = [
+ "proc-macro-crate 3.2.0",
+ "proc-macro2",
+ "quote",
+ "syn 2.0.87",
+ "zbus_names 4.1.0",
+ "zvariant 5.1.0",
+ "zvariant_utils 3.0.2",
]
[[package]]
@@ -4957,7 +5062,19 @@ checksum = "437d738d3750bed6ca9b8d423ccc7a8eb284f6b1d6d4e225a0e4e6258d864c8d"
dependencies = [
"serde",
"static_assertions",
- "zvariant",
+ "zvariant 3.15.2",
+]
+
+[[package]]
+name = "zbus_names"
+version = "4.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "856b7a38811f71846fd47856ceee8bccaec8399ff53fb370247e66081ace647b"
+dependencies = [
+ "serde",
+ "static_assertions",
+ "winnow 0.6.20",
+ "zvariant 5.1.0",
]
[[package]]
@@ -4998,7 +5115,23 @@ dependencies = [
"libc",
"serde",
"static_assertions",
- "zvariant_derive",
+ "zvariant_derive 3.15.2",
+]
+
+[[package]]
+name = "zvariant"
+version = "5.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a1200ee6ac32f1e5a312e455a949a4794855515d34f9909f4a3e082d14e1a56f"
+dependencies = [
+ "endi",
+ "enumflags2",
+ "serde",
+ "static_assertions",
+ "url",
+ "winnow 0.6.20",
+ "zvariant_derive 5.1.0",
+ "zvariant_utils 3.0.2",
]
[[package]]
@@ -5011,7 +5144,20 @@ dependencies = [
"proc-macro2",
"quote",
"syn 1.0.109",
- "zvariant_utils",
+ "zvariant_utils 1.0.1",
+]
+
+[[package]]
+name = "zvariant_derive"
+version = "5.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "687e3b97fae6c9104fbbd36c73d27d149abf04fb874e2efbd84838763daa8916"
+dependencies = [
+ "proc-macro-crate 3.2.0",
+ "proc-macro2",
+ "quote",
+ "syn 2.0.87",
+ "zvariant_utils 3.0.2",
]
[[package]]
@@ -5024,3 +5170,17 @@ dependencies = [
"quote",
"syn 1.0.109",
]
+
+[[package]]
+name = "zvariant_utils"
+version = "3.0.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "20d1d011a38f12360e5fcccceeff5e2c42a8eb7f27f0dcba97a0862ede05c9c6"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "serde",
+ "static_assertions",
+ "syn 2.0.87",
+ "winnow 0.6.20",
+]
diff --git a/Cargo.toml b/Cargo.toml
index bc9c4a5..11853d8 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -25,28 +25,25 @@ authors=[
[workspace.dependencies]
anstyle = "1.0.6"
anyhow = "1.0.81"
-bitflags = { version = "2.6.0", features = ["serde"] }
camino = { version="1.1.6", features = ["serde1"] }
clap = { version = "4.5.4", features = ["derive"] }
eframe = "0.27.2"
egui = { version = "0.27.2", features = ["color-hex", "serde"] }
egui_extras = { version = "0.27.2", features = ["all_loaders"] }
env_logger = "0.11.3"
-futures = "0.3.30"
-html-escape = "0.2.13"
lazy_static = "1.4.0"
-libc = "0.2.153"
log = "0.4.21"
-notify-rust = "4.11.3"
-open = "5.3.0"
-regex = "1.11.0"
-reqwest = { version = "0.12.3", features = ["blocking", "h2", "http2", "rustls-tls"], default-features = false }
+# notify-rust = "4.11.3"
+# open = "5.3.0"
+# reqwest = { version = "0.12.3", features = ["blocking", "h2", "http2", "rustls-tls"], default-features = false }
serde = { version = "1.0.197", features = ["derive"] }
serde_json = "1.0.115"
-# serde_traitobject = "0.2.8"
-tokio = { version = "1.37.0", features = ["macros", "rt-multi-thread", "process", "sync"] }
url = { version = "2.5.0", features = ["serde"] }
uuid = { version = "1.11.0", features = ["serde", "v4"] }
windows = { version = "0.56.0", features = ["Win32_Foundation", "Win32_Storage_FileSystem", "Win32_System_Console"] }
-zip-extensions = "0.6.20"
+# zip-extensions = "0.6.20"
dirs="5.0.1"
+winresource = "0.1.17"
+toml = "0.8.19"
+rfd = "0.15.1"
+rodio = { version = "0.20.1", features = ["symphonia-all"] }
diff --git a/assets/icon.ico b/assets/icon.ico
new file mode 100644
index 0000000..6bfeaf8
Binary files /dev/null and b/assets/icon.ico differ
diff --git a/assets/info.svg b/assets/info.svg
index 3a59676..cbb1591 100644
--- a/assets/info.svg
+++ b/assets/info.svg
@@ -1 +1 @@
-
+
diff --git a/manifest.toml b/manifest.toml
new file mode 100644
index 0000000..e1f0ec6
--- /dev/null
+++ b/manifest.toml
@@ -0,0 +1,1373 @@
+[songs.6895e921-91f2-4713-b604-c2cdd9f3f1c2]
+name = "We Didnt Start The Fire"
+author = "Fall Out Boy"
+url = "https://open.spotify.com/track/60glT2wsoSHV3B8yCRSB8v"
+source_type = "Spotify"
+
+[songs.98b04764-39cc-4c82-a2e2-420dd85573ca]
+name = "Tarp amzinybes"
+author = "SEL"
+url = "https://youtu.be/UfPj-0QpGFA"
+source_type = "Youtube"
+
+[songs.3b2573d4-2e1b-4446-a377-5fcb2b0a70c5]
+name = "18 mne uze"
+author = "Ruki Verch"
+url = "https://www.youtube.com/watch?v=Hxab_Sr132o"
+source_type = "Youtube"
+
+[songs.fa339b0b-8d3a-45e7-aed9-f744afb4a666]
+name = "Eik Tu NA"
+author = "AVA"
+url = "https://www.youtube.com/watch?v=yRf3ijaIgOg"
+source_type = "Youtube"
+
+[songs.862ddd87-68c4-4bed-a8cd-0e2021a0ce79]
+name = "Renee"
+author = "SALES"
+url = "https://open.spotify.com/track/5MeUMSRreLHYLhw8ZTyqpk"
+source_type = "Spotify"
+
+[songs.95a3350c-c62f-4543-8cfe-6c512861b229]
+name = "Ryk Zuvedra ryk"
+author = "Zilviokas"
+url = "https://www.youtube.com/watch?v=eN6BOI7UlSE"
+source_type = "Youtube"
+
+[songs.e71a61d4-b54a-47d1-b0ad-db6695bd0739]
+name = "Hey Lover!"
+author = "Wabie"
+url = "https://open.spotify.com/track/7rC3P7tpWriaC4hYWKwGQd"
+source_type = "Spotify"
+
+[songs.bd85a9d7-1fb6-4154-a290-e5137590e419]
+name = "I gamta"
+author = "Andzikas"
+url = "https://www.youtube.com/watch?v=UyLdjC-hihM"
+source_type = "Youtube"
+
+[songs.c28f14ca-ec10-469a-b0cc-3e26e9f56007]
+name = "VASARA ZJBS"
+author = "Depresinis & MERAKI2004"
+url = "https://www.youtube.com/watch?v=BD-pBjRy-5A"
+source_type = "Youtube"
+
+[songs.b26f7338-0d1f-4dba-b639-3bdae9d7e10e]
+name = "Po stikliuka"
+author = "Tipo grupe ir Kastaneda"
+url = "https://www.youtube.com/watch?v=EtmE60nE7fI"
+source_type = "Youtube"
+
+[songs.720d5b94-ac54-4162-8da7-ba8d258139e6]
+name = "KINO FILMAI"
+author = "RADVIS"
+url = "https://www.youtube.com/watch?v=vhAEkC3xNMo"
+source_type = "Youtube"
+
+[songs.91629850-33e2-4e18-8c37-6eafdfcde3f4]
+name = "Give It To Me"
+author = "Unknown"
+url = "https://www.youtube.com/watch?v=upQe8EeSyZU"
+source_type = "Youtube"
+
+[songs.2b19ae9f-2321-41fc-8a1f-2bb4c2975380]
+name = "Malunas Prie Kelio"
+author = "VainHouse"
+url = "https://www.youtube.com/watch?v=bbwuNjDXCiM"
+source_type = "Youtube"
+
+[songs.dfc0782c-33f2-414d-9b56-76a77902bcc5]
+name = "Baliavojam"
+author = "16Hz"
+url = "https://www.youtube.com/watch?v=Ia-qERX8WLs"
+source_type = "Youtube"
+
+[songs.518e23e8-72df-42de-a918-6a9a197deb9a]
+name = "Whiskey, Cola i Tequila"
+author = "Maco Mamuko"
+url = "https://www.youtube.com/watch?v=aBrN0k0Phtc"
+source_type = "Youtube"
+
+[songs.e9495391-8a6c-4942-9dc6-09b764f9d1e9]
+name = "Zalias Pasas"
+author = "Zas"
+url = "https://www.youtube.com/watch?v=SZA7IjlCfyI"
+source_type = "Youtube"
+
+[songs.0b907d17-e3f9-4dbb-9c2d-e10327316f6e]
+name = "UZ MUS IR JUS"
+author = "Mr.Bullet"
+url = "https://www.youtube.com/watch?v=85q_7jXEgH8"
+source_type = "Youtube"
+
+[songs.407155fd-3174-4778-ae93-bb01b7518f9f]
+name = "Cia Mano Rojus"
+author = "Robertas Kupstas"
+url = "https://www.youtube.com/watch?v=xij_YeEInr8"
+source_type = "Youtube"
+
+[songs.8e8c3f6c-e34b-4f4e-b9be-f36be5901435]
+name = "biznis"
+author = "PROFLAME"
+url = "https://www.youtube.com/watch?v=t139Vd83pgs"
+source_type = "Youtube"
+
+[songs.eaf1cc63-3eb6-4d86-a986-e8129fff7fcb]
+name = "Zalia Siera"
+author = "DJ Dalgis"
+url = "https://www.youtube.com/watch?v=nfentq_pez4"
+source_type = "Youtube"
+
+[songs.05a28184-e58e-42c3-a803-512bb0817a07]
+name = "Vakareja"
+author = "Karaliska Erdve"
+url = "https://www.youtube.com/watch?v=g0HmrlJ7fhE"
+source_type = "Youtube"
+
+[songs.131ab436-869b-4b3b-8ce4-3b5087d3461f]
+name = "1.9 TDI"
+author = "Grupe MX"
+url = "https://www.youtube.com/watch?v=8FBr5GQXsI8"
+source_type = "Youtube"
+
+[songs.639dc5af-b3e6-4d38-a003-80deac01d326]
+name = "Myliu kina"
+author = "Zas"
+url = "https://www.youtube.com/watch?v=ImFrfmi-qT8"
+source_type = "Youtube"
+
+[songs.0e1d36ed-d07d-48c0-a101-1bf1ac6d7a26]
+name = "Be taves"
+author = "Funky"
+url = "https://www.youtube.com/watch?v=heTDrDukLWU"
+source_type = "Youtube"
+
+[songs.4f368ef5-08d0-4d1f-b8ba-a34ee9fc6aa2]
+name = "Blizgantys Naikai"
+author = "SADBOY"
+url = "https://www.youtube.com/watch?v=p5KsYJGcfOM"
+source_type = "Youtube"
+
+[songs.4eafd817-d75a-451e-b6b4-38ef29e66fdf]
+name = "Ne, nereikia asaru"
+author = "Rytis Cicinas"
+url = "https://www.youtube.com/watch?v=48GSg1q2kPE"
+source_type = "Youtube"
+
+[songs.f3edb436-bf04-4ad5-ad45-684a601846c3]
+name = "We are the winners"
+author = "LT United"
+url = "https://www.youtube.com/watch?v=DBAdOlQPbwg"
+source_type = "Youtube"
+
+[songs.75c6e1aa-21c7-4626-9def-602d656282bc]
+name = "Ka Tu Ka Vakare"
+author = "Unknown"
+url = "https://www.youtube.com/watch?v=6SOS4ljHbJY"
+source_type = "Youtube"
+
+[songs.870502f1-e3a9-419b-9ba6-d8100d1198a5]
+name = "Is Leto Leidziasi Saule"
+author = "Jovani, Karaliska Erdve"
+url = "https://www.youtube.com/watch?v=VqSu8iG1_DE"
+source_type = "Youtube"
+
+[songs.22565196-5757-4767-8f1f-f1c9b8a87dcc]
+name = "I WAS MADE FOR LOVIN' YOU (TECHNO)"
+author = "Unknown"
+url = "https://www.youtube.com/watch?v=asVznhccYao"
+source_type = "Youtube"
+
+[songs.73ddd372-4853-4182-b212-b021e7dc8988]
+name = "Parukom"
+author = "Tnn"
+url = "https://www.youtube.com/watch?v=v9pBZK2RIPI"
+source_type = "Youtube"
+
+[songs.02d90830-7b5f-430f-b82c-f45998862e81]
+name = "Juodas Golfas"
+author = "NL"
+url = "https://www.youtube.com/watch?v=f2-ZmElSvPc"
+source_type = "Youtube"
+
+[songs.81426b49-3511-4e48-be21-3c24acba0fb9]
+name = "Kernkraft 400"
+author = "Zombie Nation"
+url = "https://www.youtube.com/watch?v=z5LW07FTJbI"
+source_type = "Youtube"
+
+[songs.cd72e9db-aaa4-485e-b3a5-7fbeed2cff36]
+name = 'I.M.P Jingle (From "Helluva Boss")'
+author = "Geek Music"
+url = "https://open.spotify.com/track/5x0bvS385Ata1irjLMyLTn"
+source_type = "Spotify"
+
+[songs.ea41c37c-7b62-4fd6-9fec-650e94d184f2]
+name = "tipo daina"
+author = "Tipo Grupe"
+url = "https://www.youtube.com/watch?v=PTIOaSjEgIU"
+source_type = "Youtube"
+
+[songs.60214f0e-9a37-49b4-92bf-b7b548e501ad]
+name = "Uzmerkiu akis"
+author = "SEL"
+url = "https://youtu.be/SdWj06XRZms?si=9TNGzjK9ITom-jaD"
+source_type = "Youtube"
+
+[songs.858d8645-8637-4d3f-b4b7-c9c75743d90a]
+name = "Dviratukas"
+author = "Eugenijus Ostapenko"
+url = "https://www.youtube.com/watch?v=ILFHZQK33Mw"
+source_type = "Youtube"
+
+[songs.e37c6cb6-ad8e-43a7-83d1-8b795db8aa1c]
+name = "Harness Your Hopes"
+author = "Pavement"
+url = "https://open.spotify.com/track/4vsoWZcvtvSsE0OiVvDDvX"
+source_type = "Spotify"
+
+[songs.0ee56450-2942-4805-9f08-b49652472258]
+name = "Tricky Disco"
+author = "Unknown"
+url = "https://www.youtube.com/watch?v=t78qVdbAiXw"
+source_type = "Youtube"
+
+[songs.9ef33d55-972d-490c-95c6-bf046bf4f979]
+name = "MONEY ON THE DASH"
+author = "Unknown"
+url = "https://open.spotify.com/track/6nVEV8CIU4dw12oVZbKJtl"
+source_type = "Spotify"
+
+[songs.df2e369f-2fee-49e5-b52c-33f73c34ffb2]
+name = "Autostrada Vilnius"
+author = "16Hz"
+url = "https://www.youtube.com/watch?v=ANS2TSegr40"
+source_type = "Youtube"
+
+[songs.bc5fe406-040b-4c46-82ce-bb4505b81947]
+name = "i like the way you kiss me"
+author = "Artemas"
+url = "https://www.youtube.com/watch?v=ODDRRXMi22E"
+source_type = "Youtube"
+
+[songs.dc6c36b0-f595-457f-8fc3-641710b0e3b9]
+name = "I Pajuri"
+author = "Mercy Dance"
+url = "https://www.youtube.com/watch?v=RPpkMh47l9w"
+source_type = "Youtube"
+
+[songs.1a22e35a-0af1-4326-b59c-00d59504446b]
+name = "Virs debesu"
+author = "Andzikas"
+url = "https://www.youtube.com/watch?v=PHJcVGhxra8"
+source_type = "Youtube"
+
+[songs.f5a31a44-fc62-4cce-b65e-05e4085864fe]
+name = "End of Beginning"
+author = "Djo"
+url = "https://open.spotify.com/track/3qhlB30KknSejmIvZZLjOD"
+source_type = "Spotify"
+
+[songs.ca765223-e4e6-4b5d-8928-919c3f491b43]
+name = "Kaifuok"
+author = "SADBOY"
+url = "https://www.youtube.com/watch?v=vclryWgfy8I"
+source_type = "Youtube"
+
+[songs.0679475a-259c-4c98-9ae7-a58c10dfeedb]
+name = "New Flesh"
+author = "Current Joys"
+url = "https://open.spotify.com/track/6HJxxqHWMdidwTVZmZWeHU"
+source_type = "Spotify"
+
+[songs.b0c0d44c-86ca-4b80-a081-734a4178c870]
+name = "After Marvins Dance (Marvin Gaye's 'After The Dance' Edit)"
+author = "Bauhouse"
+url = "https://www.youtube.com/watch?v=J-cgyYiExh8"
+source_type = "Youtube"
+
+[songs.4cf5e323-5e79-4471-816a-5fa345854cea]
+name = "rytmecio rasos"
+author = "nemuno krantai"
+url = "https://www.youtube.com/watch?v=2-fGbsrofv4"
+source_type = "Youtube"
+
+[songs.2374179d-d6a8-41b9-9cac-21147303fc8e]
+name = "Nebenoriu Laukt"
+author = "Dzordana Butkute"
+url = "https://www.youtube.com/watch?v=_AozFrAqNMk"
+source_type = "Youtube"
+
+[songs.9866acdd-97ee-4e19-ba20-a64caa9b02ee]
+name = "Where'd All the Time Go?"
+author = "Dr. Dog"
+url = "https://open.spotify.com/track/0UV5zxRMz6AO4ZwUOZNIKI"
+source_type = "Spotify"
+
+[songs.27d61e87-deec-4e4c-8015-ad8b691eea40]
+name = "Klaipeda On Top"
+author = "Deivas"
+url = "https://www.youtube.com/watch?v=g_h2M3e2OYU"
+source_type = "Youtube"
+
+[songs.c332ce2b-6b1c-4979-9977-dc00aa6e710b]
+name = "TU ESI MELAGIS (Techno Extended)"
+author = "Radvis"
+url = "https://www.youtube.com/watch?v=kmvvP7GW_bw"
+source_type = "Youtube"
+
+[songs.4a7b1853-7529-498d-addf-25e894a37ccf]
+name = "Temptation"
+author = "Arash"
+url = "https://www.youtube.com/watch?v=6X3ZJh762-I"
+source_type = "Youtube"
+
+[songs.6c3f2503-0160-4c42-8b17-25ff402f3fa7]
+name = "Visky Kola karaleva trans pola"
+author = "Dzaro and hansa"
+url = "https://www.youtube.com/watch?v=fflrMvZ2HtA"
+source_type = "Youtube"
+
+[songs.18c2b9bc-6403-4a67-94bd-1e9f5413d83b]
+name = "Skrendu"
+author = "SEL"
+url = "https://youtu.be/G5Q_asvrXlA"
+source_type = "Youtube"
+
+[songs.d563619a-344e-41cf-99d1-5a52214ada49]
+name = "Sports"
+author = "Beach Bunny"
+url = "https://open.spotify.com/track/77G0k1La0c5Dw8bAFANcyp"
+source_type = "Spotify"
+
+[songs.e1cb8b00-713b-4a0e-bc61-dbf2d2596fcf]
+name = "Tik Tok"
+author = "SEL"
+url = "https://youtu.be/RkaIUateIxg"
+source_type = "Youtube"
+
+[songs.f81f8f9d-6c56-4ae3-8f5f-28c04632d3b5]
+name = "Kcik 23"
+author = "Unknown Artist"
+url = "https://www.youtube.com/watch?v=SnnqDdZJpzA"
+source_type = "Youtube"
+
+[songs.877c24e4-c0c0-4e9c-a553-221e381c8a00]
+name = "Stumblin' In"
+author = "CYRIL"
+url = "https://open.spotify.com/track/0h3Xy4V4apMraB5NuM8U7Z"
+source_type = "Spotify"
+
+[songs.5c2ee27e-d564-404a-8259-87d1d336f106]
+name = "Money"
+author = "The Drums"
+url = "https://open.spotify.com/track/3VIJBrMpvimHEw5wtPh2wB"
+source_type = "Spotify"
+
+[songs.eb91fc63-4e0e-49e9-99c7-ea7fe5770a5b]
+name = "LEDINE"
+author = "Depresinis"
+url = "https://www.youtube.com/watch?v=qugvChkXMLk"
+source_type = "Youtube"
+
+[songs.16dafb40-ebac-4e04-961a-99d79789f1a3]
+name = "Topine Panele"
+author = "Wenona Waves"
+url = "https://www.youtube.com/watch?v=MPHuhmUomfE"
+source_type = "Youtube"
+
+[songs.4b61344d-fa89-4b79-bd45-3b1fc8c8cc1c]
+name = "Rycka klipas"
+author = "Unknown"
+url = "https://www.youtube.com/watch?v=nuTUDSQ3BBI"
+source_type = "Youtube"
+
+[songs.3d543d58-e33d-4158-9956-049309baa35c]
+name = "Movin' Out (Anthony's Song)"
+author = "Billy Joel"
+url = "https://open.spotify.com/track/16GUMo6u3D2qo9a19AkYct"
+source_type = "Spotify"
+
+[songs.7e32803d-a5a9-453c-8c81-b479c51e5bc9]
+name = "Looking Out for You"
+author = "Joy Again"
+url = "https://open.spotify.com/track/3jfZ9M23l0L7RxzYMTgBTv"
+source_type = "Spotify"
+
+[songs.bb16708f-0cd7-4918-8014-d374d68acbd2]
+name = "Not Allowed"
+author = "TV Girl"
+url = "https://open.spotify.com/track/3IznIgmXtrUaoPWpQTy5jB"
+source_type = "Spotify"
+
+[songs.76f7945f-e677-4297-a98a-708314f92de8]
+name = "Kanikuli"
+author = "Unknown"
+url = "https://www.youtube.com/watch?v=GNMiDZTL7jo"
+source_type = "Youtube"
+
+[songs.4a56c71c-70f0-45e7-a9b8-2304d41e1877]
+name = "Big Jet Plane"
+author = "Restricted"
+url = "https://www.youtube.com/watch?v=NpVF0z5N-tE"
+source_type = "Youtube"
+
+[songs.201c6680-7dcf-4d75-8bdc-76ff9a715167]
+name = "Gyvenimo man vieno neuztenka"
+author = "SEL"
+url = "https://youtu.be/7Gd9XaEG5o4"
+source_type = "Youtube"
+
+[songs.d71c0e55-5d40-4045-853e-b32da713c0b8]
+name = "Lietuva (Nebelinksmas musu kaimas)"
+author = "Vytautas siskauskas"
+url = "https://www.youtube.com/watch?v=31UkGzde14c"
+source_type = "Youtube"
+
+[songs.92561031-92ae-471a-b154-8519c715011d]
+name = "On The Rob"
+author = "Cheap Dirty Horse"
+url = "https://open.spotify.com/track/2kZ2fD3ohaGhDHMBTAppto"
+source_type = "Spotify"
+
+[songs.9fabb799-f6aa-45a0-93b7-8cb08ab8f0a8]
+name = "16 metu"
+author = "69 Danguje"
+url = "https://www.youtube.com/watch?v=nwjoqPcUPrw"
+source_type = "Youtube"
+
+[songs.22f18d88-fb39-4cec-98be-02d4ebe3a6ee]
+name = "Party maker"
+author = "MUTA"
+url = "https://www.youtube.com/watch?v=LT9VNK1aCXY"
+source_type = "Youtube"
+
+[songs.90ae639e-0214-4a8e-b582-4949674ffaa2]
+name = "Coco Jumbo"
+author = "Mr President"
+url = "https://www.youtube.com/watch?v=cOrc37wNUqU"
+source_type = "Youtube"
+
+[songs.abd6a036-f17a-4a6a-8420-2cc3e7e4a181]
+name = "Ten kazkur giliai"
+author = "SEL"
+url = "https://youtu.be/N7YNlegeiqs?si=9BBH6hjrgOjJ01Nz"
+source_type = "Youtube"
+
+[songs.03f406b5-d5e9-4d22-b9bc-d0b36354400a]
+name = "Diskoteka is 90 hit"
+author = "Raim & Artur feat. Zhenis"
+url = "https://www.youtube.com/watch?v=GfBhxlNhrn0"
+source_type = "Youtube"
+
+[songs.2e9ef729-1560-446c-af6a-8a9d4c43ac9a]
+name = "Big Sis"
+author = "SALES"
+url = "https://open.spotify.com/track/0ARp9mXXpPflIwehy25kCa"
+source_type = "Spotify"
+
+[songs.2bd2e64e-28f0-4bd7-aee3-e1d27ed0797d]
+name = "In the End"
+author = "Linkin Park"
+url = "https://open.spotify.com/track/60a0Rd6pjrkxjPbaKzXjfq"
+source_type = "Spotify"
+
+[songs.9be83d96-6dfb-47a0-b58e-a1aed1cf6031]
+name = "Pasitusinam"
+author = "NL"
+url = "https://www.youtube.com/watch?v=WhSFudvloog"
+source_type = "Youtube"
+
+[songs.2b33d0b1-aecb-4ff9-841a-3a7b2861f28d]
+name = "Degtine"
+author = "Grupiokai"
+url = "https://www.youtube.com/watch?v=8SqbG2VmEFw"
+source_type = "Youtube"
+
+[songs.457bc1f5-f804-4665-a595-8e56a6558d1d]
+name = "Sombrero"
+author = "Kastanenda"
+url = "https://www.youtube.com/watch?v=3Z3_4TknCfQ"
+source_type = "Youtube"
+
+[songs.0491fd9f-149d-4e70-bc5c-0fae9a21d9b0]
+name = "Rozovoe vino"
+author = "Eldzej and Feduk"
+url = "https://www.youtube.com/watch?v=S9oXj3a4gZ4"
+source_type = "Youtube"
+
+[songs.85098bbc-86fa-4da4-b342-d2a512738060]
+name = "Topolini puh"
+author = "Unknown"
+url = "https://www.youtube.com/watch?v=UUryvYF8tUs"
+source_type = "Youtube"
+
+[songs.d8e93863-6102-4cda-a0b2-3728d20c9cf8]
+name = "dashstar"
+author = "Knock2"
+url = "https://www.youtube.com/watch?v=58ml5b_On6w"
+source_type = "Youtube"
+
+[songs.8858c7a1-ea6d-409b-9fc3-7256a39d4d8b]
+name = "PENKTADIENIS"
+author = "NIERKA"
+url = "https://www.youtube.com/watch?v=h3TuZj_OAf0"
+source_type = "Youtube"
+
+[songs.190a5577-124a-4232-9e6f-1d0a50f4d881]
+name = "Cheri cheri lady"
+author = "Unknown"
+url = "https://www.youtube.com/watch?v=lrIKt5uDWZo"
+source_type = "Youtube"
+
+[songs.08995ab0-2954-4d6b-913e-27609e90b354]
+name = "Caramelldansen"
+author = "Alfons, LOOKET, DJ BERIT"
+url = "https://open.spotify.com/track/1v7aZHBUwKxeCgmpe6fVUH"
+source_type = "Spotify"
+
+[songs.fc3f499b-2c33-41d0-b79c-ca68486b2a4d]
+name = "Blaue Augen"
+author = "08 Blumchen"
+url = "https://www.youtube.com/watch?v=mE4PZcUfiwE"
+source_type = "Youtube"
+
+[songs.5015106c-abcb-45c2-aed3-1f28674f92f6]
+name = "Dance For Me"
+author = "Unknown"
+url = "https://www.youtube.com/watch?v=5DTSvGO_944"
+source_type = "Youtube"
+
+[songs.4e903bfb-e9bf-49dc-a299-e08949ab78f9]
+name = "DANCE"
+author = "ROMANCEPLANET"
+url = "https://www.youtube.com/watch?v=ircOfMb4gEw"
+source_type = "Youtube"
+
+[songs.35877af8-65ad-4d85-9f18-93b98fd3ebd4]
+name = "Le Son Dancefloor"
+author = "Sweely"
+url = "https://www.youtube.com/watch?v=5uEvZgmoG6Y"
+source_type = "Youtube"
+
+[songs.4c849092-b6c4-450c-b69c-304e54f63611]
+name = "Deginam"
+author = "SADBOY"
+url = "https://www.youtube.com/watch?v=w3R0Aq1EGXg"
+source_type = "Youtube"
+
+[songs.fa7d62b1-2f9a-4499-b07b-ac3127c0f69f]
+name = "Mano Skonis Sokolado (Matuze & Arnisxd Remix)"
+author = "Sokoledas"
+url = "https://soundcloud.com/matuze/sokoledas-mano-skonis-sokolado-matuze-arnisxd-remix"
+source_type = "Soundcloud"
+
+[songs.795b22ae-d19e-4571-a5d7-fba5a66d5b6e]
+name = "PLAIN WHITE TEE"
+author = "ROMANCEPLANET"
+url = "https://www.youtube.com/watch?v=tdVQbNwjGac"
+source_type = "Youtube"
+
+[songs.c7867d88-60f2-483b-b435-883d4ff5b2f9]
+name = "Alien Blues"
+author = "Vundabar"
+url = "https://open.spotify.com/track/11iIikXxC6NP0Ma8vMD27x"
+source_type = "Spotify"
+
+[songs.eadbd827-0f71-4401-97bd-d11c365c3243]
+name = "I Only See Things I Dont Have"
+author = "Fidde"
+url = "https://www.youtube.com/watch?v=vX_Ye_ZzI-Y"
+source_type = "Youtube"
+
+[songs.0c5a22fc-2a61-4171-a5b9-f24da0c71466]
+name = "As ziuriu i tave pasauli"
+author = "SEL"
+url = "https://youtu.be/_SPDjl80kKw"
+source_type = "Youtube"
+
+[songs.8bf09666-9aba-4c71-a58a-a64200d49f9e]
+name = "Cloud 9"
+author = "Beach Bunny"
+url = "https://open.spotify.com/track/6vFsBXYczYsP0H3lgunZOm"
+source_type = "Spotify"
+
+[songs.65fe7b10-2395-47f4-b920-089fe0d129ac]
+name = "Kelyje"
+author = "Kastaneda"
+url = "https://www.youtube.com/watch?v=JVE6NQqKPL4"
+source_type = "Youtube"
+
+[songs.a0bd5ffe-4be8-498e-8f43-c5a9505a4872]
+name = "Ten kur sapnai"
+author = "SEL"
+url = "https://youtu.be/GDj3NgjDbRo"
+source_type = "Youtube"
+
+[songs.2eae7db0-4263-4870-b0c0-638a0d944c8e]
+name = "Negeriau"
+author = "DJ Dalgis"
+url = "https://www.youtube.com/watch?v=c89YvG3MCcs"
+source_type = "Youtube"
+
+[songs.80a169ba-f711-408c-834c-744bac3660ca]
+name = "If Theres A Heaven I Wanna See It"
+author = "Fidde"
+url = "https://www.youtube.com/watch?v=l2Nw7cIh7qg"
+source_type = "Youtube"
+
+[songs.8e82aa7c-a9e0-42ee-a845-6ba7d3439452]
+name = "Apzavai"
+author = "Vaidas Baumila"
+url = "https://www.youtube.com/watch?v=H8uGi4ZMgHA"
+source_type = "Youtube"
+
+[songs.2c650e29-9f87-4253-8074-146f3653051d]
+name = "Satisfaction"
+author = "Benny Benassi"
+url = "https://www.youtube.com/watch?v=a0fkNdPiIL4"
+source_type = "Youtube"
+
+[songs.6dd614cd-ba76-483c-bf6d-26eb1f3b6f1a]
+name = "99 Red Balloons"
+author = "Nena"
+url = "https://open.spotify.com/track/7p8HVe22aGW1XtO1hoDHGo"
+source_type = "Spotify"
+
+[songs.c72768c0-1f2e-4992-b66f-e569c572435a]
+name = "Nezinau, Kodel..."
+author = "Unknown"
+url = "https://www.youtube.com/watch?v=A-i2CkCnPoc"
+source_type = "Youtube"
+
+[songs.ae2c4c10-4fac-4d80-a114-81adf209c6a1]
+name = "Kaip Diena"
+author = "morre"
+url = "https://www.youtube.com/watch?v=6LDgLWCQSSM"
+source_type = "Youtube"
+
+[songs.d617c8c2-6237-40c4-8b47-b612a93c3689]
+name = "vse shto nas ne Ubivaet"
+author = "Pimp Schwab"
+url = "https://www.youtube.com/watch?v=NTEXFyUE9Ww"
+source_type = "Youtube"
+
+[songs.7da79c32-9eda-4897-be69-69fb39437404]
+name = "Tricky Disco"
+author = "DR. VODKA"
+url = "https://www.youtube.com/watch?v=IknAUhl3i2o"
+source_type = "Youtube"
+
+[songs.3525668b-4402-4fb8-b106-bed44d8c9db2]
+name = "I Kluba"
+author = "Raketa"
+url = "https://www.youtube.com/watch?v=FkSjtpYN3EI"
+source_type = "Youtube"
+
+[songs.c393d1a6-b265-4a73-a7bd-d33667b08445]
+name = "Kauniete"
+author = "DJ Dalgis"
+url = "https://www.youtube.com/watch?v=b3xPE9Iyuzc"
+source_type = "Youtube"
+
+[songs.009b9458-3205-4b13-9c65-03f16fea2293]
+name = "Whiskey Cola Lietuviskai (sultys degtinele) remix"
+author = "L1GHT CASH"
+url = "https://www.youtube.com/watch?v=YVaqDaf1KXU"
+source_type = "Youtube"
+
+[songs.484b9560-731b-447d-9e7f-3d32eb68b05f]
+name = "Freed from Desire"
+author = "Drenchill"
+url = "https://www.youtube.com/watch?v=6b6FBneAENQ"
+source_type = "Youtube"
+
+[songs.9d93859c-dae3-4c03-8f8d-d4f6bc0e287e]
+name = "Jealous"
+author = "Eyedress"
+url = "https://open.spotify.com/track/1aXV8GrmQLvgoFtBPERP7E"
+source_type = "Spotify"
+
+[songs.ec3edf2d-1e19-4c71-8f5c-e53d9cd06c3b]
+name = "Cigarette Daydreams"
+author = "Cage The Elephant"
+url = "https://open.spotify.com/track/6jHvX8ZnHKC1PnrPMJ0Emt"
+source_type = "Spotify"
+
+[songs.59c20651-6ffe-493d-886b-ccd8b7fe6dee]
+name = "EVERYTHING WHAT"
+author = "Unknown"
+url = "https://www.youtube.com/watch?v=Gjdsq4kc5cA"
+source_type = "Youtube"
+
+[songs.402a9066-127a-4a62-b711-50e979abbd51]
+name = "Naktinis Tusas"
+author = "Dove"
+url = "https://www.youtube.com/watch?v=pz-HEAwFEnk"
+source_type = "Youtube"
+
+[songs.7f2b2809-1b38-45b5-876c-cbdb42b26bdf]
+name = "Volkswagina"
+author = "Depresinis"
+url = "https://www.youtube.com/watch?v=1lZR1VKsQHo"
+source_type = "Youtube"
+
+[songs.9000f22e-83ff-4261-be0c-625fe1d806ab]
+name = "MAMA MANE RODYS PER FARUS"
+author = "Unknown"
+url = "https://www.youtube.com/watch?v=F5HqXYRDZaE"
+source_type = "Youtube"
+
+[songs.c374f3c2-b498-49b3-88c5-a2e6e741e1e8]
+name = "Leisk"
+author = "SEL"
+url = "https://youtu.be/DDPmFMrffXQ"
+source_type = "Youtube"
+
+[songs.0e33d04e-776e-4746-ab17-c4602d556716]
+name = "1001 Naktis"
+author = "SADBOY"
+url = "https://www.youtube.com/watch?v=mLJIjGvWmKI"
+source_type = "Youtube"
+
+[songs.7832d70f-2af3-417f-88dd-e193622ac599]
+name = "DZIEWCZYNO Z TIKTOKA"
+author = "DR. VODKA"
+url = "https://www.youtube.com/watch?v=HLbw1WQt64o"
+source_type = "Youtube"
+
+[songs.6a00704f-6da9-44d7-8fee-75cf487036f0]
+name = "Lovoj Vezi"
+author = "Tipo grupe"
+url = "https://www.youtube.com/watch?v=M3zVMzWCy_c"
+source_type = "Youtube"
+
+[songs.7e1d6501-ec53-4513-9917-84886d7523e5]
+name = "Amerikonas grizo sunus"
+author = "Zilvinas Zvagulis"
+url = "https://www.youtube.com/watch?v=UvzJEz5ADY8"
+source_type = "Youtube"
+
+[songs.e53d24bf-930d-490a-b41b-9b3382e24e3c]
+name = "Truputi"
+author = "Ganja"
+url = "https://www.youtube.com/watch?v=Pxve7CwiCHM"
+source_type = "Youtube"
+
+[songs.eb08806a-5fe3-46d7-a989-89f6a55187cc]
+name = "Tailwhip (Lewii Edit)"
+author = "Men I Trust"
+url = "https://www.youtube.com/watch?v=XhyM-JUWwWQ"
+source_type = "Youtube"
+
+[songs.78d3766c-7d79-43a1-aa37-f680cfd253e0]
+name = "Perfect remix 1991"
+author = "Mason"
+url = "https://open.spotify.com/track/1VKWQgq0g2uKtgNfL0ceNM?si=b3f1dc504ddb41ed"
+source_type = "Spotify"
+
+[songs.47001462-76c8-4aee-8ed4-65242600f840]
+name = "Fireball"
+author = "Pitbul"
+url = "https://www.youtube.com/watch?v=HMqgVXSvwGo"
+source_type = "Youtube"
+
+[songs.3453b523-e1f0-49d3-b60b-5ba063b6c26d]
+name = "The Love Parade ( Bounce Invaderz )"
+author = "Unknown"
+url = "https://www.youtube.com/watch?v=xogT6KBWjCU3"
+source_type = "Youtube"
+
+[songs.a6a20f00-cbf9-4506-9655-d6fd27944fea]
+name = "Rhodes Trip"
+author = "THEOS"
+url = "https://www.youtube.com/watch?v=m7guRO0Uz_c"
+source_type = "Youtube"
+
+[songs.9d482849-d488-4222-adc0-727f92babcab]
+name = "Jau Nutilo Sirgaliai"
+author = "Ciulpuoneliai"
+url = "https://www.youtube.com/watch?v=s8qIVA1U0C0"
+source_type = "Youtube"
+
+[songs.6bc8cd16-722b-42e4-9824-a1de9c67029f]
+name = "Tears Drop"
+author = "Baltra"
+url = "https://www.youtube.com/watch?v=EXXMtKPfuzY"
+source_type = "Youtube"
+
+[songs.34e3c90b-7763-4154-9035-367e22ed7d75]
+name = "Robinzonas"
+author = "Vitalija Katunskyte"
+url = "https://www.youtube.com/watch?v=erDHG-QpbPY"
+source_type = "Youtube"
+
+[songs.a59cb678-f229-43cf-b438-4ce53b7264ae]
+name = "R1"
+author = "NL"
+url = "https://www.youtube.com/watch?v=hSgav4fYnZ8"
+source_type = "Youtube"
+
+[songs.7d232736-f977-4645-bd64-106e94f91bb0]
+name = "Judam Lietuvoj"
+author = "Unknown"
+url = "https://www.youtube.com/watch?v=WDzWSEgSy5U"
+source_type = "Youtube"
+
+[songs.9e6095bb-8b05-40eb-b5d3-b6410ac65171]
+name = "C'est Beau La Bourgeoisie"
+author = "Discobitch"
+url = "https://www.youtube.com/watch?v=RCeQN2aEWxo"
+source_type = "Youtube"
+
+[songs.9d7a0837-04d4-40f7-92dc-48c7db70be51]
+name = "As Judu"
+author = "Adomas Vysniauskas"
+url = "https://www.youtube.com/watch?v=dMm16TzZrjg"
+source_type = "Youtube"
+
+[songs.2f179aab-c51b-4501-b5d1-8a2e51b19a80]
+name = "Applause"
+author = "Nicolas Julian"
+url = "https://www.youtube.com/watch?v=9qHLELnq1B0"
+source_type = "Youtube"
+
+[songs.1e77552d-702f-4c61-8e9f-bd02bbafe434]
+name = "Morning Sex (Mochakk Remix)"
+author = "Ralph Castelli"
+url = "https://www.youtube.com/watch?v=6bCwJ_TIDG4"
+source_type = "Youtube"
+
+[songs.be09f147-cda5-4e5f-ac29-eef9c3a7414f]
+name = "Blue Monday"
+author = "MOGUAI, Tim Hox, Iggy"
+url = "https://open.spotify.com/track/4vAP9OY2Ci79fUCuc9EBdD"
+source_type = "Spotify"
+
+[songs.7ed02ac9-cb84-4a34-870d-56b226f0a6be]
+name = "Always Forever"
+author = "Cults"
+url = "https://open.spotify.com/track/2enPRFda84VE2wtI8c86Uf"
+source_type = "Spotify"
+
+[songs.6d78099b-042c-4b19-b3d9-15af1bf4ef10]
+name = "JUODA ORCHIDEJA"
+author = "MG INTERNATIONAL"
+url = "https://www.youtube.com/watch?v=HQvceFRBq9M"
+source_type = "Youtube"
+
+[songs.56684f28-ff1a-4f65-a585-e9ea532babb2]
+name = "AUDI"
+author = "MC ENDRAY"
+url = "https://www.youtube.com/watch?v=oIjNoMGEuRg"
+source_type = "Youtube"
+
+[songs.f1878fda-ecec-4303-a6df-dc6adf1ea3c0]
+name = "Juodas Garvezys (Remix)"
+author = "Unknown"
+url = "https://www.youtube.com/watch?v=D-7qQbXHSAw"
+source_type = "Youtube"
+
+[songs.70a80d91-581e-4599-81e6-d047f28e9068]
+name = "Margarita"
+author = "Rondo"
+url = "https://www.youtube.com/watch?v=rF4w-Rxsiv4"
+source_type = "Youtube"
+
+[songs.192a8655-7dfc-4c4b-b351-87c75cbbe048]
+name = "It Wasn't Me"
+author = "Shaggy"
+url = "https://www.youtube.com/watch?v=ssVj50ombaM"
+source_type = "Youtube"
+
+[songs.7b680328-e576-4185-a5da-3c37de98ca42]
+name = "cabin fever"
+author = "re6ce"
+url = "https://open.spotify.com/track/3c3oUMxOr2cVFBY6V3v08C"
+source_type = "Spotify"
+
+[songs.383cb60f-13fd-4947-9924-6c5d8c39d453]
+name = "Kur Tu"
+author = "Patruliai"
+url = "https://www.youtube.com/watch?v=OPWhiu3cvj0"
+source_type = "Youtube"
+
+[songs.bcb9af16-7bdb-458a-b092-42225abf2645]
+name = "LEDUKAI"
+author = "Depresinis feat. Deivas"
+url = "https://www.youtube.com/watch?v=R2-MtpkKgGI"
+source_type = "Youtube"
+
+[songs.50186034-e8b4-4778-81e3-ab327e54dbff]
+name = "We Are The People"
+author = "Empire Of The Sun, southstar"
+url = "https://www.youtube.com/watch?v=qguEGR5BK2k"
+source_type = "Youtube"
+
+[songs.080d60fb-bdf1-40f3-8b81-967c5ea2fe44]
+name = "moi marmeladni (paprobui mua mua)"
+author = "Katja lel"
+url = "https://www.youtube.com/watch?v=0JMdXFHo5SY"
+source_type = "Youtube"
+
+[songs.4a4abe26-de77-4e0f-83c9-8eab9ca32281]
+name = "Parnesk alaus OG"
+author = "Unknown"
+url = "https://www.youtube.com/watch?v=e7cB1JIlZ2k"
+source_type = "Youtube"
+
+[songs.017e1086-7ca7-4788-834d-5490e0cf8276]
+name = "Mersas"
+author = "Tweaxx"
+url = "https://www.youtube.com/watch?v=7ljAzgALPdA"
+source_type = "Youtube"
+
+[songs.a1ab9370-70bc-488d-a78b-5cdc09c82f73]
+name = "Trys Trys Trys"
+author = "Riaukenzo"
+url = "https://www.youtube.com/watch?v=qJv6GRQCnCk"
+source_type = "Youtube"
+
+[songs.0e969066-b7f9-4e60-a24f-989aad28dfac]
+name = "Svajoklis"
+author = "Vairas"
+url = "https://www.youtube.com/watch?v=hs8_KTV0Vrw"
+source_type = "Youtube"
+
+[songs.8c641f7f-e151-4abc-932c-a9895823a278]
+name = "Nevaidink"
+author = "SixthBoi"
+url = "https://www.youtube.com/watch?v=nOTNnnrqTII"
+source_type = "Youtube"
+
+[songs.cbc90bff-5476-408c-9cc8-afd1af5b536d]
+name = "Vienna"
+author = "Billy Joel"
+url = "https://open.spotify.com/track/4U45aEWtQhrm8A5mxPaFZ7"
+source_type = "Spotify"
+
+[songs.d01635a4-1a28-4d86-a524-9876eb3094de]
+name = "VAIKAI PO LELIJOM (REMIX)"
+author = "Unknown"
+url = "https://www.youtube.com/watch?v=k1amBbsAZuo"
+source_type = "Youtube"
+
+[songs.d286a3a7-ab7b-488c-9c68-b7521e290c5e]
+name = "Bossa Nova (Lewii Edit)"
+author = "Billie Eilish"
+url = "https://www.youtube.com/watch?v=gNawHj2NCxA"
+source_type = "Youtube"
+
+[songs.0c77785e-7149-45a7-84a1-a9c8ccaa9f7f]
+name = "Lyja"
+author = "SEL"
+url = "https://www.youtube.com/watch?v=KL6JBWnXCxw"
+source_type = "Youtube"
+
+[songs.3ea2a490-749f-4956-b7ef-bb0c428663a9]
+name = "Dabar Geriausi Musu Vakarai"
+author = "Vilija ir Marijonas mikutavicius"
+url = "https://www.youtube.com/watch?v=MPnZkEscWo0"
+source_type = "Youtube"
+
+[songs.8fcad64e-bb63-414b-9cad-243f1f4c4293]
+name = "FALL FROM THE SKY"
+author = "ROMANCEPLANET"
+url = "https://www.youtube.com/watch?v=HMhzxzXBisw"
+source_type = "Youtube"
+
+[songs.88fe04aa-a7be-4f6c-b32f-d3d25e58bbf8]
+name = "Because I Got High"
+author = "Afroman"
+url = "https://www.youtube.com/watch?v=WeYsTmIzjkw"
+source_type = "Youtube"
+
+[songs.d8423579-1225-4865-9955-149e6878b59d]
+name = "Black Sabbath"
+author = "Unknown"
+url = "https://www.youtube.com/watch?v=BOTIIw76qiE"
+source_type = "Youtube"
+
+[songs.b23f9b7f-1668-43f0-a1bd-9471759db18f]
+name = "Juda Tavo rankos"
+author = "Elektra"
+url = "https://www.youtube.com/watch?v=k2RuDoudnOE"
+source_type = "Youtube"
+
+[songs.615d63ee-51aa-4ac2-b826-9a41f59b8622]
+name = "I Love It"
+author = "Icona Pop"
+url = "https://www.youtube.com/watch?v=UxxajLWwzqY"
+source_type = "Youtube"
+
+[songs.41a8d7c8-4b75-46f6-9940-7f0b8f5f5feb]
+name = "Beggin' (Techno)"
+author = "Unknown"
+url = "https://www.youtube.com/watch?v=tXPs1FwW6lk"
+source_type = "Youtube"
+
+[songs.bf529d54-1ad0-4396-84c7-82bd7fe495c6]
+name = "P.R.O.T.E.C.T.T.R.A.N.S.K.I.D.S."
+author = "Cheap Dirty Horse"
+url = "https://open.spotify.com/track/1ytHY16pP1P6A2noeiPXuU"
+source_type = "Spotify"
+
+[songs.83ff90a9-4a19-4a66-a452-c2bafdfb884b]
+name = "You're My Heart, You're My Soul"
+author = "Modern Talking"
+url = "https://www.youtube.com/watch?v=4kHl4FoK1Ys"
+source_type = "Youtube"
+
+[songs.ee1e8d12-75ac-43f7-bf85-a1b69eb66e86]
+name = "Vanished"
+author = "Crystal Castles"
+url = "https://open.spotify.com/track/3gGMsx3jxYKfhXq8LMj1tz"
+source_type = "Spotify"
+
+[songs.83808446-bd43-4140-86ff-853fea897a89]
+name = "Bla Bla Bla"
+author = "Gigi D'Agostino"
+url = "https://www.youtube.com/watch?v=yKapqR2XRJE"
+source_type = "Youtube"
+
+[songs.a950fac9-3b5f-4215-8a49-7680fd5b90bf]
+name = "Gyvenu"
+author = "69 Danguje"
+url = "https://www.youtube.com/watch?v=HMXlhmAoux4"
+source_type = "Youtube"
+
+[songs.b0732ead-9a8c-4537-b98a-a4618d33959f]
+name = "O Mazuti"
+author = "Depresinis, Jypas"
+url = "https://www.youtube.com/watch?v=4t_DPbe2r3M"
+source_type = "Youtube"
+
+[songs.101166d0-1b7c-4d1c-b0ce-35b9a89b0822]
+name = "Nes as tik vejas"
+author = "SEL"
+url = "https://youtu.be/acMG37IZ6z8"
+source_type = "Youtube"
+
+[songs.2645c0f2-fade-489a-87ad-261d5d983ef6]
+name = "Bad Boys"
+author = "Unknown"
+url = "https://www.youtube.com/watch?v=NTC7RD8xzCY"
+source_type = "Youtube"
+
+[songs.6bfe21f8-b6e5-409b-b6cb-a5692f6dfeeb]
+name = "Welcome To The Club"
+author = "Manian"
+url = "https://www.youtube.com/watch?v=7F9xDewtgNA"
+source_type = "Youtube"
+
+[songs.8dca8d6c-3388-4133-b773-79b9f076e467]
+name = "0,7"
+author = "Depresinis feat. Deivas"
+url = "https://www.youtube.com/watch?v=rjwFjBgTzAA"
+source_type = "Youtube"
+
+[songs.afdc69fa-c2ee-4dee-9794-e3166fc9dd20]
+name = "Pedro (Jaxomy & Agatino Romero Remix)"
+author = "Raffaella Carra"
+url = "https://www.youtube.com/watch?v=AIGnRc7F86Q"
+source_type = "Youtube"
+
+[songs.47f2a63e-e9eb-48df-a61b-dbcdfa3aaf35]
+name = "Everlong"
+author = "Foo Fighters"
+url = "https://open.spotify.com/track/5UWwZ5lm5PKu6eKsHAGxOk"
+source_type = "Spotify"
+
+[songs.cec368bd-6b4c-4c89-9eeb-1b88bed0efc3]
+name = "PARIS (TECHNO)"
+author = "$UICIDEBOY$"
+url = "https://www.youtube.com/watch?v=YYDJmGDkEyw"
+source_type = "Youtube"
+
+[songs.83b50be2-c286-4dcc-9e7f-cacf5e4031fe]
+name = "Not Fair"
+author = "Lily Allen"
+url = "https://www.youtube.com/watch?v=WON_YIbeLis"
+source_type = "Youtube"
+
+[songs.6f28b04a-588e-4a81-8b4e-aa4ed4f88e31]
+name = "Pavasaris"
+author = "Depresinis feat. Deivas"
+url = "https://www.youtube.com/watch?v=yWWAucfQdN4"
+source_type = "Youtube"
+
+[songs.20945b3f-e9d6-4023-9a97-4a04424cc8a3]
+name = "Basket Case"
+author = "Green Day"
+url = "https://www.youtube.com/watch?v=wZ8eZRxFA-0"
+source_type = "Youtube"
+
+[playlists.ba68f47d-384d-4808-a69d-48c6a85be479]
+name = "electronic"
+author = "Unknown"
+songs = [
+ "81426b49-3511-4e48-be21-3c24acba0fb9",
+ "2c650e29-9f87-4253-8074-146f3653051d",
+ "9ef33d55-972d-490c-95c6-bf046bf4f979",
+]
+
+[playlists.8e806ac3-63d9-4366-95d6-04f5a0aae31e]
+name = "rusiskos"
+author = "Unknown"
+songs = [
+ "76f7945f-e677-4297-a98a-708314f92de8",
+ "0491fd9f-149d-4e70-bc5c-0fae9a21d9b0",
+ "4a7b1853-7529-498d-addf-25e894a37ccf",
+ "6c3f2503-0160-4c42-8b17-25ff402f3fa7",
+ "3b2573d4-2e1b-4446-a377-5fcb2b0a70c5",
+ "03f406b5-d5e9-4d22-b9bc-d0b36354400a",
+ "85098bbc-86fa-4da4-b342-d2a512738060",
+ "d617c8c2-6237-40c4-8b47-b612a93c3689",
+ "080d60fb-bdf1-40f3-8b81-967c5ea2fe44",
+]
+
+[playlists.27afdb2f-2672-43f1-8a55-b7c6bbb7927a]
+name = "lietuviskos"
+author = "Unknown"
+songs = [
+ "8e8c3f6c-e34b-4f4e-b9be-f36be5901435",
+ "7d232736-f977-4645-bd64-106e94f91bb0",
+ "ae2c4c10-4fac-4d80-a114-81adf209c6a1",
+ "34e3c90b-7763-4154-9035-367e22ed7d75",
+ "858d8645-8637-4d3f-b4b7-c9c75743d90a",
+ "a1ab9370-70bc-488d-a78b-5cdc09c82f73",
+ "8858c7a1-ea6d-409b-9fc3-7256a39d4d8b",
+ "009b9458-3205-4b13-9c65-03f16fea2293",
+ "8dca8d6c-3388-4133-b773-79b9f076e467",
+ "9be83d96-6dfb-47a0-b58e-a1aed1cf6031",
+ "0e969066-b7f9-4e60-a24f-989aad28dfac",
+ "9d482849-d488-4222-adc0-727f92babcab",
+ "6d78099b-042c-4b19-b3d9-15af1bf4ef10",
+ "bd85a9d7-1fb6-4154-a290-e5137590e419",
+ "131ab436-869b-4b3b-8ce4-3b5087d3461f",
+ "1a22e35a-0af1-4326-b59c-00d59504446b",
+ "bcb9af16-7bdb-458a-b092-42225abf2645",
+ "3525668b-4402-4fb8-b106-bed44d8c9db2",
+ "4c849092-b6c4-450c-b69c-304e54f63611",
+ "8c641f7f-e151-4abc-932c-a9895823a278",
+ "c393d1a6-b265-4a73-a7bd-d33667b08445",
+ "ea41c37c-7b62-4fd6-9fec-650e94d184f2",
+ "8e82aa7c-a9e0-42ee-a845-6ba7d3439452",
+ "9000f22e-83ff-4261-be0c-625fe1d806ab",
+ "402a9066-127a-4a62-b711-50e979abbd51",
+ "4b61344d-fa89-4b79-bd45-3b1fc8c8cc1c",
+ "7e1d6501-ec53-4513-9917-84886d7523e5",
+ "9fabb799-f6aa-45a0-93b7-8cb08ab8f0a8",
+ "ca765223-e4e6-4b5d-8928-919c3f491b43",
+ "16dafb40-ebac-4e04-961a-99d79789f1a3",
+ "95a3350c-c62f-4543-8cfe-6c512861b229",
+ "720d5b94-ac54-4162-8da7-ba8d258139e6",
+ "c28f14ca-ec10-469a-b0cc-3e26e9f56007",
+ "6a00704f-6da9-44d7-8fee-75cf487036f0",
+ "4eafd817-d75a-451e-b6b4-38ef29e66fdf",
+ "b0732ead-9a8c-4537-b98a-a4618d33959f",
+ "2b33d0b1-aecb-4ff9-841a-3a7b2861f28d",
+ "2374179d-d6a8-41b9-9cac-21147303fc8e",
+ "f1878fda-ecec-4303-a6df-dc6adf1ea3c0",
+ "7f2b2809-1b38-45b5-876c-cbdb42b26bdf",
+ "65fe7b10-2395-47f4-b920-089fe0d129ac",
+ "dfc0782c-33f2-414d-9b56-76a77902bcc5",
+ "b23f9b7f-1668-43f0-a1bd-9471759db18f",
+ "0e33d04e-776e-4746-ab17-c4602d556716",
+ "0b907d17-e3f9-4dbb-9c2d-e10327316f6e",
+ "fa339b0b-8d3a-45e7-aed9-f744afb4a666",
+ "eb91fc63-4e0e-49e9-99c7-ea7fe5770a5b",
+ "4cf5e323-5e79-4471-816a-5fa345854cea",
+ "d01635a4-1a28-4d86-a524-9876eb3094de",
+ "017e1086-7ca7-4788-834d-5490e0cf8276",
+ "c332ce2b-6b1c-4979-9977-dc00aa6e710b",
+ "d71c0e55-5d40-4045-853e-b32da713c0b8",
+ "70a80d91-581e-4599-81e6-d047f28e9068",
+ "3ea2a490-749f-4956-b7ef-bb0c428663a9",
+ "4f368ef5-08d0-4d1f-b8ba-a34ee9fc6aa2",
+ "870502f1-e3a9-419b-9ba6-d8100d1198a5",
+ "75c6e1aa-21c7-4626-9def-602d656282bc",
+ "a59cb678-f229-43cf-b438-4ce53b7264ae",
+ "a950fac9-3b5f-4215-8a49-7680fd5b90bf",
+ "9d7a0837-04d4-40f7-92dc-48c7db70be51",
+ "e9495391-8a6c-4942-9dc6-09b764f9d1e9",
+ "2eae7db0-4263-4870-b0c0-638a0d944c8e",
+ "6f28b04a-588e-4a81-8b4e-aa4ed4f88e31",
+ "05a28184-e58e-42c3-a803-512bb0817a07",
+ "457bc1f5-f804-4665-a595-8e56a6558d1d",
+ "4a4abe26-de77-4e0f-83c9-8eab9ca32281",
+ "dc6c36b0-f595-457f-8fc3-641710b0e3b9",
+ "0e1d36ed-d07d-48c0-a101-1bf1ac6d7a26",
+ "56684f28-ff1a-4f65-a585-e9ea532babb2",
+ "73ddd372-4853-4182-b212-b021e7dc8988",
+ "b26f7338-0d1f-4dba-b639-3bdae9d7e10e",
+ "639dc5af-b3e6-4d38-a003-80deac01d326",
+ "e53d24bf-930d-490a-b41b-9b3382e24e3c",
+ "02d90830-7b5f-430f-b82c-f45998862e81",
+ "407155fd-3174-4778-ae93-bb01b7518f9f",
+ "df2e369f-2fee-49e5-b52c-33f73c34ffb2",
+ "27d61e87-deec-4e4c-8015-ad8b691eea40",
+ "383cb60f-13fd-4947-9924-6c5d8c39d453",
+ "c72768c0-1f2e-4992-b66f-e569c572435a",
+ "eaf1cc63-3eb6-4d86-a986-e8129fff7fcb",
+]
+
+[playlists.e4203363-48dd-430c-8a63-f997ebe7e2bc]
+name = "hip-hop"
+author = "Unknown"
+songs = [
+ "88fe04aa-a7be-4f6c-b32f-d3d25e58bbf8",
+ "484b9560-731b-447d-9e7f-3d32eb68b05f",
+]
+
+[playlists.a906d80f-2028-407e-8263-6eacb53325a2]
+name = "lietuviskos/rave"
+author = "Unknown"
+songs = [
+ "2b19ae9f-2321-41fc-8a1f-2bb4c2975380",
+ "fa7d62b1-2f9a-4499-b07b-ac3127c0f69f",
+]
+
+[playlists.b633b0c7-8151-4b5c-bf18-a0b8d2f85f27]
+name = "Ours <3"
+author = "Unknown"
+songs = [
+ "e71a61d4-b54a-47d1-b0ad-db6695bd0739",
+ "bf529d54-1ad0-4396-84c7-82bd7fe495c6",
+ "7e32803d-a5a9-453c-8c81-b479c51e5bc9",
+ "6dd614cd-ba76-483c-bf6d-26eb1f3b6f1a",
+ "9866acdd-97ee-4e19-ba20-a64caa9b02ee",
+ "ee1e8d12-75ac-43f7-bf85-a1b69eb66e86",
+ "5c2ee27e-d564-404a-8259-87d1d336f106",
+ "f5a31a44-fc62-4cce-b65e-05e4085864fe",
+ "7b680328-e576-4185-a5da-3c37de98ca42",
+ "877c24e4-c0c0-4e9c-a553-221e381c8a00",
+ "d563619a-344e-41cf-99d1-5a52214ada49",
+ "92561031-92ae-471a-b154-8519c715011d",
+ "cd72e9db-aaa4-485e-b3a5-7fbeed2cff36",
+ "9d93859c-dae3-4c03-8f8d-d4f6bc0e287e",
+ "bb16708f-0cd7-4918-8014-d374d68acbd2",
+ "be09f147-cda5-4e5f-ac29-eef9c3a7414f",
+ "7ed02ac9-cb84-4a34-870d-56b226f0a6be",
+ "47f2a63e-e9eb-48df-a61b-dbcdfa3aaf35",
+ "e37c6cb6-ad8e-43a7-83d1-8b795db8aa1c",
+ "3d543d58-e33d-4158-9956-049309baa35c",
+ "2e9ef729-1560-446c-af6a-8a9d4c43ac9a",
+ "ec3edf2d-1e19-4c71-8f5c-e53d9cd06c3b",
+ "c7867d88-60f2-483b-b435-883d4ff5b2f9",
+ "cbc90bff-5476-408c-9cc8-afd1af5b536d",
+ "2bd2e64e-28f0-4bd7-aee3-e1d27ed0797d",
+ "08995ab0-2954-4d6b-913e-27609e90b354",
+ "862ddd87-68c4-4bed-a8cd-0e2021a0ce79",
+ "0679475a-259c-4c98-9ae7-a58c10dfeedb",
+ "6895e921-91f2-4713-b604-c2cdd9f3f1c2",
+ "8bf09666-9aba-4c71-a58a-a64200d49f9e",
+]
+
+[playlists.2391911d-2d2c-4c33-b8f5-1a6e543512cc]
+name = "noclue"
+author = "Unknown"
+songs = [
+ "9e6095bb-8b05-40eb-b5d3-b6410ac65171",
+ "7832d70f-2af3-417f-88dd-e193622ac599",
+ "518e23e8-72df-42de-a918-6a9a197deb9a",
+ "2645c0f2-fade-489a-87ad-261d5d983ef6",
+]
+
+[playlists.c3ebcdd5-82b6-49b3-a340-dc786a66eee6]
+name = "reggea"
+author = "Unknown"
+songs = ["192a8655-7dfc-4c4b-b351-87c75cbbe048"]
+
+[playlists.5f2e4043-cb32-4e90-9fb6-69f0466d6398]
+name = "house"
+author = "Unknown"
+songs = [
+ "80a169ba-f711-408c-834c-744bac3660ca",
+ "b0c0d44c-86ca-4b80-a081-734a4178c870",
+ "eadbd827-0f71-4401-97bd-d11c365c3243",
+ "eb08806a-5fe3-46d7-a989-89f6a55187cc",
+ "6bc8cd16-722b-42e4-9824-a1de9c67029f",
+ "1e77552d-702f-4c61-8e9f-bd02bbafe434",
+ "35877af8-65ad-4d85-9f18-93b98fd3ebd4",
+ "a6a20f00-cbf9-4506-9655-d6fd27944fea",
+ "d286a3a7-ab7b-488c-9c68-b7521e290c5e",
+ "f81f8f9d-6c56-4ae3-8f5f-28c04632d3b5",
+]
+
+[playlists.8c9d2cf1-6f0b-4ea9-993a-c3193867a914]
+name = "rave"
+author = "Unknown"
+songs = [
+ "59c20651-6ffe-493d-886b-ccd8b7fe6dee",
+ "78d3766c-7d79-43a1-aa37-f680cfd253e0",
+ "7da79c32-9eda-4897-be69-69fb39437404",
+ "0ee56450-2942-4805-9f08-b49652472258",
+]
+
+[playlists.7d7c0020-7daa-4d85-8aa9-311f24eeb702]
+name = "lietuviskos/sel"
+author = "Unknown"
+songs = [
+ "60214f0e-9a37-49b4-92bf-b7b548e501ad",
+ "e1cb8b00-713b-4a0e-bc61-dbf2d2596fcf",
+ "abd6a036-f17a-4a6a-8420-2cc3e7e4a181",
+ "a0bd5ffe-4be8-498e-8f43-c5a9505a4872",
+ "201c6680-7dcf-4d75-8bdc-76ff9a715167",
+ "18c2b9bc-6403-4a67-94bd-1e9f5413d83b",
+ "c374f3c2-b498-49b3-88c5-a2e6e741e1e8",
+ "0c5a22fc-2a61-4171-a5b9-f24da0c71466",
+ "0c77785e-7149-45a7-84a1-a9c8ccaa9f7f",
+ "98b04764-39cc-4c82-a2e2-420dd85573ca",
+ "101166d0-1b7c-4d1c-b0ce-35b9a89b0822",
+]
+
+[playlists.4230f218-7de4-463c-a42f-ad05c745de5a]
+name = "techno"
+author = "Unknown"
+songs = [
+ "3453b523-e1f0-49d3-b60b-5ba063b6c26d",
+ "6bfe21f8-b6e5-409b-b6cb-a5692f6dfeeb",
+ "41a8d7c8-4b75-46f6-9940-7f0b8f5f5feb",
+ "fc3f499b-2c33-41d0-b79c-ca68486b2a4d",
+ "22f18d88-fb39-4cec-98be-02d4ebe3a6ee",
+ "4a56c71c-70f0-45e7-a9b8-2304d41e1877",
+ "5015106c-abcb-45c2-aed3-1f28674f92f6",
+ "2f179aab-c51b-4501-b5d1-8a2e51b19a80",
+ "83b50be2-c286-4dcc-9e7f-cacf5e4031fe",
+ "22565196-5757-4767-8f1f-f1c9b8a87dcc",
+ "83808446-bd43-4140-86ff-853fea897a89",
+ "50186034-e8b4-4778-81e3-ab327e54dbff",
+ "afdc69fa-c2ee-4dee-9794-e3166fc9dd20",
+ "91629850-33e2-4e18-8c37-6eafdfcde3f4",
+ "d8e93863-6102-4cda-a0b2-3728d20c9cf8",
+ "cec368bd-6b4c-4c89-9eeb-1b88bed0efc3",
+]
+
+[playlists.a37e3c7c-584d-4803-981b-5be9244dd7c1]
+name = "rock"
+author = "Unknown"
+songs = ["d8423579-1225-4865-9955-149e6878b59d"]
+
+[playlists.cb15d471-58bc-4d5a-9e3c-b13e1433818c]
+name = "pop"
+author = "Unknown"
+songs = [
+ "83ff90a9-4a19-4a66-a452-c2bafdfb884b",
+ "f3edb436-bf04-4ad5-ad45-684a601846c3",
+ "20945b3f-e9d6-4023-9a97-4a04424cc8a3",
+ "90ae639e-0214-4a8e-b582-4949674ffaa2",
+ "615d63ee-51aa-4ac2-b826-9a41f59b8622",
+ "190a5577-124a-4232-9e6f-1d0a50f4d881",
+ "47001462-76c8-4aee-8ed4-65242600f840",
+]
+
+[playlists.ff11b5bc-6bf7-4cdd-a47a-023dcc68a8c3]
+name = "alt"
+author = "Unknown"
+songs = [
+ "4e903bfb-e9bf-49dc-a299-e08949ab78f9",
+ "bc5fe406-040b-4c46-82ce-bb4505b81947",
+ "8fcad64e-bb63-414b-9cad-243f1f4c4293",
+ "795b22ae-d19e-4571-a5d7-fba5a66d5b6e",
+]
diff --git a/xmpd-cache/src/downloader/song.rs b/xmpd-cache/src/downloader/song.rs
index d2b5627..11f7cee 100644
--- a/xmpd-cache/src/downloader/song.rs
+++ b/xmpd-cache/src/downloader/song.rs
@@ -108,11 +108,12 @@ impl SongCacheDl {
}
let mut from = song_p.clone();
from.pop();
- from.push("{song_format}.{song_format}");
+ from.push(format!("{song_format}.{song_format}"));
let mut to = song_p.clone();
to.pop();
to.set_extension(&song_format);
+ log::debug!("from: {from:?} to: {to:?}");
std::fs::copy(&from, &to).unwrap();
from.pop();
std::fs::remove_dir_all(from).unwrap();
diff --git a/xmpd-cache/src/lib.rs b/xmpd-cache/src/lib.rs
index 91e7bc1..ed3774d 100644
--- a/xmpd-cache/src/lib.rs
+++ b/xmpd-cache/src/lib.rs
@@ -1,6 +1,5 @@
use std::{collections::HashMap, str::FromStr, sync::{mpsc::{self, Receiver, Sender}, Arc, Mutex, MutexGuard}, time::Duration};
use downloader::song::SongStatus;
-use log::warn;
use xmpd_manifest::song::Song;
pub mod downloader;
@@ -106,7 +105,6 @@ fn start_cache_mv_thread(tx: Sender) {
song_p.push("songs");
song_p.push(sid.clone().to_string());
let song_p = song_p.with_extension(&song_format);
- log::debug!("Found done: {:?}: {}", song_p, song_p.exists());
if song_p.exists() {
let _ = tx.send(Message::DownloadDone(sid.clone()));
cache.song_cache.insert(sid.clone(), DlStatus::Done(song_p));
diff --git a/xmpd-core/Cargo.toml b/xmpd-core/Cargo.toml
index ec37421..b15e1d4 100644
--- a/xmpd-core/Cargo.toml
+++ b/xmpd-core/Cargo.toml
@@ -30,3 +30,6 @@ camino.workspace = true
anyhow.workspace = true
log.workspace = true
env_logger.workspace = true
+
+[build-dependencies]
+winresource.workspace = true
diff --git a/xmpd-core/build.rs b/xmpd-core/build.rs
new file mode 100644
index 0000000..bfe89d0
--- /dev/null
+++ b/xmpd-core/build.rs
@@ -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(())
+}
diff --git a/xmpd-gui/Cargo.toml b/xmpd-gui/Cargo.toml
index 7d43d71..8e7a187 100644
--- a/xmpd-gui/Cargo.toml
+++ b/xmpd-gui/Cargo.toml
@@ -22,13 +22,14 @@ xmpd-manifest.path = "../xmpd-manifest"
xmpd-settings.path = "../xmpd-settings"
xmpd-cliargs.path = "../xmpd-cliargs"
xmpd-cache.path = "../xmpd-cache"
+xmpd-player.path = "../xmpd-player"
egui.workspace = true
eframe.workspace = true
-tokio.workspace = true
anyhow.workspace = true
lazy_static.workspace = true
log.workspace = true
egui_extras.workspace = true
-egui-aesthetix = "0.2.4"
uuid.workspace = true
camino.workspace = true
+rfd.workspace = true
+dirs.workspace = true
diff --git a/xmpd-gui/src/components/left_nav.rs b/xmpd-gui/src/components/left_nav.rs
index 11d1b63..824c13e 100644
--- a/xmpd-gui/src/components/left_nav.rs
+++ b/xmpd-gui/src/components/left_nav.rs
@@ -1,4 +1,4 @@
-use egui::RichText;
+use egui::{CursorIcon, RichText, Sense};
use xmpd_manifest::store::BaseStore;
use super::{CompGetter, CompUi};
@@ -50,6 +50,7 @@ fn add_playlist_tab(ui: &mut egui::Ui, pid: &Option, title: &str, au
ui.add(
egui::Image::new(crate::data::NOTE_ICON)
.tint(theme.accent_color)
+ .sense(Sense::click())
.fit_to_exact_size(egui::Vec2::new(32.0, 32.0))
);
ui.vertical(|ui| {
@@ -83,9 +84,13 @@ fn add_playlist_tab(ui: &mut egui::Ui, pid: &Option, title: &str, au
});
}).response.rect;
- if ui.interact(wdg_rect, format!("left_nav_playlist_{pid:?}").into(), egui::Sense::click()).clicked() {
+ let blob = ui.interact(wdg_rect, format!("left_nav_playlist_{pid:?}").into(), egui::Sense::click());
+ if blob.clicked() {
handle_error_ui!(LeftNav::get()).selected_playlist_id = pid.clone();
}
+ if blob.hovered() {
+ ui.output_mut(|o| o.cursor_icon = CursorIcon::PointingHand);
+ }
ui.separator();
}
diff --git a/xmpd-gui/src/components/player.rs b/xmpd-gui/src/components/player.rs
index b3832ac..bfd97ec 100644
--- a/xmpd-gui/src/components/player.rs
+++ b/xmpd-gui/src/components/player.rs
@@ -1,19 +1,30 @@
use egui::{Sense, Stroke, Vec2};
-use super::{CompGetter, CompUi};
+use super::{song_list::SongList, CompGetter, CompUi};
-#[derive(Debug, Default)]
+#[derive(Debug)]
pub struct Player {
slider_progress: usize,
- is_playing: bool,
+ old_slider_progress: usize,
+ volume_slider: f64,
+}
+
+impl Default for Player {
+ fn default() -> Self {
+ Self {
+ volume_slider: 1.0,
+ old_slider_progress: 0,
+ slider_progress: 0
+ }
+ }
}
component_register!(Player);
impl CompUi for Player {
- fn draw(ui: &mut egui::Ui, _: &mut crate::GuiState) -> crate::Result<()> {
+ fn draw(ui: &mut egui::Ui, state: &mut crate::GuiState) -> crate::Result<()> {
let theme = xmpd_settings::Settings::get()?.theme.clone();
let avail = ui.available_size();
ui.vertical_centered_justified(|ui| {
@@ -21,10 +32,7 @@ impl CompUi for Player {
ui.horizontal(|ui| {
{
ui.add_space(avail.x * 0.05 / 2.0);
- let mut slf = handle_error_ui!(Player::get());
- let slider = egui::Slider::new(&mut slf.slider_progress, 0..=100)
- .show_value(false);
- ui.style_mut().spacing.slider_width = avail.x * 0.90;
+ ui.style_mut().spacing.slider_width = avail.x * 0.87;
let s = Stroke {
color: theme.accent_color,
width: 2.0
@@ -32,19 +40,35 @@ impl CompUi for Player {
ui.style_mut().visuals.widgets.inactive.fg_stroke = s;
ui.style_mut().visuals.widgets.active.fg_stroke = s;
ui.style_mut().visuals.widgets.hovered.fg_stroke = s;
- ui.add(slider);
- ui.label("00:00");
+
+ let mut slf = handle_error_ui!(Player::get());
+ ui.add(
+ egui::Slider::new(&mut slf.slider_progress, 0..=100)
+ .show_value(false)
+ );
+ if slf.slider_progress == slf.old_slider_progress {
+ slf.slider_progress = (state.player.get_played_f() * 100.0) as usize;
+ slf.old_slider_progress = slf.slider_progress;
+ } else {
+ handle_error_ui!(state.player.seek_to_f(slf.slider_progress as f64 / 100.0 ));
+ slf.old_slider_progress = slf.slider_progress;
+ }
+ let secs_left = state.player.get_ms_left() as f64 / 1000.0;
+ let h = (secs_left/60.0/60.0).floor();
+ let m = ((secs_left - h * 60.0)/60.0).floor();
+ let s = (secs_left - m * 60.0).floor();
+
+ ui.label(format!("{h:02}:{m:02}:{s:02}"));
}
});
ui.horizontal(|ui| {
ui.add_space((avail.x / 2.0) - 16.0 - 8.0 - ui.spacing().item_spacing.x);
- let pp = if handle_error_ui!(Player::get()).is_playing {
- crate::data::PAUSE_ICON
- } else {
+ let pp = if state.player.is_paused() {
crate::data::PLAY_ICON
+ } else {
+ crate::data::PAUSE_ICON
};
-
let prev = egui::Image::new(crate::data::PREV_ICON)
.tint(theme.accent_color)
.sense(Sense::click())
@@ -57,12 +81,43 @@ impl CompUi for Player {
.tint(theme.accent_color)
.sense(Sense::click())
.max_size(Vec2::new(16.0, 16.0));
- if ui.add(prev).clicked() {}
- if ui.add(pp).clicked() {
- let mut slf = handle_error_ui!(Player::get());
- slf.is_playing = !slf.is_playing;
+ if ui.add(prev).clicked() {
+ handle_error_ui!(handle_error_ui!(SongList::get()).play_prev(state));
}
- if ui.add(next).clicked() {}
+ if ui.add(pp).clicked() {
+ if state.player.is_paused() {
+ state.player.play();
+ } else {
+ state.player.pause();
+ }
+ }
+ if ui.add(next).clicked() || state.player.just_stopped() {
+ handle_error_ui!(handle_error_ui!(SongList::get()).play_next(state));
+ }
+
+
+
+ ui.with_layout(egui::Layout::right_to_left(egui::Align::RIGHT), |ui| {
+ ui.add_space(15.0);
+ ui.style_mut().spacing.slider_width = avail.x * 0.15;
+ let s = Stroke {
+ color: theme.accent_color,
+ width: 1.0
+ };
+ ui.style_mut().visuals.widgets.inactive.fg_stroke = s;
+ ui.style_mut().visuals.widgets.active.fg_stroke = s;
+ ui.style_mut().visuals.widgets.hovered.fg_stroke = s;
+
+ let mut slf = handle_error_ui!(Player::get());
+ let slider =ui.add(
+ egui::Slider::new(&mut slf.volume_slider, 0.0..=1.0)
+ .show_value(false)
+ );
+
+ if slider.changed() {
+ state.player.set_volume(slf.volume_slider);
+ }
+ });
});
ui.add_space(3.0);
});
diff --git a/xmpd-gui/src/components/song_list/mod.rs b/xmpd-gui/src/components/song_list/mod.rs
index d4842d8..0c4e158 100644
--- a/xmpd-gui/src/components/song_list/mod.rs
+++ b/xmpd-gui/src/components/song_list/mod.rs
@@ -1,6 +1,4 @@
-use std::fmt::write;
-
-use egui::{Color32, RichText, Sense, Vec2};
+use egui::{Color32, CursorIcon, RichText, Sense, Vec2};
use song_list_nav::SearchType;
use xmpd_cache::DlStatus;
use xmpd_manifest::{song::Song, store::BaseStore};
@@ -10,171 +8,264 @@ pub mod song_list_nav;
#[derive(Debug, Default)]
pub struct SongList {
- selected_song_id: uuid::Uuid
+ selected_sid: uuid::Uuid,
+ playable_songs: Vec,
}
component_register!(SongList);
impl CompUi for SongList {
fn draw(ui: &mut egui::Ui, state: &mut crate::GuiState) -> crate::Result<()> {
+ let songs = Self::get_and_sort_songs(state)?;
+ let disp_songs = Self::get_songs_to_display(&songs)?;
+ {
+ let mut sl = SongList::get()?;
+ sl.playable_songs = Self::get_playable_songs(&songs)?;
+ if let Some((sid, _)) = songs.first() {
+ if sl.selected_sid == Default::default() {
+ sl.selected_sid = sid.clone();
+ }
+ }
+ }
egui::ScrollArea::vertical()
.id_source("song_list")
.drag_to_scroll(false)
.show(ui, |ui| {
ui.vertical(|ui| {
ui.add_space(3.0);
- let pid = {handle_error_ui!(super::left_nav::LeftNav::get()).selected_playlist_id.clone()};
- match pid {
- None => {
- let mut songs: Vec<_> = state.manifest.store().get_songs().iter().collect();
- songs.sort_by(|a, b| {
- let a = a.1.name().to_lowercase();
- let b = b.1.name().to_lowercase();
- a.cmp(&b)
- });
-
- for (sid, song) in songs {
- let query = {handle_error_ui!(song_list_nav::SongListNav::get()).parse_search()}.clone();
- let should_display = match query {
- SearchType::Source(s) if !s.is_empty() => format!("{:?}", &song.source_type()).to_lowercase().contains(&s),
- SearchType::Author(s) if !s.is_empty() => song.author().to_lowercase().contains(&s),
- SearchType::Name(s) if !s.is_empty() => song.name().to_owned().contains(&s),
- _ => true
- };
- if should_display {
- display_song_tab(ui, sid, song);
- }
- }
- }
- 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);
- }
- }
- }
- }
+ for (sid, song) in disp_songs {
+ handle_error_ui!(Self::display_song_tab(ui, state, &sid, &song));
}
-
});
});
Ok(())
}
}
-fn display_song_tab(ui: &mut egui::Ui, sid: &uuid::Uuid, song: &Song) {
- let theme = handle_error_ui!(xmpd_settings::Settings::get()).theme.clone();
- ui.horizontal(|ui| {
- let mut clicked = ui.add(
- egui::Image::new(crate::data::NOTE_ICON)
- .tint(theme.accent_color)
- .sense(Sense::click())
- .fit_to_exact_size(Vec2::new(32.0, 32.0))
- ).clicked();
+impl SongList {
+ fn get_and_sort_songs(state: &mut crate::GuiState) -> crate::Result> {
+ 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)
+ }
+ }
+ }
- ui.vertical(|ui| {
- let selected_song_id = {handle_error_ui!(SongList::get()).selected_song_id};
- let label = if selected_song_id == *sid {
- ui.label(
- RichText::new(song.name())
- .color(theme.accent_color)
- )
- } else {
- ui.label(
- RichText::new(song.name())
- .color(theme.text_color)
- )
+ fn get_songs_to_display(songs: &Vec<(uuid::Uuid, Song)>) -> crate::Result>{
+ 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 label.clicked() {
- clicked = true;
+ if should_display {
+ to_display.push((sid.clone(), song.clone()));
}
- ui.monospace(
- RichText::new(format!("By {}", song.author()))
- .color(theme.dim_text_color)
- .size(10.0)
- );
+ }
+ Ok(to_display)
+ }
- });
-
- ui.with_layout(egui::Layout::right_to_left(egui::Align::RIGHT), |ui| {
- ui.add_space(3.0);
+ fn display_song_tab(ui: &mut egui::Ui, state: &mut crate::GuiState, sid: &uuid::Uuid, song: &Song) -> crate::Result<()> {
+ let mut clicked = false;
+ ui.horizontal(|ui| {
+ let theme = handle_error_ui!(xmpd_settings::Settings::get()).theme.clone();
+ let img = ui.add(
+ egui::Image::new(crate::data::NOTE_ICON)
+ .tint(theme.accent_color)
+ .sense(Sense::click())
+ .fit_to_exact_size(Vec2::new(32.0, 32.0))
+ );
let status = {
handle_error_ui!(xmpd_cache::Cache::get()).get_cached_song_status(&sid).clone()
};
- match status {
- Some(DlStatus::Done(p)) => {
- let img = ui.add(
- egui::Image::new(crate::data::CHECK_ICON)
- .tint(Color32::LIGHT_GREEN)
- .sense(Sense::hover())
- .fit_to_exact_size(Vec2::new(16.0, 16.0))
- );
-
- img.on_hover_ui(|ui| {
- ui.label(format!("Path: {p}"));
- });
- }
- Some(DlStatus::Downloading) => {
- ui.add(
- egui::Spinner::new()
- .color(theme.accent_color)
- .size(16.0)
- );
- }
- Some(DlStatus::Error(e)) => {
- let img = ui.add(
- egui::Image::new(crate::data::WARN_ICON)
- .tint(Color32::LIGHT_YELLOW)
- .sense(Sense::hover())
- .fit_to_exact_size(Vec2::new(16.0, 16.0))
- );
- img.on_hover_ui(|ui| {
- ui.label(e);
- });
- }
- None => {
- let img = ui.add(
- egui::Image::new(crate::data::DL_ICON)
- .tint(theme.accent_color)
- .sense(Sense::click())
- .fit_to_exact_size(Vec2::new(16.0, 16.0))
- );
- if img.clicked() {
- handle_error_ui!(xmpd_cache::Cache::get()).download_to_cache(sid.clone(), song.clone());
- let mut toast = handle_error_ui!(crate::components::toast::Toast::get());
- toast.show_toast(
- "Downloading Song",
- &format!("Started downloading {} by {}", song.name(), song.author()),
- super::toast::ToastType::Info
- );
- }
+ if img.clicked() {
+ clicked = true;
+ }
+ if img.hovered() {
+ if matches!(status, Some(DlStatus::Done(_))) {
+ ui.output_mut(|o| o.cursor_icon = CursorIcon::PointingHand);
+ } else {
+ ui.output_mut(|o| o.cursor_icon = CursorIcon::Default);
}
}
+
+ ui.vertical(|ui| {
+ let slf = handle_error_ui!(SongList::get());
+ let label = if slf.selected_sid == *sid {
+ RichText::new(song.name())
+ .color(theme.accent_color)
+ } else if matches!(status, Some(DlStatus::Done(_))) {
+ RichText::new(song.name())
+ .color(theme.text_color)
+ } else {
+ RichText::new(song.name())
+ .color(theme.dim_text_color)
+ };
+ let label = ui.label(label);
+
+ if label.clicked() {
+ clicked = true;
+ }
+ if label.hovered() {
+ if matches!(status, Some(DlStatus::Done(_))) {
+ ui.output_mut(|o| o.cursor_icon = CursorIcon::PointingHand);
+ } else {
+ ui.output_mut(|o| o.cursor_icon = CursorIcon::Default);
+ }
+ }
+ ui.monospace(
+ RichText::new(format!("By {}", song.author()))
+ .color(theme.dim_text_color)
+ .size(10.0)
+ );
+
+ });
+
+ ui.with_layout(egui::Layout::right_to_left(egui::Align::RIGHT), |ui| {
+ ui.add_space(3.0);
+
+ match status {
+ Some(DlStatus::Done(_p)) => {
+ //let img = egui::Image::new(crate::data::CHECK_ICON)
+ // .tint(Color32::LIGHT_GREEN)
+ // .sense(Sense::hover())
+ // .fit_to_exact_size(Vec2::new(16.0, 16.0));
+
+ //ui.add(img).on_hover_ui(|ui| {
+ // ui.label(format!("Path: {p}"));
+ //});
+ }
+ Some(DlStatus::Downloading) => {
+ let spinner = egui::Spinner::new()
+ .color(theme.accent_color)
+ .size(16.0);
+ ui.add(spinner);
+ }
+ Some(DlStatus::Error(e)) => {
+ let img = egui::Image::new(crate::data::WARN_ICON)
+ .tint(Color32::LIGHT_YELLOW)
+ .sense(Sense::hover())
+ .fit_to_exact_size(Vec2::new(16.0, 16.0));
+
+ ui.add(img).on_hover_ui(|ui| {
+ ui.label(e);
+ });
+ }
+ None => {
+ let img = egui::Image::new(crate::data::DL_ICON)
+ .tint(theme.accent_color)
+ .sense(Sense::click())
+ .fit_to_exact_size(Vec2::new(16.0, 16.0));
+
+ if ui.add(img).clicked() {
+ handle_error_ui!(xmpd_cache::Cache::get()).download_to_cache(sid.clone(), song.clone());
+ let mut toast = handle_error_ui!(crate::components::toast::Toast::get());
+ toast.show_toast(
+ "Downloading Song",
+ &format!("Started downloading {} by {}", song.name(), song.author()),
+ super::toast::ToastType::Info
+ );
+ }
+ }
+ }
+ });
});
+ ui.separator();
if clicked {
- handle_error_ui!(SongList::get()).selected_song_id = sid.clone();
+ let mut sl = SongList::get()?;
+ sl.play_song(sid.clone(), state)?;
}
- });
-
- ui.separator();
+
+ 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> {
+ 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)
+ }
}
diff --git a/xmpd-gui/src/components/song_list/song_list_nav.rs b/xmpd-gui/src/components/song_list/song_list_nav.rs
index 0224ed7..7dc3c80 100644
--- a/xmpd-gui/src/components/song_list/song_list_nav.rs
+++ b/xmpd-gui/src/components/song_list/song_list_nav.rs
@@ -1,8 +1,11 @@
use uuid::Uuid;
-use xmpd_manifest::store::BaseStore;
+use xmpd_cache::DlStatus;
+use xmpd_manifest::{song::Song, store::BaseStore};
use crate::components::{left_nav::LeftNav, toast::ToastType, CompGetter, CompUi};
+use super::SongList;
+
#[derive(Debug, Clone)]
pub enum SearchType {
Name(String),
@@ -53,8 +56,9 @@ impl CompUi for SongListNav {
None => {
songs = state.manifest.store().get_songs().keys().cloned().collect();
}
+
}
- for sid in &songs {
+ for sid in handle_error_ui!(Self::get_songs_to_download(&songs)) {
if let Some(song) = state.manifest.store().get_song(&sid) {
handle_error_ui!(xmpd_cache::Cache::get()).download_to_cache(sid.clone(), song.clone())
}
@@ -86,6 +90,16 @@ impl SongListNav {
i @ _ => SearchType::Name(i.to_string().to_lowercase())
}
}
+ fn get_songs_to_download(songs: &Vec) -> crate::Result> {
+ 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)
+ }
}
diff --git a/xmpd-gui/src/components/toast.rs b/xmpd-gui/src/components/toast.rs
index d9ac3f9..c9a7591 100644
--- a/xmpd-gui/src/components/toast.rs
+++ b/xmpd-gui/src/components/toast.rs
@@ -1,7 +1,7 @@
use std::{collections::VecDeque, time::SystemTime};
-use egui::{epaint::Shadow, load::TexturePoll, Align2, Color32, Frame, Image, Margin, Pos2, Rect, RichText, Rounding, Stroke, Style, Vec2};
+use egui::{epaint::Shadow, load::TexturePoll, Align2, Color32, Frame, Image, ImageSource, Margin, Pos2, Rect, RichText, Rounding, Stroke, Style, TextureFilter, TextureOptions, TextureWrapMode, Vec2};
use super::{CompGetter, CompUi};
@@ -43,19 +43,24 @@ impl CompUi for Toast {
ToastType::Info => {
color = theme.accent_color;
img = Image::new(crate::data::INFO_ICON)
- .max_size(Vec2::new(16.0, 16.0))
+ .fit_to_exact_size(Vec2::new(16.0, 16.0))
.tint(color);
}
ToastType::Warn => {
color = crate::data::C_WARN;
img = Image::new(crate::data::WARN_ICON)
- .max_size(Vec2::new(16.0, 16.0))
+ .fit_to_exact_size(Vec2::new(16.0, 16.0))
+ .texture_options(TextureOptions {
+ magnification: TextureFilter::Linear,
+ minification: TextureFilter::Linear,
+ wrap_mode: TextureWrapMode::ClampToEdge,
+ })
.tint(color);
}
ToastType::Error => {
color = Color32::LIGHT_RED;
img = Image::new(crate::data::ERROR_ICON)
- .max_size(Vec2::new(16.0, 16.0))
+ .fit_to_exact_size(Vec2::new(16.0, 16.0))
.tint(color);
}
}
diff --git a/xmpd-gui/src/components/top_nav.rs b/xmpd-gui/src/components/top_nav.rs
index 5684558..c2d7988 100644
--- a/xmpd-gui/src/components/top_nav.rs
+++ b/xmpd-gui/src/components/top_nav.rs
@@ -1,9 +1,19 @@
+use std::path::PathBuf;
+
+use egui::TextBuffer;
+use xmpd_manifest::store::{JsonStore, TomlStore};
+
use crate::windows::WindowId;
-use super::CompUi;
+use super::{CompGetter, CompUi};
#[derive(Debug, Default)]
-pub struct TopNav;
+pub struct TopNav {
+ // for dialog
+ manifest_path: Option<(PathBuf, String)>
+}
+
+component_register!(TopNav);
impl CompUi for TopNav {
fn draw(ui: &mut egui::Ui, state: &mut crate::GuiState) -> crate::Result<()> {
@@ -22,6 +32,25 @@ impl CompUi for TopNav {
handle_error_ui!(state.manifest.save());
ui.close_menu();
}
+ if ui.button("Save As").clicked() {
+ std::thread::spawn(|| -> crate::Result<()> {
+ let mut dialog = rfd::FileDialog::new()
+ .add_filter("Json", &["json"])
+ .add_filter("Toml", &["toml"])
+ .set_title("Save Manifest As")
+ .set_can_create_directories(true)
+ .set_file_name("manifest");
+ if let Some(home_dir) = dirs::home_dir() {
+ dialog = dialog.set_directory(home_dir);
+ }
+ if let Some(path) = dialog.save_file() {
+ if let Some(ext) = path.extension() {
+ TopNav::get()?.manifest_path = Some((path.clone(), ext.to_string_lossy().to_string()))
+ }
+ }
+ Ok(())
+ });
+ }
});
ui.menu_button("Help", |ui| {
if ui.button("Source").clicked() {
@@ -39,7 +68,20 @@ impl CompUi for TopNav {
});
});
-
- Ok(())
+ let mut used = false;
+ if let Some((path, ext)) = &TopNav::get()?.manifest_path {
+ match ext.as_str() {
+ "json" => state.manifest.convert_and_save_to::(&path)?,
+ "toml" => state.manifest.convert_and_save_to::(&path)?,
+ _ => ()
+ }
+ used = true;
+ }
+ if used {
+ TopNav::get()?.manifest_path = None;
+ }
+ Ok(())
}
}
+
+
diff --git a/xmpd-gui/src/lib.rs b/xmpd-gui/src/lib.rs
index c7c2d10..8604124 100644
--- a/xmpd-gui/src/lib.rs
+++ b/xmpd-gui/src/lib.rs
@@ -36,11 +36,13 @@ pub fn start() -> Result<()> {
pub struct GuiState {
pub manifest: Manifest,
pub windows: windows::Windows,
+ pub player: xmpd_player::Player,
}
impl GuiState {
pub fn new() -> Result {
Ok(Self {
+ player: xmpd_player::Player::new(),
manifest: Manifest::new(&xmpd_cliargs::CLIARGS.manifest_path())?,
windows: windows::Windows::new(),
})
diff --git a/xmpd-gui/src/macros.rs b/xmpd-gui/src/macros.rs
index 3497e4c..79d9668 100644
--- a/xmpd-gui/src/macros.rs
+++ b/xmpd-gui/src/macros.rs
@@ -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;
+ }
+ };
+}
diff --git a/xmpd-manifest/Cargo.toml b/xmpd-manifest/Cargo.toml
index b8df441..48a1fd6 100644
--- a/xmpd-manifest/Cargo.toml
+++ b/xmpd-manifest/Cargo.toml
@@ -18,8 +18,11 @@ crate-type = ["rlib"]
bench = false
[dependencies]
+xmpd-cliargs.path = "../xmpd-cliargs"
+xmpd-settings.path = "../xmpd-settings"
anyhow.workspace = true
uuid.workspace = true
serde.workspace = true
serde_json.workspace = true
url.workspace = true
+toml.workspace = true
diff --git a/xmpd-manifest/src/lib.rs b/xmpd-manifest/src/lib.rs
index 864230d..353c0d0 100644
--- a/xmpd-manifest/src/lib.rs
+++ b/xmpd-manifest/src/lib.rs
@@ -1,4 +1,4 @@
-use std::path::Path;
+use std::path::{Path, PathBuf};
#[cfg(test)]
@@ -43,6 +43,29 @@ impl Manifest {
self.store_mut().load_from(&p)?;
Ok(())
}
+ pub fn convert_to(&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(&self, path: &Path) -> Result<()> {
+ let st2 = self.convert_to::();
+ std::fs::write(path, st2.to_bytes()?)?;
+ Ok(())
+ }
+
+ pub fn get_song_as_path(&self, sid: uuid::Uuid) -> Result {
+ 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)
+ }
}
diff --git a/xmpd-manifest/src/song.rs b/xmpd-manifest/src/song.rs
index b5986cc..9bbf5d3 100644
--- a/xmpd-manifest/src/song.rs
+++ b/xmpd-manifest/src/song.rs
@@ -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:?}"),
+ }
+ }
+}
+
diff --git a/xmpd-manifest/src/store/mod.rs b/xmpd-manifest/src/store/mod.rs
index acb4392..a7fbe8c 100644
--- a/xmpd-manifest/src/store/mod.rs
+++ b/xmpd-manifest/src/store/mod.rs
@@ -5,7 +5,9 @@ use uuid::Uuid;
use crate::{playlist::Playlist, song::Song};
mod json;
+mod toml;
pub use json::JsonStore;
+pub use toml::TomlStore;
pub trait BaseStore {
fn get_songs(&self) -> &HashMap;
diff --git a/xmpd-manifest/src/store/toml.rs b/xmpd-manifest/src/store/toml.rs
new file mode 100644
index 0000000..be4ec0b
--- /dev/null
+++ b/xmpd-manifest/src/store/toml.rs
@@ -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,
+ playlists: HashMap
+}
+
+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> {
+ let s: Vec = toml::to_string_pretty(self)?.chars().map(|c| c as u8).collect();
+ Ok(s)
+ }
+ fn from_bytes(s: &[u8]) -> crate::Result where Self: Sized {
+ let s: Self = toml::from_str(&String::from_utf8(s.to_vec())?)?;
+ Ok(s)
+ }
+ fn get_songs(&self) -> &HashMap {
+ &self.songs
+ }
+ fn get_songs_mut(&mut self) -> &mut HashMap {
+ &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 {
+ &self.playlists
+ }
+ fn get_playlists_mut(&mut self) -> &mut HashMap {
+ &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
+ }
+}
+
diff --git a/xmpd-player/Cargo.toml b/xmpd-player/Cargo.toml
index f3dc537..e4164a8 100644
--- a/xmpd-player/Cargo.toml
+++ b/xmpd-player/Cargo.toml
@@ -7,4 +7,7 @@ license.workspace = true
authors.workspace = true
[dependencies]
-rodio = { version = "0.20.1", features = ["symphonia-all"] }
+rodio.workspace = true
+lazy_static.workspace = true
+anyhow.workspace = true
+log.workspace = true
diff --git a/xmpd-player/src/lib.rs b/xmpd-player/src/lib.rs
index e69de29..4ed8f25 100644
--- a/xmpd-player/src/lib.rs
+++ b/xmpd-player/src/lib.rs
@@ -0,0 +1,101 @@
+use std::{path::Path, time::Duration};
+
+use rodio::{Decoder, OutputStream, OutputStreamHandle, Sink, Source};
+
+type Result = anyhow::Result;
+
+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);
+ }
+}
diff --git a/xmpd-settings/Cargo.toml b/xmpd-settings/Cargo.toml
index ff2c216..2f701f7 100644
--- a/xmpd-settings/Cargo.toml
+++ b/xmpd-settings/Cargo.toml
@@ -12,4 +12,4 @@ camino.workspace = true
egui.workspace = true
lazy_static.workspace = true
serde.workspace = true
-toml = "0.8.19"
+toml.workspace = true