Now saves and loads the default playlist in an XML plist format, so loaded metadata is cached

CQTexperiment
Chris Moeller 2013-10-09 08:45:16 -07:00
parent b4ffb2684f
commit b5fd1207b8
6 changed files with 245 additions and 7 deletions

View File

@ -248,8 +248,19 @@ increase/decrease as long as the user holds the left/right, plus/minus button */
}
[[playlistController undoManager] disableUndoRegistration];
NSString *filename = @"~/Library/Application Support/Cog/Default.m3u";
[playlistLoader addURL:[NSURL fileURLWithPath:[filename stringByExpandingTildeInPath]]];
NSString *basePath = [@"~/Library/Application Support/Cog/" stringByExpandingTildeInPath];
NSString *oldFilename = @"Default.m3u";
NSString *newFilename = @"Default.xml";
if ([[NSFileManager defaultManager] fileExistsAtPath:[basePath stringByAppendingPathComponent:newFilename]])
{
[playlistLoader addURL:[NSURL fileURLWithPath:[basePath stringByAppendingPathComponent:newFilename]]];
}
else
{
[playlistLoader addURL:[NSURL fileURLWithPath:[basePath stringByAppendingPathComponent:oldFilename]]];
}
[[playlistController undoManager] enableUndoRegistration];
int lastStatus = [[NSUserDefaults standardUserDefaults] integerForKey:@"lastPlaybackStatus"];
@ -292,10 +303,14 @@ increase/decrease as long as the user holds the left/right, plus/minus button */
[fileManager createDirectoryAtPath: folder withIntermediateDirectories:NO attributes:nil error:nil];
}
NSString *fileName = @"Default.m3u";
[playlistLoader saveM3u:[folder stringByAppendingPathComponent: fileName]];
NSString * fileName = @"Default.xml";
[playlistLoader saveXml:[folder stringByAppendingPathComponent: fileName]];
fileName = @"Default.m3u";
NSError *error;
[[NSFileManager defaultManager] removeItemAtPath:[folder stringByAppendingPathComponent:fileName] error:&error];
}
- (BOOL)applicationShouldHandleReopen:(NSApplication *)theApplication hasVisibleWindows:(BOOL)flag

View File

@ -147,6 +147,7 @@
8359009D17FF06570060F3ED /* ArchiveSource.bundle in CopyFiles */ = {isa = PBXBuildFile; fileRef = 8359FF3117FEF35D0060F3ED /* ArchiveSource.bundle */; };
8360EF6D17F92E56005208A4 /* HighlyComplete.bundle in CopyFiles */ = {isa = PBXBuildFile; fileRef = 8360EF0517F92B24005208A4 /* HighlyComplete.bundle */; };
8375B36517FFEF130092A79F /* Opus.bundle in CopyFiles */ = {isa = PBXBuildFile; fileRef = 8375B05717FFEA410092A79F /* Opus.bundle */; };
8399D4E21805A55000B503B1 /* XmlContainer.m in Sources */ = {isa = PBXBuildFile; fileRef = 8399D4E01805A55000B503B1 /* XmlContainer.m */; };
83BCB8DE17FC971300760340 /* FFMPEG.bundle in CopyFiles */ = {isa = PBXBuildFile; fileRef = B09E94350D747F7B0064F138 /* FFMPEG.bundle */; };
8D11072B0486CEB800E47090 /* InfoPlist.strings in Resources */ = {isa = PBXBuildFile; fileRef = 089C165CFE840E0CC02AAC07 /* InfoPlist.strings */; };
8D11072D0486CEB800E47090 /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 29B97316FDCFA39411CA2CEA /* main.m */; settings = {ATTRIBUTES = (); }; };
@ -748,6 +749,8 @@
8359FF2C17FEF35C0060F3ED /* ArchiveSource.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = ArchiveSource.xcodeproj; path = Plugins/ArchiveSource/ArchiveSource.xcodeproj; sourceTree = "<group>"; };
8360EF0017F92B23005208A4 /* HighlyComplete.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = HighlyComplete.xcodeproj; path = Plugins/HighlyComplete/HighlyComplete.xcodeproj; sourceTree = "<group>"; };
8375B05117FFEA400092A79F /* Opus.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = Opus.xcodeproj; path = Plugins/Opus/Opus.xcodeproj; sourceTree = "<group>"; };
8399D4E01805A55000B503B1 /* XmlContainer.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = XmlContainer.m; sourceTree = "<group>"; };
8399D4E11805A55000B503B1 /* XmlContainer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = XmlContainer.h; sourceTree = "<group>"; };
8D1107310486CEB800E47090 /* Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist; path = Info.plist; sourceTree = "<group>"; };
8E07AB760AAC930B00A4B32F /* PreferencesController.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; name = PreferencesController.h; path = Preferences/PreferencesController.h; sourceTree = "<group>"; };
8E07AB770AAC930B00A4B32F /* PreferencesController.m */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.objc; name = PreferencesController.m; path = Preferences/PreferencesController.m; sourceTree = "<group>"; };
@ -1407,6 +1410,8 @@
8E75752A09F31D5A0080F1EE /* Playlist */ = {
isa = PBXGroup;
children = (
8399D4E01805A55000B503B1 /* XmlContainer.m */,
8399D4E11805A55000B503B1 /* XmlContainer.h */,
8E75752D09F31D5A0080F1EE /* PlaylistController.h */,
8E75752E09F31D5A0080F1EE /* PlaylistController.m */,
8E75753109F31D5A0080F1EE /* PlaylistView.h */,
@ -1952,6 +1957,7 @@
17D1B25D0F633A4F00694C57 /* PreferencePluginController.m in Sources */,
171CB3DC0F63670D0047EF0A /* PreferencesWindow.m in Sources */,
1778D3CA0F645BF00037E7A0 /* MissingAlbumArtTransformer.m in Sources */,
8399D4E21805A55000B503B1 /* XmlContainer.m in Sources */,
17FAEBAC0F662985007C8707 /* ToolTipTextField.m in Sources */,
F6F96719102C709000D2C9B4 /* NSString+FinderCompare.m in Sources */,
);

View File

@ -16,6 +16,7 @@
typedef enum {
kPlaylistM3u,
kPlaylistPls,
kPlaylistXml,
} PlaylistType;
@interface PlaylistLoader : NSObject {
@ -39,6 +40,7 @@ typedef enum {
- (BOOL)save:(NSString *)filename asType:(PlaylistType)type;
- (BOOL)saveM3u:(NSString *)filename;
- (BOOL)savePls:(NSString *)filename;
- (BOOL)saveXml:(NSString *)filename;
// Read info for a playlist entry
- (NSDictionary *)readEntryInfo:(PlaylistEntry *)pe;

View File

@ -6,6 +6,8 @@
// Copyright 2007 Vincent Spader All rights reserved.
//
#include <objc/runtime.h>
#import "PlaylistLoader.h"
#import "PlaylistController.h"
#import "PlaylistEntry.h"
@ -19,6 +21,8 @@
#import "CogAudio/AudioPropertiesReader.h"
#import "CogAudio/AudioMetadataReader.h"
#import "XMlContainer.h"
@implementation PlaylistLoader
- (id)init
@ -58,6 +62,10 @@
{
return [self save:filename asType:kPlaylistPls];
}
else if ([ext isEqualToString:@"xml"])
{
return [self save:filename asType:kPlaylistXml];
}
else
{
return [self save:filename asType:kPlaylistM3u];
@ -74,6 +82,10 @@
{
return [self savePls:filename];
}
else if (type == kPlaylistXml)
{
return [self saveXml:filename];
}
return NO;
}
@ -147,6 +159,66 @@
return YES;
}
NSMutableDictionary * dictionaryWithPropertiesOfObject(id obj, NSArray * filterList)
{
NSMutableDictionary *dict = [NSMutableDictionary dictionary];
unsigned count;
objc_property_t *properties = class_copyPropertyList([obj class], &count);
for (int i = 0; i < count; i++) {
NSString *key = [NSString stringWithUTF8String:property_getName(properties[i])];
if ([filterList containsObject:key]) continue;
Class classObject = NSClassFromString([key capitalizedString]);
if (classObject) {
id subObj = dictionaryWithPropertiesOfObject([obj valueForKey:key], filterList);
[dict setObject:subObj forKey:key];
}
else
{
id value = [obj valueForKey:key];
if(value) [dict setObject:value forKey:key];
}
}
free(properties);
return dict;
}
- (BOOL)saveXml:(NSString *)filename
{
NSFileHandle *fileHandle = [NSFileHandle fileHandleForWritingAtPath:filename createFile:YES];
if (!fileHandle) {
return NO;
}
[fileHandle truncateFileAtOffset:0];
NSArray * filterList = [NSArray arrayWithObjects:@"display", @"length", @"path", @"filename", @"status", @"statusMessage", @"spam", @"stopAfter", @"shuffleIndex", @"index", @"current", @"queued", @"currentPosition", @"queuePosition", @"error", @"removed", @"url", nil];
NSMutableArray * topLevel = [[NSMutableArray alloc] init];
for (PlaylistEntry *pe in [playlistController content])
{
NSMutableDictionary * dict = dictionaryWithPropertiesOfObject(pe, filterList);
NSString *path = [self relativePathFrom:filename toURL:[pe URL]];
[dict setObject:path forKey:@"URL"];
[topLevel addObject:dict];
}
NSData * data = [NSPropertyListSerialization dataWithPropertyList:topLevel format:NSPropertyListXMLFormat_v1_0 options:0 error:0];
[fileHandle writeData:data];
[fileHandle closeFile];
return YES;
}
- (NSArray *)fileURLsAtPath:(NSString *)path
{
NSFileManager *manager = [NSFileManager defaultManager];
@ -181,6 +253,7 @@
NSMutableArray *containedURLs = [NSMutableArray array];
NSMutableArray *fileURLs = [NSMutableArray array];
NSMutableArray *validURLs = [NSMutableArray array];
NSArray *xmlURLs = nil;
if (!urls)
return [NSArray array];
@ -235,6 +308,10 @@
//Make sure the container isn't added twice.
[uniqueURLs addObjectsFromArray:containedURLs];
}
else if ([[[[url path] pathExtension] lowercaseString] isEqualToString:@"xml"])
{
xmlURLs = [XmlContainer entriesForContainerURL:url];
}
else
{
[fileURLs addObject:url];
@ -277,8 +354,11 @@
}
//Create actual entries
int count = [validURLs count];
if (xmlURLs) count += [xmlURLs count];
int i;
NSMutableArray *entries = [NSMutableArray arrayWithCapacity:[validURLs count]];
NSMutableArray *entries = [NSMutableArray arrayWithCapacity:count];
for (i = 0; i < [validURLs count]; i++)
{
NSURL *url = [validURLs objectAtIndex:i];
@ -297,6 +377,24 @@
[pe release];
}
for (i = 0; i < [xmlURLs count]; i++)
{
NSDictionary * entry = [xmlURLs objectAtIndex:i];
PlaylistEntry *pe;
if ([[entry objectForKey:@"URL"] isFileURL])
pe = [[FilePlaylistEntry alloc] init];
else
pe = [[PlaylistEntry alloc] init];
[pe setValuesForKeysWithDictionary:entry];
pe.index = index+i+[validURLs count];
pe.queuePosition = -1;
[entries addObject:pe];
[pe release];
}
NSIndexSet *is = [NSIndexSet indexSetWithIndexesInRange:NSMakeRange(index, [entries count])];
@ -312,6 +410,8 @@
{
for (PlaylistEntry *pe in entries)
{
if ([pe metadataLoaded]) continue;
NSAutoreleasePool *pool =[[NSAutoreleasePool alloc] init];
NSInvocationOperation *readEntryInfoOperation;

19
Playlist/XmlContainer.h Normal file
View File

@ -0,0 +1,19 @@
//
// XmlContainer.h
// Xml
//
// Created by Christopher Snowhill on 10/9/13.
// Copyright 2013 __NoWork, Inc__. All rights reserved.
//
#import <Cocoa/Cocoa.h>
@interface XmlContainer : NSObject {
}
+ (NSURL *)urlForPath:(NSString *)path relativeTo:(NSString *)baseFilename;
+ (NSArray *)entriesForContainerURL:(NSURL *)url;
@end

96
Playlist/XmlContainer.m Normal file
View File

@ -0,0 +1,96 @@
//
// XmlContainer.m
// Xml
//
// Created by Christopher Snowhill on 10/9/13.
// Copyright 2013 __NoWork, Inc__. All rights reserved.
//
#import "XmlContainer.h"
#import <PlaylistEntry.h>
@implementation XmlContainer
+ (NSURL *)urlForPath:(NSString *)path relativeTo:(NSString *)baseFilename
{
NSRange protocolRange = [path rangeOfString:@"://"];
if (protocolRange.location != NSNotFound)
{
return [NSURL URLWithString:path];
}
NSMutableString *unixPath = [path mutableCopy];
//Get the fragment
NSString *fragment = @"";
NSScanner *scanner = [NSScanner scannerWithString:unixPath];
NSCharacterSet *characterSet = [NSCharacterSet characterSetWithCharactersInString:@"#1234567890"];
while (![scanner isAtEnd]) {
NSString *possibleFragment;
[scanner scanUpToString:@"#" intoString:nil];
if ([scanner scanCharactersFromSet:characterSet intoString:&possibleFragment] && [scanner isAtEnd])
{
fragment = possibleFragment;
[unixPath deleteCharactersInRange:NSMakeRange([scanner scanLocation] - [possibleFragment length], [possibleFragment length])];
break;
}
}
NSLog(@"Fragment: %@", fragment);
if (![unixPath hasPrefix:@"/"]) {
//Only relative paths would have windows backslashes.
[unixPath replaceOccurrencesOfString:@"\\" withString:@"/" options:0 range:NSMakeRange(0, [unixPath length])];
NSString *basePath = [[[baseFilename stringByStandardizingPath] stringByDeletingLastPathComponent] stringByAppendingString:@"/"];
[unixPath insertString:basePath atIndex:0];
}
//Append the fragment
NSURL *url = [NSURL URLWithString:[[[NSURL fileURLWithPath:unixPath] absoluteString] stringByAppendingString: fragment]];
[unixPath release];
return url;
}
+ (NSArray *)entriesForContainerURL:(NSURL *)url
{
if (![url isFileURL])
return [NSArray array];
NSError *nserr;
NSString *error;
NSString *filename = [url path];
NSString * contents = [NSString stringWithContentsOfFile:filename encoding:NSUTF8StringEncoding error:&nserr];
NSData* plistData = [contents dataUsingEncoding:NSUTF8StringEncoding];
NSPropertyListFormat format;
NSArray* plist = [NSPropertyListSerialization propertyListFromData:plistData mutabilityOption:NSPropertyListImmutable format:&format errorDescription:&error];
if(!plist){
NSLog(@"Error: %@",error);
[error release];
return nil;
}
NSDictionary *entry;
NSEnumerator *e = [plist objectEnumerator];
NSMutableArray *entries = [NSMutableArray array];
while ((entry = [e nextObject]))
{
NSMutableDictionary * preparedEntry = [NSMutableDictionary dictionaryWithDictionary:entry];
[preparedEntry setObject:[self urlForPath:[preparedEntry objectForKey:@"URL"] relativeTo:filename] forKey:@"URL"];
[entries addObject:[NSDictionary dictionaryWithDictionary:preparedEntry]];
}
return entries;
}
@end