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",
]
[[package]]
name = "base64"
version = "0.21.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9d297deb1925b89f2ccc13d7635fa0714f12c87adce1c75356b39ca9b7178567"
[[package]]
name = "base64"
version = "0.22.1"
@ -953,6 +959,12 @@ dependencies = [
"unicode-width",
]
[[package]]
name = "color-hex"
version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ecdffb913a326b6c642290a0d0ec8e8d6597291acdc07cc4c9cb4b3635d44cf9"
[[package]]
name = "color_quant"
version = "1.1.0"
@ -1101,6 +1113,12 @@ version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "96a6ac251f4a2aca6b3f91340350eab87ae57c3f127ffeb585e92bd336717991"
[[package]]
name = "data-url"
version = "0.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5c297a1c74b71ae29df00c3e22dd9534821d60eb9af5a0192823fa2acea70c2a"
[[package]]
name = "deranged"
version = "0.3.11"
@ -1190,6 +1208,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "20930a432bbd57a6d55e07976089708d4893f3d556cf42a0d79e9e321fa73b10"
dependencies = [
"bytemuck",
"color-hex",
"serde",
]
@ -1283,9 +1302,12 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1b78779f35ded1a853786c9ce0b43fe1053e10a21ea3b23ebea411805ce41593"
dependencies = [
"egui",
"ehttp",
"enum-map",
"image",
"log",
"mime_guess2",
"resvg",
"serde",
]
@ -1305,6 +1327,20 @@ dependencies = [
"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]]
name = "emath"
version = "0.27.2"
@ -1508,6 +1544,12 @@ dependencies = [
"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]]
name = "fnv"
version = "1.0.7"
@ -1936,6 +1978,15 @@ dependencies = [
"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]]
name = "http"
version = "1.1.0"
@ -2074,6 +2125,12 @@ dependencies = [
"png",
]
[[package]]
name = "imagesize"
version = "0.12.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "029d73f573d8e8d63e6d5020011d3255b28c3ba85d6cf870a07184ed23de9284"
[[package]]
name = "indexmap"
version = "2.5.0"
@ -2207,6 +2264,15 @@ version = "3.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e2db585e1d738fc771bf08a151420d3ed193d9d895a36df7f6f8a9456b911ddc"
[[package]]
name = "kurbo"
version = "0.9.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bd85a5776cd9500c2e2059c8c76c3b01528566b7fcbaf8098b55a33fc298849b"
dependencies = [
"arrayvec",
]
[[package]]
name = "lazy_static"
version = "1.5.0"
@ -2329,11 +2395,13 @@ dependencies = [
"egui_extras",
"env_logger",
"futures",
"html-escape",
"lazy_static",
"libc",
"log",
"notify-rust",
"open",
"regex",
"reqwest",
"serde",
"serde_json",
@ -2873,6 +2941,12 @@ version = "2.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e"
[[package]]
name = "pico-args"
version = "0.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5be167a7af36ee22fe3115051bc51f6e6c7054c9348e28deb4f49bd6f705a315"
[[package]]
name = "pin-project"
version = "1.1.5"
@ -3138,6 +3212,12 @@ version = "0.6.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "20675572f6f24e9e76ef639bc5552774ed45f1c30e2951e1e99c59888861c539"
[[package]]
name = "rctree"
version = "0.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3b42e27ef78c35d3998403c1d26f3efd9e135d3e5121b0a4845cc5cc27547f4f"
[[package]]
name = "redox_syscall"
version = "0.3.5"
@ -3178,9 +3258,9 @@ dependencies = [
[[package]]
name = "regex"
version = "1.10.6"
version = "1.11.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4219d74c6b67a3654a9fbebc4b419e22126d13d2f3c4a07ee0cb61ff79a79619"
checksum = "38200e5ee88914975b69f657f0801b6f6dccafd44fd9326302a4aaeecfacb1d8"
dependencies = [
"aho-corasick",
"memchr",
@ -3190,9 +3270,9 @@ dependencies = [
[[package]]
name = "regex-automata"
version = "0.4.7"
version = "0.4.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "38caf58cc5ef2fed281f89292ef23f6365465ed9a41b7a7754eb4e26496c92df"
checksum = "368758f23274712b504848e9d5a6f010445cc8b87a7cdb4d7cbee666c1288da3"
dependencies = [
"aho-corasick",
"memchr",
@ -3201,9 +3281,9 @@ dependencies = [
[[package]]
name = "regex-syntax"
version = "0.8.4"
version = "0.8.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7a66a03ae7c801facd77a29370b4faec201768915ac14a721ba36f20bc9c209b"
checksum = "2b15c43186be67a4fd63bee50d0303afffcef381492ebe2c5d87f324e1b8815c"
[[package]]
name = "renderdoc-sys"
@ -3217,8 +3297,9 @@ version = "0.12.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f8f4955649ef5c38cc7f9e8aa41761d48fb9677197daea9984dc54f56aad5e63"
dependencies = [
"base64",
"base64 0.22.1",
"bytes",
"futures-channel",
"futures-core",
"futures-util",
"h2",
@ -3254,6 +3335,29 @@ dependencies = [
"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]]
name = "ring"
version = "0.17.8"
@ -3269,6 +3373,12 @@ dependencies = [
"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]]
name = "rustc-demangle"
version = "0.1.24"
@ -3320,6 +3430,7 @@ version = "0.23.13"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f2dabaac7466917e566adb06783a81ca48944c6898a1b08b9374106dd671f4c8"
dependencies = [
"log",
"once_cell",
"ring",
"rustls-pki-types",
@ -3334,7 +3445,7 @@ version = "2.1.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "196fe16b00e106300d3e45ecfcb764fa292a535d7326a29a5875c579c7417425"
dependencies = [
"base64",
"base64 0.22.1",
"rustls-pki-types",
]
@ -3493,6 +3604,21 @@ version = "0.3.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
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]]
name = "slab"
version = "0.4.9"
@ -3633,6 +3759,9 @@ name = "strict-num"
version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6637bab7722d379c8b41ba849228d680cc12d0a45ba1fa2b48f2a30577a06731"
dependencies = [
"float-cmp",
]
[[package]]
name = "strsim"
@ -3646,6 +3775,16 @@ version = "2.6.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
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]]
name = "syn"
version = "1.0.109"
@ -3760,6 +3899,7 @@ dependencies = [
"bytemuck",
"cfg-if",
"log",
"png",
"tiny-skia-path",
]
@ -4019,6 +4159,22 @@ version = "0.9.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
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]]
name = "url"
version = "2.5.2"
@ -4030,6 +4186,56 @@ dependencies = [
"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]]
name = "utf8parse"
version = "0.2.2"
@ -4957,6 +5163,12 @@ version = "0.8.22"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "af4e2e2f7cba5a093896c1e150fbfe177d1883e7448200efb81d40b9d339ef26"
[[package]]
name = "xmlwriter"
version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ec7a2a501ed189703dba8b08142f057e887dfc4b2cc4db2d343ac6376ba3e0b9"
[[package]]
name = "zbus"
version = "3.15.2"

View File

@ -11,16 +11,18 @@ anyhow = "1.0.81"
camino = "1.1.6"
clap = { version = "4.5.4", features = ["derive"] }
eframe = "0.27.2"
egui = "0.27.2"
egui_extras = "0.27.2"
egui = { version = "0.27.2", features = ["color-hex"] }
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"
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_json = "1.0.115"
# 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");
let mut cmd = match song.get_type() {
&SongType::Youtube => {
log::debug!("Song {} is from yotube", song.get_url_str());
SongType::Youtube | SongType::Soundcloud=> {
log::debug!("Song {} is from youtube or sondclound", song.get_url_str());
let mut cmd = tokio::process::Command::new(&cfg.cfg.ytdlp.path);
cmd.args([
"-x",
@ -168,7 +168,7 @@ impl Downloader {
]);
cmd
}
url @ SongType::Soundcloud => {
url => {
log::error!("Unknown or unsupported hostname '{:?}'", url);
return Ok(());
}

View File

@ -1,4 +1,5 @@
#![feature(downcast_unchecked)]
#![feature(async_closure)]
use config::ConfigWrapper;
@ -13,12 +14,14 @@ mod constants;
mod process_manager;
mod ui;
mod prompt;
mod data;
fn main() {
#[tokio::main]
async fn main() {
let Ok(cfg) = ConfigWrapper::parse() else {
return;
};
let mut manifest = match manifest::Manifest::load_new(&cfg.cli.manifest.clone().into_std_path_buf()) {
Ok(m) => m,
Err(e) => {

View File

@ -18,7 +18,6 @@ lazy_static::lazy_static!(
static PROC_INC: AtomicUsize = AtomicUsize::new(0);
pub fn add_proc(mut cmd: Command, msg: String) -> anyhow::Result<()> {
let mut proc = cmd.spawn()?;
let id = PROC_INC.fetch_add(1, Ordering::AcqRel);
@ -65,7 +64,7 @@ pub fn purge_done_procs() -> usize {
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> {
// 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

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) {
if ui.button("Edit").clicked() {
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);
ui.close_menu();
}

View File

@ -4,6 +4,8 @@ use super::Gui;
pub mod nav;
pub mod song_list;
pub mod context_menu;
pub mod side_nav;
pub mod search_bar;
pub trait Component {
fn ui(gui: &mut Gui, ctx: &egui::Context);
@ -12,3 +14,7 @@ pub trait Component {
pub trait ComponentUi {
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 super::{context_menu::ContextMenu, ComponentUi};
use super::{context_menu::ContextMenu, search_bar::SearchType, ComponentUi};
pub struct SongList;
#[derive(Debug, Default)]
pub struct SongList {
}
impl ComponentUi for SongList {
fn ui(gui: &mut crate::ui::gui::Gui, ui: &mut egui::Ui) {
let fltr_by;
let filter_clean;
if gui.filter.starts_with("playlist:") {
fltr_by = "playlist";
filter_clean = gui.filter.strip_prefix("playlist:").unwrap_or("").to_string().to_lowercase();
} else if gui.filter.starts_with("source:") {
fltr_by = "source";
filter_clean = gui.filter.strip_prefix("source:").unwrap_or("").to_string().to_lowercase();
} else if gui.filter.starts_with("url:") {
fltr_by = "url";
filter_clean = gui.filter.strip_prefix("url:").unwrap_or("").to_string();
} else {
fltr_by = "";
filter_clean = gui.filter.clone();
}
ui.vertical(|ui| {
ui.horizontal(|ui| {
ui.colored_label(Color32::from_hex("#4444aa").unwrap(), "Filter: ");
ui.text_edit_singleline(&mut gui.filter);
});
});
ui.vertical(|ui| {
let available_height = ui.available_height();
let table = TableBuilder::new(ui)
.striped(true)
.cell_layout(egui::Layout::left_to_right(egui::Align::Center))
.resizable(true)
//.column(Column::auto())
.column(Column::auto())
//.column(
// Column::remainder()
// .at_least(40.0)
// .clip(true)
// .resizable(true),
//)
.column(Column::auto())
.column(Column::remainder())
//.column(Column::remainder())
.min_scrolled_height(0.0)
.max_scroll_height(available_height)
.sense(egui::Sense::click());
{
use crate::ui::gui::components::ComponentUiMut;
let mut search = gui.search.clone();
search.ui(gui, ui);
gui.search = search;
}
let playlists = gui.manifest.get_playlists().clone();
ui.vertical(|ui| {
let available_height = ui.available_height();
let table = TableBuilder::new(ui)
.striped(true)
.cell_layout(egui::Layout::left_to_right(egui::Align::Center))
.resizable(true)
//.column(Column::auto())
//.column(Column::auto())
//.column(
// Column::remainder()
// .at_least(40.0)
// .clip(true)
// .resizable(true),
//)
.column(Column::auto())
.column(Column::remainder())
//.column(Column::remainder())
.min_scrolled_height(0.0)
.max_scroll_height(available_height)
.sense(egui::Sense::click());
let playlists = gui.manifest.get_playlists().clone();
let songs = {
let mut songs = Vec::new();
for (pname, p) in playlists {
for (sname, s) in p {
songs.push((pname.clone(), sname, s));
let songs = {
let mut songs = Vec::new();
for (pname, p) in playlists {
for (sname, s) in p {
songs.push((pname.clone(), sname, s));
}
}
}
songs
};
table.header(20.0, |mut header| {
// header.col(|_|{});
header.col(|ui| {
ui.strong("Playlist");
});
header.col(|ui| {
ui.strong("Source");
});
header.col(|ui| {
ui.strong("Name");
});
}).body(|mut body| {
for (pname, sname, s) in songs {
if fltr_by == "playlist" && !filter_clean.is_empty() {
if !pname.to_lowercase().contains(&filter_clean) {
continue;
}
} else if fltr_by == "type" && !filter_clean.is_empty(){
if !s.get_type().to_string().to_lowercase().contains(&filter_clean) {
continue;
}
} else if fltr_by == "url" && !filter_clean.is_empty(){
if !s.get_url_str().contains(&filter_clean) {
continue;
}
} else if !filter_clean.is_empty() && !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));
songs.sort_by_key(|song| song.1.to_lowercase());
songs
};
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;
use components::{Component, ComponentUi};
use egui_extras::install_image_loaders;
use windows::{State, WindowIndex, WindowManager};
use crate::{config::ConfigWrapper, downloader::Downloader, manifest::Manifest};
@ -10,10 +11,11 @@ use crate::{config::ConfigWrapper, downloader::Downloader, manifest::Manifest};
pub struct Gui {
windows: WindowManager,
manifest: Manifest,
filter: String,
downloader: Downloader,
cfg: ConfigWrapper,
downloading: bool,
search: components::search_bar::SearchBar,
current_playlist: String,
}
impl Gui {
@ -33,13 +35,13 @@ impl Gui {
.with_inner_size([400.0, 300.0])
.with_min_inner_size([300.0, 220.0])
.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()
};
if let Err(e) = eframe::run_native(
"eframe template",
"McMG",
native_options,
Box::new(|cc| Box::new(Gui::new(cc, manifest, downloader, cfg))),
) {
@ -59,7 +61,7 @@ impl Gui {
impl eframe::App for Gui {
fn update(&mut self, ctx: &egui::Context, _: &mut eframe::Frame) {
components::nav::NavBar::ui(self, ctx);
install_image_loaders(ctx);
{
let mut state = State {
cfg: self.cfg.clone(),
@ -72,14 +74,24 @@ impl eframe::App for Gui {
self.manifest = state.manifest;
}
components::nav::NavBar::ui(self, ctx);
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
//ui.heading(format!("Songs ({})", self.manifest.get_song_count()));
components::song_list::SongList::ui(self, ui);
ui.separator();
ui.with_layout(egui::Layout::bottom_up(egui::Align::LEFT), |ui| {
egui::warn_if_debug_build(ui);
ui.vertical_centered_justified(|ui| {
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| {
egui::warn_if_debug_build(ui);
});
});
});
});
}

View File

@ -1,11 +1,16 @@
use crate::manifest::song::{Song, SongType};
use super::{State, Window};
#[allow(clippy::pedantic)]
#[derive(Debug, Default)]
pub struct GuiImportPlaylist {
ed_type: SongType,
ed_name: String,
ed_url: String,
urls_to_add: Vec<String>,
playlist_name: String,
}
@ -16,7 +21,15 @@ impl Window for GuiImportPlaylist {
.open(open)
.show(ctx, |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| {
@ -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 {
let name = self.ed_name.clone();
let url = self.ed_url.clone();
@ -40,15 +67,22 @@ impl Window for GuiImportPlaylist {
if state.manifest.get_playlist(&name).is_some() {
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();
state.manifest.add_playlist(name.clone());
let playlist = state.manifest.get_playlist_mut(&name).expect("Unreachable");
for (sname, song) in songs {
log::info!("Added: {sname}");
playlist.add_song(sname, song);
let playlist = state.manifest.get_playlist_mut(&name).expect("Unreachable");
for (sname, song) in songs {
log::info!("Added: {sname}");
playlist.add_song(sname, song);
}
}
let _ = state.manifest.save(None);
*open = false;

View File

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