[Job Queue] Overhauled long action handling

Long actions, such as file opening, playlist loading, metadata loading
and refreshing, etc, are now handled through NSProgress. Additionally,
a new status bar change displays the progress of the task instead of
the total duration of the playlist. Finally, app quit is blocked by a
running task, and if the app is quit while a task is running, it will
be delayed until the task completes, at which time the app will
terminate cleanly.

Signed-off-by: Christopher Snowhill <kode54@gmail.com>
swiftingly
Christopher Snowhill 2022-06-15 01:01:45 -07:00
parent 6c7a3e581c
commit 92573ec088
14 changed files with 447 additions and 84 deletions

View File

@ -21,6 +21,8 @@
#import "ColorToValueTransformer.h" #import "ColorToValueTransformer.h"
#import "TotalTimeTransformer.h"
#import "Shortcuts.h" #import "Shortcuts.h"
#import <MASShortcut/Shortcut.h> #import <MASShortcut/Shortcut.h>
@ -53,8 +55,11 @@ void *kAppControllerContext = &kAppControllerContext;
NSValueTransformer *colorToValueTransformer = [[ColorToValueTransformer alloc] init]; NSValueTransformer *colorToValueTransformer = [[ColorToValueTransformer alloc] init];
[NSValueTransformer setValueTransformer:colorToValueTransformer [NSValueTransformer setValueTransformer:colorToValueTransformer
forName:@"ColorToValueTransformer"]; forName:@"ColorToValueTransformer"];
}
NSValueTransformer *totalTimeTransformer = [[TotalTimeTransformer alloc] init];
[NSValueTransformer setValueTransformer:totalTimeTransformer
forName:@"TotalTimeTransformer"];
}
- (id)init { - (id)init {
self = [super init]; self = [super init];
if(self) { if(self) {
@ -307,6 +312,12 @@ void *kAppControllerContext = &kAppControllerContext;
miniWindow.title = title; miniWindow.title = title;
mainWindow.title = title; mainWindow.title = title;
} }
} else if([keyPath isEqualToString:@"finished"]) {
NSProgress *progress = (NSProgress *)object;
if([progress isFinished]) {
playbackController.progressOverall = nil;
[NSApp terminate:nil];
}
} }
} }
@ -324,6 +335,15 @@ void *kAppControllerContext = &kAppControllerContext;
[expandedNodes removeObject:url]; [expandedNodes removeObject:url];
} }
- (NSApplicationTerminateReply)applicationShouldTerminate:(NSApplication *)sender {
if(playbackController.progressOverall) {
[playbackController.progressOverall addObserver:self forKeyPath:@"finished" options:0 context:kAppControllerContext];
return NSTerminateLater;
} else {
return NSTerminateNow;
}
}
- (void)applicationWillTerminate:(NSNotification *)aNotification { - (void)applicationWillTerminate:(NSNotification *)aNotification {
CogStatus currentStatus = [playbackController playbackStatus]; CogStatus currentStatus = [playbackController playbackStatus];
NSInteger lastTrackPlaying = -1; NSInteger lastTrackPlaying = -1;

View File

@ -16,14 +16,22 @@ static NSString *DockIconPlaybackStatusObservationContext = @"DockIconPlaybackSt
- (void)startObserving { - (void)startObserving {
[playbackController addObserver:self forKeyPath:@"playbackStatus" options:(NSKeyValueObservingOptionNew | NSKeyValueObservingOptionInitial) context:(__bridge void *_Nullable)(DockIconPlaybackStatusObservationContext)]; [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)]; [playbackController addObserver:self forKeyPath:@"progressOverall" options:(NSKeyValueObservingOptionNew | NSKeyValueObservingOptionInitial | NSKeyValueObservingOptionOld) context:(__bridge void *_Nullable)(DockIconPlaybackStatusObservationContext)];
[[NSUserDefaultsController sharedUserDefaultsController] addObserver:self forKeyPath:@"values.colorfulDockIcons" options:0 context:(__bridge void *_Nullable)(DockIconPlaybackStatusObservationContext)]; [[NSUserDefaultsController sharedUserDefaultsController] addObserver:self forKeyPath:@"values.colorfulDockIcons" options:0 context:(__bridge void *_Nullable)(DockIconPlaybackStatusObservationContext)];
} }
- (void)stopObserving { - (void)stopObserving {
[playbackController removeObserver:self forKeyPath:@"playbackStatus"]; [playbackController removeObserver:self forKeyPath:@"playbackStatus" context:(__bridge void *_Nullable)(DockIconPlaybackStatusObservationContext)];
[playbackController removeObserver:self forKeyPath:@"progressBarStatus"]; [playbackController removeObserver:self forKeyPath:@"progressOverall" context:(__bridge void *_Nullable)(DockIconPlaybackStatusObservationContext)];
[[NSUserDefaultsController sharedUserDefaultsController] removeObserver:self forKeyPath:@"values.colorfulDockIcons"]; [[NSUserDefaultsController sharedUserDefaultsController] removeObserver:self forKeyPath:@"values.colorfulDockIcons" context:(__bridge void *_Nullable)(DockIconPlaybackStatusObservationContext)];
}
- (void)startObservingProgress:(NSProgress *)progress {
[progress addObserver:self forKeyPath:@"fractionCompleted" options:0 context:(__bridge void *_Nullable)(DockIconPlaybackStatusObservationContext)];
}
- (void)stopObservingProgress:(NSProgress *)progress {
[progress removeObserver:self forKeyPath:@"fractionCompleted" context:(__bridge void *_Nullable)(DockIconPlaybackStatusObservationContext)];
} }
static NSString *getBadgeName(NSString *baseName, BOOL colorfulIcons) { static NSString *getBadgeName(NSString *baseName, BOOL colorfulIcons) {
@ -151,12 +159,35 @@ static NSString *getBadgeName(NSString *baseName, BOOL colorfulIcons) {
NSInteger playbackStatus = [[change objectForKey:NSKeyValueChangeNewKey] integerValue]; NSInteger playbackStatus = [[change objectForKey:NSKeyValueChangeNewKey] integerValue];
[self refreshDockIcon:playbackStatus withProgress:-10]; [self refreshDockIcon:playbackStatus withProgress:-10];
} else if([keyPath isEqualToString:@"progressBarStatus"]) { } else if([keyPath isEqualToString:@"progressOverall"]) {
double progressStatus = [[change objectForKey:NSKeyValueChangeNewKey] doubleValue]; double progressStatus = [lastProgressStatus doubleValue];
id objNew = [change objectForKey:NSKeyValueChangeNewKey];
id objOld = [change objectForKey:NSKeyValueChangeOldKey];
NSProgress *progressNew = nil, *progressOld = nil;
if(objNew && [objNew isKindOfClass:[NSProgress class]])
progressNew = (NSProgress *)objNew;
if(objOld && [objOld isKindOfClass:[NSProgress class]])
progressOld = (NSProgress *)objOld;
if(progressOld) {
[self stopObservingProgress:progressOld];
progressStatus = -1;
}
if(progressNew) {
[self startObservingProgress:progressNew];
progressStatus = progressNew.fractionCompleted * 100.0;
}
[self refreshDockIcon:-1 withProgress:progressStatus]; [self refreshDockIcon:-1 withProgress:progressStatus];
} else if([keyPath isEqualToString:@"values.colorfulDockIcons"]) { } else if([keyPath isEqualToString:@"values.colorfulDockIcons"]) {
[self refreshDockIcon:-1 withProgress:-10]; [self refreshDockIcon:-1 withProgress:-10];
} else if([keyPath isEqualToString:@"fractionCompleted"]) {
double progressStatus = [(NSProgress *)object fractionCompleted];
[self refreshDockIcon:-1 withProgress:progressStatus * 100.0];
} }
} else { } else {
[super observeValueForKeyPath:keyPath ofObject:object change:change context:context]; [super observeValueForKeyPath:keyPath ofObject:object change:change context:context];

View File

@ -53,14 +53,16 @@ extern NSDictionary *makeRGInfo(PlaylistEntry *pe);
BOOL fading; BOOL fading;
// progress bar display // progress bar display
double progressBarStatus; NSProgress *progressOverall;
NSProgress *progressJob;
AudioUnit _eq; AudioUnit _eq;
} }
@property CogStatus playbackStatus; @property CogStatus playbackStatus;
@property double progressBarStatus; @property NSProgress *progressOverall;
@property NSProgress *progressJob;
- (IBAction)changeVolume:(id)sender; - (IBAction)changeVolume:(id)sender;
- (IBAction)volumeDown:(id)sender; - (IBAction)volumeDown:(id)sender;

View File

@ -30,7 +30,8 @@ NSString *CogPlaybackDidStopNotficiation = @"CogPlaybackDidStopNotficiation";
@synthesize playbackStatus; @synthesize playbackStatus;
@synthesize progressBarStatus; @synthesize progressOverall;
@synthesize progressJob;
+ (NSSet *)keyPathsForValuesAffectingSeekable { + (NSSet *)keyPathsForValuesAffectingSeekable {
return [NSSet setWithObjects:@"playlistController.currentEntry", @"playlistController.currentEntry.seekable", nil]; return [NSSet setWithObjects:@"playlistController.currentEntry", @"playlistController.currentEntry.seekable", nil];
@ -44,7 +45,8 @@ NSString *CogPlaybackDidStopNotficiation = @"CogPlaybackDidStopNotficiation";
seekable = NO; seekable = NO;
fading = NO; fading = NO;
progressBarStatus = -1; progressOverall = nil;
progressJob = nil;
audioPlayer = [[AudioPlayer alloc] init]; audioPlayer = [[AudioPlayer alloc] init];
[audioPlayer setDelegate:self]; [audioPlayer setDelegate:self];

View File

@ -25,17 +25,17 @@
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/> <autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<subviews> <subviews>
<splitView dividerStyle="thin" vertical="YES" translatesAutoresizingMaskIntoConstraints="NO" id="2123"> <splitView dividerStyle="thin" vertical="YES" translatesAutoresizingMaskIntoConstraints="NO" id="2123">
<rect key="frame" x="0.0" y="73" width="1000" height="327"/> <rect key="frame" x="0.0" y="107" width="1000" height="293"/>
<subviews> <subviews>
<scrollView fixedFrame="YES" borderType="none" autohidesScrollers="YES" horizontalLineScroll="24" horizontalPageScroll="0.0" verticalLineScroll="24" verticalPageScroll="0.0" hasHorizontalScroller="NO" usesPredominantAxisScrolling="NO" id="206" userLabel="Scroll View - Playlist View"> <scrollView fixedFrame="YES" borderType="none" autohidesScrollers="YES" horizontalLineScroll="24" horizontalPageScroll="0.0" verticalLineScroll="24" verticalPageScroll="0.0" hasHorizontalScroller="NO" usesPredominantAxisScrolling="NO" id="206" userLabel="Scroll View - Playlist View">
<rect key="frame" x="0.0" y="0.0" width="1000" height="344"/> <rect key="frame" x="0.0" y="0.0" width="1000" height="293"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/> <autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<clipView key="contentView" drawsBackground="NO" copiesOnScroll="NO" id="KWC-Ti-8KY"> <clipView key="contentView" drawsBackground="NO" copiesOnScroll="NO" id="KWC-Ti-8KY">
<rect key="frame" x="0.0" y="0.0" width="1000" height="344"/> <rect key="frame" x="0.0" y="0.0" width="1000" height="293"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/> <autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<subviews> <subviews>
<tableView focusRingType="none" verticalHuggingPriority="750" allowsExpansionToolTips="YES" alternatingRowBackgroundColors="YES" autosaveName="Playlist" rowHeight="18" headerView="1517" viewBased="YES" id="207" customClass="PlaylistView"> <tableView focusRingType="none" verticalHuggingPriority="750" allowsExpansionToolTips="YES" alternatingRowBackgroundColors="YES" autosaveName="Playlist" rowHeight="18" headerView="1517" viewBased="YES" id="207" customClass="PlaylistView">
<rect key="frame" x="0.0" y="0.0" width="1000" height="327"/> <rect key="frame" x="0.0" y="0.0" width="1000" height="276"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/> <autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<size key="intercellSpacing" width="3" height="6"/> <size key="intercellSpacing" width="3" height="6"/>
<color key="backgroundColor" name="controlBackgroundColor" catalog="System" colorSpace="catalog"/> <color key="backgroundColor" name="controlBackgroundColor" catalog="System" colorSpace="catalog"/>
@ -651,9 +651,15 @@
<color key="backgroundColor" name="controlColor" catalog="System" colorSpace="catalog"/> <color key="backgroundColor" name="controlColor" catalog="System" colorSpace="catalog"/>
</textFieldCell> </textFieldCell>
<connections> <connections>
<binding destination="218" name="displayPatternValue1" keyPath="totalTime" id="1891"> <binding destination="218" name="displayPatternValue1" keyPath="totalTime" id="Kih-ky-A1h">
<dictionary key="options"> <dictionary key="options">
<string key="NSDisplayPattern">Total Duration: %{value1}@</string> <string key="NSDisplayPattern">%{value1}@%{value2}@</string>
<string key="NSValueTransformerName">TotalTimeTransformer</string>
</dictionary>
</binding>
<binding destination="218" name="displayPatternValue2" keyPath="currentStatus" previousBinding="Kih-ky-A1h" id="5bQ-Qy-2KR">
<dictionary key="options">
<string key="NSDisplayPattern">%{value1}@%{value2}@</string>
</dictionary> </dictionary>
</binding> </binding>
</connections> </connections>

View File

@ -111,6 +111,7 @@
83489C6B2782F78700BDCEA2 /* libvgmPlayer.bundle in CopyFiles */ = {isa = PBXBuildFile; fileRef = 83489C542782F2DF00BDCEA2 /* libvgmPlayer.bundle */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; 83489C6B2782F78700BDCEA2 /* libvgmPlayer.bundle in CopyFiles */ = {isa = PBXBuildFile; fileRef = 83489C542782F2DF00BDCEA2 /* libvgmPlayer.bundle */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; };
8349270C27B4EFFC0009AB2B /* duplicateItemsTemplate.pdf in Resources */ = {isa = PBXBuildFile; fileRef = 8349270127B4EFFC0009AB2B /* duplicateItemsTemplate.pdf */; }; 8349270C27B4EFFC0009AB2B /* duplicateItemsTemplate.pdf in Resources */ = {isa = PBXBuildFile; fileRef = 8349270127B4EFFC0009AB2B /* duplicateItemsTemplate.pdf */; };
8349270D27B4EFFC0009AB2B /* deadItemsTemplate.pdf in Resources */ = {isa = PBXBuildFile; fileRef = 8349270B27B4EFFC0009AB2B /* deadItemsTemplate.pdf */; }; 8349270D27B4EFFC0009AB2B /* deadItemsTemplate.pdf in Resources */ = {isa = PBXBuildFile; fileRef = 8349270B27B4EFFC0009AB2B /* deadItemsTemplate.pdf */; };
834B05EA2859C006000B7DC0 /* TotalTimeTransformer.m in Sources */ = {isa = PBXBuildFile; fileRef = 834B05E92859C006000B7DC0 /* TotalTimeTransformer.m */; };
834D793F20E4EFEA00C4A5CC /* OpusPlugin.bundle in CopyFiles */ = {isa = PBXBuildFile; fileRef = 830B62B320E4EF89004A74B2 /* OpusPlugin.bundle */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; 834D793F20E4EFEA00C4A5CC /* OpusPlugin.bundle in CopyFiles */ = {isa = PBXBuildFile; fileRef = 830B62B320E4EF89004A74B2 /* OpusPlugin.bundle */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; };
834D794020E4EFEF00C4A5CC /* VorbisPlugin.bundle in CopyFiles */ = {isa = PBXBuildFile; fileRef = 8301F94520E4EEF70017B2DC /* VorbisPlugin.bundle */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; 834D794020E4EFEF00C4A5CC /* VorbisPlugin.bundle in CopyFiles */ = {isa = PBXBuildFile; fileRef = 8301F94520E4EEF70017B2DC /* VorbisPlugin.bundle */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; };
834F7F4320E4E4ED00228DAB /* AdPlug.bundle in CopyFiles */ = {isa = PBXBuildFile; fileRef = 8303A30920E4E3D000951EF8 /* AdPlug.bundle */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; 834F7F4320E4E4ED00228DAB /* AdPlug.bundle in CopyFiles */ = {isa = PBXBuildFile; fileRef = 8303A30920E4E3D000951EF8 /* AdPlug.bundle */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; };
@ -1004,6 +1005,8 @@
83489C4E2782F2DF00BDCEA2 /* libvgmPlayer.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = libvgmPlayer.xcodeproj; path = Plugins/libvgmPlayer/libvgmPlayer.xcodeproj; sourceTree = "<group>"; }; 83489C4E2782F2DF00BDCEA2 /* libvgmPlayer.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = libvgmPlayer.xcodeproj; path = Plugins/libvgmPlayer/libvgmPlayer.xcodeproj; sourceTree = "<group>"; };
8349270127B4EFFC0009AB2B /* duplicateItemsTemplate.pdf */ = {isa = PBXFileReference; lastKnownFileType = image.pdf; name = duplicateItemsTemplate.pdf; path = Images/duplicateItemsTemplate.pdf; sourceTree = "<group>"; }; 8349270127B4EFFC0009AB2B /* duplicateItemsTemplate.pdf */ = {isa = PBXFileReference; lastKnownFileType = image.pdf; name = duplicateItemsTemplate.pdf; path = Images/duplicateItemsTemplate.pdf; sourceTree = "<group>"; };
8349270B27B4EFFC0009AB2B /* deadItemsTemplate.pdf */ = {isa = PBXFileReference; lastKnownFileType = image.pdf; name = deadItemsTemplate.pdf; path = Images/deadItemsTemplate.pdf; sourceTree = "<group>"; }; 8349270B27B4EFFC0009AB2B /* deadItemsTemplate.pdf */ = {isa = PBXFileReference; lastKnownFileType = image.pdf; name = deadItemsTemplate.pdf; path = Images/deadItemsTemplate.pdf; sourceTree = "<group>"; };
834B05E82859C006000B7DC0 /* TotalTimeTransformer.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = TotalTimeTransformer.h; path = Transformers/TotalTimeTransformer.h; sourceTree = "<group>"; };
834B05E92859C006000B7DC0 /* TotalTimeTransformer.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; name = TotalTimeTransformer.m; path = Transformers/TotalTimeTransformer.m; sourceTree = "<group>"; };
8355D6B4180612F300D05687 /* NSData+MD5.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "NSData+MD5.h"; sourceTree = "<group>"; }; 8355D6B4180612F300D05687 /* NSData+MD5.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "NSData+MD5.h"; sourceTree = "<group>"; };
8355D6B5180612F300D05687 /* NSData+MD5.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "NSData+MD5.m"; sourceTree = "<group>"; }; 8355D6B5180612F300D05687 /* NSData+MD5.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "NSData+MD5.m"; sourceTree = "<group>"; };
8355D6B7180613FB00D05687 /* Security.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Security.framework; path = System/Library/Frameworks/Security.framework; sourceTree = SDKROOT; }; 8355D6B7180613FB00D05687 /* Security.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Security.framework; path = System/Library/Frameworks/Security.framework; sourceTree = SDKROOT; };
@ -1539,6 +1542,8 @@
17E0D6140F520F87005B6FED /* StringToURLTransformer.h */, 17E0D6140F520F87005B6FED /* StringToURLTransformer.h */,
17E0D6150F520F87005B6FED /* StringToURLTransformer.m */, 17E0D6150F520F87005B6FED /* StringToURLTransformer.m */,
838F84FF25687C5C00C3E614 /* Cog-Bridging-Header.h */, 838F84FF25687C5C00C3E614 /* Cog-Bridging-Header.h */,
834B05E82859C006000B7DC0 /* TotalTimeTransformer.h */,
834B05E92859C006000B7DC0 /* TotalTimeTransformer.m */,
); );
name = Transformers; name = Transformers;
sourceTree = "<group>"; sourceTree = "<group>";
@ -2775,6 +2780,7 @@
17249F0F0D82E17700F33392 /* ToggleQueueTitleTransformer.m in Sources */, 17249F0F0D82E17700F33392 /* ToggleQueueTitleTransformer.m in Sources */,
179D031E0E0CB2500064A77A /* ContainedNode.m in Sources */, 179D031E0E0CB2500064A77A /* ContainedNode.m in Sources */,
179D031F0E0CB2500064A77A /* ContainerNode.m in Sources */, 179D031F0E0CB2500064A77A /* ContainerNode.m in Sources */,
834B05EA2859C006000B7DC0 /* TotalTimeTransformer.m in Sources */,
839DA7CF274A2D4C001B18E5 /* NSDictionary+Merge.m in Sources */, 839DA7CF274A2D4C001B18E5 /* NSDictionary+Merge.m in Sources */,
179D03200E0CB2500064A77A /* DirectoryNode.m in Sources */, 179D03200E0CB2500064A77A /* DirectoryNode.m in Sources */,
179D03210E0CB2500064A77A /* FileIconCell.m in Sources */, 179D03210E0CB2500064A77A /* FileIconCell.m in Sources */,

View File

@ -46,16 +46,20 @@ typedef NS_ENUM(NSInteger, URLOrigin) {
NSMutableArray *queueList; NSMutableArray *queueList;
NSString *totalTime; NSString *totalTime;
NSString *currentStatus;
PlaylistEntry *currentEntry; PlaylistEntry *currentEntry;
PlaylistEntry *nextEntryAfterDeleted; PlaylistEntry *nextEntryAfterDeleted;
NSUndoManager *undoManager; NSUndoManager *undoManager;
BOOL observersRegistered;
} }
@property(nonatomic, retain) PlaylistEntry *_Nullable currentEntry; @property(nonatomic, retain) PlaylistEntry *_Nullable currentEntry;
@property(retain) NSString *_Nullable totalTime; @property(retain) NSString *_Nullable totalTime;
@property(retain) NSString *_Nullable currentStatus;
// Private Methods // Private Methods
- (void)updateTotalTime; - (void)updateTotalTime;

View File

@ -28,9 +28,12 @@
@synthesize currentEntry; @synthesize currentEntry;
@synthesize totalTime; @synthesize totalTime;
@synthesize currentStatus;
static NSArray *cellIdentifiers = nil; static NSArray *cellIdentifiers = nil;
static void *playlistControllerContext = &playlistControllerContext;
+ (void)initialize { + (void)initialize {
cellIdentifiers = @[@"index", @"status", @"title", @"albumartist", @"artist", cellIdentifiers = @[@"index", @"status", @"title", @"albumartist", @"artist",
@"album", @"length", @"year", @"genre", @"track", @"path", @"album", @"length", @"year", @"genre", @"track", @"path",
@ -117,22 +120,93 @@ static NSArray *cellIdentifiers = nil;
[self addObserver:self [self addObserver:self
forKeyPath:@"arrangedObjects" forKeyPath:@"arrangedObjects"
options:(NSKeyValueObservingOptionNew | NSKeyValueObservingOptionOld) options:(NSKeyValueObservingOptionNew | NSKeyValueObservingOptionOld)
context:nil]; context:playlistControllerContext];
[[NSUserDefaultsController sharedUserDefaultsController] addObserver:self forKeyPath:@"values.fontSize" options:(NSKeyValueObservingOptionNew | NSKeyValueObservingOptionInitial) context:nil]; [playbackController addObserver:self
forKeyPath:@"progressOverall"
options:(NSKeyValueObservingOptionNew | NSKeyValueObservingOptionInitial | NSKeyValueObservingOptionOld)
context:playlistControllerContext];
[[NSUserDefaultsController sharedUserDefaultsController] addObserver:self forKeyPath:@"values.fontSize" options:(NSKeyValueObservingOptionNew | NSKeyValueObservingOptionInitial) context:playlistControllerContext];
observersRegistered = YES;
[self.tableView setDraggingSourceOperationMask:NSDragOperationCopy forLocal:NO]; [self.tableView setDraggingSourceOperationMask:NSDragOperationCopy forLocal:NO];
} }
- (void)deinit {
if(observersRegistered) {
[self removeObserver:self forKeyPath:@"arrangedObjects" context:playlistControllerContext];
[playbackController removeObserver:self forKeyPath:@"progressOverall" context:playlistControllerContext];
[[NSUserDefaultsController sharedUserDefaultsController] removeObserver:self forKeyPath:@"values.fontSize" context:playlistControllerContext];
}
}
- (void)startObservingProgress:(NSProgress *)progress {
[progress addObserver:self forKeyPath:@"localizedDescription" options:0 context:playlistControllerContext];
[progress addObserver:self forKeyPath:@"fractionCompleted" options:0 context:playlistControllerContext];
}
- (void)stopObservingProgress:(NSProgress *)progress {
[progress removeObserver:self forKeyPath:@"localizedDescription" context:playlistControllerContext];
[progress removeObserver:self forKeyPath:@"fractionCompleted" context:playlistControllerContext];
}
- (void)observeValueForKeyPath:(NSString *)keyPath - (void)observeValueForKeyPath:(NSString *)keyPath
ofObject:(id)object ofObject:(id)object
change:(NSDictionary *)change change:(NSDictionary *)change
context:(void *)context { context:(void *)context {
if([keyPath isEqualToString:@"arrangedObjects"]) { if(context == playlistControllerContext) {
[self updatePlaylistIndexes]; if([keyPath isEqualToString:@"arrangedObjects"]) {
[self updatePlaylistIndexes];
[self updateTotalTime];
[self.tableView reloadData];
} else if([keyPath isEqualToString:@"values.fontSize"]) {
[self updateRowSize];
} else if([keyPath isEqualToString:@"progressOverall"]) {
id objNew = [change objectForKey:NSKeyValueChangeNewKey];
id objOld = [change objectForKey:NSKeyValueChangeOldKey];
NSProgress *progressNew = nil, *progressOld = nil;
if(objNew && [objNew isKindOfClass:[NSProgress class]])
progressNew = (NSProgress *)objNew;
if(objOld && [objOld isKindOfClass:[NSProgress class]])
progressOld = (NSProgress *)objOld;
if(progressOld) {
[self stopObservingProgress:progressOld];
}
if(progressNew) {
[self startObservingProgress:progressNew];
}
[self updateProgressText];
} else if([keyPath isEqualToString:@"localizedDescription"] ||
[keyPath isEqualToString:@"fractionCompleted"]) {
[self updateProgressText];
}
} else {
[super observeValueForKeyPath:keyPath ofObject:object change:change context:context];
}
}
- (void)updateProgressText {
NSString *description = nil;
if(playbackController.progressOverall) {
if(playbackController.progressJob) {
description = [NSString stringWithFormat:@"%@ - %@", playbackController.progressOverall.localizedDescription, playbackController.progressJob.localizedDescription];
} else {
description = playbackController.progressOverall.localizedDescription;
}
}
if(description) {
[self setTotalTime:nil];
description = [description stringByAppendingFormat:@" - %.2f%% complete", playbackController.progressOverall.fractionCompleted * 100.0];
[self setCurrentStatus:description];
} else {
[self setCurrentStatus:nil];
[self updateTotalTime]; [self updateTotalTime];
[self.tableView reloadData];
} else if([keyPath isEqualToString:@"values.fontSize"]) {
[self updateRowSize];
} }
} }
@ -144,13 +218,31 @@ static inline void dispatch_sync_reentrant(dispatch_queue_t queue, dispatch_bloc
} }
} }
- (void)setProgressBarStatus:(double)status { - (void)beginProgress:(NSString *)localizedDescription {
dispatch_sync_reentrant(dispatch_get_main_queue(), ^{ dispatch_sync_reentrant(dispatch_get_main_queue(), ^{
[self->playbackController setProgressBarStatus:status]; self->playbackController.progressOverall = [NSProgress progressWithTotalUnitCount:100000];
[[NSRunLoop currentRunLoop] runUntilDate:[NSDate dateWithTimeIntervalSinceNow:0.001]]; self->playbackController.progressOverall.localizedDescription = localizedDescription;
}); });
} }
- (void)completeProgress {
dispatch_sync_reentrant(dispatch_get_main_queue(), ^{
[self->playbackController.progressOverall setCompletedUnitCount:100000];
self->playbackController.progressOverall = nil;
});
}
- (void)setProgressStatus:(double)status {
if(status >= 0) {
dispatch_sync_reentrant(dispatch_get_main_queue(), ^{
NSUInteger jobCount = (NSUInteger)ceil(1000.0 * status);
[self->playbackController.progressOverall setCompletedUnitCount:jobCount];
});
} else {
[self completeProgress];
}
}
- (void)updatePlaylistIndexes { - (void)updatePlaylistIndexes {
NSArray *arranged = [self arrangedObjects]; NSArray *arranged = [self arrangedObjects];
NSUInteger n = [arranged count]; NSUInteger n = [arranged count];
@ -163,10 +255,12 @@ static inline void dispatch_sync_reentrant(dispatch_queue_t queue, dispatch_bloc
} }
} }
if(updated) { if(updated) {
[self beginProgress:NSLocalizedString(@"ProgressActionUpdatingIndexes", @"updating playlist indexes")];
[[SQLiteStore sharedStore] syncPlaylistEntries:arranged [[SQLiteStore sharedStore] syncPlaylistEntries:arranged
progressCall:^(double progress) { progressCall:^(double progress) {
[self setProgressBarStatus:progress]; [self setProgressStatus:progress];
}]; }];
[self completeProgress];
} }
} }
@ -461,12 +555,16 @@ static inline void dispatch_sync_reentrant(dispatch_queue_t queue, dispatch_bloc
[super moveObjectsFromIndex:fromIndex toArrangedObjectIndexes:indexSet]; [super moveObjectsFromIndex:fromIndex toArrangedObjectIndexes:indexSet];
[self beginProgress:NSLocalizedString(@"ProgressActionMovingEntries", @"moving playlist entries")];
[[SQLiteStore sharedStore] playlistMoveObjectsFromIndex:fromIndex [[SQLiteStore sharedStore] playlistMoveObjectsFromIndex:fromIndex
toArrangedObjectIndexes:indexSet toArrangedObjectIndexes:indexSet
progressCall:^(double progress) { progressCall:^(double progress) {
[self setProgressBarStatus:progress]; [self setProgressStatus:progress];
}]; }];
[self completeProgress];
[playbackController playlistDidChange:self]; [playbackController playlistDidChange:self];
} }
@ -481,12 +579,16 @@ static inline void dispatch_sync_reentrant(dispatch_queue_t queue, dispatch_bloc
[super moveObjectsInArrangedObjectsFromIndexes:indexSet toIndex:insertIndex]; [super moveObjectsInArrangedObjectsFromIndexes:indexSet toIndex:insertIndex];
[self beginProgress:NSLocalizedString(@"ProgressActionMovingEntries", @"moving playlist entries")];
[[SQLiteStore sharedStore] playlistMoveObjectsInArrangedObjectsFromIndexes:indexSet [[SQLiteStore sharedStore] playlistMoveObjectsInArrangedObjectsFromIndexes:indexSet
toIndex:insertIndex toIndex:insertIndex
progressCall:^(double progress) { progressCall:^(double progress) {
[self setProgressBarStatus:progress]; [self setProgressStatus:progress];
}]; }];
[self completeProgress];
[playbackController playlistDidChange:self]; [playbackController playlistDidChange:self];
} }
@ -669,14 +771,18 @@ static inline void dispatch_sync_reentrant(dispatch_queue_t queue, dispatch_bloc
pe.deleted = NO; pe.deleted = NO;
} }
[self beginProgress:NSLocalizedString(@"ProgressActionInsertingEntries", @"inserting playlist entries")];
[[SQLiteStore sharedStore] playlistInsertTracks:objects [[SQLiteStore sharedStore] playlistInsertTracks:objects
atObjectIndexes:indexes atObjectIndexes:indexes
progressCall:^(double progress) { progressCall:^(double progress) {
[self setProgressBarStatus:progress]; [self setProgressStatus:progress];
}]; }];
[super insertObjects:objects atArrangedObjectIndexes:indexes]; [super insertObjects:objects atArrangedObjectIndexes:indexes];
[self completeProgress];
if([self shuffle] != ShuffleOff) [self resetShuffleList]; if([self shuffle] != ShuffleOff) [self resetShuffleList];
} }
@ -696,14 +802,18 @@ static inline void dispatch_sync_reentrant(dispatch_queue_t queue, dispatch_bloc
pe.trashURL = nil; pe.trashURL = nil;
} }
[self beginProgress:NSLocalizedString(@"ProgressActionUntrashingEntries", @"restoring playlist entries from the trash")];
[[SQLiteStore sharedStore] playlistInsertTracks:objects [[SQLiteStore sharedStore] playlistInsertTracks:objects
atObjectIndexes:indexes atObjectIndexes:indexes
progressCall:^(double progress) { progressCall:^(double progress) {
[self setProgressBarStatus:progress]; [self setProgressStatus:progress];
}]; }];
[super insertObjects:objects atArrangedObjectIndexes:indexes]; [super insertObjects:objects atArrangedObjectIndexes:indexes];
[self completeProgress];
if([self shuffle] != ShuffleOff) [self resetShuffleList]; if([self shuffle] != ShuffleOff) [self resetShuffleList];
} }
@ -759,15 +869,19 @@ static inline void dispatch_sync_reentrant(dispatch_queue_t queue, dispatch_bloc
currentEntry.index = -i - 1; currentEntry.index = -i - 1;
} }
[self beginProgress:NSLocalizedString(@"ProgressActionRemovingEntries", @"removing playlist entries")];
[[SQLiteStore sharedStore] playlistRemoveTracksAtIndexes:unarrangedIndexes [[SQLiteStore sharedStore] playlistRemoveTracksAtIndexes:unarrangedIndexes
progressCall:^(double progress) { progressCall:^(double progress) {
[self setProgressBarStatus:progress]; [self setProgressStatus:progress];
}]; }];
[super removeObjectsAtArrangedObjectIndexes:indexes]; [super removeObjectsAtArrangedObjectIndexes:indexes];
if([self shuffle] != ShuffleOff) [self resetShuffleList]; if([self shuffle] != ShuffleOff) [self resetShuffleList];
[self completeProgress];
[playbackController playlistDidChange:self]; [playbackController playlistDidChange:self];
} }
@ -799,9 +913,11 @@ static inline void dispatch_sync_reentrant(dispatch_queue_t queue, dispatch_bloc
} }
} }
[self beginProgress:NSLocalizedString(@"ProgressActionTrashingEntries", @"moving playlist entries to the trash")];
[[SQLiteStore sharedStore] playlistRemoveTracksAtIndexes:unarrangedIndexes [[SQLiteStore sharedStore] playlistRemoveTracksAtIndexes:unarrangedIndexes
progressCall:^(double progress) { progressCall:^(double progress) {
[self setProgressBarStatus:progress]; [self setProgressStatus:progress];
}]; }];
[super removeObjectsAtArrangedObjectIndexes:indexes]; [super removeObjectsAtArrangedObjectIndexes:indexes];
@ -818,6 +934,8 @@ static inline void dispatch_sync_reentrant(dispatch_queue_t queue, dispatch_bloc
pe.trashURL = removed; pe.trashURL = removed;
} }
} }
[self completeProgress];
} }
- (void)setSortDescriptors:(NSArray *)sortDescriptors { - (void)setSortDescriptors:(NSArray *)sortDescriptors {

View File

@ -26,6 +26,8 @@ typedef enum {
IBOutlet PlaybackController *playbackController; IBOutlet PlaybackController *playbackController;
NSOperationQueue *queue; NSOperationQueue *queue;
BOOL metadataLoadInProgress;
} }
- (void)initDefaults; - (void)initDefaults;

View File

@ -261,12 +261,62 @@ static inline void dispatch_sync_reentrant(dispatch_queue_t queue, dispatch_bloc
} }
} }
- (void)setProgressBarStatus:(double)status { - (void)beginProgress:(NSString *)localizedDescription {
dispatch_sync_reentrant(dispatch_get_main_queue(), ^{ dispatch_sync_reentrant(dispatch_get_main_queue(), ^{
[self->playbackController setProgressBarStatus:status]; self->playbackController.progressOverall = [NSProgress progressWithTotalUnitCount:100000];
self->playbackController.progressOverall.localizedDescription = localizedDescription;
}); });
} }
- (void)completeProgress {
dispatch_sync_reentrant(dispatch_get_main_queue(), ^{
if(self->playbackController.progressJob) {
[self->playbackController.progressJob setCompletedUnitCount:100000];
self->playbackController.progressJob = nil;
}
[self->playbackController.progressOverall setCompletedUnitCount:100000];
self->playbackController.progressOverall = nil;
});
}
- (void)beginProgressJob:(NSString *)localizedDescription percentOfTotal:(double)percent {
dispatch_sync_reentrant(dispatch_get_main_queue(), ^{
NSUInteger jobCount = (NSUInteger)ceil(1000.0 * percent);
self->playbackController.progressJob = [NSProgress progressWithTotalUnitCount:100000];
self->playbackController.progressJob.localizedDescription = localizedDescription;
[self->playbackController.progressOverall addChild:self->playbackController.progressJob withPendingUnitCount:jobCount];
});
}
- (void)completeProgressJob {
dispatch_sync_reentrant(dispatch_get_main_queue(), ^{
[self->playbackController.progressJob setCompletedUnitCount:100000];
self->playbackController.progressJob = nil;
});
}
- (void)setProgressStatus:(double)status {
if(status >= 0) {
dispatch_sync_reentrant(dispatch_get_main_queue(), ^{
NSUInteger jobCount = (NSUInteger)ceil(1000.0 * status);
[self->playbackController.progressOverall setCompletedUnitCount:jobCount];
});
} else {
[self completeProgress];
}
}
- (void)setProgressJobStatus:(double)status {
if(status >= 0) {
dispatch_sync_reentrant(dispatch_get_main_queue(), ^{
NSUInteger jobCount = (NSUInteger)ceil(1000.0 * status);
[self->playbackController.progressJob setCompletedUnitCount:jobCount];
});
} else {
[self completeProgressJob];
}
}
- (NSArray *)insertURLs:(NSArray *)urls atIndex:(NSInteger)index sort:(BOOL)sort { - (NSArray *)insertURLs:(NSArray *)urls atIndex:(NSInteger)index sort:(BOOL)sort {
NSMutableSet *uniqueURLs = [NSMutableSet set]; NSMutableSet *uniqueURLs = [NSMutableSet set];
@ -276,19 +326,23 @@ static inline void dispatch_sync_reentrant(dispatch_queue_t queue, dispatch_bloc
NSMutableArray *validURLs = [NSMutableArray array]; NSMutableArray *validURLs = [NSMutableArray array];
NSDictionary *xmlData = nil; NSDictionary *xmlData = nil;
double progress = 0.0; double progress;
if(!urls) { if(!urls) {
[self setProgressBarStatus:-1]; [self completeProgress];
return @[]; return @[];
} }
[self beginProgress:NSLocalizedString(@"ProgressActionLoader", @"playlist loader inserting files")];
[self beginProgressJob:NSLocalizedString(@"ProgressSubActionLoaderListingFiles", @"collecting files") percentOfTotal:20.0];
if(index < 0) if(index < 0)
index = 0; index = 0;
[self setProgressBarStatus:progress]; progress = 0.0;
double progressstep = [urls count] ? 20.0 / (double)([urls count]) : 0; double progressstep = [urls count] ? 100.0 / (double)([urls count]) : 0;
NSURL *url; NSURL *url;
for(url in urls) { for(url in urls) {
@ -309,12 +363,12 @@ static inline void dispatch_sync_reentrant(dispatch_queue_t queue, dispatch_bloc
progress += progressstep; progress += progressstep;
[self setProgressBarStatus:progress]; [self setProgressJobStatus:progress];
} }
progress = 20.0; [self completeProgressJob];
[self setProgressBarStatus:progress]; progress = 0.0;
DLog(@"Expanded urls: %@", expandedURLs); DLog(@"Expanded urls: %@", expandedURLs);
@ -326,7 +380,9 @@ static inline void dispatch_sync_reentrant(dispatch_queue_t queue, dispatch_bloc
sortedURLs = expandedURLs; sortedURLs = expandedURLs;
} }
progressstep = [sortedURLs count] ? 20.0 / (double)([sortedURLs count]) : 0; [self beginProgressJob:NSLocalizedString(@"ProgressSubActionLoaderFilteringContainerFiles", @"handling container file types") percentOfTotal:20.0];
progressstep = [sortedURLs count] ? 100.0 / (double)([sortedURLs count]) : 0;
for(url in sortedURLs) { for(url in sortedURLs) {
// Container vs non-container url // Container vs non-container url
@ -349,17 +405,23 @@ static inline void dispatch_sync_reentrant(dispatch_queue_t queue, dispatch_bloc
} }
progress += progressstep; progress += progressstep;
[self setProgressBarStatus:progress]; [self setProgressJobStatus:progress];
} }
progress = 40.0; progress = 0.0;
[self setProgressBarStatus:progress]; [self completeProgressJob];
if([fileURLs count] > 0) {
[self beginProgressJob:NSLocalizedString(@"ProgressSubActionLoaderFilteringFiles", @"eliminating unsupported file types") percentOfTotal:20.0];
} else {
[self setProgressStatus:60.0];
}
DLog(@"File urls: %@", fileURLs); DLog(@"File urls: %@", fileURLs);
DLog(@"Contained urls: %@", containedURLs); DLog(@"Contained urls: %@", containedURLs);
progressstep = [fileURLs count] ? 20.0 / (double)([fileURLs count]) : 0; progressstep = [fileURLs count] ? 100.0 / (double)([fileURLs count]) : 0;
for(url in fileURLs) { for(url in fileURLs) {
progress += progressstep; progress += progressstep;
@ -379,16 +441,24 @@ static inline void dispatch_sync_reentrant(dispatch_queue_t queue, dispatch_bloc
[uniqueURLs addObject:url]; [uniqueURLs addObject:url];
} }
[self setProgressBarStatus:progress]; [self setProgressJobStatus:progress];
} }
progress = 60.0; progress = 0.0;
[self setProgressBarStatus:progress]; if([fileURLs count] > 0) {
[self completeProgressJob];
}
if([containedURLs count] > 0) {
[self beginProgressJob:NSLocalizedString(@"ProgressSubActionLoaderFilteringContainedFiles", @"eliminating unsupported file types from containers") percentOfTotal:20.0];
} else {
[self setProgressStatus:80.0];
}
DLog(@"Valid urls: %@", validURLs); DLog(@"Valid urls: %@", validURLs);
progressstep = [containedURLs count] ? 20.0 / (double)([containedURLs count]) : 0; progressstep = [containedURLs count] ? 100.0 / (double)([containedURLs count]) : 0;
for(url in containedURLs) { for(url in containedURLs) {
progress += progressstep; progress += progressstep;
@ -402,12 +472,13 @@ static inline void dispatch_sync_reentrant(dispatch_queue_t queue, dispatch_bloc
[validURLs addObject:url]; [validURLs addObject:url];
[self setProgressBarStatus:progress]; [self setProgressJobStatus:progress];
} }
progress = 80.0; progress = 0.0;
if([containedURLs count] > 0) {
[self setProgressBarStatus:progress]; [self completeProgressJob];
}
// Create actual entries // Create actual entries
int count = (int)[validURLs count]; int count = (int)[validURLs count];
@ -415,11 +486,13 @@ static inline void dispatch_sync_reentrant(dispatch_queue_t queue, dispatch_bloc
// no valid URLs, or they use an unsupported URL scheme // no valid URLs, or they use an unsupported URL scheme
if(!count) { if(!count) {
[self setProgressBarStatus:-1]; [self completeProgress];
return @[]; return @[];
} }
progressstep = 20.0 / (double)(count); [self beginProgressJob:NSLocalizedString(@"ProgressSubActionLoaderAddingEntries", @"creating and adding playlist entries") percentOfTotal:20.0];
progressstep = 100.0 / (double)(count);
NSInteger i = 0; NSInteger i = 0;
NSMutableArray *entries = [NSMutableArray arrayWithCapacity:count]; NSMutableArray *entries = [NSMutableArray arrayWithCapacity:count];
@ -436,7 +509,7 @@ static inline void dispatch_sync_reentrant(dispatch_queue_t queue, dispatch_bloc
++i; ++i;
progress += progressstep; progress += progressstep;
[self setProgressBarStatus:progress]; [self setProgressJobStatus:progress];
} }
NSInteger j = index + i; NSInteger j = index + i;
@ -455,8 +528,7 @@ static inline void dispatch_sync_reentrant(dispatch_queue_t queue, dispatch_bloc
} }
} }
progress = 100.0; [self completeProgress];
[self setProgressBarStatus:progress];
NSIndexSet *is = [NSIndexSet indexSetWithIndexesInRange:NSMakeRange(index, [entries count])]; NSIndexSet *is = [NSIndexSet indexSetWithIndexesInRange:NSMakeRange(index, [entries count])];
@ -486,19 +558,21 @@ static inline void dispatch_sync_reentrant(dispatch_queue_t queue, dispatch_bloc
NSMutableArray *arrayRest = [entries mutableCopy]; NSMutableArray *arrayRest = [entries mutableCopy];
[arrayRest removeObjectAtIndex:0]; [arrayRest removeObjectAtIndex:0];
progress = 0.0; metadataLoadInProgress = YES;
[self setProgressBarStatus:progress];
[self beginProgress:NSLocalizedString(@"ProgressActionLoadingMetadata", @"loading metadata for tracks")];
[self beginProgressJob:NSLocalizedString(@"ProgressSubActionLoadingMetadata", @"processing files") percentOfTotal:50.0];
[self performSelectorOnMainThread:@selector(syncLoadInfoForEntries:) withObject:arrayFirst waitUntilDone:YES]; [self performSelectorOnMainThread:@selector(syncLoadInfoForEntries:) withObject:arrayFirst waitUntilDone:YES];
progressstep = 100.0 / (double)([entries count]); progressstep = 100.0 / (double)([entries count]);
progress += progressstep; progress = progressstep;
[self setProgressBarStatus:progress]; [self setProgressJobStatus:progress];
if([arrayRest count]) if([arrayRest count])
[self performSelectorInBackground:@selector(loadInfoForEntries:) withObject:arrayRest]; [self performSelectorInBackground:@selector(loadInfoForEntries:) withObject:arrayRest];
else else
[self setProgressBarStatus:-1]; [self completeProgress];
return entries; return entries;
} }
} }
@ -510,17 +584,20 @@ static inline void dispatch_sync_reentrant(dispatch_queue_t queue, dispatch_bloc
SQLiteStore *store = [SQLiteStore sharedStore]; SQLiteStore *store = [SQLiteStore sharedStore];
__block double progress = [playbackController progressBarStatus]; __block double progress = 0.0;
if(progress < 0 || progress >= 100) double progressstep;
progress = 0;
double progressRemaining = 100.0 - progress; if(metadataLoadInProgress && [entries count]) {
progressstep = 100.0 / (double)([entries count] + 1);
progress = progressstep;
} else if([entries count]) {
[self beginProgress:NSLocalizedString(@"ProgressActionLoadingMetadata", @"loading metadata for tracks")];
[self beginProgressJob:NSLocalizedString(@"ProgressSubActionLoadingMetadata", @"processing files") percentOfTotal:50.0];
// 50% for properties reading, 50% for applying them to the main thread progressstep = 100.0 / (double)([entries count]);
const double progressstep = [entries count] ? (progressRemaining / 2.0) / [entries count] : 0; progress = 0.0;
}
progressRemaining = progress + (progressRemaining / 2.0);
i = 0; i = 0;
j = 0; j = 0;
@ -537,6 +614,8 @@ static inline void dispatch_sync_reentrant(dispatch_queue_t queue, dispatch_bloc
if(!i) { if(!i) {
[playlistController performSelectorOnMainThread:@selector(updateTotalTime) withObject:nil waitUntilDone:NO]; [playlistController performSelectorOnMainThread:@selector(updateTotalTime) withObject:nil waitUntilDone:NO];
[self completeProgress];
metadataLoadInProgress = NO;
return; return;
} }
@ -555,11 +634,13 @@ static inline void dispatch_sync_reentrant(dispatch_queue_t queue, dispatch_bloc
NSBlockOperation *op = [[NSBlockOperation alloc] init]; NSBlockOperation *op = [[NSBlockOperation alloc] init];
[op addExecutionBlock:^{ [op addExecutionBlock:^{
[weakLock lock]; if(weakPe.deleted) {
progress += progressstep; [weakLock lock];
[weakLock unlock]; progress += progressstep;
[self setProgressJobStatus:progress];
if(weakPe.deleted) return; [weakLock unlock];
return;
}
DLog(@"Loading metadata for %@", weakPe.URL); DLog(@"Loading metadata for %@", weakPe.URL);
@ -575,7 +656,8 @@ static inline void dispatch_sync_reentrant(dispatch_queue_t queue, dispatch_bloc
entryInfo = [weakDataStore coalesceEntryInfo:entryInfo]; entryInfo = [weakDataStore coalesceEntryInfo:entryInfo];
[weakArray addObject:weakPe]; [weakArray addObject:weakPe];
[weakArray addObject:entryInfo]; [weakArray addObject:entryInfo];
[self setProgressBarStatus:progress]; progress += progressstep;
[self setProgressJobStatus:progress];
[weakLock unlock]; [weakLock unlock];
}]; }];
@ -585,8 +667,12 @@ static inline void dispatch_sync_reentrant(dispatch_queue_t queue, dispatch_bloc
[queue waitUntilAllOperationsAreFinished]; [queue waitUntilAllOperationsAreFinished];
progress = progressRemaining; progress = 0.0;
[self setProgressBarStatus:progress]; [self completeProgressJob];
[self beginProgressJob:NSLocalizedString(@"ProgressSubActionMetadataApply", @"applying info to playlist storage") percentOfTotal:50.0];
progressstep = 200.0 / (double)([outArray count]);
for(i = 0, j = [outArray count]; i < j; i += 2) { for(i = 0, j = [outArray count]; i < j; i += 2) {
__block PlaylistEntry *weakPe = [outArray objectAtIndex:i]; __block PlaylistEntry *weakPe = [outArray objectAtIndex:i];
@ -597,7 +683,7 @@ static inline void dispatch_sync_reentrant(dispatch_queue_t queue, dispatch_bloc
[store trackUpdate:weakPe]; [store trackUpdate:weakPe];
} }
progress += progressstep; progress += progressstep;
[self setProgressBarStatus:progress]; [self setProgressJobStatus:progress];
}); });
} }
@ -612,8 +698,10 @@ static inline void dispatch_sync_reentrant(dispatch_queue_t queue, dispatch_bloc
}); });
} }
[self setProgressBarStatus:-1]; [self completeProgress];
metadataLoadInProgress = NO;
} }
// To be called on main thread only // To be called on main thread only
- (void)syncLoadInfoForEntries:(NSArray *)entries { - (void)syncLoadInfoForEntries:(NSArray *)entries {
NSMutableIndexSet *update_indexes = [[NSMutableIndexSet alloc] init]; NSMutableIndexSet *update_indexes = [[NSMutableIndexSet alloc] init];

View File

@ -0,0 +1,16 @@
//
// TotalTimeTransformer.h
// Cog
//
// Created by Christopher Snowhill on 6/15/22.
//
#import <Foundation/Foundation.h>
NS_ASSUME_NONNULL_BEGIN
@interface TotalTimeTransformer : NSValueTransformer
@end
NS_ASSUME_NONNULL_END

View File

@ -0,0 +1,32 @@
//
// TotalTimeTransformer.m
// Cog
//
// Created by Christopher Snowhill on 6/15/22.
//
#import "TotalTimeTransformer.h"
@implementation TotalTimeTransformer
+ (Class)transformedValueClass {
return [NSString class];
}
+ (BOOL)allowsReverseTransformation {
return NO;
}
- (id)transformedValue:(id)value {
if(value == nil) return @"";
if([value isKindOfClass:[NSString class]]) {
NSString *string = (NSString *)value;
if([string length] > 0) {
return [@"Total Duration: " stringByAppendingString:string];
}
}
return @"";
}
@end

View File

@ -45,3 +45,21 @@
"CogTitle" = "Cog"; "CogTitle" = "Cog";
"PreferencesTitle" = "Preferences"; "PreferencesTitle" = "Preferences";
"ProgressActionUpdatingIndexes" = "updating playlist indexes";
"ProgressActionMovingEntries" = "moving playlist entries";
"ProgressActionInsertingEntries" = "inserting playlist entries";
"ProgressActionUntrashingEntries" = "restoring playlist entries from the trash";
"ProgressActionRemovingEntries" = "removing playlist entries";
"ProgressActionTrashingEntries" = "moving playlist entries to the trash";
"ProgressActionLoader" = "playlist loader inserting files";
"ProgressSubActionLoaderListingFiles" = "collecting files";
"ProgressSubActionLoaderFilteringContainerFiles" = "handling container file types";
"ProgressSubActionLoaderFilteringFiles" = "eliminating unsupported file types";
"ProgressSubActionLoaderFilteringContainedFiles" = "eliminating unsupported file types from containers";
"ProgressSubActionLoaderAddingEntries" = "creating and adding playlist entries";
"ProgressActionLoadingMetadata" = "loading metadata for tracks";
"ProgressSubActionLoadingMetadata" = "processing files";
"ProgressSubActionMetadataApply" = "applying info to playlist storage";

View File

@ -45,3 +45,21 @@
"CogTitle" = "Cog"; "CogTitle" = "Cog";
"PreferencesTitle" = "Preferences"; "PreferencesTitle" = "Preferences";
"ProgressActionUpdatingIndexes" = "updating playlist indexes";
"ProgressActionMovingEntries" = "moving playlist entries";
"ProgressActionInsertingEntries" = "inserting playlist entries";
"ProgressActionUntrashingEntries" = "restoring playlist entries from the trash";
"ProgressActionRemovingEntries" = "removing playlist entries";
"ProgressActionTrashingEntries" = "moving playlist entries to the trash";
"ProgressActionLoader" = "playlist loader inserting files";
"ProgressSubActionLoaderListingFiles" = "collecting files";
"ProgressSubActionLoaderFilteringContainerFiles" = "handling container file types";
"ProgressSubActionLoaderFilteringFiles" = "eliminating unsupported file types";
"ProgressSubActionLoaderFilteringContainedFiles" = "eliminating unsupported file types from containers";
"ProgressSubActionLoaderAddingEntries" = "creating and adding playlist entries";
"ProgressActionLoadingMetadata" = "loading metadata for tracks";
"ProgressSubActionLoadingMetadata" = "processing files";
"ProgressSubActionMetadataApply" = "applying info to playlist storage";