- 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
Christopher Snowhill 2021-12-24 01:01:21 -08:00
parent fd75e1b260
commit 2445cc94a9
12 changed files with 2206 additions and 50 deletions

View File

@ -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,10 +354,9 @@ 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)
{
[fileManager createDirectoryAtPath: folder withIntermediateDirectories:NO attributes:nil error:nil];
@ -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]);

View File

@ -109,8 +109,11 @@ static PluginController *sharedPluginController = nil;
- (void)loadPlugins
{
[self loadPluginsAtPath:[[NSBundle mainBundle] builtInPlugInsPath]];
[self loadPluginsAtPath:[@"~/Library/Application Support/Cog/Plugins" stringByExpandingTildeInPath]];
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSApplicationSupportDirectory, NSUserDomainMask, YES);
NSString *basePath = [[paths firstObject] stringByAppendingPathComponent:@"Cog"];
[self loadPluginsAtPath:[[NSBundle mainBundle] builtInPlugInsPath]];
[self loadPluginsAtPath:[basePath stringByAppendingPathComponent:@"Plugins"]];
}
- (void)setupContainer:(NSString *)className

View File

@ -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>

View File

@ -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 */,

View File

@ -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

View File

@ -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 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 {
[self setTotalTime:[NSString stringWithFormat:@"%ld hours %ld minutes %ld seconds",
hoursAndMinutes.quot, hoursAndMinutes.rem,
sec % 60]];
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,15 +383,23 @@
[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]];
NSString *actionName =
[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];
}
@ -389,6 +442,8 @@
}
currentEntry.index = -i - 1;
}
[[SQLiteStore sharedStore] playlistRemoveTracksAtIndexes:unarrangedIndexes];
[super removeObjectsAtArrangedObjectIndexes:indexes];
@ -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;

View File

@ -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;

View File

@ -13,6 +13,7 @@
@synthesize index;
@synthesize shuffleIndex;
@synthesize dbIndex;
@synthesize current;
@synthesize removed;

View File

@ -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;

View File

@ -28,6 +28,8 @@
#import "NSString+FinderCompare.h"
#import "SQLiteStore.h"
#import "Logging.h"
@implementation PlaylistLoader
@ -438,7 +440,7 @@ NSMutableDictionary * dictionaryWithPropertiesOfObject(id obj, NSArray * filterL
++i;
}
}
NSIndexSet *is = [NSIndexSet indexSetWithIndexesInRange:NSMakeRange(index, [entries count])];
[playlistController insertObjects:entries atArrangedObjectIndexes:is];
@ -488,6 +490,8 @@ static inline void dispatch_sync_reentrant(dispatch_queue_t queue, dispatch_bloc
NSMutableIndexSet *update_indexes = [[NSMutableIndexSet alloc] init];
long i, j;
NSMutableIndexSet *load_info_indexes = [[NSMutableIndexSet alloc] init];
SQLiteStore *store = [SQLiteStore sharedStore];
i = 0;
j = 0;
@ -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)
@ -591,20 +598,21 @@ static inline void dispatch_sync_reentrant(dispatch_queue_t queue, dispatch_bloc
}
[load_info_indexes enumerateIndexesUsingBlock:^(NSUInteger idx, BOOL * _Nonnull stop)
{
PlaylistEntry *pe = [entries objectAtIndex:idx];
NSMutableDictionary *entryInfo = [NSMutableDictionary dictionaryWithCapacity:20];
NSDictionary *entryProperties = [AudioPropertiesReader propertiesForURL:pe.URL];
if (entryProperties == nil)
return;
[entryInfo addEntriesFromDictionary:entryProperties];
[entryInfo addEntriesFromDictionary:[AudioMetadataReader metadataForURL:pe.URL]];
[pe setMetadata:entryInfo];
}];
{
PlaylistEntry *pe = [entries objectAtIndex:idx];
NSMutableDictionary *entryInfo = [NSMutableDictionary dictionaryWithCapacity:20];
NSDictionary *entryProperties = [AudioPropertiesReader propertiesForURL:pe.URL];
if (entryProperties == nil)
return;
[entryInfo addEntriesFromDictionary:entryProperties];
[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]];

55
Utils/SQLiteStore.h Normal file
View File

@ -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 */

1940
Utils/SQLiteStore.m Normal file

File diff suppressed because it is too large Load Diff