Compare commits

..

No commits in common. "712e918e081b7110ec7b3d4e044fbfad002f620b" and "2ddb2b8a89e36c037f67a48968708624f1344d4b" have entirely different histories.

22 changed files with 160 additions and 410 deletions

View File

@ -1,37 +0,0 @@
on: [push, pull_request]
name: Continuous integration
jobs:
#check:
# name: Check
# runs-on: ubuntu-latest
# steps:
# - uses: actions/checkout@v4
# - uses: actions-rust-lang/setup-rust-toolchain@v1
# - run: cargo check
#test:
# name: Test Suite
# runs-on: ubuntu-latest
# steps:
# - uses: actions/checkout@v4
# - uses: actions-rust-lang/setup-rust-toolchain@v1
# - run: cargo test
clippy:
name: Clippy
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions-rust-lang/setup-rust-toolchain@v1
- run: rustup component add clippy
- run: cargo clippy
build:
name: build
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions-rust-lang/setup-rust-toolchain@v1
- run: cargo build

31
DEV.md
View File

@ -6,11 +6,11 @@ Todo types:
[BUG] \[loc\](/src/...) - Bugfix, mandatory location
[GIT] \[loc\](/src/...) - Git related feature, optional location
Todos that have been merged have to add `**DONE**` prefix to the type
Fixed todos have to add `**DONE**` prefix to the type
### #0
**DONE** ~~[FEAT] - [side_nav](/src/ui/gui/components/mod.rs)
Add dropdown menu for `side_nav` playlist~~
[FEAT] - [side_nav](/src/ui/gui/components/side_nav.rs)
Add dropdown menu for `side_nav` playlist
### #1
[FEAT] - [gui](/src/ui/gui/)
@ -25,7 +25,7 @@ Better styling
Add music player footer
### #4
[FEAT] - [gui](/src/ui/gui/components/song_list/mod.rs)
[FEAT] - [gui](/src/ui/gui/components/song_list.rs)
Add numbers to `song_list` table
### #5
@ -33,7 +33,7 @@ Add numbers to `song_list` table
Add music player logic
### #6
[FEAT] - [manifest](/src/manifest/mod.rs)
[FEAT] - [manifest](/src/manifest/)
Add support for images by possibly storing the images in json or custom format
### #7
@ -47,10 +47,9 @@ standalone one, moving default paths and using [#10](#10):
| music-output | `~/Music/mcmg/*` | `%userprofile%/Music/mcmg/*` |
### #8
[FEAT] - [cli](/src/ui/cli/mod.rs)
[FEAT] - [cli](/src/ui/cli/)
add missing commands that are available via gui
- Downloading single songs, from the manifest and standalone as an utility
- removing playlists, single songs
### #9
[BUG] - [utils](/src/util.rs)
@ -62,24 +61,12 @@ Add an utility to detect if this is ran as a standalone application
### #11
[FEAT] - [downloader](/src/downloader.rs)
Refractor downloader for better readability and usage
Refractor for better readability and usage
### #12
[GIT]
Add ci that runs clippy and builds in release mode
### #13
[FEAT] - [assets](/assets/)
Make new icons for the app, preferably svg, except the app icon must be both svg and png
Constant todos:
TODO: Run code through clippy and fix any errors
### #14
[FEAT] - [manifest](/src/manifest/) [downloader](/src/downloader.rs)
Add custom type for downloading, one for simple http downloads, and archived ones (zip, 7z, etc)
### #15
[FEAT] - [dependencies](/Cargo.toml)
Clean up dependencies, remove unneeded features for executable size
### #16
[FEAT] - [song_list](/src/ui/gui/components/song_list/mod.rs)
Add a checkmark or an X depending on if the song is downloaded to disk

View File

@ -10,7 +10,7 @@ The music is downloaded via [ytdlp](#Dependencies) and [spotdl](#Dependencies),
## Offline usage
Your whole music library is downloaded to your music folder (unless its being ran in standalone mode). Saved in your selected format.
All of the info required to download your songs is stored in 1 file (!). So all you need to backup all of your music is just 1 relatively small file AND you get the added benefit of easily moving your music between devices with just 1 manifest file, 1 executable (and 3 [dependencies](#dependencies)). Just press `download all` and see as all of your playlists appear in your hard drive, powered by Open Source software.
All of the info required to download your songs is stored in 1 file (!). So all you need to backup all of your music is just 1 relatively small file AND you get the added benefit of easily moving your music between devices with just 1 manifest file, 1 executable (and 3 [dependencies](#dependencies)). Just press `download all` and see as all of your playlists appear in your hard drive, powereded by Open Source software.
## Dependencies
[ffmpeg](https://ffmpeg.org/): To convert your music files to your desired format.

View File

@ -1,2 +0,0 @@
[toolchain]
channel="nightly"

View File

@ -82,10 +82,10 @@ impl Downloader {
for (name, playlist) in manifest.get_playlists() {
for (song_name, song) in playlist.get_songs() {
self.download_song(cfg, song_name, song, name, format)?;
self.count += crate::process_manager::wait_for_procs_until(10)?;
self.count += crate::process_manager::wait_for_procs_untill(10)?;
}
}
self.count += crate::process_manager::wait_for_procs_until(0)?;
self.count += crate::process_manager::wait_for_procs_untill(0)?;
Ok(self.count)
}
@ -168,6 +168,10 @@ impl Downloader {
]);
cmd
}
url => {
log::error!("Unknown or unsupported hostname '{:?}'", url);
return Ok(());
}
};
if log::max_level() < Level::Debug {

View File

@ -66,12 +66,6 @@ impl Manifest {
pub fn get_playlists_mut(&mut self) -> &mut HashMap<String, playlist::Playlist> {
&mut self.playlists
}
pub fn remove_playlist(&mut self, playlist_name: &String) -> Option<playlist::Playlist> {
self.playlists.remove(playlist_name)
}
pub fn remove_song(&mut self, playlist_name: &String, song_name: &String) -> Option<Song> {
self.get_playlist_mut(playlist_name)?.remove_song(song_name)
}
pub fn get_song_count(&self) -> usize {
let mut count = 0;
for v in self.playlists.values() {

View File

@ -65,7 +65,7 @@ pub fn purge_done_procs() -> usize {
}
/// Waits for processes to finish until the proc count is lower or equal to `max`
pub fn wait_for_procs_until(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: So its also kinda really slow
let mut finish_count = 0;

View File

@ -27,7 +27,7 @@ pub fn song(cfg: &ConfigWrapper, manifest: &mut Manifest, downloader: &mut Downl
if should_download {
downloader.download_song(cfg, name, &song, playlist, manifest.get_format())?;
crate::process_manager::wait_for_procs_until(0)?;
crate::process_manager::wait_for_procs_untill(0)?;
}
Ok(())

View File

@ -0,0 +1,57 @@
use egui::{Color32, RichText};
use crate::{manifest::song::Song, ui::gui::windows::{song_edit::GuiSongEditor, WindowIndex}};
pub struct ContextMenu;
// NOTE: This should be a component but theres no easy way to do that, so we just make it folow the
// trait manually, ish, more like a convention
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(), song.get_type());
gui.windows.open(WindowIndex::SongEdit, true);
ui.close_menu();
}
if ui.button("Download").clicked() {
if let Err(e) = gui.downloader.download_song_nb(&gui.cfg, pname, sname, song, gui.manifest.get_format()) {
log::error!("{e}");
gui.throw_error(format!("Failed to download song {sname}: {e}"));
}
ui.close_menu();
}
if ui.button("Open Source").clicked() {
if let Err(e) = open::that(song.get_url_str()) {
log::error!("{e}");
gui.throw_error(format!("Failed to open song source: {e}"));
}
ui.close_menu();
}
if ui.button("Play").clicked() {
let p = crate::util::get_song_path(pname, sname, gui.manifest.get_format());
if !p.exists() {
gui.throw_error("Song does not exist on disk".to_string());
} else if let Err(e) = open::that(p) {
log::error!("{e}");
gui.throw_error(format!("Failed to play song: {e}"));
}
ui.close_menu();
}
if ui.button("Delete from disk").clicked() {
let p = crate::util::get_song_path(pname, sname, gui.manifest.get_format());
if p.exists() {
if let Err(e) = std::fs::remove_file(p) {
gui.throw_error(format!("Failed to delete file: {e}"));
}
}
ui.close_menu();
}
if ui.button(RichText::new("Delete").color(Color32::RED)).clicked() {
gui.throw_error("TODO");
ui.close_menu();
}
}
}

View File

@ -3,6 +3,7 @@ use super::Gui;
pub mod nav;
pub mod song_list;
pub mod context_menu;
pub mod side_nav;
pub mod search_bar;
@ -17,8 +18,3 @@ pub trait ComponentUi {
pub trait ComponentUiMut {
fn ui(&mut self, gui: &mut Gui, ui: &mut egui::Ui);
}
pub trait ComponentContextMenu {
type Data;
fn ui(gui: &mut Gui, ui: &mut egui::Ui, data: &Self::Data);
}

View File

@ -2,7 +2,10 @@ use crate::ui::gui::{windows::WindowIndex, Gui};
use super::Component;
#[allow(clippy::pedantic)]
pub struct NavBar;
#[warn(clippy::pedantic)]
impl Component for NavBar {
fn ui(gui: &mut Gui, ctx: &egui::Context) {

View File

@ -0,0 +1,46 @@
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()).selectable(false);
if ui.add(button).clicked() {
gui.current_playlist = pname.to_string();
}
});
});
}
});
}
// #333377
}

View File

@ -1,55 +0,0 @@
use egui::{Color32, RichText};
use crate::ui::gui::{components::ComponentContextMenu, windows::{self, WindowIndex}};
#[derive(Debug)]
pub struct ContextMenu;
impl ComponentContextMenu for ContextMenu {
type Data = String; // Playlist name
fn ui(gui: &mut crate::ui::gui::Gui, ui: &mut egui::Ui, playlist_name: &Self::Data) {
if ui.button("Edit").clicked() {
ui.close_menu();
}
if ui.button("Download all").clicked() {
let Some(playlist) = gui.manifest.get_playlist(playlist_name) else {
gui.throw_error(format!("Playlist not found: {}", playlist_name));
ui.close_menu();
return;
};
for (song_name, song) in playlist.get_songs() {
if let Err(e) = gui.downloader.download_song_nb(&gui.cfg, playlist_name, song_name, song, gui.manifest.get_format()) {
gui.throw_error(format!("Could not download song: {e}"));
ui.close_menu();
return;
}
}
ui.close_menu();
}
if ui.button("Delete from disk").clicked() {
let p = crate::util::get_playlist_path(playlist_name);
if p.exists() {
if let Err(e) = std::fs::remove_dir_all(p) {
gui.throw_error(format!("Failed to delete directory: {e}"));
}
}
ui.close_menu();
}
if ui.button(RichText::new("Delete").color(Color32::RED)).clicked() {
let w = gui.windows.get_window::<windows::confirm::ConfirmW>(WindowIndex::Confirm);
w.set_message(
"side_nav_playlist_manifest_delete",
"This will delete the playlist from the manifest file. This is NOT reversible",
&[playlist_name.clone()]
);
gui.windows.open(WindowIndex::Confirm, true);
ui.close_menu();
}
}
}

View File

@ -1,72 +0,0 @@
use egui::{Color32, Label, RichText, Sense};
use crate::ui::gui::windows::{self, WindowIndex};
use super::{ComponentContextMenu, ComponentUi};
mod context_menu;
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))
.context_menu(|ui| context_menu::ContextMenu::ui(gui, ui, &pname));
ui.horizontal(|ui| {
let text = if gui.current_playlist == *pname {
RichText::new(&pname).color(tint)
} else {
RichText::new(&pname)
};
let button = Label::new(text).sense(Sense::click()).selectable(false);
let button = ui.add(button);
if button.clicked() {
gui.current_playlist = pname.to_string();
}
button.context_menu(|ui| context_menu::ContextMenu::ui(gui, ui, &pname));
});
});
}
});
check_if_needs_delete(gui);
}
// #333377
}
fn check_if_needs_delete(gui: &mut crate::ui::gui::Gui) {
// Check for items that need to be deleted
let (id, resp, data) = gui.windows.get_window::<windows::confirm::ConfirmW>(WindowIndex::Confirm).get_response();
match (id.as_str(), resp) {
("side_nav_playlist_manifest_delete", Some(true)) => {
gui.manifest.remove_playlist(&data[0]);
let _ = gui.manifest.save(None);
gui.windows.get_window::<windows::confirm::ConfirmW>(WindowIndex::Confirm).reset();
}
("side_nav_playlist_manifest_delete", Some(false)) => {
log::debug!("FALSE");
gui.windows.get_window::<windows::confirm::ConfirmW>(WindowIndex::Confirm).reset();
}
_ => ()
}
}

View File

@ -1,14 +1,13 @@
use egui::Color32;
use egui_extras::{Column, TableBuilder};
use crate::{manifest::song::SongType, ui::gui::windows::{self, WindowIndex}};
use crate::manifest::song::SongType;
use super::{search_bar::SearchType, ComponentContextMenu, ComponentUi};
mod context_menu;
use super::{context_menu::ContextMenu, search_bar::SearchType, ComponentUi};
#[derive(Debug, Default)]
pub struct SongList;
pub struct SongList {
}
impl ComponentUi for SongList {
fn ui(gui: &mut crate::ui::gui::Gui, ui: &mut egui::Ui) {
@ -26,8 +25,17 @@ impl ComponentUi for SongList {
.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());
@ -47,6 +55,10 @@ impl ComponentUi for SongList {
};
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");
});
@ -85,8 +97,11 @@ impl ComponentUi for SongList {
(SearchType::Url, _) => (),
}
body.row(18.0, |mut row| {
let song_info = context_menu::SongInfo::new(&pname, &sname, &s);
//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() {
@ -96,41 +111,20 @@ impl ComponentUi for SongList {
};
ui.colored_label(color, s.get_type().to_string())
.context_menu(|ui| context_menu::ContextMenu::ui(gui, ui, &song_info));
.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| context_menu::ContextMenu::ui(gui, ui, &song_info));
.context_menu(|ui| ContextMenu::ui(gui, ui, &pname, &sname, &s));
});
row.response()
.context_menu(|ui| context_menu::ContextMenu::ui(gui, ui, &song_info));
.context_menu(|ui| ContextMenu::ui(gui, ui, &pname, &sname, &s));
});
}
});
});
});
check_if_needs_delete(gui);
}
}
fn check_if_needs_delete(gui: &mut crate::ui::gui::Gui) {
// Check for items that need to be deleted
let (id, resp, data) = gui.windows.get_window::<windows::confirm::ConfirmW>(WindowIndex::Confirm).get_response();
match (id.as_str(), resp) {
("song_list_song_manifest_delete", Some(true)) => {
gui.manifest.remove_song(&data[0], &data[1]);
let _ = gui.manifest.save(None);
gui.windows.get_window::<windows::confirm::ConfirmW>(WindowIndex::Confirm).reset();
}
("song_list_song_manifest_delete", Some(false)) => {
log::debug!("FALSE");
gui.windows.get_window::<windows::confirm::ConfirmW>(WindowIndex::Confirm).reset();
}
_ => ()
}
}

View File

@ -1,96 +0,0 @@
use egui::{Color32, RichText};
use crate::{manifest::song::{Song, SongType}, ui::gui::windows::{self, song_edit::GuiSongEditor, WindowIndex}};
use super::ComponentContextMenu;
pub struct ContextMenu;
pub struct SongInfo {
pname: String,
sname: String,
song: Song,
}
impl SongInfo {
pub fn new(pname: &str, sname: &str, song: &Song) -> Self {
Self {
pname: pname.to_string(),
sname: sname.to_string(),
song: song.clone()
}
}
pub fn playlist_name(&self) -> &String {
&self.pname
}
pub fn song_name(&self) -> &String {
&self.sname
}
pub fn song_url(&self) -> &String {
self.song.get_url_str()
}
pub fn song_type(&self) -> &SongType {
self.song.get_type()
}
pub fn song(&self) -> &Song {
&self.song
}
}
impl ComponentContextMenu for ContextMenu {
type Data = SongInfo;
fn ui(gui: &mut crate::ui::gui::Gui, ui: &mut egui::Ui, data: &Self::Data) {
if ui.button("Edit").clicked() {
let w = gui.windows.get_window::<GuiSongEditor>(WindowIndex::SongEdit);
w.set_active_song(data.playlist_name(), data.song_name(), data.song_url(), data.song_type());
gui.windows.open(WindowIndex::SongEdit, true);
ui.close_menu();
}
if ui.button("Download").clicked() {
if let Err(e) = gui.downloader.download_song_nb(&gui.cfg, data.playlist_name(), data.song_name(), data.song(), gui.manifest.get_format()) {
log::error!("{e}");
gui.throw_error(format!("Failed to download song {}: {e}", data.song_name()));
}
ui.close_menu();
}
if ui.button("Open Source").clicked() {
if let Err(e) = open::that(data.song_url()) {
log::error!("{e}");
gui.throw_error(format!("Failed to open song source: {e}"));
}
ui.close_menu();
}
if ui.button("Play").clicked() {
let p = crate::util::get_song_path(data.playlist_name(), data.song_name(), gui.manifest.get_format());
if !p.exists() {
gui.throw_error("Song does not exist on disk".to_string());
} else if let Err(e) = open::that(p) {
log::error!("{e}");
gui.throw_error(format!("Failed to play song: {e}"));
}
ui.close_menu();
}
if ui.button("Delete from disk").clicked() {
let p = crate::util::get_song_path(data.playlist_name(), data.song_name(), gui.manifest.get_format());
if p.exists() {
if let Err(e) = std::fs::remove_file(p) {
gui.throw_error(format!("Failed to delete file: {e}"));
}
}
ui.close_menu();
}
if ui.button(RichText::new("Delete").color(Color32::RED)).clicked() {
let w = gui.windows.get_window::<windows::confirm::ConfirmW>(WindowIndex::Confirm);
w.set_message(
"song_list_song_manifest_delete",
"This will delete the song from the manifest file. This is NOT reversible",
&[data.playlist_name().clone(), data.song_name().clone()]
);
gui.windows.open(WindowIndex::Confirm, true);
ui.close_menu();
ui.close_menu();
}
}
}

View File

@ -51,9 +51,10 @@ impl Gui {
Ok(())
}
#[allow(clippy::pedantic)]
pub fn throw_error<S: ToString>(&mut self, text: S) {
let w = self.windows.get_window::<windows::error::GuiError>(WindowIndex::Error);
w.set_error_message(text);
w.set_error_message(&text);
self.windows.open(WindowIndex::Error, true);
}
}
@ -93,7 +94,5 @@ impl eframe::App for Gui {
});
});
});
// Make sure we dont wait for any updates cause we depend on the gui code for downloads
ctx.request_repaint();
}
}

View File

@ -1,57 +0,0 @@
use egui::{Color32, Label, RichText};
use super::{State, Window};
#[allow(clippy::pedantic)]
#[derive(Debug, Default)]
pub struct ConfirmW {
id: String,
text: String,
response: Option<bool>,
data: Vec<String>
}
impl Window for ConfirmW {
fn ui(&mut self, _: &mut State, ctx: &egui::Context, open: &mut bool) -> anyhow::Result<()> {
let mut should_close = false;
egui::Window::new("Are you sure?").open(open).show(ctx, |ui| {
ui.vertical(|ui| {
ui.label(RichText::new("Are you sure you want to do this?").size(15.0).color(Color32::BLUE));
ui.horizontal(|ui| {
ui.add(Label::new(self.text.clone()).wrap(true));
});
ui.horizontal(|ui| {
if ui.button("Cancel").clicked() {
self.response = Some(false);
should_close = true;
} else if ui.button("Continue").clicked() {
self.response = Some(true);
should_close = true;
}
})
})
});
if should_close {
*open = false;
}
Ok(())
}
}
impl ConfirmW {
pub fn set_message<S: ToString>(&mut self, new_id: S, text: S, data: &[String]) {
self.text = text.to_string();
self.id = new_id.to_string();
self.data = data.to_vec();
}
pub fn get_response(&self) -> (&String, &Option<bool>, &Vec<String>) {
(&self.id, &self.response, &self.data)
}
pub fn reset(&mut self) {
self.id.clear();
self.text.clear();
self.response = None;
}
}

View File

@ -16,7 +16,7 @@ impl Window for GuiError {
.open(open)
.show(ctx, |ui| {
ui.vertical(|ui| {
ui.label(RichText::new("Error:").size(15.0).color(Color32::RED));
ui.label(RichText::new("Error:").size(30.0).color(Color32::RED));
ui.horizontal(|ui| {
ui.add(Label::new(self.text.clone()).wrap(true));
})
@ -27,7 +27,7 @@ impl Window for GuiError {
}
impl GuiError {
pub fn set_error_message<S: ToString>(&mut self, text: S) {
pub fn set_error_message<S: ToString>(&mut self, text: &S) {
self.text = text.to_string();
}
}

View File

@ -1,4 +1,4 @@
use crate::manifest::song::SongType;
use crate::manifest::song::{Song, SongType};
use super::{State, Window};
@ -9,8 +9,8 @@ pub struct GuiImportPlaylist {
ed_type: SongType,
ed_name: String,
ed_url: String,
//urls_to_add: Vec<String>,
// playlist_name: String,
urls_to_add: Vec<String>,
playlist_name: String,
}
@ -46,8 +46,8 @@ impl Window for GuiImportPlaylist {
}
});
//if let Some(_) = self.urls_to_add.pop() {
// todo!();
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())?;
@ -57,7 +57,7 @@ impl Window for GuiImportPlaylist {
// playlist.add_song(song_name, song);
//}
//let _ = state.manifest.save(None);
//}
}
if save {

View File

@ -5,7 +5,6 @@ pub mod song_edit;
pub mod error;
pub mod import_playlist;
pub mod song_new;
pub mod confirm;
pub trait Window: std::fmt::Debug {
fn ui(&mut self, state: &mut State, ctx: &egui::Context, open: &mut bool) -> anyhow::Result<()>;
@ -17,7 +16,6 @@ pub enum WindowIndex {
ImportPlaylist,
SongEdit,
SongNew,
Confirm
}
@ -40,7 +38,6 @@ impl WindowManager {
windows.insert(WindowIndex::ImportPlaylist, Box::<import_playlist::GuiImportPlaylist>::default());
windows.insert(WindowIndex::SongEdit, Box::<song_edit::GuiSongEditor>::default());
windows.insert(WindowIndex::SongNew, Box::<song_new::GuiNewSong>::default());
windows.insert(WindowIndex::Confirm, Box::<confirm::ConfirmW>::default());
Self {
windows,
..Default::default()

View File

@ -70,12 +70,4 @@ pub fn get_song_path/*<P: TryInto<PathBuf>>*/(/*basepath: Option<P>,*/ pname: &S
path.push(sname);
path.set_extension(format.to_string());
path
}
pub fn get_playlist_path(pname: &String) -> PathBuf {
let mut path = std::env::current_dir().unwrap_or_default();
// TODO: Get this from cfg
path.push("out");
path.push(pname);
path
}