diff --git a/Application/DockIconController.h b/Application/DockIconController.h index 2142402f1..c3e4984a4 100644 --- a/Application/DockIconController.h +++ b/Application/DockIconController.h @@ -16,6 +16,11 @@ IBOutlet PlaybackController *playbackController; NSInteger lastPlaybackStatus; + NSInteger lastColorfulStatus; + NSNumber *lastProgressStatus; + + NSImageView *imageView; + NSProgressIndicator *progressIndicator; } @end diff --git a/Application/DockIconController.m b/Application/DockIconController.m index 354433422..da4ee5ab8 100644 --- a/Application/DockIconController.m +++ b/Application/DockIconController.m @@ -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]; } diff --git a/Application/PlaybackController.h b/Application/PlaybackController.h index 7516d0481..561936fc0 100644 --- a/Application/PlaybackController.h +++ b/Application/PlaybackController.h @@ -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; diff --git a/Application/PlaybackController.m b/Application/PlaybackController.m index b40f9c1ac..05371b185 100644 --- a/Application/PlaybackController.m +++ b/Application/PlaybackController.m @@ -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]; diff --git a/Base.lproj/MainMenu.xib b/Base.lproj/MainMenu.xib index f8e1fd0b6..6d36c3245 100644 --- a/Base.lproj/MainMenu.xib +++ b/Base.lproj/MainMenu.xib @@ -1770,6 +1770,7 @@ Gw + diff --git a/Playlist/PlaylistController.m b/Playlist/PlaylistController.m index 08f66f77e..32ffa3bf0 100644 --- a/Playlist/PlaylistController.m +++ b/Playlist/PlaylistController.m @@ -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]; diff --git a/Playlist/PlaylistLoader.h b/Playlist/PlaylistLoader.h index 3eb2ea80f..d3397b8fd 100755 --- a/Playlist/PlaylistLoader.h +++ b/Playlist/PlaylistLoader.h @@ -23,6 +23,7 @@ typedef enum { @interface PlaylistLoader : NSObject { IBOutlet PlaylistController *playlistController; IBOutlet NSScrollView *playlistView; + IBOutlet PlaybackController *playbackController; NSOperationQueue *queue; } diff --git a/Playlist/PlaylistLoader.m b/Playlist/PlaylistLoader.m index 3bda82347..3de36ea0e 100755 --- a/Playlist/PlaylistLoader.m +++ b/Playlist/PlaylistLoader.m @@ -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 diff --git a/Utils/SQLiteStore.h b/Utils/SQLiteStore.h index ab18bf138..2d7257096 100644 --- a/Utils/SQLiteStore.h +++ b/Utils/SQLiteStore.h @@ -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; diff --git a/Utils/SQLiteStore.m b/Utils/SQLiteStore.m index 7827da3a5..18de8f69d 100644 --- a/Utils/SQLiteStore.m +++ b/Utils/SQLiteStore.m @@ -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