cog/Playlist/PlaylistLoader.m

423 lines
11 KiB
Matlab
Raw Normal View History

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"
#import "FilePlaylistEntry.h"
#import "AppController.h"
2007-03-07 01:26:50 +00:00
#import "NSFileHandle+CreateFile.h"
#import "CogAudio/AudioPlayer.h"
#import "CogAudio/AudioContainer.h"
#import "CogAudio/AudioPropertiesReader.h"
#import "CogAudio/AudioMetadataReader.h"
2007-03-07 01:26:50 +00:00
@implementation PlaylistLoader
- (id)init
{
self = [super init];
if (self)
{
[self initDefaults];
}
return self;
}
- (void)initDefaults
{
}
2007-03-07 01:26:50 +00:00
- (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;
}
- (NSString *)relativePathFrom:(NSString *)filename toURL:(NSURL *)entryURL
2007-03-07 01:26:50 +00:00
{
NSString *basePath = [[[filename stringByStandardizingPath] stringByDeletingLastPathComponent] stringByAppendingString:@"/"];
if ([entryURL isFileURL]) {
2007-03-07 01:26:50 +00:00
//We want relative paths.
NSMutableString *entryPath = [[[[entryURL path] stringByStandardizingPath] mutableCopy] autorelease];
[entryPath replaceOccurrencesOfString:basePath withString:@"" options:(NSAnchoredSearch | NSLiteralSearch | NSCaseInsensitiveSearch) range:NSMakeRange(0, [entryPath length])];
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
{
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!");
return NO;
2007-03-07 01:26:50 +00:00
}
[fileHandle truncateFileAtOffset:0];
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]];
}
[fileHandle closeFile];
2007-03-07 01:26:50 +00:00
return YES;
}
- (BOOL)savePls:(NSString *)filename
{
NSFileHandle *fileHandle = [NSFileHandle fileHandleForWritingAtPath:filename createFile:YES];
2007-03-07 01:26:50 +00:00
if (!fileHandle) {
return NO;
}
[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
int i = 1;
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]];
i++;
2007-03-07 01:26:50 +00:00
}
[fileHandle writeData:[@"\nVERSION=2" dataUsingEncoding:NSUTF8StringEncoding]];
[fileHandle closeFile];
2007-03-07 01:26:50 +00:00
return YES;
}
- (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)
{
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;
}
- (NSArray*)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];
if (!urls)
return [NSArray array];
if (index < 0)
index = 0;
NSURL *url;
for (url in urls)
{
if ([url isFileURL]) {
BOOL isDir;
if ([[NSFileManager defaultManager] fileExistsAtPath:[url path] isDirectory:&isDir])
{
if (isDir == YES)
{
//Get subpaths
2007-05-28 14:58:08 +00:00
[expandedURLs addObjectsFromArray:[self fileURLsAtPath:[url path]]];
}
else
{
2007-05-28 14:58:08 +00:00
[expandedURLs addObject:url];
}
}
}
else
{
//Non-file URL..
2007-05-28 14:58:08 +00:00
[expandedURLs addObject:url];
}
}
2007-10-19 02:23:10 +00:00
NSLog(@"Expanded urls: %@", expandedURLs);
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];
}
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-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);
for (url in fileURLs)
{
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]])
continue;
if (![uniqueURLs containsObject:url])
{
[validURLs addObject:url];
[uniqueURLs addObject:url];
}
}
2007-10-19 02:23:10 +00:00
NSLog(@"Valid urls: %@", validURLs);
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];
}
//Create actual entries
int i;
NSMutableArray *entries = [NSMutableArray arrayWithCapacity:[validURLs count]];
2007-05-28 14:58:08 +00:00
for (i = 0; i < [validURLs count]; i++)
{
2007-05-28 14:58:08 +00:00
NSURL *url = [validURLs objectAtIndex:i];
PlaylistEntry *pe;
if ([url isFileURL])
pe = [[FilePlaylistEntry alloc] init];
else
pe = [[PlaylistEntry alloc] init];
pe.URL = url;
2008-02-23 19:46:23 +00:00
pe.index = index+i;
pe.title = [[url path] lastPathComponent];
pe.queuePosition = -1;
[entries addObject:pe];
[pe release];
}
NSIndexSet *is = [NSIndexSet indexSetWithIndexesInRange:NSMakeRange(index, [entries count])];
[playlistController insertObjects:entries atArrangedObjectIndexes:is];
//Select the first entry in the group that was just added
[playlistController setSelectionIndex:index];
[self performSelectorInBackground:@selector(loadInfoForEntries:) withObject:entries];
return entries;
}
- (void)loadInfoForEntries:(NSArray *)entries
{
NSOperationQueue *queue;
queue = [[[NSApplication sharedApplication] delegate] sharedOperationQueue];
2008-03-04 00:35:20 +00:00
NSInvocationOperation *oldReadEntryInfoOperation = nil;
for (PlaylistEntry *pe in entries)
{
2008-03-04 00:35:20 +00:00
NSAutoreleasePool *pool =[[NSAutoreleasePool alloc] init];
NSInvocationOperation *readEntryInfoOperation;
readEntryInfoOperation = [[NSInvocationOperation alloc]
initWithTarget:self
selector:@selector(readEntryInfo:)
object:pe];
if (oldReadEntryInfoOperation)
{
[readEntryInfoOperation addDependency:oldReadEntryInfoOperation];
[oldReadEntryInfoOperation release];
}
[readEntryInfoOperation addObserver:self
forKeyPath:@"isFinished"
options:NSKeyValueObservingOptionNew
context:NULL];
[queue addOperation:readEntryInfoOperation];
oldReadEntryInfoOperation = [readEntryInfoOperation retain];
[readEntryInfoOperation release];
2008-03-04 00:35:20 +00:00
[pool release];
}
[oldReadEntryInfoOperation release];
[queue waitUntilAllOperationsAreFinished];
[playlistController performSelectorOnMainThread:@selector(updateTotalTime) withObject:nil waitUntilDone:NO];
}
- (NSDictionary *)readEntryInfo:(PlaylistEntry *)pe
{
// Just setting this to 20 for now...
NSMutableDictionary *entryInfo = [NSMutableDictionary dictionaryWithCapacity:20];
NSDictionary *entryProperties;
entryProperties = [AudioPropertiesReader propertiesForURL:pe.URL];
if (entryProperties == nil)
return nil;
[entryInfo addEntriesFromDictionary:entryProperties];
[entryInfo addEntriesFromDictionary:[AudioMetadataReader metadataForURL:pe.URL]];
return entryInfo;
}
- (void)processEntryInfo:(NSInvocationOperation *)operation
{
NSDictionary *entryInfo = [operation result];
PlaylistEntry *pe;
// get the playlist entry that the thread was inspecting
[[operation invocation]getArgument:&pe atIndex:2];
if (entryInfo == nil)
{
pe.error = YES;
pe.errorMessage = @"Unable to retrieve properties.";
}
else
{
[pe setValuesForKeysWithDictionary:entryInfo];
}
return;
}
- (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])
{
// stop observing
[object removeObserver:self forKeyPath:keyPath];
[self performSelectorOnMainThread:@selector(processEntryInfo:) withObject:object waitUntilDone:NO];
}
2008-03-01 18:38:47 +00:00
else
{
[super observeValueForKeyPath:keyPath
ofObject:object
change:change
context:context];
}
}
- (NSArray*)addURLs:(NSArray *)urls sort:(BOOL)sort
{
return [self insertURLs:urls atIndex:[[playlistController content] count] sort:sort];
}
- (NSArray*)addURL:(NSURL *)url
{
return [self insertURLs:[NSArray arrayWithObject:url] atIndex:[[playlistController content] count] sort:NO];
}
- (NSArray *)acceptableFileTypes
{
return [[self acceptableContainerTypes] arrayByAddingObjectsFromArray:[AudioPlayer fileTypes]];
}
- (NSArray *)acceptablePlaylistTypes
{
return [NSArray arrayWithObjects:@"m3u", @"pls", nil];
}
- (NSArray *)acceptableContainerTypes
2007-03-07 01:26:50 +00:00
{
return [AudioPlayer containerTypes];
2007-03-07 01:26:50 +00:00
}
- (void)willInsertFiles:(NSArray*)urls origin:(AddedFilesSource)src
{
[playlistController willInsertFiles:urls origin:src];
}
- (void)didInsertFiles:(NSArray*)entries origin:(AddedFilesSource)src
{
[playlistController didInsertFiles:entries origin:src];
}
2007-03-07 01:26:50 +00:00
@end