New icons, new side panel, table no longer shows playlists, as they are selectable seperately

This commit is contained in:
Gvidas Juknevičius 2024-10-09 15:02:24 +03:00
parent 847aa2bb4f
commit 70b92f4ebf
Signed by: MCorange
GPG Key ID: 12B1346D720B7FBB
19 changed files with 6712 additions and 703 deletions

228
Cargo.lock generated
View File

@ -579,6 +579,12 @@ dependencies = [
"windows-targets 0.52.6", "windows-targets 0.52.6",
] ]
[[package]]
name = "base64"
version = "0.21.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9d297deb1925b89f2ccc13d7635fa0714f12c87adce1c75356b39ca9b7178567"
[[package]] [[package]]
name = "base64" name = "base64"
version = "0.22.1" version = "0.22.1"
@ -953,6 +959,12 @@ dependencies = [
"unicode-width", "unicode-width",
] ]
[[package]]
name = "color-hex"
version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ecdffb913a326b6c642290a0d0ec8e8d6597291acdc07cc4c9cb4b3635d44cf9"
[[package]] [[package]]
name = "color_quant" name = "color_quant"
version = "1.1.0" version = "1.1.0"
@ -1101,6 +1113,12 @@ version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "96a6ac251f4a2aca6b3f91340350eab87ae57c3f127ffeb585e92bd336717991" checksum = "96a6ac251f4a2aca6b3f91340350eab87ae57c3f127ffeb585e92bd336717991"
[[package]]
name = "data-url"
version = "0.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5c297a1c74b71ae29df00c3e22dd9534821d60eb9af5a0192823fa2acea70c2a"
[[package]] [[package]]
name = "deranged" name = "deranged"
version = "0.3.11" version = "0.3.11"
@ -1190,6 +1208,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "20930a432bbd57a6d55e07976089708d4893f3d556cf42a0d79e9e321fa73b10" checksum = "20930a432bbd57a6d55e07976089708d4893f3d556cf42a0d79e9e321fa73b10"
dependencies = [ dependencies = [
"bytemuck", "bytemuck",
"color-hex",
"serde", "serde",
] ]
@ -1283,9 +1302,12 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1b78779f35ded1a853786c9ce0b43fe1053e10a21ea3b23ebea411805ce41593" checksum = "1b78779f35ded1a853786c9ce0b43fe1053e10a21ea3b23ebea411805ce41593"
dependencies = [ dependencies = [
"egui", "egui",
"ehttp",
"enum-map", "enum-map",
"image",
"log", "log",
"mime_guess2", "mime_guess2",
"resvg",
"serde", "serde",
] ]
@ -1305,6 +1327,20 @@ dependencies = [
"winit", "winit",
] ]
[[package]]
name = "ehttp"
version = "0.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "59a81c221a1e4dad06cb9c9deb19aea1193a5eea084e8cd42d869068132bf876"
dependencies = [
"document-features",
"js-sys",
"ureq",
"wasm-bindgen",
"wasm-bindgen-futures",
"web-sys",
]
[[package]] [[package]]
name = "emath" name = "emath"
version = "0.27.2" version = "0.27.2"
@ -1508,6 +1544,12 @@ dependencies = [
"miniz_oxide 0.8.0", "miniz_oxide 0.8.0",
] ]
[[package]]
name = "float-cmp"
version = "0.9.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "98de4bbd547a563b716d8dfa9aad1cb19bfab00f4fa09a6a4ed21dbcf44ce9c4"
[[package]] [[package]]
name = "fnv" name = "fnv"
version = "1.0.7" version = "1.0.7"
@ -1936,6 +1978,15 @@ dependencies = [
"windows-sys 0.52.0", "windows-sys 0.52.0",
] ]
[[package]]
name = "html-escape"
version = "0.2.13"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6d1ad449764d627e22bfd7cd5e8868264fc9236e07c752972b4080cd351cb476"
dependencies = [
"utf8-width",
]
[[package]] [[package]]
name = "http" name = "http"
version = "1.1.0" version = "1.1.0"
@ -2074,6 +2125,12 @@ dependencies = [
"png", "png",
] ]
[[package]]
name = "imagesize"
version = "0.12.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "029d73f573d8e8d63e6d5020011d3255b28c3ba85d6cf870a07184ed23de9284"
[[package]] [[package]]
name = "indexmap" name = "indexmap"
version = "2.5.0" version = "2.5.0"
@ -2207,6 +2264,15 @@ version = "3.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e2db585e1d738fc771bf08a151420d3ed193d9d895a36df7f6f8a9456b911ddc" checksum = "e2db585e1d738fc771bf08a151420d3ed193d9d895a36df7f6f8a9456b911ddc"
[[package]]
name = "kurbo"
version = "0.9.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bd85a5776cd9500c2e2059c8c76c3b01528566b7fcbaf8098b55a33fc298849b"
dependencies = [
"arrayvec",
]
[[package]] [[package]]
name = "lazy_static" name = "lazy_static"
version = "1.5.0" version = "1.5.0"
@ -2329,11 +2395,13 @@ dependencies = [
"egui_extras", "egui_extras",
"env_logger", "env_logger",
"futures", "futures",
"html-escape",
"lazy_static", "lazy_static",
"libc", "libc",
"log", "log",
"notify-rust", "notify-rust",
"open", "open",
"regex",
"reqwest", "reqwest",
"serde", "serde",
"serde_json", "serde_json",
@ -2873,6 +2941,12 @@ version = "2.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e" checksum = "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e"
[[package]]
name = "pico-args"
version = "0.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5be167a7af36ee22fe3115051bc51f6e6c7054c9348e28deb4f49bd6f705a315"
[[package]] [[package]]
name = "pin-project" name = "pin-project"
version = "1.1.5" version = "1.1.5"
@ -3138,6 +3212,12 @@ version = "0.6.2"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "20675572f6f24e9e76ef639bc5552774ed45f1c30e2951e1e99c59888861c539" checksum = "20675572f6f24e9e76ef639bc5552774ed45f1c30e2951e1e99c59888861c539"
[[package]]
name = "rctree"
version = "0.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3b42e27ef78c35d3998403c1d26f3efd9e135d3e5121b0a4845cc5cc27547f4f"
[[package]] [[package]]
name = "redox_syscall" name = "redox_syscall"
version = "0.3.5" version = "0.3.5"
@ -3178,9 +3258,9 @@ dependencies = [
[[package]] [[package]]
name = "regex" name = "regex"
version = "1.10.6" version = "1.11.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4219d74c6b67a3654a9fbebc4b419e22126d13d2f3c4a07ee0cb61ff79a79619" checksum = "38200e5ee88914975b69f657f0801b6f6dccafd44fd9326302a4aaeecfacb1d8"
dependencies = [ dependencies = [
"aho-corasick", "aho-corasick",
"memchr", "memchr",
@ -3190,9 +3270,9 @@ dependencies = [
[[package]] [[package]]
name = "regex-automata" name = "regex-automata"
version = "0.4.7" version = "0.4.8"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "38caf58cc5ef2fed281f89292ef23f6365465ed9a41b7a7754eb4e26496c92df" checksum = "368758f23274712b504848e9d5a6f010445cc8b87a7cdb4d7cbee666c1288da3"
dependencies = [ dependencies = [
"aho-corasick", "aho-corasick",
"memchr", "memchr",
@ -3201,9 +3281,9 @@ dependencies = [
[[package]] [[package]]
name = "regex-syntax" name = "regex-syntax"
version = "0.8.4" version = "0.8.5"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7a66a03ae7c801facd77a29370b4faec201768915ac14a721ba36f20bc9c209b" checksum = "2b15c43186be67a4fd63bee50d0303afffcef381492ebe2c5d87f324e1b8815c"
[[package]] [[package]]
name = "renderdoc-sys" name = "renderdoc-sys"
@ -3217,8 +3297,9 @@ version = "0.12.7"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f8f4955649ef5c38cc7f9e8aa41761d48fb9677197daea9984dc54f56aad5e63" checksum = "f8f4955649ef5c38cc7f9e8aa41761d48fb9677197daea9984dc54f56aad5e63"
dependencies = [ dependencies = [
"base64", "base64 0.22.1",
"bytes", "bytes",
"futures-channel",
"futures-core", "futures-core",
"futures-util", "futures-util",
"h2", "h2",
@ -3254,6 +3335,29 @@ dependencies = [
"windows-registry", "windows-registry",
] ]
[[package]]
name = "resvg"
version = "0.37.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cadccb3d99a9efb8e5e00c16fbb732cbe400db2ec7fc004697ee7d97d86cf1f4"
dependencies = [
"log",
"pico-args",
"rgb",
"svgtypes",
"tiny-skia",
"usvg",
]
[[package]]
name = "rgb"
version = "0.8.50"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "57397d16646700483b67d2dd6511d79318f9d057fdbd21a4066aeac8b41d310a"
dependencies = [
"bytemuck",
]
[[package]] [[package]]
name = "ring" name = "ring"
version = "0.17.8" version = "0.17.8"
@ -3269,6 +3373,12 @@ dependencies = [
"windows-sys 0.52.0", "windows-sys 0.52.0",
] ]
[[package]]
name = "roxmltree"
version = "0.19.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3cd14fd5e3b777a7422cca79358c57a8f6e3a703d9ac187448d0daf220c2407f"
[[package]] [[package]]
name = "rustc-demangle" name = "rustc-demangle"
version = "0.1.24" version = "0.1.24"
@ -3320,6 +3430,7 @@ version = "0.23.13"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f2dabaac7466917e566adb06783a81ca48944c6898a1b08b9374106dd671f4c8" checksum = "f2dabaac7466917e566adb06783a81ca48944c6898a1b08b9374106dd671f4c8"
dependencies = [ dependencies = [
"log",
"once_cell", "once_cell",
"ring", "ring",
"rustls-pki-types", "rustls-pki-types",
@ -3334,7 +3445,7 @@ version = "2.1.3"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "196fe16b00e106300d3e45ecfcb764fa292a535d7326a29a5875c579c7417425" checksum = "196fe16b00e106300d3e45ecfcb764fa292a535d7326a29a5875c579c7417425"
dependencies = [ dependencies = [
"base64", "base64 0.22.1",
"rustls-pki-types", "rustls-pki-types",
] ]
@ -3493,6 +3604,21 @@ version = "0.3.7"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d66dc143e6b11c1eddc06d5c423cfc97062865baf299914ab64caa38182078fe" checksum = "d66dc143e6b11c1eddc06d5c423cfc97062865baf299914ab64caa38182078fe"
[[package]]
name = "simplecss"
version = "0.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a11be7c62927d9427e9f40f3444d5499d868648e2edbc4e2116de69e7ec0e89d"
dependencies = [
"log",
]
[[package]]
name = "siphasher"
version = "0.3.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "38b58827f4464d87d377d175e90bf58eb00fd8716ff0a62f80356b5e61555d0d"
[[package]] [[package]]
name = "slab" name = "slab"
version = "0.4.9" version = "0.4.9"
@ -3633,6 +3759,9 @@ name = "strict-num"
version = "0.1.1" version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6637bab7722d379c8b41ba849228d680cc12d0a45ba1fa2b48f2a30577a06731" checksum = "6637bab7722d379c8b41ba849228d680cc12d0a45ba1fa2b48f2a30577a06731"
dependencies = [
"float-cmp",
]
[[package]] [[package]]
name = "strsim" name = "strsim"
@ -3646,6 +3775,16 @@ version = "2.6.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292" checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292"
[[package]]
name = "svgtypes"
version = "0.13.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6e44e288cd960318917cbd540340968b90becc8bc81f171345d706e7a89d9d70"
dependencies = [
"kurbo",
"siphasher",
]
[[package]] [[package]]
name = "syn" name = "syn"
version = "1.0.109" version = "1.0.109"
@ -3760,6 +3899,7 @@ dependencies = [
"bytemuck", "bytemuck",
"cfg-if", "cfg-if",
"log", "log",
"png",
"tiny-skia-path", "tiny-skia-path",
] ]
@ -4019,6 +4159,22 @@ version = "0.9.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8ecb6da28b8a351d773b68d5825ac39017e680750f980f3a1a85cd8dd28a47c1" checksum = "8ecb6da28b8a351d773b68d5825ac39017e680750f980f3a1a85cd8dd28a47c1"
[[package]]
name = "ureq"
version = "2.10.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b74fc6b57825be3373f7054754755f03ac3a8f5d70015ccad699ba2029956f4a"
dependencies = [
"base64 0.22.1",
"flate2",
"log",
"once_cell",
"rustls",
"rustls-pki-types",
"url",
"webpki-roots",
]
[[package]] [[package]]
name = "url" name = "url"
version = "2.5.2" version = "2.5.2"
@ -4030,6 +4186,56 @@ dependencies = [
"percent-encoding", "percent-encoding",
] ]
[[package]]
name = "usvg"
version = "0.37.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "38b0a51b72ab80ca511d126b77feeeb4fb1e972764653e61feac30adc161a756"
dependencies = [
"base64 0.21.7",
"log",
"pico-args",
"usvg-parser",
"usvg-tree",
"xmlwriter",
]
[[package]]
name = "usvg-parser"
version = "0.37.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9bd4e3c291f45d152929a31f0f6c819245e2921bfd01e7bd91201a9af39a2bdc"
dependencies = [
"data-url",
"flate2",
"imagesize",
"kurbo",
"log",
"roxmltree",
"simplecss",
"siphasher",
"svgtypes",
"usvg-tree",
]
[[package]]
name = "usvg-tree"
version = "0.37.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8ee3d202ebdb97a6215604b8f5b4d6ef9024efd623cf2e373a6416ba976ec7d3"
dependencies = [
"rctree",
"strict-num",
"svgtypes",
"tiny-skia-path",
]
[[package]]
name = "utf8-width"
version = "0.1.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "86bd8d4e895da8537e5315b8254664e6b769c4ff3db18321b297a1e7004392e3"
[[package]] [[package]]
name = "utf8parse" name = "utf8parse"
version = "0.2.2" version = "0.2.2"
@ -4957,6 +5163,12 @@ version = "0.8.22"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "af4e2e2f7cba5a093896c1e150fbfe177d1883e7448200efb81d40b9d339ef26" checksum = "af4e2e2f7cba5a093896c1e150fbfe177d1883e7448200efb81d40b9d339ef26"
[[package]]
name = "xmlwriter"
version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ec7a2a501ed189703dba8b08142f057e887dfc4b2cc4db2d343ac6376ba3e0b9"
[[package]] [[package]]
name = "zbus" name = "zbus"
version = "3.15.2" version = "3.15.2"

View File

@ -11,16 +11,18 @@ anyhow = "1.0.81"
camino = "1.1.6" camino = "1.1.6"
clap = { version = "4.5.4", features = ["derive"] } clap = { version = "4.5.4", features = ["derive"] }
eframe = "0.27.2" eframe = "0.27.2"
egui = "0.27.2" egui = { version = "0.27.2", features = ["color-hex"] }
egui_extras = "0.27.2" egui_extras = { version = "0.27.2", features = ["all_loaders"] }
env_logger = "0.11.3" env_logger = "0.11.3"
futures = "0.3.30" futures = "0.3.30"
html-escape = "0.2.13"
lazy_static = "1.4.0" lazy_static = "1.4.0"
libc = "0.2.153" libc = "0.2.153"
log = "0.4.21" log = "0.4.21"
notify-rust = "4.11.3" notify-rust = "4.11.3"
open = "5.3.0" open = "5.3.0"
reqwest = { version = "0.12.3", features = ["h2", "http2", "rustls-tls"], default-features = false } regex = "1.11.0"
reqwest = { version = "0.12.3", features = ["blocking", "h2", "http2", "rustls-tls"], default-features = false }
serde = { version = "1.0.197", features = ["derive"] } serde = { version = "1.0.197", features = ["derive"] }
serde_json = "1.0.115" serde_json = "1.0.115"
# serde_traitobject = "0.2.8" # serde_traitobject = "0.2.8"

5475
a.spotdl Normal file

File diff suppressed because it is too large Load Diff

Binary file not shown.

Before

Width:  |  Height:  |  Size: 16 KiB

After

Width:  |  Height:  |  Size: 16 KiB

19
assets/note.svg Normal file
View File

@ -0,0 +1,19 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Uploaded to: SVG Repo, www.svgrepo.com, Generator: SVG Repo Mixer Tools -->
<svg width="800px" height="800px" viewBox="0 0 20 20" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<title>music [#1005]</title>
<desc>Created with Sketch.</desc>
<defs>
</defs>
<g id="Page-1" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
<g id="Dribbble-Light-Preview" transform="translate(-260.000000, -3759.000000)" fill="#ffffff">
<g id="icons" transform="translate(56.000000, 160.000000)">
<path d="M224,3601.05129 L224,3610.55901 C224,3612.90979 222.17612,3614.95492 219.888035,3614.89646 C217.266519,3614.82877 215.248971,3612.1662 216.234285,3609.31593 C216.777356,3607.74464 218.297755,3606.71797 219.920978,3606.69233 C220.695653,3606.68105 220.976173,3606.88208 222.003416,3607.24105 L222.003416,3604.12822 C222.003416,3603.56207 221.556181,3603.10258 221.005124,3603.10258 L213.018786,3603.10258 C212.467729,3603.10258 212.020494,3603.56207 212.020494,3604.12822 L212.020494,3614.65851 C212.020494,3617.02057 210.179644,3619.07289 207.881575,3618.99801 C205.681339,3618.92622 203.914362,3617.02775 204.00321,3614.73031 C204.090061,3612.51594 205.989811,3610.84209 208.147121,3610.79081 C209.166377,3610.76619 209.352059,3610.92619 210.02391,3611.34363 L210.02391,3601.05129 C210.02391,3599.91795 210.91838,3599 212.020494,3599 L222.003416,3599 C223.106529,3599 224,3599.91795 224,3601.05129" id="music-[#1005]">
</path>
</g>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 1.6 KiB

4
assets/search.svg Normal file
View File

@ -0,0 +1,4 @@
<?xml version="1.0" encoding="utf-8"?><!-- Uploaded to: SVG Repo, www.svgrepo.com, Generator: SVG Repo Mixer Tools -->
<svg width="800px" height="800px" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M11 6C13.7614 6 16 8.23858 16 11M16.6588 16.6549L21 21M19 11C19 15.4183 15.4183 19 11 19C6.58172 19 3 15.4183 3 11C3 6.58172 6.58172 3 11 3C15.4183 3 19 6.58172 19 11Z" stroke="#ffffff" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
</svg>

After

Width:  |  Height:  |  Size: 493 B

File diff suppressed because it is too large Load Diff

5
src/data.rs Normal file
View File

@ -0,0 +1,5 @@
// pub const APP_ICON: egui::ImageSource = egui::include_image!("../assets/app_icon.png");
pub const APP_ICON_BYTES: &[u8] = include_bytes!("../assets/app_icon.png");
pub const NOTE_ICON: egui::ImageSource = egui::include_image!("../assets/note.svg");
pub const SEARCH_ICON: egui::ImageSource = egui::include_image!("../assets/search.svg");

View File

@ -143,8 +143,8 @@ impl Downloader {
log::debug!("File {dl_file} doesnt exist, downloading"); log::debug!("File {dl_file} doesnt exist, downloading");
let mut cmd = match song.get_type() { let mut cmd = match song.get_type() {
&SongType::Youtube => { SongType::Youtube | SongType::Soundcloud=> {
log::debug!("Song {} is from yotube", song.get_url_str()); log::debug!("Song {} is from youtube or sondclound", song.get_url_str());
let mut cmd = tokio::process::Command::new(&cfg.cfg.ytdlp.path); let mut cmd = tokio::process::Command::new(&cfg.cfg.ytdlp.path);
cmd.args([ cmd.args([
"-x", "-x",
@ -168,7 +168,7 @@ impl Downloader {
]); ]);
cmd cmd
} }
url @ SongType::Soundcloud => { url => {
log::error!("Unknown or unsupported hostname '{:?}'", url); log::error!("Unknown or unsupported hostname '{:?}'", url);
return Ok(()); return Ok(());
} }

View File

@ -1,4 +1,5 @@
#![feature(downcast_unchecked)] #![feature(downcast_unchecked)]
#![feature(async_closure)]
use config::ConfigWrapper; use config::ConfigWrapper;
@ -13,8 +14,10 @@ mod constants;
mod process_manager; mod process_manager;
mod ui; mod ui;
mod prompt; mod prompt;
mod data;
fn main() { #[tokio::main]
async fn main() {
let Ok(cfg) = ConfigWrapper::parse() else { let Ok(cfg) = ConfigWrapper::parse() else {
return; return;
}; };

View File

@ -18,7 +18,6 @@ lazy_static::lazy_static!(
static PROC_INC: AtomicUsize = AtomicUsize::new(0); static PROC_INC: AtomicUsize = AtomicUsize::new(0);
pub fn add_proc(mut cmd: Command, msg: String) -> anyhow::Result<()> { pub fn add_proc(mut cmd: Command, msg: String) -> anyhow::Result<()> {
let mut proc = cmd.spawn()?; let mut proc = cmd.spawn()?;
let id = PROC_INC.fetch_add(1, Ordering::AcqRel); let id = PROC_INC.fetch_add(1, Ordering::AcqRel);
@ -65,7 +64,7 @@ pub fn purge_done_procs() -> usize {
finish_count finish_count
} }
/// Waits for processes to finish untill the proc count is lower or equal to `max` /// Waits for processes to finish until the proc count is lower or equal to `max`
pub fn wait_for_procs_untill(max: usize) -> anyhow::Result<usize> { pub fn wait_for_procs_untill(max: usize) -> anyhow::Result<usize> {
// NOTE: This looks really fucked because i dont want to deadlock the processes so i lock PROCESSES for as little as possible // NOTE: This looks really fucked because i dont want to deadlock the processes so i lock PROCESSES for as little as possible
// NOTE: So its also kinda really slow // NOTE: So its also kinda really slow

View File

@ -9,7 +9,7 @@ impl /* ComponentUi for */ ContextMenu {
pub fn ui(gui: &mut crate::ui::gui::Gui, ui: &mut egui::Ui, pname: &String, sname: &String, song: &Song) { pub fn ui(gui: &mut crate::ui::gui::Gui, ui: &mut egui::Ui, pname: &String, sname: &String, song: &Song) {
if ui.button("Edit").clicked() { if ui.button("Edit").clicked() {
let w = gui.windows.get_window::<GuiSongEditor>(WindowIndex::SongEdit); let w = gui.windows.get_window::<GuiSongEditor>(WindowIndex::SongEdit);
w.set_active_song(pname, sname, song.get_url_str()); w.set_active_song(pname, sname, song.get_url_str(), song.get_type());
gui.windows.open(WindowIndex::SongEdit, true); gui.windows.open(WindowIndex::SongEdit, true);
ui.close_menu(); ui.close_menu();
} }

View File

@ -4,6 +4,8 @@ use super::Gui;
pub mod nav; pub mod nav;
pub mod song_list; pub mod song_list;
pub mod context_menu; pub mod context_menu;
pub mod side_nav;
pub mod search_bar;
pub trait Component { pub trait Component {
fn ui(gui: &mut Gui, ctx: &egui::Context); fn ui(gui: &mut Gui, ctx: &egui::Context);
@ -12,3 +14,7 @@ pub trait Component {
pub trait ComponentUi { pub trait ComponentUi {
fn ui(gui: &mut Gui, ui: &mut egui::Ui); fn ui(gui: &mut Gui, ui: &mut egui::Ui);
} }
pub trait ComponentUiMut {
fn ui(&mut self, gui: &mut Gui, ui: &mut egui::Ui);
}

View File

@ -0,0 +1,53 @@
use egui::Color32;
use super::ComponentUiMut;
#[derive(Debug, Default, Clone)]
pub struct SearchBar {
text: String
}
pub enum SearchType {
Generic,
Song,
Url,
Source,
}
impl SearchBar {
pub fn get_search(&self) -> (SearchType, String) {
if self.text.starts_with("source:") {
(
SearchType::Source,
self.text.strip_prefix("source:").unwrap_or("").to_string().to_lowercase()
)
} else if self.text.starts_with("song:") {
(
SearchType::Song,
self.text.strip_prefix("song:").unwrap_or("").to_string().to_lowercase()
)
} else if self.text.starts_with("url:") {
(
SearchType::Url,
self.text.strip_prefix("url:").unwrap_or("").to_string().to_lowercase()
)
} else {
(
SearchType::Generic,
self.text.clone()
)
}
}
}
impl ComponentUiMut for SearchBar {
fn ui(&mut self, _: &mut crate::ui::gui::Gui, ui: &mut egui::Ui) {
ui.vertical(|ui| {
ui.horizontal(|ui| {
let tint = Color32::from_hex("#333377").unwrap();
ui.add(egui::Image::new(crate::data::SEARCH_ICON).tint(tint));
ui.text_edit_singleline(&mut self.text);
});
});
}
}

View File

@ -0,0 +1,45 @@
use std::borrow::BorrowMut;
use egui::{Button, Color32, Label, RichText, Sense};
use super::ComponentUi;
pub struct SideNav;
impl ComponentUi for SideNav {
fn ui(gui: &mut crate::ui::gui::Gui, ui: &mut egui::Ui) {
let mut playlist_names = gui.manifest
.get_playlists()
.keys().cloned().collect::<Vec<String>>();
playlist_names.sort_by_key(|name| name.to_lowercase());
ui.with_layout(egui::Layout::top_down(egui::Align::TOP), |ui| {
for pname in playlist_names {
if gui.current_playlist.is_empty() {
gui.current_playlist = pname.to_string();
}
ui.horizontal(|ui| {
let tint = Color32::from_hex("#333377").unwrap();
ui.add(egui::Image::new(crate::data::NOTE_ICON).tint(tint));
ui.horizontal(|ui| {
let text;
if gui.current_playlist == *pname {
text = RichText::new(&pname).color(tint);
} else {
text = RichText::new(&pname);
}
let button = Label::new(text).sense(Sense::click());
if ui.add(button).clicked() {
gui.current_playlist = pname.to_string();
}
});
});
}
});
}
// #333377
}

View File

@ -3,126 +3,128 @@ use egui_extras::{Column, TableBuilder};
use crate::manifest::song::SongType; use crate::manifest::song::SongType;
use super::{context_menu::ContextMenu, ComponentUi}; use super::{context_menu::ContextMenu, search_bar::SearchType, ComponentUi};
#[derive(Debug, Default)]
pub struct SongList; pub struct SongList {
}
impl ComponentUi for SongList { impl ComponentUi for SongList {
fn ui(gui: &mut crate::ui::gui::Gui, ui: &mut egui::Ui) { fn ui(gui: &mut crate::ui::gui::Gui, ui: &mut egui::Ui) {
let fltr_by;
let filter_clean;
if gui.filter.starts_with("playlist:") {
fltr_by = "playlist";
filter_clean = gui.filter.strip_prefix("playlist:").unwrap_or("").to_string().to_lowercase();
} else if gui.filter.starts_with("source:") {
fltr_by = "source";
filter_clean = gui.filter.strip_prefix("source:").unwrap_or("").to_string().to_lowercase();
} else if gui.filter.starts_with("url:") {
fltr_by = "url";
filter_clean = gui.filter.strip_prefix("url:").unwrap_or("").to_string();
} else {
fltr_by = "";
filter_clean = gui.filter.clone();
}
ui.vertical(|ui| { ui.vertical(|ui| {
ui.horizontal(|ui| { {
ui.colored_label(Color32::from_hex("#4444aa").unwrap(), "Filter: "); use crate::ui::gui::components::ComponentUiMut;
ui.text_edit_singleline(&mut gui.filter); let mut search = gui.search.clone();
}); search.ui(gui, ui);
}); gui.search = search;
}
ui.vertical(|ui| { ui.vertical(|ui| {
let available_height = ui.available_height(); let available_height = ui.available_height();
let table = TableBuilder::new(ui) let table = TableBuilder::new(ui)
.striped(true) .striped(true)
.cell_layout(egui::Layout::left_to_right(egui::Align::Center)) .cell_layout(egui::Layout::left_to_right(egui::Align::Center))
.resizable(true) .resizable(true)
//.column(Column::auto()) //.column(Column::auto())
.column(Column::auto()) //.column(Column::auto())
//.column( //.column(
// Column::remainder() // Column::remainder()
// .at_least(40.0) // .at_least(40.0)
// .clip(true) // .clip(true)
// .resizable(true), // .resizable(true),
//) //)
.column(Column::auto()) .column(Column::auto())
.column(Column::remainder()) .column(Column::remainder())
//.column(Column::remainder()) //.column(Column::remainder())
.min_scrolled_height(0.0) .min_scrolled_height(0.0)
.max_scroll_height(available_height) .max_scroll_height(available_height)
.sense(egui::Sense::click()); .sense(egui::Sense::click());
let playlists = gui.manifest.get_playlists().clone(); let playlists = gui.manifest.get_playlists().clone();
let songs = { let songs = {
let mut songs = Vec::new(); let mut songs = Vec::new();
for (pname, p) in playlists { for (pname, p) in playlists {
for (sname, s) in p { for (sname, s) in p {
songs.push((pname.clone(), sname, s)); songs.push((pname.clone(), sname, s));
}
} }
}
songs
};
table.header(20.0, |mut header| { songs.sort_by_key(|song| song.1.to_lowercase());
// header.col(|_|{}); songs
header.col(|ui| { };
ui.strong("Playlist");
});
header.col(|ui| {
ui.strong("Source");
});
header.col(|ui| {
ui.strong("Name");
});
}).body(|mut body| {
for (pname, sname, s) in songs {
if fltr_by == "playlist" && !filter_clean.is_empty() {
if !pname.to_lowercase().contains(&filter_clean) {
continue;
}
} else if fltr_by == "type" && !filter_clean.is_empty(){
if !s.get_type().to_string().to_lowercase().contains(&filter_clean) {
continue;
}
} else if fltr_by == "url" && !filter_clean.is_empty(){
if !s.get_url_str().contains(&filter_clean) {
continue;
}
} else if !filter_clean.is_empty() && !sname.to_lowercase().contains(&filter_clean) {
continue;
}
body.row(18.0, |mut row| {
row.col(|ui| {
ui.label(pname.clone())
.context_menu(|ui| ContextMenu::ui(gui, ui, &pname, &sname, &s));
});
row.col(|ui| {
let color =
match s.get_type() {
SongType::Youtube => Color32::from_hex("#FF0000").unwrap(),
SongType::Spotify => Color32::from_hex("#1db954").unwrap(),
SongType::Soundcloud => Color32::from_hex("#F26F23").unwrap()
};
ui.colored_label(color, s.get_type().to_string())
.context_menu(|ui| ContextMenu::ui(gui, ui, &pname, &sname, &s));
});
row.col(|ui| {
ui.hyperlink_to(sname.clone(), s.get_url_str())
.context_menu(|ui| ContextMenu::ui(gui, ui, &pname, &sname, &s));
});
row.response()
.context_menu(|ui| ContextMenu::ui(gui, ui, &pname, &sname, &s));
table.header(20.0, |mut header| {
// header.col(|_|{});
//header.col(|ui| {
// ui.strong("Playlist");vec.sort_by_key(|name| name.to_lowercase());
//});
header.col(|ui| {
ui.strong("Source");
}); });
} header.col(|ui| {
ui.strong("Name");
});
}).body(|mut body| {
for (pname, sname, s) in songs {
if pname != gui.current_playlist {
continue;
}
match gui.search.get_search() {
(SearchType::Generic, filter) if !filter.is_empty() => {
if !pname.to_lowercase().contains(&filter) {
continue;
}
}
(SearchType::Song, filter) if !filter.is_empty() => {
if !sname.to_lowercase().contains(&filter) {
continue;
}
}
(SearchType::Source, filter) if !filter.is_empty() => {
if !s.get_type().to_string().to_lowercase().contains(&filter) {
continue;
}
}
(SearchType::Url, filter) if !filter.is_empty() => {
if !s.get_url_str().contains(&filter) {
continue;
}
}
(SearchType::Source, _) => (),
(SearchType::Song, _) => (),
(SearchType::Generic, _) => (),
(SearchType::Url, _) => (),
}
body.row(18.0, |mut row| {
//row.col(|ui| {
// ui.label(pname.clone())
// .context_menu(|ui| ContextMenu::ui(gui, ui, &pname, &sname, &s));
//});
row.col(|ui| {
let color =
match s.get_type() {
SongType::Youtube => Color32::from_hex("#FF0000").unwrap(),
SongType::Spotify => Color32::from_hex("#1db954").unwrap(),
SongType::Soundcloud => Color32::from_hex("#F26F23").unwrap()
};
ui.colored_label(color, s.get_type().to_string())
.context_menu(|ui| ContextMenu::ui(gui, ui, &pname, &sname, &s));
});
row.col(|ui| {
ui.hyperlink_to(sname.clone(), s.get_url_str())
.context_menu(|ui| ContextMenu::ui(gui, ui, &pname, &sname, &s));
});
row.response()
.context_menu(|ui| ContextMenu::ui(gui, ui, &pname, &sname, &s));
});
}
});
}); });
}); });
} }
} }

View File

@ -2,6 +2,7 @@ mod windows;
mod components; mod components;
use components::{Component, ComponentUi}; use components::{Component, ComponentUi};
use egui_extras::install_image_loaders;
use windows::{State, WindowIndex, WindowManager}; use windows::{State, WindowIndex, WindowManager};
use crate::{config::ConfigWrapper, downloader::Downloader, manifest::Manifest}; use crate::{config::ConfigWrapper, downloader::Downloader, manifest::Manifest};
@ -10,10 +11,11 @@ use crate::{config::ConfigWrapper, downloader::Downloader, manifest::Manifest};
pub struct Gui { pub struct Gui {
windows: WindowManager, windows: WindowManager,
manifest: Manifest, manifest: Manifest,
filter: String,
downloader: Downloader, downloader: Downloader,
cfg: ConfigWrapper, cfg: ConfigWrapper,
downloading: bool, downloading: bool,
search: components::search_bar::SearchBar,
current_playlist: String,
} }
impl Gui { impl Gui {
@ -33,13 +35,13 @@ impl Gui {
.with_inner_size([400.0, 300.0]) .with_inner_size([400.0, 300.0])
.with_min_inner_size([300.0, 220.0]) .with_min_inner_size([300.0, 220.0])
.with_icon( .with_icon(
eframe::icon_data::from_png_bytes(&include_bytes!("../../../assets/icon.png")[..])?, eframe::icon_data::from_png_bytes(crate::data::APP_ICON_BYTES)?,
), ),
..Default::default() ..Default::default()
}; };
if let Err(e) = eframe::run_native( if let Err(e) = eframe::run_native(
"eframe template", "McMG",
native_options, native_options,
Box::new(|cc| Box::new(Gui::new(cc, manifest, downloader, cfg))), Box::new(|cc| Box::new(Gui::new(cc, manifest, downloader, cfg))),
) { ) {
@ -59,7 +61,7 @@ impl Gui {
impl eframe::App for Gui { impl eframe::App for Gui {
fn update(&mut self, ctx: &egui::Context, _: &mut eframe::Frame) { fn update(&mut self, ctx: &egui::Context, _: &mut eframe::Frame) {
components::nav::NavBar::ui(self, ctx); install_image_loaders(ctx);
{ {
let mut state = State { let mut state = State {
cfg: self.cfg.clone(), cfg: self.cfg.clone(),
@ -72,14 +74,24 @@ impl eframe::App for Gui {
self.manifest = state.manifest; self.manifest = state.manifest;
} }
components::nav::NavBar::ui(self, ctx);
egui::CentralPanel::default().show(ctx, |ui| { egui::CentralPanel::default().show(ctx, |ui| {
let avail_height = ui.available_height();
// The central panel the region left after adding TopPanel's and SidePanel's // The central panel the region left after adding TopPanel's and SidePanel's
//ui.heading(format!("Songs ({})", self.manifest.get_song_count())); //ui.heading(format!("Songs ({})", self.manifest.get_song_count()));
components::song_list::SongList::ui(self, ui); ui.vertical_centered_justified(|ui| {
ui.separator(); ui.with_layout(egui::Layout::top_down_justified(egui::Align::TOP), |ui| {
ui.horizontal(|ui| {
ui.set_height(avail_height);
components::side_nav::SideNav::ui(self, ui);
components::song_list::SongList::ui(self, ui);
});
ui.separator();
ui.with_layout(egui::Layout::bottom_up(egui::Align::LEFT), |ui| { ui.with_layout(egui::Layout::bottom_up(egui::Align::LEFT), |ui| {
egui::warn_if_debug_build(ui); egui::warn_if_debug_build(ui);
});
});
}); });
}); });
} }

View File

@ -1,11 +1,16 @@
use crate::manifest::song::{Song, SongType};
use super::{State, Window}; use super::{State, Window};
#[allow(clippy::pedantic)] #[allow(clippy::pedantic)]
#[derive(Debug, Default)] #[derive(Debug, Default)]
pub struct GuiImportPlaylist { pub struct GuiImportPlaylist {
ed_type: SongType,
ed_name: String, ed_name: String,
ed_url: String, ed_url: String,
urls_to_add: Vec<String>,
playlist_name: String,
} }
@ -16,7 +21,15 @@ impl Window for GuiImportPlaylist {
.open(open) .open(open)
.show(ctx, |ui| { .show(ctx, |ui| {
ui.horizontal(|ui| { ui.horizontal(|ui| {
ui.label("Type: Youtube"); ui.label("Type: ");
egui::ComboBox::from_id_source("new_playlist_window_type")
.selected_text(format!("{:?}", self.ed_type))
.show_ui(ui, |ui| {
ui.selectable_value(&mut self.ed_type, SongType::Youtube, "Youtube");
ui.selectable_value(&mut self.ed_type, SongType::Spotify, "Spotify");
// ui.selectable_value(&mut self.ed_type, SongType::Soundcloud, "Soundcloud");
}
);
}); });
ui.horizontal(|ui| { ui.horizontal(|ui| {
@ -33,6 +46,20 @@ impl Window for GuiImportPlaylist {
} }
}); });
if let Some(url) = self.urls_to_add.pop() {
todo!();
//let client = reqwest::blocking::Client::new();
// let song_name = crate::crawler::spotify::get_song_name(&client, url.clone())?;
//if let Some(playlist) = state.manifest.get_playlist_mut(&self.playlist_name) {
// let mut song = Song::from_url_str(url)?;
// song.set_type(SongType::Spotify);
// playlist.add_song(song_name, song);
//}
//let _ = state.manifest.save(None);
}
if save { if save {
let name = self.ed_name.clone(); let name = self.ed_name.clone();
let url = self.ed_url.clone(); let url = self.ed_url.clone();
@ -40,15 +67,22 @@ impl Window for GuiImportPlaylist {
if state.manifest.get_playlist(&name).is_some() { if state.manifest.get_playlist(&name).is_some() {
log::error!("Playlist {name} already exists"); log::error!("Playlist {name} already exists");
} }
if self.ed_type == SongType::Spotify {
todo!()
//let client = reqwest::blocking::Client::new();
//self.urls_to_add = crate::crawler::spotify::get_playlist_song_urls(&client, self.ed_url.clone())?;
//self.playlist_name = self.ed_name.clone();
//state.manifest.add_playlist(name.clone());
} else if self.ed_type == SongType::Youtube {
let songs = state.downloader.download_playlist_nb(&state.cfg, &url, &name, state.manifest.get_format()).unwrap();
state.manifest.add_playlist(name.clone());
let songs = state.downloader.download_playlist_nb(&state.cfg, &url, &name, state.manifest.get_format()).unwrap(); let playlist = state.manifest.get_playlist_mut(&name).expect("Unreachable");
state.manifest.add_playlist(name.clone());
let playlist = state.manifest.get_playlist_mut(&name).expect("Unreachable"); for (sname, song) in songs {
log::info!("Added: {sname}");
for (sname, song) in songs { playlist.add_song(sname, song);
log::info!("Added: {sname}"); }
playlist.add_song(sname, song);
} }
let _ = state.manifest.save(None); let _ = state.manifest.save(None);
*open = false; *open = false;

View File

@ -1,6 +1,8 @@
use anyhow::bail; use anyhow::bail;
use egui::Color32; use egui::Color32;
use crate::manifest::song::SongType;
use super::{State, Window}; use super::{State, Window};
@ -9,19 +11,20 @@ pub struct GuiSongEditor {
song: (String, String), song: (String, String),
ed_url: String, ed_url: String,
ed_name: String, ed_name: String,
ed_type: SongType
} }
impl Window for GuiSongEditor { impl Window for GuiSongEditor {
fn ui(&mut self, state: &mut State, ctx: &egui::Context, open: &mut bool) -> anyhow::Result<()> { fn ui(&mut self, state: &mut State, ctx: &egui::Context, open: &mut bool) -> anyhow::Result<()> {
let mut save = false; let mut save = false;
let (playlist, song_name) = self.song.clone(); let (playlist_name, song_name) = self.song.clone();
if playlist.is_empty() { if playlist_name.is_empty() {
return Ok(()); return Ok(());
} }
let Some(song) = state.manifest.get_song(&playlist, &song_name) else { let Some(song) = state.manifest.get_song(&playlist_name, &song_name) else {
bail!("Failed to get song (1)"); bail!("Failed to get song (1)");
}; };
let song = song.clone(); let song = song.clone();
@ -36,7 +39,7 @@ impl Window for GuiSongEditor {
ui.label("["); ui.label("[");
ui.hyperlink_to("link", song.get_url().unwrap()); ui.hyperlink_to("link", song.get_url().unwrap());
ui.label("] "); ui.label("] ");
ui.colored_label(Color32::LIGHT_BLUE, &playlist); ui.colored_label(Color32::LIGHT_BLUE, &playlist_name);
ui.label(": "); ui.label(": ");
ui.label(&song_name) ui.label(&song_name)
}); });
@ -44,6 +47,14 @@ impl Window for GuiSongEditor {
ui.horizontal(|ui| { ui.horizontal(|ui| {
ui.label("Type: "); ui.label("Type: ");
ui.label(song.get_type().to_string()); ui.label(song.get_type().to_string());
egui::ComboBox::from_id_source("song_edit_window_type")
.selected_text(format!("{:?}", self.ed_type))
.show_ui(ui, |ui| {
ui.selectable_value(&mut self.ed_type, SongType::Youtube, "Youtube");
ui.selectable_value(&mut self.ed_type, SongType::Spotify, "Spotify");
ui.selectable_value(&mut self.ed_type, SongType::Soundcloud, "Soundcloud");
}
);
}); });
ui.horizontal(|ui| { ui.horizontal(|ui| {
@ -61,21 +72,23 @@ impl Window for GuiSongEditor {
}); });
if save { if save {
{ let song = {
let Some(song) = state.manifest.get_song_mut(&playlist, &song_name) else { let Some(song) = state.manifest.get_song_mut(&playlist_name, &song_name) else {
bail!("Failed to get song (2)"); bail!("Failed to get song (2)");
}; };
song.get_url_str_mut().clone_from(&self.ed_url); song.get_url_str_mut().clone_from(&self.ed_url);
} song.get_type_mut().clone_from(&self.ed_type);
song.clone()
let Some(playlist) = state.manifest.get_playlist_mut(&playlist) else {
bail!("Failed to get playlist");
}; };
let Some(playlist) = state.manifest.get_playlist_mut(&playlist_name) else {
bail!("Failed to get playlist");
};
playlist.remove_song(&song_name); playlist.remove_song(&song_name);
playlist.add_song(self.ed_name.clone(), song); playlist.add_song(self.ed_name.clone(), song.clone());
*open = false; *open = false;
let _ = state.manifest.save(None); let _ = state.manifest.save(None);
} }
@ -85,10 +98,11 @@ impl Window for GuiSongEditor {
} }
impl GuiSongEditor { impl GuiSongEditor {
pub fn set_active_song(&mut self, pname: &str, sname: &str, url: &str) { pub fn set_active_song(&mut self, pname: &str, sname: &str, url: &str, typ: &SongType) {
self.song.0 = pname.to_string(); self.song.0 = pname.to_string();
self.song.1 = sname.to_string(); self.song.1 = sname.to_string();
self.ed_name = sname.to_string(); self.ed_name = sname.to_string();
self.ed_url = url.to_string(); self.ed_url = url.to_string();
self.ed_type = typ.clone();
} }
} }