2007-03-07 01:26:50 +00:00
|
|
|
//
|
|
|
|
// PlaylistLoader.m
|
|
|
|
// Cog
|
|
|
|
//
|
|
|
|
// Created by Vincent Spader on 3/05/07.
|
|
|
|
// Copyright 2007 Vincent Spader All rights reserved.
|
|
|
|
//
|
|
|
|
|
|
|
|
#import "PlaylistLoader.h"
|
|
|
|
#import "PlaylistController.h"
|
|
|
|
#import "PlaylistEntry.h"
|
2008-03-01 18:29:14 +00:00
|
|
|
#import "AppController.h"
|
2007-03-07 01:26:50 +00:00
|
|
|
|
2007-03-14 02:28:30 +00:00
|
|
|
#import "NSFileHandle+CreateFile.h"
|
|
|
|
|
2007-03-09 01:16:06 +00:00
|
|
|
#import "CogAudio/AudioPlayer.h"
|
2007-10-09 01:20:46 +00:00
|
|
|
#import "CogAudio/AudioContainer.h"
|
2008-03-01 18:29:14 +00:00
|
|
|
#import "CogAudio/AudioPropertiesReader.h"
|
|
|
|
#import "CogAudio/AudioMetadataReader.h"
|
2007-03-09 01:16:06 +00:00
|
|
|
|
2007-03-07 01:26:50 +00:00
|
|
|
@implementation PlaylistLoader
|
|
|
|
|
|
|
|
- (BOOL)save:(NSString *)filename
|
|
|
|
{
|
|
|
|
NSString *ext = [filename pathExtension];
|
|
|
|
if ([ext isEqualToString:@"pls"])
|
|
|
|
{
|
|
|
|
return [self save:filename asType:kPlaylistPls];
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
return [self save:filename asType:kPlaylistM3u];
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
- (BOOL)save:(NSString *)filename asType:(PlaylistType)type
|
|
|
|
{
|
|
|
|
if (type == kPlaylistM3u)
|
|
|
|
{
|
|
|
|
return [self saveM3u:filename];
|
|
|
|
}
|
|
|
|
else if (type == kPlaylistPls)
|
|
|
|
{
|
|
|
|
return [self savePls:filename];
|
|
|
|
}
|
|
|
|
|
|
|
|
return NO;
|
|
|
|
}
|
|
|
|
|
2007-03-09 01:16:06 +00:00
|
|
|
- (NSString *)relativePathFrom:(NSString *)filename toURL:(NSURL *)entryURL
|
2007-03-07 01:26:50 +00:00
|
|
|
{
|
|
|
|
NSString *basePath = [[[filename stringByStandardizingPath] stringByDeletingLastPathComponent] stringByAppendingString:@"/"];
|
|
|
|
|
2007-10-15 22:19:14 +00:00
|
|
|
if ([entryURL isFileURL]) {
|
2007-03-07 01:26:50 +00:00
|
|
|
//We want relative paths.
|
|
|
|
NSMutableString *entryPath = [[[[entryURL path] stringByStandardizingPath] mutableCopy] autorelease];
|
|
|
|
|
2007-03-09 01:16:06 +00:00
|
|
|
[entryPath replaceOccurrencesOfString:basePath withString:@"" options:(NSAnchoredSearch | NSLiteralSearch | NSCaseInsensitiveSearch) range:NSMakeRange(0, [entryPath length])];
|
2007-10-15 22:19:14 +00:00
|
|
|
if ([entryURL fragment])
|
|
|
|
{
|
|
|
|
[entryPath appendString:@"#"];
|
|
|
|
[entryPath appendString:[entryURL fragment]];
|
|
|
|
}
|
2007-03-07 01:26:50 +00:00
|
|
|
|
|
|
|
return entryPath;
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
//Write [entryURL absoluteString] to file
|
|
|
|
return [entryURL absoluteString];
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
- (BOOL)saveM3u:(NSString *)filename
|
|
|
|
{
|
2007-03-14 02:28:30 +00:00
|
|
|
NSFileHandle *fileHandle = [NSFileHandle fileHandleForWritingAtPath:filename createFile:YES];
|
2007-03-07 01:26:50 +00:00
|
|
|
if (!fileHandle) {
|
2007-07-11 01:20:32 +00:00
|
|
|
NSLog(@"Error saving m3u!");
|
2008-02-13 01:50:39 +00:00
|
|
|
return NO;
|
2007-03-07 01:26:50 +00:00
|
|
|
}
|
2007-03-09 01:16:06 +00:00
|
|
|
[fileHandle truncateFileAtOffset:0];
|
|
|
|
|
2008-02-20 00:54:45 +00:00
|
|
|
for (PlaylistEntry *pe in [playlistController content])
|
2007-03-07 01:26:50 +00:00
|
|
|
{
|
2008-02-20 00:44:40 +00:00
|
|
|
NSString *path = [self relativePathFrom:filename toURL:[pe URL]];
|
2007-03-07 01:26:50 +00:00
|
|
|
[fileHandle writeData:[[path stringByAppendingString:@"\n"] dataUsingEncoding:NSUTF8StringEncoding]];
|
|
|
|
}
|
|
|
|
|
2007-03-09 01:16:06 +00:00
|
|
|
[fileHandle closeFile];
|
|
|
|
|
2007-03-07 01:26:50 +00:00
|
|
|
return YES;
|
|
|
|
}
|
|
|
|
|
|
|
|
- (BOOL)savePls:(NSString *)filename
|
|
|
|
{
|
2007-03-14 02:28:30 +00:00
|
|
|
NSFileHandle *fileHandle = [NSFileHandle fileHandleForWritingAtPath:filename createFile:YES];
|
2007-03-07 01:26:50 +00:00
|
|
|
if (!fileHandle) {
|
|
|
|
return NO;
|
|
|
|
}
|
2007-03-09 01:16:06 +00:00
|
|
|
[fileHandle truncateFileAtOffset:0];
|
|
|
|
|
|
|
|
[fileHandle writeData:[[NSString stringWithFormat:@"[playlist]\nnumberOfEntries=%i\n\n",[[playlistController content] count]] dataUsingEncoding:NSUTF8StringEncoding]];
|
2007-03-07 01:26:50 +00:00
|
|
|
|
2007-03-07 01:45:45 +00:00
|
|
|
int i = 1;
|
2008-02-20 00:54:45 +00:00
|
|
|
for (PlaylistEntry *pe in [playlistController content])
|
2007-03-07 01:26:50 +00:00
|
|
|
{
|
2008-02-20 00:44:40 +00:00
|
|
|
NSString *path = [self relativePathFrom:filename toURL:[pe URL]];
|
2007-03-07 01:26:50 +00:00
|
|
|
NSString *entry = [NSString stringWithFormat:@"File%i=%@\n",i,path];
|
|
|
|
|
|
|
|
[fileHandle writeData:[entry dataUsingEncoding:NSUTF8StringEncoding]];
|
2007-03-07 01:45:45 +00:00
|
|
|
i++;
|
2007-03-07 01:26:50 +00:00
|
|
|
}
|
|
|
|
|
2007-03-09 01:16:06 +00:00
|
|
|
[fileHandle writeData:[@"\nVERSION=2" dataUsingEncoding:NSUTF8StringEncoding]];
|
|
|
|
[fileHandle closeFile];
|
|
|
|
|
2007-03-07 01:26:50 +00:00
|
|
|
return YES;
|
|
|
|
}
|
|
|
|
|
2007-03-09 01:16:06 +00:00
|
|
|
- (NSArray *)fileURLsAtPath:(NSString *)path
|
|
|
|
{
|
|
|
|
NSFileManager *manager = [NSFileManager defaultManager];
|
|
|
|
|
|
|
|
NSMutableArray *urls = [NSMutableArray array];
|
|
|
|
|
|
|
|
NSArray *subpaths = [manager subpathsAtPath:path];
|
|
|
|
|
2008-03-01 15:04:46 +00:00
|
|
|
for (NSString *subpath in subpaths)
|
2007-03-09 01:16:06 +00:00
|
|
|
{
|
|
|
|
NSString *absoluteSubpath = [NSString pathWithComponents:[NSArray arrayWithObjects:path,subpath,nil]];
|
|
|
|
|
|
|
|
BOOL isDir;
|
|
|
|
if ( [manager fileExistsAtPath:absoluteSubpath isDirectory:&isDir] && isDir == NO)
|
|
|
|
{
|
|
|
|
[urls addObject:[NSURL fileURLWithPath:absoluteSubpath]];
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return urls;
|
|
|
|
}
|
|
|
|
|
|
|
|
- (void)insertURLs:(NSArray *)urls atIndex:(int)index sort:(BOOL)sort
|
|
|
|
{
|
2007-05-28 14:58:08 +00:00
|
|
|
NSMutableSet *uniqueURLs = [NSMutableSet set];
|
|
|
|
|
|
|
|
NSMutableArray *expandedURLs = [NSMutableArray array];
|
2007-10-19 02:23:10 +00:00
|
|
|
NSMutableArray *containedURLs = [NSMutableArray array];
|
|
|
|
NSMutableArray *fileURLs = [NSMutableArray array];
|
2007-05-28 14:58:08 +00:00
|
|
|
NSMutableArray *validURLs = [NSMutableArray array];
|
2007-03-09 01:16:06 +00:00
|
|
|
|
|
|
|
if (!urls)
|
|
|
|
return;
|
|
|
|
|
|
|
|
if (index < 0)
|
|
|
|
index = 0;
|
|
|
|
|
|
|
|
NSURL *url;
|
2008-02-20 00:54:45 +00:00
|
|
|
for (url in urls)
|
2007-03-09 01:16:06 +00:00
|
|
|
{
|
|
|
|
if ([url isFileURL]) {
|
|
|
|
BOOL isDir;
|
2007-10-09 01:20:46 +00:00
|
|
|
if ([[NSFileManager defaultManager] fileExistsAtPath:[url path] isDirectory:&isDir])
|
|
|
|
{
|
2007-03-09 01:16:06 +00:00
|
|
|
if (isDir == YES)
|
|
|
|
{
|
|
|
|
//Get subpaths
|
2007-05-28 14:58:08 +00:00
|
|
|
[expandedURLs addObjectsFromArray:[self fileURLsAtPath:[url path]]];
|
2007-03-09 01:16:06 +00:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2007-05-28 14:58:08 +00:00
|
|
|
[expandedURLs addObject:url];
|
2007-03-09 01:16:06 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
//Non-file URL..
|
2007-05-28 14:58:08 +00:00
|
|
|
[expandedURLs addObject:url];
|
2007-03-09 01:16:06 +00:00
|
|
|
}
|
|
|
|
}
|
2007-10-19 02:23:10 +00:00
|
|
|
|
|
|
|
NSLog(@"Expanded urls: %@", expandedURLs);
|
2007-03-09 01:16:06 +00:00
|
|
|
|
2007-05-28 14:58:08 +00:00
|
|
|
NSArray *sortedURLs;
|
|
|
|
if (sort == YES)
|
|
|
|
{
|
|
|
|
NSSortDescriptor *sortDescriptor = [[NSSortDescriptor alloc] initWithKey:@"absoluteString" ascending:YES];
|
|
|
|
|
|
|
|
sortedURLs = [expandedURLs sortedArrayUsingDescriptors:[NSArray arrayWithObject:sortDescriptor]];
|
|
|
|
|
|
|
|
[sortDescriptor release];
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
sortedURLs = [expandedURLs copy];
|
|
|
|
}
|
|
|
|
|
2008-02-20 00:54:45 +00:00
|
|
|
for (url in sortedURLs)
|
2007-05-28 14:58:08 +00:00
|
|
|
{
|
2007-10-20 03:22:13 +00:00
|
|
|
//Container vs non-container url
|
|
|
|
if ([[self acceptableContainerTypes] containsObject:[[[url path] pathExtension] lowercaseString]]) {
|
|
|
|
[containedURLs addObjectsFromArray:[AudioContainer urlsForContainerURL:url]];
|
2007-10-14 20:36:10 +00:00
|
|
|
|
2007-10-20 03:22:13 +00:00
|
|
|
//Make sure the container isn't added twice.
|
|
|
|
[uniqueURLs addObjectsFromArray:containedURLs];
|
2007-05-28 14:58:08 +00:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2007-10-19 02:23:10 +00:00
|
|
|
[fileURLs addObject:url];
|
2007-05-28 14:58:08 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2007-10-19 02:23:10 +00:00
|
|
|
NSLog(@"File urls: %@", fileURLs);
|
|
|
|
|
|
|
|
NSLog(@"Contained urls: %@", containedURLs);
|
|
|
|
|
2008-02-20 00:54:45 +00:00
|
|
|
for (url in fileURLs)
|
2007-03-09 01:16:06 +00:00
|
|
|
{
|
|
|
|
if (![[AudioPlayer schemes] containsObject:[url scheme]])
|
|
|
|
continue;
|
|
|
|
|
|
|
|
//Need a better way to determine acceptable file types than basing it on extensions.
|
2007-10-14 18:12:15 +00:00
|
|
|
if ([url isFileURL] && ![[AudioPlayer fileTypes] containsObject:[[[url path] pathExtension] lowercaseString]])
|
2007-03-09 01:16:06 +00:00
|
|
|
continue;
|
2007-05-27 16:14:29 +00:00
|
|
|
|
|
|
|
if (![uniqueURLs containsObject:url])
|
|
|
|
{
|
|
|
|
[validURLs addObject:url];
|
2007-03-09 01:16:06 +00:00
|
|
|
|
2007-05-27 16:14:29 +00:00
|
|
|
[uniqueURLs addObject:url];
|
|
|
|
}
|
2007-03-09 01:16:06 +00:00
|
|
|
}
|
2007-10-19 02:23:10 +00:00
|
|
|
|
|
|
|
NSLog(@"Valid urls: %@", validURLs);
|
|
|
|
|
2008-02-20 00:54:45 +00:00
|
|
|
for (url in containedURLs)
|
2007-10-19 02:23:10 +00:00
|
|
|
{
|
|
|
|
if (![[AudioPlayer schemes] containsObject:[url scheme]])
|
|
|
|
continue;
|
|
|
|
|
|
|
|
//Need a better way to determine acceptable file types than basing it on extensions.
|
|
|
|
if (![[AudioPlayer fileTypes] containsObject:[[[url path] pathExtension] lowercaseString]])
|
|
|
|
continue;
|
|
|
|
|
|
|
|
[validURLs addObject:url];
|
|
|
|
}
|
|
|
|
|
2007-03-09 01:16:06 +00:00
|
|
|
//Create actual entries
|
|
|
|
int i;
|
|
|
|
NSMutableArray *entries = [NSMutableArray array];
|
2007-05-28 14:58:08 +00:00
|
|
|
for (i = 0; i < [validURLs count]; i++)
|
2007-03-09 01:16:06 +00:00
|
|
|
{
|
|
|
|
PlaylistEntry *pe = [[PlaylistEntry alloc] init];
|
2007-05-28 14:58:08 +00:00
|
|
|
NSURL *url = [validURLs objectAtIndex:i];
|
2007-03-09 01:16:06 +00:00
|
|
|
|
|
|
|
[pe setURL:url];
|
2008-02-23 19:46:23 +00:00
|
|
|
pe.index = index+i;
|
2007-03-09 01:16:06 +00:00
|
|
|
[pe setTitle:[[url path] lastPathComponent]];
|
2008-02-22 15:26:46 +00:00
|
|
|
[pe setQueuePosition:-1];
|
2007-03-09 01:16:06 +00:00
|
|
|
[entries addObject:pe];
|
|
|
|
|
|
|
|
[pe release];
|
|
|
|
}
|
|
|
|
|
|
|
|
NSIndexSet *is = [NSIndexSet indexSetWithIndexesInRange:NSMakeRange(index, [entries count])];
|
2008-02-10 16:16:45 +00:00
|
|
|
|
2007-03-09 01:16:06 +00:00
|
|
|
[playlistController insertObjects:entries atArrangedObjectIndexes:is];
|
|
|
|
|
|
|
|
//Select the first entry in the group that was just added
|
|
|
|
[playlistController setSelectionIndex:index];
|
|
|
|
|
2008-03-01 18:29:14 +00:00
|
|
|
NSOperationQueue *queue;
|
|
|
|
queue = [[[NSApplication sharedApplication] delegate] sharedOperationQueue];
|
|
|
|
|
|
|
|
NSInvocationOperation *oldReadEntryInfoOperation = Nil;
|
|
|
|
for (PlaylistEntry *pe in entries)
|
|
|
|
{
|
|
|
|
NSInvocationOperation *readEntryInfoOperation;
|
|
|
|
readEntryInfoOperation = [[[NSInvocationOperation alloc]
|
|
|
|
initWithTarget:self
|
|
|
|
selector:@selector(readEntryInfo:)
|
|
|
|
object:pe] autorelease];
|
|
|
|
if (oldReadEntryInfoOperation)
|
|
|
|
{
|
|
|
|
[readEntryInfoOperation addDependency:oldReadEntryInfoOperation];
|
|
|
|
[oldReadEntryInfoOperation release];
|
|
|
|
}
|
|
|
|
[readEntryInfoOperation addObserver:self
|
|
|
|
forKeyPath:@"isFinished"
|
|
|
|
options:NSKeyValueObservingOptionNew
|
|
|
|
context:NULL];
|
|
|
|
[queue addOperation:readEntryInfoOperation];
|
|
|
|
oldReadEntryInfoOperation = [readEntryInfoOperation retain];
|
|
|
|
}
|
2007-03-09 01:16:06 +00:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2008-03-01 18:29:14 +00:00
|
|
|
- (NSDictionary *)readEntryInfo:(PlaylistEntry *)pe
|
2007-03-09 01:16:06 +00:00
|
|
|
{
|
2008-03-01 18:29:14 +00:00
|
|
|
// Just setting this to 30 for now...
|
|
|
|
NSMutableDictionary *entryInfo = [NSMutableDictionary dictionaryWithCapacity:30];
|
|
|
|
NSDictionary *entryProperties;
|
|
|
|
entryProperties = [AudioPropertiesReader propertiesForURL:pe.URL];
|
|
|
|
if (entryProperties == nil)
|
|
|
|
return nil;
|
|
|
|
|
|
|
|
[entryInfo addEntriesFromDictionary:entryProperties];
|
|
|
|
[entryInfo addEntriesFromDictionary:[AudioMetadataReader metadataForURL:pe.URL]];
|
|
|
|
return entryInfo;
|
|
|
|
}
|
2007-03-09 01:16:06 +00:00
|
|
|
|
2008-03-01 18:29:14 +00:00
|
|
|
- (void)observeValueForKeyPath:(NSString *)keyPath
|
|
|
|
ofObject:(id)object
|
|
|
|
change:(NSDictionary *)change
|
|
|
|
context:(void *)context
|
|
|
|
{
|
|
|
|
// We finished reading the info for a playlist entry
|
|
|
|
if ([keyPath isEqualToString:@"isFinished"] && [object isFinished])
|
|
|
|
{
|
|
|
|
// get the results
|
|
|
|
NSDictionary *entryInfo = [object result];
|
|
|
|
// get the playlist entry that the thread was inspecting
|
|
|
|
PlaylistEntry *pe;
|
|
|
|
[[object invocation]getArgument:&pe atIndex:2];
|
|
|
|
|
|
|
|
if (entryInfo == nil)
|
|
|
|
{
|
|
|
|
pe.error = YES;
|
|
|
|
pe.errorMessage = @"Unable to retrieve properties.";
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
[pe setValuesForKeysWithDictionary:entryInfo];
|
|
|
|
[playlistController updateTotalTime];
|
|
|
|
}
|
|
|
|
|
|
|
|
//Hack so the display gets updated
|
|
|
|
if (pe == [playlistController currentEntry])
|
|
|
|
[playlistController setCurrentEntry:[playlistController currentEntry]];
|
|
|
|
// stop observing
|
|
|
|
[object removeObserver:self forKeyPath:keyPath];
|
|
|
|
}
|
2007-03-09 01:16:06 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
- (void)addURLs:(NSArray *)urls sort:(BOOL)sort
|
|
|
|
{
|
|
|
|
[self insertURLs:urls atIndex:[[playlistController content] count] sort:sort];
|
|
|
|
}
|
|
|
|
|
2007-05-27 16:14:29 +00:00
|
|
|
- (void)addURL:(NSURL *)url
|
|
|
|
{
|
|
|
|
[self insertURLs:[NSArray arrayWithObject:url] atIndex:[[playlistController content] count] sort:NO];
|
|
|
|
}
|
|
|
|
|
2007-03-09 01:16:06 +00:00
|
|
|
- (NSArray *)acceptableFileTypes
|
|
|
|
{
|
2007-10-09 01:20:46 +00:00
|
|
|
return [[self acceptableContainerTypes] arrayByAddingObjectsFromArray:[AudioPlayer fileTypes]];
|
2007-03-09 01:16:06 +00:00
|
|
|
}
|
|
|
|
|
2007-10-13 04:53:48 +00:00
|
|
|
- (NSArray *)acceptablePlaylistTypes
|
|
|
|
{
|
|
|
|
return [NSArray arrayWithObjects:@"m3u", @"pls", nil];
|
|
|
|
}
|
|
|
|
|
2007-10-09 01:20:46 +00:00
|
|
|
- (NSArray *)acceptableContainerTypes
|
2007-03-07 01:26:50 +00:00
|
|
|
{
|
2007-10-09 01:20:46 +00:00
|
|
|
return [AudioPlayer containerTypes];
|
2007-03-07 01:26:50 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
@end
|