Now saves and loads the default playlist in an XML plist format, so loaded metadata is cached
parent
b4ffb2684f
commit
b5fd1207b8
|
@ -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
|
||||
|
|
|
@ -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 */,
|
||||
);
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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
|
|
@ -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
|
Loading…
Reference in New Issue