From 64c4aa2e258a382d60a81fa77bb210479960814a Mon Sep 17 00:00:00 2001 From: Christopher Snowhill Date: Wed, 9 Feb 2022 21:04:17 -0800 Subject: [PATCH] Handle deleting the current track gracefully Now it should flow playback correctly to the next remaining track after the block of deleted tracks. And if the user deletes the next queued track, it will still be queued to flow past the deleted block. If the user undoes their deletes and restores the tracks, playback will resume after the originally deleted track. Signed-off-by: Christopher Snowhill --- Playlist/PlaylistController.h | 2 ++ Playlist/PlaylistController.m | 47 +++++++++++++++++++++++++++++++---- Playlist/PlaylistEntry.h | 4 +++ Playlist/PlaylistEntry.m | 5 ++++ 4 files changed, 53 insertions(+), 5 deletions(-) diff --git a/Playlist/PlaylistController.h b/Playlist/PlaylistController.h index 8a508a37f..45837123e 100644 --- a/Playlist/PlaylistController.h +++ b/Playlist/PlaylistController.h @@ -49,6 +49,8 @@ typedef NS_ENUM(NSInteger, URLOrigin) { PlaylistEntry *currentEntry; + PlaylistEntry *nextEntryAfterDeleted; + NSUndoManager *undoManager; } diff --git a/Playlist/PlaylistController.m b/Playlist/PlaylistController.m index 5296aa7ec..6b48b3fc2 100644 --- a/Playlist/PlaylistController.m +++ b/Playlist/PlaylistController.m @@ -362,6 +362,31 @@ static inline void dispatch_sync_reentrant(dispatch_queue_t queue, dispatch_bloc if(currentEntry != nil) [self.tableView scrollRowToVisible:currentEntry.index]; } +- (void)updateNextAfterDeleted:(PlaylistEntry *)lastEntry withDeleteIndexes:(NSIndexSet *)indexes { + __block PlaylistEntry *pe = nil; + NSArray *allObjects = [self arrangedObjects]; + [indexes enumerateRangesUsingBlock:^(NSRange range, BOOL *_Nonnull stop) { + if(range.location <= lastEntry.index && + range.location + range.length > lastEntry.index) { + NSUInteger index = range.location + range.length; + if(index < [allObjects count]) + pe = [allObjects objectAtIndex:index]; + else + pe = nil; + } else if(pe && range.location <= [pe index] && + range.location + range.length > [pe index]) { + NSUInteger index = range.location + range.length; + if(index < [allObjects count]) + pe = [allObjects objectAtIndex:index]; + else + pe = nil; + } else if(pe && range.location > [pe index]) { + *stop = YES; + } + }]; + nextEntryAfterDeleted = pe; +} + - (void)tableView:(NSTableView *)tableView didClickTableColumn:(NSTableColumn *)tableColumn { if([self shuffle] != ShuffleOff) [self resetShuffleList]; } @@ -577,6 +602,10 @@ static inline void dispatch_sync_reentrant(dispatch_queue_t queue, dispatch_bloc [NSString stringWithFormat:@"Adding %lu entries", (unsigned long)[objects count]]; [[self undoManager] setActionName:actionName]; + for(PlaylistEntry *pe in objects) { + pe.deleted = NO; + } + [[SQLiteStore sharedStore] playlistInsertTracks:objects atObjectIndexes:indexes progressCall:^(double progress) { @@ -607,11 +636,14 @@ static inline void dispatch_sync_reentrant(dispatch_queue_t queue, dispatch_bloc NSMutableIndexSet *unarrangedIndexes = [[NSMutableIndexSet alloc] init]; for(PlaylistEntry *pe in objects) { [unarrangedIndexes addIndex:[pe index]]; + pe.deleted = YES; } if([indexes containsIndex:currentEntry.index]) { - // Safety check. The player doesn't like committing actions on a removed track - [playbackController stop:nil]; + [self updateNextAfterDeleted:currentEntry withDeleteIndexes:indexes]; + } else if(nextEntryAfterDeleted && + [indexes containsIndex:nextEntryAfterDeleted.index]) { + [self updateNextAfterDeleted:nextEntryAfterDeleted withDeleteIndexes:indexes]; } if(currentEntry.index >= 0 && [unarrangedIndexes containsIndex:currentEntry.index]) { @@ -828,9 +860,14 @@ static inline void dispatch_sync_reentrant(dispatch_queue_t queue, dispatch_bloc return [self shuffledEntryAtIndex:(pe.shuffleIndex + 1)]; } else { NSInteger i; - if(pe.index < 0) // Was a current entry, now removed. + + if(pe.deleted) // Was a current entry, now removed. { - i = -pe.index - 1; + if(nextEntryAfterDeleted) + i = nextEntryAfterDeleted.index; + else + i = 0; + nextEntryAfterDeleted = nil; } else { i = pe.index + 1; } @@ -1038,7 +1075,7 @@ static inline void dispatch_sync_reentrant(dispatch_queue_t queue, dispatch_bloc NSMutableIndexSet *refreshSet = [[NSMutableIndexSet alloc] init]; - if(currentEntry != nil) [refreshSet addIndex:currentEntry.index]; + if(currentEntry != nil && !currentEntry.deleted) [refreshSet addIndex:currentEntry.index]; if(pe != nil) [refreshSet addIndex:pe.index]; // Refresh entire row to refresh tooltips diff --git a/Playlist/PlaylistEntry.h b/Playlist/PlaylistEntry.h index 8e019c907..93e098d38 100644 --- a/Playlist/PlaylistEntry.h +++ b/Playlist/PlaylistEntry.h @@ -66,6 +66,8 @@ BOOL seekable; BOOL metadataLoaded; + + BOOL deleted; } + (NSSet *)keyPathsForValuesAffectingDisplay; @@ -167,6 +169,8 @@ @property BOOL metadataLoaded; +@property BOOL deleted; + - (void)setMetadata:(NSDictionary *)metadata; @end diff --git a/Playlist/PlaylistEntry.m b/Playlist/PlaylistEntry.m index 0685168ab..317da72bb 100644 --- a/Playlist/PlaylistEntry.m +++ b/Playlist/PlaylistEntry.m @@ -66,6 +66,8 @@ @synthesize metadataLoaded; +@synthesize deleted; + // The following read-only keys depend on the values of other properties + (NSSet *)keyPathsForValuesAffectingDisplay { @@ -139,6 +141,7 @@ self.replayGainTrackGain = 0; self.replayGainTrackPeak = 0; self.volume = 1; + self.deleted = NO; } return self; } @@ -507,6 +510,8 @@ pe->seekable = seekable; pe->metadataLoaded = metadataLoaded; + + pe->deleted = deleted; } return pe;