458 lines
13 KiB
Objective-C
458 lines
13 KiB
Objective-C
#import "PluginController.h"
|
|
#import "Plugin.h"
|
|
#import "CogPluginMulti.h"
|
|
|
|
#import "Logging.h"
|
|
|
|
@implementation PluginController
|
|
|
|
@synthesize sources;
|
|
@synthesize containers;
|
|
@synthesize metadataReaders;
|
|
|
|
@synthesize propertiesReadersByExtension;
|
|
@synthesize propertiesReadersByMimeType;
|
|
|
|
@synthesize decodersByExtension;
|
|
@synthesize decodersByMimeType;
|
|
|
|
@synthesize configured;
|
|
|
|
static PluginController *sharedPluginController = nil;
|
|
|
|
+ (id<CogPluginController>)sharedPluginController
|
|
{
|
|
@synchronized(self) {
|
|
if (sharedPluginController == nil) {
|
|
sharedPluginController = [[self alloc] init];
|
|
}
|
|
}
|
|
|
|
return sharedPluginController;
|
|
}
|
|
|
|
|
|
- (id)init {
|
|
self = [super init];
|
|
if (self) {
|
|
self.sources = [[NSMutableDictionary alloc] init];
|
|
self.containers = [[NSMutableDictionary alloc] init];
|
|
|
|
self.metadataReaders = [[NSMutableDictionary alloc] init];
|
|
|
|
self.propertiesReadersByExtension = [[NSMutableDictionary alloc] init];
|
|
self.propertiesReadersByMimeType = [[NSMutableDictionary alloc] init];
|
|
|
|
self.decodersByExtension = [[NSMutableDictionary alloc] init];
|
|
self.decodersByMimeType = [[NSMutableDictionary alloc] init];
|
|
|
|
[self setup];
|
|
}
|
|
|
|
return self;
|
|
}
|
|
|
|
- (void)setup
|
|
{
|
|
if (self.configured == NO) {
|
|
self.configured = YES;
|
|
|
|
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(bundleDidLoad:) name:NSBundleDidLoadNotification object:nil];
|
|
|
|
[self loadPlugins];
|
|
[self printPluginInfo];
|
|
}
|
|
}
|
|
|
|
- (void)bundleDidLoad:(NSNotification *)notification
|
|
{
|
|
NSArray *classNames = [[notification userInfo] objectForKey:@"NSLoadedClasses"];
|
|
for (NSString *className in classNames)
|
|
{
|
|
Class bundleClass = NSClassFromString(className);
|
|
if ([bundleClass conformsToProtocol:@protocol(CogVersionCheck)]) {
|
|
DLog(@"Component has version check: %@", className);
|
|
if (![bundleClass shouldLoadForOSVersion:[[NSProcessInfo processInfo] operatingSystemVersion]])
|
|
{
|
|
DLog(@"Plugin fails OS version check, ignoring");
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
for (NSString *className in classNames)
|
|
{
|
|
DLog(@"Class loaded: %@", className);
|
|
Class bundleClass = NSClassFromString(className);
|
|
if ([bundleClass conformsToProtocol:@protocol(CogContainer)]) {
|
|
[self setupContainer:className];
|
|
}
|
|
if ([bundleClass conformsToProtocol:@protocol(CogDecoder)]) {
|
|
[self setupDecoder:className];
|
|
}
|
|
if ([bundleClass conformsToProtocol:@protocol(CogMetadataReader)]) {
|
|
[self setupMetadataReader:className];
|
|
}
|
|
if ([bundleClass conformsToProtocol:@protocol(CogPropertiesReader)]) {
|
|
[self setupPropertiesReader:className];
|
|
}
|
|
if ([bundleClass conformsToProtocol:@protocol(CogSource)]) {
|
|
[self setupSource:className];
|
|
}
|
|
}
|
|
}
|
|
|
|
- (void)loadPluginsAtPath:(NSString *)path
|
|
{
|
|
|
|
NSArray *dirContents = [[NSFileManager defaultManager] contentsOfDirectoryAtPath:path error:nil];
|
|
|
|
for (NSString *pname in dirContents)
|
|
{
|
|
NSString *ppath;
|
|
ppath = [NSString pathWithComponents:[NSArray arrayWithObjects:path,pname,nil]];
|
|
|
|
if ([[pname pathExtension] isEqualToString:@"bundle"])
|
|
{
|
|
NSBundle *b = [NSBundle bundleWithPath:ppath];
|
|
[b load];
|
|
}
|
|
}
|
|
}
|
|
|
|
- (void)loadPlugins
|
|
{
|
|
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
|
|
{
|
|
Class container = NSClassFromString(className);
|
|
if (container && [container respondsToSelector:@selector(fileTypes)]) {
|
|
for (id fileType in [container fileTypes])
|
|
{
|
|
NSString *ext = [fileType lowercaseString];
|
|
NSMutableArray *containerSet;
|
|
if (![containers objectForKey:ext])
|
|
{
|
|
containerSet = [[NSMutableArray alloc] init];
|
|
[containers setObject:containerSet forKey:ext];
|
|
}
|
|
else
|
|
containerSet = [containers objectForKey:ext];
|
|
[containerSet addObject:className];
|
|
}
|
|
}
|
|
}
|
|
|
|
- (void)setupDecoder:(NSString *)className
|
|
{
|
|
Class decoder = NSClassFromString(className);
|
|
if (decoder && [decoder respondsToSelector:@selector(fileTypes)]) {
|
|
for (id fileType in [decoder fileTypes])
|
|
{
|
|
NSString *ext = [fileType lowercaseString];
|
|
NSMutableArray *decoders;
|
|
if (![decodersByExtension objectForKey:ext])
|
|
{
|
|
decoders = [[NSMutableArray alloc] init];
|
|
[decodersByExtension setObject:decoders forKey:ext];
|
|
}
|
|
else
|
|
decoders = [decodersByExtension objectForKey:ext];
|
|
[decoders addObject:className];
|
|
}
|
|
}
|
|
|
|
if (decoder && [decoder respondsToSelector:@selector(mimeTypes)]) {
|
|
for (id mimeType in [decoder mimeTypes])
|
|
{
|
|
NSString *mimetype = [mimeType lowercaseString];
|
|
NSMutableArray *decoders;
|
|
if (![decodersByMimeType objectForKey:mimetype])
|
|
{
|
|
decoders = [[NSMutableArray alloc] init];
|
|
[decodersByMimeType setObject:decoders forKey:mimetype];
|
|
}
|
|
else
|
|
decoders = [decodersByMimeType objectForKey:mimetype];
|
|
[decoders addObject:className];
|
|
}
|
|
}
|
|
}
|
|
|
|
- (void)setupMetadataReader:(NSString *)className
|
|
{
|
|
Class metadataReader = NSClassFromString(className);
|
|
if (metadataReader && [metadataReader respondsToSelector:@selector(fileTypes)]) {
|
|
for (id fileType in [metadataReader fileTypes])
|
|
{
|
|
NSString *ext = [fileType lowercaseString];
|
|
NSMutableArray *readers;
|
|
if (![metadataReaders objectForKey:ext])
|
|
{
|
|
readers = [[NSMutableArray alloc] init];
|
|
[metadataReaders setObject:readers forKey:ext];
|
|
}
|
|
else
|
|
readers = [metadataReaders objectForKey:ext];
|
|
[readers addObject:className];
|
|
}
|
|
}
|
|
}
|
|
|
|
- (void)setupPropertiesReader:(NSString *)className
|
|
{
|
|
Class propertiesReader = NSClassFromString(className);
|
|
if (propertiesReader && [propertiesReader respondsToSelector:@selector(fileTypes)]) {
|
|
for (id fileType in [propertiesReader fileTypes])
|
|
{
|
|
NSString *ext = [fileType lowercaseString];
|
|
NSMutableArray *readers;
|
|
if (![propertiesReadersByExtension objectForKey:ext])
|
|
{
|
|
readers = [[NSMutableArray alloc] init];
|
|
[propertiesReadersByExtension setObject:readers forKey:ext];
|
|
}
|
|
else
|
|
readers = [propertiesReadersByExtension objectForKey:ext];
|
|
[readers addObject:className];
|
|
}
|
|
}
|
|
|
|
if (propertiesReader && [propertiesReader respondsToSelector:@selector(mimeTypes)]) {
|
|
for (id mimeType in [propertiesReader mimeTypes])
|
|
{
|
|
NSString *mimetype = [mimeType lowercaseString];
|
|
NSMutableArray *readers;
|
|
if (![propertiesReadersByMimeType objectForKey:mimetype])
|
|
{
|
|
readers = [[NSMutableArray alloc] init];
|
|
[propertiesReadersByMimeType setObject:readers forKey:mimetype];
|
|
}
|
|
else
|
|
readers = [propertiesReadersByMimeType objectForKey:mimetype];
|
|
[readers addObject:className];
|
|
}
|
|
}
|
|
}
|
|
|
|
- (void)setupSource:(NSString *)className
|
|
{
|
|
Class source = NSClassFromString(className);
|
|
if (source && [source respondsToSelector:@selector(schemes)]) {
|
|
for (id scheme in [source schemes])
|
|
{
|
|
[sources setObject:className forKey:scheme];
|
|
}
|
|
}
|
|
}
|
|
|
|
- (void)printPluginInfo
|
|
{
|
|
ALog(@"Sources: %@", self.sources);
|
|
ALog(@"Containers: %@", self.containers);
|
|
ALog(@"Metadata Readers: %@", self.metadataReaders);
|
|
|
|
ALog(@"Properties Readers By Extension: %@", self.propertiesReadersByExtension);
|
|
ALog(@"Properties Readers By Mime Type: %@", self.propertiesReadersByMimeType);
|
|
|
|
ALog(@"Decoders by Extension: %@", self.decodersByExtension);
|
|
ALog(@"Decoders by Mime Type: %@", self.decodersByMimeType);
|
|
}
|
|
|
|
- (id<CogSource>) audioSourceForURL:(NSURL *)url
|
|
{
|
|
NSString *scheme = [url scheme];
|
|
|
|
Class source = NSClassFromString([sources objectForKey:scheme]);
|
|
|
|
return [[source alloc] init];
|
|
}
|
|
|
|
- (NSArray *) urlsForContainerURL:(NSURL *)url
|
|
{
|
|
NSString *ext = [url pathExtension];
|
|
NSArray *containerSet = [containers objectForKey:[ext lowercaseString]];
|
|
NSString *classString;
|
|
if (containerSet) {
|
|
if ( [containerSet count] > 1 ) {
|
|
return [CogContainerMulti urlsForContainerURL:url containers:containerSet];
|
|
}
|
|
else {
|
|
classString = [containerSet objectAtIndex:0];
|
|
}
|
|
}
|
|
else {
|
|
return nil;
|
|
}
|
|
|
|
Class container = NSClassFromString(classString);
|
|
|
|
return [container urlsForContainerURL:url];
|
|
}
|
|
|
|
//Note: Source is assumed to already be opened.
|
|
- (id<CogDecoder>) audioDecoderForSource:(id <CogSource>)source skipCue:(BOOL)skip
|
|
{
|
|
NSString *ext = [[source url] pathExtension];
|
|
NSArray *decoders = [decodersByExtension objectForKey:[ext lowercaseString]];
|
|
NSString *classString;
|
|
if (decoders) {
|
|
if ( [decoders count] > 1 ) {
|
|
if (skip)
|
|
{
|
|
NSMutableArray * _decoders = [decoders mutableCopy];
|
|
for (int i = 0; i < [_decoders count];)
|
|
{
|
|
if ([[_decoders objectAtIndex:i] isEqualToString:@"CueSheetDecoder"])
|
|
[_decoders removeObjectAtIndex:i];
|
|
else
|
|
++i;
|
|
}
|
|
return [[CogDecoderMulti alloc] initWithDecoders:_decoders];
|
|
}
|
|
return [[CogDecoderMulti alloc] initWithDecoders:decoders];
|
|
}
|
|
else {
|
|
classString = [decoders objectAtIndex:0];
|
|
}
|
|
}
|
|
else {
|
|
decoders = [decodersByMimeType objectForKey:[[source mimeType] lowercaseString]];
|
|
if (decoders) {
|
|
if ( [decoders count] > 1 ) {
|
|
return [[CogDecoderMulti alloc] initWithDecoders:decoders];
|
|
}
|
|
else {
|
|
classString = [decoders objectAtIndex:0];
|
|
}
|
|
}
|
|
else {
|
|
classString = @"SilenceDecoder";
|
|
}
|
|
}
|
|
|
|
Class decoder = NSClassFromString(classString);
|
|
|
|
return [[decoder alloc] init];
|
|
}
|
|
|
|
- (NSDictionary *)metadataForURL:(NSURL *)url skipCue:(BOOL)skip
|
|
{
|
|
NSString * urlScheme = [url scheme];
|
|
if ([urlScheme isEqualToString:@"http"] ||
|
|
[urlScheme isEqualToString:@"https"])
|
|
return nil;
|
|
|
|
NSString *ext = [url pathExtension];
|
|
NSArray *readers = [metadataReaders objectForKey:[ext lowercaseString]];
|
|
NSString *classString;
|
|
if (readers) {
|
|
if ( [readers count] > 1 ) {
|
|
if (skip)
|
|
{
|
|
NSMutableArray *_readers = [readers mutableCopy];
|
|
for (int i = 0; i < [_readers count];)
|
|
{
|
|
if ([[_readers objectAtIndex:i] isEqualToString:@"CueSheetMetadataReader"])
|
|
[_readers removeObjectAtIndex:i];
|
|
else
|
|
++i;
|
|
}
|
|
return [CogMetadataReaderMulti metadataForURL:url readers:_readers];
|
|
}
|
|
return [CogMetadataReaderMulti metadataForURL:url readers:readers];
|
|
}
|
|
else {
|
|
classString = [readers objectAtIndex:0];
|
|
}
|
|
}
|
|
else {
|
|
return nil;
|
|
}
|
|
|
|
Class metadataReader = NSClassFromString(classString);
|
|
|
|
return [metadataReader metadataForURL:url];
|
|
}
|
|
|
|
|
|
//If no properties reader is defined, use the decoder's properties.
|
|
- (NSDictionary *)propertiesForURL:(NSURL *)url
|
|
{
|
|
NSString * urlScheme = [url scheme];
|
|
if ([urlScheme isEqualToString:@"http"] ||
|
|
[urlScheme isEqualToString:@"https"])
|
|
return nil;
|
|
|
|
NSDictionary *properties = nil;
|
|
NSString *ext = [url pathExtension];
|
|
|
|
id<CogSource> source = [self audioSourceForURL:url];
|
|
if (![source open:url])
|
|
return nil;
|
|
|
|
NSArray *readers = [propertiesReadersByExtension objectForKey:[ext lowercaseString]];
|
|
NSString *classString = nil;
|
|
if (readers)
|
|
{
|
|
if ( [readers count] > 1 ) {
|
|
properties = [CogPropertiesReaderMulti propertiesForSource:source readers:readers];
|
|
if (properties != nil && [properties count])
|
|
return properties;
|
|
}
|
|
else {
|
|
classString = [readers objectAtIndex:0];
|
|
}
|
|
}
|
|
else {
|
|
readers = [propertiesReadersByMimeType objectForKey:[[source mimeType] lowercaseString]];
|
|
if (readers)
|
|
{
|
|
if ( [readers count] > 1 ) {
|
|
properties = [CogPropertiesReaderMulti propertiesForSource:source readers:readers];
|
|
if (properties != nil && [properties count])
|
|
return properties;
|
|
}
|
|
else {
|
|
classString = [readers objectAtIndex:0];
|
|
}
|
|
}
|
|
}
|
|
|
|
if (classString)
|
|
{
|
|
Class propertiesReader = NSClassFromString(classString);
|
|
|
|
properties = [propertiesReader propertiesForSource:source];
|
|
if (properties != nil && [properties count])
|
|
return properties;
|
|
}
|
|
|
|
{
|
|
id<CogDecoder> decoder = [self audioDecoderForSource:source skipCue:NO];
|
|
if (![decoder open:source])
|
|
{
|
|
return nil;
|
|
}
|
|
|
|
NSDictionary *properties = [decoder properties];
|
|
|
|
[decoder close];
|
|
|
|
return properties;
|
|
}
|
|
}
|
|
|
|
- (int)putMetadataInURL:(NSURL *)url
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
@end
|
|
|