fix: update playlist changes in local store

Move playlist change logic out of the library while we're at it and notify
the library of changes instead.

fixes #302
This commit is contained in:
Henrik Friedrichsen
2020-10-25 00:09:23 +02:00
parent 6587efdfd2
commit 5fb4eb7af2
6 changed files with 85 additions and 52 deletions

View File

@@ -523,41 +523,14 @@ impl Library {
} }
} }
// Return true if the given playlist contains the given track pub fn playlist_update(&self, updated: &Playlist) {
pub fn playlist_has_track(&self, playlist_id: &str, track_id: &str) -> bool { {
let playlists = self.playlists.read().expect("can't readlock playlists");
if let Some(playlist) = playlists.iter().find(|p| p.id == playlist_id) {
playlist.tracks.as_ref().map_or(false, |tracks| {
tracks.iter().any(|t| t.id == Some(track_id.to_string()))
})
} else {
false
}
}
pub fn playlist_append_tracks(&self, playlist_id: &str, new_tracks: &[Track]) {
let track_ids: Vec<String> = new_tracks
.to_vec()
.iter()
.filter(|t| t.id.is_some())
.map(|t| t.id.clone().unwrap())
.collect();
let mut has_modified = false;
if self.spotify.append_tracks(playlist_id, &track_ids, None) {
let mut playlists = self.playlists.write().expect("can't writelock playlists"); let mut playlists = self.playlists.write().expect("can't writelock playlists");
if let Some(playlist) = playlists.iter_mut().find(|p| p.id == playlist_id) { if let Some(playlist) = playlists.iter_mut().find(|p| p.id == updated.id) {
if let Some(tracks) = &mut playlist.tracks { *playlist = updated.clone();
tracks.append(&mut new_tracks.to_vec());
has_modified = true;
}
} }
} }
self.save_cache(config::cache_path(CACHE_PLAYLISTS), self.playlists.clone());
if has_modified {
self.save_cache(config::cache_path(CACHE_PLAYLISTS), self.playlists.clone());
}
} }
pub fn is_saved_track(&self, track: &Playable) -> bool { pub fn is_saved_track(&self, track: &Playable) -> bool {

View File

@@ -17,7 +17,6 @@ extern crate tokio_timer;
extern crate unicode_width; extern crate unicode_width;
extern crate webbrowser; extern crate webbrowser;
#[macro_use] #[macro_use]
extern crate serde; extern crate serde;
extern crate serde_json; extern crate serde_json;

View File

@@ -1,5 +1,5 @@
extern crate dbus_tree;
extern crate dbus; extern crate dbus;
extern crate dbus_tree;
use std::collections::HashMap; use std::collections::HashMap;
use std::rc::Rc; use std::rc::Rc;
@@ -9,8 +9,8 @@ use std::time::Duration;
use dbus::arg::{RefArg, Variant}; use dbus::arg::{RefArg, Variant};
use dbus::ffidisp::stdintf::org_freedesktop_dbus::PropertiesPropertiesChanged; use dbus::ffidisp::stdintf::org_freedesktop_dbus::PropertiesPropertiesChanged;
use dbus::message::SignalArgs; use dbus::message::SignalArgs;
use dbus_tree::{Access, Factory};
use dbus::strings::Path; use dbus::strings::Path;
use dbus_tree::{Access, Factory};
use crate::album::Album; use crate::album::Album;
use crate::episode::Episode; use crate::episode::Episode;

View File

@@ -30,7 +30,7 @@ impl Playlist {
self.tracks = Some(self.get_all_tracks(spotify)); self.tracks = Some(self.get_all_tracks(spotify));
} }
pub fn get_all_tracks(&self, spotify: Arc<Spotify>) -> Vec<Track> { fn get_all_tracks(&self, spotify: Arc<Spotify>) -> Vec<Track> {
let mut collected_tracks = Vec::new(); let mut collected_tracks = Vec::new();
let mut tracks_result = spotify.user_playlist_tracks(&self.id, 100, 0); let mut tracks_result = spotify.user_playlist_tracks(&self.id, 100, 0);
@@ -59,15 +59,56 @@ impl Playlist {
collected_tracks collected_tracks
} }
pub fn delete_tracks(&mut self, track_pos_pairs: &[(Track, usize)], spotify: Arc<Spotify>) { pub fn has_track(&self, track_id: &str) -> bool {
self.tracks.as_ref().map_or(false, |tracks| {
tracks
.iter()
.any(|track| track.id == Some(track_id.to_string()))
})
}
pub fn delete_tracks(
&mut self,
track_pos_pairs: &[(Track, usize)],
spotify: Arc<Spotify>,
library: Arc<Library>,
) {
if spotify.delete_tracks(&self.id, track_pos_pairs) { if spotify.delete_tracks(&self.id, track_pos_pairs) {
if let Some(tracks) = &mut self.tracks { if let Some(tracks) = &mut self.tracks {
for (_track, pos) in track_pos_pairs { for (_track, pos) in track_pos_pairs {
tracks.remove(*pos); tracks.remove(*pos);
} }
library.playlist_update(&self);
} }
} }
} }
pub fn append_tracks(
&mut self,
new_tracks: &[Track],
spotify: Arc<Spotify>,
library: Arc<Library>,
) {
let track_ids: Vec<String> = new_tracks
.to_vec()
.iter()
.filter(|t| t.id.is_some())
.map(|t| t.id.clone().unwrap())
.collect();
let mut has_modified = false;
if spotify.append_tracks(&self.id, &track_ids, None) {
if let Some(tracks) = &mut self.tracks {
tracks.append(&mut new_tracks.to_vec());
has_modified = true;
}
}
if has_modified {
library.playlist_update(self);
}
}
} }
impl From<&SimplifiedPlaylist> for Playlist { impl From<&SimplifiedPlaylist> for Playlist {

View File

@@ -4,7 +4,6 @@ use cursive::view::{Margins, ViewWrapper};
use cursive::views::{Dialog, NamedView, ScrollView, SelectView}; use cursive::views::{Dialog, NamedView, ScrollView, SelectView};
use cursive::Cursive; use cursive::Cursive;
use crate::command::{Command, MoveAmount, MoveMode};
use crate::commands::CommandResult; use crate::commands::CommandResult;
use crate::library::Library; use crate::library::Library;
use crate::queue::Queue; use crate::queue::Queue;
@@ -12,6 +11,11 @@ use crate::track::Track;
use crate::traits::{ListItem, ViewExt}; use crate::traits::{ListItem, ViewExt};
use crate::ui::layout::Layout; use crate::ui::layout::Layout;
use crate::ui::modal::Modal; use crate::ui::modal::Modal;
use crate::{
command::{Command, MoveAmount, MoveMode},
playlist::Playlist,
spotify::Spotify,
};
#[cfg(feature = "share_clipboard")] #[cfg(feature = "share_clipboard")]
use clipboard::{ClipboardContext, ClipboardProvider}; use clipboard::{ClipboardContext, ClipboardProvider};
use cursive::traits::{Finder, Nameable}; use cursive::traits::{Finder, Nameable};
@@ -28,23 +32,33 @@ enum ContextMenuAction {
} }
impl ContextMenu { impl ContextMenu {
pub fn add_track_dialog(library: Arc<Library>, track: Track) -> Modal<Dialog> { pub fn add_track_dialog(
let mut list_select: SelectView<String> = SelectView::new().autojump(); library: Arc<Library>,
spotify: Arc<Spotify>,
track: Track,
) -> Modal<Dialog> {
let mut list_select: SelectView<Playlist> = SelectView::new().autojump();
for list in library.items().iter() { for list in library.items().iter() {
list_select.add_item(list.name.clone(), list.id.clone()); list_select.add_item(list.name.clone(), list.clone());
} }
list_select.set_on_submit(move |s, selected| { list_select.set_on_submit(move |s, selected| {
let track = track.clone(); let track = track.clone();
let mut playlist = selected.clone();
let spotify = spotify.clone();
let library = library.clone();
if library.playlist_has_track(selected, &track.id.as_ref().unwrap_or(&"".to_string())) { if playlist.has_track(track.id.as_ref().unwrap_or(&String::new())) {
let mut already_added_dialog = Self::track_already_added(); let mut already_added_dialog = Self::track_already_added();
let lib = Arc::clone(&library);
let selected = selected.to_string();
already_added_dialog.add_button("Add anyway", move |c| { already_added_dialog.add_button("Add anyway", move |c| {
lib.playlist_append_tracks(&selected.clone(), &[track.clone()]); let mut playlist = playlist.clone();
let spotify = spotify.clone();
let library = library.clone();
playlist.append_tracks(&[track.clone()], spotify, library);
// playlist.map(|p| p.append_tracks(&[track.clone()], spotify, library));
c.pop_layer(); c.pop_layer();
// Close add_track_dialog too // Close add_track_dialog too
@@ -55,10 +69,10 @@ impl ContextMenu {
s.add_layer(modal); s.add_layer(modal);
return; return;
} else {
playlist.append_tracks(&[track.clone()], spotify, library);
s.pop_layer();
} }
library.playlist_append_tracks(selected, &[track]);
s.pop_layer();
}); });
let dialog = Dialog::new() let dialog = Dialog::new()
@@ -118,7 +132,8 @@ impl ContextMenu {
.ok(); .ok();
} }
ContextMenuAction::AddToPlaylist(track) => { ContextMenuAction::AddToPlaylist(track) => {
let dialog = Self::add_track_dialog(library, *track.clone()); let dialog =
Self::add_track_dialog(library, queue.get_spotify(), *track.clone());
s.add_layer(dialog); s.add_layer(dialog);
} }
ContextMenuAction::ShowRecommentations(item) => { ContextMenuAction::ShowRecommentations(item) => {

View File

@@ -17,6 +17,7 @@ pub struct PlaylistView {
playlist: Playlist, playlist: Playlist,
list: ListView<Track>, list: ListView<Track>,
spotify: Arc<Spotify>, spotify: Arc<Spotify>,
library: Arc<Library>,
} }
impl PlaylistView { impl PlaylistView {
@@ -31,12 +32,13 @@ impl PlaylistView {
}; };
let spotify = queue.get_spotify(); let spotify = queue.get_spotify();
let list = ListView::new(Arc::new(RwLock::new(tracks)), queue, library); let list = ListView::new(Arc::new(RwLock::new(tracks)), queue, library.clone());
Self { Self {
playlist, playlist,
list, list,
spotify, spotify,
library,
} }
} }
} }
@@ -60,8 +62,11 @@ impl ViewExt for PlaylistView {
}; };
let track = tracks.get(pos); let track = tracks.get(pos);
if let Some(t) = track { if let Some(t) = track {
self.playlist self.playlist.delete_tracks(
.delete_tracks(&[(t.clone(), pos)], self.spotify.clone()); &[(t.clone(), pos)],
self.spotify.clone(),
self.library.clone(),
);
self.list.remove(pos); self.list.remove(pos);
} }
return Ok(CommandResult::Consumed(None)); return Ok(CommandResult::Consumed(None));