Implement dock icon progress bar indicator for many processing operations, including adding tracks, removing tracks, and loading or reloading track metadata

CQTexperiment
Christopher Snowhill 2022-01-09 02:10:08 -08:00
parent 0d90ccb7c1
commit 7fe67b1630
10 changed files with 378 additions and 50 deletions

View File

@ -16,6 +16,11 @@
IBOutlet PlaybackController *playbackController;
NSInteger lastPlaybackStatus;
NSInteger lastColorfulStatus;
NSNumber *lastProgressStatus;
NSImageView *imageView;
NSProgressIndicator *progressIndicator;
}
@end

View File

@ -17,12 +17,14 @@ static NSString *DockIconPlaybackStatusObservationContext = @"DockIconPlaybackSt
- (void)startObserving
{
[playbackController addObserver:self forKeyPath:@"playbackStatus" options:(NSKeyValueObservingOptionNew | NSKeyValueObservingOptionInitial) context:(__bridge void * _Nullable)(DockIconPlaybackStatusObservationContext)];
[playbackController addObserver:self forKeyPath:@"progressBarStatus" options:(NSKeyValueObservingOptionNew | NSKeyValueObservingOptionInitial) context:(__bridge void * _Nullable)(DockIconPlaybackStatusObservationContext)];
[[NSUserDefaultsController sharedUserDefaultsController] addObserver:self forKeyPath:@"values.colorfulDockIcons" options:0 context:(__bridge void * _Nullable)(DockIconPlaybackStatusObservationContext)];
}
- (void)stopObserving
{
[playbackController removeObserver:self forKeyPath:@"playbackStatus"];
[playbackController removeObserver:self forKeyPath:@"progressBarStatus"];
[[NSUserDefaultsController sharedUserDefaultsController] removeObserver:self forKeyPath:@"values.colorfulDockIcons"];
}
@ -38,41 +40,123 @@ static NSString *getBadgeName(NSString *baseName, BOOL colorfulIcons)
}
}
- (void)refreshDockIcon:(NSInteger)playbackStatus
- (void)refreshDockIcon:(NSInteger)playbackStatus withProgress:(double)progressStatus
{
BOOL displayChanged = NO;
BOOL drawIcon = NO;
BOOL removeProgress = NO;
if ( playbackStatus < 0 )
playbackStatus = lastPlaybackStatus;
else
{
lastPlaybackStatus = playbackStatus;
drawIcon = YES;
}
if ( progressStatus < -2 )
progressStatus = [lastProgressStatus doubleValue];
else
{
if (progressStatus < 0 && [lastProgressStatus doubleValue] >= 0)
removeProgress = YES;
lastProgressStatus = [NSNumber numberWithDouble:progressStatus];
}
BOOL displayProgress = (progressStatus >= 0.0);
NSImage *badgeImage = nil;
BOOL colorfulIcons = [[NSUserDefaults standardUserDefaults] boolForKey:@"colorfulDockIcons"];
switch (playbackStatus) {
case CogStatusPlaying:
badgeImage = [NSImage imageNamed:getBadgeName(@"playDockBadge", colorfulIcons)];
break;
case CogStatusPaused:
badgeImage = [NSImage imageNamed:getBadgeName(@"pauseDockBadge", colorfulIcons)];
break;
default:
badgeImage = [NSImage imageNamed:getBadgeName(@"stopDockBadge", colorfulIcons)];
break;
if ((colorfulIcons && lastColorfulStatus < 1) ||
(!colorfulIcons && lastColorfulStatus != 0))
{
lastColorfulStatus = colorfulIcons ? 1 : 0;
drawIcon = YES;
}
NSSize badgeSize = [badgeImage size];
NSDockTile *dockTile = [NSApp dockTile];
if (drawIcon)
{
switch (playbackStatus) {
case CogStatusPlaying:
badgeImage = [NSImage imageNamed:getBadgeName(@"playDockBadge", colorfulIcons)];
break;
case CogStatusPaused:
badgeImage = [NSImage imageNamed:getBadgeName(@"pauseDockBadge", colorfulIcons)];
break;
default:
badgeImage = [NSImage imageNamed:getBadgeName(@"stopDockBadge", colorfulIcons)];
break;
}
NSImage *newDockImage = [dockImage copy];
[newDockImage lockFocus];
NSSize badgeSize = [badgeImage size];
[badgeImage drawInRect:NSMakeRect(0, 0, 128, 128)
fromRect:NSMakeRect(0, 0, badgeSize.width, badgeSize.height)
operation:NSCompositingOperationSourceOver fraction:1.0];
NSImage *newDockImage = [dockImage copy];
[newDockImage lockFocus];
[newDockImage unlockFocus];
[NSApp setApplicationIconImage:newDockImage];
[badgeImage drawInRect:NSMakeRect(0, 0, 128, 128)
fromRect:NSMakeRect(0, 0, badgeSize.width, badgeSize.height)
operation:NSCompositingOperationSourceOver fraction:1.0];
[newDockImage unlockFocus];
imageView = [[NSImageView alloc] init];
[imageView setImage:newDockImage];
[dockTile setContentView:imageView];
progressIndicator = [[NSProgressIndicator alloc] initWithFrame:NSMakeRect(0.0, 0.0, dockTile.size.width, 10.0)];
[progressIndicator setStyle:NSProgressIndicatorBarStyle];
[progressIndicator setIndeterminate:NO];
[progressIndicator setBezeled:YES];
[progressIndicator setMinValue:0];
[progressIndicator setMaxValue:100];
[progressIndicator setHidden:YES];
[imageView addSubview:progressIndicator];
displayChanged = YES;
}
if (displayProgress)
{
if (!imageView)
{
imageView = [[NSImageView alloc] init];
[imageView setImage:[NSApp applicationIconImage]];
[dockTile setContentView:imageView];
}
if (!progressIndicator)
{
progressIndicator = [[NSProgressIndicator alloc] initWithFrame:NSMakeRect(0.0, 0.0, dockTile.size.width, 10.0)];
[progressIndicator setIndeterminate:NO];
[progressIndicator setBezeled:YES];
[progressIndicator setMinValue:0];
[progressIndicator setMaxValue:100];
[imageView addSubview:progressIndicator];
}
[progressIndicator setDoubleValue:progressStatus];
[progressIndicator setHidden:NO];
displayChanged = YES;
}
if (removeProgress)
{
if (progressIndicator)
[progressIndicator setHidden:YES];
displayChanged = YES;
}
if (displayChanged)
[dockTile display];
}
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context
@ -83,11 +167,17 @@ static NSString *getBadgeName(NSString *baseName, BOOL colorfulIcons)
{
NSInteger playbackStatus = [[change objectForKey:NSKeyValueChangeNewKey] integerValue];
[self refreshDockIcon:playbackStatus];
[self refreshDockIcon:playbackStatus withProgress:-10];
}
else if ([keyPath isEqualToString:@"progressBarStatus"])
{
double progressStatus = [[change objectForKey:NSKeyValueChangeNewKey] doubleValue];
[self refreshDockIcon:-1 withProgress:progressStatus];
}
else if ([keyPath isEqualToString:@"values.colorfulDockIcons"])
{
[self refreshDockIcon:-1];
[self refreshDockIcon:-1 withProgress:-10];
}
}
else
@ -99,6 +189,10 @@ static NSString *getBadgeName(NSString *baseName, BOOL colorfulIcons)
- (void)awakeFromNib
{
dockImage = [[NSImage imageNamed:@"icon_blank"] copy];
lastColorfulStatus = -1;
lastProgressStatus = [NSNumber numberWithDouble:-1];
imageView = nil;
progressIndicator = nil;
[self startObserving];
}

View File

@ -39,10 +39,15 @@ extern NSDictionary * makeRGInfo(PlaylistEntry *pe);
double position;
BOOL seekable;
BOOL fading;
// progress bar display
double progressBarStatus;
}
@property CogStatus playbackStatus;
@property double progressBarStatus;
- (IBAction)changeVolume:(id)sender;
- (IBAction)volumeDown:(id)sender;
- (IBAction)volumeUp:(id)sender;

View File

@ -27,6 +27,8 @@ NSString *CogPlaybackDidStopNotficiation = @"CogPlaybackDidStopNotficiation";
@synthesize playbackStatus;
@synthesize progressBarStatus;
+ (NSSet *)keyPathsForValuesAffectingSeekable
{
return [NSSet setWithObjects:@"playlistController.currentEntry",@"playlistController.currentEntry.seekable",nil];
@ -41,6 +43,8 @@ NSString *CogPlaybackDidStopNotficiation = @"CogPlaybackDidStopNotficiation";
seekable = NO;
fading = NO;
progressBarStatus = -1;
audioPlayer = [[AudioPlayer alloc] init];
[audioPlayer setDelegate:self];

View File

@ -1770,6 +1770,7 @@ Gw
<connections>
<outlet property="playlistController" destination="218" id="1320"/>
<outlet property="playlistView" destination="206" id="6MS-vO-DQp"/>
<outlet property="playbackController" destination="705" id="EBV-A8-3bM"/>
</connections>
</customObject>
<menu title="Menu" id="1324" userLabel="TableMenu">

View File

@ -117,6 +117,22 @@
}
}
static inline void dispatch_sync_reentrant(dispatch_queue_t queue, dispatch_block_t block) {
if (dispatch_queue_get_label(queue) == dispatch_queue_get_label(DISPATCH_CURRENT_QUEUE_LABEL)) {
block();
}
else {
dispatch_sync(queue, block);
}
}
- (void)setProgressBarStatus:(double)status {
dispatch_sync_reentrant(dispatch_get_main_queue(), ^{
[self->playbackController setProgressBarStatus:status];
[[NSRunLoop currentRunLoop] runUntilDate:[NSDate dateWithTimeIntervalSinceNow:0.001]];
});
}
- (void)updatePlaylistIndexes {
NSArray *arranged = [self arrangedObjects];
NSUInteger n = [arranged count];
@ -129,7 +145,9 @@
}
}
if (updated) {
[[SQLiteStore sharedStore] syncPlaylistEntries:arranged];
[[SQLiteStore sharedStore] syncPlaylistEntries:arranged progressCall:^(double progress) {
[self setProgressBarStatus:progress];
}];
}
}
@ -396,7 +414,9 @@
[NSString stringWithFormat:@"Adding %lu entries", (unsigned long) [objects count]];
[[self undoManager] setActionName:actionName];
[[SQLiteStore sharedStore] playlistInsertTracks:objects atObjectIndexes:indexes];
[[SQLiteStore sharedStore] playlistInsertTracks:objects atObjectIndexes:indexes progressCall:^(double progress) {
[self setProgressBarStatus:progress];
}];
[super insertObjects:objects atArrangedObjectIndexes:indexes];
@ -443,7 +463,9 @@
currentEntry.index = -i - 1;
}
[[SQLiteStore sharedStore] playlistRemoveTracksAtIndexes:unarrangedIndexes];
[[SQLiteStore sharedStore] playlistRemoveTracksAtIndexes:unarrangedIndexes progressCall:^(double progress) {
[self setProgressBarStatus:progress];
}];
[super removeObjectsAtArrangedObjectIndexes:indexes];

View File

@ -23,6 +23,7 @@ typedef enum {
@interface PlaylistLoader : NSObject {
IBOutlet PlaylistController *playlistController;
IBOutlet NSScrollView *playlistView;
IBOutlet PlaybackController *playbackController;
NSOperationQueue *queue;
}

View File

@ -119,6 +119,7 @@
return NO;
}
[fileHandle truncateFileAtOffset:0];
[fileHandle writeData:[@"#\n" dataUsingEncoding:NSUTF8StringEncoding]];
for (PlaylistEntry *pe in [playlistController arrangedObjects])
{
@ -281,6 +282,21 @@ NSMutableDictionary * dictionaryWithPropertiesOfObject(id obj, NSArray * filterL
return urls;
}
static inline void dispatch_sync_reentrant(dispatch_queue_t queue, dispatch_block_t block) {
if (dispatch_queue_get_label(queue) == dispatch_queue_get_label(DISPATCH_CURRENT_QUEUE_LABEL)) {
block();
}
else {
dispatch_sync(queue, block);
}
}
- (void)setProgressBarStatus:(double)status {
dispatch_sync_reentrant(dispatch_get_main_queue(), ^{
[self->playbackController setProgressBarStatus:status];
});
}
- (NSArray*)insertURLs:(NSArray *)urls atIndex:(NSInteger)index sort:(BOOL)sort
{
NSMutableSet *uniqueURLs = [NSMutableSet set];
@ -290,12 +306,21 @@ NSMutableDictionary * dictionaryWithPropertiesOfObject(id obj, NSArray * filterL
NSMutableArray *fileURLs = [NSMutableArray array];
NSMutableArray *validURLs = [NSMutableArray array];
NSDictionary *xmlData = nil;
double progress = 0.0;
if (!urls)
{
[self setProgressBarStatus:-1];
return [NSArray array];
}
if (index < 0)
index = 0;
[self setProgressBarStatus:progress];
double progressstep = [urls count] ? 20.0 / (double)([urls count]) : 0;
NSURL *url;
for (url in urls)
@ -320,7 +345,15 @@ NSMutableDictionary * dictionaryWithPropertiesOfObject(id obj, NSArray * filterL
//Non-file URL..
[expandedURLs addObject:url];
}
progress += progressstep;
[self setProgressBarStatus:progress];
}
progress = 20.0;
[self setProgressBarStatus:progress];
DLog(@"Expanded urls: %@", expandedURLs);
@ -334,6 +367,8 @@ NSMutableDictionary * dictionaryWithPropertiesOfObject(id obj, NSArray * filterL
{
sortedURLs = expandedURLs;
}
progressstep = [sortedURLs count] ? 20.0 / (double)([sortedURLs count]) : 0;
for (url in sortedURLs)
{
@ -360,14 +395,24 @@ NSMutableDictionary * dictionaryWithPropertiesOfObject(id obj, NSArray * filterL
{
[fileURLs addObject:url];
}
progress += progressstep;
[self setProgressBarStatus:progress];
}
progress = 40.0;
[self setProgressBarStatus:progress];
DLog(@"File urls: %@", fileURLs);
DLog(@"Contained urls: %@", containedURLs);
progressstep = [fileURLs count] ? 20.0 / (double)([fileURLs count]) : 0;
for (url in fileURLs)
{
progress += progressstep;
if (![[AudioPlayer schemes] containsObject:[url scheme]])
continue;
@ -383,12 +428,22 @@ NSMutableDictionary * dictionaryWithPropertiesOfObject(id obj, NSArray * filterL
[uniqueURLs addObject:url];
}
[self setProgressBarStatus:progress];
}
progress = 60.0;
[self setProgressBarStatus:progress];
DLog(@"Valid urls: %@", validURLs);
progressstep = [containedURLs count] ? 20.0 / (double)([containedURLs count]) : 0;
for (url in containedURLs)
{
progress += progressstep;
if (![[AudioPlayer schemes] containsObject:[url scheme]])
continue;
@ -397,7 +452,13 @@ NSMutableDictionary * dictionaryWithPropertiesOfObject(id obj, NSArray * filterL
continue;
[validURLs addObject:url];
[self setProgressBarStatus:progress];
}
progress = 80.0;
[self setProgressBarStatus:progress];
//Create actual entries
int count = (int) [validURLs count];
@ -405,7 +466,12 @@ NSMutableDictionary * dictionaryWithPropertiesOfObject(id obj, NSArray * filterL
// no valid URLs, or they use an unsupported URL scheme
if (!count)
{
[self setProgressBarStatus:-1];
return [NSArray array];
}
progressstep = 20.0 / (double)(count);
NSInteger i = 0;
NSMutableArray *entries = [NSMutableArray arrayWithCapacity:count];
@ -421,6 +487,9 @@ NSMutableDictionary * dictionaryWithPropertiesOfObject(id obj, NSArray * filterL
[entries addObject:pe];
++i;
progress += progressstep;
[self setProgressBarStatus:progress];
}
NSInteger j = index + i;
@ -441,6 +510,9 @@ NSMutableDictionary * dictionaryWithPropertiesOfObject(id obj, NSArray * filterL
}
}
progress = 100.0;
[self setProgressBarStatus:progress];
NSIndexSet *is = [NSIndexSet indexSetWithIndexesInRange:NSMakeRange(index, [entries count])];
[playlistController insertObjects:entries atArrangedObjectIndexes:is];
@ -470,23 +542,24 @@ NSMutableDictionary * dictionaryWithPropertiesOfObject(id obj, NSArray * filterL
NSArray* arrayFirst = [NSArray arrayWithObject:[entries objectAtIndex:0]];
NSMutableArray* arrayRest = [entries mutableCopy];
[arrayRest removeObjectAtIndex:0];
progress = 0.0;
[self setProgressBarStatus:progress];
[self performSelectorOnMainThread:@selector(syncLoadInfoForEntries:) withObject:arrayFirst waitUntilDone:YES];
progressstep = 100.0 / (double)([entries count]);
progress += progressstep;
[self setProgressBarStatus:progress];
if ([arrayRest count])
[self performSelectorInBackground:@selector(loadInfoForEntries:) withObject:arrayRest];
else
[self setProgressBarStatus:-1];
return entries;
}
}
static inline void dispatch_sync_reentrant(dispatch_queue_t queue, dispatch_block_t block) {
if (dispatch_queue_get_label(queue) == dispatch_queue_get_label(DISPATCH_CURRENT_QUEUE_LABEL)) {
block();
}
else {
dispatch_sync(queue, block);
}
}
- (void)loadInfoForEntries:(NSArray *)entries
{
NSMutableIndexSet *update_indexes = [[NSMutableIndexSet alloc] init];
@ -494,6 +567,18 @@ static inline void dispatch_sync_reentrant(dispatch_queue_t queue, dispatch_bloc
NSMutableIndexSet *load_info_indexes = [[NSMutableIndexSet alloc] init];
SQLiteStore *store = [SQLiteStore sharedStore];
__block double progress = [playbackController progressBarStatus];
if (progress < 0 || progress >= 100)
progress = 0;
double progressRemaining = 100.0 - progress;
// 50% for properties reading, 50% for applying them to the main thread
const double progressstep = [entries count] ? (progressRemaining / 2.0) / [entries count] : 0;
progressRemaining = progress + (progressRemaining / 2.0);
i = 0;
j = 0;
@ -529,6 +614,10 @@ static inline void dispatch_sync_reentrant(dispatch_queue_t queue, dispatch_bloc
NSBlockOperation *op = [[NSBlockOperation alloc] init];
[op addExecutionBlock:^{
[weakLock lock];
progress += progressstep;
[weakLock unlock];
NSMutableDictionary *entryInfo = [NSMutableDictionary dictionaryWithCapacity:20];
NSDictionary *entryProperties = [AudioPropertiesReader propertiesForURL:weakPe.URL];
@ -541,6 +630,7 @@ static inline void dispatch_sync_reentrant(dispatch_queue_t queue, dispatch_bloc
[weakLock lock];
[weakArray addObject:weakPe];
[weakArray addObject:entryInfo];
[self setProgressBarStatus:progress];
[weakLock unlock];
}];
@ -549,6 +639,9 @@ static inline void dispatch_sync_reentrant(dispatch_queue_t queue, dispatch_bloc
}
[queue waitUntilAllOperationsAreFinished];
progress = progressRemaining;
[self setProgressBarStatus:progress];
for (i = 0, j = [outArray count]; i < j; i += 2) {
__block PlaylistEntry *weakPe = [outArray objectAtIndex:i];
@ -556,6 +649,8 @@ static inline void dispatch_sync_reentrant(dispatch_queue_t queue, dispatch_bloc
dispatch_sync_reentrant(dispatch_get_main_queue(), ^{
[weakPe setMetadata:entryInfo];
[store trackUpdate:weakPe];
progress += progressstep;
[self setProgressBarStatus:progress];
});
}
@ -569,6 +664,8 @@ static inline void dispatch_sync_reentrant(dispatch_queue_t queue, dispatch_bloc
[weakPlaylistView.documentView reloadDataForRowIndexes:weakIndexSet columnIndexes:[NSIndexSet indexSetWithIndexesInRange:NSMakeRange(0,columns)]];
});
}
[self setProgressBarStatus:-1];
}
// To be called on main thread only
- (void)syncLoadInfoForEntries:(NSArray *)entries

View File

@ -27,17 +27,17 @@
- (void) trackUpdate:(PlaylistEntry *)track;
- (void)playlistInsertTracks:(NSArray *)tracks atIndex:(int64_t)index;
- (void)playlistInsertTracks:(NSArray *)tracks atObjectIndexes:(NSIndexSet *)indexes;
- (void)playlistRemoveTracks:(int64_t)index forCount:(int64_t)count;
- (void)playlistRemoveTracksAtIndexes:(NSIndexSet *)indexes;
- (void)playlistInsertTracks:(NSArray *)tracks atIndex:(int64_t)index progressCall:(void(^)(double progress))callback;
- (void)playlistInsertTracks:(NSArray *)tracks atObjectIndexes:(NSIndexSet *)indexes progressCall:(void(^)(double progress))callback;
- (void)playlistRemoveTracks:(int64_t)index forCount:(int64_t)count progressCall:(void(^)(double progress))callback;
- (void)playlistRemoveTracksAtIndexes:(NSIndexSet *)indexes progressCall:(void(^)(double progress))callback;
- (PlaylistEntry *)playlistGetItem:(int64_t)index;
- (int64_t)playlistGetCount;
#if 0
- (void)playlistMoveObjectsInArrangedObjectsFromIndexes:(NSIndexSet *)indexSet toIndex:(NSUInteger)insertIndex;
#endif
- (void)syncPlaylistEntries:(NSArray *)entries;
- (void)syncPlaylistEntries:(NSArray *)entries progressCall:(void(^)(double progress))callback;
- (void)queueAddItem:(int64_t)playlistIndex;
- (void)queueAddItems:(NSArray *)playlistIndexes;

View File

@ -1466,9 +1466,15 @@ static SQLiteStore *g_sharedStore = NULL;
}
}
- (void)playlistInsertTracks:(NSArray *)tracks atIndex:(int64_t)index
- (void)playlistInsertTracks:(NSArray *)tracks atIndex:(int64_t)index progressCall:(void (^)(double))callback
{
if (!tracks) return;
if (!tracks)
{
callback(-1);
return;
}
callback(0);
sqlite3_stmt *st = stmt[stmt_increment_playlist_for_insert];
@ -1478,9 +1484,15 @@ static SQLiteStore *g_sharedStore = NULL;
sqlite3_step(st) != SQLITE_DONE ||
sqlite3_reset(st))
{
callback(-1);
return;
}
callback(25);
double progress = 25.0;
double progressstep = [tracks count] ? 75.0 / (double)([tracks count]) : 0;
st = stmt[stmt_add_playlist];
for (PlaylistEntry *entry in tracks)
@ -1492,32 +1504,61 @@ static SQLiteStore *g_sharedStore = NULL;
sqlite3_bind_int64(st, add_playlist_in_track_id, trackId) ||
sqlite3_step(st) != SQLITE_DONE)
{
callback(-1);
return;
}
++index;
progress += progressstep;
callback(progress);
}
sqlite3_reset(st);
callback(-1);
}
- (void)playlistInsertTracks:(NSArray *)tracks atObjectIndexes:(NSIndexSet *)indexes
- (void)playlistInsertTracks:(NSArray *)tracks atObjectIndexes:(NSIndexSet *)indexes progressCall:(void (^)(double))callback
{
if (!tracks || !indexes) return;
if (!tracks || !indexes)
{
callback(-1);
return;
}
__block int64_t total_count = 0;
[indexes enumerateRangesUsingBlock:^(NSRange range, BOOL * _Nonnull stop) {
total_count += range.length;
}];
__block int64_t i = 0;
__block double progress = 0;
[indexes enumerateRangesUsingBlock:^(NSRange range, BOOL * _Nonnull stop) {
double progresschunk = (double)range.length / (double)total_count;
double progressbase = progress;
NSRange trackRange = NSMakeRange(i, range.length);
NSArray *trackSet = (i == 0 && range.length == [tracks count]) ? tracks : [tracks subarrayWithRange:trackRange];
[self playlistInsertTracks:trackSet atIndex:range.location];
[self playlistInsertTracks:trackSet atIndex:range.location progressCall:^(double _progress){
if (_progress < 0) return;
callback(progressbase + progresschunk * _progress);
}];
i += range.length;
progress += 100.0 * progresschunk;
callback(progress);
}];
callback(-1);
}
- (void)playlistRemoveTracks:(int64_t)index forCount:(int64_t)count
- (void)playlistRemoveTracks:(int64_t)index forCount:(int64_t)count progressCall:(void (^)(double))callback
{
if (!count) return;
if (!count)
{
callback(-1);
return;
}
sqlite3_stmt *st = stmt[stmt_select_playlist_range];
@ -1525,9 +1566,15 @@ static SQLiteStore *g_sharedStore = NULL;
sqlite3_bind_int64(st, select_playlist_range_in_id_low, index) ||
sqlite3_bind_int64(st, select_playlist_range_in_id_high, index + count - 1))
{
callback(-1);
return;
}
callback(0);
double progress = 0;
double progressstep = 100.0 / ((double)count);
int rc = sqlite3_step(st);
while (rc == SQLITE_ROW)
@ -1535,8 +1582,12 @@ static SQLiteStore *g_sharedStore = NULL;
int64_t trackId = sqlite3_column_int64(st, select_playlist_range_out_track_id);
[self removeTrack:trackId];
rc = sqlite3_step(st);
progress += progressstep;
callback(progress);
}
callback(100);
sqlite3_reset(st);
if (rc != SQLITE_DONE)
@ -1552,6 +1603,7 @@ static SQLiteStore *g_sharedStore = NULL;
sqlite3_step(st) != SQLITE_DONE ||
sqlite3_reset(st))
{
callback(-1);
return;
}
@ -1563,6 +1615,7 @@ static SQLiteStore *g_sharedStore = NULL;
sqlite3_step(st) != SQLITE_DONE ||
sqlite3_reset(st))
{
callback(-1);
return;
}
@ -1574,18 +1627,42 @@ static SQLiteStore *g_sharedStore = NULL;
}
[self queueRemovePlaylistItems:items];
callback(-1);
}
- (void)playlistRemoveTracksAtIndexes:(NSIndexSet *)indexes
- (void)playlistRemoveTracksAtIndexes:(NSIndexSet *)indexes progressCall:(void (^)(double))callback
{
if (!indexes) return;
if (!indexes)
{
callback(-1);
return;
}
__block int64_t total_count = 0;
[indexes enumerateRangesUsingBlock:^(NSRange range, BOOL * _Nonnull stop) {
total_count += range.length;
}];
__block int64_t i = 0;
__block double progress = 0;
callback(progress);
[indexes enumerateRangesUsingBlock:^(NSRange range, BOOL * _Nonnull stop) {
[self playlistRemoveTracks:(range.location - i) forCount:range.length];
double progresschunk = (double)range.length / (double)total_count;
double progressbase = progress;
[self playlistRemoveTracks:(range.location - i) forCount:range.length progressCall:^(double _progress) {
if (_progress < 0) return;
callback(progressbase + progresschunk * _progress);
}];
i += range.length;
progress += 100.0 * progresschunk;
callback(progress);
}];
callback(-1);
}
- (PlaylistEntry *)playlistGetItem:(int64_t)index
@ -1740,18 +1817,27 @@ static SQLiteStore *g_sharedStore = NULL;
}
#endif
- (void)syncPlaylistEntries:(NSArray *)entries
- (void)syncPlaylistEntries:(NSArray *)entries progressCall:(void (^)(double))callback
{
if (!entries || ![entries count])
{
callback(-1);
return;
}
int64_t count = [self playlistGetCount];
if (count != [entries count])
{
callback(-1);
return;
}
callback(0);
double progress = 0;
double progressstep = 50.0 / (double)(count);
NSMutableArray * entryIds = [[NSMutableArray alloc] init];
NSMutableArray * entryIndexes = [[NSMutableArray alloc] init];
NSMutableArray * trackIds = [[NSMutableArray alloc] init];
@ -1763,6 +1849,7 @@ static SQLiteStore *g_sharedStore = NULL;
if (sqlite3_reset(st) ||
(rc = sqlite3_step(st)) != SQLITE_ROW)
{
callback(-1);
return;
}
@ -1777,11 +1864,17 @@ static SQLiteStore *g_sharedStore = NULL;
[trackIds addObject:[NSNumber numberWithInteger:trackId]];
rc = sqlite3_step(st);
progress += progressstep;
callback(progress);
}
while (rc == SQLITE_ROW);
sqlite3_reset(st);
progress = 50;
callback(progress);
st = stmt[stmt_update_playlist];
int64_t i = 0;
@ -1793,6 +1886,7 @@ static SQLiteStore *g_sharedStore = NULL;
int64_t trackId = [[trackIds objectAtIndex:i] integerValue];
++i;
progress += progressstep;
if ([entry index] == entryIndex &&
[entry dbIndex] == trackId)
@ -1804,11 +1898,16 @@ static SQLiteStore *g_sharedStore = NULL;
sqlite3_bind_int64(st, update_playlist_in_id, entryId) ||
sqlite3_step(st) != SQLITE_DONE)
{
callback(-1);
return;
}
callback(progress);
}
sqlite3_reset(st);
callback(-1);
}
- (void)queueAddItem:(int64_t)playlistIndex