implement functionality to save queues to playlists

This commit is contained in:
Henrik Friedrichsen
2019-03-24 16:31:49 +01:00
parent 212edcb18c
commit e0f7b5c156
6 changed files with 113 additions and 11 deletions

View File

@@ -78,7 +78,7 @@ fn setup_logging(filename: &str) -> Result<(), fern::InitError> {
fn main() {
let matches = App::new("ncspot")
.version("0.1.0")
.author("Henrik Friedrichsen <henrik@affekt.org>")
.author("Henrik Friedrichsen <henrik@affekt.org> and contributors")
.about("cross-platform ncurses Spotify client")
.arg(
Arg::with_name("debug")
@@ -152,7 +152,7 @@ fn main() {
let playlistsview = ui::playlists::PlaylistView::new(&playlists, queue.clone());
let queueview = ui::queue::QueueView::new(queue.clone());
let queueview = ui::queue::QueueView::new(queue.clone(), playlists.clone());
let status = ui::statusbar::StatusBar::new(queue.clone(), spotify.clone());

View File

@@ -1,7 +1,7 @@
use std::iter::Iterator;
use std::ops::Deref;
use std::path::PathBuf;
use std::sync::{Arc, RwLock};
use std::sync::{Arc, RwLock, RwLockReadGuard};
use rspotify::spotify::model::playlist::SimplifiedPlaylist;
@@ -58,6 +58,12 @@ impl Playlists {
}
}
pub fn items(&self) -> RwLockReadGuard<Vec<Playlist>> {
self.store
.read()
.expect("could not readlock listview content")
}
pub fn load_cache(&self) {
if let Ok(contents) = std::fs::read_to_string(&self.cache_path) {
debug!(
@@ -142,6 +148,22 @@ impl Playlists {
store.len() - 1
}
pub fn overwrite_playlist(&self, id: &str, tracks: &Vec<Track>) {
debug!("saving {} tracks to {}", tracks.len(), id);
self.spotify.overwrite_playlist(id, &tracks);
self.fetch_playlists();
self.save_cache();
}
pub fn save_playlist(&self, name: &str, tracks: &Vec<Track>) {
debug!("saving {} tracks to new list {}", tracks.len(), name);
match self.spotify.create_playlist(name, None, None) {
Some(id) => self.overwrite_playlist(&id, &tracks),
None => error!("could not create new playlist.."),
}
}
pub fn fetch_playlists(&self) {
debug!("loading playlists");
let mut stale_lists = self.store.read().unwrap().clone();

View File

@@ -17,7 +17,7 @@ macro_rules! load_color {
pub fn load(cfg: &Config) -> Theme {
let mut palette = Palette::default();
let borders = BorderStyle::None;
let borders = BorderStyle::Simple;
palette[Background] = load_color!(cfg, background, TerminalDefault);
palette[View] = load_color!(cfg, background, TerminalDefault);

View File

@@ -1,5 +1,5 @@
use std::cmp::{max, min};
use std::sync::{Arc, RwLock};
use std::sync::{Arc, RwLock, RwLockReadGuard};
use cursive::align::HAlign;
use cursive::event::{Event, EventResult, MouseButton, MouseEvent};
@@ -54,6 +54,12 @@ impl<I: ListItem> ListView<I> {
let new = self.selected as i32 + delta;
self.move_focus_to(max(new, 0) as usize);
}
pub fn content(&self) -> RwLockReadGuard<Vec<I>> {
self.content
.read()
.expect("could not readlock listview content")
}
}
impl<I: ListItem> View for ListView<I> {

View File

@@ -1,25 +1,98 @@
use cursive::traits::Identifiable;
use cursive::event::{Callback, Event, EventResult};
use cursive::traits::{Boxable, Identifiable, View};
use cursive::view::ViewWrapper;
use cursive::views::IdView;
use cursive::views::{Dialog, EditView, IdView, ScrollView, SelectView};
use cursive::Cursive;
use std::sync::Arc;
use playlists::Playlists;
use queue::Queue;
use track::Track;
use ui::listview::ListView;
use ui::modal::Modal;
pub struct QueueView {
list: IdView<ListView<Track>>,
playlists: Arc<Playlists>,
}
impl QueueView {
pub fn new(queue: Arc<Queue>) -> QueueView {
pub fn new(queue: Arc<Queue>, playlists: Arc<Playlists>) -> QueueView {
let list = ListView::new(queue.queue.clone(), queue.clone()).with_id("queue_list");
QueueView { list: list }
QueueView {
list: list,
playlists: playlists,
}
}
fn save_dialog_cb(s: &mut Cursive, playlists: Arc<Playlists>, id: Option<String>) {
let tracks = s
.call_on_id("queue_list", |view: &mut ListView<_>| {
view.content().clone()
})
.unwrap();
match id {
Some(id) => {
playlists.overwrite_playlist(&id, &tracks);
s.pop_layer();
}
None => {
s.pop_layer();
let edit = EditView::new()
.on_submit(move |s: &mut Cursive, name| {
playlists.save_playlist(name, &tracks);
s.pop_layer();
})
.with_id("name")
.fixed_width(20);
let dialog = Dialog::new()
.title("Enter name")
.dismiss_button("Cancel")
.padding((1, 1, 1, 0))
.content(edit);
s.add_layer(Modal::new(dialog));
}
}
}
fn save_dialog(playlists: Arc<Playlists>) -> Modal<Dialog> {
let mut list_select: SelectView<Option<String>> = SelectView::new().autojump();
list_select.add_item("[Create new]", None);
for ref list in playlists.items().iter() {
list_select.add_item(list.meta.name.clone(), Some(list.meta.id.clone()));
}
list_select.set_on_submit(move |s, selected| {
Self::save_dialog_cb(s, playlists.clone(), selected.clone())
});
let dialog = Dialog::new()
.title("Save to existing or new playlist?")
.dismiss_button("Cancel")
.padding((1, 1, 1, 0))
.content(ScrollView::new(list_select));
Modal::new(dialog)
}
}
impl ViewWrapper for QueueView {
wrap_impl!(self.list: IdView<ListView<Track>>);
fn wrap_on_event(&mut self, ch: Event) -> EventResult {
match ch {
Event::Char('s') => {
debug!("save list");
let playlists = self.playlists.clone();
let cb = move |s: &mut Cursive| {
let dialog = Self::save_dialog(playlists.clone());
s.add_layer(dialog)
};
EventResult::Consumed(Some(Callback::from_fn(cb)))
}
_ => self.list.on_event(ch),
}
}
}