fix: more resilient playlist track deletion
Introduction of the sort command with #328 broke the deletion of playlist items, because sorted track indices got out of sync with their actual index within the playlist at the Spotify backend. The new approach is not perfect, as it doesn't update the list index of items after deletion, but by supplying the playlist snapshot id the backend is able to apply the requested changes. This should still be improved in the future, though.
This commit is contained in:
@@ -10,9 +10,7 @@ use cursive::{Cursive, Printer, Rect, Vec2};
|
||||
use unicode_width::UnicodeWidthStr;
|
||||
|
||||
use crate::artist::Artist;
|
||||
use crate::command::{
|
||||
Command, GotoMode, JumpMode, MoveAmount, MoveMode, SortDirection, SortKey, TargetMode,
|
||||
};
|
||||
use crate::command::{Command, GotoMode, JumpMode, MoveAmount, MoveMode, TargetMode};
|
||||
use crate::commands::CommandResult;
|
||||
use crate::episode::Episode;
|
||||
use crate::library::Library;
|
||||
@@ -135,58 +133,6 @@ impl<I: ListItem> ListView<I> {
|
||||
let mut c = self.content.write().unwrap();
|
||||
c.remove(index);
|
||||
}
|
||||
|
||||
pub fn sort(&self, key: &SortKey, direction: &SortDirection) {
|
||||
fn compare_artists(a: Vec<String>, b: Vec<String>) -> Ordering {
|
||||
let sanitize_artists_name = |x: Vec<String>| -> Vec<String> {
|
||||
x.iter()
|
||||
.map(|x| {
|
||||
x.to_lowercase()
|
||||
.split(' ')
|
||||
.skip_while(|x| x == &"the")
|
||||
.collect()
|
||||
})
|
||||
.collect()
|
||||
};
|
||||
|
||||
let a = sanitize_artists_name(a);
|
||||
let b = sanitize_artists_name(b);
|
||||
|
||||
a.cmp(&b)
|
||||
}
|
||||
|
||||
let mut c = self.content.write().unwrap();
|
||||
|
||||
c.sort_by(|a, b| match (a.track(), b.track()) {
|
||||
(Some(a), Some(b)) => match (key, direction) {
|
||||
(SortKey::Title, SortDirection::Ascending) => {
|
||||
a.title.to_lowercase().cmp(&b.title.to_lowercase())
|
||||
}
|
||||
(SortKey::Title, SortDirection::Descending) => {
|
||||
b.title.to_lowercase().cmp(&a.title.to_lowercase())
|
||||
}
|
||||
(SortKey::Duration, SortDirection::Ascending) => a.duration.cmp(&b.duration),
|
||||
(SortKey::Duration, SortDirection::Descending) => b.duration.cmp(&a.duration),
|
||||
(SortKey::Album, SortDirection::Ascending) => a
|
||||
.album
|
||||
.map(|x| x.to_lowercase())
|
||||
.cmp(&b.album.map(|x| x.to_lowercase())),
|
||||
(SortKey::Album, SortDirection::Descending) => b
|
||||
.album
|
||||
.map(|x| x.to_lowercase())
|
||||
.cmp(&a.album.map(|x| x.to_lowercase())),
|
||||
(SortKey::Added, SortDirection::Ascending) => a.added_at.cmp(&b.added_at),
|
||||
(SortKey::Added, SortDirection::Descending) => b.added_at.cmp(&a.added_at),
|
||||
(SortKey::Artist, SortDirection::Ascending) => {
|
||||
compare_artists(a.artists, b.artists)
|
||||
}
|
||||
(SortKey::Artist, SortDirection::Descending) => {
|
||||
compare_artists(b.artists, a.artists)
|
||||
}
|
||||
},
|
||||
_ => std::cmp::Ordering::Equal,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl<I: ListItem> View for ListView<I> {
|
||||
|
||||
@@ -18,6 +18,7 @@ pub struct PlaylistView {
|
||||
list: ListView<Track>,
|
||||
spotify: Arc<Spotify>,
|
||||
library: Arc<Library>,
|
||||
queue: Arc<Queue>,
|
||||
}
|
||||
|
||||
impl PlaylistView {
|
||||
@@ -25,6 +26,10 @@ impl PlaylistView {
|
||||
let mut playlist = playlist.clone();
|
||||
playlist.load_tracks(queue.get_spotify());
|
||||
|
||||
if let Some(order) = library.cfg.state().playlist_orders.get(&playlist.id) {
|
||||
playlist.sort(&order.key, &order.direction);
|
||||
}
|
||||
|
||||
let tracks = if let Some(t) = playlist.tracks.as_ref() {
|
||||
t.clone()
|
||||
} else {
|
||||
@@ -32,16 +37,18 @@ impl PlaylistView {
|
||||
};
|
||||
|
||||
let spotify = queue.get_spotify();
|
||||
let list = ListView::new(Arc::new(RwLock::new(tracks)), queue, library.clone());
|
||||
if let Some(order) = library.cfg.state().playlist_orders.get(&playlist.id) {
|
||||
list.sort(&order.key, &order.direction);
|
||||
}
|
||||
let list = ListView::new(
|
||||
Arc::new(RwLock::new(tracks)),
|
||||
queue.clone(),
|
||||
library.clone(),
|
||||
);
|
||||
|
||||
Self {
|
||||
playlist,
|
||||
list,
|
||||
spotify,
|
||||
library,
|
||||
queue,
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -58,20 +65,11 @@ impl ViewExt for PlaylistView {
|
||||
fn on_command(&mut self, s: &mut Cursive, cmd: &Command) -> Result<CommandResult, String> {
|
||||
if let Command::Delete = cmd {
|
||||
let pos = self.list.get_selected_index();
|
||||
let tracks = if let Some(t) = self.playlist.tracks.as_ref() {
|
||||
t.clone()
|
||||
} else {
|
||||
Vec::new()
|
||||
};
|
||||
let track = tracks.get(pos);
|
||||
if let Some(t) = track {
|
||||
if self.playlist.delete_tracks(
|
||||
&[(t.clone(), pos)],
|
||||
self.spotify.clone(),
|
||||
self.library.clone(),
|
||||
) {
|
||||
self.list.remove(pos);
|
||||
}
|
||||
if self
|
||||
.playlist
|
||||
.delete_track(pos, self.spotify.clone(), self.library.clone())
|
||||
{
|
||||
self.list.remove(pos);
|
||||
}
|
||||
return Ok(CommandResult::Consumed(None));
|
||||
}
|
||||
@@ -86,7 +84,14 @@ impl ViewExt for PlaylistView {
|
||||
.playlist_orders
|
||||
.insert(self.playlist.id.clone(), order);
|
||||
});
|
||||
self.list.sort(key, direction);
|
||||
|
||||
self.playlist.sort(key, direction);
|
||||
let tracks = self.playlist.tracks.as_ref().unwrap_or(&Vec::new()).clone();
|
||||
self.list = ListView::new(
|
||||
Arc::new(RwLock::new(tracks)),
|
||||
self.queue.clone(),
|
||||
self.library.clone(),
|
||||
);
|
||||
return Ok(CommandResult::Consumed(None));
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user