- Retrieve profile paths properly instead of hard coding
- Display playlist total duration in units up to weeks and down to just seconds, and only pluralize units as necessary - Major change: Implemented a SQLite disk backed playlist, track data, and queue storage system, which will be synchronized from the player in real time, and will hopefully survive system or app crashes. Existing plist playlist will be imported on first run, and removed on shutdown.CQTexperiment
parent
fd75e1b260
commit
2445cc94a9
|
@ -150,11 +150,19 @@ void* kAppControllerContext = &kAppControllerContext;
|
|||
(void) [spotlightWindowController init];
|
||||
|
||||
[[playlistController undoManager] disableUndoRegistration];
|
||||
NSString *basePath = [@"~/Library/Application Support/Cog/" stringByExpandingTildeInPath];
|
||||
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSApplicationSupportDirectory, NSUserDomainMask, YES);
|
||||
NSString *basePath = [[paths firstObject] stringByAppendingPathComponent:@"Cog"];
|
||||
|
||||
NSString *dbFilename = @"Default.sqlite";
|
||||
|
||||
NSString *oldFilename = @"Default.m3u";
|
||||
NSString *newFilename = @"Default.xml";
|
||||
|
||||
if ([[NSFileManager defaultManager] fileExistsAtPath:[basePath stringByAppendingPathComponent:newFilename]])
|
||||
if ([[NSFileManager defaultManager] fileExistsAtPath:[basePath stringByAppendingPathComponent:dbFilename]])
|
||||
{
|
||||
[playlistLoader addDatabase];
|
||||
}
|
||||
else if ([[NSFileManager defaultManager] fileExistsAtPath:[basePath stringByAppendingPathComponent:newFilename]])
|
||||
{
|
||||
[playlistLoader addURL:[NSURL fileURLWithPath:[basePath stringByAppendingPathComponent:newFilename]]];
|
||||
}
|
||||
|
@ -346,9 +354,8 @@ void* kAppControllerContext = &kAppControllerContext;
|
|||
[playbackController stop:self];
|
||||
|
||||
NSFileManager *fileManager = [NSFileManager defaultManager];
|
||||
NSString *folder = @"~/Library/Application Support/Cog/";
|
||||
|
||||
folder = [folder stringByExpandingTildeInPath];
|
||||
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSApplicationSupportDirectory, NSUserDomainMask, YES);
|
||||
NSString *folder = [[paths firstObject] stringByAppendingPathComponent:@"Cog"];
|
||||
|
||||
if ([fileManager fileExistsAtPath: folder] == NO)
|
||||
{
|
||||
|
@ -359,11 +366,11 @@ void* kAppControllerContext = &kAppControllerContext;
|
|||
|
||||
NSString * fileName = @"Default.xml";
|
||||
|
||||
[playlistLoader saveXml:[folder stringByAppendingPathComponent: fileName]];
|
||||
NSError *error;
|
||||
[[NSFileManager defaultManager] removeItemAtPath:[folder stringByAppendingPathComponent:fileName] error:&error];
|
||||
|
||||
fileName = @"Default.m3u";
|
||||
|
||||
NSError *error;
|
||||
[[NSFileManager defaultManager] removeItemAtPath:[folder stringByAppendingPathComponent:fileName] error:&error];
|
||||
|
||||
DLog(@"Saving expanded nodes: %@", [expandedNodes description]);
|
||||
|
|
|
@ -109,8 +109,11 @@ static PluginController *sharedPluginController = nil;
|
|||
|
||||
- (void)loadPlugins
|
||||
{
|
||||
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSApplicationSupportDirectory, NSUserDomainMask, YES);
|
||||
NSString *basePath = [[paths firstObject] stringByAppendingPathComponent:@"Cog"];
|
||||
|
||||
[self loadPluginsAtPath:[[NSBundle mainBundle] builtInPlugInsPath]];
|
||||
[self loadPluginsAtPath:[@"~/Library/Application Support/Cog/Plugins" stringByExpandingTildeInPath]];
|
||||
[self loadPluginsAtPath:[basePath stringByAppendingPathComponent:@"Plugins"]];
|
||||
}
|
||||
|
||||
- (void)setupContainer:(NSString *)className
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<document type="com.apple.InterfaceBuilder3.Cocoa.XIB" version="3.0" toolsVersion="19158" targetRuntime="MacOSX.Cocoa" propertyAccessControl="none" useAutolayout="YES">
|
||||
<document type="com.apple.InterfaceBuilder3.Cocoa.XIB" version="3.0" toolsVersion="19529" targetRuntime="MacOSX.Cocoa" propertyAccessControl="none" useAutolayout="YES">
|
||||
<dependencies>
|
||||
<deployment identifier="macosx"/>
|
||||
<plugIn identifier="com.apple.InterfaceBuilder.CocoaPlugin" version="19158"/>
|
||||
<plugIn identifier="com.apple.InterfaceBuilder.CocoaPlugin" version="19529"/>
|
||||
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
|
||||
</dependencies>
|
||||
<objects>
|
||||
|
|
|
@ -113,6 +113,8 @@
|
|||
836D28A818086386005B7299 /* MiniModeMenuTitleTransformer.m in Sources */ = {isa = PBXBuildFile; fileRef = 836D28A718086386005B7299 /* MiniModeMenuTitleTransformer.m */; };
|
||||
836F5BF91A357A01002730CC /* sidplay.bundle in CopyFiles */ = {isa = PBXBuildFile; fileRef = 8314D6411A354DFF00EEE8E6 /* sidplay.bundle */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; };
|
||||
836FB5A718206F2500B3AD2D /* Hively.bundle in CopyFiles */ = {isa = PBXBuildFile; fileRef = 836FB5471820538800B3AD2D /* Hively.bundle */; settings = {ATTRIBUTES = (CodeSignOnCopy, ); }; };
|
||||
8370D73D277419F700245CE0 /* SQLiteStore.m in Sources */ = {isa = PBXBuildFile; fileRef = 8370D73C277419F700245CE0 /* SQLiteStore.m */; };
|
||||
8370D73F2775AE1300245CE0 /* libsqlite3.tbd in Frameworks */ = {isa = PBXBuildFile; fileRef = 8370D73E2775AE1300245CE0 /* libsqlite3.tbd */; };
|
||||
8384914018083E4E00E7332D /* filetype.icns in Resources */ = {isa = PBXBuildFile; fileRef = 8384913D18083E4E00E7332D /* filetype.icns */; };
|
||||
8384915918083EAB00E7332D /* infoTemplate.pdf in Resources */ = {isa = PBXBuildFile; fileRef = 8384914318083EAB00E7332D /* infoTemplate.pdf */; };
|
||||
8384915A18083EAB00E7332D /* missingArt@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = 8384914418083EAB00E7332D /* missingArt@2x.png */; };
|
||||
|
@ -843,6 +845,9 @@
|
|||
836D28A718086386005B7299 /* MiniModeMenuTitleTransformer.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = MiniModeMenuTitleTransformer.m; path = Window/MiniModeMenuTitleTransformer.m; sourceTree = "<group>"; };
|
||||
836F6B2518BDB80D0095E648 /* vgmstream.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = vgmstream.xcodeproj; path = Plugins/vgmstream/vgmstream.xcodeproj; sourceTree = "<group>"; };
|
||||
836FB5421820538700B3AD2D /* Hively.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = Hively.xcodeproj; path = Plugins/Hively/Hively.xcodeproj; sourceTree = "<group>"; };
|
||||
8370D739277419D200245CE0 /* SQLiteStore.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = SQLiteStore.h; sourceTree = "<group>"; };
|
||||
8370D73C277419F700245CE0 /* SQLiteStore.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = SQLiteStore.m; sourceTree = "<group>"; };
|
||||
8370D73E2775AE1300245CE0 /* libsqlite3.tbd */ = {isa = PBXFileReference; lastKnownFileType = "sourcecode.text-based-dylib-definition"; name = libsqlite3.tbd; path = usr/lib/libsqlite3.tbd; sourceTree = SDKROOT; };
|
||||
8375B05117FFEA400092A79F /* OpusPlugin.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = OpusPlugin.xcodeproj; path = Plugins/Opus/OpusPlugin.xcodeproj; sourceTree = "<group>"; };
|
||||
8384912518080F2D00E7332D /* Logging.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Logging.h; sourceTree = "<group>"; };
|
||||
8384913D18083E4E00E7332D /* filetype.icns */ = {isa = PBXFileReference; lastKnownFileType = image.icns; path = filetype.icns; sourceTree = "<group>"; };
|
||||
|
@ -940,6 +945,7 @@
|
|||
isa = PBXFrameworksBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
8370D73F2775AE1300245CE0 /* libsqlite3.tbd in Frameworks */,
|
||||
ED69CBC725BE32C00090B90D /* MASShortcut.framework in Frameworks */,
|
||||
8355D6B8180613FB00D05687 /* Security.framework in Frameworks */,
|
||||
8D11072F0486CEB800E47090 /* Cocoa.framework in Frameworks */,
|
||||
|
@ -1081,6 +1087,8 @@
|
|||
177EC01B0B8BC2CF0000BC8C /* TrackingCell.m */,
|
||||
177EC01C0B8BC2CF0000BC8C /* TrackingSlider.h */,
|
||||
177EC01D0B8BC2CF0000BC8C /* TrackingSlider.m */,
|
||||
8370D739277419D200245CE0 /* SQLiteStore.h */,
|
||||
8370D73C277419F700245CE0 /* SQLiteStore.m */,
|
||||
);
|
||||
path = Utils;
|
||||
sourceTree = "<group>";
|
||||
|
@ -1381,6 +1389,7 @@
|
|||
29B97323FDCFA39411CA2CEA /* Frameworks */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
8370D73E2775AE1300245CE0 /* libsqlite3.tbd */,
|
||||
83AB9031237CEFD300A433D5 /* MediaPlayer.framework */,
|
||||
8355D6B7180613FB00D05687 /* Security.framework */,
|
||||
1058C7A0FEA54F0111CA2CBB /* Linked Frameworks */,
|
||||
|
@ -2236,6 +2245,7 @@
|
|||
179D03220E0CB2500064A77A /* FileNode.m in Sources */,
|
||||
179D03230E0CB2500064A77A /* FileTreeDataSource.m in Sources */,
|
||||
179D03240E0CB2500064A77A /* FileTreeController.m in Sources */,
|
||||
8370D73D277419F700245CE0 /* SQLiteStore.m in Sources */,
|
||||
179D03260E0CB2500064A77A /* PathNode.m in Sources */,
|
||||
179D03270E0CB2500064A77A /* PathWatcher.m in Sources */,
|
||||
179D03280E0CB2500064A77A /* SmartFolderNode.m in Sources */,
|
||||
|
|
|
@ -106,6 +106,7 @@ typedef NS_ENUM(NSInteger, URLOrigin) {
|
|||
// queue methods
|
||||
- (IBAction)toggleQueued:(id)sender;
|
||||
- (IBAction)emptyQueueList:(id)sender;
|
||||
- (void)emptyQueueListUnsynced;
|
||||
- (NSMutableArray *)queueList;
|
||||
|
||||
// reload metadata of selection
|
||||
|
@ -114,4 +115,6 @@ typedef NS_ENUM(NSInteger, URLOrigin) {
|
|||
- (void)moveObjectsInArrangedObjectsFromIndexes:(NSIndexSet *)indexSet
|
||||
toIndex:(NSUInteger)insertIndex;
|
||||
|
||||
- (void)insertObjectsUnsynced:(NSArray *)objects atArrangedObjectIndexes:(NSIndexSet *)indexes;
|
||||
|
||||
@end
|
||||
|
|
|
@ -16,6 +16,7 @@
|
|||
#import "SpotlightWindowController.h"
|
||||
#import "StatusImageTransformer.h"
|
||||
#import "ToggleQueueTitleTransformer.h"
|
||||
#import "SQLiteStore.h"
|
||||
|
||||
#import "Logging.h"
|
||||
|
||||
|
@ -119,10 +120,16 @@
|
|||
- (void)updatePlaylistIndexes {
|
||||
NSArray *arranged = [self arrangedObjects];
|
||||
NSUInteger n = [arranged count];
|
||||
BOOL updated = NO;
|
||||
for (NSUInteger i = 0; i < n; i++) {
|
||||
PlaylistEntry *pe = arranged[i];
|
||||
if (pe.index != i) // Make sure we don't get into some kind of crazy observing loop...
|
||||
pe.index = (int) i;
|
||||
if (pe.index != i) { // Make sure we don't get into some kind of crazy observing loop...
|
||||
pe.index = i;
|
||||
updated = YES;
|
||||
}
|
||||
}
|
||||
if (updated) {
|
||||
[[SQLiteStore sharedStore] syncPlaylistEntries:arranged];
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -130,6 +137,7 @@
|
|||
double tt = 0;
|
||||
ldiv_t hoursAndMinutes;
|
||||
ldiv_t daysAndHours;
|
||||
ldiv_t weeksAndDays;
|
||||
|
||||
for (PlaylistEntry *pe in [self arrangedObjects]) {
|
||||
if (!isnan([pe.length doubleValue])) tt += [pe.length doubleValue];
|
||||
|
@ -140,13 +148,52 @@
|
|||
|
||||
if (hoursAndMinutes.quot >= 24) {
|
||||
daysAndHours = ldiv(hoursAndMinutes.quot, 24);
|
||||
[self setTotalTime:[NSString stringWithFormat:@"%ld days %ld hours %ld minutes %ld seconds",
|
||||
daysAndHours.quot, daysAndHours.rem,
|
||||
hoursAndMinutes.rem, sec % 60]];
|
||||
if (daysAndHours.quot >= 7) {
|
||||
weeksAndDays = ldiv(daysAndHours.quot, 7);
|
||||
[self setTotalTime:[NSString stringWithFormat:@"%ld week%@ %ld day%@ %ld hour%@ %ld minute%@ %ld second%@",
|
||||
weeksAndDays.quot,
|
||||
weeksAndDays.quot != 1 ? @"s" : @"",
|
||||
weeksAndDays.rem,
|
||||
weeksAndDays.rem != 1 ? @"s" : @"",
|
||||
daysAndHours.rem,
|
||||
daysAndHours.rem != 1 ? @"s" : @"",
|
||||
hoursAndMinutes.rem,
|
||||
hoursAndMinutes.rem != 1 ? @"s" : @"",
|
||||
sec % 60,
|
||||
(sec % 60) != 1 ? @"s" : @""]];
|
||||
} else {
|
||||
[self setTotalTime:[NSString stringWithFormat:@"%ld hours %ld minutes %ld seconds",
|
||||
hoursAndMinutes.quot, hoursAndMinutes.rem,
|
||||
sec % 60]];
|
||||
[self setTotalTime:[NSString stringWithFormat:@"%ld day%@ %ld hour%@ %ld minute%@ %ld second%@",
|
||||
daysAndHours.quot,
|
||||
daysAndHours.quot != 1 ? @"s" : @"",
|
||||
daysAndHours.rem,
|
||||
daysAndHours.rem != 1 ? @"s" : @"",
|
||||
hoursAndMinutes.rem,
|
||||
hoursAndMinutes.rem != 1 ? @"s" : @"",
|
||||
sec % 60,
|
||||
(sec % 60) != 1 ? @"s" : @""]];
|
||||
}
|
||||
} else {
|
||||
if (hoursAndMinutes.quot > 0) {
|
||||
[self setTotalTime:[NSString stringWithFormat:@"%ld hour%@ %ld minute%@ %ld second%@",
|
||||
hoursAndMinutes.quot,
|
||||
hoursAndMinutes.quot != 1 ? @"s" : @"",
|
||||
hoursAndMinutes.rem,
|
||||
hoursAndMinutes.rem != 1 ? @"s" : @"",
|
||||
sec % 60,
|
||||
(sec % 60) != 1 ? @"s" : @""]];
|
||||
} else {
|
||||
if (hoursAndMinutes.rem > 0) {
|
||||
[self setTotalTime:[NSString stringWithFormat:@"%ld minute%@ %ld second%@",
|
||||
hoursAndMinutes.rem,
|
||||
hoursAndMinutes.rem != 1 ? @"s" : @"",
|
||||
sec % 60,
|
||||
(sec % 60) != 1 ? @"s" : @""]];
|
||||
} else {
|
||||
[self setTotalTime:[NSString stringWithFormat:@"%ld second%@",
|
||||
sec,
|
||||
sec != 1 ? @"s" : @""]];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -169,6 +216,10 @@
|
|||
toIndex:(NSUInteger)insertIndex {
|
||||
[super moveObjectsInArrangedObjectsFromIndexes:indexSet toIndex:insertIndex];
|
||||
|
||||
#if 0 // syncPlaylistEntries is already called for rearrangement
|
||||
[[SQLiteStore sharedStore] playlistMoveObjectsInArrangedObjectsFromIndexes:indexSet toIndex:insertIndex];
|
||||
#endif
|
||||
|
||||
[playbackController playlistDidChange:self];
|
||||
}
|
||||
|
||||
|
@ -298,12 +349,9 @@
|
|||
|
||||
NSMutableIndexSet *disarrangedIndexes = [[NSMutableIndexSet alloc] init];
|
||||
|
||||
NSUInteger index = [indexes firstIndex];
|
||||
while (index != NSNotFound) {
|
||||
[disarrangedIndexes
|
||||
addIndex:[[self content] indexOfObject:[[self arrangedObjects] objectAtIndex:index]]];
|
||||
index = [indexes indexGreaterThanIndex:index];
|
||||
}
|
||||
[indexes enumerateIndexesUsingBlock:^(NSUInteger idx, BOOL * _Nonnull stop) {
|
||||
[disarrangedIndexes addIndex:[[self content] indexOfObject:[[self arrangedObjects] objectAtIndex:idx]]];
|
||||
}];
|
||||
|
||||
return disarrangedIndexes;
|
||||
}
|
||||
|
@ -323,12 +371,9 @@
|
|||
|
||||
NSMutableIndexSet *rearrangedIndexes = [[NSMutableIndexSet alloc] init];
|
||||
|
||||
NSUInteger index = [indexes firstIndex];
|
||||
while (index != NSNotFound) {
|
||||
[rearrangedIndexes
|
||||
addIndex:[[self arrangedObjects] indexOfObject:[[self content] objectAtIndex:index]]];
|
||||
index = [indexes indexGreaterThanIndex:index];
|
||||
}
|
||||
[indexes enumerateIndexesUsingBlock:^(NSUInteger idx, BOOL * _Nonnull stop) {
|
||||
[rearrangedIndexes addIndex:[[self arrangedObjects] indexOfObject:[[self content] objectAtIndex:idx]]];
|
||||
}];
|
||||
|
||||
return rearrangedIndexes;
|
||||
}
|
||||
|
@ -338,6 +383,12 @@
|
|||
[self rearrangeObjects];
|
||||
}
|
||||
|
||||
- (void)insertObjectsUnsynced:(NSArray *)objects atArrangedObjectIndexes:(NSIndexSet *)indexes {
|
||||
[super insertObjects:objects atArrangedObjectIndexes:indexes];
|
||||
|
||||
if ([self shuffle] != ShuffleOff) [self resetShuffleList];
|
||||
}
|
||||
|
||||
- (void)insertObjects:(NSArray *)objects atArrangedObjectIndexes:(NSIndexSet *)indexes {
|
||||
[[[self undoManager] prepareWithInvocationTarget:self]
|
||||
removeObjectsAtIndexes:[self disarrangeIndexes:indexes]];
|
||||
|
@ -345,6 +396,8 @@
|
|||
[NSString stringWithFormat:@"Adding %lu entries", (unsigned long) [objects count]];
|
||||
[[self undoManager] setActionName:actionName];
|
||||
|
||||
[[SQLiteStore sharedStore] playlistInsertTracks:objects atObjectIndexes:indexes];
|
||||
|
||||
[super insertObjects:objects atArrangedObjectIndexes:indexes];
|
||||
|
||||
if ([self shuffle] != ShuffleOff) [self resetShuffleList];
|
||||
|
@ -390,6 +443,8 @@
|
|||
currentEntry.index = -i - 1;
|
||||
}
|
||||
|
||||
[[SQLiteStore sharedStore] playlistRemoveTracksAtIndexes:unarrangedIndexes];
|
||||
|
||||
[super removeObjectsAtArrangedObjectIndexes:indexes];
|
||||
|
||||
if ([self shuffle] != ShuffleOff) [self resetShuffleList];
|
||||
|
@ -563,6 +618,7 @@
|
|||
if ([queueList count] > 0) {
|
||||
pe = queueList[0];
|
||||
[queueList removeObjectAtIndex:0];
|
||||
[[SQLiteStore sharedStore] queueRemoveItem:0];
|
||||
pe.queued = NO;
|
||||
[pe setQueuePosition:-1];
|
||||
|
||||
|
@ -863,6 +919,11 @@
|
|||
}
|
||||
|
||||
- (IBAction)emptyQueueList:(id)sender {
|
||||
[self emptyQueueListUnsynced];
|
||||
[[SQLiteStore sharedStore] queueEmpty];
|
||||
}
|
||||
|
||||
- (void)emptyQueueListUnsynced {
|
||||
for (PlaylistEntry *queueItem in queueList) {
|
||||
queueItem.queued = NO;
|
||||
[queueItem setQueuePosition:-1];
|
||||
|
@ -872,17 +933,23 @@
|
|||
}
|
||||
|
||||
- (IBAction)toggleQueued:(id)sender {
|
||||
SQLiteStore *store = [SQLiteStore sharedStore];
|
||||
|
||||
for (PlaylistEntry *queueItem in [self selectedObjects]) {
|
||||
if (queueItem.queued) {
|
||||
queueItem.queued = NO;
|
||||
queueItem.queuePosition = -1;
|
||||
|
||||
[queueList removeObject:queueItem];
|
||||
|
||||
[store queueRemovePlaylistItems:[NSArray arrayWithObject:queueItem]];
|
||||
} else {
|
||||
queueItem.queued = YES;
|
||||
queueItem.queuePosition = (int) [queueList count];
|
||||
|
||||
[queueList addObject:queueItem];
|
||||
|
||||
[store queueAddItem:[queueItem index]];
|
||||
}
|
||||
|
||||
DLog(@"TOGGLE QUEUED: %i", queueItem.queued);
|
||||
|
@ -895,11 +962,14 @@
|
|||
}
|
||||
|
||||
- (IBAction)removeFromQueue:(id)sender {
|
||||
SQLiteStore *store = [SQLiteStore sharedStore];
|
||||
|
||||
for (PlaylistEntry *queueItem in [self selectedObjects]) {
|
||||
queueItem.queued = NO;
|
||||
queueItem.queuePosition = -1;
|
||||
|
||||
[queueList removeObject:queueItem];
|
||||
[store queueRemovePlaylistItems:[NSArray arrayWithObject:queueItem]];
|
||||
}
|
||||
|
||||
int i = 0;
|
||||
|
@ -909,11 +979,14 @@
|
|||
}
|
||||
|
||||
- (IBAction)addToQueue:(id)sender {
|
||||
SQLiteStore *store = [SQLiteStore sharedStore];
|
||||
|
||||
for (PlaylistEntry *queueItem in [self selectedObjects]) {
|
||||
queueItem.queued = YES;
|
||||
queueItem.queuePosition = (int) [queueList count];
|
||||
|
||||
[queueList addObject:queueItem];
|
||||
[store queueAddItem:[queueItem index]];
|
||||
}
|
||||
|
||||
int i = 0;
|
||||
|
|
|
@ -11,6 +11,7 @@
|
|||
@interface PlaylistEntry : NSObject {
|
||||
NSInteger index;
|
||||
NSInteger shuffleIndex;
|
||||
NSInteger dbIndex;
|
||||
|
||||
BOOL current;
|
||||
BOOL removed;
|
||||
|
@ -82,6 +83,7 @@
|
|||
|
||||
@property NSInteger index;
|
||||
@property NSInteger shuffleIndex;
|
||||
@property NSInteger dbIndex;
|
||||
|
||||
@property(readonly) NSString *status;
|
||||
@property(readonly) NSString *statusMessage;
|
||||
|
|
|
@ -13,6 +13,7 @@
|
|||
|
||||
@synthesize index;
|
||||
@synthesize shuffleIndex;
|
||||
@synthesize dbIndex;
|
||||
|
||||
@synthesize current;
|
||||
@synthesize removed;
|
||||
|
|
|
@ -37,6 +37,8 @@ typedef enum {
|
|||
- (NSArray*)addURL:(NSURL *)url;
|
||||
- (NSArray*)insertURLs:(NSArray *)urls atIndex:(NSInteger)index sort:(BOOL)sort;
|
||||
|
||||
- (NSArray*)addDatabase;
|
||||
|
||||
// Save playlist, auto-determines type based on extension. Uses m3u if it cannot be determined.
|
||||
- (BOOL)save:(NSString *)filename;
|
||||
- (BOOL)save:(NSString *)filename asType:(PlaylistType)type;
|
||||
|
|
|
@ -28,6 +28,8 @@
|
|||
|
||||
#import "NSString+FinderCompare.h"
|
||||
|
||||
#import "SQLiteStore.h"
|
||||
|
||||
#import "Logging.h"
|
||||
|
||||
@implementation PlaylistLoader
|
||||
|
@ -489,6 +491,8 @@ static inline void dispatch_sync_reentrant(dispatch_queue_t queue, dispatch_bloc
|
|||
long i, j;
|
||||
NSMutableIndexSet *load_info_indexes = [[NSMutableIndexSet alloc] init];
|
||||
|
||||
SQLiteStore *store = [SQLiteStore sharedStore];
|
||||
|
||||
i = 0;
|
||||
j = 0;
|
||||
for (PlaylistEntry *pe in entries)
|
||||
|
@ -549,6 +553,7 @@ static inline void dispatch_sync_reentrant(dispatch_queue_t queue, dispatch_bloc
|
|||
__block NSDictionary *entryInfo = [outArray objectAtIndex:i + 1];
|
||||
dispatch_sync_reentrant(dispatch_get_main_queue(), ^{
|
||||
[weakPe setMetadata:entryInfo];
|
||||
[store trackUpdate:weakPe];
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -559,7 +564,7 @@ static inline void dispatch_sync_reentrant(dispatch_queue_t queue, dispatch_bloc
|
|||
__block NSIndexSet *weakIndexSet = update_indexes;
|
||||
dispatch_sync_reentrant(dispatch_get_main_queue(), ^{
|
||||
unsigned long columns = [[[weakPlaylistView documentView] tableColumns] count];
|
||||
[weakPlaylistView.documentView reloadDataForRowIndexes:weakIndexSet columnIndexes:[NSIndexSet indexSetWithIndexesInRange:NSMakeRange(0,columns-1)]];
|
||||
[weakPlaylistView.documentView reloadDataForRowIndexes:weakIndexSet columnIndexes:[NSIndexSet indexSetWithIndexesInRange:NSMakeRange(0,columns)]];
|
||||
});
|
||||
}
|
||||
}
|
||||
|
@ -570,6 +575,8 @@ static inline void dispatch_sync_reentrant(dispatch_queue_t queue, dispatch_bloc
|
|||
long i, j;
|
||||
NSMutableIndexSet *load_info_indexes = [[NSMutableIndexSet alloc] init];
|
||||
|
||||
SQLiteStore *store = [SQLiteStore sharedStore];
|
||||
|
||||
i = 0;
|
||||
j = 0;
|
||||
for (PlaylistEntry *pe in entries)
|
||||
|
@ -604,6 +611,7 @@ static inline void dispatch_sync_reentrant(dispatch_queue_t queue, dispatch_bloc
|
|||
[entryInfo addEntriesFromDictionary:[AudioMetadataReader metadataForURL:pe.URL]];
|
||||
|
||||
[pe setMetadata:entryInfo];
|
||||
[store trackUpdate:pe];
|
||||
}];
|
||||
|
||||
[self->playlistController updateTotalTime];
|
||||
|
@ -629,6 +637,58 @@ static inline void dispatch_sync_reentrant(dispatch_queue_t queue, dispatch_bloc
|
|||
return [self insertURLs:[NSArray arrayWithObject:url] atIndex:(int)[[playlistController content] count] sort:NO];
|
||||
}
|
||||
|
||||
- (NSArray*)addDatabase
|
||||
{
|
||||
SQLiteStore *store = [SQLiteStore sharedStore];
|
||||
|
||||
int64_t count = [store playlistGetCount];
|
||||
|
||||
NSInteger i = 0;
|
||||
NSMutableArray *entries = [NSMutableArray arrayWithCapacity:count];
|
||||
|
||||
for (i = 0; i < count; ++i)
|
||||
{
|
||||
PlaylistEntry *pe = [store playlistGetItem:i];
|
||||
|
||||
pe.queuePosition = -1;
|
||||
|
||||
[entries addObject:pe];
|
||||
}
|
||||
|
||||
NSIndexSet *is = [NSIndexSet indexSetWithIndexesInRange:NSMakeRange(0, [entries count])];
|
||||
|
||||
[playlistController insertObjectsUnsynced:entries atArrangedObjectIndexes:is];
|
||||
|
||||
count = [store queueGetCount];
|
||||
|
||||
if (count)
|
||||
{
|
||||
[playlistController emptyQueueListUnsynced];
|
||||
|
||||
for (i = 0; i < count; ++i)
|
||||
{
|
||||
NSInteger indexVal = [store queueGetEntry:i];
|
||||
PlaylistEntry *pe = [entries objectAtIndex:indexVal];
|
||||
pe.queuePosition = i;
|
||||
pe.queued = YES;
|
||||
|
||||
[[playlistController queueList] addObject:pe];
|
||||
}
|
||||
}
|
||||
|
||||
//Clear the selection
|
||||
[playlistController setSelectionIndexes:[NSIndexSet indexSet]];
|
||||
|
||||
NSArray* arrayFirst = [NSArray arrayWithObject:[entries objectAtIndex:0]];
|
||||
NSMutableArray* arrayRest = [entries mutableCopy];
|
||||
[arrayRest removeObjectAtIndex:0];
|
||||
|
||||
[self performSelectorOnMainThread:@selector(syncLoadInfoForEntries:) withObject:arrayFirst waitUntilDone:YES];
|
||||
if ([arrayRest count])
|
||||
[self performSelectorInBackground:@selector(loadInfoForEntries:) withObject:arrayRest];
|
||||
return entries;
|
||||
}
|
||||
|
||||
- (NSArray *)acceptableFileTypes
|
||||
{
|
||||
return [[self acceptableContainerTypes] arrayByAddingObjectsFromArray:[AudioPlayer fileTypes]];
|
||||
|
|
|
@ -0,0 +1,55 @@
|
|||
//
|
||||
// NSTableViewDataSource+sqlite.h
|
||||
// Cog
|
||||
//
|
||||
// Created by Christopher Snowhill on 12/22/21.
|
||||
//
|
||||
|
||||
#ifndef NSTableViewDataSource_sqlite_h
|
||||
#import <Cocoa/Cocoa.h>
|
||||
#import <sqlite3.h>
|
||||
#import "PlaylistEntry.h"
|
||||
|
||||
@interface SQLiteStore : NSObject
|
||||
{
|
||||
@private NSString *g_databasePath;
|
||||
@private sqlite3 *g_database;
|
||||
@private sqlite3_stmt *stmt[38];
|
||||
}
|
||||
|
||||
@property (nonatomic, readwrite) NSString *databasePath;
|
||||
@property (nonatomic, assign, readwrite) sqlite3 *database;
|
||||
|
||||
+ (SQLiteStore *)sharedStore;
|
||||
|
||||
- (id)init;
|
||||
- (void)dealloc;
|
||||
|
||||
- (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;
|
||||
- (PlaylistEntry *)playlistGetItem:(int64_t)index;
|
||||
- (int64_t)playlistGetCount;
|
||||
#if 0
|
||||
- (void)playlistMoveObjectsInArrangedObjectsFromIndexes:(NSIndexSet *)indexSet toIndex:(NSUInteger)insertIndex;
|
||||
#endif
|
||||
|
||||
- (void)syncPlaylistEntries:(NSArray *)entries;
|
||||
|
||||
- (void)queueAddItem:(int64_t)playlistIndex;
|
||||
- (void)queueAddItems:(NSArray *)playlistIndexes;
|
||||
- (void)queueRemoveItem:(int64_t)queueIndex;
|
||||
- (void)queueRemovePlaylistItems:(NSArray *)playlistIndexes;
|
||||
- (int64_t)queueGetEntry:(int64_t)queueIndex;
|
||||
- (int64_t)queueGetCount;
|
||||
- (void)queueEmpty;
|
||||
|
||||
@end
|
||||
|
||||
#define NSTableViewDataSource_sqlite_h
|
||||
|
||||
|
||||
#endif /* NSTableViewDataSource_sqlite_h */
|
File diff suppressed because it is too large
Load Diff
Loading…
Reference in New Issue