Initial open source commit.
This commit is contained in:
commit
e5db00ae3d
11
.gitignore
vendored
Normal file
11
.gitignore
vendored
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
build
|
||||||
|
*.zip
|
||||||
|
*.xcodeproj/*.pbxuser
|
||||||
|
*.xcodeproj/*.mode*
|
||||||
|
*.xcodeproj/*.perspective*
|
||||||
|
*.xcodeproj/xcuserdata
|
||||||
|
*.xcodeproj/project.xcworkspace/xcuserdata
|
||||||
|
*.xcworkspace/xcuserdata
|
||||||
|
YouView/*.app
|
||||||
|
*.app
|
||||||
|
Screenshots
|
20
About.rtf
Normal file
20
About.rtf
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
{\rtf1\ansi\ansicpg1252\cocoartf1347\cocoasubrtf570
|
||||||
|
\cocoascreenfonts1{\fonttbl\f0\fswiss\fcharset0 Helvetica;}
|
||||||
|
{\colortbl;\red255\green255\blue255;}
|
||||||
|
\margl1440\margr1440\vieww9000\viewh8400\viewkind0
|
||||||
|
\pard\tx720\tx1440\tx2160\tx2880\tx3600\tx4320\tx5040\tx5760\tx6480\tx7200\tx7920\tx8640\pardirnatural\qc
|
||||||
|
|
||||||
|
\f0\fs32 \cf0 Special Thanks To\
|
||||||
|
|
||||||
|
\fs24 MegaEduX ({\field{\*\fldinst{HYPERLINK "http://megaedux.com"}}{\fldrslt megaedux.com}})\
|
||||||
|
RockStar ({\field{\*\fldinst{HYPERLINK "http://rocknthesweater.com"}}{\fldrslt rocknthesweater.com}})\
|
||||||
|
\
|
||||||
|
\pard\tx720\tx1440\tx2160\tx2880\tx3600\tx4320\tx5040\tx5760\tx6480\tx7200\tx7920\tx8640\pardirnatural
|
||||||
|
\cf0 YouView Uses {\field{\*\fldinst{HYPERLINK "http://ffmpeg.org"}}{\fldrslt FFmpeg}} for converting video, {\field{\*\fldinst{HYPERLINK "http://mediainfo.sourceforge.net"}}{\fldrslt mediainfo}} to determine information about video, {\field{\*\fldinst{HYPERLINK "http://sparkle.andymatuschak.org"}}{\fldrslt Sparkle}} for software update, and {\field{\*\fldinst{HYPERLINK "http://code.google.com/apis/gdata/"}}{\fldrslt GData}} for gathering information.\
|
||||||
|
\
|
||||||
|
Browse, watch, convert, and download YouTube videos on your Mac faster, because YouView uses the core technologies of Mac OS X to play instead of Flash allowing video to run smoother on systems with lower specs.\
|
||||||
|
YouView contains great parental controls that'll allow any parents control what their children see on YouTube via filters such as Safe search and flagged videos. By default YouView is moderate on parental controls but can be strict if needed.\
|
||||||
|
YouView allows you to choose the max size of video that is loaded such as 1080P. By default it is 480P but can go all the way up to 4k if you system supports it.\
|
||||||
|
When logged in with YouView you have the ability to view your special feeds such as recommendations which shows videos that YouTube thinks you may also like based on your history of viewing.\
|
||||||
|
There is a lot more to YouView than described here, the best way to find out what you can do is to try it out your self.\
|
||||||
|
I require donations for download/converting of video as it isn't an easy task to get together.}
|
17
Classes/YVTool/MGMYVTool.h
Normal file
17
Classes/YVTool/MGMYVTool.h
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
//
|
||||||
|
// MGMYVTool.h
|
||||||
|
// YouView
|
||||||
|
//
|
||||||
|
// Created by Mr. Gecko on 7/29/10.
|
||||||
|
// Copyright (c) 2015 Mr. Gecko's Media (James Coleman). https://mrgeckosmedia.com/ All rights reserved.
|
||||||
|
//
|
||||||
|
|
||||||
|
#import <Cocoa/Cocoa.h>
|
||||||
|
|
||||||
|
|
||||||
|
@interface MGMYVTool : NSObject {
|
||||||
|
pid_t parentProcessId;
|
||||||
|
NSTimer *shutdownCheck;
|
||||||
|
}
|
||||||
|
- (id)initWithPid:(pid_t)thePid;
|
||||||
|
@end
|
54
Classes/YVTool/MGMYVTool.m
Normal file
54
Classes/YVTool/MGMYVTool.m
Normal file
@ -0,0 +1,54 @@
|
|||||||
|
//
|
||||||
|
// MGMYVTool.m
|
||||||
|
// YouView
|
||||||
|
//
|
||||||
|
// Created by Mr. Gecko on 7/29/10.
|
||||||
|
// Copyright (c) 2015 Mr. Gecko's Media (James Coleman). https://mrgeckosmedia.com/ All rights reserved.
|
||||||
|
//
|
||||||
|
|
||||||
|
#import "MGMYVTool.h"
|
||||||
|
|
||||||
|
@implementation MGMYVTool
|
||||||
|
- (id)initWithPid:(pid_t)thePid {
|
||||||
|
if (self = [super init]) {
|
||||||
|
parentProcessId = thePid;
|
||||||
|
shutdownCheck = [[NSTimer scheduledTimerWithTimeInterval:1.0 target:self selector:@selector(checkProcess) userInfo:nil repeats:YES] retain];
|
||||||
|
}
|
||||||
|
return self;
|
||||||
|
}
|
||||||
|
- (void)dealloc {
|
||||||
|
[shutdownCheck invalidate];
|
||||||
|
[shutdownCheck release];
|
||||||
|
[super dealloc];
|
||||||
|
}
|
||||||
|
- (void)checkProcess {
|
||||||
|
ProcessSerialNumber psn;
|
||||||
|
if (GetProcessForPID(parentProcessId, &psn) == procNotFound) {
|
||||||
|
exit(0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
- (void)shouldQuit {
|
||||||
|
exit(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)changePremissionsForPath:(NSString *)thePath to:(NSString *)thePermissions {
|
||||||
|
NSTask *task = [[NSTask new] autorelease];
|
||||||
|
[task setLaunchPath:@"/bin/chmod"];
|
||||||
|
[task setArguments:[NSArray arrayWithObjects:thePermissions, thePath, nil]];
|
||||||
|
[task launch];
|
||||||
|
[task waitUntilExit];
|
||||||
|
}
|
||||||
|
- (void)changeOwnerForPath:(NSString *)thePath to:(NSString *)theOwner {
|
||||||
|
NSTask *task = [[NSTask new] autorelease];
|
||||||
|
[task setLaunchPath:@"/usr/sbin/chown"];
|
||||||
|
[task setArguments:[NSArray arrayWithObjects:theOwner, thePath, nil]];
|
||||||
|
[task launch];
|
||||||
|
[task waitUntilExit];
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)quit {
|
||||||
|
[shutdownCheck invalidate];
|
||||||
|
[shutdownCheck release];
|
||||||
|
shutdownCheck = [[NSTimer scheduledTimerWithTimeInterval:0.2 target:self selector:@selector(shouldQuit) userInfo:nil repeats:NO] retain];
|
||||||
|
}
|
||||||
|
@end
|
36
Classes/YVTool/main.m
Normal file
36
Classes/YVTool/main.m
Normal file
@ -0,0 +1,36 @@
|
|||||||
|
//
|
||||||
|
// main.m
|
||||||
|
// YouView
|
||||||
|
//
|
||||||
|
// Created by Mr. Gecko on 7/29/10.
|
||||||
|
// Copyright (c) 2015 Mr. Gecko's Media (James Coleman). https://mrgeckosmedia.com/ All rights reserved.
|
||||||
|
//
|
||||||
|
|
||||||
|
#import <Cocoa/Cocoa.h>
|
||||||
|
#import "MGMYVTool.h"
|
||||||
|
|
||||||
|
MGMYVTool *yvServer;
|
||||||
|
|
||||||
|
int main(int argc, char *argv[]) {
|
||||||
|
NSAutoreleasePool * pool = [NSAutoreleasePool new];
|
||||||
|
yvServer = [[MGMYVTool alloc] initWithPid:atoi(argv[1])];
|
||||||
|
|
||||||
|
NSConnection *theYVServer = [NSConnection new];
|
||||||
|
|
||||||
|
if(theYVServer==nil) {
|
||||||
|
NSLog(@"Server couldn't start");
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
[theYVServer setRootObject:yvServer];
|
||||||
|
[theYVServer registerName:@"MGMYVServer"];
|
||||||
|
[theYVServer setDelegate:yvServer];
|
||||||
|
|
||||||
|
NSLog(@"Server Started");
|
||||||
|
|
||||||
|
[[NSRunLoop currentRunLoop] run];
|
||||||
|
[yvServer release];
|
||||||
|
[theYVServer release];
|
||||||
|
[pool release];
|
||||||
|
return 0;
|
||||||
|
}
|
29
Classes/YouView/MGMAddons.h
Normal file
29
Classes/YouView/MGMAddons.h
Normal file
@ -0,0 +1,29 @@
|
|||||||
|
//
|
||||||
|
// NSAddons.h
|
||||||
|
// YouView
|
||||||
|
//
|
||||||
|
// Created by Mr. Gecko on 3/4/09.
|
||||||
|
// Copyright (c) 2015 Mr. Gecko's Media (James Coleman). https://mrgeckosmedia.com/ All rights reserved.
|
||||||
|
//
|
||||||
|
|
||||||
|
#import <Cocoa/Cocoa.h>
|
||||||
|
|
||||||
|
@interface NSString (MGMAddons)
|
||||||
|
+ (NSString *)stringWithSeconds:(int)seconds;
|
||||||
|
- (NSString *)flattenHTML;
|
||||||
|
- (NSString *)replace:(NSString *)targetString with:(NSString *)replaceString;
|
||||||
|
- (BOOL)containsString:(NSString *)string;
|
||||||
|
- (NSString *)addPercentEscapes;
|
||||||
|
- (NSString *)URLParameterWithName:(NSString *)theName;
|
||||||
|
- (NSDictionary *)URLParameters;
|
||||||
|
@end
|
||||||
|
|
||||||
|
@interface NSURL (MGMAddons)
|
||||||
|
- (NSString *)URLParameterWithName:(NSString *)theName;
|
||||||
|
- (NSDictionary *)URLParameters;
|
||||||
|
- (NSURL *)URLByAppendingPathComponent:(NSString *)theComponent;
|
||||||
|
@end
|
||||||
|
|
||||||
|
@interface NSUserDefaults (MGMAddons)
|
||||||
|
+ (void)registerDefaults;
|
||||||
|
@end
|
164
Classes/YouView/MGMAddons.m
Normal file
164
Classes/YouView/MGMAddons.m
Normal file
@ -0,0 +1,164 @@
|
|||||||
|
//
|
||||||
|
// NSAddons.m
|
||||||
|
// YouView
|
||||||
|
//
|
||||||
|
// Created by Mr. Gecko on 3/4/09.
|
||||||
|
// Copyright (c) 2015 Mr. Gecko's Media (James Coleman). https://mrgeckosmedia.com/ All rights reserved.
|
||||||
|
//
|
||||||
|
|
||||||
|
#import "MGMAddons.h"
|
||||||
|
#import "MGMController.h"
|
||||||
|
#import "MGMXML.h"
|
||||||
|
#import <GeckoReporter/MGMLog.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <sys/types.h>
|
||||||
|
#include <CommonCrypto/CommonDigest.h>
|
||||||
|
#import <openssl/evp.h>
|
||||||
|
#import <openssl/rand.h>
|
||||||
|
#import <openssl/rsa.h>
|
||||||
|
#import <openssl/engine.h>
|
||||||
|
#import <openssl/sha.h>
|
||||||
|
#import <openssl/pem.h>
|
||||||
|
#import <openssl/bio.h>
|
||||||
|
#import <openssl/err.h>
|
||||||
|
#import <openssl/ssl.h>
|
||||||
|
|
||||||
|
@implementation NSString (MGMAddons)
|
||||||
|
+ (NSString *)stringWithSeconds:(int)time {
|
||||||
|
int seconds = time%60;
|
||||||
|
time = time/60;
|
||||||
|
int minutes = time%60;
|
||||||
|
time = time/60;
|
||||||
|
int hours = time%24;
|
||||||
|
int days = time/24;
|
||||||
|
NSString *string;
|
||||||
|
if (days!=0) {
|
||||||
|
string = [NSString stringWithFormat:@"%d:%02d:%02d:%02d", days, hours, minutes, seconds];
|
||||||
|
} else if (hours!=0) {
|
||||||
|
string = [NSString stringWithFormat:@"%d:%02d:%02d", hours, minutes, seconds];
|
||||||
|
} else {
|
||||||
|
string = [NSString stringWithFormat:@"%02d:%02d", minutes, seconds];
|
||||||
|
}
|
||||||
|
return string;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
//Remove html
|
||||||
|
- (NSString *)flattenHTML {
|
||||||
|
NSString *xml = [NSString stringWithFormat:@"<div>%@</div>", self];
|
||||||
|
MGMXMLDocument *document = [[MGMXMLDocument alloc] initWithXMLString:xml options:MGMXMLDocumentTidyHTML error:nil];
|
||||||
|
MGMXMLElement *element = nil;
|
||||||
|
if (document!=nil) {
|
||||||
|
element = [[[document rootElement] retain] autorelease];
|
||||||
|
if (element!=nil)
|
||||||
|
[element detach];
|
||||||
|
[document release];
|
||||||
|
}
|
||||||
|
return [element stringValue];
|
||||||
|
}
|
||||||
|
|
||||||
|
//Replace
|
||||||
|
- (NSString *)replace:(NSString *)targetString with:(NSString *)replaceString {
|
||||||
|
NSAutoreleasePool *pool = [NSAutoreleasePool new];
|
||||||
|
NSMutableString *temp = [NSMutableString new];
|
||||||
|
NSRange replaceRange = NSMakeRange(0, [self length]);
|
||||||
|
NSRange rangeInOriginalString = replaceRange;
|
||||||
|
int replaced = 0;
|
||||||
|
|
||||||
|
while (1) {
|
||||||
|
NSRange rangeToCopy;
|
||||||
|
NSRange foundRange = [self rangeOfString:targetString options:0 range:rangeInOriginalString];
|
||||||
|
if (foundRange.length == 0) break;
|
||||||
|
rangeToCopy = NSMakeRange(rangeInOriginalString.location, foundRange.location - rangeInOriginalString.location);
|
||||||
|
[temp appendString:[self substringWithRange:rangeToCopy]];
|
||||||
|
[temp appendString:replaceString];
|
||||||
|
rangeInOriginalString.length -= NSMaxRange(foundRange) -
|
||||||
|
rangeInOriginalString.location;
|
||||||
|
rangeInOriginalString.location = NSMaxRange(foundRange);
|
||||||
|
replaced++;
|
||||||
|
if (replaced % 100 == 0) {
|
||||||
|
[pool release];
|
||||||
|
pool = [NSAutoreleasePool new];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (rangeInOriginalString.length > 0) [temp appendString:[self substringWithRange:rangeInOriginalString]];
|
||||||
|
[pool release];
|
||||||
|
|
||||||
|
return [temp autorelease];
|
||||||
|
}
|
||||||
|
- (BOOL)containsString:(NSString *)string {
|
||||||
|
return ([[self lowercaseString] rangeOfString:[string lowercaseString]].location != NSNotFound);
|
||||||
|
}
|
||||||
|
- (NSString *)addPercentEscapes {
|
||||||
|
NSString *result = [self stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
|
||||||
|
CFStringRef escapedString = CFURLCreateStringByAddingPercentEscapes(kCFAllocatorDefault, (CFStringRef)self, NULL, CFSTR("!*'();:^@&=+$,/?%#[]|"), kCFStringEncodingUTF8);
|
||||||
|
|
||||||
|
if (escapedString!=NULL)
|
||||||
|
result = [(NSString *)escapedString autorelease];
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
- (NSString *)URLParameterWithName:(NSString *)theName {
|
||||||
|
NSArray *parameters = [self componentsSeparatedByString:@"&"];
|
||||||
|
for (int i=0; i<[parameters count]; i++) {
|
||||||
|
NSArray *parameter = [[parameters objectAtIndex:i] componentsSeparatedByString:@"="];
|
||||||
|
if ([[parameter objectAtIndex:0] isEqual:theName])
|
||||||
|
return [[parameter objectAtIndex:1] stringByReplacingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
|
||||||
|
}
|
||||||
|
return nil;
|
||||||
|
}
|
||||||
|
- (NSDictionary *)URLParameters {
|
||||||
|
NSArray *parameters = [self componentsSeparatedByString:@"&"];
|
||||||
|
NSMutableDictionary *returnParameters = [NSMutableDictionary dictionary];
|
||||||
|
for (int i=0; i<[parameters count]; i++) {
|
||||||
|
NSArray *parameter = [[parameters objectAtIndex:i] componentsSeparatedByString:@"="];
|
||||||
|
[returnParameters setObject:[[parameter objectAtIndex:1] stringByReplacingPercentEscapesUsingEncoding:NSUTF8StringEncoding] forKey:[[parameter objectAtIndex:0] stringByReplacingPercentEscapesUsingEncoding:NSUTF8StringEncoding]];
|
||||||
|
}
|
||||||
|
return returnParameters;
|
||||||
|
}
|
||||||
|
@end
|
||||||
|
|
||||||
|
@implementation NSURL (MGMAddons)
|
||||||
|
- (NSString *)URLParameterWithName:(NSString *)theName {
|
||||||
|
NSArray *parameters = [[self query] componentsSeparatedByString:@"&"];
|
||||||
|
for (int i=0; i<[parameters count]; i++) {
|
||||||
|
NSArray *parameter = [[parameters objectAtIndex:i] componentsSeparatedByString:@"="];
|
||||||
|
if ([[parameter objectAtIndex:0] isEqual:theName])
|
||||||
|
return [[parameter objectAtIndex:1] stringByReplacingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
|
||||||
|
}
|
||||||
|
return nil;
|
||||||
|
}
|
||||||
|
- (NSDictionary *)URLParameters {
|
||||||
|
NSArray *parameters = [[self query] componentsSeparatedByString:@"&"];
|
||||||
|
NSMutableDictionary *returnParameters = [NSMutableDictionary dictionary];
|
||||||
|
for (int i=0; i<[parameters count]; i++) {
|
||||||
|
NSArray *parameter = [[parameters objectAtIndex:i] componentsSeparatedByString:@"="];
|
||||||
|
[returnParameters setObject:[[parameter objectAtIndex:1] stringByReplacingPercentEscapesUsingEncoding:NSUTF8StringEncoding] forKey:[[parameter objectAtIndex:0] stringByReplacingPercentEscapesUsingEncoding:NSUTF8StringEncoding]];
|
||||||
|
}
|
||||||
|
return returnParameters;
|
||||||
|
}
|
||||||
|
- (NSURL *)URLByAppendingPathComponent:(NSString *)theComponent {
|
||||||
|
NSString *path = [self path];
|
||||||
|
path = [path stringByAppendingPathComponent:theComponent];
|
||||||
|
NSString *url = [NSString stringWithFormat:@"%@://%@%@?%@", [self scheme], [self host], path, [self query]];
|
||||||
|
return [NSURL URLWithString:url];
|
||||||
|
}
|
||||||
|
@end
|
||||||
|
|
||||||
|
@implementation NSUserDefaults (MGMAddons)
|
||||||
|
+ (void)registerDefaults {
|
||||||
|
NSMutableDictionary *defaults = [NSMutableDictionary dictionary];
|
||||||
|
[defaults setObject:[NSNumber numberWithInt:0] forKey:MGMMaxQuality];
|
||||||
|
[defaults setObject:[NSNumber numberWithInt:0] forKey:MGMWindowMode];
|
||||||
|
[defaults setObject:[NSNumber numberWithBool:YES] forKey:MGMAnimations];
|
||||||
|
[defaults setObject:[NSNumber numberWithBool:NO] forKey:MGMFSFloat];
|
||||||
|
[defaults setObject:[NSNumber numberWithBool:YES] forKey:MGMFSSpaces];
|
||||||
|
[defaults setObject:[NSNumber numberWithInt:50] forKey:MGMPageMax];
|
||||||
|
[defaults setObject:[NSArray array] forKey:MGMRecentSearches];
|
||||||
|
[defaults setObject:[NSNumber numberWithInt:1] forKey:MGMOrderBy];
|
||||||
|
|
||||||
|
[[NSUserDefaults standardUserDefaults] registerDefaults:defaults];
|
||||||
|
}
|
||||||
|
@end
|
24
Classes/YouView/MGMAdminAction.h
Normal file
24
Classes/YouView/MGMAdminAction.h
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
//
|
||||||
|
// MGMAdminAction.h
|
||||||
|
// YouView
|
||||||
|
//
|
||||||
|
// Created by Mr. Gecko on 3/25/08.
|
||||||
|
// Copyright (c) 2015 Mr. Gecko's Media (James Coleman). https://mrgeckosmedia.com/ All rights reserved.
|
||||||
|
//
|
||||||
|
|
||||||
|
#import <Cocoa/Cocoa.h>
|
||||||
|
#import <Security/Authorization.h>
|
||||||
|
#import <Security/AuthorizationTags.h>
|
||||||
|
|
||||||
|
@interface MGMAdminAction : NSObject {
|
||||||
|
AuthorizationRef authorization;
|
||||||
|
BOOL authorizationSetByUs;
|
||||||
|
}
|
||||||
|
- (void)setAuthorization:(AuthorizationRef)theAuthorization;
|
||||||
|
+ (AuthorizationRights)rightsForCommands:(NSArray *)theCommands;
|
||||||
|
- (BOOL)isAuthenticated:(NSArray *)theCommands;
|
||||||
|
- (BOOL)fetchPassword:(NSArray *)theCommands;
|
||||||
|
- (BOOL)authenticate:(NSArray *)theCommands;
|
||||||
|
- (NSString *)executeCommand:(NSString *)pathToCommand withArguments:(NSArray *)arguments;
|
||||||
|
- (void)launchCommand:(NSString *)thePath withArguments:(NSArray *)theArguments;
|
||||||
|
@end
|
165
Classes/YouView/MGMAdminAction.m
Normal file
165
Classes/YouView/MGMAdminAction.m
Normal file
@ -0,0 +1,165 @@
|
|||||||
|
//
|
||||||
|
// MGMAdminAction.m
|
||||||
|
// YouView
|
||||||
|
//
|
||||||
|
// Created by Mr. Gecko on 3/25/08.
|
||||||
|
// Copyright (c) 2015 Mr. Gecko's Media (James Coleman). https://mrgeckosmedia.com/ All rights reserved.
|
||||||
|
//
|
||||||
|
|
||||||
|
#import "MGMAdminAction.h"
|
||||||
|
#import <Security/AuthorizationTags.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <fcntl.h>
|
||||||
|
#include <sys/time.h>
|
||||||
|
#include <sys/resource.h>
|
||||||
|
#include <sys/stat.h>
|
||||||
|
#include <sys/wait.h>
|
||||||
|
#include <sys/param.h>
|
||||||
|
#include <sys/socket.h>
|
||||||
|
|
||||||
|
@implementation MGMAdminAction
|
||||||
|
- (id)init {
|
||||||
|
if (self = [super init]) {
|
||||||
|
authorization = NULL;
|
||||||
|
authorizationSetByUs = YES;
|
||||||
|
}
|
||||||
|
return self;
|
||||||
|
}
|
||||||
|
- (void)dealloc {
|
||||||
|
if (authorization!=NULL && authorizationSetByUs)
|
||||||
|
AuthorizationFree(authorization, kAuthorizationFlagDestroyRights);
|
||||||
|
[super dealloc];
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)setAuthorization:(AuthorizationRef)theAuthorization {
|
||||||
|
if (authorization!=NULL && authorizationSetByUs)
|
||||||
|
AuthorizationFree(authorization, kAuthorizationFlagDestroyRights);
|
||||||
|
authorization = theAuthorization;
|
||||||
|
authorizationSetByUs = NO;
|
||||||
|
}
|
||||||
|
|
||||||
|
+ (AuthorizationRights)rightsForCommands:(NSArray *)theCommands {
|
||||||
|
AuthorizationItem *items = malloc(sizeof(AuthorizationItem) *[theCommands count]);
|
||||||
|
for (int i=0; i<[theCommands count]; i++) {
|
||||||
|
items[i].name = kAuthorizationRightExecute;
|
||||||
|
items[i].value = (char *)[[theCommands objectAtIndex:i] UTF8String];
|
||||||
|
items[i].valueLength = [[theCommands objectAtIndex:i] length];
|
||||||
|
items[i].flags = 0;
|
||||||
|
}
|
||||||
|
AuthorizationRights rights;
|
||||||
|
rights.count = [theCommands count];
|
||||||
|
rights.items = items;
|
||||||
|
return rights;
|
||||||
|
}
|
||||||
|
|
||||||
|
- (AuthorizationRights)rightsForCommands:(NSArray *)theCommands {
|
||||||
|
return [[self class] rightsForCommands:theCommands];
|
||||||
|
}
|
||||||
|
|
||||||
|
- (BOOL)isAuthenticated:(NSArray *)theCommands {
|
||||||
|
AuthorizationRights rights;
|
||||||
|
AuthorizationRights *authorizedRights;
|
||||||
|
OSStatus status;
|
||||||
|
|
||||||
|
if (theCommands==nil || [theCommands count]==0)
|
||||||
|
return NO;
|
||||||
|
|
||||||
|
if (authorization==NULL) {
|
||||||
|
rights.count=0;
|
||||||
|
rights.items = NULL;
|
||||||
|
status = AuthorizationCreate(&rights, kAuthorizationEmptyEnvironment, kAuthorizationFlagDefaults, &authorization);
|
||||||
|
}
|
||||||
|
|
||||||
|
rights = [self rightsForCommands:theCommands];
|
||||||
|
status = AuthorizationCopyRights(authorization, &rights, kAuthorizationEmptyEnvironment, kAuthorizationFlagExtendRights, &authorizedRights);
|
||||||
|
free(rights.items);
|
||||||
|
|
||||||
|
if (status==errAuthorizationSuccess) {
|
||||||
|
AuthorizationFreeItemSet(authorizedRights);
|
||||||
|
}
|
||||||
|
|
||||||
|
return (status==errAuthorizationSuccess);
|
||||||
|
}
|
||||||
|
|
||||||
|
- (BOOL)fetchPassword:(NSArray *)theCommands {
|
||||||
|
if (theCommands==nil || [theCommands count]==0)
|
||||||
|
return NO;
|
||||||
|
|
||||||
|
AuthorizationRights rights = [self rightsForCommands:theCommands];
|
||||||
|
OSStatus status = AuthorizationCopyRights(authorization, &rights, kAuthorizationEmptyEnvironment, kAuthorizationFlagInteractionAllowed | kAuthorizationFlagExtendRights, NULL);
|
||||||
|
free(rights.items);
|
||||||
|
|
||||||
|
return (status==errAuthorizationSuccess);
|
||||||
|
}
|
||||||
|
|
||||||
|
- (BOOL)authenticate:(NSArray *)theCommands {
|
||||||
|
if (![self isAuthenticated:theCommands]) {
|
||||||
|
[self fetchPassword:theCommands];
|
||||||
|
}
|
||||||
|
return [self isAuthenticated:theCommands];
|
||||||
|
}
|
||||||
|
|
||||||
|
- (NSString *)executeCommand:(NSString *)thePath withArguments:(NSArray *)theArguments {
|
||||||
|
char *args[30];
|
||||||
|
int status;
|
||||||
|
FILE *pipe = NULL;
|
||||||
|
NSString *output = nil;
|
||||||
|
|
||||||
|
if (![self authenticate:[NSArray arrayWithObject:thePath]])
|
||||||
|
return nil;
|
||||||
|
|
||||||
|
if (theArguments==nil || [theArguments count]==0) {
|
||||||
|
args[0] = NULL;
|
||||||
|
} else {
|
||||||
|
for (int i=0; i<[theArguments count]; i++) {
|
||||||
|
args[i] = (char *)[[theArguments objectAtIndex:i] UTF8String];
|
||||||
|
args[i+1] = NULL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
status = AuthorizationExecuteWithPrivileges(authorization, [thePath UTF8String], kAuthorizationFlagDefaults, args, &pipe);
|
||||||
|
|
||||||
|
NSMutableData *outputData = [NSMutableData data];
|
||||||
|
NSMutableData *tempData = [NSMutableData dataWithLength:512];
|
||||||
|
int len;
|
||||||
|
if (pipe!=NULL) {
|
||||||
|
do {
|
||||||
|
[tempData setLength:512];
|
||||||
|
len = fread([tempData mutableBytes], 1, 512, pipe);
|
||||||
|
if (len>0) {
|
||||||
|
[tempData setLength:len];
|
||||||
|
[outputData appendData:tempData];
|
||||||
|
}
|
||||||
|
} while (len==512);
|
||||||
|
}
|
||||||
|
if ([outputData length]!=0)
|
||||||
|
output = [[[NSString alloc] initWithData:outputData encoding:NSUTF8StringEncoding] autorelease];
|
||||||
|
|
||||||
|
if (status!=0) {
|
||||||
|
NSLog(@"Error %d in AuthorizationExecuteWithPrivileges", status);
|
||||||
|
}
|
||||||
|
|
||||||
|
return output;
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)launchCommand:(NSString *)thePath withArguments:(NSArray *)theArguments {
|
||||||
|
char *args[30];
|
||||||
|
int status;
|
||||||
|
|
||||||
|
if (![self authenticate:[NSArray arrayWithObject:thePath]])
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (theArguments==nil || [theArguments count]==0) {
|
||||||
|
args[0] = NULL;
|
||||||
|
} else {
|
||||||
|
for (int i=0; i<[theArguments count]; i++) {
|
||||||
|
args[i] = (char *)[[theArguments objectAtIndex:i] UTF8String];
|
||||||
|
args[i+1] = NULL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
status = AuthorizationExecuteWithPrivileges(authorization, [thePath UTF8String], kAuthorizationFlagDefaults, args, NULL);
|
||||||
|
|
||||||
|
if (status!=0) {
|
||||||
|
NSLog(@"Error %d in AuthorizationExecuteWithPrivileges", status);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@end
|
123
Classes/YouView/MGMController.h
Normal file
123
Classes/YouView/MGMController.h
Normal file
@ -0,0 +1,123 @@
|
|||||||
|
//
|
||||||
|
// Controller.h
|
||||||
|
// YouView
|
||||||
|
//
|
||||||
|
// Created by Mr. Gecko on 1/17/09.
|
||||||
|
// Copyright (c) 2015 Mr. Gecko's Media (James Coleman). https://mrgeckosmedia.com/ All rights reserved.
|
||||||
|
//
|
||||||
|
|
||||||
|
#import <Cocoa/Cocoa.h>
|
||||||
|
|
||||||
|
#define youviewdebug 1
|
||||||
|
#define releaseDebug 0
|
||||||
|
|
||||||
|
extern NSString * const MGMUserAgent;
|
||||||
|
|
||||||
|
extern NSString * const MGMSafeSearch;
|
||||||
|
extern NSString * const MGMAllowFlaggedVideos;
|
||||||
|
extern NSString * const MGMSalt1;
|
||||||
|
extern NSString * const MGMSalt2;
|
||||||
|
extern NSString * const MGMDonationPath;
|
||||||
|
extern NSString * const MGMRecentSearchesChangeNotification;
|
||||||
|
extern NSString * const MGMRecentSearches;
|
||||||
|
|
||||||
|
extern NSString * const MGMSubscriptionFile;
|
||||||
|
|
||||||
|
extern NSString * const MGMIAuthor;
|
||||||
|
extern NSString * const MGMITitle;
|
||||||
|
extern NSString * const MGMIViews;
|
||||||
|
extern NSString * const MGMIFavorites;
|
||||||
|
extern NSString * const MGMIRating;
|
||||||
|
extern NSString * const MGMIAdded;
|
||||||
|
extern NSString * const MGMITime;
|
||||||
|
extern NSString * const MGMIKeywords;
|
||||||
|
extern NSString * const MGMIDescription;
|
||||||
|
|
||||||
|
extern NSString * const MGMAnimations;
|
||||||
|
extern NSString * const MGMFSFloat;
|
||||||
|
extern NSString * const MGMFSSpaces;
|
||||||
|
extern NSString * const MGMFSSettingsNotification;
|
||||||
|
extern NSString * const MGMPageMax;
|
||||||
|
extern NSString * const MGMOrderBy;
|
||||||
|
extern NSString * const MGMRelevance;
|
||||||
|
extern NSString * const MGMPublished;
|
||||||
|
extern NSString * const MGMViewCount;
|
||||||
|
extern NSString * const MGMRating;
|
||||||
|
extern NSString * const MGMMaxQuality;
|
||||||
|
extern NSString * const MGMWindowMode;
|
||||||
|
|
||||||
|
extern NSString * const MGMYTURL;
|
||||||
|
extern NSString * const MGMYTSDURL;
|
||||||
|
extern NSString * const MGMYTImageURL;
|
||||||
|
|
||||||
|
@class WebView, MGMTaskManager, MGMPlayer, RemoteControlContainer, MultiClickRemoteBehavior, MGMPreferences;
|
||||||
|
|
||||||
|
@interface MGMController : NSObject {
|
||||||
|
//Essentials
|
||||||
|
NSMutableDictionary *subscriptionsDate;
|
||||||
|
//Apple Remote
|
||||||
|
NSTimer *holding;
|
||||||
|
RemoteControlContainer *remoteControl;
|
||||||
|
MultiClickRemoteBehavior *remoteControlBehavior;
|
||||||
|
//About
|
||||||
|
IBOutlet NSWindow *aboutWin;
|
||||||
|
IBOutlet NSTextField *aboutTitle;
|
||||||
|
//Prefences
|
||||||
|
MGMPreferences *preferences;
|
||||||
|
//Sorting
|
||||||
|
IBOutlet NSMenuItem *relevance;
|
||||||
|
IBOutlet NSMenuItem *published;
|
||||||
|
IBOutlet NSMenuItem *viewCount;
|
||||||
|
IBOutlet NSMenuItem *rating;
|
||||||
|
int orderBy;
|
||||||
|
//Open
|
||||||
|
IBOutlet NSWindow *openWindow;
|
||||||
|
IBOutlet NSTextField *openField;
|
||||||
|
IBOutlet NSButton *openButton;
|
||||||
|
NSString *openURL;
|
||||||
|
//Task
|
||||||
|
IBOutlet MGMTaskManager *taskManager;
|
||||||
|
|
||||||
|
NSMutableArray *players;
|
||||||
|
int currPlayer;
|
||||||
|
|
||||||
|
NSMutableArray *recentSearches;
|
||||||
|
NSTimer *systemStatusTimer;
|
||||||
|
|
||||||
|
BOOL openingURL;
|
||||||
|
}
|
||||||
|
- (void)setup;
|
||||||
|
- (NSArray *)recentSearches;
|
||||||
|
- (void)addRecentSearch:(NSString *)theSearch;
|
||||||
|
- (void)clearRecentSearches;
|
||||||
|
- (NSMutableDictionary *)subscriptionsDate;
|
||||||
|
- (NSString *)orderBy;
|
||||||
|
- (int)orderByNum;
|
||||||
|
|
||||||
|
- (IBAction)sourceCode:(id)sender;
|
||||||
|
- (IBAction)donate:(id)sender;
|
||||||
|
|
||||||
|
- (IBAction)installSafari:(id)sender;
|
||||||
|
- (IBAction)installSafariExt:(id)sender;
|
||||||
|
- (IBAction)installChrome:(id)sender;
|
||||||
|
- (IBAction)installFirefox:(id)sender;
|
||||||
|
- (void)holding:(NSTimer *)theTimer;
|
||||||
|
- (void)getUrl:(NSAppleEventDescriptor *)event withReplyEvent:(NSAppleEventDescriptor *)replyEvent;
|
||||||
|
- (IBAction)showOpenPanel:(id)sender;
|
||||||
|
- (IBAction)loop:(id)sender;
|
||||||
|
- (IBAction)openURL:(id)sender;
|
||||||
|
- (void)openVideo:(NSURL *)theVideo;
|
||||||
|
- (void)playerClosed:(MGMPlayer *)player;
|
||||||
|
- (void)playerBecameKey:(MGMPlayer *)player;
|
||||||
|
- (IBAction)goFullScreen:(id)sender;
|
||||||
|
- (IBAction)newWindow:(id)sender;
|
||||||
|
- (IBAction)copyURL:(id)sender;
|
||||||
|
- (IBAction)about:(id)sender;
|
||||||
|
- (IBAction)preferences:(id)sender;
|
||||||
|
- (void)setOrder:(int)order;
|
||||||
|
- (IBAction)setOrderBy:(id)sender;
|
||||||
|
- (void)setIcon:(NSImage *)preview;
|
||||||
|
- (IBAction)saveVideo:(id)sender;
|
||||||
|
|
||||||
|
- (NSURL *)generateAPIURL:(NSString *)path arguments:(NSMutableDictionary *)arguments;
|
||||||
|
@end
|
1124
Classes/YouView/MGMController.m
Normal file
1124
Classes/YouView/MGMController.m
Normal file
File diff suppressed because it is too large
Load Diff
19
Classes/YouView/MGMNotifications.h
Normal file
19
Classes/YouView/MGMNotifications.h
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
//
|
||||||
|
// MGMNotifications.h
|
||||||
|
// YouView
|
||||||
|
//
|
||||||
|
// Created by Mr. Gecko on 11/11/09.
|
||||||
|
// Copyright (c) 2015 Mr. Gecko's Media (James Coleman). https://mrgeckosmedia.com/ All rights reserved.
|
||||||
|
//
|
||||||
|
|
||||||
|
#import <Cocoa/Cocoa.h>
|
||||||
|
|
||||||
|
|
||||||
|
@interface MGMNotifications : NSObject {
|
||||||
|
|
||||||
|
}
|
||||||
|
+ (void)startNotifications;
|
||||||
|
+ (void)stopNotifications;
|
||||||
|
- (void)distributedNotifications:(NSNotification *)notification;
|
||||||
|
- (void)selfNotifications:(NSNotification *)notification;
|
||||||
|
@end
|
47
Classes/YouView/MGMNotifications.m
Normal file
47
Classes/YouView/MGMNotifications.m
Normal file
@ -0,0 +1,47 @@
|
|||||||
|
//
|
||||||
|
// MGMNotifications.m
|
||||||
|
// YouView
|
||||||
|
//
|
||||||
|
// Created by Mr. Gecko on 11/11/09.
|
||||||
|
// Copyright (c) 2015 Mr. Gecko's Media (James Coleman). https://mrgeckosmedia.com/ All rights reserved.
|
||||||
|
//
|
||||||
|
|
||||||
|
#import "MGMNotifications.h"
|
||||||
|
|
||||||
|
static MGMNotifications *MGMNotificationsSingleton = nil;
|
||||||
|
|
||||||
|
@implementation MGMNotifications
|
||||||
|
+ (void)startNotifications {
|
||||||
|
@synchronized(self) {
|
||||||
|
if (MGMNotificationsSingleton == nil) {
|
||||||
|
MGMNotificationsSingleton = [[self alloc] init];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
+ (void)stopNotifications {
|
||||||
|
@synchronized(self) {
|
||||||
|
if (MGMNotificationsSingleton != nil) {
|
||||||
|
[MGMNotificationsSingleton release];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
- (id)init {
|
||||||
|
if (self = [super init]) {
|
||||||
|
[[NSDistributedNotificationCenter defaultCenter] addObserver:self selector:@selector(distributedNotifications:) name:nil object:nil];
|
||||||
|
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(selfNotifications:) name:nil object:nil];
|
||||||
|
}
|
||||||
|
return self;
|
||||||
|
}
|
||||||
|
- (void)dealloc {
|
||||||
|
[[NSDistributedNotificationCenter defaultCenter] removeObserver:self];
|
||||||
|
[[NSNotificationCenter defaultCenter] removeObserver:self];
|
||||||
|
[super dealloc];
|
||||||
|
}
|
||||||
|
- (void)distributedNotifications:(NSNotification *)notification {
|
||||||
|
NSLog(@"Distributed Notification: %@\n%@", [notification name], [notification userInfo]);
|
||||||
|
}
|
||||||
|
- (void)selfNotifications:(NSNotification *)notification {
|
||||||
|
NSLog(@"Notification: %@\n%@", [notification name], [notification userInfo]);
|
||||||
|
}
|
||||||
|
@end
|
25
Classes/YouView/MGMParentalControls.h
Normal file
25
Classes/YouView/MGMParentalControls.h
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
//
|
||||||
|
// MGMParentalControls.h
|
||||||
|
// YouView
|
||||||
|
//
|
||||||
|
// Created by Mr. Gecko on 4/25/09.
|
||||||
|
// Copyright (c) 2015 Mr. Gecko's Media (James Coleman). https://mrgeckosmedia.com/ All rights reserved.
|
||||||
|
//
|
||||||
|
|
||||||
|
#import <Cocoa/Cocoa.h>
|
||||||
|
|
||||||
|
|
||||||
|
@interface MGMParentalControls : NSObject {
|
||||||
|
NSMutableDictionary *parentalControls;
|
||||||
|
NSString *masterKey;
|
||||||
|
}
|
||||||
|
+ (id)standardParentalControls;
|
||||||
|
- (NSString *)path;
|
||||||
|
- (NSData *)encrypt:(NSString *)string withKey:(NSString *)key;
|
||||||
|
- (NSString *)decrypt:(NSData *)encryptedData withKey:(NSString *)key;
|
||||||
|
- (void)setString:(NSString *)object forKey:(NSString *)key;
|
||||||
|
- (NSString *)stringForKey:(NSString *)key;
|
||||||
|
- (void)setBool:(BOOL)object forKey:(NSString *)key;
|
||||||
|
- (BOOL)boolForKey:(NSString *)key;
|
||||||
|
- (void)save;
|
||||||
|
@end
|
188
Classes/YouView/MGMParentalControls.m
Normal file
188
Classes/YouView/MGMParentalControls.m
Normal file
@ -0,0 +1,188 @@
|
|||||||
|
//
|
||||||
|
// MGMParentalControls.m
|
||||||
|
// YouView
|
||||||
|
//
|
||||||
|
// Created by Mr. Gecko on 4/25/09.
|
||||||
|
// Copyright (c) 2015 Mr. Gecko's Media (James Coleman). https://mrgeckosmedia.com/ All rights reserved.
|
||||||
|
//
|
||||||
|
|
||||||
|
#import "MGMParentalControls.h"
|
||||||
|
#import "MGMController.h"
|
||||||
|
#import "MGMAddons.h"
|
||||||
|
#import <MGMUsers/MGMUsers.h>
|
||||||
|
#import <GeckoReporter/GeckoReporter.h>
|
||||||
|
#import <CommonCrypto/CommonDigest.h>
|
||||||
|
#import <openssl/evp.h>
|
||||||
|
#import <openssl/rand.h>
|
||||||
|
#import <openssl/rsa.h>
|
||||||
|
#import <openssl/engine.h>
|
||||||
|
#import <openssl/sha.h>
|
||||||
|
#import <openssl/pem.h>
|
||||||
|
#import <openssl/bio.h>
|
||||||
|
#import <openssl/err.h>
|
||||||
|
#import <openssl/ssl.h>
|
||||||
|
|
||||||
|
NSString * const MGMKey = @"LJlmlj832jfs";
|
||||||
|
NSString * const MGMParentalPath = @".parental.plist";
|
||||||
|
NSString * const MGMMasterKey = @"PCMasterKey";
|
||||||
|
|
||||||
|
@implementation MGMParentalControls
|
||||||
|
+ (id)standardParentalControls {
|
||||||
|
return [[[self alloc] init] autorelease];
|
||||||
|
}
|
||||||
|
- (id)init {
|
||||||
|
if (self = [super init]) {
|
||||||
|
if ([[NSFileManager defaultManager] fileExistsAtPath:[[self path] stringByExpandingTildeInPath]]) {
|
||||||
|
parentalControls = [[NSMutableDictionary dictionaryWithContentsOfFile:[[self path] stringByExpandingTildeInPath]] retain];
|
||||||
|
masterKey = [[self decrypt:[[NSUserDefaults standardUserDefaults] objectForKey:MGMMasterKey] withKey:MGMKey] retain];
|
||||||
|
} else {
|
||||||
|
parentalControls = [NSMutableDictionary new];
|
||||||
|
srandomdev();
|
||||||
|
masterKey = [[[NSString stringWithFormat:@"%d", random()] MD5] retain];
|
||||||
|
[[NSUserDefaults standardUserDefaults] setObject:[self encrypt:masterKey withKey:MGMKey] forKey:MGMMasterKey];
|
||||||
|
[self setBool:NO forKey:MGMAllowFlaggedVideos];
|
||||||
|
[self setString:@"moderate" forKey:MGMSafeSearch];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return self;
|
||||||
|
}
|
||||||
|
- (void)dealloc {
|
||||||
|
#if releaseDebug
|
||||||
|
MGMLog(@"%s Releasing", __PRETTY_FUNCTION__);
|
||||||
|
#endif
|
||||||
|
[parentalControls release];
|
||||||
|
[masterKey release];
|
||||||
|
[super dealloc];
|
||||||
|
}
|
||||||
|
|
||||||
|
- (NSString *)path {
|
||||||
|
return [[MGMUser applicationSupportPath] stringByAppendingPathComponent:MGMParentalPath];
|
||||||
|
}
|
||||||
|
- (NSData *)encrypt:(NSString *)string withKey:(NSString *)key {
|
||||||
|
OpenSSL_add_all_algorithms();
|
||||||
|
NSData *data = [string dataUsingEncoding:NSUTF8StringEncoding];
|
||||||
|
|
||||||
|
unsigned char *input = (unsigned char *)[data bytes];
|
||||||
|
unsigned char *outbuf, iv[EVP_MAX_IV_LENGTH];
|
||||||
|
int outlen, templen, inlen;
|
||||||
|
inlen = [data length];
|
||||||
|
|
||||||
|
if(inlen==0)
|
||||||
|
return nil;
|
||||||
|
|
||||||
|
//Key
|
||||||
|
unsigned char evp_key[EVP_MAX_KEY_LENGTH] = {"\0"};
|
||||||
|
EVP_CIPHER_CTX cCtx;
|
||||||
|
const EVP_CIPHER *cipher;
|
||||||
|
|
||||||
|
cipher = EVP_get_cipherbyname("aes128");
|
||||||
|
if (!cipher){
|
||||||
|
MGMLog(@"cannot get cipher with name aes128");
|
||||||
|
return nil;
|
||||||
|
}
|
||||||
|
|
||||||
|
EVP_BytesToKey(cipher, EVP_md5(), NULL, (unsigned char *)[key UTF8String], [key length], 1, evp_key, iv);
|
||||||
|
EVP_CIPHER_CTX_init(&cCtx);
|
||||||
|
|
||||||
|
if (!EVP_EncryptInit(&cCtx, cipher, evp_key, iv)) {
|
||||||
|
MGMLog(@"EVP_EncryptInit() failed!");
|
||||||
|
EVP_CIPHER_CTX_cleanup(&cCtx);
|
||||||
|
return nil;
|
||||||
|
}
|
||||||
|
EVP_CIPHER_CTX_set_key_length(&cCtx, EVP_MAX_KEY_LENGTH);
|
||||||
|
|
||||||
|
//Encrypt
|
||||||
|
outbuf = (unsigned char *)calloc(inlen + EVP_CIPHER_CTX_block_size(&cCtx), sizeof(unsigned char));
|
||||||
|
NSAssert(outbuf, @"Cannot allocate memory for buffer!");
|
||||||
|
|
||||||
|
if (!EVP_EncryptUpdate(&cCtx, outbuf, &outlen, input, inlen)){
|
||||||
|
MGMLog(@"EVP_EncryptUpdate() failed!");
|
||||||
|
EVP_CIPHER_CTX_cleanup(&cCtx);
|
||||||
|
return nil;
|
||||||
|
}
|
||||||
|
if (!EVP_EncryptFinal(&cCtx, outbuf + outlen, &templen)){
|
||||||
|
MGMLog(@"EVP_EncryptFinal() failed!");
|
||||||
|
EVP_CIPHER_CTX_cleanup(&cCtx);
|
||||||
|
return nil;
|
||||||
|
}
|
||||||
|
outlen += templen;
|
||||||
|
EVP_CIPHER_CTX_cleanup(&cCtx);
|
||||||
|
NSData *returnData = [NSData dataWithBytes:outbuf length:outlen];
|
||||||
|
free(outbuf);
|
||||||
|
return returnData;
|
||||||
|
}
|
||||||
|
|
||||||
|
- (NSString *)decrypt:(NSData *)encryptedData withKey:(NSString *)key {
|
||||||
|
OpenSSL_add_all_algorithms();
|
||||||
|
unsigned char *outbuf, iv[EVP_MAX_IV_LENGTH];
|
||||||
|
int outlen, templen, inlen;
|
||||||
|
inlen = [encryptedData length];
|
||||||
|
unsigned char *input = (unsigned char *)[encryptedData bytes];
|
||||||
|
|
||||||
|
unsigned char evp_key[EVP_MAX_KEY_LENGTH] = {"\0"};
|
||||||
|
EVP_CIPHER_CTX cCtx;
|
||||||
|
const EVP_CIPHER *cipher;
|
||||||
|
|
||||||
|
cipher = EVP_get_cipherbyname("aes128");
|
||||||
|
if(!cipher)
|
||||||
|
{
|
||||||
|
MGMLog(@"cannot get cipher with name aes128");
|
||||||
|
return nil;
|
||||||
|
}
|
||||||
|
|
||||||
|
EVP_BytesToKey(cipher, EVP_md5(), NULL, (unsigned char *)[key UTF8String], [key length], 1, evp_key, iv);
|
||||||
|
EVP_CIPHER_CTX_init(&cCtx);
|
||||||
|
|
||||||
|
if (!EVP_DecryptInit(&cCtx, cipher, evp_key, iv)) {
|
||||||
|
MGMLog(@"EVP_DecryptInit() failed!");
|
||||||
|
EVP_CIPHER_CTX_cleanup(&cCtx);
|
||||||
|
return nil;
|
||||||
|
}
|
||||||
|
EVP_CIPHER_CTX_set_key_length(&cCtx, EVP_MAX_KEY_LENGTH);
|
||||||
|
|
||||||
|
outbuf = (unsigned char *)calloc(inlen+32, sizeof(unsigned char));
|
||||||
|
NSAssert(outbuf, @"Cannot allocate memory for buffer!");
|
||||||
|
|
||||||
|
//Decrypt
|
||||||
|
if (!EVP_DecryptUpdate(&cCtx, outbuf, &outlen, input, inlen)){
|
||||||
|
MGMLog(@"EVP_DecryptUpdate() failed!");
|
||||||
|
EVP_CIPHER_CTX_cleanup(&cCtx);
|
||||||
|
return nil;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!EVP_DecryptFinal(&cCtx, outbuf + outlen, &templen)){
|
||||||
|
MGMLog(@"EVP_DecryptFinal() failed!");
|
||||||
|
EVP_CIPHER_CTX_cleanup(&cCtx);
|
||||||
|
return nil;
|
||||||
|
}
|
||||||
|
|
||||||
|
outlen += templen;
|
||||||
|
EVP_CIPHER_CTX_cleanup(&cCtx);
|
||||||
|
NSString *returnString = [[[NSString alloc] initWithData:[NSData dataWithBytes:outbuf length:outlen] encoding:NSUTF8StringEncoding] autorelease];
|
||||||
|
free(outbuf);
|
||||||
|
return returnString;
|
||||||
|
}
|
||||||
|
- (void)setString:(NSString *)object forKey:(NSString *)key {
|
||||||
|
[parentalControls setObject:[self encrypt:object withKey:masterKey] forKey:key];
|
||||||
|
[self save];
|
||||||
|
}
|
||||||
|
- (NSString *)stringForKey:(NSString *)key {
|
||||||
|
if ([parentalControls objectForKey:key]!=nil)
|
||||||
|
return [self decrypt:[parentalControls objectForKey:key] withKey:masterKey];
|
||||||
|
return nil;
|
||||||
|
}
|
||||||
|
- (void)setBool:(BOOL)object forKey:(NSString *)key {
|
||||||
|
if (object)
|
||||||
|
[parentalControls setObject:[self encrypt:@"YES" withKey:masterKey] forKey:key];
|
||||||
|
else
|
||||||
|
[parentalControls setObject:[self encrypt:@"NO" withKey:masterKey] forKey:key];
|
||||||
|
[self save];
|
||||||
|
}
|
||||||
|
- (BOOL)boolForKey:(NSString *)key {
|
||||||
|
return [[self decrypt:[parentalControls objectForKey:key] withKey:masterKey] isEqual:@"YES"];
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)save {
|
||||||
|
[parentalControls writeToFile:[self path] atomically:YES];
|
||||||
|
}
|
||||||
|
@end
|
45
Classes/YouView/MGMVideoFinder.h
Normal file
45
Classes/YouView/MGMVideoFinder.h
Normal file
@ -0,0 +1,45 @@
|
|||||||
|
//
|
||||||
|
// MGMVideoFinder.h
|
||||||
|
// YouView
|
||||||
|
//
|
||||||
|
// Created by Mr. Gecko on 8/3/10.
|
||||||
|
// Copyright (c) 2015 Mr. Gecko's Media (James Coleman). https://mrgeckosmedia.com/ All rights reserved.
|
||||||
|
//
|
||||||
|
|
||||||
|
#import <Cocoa/Cocoa.h>
|
||||||
|
|
||||||
|
@protocol MGMVideoFinderDelegate
|
||||||
|
- (void)loadFlash:(NSDictionary *)info;
|
||||||
|
- (void)loadVideo:(NSDictionary *)info;
|
||||||
|
@end
|
||||||
|
|
||||||
|
extern NSString * const MGMVLVideoURLKey;
|
||||||
|
extern NSString * const MGMVLAudioURLKey;
|
||||||
|
extern NSString * const MGMVLURLKey;
|
||||||
|
extern NSString * const MGMVLTitle;
|
||||||
|
extern NSString * const MGMVLVersion;
|
||||||
|
|
||||||
|
@class MGMURLConnectionManager;
|
||||||
|
|
||||||
|
@interface MGMVideoFinder : NSObject {
|
||||||
|
id<MGMVideoFinderDelegate> delegate;
|
||||||
|
int maxQuality;
|
||||||
|
NSURL *URL;
|
||||||
|
MGMURLConnectionManager *connectionManager;
|
||||||
|
NSMutableArray *videoURLS;
|
||||||
|
NSString *title;
|
||||||
|
NSString *version;
|
||||||
|
BOOL requestingSD;
|
||||||
|
|
||||||
|
NSMutableDictionary *videoQualities;
|
||||||
|
}
|
||||||
|
- (id)initWithURL:(NSURL *)theURL connectionManager:(MGMURLConnectionManager *)theConnectionManager maxQuality:(int)theMaxQuality delegate:(id)theDelegate;
|
||||||
|
- (void)setTitle:(NSString *)theTitle;
|
||||||
|
- (NSString *)title;
|
||||||
|
- (void)setVersion:(NSString *)theVersion;
|
||||||
|
- (NSString *)version;
|
||||||
|
- (void)startVideo;
|
||||||
|
- (void)loadSD;
|
||||||
|
- (void)shouldLoadFlash;
|
||||||
|
- (void)loadFlash;
|
||||||
|
@end
|
547
Classes/YouView/MGMVideoFinder.m
Normal file
547
Classes/YouView/MGMVideoFinder.m
Normal file
@ -0,0 +1,547 @@
|
|||||||
|
//
|
||||||
|
// MGMVideoFinder.m
|
||||||
|
// YouView
|
||||||
|
//
|
||||||
|
// Created by Mr. Gecko on 8/3/10.
|
||||||
|
// Copyright (c) 2015 Mr. Gecko's Media (James Coleman). https://mrgeckosmedia.com/ All rights reserved.
|
||||||
|
//
|
||||||
|
|
||||||
|
#import "MGMVideoFinder.h"
|
||||||
|
#import "MGMController.h"
|
||||||
|
#import "MGMAddons.h"
|
||||||
|
#import "MGMParentalControls.h"
|
||||||
|
#import <MGMUsers/MGMUsers.h>
|
||||||
|
#import <GeckoReporter/GeckoReporter.h>
|
||||||
|
|
||||||
|
NSString * const MGMVLVideoURLKey = @"videoURL";
|
||||||
|
NSString * const MGMVLAudioURLKey = @"audioURL";
|
||||||
|
NSString * const MGMVLURLKey = @"URL";
|
||||||
|
NSString * const MGMVLTitle = @"title";
|
||||||
|
NSString * const MGMVLVersion = @"version";
|
||||||
|
|
||||||
|
NSString * const MGMULType = @"type";
|
||||||
|
NSString * const MGMULTag = @"itag";
|
||||||
|
NSString * const MGMULURL = @"url";
|
||||||
|
|
||||||
|
NSString * const MGMVTType = @"type";
|
||||||
|
NSString * const MGMVTQuality = @"quality";
|
||||||
|
NSString * const MGMVTAudio = @"audio";
|
||||||
|
NSString * const MGMVTVideo = @"video";
|
||||||
|
NSString * const MGMVT3D = @"3D";
|
||||||
|
|
||||||
|
@implementation MGMVideoFinder
|
||||||
|
- (id)initWithURL:(NSURL *)theURL connectionManager:(MGMURLConnectionManager *)theConnectionManager maxQuality:(int)theMaxQuality delegate:(id)theDelegate {
|
||||||
|
if (self = [super init]) {
|
||||||
|
if (theURL==nil || theDelegate==nil) {
|
||||||
|
[self release];
|
||||||
|
self = nil;
|
||||||
|
} else {
|
||||||
|
maxQuality = theMaxQuality;
|
||||||
|
requestingSD = NO;
|
||||||
|
videoURLS = [NSMutableArray new];
|
||||||
|
URL = [theURL retain];
|
||||||
|
delegate = theDelegate;
|
||||||
|
if (theConnectionManager!=nil) {
|
||||||
|
connectionManager = [theConnectionManager retain];
|
||||||
|
} else {
|
||||||
|
connectionManager = [[MGMURLConnectionManager manager] retain];
|
||||||
|
[connectionManager setUserAgent:MGMUserAgent];
|
||||||
|
}
|
||||||
|
#if youviewdebug
|
||||||
|
NSLog(@"Finding video for %@", theURL);
|
||||||
|
#endif
|
||||||
|
NSURLRequest *theRequest = [NSURLRequest requestWithURL:theURL cachePolicy:NSURLRequestUseProtocolCachePolicy timeoutInterval:60.0];
|
||||||
|
MGMURLBasicHandler *handler = [MGMURLBasicHandler handlerWithRequest:theRequest delegate:self];
|
||||||
|
[connectionManager addHandler:handler];
|
||||||
|
|
||||||
|
|
||||||
|
videoQualities = [NSMutableDictionary new];
|
||||||
|
[videoQualities setObject:[NSDictionary dictionaryWithObjectsAndKeys:@"3GP", MGMVTType, @"144p", MGMVTQuality, [NSNumber numberWithBool:YES], MGMVTAudio, [NSNumber numberWithBool:YES], MGMVTVideo, [NSNumber numberWithBool:NO], MGMVT3D, nil] forKey:@"13"];
|
||||||
|
[videoQualities setObject:[NSDictionary dictionaryWithObjectsAndKeys:@"3GP", MGMVTType, @"144p", MGMVTQuality, [NSNumber numberWithBool:YES], MGMVTAudio, [NSNumber numberWithBool:YES], MGMVTVideo, [NSNumber numberWithBool:NO], MGMVT3D, nil] forKey:@"17"];
|
||||||
|
[videoQualities setObject:[NSDictionary dictionaryWithObjectsAndKeys:@"3GP", MGMVTType, @"240p", MGMVTQuality, [NSNumber numberWithBool:YES], MGMVTAudio, [NSNumber numberWithBool:YES], MGMVTVideo, [NSNumber numberWithBool:NO], MGMVT3D, nil] forKey:@"36"];
|
||||||
|
[videoQualities setObject:[NSDictionary dictionaryWithObjectsAndKeys:@"FLV", MGMVTType, @"240p", MGMVTQuality, [NSNumber numberWithBool:YES], MGMVTAudio, [NSNumber numberWithBool:YES], MGMVTVideo, [NSNumber numberWithBool:NO], MGMVT3D, nil] forKey:@"5"];
|
||||||
|
[videoQualities setObject:[NSDictionary dictionaryWithObjectsAndKeys:@"FLV", MGMVTType, @"270p", MGMVTQuality, [NSNumber numberWithBool:YES], MGMVTAudio, [NSNumber numberWithBool:YES], MGMVTVideo, [NSNumber numberWithBool:NO], MGMVT3D, nil] forKey:@"6"];
|
||||||
|
[videoQualities setObject:[NSDictionary dictionaryWithObjectsAndKeys:@"FLV", MGMVTType, @"360p", MGMVTQuality, [NSNumber numberWithBool:YES], MGMVTAudio, [NSNumber numberWithBool:YES], MGMVTVideo, [NSNumber numberWithBool:NO], MGMVT3D, nil] forKey:@"34"];
|
||||||
|
[videoQualities setObject:[NSDictionary dictionaryWithObjectsAndKeys:@"FLV", MGMVTType, @"480p", MGMVTQuality, [NSNumber numberWithBool:YES], MGMVTAudio, [NSNumber numberWithBool:YES], MGMVTVideo, [NSNumber numberWithBool:NO], MGMVT3D, nil] forKey:@"35"];
|
||||||
|
[videoQualities setObject:[NSDictionary dictionaryWithObjectsAndKeys:@"MP4", MGMVTType, @"480p", MGMVTQuality, [NSNumber numberWithBool:YES], MGMVTAudio, [NSNumber numberWithBool:YES], MGMVTVideo, [NSNumber numberWithBool:NO], MGMVT3D, nil] forKey:@"18"];
|
||||||
|
[videoQualities setObject:[NSDictionary dictionaryWithObjectsAndKeys:@"MP4", MGMVTType, @"720p", MGMVTQuality, [NSNumber numberWithBool:YES], MGMVTAudio, [NSNumber numberWithBool:YES], MGMVTVideo, [NSNumber numberWithBool:NO], MGMVT3D, nil] forKey:@"22"];
|
||||||
|
[videoQualities setObject:[NSDictionary dictionaryWithObjectsAndKeys:@"MP4", MGMVTType, @"1080p", MGMVTQuality, [NSNumber numberWithBool:YES], MGMVTAudio, [NSNumber numberWithBool:YES], MGMVTVideo, [NSNumber numberWithBool:NO], MGMVT3D, nil] forKey:@"37"];
|
||||||
|
[videoQualities setObject:[NSDictionary dictionaryWithObjectsAndKeys:@"MP4", MGMVTType, @"2160p", MGMVTQuality, [NSNumber numberWithBool:YES], MGMVTAudio, [NSNumber numberWithBool:YES], MGMVTVideo, [NSNumber numberWithBool:NO], MGMVT3D, nil] forKey:@"38"];
|
||||||
|
[videoQualities setObject:[NSDictionary dictionaryWithObjectsAndKeys:@"MP4", MGMVTType, @"144p", MGMVTQuality, [NSNumber numberWithBool:NO], MGMVTAudio, [NSNumber numberWithBool:YES], MGMVTVideo, [NSNumber numberWithBool:NO], MGMVT3D, nil] forKey:@"160"];
|
||||||
|
[videoQualities setObject:[NSDictionary dictionaryWithObjectsAndKeys:@"MP4", MGMVTType, @"240p", MGMVTQuality, [NSNumber numberWithBool:NO], MGMVTAudio, [NSNumber numberWithBool:YES], MGMVTVideo, [NSNumber numberWithBool:NO], MGMVT3D, nil] forKey:@"133"];
|
||||||
|
[videoQualities setObject:[NSDictionary dictionaryWithObjectsAndKeys:@"MP4", MGMVTType, @"360p", MGMVTQuality, [NSNumber numberWithBool:NO], MGMVTAudio, [NSNumber numberWithBool:YES], MGMVTVideo, [NSNumber numberWithBool:NO], MGMVT3D, nil] forKey:@"134"];
|
||||||
|
[videoQualities setObject:[NSDictionary dictionaryWithObjectsAndKeys:@"MP4", MGMVTType, @"480p", MGMVTQuality, [NSNumber numberWithBool:NO], MGMVTAudio, [NSNumber numberWithBool:YES], MGMVTVideo, [NSNumber numberWithBool:NO], MGMVT3D, nil] forKey:@"135"];
|
||||||
|
[videoQualities setObject:[NSDictionary dictionaryWithObjectsAndKeys:@"MP4", MGMVTType, @"720p", MGMVTQuality, [NSNumber numberWithBool:NO], MGMVTAudio, [NSNumber numberWithBool:YES], MGMVTVideo, [NSNumber numberWithBool:NO], MGMVT3D, nil] forKey:@"136"];
|
||||||
|
[videoQualities setObject:[NSDictionary dictionaryWithObjectsAndKeys:@"MP4", MGMVTType, @"1080p", MGMVTQuality, [NSNumber numberWithBool:NO], MGMVTAudio, [NSNumber numberWithBool:YES], MGMVTVideo, [NSNumber numberWithBool:NO], MGMVT3D, nil] forKey:@"137"];
|
||||||
|
[videoQualities setObject:[NSDictionary dictionaryWithObjectsAndKeys:@"MP4", MGMVTType, @"1440p", MGMVTQuality, [NSNumber numberWithBool:NO], MGMVTAudio, [NSNumber numberWithBool:YES], MGMVTVideo, [NSNumber numberWithBool:NO], MGMVT3D, nil] forKey:@"264"];
|
||||||
|
[videoQualities setObject:[NSDictionary dictionaryWithObjectsAndKeys:@"MP4", MGMVTType, @"2160p", MGMVTQuality, [NSNumber numberWithBool:NO], MGMVTAudio, [NSNumber numberWithBool:YES], MGMVTVideo, [NSNumber numberWithBool:NO], MGMVT3D, nil] forKey:@"138"];
|
||||||
|
[videoQualities setObject:[NSDictionary dictionaryWithObjectsAndKeys:@"M4A", MGMVTType, @"48Kbps", MGMVTQuality, [NSNumber numberWithBool:YES], MGMVTAudio, [NSNumber numberWithBool:NO], MGMVTVideo, [NSNumber numberWithBool:NO], MGMVT3D, nil] forKey:@"139"];
|
||||||
|
[videoQualities setObject:[NSDictionary dictionaryWithObjectsAndKeys:@"M4A", MGMVTType, @"128kbps", MGMVTQuality, [NSNumber numberWithBool:YES], MGMVTAudio, [NSNumber numberWithBool:NO], MGMVTVideo, [NSNumber numberWithBool:NO], MGMVT3D, nil] forKey:@"140"];
|
||||||
|
[videoQualities setObject:[NSDictionary dictionaryWithObjectsAndKeys:@"M4A", MGMVTType, @"256kbps", MGMVTQuality, [NSNumber numberWithBool:YES], MGMVTAudio, [NSNumber numberWithBool:NO], MGMVTVideo, [NSNumber numberWithBool:NO], MGMVT3D, nil] forKey:@"141"];
|
||||||
|
[videoQualities setObject:[NSDictionary dictionaryWithObjectsAndKeys:@"WEBM", MGMVTType, @"128kbps", MGMVTQuality, [NSNumber numberWithBool:YES], MGMVTAudio, [NSNumber numberWithBool:NO], MGMVTVideo, [NSNumber numberWithBool:NO], MGMVT3D, nil] forKey:@"171"];
|
||||||
|
[videoQualities setObject:[NSDictionary dictionaryWithObjectsAndKeys:@"WEBM", MGMVTType, @"192kbps", MGMVTQuality, [NSNumber numberWithBool:YES], MGMVTAudio, [NSNumber numberWithBool:NO], MGMVTVideo, [NSNumber numberWithBool:NO], MGMVT3D, nil] forKey:@"172"];
|
||||||
|
[videoQualities setObject:[NSDictionary dictionaryWithObjectsAndKeys:@"MP4", MGMVTType, @"240p", MGMVTQuality, [NSNumber numberWithBool:YES], MGMVTAudio, [NSNumber numberWithBool:YES], MGMVTVideo, [NSNumber numberWithBool:YES], MGMVT3D, nil] forKey:@"83"];
|
||||||
|
[videoQualities setObject:[NSDictionary dictionaryWithObjectsAndKeys:@"MP4", MGMVTType, @"360p", MGMVTQuality, [NSNumber numberWithBool:YES], MGMVTAudio, [NSNumber numberWithBool:YES], MGMVTVideo, [NSNumber numberWithBool:YES], MGMVT3D, nil] forKey:@"82"];
|
||||||
|
[videoQualities setObject:[NSDictionary dictionaryWithObjectsAndKeys:@"MP4", MGMVTType, @"520p", MGMVTQuality, [NSNumber numberWithBool:YES], MGMVTAudio, [NSNumber numberWithBool:YES], MGMVTVideo, [NSNumber numberWithBool:YES], MGMVT3D, nil] forKey:@"85"];
|
||||||
|
[videoQualities setObject:[NSDictionary dictionaryWithObjectsAndKeys:@"MP4", MGMVTType, @"720p", MGMVTQuality, [NSNumber numberWithBool:YES], MGMVTAudio, [NSNumber numberWithBool:YES], MGMVTVideo, [NSNumber numberWithBool:YES], MGMVT3D, nil] forKey:@"84"];
|
||||||
|
[videoQualities setObject:[NSDictionary dictionaryWithObjectsAndKeys:@"WEBM", MGMVTType, @"360p", MGMVTQuality, [NSNumber numberWithBool:YES], MGMVTAudio, [NSNumber numberWithBool:YES], MGMVTVideo, [NSNumber numberWithBool:NO], MGMVT3D, nil] forKey:@"43"];
|
||||||
|
[videoQualities setObject:[NSDictionary dictionaryWithObjectsAndKeys:@"WEBM", MGMVTType, @"480p", MGMVTQuality, [NSNumber numberWithBool:YES], MGMVTAudio, [NSNumber numberWithBool:YES], MGMVTVideo, [NSNumber numberWithBool:NO], MGMVT3D, nil] forKey:@"44"];
|
||||||
|
[videoQualities setObject:[NSDictionary dictionaryWithObjectsAndKeys:@"WEBM", MGMVTType, @"720p", MGMVTQuality, [NSNumber numberWithBool:YES], MGMVTAudio, [NSNumber numberWithBool:YES], MGMVTVideo, [NSNumber numberWithBool:NO], MGMVT3D, nil] forKey:@"45"];
|
||||||
|
[videoQualities setObject:[NSDictionary dictionaryWithObjectsAndKeys:@"WEBM", MGMVTType, @"1080p", MGMVTQuality, [NSNumber numberWithBool:YES], MGMVTAudio, [NSNumber numberWithBool:YES], MGMVTVideo, [NSNumber numberWithBool:NO], MGMVT3D, nil] forKey:@"46"];
|
||||||
|
[videoQualities setObject:[NSDictionary dictionaryWithObjectsAndKeys:@"WEBM", MGMVTType, @"240p", MGMVTQuality, [NSNumber numberWithBool:NO], MGMVTAudio, [NSNumber numberWithBool:YES], MGMVTVideo, [NSNumber numberWithBool:NO], MGMVT3D, nil] forKey:@"242"];
|
||||||
|
[videoQualities setObject:[NSDictionary dictionaryWithObjectsAndKeys:@"WEBM", MGMVTType, @"360p", MGMVTQuality, [NSNumber numberWithBool:NO], MGMVTAudio, [NSNumber numberWithBool:YES], MGMVTVideo, [NSNumber numberWithBool:NO], MGMVT3D, nil] forKey:@"243"];
|
||||||
|
[videoQualities setObject:[NSDictionary dictionaryWithObjectsAndKeys:@"WEBM", MGMVTType, @"480p 64kbps", MGMVTQuality, [NSNumber numberWithBool:NO], MGMVTAudio, [NSNumber numberWithBool:YES], MGMVTVideo, [NSNumber numberWithBool:NO], MGMVT3D, nil] forKey:@"244"];
|
||||||
|
[videoQualities setObject:[NSDictionary dictionaryWithObjectsAndKeys:@"WEBM", MGMVTType, @"480p 110kbps", MGMVTQuality, [NSNumber numberWithBool:NO], MGMVTAudio, [NSNumber numberWithBool:YES], MGMVTVideo, [NSNumber numberWithBool:NO], MGMVT3D, nil] forKey:@"245"];
|
||||||
|
[videoQualities setObject:[NSDictionary dictionaryWithObjectsAndKeys:@"WEBM", MGMVTType, @"480p 210kbps", MGMVTQuality, [NSNumber numberWithBool:NO], MGMVTAudio, [NSNumber numberWithBool:YES], MGMVTVideo, [NSNumber numberWithBool:NO], MGMVT3D, nil] forKey:@"246"];
|
||||||
|
[videoQualities setObject:[NSDictionary dictionaryWithObjectsAndKeys:@"WEBM", MGMVTType, @"720p", MGMVTQuality, [NSNumber numberWithBool:NO], MGMVTAudio, [NSNumber numberWithBool:YES], MGMVTVideo, [NSNumber numberWithBool:NO], MGMVT3D, nil] forKey:@"247"];
|
||||||
|
[videoQualities setObject:[NSDictionary dictionaryWithObjectsAndKeys:@"WEBM", MGMVTType, @"1080p", MGMVTQuality, [NSNumber numberWithBool:NO], MGMVTAudio, [NSNumber numberWithBool:YES], MGMVTVideo, [NSNumber numberWithBool:NO], MGMVT3D, nil] forKey:@"248"];
|
||||||
|
[videoQualities setObject:[NSDictionary dictionaryWithObjectsAndKeys:@"WEBM", MGMVTType, @"1440p", MGMVTQuality, [NSNumber numberWithBool:NO], MGMVTAudio, [NSNumber numberWithBool:YES], MGMVTVideo, [NSNumber numberWithBool:NO], MGMVT3D, nil] forKey:@"271"];
|
||||||
|
[videoQualities setObject:[NSDictionary dictionaryWithObjectsAndKeys:@"WEBM", MGMVTType, @"2160p", MGMVTQuality, [NSNumber numberWithBool:NO], MGMVTAudio, [NSNumber numberWithBool:YES], MGMVTVideo, [NSNumber numberWithBool:NO], MGMVT3D, nil] forKey:@"272"];
|
||||||
|
[videoQualities setObject:[NSDictionary dictionaryWithObjectsAndKeys:@"WEBM", MGMVTType, @"360p", MGMVTQuality, [NSNumber numberWithBool:YES], MGMVTAudio, [NSNumber numberWithBool:YES], MGMVTVideo, [NSNumber numberWithBool:YES], MGMVT3D, nil] forKey:@"100"];
|
||||||
|
[videoQualities setObject:[NSDictionary dictionaryWithObjectsAndKeys:@"WEBM", MGMVTType, @"360p", MGMVTQuality, [NSNumber numberWithBool:YES], MGMVTAudio, [NSNumber numberWithBool:YES], MGMVTVideo, [NSNumber numberWithBool:YES], MGMVT3D, nil] forKey:@"101"];
|
||||||
|
[videoQualities setObject:[NSDictionary dictionaryWithObjectsAndKeys:@"WEBM", MGMVTType, @"720p", MGMVTQuality, [NSNumber numberWithBool:YES], MGMVTAudio, [NSNumber numberWithBool:YES], MGMVTVideo, [NSNumber numberWithBool:YES], MGMVT3D, nil] forKey:@"102"];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return self;
|
||||||
|
}
|
||||||
|
- (void)dealloc {
|
||||||
|
[URL release];
|
||||||
|
[connectionManager cancelAll];
|
||||||
|
[connectionManager release];
|
||||||
|
[videoURLS release];
|
||||||
|
[videoQualities release];
|
||||||
|
[title release];
|
||||||
|
[version release];
|
||||||
|
[super dealloc];
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)processURLs:(NSArray *)urls {
|
||||||
|
for (int i=0; i<[urls count]; i++) {
|
||||||
|
NSString *type = [[urls objectAtIndex:i] URLParameterWithName:MGMULType];
|
||||||
|
NSString *itag = [[urls objectAtIndex:i] URLParameterWithName:MGMULTag];
|
||||||
|
NSString *videoURL = [[urls objectAtIndex:i] URLParameterWithName:MGMULURL];
|
||||||
|
NSString *signature = [[urls objectAtIndex:i] URLParameterWithName:@"sig"];
|
||||||
|
if (signature==nil)
|
||||||
|
signature = [[urls objectAtIndex:i] URLParameterWithName:@"s"];
|
||||||
|
if (signature==nil)
|
||||||
|
signature = [videoURL URLParameterWithName:@"signature"];
|
||||||
|
if ([signature length]==86) {
|
||||||
|
NSMutableString *newSignature = [NSMutableString string];
|
||||||
|
NSArray *signatureBreakDown = [signature componentsSeparatedByString:@"."];
|
||||||
|
if ([signatureBreakDown count]>=2 && [[signatureBreakDown objectAtIndex:0] length]==42 && [[signatureBreakDown objectAtIndex:1] length]==43) {
|
||||||
|
[newSignature appendFormat:@"%@.", [[signatureBreakDown objectAtIndex:0] substringFromIndex:2]];
|
||||||
|
[newSignature appendString:[[signatureBreakDown objectAtIndex:1] substringToIndex:20]];
|
||||||
|
[newSignature appendString:[[signatureBreakDown objectAtIndex:1] substringWithRange:NSMakeRange(39, 1)]];
|
||||||
|
[newSignature appendString:[[signatureBreakDown objectAtIndex:1] substringWithRange:NSMakeRange(21, 18)]];
|
||||||
|
[newSignature appendString:[[signatureBreakDown objectAtIndex:1] substringWithRange:NSMakeRange(20, 1)]];
|
||||||
|
} else if ([signatureBreakDown count]>=2 && [[signatureBreakDown objectAtIndex:0] length]==43 && [[signatureBreakDown objectAtIndex:1] length]==42) {
|
||||||
|
[newSignature appendFormat:@"%@.", [[signatureBreakDown objectAtIndex:0] substringFromIndex:3]];
|
||||||
|
[newSignature appendString:[[signatureBreakDown objectAtIndex:1] substringToIndex:[[signatureBreakDown objectAtIndex:1] length]-2]];
|
||||||
|
}
|
||||||
|
if ([newSignature length]!=0) {
|
||||||
|
signature = newSignature;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
NSURL *url = [NSURL URLWithString:[NSString stringWithFormat:@"%@&signature=%@", videoURL, signature]];
|
||||||
|
[videoURLS addObject:[NSDictionary dictionaryWithObjectsAndKeys:type, MGMULType, itag, MGMULTag, url, MGMULURL, nil]];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)handler:(MGMURLBasicHandler *)theHandler didFailWithError:(NSError *)theError {
|
||||||
|
[connectionManager release];
|
||||||
|
connectionManager = nil;
|
||||||
|
NSLog(@"Error Getting Video: %@", theError);
|
||||||
|
NSAlert *theAlert = [[NSAlert new] autorelease];
|
||||||
|
[theAlert addButtonWithTitle:@"Ok"];
|
||||||
|
[theAlert setMessageText:@"Error while finding video"];
|
||||||
|
[theAlert setInformativeText:[theError localizedDescription]];
|
||||||
|
[theAlert setAlertStyle:2];
|
||||||
|
[theAlert runModal];
|
||||||
|
}
|
||||||
|
- (void)handlerDidFinish:(MGMURLBasicHandler *)theHandler {
|
||||||
|
NSString *receivedString = [theHandler string];
|
||||||
|
//NSLog(@"%@", theHandler);
|
||||||
|
//NSLog(@"%@", receivedString);
|
||||||
|
|
||||||
|
if (![[[theHandler response] MIMEType] containsString:@"text/html"]) {
|
||||||
|
NSLog(@"Hello from special loader.");
|
||||||
|
NSString *urlMap = nil;
|
||||||
|
NSRange range = [receivedString rangeOfString:@"url_encoded_fmt_stream_map\": \""];
|
||||||
|
if (range.location!=NSNotFound) {
|
||||||
|
NSString *s = [receivedString substringFromIndex:range.location + range.length];
|
||||||
|
|
||||||
|
range = [s rangeOfString:@"\""];
|
||||||
|
if (range.location!=NSNotFound)
|
||||||
|
urlMap = [s substringFromIndex:range.location + range.length];
|
||||||
|
}
|
||||||
|
if (urlMap==nil) {
|
||||||
|
NSRange range = [receivedString rangeOfString:@"url_encoded_fmt_stream_map': '"];
|
||||||
|
if (range.location!=NSNotFound) {
|
||||||
|
NSString *s = [receivedString substringFromIndex:range.location + range.length];
|
||||||
|
|
||||||
|
range = [s rangeOfString:@"'"];
|
||||||
|
if (range.location!=NSNotFound)
|
||||||
|
urlMap = [s substringFromIndex:range.location + range.length];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (urlMap==nil) {
|
||||||
|
NSDictionary *parameters = [receivedString URLParameters];
|
||||||
|
NSArray *keys = [parameters allKeys];
|
||||||
|
for (int i=0; i<[keys count]; i++) {
|
||||||
|
if ([[keys objectAtIndex:i] containsString:@"url_encoded_fmt_stream_map"]) {
|
||||||
|
urlMap = [parameters objectForKey:[keys objectAtIndex:i]];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
NSString *adaptiveMap = nil;
|
||||||
|
range = [receivedString rangeOfString:@"adaptive_fmts\": \""];
|
||||||
|
if (range.location!=NSNotFound) {
|
||||||
|
NSString *s = [receivedString substringFromIndex:range.location + range.length];
|
||||||
|
|
||||||
|
range = [s rangeOfString:@"\""];
|
||||||
|
if (range.location!=NSNotFound)
|
||||||
|
adaptiveMap = [s substringFromIndex:range.location + range.length];
|
||||||
|
}
|
||||||
|
if (adaptiveMap==nil) {
|
||||||
|
NSRange range = [receivedString rangeOfString:@"adaptive_fmts': '"];
|
||||||
|
if (range.location!=NSNotFound) {
|
||||||
|
NSString *s = [receivedString substringFromIndex:range.location + range.length];
|
||||||
|
|
||||||
|
range = [s rangeOfString:@"'"];
|
||||||
|
if (range.location!=NSNotFound)
|
||||||
|
adaptiveMap = [s substringFromIndex:range.location + range.length];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (adaptiveMap==nil) {
|
||||||
|
NSDictionary *parameters = [receivedString URLParameters];
|
||||||
|
NSArray *keys = [parameters allKeys];
|
||||||
|
for (int i=0; i<[keys count]; i++) {
|
||||||
|
if ([[keys objectAtIndex:i] containsString:@"adaptive_fmts"]) {
|
||||||
|
adaptiveMap = [parameters objectForKey:[keys objectAtIndex:i]];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (urlMap!=nil) {
|
||||||
|
NSArray *urls = [urlMap componentsSeparatedByString:@","];
|
||||||
|
[self processURLs:urls];
|
||||||
|
NSArray *adaptiveURLs = [adaptiveMap componentsSeparatedByString:@","];
|
||||||
|
[self processURLs:adaptiveURLs];
|
||||||
|
} else {
|
||||||
|
NSLog(@"Unable to find url map.");
|
||||||
|
}
|
||||||
|
#if youviewdebug
|
||||||
|
NSLog(@"%@", videoURLS);
|
||||||
|
#endif
|
||||||
|
[self startVideo];
|
||||||
|
} else if (![receivedString containsString:@"verify-age"]) {
|
||||||
|
NSRange range = NSMakeRange(0, [receivedString length]);
|
||||||
|
id config = nil;
|
||||||
|
NSAutoreleasePool *pool = [NSAutoreleasePool new];
|
||||||
|
while (range.length>1) {
|
||||||
|
NSRange configRange = [receivedString rangeOfString:@"ytplayer.config = " options:NSCaseInsensitiveSearch range:range];
|
||||||
|
if (configRange.location!=NSNotFound) {
|
||||||
|
range.location = configRange.location+configRange.length;
|
||||||
|
range.length = [receivedString length]-range.location;
|
||||||
|
NSRange endRange = [receivedString rangeOfString:@"};" options:NSCaseInsensitiveSearch range:range];
|
||||||
|
if (endRange.location==NSNotFound)
|
||||||
|
break;
|
||||||
|
NSString *configString = [receivedString substringWithRange:NSMakeRange(range.location, (endRange.location+endRange.length)-range.location)];
|
||||||
|
range.location = endRange.location+endRange.length;
|
||||||
|
range.length = [receivedString length]-range.location;
|
||||||
|
|
||||||
|
[config release];
|
||||||
|
config = [[configString parseJSON] retain];
|
||||||
|
//NSLog(@"%@", config);
|
||||||
|
if (config!=nil && [config isKindOfClass:[NSDictionary class]]) {
|
||||||
|
if ([config objectForKey:@"args"]!=nil) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
[config release];
|
||||||
|
config = nil;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
[pool drain];
|
||||||
|
pool = [NSAutoreleasePool new];
|
||||||
|
}
|
||||||
|
[pool drain];
|
||||||
|
/*if (config==nil) {
|
||||||
|
NSRange range = NSMakeRange(0, [receivedString length]);
|
||||||
|
NSAutoreleasePool *pool = [NSAutoreleasePool new];
|
||||||
|
while (range.length>1) {
|
||||||
|
NSRange configRange = [receivedString rangeOfString:@"ytplayer.config = " options:NSCaseInsensitiveSearch range:range];
|
||||||
|
if (configRange.location!=NSNotFound) {
|
||||||
|
range.location = configRange.location+configRange.length;
|
||||||
|
range.length = [receivedString length]-range.location;
|
||||||
|
NSRange endRange = [receivedString rangeOfString:@"};" options:NSCaseInsensitiveSearch range:range];
|
||||||
|
if (endRange.location==NSNotFound)
|
||||||
|
break;
|
||||||
|
NSString *configString = [receivedString substringWithRange:NSMakeRange(range.location, (endRange.location+endRange.length)-range.location)];
|
||||||
|
range.location = endRange.location+endRange.length;
|
||||||
|
range.length = [receivedString length]-range.location;
|
||||||
|
|
||||||
|
[config release];
|
||||||
|
config = [[configString parseJSON] retain];
|
||||||
|
//NSLog(@"%@", config);
|
||||||
|
if (config!=nil && [config isKindOfClass:[NSDictionary class]]) {
|
||||||
|
if ([config objectForKey:@"args"]!=nil) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
[config release];
|
||||||
|
config = nil;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
[pool drain];
|
||||||
|
pool = [NSAutoreleasePool new];
|
||||||
|
}
|
||||||
|
[pool drain];
|
||||||
|
}*/
|
||||||
|
[config autorelease];
|
||||||
|
NSString *urlMap = nil;
|
||||||
|
NSString *adaptiveMap = nil;
|
||||||
|
if (config!=nil && [config isKindOfClass:[NSDictionary class]]) {
|
||||||
|
//NSLog(@"%@", config);
|
||||||
|
id tmp = [config objectForKey:@"args"];
|
||||||
|
if ([tmp isKindOfClass:[NSDictionary class]]) {
|
||||||
|
urlMap = [tmp objectForKey:@"url_encoded_fmt_stream_map"];
|
||||||
|
adaptiveMap = [tmp objectForKey:@"adaptive_fmts"];
|
||||||
|
}
|
||||||
|
if (urlMap==nil)
|
||||||
|
NSLog(@"%@", config);
|
||||||
|
} else {
|
||||||
|
NSLog(@"What happened here? %@", config);
|
||||||
|
}
|
||||||
|
if (urlMap!=nil) {
|
||||||
|
NSArray *urls = [urlMap componentsSeparatedByString:@","];
|
||||||
|
[self processURLs:urls];
|
||||||
|
NSArray *adaptiveURLs = [adaptiveMap componentsSeparatedByString:@","];
|
||||||
|
[self processURLs:adaptiveURLs];
|
||||||
|
}
|
||||||
|
#if youviewdebug
|
||||||
|
NSLog(@"%@", videoURLS);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
if (title==nil || [title isEqual:@""]) {
|
||||||
|
NSString *thisTitle = @"";
|
||||||
|
range = [receivedString rangeOfString:@"<title>"];
|
||||||
|
if (range.location!=NSNotFound) {
|
||||||
|
NSString *s = [receivedString substringFromIndex:range.location + range.length];
|
||||||
|
|
||||||
|
range = [s rangeOfString:@"</title>"];
|
||||||
|
if (range.location == NSNotFound) NSLog(@"failed");
|
||||||
|
thisTitle = [s substringWithRange:NSMakeRange(0, range.location)];
|
||||||
|
range = [thisTitle rangeOfString:@"-"];
|
||||||
|
if (range.location!=NSNotFound) {
|
||||||
|
thisTitle = [thisTitle substringToIndex:range.location];
|
||||||
|
}
|
||||||
|
thisTitle = [[thisTitle stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]] flattenHTML];
|
||||||
|
#if youviewdebug
|
||||||
|
NSLog(@"Found Title: %@", thisTitle);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
[self setTitle:thisTitle];
|
||||||
|
}
|
||||||
|
|
||||||
|
if (urlMap==nil) {
|
||||||
|
NSDictionary *parameters = [[URL query] URLParameters];
|
||||||
|
NSURLRequest *theRequest = [NSURLRequest requestWithURL:[NSURL URLWithString:[NSString stringWithFormat:@"http://www.youtube.com/get_video_info?video_id=%@&asv=3&el=detailpage&hl=en_US&sts=16136", [parameters objectForKey:@"v"]]] cachePolicy:NSURLRequestUseProtocolCachePolicy timeoutInterval:60.0];
|
||||||
|
MGMURLBasicHandler *handler = [MGMURLBasicHandler handlerWithRequest:theRequest delegate:self];
|
||||||
|
[connectionManager addHandler:handler];
|
||||||
|
} else {
|
||||||
|
[self startVideo];
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if ([[MGMParentalControls standardParentalControls] boolForKey:MGMAllowFlaggedVideos]) {
|
||||||
|
NSMutableURLRequest *postRequest = [NSMutableURLRequest requestWithURL:[NSURL URLWithString:@"http://www.youtube.com/verify_age"] cachePolicy:NSURLRequestReloadIgnoringCacheData timeoutInterval:30.0];
|
||||||
|
[postRequest setHTTPMethod:@"POST"];
|
||||||
|
[postRequest setValue:@"application/x-www-form-urlencoded" forHTTPHeaderField:@"content-type"];
|
||||||
|
[postRequest setHTTPBody:[[NSString stringWithFormat:@"next_url=%@?%@&action_confirm=Confirm%20Birth%20Date", [URL path], [URL query]] dataUsingEncoding:NSASCIIStringEncoding]];
|
||||||
|
|
||||||
|
MGMURLBasicHandler *handler = [MGMURLBasicHandler handlerWithRequest:postRequest delegate:self];
|
||||||
|
[connectionManager addHandler:handler];
|
||||||
|
} else {
|
||||||
|
NSAlert *theAlert = [[NSAlert new] autorelease];
|
||||||
|
[theAlert addButtonWithTitle:@"OK"];
|
||||||
|
[theAlert setMessageText:@"Parental Controls"];
|
||||||
|
[theAlert setInformativeText:@"You cannot view this video because it is flagged.\nTo enable watching flagged videos, go to YouView->Parental Controls."];
|
||||||
|
[theAlert runModal];
|
||||||
|
[connectionManager release];
|
||||||
|
connectionManager = nil;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
- (void)setTitle:(NSString *)theTitle {
|
||||||
|
[title release];
|
||||||
|
title = [theTitle retain];
|
||||||
|
}
|
||||||
|
- (NSString *)title {
|
||||||
|
return title;
|
||||||
|
}
|
||||||
|
- (void)setVersion:(NSString *)theVersion {
|
||||||
|
[version release];
|
||||||
|
version = [theVersion retain];
|
||||||
|
}
|
||||||
|
- (NSString *)version {
|
||||||
|
return version;
|
||||||
|
}
|
||||||
|
- (void)startVideo {
|
||||||
|
//NSLog(@"%@", videoQualities);
|
||||||
|
|
||||||
|
int videoQualityFound = 0;
|
||||||
|
NSDictionary *video = nil;
|
||||||
|
//int audioQualityFound = 0;
|
||||||
|
NSDictionary *audio = nil;
|
||||||
|
for (int i=0; i<[videoURLS count]; i++) {
|
||||||
|
NSDictionary *videoURL = [videoURLS objectAtIndex:i];
|
||||||
|
NSDictionary *videoQuality = [videoQualities objectForKey:[videoURL objectForKey:MGMULTag]];
|
||||||
|
NSLog(@"%@ %@ %@", [videoURL objectForKey:MGMULTag], [videoURL objectForKey:MGMULURL], videoQuality);
|
||||||
|
if (videoQuality==nil) {
|
||||||
|
NSLog(@"Unknown Video Quality %@", videoURL);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
/*if (audioQualityFound<3 && ![[videoQuality objectForKey:MGMVTVideo] boolValue] && [[videoQuality objectForKey:MGMVTAudio] boolValue] && [[videoQuality objectForKey:MGMVTType] isEqual:@"M4A"] && [[videoQuality objectForKey:MGMVTQuality] containsString:@"256kbps"]) {
|
||||||
|
audioQualityFound = 3;
|
||||||
|
audio = videoURL;
|
||||||
|
} else if (audioQualityFound<2 && ![[videoQuality objectForKey:MGMVTVideo] boolValue] && [[videoQuality objectForKey:MGMVTAudio] boolValue] && [[videoQuality objectForKey:MGMVTType] isEqual:@"M4A"] && [[videoQuality objectForKey:MGMVTQuality] containsString:@"192kbps"]) {
|
||||||
|
audioQualityFound = 2;
|
||||||
|
audio = videoURL;
|
||||||
|
} else if (audioQualityFound<1 && ![[videoQuality objectForKey:MGMVTVideo] boolValue] && [[videoQuality objectForKey:MGMVTAudio] boolValue] && [[videoQuality objectForKey:MGMVTType] isEqual:@"M4A"] && [[videoQuality objectForKey:MGMVTQuality] containsString:@"128kbps"]) {
|
||||||
|
audioQualityFound = 1;
|
||||||
|
audio = videoURL;
|
||||||
|
} else if (audioQualityFound<=0 && ![[videoQuality objectForKey:MGMVTVideo] boolValue] && [[videoQuality objectForKey:MGMVTAudio] boolValue] && [[videoQuality objectForKey:MGMVTType] isEqual:@"M4A"]) {
|
||||||
|
audio = videoURL;
|
||||||
|
}*/
|
||||||
|
|
||||||
|
if (videoQualityFound<5 && maxQuality>=3 && ![[videoQuality objectForKey:MGMVT3D] boolValue] && [[videoQuality objectForKey:MGMVTVideo] boolValue] && [[videoQuality objectForKey:MGMVTAudio] boolValue] && [[videoQuality objectForKey:MGMVTType] isEqual:@"MP4"] && [[videoQuality objectForKey:MGMVTQuality] containsString:@"2160p"]) {
|
||||||
|
videoQualityFound = 5;
|
||||||
|
video = videoURL;
|
||||||
|
[self setVersion:[videoQuality objectForKey:MGMVTType]];
|
||||||
|
} else if (videoQualityFound<4 && maxQuality>=3 && ![[videoQuality objectForKey:MGMVT3D] boolValue] && [[videoQuality objectForKey:MGMVTVideo] boolValue] && [[videoQuality objectForKey:MGMVTAudio] boolValue] && [[videoQuality objectForKey:MGMVTType] isEqual:@"MP4"] && [[videoQuality objectForKey:MGMVTQuality] containsString:@"1440p"]) {
|
||||||
|
videoQualityFound = 4;
|
||||||
|
video = videoURL;
|
||||||
|
[self setVersion:[videoQuality objectForKey:MGMVTType]];
|
||||||
|
} else if (videoQualityFound<3 && maxQuality>=2 && ![[videoQuality objectForKey:MGMVT3D] boolValue] && [[videoQuality objectForKey:MGMVTVideo] boolValue] && [[videoQuality objectForKey:MGMVTAudio] boolValue] && [[videoQuality objectForKey:MGMVTType] isEqual:@"MP4"] && [[videoQuality objectForKey:MGMVTQuality] containsString:@"1080p"]) {
|
||||||
|
videoQualityFound = 3;
|
||||||
|
video = videoURL;
|
||||||
|
[self setVersion:[videoQuality objectForKey:MGMVTType]];
|
||||||
|
} else if (videoQualityFound<2 && maxQuality>=1 && ![[videoQuality objectForKey:MGMVT3D] boolValue] && [[videoQuality objectForKey:MGMVTVideo] boolValue] && [[videoQuality objectForKey:MGMVTAudio] boolValue] && [[videoQuality objectForKey:MGMVTType] isEqual:@"MP4"] && [[videoQuality objectForKey:MGMVTQuality] containsString:@"720p"]) {
|
||||||
|
videoQualityFound = 2;
|
||||||
|
video = videoURL;
|
||||||
|
[self setVersion:[videoQuality objectForKey:MGMVTType]];
|
||||||
|
} else if (videoQualityFound<1 && ![[videoQuality objectForKey:MGMVT3D] boolValue] && [[videoQuality objectForKey:MGMVTVideo] boolValue] && [[videoQuality objectForKey:MGMVTAudio] boolValue] && [[videoQuality objectForKey:MGMVTType] isEqual:@"MP4"] && [[videoQuality objectForKey:MGMVTQuality] containsString:@"480p"]) {
|
||||||
|
videoQualityFound = 1;
|
||||||
|
video = videoURL;
|
||||||
|
[self setVersion:[videoQuality objectForKey:MGMVTType]];
|
||||||
|
} else if (videoQualityFound<=0 && ![[videoQuality objectForKey:MGMVT3D] boolValue] && [[videoQuality objectForKey:MGMVTVideo] boolValue] && [[videoQuality objectForKey:MGMVTAudio] boolValue] && [[videoQuality objectForKey:MGMVTType] isEqual:@"MP4"]) {
|
||||||
|
video = videoURL;
|
||||||
|
[self setVersion:[videoQuality objectForKey:MGMVTType]];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if ([videoURLS count]>0 && video==nil) {
|
||||||
|
[self shouldLoadFlash];
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
#if youviewdebug
|
||||||
|
NSLog(@"Using video %@ with %@", [[videoQualities objectForKey:[video objectForKey:MGMULTag]] objectForKey:MGMVTQuality], video);
|
||||||
|
if (audio!=nil)
|
||||||
|
NSLog(@"Using audio %@ with %@", [[videoQualities objectForKey:[audio objectForKey:MGMULTag]] objectForKey:MGMVTQuality], audio);
|
||||||
|
#endif
|
||||||
|
if (video==nil) {
|
||||||
|
NSAlert *theAlert = [[NSAlert new] autorelease];
|
||||||
|
[theAlert addButtonWithTitle:@"OK"];
|
||||||
|
[theAlert setMessageText:@"Video"];
|
||||||
|
[theAlert setInformativeText:@"YouView tried as much as possible and couldn't find the video.\nPlease contact support via the help menu."];
|
||||||
|
[theAlert runModal];
|
||||||
|
} else {
|
||||||
|
NSMutableDictionary *info = [NSMutableDictionary dictionary];
|
||||||
|
if (![[MGMSystemInfo info] isAfterSnowLeopard]) {
|
||||||
|
[info setObject:[NSURL URLWithString:[[[video objectForKey:MGMULURL] absoluteString] replace:@"https://" with:@"http://"]] forKey:MGMVLVideoURLKey];
|
||||||
|
} else {
|
||||||
|
[info setObject:[video objectForKey:MGMULURL] forKey:MGMVLVideoURLKey];
|
||||||
|
}
|
||||||
|
|
||||||
|
if (![[[videoQualities objectForKey:[video objectForKey:MGMULTag]] objectForKey:MGMVTAudio] boolValue]) {
|
||||||
|
[info setObject:[audio objectForKey:MGMULURL] forKey:MGMVLAudioURLKey];
|
||||||
|
}
|
||||||
|
[info setObject:URL forKey:MGMVLURLKey];
|
||||||
|
[info setObject:title forKey:MGMVLTitle];
|
||||||
|
[info setObject:version forKey:MGMVLVersion];
|
||||||
|
#if youviewdebug
|
||||||
|
NSLog(@"Passing %@ to load", info);
|
||||||
|
#endif
|
||||||
|
[delegate loadVideo:info];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
- (void)loadSD {
|
||||||
|
#if youviewdebug
|
||||||
|
NSLog(@"Finding SD version.");
|
||||||
|
#endif
|
||||||
|
requestingSD = YES;
|
||||||
|
NSURLRequest *theRequest = [NSURLRequest requestWithURL:[NSURL URLWithString:[NSString stringWithFormat:MGMYTSDURL, [URL URLParameterWithName:@"v"]]] cachePolicy:NSURLRequestUseProtocolCachePolicy timeoutInterval:60.0];
|
||||||
|
MGMURLBasicHandler *handlers = [MGMURLBasicHandler handlerWithRequest:theRequest delegate:self];
|
||||||
|
[connectionManager addHandler:handlers];
|
||||||
|
}
|
||||||
|
- (void)shouldLoadFlash {
|
||||||
|
if (requestingSD) {
|
||||||
|
NSMutableDictionary *info = [NSMutableDictionary dictionary];
|
||||||
|
[info setObject:URL forKey:MGMVLURLKey];
|
||||||
|
[info setObject:title forKey:MGMVLTitle];
|
||||||
|
[delegate loadFlash:info];
|
||||||
|
} else {
|
||||||
|
[self loadSD];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
- (void)loadFlash {
|
||||||
|
#if youviewdebug
|
||||||
|
NSLog(@"Loading flash version.");
|
||||||
|
#endif
|
||||||
|
int qualityFound = 0;
|
||||||
|
NSDictionary *video = nil;
|
||||||
|
for (int i=0; i<[videoURLS count]; i++) {
|
||||||
|
NSDictionary *videoURL = [videoURLS objectAtIndex:i];
|
||||||
|
NSDictionary *videoQuality = [videoQualities objectForKey:[videoURL objectForKey:MGMULTag]];
|
||||||
|
if (videoQuality==nil) {
|
||||||
|
NSLog(@"Unknown Video Quality %@", [videoURL objectForKey:MGMULTag]);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (qualityFound<3 && ![[videoQuality objectForKey:MGMVT3D] boolValue] && [[videoQuality objectForKey:MGMVTVideo] boolValue] && [[videoQuality objectForKey:MGMVTAudio] boolValue] && [[videoQuality objectForKey:MGMVTType] isEqual:@"FLV"] && [[videoQuality objectForKey:MGMVTQuality] containsString:@"480p"]) {
|
||||||
|
qualityFound = 3;
|
||||||
|
video = videoURL;
|
||||||
|
} else if (qualityFound<2 && ![[videoQuality objectForKey:MGMVT3D] boolValue] && [[videoQuality objectForKey:MGMVTVideo] boolValue] && [[videoQuality objectForKey:MGMVTAudio] boolValue] && [[videoQuality objectForKey:MGMVTType] isEqual:@"FLV"] && [[videoQuality objectForKey:MGMVTQuality] containsString:@"360p"]) {
|
||||||
|
qualityFound = 2;
|
||||||
|
video = videoURL;
|
||||||
|
} else if (qualityFound<1 && ![[videoQuality objectForKey:MGMVT3D] boolValue] && [[videoQuality objectForKey:MGMVTVideo] boolValue] && [[videoQuality objectForKey:MGMVTAudio] boolValue] && [[videoQuality objectForKey:MGMVTType] isEqual:@"FLV"] && [[videoQuality objectForKey:MGMVTQuality] containsString:@"270p"]) {
|
||||||
|
qualityFound = 1;
|
||||||
|
video = videoURL;
|
||||||
|
} else if (qualityFound<=0 && ![[videoQuality objectForKey:MGMVT3D] boolValue] && [[videoQuality objectForKey:MGMVTVideo] boolValue] && [[videoQuality objectForKey:MGMVTAudio] boolValue] && [[videoQuality objectForKey:MGMVTType] isEqual:@"FLV"]) {
|
||||||
|
video = videoURL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#if youviewdebug
|
||||||
|
NSLog(@"Should load video %@ with URL %@.", [[videoQualities objectForKey:[video objectForKey:MGMULTag]] objectForKey:MGMVTQuality], video);
|
||||||
|
#endif
|
||||||
|
if (video==nil) {
|
||||||
|
NSAlert *theAlert = [[NSAlert new] autorelease];
|
||||||
|
[theAlert addButtonWithTitle:@"OK"];
|
||||||
|
[theAlert setMessageText:@"Video"];
|
||||||
|
[theAlert setInformativeText:@"YouView tried as much as possible and couldn't find the video.\nPlease contact support via the help menu."];
|
||||||
|
[theAlert runModal];
|
||||||
|
} else {
|
||||||
|
NSMutableDictionary *info = [NSMutableDictionary dictionary];
|
||||||
|
[info setObject:[video objectForKey:MGMULURL] forKey:MGMVLVideoURLKey];
|
||||||
|
[info setObject:URL forKey:MGMVLURLKey];
|
||||||
|
[info setObject:title forKey:MGMVLTitle];
|
||||||
|
[info setObject:version forKey:MGMVLVersion];
|
||||||
|
[delegate loadVideo:info];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@end
|
27
Classes/YouView/MGMYVToolConnection.h
Normal file
27
Classes/YouView/MGMYVToolConnection.h
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
//
|
||||||
|
// MGMYVToolConnection.h
|
||||||
|
// YouView
|
||||||
|
//
|
||||||
|
// Created by Mr. Gecko on 5/28/10.
|
||||||
|
// Copyright (c) 2015 Mr. Gecko's Media (James Coleman). https://mrgeckosmedia.com/ All rights reserved.
|
||||||
|
//
|
||||||
|
|
||||||
|
#import <Cocoa/Cocoa.h>
|
||||||
|
#import <Security/Authorization.h>
|
||||||
|
#import <Security/AuthorizationTags.h>
|
||||||
|
|
||||||
|
@protocol MGMYVToolProtocol;
|
||||||
|
@class MGMAdminAction;
|
||||||
|
|
||||||
|
@interface MGMYVToolConnection : NSObject {
|
||||||
|
MGMAdminAction *adminAction;
|
||||||
|
id yvTool;
|
||||||
|
NSConnection *yvToolConnection;
|
||||||
|
}
|
||||||
|
+ (id)connectionWithAuthorization:(AuthorizationRef)theAuthorization;
|
||||||
|
- (id)initWithAuthorization:(AuthorizationRef)theAuthorization;
|
||||||
|
- (void)connectToServer;
|
||||||
|
- (void)disconnectFromServer;
|
||||||
|
- (BOOL)isServerConnected;
|
||||||
|
- (id<MGMYVToolProtocol>)server;
|
||||||
|
@end
|
77
Classes/YouView/MGMYVToolConnection.m
Normal file
77
Classes/YouView/MGMYVToolConnection.m
Normal file
@ -0,0 +1,77 @@
|
|||||||
|
//
|
||||||
|
// MGMYVToolConnection.m
|
||||||
|
// YouView
|
||||||
|
//
|
||||||
|
// Created by Mr. Gecko on 5/28/10.
|
||||||
|
// Copyright (c) 2015 Mr. Gecko's Media (James Coleman). https://mrgeckosmedia.com/ All rights reserved.
|
||||||
|
//
|
||||||
|
|
||||||
|
#import "MGMYVToolConnection.h"
|
||||||
|
#import "MGMYVToolProtocol.h"
|
||||||
|
#import "MGMAdminAction.h"
|
||||||
|
|
||||||
|
@implementation MGMYVToolConnection
|
||||||
|
+ (id)connectionWithAuthorization:(AuthorizationRef)theAuthorization {
|
||||||
|
return [[[self alloc] initWithAuthorization:theAuthorization] autorelease];
|
||||||
|
}
|
||||||
|
- (id)initWithAuthorization:(AuthorizationRef)theAuthorization {
|
||||||
|
if (self = [super init]) {
|
||||||
|
adminAction = [MGMAdminAction new];
|
||||||
|
if (theAuthorization!=NULL)
|
||||||
|
[adminAction setAuthorization:theAuthorization];
|
||||||
|
[adminAction launchCommand:[[NSBundle mainBundle] pathForResource:@"YVTool" ofType:@""] withArguments:[NSArray arrayWithObject:[NSString stringWithFormat:@"%d", [[NSProcessInfo processInfo] processIdentifier]]]];
|
||||||
|
[NSThread sleepUntilDate:[NSDate dateWithTimeIntervalSinceNow:0.5]];
|
||||||
|
[self connectToServer];
|
||||||
|
}
|
||||||
|
return self;
|
||||||
|
}
|
||||||
|
- (void)dealloc {
|
||||||
|
#if releaseDebug
|
||||||
|
NSLog(@"%s Releasing", __PRETTY_FUNCTION__);
|
||||||
|
#endif
|
||||||
|
[self disconnectFromServer];
|
||||||
|
[adminAction release];
|
||||||
|
[yvToolConnection release];
|
||||||
|
[super dealloc];
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)connectToServer {
|
||||||
|
[yvToolConnection release];
|
||||||
|
yvToolConnection = [[NSConnection connectionWithRegisteredName:@"MGMYVServer" host:nil] retain];
|
||||||
|
if (yvToolConnection==nil) {
|
||||||
|
NSLog(@"Couldn't connect to YVTool server.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
[yvTool release];
|
||||||
|
yvTool = [[yvToolConnection rootProxy] retain];
|
||||||
|
if (yvTool==nil) {
|
||||||
|
NSLog(@"Couldn't set proxy with YVTool server.");
|
||||||
|
[yvToolConnection release];
|
||||||
|
yvToolConnection = nil;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
[yvTool setProtocolForProxy:@protocol(MGMYVToolProtocol)];
|
||||||
|
}
|
||||||
|
- (void)disconnectFromServer {
|
||||||
|
@try {
|
||||||
|
[yvTool quit];
|
||||||
|
[yvTool release];
|
||||||
|
yvTool = nil;
|
||||||
|
[yvToolConnection release];
|
||||||
|
yvToolConnection = nil;
|
||||||
|
}
|
||||||
|
@catch (NSException * e) {
|
||||||
|
NSLog(@"Couldn't disconnect.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
- (BOOL)isServerConnected {
|
||||||
|
return (yvToolConnection!=nil);
|
||||||
|
}
|
||||||
|
|
||||||
|
- (id<MGMYVToolProtocol>)server {
|
||||||
|
if (![self isServerConnected]) {
|
||||||
|
[self connectToServer];
|
||||||
|
}
|
||||||
|
return yvTool;
|
||||||
|
}
|
||||||
|
@end
|
16
Classes/YouView/MGMYVToolProtocol.h
Normal file
16
Classes/YouView/MGMYVToolProtocol.h
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
//
|
||||||
|
// MGMYVToolProtocol.h
|
||||||
|
// YouView
|
||||||
|
//
|
||||||
|
// Created by Mr. Gecko on 5/28/10.
|
||||||
|
// Copyright (c) 2015 Mr. Gecko's Media (James Coleman). https://mrgeckosmedia.com/ All rights reserved.
|
||||||
|
//
|
||||||
|
|
||||||
|
#import <Cocoa/Cocoa.h>
|
||||||
|
|
||||||
|
|
||||||
|
@protocol MGMYVToolProtocol
|
||||||
|
- (void)changePremissionsForPath:(NSString *)thePath to:(NSString *)thePermissions;
|
||||||
|
- (void)changeOwnerForPath:(NSString *)thePath to:(NSString *)theOwner;
|
||||||
|
- (void)quit;
|
||||||
|
@end
|
193
Classes/YouView/Player/MGMPlayer.h
Normal file
193
Classes/YouView/Player/MGMPlayer.h
Normal file
@ -0,0 +1,193 @@
|
|||||||
|
//
|
||||||
|
// MGMPlayer.h
|
||||||
|
// YouView
|
||||||
|
//
|
||||||
|
// Created by Mr. Gecko on 10/15/09.
|
||||||
|
// Copyright (c) 2015 Mr. Gecko's Media (James Coleman). https://mrgeckosmedia.com/ All rights reserved.
|
||||||
|
//
|
||||||
|
|
||||||
|
#import <Cocoa/Cocoa.h>
|
||||||
|
#import <QTKit/QTKit.h>
|
||||||
|
|
||||||
|
extern NSString * const MGMILObject;
|
||||||
|
extern NSString * const MGMILURL;
|
||||||
|
extern NSString * const MGMILID;
|
||||||
|
extern NSString * const MGMILPath;
|
||||||
|
|
||||||
|
@class MGMBackgroundView, MGMURLConnectionManager, MGMVideoFinder, QTMovieView, QTMovie, MGMController, MGMPlayerTracking, MGMPlayerControls;
|
||||||
|
|
||||||
|
@interface MGMPlayer : NSObject {
|
||||||
|
BOOL player;
|
||||||
|
IBOutlet NSWindow *mainWindow;
|
||||||
|
IBOutlet MGMBackgroundView *mainView;
|
||||||
|
IBOutlet NSWindow *fullScreenWindow;
|
||||||
|
IBOutlet MGMBackgroundView *fullScreenView;
|
||||||
|
IBOutlet MGMPlayerControls *controlsView;
|
||||||
|
IBOutlet NSWindow *controlsWindow;
|
||||||
|
IBOutlet NSView *browserView;
|
||||||
|
IBOutlet NSView *movieView;
|
||||||
|
MGMController *controller;
|
||||||
|
//Essentials
|
||||||
|
NSMutableDictionary *currentQuery;
|
||||||
|
NSString *nextPageToken;
|
||||||
|
NSString *prevPageToken;
|
||||||
|
NSArray *resultsArray;
|
||||||
|
IBOutlet NSTableView *resultsTable;
|
||||||
|
IBOutlet NSSearchField *searchBox;
|
||||||
|
IBOutlet NSProgressIndicator *progressIndecator;
|
||||||
|
IBOutlet NSSegmentedControl *nextPrevious;
|
||||||
|
IBOutlet NSTextField *foundResults;
|
||||||
|
IBOutlet NSTextField *page;
|
||||||
|
NSLock *loaderLock;
|
||||||
|
MGMURLConnectionManager *connectionManager;
|
||||||
|
MGMURLConnectionManager *imageLoaderManager;
|
||||||
|
int totalResults;
|
||||||
|
int start;
|
||||||
|
int resultsCount;
|
||||||
|
NSString *query;
|
||||||
|
IBOutlet NSButton *loginButton;
|
||||||
|
//Message
|
||||||
|
NSTimer *messageTimer;
|
||||||
|
NSMutableArray *messages;
|
||||||
|
IBOutlet NSTextField *messageField;
|
||||||
|
//Order
|
||||||
|
IBOutlet NSMenuItem *relevance;
|
||||||
|
IBOutlet NSMenuItem *published;
|
||||||
|
IBOutlet NSMenuItem *viewCount;
|
||||||
|
IBOutlet NSMenuItem *rating;
|
||||||
|
|
||||||
|
//Movie View
|
||||||
|
NSURL *entry;
|
||||||
|
NSString *title;
|
||||||
|
MGMVideoFinder *videoFinder;
|
||||||
|
IBOutlet QTMovieView *moviePlayer;
|
||||||
|
IBOutlet MGMPlayerTracking *PlayerTracking;
|
||||||
|
BOOL FullScreen;
|
||||||
|
BOOL resized;
|
||||||
|
NSRect windowFrame;
|
||||||
|
NSRect windowContentFrame;
|
||||||
|
BOOL movieOpen;
|
||||||
|
BOOL moviePlaying;
|
||||||
|
BOOL transitioning;
|
||||||
|
NSTimer *updateControlsTimer;
|
||||||
|
}
|
||||||
|
+ (id)playerWithVideo:(NSURL *)theVideo controller:(MGMController *)theController player:(BOOL)isPlayer;
|
||||||
|
- (id)initWithVideo:(NSURL *)theVideo controller:(MGMController *)theController player:(BOOL)isPlayer;
|
||||||
|
- (NSWindow *)mainWindow;
|
||||||
|
- (NSWindow *)fullScreenWindow;
|
||||||
|
- (NSWindow *)controlsWindow;
|
||||||
|
- (MGMPlayerControls *)controlsView;
|
||||||
|
- (MGMPlayerTracking *)playerTracking;
|
||||||
|
- (QTMovieView *)moviePlayer;
|
||||||
|
- (void)setResults:(NSArray *)theResults;
|
||||||
|
- (NSArray *)results;
|
||||||
|
- (int)resultsCount;
|
||||||
|
- (NSTableView *)resultsTable;
|
||||||
|
- (int)totalResults;
|
||||||
|
- (int)start;
|
||||||
|
- (BOOL)player;
|
||||||
|
- (BOOL)isMovieOpen;
|
||||||
|
- (BOOL)isMoviePlaying;
|
||||||
|
- (BOOL)isFullScreen;
|
||||||
|
- (NSURL *)entry;
|
||||||
|
- (NSString *)title;
|
||||||
|
- (void)startProgress;
|
||||||
|
- (void)stopProgress;
|
||||||
|
- (void)startImageLoader;
|
||||||
|
- (void)reloadRecentSearches;
|
||||||
|
- (IBAction)clearRecentSearches:(id)sender;
|
||||||
|
- (IBAction)setOrderBy:(id)sender;
|
||||||
|
- (void)reloadData;
|
||||||
|
- (void)search;
|
||||||
|
- (IBAction)nextPrevious:(id)sender;
|
||||||
|
- (void)addMessage:(NSString *)message;
|
||||||
|
- (void)messageTimer;
|
||||||
|
- (void)removeAllMessages;
|
||||||
|
- (IBAction)openVideo:(id)sender;
|
||||||
|
- (IBAction)openVideoReverse:(id)sender;
|
||||||
|
- (void)openMovie:(NSURL *)theVideo;
|
||||||
|
- (void)closeMovie;
|
||||||
|
- (void)loop;
|
||||||
|
- (void)goFullScreen;
|
||||||
|
- (void)updateControls;
|
||||||
|
- (void)controlWithKeyEvent:(NSEvent *)theEvent;
|
||||||
|
- (void)setTitle;
|
||||||
|
@end
|
||||||
|
|
||||||
|
@interface MGMFullScreenWindow : NSWindow {
|
||||||
|
IBOutlet MGMPlayer *player;
|
||||||
|
}
|
||||||
|
|
||||||
|
@end
|
||||||
|
|
||||||
|
@interface MGMResultsTable : NSTableView {
|
||||||
|
IBOutlet MGMPlayer *player;
|
||||||
|
}
|
||||||
|
|
||||||
|
@end
|
||||||
|
|
||||||
|
@interface MGMBottomControl : NSView {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@end
|
||||||
|
|
||||||
|
@interface MGMPlayerTracking : NSView {
|
||||||
|
IBOutlet MGMPlayer *player;
|
||||||
|
NSTrackingRectTag rolloverTrackingRectTag;
|
||||||
|
NSTimer *hideCursorTimer;
|
||||||
|
BOOL wasAcceptingMouseEvents;
|
||||||
|
|
||||||
|
BOOL fadeIn;
|
||||||
|
NSTimer *fadeTimer;
|
||||||
|
BOOL controls;
|
||||||
|
BOOL playerEntered;
|
||||||
|
NSTimer *controlsTimer;
|
||||||
|
}
|
||||||
|
- (void)releaseTimers;
|
||||||
|
- (void)resetTracking;
|
||||||
|
- (void)hideCursorNow;
|
||||||
|
- (void)controlsEntered;
|
||||||
|
- (void)controlsExited;
|
||||||
|
@end
|
||||||
|
|
||||||
|
@interface QTMovie (MGMAdditions)
|
||||||
|
- (QTTime)maxTimeLoaded;
|
||||||
|
- (NSArray *)loadedRanges;
|
||||||
|
- (long)loadState;
|
||||||
|
@end
|
||||||
|
|
||||||
|
@interface MGMPlayerControls : NSView {
|
||||||
|
IBOutlet MGMPlayer *player;
|
||||||
|
NSTrackingRectTag rolloverTrackingRectTag;
|
||||||
|
BOOL wasAcceptingMouseEvents;
|
||||||
|
|
||||||
|
//Controls
|
||||||
|
NSRect VCMuteR;
|
||||||
|
NSRect VCPlayR;
|
||||||
|
NSRect durationR;
|
||||||
|
NSRect currentTimeR;
|
||||||
|
NSRect VCFullScreenR;
|
||||||
|
NSRect VCSizeR;
|
||||||
|
NSRect VCPositionR;
|
||||||
|
NSRect VCBarRect;
|
||||||
|
|
||||||
|
BOOL inBar;
|
||||||
|
BOOL resizing;
|
||||||
|
float lastVolume;
|
||||||
|
}
|
||||||
|
- (void)resetTracking;
|
||||||
|
@end
|
||||||
|
|
||||||
|
@interface MGMControlsWindow : NSWindow {
|
||||||
|
IBOutlet MGMPlayerControls *playerControls;
|
||||||
|
}
|
||||||
|
|
||||||
|
@end
|
||||||
|
|
||||||
|
@interface MGMBackgroundView : NSView {
|
||||||
|
NSColor *backgroundColor;
|
||||||
|
}
|
||||||
|
- (void)setBackgroundColor:(NSColor *)theColor;
|
||||||
|
- (NSColor *)backgroundColor;
|
||||||
|
@end
|
1255
Classes/YouView/Player/MGMPlayer.m
Normal file
1255
Classes/YouView/Player/MGMPlayer.m
Normal file
File diff suppressed because it is too large
Load Diff
28
Classes/YouView/Player/MGMResult.h
Normal file
28
Classes/YouView/Player/MGMResult.h
Normal file
@ -0,0 +1,28 @@
|
|||||||
|
//
|
||||||
|
// MGMResult.h
|
||||||
|
// YouView
|
||||||
|
//
|
||||||
|
// Created by Mr. Gecko on 4/25/09.
|
||||||
|
// Copyright (c) 2015 Mr. Gecko's Media (James Coleman). https://mrgeckosmedia.com/ All rights reserved.
|
||||||
|
//
|
||||||
|
|
||||||
|
#import <Cocoa/Cocoa.h>
|
||||||
|
|
||||||
|
@interface MGMResult : NSObject {
|
||||||
|
NSDictionary *item;
|
||||||
|
IBOutlet NSView *mainView;
|
||||||
|
IBOutlet NSImageView *previewView;
|
||||||
|
IBOutlet NSTextField *titleField;
|
||||||
|
IBOutlet NSTextField *descriptionField;
|
||||||
|
IBOutlet NSTextField *authorField;
|
||||||
|
IBOutlet NSTextField *addedField;
|
||||||
|
}
|
||||||
|
+ (id)resultWithItem:(NSDictionary *)video;
|
||||||
|
- (id)initWithItem:(NSDictionary *)video;
|
||||||
|
- (NSView *)view;
|
||||||
|
- (NSURL *)entry;
|
||||||
|
- (NSDictionary *)item;
|
||||||
|
- (void)loadImage;
|
||||||
|
- (void)setImage:(NSImage *)image;
|
||||||
|
- (NSImage *)image;
|
||||||
|
@end
|
101
Classes/YouView/Player/MGMResult.m
Normal file
101
Classes/YouView/Player/MGMResult.m
Normal file
@ -0,0 +1,101 @@
|
|||||||
|
//
|
||||||
|
// MGMResult.m
|
||||||
|
// YouView
|
||||||
|
//
|
||||||
|
// Created by Mr. Gecko on 4/25/09.
|
||||||
|
// Copyright (c) 2015 Mr. Gecko's Media (James Coleman). https://mrgeckosmedia.com/ All rights reserved.
|
||||||
|
//
|
||||||
|
|
||||||
|
#import "MGMResult.h"
|
||||||
|
#import "MGMAddons.h"
|
||||||
|
#import "MGMController.h"
|
||||||
|
#import <MGMUsers/MGMUsers.h>
|
||||||
|
#import <GeckoReporter/GeckoReporter.h>
|
||||||
|
|
||||||
|
@implementation MGMResult
|
||||||
|
+ (id)resultWithItem:(NSDictionary *)video {
|
||||||
|
return [[[self alloc] initWithItem:video] autorelease];
|
||||||
|
}
|
||||||
|
- (id)initWithItem:(NSDictionary *)video {
|
||||||
|
if (self = [super init]) {
|
||||||
|
if (![NSBundle loadNibNamed:@"MGMResultView" owner:self]) {
|
||||||
|
[self release];
|
||||||
|
self = nil;
|
||||||
|
} else {
|
||||||
|
NSLog(@"%@", video);
|
||||||
|
item = [video retain];
|
||||||
|
[self loadImage];
|
||||||
|
NSDictionary *snippet = [item objectForKey:@"snippet"];
|
||||||
|
[titleField setStringValue:[snippet objectForKey:@"title"]];
|
||||||
|
[descriptionField setStringValue:[snippet objectForKey:@"description"]];
|
||||||
|
NSString *author = [snippet objectForKey:@"channelTitle"];
|
||||||
|
if ([author isEqual:@""]) {
|
||||||
|
author = [snippet objectForKey:@"channelId"];
|
||||||
|
}
|
||||||
|
[authorField setStringValue:author];
|
||||||
|
|
||||||
|
NSString *published = [snippet objectForKey:@"publishedAt"];
|
||||||
|
NSRange dotRange = [published rangeOfString:@"."];
|
||||||
|
if (dotRange.location!=NSNotFound) {
|
||||||
|
published = [published substringToIndex:dotRange.location];
|
||||||
|
}
|
||||||
|
NSDateFormatter *formatter = [NSDateFormatter new];
|
||||||
|
[formatter setTimeZone:[NSTimeZone timeZoneForSecondsFromGMT:0]];
|
||||||
|
[formatter setDateFormat:@"yyyy-MM-dd'T'HH:mm:ss"];//2015-03-14T01:45:20.000Z
|
||||||
|
NSDate *date = [formatter dateFromString:published];
|
||||||
|
[formatter setTimeZone:[NSTimeZone localTimeZone]];
|
||||||
|
[formatter setDateFormat:@"M/dd/yy hh:mm:ss a"];
|
||||||
|
NSString *formattedDate = [formatter stringFromDate:date];
|
||||||
|
if (formattedDate!=nil)
|
||||||
|
[addedField setStringValue:formattedDate];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return self;
|
||||||
|
}
|
||||||
|
- (void)dealloc {
|
||||||
|
#if releaseDebug
|
||||||
|
MGMLog(@"%s Releasing", __PRETTY_FUNCTION__);
|
||||||
|
#endif
|
||||||
|
[item release];
|
||||||
|
[mainView removeFromSuperview];
|
||||||
|
[mainView release];
|
||||||
|
[super dealloc];
|
||||||
|
}
|
||||||
|
- (NSView *)view {
|
||||||
|
return mainView;
|
||||||
|
}
|
||||||
|
- (NSString *)accessibilityRole {
|
||||||
|
return @"video";
|
||||||
|
}
|
||||||
|
- (NSString *)accessibilityDescription {
|
||||||
|
return @"";
|
||||||
|
}
|
||||||
|
- (NSDictionary *)item {
|
||||||
|
return item;
|
||||||
|
}
|
||||||
|
- (NSURL *)entry {
|
||||||
|
return [NSURL URLWithString:[NSString stringWithFormat:@"http://www.youtube.com/watch?v=%@", [[item objectForKey:@"id"] objectForKey:@"videoId"]]];
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)loadImage {
|
||||||
|
NSString *imagePath = [[[MGMUser cachePath] stringByAppendingPathComponent:[[item objectForKey:@"id"] objectForKey:@"videoId"]] stringByAppendingPathExtension:@"tiff"];
|
||||||
|
if ([[NSFileManager defaultManager] fileExistsAtPath:imagePath]) {
|
||||||
|
[self setImage:[[[NSImage alloc] initWithContentsOfFile:imagePath] autorelease]];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
- (void)setImage:(NSImage *)image {
|
||||||
|
[previewView setImage:image];
|
||||||
|
[previewView setHidden:NO];
|
||||||
|
if ([[NSUserDefaults standardUserDefaults] boolForKey:MGMAnimations]) {
|
||||||
|
NSMutableDictionary *animationInfo = [NSMutableDictionary dictionary];
|
||||||
|
[animationInfo setObject:previewView forKey:NSViewAnimationTargetKey];
|
||||||
|
[animationInfo setObject:NSViewAnimationFadeInEffect forKey:NSViewAnimationEffectKey];
|
||||||
|
NSViewAnimation *animation = [[[NSViewAnimation alloc] initWithViewAnimations:[NSArray arrayWithObject:animationInfo]] autorelease];
|
||||||
|
[animation setDuration:1.0];
|
||||||
|
[animation startAnimation];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
- (NSImage *)image {
|
||||||
|
return [previewView image];
|
||||||
|
}
|
||||||
|
@end
|
28
Classes/YouView/Preferences/MGMDonatePane.h
Normal file
28
Classes/YouView/Preferences/MGMDonatePane.h
Normal file
@ -0,0 +1,28 @@
|
|||||||
|
//
|
||||||
|
// MGMDonatePane.h
|
||||||
|
// YouView
|
||||||
|
//
|
||||||
|
// Created by Mr. Gecko on 7/29/10.
|
||||||
|
// Copyright (c) 2015 Mr. Gecko's Media (James Coleman). https://mrgeckosmedia.com/ All rights reserved.
|
||||||
|
//
|
||||||
|
|
||||||
|
#import <Cocoa/Cocoa.h>
|
||||||
|
#import <MGMUsers/MGMUsers.h>
|
||||||
|
|
||||||
|
extern NSString * const MGMDSalt1;
|
||||||
|
extern NSString * const MGMDSalt2;
|
||||||
|
extern NSString * const MGMDonationPath;
|
||||||
|
extern NSString * const MGMDKey;
|
||||||
|
extern NSString * const MGMDHash;
|
||||||
|
extern NSString * const MGMDName;
|
||||||
|
extern NSString * const MGMDStatus;
|
||||||
|
|
||||||
|
@interface MGMDonatePane : MGMPreferencesPane {
|
||||||
|
IBOutlet NSView *mainView;
|
||||||
|
}
|
||||||
|
- (id)initWithPreferences:(MGMPreferences *)thePreferences;
|
||||||
|
+ (void)setUpToolbarItem:(NSToolbarItem *)theItem;
|
||||||
|
+ (NSString *)title;
|
||||||
|
- (NSView *)preferencesView;
|
||||||
|
- (IBAction)donate:(id)sender;
|
||||||
|
@end
|
52
Classes/YouView/Preferences/MGMDonatePane.m
Normal file
52
Classes/YouView/Preferences/MGMDonatePane.m
Normal file
@ -0,0 +1,52 @@
|
|||||||
|
//
|
||||||
|
// MGMDonatePane.m
|
||||||
|
// YouView
|
||||||
|
//
|
||||||
|
// Created by Mr. Gecko on 7/29/10.
|
||||||
|
// Copyright (c) 2015 Mr. Gecko's Media (James Coleman). https://mrgeckosmedia.com/ All rights reserved.
|
||||||
|
//
|
||||||
|
|
||||||
|
#import "MGMDonatePane.h"
|
||||||
|
#import "MGMController.h"
|
||||||
|
|
||||||
|
NSString * const MGMDSalt1 = @"lll3jh653k";
|
||||||
|
NSString * const MGMDSalt2 = @"jgsllwi32l5";
|
||||||
|
NSString * const MGMDonationPath = @".info.plist";
|
||||||
|
NSString * const MGMDKey = @"key";
|
||||||
|
NSString * const MGMDHash = @"hash";
|
||||||
|
NSString * const MGMDName = @"name";
|
||||||
|
NSString * const MGMDStatus = @"status";
|
||||||
|
|
||||||
|
@implementation MGMDonatePane
|
||||||
|
- (id)initWithPreferences:(MGMPreferences *)thePreferences {
|
||||||
|
if (self = [super initWithPreferences:thePreferences]) {
|
||||||
|
if (![NSBundle loadNibNamed:@"DonatePane" owner:self]) {
|
||||||
|
NSLog(@"Unable to load Nib for Donate Preferences");
|
||||||
|
[self release];
|
||||||
|
self = nil;
|
||||||
|
} else {
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return self;
|
||||||
|
}
|
||||||
|
- (void)dealloc {
|
||||||
|
[mainView release];
|
||||||
|
[super dealloc];
|
||||||
|
}
|
||||||
|
+ (void)setUpToolbarItem:(NSToolbarItem *)theItem {
|
||||||
|
[theItem setLabel:[self title]];
|
||||||
|
[theItem setPaletteLabel:[theItem label]];
|
||||||
|
[theItem setImage:[NSImage imageNamed:@"Donate"]];
|
||||||
|
}
|
||||||
|
+ (NSString *)title {
|
||||||
|
return @"Donate";
|
||||||
|
}
|
||||||
|
- (NSView *)preferencesView {
|
||||||
|
return mainView;
|
||||||
|
}
|
||||||
|
|
||||||
|
- (IBAction)donate:(id)sender {
|
||||||
|
[[NSWorkspace sharedWorkspace] openURL:[NSURL URLWithString:@"https://mrgeckosmedia.com/donate?purpose=youview"]];
|
||||||
|
}
|
||||||
|
@end
|
31
Classes/YouView/Preferences/MGMGeneralPane.h
Normal file
31
Classes/YouView/Preferences/MGMGeneralPane.h
Normal file
@ -0,0 +1,31 @@
|
|||||||
|
//
|
||||||
|
// MGMGeneralPane.h
|
||||||
|
// YouView
|
||||||
|
//
|
||||||
|
// Created by Mr. Gecko on 7/29/10.
|
||||||
|
// Copyright (c) 2015 Mr. Gecko's Media (James Coleman). https://mrgeckosmedia.com/ All rights reserved.
|
||||||
|
//
|
||||||
|
|
||||||
|
#import <Cocoa/Cocoa.h>
|
||||||
|
#import <MGMUsers/MGMUsers.h>
|
||||||
|
|
||||||
|
@interface MGMGeneralPane : MGMPreferencesPane {
|
||||||
|
IBOutlet NSView *mainView;
|
||||||
|
IBOutlet NSMatrix *maxQuality;
|
||||||
|
IBOutlet NSMatrix *windowMode;
|
||||||
|
IBOutlet NSButton *animationsButton;
|
||||||
|
IBOutlet NSButton *FSFloatButton;
|
||||||
|
IBOutlet NSButton *FSSpacesButton;
|
||||||
|
IBOutlet NSTextField *pageMax;
|
||||||
|
}
|
||||||
|
- (id)initWithPreferences:(MGMPreferences *)thePreferences;
|
||||||
|
+ (void)setUpToolbarItem:(NSToolbarItem *)theItem;
|
||||||
|
+ (NSString *)title;
|
||||||
|
- (NSView *)preferencesView;
|
||||||
|
- (IBAction)saveVideoQuality:(id)sender;
|
||||||
|
- (IBAction)saveWindowMode:(id)sender;
|
||||||
|
- (IBAction)saveAnimations:(id)sender;
|
||||||
|
- (IBAction)saveFSFloat:(id)sender;
|
||||||
|
- (IBAction)saveFSSpaces:(id)sender;
|
||||||
|
- (IBAction)save:(id)sender;
|
||||||
|
@end
|
69
Classes/YouView/Preferences/MGMGeneralPane.m
Normal file
69
Classes/YouView/Preferences/MGMGeneralPane.m
Normal file
@ -0,0 +1,69 @@
|
|||||||
|
//
|
||||||
|
// MGMGeneralPane.m
|
||||||
|
// YouView
|
||||||
|
//
|
||||||
|
// Created by Mr. Gecko on 7/29/10.
|
||||||
|
// Copyright (c) 2015 Mr. Gecko's Media (James Coleman). https://mrgeckosmedia.com/ All rights reserved.
|
||||||
|
//
|
||||||
|
|
||||||
|
#import "MGMGeneralPane.h"
|
||||||
|
#import "MGMController.h"
|
||||||
|
|
||||||
|
@implementation MGMGeneralPane
|
||||||
|
- (id)initWithPreferences:(MGMPreferences *)thePreferences {
|
||||||
|
if (self = [super initWithPreferences:thePreferences]) {
|
||||||
|
if (![NSBundle loadNibNamed:@"GeneralPane" owner:self]) {
|
||||||
|
NSLog(@"Unable to load Nib for General Preferences");
|
||||||
|
[self release];
|
||||||
|
self = nil;
|
||||||
|
} else {
|
||||||
|
[maxQuality selectCellAtRow:0 column:[preferences integerForKey:MGMMaxQuality]];
|
||||||
|
[windowMode selectCellAtRow:0 column:[preferences integerForKey:MGMWindowMode]];
|
||||||
|
[animationsButton setState:([preferences boolForKey:MGMAnimations] ? NSOnState : NSOffState)];
|
||||||
|
[FSFloatButton setState:([preferences boolForKey:MGMFSFloat] ? NSOnState : NSOffState)];
|
||||||
|
[FSSpacesButton setState:([preferences boolForKey:MGMFSSpaces] ? NSOnState : NSOffState)];
|
||||||
|
[pageMax setIntValue:[preferences integerForKey:MGMPageMax]];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return self;
|
||||||
|
}
|
||||||
|
- (void)dealloc {
|
||||||
|
[mainView release];
|
||||||
|
[super dealloc];
|
||||||
|
}
|
||||||
|
+ (void)setUpToolbarItem:(NSToolbarItem *)theItem {
|
||||||
|
[theItem setLabel:[self title]];
|
||||||
|
[theItem setPaletteLabel:[theItem label]];
|
||||||
|
[theItem setImage:[NSImage imageNamed:@"General"]];
|
||||||
|
}
|
||||||
|
+ (NSString *)title {
|
||||||
|
return @"General";
|
||||||
|
}
|
||||||
|
- (NSView *)preferencesView {
|
||||||
|
return mainView;
|
||||||
|
}
|
||||||
|
- (IBAction)saveVideoQuality:(id)sender {
|
||||||
|
[preferences setInteger:[maxQuality selectedColumn] forKey:MGMMaxQuality];
|
||||||
|
}
|
||||||
|
- (IBAction)saveWindowMode:(id)sender {
|
||||||
|
[preferences setInteger:[windowMode selectedColumn] forKey:MGMWindowMode];
|
||||||
|
}
|
||||||
|
- (IBAction)saveAnimations:(id)sender {
|
||||||
|
[preferences setBool:([animationsButton state]==NSOnState) forKey:MGMAnimations];
|
||||||
|
}
|
||||||
|
- (IBAction)saveFSFloat:(id)sender {
|
||||||
|
[preferences setBool:([FSFloatButton state]==NSOnState) forKey:MGMFSFloat];
|
||||||
|
[[NSNotificationCenter defaultCenter] postNotificationName:MGMFSSettingsNotification object:nil];
|
||||||
|
}
|
||||||
|
- (IBAction)saveFSSpaces:(id)sender {
|
||||||
|
[preferences setBool:([FSSpacesButton state]==NSOnState) forKey:MGMFSSpaces];
|
||||||
|
[[NSNotificationCenter defaultCenter] postNotificationName:MGMFSSettingsNotification object:nil];
|
||||||
|
}
|
||||||
|
- (IBAction)save:(id)sender {
|
||||||
|
int max = [pageMax intValue];
|
||||||
|
if (max<=0)
|
||||||
|
max = [preferences integerForKey:MGMPageMax];
|
||||||
|
[pageMax setIntValue:max];
|
||||||
|
[preferences setInteger:max forKey:MGMPageMax];
|
||||||
|
}
|
||||||
|
@end
|
28
Classes/YouView/Preferences/MGMParentalControlsPane.h
Normal file
28
Classes/YouView/Preferences/MGMParentalControlsPane.h
Normal file
@ -0,0 +1,28 @@
|
|||||||
|
//
|
||||||
|
// MGMParentalControlsPane.h
|
||||||
|
// YouView
|
||||||
|
//
|
||||||
|
// Created by Mr. Gecko on 7/29/10.
|
||||||
|
// Copyright (c) 2015 Mr. Gecko's Media (James Coleman). https://mrgeckosmedia.com/ All rights reserved.
|
||||||
|
//
|
||||||
|
|
||||||
|
#import <Cocoa/Cocoa.h>
|
||||||
|
#import <MGMUsers/MGMUsers.h>
|
||||||
|
|
||||||
|
@class SFAuthorizationView, MGMYVToolConnection;
|
||||||
|
|
||||||
|
@interface MGMParentalControlsPane : MGMPreferencesPane {
|
||||||
|
IBOutlet NSView *mainView;
|
||||||
|
IBOutlet NSButton *flaggedVideo;
|
||||||
|
IBOutlet NSMatrix *safeSearch;
|
||||||
|
AuthorizationRights rights;
|
||||||
|
IBOutlet SFAuthorizationView *autherizationView;
|
||||||
|
MGMYVToolConnection *yvToolConnection;
|
||||||
|
}
|
||||||
|
- (id)initWithPreferences:(MGMPreferences *)thePreferences;
|
||||||
|
+ (void)setUpToolbarItem:(NSToolbarItem *)theItem;
|
||||||
|
+ (NSString *)title;
|
||||||
|
- (NSView *)preferencesView;
|
||||||
|
- (IBAction)saveFlaggedVideos:(id)sender;
|
||||||
|
- (IBAction)saveSafeSearch:(id)sender;
|
||||||
|
@end
|
106
Classes/YouView/Preferences/MGMParentalControlsPane.m
Normal file
106
Classes/YouView/Preferences/MGMParentalControlsPane.m
Normal file
@ -0,0 +1,106 @@
|
|||||||
|
//
|
||||||
|
// MGMParentalControlsPane.m
|
||||||
|
// YouView
|
||||||
|
//
|
||||||
|
// Created by Mr. Gecko on 7/29/10.
|
||||||
|
// Copyright (c) 2015 Mr. Gecko's Media (James Coleman). https://mrgeckosmedia.com/ All rights reserved.
|
||||||
|
//
|
||||||
|
|
||||||
|
#import "MGMParentalControlsPane.h"
|
||||||
|
#import "MGMController.h"
|
||||||
|
#import "MGMParentalControls.h"
|
||||||
|
#import "MGMAdminAction.h"
|
||||||
|
#import "MGMYVToolConnection.h"
|
||||||
|
#import "MGMYVToolProtocol.h"
|
||||||
|
#import <SecurityInterface/SFAuthorizationView.h>
|
||||||
|
|
||||||
|
@implementation MGMParentalControlsPane
|
||||||
|
- (id)initWithPreferences:(MGMPreferences *)thePreferences {
|
||||||
|
if (self = [super initWithPreferences:thePreferences]) {
|
||||||
|
if (![NSBundle loadNibNamed:@"ParentalControlsPane" owner:self]) {
|
||||||
|
NSLog(@"Unable to load Nib for General Preferences");
|
||||||
|
[self release];
|
||||||
|
self = nil;
|
||||||
|
} else {
|
||||||
|
rights = [MGMAdminAction rightsForCommands:[NSArray arrayWithObject:[[NSBundle mainBundle] pathForResource:@"YVTool" ofType:@""]]];
|
||||||
|
[autherizationView setAuthorizationRights:&rights];
|
||||||
|
[autherizationView setDelegate:self];
|
||||||
|
[autherizationView updateStatus:self];
|
||||||
|
[autherizationView setAutoupdate:YES];
|
||||||
|
|
||||||
|
MGMParentalControls *parentalControls = [MGMParentalControls standardParentalControls];
|
||||||
|
if ([parentalControls boolForKey:MGMAllowFlaggedVideos]) {
|
||||||
|
[flaggedVideo setState:NSOnState];
|
||||||
|
} else {
|
||||||
|
[flaggedVideo setState:NSOffState];
|
||||||
|
}
|
||||||
|
NSString *safeSearchValue = [parentalControls stringForKey:MGMSafeSearch];
|
||||||
|
if (safeSearchValue==nil) {
|
||||||
|
safeSearchValue = @"moderate";
|
||||||
|
}
|
||||||
|
if ([safeSearchValue isEqual:@"none"])
|
||||||
|
[safeSearch selectCellAtRow:0 column:0];
|
||||||
|
else if ([safeSearchValue isEqual:@"moderate"])
|
||||||
|
[safeSearch selectCellAtRow:1 column:0];
|
||||||
|
else if ([safeSearchValue isEqual:@"strict"])
|
||||||
|
[safeSearch selectCellAtRow:2 column:0];
|
||||||
|
[parentalControls setString:safeSearchValue forKey:MGMSafeSearch];
|
||||||
|
[parentalControls save];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return self;
|
||||||
|
}
|
||||||
|
- (void)dealloc {
|
||||||
|
[mainView release];
|
||||||
|
[yvToolConnection release];
|
||||||
|
free(rights.items);
|
||||||
|
[super dealloc];
|
||||||
|
}
|
||||||
|
+ (void)setUpToolbarItem:(NSToolbarItem *)theItem {
|
||||||
|
[theItem setLabel:[self title]];
|
||||||
|
[theItem setPaletteLabel:[theItem label]];
|
||||||
|
[theItem setImage:[NSImage imageNamed:@"ParentalControls"]];
|
||||||
|
}
|
||||||
|
+ (NSString *)title {
|
||||||
|
return @"Parental Controls";
|
||||||
|
}
|
||||||
|
- (NSView *)preferencesView {
|
||||||
|
return mainView;
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)authorizationViewDidAuthorize:(SFAuthorizationView *)view {
|
||||||
|
if (yvToolConnection==nil)
|
||||||
|
yvToolConnection = [[MGMYVToolConnection connectionWithAuthorization:[[autherizationView authorization] authorizationRef]] retain];
|
||||||
|
[yvToolConnection isServerConnected];
|
||||||
|
[flaggedVideo setEnabled:YES];
|
||||||
|
[safeSearch setEnabled:YES];
|
||||||
|
}
|
||||||
|
- (void)authorizationViewDidDeauthorize:(SFAuthorizationView *)view {
|
||||||
|
[flaggedVideo setEnabled:NO];
|
||||||
|
[safeSearch setEnabled:NO];
|
||||||
|
}
|
||||||
|
|
||||||
|
- (IBAction)saveFlaggedVideos:(id)sender {
|
||||||
|
if ([autherizationView authorizationState]==SFAuthorizationViewUnlockedState) {
|
||||||
|
MGMParentalControls *parentalControls = [MGMParentalControls standardParentalControls];
|
||||||
|
[[yvToolConnection server] changePremissionsForPath:[parentalControls path] to:@"666"];
|
||||||
|
[parentalControls setBool:([flaggedVideo state]==NSOnState) forKey:MGMAllowFlaggedVideos];
|
||||||
|
[[yvToolConnection server] changePremissionsForPath:[parentalControls path] to:@"644"];
|
||||||
|
[[yvToolConnection server] changeOwnerForPath:[parentalControls path] to:@"root"];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
- (IBAction)saveSafeSearch:(id)sender {
|
||||||
|
if ([autherizationView authorizationState]==SFAuthorizationViewUnlockedState) {
|
||||||
|
MGMParentalControls *parentalControls = [MGMParentalControls standardParentalControls];
|
||||||
|
[[yvToolConnection server] changePremissionsForPath:[parentalControls path] to:@"666"];
|
||||||
|
NSString *safeSearchValue = @"strict";
|
||||||
|
if ([safeSearch selectedRow]==0)
|
||||||
|
safeSearchValue = @"none";
|
||||||
|
else if ([safeSearch selectedRow]==1)
|
||||||
|
safeSearchValue = @"moderate";
|
||||||
|
[parentalControls setString:safeSearchValue forKey:MGMSafeSearch];
|
||||||
|
[[yvToolConnection server] changePremissionsForPath:[parentalControls path] to:@"644"];
|
||||||
|
[[yvToolConnection server] changeOwnerForPath:[parentalControls path] to:@"root"];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@end
|
37
Classes/YouView/Remote Control/AppleRemote.h
Normal file
37
Classes/YouView/Remote Control/AppleRemote.h
Normal file
@ -0,0 +1,37 @@
|
|||||||
|
/*****************************************************************************
|
||||||
|
* RemoteControlWrapper.h
|
||||||
|
* RemoteControlWrapper
|
||||||
|
*
|
||||||
|
* Created by Martin Kahr on 11.03.06 under a MIT-style license.
|
||||||
|
* Copyright (c) 2006 martinkahr.com. All rights reserved.
|
||||||
|
*
|
||||||
|
* Permission is hereby granted, free of charge, to any person obtaining a
|
||||||
|
* copy of this software and associated documentation files (the "Software"),
|
||||||
|
* to deal in the Software without restriction, including without limitation
|
||||||
|
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
||||||
|
* and/or sell copies of the Software, and to permit persons to whom the
|
||||||
|
* Software is furnished to do so, subject to the following conditions:
|
||||||
|
*
|
||||||
|
* The above copyright notice and this permission notice shall be included
|
||||||
|
* in all copies or substantial portions of the Software.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
||||||
|
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||||
|
* THE SOFTWARE.
|
||||||
|
*
|
||||||
|
*****************************************************************************/
|
||||||
|
|
||||||
|
#import <Cocoa/Cocoa.h>
|
||||||
|
#import "HIDRemoteControlDevice.h"
|
||||||
|
|
||||||
|
/* Interacts with the Apple Remote Control HID device
|
||||||
|
The class is not thread safe
|
||||||
|
*/
|
||||||
|
@interface AppleRemote : HIDRemoteControlDevice {
|
||||||
|
}
|
||||||
|
|
||||||
|
@end
|
105
Classes/YouView/Remote Control/AppleRemote.m
Normal file
105
Classes/YouView/Remote Control/AppleRemote.m
Normal file
@ -0,0 +1,105 @@
|
|||||||
|
/*****************************************************************************
|
||||||
|
* RemoteControlWrapper.m
|
||||||
|
* RemoteControlWrapper
|
||||||
|
*
|
||||||
|
* Created by Martin Kahr on 11.03.06 under a MIT-style license.
|
||||||
|
* Copyright (c) 2006 martinkahr.com. All rights reserved.
|
||||||
|
*
|
||||||
|
* Permission is hereby granted, free of charge, to any person obtaining a
|
||||||
|
* copy of this software and associated documentation files (the "Software"),
|
||||||
|
* to deal in the Software without restriction, including without limitation
|
||||||
|
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
||||||
|
* and/or sell copies of the Software, and to permit persons to whom the
|
||||||
|
* Software is furnished to do so, subject to the following conditions:
|
||||||
|
*
|
||||||
|
* The above copyright notice and this permission notice shall be included
|
||||||
|
* in all copies or substantial portions of the Software.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
||||||
|
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||||
|
* THE SOFTWARE.
|
||||||
|
*
|
||||||
|
*****************************************************************************/
|
||||||
|
|
||||||
|
#import "AppleRemote.h"
|
||||||
|
|
||||||
|
#import <mach/mach.h>
|
||||||
|
#import <mach/mach_error.h>
|
||||||
|
#import <IOKit/IOKitLib.h>
|
||||||
|
#import <IOKit/IOCFPlugIn.h>
|
||||||
|
#import <IOKit/hid/IOHIDKeys.h>
|
||||||
|
#import <GeckoReporter/MGMSystemInfo.h>
|
||||||
|
|
||||||
|
const char* AppleRemoteDeviceName = "AppleIRController";
|
||||||
|
|
||||||
|
// the WWDC 07 Leopard Build is missing the constant
|
||||||
|
#ifndef NSAppKitVersionNumber10_4
|
||||||
|
#define NSAppKitVersionNumber10_4 824
|
||||||
|
#endif
|
||||||
|
|
||||||
|
@implementation AppleRemote
|
||||||
|
|
||||||
|
+ (const char*) remoteControlDeviceName {
|
||||||
|
return AppleRemoteDeviceName;
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void) setCookieMappingInDictionary: (NSMutableDictionary*) _cookieToButtonMapping {
|
||||||
|
MGMSystemInfo *sysInfo = [[MGMSystemInfo new] autorelease];
|
||||||
|
if ([sysInfo isAfterSnowLeopard]) {
|
||||||
|
[_cookieToButtonMapping setObject:[NSNumber numberWithInt:kRemoteButtonPlus] forKey:@"33_31_30_21_20_2_"];
|
||||||
|
[_cookieToButtonMapping setObject:[NSNumber numberWithInt:kRemoteButtonMinus] forKey:@"33_32_30_21_20_2_"];
|
||||||
|
[_cookieToButtonMapping setObject:[NSNumber numberWithInt:kRemoteButtonMenu] forKey:@"33_22_21_20_2_33_22_21_20_2_"];
|
||||||
|
[_cookieToButtonMapping setObject:[NSNumber numberWithInt:kRemoteButtonPlay] forKey:@"33_23_21_20_2_33_23_21_20_2_"];
|
||||||
|
[_cookieToButtonMapping setObject:[NSNumber numberWithInt:kRemoteButtonRight] forKey:@"33_24_21_20_2_33_24_21_20_2_"];
|
||||||
|
[_cookieToButtonMapping setObject:[NSNumber numberWithInt:kRemoteButtonLeft] forKey:@"33_25_21_20_2_33_25_21_20_2_"];
|
||||||
|
[_cookieToButtonMapping setObject:[NSNumber numberWithInt:kRemoteButtonRight_Hold] forKey:@"33_21_20_14_12_2_"];
|
||||||
|
[_cookieToButtonMapping setObject:[NSNumber numberWithInt:kRemoteButtonLeft_Hold] forKey:@"33_21_20_13_12_2_"];
|
||||||
|
[_cookieToButtonMapping setObject:[NSNumber numberWithInt:kRemoteButtonMenu_Hold] forKey:@"33_21_20_2_33_21_20_2_"];
|
||||||
|
[_cookieToButtonMapping setObject:[NSNumber numberWithInt:kRemoteButtonPlay_Hold] forKey:@"37_33_21_20_2_37_33_21_20_2_"];
|
||||||
|
[_cookieToButtonMapping setObject:[NSNumber numberWithInt:kRemoteControl_Switched] forKey:@"19_"];
|
||||||
|
} else if ([sysInfo isAfterLeopard]) {
|
||||||
|
[_cookieToButtonMapping setObject:[NSNumber numberWithInt:kRemoteButtonPlus] forKey:@"31_29_28_19_18_"];
|
||||||
|
[_cookieToButtonMapping setObject:[NSNumber numberWithInt:kRemoteButtonMinus] forKey:@"31_30_28_19_18_"];
|
||||||
|
[_cookieToButtonMapping setObject:[NSNumber numberWithInt:kRemoteButtonMenu] forKey:@"31_20_19_18_31_20_19_18_"];
|
||||||
|
[_cookieToButtonMapping setObject:[NSNumber numberWithInt:kRemoteButtonPlay] forKey:@"31_21_19_18_31_21_19_18_"];
|
||||||
|
[_cookieToButtonMapping setObject:[NSNumber numberWithInt:kRemoteButtonRight] forKey:@"31_22_19_18_31_22_19_18_"];
|
||||||
|
[_cookieToButtonMapping setObject:[NSNumber numberWithInt:kRemoteButtonLeft] forKey:@"31_23_19_18_31_23_19_18_"];
|
||||||
|
[_cookieToButtonMapping setObject:[NSNumber numberWithInt:kRemoteButtonRight_Hold] forKey:@"31_19_18_4_2_"];
|
||||||
|
[_cookieToButtonMapping setObject:[NSNumber numberWithInt:kRemoteButtonLeft_Hold] forKey:@"31_19_18_3_2_"];
|
||||||
|
[_cookieToButtonMapping setObject:[NSNumber numberWithInt:kRemoteButtonMenu_Hold] forKey:@"31_19_18_31_19_18_"];
|
||||||
|
[_cookieToButtonMapping setObject:[NSNumber numberWithInt:kRemoteButtonPlay_Hold] forKey:@"35_31_19_18_35_31_19_18_"];
|
||||||
|
[_cookieToButtonMapping setObject:[NSNumber numberWithInt:kRemoteControl_Switched] forKey:@"19_"];
|
||||||
|
} else if ([sysInfo isAfterTiger]) {
|
||||||
|
[_cookieToButtonMapping setObject:[NSNumber numberWithInt:kRemoteButtonPlus] forKey:@"14_12_11_6_"];
|
||||||
|
[_cookieToButtonMapping setObject:[NSNumber numberWithInt:kRemoteButtonMinus] forKey:@"14_13_11_6_"];
|
||||||
|
[_cookieToButtonMapping setObject:[NSNumber numberWithInt:kRemoteButtonMenu] forKey:@"14_7_6_14_7_6_"];
|
||||||
|
[_cookieToButtonMapping setObject:[NSNumber numberWithInt:kRemoteButtonPlay] forKey:@"14_8_6_14_8_6_"];
|
||||||
|
[_cookieToButtonMapping setObject:[NSNumber numberWithInt:kRemoteButtonRight] forKey:@"14_9_6_14_9_6_"];
|
||||||
|
[_cookieToButtonMapping setObject:[NSNumber numberWithInt:kRemoteButtonLeft] forKey:@"14_10_6_14_10_6_"];
|
||||||
|
[_cookieToButtonMapping setObject:[NSNumber numberWithInt:kRemoteButtonRight_Hold] forKey:@"14_6_4_2_"];
|
||||||
|
[_cookieToButtonMapping setObject:[NSNumber numberWithInt:kRemoteButtonLeft_Hold] forKey:@"14_6_3_2_"];
|
||||||
|
[_cookieToButtonMapping setObject:[NSNumber numberWithInt:kRemoteButtonMenu_Hold] forKey:@"14_6_14_6_"];
|
||||||
|
[_cookieToButtonMapping setObject:[NSNumber numberWithInt:kRemoteButtonPlay_Hold] forKey:@"18_14_6_18_14_6_"];
|
||||||
|
[_cookieToButtonMapping setObject:[NSNumber numberWithInt:kRemoteControl_Switched] forKey:@"19_"];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void) sendRemoteButtonEvent: (RemoteControlEventIdentifier) event pressedDown: (BOOL) pressedDown {
|
||||||
|
if (pressedDown == NO && event == kRemoteButtonMenu_Hold) {
|
||||||
|
// There is no seperate event for pressed down on menu hold. We are simulating that event here
|
||||||
|
[super sendRemoteButtonEvent:event pressedDown:YES];
|
||||||
|
}
|
||||||
|
|
||||||
|
[super sendRemoteButtonEvent:event pressedDown:pressedDown];
|
||||||
|
|
||||||
|
if (pressedDown && (event == kRemoteButtonRight || event == kRemoteButtonLeft || event == kRemoteButtonPlay || event == kRemoteButtonMenu || event == kRemoteButtonPlay_Hold)) {
|
||||||
|
// There is no seperate event when the button is being released. We are simulating that event here
|
||||||
|
[super sendRemoteButtonEvent:event pressedDown:NO];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@end
|
49
Classes/YouView/Remote Control/GlobalKeyboardDevice.h
Normal file
49
Classes/YouView/Remote Control/GlobalKeyboardDevice.h
Normal file
@ -0,0 +1,49 @@
|
|||||||
|
/*****************************************************************************
|
||||||
|
* GlobalKeyboardDevice.h
|
||||||
|
* RemoteControlWrapper
|
||||||
|
*
|
||||||
|
* Created by Martin Kahr on 11.03.06 under a MIT-style license.
|
||||||
|
* Copyright (c) 2006 martinkahr.com. All rights reserved.
|
||||||
|
*
|
||||||
|
* Permission is hereby granted, free of charge, to any person obtaining a
|
||||||
|
* copy of this software and associated documentation files (the "Software"),
|
||||||
|
* to deal in the Software without restriction, including without limitation
|
||||||
|
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
||||||
|
* and/or sell copies of the Software, and to permit persons to whom the
|
||||||
|
* Software is furnished to do so, subject to the following conditions:
|
||||||
|
*
|
||||||
|
* The above copyright notice and this permission notice shall be included
|
||||||
|
* in all copies or substantial portions of the Software.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
||||||
|
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||||
|
* THE SOFTWARE.
|
||||||
|
*
|
||||||
|
*****************************************************************************/
|
||||||
|
|
||||||
|
#import <Cocoa/Cocoa.h>
|
||||||
|
#import <Carbon/Carbon.h>
|
||||||
|
|
||||||
|
#import "RemoteControl.h"
|
||||||
|
|
||||||
|
/*
|
||||||
|
This class registers for a number of global keyboard shortcuts to simulate a remote control
|
||||||
|
*/
|
||||||
|
@interface GlobalKeyboardDevice : RemoteControl {
|
||||||
|
|
||||||
|
NSMutableDictionary* hotKeyRemoteEventMapping;
|
||||||
|
EventHandlerRef eventHandlerRef;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void) mapRemoteButton: (RemoteControlEventIdentifier) remoteButtonIdentifier defaultKeycode: (unsigned int) defaultKeycode defaultModifiers: (unsigned int) defaultModifiers;
|
||||||
|
|
||||||
|
- (BOOL)registerHotKeyCode: (unsigned int) keycode modifiers: (unsigned int) modifiers remoteEventIdentifier: (RemoteControlEventIdentifier) identifier;
|
||||||
|
|
||||||
|
static OSStatus hotKeyEventHandler(EventHandlerCallRef inHandlerRef, EventRef inEvent, void* refCon );
|
||||||
|
|
||||||
|
@end
|
241
Classes/YouView/Remote Control/GlobalKeyboardDevice.m
Normal file
241
Classes/YouView/Remote Control/GlobalKeyboardDevice.m
Normal file
@ -0,0 +1,241 @@
|
|||||||
|
/*****************************************************************************
|
||||||
|
* GlobalKeyboardDevice.m
|
||||||
|
* RemoteControlWrapper
|
||||||
|
*
|
||||||
|
* Created by Martin Kahr on 11.03.06 under a MIT-style license.
|
||||||
|
* Copyright (c) 2006 martinkahr.com. All rights reserved.
|
||||||
|
*
|
||||||
|
* Permission is hereby granted, free of charge, to any person obtaining a
|
||||||
|
* copy of this software and associated documentation files (the "Software"),
|
||||||
|
* to deal in the Software without restriction, including without limitation
|
||||||
|
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
||||||
|
* and/or sell copies of the Software, and to permit persons to whom the
|
||||||
|
* Software is furnished to do so, subject to the following conditions:
|
||||||
|
*
|
||||||
|
* The above copyright notice and this permission notice shall be included
|
||||||
|
* in all copies or substantial portions of the Software.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
||||||
|
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||||
|
* THE SOFTWARE.
|
||||||
|
*
|
||||||
|
*****************************************************************************/
|
||||||
|
|
||||||
|
|
||||||
|
#import "GlobalKeyboardDevice.h"
|
||||||
|
|
||||||
|
#define F1 122
|
||||||
|
#define F2 120
|
||||||
|
#define F3 99
|
||||||
|
#define F4 118
|
||||||
|
#define F5 96
|
||||||
|
#define F6 97
|
||||||
|
#define F7 98
|
||||||
|
|
||||||
|
/*
|
||||||
|
the following default keys are read and shall be used to change the keyboard mapping
|
||||||
|
|
||||||
|
mac.remotecontrols.GlobalKeyboardDevice.plus_modifiers
|
||||||
|
mac.remotecontrols.GlobalKeyboardDevice.plus_keycode
|
||||||
|
mac.remotecontrols.GlobalKeyboardDevice.minus_modifiers
|
||||||
|
mac.remotecontrols.GlobalKeyboardDevice.minus_keycode
|
||||||
|
mac.remotecontrols.GlobalKeyboardDevice.play_modifiers
|
||||||
|
mac.remotecontrols.GlobalKeyboardDevice.play_keycode
|
||||||
|
mac.remotecontrols.GlobalKeyboardDevice.left_modifiers
|
||||||
|
mac.remotecontrols.GlobalKeyboardDevice.left_keycode
|
||||||
|
mac.remotecontrols.GlobalKeyboardDevice.right_modifiers
|
||||||
|
mac.remotecontrols.GlobalKeyboardDevice.right_keycode
|
||||||
|
mac.remotecontrols.GlobalKeyboardDevice.menu_modifiers
|
||||||
|
mac.remotecontrols.GlobalKeyboardDevice.menu_keycode
|
||||||
|
mac.remotecontrols.GlobalKeyboardDevice.playhold_modifiers
|
||||||
|
mac.remotecontrols.GlobalKeyboardDevice.playhold_keycode
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
@implementation GlobalKeyboardDevice
|
||||||
|
|
||||||
|
- (id) initWithDelegate: (id) _remoteControlDelegate {
|
||||||
|
if (self = [super initWithDelegate: _remoteControlDelegate]) {
|
||||||
|
hotKeyRemoteEventMapping = [NSMutableDictionary new];
|
||||||
|
|
||||||
|
unsigned int modifiers = cmdKey + shiftKey /*+ optionKey*/ + controlKey;
|
||||||
|
|
||||||
|
[self mapRemoteButton:kRemoteButtonPlus defaultKeycode:F1 defaultModifiers:modifiers];
|
||||||
|
[self mapRemoteButton:kRemoteButtonMinus defaultKeycode:F2 defaultModifiers:modifiers];
|
||||||
|
[self mapRemoteButton:kRemoteButtonPlay defaultKeycode:F3 defaultModifiers:modifiers];
|
||||||
|
[self mapRemoteButton:kRemoteButtonLeft defaultKeycode:F4 defaultModifiers:modifiers];
|
||||||
|
[self mapRemoteButton:kRemoteButtonRight defaultKeycode:F5 defaultModifiers:modifiers];
|
||||||
|
[self mapRemoteButton:kRemoteButtonMenu defaultKeycode:F6 defaultModifiers:modifiers];
|
||||||
|
[self mapRemoteButton:kRemoteButtonPlay_Hold defaultKeycode:F7 defaultModifiers:modifiers];
|
||||||
|
}
|
||||||
|
return self;
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void) dealloc {
|
||||||
|
[hotKeyRemoteEventMapping release];
|
||||||
|
[super dealloc];
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void) mapRemoteButton: (RemoteControlEventIdentifier) remoteButtonIdentifier defaultKeycode: (unsigned int) defaultKeycode defaultModifiers: (unsigned int) defaultModifiers {
|
||||||
|
NSString* defaultsKey = @"unknown";
|
||||||
|
|
||||||
|
switch(remoteButtonIdentifier) {
|
||||||
|
case kRemoteButtonPlus:
|
||||||
|
defaultsKey = @"plus";
|
||||||
|
break;
|
||||||
|
case kRemoteButtonMinus:
|
||||||
|
defaultsKey = @"minus";
|
||||||
|
break;
|
||||||
|
case kRemoteButtonMenu:
|
||||||
|
defaultsKey = @"menu";
|
||||||
|
break;
|
||||||
|
case kRemoteButtonPlay:
|
||||||
|
defaultsKey = @"play";
|
||||||
|
break;
|
||||||
|
case kRemoteButtonRight:
|
||||||
|
defaultsKey = @"right";
|
||||||
|
break;
|
||||||
|
case kRemoteButtonLeft:
|
||||||
|
defaultsKey = @"left";
|
||||||
|
break;
|
||||||
|
case kRemoteButtonPlay_Hold:
|
||||||
|
defaultsKey = @"playhold";
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
NSLog(@"Unknown global keyboard defaults key for remote button identifier %d", remoteButtonIdentifier);
|
||||||
|
}
|
||||||
|
|
||||||
|
NSNumber* modifiersCfg = [[NSUserDefaults standardUserDefaults] objectForKey: [NSString stringWithFormat: @"mac.remotecontrols.GlobalKeyboardDevice.%@_modifiers", defaultsKey]];
|
||||||
|
NSNumber* keycodeCfg = [[NSUserDefaults standardUserDefaults] objectForKey: [NSString stringWithFormat: @"mac.remotecontrols.GlobalKeyboardDevice.%@_keycode", defaultsKey]];
|
||||||
|
|
||||||
|
unsigned int modifiers = defaultModifiers;
|
||||||
|
if (modifiersCfg) modifiers = [modifiersCfg unsignedIntValue];
|
||||||
|
|
||||||
|
unsigned int keycode = defaultKeycode;
|
||||||
|
if (keycodeCfg) keycode = [keycodeCfg unsignedIntValue];
|
||||||
|
|
||||||
|
[self registerHotKeyCode: keycode modifiers: modifiers remoteEventIdentifier: remoteButtonIdentifier];
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void) setListeningToRemote: (BOOL) value {
|
||||||
|
if (value == [self isListeningToRemote]) return;
|
||||||
|
if (value) {
|
||||||
|
[self startListening: self];
|
||||||
|
} else {
|
||||||
|
[self stopListening: self];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
- (BOOL) isListeningToRemote {
|
||||||
|
return (eventHandlerRef!=NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
- (IBAction) startListening: (id) sender {
|
||||||
|
|
||||||
|
if (eventHandlerRef) return;
|
||||||
|
|
||||||
|
EventTypeSpec eventSpec[2] = {
|
||||||
|
{ kEventClassKeyboard, kEventHotKeyPressed },
|
||||||
|
{ kEventClassKeyboard, kEventHotKeyReleased }
|
||||||
|
};
|
||||||
|
|
||||||
|
InstallEventHandler( GetEventDispatcherTarget(),
|
||||||
|
(EventHandlerProcPtr)hotKeyEventHandler,
|
||||||
|
2, eventSpec, self, &eventHandlerRef);
|
||||||
|
}
|
||||||
|
- (IBAction) stopListening: (id) sender {
|
||||||
|
RemoveEventHandler(eventHandlerRef);
|
||||||
|
eventHandlerRef = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
- (BOOL) sendsEventForButtonIdentifier: (RemoteControlEventIdentifier) identifier {
|
||||||
|
NSEnumerator* values = [hotKeyRemoteEventMapping objectEnumerator];
|
||||||
|
NSNumber* remoteIdentifier;
|
||||||
|
while( (remoteIdentifier = [values nextObject]) ) {
|
||||||
|
if ([remoteIdentifier unsignedIntValue] == identifier) return YES;
|
||||||
|
}
|
||||||
|
return NO;
|
||||||
|
}
|
||||||
|
|
||||||
|
+ (const char*) remoteControlDeviceName {
|
||||||
|
return "Keyboard";
|
||||||
|
}
|
||||||
|
|
||||||
|
- (BOOL)registerHotKeyCode: (unsigned int) keycode modifiers: (unsigned int) modifiers remoteEventIdentifier: (RemoteControlEventIdentifier) identifier {
|
||||||
|
OSStatus err;
|
||||||
|
EventHotKeyID hotKeyID;
|
||||||
|
EventHotKeyRef carbonHotKey;
|
||||||
|
|
||||||
|
hotKeyID.signature = 'PTHk';
|
||||||
|
hotKeyID.id = (long)keycode;
|
||||||
|
|
||||||
|
err = RegisterEventHotKey(keycode, modifiers, hotKeyID, GetEventDispatcherTarget(), 0, &carbonHotKey );
|
||||||
|
|
||||||
|
if( err )
|
||||||
|
return NO;
|
||||||
|
|
||||||
|
[hotKeyRemoteEventMapping setObject: [NSNumber numberWithInt:identifier] forKey: [NSNumber numberWithUnsignedInt: hotKeyID.id]];
|
||||||
|
|
||||||
|
return YES;
|
||||||
|
}
|
||||||
|
/*
|
||||||
|
- (void)unregisterHotKey: (PTHotKey*)hotKey
|
||||||
|
{
|
||||||
|
OSStatus err;
|
||||||
|
EventHotKeyRef carbonHotKey;
|
||||||
|
NSValue* key;
|
||||||
|
|
||||||
|
if( [[self allHotKeys] containsObject: hotKey] == NO )
|
||||||
|
return;
|
||||||
|
|
||||||
|
carbonHotKey = [self _carbonHotKeyForHotKey: hotKey];
|
||||||
|
NSAssert( carbonHotKey != nil, @"" );
|
||||||
|
|
||||||
|
err = UnregisterEventHotKey( carbonHotKey );
|
||||||
|
//Watch as we ignore 'err':
|
||||||
|
|
||||||
|
key = [NSValue valueWithPointer: carbonHotKey];
|
||||||
|
[mHotKeys removeObjectForKey: key];
|
||||||
|
|
||||||
|
[self _updateEventHandler];
|
||||||
|
|
||||||
|
//See that? Completely ignored
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
|
- (RemoteControlEventIdentifier) remoteControlEventIdentifierForID: (unsigned int) id {
|
||||||
|
NSNumber* remoteEventIdentifier = [hotKeyRemoteEventMapping objectForKey:[NSNumber numberWithUnsignedInt: id]];
|
||||||
|
return [remoteEventIdentifier unsignedIntValue];
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void) sendRemoteButtonEvent: (RemoteControlEventIdentifier) event pressedDown: (BOOL) pressedDown {
|
||||||
|
[delegate sendRemoteButtonEvent: event pressedDown: pressedDown remoteControl:self];
|
||||||
|
}
|
||||||
|
|
||||||
|
static RemoteControlEventIdentifier lastEvent;
|
||||||
|
|
||||||
|
static OSStatus hotKeyEventHandler(EventHandlerCallRef inHandlerRef, EventRef inEvent, void* userData )
|
||||||
|
{
|
||||||
|
GlobalKeyboardDevice* keyboardDevice = (GlobalKeyboardDevice*) userData;
|
||||||
|
EventHotKeyID hkCom;
|
||||||
|
GetEventParameter(inEvent,kEventParamDirectObject,typeEventHotKeyID,NULL,sizeof(hkCom),NULL,&hkCom);
|
||||||
|
|
||||||
|
RemoteControlEventIdentifier identifier = [keyboardDevice remoteControlEventIdentifierForID:hkCom.id];
|
||||||
|
if (identifier == 0) return noErr;
|
||||||
|
|
||||||
|
BOOL pressedDown = YES;
|
||||||
|
if (identifier != lastEvent) {
|
||||||
|
lastEvent = identifier;
|
||||||
|
} else {
|
||||||
|
lastEvent = 0;
|
||||||
|
pressedDown = NO;
|
||||||
|
}
|
||||||
|
[keyboardDevice sendRemoteButtonEvent: identifier pressedDown: pressedDown];
|
||||||
|
|
||||||
|
return noErr;
|
||||||
|
}
|
||||||
|
|
||||||
|
@end
|
64
Classes/YouView/Remote Control/HIDRemoteControlDevice.h
Normal file
64
Classes/YouView/Remote Control/HIDRemoteControlDevice.h
Normal file
@ -0,0 +1,64 @@
|
|||||||
|
/*****************************************************************************
|
||||||
|
* HIDRemoteControlDevice.h
|
||||||
|
* RemoteControlWrapper
|
||||||
|
*
|
||||||
|
* Created by Martin Kahr on 11.03.06 under a MIT-style license.
|
||||||
|
* Copyright (c) 2006 martinkahr.com. All rights reserved.
|
||||||
|
*
|
||||||
|
* Permission is hereby granted, free of charge, to any person obtaining a
|
||||||
|
* copy of this software and associated documentation files (the "Software"),
|
||||||
|
* to deal in the Software without restriction, including without limitation
|
||||||
|
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
||||||
|
* and/or sell copies of the Software, and to permit persons to whom the
|
||||||
|
* Software is furnished to do so, subject to the following conditions:
|
||||||
|
*
|
||||||
|
* The above copyright notice and this permission notice shall be included
|
||||||
|
* in all copies or substantial portions of the Software.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
||||||
|
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||||
|
* THE SOFTWARE.
|
||||||
|
*
|
||||||
|
*****************************************************************************/
|
||||||
|
|
||||||
|
#import <Cocoa/Cocoa.h>
|
||||||
|
#import <IOKit/hid/IOHIDLib.h>
|
||||||
|
|
||||||
|
#import "RemoteControl.h"
|
||||||
|
|
||||||
|
/*
|
||||||
|
Base class for HID based remote control devices
|
||||||
|
*/
|
||||||
|
@interface HIDRemoteControlDevice : RemoteControl {
|
||||||
|
IOHIDDeviceInterface** hidDeviceInterface;
|
||||||
|
IOHIDQueueInterface** queue;
|
||||||
|
NSMutableArray* allCookies;
|
||||||
|
NSMutableDictionary* cookieToButtonMapping;
|
||||||
|
CFRunLoopSourceRef eventSource;
|
||||||
|
|
||||||
|
BOOL fixSecureEventInputBug;
|
||||||
|
BOOL openInExclusiveMode;
|
||||||
|
BOOL processesBacklog;
|
||||||
|
|
||||||
|
int supportedButtonEvents;
|
||||||
|
}
|
||||||
|
|
||||||
|
// When your application needs to much time on the main thread when processing an event other events
|
||||||
|
// may already be received which are put on a backlog. As soon as your main thread
|
||||||
|
// has some spare time this backlog is processed and may flood your delegate with calls.
|
||||||
|
// Backlog processing is turned off by default.
|
||||||
|
- (BOOL) processesBacklog;
|
||||||
|
- (void) setProcessesBacklog: (BOOL) value;
|
||||||
|
|
||||||
|
// methods that should be overwritten by subclasses
|
||||||
|
- (void) setCookieMappingInDictionary: (NSMutableDictionary*) cookieToButtonMapping;
|
||||||
|
|
||||||
|
- (void) sendRemoteButtonEvent: (RemoteControlEventIdentifier) event pressedDown: (BOOL) pressedDown;
|
||||||
|
|
||||||
|
+ (BOOL) isRemoteAvailable;
|
||||||
|
|
||||||
|
@end
|
517
Classes/YouView/Remote Control/HIDRemoteControlDevice.m
Normal file
517
Classes/YouView/Remote Control/HIDRemoteControlDevice.m
Normal file
@ -0,0 +1,517 @@
|
|||||||
|
/*****************************************************************************
|
||||||
|
* HIDRemoteControlDevice.m
|
||||||
|
* RemoteControlWrapper
|
||||||
|
*
|
||||||
|
* Created by Martin Kahr on 11.03.06 under a MIT-style license.
|
||||||
|
* Copyright (c) 2006 martinkahr.com. All rights reserved.
|
||||||
|
*
|
||||||
|
* Permission is hereby granted, free of charge, to any person obtaining a
|
||||||
|
* copy of this software and associated documentation files (the "Software"),
|
||||||
|
* to deal in the Software without restriction, including without limitation
|
||||||
|
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
||||||
|
* and/or sell copies of the Software, and to permit persons to whom the
|
||||||
|
* Software is furnished to do so, subject to the following conditions:
|
||||||
|
*
|
||||||
|
* The above copyright notice and this permission notice shall be included
|
||||||
|
* in all copies or substantial portions of the Software.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
||||||
|
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||||
|
* THE SOFTWARE.
|
||||||
|
*
|
||||||
|
*****************************************************************************/
|
||||||
|
|
||||||
|
#import "HIDRemoteControlDevice.h"
|
||||||
|
|
||||||
|
#import <mach/mach.h>
|
||||||
|
#import <mach/mach_error.h>
|
||||||
|
#import <IOKit/IOKitLib.h>
|
||||||
|
#import <IOKit/IOCFPlugIn.h>
|
||||||
|
#import <IOKit/hid/IOHIDKeys.h>
|
||||||
|
#import <Carbon/Carbon.h>
|
||||||
|
|
||||||
|
@interface HIDRemoteControlDevice (PrivateMethods)
|
||||||
|
- (NSDictionary*) cookieToButtonMapping;
|
||||||
|
- (IOHIDQueueInterface**) queue;
|
||||||
|
- (IOHIDDeviceInterface**) hidDeviceInterface;
|
||||||
|
- (void) handleEventWithCookieString: (NSString*) cookieString sumOfValues: (SInt32) sumOfValues;
|
||||||
|
- (void) removeNotifcationObserver;
|
||||||
|
- (void) remoteControlAvailable:(NSNotification *)notification;
|
||||||
|
|
||||||
|
@end
|
||||||
|
|
||||||
|
@interface HIDRemoteControlDevice (IOKitMethods)
|
||||||
|
+ (io_object_t) findRemoteDevice;
|
||||||
|
- (IOHIDDeviceInterface**) createInterfaceForDevice: (io_object_t) hidDevice;
|
||||||
|
- (BOOL) initializeCookies;
|
||||||
|
- (BOOL) openDevice;
|
||||||
|
@end
|
||||||
|
|
||||||
|
@implementation HIDRemoteControlDevice
|
||||||
|
|
||||||
|
+ (const char*) remoteControlDeviceName {
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
|
||||||
|
+ (BOOL) isRemoteAvailable {
|
||||||
|
io_object_t hidDevice = [self findRemoteDevice];
|
||||||
|
if (hidDevice != 0) {
|
||||||
|
IOObjectRelease(hidDevice);
|
||||||
|
return YES;
|
||||||
|
} else {
|
||||||
|
return NO;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
- (id) initWithDelegate: (id) _remoteControlDelegate {
|
||||||
|
if (self = [super initWithDelegate:_remoteControlDelegate]) {
|
||||||
|
if (![[self class] isRemoteAvailable]) {
|
||||||
|
[self release];
|
||||||
|
self = nil;
|
||||||
|
} else {
|
||||||
|
openInExclusiveMode = YES;
|
||||||
|
queue = NULL;
|
||||||
|
hidDeviceInterface = NULL;
|
||||||
|
cookieToButtonMapping = [NSMutableDictionary new];
|
||||||
|
|
||||||
|
[self setCookieMappingInDictionary: cookieToButtonMapping];
|
||||||
|
|
||||||
|
NSEnumerator* enumerator = [cookieToButtonMapping objectEnumerator];
|
||||||
|
NSNumber* identifier;
|
||||||
|
supportedButtonEvents = 0;
|
||||||
|
while(identifier = [enumerator nextObject]) {
|
||||||
|
supportedButtonEvents |= [identifier intValue];
|
||||||
|
}
|
||||||
|
|
||||||
|
fixSecureEventInputBug = [[NSUserDefaults standardUserDefaults] boolForKey: @"remoteControlWrapperFixSecureEventInputBug"];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return self;
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void) dealloc {
|
||||||
|
[self removeNotifcationObserver];
|
||||||
|
[self stopListening:self];
|
||||||
|
[cookieToButtonMapping release];
|
||||||
|
[super dealloc];
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void) sendRemoteButtonEvent: (RemoteControlEventIdentifier) event pressedDown: (BOOL) pressedDown {
|
||||||
|
[delegate sendRemoteButtonEvent: event pressedDown: pressedDown remoteControl:self];
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void) setCookieMappingInDictionary: (NSMutableDictionary*) cookieToButtonMapping {
|
||||||
|
}
|
||||||
|
- (int) remoteIdSwitchCookie {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
- (BOOL) sendsEventForButtonIdentifier: (RemoteControlEventIdentifier) identifier {
|
||||||
|
return (supportedButtonEvents & identifier) == identifier;
|
||||||
|
}
|
||||||
|
|
||||||
|
- (BOOL) isListeningToRemote {
|
||||||
|
return (hidDeviceInterface != NULL && allCookies != NULL && queue != NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void) setListeningToRemote: (BOOL) value {
|
||||||
|
if (value == NO) {
|
||||||
|
[self stopListening:self];
|
||||||
|
} else {
|
||||||
|
[self startListening:self];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
- (BOOL) isOpenInExclusiveMode {
|
||||||
|
return openInExclusiveMode;
|
||||||
|
}
|
||||||
|
- (void) setOpenInExclusiveMode: (BOOL) value {
|
||||||
|
openInExclusiveMode = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
- (BOOL) processesBacklog {
|
||||||
|
return processesBacklog;
|
||||||
|
}
|
||||||
|
- (void) setProcessesBacklog: (BOOL) value {
|
||||||
|
processesBacklog = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
- (IBAction) startListening: (id) sender {
|
||||||
|
if ([self isListeningToRemote]) return;
|
||||||
|
|
||||||
|
// 4th July 2007
|
||||||
|
//
|
||||||
|
// A security update in february of 2007 introduced an odd behavior.
|
||||||
|
// Whenever SecureEventInput is activated or deactivated the exclusive access
|
||||||
|
// to the remote control device is lost. This leads to very strange behavior where
|
||||||
|
// a press on the Menu button activates FrontRow while your app still gets the event.
|
||||||
|
// A great number of people have complained about this.
|
||||||
|
//
|
||||||
|
// Enabling the SecureEventInput and keeping it enabled does the trick.
|
||||||
|
//
|
||||||
|
// I'm pretty sure this is a kind of bug at Apple and I'm in contact with the responsible
|
||||||
|
// Apple Engineer. This solution is not a perfect one - I know.
|
||||||
|
// One of the side effects is that applications that listen for special global keyboard shortcuts (like Quicksilver)
|
||||||
|
// may get into problems as they no longer get the events.
|
||||||
|
// As there is no official Apple Remote API from Apple I also failed to open a technical incident on this.
|
||||||
|
//
|
||||||
|
// Note that there is a corresponding DisableSecureEventInput in the stopListening method below.
|
||||||
|
//
|
||||||
|
if ([self isOpenInExclusiveMode] && fixSecureEventInputBug) EnableSecureEventInput();
|
||||||
|
|
||||||
|
[self removeNotifcationObserver];
|
||||||
|
|
||||||
|
io_object_t hidDevice = [[self class] findRemoteDevice];
|
||||||
|
if (hidDevice == 0) return;
|
||||||
|
|
||||||
|
if ([self createInterfaceForDevice:hidDevice] == NULL) {
|
||||||
|
goto error;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ([self initializeCookies]==NO) {
|
||||||
|
goto error;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ([self openDevice]==NO) {
|
||||||
|
goto error;
|
||||||
|
}
|
||||||
|
// be KVO friendly
|
||||||
|
[self willChangeValueForKey:@"listeningToRemote"];
|
||||||
|
[self didChangeValueForKey:@"listeningToRemote"];
|
||||||
|
goto cleanup;
|
||||||
|
|
||||||
|
error:
|
||||||
|
[self stopListening:self];
|
||||||
|
DisableSecureEventInput();
|
||||||
|
|
||||||
|
cleanup:
|
||||||
|
IOObjectRelease(hidDevice);
|
||||||
|
}
|
||||||
|
|
||||||
|
- (IBAction) stopListening: (id) sender {
|
||||||
|
if ([self isListeningToRemote]==NO) return;
|
||||||
|
|
||||||
|
BOOL sendNotification = NO;
|
||||||
|
|
||||||
|
if (eventSource != NULL) {
|
||||||
|
CFRunLoopRemoveSource(CFRunLoopGetCurrent(), eventSource, kCFRunLoopDefaultMode);
|
||||||
|
CFRelease(eventSource);
|
||||||
|
eventSource = NULL;
|
||||||
|
}
|
||||||
|
if (queue != NULL) {
|
||||||
|
(*queue)->stop(queue);
|
||||||
|
|
||||||
|
//dispose of queue
|
||||||
|
(*queue)->dispose(queue);
|
||||||
|
|
||||||
|
//release the queue we allocated
|
||||||
|
(*queue)->Release(queue);
|
||||||
|
|
||||||
|
queue = NULL;
|
||||||
|
|
||||||
|
sendNotification = YES;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (allCookies != nil) {
|
||||||
|
[allCookies release];
|
||||||
|
allCookies = nil;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (hidDeviceInterface != NULL) {
|
||||||
|
//close the device
|
||||||
|
(*hidDeviceInterface)->close(hidDeviceInterface);
|
||||||
|
|
||||||
|
//release the interface
|
||||||
|
(*hidDeviceInterface)->Release(hidDeviceInterface);
|
||||||
|
|
||||||
|
hidDeviceInterface = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ([self isOpenInExclusiveMode] && fixSecureEventInputBug) DisableSecureEventInput();
|
||||||
|
|
||||||
|
if ([self isOpenInExclusiveMode] && sendNotification) {
|
||||||
|
[[self class] sendFinishedNotifcationForAppIdentifier: nil];
|
||||||
|
}
|
||||||
|
// be KVO friendly
|
||||||
|
[self willChangeValueForKey:@"listeningToRemote"];
|
||||||
|
[self didChangeValueForKey:@"listeningToRemote"];
|
||||||
|
}
|
||||||
|
|
||||||
|
@end
|
||||||
|
|
||||||
|
@implementation HIDRemoteControlDevice (PrivateMethods)
|
||||||
|
|
||||||
|
- (IOHIDQueueInterface**) queue {
|
||||||
|
return queue;
|
||||||
|
}
|
||||||
|
|
||||||
|
- (IOHIDDeviceInterface**) hidDeviceInterface {
|
||||||
|
return hidDeviceInterface;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
- (NSDictionary*) cookieToButtonMapping {
|
||||||
|
return cookieToButtonMapping;
|
||||||
|
}
|
||||||
|
|
||||||
|
- (NSString*) validCookieSubstring: (NSString*) cookieString {
|
||||||
|
if (cookieString == nil || [cookieString length] == 0) return nil;
|
||||||
|
NSEnumerator* keyEnum = [[self cookieToButtonMapping] keyEnumerator];
|
||||||
|
NSString* key;
|
||||||
|
while(key = [keyEnum nextObject]) {
|
||||||
|
NSRange range = [cookieString rangeOfString:key];
|
||||||
|
if (range.location == 0) return key;
|
||||||
|
}
|
||||||
|
return nil;
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void) handleEventWithCookieString: (NSString*) cookieString sumOfValues: (SInt32) sumOfValues {
|
||||||
|
/*
|
||||||
|
if (previousRemainingCookieString) {
|
||||||
|
cookieString = [previousRemainingCookieString stringByAppendingString: cookieString];
|
||||||
|
NSLog(@"New cookie string is %@", cookieString);
|
||||||
|
[previousRemainingCookieString release], previousRemainingCookieString=nil;
|
||||||
|
}*/
|
||||||
|
if (cookieString == nil || [cookieString length] == 0) return;
|
||||||
|
|
||||||
|
NSNumber* buttonId = [[self cookieToButtonMapping] objectForKey: cookieString];
|
||||||
|
if (buttonId != nil) {
|
||||||
|
[self sendRemoteButtonEvent: [buttonId intValue] pressedDown: (sumOfValues>0)];
|
||||||
|
} else {
|
||||||
|
// let's see if a number of events are stored in the cookie string. this does
|
||||||
|
// happen when the main thread is too busy to handle all incoming events in time.
|
||||||
|
NSString* subCookieString;
|
||||||
|
NSString* lastSubCookieString=nil;
|
||||||
|
while([subCookieString isEqual:[self validCookieSubstring: cookieString]]) {
|
||||||
|
cookieString = [cookieString substringFromIndex: [subCookieString length]];
|
||||||
|
lastSubCookieString = subCookieString;
|
||||||
|
if (processesBacklog) [self handleEventWithCookieString: subCookieString sumOfValues:sumOfValues];
|
||||||
|
}
|
||||||
|
if (processesBacklog == NO && lastSubCookieString != nil) {
|
||||||
|
// process the last event of the backlog and assume that the button is not pressed down any longer.
|
||||||
|
// The events in the backlog do not seem to be in order and therefore (in rare cases) the last event might be
|
||||||
|
// a button pressed down event while in reality the user has released it.
|
||||||
|
// NSLog(@"processing last event of backlog");
|
||||||
|
[self handleEventWithCookieString: lastSubCookieString sumOfValues:0];
|
||||||
|
}
|
||||||
|
if ([cookieString length] > 0) {
|
||||||
|
NSLog(@"Unknown button for cookiestring %@", cookieString);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void) removeNotifcationObserver {
|
||||||
|
[[NSDistributedNotificationCenter defaultCenter] removeObserver:self name:FINISHED_USING_REMOTE_CONTROL_NOTIFICATION object:nil];
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void) remoteControlAvailable:(NSNotification *)notification {
|
||||||
|
[self removeNotifcationObserver];
|
||||||
|
[self startListening: self];
|
||||||
|
}
|
||||||
|
|
||||||
|
@end
|
||||||
|
|
||||||
|
/* Callback method for the device queue
|
||||||
|
Will be called for any event of any type (cookie) to which we subscribe
|
||||||
|
*/
|
||||||
|
static void QueueCallbackFunction(void* target, IOReturn result, void* refcon, void* sender) {
|
||||||
|
if (target < 0) {
|
||||||
|
NSLog(@"QueueCallbackFunction called with invalid target!");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
NSAutoreleasePool* pool = [NSAutoreleasePool new];
|
||||||
|
|
||||||
|
HIDRemoteControlDevice* remote = (HIDRemoteControlDevice*)target;
|
||||||
|
IOHIDEventStruct event;
|
||||||
|
AbsoluteTime zeroTime = {0,0};
|
||||||
|
NSMutableString* cookieString = [NSMutableString string];
|
||||||
|
SInt32 sumOfValues = 0;
|
||||||
|
while (result == kIOReturnSuccess)
|
||||||
|
{
|
||||||
|
result = (*[remote queue])->getNextEvent([remote queue], &event, zeroTime, 0);
|
||||||
|
if ( result != kIOReturnSuccess )
|
||||||
|
continue;
|
||||||
|
|
||||||
|
//printf("%d %d %d\n", event.elementCookie, event.value, event.longValue);
|
||||||
|
|
||||||
|
if (((int)event.elementCookie)!=5) {
|
||||||
|
sumOfValues+=event.value;
|
||||||
|
[cookieString appendString:[NSString stringWithFormat:@"%d_", event.elementCookie]];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
[remote handleEventWithCookieString: cookieString sumOfValues: sumOfValues];
|
||||||
|
|
||||||
|
[pool release];
|
||||||
|
}
|
||||||
|
|
||||||
|
@implementation HIDRemoteControlDevice (IOKitMethods)
|
||||||
|
|
||||||
|
- (IOHIDDeviceInterface**) createInterfaceForDevice: (io_object_t) hidDevice {
|
||||||
|
io_name_t className;
|
||||||
|
IOCFPlugInInterface** plugInInterface = NULL;
|
||||||
|
HRESULT plugInResult = S_OK;
|
||||||
|
SInt32 score = 0;
|
||||||
|
IOReturn ioReturnValue = kIOReturnSuccess;
|
||||||
|
|
||||||
|
hidDeviceInterface = NULL;
|
||||||
|
|
||||||
|
ioReturnValue = IOObjectGetClass(hidDevice, className);
|
||||||
|
|
||||||
|
if (ioReturnValue != kIOReturnSuccess) {
|
||||||
|
NSLog(@"Error: Failed to get class name.");
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
ioReturnValue = IOCreatePlugInInterfaceForService(hidDevice,
|
||||||
|
kIOHIDDeviceUserClientTypeID,
|
||||||
|
kIOCFPlugInInterfaceID,
|
||||||
|
&plugInInterface,
|
||||||
|
&score);
|
||||||
|
if (ioReturnValue == kIOReturnSuccess)
|
||||||
|
{
|
||||||
|
//Call a method of the intermediate plug-in to create the device interface
|
||||||
|
plugInResult = (*plugInInterface)->QueryInterface(plugInInterface, CFUUIDGetUUIDBytes(kIOHIDDeviceInterfaceID), (LPVOID) &hidDeviceInterface);
|
||||||
|
|
||||||
|
if (plugInResult != S_OK) {
|
||||||
|
NSLog(@"Error: Couldn't create HID class device interface");
|
||||||
|
}
|
||||||
|
// Release
|
||||||
|
if (plugInInterface) (*plugInInterface)->Release(plugInInterface);
|
||||||
|
}
|
||||||
|
return hidDeviceInterface;
|
||||||
|
}
|
||||||
|
|
||||||
|
- (BOOL) initializeCookies {
|
||||||
|
IOHIDDeviceInterface122** handle = (IOHIDDeviceInterface122**)hidDeviceInterface;
|
||||||
|
IOHIDElementCookie cookie;
|
||||||
|
long usage;
|
||||||
|
long usagePage;
|
||||||
|
id object;
|
||||||
|
NSArray* elements = nil;
|
||||||
|
NSDictionary* element;
|
||||||
|
IOReturn success;
|
||||||
|
|
||||||
|
if (!handle || !(*handle)) return NO;
|
||||||
|
|
||||||
|
// Copy all elements, since we're grabbing most of the elements
|
||||||
|
// for this device anyway, and thus, it's faster to iterate them
|
||||||
|
// ourselves. When grabbing only one or two elements, a matching
|
||||||
|
// dictionary should be passed in here instead of NULL.
|
||||||
|
success = (*handle)->copyMatchingElements(handle, NULL, (CFArrayRef*)&elements);
|
||||||
|
|
||||||
|
if (success == kIOReturnSuccess) {
|
||||||
|
|
||||||
|
[elements autorelease];
|
||||||
|
/*
|
||||||
|
cookies = calloc(NUMBER_OF_APPLE_REMOTE_ACTIONS, sizeof(IOHIDElementCookie));
|
||||||
|
memset(cookies, 0, sizeof(IOHIDElementCookie) * NUMBER_OF_APPLE_REMOTE_ACTIONS);
|
||||||
|
*/
|
||||||
|
allCookies = [NSMutableArray new];
|
||||||
|
|
||||||
|
NSEnumerator *elementsEnumerator = [elements objectEnumerator];
|
||||||
|
|
||||||
|
while (element = [elementsEnumerator nextObject]) {
|
||||||
|
//Get cookie
|
||||||
|
object = [element valueForKey: (NSString*)CFSTR(kIOHIDElementCookieKey) ];
|
||||||
|
if (object == nil || ![object isKindOfClass:[NSNumber class]]) continue;
|
||||||
|
if (object == 0 || CFGetTypeID(object) != CFNumberGetTypeID()) continue;
|
||||||
|
cookie = (IOHIDElementCookie) [object longValue];
|
||||||
|
|
||||||
|
//Get usage
|
||||||
|
object = [element valueForKey: (NSString*)CFSTR(kIOHIDElementUsageKey) ];
|
||||||
|
if (object == nil || ![object isKindOfClass:[NSNumber class]]) continue;
|
||||||
|
usage = [object longValue];
|
||||||
|
|
||||||
|
//Get usage page
|
||||||
|
object = [element valueForKey: (NSString*)CFSTR(kIOHIDElementUsagePageKey) ];
|
||||||
|
if (object == nil || ![object isKindOfClass:[NSNumber class]]) continue;
|
||||||
|
usagePage = [object longValue];
|
||||||
|
|
||||||
|
[allCookies addObject: [NSNumber numberWithInt:(int)cookie]];
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return NO;
|
||||||
|
}
|
||||||
|
|
||||||
|
return YES;
|
||||||
|
}
|
||||||
|
|
||||||
|
- (BOOL) openDevice {
|
||||||
|
HRESULT result;
|
||||||
|
|
||||||
|
IOHIDOptionsType openMode = kIOHIDOptionsTypeNone;
|
||||||
|
if ([self isOpenInExclusiveMode]) openMode = kIOHIDOptionsTypeSeizeDevice;
|
||||||
|
IOReturn ioReturnValue = (*hidDeviceInterface)->open(hidDeviceInterface, openMode);
|
||||||
|
|
||||||
|
if (ioReturnValue == KERN_SUCCESS) {
|
||||||
|
queue = (*hidDeviceInterface)->allocQueue(hidDeviceInterface);
|
||||||
|
if (queue) {
|
||||||
|
result = (*queue)->create(queue, 0, 12); //depth: maximum number of elements in queue before oldest elements in queue begin to be lost.
|
||||||
|
|
||||||
|
IOHIDElementCookie cookie;
|
||||||
|
NSEnumerator *allCookiesEnumerator = [allCookies objectEnumerator];
|
||||||
|
|
||||||
|
while ((cookie = (IOHIDElementCookie)[[allCookiesEnumerator nextObject] intValue])!=0) {
|
||||||
|
(*queue)->addElement(queue, cookie, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
// add callback for async events
|
||||||
|
ioReturnValue = (*queue)->createAsyncEventSource(queue, &eventSource);
|
||||||
|
if (ioReturnValue == KERN_SUCCESS) {
|
||||||
|
ioReturnValue = (*queue)->setEventCallout(queue,QueueCallbackFunction, self, NULL);
|
||||||
|
if (ioReturnValue == KERN_SUCCESS) {
|
||||||
|
CFRunLoopAddSource(CFRunLoopGetCurrent(), eventSource, kCFRunLoopDefaultMode);
|
||||||
|
|
||||||
|
//start data delivery to queue
|
||||||
|
(*queue)->start(queue);
|
||||||
|
return YES;
|
||||||
|
} else {
|
||||||
|
NSLog(@"Error when setting event callback");
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
NSLog(@"Error when creating async event source");
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
NSLog(@"Error when opening device");
|
||||||
|
}
|
||||||
|
} else if (ioReturnValue == kIOReturnExclusiveAccess) {
|
||||||
|
// the device is used exclusive by another application
|
||||||
|
|
||||||
|
// 1. we register for the FINISHED_USING_REMOTE_CONTROL_NOTIFICATION notification
|
||||||
|
[[NSDistributedNotificationCenter defaultCenter] addObserver:self selector:@selector(remoteControlAvailable:) name:FINISHED_USING_REMOTE_CONTROL_NOTIFICATION object:nil];
|
||||||
|
|
||||||
|
// 2. send a distributed notification that we wanted to use the remote control
|
||||||
|
[[self class] sendRequestForRemoteControlNotification];
|
||||||
|
}
|
||||||
|
return NO;
|
||||||
|
}
|
||||||
|
|
||||||
|
+ (io_object_t) findRemoteDevice {
|
||||||
|
CFMutableDictionaryRef hidMatchDictionary = NULL;
|
||||||
|
IOReturn ioReturnValue = kIOReturnSuccess;
|
||||||
|
io_iterator_t hidObjectIterator = 0;
|
||||||
|
io_object_t hidDevice = 0;
|
||||||
|
|
||||||
|
// Set up a matching dictionary to search the I/O Registry by class
|
||||||
|
// name for all HID class devices
|
||||||
|
hidMatchDictionary = IOServiceMatching([self remoteControlDeviceName]);
|
||||||
|
|
||||||
|
// Now search I/O Registry for matching devices.
|
||||||
|
ioReturnValue = IOServiceGetMatchingServices(kIOMasterPortDefault, hidMatchDictionary, &hidObjectIterator);
|
||||||
|
|
||||||
|
if ((ioReturnValue == kIOReturnSuccess) && (hidObjectIterator != 0)) {
|
||||||
|
hidDevice = IOIteratorNext(hidObjectIterator);
|
||||||
|
}
|
||||||
|
|
||||||
|
// release the iterator
|
||||||
|
IOObjectRelease(hidObjectIterator);
|
||||||
|
|
||||||
|
return hidDevice;
|
||||||
|
}
|
||||||
|
|
||||||
|
@end
|
||||||
|
|
39
Classes/YouView/Remote Control/KeyspanFrontRowControl.h
Normal file
39
Classes/YouView/Remote Control/KeyspanFrontRowControl.h
Normal file
@ -0,0 +1,39 @@
|
|||||||
|
/*****************************************************************************
|
||||||
|
* KeyspanFrontRowControl.h
|
||||||
|
* RemoteControlWrapper
|
||||||
|
*
|
||||||
|
* Created by Martin Kahr on 11.03.06 under a MIT-style license.
|
||||||
|
* Copyright (c) 2006 martinkahr.com. All rights reserved.
|
||||||
|
*
|
||||||
|
* Permission is hereby granted, free of charge, to any person obtaining a
|
||||||
|
* copy of this software and associated documentation files (the "Software"),
|
||||||
|
* to deal in the Software without restriction, including without limitation
|
||||||
|
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
||||||
|
* and/or sell copies of the Software, and to permit persons to whom the
|
||||||
|
* Software is furnished to do so, subject to the following conditions:
|
||||||
|
*
|
||||||
|
* The above copyright notice and this permission notice shall be included
|
||||||
|
* in all copies or substantial portions of the Software.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
||||||
|
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||||
|
* THE SOFTWARE.
|
||||||
|
*
|
||||||
|
*****************************************************************************/
|
||||||
|
|
||||||
|
|
||||||
|
#import <Cocoa/Cocoa.h>
|
||||||
|
#import "HIDRemoteControlDevice.h"
|
||||||
|
|
||||||
|
/* Interacts with the Keyspan FrontRow Remote Control HID device
|
||||||
|
The class is not thread safe
|
||||||
|
*/
|
||||||
|
@interface KeyspanFrontRowControl : HIDRemoteControlDevice {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@end
|
87
Classes/YouView/Remote Control/KeyspanFrontRowControl.m
Normal file
87
Classes/YouView/Remote Control/KeyspanFrontRowControl.m
Normal file
@ -0,0 +1,87 @@
|
|||||||
|
/*****************************************************************************
|
||||||
|
* KeyspanFrontRowControl.m
|
||||||
|
* RemoteControlWrapper
|
||||||
|
*
|
||||||
|
* Created by Martin Kahr on 11.03.06 under a MIT-style license.
|
||||||
|
* Copyright (c) 2006 martinkahr.com. All rights reserved.
|
||||||
|
*
|
||||||
|
* Permission is hereby granted, free of charge, to any person obtaining a
|
||||||
|
* copy of this software and associated documentation files (the "Software"),
|
||||||
|
* to deal in the Software without restriction, including without limitation
|
||||||
|
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
||||||
|
* and/or sell copies of the Software, and to permit persons to whom the
|
||||||
|
* Software is furnished to do so, subject to the following conditions:
|
||||||
|
*
|
||||||
|
* The above copyright notice and this permission notice shall be included
|
||||||
|
* in all copies or substantial portions of the Software.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
||||||
|
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||||
|
* THE SOFTWARE.
|
||||||
|
*
|
||||||
|
*****************************************************************************/
|
||||||
|
|
||||||
|
#import "KeyspanFrontRowControl.h"
|
||||||
|
#import <mach/mach.h>
|
||||||
|
#import <mach/mach_error.h>
|
||||||
|
#import <IOKit/IOKitLib.h>
|
||||||
|
#import <IOKit/IOCFPlugIn.h>
|
||||||
|
#import <IOKit/hid/IOHIDKeys.h>
|
||||||
|
|
||||||
|
@implementation KeyspanFrontRowControl
|
||||||
|
|
||||||
|
- (void) setCookieMappingInDictionary: (NSMutableDictionary*) _cookieToButtonMapping {
|
||||||
|
[_cookieToButtonMapping setObject:[NSNumber numberWithInt:kRemoteButtonPlus] forKey:@"11_18_99_10_"];
|
||||||
|
[_cookieToButtonMapping setObject:[NSNumber numberWithInt:kRemoteButtonMinus] forKey:@"11_18_98_10_"];
|
||||||
|
[_cookieToButtonMapping setObject:[NSNumber numberWithInt:kRemoteButtonMenu] forKey:@"11_18_58_10_"];
|
||||||
|
[_cookieToButtonMapping setObject:[NSNumber numberWithInt:kRemoteButtonPlay] forKey:@"11_18_61_10_"];
|
||||||
|
[_cookieToButtonMapping setObject:[NSNumber numberWithInt:kRemoteButtonRight] forKey:@"11_18_96_10_"];
|
||||||
|
[_cookieToButtonMapping setObject:[NSNumber numberWithInt:kRemoteButtonLeft] forKey:@"11_18_97_10_"];
|
||||||
|
/* hold events are not being send by this device
|
||||||
|
[_cookieToButtonMapping setObject:[NSNumber numberWithInt:kRemoteButtonRight_Hold] forKey:@"14_6_4_2_"];
|
||||||
|
[_cookieToButtonMapping setObject:[NSNumber numberWithInt:kRemoteButtonLeft_Hold] forKey:@"14_6_3_2_"];
|
||||||
|
[_cookieToButtonMapping setObject:[NSNumber numberWithInt:kRemoteButtonMenu_Hold] forKey:@"14_6_14_6_"];
|
||||||
|
[_cookieToButtonMapping setObject:[NSNumber numberWithInt:kRemoteButtonPlay_Sleep] forKey:@"18_14_6_18_14_6_"];
|
||||||
|
[_cookieToButtonMapping setObject:[NSNumber numberWithInt:kRemoteControl_Switched] forKey:@"19_"];
|
||||||
|
*/
|
||||||
|
}
|
||||||
|
|
||||||
|
+ (io_object_t) findRemoteDevice {
|
||||||
|
CFMutableDictionaryRef hidMatchDictionary = NULL;
|
||||||
|
IOReturn ioReturnValue = kIOReturnSuccess;
|
||||||
|
io_iterator_t hidObjectIterator = 0;
|
||||||
|
io_object_t hidDevice = 0;
|
||||||
|
SInt32 idVendor = 1741;
|
||||||
|
SInt32 idProduct = 0x420;
|
||||||
|
|
||||||
|
// Set up a matching dictionary to search the I/O Registry by class
|
||||||
|
// name for all HID class devices
|
||||||
|
hidMatchDictionary = IOServiceMatching(kIOHIDDeviceKey);
|
||||||
|
|
||||||
|
CFNumberRef numberRef = CFNumberCreate(kCFAllocatorDefault, kCFNumberSInt32Type, &idVendor);
|
||||||
|
CFDictionaryAddValue(hidMatchDictionary, CFSTR(kIOHIDVendorIDKey), numberRef);
|
||||||
|
CFRelease(numberRef);
|
||||||
|
|
||||||
|
numberRef = CFNumberCreate(kCFAllocatorDefault, kCFNumberSInt32Type, &idProduct);
|
||||||
|
CFDictionaryAddValue(hidMatchDictionary, CFSTR(kIOHIDProductIDKey), numberRef);
|
||||||
|
CFRelease(numberRef);
|
||||||
|
|
||||||
|
// Now search I/O Registry for matching devices.
|
||||||
|
ioReturnValue = IOServiceGetMatchingServices(kIOMasterPortDefault, hidMatchDictionary, &hidObjectIterator);
|
||||||
|
|
||||||
|
if ((ioReturnValue == kIOReturnSuccess) && (hidObjectIterator != 0)) {
|
||||||
|
hidDevice = IOIteratorNext(hidObjectIterator);
|
||||||
|
}
|
||||||
|
|
||||||
|
// release the iterator
|
||||||
|
IOObjectRelease(hidObjectIterator);
|
||||||
|
|
||||||
|
return hidDevice;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@end
|
90
Classes/YouView/Remote Control/MultiClickRemoteBehavior.h
Normal file
90
Classes/YouView/Remote Control/MultiClickRemoteBehavior.h
Normal file
@ -0,0 +1,90 @@
|
|||||||
|
/*****************************************************************************
|
||||||
|
* MultiClickRemoteBehavior.h
|
||||||
|
* RemoteControlWrapper
|
||||||
|
*
|
||||||
|
* Created by Martin Kahr on 11.03.06 under a MIT-style license.
|
||||||
|
* Copyright (c) 2006 martinkahr.com. All rights reserved.
|
||||||
|
*
|
||||||
|
* Permission is hereby granted, free of charge, to any person obtaining a
|
||||||
|
* copy of this software and associated documentation files (the "Software"),
|
||||||
|
* to deal in the Software without restriction, including without limitation
|
||||||
|
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
||||||
|
* and/or sell copies of the Software, and to permit persons to whom the
|
||||||
|
* Software is furnished to do so, subject to the following conditions:
|
||||||
|
*
|
||||||
|
* The above copyright notice and this permission notice shall be included
|
||||||
|
* in all copies or substantial portions of the Software.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
||||||
|
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||||
|
* THE SOFTWARE.
|
||||||
|
*
|
||||||
|
*****************************************************************************/
|
||||||
|
|
||||||
|
|
||||||
|
#import <Cocoa/Cocoa.h>
|
||||||
|
#import "RemoteControl.h"
|
||||||
|
|
||||||
|
/**
|
||||||
|
A behavior that adds multiclick and hold events on top of a device.
|
||||||
|
Events are generated and send to a delegate
|
||||||
|
*/
|
||||||
|
@interface MultiClickRemoteBehavior : NSObject {
|
||||||
|
id delegate;
|
||||||
|
|
||||||
|
// state for simulating plus/minus hold
|
||||||
|
BOOL simulateHoldEvents;
|
||||||
|
BOOL lastEventSimulatedHold;
|
||||||
|
RemoteControlEventIdentifier lastHoldEvent;
|
||||||
|
NSTimeInterval lastHoldEventTime;
|
||||||
|
|
||||||
|
// state for multi click
|
||||||
|
unsigned int clickCountEnabledButtons;
|
||||||
|
NSTimeInterval maxClickTimeDifference;
|
||||||
|
NSTimeInterval lastClickCountEventTime;
|
||||||
|
RemoteControlEventIdentifier lastClickCountEvent;
|
||||||
|
unsigned int eventClickCount;
|
||||||
|
}
|
||||||
|
|
||||||
|
- (id) init;
|
||||||
|
|
||||||
|
// Delegates are not retained
|
||||||
|
- (void) setDelegate: (id) delegate;
|
||||||
|
- (id) delegate;
|
||||||
|
|
||||||
|
// Simulating hold events does deactivate sending of individual requests for pressed down/released.
|
||||||
|
// Instead special hold events are being triggered when the user is pressing and holding a button for a small period.
|
||||||
|
// Simulation is activated only for those buttons and remote control that do not have a seperate event already
|
||||||
|
- (BOOL) simulateHoldEvent;
|
||||||
|
- (void) setSimulateHoldEvent: (BOOL) value;
|
||||||
|
|
||||||
|
// click counting makes it possible to recognize if the user has pressed a button repeatedly
|
||||||
|
// click counting does delay each event as it has to wait if there is another event (second click)
|
||||||
|
// therefore there is a slight time difference (maximumClickCountTimeDifference) between a single click
|
||||||
|
// of the user and the call of your delegate method
|
||||||
|
// click counting can be enabled individually for specific buttons. Use the property clickCountEnableButtons to
|
||||||
|
// set the buttons for which click counting shall be enabled
|
||||||
|
- (BOOL) clickCountingEnabled;
|
||||||
|
- (void) setClickCountingEnabled: (BOOL) value;
|
||||||
|
|
||||||
|
- (unsigned int) clickCountEnabledButtons;
|
||||||
|
- (void) setClickCountEnabledButtons: (unsigned int)value;
|
||||||
|
|
||||||
|
// the maximum time difference till which clicks are recognized as multi clicks
|
||||||
|
- (NSTimeInterval) maximumClickCountTimeDifference;
|
||||||
|
- (void) setMaximumClickCountTimeDifference: (NSTimeInterval) timeDiff;
|
||||||
|
|
||||||
|
@end
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Method definitions for the delegate of the MultiClickRemoteBehavior class
|
||||||
|
*/
|
||||||
|
@interface NSObject(MultiClickRemoteBehaviorDelegate)
|
||||||
|
|
||||||
|
- (void) remoteButton: (RemoteControlEventIdentifier)buttonIdentifier pressedDown: (BOOL) pressedDown clickCount: (unsigned int) count;
|
||||||
|
|
||||||
|
@end
|
210
Classes/YouView/Remote Control/MultiClickRemoteBehavior.m
Normal file
210
Classes/YouView/Remote Control/MultiClickRemoteBehavior.m
Normal file
@ -0,0 +1,210 @@
|
|||||||
|
/*****************************************************************************
|
||||||
|
* MultiClickRemoteBehavior.m
|
||||||
|
* RemoteControlWrapper
|
||||||
|
*
|
||||||
|
* Created by Martin Kahr on 11.03.06 under a MIT-style license.
|
||||||
|
* Copyright (c) 2006 martinkahr.com. All rights reserved.
|
||||||
|
*
|
||||||
|
* Permission is hereby granted, free of charge, to any person obtaining a
|
||||||
|
* copy of this software and associated documentation files (the "Software"),
|
||||||
|
* to deal in the Software without restriction, including without limitation
|
||||||
|
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
||||||
|
* and/or sell copies of the Software, and to permit persons to whom the
|
||||||
|
* Software is furnished to do so, subject to the following conditions:
|
||||||
|
*
|
||||||
|
* The above copyright notice and this permission notice shall be included
|
||||||
|
* in all copies or substantial portions of the Software.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
||||||
|
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||||
|
* THE SOFTWARE.
|
||||||
|
*
|
||||||
|
*****************************************************************************/
|
||||||
|
|
||||||
|
#import "MultiClickRemoteBehavior.h"
|
||||||
|
|
||||||
|
const NSTimeInterval DEFAULT_MAXIMUM_CLICK_TIME_DIFFERENCE=0.35;
|
||||||
|
const NSTimeInterval HOLD_RECOGNITION_TIME_INTERVAL=0.4;
|
||||||
|
|
||||||
|
@implementation MultiClickRemoteBehavior
|
||||||
|
|
||||||
|
- (id) init {
|
||||||
|
if (self = [super init]) {
|
||||||
|
maxClickTimeDifference = DEFAULT_MAXIMUM_CLICK_TIME_DIFFERENCE;
|
||||||
|
}
|
||||||
|
return self;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Delegates are not retained!
|
||||||
|
// http://developer.apple.com/documentation/Cocoa/Conceptual/CocoaFundamentals/CommunicatingWithObjects/chapter_6_section_4.html
|
||||||
|
// Delegating objects do not (and should not) retain their delegates.
|
||||||
|
// However, clients of delegating objects (applications, usually) are responsible for ensuring that their delegates are around
|
||||||
|
// to receive delegation messages. To do this, they may have to retain the delegate.
|
||||||
|
- (void) setDelegate: (id) _delegate {
|
||||||
|
if (_delegate && [_delegate respondsToSelector:@selector(remoteButton:pressedDown:clickCount:)]==NO) return;
|
||||||
|
|
||||||
|
delegate = _delegate;
|
||||||
|
}
|
||||||
|
- (id) delegate {
|
||||||
|
return delegate;
|
||||||
|
}
|
||||||
|
|
||||||
|
- (BOOL) simulateHoldEvent {
|
||||||
|
return simulateHoldEvents;
|
||||||
|
}
|
||||||
|
- (void) setSimulateHoldEvent: (BOOL) value {
|
||||||
|
simulateHoldEvents = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
- (BOOL) simulatesHoldForButtonIdentifier: (RemoteControlEventIdentifier) identifier remoteControl: (RemoteControl*) remoteControl {
|
||||||
|
// we do that check only for the normal button identifiers as we would check for hold support for hold events instead
|
||||||
|
if (identifier > (1 << EVENT_TO_HOLD_EVENT_OFFSET)) return NO;
|
||||||
|
|
||||||
|
return [self simulateHoldEvent] && [remoteControl sendsEventForButtonIdentifier: (identifier << EVENT_TO_HOLD_EVENT_OFFSET)]==NO;
|
||||||
|
}
|
||||||
|
|
||||||
|
- (BOOL) clickCountingEnabled {
|
||||||
|
return clickCountEnabledButtons != 0;
|
||||||
|
}
|
||||||
|
- (void) setClickCountingEnabled: (BOOL) value {
|
||||||
|
if (value) {
|
||||||
|
[self setClickCountEnabledButtons: kRemoteButtonPlus | kRemoteButtonMinus | kRemoteButtonPlay | kRemoteButtonLeft | kRemoteButtonRight | kRemoteButtonMenu];
|
||||||
|
} else {
|
||||||
|
[self setClickCountEnabledButtons: 0];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
- (unsigned int) clickCountEnabledButtons {
|
||||||
|
return clickCountEnabledButtons;
|
||||||
|
}
|
||||||
|
- (void) setClickCountEnabledButtons: (unsigned int)value {
|
||||||
|
clickCountEnabledButtons = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
- (NSTimeInterval) maximumClickCountTimeDifference {
|
||||||
|
return maxClickTimeDifference;
|
||||||
|
}
|
||||||
|
- (void) setMaximumClickCountTimeDifference: (NSTimeInterval) timeDiff {
|
||||||
|
maxClickTimeDifference = timeDiff;
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void) sendSimulatedHoldEvent: (id) time {
|
||||||
|
BOOL startSimulateHold = NO;
|
||||||
|
RemoteControlEventIdentifier event = lastHoldEvent;
|
||||||
|
@synchronized(self) {
|
||||||
|
startSimulateHold = (lastHoldEvent>0 && lastHoldEventTime == [time doubleValue]);
|
||||||
|
}
|
||||||
|
if (startSimulateHold) {
|
||||||
|
lastEventSimulatedHold = YES;
|
||||||
|
event = (event << EVENT_TO_HOLD_EVENT_OFFSET);
|
||||||
|
[delegate remoteButton:event pressedDown: YES clickCount: 1];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void) executeClickCountEvent: (NSArray*) values {
|
||||||
|
RemoteControlEventIdentifier event = [[values objectAtIndex: 0] unsignedIntValue];
|
||||||
|
NSTimeInterval eventTimePoint = [[values objectAtIndex: 1] doubleValue];
|
||||||
|
|
||||||
|
BOOL finishedClicking = NO;
|
||||||
|
int finalClickCount = eventClickCount;
|
||||||
|
|
||||||
|
@synchronized(self) {
|
||||||
|
finishedClicking = (event != lastClickCountEvent || eventTimePoint == lastClickCountEventTime);
|
||||||
|
if (finishedClicking) {
|
||||||
|
eventClickCount = 0;
|
||||||
|
lastClickCountEvent = 0;
|
||||||
|
lastClickCountEventTime = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (finishedClicking) {
|
||||||
|
[delegate remoteButton:event pressedDown: YES clickCount:finalClickCount];
|
||||||
|
// trigger a button release event, too
|
||||||
|
[NSThread sleepUntilDate: [NSDate dateWithTimeIntervalSinceNow:0.1]];
|
||||||
|
[delegate remoteButton:event pressedDown: NO clickCount:finalClickCount];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void) sendRemoteButtonEvent: (RemoteControlEventIdentifier) event pressedDown: (BOOL) pressedDown remoteControl: (RemoteControl*) remoteControl {
|
||||||
|
if (!delegate) return;
|
||||||
|
|
||||||
|
BOOL clickCountingForEvent = ([self clickCountEnabledButtons] & event) == event;
|
||||||
|
|
||||||
|
if ([self simulatesHoldForButtonIdentifier: event remoteControl: remoteControl] && lastClickCountEvent==0) {
|
||||||
|
if (pressedDown) {
|
||||||
|
// wait to see if it is a hold
|
||||||
|
lastHoldEvent = event;
|
||||||
|
lastHoldEventTime = [NSDate timeIntervalSinceReferenceDate];
|
||||||
|
[self performSelector:@selector(sendSimulatedHoldEvent:)
|
||||||
|
withObject:[NSNumber numberWithDouble:lastHoldEventTime]
|
||||||
|
afterDelay:HOLD_RECOGNITION_TIME_INTERVAL];
|
||||||
|
return;
|
||||||
|
} else {
|
||||||
|
if (lastEventSimulatedHold) {
|
||||||
|
// it was a hold
|
||||||
|
// send an event for "hold release"
|
||||||
|
event = (event << EVENT_TO_HOLD_EVENT_OFFSET);
|
||||||
|
lastHoldEvent = 0;
|
||||||
|
lastEventSimulatedHold = NO;
|
||||||
|
|
||||||
|
[delegate remoteButton:event pressedDown: pressedDown clickCount:1];
|
||||||
|
return;
|
||||||
|
} else {
|
||||||
|
RemoteControlEventIdentifier previousEvent = lastHoldEvent;
|
||||||
|
@synchronized(self) {
|
||||||
|
lastHoldEvent = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// in case click counting is enabled we have to setup the state for that, too
|
||||||
|
if (clickCountingForEvent) {
|
||||||
|
lastClickCountEvent = previousEvent;
|
||||||
|
lastClickCountEventTime = lastHoldEventTime;
|
||||||
|
NSNumber* eventNumber;
|
||||||
|
NSNumber* timeNumber;
|
||||||
|
eventClickCount = 1;
|
||||||
|
timeNumber = [NSNumber numberWithDouble:lastClickCountEventTime];
|
||||||
|
eventNumber= [NSNumber numberWithUnsignedInt:previousEvent];
|
||||||
|
NSTimeInterval diffTime = maxClickTimeDifference-([NSDate timeIntervalSinceReferenceDate]-lastHoldEventTime);
|
||||||
|
[self performSelector: @selector(executeClickCountEvent:)
|
||||||
|
withObject: [NSArray arrayWithObjects:eventNumber, timeNumber, nil]
|
||||||
|
afterDelay: diffTime];
|
||||||
|
// we do not return here because we are still in the press-release event
|
||||||
|
// that will be consumed below
|
||||||
|
} else {
|
||||||
|
// trigger the pressed down event that we consumed first
|
||||||
|
[delegate remoteButton:event pressedDown: YES clickCount:1];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (clickCountingForEvent) {
|
||||||
|
if (pressedDown == NO) return;
|
||||||
|
|
||||||
|
NSNumber* eventNumber;
|
||||||
|
NSNumber* timeNumber;
|
||||||
|
@synchronized(self) {
|
||||||
|
lastClickCountEventTime = [NSDate timeIntervalSinceReferenceDate];
|
||||||
|
if (lastClickCountEvent == event) {
|
||||||
|
eventClickCount = eventClickCount + 1;
|
||||||
|
} else {
|
||||||
|
eventClickCount = 1;
|
||||||
|
}
|
||||||
|
lastClickCountEvent = event;
|
||||||
|
timeNumber = [NSNumber numberWithDouble:lastClickCountEventTime];
|
||||||
|
eventNumber= [NSNumber numberWithUnsignedInt:event];
|
||||||
|
}
|
||||||
|
[self performSelector: @selector(executeClickCountEvent:)
|
||||||
|
withObject: [NSArray arrayWithObjects:eventNumber, timeNumber, nil]
|
||||||
|
afterDelay: maxClickTimeDifference];
|
||||||
|
} else {
|
||||||
|
[delegate remoteButton:event pressedDown: pressedDown clickCount:1];
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@end
|
102
Classes/YouView/Remote Control/RemoteControl.h
Normal file
102
Classes/YouView/Remote Control/RemoteControl.h
Normal file
@ -0,0 +1,102 @@
|
|||||||
|
/*****************************************************************************
|
||||||
|
* RemoteControl.h
|
||||||
|
* RemoteControlWrapper
|
||||||
|
*
|
||||||
|
* Created by Martin Kahr on 11.03.06 under a MIT-style license.
|
||||||
|
* Copyright (c) 2006 martinkahr.com. All rights reserved.
|
||||||
|
*
|
||||||
|
* Permission is hereby granted, free of charge, to any person obtaining a
|
||||||
|
* copy of this software and associated documentation files (the "Software"),
|
||||||
|
* to deal in the Software without restriction, including without limitation
|
||||||
|
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
||||||
|
* and/or sell copies of the Software, and to permit persons to whom the
|
||||||
|
* Software is furnished to do so, subject to the following conditions:
|
||||||
|
*
|
||||||
|
* The above copyright notice and this permission notice shall be included
|
||||||
|
* in all copies or substantial portions of the Software.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
||||||
|
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||||
|
* THE SOFTWARE.
|
||||||
|
*
|
||||||
|
*****************************************************************************/
|
||||||
|
|
||||||
|
#import <Cocoa/Cocoa.h>
|
||||||
|
|
||||||
|
// notifaction names that are being used to signal that an application wants to
|
||||||
|
// have access to the remote control device or if the application has finished
|
||||||
|
// using the remote control device
|
||||||
|
extern NSString* REQUEST_FOR_REMOTE_CONTROL_NOTIFCATION;
|
||||||
|
extern NSString* FINISHED_USING_REMOTE_CONTROL_NOTIFICATION;
|
||||||
|
|
||||||
|
// keys used in user objects for distributed notifications
|
||||||
|
extern NSString* kRemoteControlDeviceName;
|
||||||
|
extern NSString* kApplicationIdentifier;
|
||||||
|
extern NSString* kTargetApplicationIdentifier;
|
||||||
|
|
||||||
|
// we have a 6 bit offset to make a hold event out of a normal event
|
||||||
|
#define EVENT_TO_HOLD_EVENT_OFFSET 6
|
||||||
|
|
||||||
|
@class RemoteControl;
|
||||||
|
|
||||||
|
typedef enum _RemoteControlEventIdentifier {
|
||||||
|
// normal events
|
||||||
|
kRemoteButtonPlus =1<<1,
|
||||||
|
kRemoteButtonMinus =1<<2,
|
||||||
|
kRemoteButtonMenu =1<<3,
|
||||||
|
kRemoteButtonPlay =1<<4,
|
||||||
|
kRemoteButtonRight =1<<5,
|
||||||
|
kRemoteButtonLeft =1<<6,
|
||||||
|
|
||||||
|
// hold events
|
||||||
|
kRemoteButtonPlus_Hold =1<<7,
|
||||||
|
kRemoteButtonMinus_Hold =1<<8,
|
||||||
|
kRemoteButtonMenu_Hold =1<<9,
|
||||||
|
kRemoteButtonPlay_Hold =1<<10,
|
||||||
|
kRemoteButtonRight_Hold =1<<11,
|
||||||
|
kRemoteButtonLeft_Hold =1<<12,
|
||||||
|
|
||||||
|
// special events (not supported by all devices)
|
||||||
|
kRemoteControl_Switched =1<<13,
|
||||||
|
} RemoteControlEventIdentifier;
|
||||||
|
|
||||||
|
@interface NSObject(RemoteControlDelegate)
|
||||||
|
|
||||||
|
- (void) sendRemoteButtonEvent: (RemoteControlEventIdentifier) event pressedDown: (BOOL) pressedDown remoteControl: (RemoteControl*) remoteControl;
|
||||||
|
|
||||||
|
@end
|
||||||
|
|
||||||
|
/*
|
||||||
|
Base Interface for Remote Control devices
|
||||||
|
*/
|
||||||
|
@interface RemoteControl : NSObject {
|
||||||
|
id delegate;
|
||||||
|
}
|
||||||
|
|
||||||
|
// returns nil if the remote control device is not available
|
||||||
|
- (id) initWithDelegate: (id) remoteControlDelegate;
|
||||||
|
|
||||||
|
- (void) setListeningToRemote: (BOOL) value;
|
||||||
|
- (BOOL) isListeningToRemote;
|
||||||
|
|
||||||
|
- (BOOL) isOpenInExclusiveMode;
|
||||||
|
- (void) setOpenInExclusiveMode: (BOOL) value;
|
||||||
|
|
||||||
|
- (IBAction) startListening: (id) sender;
|
||||||
|
- (IBAction) stopListening: (id) sender;
|
||||||
|
|
||||||
|
// is this remote control sending the given event?
|
||||||
|
- (BOOL) sendsEventForButtonIdentifier: (RemoteControlEventIdentifier) identifier;
|
||||||
|
|
||||||
|
// sending of notifications between applications
|
||||||
|
+ (void) sendFinishedNotifcationForAppIdentifier: (NSString*) identifier;
|
||||||
|
+ (void) sendRequestForRemoteControlNotification;
|
||||||
|
|
||||||
|
// name of the device
|
||||||
|
+ (const char*) remoteControlDeviceName;
|
||||||
|
|
||||||
|
@end
|
103
Classes/YouView/Remote Control/RemoteControl.m
Normal file
103
Classes/YouView/Remote Control/RemoteControl.m
Normal file
@ -0,0 +1,103 @@
|
|||||||
|
/*****************************************************************************
|
||||||
|
* RemoteControl.m
|
||||||
|
* RemoteControlWrapper
|
||||||
|
*
|
||||||
|
* Created by Martin Kahr on 11.03.06 under a MIT-style license.
|
||||||
|
* Copyright (c) 2006 martinkahr.com. All rights reserved.
|
||||||
|
*
|
||||||
|
* Permission is hereby granted, free of charge, to any person obtaining a
|
||||||
|
* copy of this software and associated documentation files (the "Software"),
|
||||||
|
* to deal in the Software without restriction, including without limitation
|
||||||
|
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
||||||
|
* and/or sell copies of the Software, and to permit persons to whom the
|
||||||
|
* Software is furnished to do so, subject to the following conditions:
|
||||||
|
*
|
||||||
|
* The above copyright notice and this permission notice shall be included
|
||||||
|
* in all copies or substantial portions of the Software.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
||||||
|
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||||
|
* THE SOFTWARE.
|
||||||
|
*
|
||||||
|
*****************************************************************************/
|
||||||
|
|
||||||
|
#import "RemoteControl.h"
|
||||||
|
|
||||||
|
// notifaction names that are being used to signal that an application wants to
|
||||||
|
// have access to the remote control device or if the application has finished
|
||||||
|
// using the remote control device
|
||||||
|
NSString* REQUEST_FOR_REMOTE_CONTROL_NOTIFCATION = @"mac.remotecontrols.RequestForRemoteControl";
|
||||||
|
NSString* FINISHED_USING_REMOTE_CONTROL_NOTIFICATION = @"mac.remotecontrols.FinishedUsingRemoteControl";
|
||||||
|
|
||||||
|
// keys used in user objects for distributed notifications
|
||||||
|
NSString* kRemoteControlDeviceName = @"RemoteControlDeviceName";
|
||||||
|
NSString* kApplicationIdentifier = @"CFBundleIdentifier";
|
||||||
|
// bundle identifier of the application that should get access to the remote control
|
||||||
|
// this key is being used in the FINISHED notification only
|
||||||
|
NSString* kTargetApplicationIdentifier = @"TargetBundleIdentifier";
|
||||||
|
|
||||||
|
|
||||||
|
@implementation RemoteControl
|
||||||
|
|
||||||
|
// returns nil if the remote control device is not available
|
||||||
|
- (id) initWithDelegate: (id) _remoteControlDelegate {
|
||||||
|
if (self = [super init]) {
|
||||||
|
delegate = [_remoteControlDelegate retain];
|
||||||
|
}
|
||||||
|
return self;
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void) dealloc {
|
||||||
|
[delegate release];
|
||||||
|
[super dealloc];
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void) setListeningToRemote: (BOOL) value {
|
||||||
|
}
|
||||||
|
- (BOOL) isListeningToRemote {
|
||||||
|
return NO;
|
||||||
|
}
|
||||||
|
|
||||||
|
- (IBAction) startListening: (id) sender {
|
||||||
|
}
|
||||||
|
- (IBAction) stopListening: (id) sender {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
- (BOOL) isOpenInExclusiveMode {
|
||||||
|
return YES;
|
||||||
|
}
|
||||||
|
- (void) setOpenInExclusiveMode: (BOOL) value {
|
||||||
|
}
|
||||||
|
|
||||||
|
- (BOOL) sendsEventForButtonIdentifier: (RemoteControlEventIdentifier) identifier {
|
||||||
|
return YES;
|
||||||
|
}
|
||||||
|
|
||||||
|
+ (void) sendDistributedNotification: (NSString*) notificationName targetBundleIdentifier: (NSString*) targetIdentifier {
|
||||||
|
NSDictionary* userInfo = [NSDictionary dictionaryWithObjectsAndKeys: [NSString stringWithCString:[self remoteControlDeviceName] encoding:NSASCIIStringEncoding],
|
||||||
|
kRemoteControlDeviceName, [[NSBundle mainBundle] bundleIdentifier], kApplicationIdentifier,
|
||||||
|
targetIdentifier, kTargetApplicationIdentifier, nil];
|
||||||
|
|
||||||
|
[[NSDistributedNotificationCenter defaultCenter] postNotificationName:notificationName
|
||||||
|
object:nil
|
||||||
|
userInfo:userInfo
|
||||||
|
deliverImmediately:YES];
|
||||||
|
}
|
||||||
|
|
||||||
|
+ (void) sendFinishedNotifcationForAppIdentifier: (NSString*) identifier {
|
||||||
|
[self sendDistributedNotification:FINISHED_USING_REMOTE_CONTROL_NOTIFICATION targetBundleIdentifier:identifier];
|
||||||
|
}
|
||||||
|
+ (void) sendRequestForRemoteControlNotification {
|
||||||
|
[self sendDistributedNotification:REQUEST_FOR_REMOTE_CONTROL_NOTIFCATION targetBundleIdentifier:nil];
|
||||||
|
}
|
||||||
|
|
||||||
|
+ (const char*) remoteControlDeviceName {
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
@end
|
38
Classes/YouView/Remote Control/RemoteControlContainer.h
Normal file
38
Classes/YouView/Remote Control/RemoteControlContainer.h
Normal file
@ -0,0 +1,38 @@
|
|||||||
|
/*****************************************************************************
|
||||||
|
* RemoteControlContainer.h
|
||||||
|
* RemoteControlWrapper
|
||||||
|
*
|
||||||
|
* Created by Martin Kahr on 11.03.06 under a MIT-style license.
|
||||||
|
* Copyright (c) 2006 martinkahr.com. All rights reserved.
|
||||||
|
*
|
||||||
|
* Permission is hereby granted, free of charge, to any person obtaining a
|
||||||
|
* copy of this software and associated documentation files (the "Software"),
|
||||||
|
* to deal in the Software without restriction, including without limitation
|
||||||
|
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
||||||
|
* and/or sell copies of the Software, and to permit persons to whom the
|
||||||
|
* Software is furnished to do so, subject to the following conditions:
|
||||||
|
*
|
||||||
|
* The above copyright notice and this permission notice shall be included
|
||||||
|
* in all copies or substantial portions of the Software.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
||||||
|
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||||
|
* THE SOFTWARE.
|
||||||
|
*
|
||||||
|
*****************************************************************************/
|
||||||
|
|
||||||
|
#import <Cocoa/Cocoa.h>
|
||||||
|
#import "RemoteControl.h"
|
||||||
|
|
||||||
|
@interface RemoteControlContainer : RemoteControl {
|
||||||
|
NSMutableArray* remoteControls;
|
||||||
|
}
|
||||||
|
|
||||||
|
- (BOOL)addRemoteControlDevice:(RemoteControl *)theRemote;
|
||||||
|
- (unsigned int) count;
|
||||||
|
|
||||||
|
@end
|
107
Classes/YouView/Remote Control/RemoteControlContainer.m
Normal file
107
Classes/YouView/Remote Control/RemoteControlContainer.m
Normal file
@ -0,0 +1,107 @@
|
|||||||
|
/*****************************************************************************
|
||||||
|
* RemoteControlContainer.m
|
||||||
|
* RemoteControlWrapper
|
||||||
|
*
|
||||||
|
* Created by Martin Kahr on 11.03.06 under a MIT-style license.
|
||||||
|
* Copyright (c) 2006 martinkahr.com. All rights reserved.
|
||||||
|
*
|
||||||
|
* Permission is hereby granted, free of charge, to any person obtaining a
|
||||||
|
* copy of this software and associated documentation files (the "Software"),
|
||||||
|
* to deal in the Software without restriction, including without limitation
|
||||||
|
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
||||||
|
* and/or sell copies of the Software, and to permit persons to whom the
|
||||||
|
* Software is furnished to do so, subject to the following conditions:
|
||||||
|
*
|
||||||
|
* The above copyright notice and this permission notice shall be included
|
||||||
|
* in all copies or substantial portions of the Software.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
||||||
|
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||||
|
* THE SOFTWARE.
|
||||||
|
*
|
||||||
|
*****************************************************************************/
|
||||||
|
|
||||||
|
#import "RemoteControlContainer.h"
|
||||||
|
|
||||||
|
|
||||||
|
@implementation RemoteControlContainer
|
||||||
|
|
||||||
|
- (id) initWithDelegate: (id) _remoteControlDelegate {
|
||||||
|
if (self = [super initWithDelegate:_remoteControlDelegate]) {
|
||||||
|
remoteControls = [NSMutableArray new];
|
||||||
|
}
|
||||||
|
return self;
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void) dealloc {
|
||||||
|
[self stopListening: self];
|
||||||
|
[remoteControls release];
|
||||||
|
[super dealloc];
|
||||||
|
}
|
||||||
|
|
||||||
|
- (BOOL)addRemoteControlDevice:(RemoteControl *)theRemote {
|
||||||
|
if (theRemote) {
|
||||||
|
[remoteControls addObject: theRemote];
|
||||||
|
[theRemote addObserver: self forKeyPath:@"listeningToRemote" options:NSKeyValueObservingOptionNew context:nil];
|
||||||
|
return YES;
|
||||||
|
}
|
||||||
|
return NO;
|
||||||
|
}
|
||||||
|
|
||||||
|
- (unsigned int) count {
|
||||||
|
return [remoteControls count];
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void) reset {
|
||||||
|
[self willChangeValueForKey:@"listeningToRemote"];
|
||||||
|
[self didChangeValueForKey:@"listeningToRemote"];
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context {
|
||||||
|
[self reset];
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void) setListeningToRemote: (BOOL) value {
|
||||||
|
for(int i=0; i < [remoteControls count]; i++) {
|
||||||
|
[[remoteControls objectAtIndex: i] setListeningToRemote: value];
|
||||||
|
}
|
||||||
|
if (value && value != [self isListeningToRemote]) [self performSelector:@selector(reset) withObject:nil afterDelay:0.01];
|
||||||
|
}
|
||||||
|
- (BOOL) isListeningToRemote {
|
||||||
|
for(int i=0; i < [remoteControls count]; i++) {
|
||||||
|
if ([[remoteControls objectAtIndex: i] isListeningToRemote]) {
|
||||||
|
return YES;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return NO;
|
||||||
|
}
|
||||||
|
|
||||||
|
- (IBAction) startListening: (id) sender {
|
||||||
|
for(int i=0; i < [remoteControls count]; i++) {
|
||||||
|
[[remoteControls objectAtIndex: i] startListening: sender];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
- (IBAction) stopListening: (id) sender {
|
||||||
|
for(int i=0; i < [remoteControls count]; i++) {
|
||||||
|
[[remoteControls objectAtIndex: i] stopListening: sender];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
- (BOOL) isOpenInExclusiveMode {
|
||||||
|
BOOL mode = YES;
|
||||||
|
for(int i=0; i < [remoteControls count]; i++) {
|
||||||
|
mode = mode && ([[remoteControls objectAtIndex: i] isOpenInExclusiveMode]);
|
||||||
|
}
|
||||||
|
return mode;
|
||||||
|
}
|
||||||
|
- (void) setOpenInExclusiveMode: (BOOL) value {
|
||||||
|
for(int i=0; i < [remoteControls count]; i++) {
|
||||||
|
[[remoteControls objectAtIndex: i] setOpenInExclusiveMode:value];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@end
|
108
Classes/YouView/Tasks/MGMTaskManager.h
Normal file
108
Classes/YouView/Tasks/MGMTaskManager.h
Normal file
@ -0,0 +1,108 @@
|
|||||||
|
//
|
||||||
|
// MGMTaskManager.h
|
||||||
|
// YouView
|
||||||
|
//
|
||||||
|
// Created by Mr. Gecko on 4/16/09.
|
||||||
|
// Copyright (c) 2015 Mr. Gecko's Media (James Coleman). https://mrgeckosmedia.com/ All rights reserved.
|
||||||
|
//
|
||||||
|
|
||||||
|
#import <Cocoa/Cocoa.h>
|
||||||
|
|
||||||
|
extern NSString * const MGMURL;
|
||||||
|
extern NSString * const MGMPreviewURL;
|
||||||
|
extern NSString * const MGMFilePath;
|
||||||
|
extern NSString * const MGMYVTaskPath;
|
||||||
|
extern NSString * const MGMFileName;
|
||||||
|
extern NSString * const MGMConverting;
|
||||||
|
extern NSString * const MGMConvertPath;
|
||||||
|
extern NSString * const MGMConvertFormat;
|
||||||
|
extern NSString * const MGMDonePath;
|
||||||
|
extern NSString * const MGMRevealPath;
|
||||||
|
extern NSString * const MGMTime;
|
||||||
|
|
||||||
|
extern NSString * const MGMFExtension;
|
||||||
|
extern NSString * const MGMFArguments;
|
||||||
|
extern NSString * const MGMFAudio;
|
||||||
|
|
||||||
|
extern NSString * const MGMMediaInfoKey;
|
||||||
|
extern NSString * const MGMTypeKey;
|
||||||
|
extern NSString * const MGMTypeGeneralKey;
|
||||||
|
extern NSString * const MGMTypeVideoKey;
|
||||||
|
extern NSString * const MGMTypeAudioKey;
|
||||||
|
extern NSString * const MGMBitrateKey;
|
||||||
|
extern NSString * const MGMCustomBitrateKey;
|
||||||
|
extern NSString * const MGMStreamKindIDKey;
|
||||||
|
extern NSString * const MGMIDKey;
|
||||||
|
extern NSString * const MGMLanguageKey;
|
||||||
|
extern NSString * const MGMFormatKey;
|
||||||
|
|
||||||
|
extern NSString * const MGMFileSizeKey;
|
||||||
|
extern NSString * const MGMFormatKey;
|
||||||
|
extern NSString * const MGMFormatProfileKey;
|
||||||
|
extern NSString * const MGMOveralBitRateKey;
|
||||||
|
extern NSString * const MGMPlayTimeKey;
|
||||||
|
|
||||||
|
extern NSString * const MGMCodecProfileKey;
|
||||||
|
extern NSString * const MGMDisplayAspectRatioKey;
|
||||||
|
extern NSString * const MGMFrameRateKey;
|
||||||
|
extern NSString * const MGMHeightKey;
|
||||||
|
extern NSString * const MGMWidthKey;
|
||||||
|
extern NSString * const MGMCustomHeightKey;
|
||||||
|
extern NSString * const MGMCustomWidthKey;
|
||||||
|
extern NSString * const MGMPixelAspectRatioKey;
|
||||||
|
extern NSString * const MGMScanOrderKey;
|
||||||
|
extern NSString * const MGMScanTypeKey;
|
||||||
|
|
||||||
|
extern NSString * const MGMSamplingRateKey;
|
||||||
|
|
||||||
|
extern NSString * const MGMMaxQualityKey;
|
||||||
|
|
||||||
|
extern NSString * const MGMInfoPlist;
|
||||||
|
|
||||||
|
extern NSString * const MGMYVTaskExt;
|
||||||
|
extern NSString * const MGMMP4Ext;
|
||||||
|
extern NSString * const MGMM4VExt;
|
||||||
|
extern NSString * const MGMMOVExt;
|
||||||
|
extern NSString * const MGMWMVExt;
|
||||||
|
extern NSString * const MGMAVIExt;
|
||||||
|
extern NSString * const MGMFLVExt;
|
||||||
|
extern NSString * const MGMMKVExt;
|
||||||
|
extern NSString * const MGMDVExt;
|
||||||
|
extern NSString * const MGMMPGExt;
|
||||||
|
extern NSString * const MGMMP3Ext;
|
||||||
|
extern NSString * const MGMM4AExt;
|
||||||
|
extern NSString * const MGMWAVExt;
|
||||||
|
extern NSString * const MGMAIFFExt;
|
||||||
|
extern NSString * const MGMAIFExt;
|
||||||
|
extern NSString * const MGMWMAExt;
|
||||||
|
extern NSString * const MGMJPGExt;
|
||||||
|
|
||||||
|
@class MGMTaskView, MGMController;
|
||||||
|
|
||||||
|
@interface MGMTaskManager : NSObject {
|
||||||
|
IBOutlet MGMController *controller;
|
||||||
|
IBOutlet NSView *exportView;
|
||||||
|
IBOutlet NSMatrix *maxQuality;
|
||||||
|
IBOutlet NSPopUpButton *exportFormat;
|
||||||
|
IBOutlet NSPopUpButton *videoFormat;
|
||||||
|
IBOutlet NSPopUpButton *audioFormat;
|
||||||
|
IBOutlet NSTextField *widthField;
|
||||||
|
IBOutlet NSTextField *heightField;
|
||||||
|
IBOutlet NSTextField *bitrateField;
|
||||||
|
|
||||||
|
IBOutlet NSWindow *mainWindow;
|
||||||
|
IBOutlet NSTableView *taskTable;
|
||||||
|
IBOutlet NSTextField *numTasks;
|
||||||
|
NSMutableArray *tasks;
|
||||||
|
NSMutableArray *formats;
|
||||||
|
}
|
||||||
|
- (MGMController *)controller;
|
||||||
|
- (NSArray *)formats;
|
||||||
|
- (void)updateCount;
|
||||||
|
- (IBAction)showTaskManager:(id)sender;
|
||||||
|
- (IBAction)clear:(id)sender;
|
||||||
|
- (void)addTask:(NSDictionary *)task withVideo:(NSURL *)video;
|
||||||
|
- (BOOL)ableToConvert:(MGMTaskView *)sender;
|
||||||
|
- (void)nextConversion;
|
||||||
|
- (void)saveEntry:(NSURL *)theEntry title:(NSString *)theTitle;
|
||||||
|
@end
|
297
Classes/YouView/Tasks/MGMTaskManager.m
Normal file
297
Classes/YouView/Tasks/MGMTaskManager.m
Normal file
@ -0,0 +1,297 @@
|
|||||||
|
//
|
||||||
|
// MGMTaskManager.m
|
||||||
|
// YouView
|
||||||
|
//
|
||||||
|
// Created by Mr. Gecko on 4/16/09.
|
||||||
|
// Copyright (c) 2015 Mr. Gecko's Media (James Coleman). https://mrgeckosmedia.com/ All rights reserved.
|
||||||
|
//
|
||||||
|
|
||||||
|
#import "MGMTaskManager.h"
|
||||||
|
#import "MGMTaskView.h"
|
||||||
|
#import "MGMController.h"
|
||||||
|
#import "MGMViewCell.h"
|
||||||
|
#import <GeckoReporter/GeckoReporter.h>
|
||||||
|
|
||||||
|
NSString * const MGMURL = @"TaskURL";
|
||||||
|
NSString * const MGMPreviewURL = @"TaskPreviewURL";
|
||||||
|
NSString * const MGMFilePath = @"TaskFilePath";
|
||||||
|
NSString * const MGMYVTaskPath = @"TaskYVTaskPath";
|
||||||
|
NSString * const MGMFileName = @"TaskFileName";
|
||||||
|
NSString * const MGMConverting = @"TaskConverting";
|
||||||
|
NSString * const MGMConvertPath = @"TaskConvertPath";
|
||||||
|
NSString * const MGMConvertFormat = @"TaskConvertFormat";
|
||||||
|
NSString * const MGMDonePath = @"TaskDonePath";
|
||||||
|
NSString * const MGMRevealPath = @"TaskRevealPath";
|
||||||
|
NSString * const MGMTime = @"TaskTime";
|
||||||
|
|
||||||
|
NSString * const MGMFExtension = @"FormatExtension";
|
||||||
|
NSString * const MGMFArguments = @"FormatArguments";
|
||||||
|
NSString * const MGMFAudio = @"FormatAudio";
|
||||||
|
|
||||||
|
NSString * const MGMMediaInfoKey = @"MediaInfo";
|
||||||
|
NSString * const MGMTypeKey = @"Type";
|
||||||
|
NSString * const MGMTypeGeneralKey = @"General";
|
||||||
|
NSString * const MGMTypeVideoKey = @"Video";
|
||||||
|
NSString * const MGMTypeAudioKey = @"Audio";
|
||||||
|
NSString * const MGMBitrateKey = @"Bitrate";
|
||||||
|
NSString * const MGMCustomBitrateKey = @"CustomBitrate";
|
||||||
|
NSString * const MGMStreamKindIDKey = @"StreamKindID";
|
||||||
|
NSString * const MGMIDKey = @"ID";
|
||||||
|
NSString * const MGMLanguageKey = @"Language";
|
||||||
|
NSString * const MGMFormatKey = @"Format";
|
||||||
|
|
||||||
|
NSString * const MGMFileSizeKey = @"FileSize";
|
||||||
|
NSString * const MGMFormatProfileKey = @"Format_Profile";
|
||||||
|
NSString * const MGMOveralBitRateKey = @"OveralBitrate";
|
||||||
|
NSString * const MGMPlayTimeKey = @"PlayTime";
|
||||||
|
|
||||||
|
NSString * const MGMCodecProfileKey = @"Codec_Profile";
|
||||||
|
NSString * const MGMDisplayAspectRatioKey = @"DisplayAspectRatio";
|
||||||
|
NSString * const MGMFrameRateKey = @"FrameRate";
|
||||||
|
NSString * const MGMHeightKey = @"Height";
|
||||||
|
NSString * const MGMWidthKey = @"Width";
|
||||||
|
NSString * const MGMCustomHeightKey = @"CustomHeight";
|
||||||
|
NSString * const MGMCustomWidthKey = @"CustomWidth";
|
||||||
|
NSString * const MGMPixelAspectRatioKey = @"PixelAspectRatio";
|
||||||
|
NSString * const MGMScanOrderKey = @"ScanOrder";
|
||||||
|
NSString * const MGMScanTypeKey = @"ScanType";
|
||||||
|
|
||||||
|
NSString * const MGMSamplingRateKey = @"SamplingRate";
|
||||||
|
|
||||||
|
NSString * const MGMMaxQualityKey = @"MaxQuality";
|
||||||
|
|
||||||
|
NSString * const MGMInfoPlist = @"info.plist";
|
||||||
|
|
||||||
|
NSString * const MGMYVTaskExt = @"yvtask";
|
||||||
|
NSString * const MGMMP4Ext = @"mp4";
|
||||||
|
NSString * const MGMM4VExt = @"m4v";
|
||||||
|
NSString * const MGMMOVExt = @"mov";
|
||||||
|
NSString * const MGMWMVExt = @"wmv";
|
||||||
|
NSString * const MGMAVIExt = @"avi";
|
||||||
|
NSString * const MGMFLVExt = @"flv";
|
||||||
|
NSString * const MGMMKVExt = @"mkv";
|
||||||
|
NSString * const MGMDVExt = @"dv";
|
||||||
|
NSString * const MGMMPGExt = @"mpg";
|
||||||
|
NSString * const MGMMP3Ext = @"mp3";
|
||||||
|
NSString * const MGMM4AExt = @"m4a";
|
||||||
|
NSString * const MGMWAVExt = @"wav";
|
||||||
|
NSString * const MGMAIFFExt = @"aiff";
|
||||||
|
NSString * const MGMAIFExt = @"aif";
|
||||||
|
NSString * const MGMWMAExt = @"wma";
|
||||||
|
NSString * const MGMJPGExt = @"jpg";
|
||||||
|
|
||||||
|
@protocol NSSavePanelProtocol <NSObject>
|
||||||
|
- (NSInteger)runModalForDirectory:(NSString *)path file:(NSString *)filename;
|
||||||
|
- (void)setNameFieldStringValue:(NSString *)value;
|
||||||
|
- (void)setDirectoryURL:(NSURL *)url;
|
||||||
|
@end
|
||||||
|
|
||||||
|
|
||||||
|
@implementation MGMTaskManager
|
||||||
|
- (void)awakeFromNib {
|
||||||
|
formats = [NSMutableArray new];
|
||||||
|
[formats addObject:[NSDictionary dictionaryWithObjectsAndKeys:MGMMP4Ext, MGMFExtension, [NSArray arrayWithObjects:@"-vcodec", @"mpeg4", @"-acodec", @"libfaac", nil], MGMFArguments, [NSNumber numberWithBool:NO], MGMFAudio, nil]];
|
||||||
|
[formats addObject:[NSDictionary dictionaryWithObjectsAndKeys:MGMMOVExt, MGMFExtension, [NSArray arrayWithObjects:@"-vcodec", @"libx264", @"-acodec", @"libfaac", nil], MGMFArguments, [NSNumber numberWithBool:NO], MGMFAudio, nil]];
|
||||||
|
[formats addObject:[NSDictionary dictionaryWithObjectsAndKeys:MGMFLVExt, MGMFExtension, [NSArray arrayWithObjects:@"-vcodec", @"flv", @"-acodec", @"libmp3lame", nil], MGMFArguments, [NSNumber numberWithBool:NO], MGMFAudio, nil]];
|
||||||
|
[formats addObject:[NSDictionary dictionaryWithObjectsAndKeys:@"swf", MGMFExtension, [NSArray arrayWithObjects:@"-vcodec", @"flv", @"-acodec", @"libmp3lame", nil], MGMFArguments, [NSNumber numberWithBool:NO], MGMFAudio, nil]];
|
||||||
|
[formats addObject:[NSDictionary dictionaryWithObjectsAndKeys:MGMAVIExt, MGMFExtension, [NSArray arrayWithObjects:@"-vcodec", @"mpeg4", @"-acodec", @"libmp3lame", nil], MGMFArguments, [NSNumber numberWithBool:NO], MGMFAudio, nil]];
|
||||||
|
[formats addObject:[NSDictionary dictionaryWithObjectsAndKeys:MGMAVIExt, MGMFExtension, [NSArray arrayWithObjects:@"-vcodec", @"msmpeg4v2", @"-acodec", @"libmp3lame", nil], MGMFArguments, [NSNumber numberWithBool:NO], MGMFAudio, nil]];
|
||||||
|
[formats addObject:[NSDictionary dictionaryWithObjectsAndKeys:MGMM4VExt, MGMFExtension, [NSArray arrayWithObjects:@"-vcodec", @"libx264", @"-acodec", @"libfaac", nil], MGMFArguments, [NSNumber numberWithBool:NO], MGMFAudio, nil]];
|
||||||
|
[formats addObject:[NSDictionary dictionaryWithObjectsAndKeys:MGMMPGExt, MGMFExtension, [NSArray arrayWithObjects:@"-vcodec", @"mpeg1video", @"-acodec", @"mp2", nil], MGMFArguments, [NSNumber numberWithBool:NO], MGMFAudio, nil]];
|
||||||
|
[formats addObject:[NSDictionary dictionaryWithObjectsAndKeys:MGMWMVExt, MGMFExtension, [NSArray arrayWithObjects:@"-vcodec", @"wmv2", @"-acodec", @"wmav2", nil], MGMFArguments, [NSNumber numberWithBool:NO], MGMFAudio, nil]];
|
||||||
|
[formats addObject:[NSDictionary dictionaryWithObjectsAndKeys:MGMMP3Ext, MGMFExtension, [NSArray arrayWithObjects:@"-vn", @"-acodec", @"libmp3lame", nil], MGMFArguments, [NSNumber numberWithBool:YES], MGMFAudio, nil]];
|
||||||
|
[formats addObject:[NSDictionary dictionaryWithObjectsAndKeys:MGMM4AExt, MGMFExtension, [NSArray arrayWithObjects:@"-vn", @"-acodec", @"libfaac", nil], MGMFArguments, [NSNumber numberWithBool:YES], MGMFAudio, nil]];
|
||||||
|
[formats addObject:[NSDictionary dictionaryWithObjectsAndKeys:MGMWAVExt, MGMFExtension, [NSArray arrayWithObjects:@"-vn", @"-acodec", @"pcm_s16le", nil], MGMFArguments, [NSNumber numberWithBool:YES], MGMFAudio, nil]];
|
||||||
|
[formats addObject:[NSDictionary dictionaryWithObjectsAndKeys:MGMAIFFExt, MGMFExtension, [NSArray arrayWithObjects:@"-vn", @"-acodec", @"pcm_s16be", nil], MGMFArguments, [NSNumber numberWithBool:YES], MGMFAudio, nil]];
|
||||||
|
[formats addObject:[NSDictionary dictionaryWithObjectsAndKeys:MGMWMAExt, MGMFExtension, [NSArray arrayWithObjects:@"-vn", @"-acodec", @"wmav2", nil], MGMFArguments, [NSNumber numberWithBool:YES], MGMFAudio, nil]];
|
||||||
|
[[[taskTable tableColumns] objectAtIndex:0] setDataCell:[[MGMViewCell new] autorelease]];
|
||||||
|
tasks = [NSMutableArray new];
|
||||||
|
}
|
||||||
|
- (void)dealloc {
|
||||||
|
#if releaseDebug
|
||||||
|
MGMLog(@"%s Releasing", __PRETTY_FUNCTION__);
|
||||||
|
#endif
|
||||||
|
[tasks release];
|
||||||
|
[formats release];
|
||||||
|
[super dealloc];
|
||||||
|
}
|
||||||
|
- (MGMController *)controller {
|
||||||
|
return controller;
|
||||||
|
}
|
||||||
|
- (NSArray *)formats {
|
||||||
|
return formats;
|
||||||
|
}
|
||||||
|
- (void)reloadData {
|
||||||
|
while ([[taskTable subviews] count] > 0) {
|
||||||
|
[[[taskTable subviews] lastObject] removeFromSuperviewWithoutNeedingDisplay];
|
||||||
|
}
|
||||||
|
|
||||||
|
[taskTable reloadData];
|
||||||
|
}
|
||||||
|
- (void)updateCount {
|
||||||
|
[numTasks setStringValue:[NSString stringWithFormat:@"Tasks %d", [tasks count]]];
|
||||||
|
}
|
||||||
|
- (IBAction)showTaskManager:(id)sender {
|
||||||
|
[self updateCount];
|
||||||
|
[mainWindow makeKeyAndOrderFront:sender];
|
||||||
|
}
|
||||||
|
- (IBAction)clear:(id)sender {
|
||||||
|
for (int i = [tasks count]-1; i>=0; i--) {
|
||||||
|
if (![[tasks objectAtIndex:i] working]) {
|
||||||
|
[tasks removeObjectAtIndex:i];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
[self reloadData];
|
||||||
|
[self updateCount];
|
||||||
|
}
|
||||||
|
- (void)addTask:(NSDictionary *)task withVideo:(NSURL *)video {
|
||||||
|
[tasks addObject:[MGMTaskView taskViewWithTask:task withVideo:video manager:self]];
|
||||||
|
[self reloadData];
|
||||||
|
[mainWindow makeKeyAndOrderFront:self];
|
||||||
|
[self updateCount];
|
||||||
|
}
|
||||||
|
|
||||||
|
- (BOOL)ableToConvert:(MGMTaskView *)sender {
|
||||||
|
for (int i=0; i<[tasks count]; i++) {
|
||||||
|
if ([tasks objectAtIndex:i] != sender) {
|
||||||
|
if ([[tasks objectAtIndex:i] converting]) {
|
||||||
|
return NO;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return YES;
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)nextConversion {
|
||||||
|
for (int i=0; i<[tasks count]; i++) {
|
||||||
|
if ([[tasks objectAtIndex:i] waitingToConvert]) {
|
||||||
|
[[tasks objectAtIndex:i] startConverson];
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
- (NSInteger)numberOfRowsInTableView:(NSTableView *)tableView {
|
||||||
|
return [tasks count];
|
||||||
|
}
|
||||||
|
- (id)tableView:(NSTableView *)tableView objectValueForTableColumn:(NSTableColumn *)tableColumn row:(NSInteger)row {
|
||||||
|
return nil;
|
||||||
|
}
|
||||||
|
- (void)tableView:(NSTableView *)tableView willDisplayCell:(id)cell forTableColumn:(NSTableColumn *)tableColumn row:(NSInteger)row {
|
||||||
|
[(MGMViewCell *)cell addSubview:[tasks objectAtIndex:row]];
|
||||||
|
}
|
||||||
|
- (void)saveEntry:(NSURL *)theEntry title:(NSString *)theTitle {
|
||||||
|
[maxQuality selectCellAtRow:0 column:[[NSUserDefaults standardUserDefaults] integerForKey:MGMMaxQuality]];
|
||||||
|
NSSavePanel<NSSavePanelProtocol> *panel = [NSSavePanel savePanel];
|
||||||
|
[panel setAccessoryView:exportView];
|
||||||
|
int returnCode;
|
||||||
|
if ([[[MGMSystemInfo new] autorelease] isAfterSnowLeopard]) {
|
||||||
|
[panel setNameFieldStringValue:theTitle];
|
||||||
|
returnCode = [panel runModal];
|
||||||
|
} else {
|
||||||
|
returnCode = [panel runModalForDirectory:nil file:theTitle];
|
||||||
|
}
|
||||||
|
if (returnCode==NSOKButton) {
|
||||||
|
NSString *file = [[panel URL] path];
|
||||||
|
NSMutableDictionary *taskInfo = [NSMutableDictionary dictionary];
|
||||||
|
NSString *extension = MGMMP4Ext;
|
||||||
|
if ([exportFormat indexOfSelectedItem]!=0) {
|
||||||
|
[taskInfo setObject:[NSNumber numberWithInt:[exportFormat indexOfSelectedItem]-1] forKey:MGMConvertFormat];
|
||||||
|
extension = [[formats objectAtIndex:[[taskInfo objectForKey:MGMConvertFormat] intValue]] objectForKey:MGMFExtension];
|
||||||
|
}
|
||||||
|
[taskInfo setObject:[file stringByAppendingPathExtension:extension] forKey:MGMDonePath];
|
||||||
|
[taskInfo setObject:[NSNumber numberWithInt:[maxQuality selectedColumn]] forKey:MGMMaxQualityKey];
|
||||||
|
if (![[widthField stringValue] isEqual:@""])
|
||||||
|
[taskInfo setObject:[widthField stringValue] forKey:MGMCustomWidthKey];
|
||||||
|
if (![[heightField stringValue] isEqual:@""])
|
||||||
|
[taskInfo setObject:[heightField stringValue] forKey:MGMCustomHeightKey];
|
||||||
|
if (![[bitrateField stringValue] isEqual:@""])
|
||||||
|
[taskInfo setObject:[bitrateField stringValue] forKey:MGMCustomBitrateKey];
|
||||||
|
[self addTask:taskInfo withVideo:theEntry];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
- (void)application:(NSApplication *)sender openFiles:(NSArray *)files {
|
||||||
|
for (int i=0; i<[files count]; i++) {
|
||||||
|
NSString *file = [files objectAtIndex:i];
|
||||||
|
NSString *extension = [[file pathExtension] lowercaseString];
|
||||||
|
if ([extension isEqual:MGMYVTaskExt]) {
|
||||||
|
BOOL open = YES;
|
||||||
|
for (int f=0; f<[tasks count]; f++) {
|
||||||
|
if ([[[tasks objectAtIndex:f] YVTaskPath] isEqual:file]) {
|
||||||
|
open = NO;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (open) {
|
||||||
|
[tasks addObject:[MGMTaskView taskViewWithTask:[NSDictionary dictionaryWithObject:file forKey:MGMYVTaskPath] withVideo:nil manager:self]];
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
BOOL isVideo;
|
||||||
|
NSRect frame = [exportFormat frame];
|
||||||
|
if ([extension isEqual:MGMMP4Ext] || [extension isEqual:MGMM4VExt] || [extension isEqual:MGMMOVExt] || [extension isEqual:MGMWMVExt] || [extension isEqual:MGMAVIExt] || [extension isEqual:MGMFLVExt] || [extension isEqual:MGMMKVExt] || [extension isEqual:MGMDVExt] || [extension isEqual:MGMMPGExt]) {
|
||||||
|
isVideo = YES;
|
||||||
|
[maxQuality setEnabled:NO];
|
||||||
|
[exportFormat removeFromSuperview];
|
||||||
|
[exportView addSubview:videoFormat];
|
||||||
|
[videoFormat setFrame:frame];
|
||||||
|
} else if ([extension isEqual:MGMMP3Ext] || [extension isEqual:MGMM4AExt] || [extension isEqual:MGMWAVExt] || [extension isEqual:MGMAIFFExt] || [extension isEqual:MGMAIFExt] || [extension isEqual:MGMWMAExt]) {
|
||||||
|
isVideo = NO;
|
||||||
|
[maxQuality setEnabled:NO];
|
||||||
|
[exportFormat removeFromSuperview];
|
||||||
|
[exportView addSubview:audioFormat];
|
||||||
|
[audioFormat setFrame:frame];
|
||||||
|
} else {
|
||||||
|
NSBeep();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
NSSavePanel<NSSavePanelProtocol> *panel = [NSSavePanel savePanel];
|
||||||
|
[panel setAccessoryView:exportView];
|
||||||
|
int returnCode;
|
||||||
|
if ([panel respondsToSelector:@selector(runModalForDirectory:file:)]) {
|
||||||
|
returnCode = [panel runModalForDirectory:[file stringByDeletingLastPathComponent] file:[[file lastPathComponent] stringByDeletingPathExtension]];
|
||||||
|
} else {
|
||||||
|
[panel setNameFieldStringValue:[[file lastPathComponent] stringByDeletingPathExtension]];
|
||||||
|
[panel setDirectoryURL:[NSURL fileURLWithPath:[file stringByDeletingLastPathComponent]]];
|
||||||
|
returnCode = [panel runModal];
|
||||||
|
}
|
||||||
|
if (returnCode==NSOKButton) {
|
||||||
|
NSString *exportFile = [[panel URL] path];
|
||||||
|
NSMutableDictionary *taskInfo = [NSMutableDictionary dictionary];
|
||||||
|
[taskInfo setObject:file forKey:MGMFilePath];
|
||||||
|
[taskInfo setObject:[NSNumber numberWithInt:(isVideo ? [videoFormat indexOfSelectedItem] : [audioFormat indexOfSelectedItem]+9)] forKey:MGMConvertFormat];
|
||||||
|
[taskInfo setObject:[exportFile stringByAppendingPathExtension:[[formats objectAtIndex:[[taskInfo objectForKey:MGMConvertFormat] intValue]] objectForKey:MGMFExtension]] forKey:MGMDonePath];
|
||||||
|
[taskInfo setObject:[NSNumber numberWithInt:[maxQuality selectedColumn]] forKey:MGMMaxQualityKey];
|
||||||
|
if (![[widthField stringValue] isEqual:@""])
|
||||||
|
[taskInfo setObject:[widthField stringValue] forKey:MGMCustomWidthKey];
|
||||||
|
if (![[heightField stringValue] isEqual:@""])
|
||||||
|
[taskInfo setObject:[heightField stringValue] forKey:MGMCustomHeightKey];
|
||||||
|
if (![[bitrateField stringValue] isEqual:@""])
|
||||||
|
[taskInfo setObject:[bitrateField stringValue] forKey:MGMCustomBitrateKey];
|
||||||
|
[taskInfo setObject:[NSNumber numberWithBool:YES] forKey:MGMConverting];
|
||||||
|
[tasks addObject:[MGMTaskView taskViewWithTask:taskInfo withVideo:nil manager:self]];
|
||||||
|
[[tasks lastObject] restart:self];
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isVideo)
|
||||||
|
[videoFormat removeFromSuperview];
|
||||||
|
else
|
||||||
|
[audioFormat removeFromSuperview];
|
||||||
|
[exportView addSubview:exportFormat];
|
||||||
|
[exportFormat setFrame:frame];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
[self reloadData];
|
||||||
|
[mainWindow makeKeyAndOrderFront:sender];
|
||||||
|
[self updateCount];
|
||||||
|
}
|
||||||
|
- (NSApplicationTerminateReply)applicationShouldTerminate:(NSApplication *)sender {
|
||||||
|
for (int i=0; i<[tasks count]; i++) {
|
||||||
|
if ([[tasks objectAtIndex:i] working]) {
|
||||||
|
NSBeginAlertSheet(@"Warning", nil, nil, nil, mainWindow, nil, nil, nil, nil, @"You are still downloading/converting files, please cancel them before you quit.");
|
||||||
|
return NSTerminateCancel;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return NSTerminateNow;
|
||||||
|
}
|
||||||
|
@end
|
64
Classes/YouView/Tasks/MGMTaskView.h
Normal file
64
Classes/YouView/Tasks/MGMTaskView.h
Normal file
@ -0,0 +1,64 @@
|
|||||||
|
//
|
||||||
|
// MGMTaskView.h
|
||||||
|
// YouView
|
||||||
|
//
|
||||||
|
// Created by Mr. Gecko on 4/16/09.
|
||||||
|
// Copyright (c) 2015 Mr. Gecko's Media (James Coleman). https://mrgeckosmedia.com/ All rights reserved.
|
||||||
|
//
|
||||||
|
|
||||||
|
#import <Cocoa/Cocoa.h>
|
||||||
|
|
||||||
|
@class MGMURLConnectionManager, MGMVideoFinder, MGMTaskManager;
|
||||||
|
|
||||||
|
@interface MGMTaskView : NSObject {
|
||||||
|
MGMTaskManager *manager;
|
||||||
|
IBOutlet NSView *mainView;
|
||||||
|
IBOutlet NSImageView *icon;
|
||||||
|
IBOutlet NSTextField *name;
|
||||||
|
IBOutlet NSProgressIndicator *progress;
|
||||||
|
IBOutlet NSTextField *info;
|
||||||
|
IBOutlet NSButton *stop;
|
||||||
|
IBOutlet NSButton *restart;
|
||||||
|
NSMutableDictionary *taskInfo;
|
||||||
|
NSURL *entry;
|
||||||
|
|
||||||
|
MGMURLConnectionManager *connectionManager;
|
||||||
|
MGMVideoFinder *videoFinder;
|
||||||
|
|
||||||
|
int startTime;
|
||||||
|
int bytesReceivedSec;
|
||||||
|
int bytesReceived;
|
||||||
|
NSTimer *secCheckTimer;
|
||||||
|
NSString *receivedSec;
|
||||||
|
int receivedContentLength;
|
||||||
|
int expectedContentLength;
|
||||||
|
|
||||||
|
NSTask *aTask;
|
||||||
|
NSPipe *taskPipe;
|
||||||
|
double time;
|
||||||
|
double videoPlayTime;
|
||||||
|
double videoFrameRate;
|
||||||
|
BOOL waitingToConvert;
|
||||||
|
BOOL converting;
|
||||||
|
BOOL working;
|
||||||
|
BOOL stopped;
|
||||||
|
}
|
||||||
|
+ (id)taskViewWithTask:(NSDictionary *)theTask withVideo:(NSURL *)theVideo manager:(MGMTaskManager *)theManager;
|
||||||
|
- (id)initWithTask:(NSDictionary *)theTask withVideo:(NSURL *)theVideo manager:(MGMTaskManager *)theManager;
|
||||||
|
- (void)setName;
|
||||||
|
- (NSString *)YVTaskPath;
|
||||||
|
- (BOOL)working;
|
||||||
|
- (BOOL)converting;
|
||||||
|
- (BOOL)waitingToConvert;
|
||||||
|
- (NSView *)view;
|
||||||
|
- (NSString *)bytesToString:(double)bytes;
|
||||||
|
- (NSString *)secsToString:(int)secs;
|
||||||
|
- (void)convertProcessFinish:(NSNotification *)note;
|
||||||
|
- (void)convertProcessResponse:(NSString*)response;
|
||||||
|
- (void)convertProcessRead:(NSNotification *)note;
|
||||||
|
- (IBAction)stop:(id)sender;
|
||||||
|
- (IBAction)reveal:(id)sender;
|
||||||
|
- (IBAction)restart:(id)sender;
|
||||||
|
- (void)saveInfo;
|
||||||
|
- (void)startConverson;
|
||||||
|
@end
|
664
Classes/YouView/Tasks/MGMTaskView.m
Normal file
664
Classes/YouView/Tasks/MGMTaskView.m
Normal file
@ -0,0 +1,664 @@
|
|||||||
|
//
|
||||||
|
// MGMTaskView.m
|
||||||
|
// YouView
|
||||||
|
//
|
||||||
|
// Created by Mr. Gecko on 4/16/09.
|
||||||
|
// Copyright (c) 2015 Mr. Gecko's Media (James Coleman). https://mrgeckosmedia.com/ All rights reserved.
|
||||||
|
//
|
||||||
|
|
||||||
|
#import "MGMTaskView.h"
|
||||||
|
#import "MGMTaskManager.h"
|
||||||
|
#import "MGMAddons.h"
|
||||||
|
#import "MGMVideoFinder.h"
|
||||||
|
#import "MGMParentalControls.h"
|
||||||
|
#import "MGMController.h"
|
||||||
|
#import <MGMUsers/MGMUsers.h>
|
||||||
|
#import <GeckoReporter/GeckoReporter.h>
|
||||||
|
#import <sys/sysctl.h>
|
||||||
|
|
||||||
|
@implementation MGMTaskView
|
||||||
|
+ (id)taskViewWithTask:(NSDictionary *)theTask withVideo:(NSURL *)theVideo manager:(MGMTaskManager *)theManager {
|
||||||
|
return [[[self alloc] initWithTask:theTask withVideo:theVideo manager:theManager] autorelease];
|
||||||
|
}
|
||||||
|
- (id)initWithTask:(NSDictionary *)theTask withVideo:(NSURL *)theVideo manager:(MGMTaskManager *)theManager {
|
||||||
|
if (self = [super init]) {
|
||||||
|
manager = [theManager retain];
|
||||||
|
if (![NSBundle loadNibNamed:@"MGMTaskView" owner:self]) {
|
||||||
|
[self release];
|
||||||
|
self = nil;
|
||||||
|
} else {
|
||||||
|
connectionManager = [[MGMURLConnectionManager manager] retain];
|
||||||
|
[connectionManager setUserAgent:MGMUserAgent];
|
||||||
|
|
||||||
|
if (theVideo!=nil) {
|
||||||
|
entry = [theVideo retain];
|
||||||
|
taskInfo = [[NSMutableDictionary dictionaryWithDictionary:theTask] retain];
|
||||||
|
[taskInfo setObject:[[taskInfo objectForKey:MGMDonePath] lastPathComponent] forKey:MGMFileName];
|
||||||
|
[taskInfo setObject:[[taskInfo objectForKey:MGMDonePath] stringByAppendingPathExtension:MGMYVTaskExt] forKey:MGMYVTaskPath];
|
||||||
|
[taskInfo setObject:[[taskInfo objectForKey:MGMYVTaskPath] stringByAppendingPathComponent:[[[taskInfo objectForKey:MGMFileName] stringByDeletingPathExtension] stringByAppendingPathExtension:MGMMP4Ext]] forKey:MGMFilePath];
|
||||||
|
[taskInfo setObject:[taskInfo objectForKey:MGMYVTaskPath] forKey:MGMRevealPath];
|
||||||
|
|
||||||
|
NSFileManager *fileManager = [NSFileManager defaultManager];
|
||||||
|
[fileManager createDirectoryAtPath:[taskInfo objectForKey:MGMYVTaskPath] withAttributes:nil];
|
||||||
|
|
||||||
|
NSString *imageURL = [NSString stringWithFormat:MGMYTImageURL, [entry URLParameterWithName:@"v"]];
|
||||||
|
[taskInfo setObject:imageURL forKey:MGMPreviewURL];
|
||||||
|
|
||||||
|
[progress setHidden:NO];
|
||||||
|
[progress startAnimation:self];
|
||||||
|
[progress setIndeterminate:YES];
|
||||||
|
[restart setHidden:YES];
|
||||||
|
[stop setHidden:NO];
|
||||||
|
|
||||||
|
startTime = [[NSDate date] timeIntervalSince1970];
|
||||||
|
[taskInfo setObject:[entry absoluteString] forKey:MGMURL];
|
||||||
|
[self setName];
|
||||||
|
videoFinder = [[MGMVideoFinder alloc] initWithURL:[NSURL URLWithString:[taskInfo objectForKey:MGMURL]] connectionManager:connectionManager maxQuality:[[taskInfo objectForKey:MGMMaxQualityKey] intValue] delegate:self];
|
||||||
|
} else if ([theTask objectForKey:MGMYVTaskPath]!=nil) {
|
||||||
|
if ([[NSFileManager defaultManager] fileExistsAtPath:[[theTask objectForKey:MGMYVTaskPath] stringByAppendingPathComponent:MGMInfoPlist]]) {
|
||||||
|
taskInfo = [[NSMutableDictionary dictionaryWithContentsOfFile:[[theTask objectForKey:MGMYVTaskPath] stringByAppendingPathComponent:MGMInfoPlist]] retain];
|
||||||
|
if (![[taskInfo objectForKey:MGMYVTaskPath] isEqual:[theTask objectForKey:MGMYVTaskPath]]) {
|
||||||
|
NSString *lastPath = [taskInfo objectForKey:MGMYVTaskPath];
|
||||||
|
[taskInfo setObject:[theTask objectForKey:MGMYVTaskPath] forKey:MGMYVTaskPath];
|
||||||
|
if ([lastPath isEqual:[[taskInfo objectForKey:MGMFilePath] stringByDeletingLastPathComponent]]) {
|
||||||
|
[taskInfo setObject:[[taskInfo objectForKey:MGMYVTaskPath] stringByAppendingPathComponent:[[taskInfo objectForKey:MGMFilePath] lastPathComponent]] forKey:MGMFilePath];
|
||||||
|
}
|
||||||
|
[taskInfo setObject:[[[taskInfo objectForKey:MGMYVTaskPath] stringByDeletingLastPathComponent] stringByAppendingPathComponent:[taskInfo objectForKey:MGMFileName]] forKey:MGMDonePath];
|
||||||
|
[taskInfo setObject:[taskInfo objectForKey:MGMYVTaskPath] forKey:MGMRevealPath];
|
||||||
|
}
|
||||||
|
[self setName];
|
||||||
|
[info setStringValue:@"Click restart to start"];
|
||||||
|
[stop setHidden:YES];
|
||||||
|
[restart setHidden:NO];
|
||||||
|
[progress setHidden:YES];
|
||||||
|
} else {
|
||||||
|
NSBeep();
|
||||||
|
[self release];
|
||||||
|
self = nil;
|
||||||
|
}
|
||||||
|
} else if ([theTask objectForKey:MGMFilePath]!=nil) {
|
||||||
|
taskInfo = [[NSMutableDictionary dictionaryWithDictionary:theTask] retain];
|
||||||
|
[taskInfo setObject:[[taskInfo objectForKey:MGMDonePath] lastPathComponent] forKey:MGMFileName];
|
||||||
|
[taskInfo setObject:[[taskInfo objectForKey:MGMDonePath] stringByAppendingPathExtension:MGMYVTaskExt] forKey:MGMYVTaskPath];
|
||||||
|
[taskInfo setObject:[taskInfo objectForKey:MGMYVTaskPath] forKey:MGMRevealPath];
|
||||||
|
|
||||||
|
NSFileManager *fileManager = [NSFileManager defaultManager];
|
||||||
|
[fileManager createDirectoryAtPath:[taskInfo objectForKey:MGMYVTaskPath] withAttributes:nil];
|
||||||
|
|
||||||
|
[self setName];
|
||||||
|
} else {
|
||||||
|
NSBeep();
|
||||||
|
[self release];
|
||||||
|
self = nil;
|
||||||
|
}
|
||||||
|
if ([taskInfo objectForKey:MGMPreviewURL]!=nil) {
|
||||||
|
#if youviewdebug
|
||||||
|
MGMLog(@"URL: %@", [taskInfo objectForKey:MGMPreviewURL]);
|
||||||
|
#endif
|
||||||
|
NSURLRequest *theRequest = [NSURLRequest requestWithURL:[NSURL URLWithString:[taskInfo objectForKey:MGMPreviewURL]] cachePolicy:NSURLRequestUseProtocolCachePolicy timeoutInterval:10.0];
|
||||||
|
MGMURLBasicHandler *handler = [MGMURLBasicHandler handlerWithRequest:theRequest delegate:self];
|
||||||
|
[handler setFailWithError:@selector(iconLoader:didFailWithError:)];
|
||||||
|
[handler setFinish:@selector(iconLoaderDidFinish:)];
|
||||||
|
[connectionManager addHandler:handler];
|
||||||
|
} else {
|
||||||
|
[icon setImage:[NSImage imageNamed:@"YouViewA"]];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return self;
|
||||||
|
}
|
||||||
|
- (void)dealloc {
|
||||||
|
#if releaseDebug
|
||||||
|
MGMLog(@"%s Releasing", __PRETTY_FUNCTION__);
|
||||||
|
#endif
|
||||||
|
[manager release];
|
||||||
|
[mainView release];
|
||||||
|
[taskInfo release];
|
||||||
|
[entry release];
|
||||||
|
[connectionManager cancelAll];
|
||||||
|
[connectionManager release];
|
||||||
|
[videoFinder release];
|
||||||
|
[secCheckTimer invalidate];
|
||||||
|
[secCheckTimer release];
|
||||||
|
[receivedSec release];
|
||||||
|
[aTask terminate];
|
||||||
|
[aTask release];
|
||||||
|
[taskPipe release];
|
||||||
|
[super dealloc];
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)iconLoader:(MGMURLBasicHandler *)theHandler didFailWithError:(NSError *)theError {
|
||||||
|
NSLog(@"Error loading image: %@", theError);
|
||||||
|
}
|
||||||
|
- (void)iconLoaderDidFinish:(MGMURLBasicHandler *)theHandler {
|
||||||
|
NSImage *image = [[[NSImage alloc] initWithData:[theHandler data]] autorelease];
|
||||||
|
[icon setImage:image];
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)setName {
|
||||||
|
if ([[taskInfo objectForKey:MGMConverting] boolValue]) {
|
||||||
|
[name setStringValue:[taskInfo objectForKey:MGMFileName]];
|
||||||
|
} else {
|
||||||
|
[name setStringValue:[[taskInfo objectForKey:MGMFilePath] lastPathComponent]];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
- (NSString *)YVTaskPath {
|
||||||
|
return [taskInfo objectForKey:MGMYVTaskPath];
|
||||||
|
}
|
||||||
|
- (BOOL)working {
|
||||||
|
return working;
|
||||||
|
}
|
||||||
|
- (BOOL)converting {
|
||||||
|
return converting;
|
||||||
|
}
|
||||||
|
- (BOOL)waitingToConvert {
|
||||||
|
return waitingToConvert;
|
||||||
|
}
|
||||||
|
- (NSView *)view {
|
||||||
|
return mainView;
|
||||||
|
}
|
||||||
|
|
||||||
|
- (NSString *)accessibilityRole {
|
||||||
|
return @"task";
|
||||||
|
}
|
||||||
|
- (NSString *)accessibilityDescription {
|
||||||
|
NSString *taskName = [[taskInfo objectForKey:MGMFilePath] lastPathComponent];
|
||||||
|
if ([[taskInfo objectForKey:MGMConverting] boolValue])
|
||||||
|
taskName = [taskInfo objectForKey:MGMFileName];
|
||||||
|
return [NSString stringWithFormat:@"%@ %@", taskName, [info stringValue]];
|
||||||
|
}
|
||||||
|
|
||||||
|
- (IBAction)stop:(id)sender {
|
||||||
|
stopped = YES;
|
||||||
|
if (working) {
|
||||||
|
if (converting || waitingToConvert) {
|
||||||
|
[[NSNotificationCenter defaultCenter] removeObserver:self];
|
||||||
|
[aTask terminate];
|
||||||
|
[aTask release];
|
||||||
|
aTask = nil;
|
||||||
|
[taskInfo setObject:[NSNumber numberWithBool:YES] forKey:MGMConverting];
|
||||||
|
[self saveInfo];
|
||||||
|
} else {
|
||||||
|
[connectionManager cancelAll];
|
||||||
|
[taskInfo setObject:[NSNumber numberWithBool:NO] forKey:MGMConverting];
|
||||||
|
[self saveInfo];
|
||||||
|
}
|
||||||
|
[info setStringValue:@"Canceled"];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
- (IBAction)reveal:(id)sender {
|
||||||
|
[[NSWorkspace sharedWorkspace] selectFile:[taskInfo objectForKey:MGMRevealPath] inFileViewerRootedAtPath:nil];
|
||||||
|
}
|
||||||
|
- (IBAction)restart:(id)sender {
|
||||||
|
stopped = NO;
|
||||||
|
startTime = [[NSDate date] timeIntervalSince1970]-[[taskInfo objectForKey:MGMTime] intValue];
|
||||||
|
if ([[taskInfo objectForKey:MGMConverting] boolValue]) {
|
||||||
|
[progress setHidden:NO];
|
||||||
|
[progress startAnimation:self];
|
||||||
|
[stop setHidden:NO];
|
||||||
|
[restart setHidden:YES];
|
||||||
|
working = YES;
|
||||||
|
if ([manager ableToConvert:self]) {
|
||||||
|
[self startConverson];
|
||||||
|
} else {
|
||||||
|
[info setStringValue:@"Waiting my turn to convert."];
|
||||||
|
waitingToConvert = YES;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
[progress setHidden:NO];
|
||||||
|
[progress startAnimation:self];
|
||||||
|
[progress setIndeterminate:YES];
|
||||||
|
|
||||||
|
[videoFinder release];
|
||||||
|
videoFinder = [[MGMVideoFinder alloc] initWithURL:[NSURL URLWithString:[taskInfo objectForKey:MGMURL]] connectionManager:connectionManager maxQuality:[[taskInfo objectForKey:MGMMaxQualityKey] intValue] delegate:self];
|
||||||
|
}
|
||||||
|
[self setName];
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)saveInfo {
|
||||||
|
if (secCheckTimer!=nil) {
|
||||||
|
[secCheckTimer invalidate];
|
||||||
|
[secCheckTimer release];
|
||||||
|
secCheckTimer = nil;
|
||||||
|
}
|
||||||
|
working = NO;
|
||||||
|
converting = NO;
|
||||||
|
waitingToConvert = NO;
|
||||||
|
[progress setHidden:YES];
|
||||||
|
[progress stopAnimation:self];
|
||||||
|
[stop setHidden:YES];
|
||||||
|
[restart setHidden:NO];
|
||||||
|
[taskInfo setObject:[NSNumber numberWithInt:[[NSDate date] timeIntervalSince1970]-startTime] forKey:MGMTime];
|
||||||
|
[taskInfo writeToFile:[[taskInfo objectForKey:MGMYVTaskPath] stringByAppendingPathComponent:MGMInfoPlist] atomically:YES];
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)loadFlash:(NSDictionary *)theInfo {
|
||||||
|
if ([taskInfo objectForKey:MGMConvertFormat]==nil) {
|
||||||
|
NSAlert *theAlert = [[NSAlert new] autorelease];
|
||||||
|
[theAlert setMessageText:@"Video Download"];
|
||||||
|
[theAlert setInformativeText:@"YouView was unable to download the video, would you like to download the low quality version?"];
|
||||||
|
[theAlert addButtonWithTitle:@"Yes"];
|
||||||
|
[theAlert addButtonWithTitle:@"No"];
|
||||||
|
int result = [theAlert runModal];
|
||||||
|
if (result==1000) {
|
||||||
|
[videoFinder loadFlash];
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
[videoFinder loadFlash];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
- (void)loadVideo:(NSDictionary *)theInfo {
|
||||||
|
NSFileManager *fileManager = [NSFileManager defaultManager];
|
||||||
|
[taskInfo setObject:[taskInfo objectForKey:MGMYVTaskPath] forKey:MGMRevealPath];
|
||||||
|
if ([[theInfo objectForKey:MGMVLVersion] isEqual:@"FLV"]) {
|
||||||
|
[taskInfo setObject:[[[taskInfo objectForKey:MGMFilePath] stringByDeletingPathExtension] stringByAppendingPathExtension:MGMFLVExt] forKey:MGMFilePath];
|
||||||
|
[self setName];
|
||||||
|
if ([taskInfo objectForKey:MGMConvertFormat]==nil) {
|
||||||
|
[taskInfo setObject:[[[taskInfo objectForKey:MGMDonePath] stringByDeletingPathExtension] stringByAppendingPathExtension:MGMFLVExt] forKey:MGMDonePath];
|
||||||
|
[taskInfo setObject:[[taskInfo objectForKey:MGMDonePath] lastPathComponent] forKey:MGMFileName];
|
||||||
|
NSString *lastPath = [taskInfo objectForKey:MGMYVTaskPath];
|
||||||
|
[taskInfo setObject:[[taskInfo objectForKey:MGMDonePath] stringByAppendingPathExtension:MGMYVTaskExt] forKey:MGMYVTaskPath];
|
||||||
|
if ([[taskInfo objectForKey:MGMDonePath] isEqual:[[taskInfo objectForKey:MGMFilePath] stringByDeletingLastPathComponent]]) {
|
||||||
|
[taskInfo setObject:[[taskInfo objectForKey:MGMYVTaskPath] stringByAppendingPathComponent:[[taskInfo objectForKey:MGMFilePath] lastPathComponent]] forKey:MGMFilePath];
|
||||||
|
}
|
||||||
|
[fileManager moveItemAtPath:lastPath toPath:[taskInfo objectForKey:MGMYVTaskPath]];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
MGMURLBasicHandler *handler = [MGMURLBasicHandler handlerWithRequest:[NSURLRequest requestWithURL:[theInfo objectForKey:MGMVLVideoURLKey]] delegate:self];
|
||||||
|
[handler setFile:[taskInfo objectForKey:MGMFilePath]];
|
||||||
|
[handler setBytesReceived:@selector(fileDownload:receivedBytes:totalBytes:expectedBytes:)];
|
||||||
|
[handler setReceiveResponse:@selector(fileDownload:didReceiveResponse:)];
|
||||||
|
[handler setFailWithError:@selector(fileDownload:didFailWithError:)];
|
||||||
|
[handler setFinish:@selector(fileDownloadDidFinish:)];
|
||||||
|
[connectionManager addHandler:handler];
|
||||||
|
|
||||||
|
[progress setHidden:NO];
|
||||||
|
[progress stopAnimation:self];
|
||||||
|
[progress setIndeterminate:NO];
|
||||||
|
[progress setDoubleValue:0];
|
||||||
|
[restart setHidden:YES];
|
||||||
|
[stop setHidden:NO];
|
||||||
|
working = YES;
|
||||||
|
[receivedSec release];
|
||||||
|
receivedSec = [@"0 Bytes" retain];
|
||||||
|
bytesReceivedSec = 1;
|
||||||
|
receivedContentLength = 0;
|
||||||
|
expectedContentLength = 0;
|
||||||
|
if (secCheckTimer==nil) {
|
||||||
|
secCheckTimer = [[NSTimer scheduledTimerWithTimeInterval:1.0 target:self selector:@selector(secCheck) userInfo:nil repeats:YES] retain];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
- (NSString *)bytesToString:(double)bytes {
|
||||||
|
NSString *type = @"Bytes";
|
||||||
|
if (bytes>1024.00) {
|
||||||
|
type = @"KB";
|
||||||
|
bytes = bytes/1024.00;
|
||||||
|
if (bytes>1024.00) {
|
||||||
|
type = @"MB";
|
||||||
|
bytes = bytes/1024.00;
|
||||||
|
if (bytes>1024.00) {
|
||||||
|
type = @"GB";
|
||||||
|
bytes = bytes/1024.00;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return [NSString stringWithFormat:@"%.2f %@", bytes, type];
|
||||||
|
}
|
||||||
|
|
||||||
|
- (NSString *)secsToString:(int)secs {
|
||||||
|
NSString *type = @"Second";
|
||||||
|
if (secs>60) {
|
||||||
|
type = @"Minute";
|
||||||
|
secs = secs/60;
|
||||||
|
if (secs>60) {
|
||||||
|
type = @"Hour";
|
||||||
|
secs = secs/60;
|
||||||
|
if (secs>24) {
|
||||||
|
type = @"Day";
|
||||||
|
secs = secs/24;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return [NSString stringWithFormat:@"%d %@%@", secs, type, (secs==1 ? @"" : @"s")];
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)sound:(NSSound *)sound didFinishPlaying:(BOOL)didFinish {
|
||||||
|
if (didFinish)
|
||||||
|
[sound release];
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)convertProcessFinish:(NSNotification *)note {
|
||||||
|
if (!stopped) {
|
||||||
|
[[NSNotificationCenter defaultCenter] removeObserver:self];
|
||||||
|
[aTask release];
|
||||||
|
aTask = nil;
|
||||||
|
[taskPipe release];
|
||||||
|
taskPipe = nil;
|
||||||
|
working = NO;
|
||||||
|
converting = NO;
|
||||||
|
[[NSFileManager defaultManager] moveItemAtPath:[taskInfo objectForKey:MGMConvertPath] toPath:[taskInfo objectForKey:MGMDonePath]];
|
||||||
|
[[NSFileManager defaultManager] removeItemAtPath:[taskInfo objectForKey:MGMYVTaskPath]];
|
||||||
|
[taskInfo setObject:[taskInfo objectForKey:MGMDonePath] forKey:MGMRevealPath];
|
||||||
|
int runTime = [[NSDate date] timeIntervalSince1970]-startTime;
|
||||||
|
[info setStringValue:[NSString stringWithFormat:@"Finished Task in %@", [self secsToString:runTime]]];
|
||||||
|
[progress setHidden:YES];
|
||||||
|
[progress stopAnimation:self];
|
||||||
|
[stop setHidden:YES];
|
||||||
|
NSSound *done = [[NSSound soundNamed:@"Glass"] retain];
|
||||||
|
[done setDelegate:self];
|
||||||
|
[done play];
|
||||||
|
[manager nextConversion];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
- (void)convertProcessResponse:(NSString*)response {
|
||||||
|
if ([response hasPrefix:@"frame="]) {
|
||||||
|
NSRange f, l;
|
||||||
|
NSString *s;
|
||||||
|
int frame=0, fps=0;
|
||||||
|
f = [response rangeOfString:@"frame="];
|
||||||
|
if (f.location != NSNotFound) {
|
||||||
|
s = [response substringFromIndex:f.location + f.length];
|
||||||
|
|
||||||
|
l = [s rangeOfString:@"fps"];
|
||||||
|
if (l.location == NSNotFound) MGMLog(@"failed");
|
||||||
|
frame = [[s substringWithRange:NSMakeRange(0, l.location)] intValue];
|
||||||
|
}
|
||||||
|
f = [response rangeOfString:@"fps="];
|
||||||
|
if (f.location != NSNotFound) {
|
||||||
|
s = [response substringFromIndex:f.location + f.length];
|
||||||
|
|
||||||
|
l = [s rangeOfString:@"q"];
|
||||||
|
if (l.location == NSNotFound) MGMLog(@"failed");
|
||||||
|
fps = [[s substringWithRange:NSMakeRange(0, l.location)] intValue];
|
||||||
|
if (fps==0) {
|
||||||
|
fps = frame;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
int totalFrames = videoPlayTime * videoFrameRate;
|
||||||
|
[progress setDoubleValue:(double)frame / (double)totalFrames];
|
||||||
|
int timeLeft = 0;
|
||||||
|
if (frame!=0 && fps!=0)
|
||||||
|
timeLeft = (totalFrames-frame)/fps;
|
||||||
|
[info setStringValue:[NSString stringWithFormat:@"%d FPS current frame %d - %@", fps, frame, [self secsToString:timeLeft]]];
|
||||||
|
} else if ([response hasPrefix:@"size"]) {
|
||||||
|
NSRange f, l;
|
||||||
|
NSString *s, *bitrate = nil;
|
||||||
|
double lasttime = time;
|
||||||
|
f = [response rangeOfString:@"time="];
|
||||||
|
if (f.location != NSNotFound) {
|
||||||
|
s = [response substringFromIndex:f.location + f.length];
|
||||||
|
|
||||||
|
l = [s rangeOfString:@"bitrate"];
|
||||||
|
if (l.location == NSNotFound) MGMLog(@"failed");
|
||||||
|
time = [[s substringWithRange:NSMakeRange(0, l.location)] doubleValue];
|
||||||
|
}
|
||||||
|
f = [response rangeOfString:@"bitrate="];
|
||||||
|
if (f.location != NSNotFound) {
|
||||||
|
bitrate = [[[response substringFromIndex:f.location + f.length] replace:@"\r" with:@""] stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceCharacterSet]];
|
||||||
|
}
|
||||||
|
[progress setDoubleValue:time / videoPlayTime];
|
||||||
|
int timeLeft = (videoPlayTime-time)/(time-lasttime);
|
||||||
|
if (bitrate!=nil)
|
||||||
|
[info setStringValue:[NSString stringWithFormat:@"Bitrate %@ - %@", bitrate, [self secsToString:timeLeft]]];
|
||||||
|
} else {
|
||||||
|
if ([response length] > 0)
|
||||||
|
MGMLog(@"Task: %@", response);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
- (void)convertProcessRead:(NSNotification *)note {
|
||||||
|
if (![[note name] isEqual:NSFileHandleReadCompletionNotification])
|
||||||
|
return;
|
||||||
|
|
||||||
|
NSData *data = [[note userInfo] objectForKey:NSFileHandleNotificationDataItem];
|
||||||
|
|
||||||
|
if ([data length]) {
|
||||||
|
NSMutableString *buffer = [NSMutableString string];
|
||||||
|
NSString *string = [[[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding] autorelease];
|
||||||
|
|
||||||
|
NSArray *components = [string componentsSeparatedByString:@"\n"];
|
||||||
|
assert([components count] > 0);
|
||||||
|
for (int i = 0; i < [components count]-1; ++i) {
|
||||||
|
[buffer appendString:[components objectAtIndex:i]];
|
||||||
|
[self convertProcessResponse:buffer];
|
||||||
|
[buffer setString: @""];
|
||||||
|
}
|
||||||
|
|
||||||
|
if ([string hasSuffix:@"\n"] || [string hasSuffix:@"\r"]) {
|
||||||
|
[buffer appendString:[components objectAtIndex:[components count]-1]];
|
||||||
|
[self convertProcessResponse:buffer];
|
||||||
|
[buffer setString:@""];
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
[buffer setString:[components objectAtIndex:[components count]-1]];
|
||||||
|
}
|
||||||
|
[[note object] readInBackgroundAndNotify];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)startConverson {
|
||||||
|
converting = YES;
|
||||||
|
waitingToConvert = NO;
|
||||||
|
[taskInfo setObject:[NSNumber numberWithBool:YES] forKey:MGMConverting];
|
||||||
|
NSString *file = [[self YVTaskPath] stringByAppendingPathComponent:[[[taskInfo objectForKey:MGMFilePath] lastPathComponent] stringByDeletingPathExtension]];
|
||||||
|
[taskInfo setObject:[[file stringByAppendingPathExtension:@"tmp"] stringByAppendingPathExtension:[[[manager formats] objectAtIndex:[[taskInfo objectForKey:MGMConvertFormat] intValue]] objectForKey:MGMFExtension]] forKey:MGMConvertPath];
|
||||||
|
[self setName];
|
||||||
|
|
||||||
|
NSTask *task = [[NSTask new] autorelease];
|
||||||
|
[task setLaunchPath:[[NSBundle mainBundle] pathForResource:@"mediainfo" ofType:@""]];
|
||||||
|
[task setArguments:[NSArray arrayWithObjects:[@"--Inform=file://" stringByAppendingString:[[NSBundle mainBundle] pathForResource:@"mediainfo" ofType:@"form"]], [taskInfo objectForKey:MGMFilePath], nil]];
|
||||||
|
|
||||||
|
NSPipe *pipe = [NSPipe pipe];
|
||||||
|
[task setStandardOutput:[pipe fileHandleForWriting]];
|
||||||
|
[task launch];
|
||||||
|
[task waitUntilExit];
|
||||||
|
|
||||||
|
NSString *error = nil;
|
||||||
|
NSMutableData *plistData = [NSMutableData data];
|
||||||
|
[plistData appendData:[@"<?xml version=\"1.0\" encoding=\"UTF-8\"?>\r\n<!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/PropertyList-1.0.dtd\">\r\n<plist version=\"1.0\">\r\n<array>\r\n" dataUsingEncoding:NSUTF8StringEncoding]];
|
||||||
|
[plistData appendData:[[pipe fileHandleForReading] availableData]];
|
||||||
|
[plistData appendData:[@"\r\n</array>\r\n</plist>" dataUsingEncoding:NSUTF8StringEncoding]];
|
||||||
|
NSArray *plist = [NSPropertyListSerialization propertyListFromData:plistData mutabilityOption:NSPropertyListImmutable format:NULL errorDescription:&error];
|
||||||
|
|
||||||
|
if (error!=nil) {
|
||||||
|
MGMLog(@"Error %@", error);
|
||||||
|
NSAlert *theAlert = [[NSAlert new] autorelease];
|
||||||
|
[theAlert addButtonWithTitle:@"OK"];
|
||||||
|
[theAlert setMessageText:@"Convert Error"];
|
||||||
|
[theAlert setInformativeText:error];
|
||||||
|
[theAlert setAlertStyle:NSWarningAlertStyle];
|
||||||
|
[theAlert runModal];
|
||||||
|
} else {
|
||||||
|
[taskInfo setObject:plist forKey:MGMMediaInfoKey];
|
||||||
|
if ([taskInfo objectForKey:MGMPreviewURL]==nil) {
|
||||||
|
int playTime = 0;
|
||||||
|
NSSize originalSize = NSZeroSize;
|
||||||
|
for (int i=0; i<[[taskInfo objectForKey:MGMMediaInfoKey] count]; i++) {
|
||||||
|
if ([[[[taskInfo objectForKey:MGMMediaInfoKey] objectAtIndex:i] objectForKey:MGMTypeKey] isEqual:MGMTypeGeneralKey]) {
|
||||||
|
playTime = [[[[taskInfo objectForKey:MGMMediaInfoKey] objectAtIndex:i] objectForKey:MGMPlayTimeKey] intValue]/1000;
|
||||||
|
} else if ([[[[taskInfo objectForKey:MGMMediaInfoKey] objectAtIndex:i] objectForKey:MGMTypeKey] isEqual:MGMTypeVideoKey]) {
|
||||||
|
originalSize.width = [[[[taskInfo objectForKey:MGMMediaInfoKey] objectAtIndex:i] objectForKey:MGMWidthKey] intValue];
|
||||||
|
originalSize.height = [[[[taskInfo objectForKey:MGMMediaInfoKey] objectAtIndex:i] objectForKey:MGMHeightKey] intValue];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!NSEqualSizes(originalSize, NSZeroSize)) {
|
||||||
|
NSTask *task = [[NSTask new] autorelease];
|
||||||
|
[task setLaunchPath:[[NSBundle mainBundle] pathForResource:@"ffmpeg" ofType:@""]];
|
||||||
|
if (playTime>8)
|
||||||
|
playTime = 8;
|
||||||
|
NSSize destSize = NSMakeSize(128, 128);
|
||||||
|
float scaleFactor = 0.0;
|
||||||
|
float scaledWidth = destSize.width;
|
||||||
|
float scaledHeight = destSize.height;
|
||||||
|
|
||||||
|
if (NSEqualSizes(originalSize, destSize) == NO) {
|
||||||
|
float widthFactor = destSize.width / originalSize.width;
|
||||||
|
float heightFactor = destSize.height / originalSize.height;
|
||||||
|
|
||||||
|
if (widthFactor < heightFactor)
|
||||||
|
scaleFactor = widthFactor;
|
||||||
|
else
|
||||||
|
scaleFactor = heightFactor;
|
||||||
|
|
||||||
|
scaledWidth = (int)(originalSize.width * scaleFactor);
|
||||||
|
scaledHeight = (int)(originalSize.height * scaleFactor);
|
||||||
|
if ((scaledWidth/2.0)!=(float)((int)(scaledWidth/2.0)))
|
||||||
|
scaledWidth -= 1;
|
||||||
|
if ((scaledHeight/2.0)!=(float)((int)(scaledHeight/2.0)))
|
||||||
|
scaledHeight -= 1;
|
||||||
|
}
|
||||||
|
srandomdev();
|
||||||
|
NSString *savePath = [[[MGMUser cachePath] stringByAppendingPathComponent:[[[NSNumber numberWithInt:random()] stringValue] MD5]] stringByAppendingPathExtension:MGMJPGExt];
|
||||||
|
[task setArguments:[NSArray arrayWithObjects:@"-y", @"-i", [taskInfo objectForKey:MGMFilePath], @"-f", @"image2", @"-ss", [[NSNumber numberWithInt:playTime] stringValue], @"-sameq", @"-t", @"0.001", @"-s", [NSString stringWithFormat:@"%.0fx%.0f", scaledWidth, scaledHeight], savePath, nil]];
|
||||||
|
[task launch];
|
||||||
|
[task waitUntilExit];
|
||||||
|
if ([task terminationStatus]==0) {
|
||||||
|
[taskInfo setObject:[[NSURL fileURLWithPath:savePath] absoluteString] forKey:MGMPreviewURL];
|
||||||
|
NSImage *image = [[[NSImage alloc] initWithContentsOfFile:savePath] autorelease];
|
||||||
|
[icon setImage:image];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
[taskInfo setObject:[NSNumber numberWithInt:[[NSDate date] timeIntervalSince1970]-startTime] forKey:MGMTime];
|
||||||
|
[taskInfo writeToFile:[[taskInfo objectForKey:MGMYVTaskPath] stringByAppendingPathComponent:MGMInfoPlist] atomically:YES];
|
||||||
|
|
||||||
|
aTask = [NSTask new];
|
||||||
|
[aTask setLaunchPath:[[NSBundle mainBundle] pathForResource:@"ffmpeg" ofType:@""]];
|
||||||
|
NSMutableArray *arguments = [NSMutableArray arrayWithObjects:@"-y", @"-i", [taskInfo objectForKey:MGMFilePath], nil];
|
||||||
|
[arguments addObjectsFromArray:[[[manager formats] objectAtIndex:[[taskInfo objectForKey:MGMConvertFormat] intValue]] objectForKey:MGMFArguments]];
|
||||||
|
int currFormat = [[taskInfo objectForKey:MGMConvertFormat] intValue];
|
||||||
|
#if youviewdebug
|
||||||
|
MGMLog(@"Format: %d", currFormat);
|
||||||
|
#endif
|
||||||
|
if ([taskInfo objectForKey:MGMCustomBitrateKey]!=nil)
|
||||||
|
[arguments addObjectsFromArray:[NSArray arrayWithObjects:@"-b", [taskInfo objectForKey:MGMCustomBitrateKey], nil]];
|
||||||
|
if (currFormat!=2 && currFormat!=3 && currFormat!=5 && currFormat!=8)
|
||||||
|
[arguments addObjectsFromArray:[NSArray arrayWithObjects:@"-threads", [NSString stringWithFormat:@"%d", [[MGMSystemInfo info] CPUCount]], nil]];
|
||||||
|
for (int i=0; i<[[taskInfo objectForKey:MGMMediaInfoKey] count]; i++) {
|
||||||
|
if ([[[[taskInfo objectForKey:MGMMediaInfoKey] objectAtIndex:i] objectForKey:MGMTypeKey] isEqual:MGMTypeGeneralKey]) {
|
||||||
|
videoPlayTime = [[[[taskInfo objectForKey:MGMMediaInfoKey] objectAtIndex:i] objectForKey:MGMPlayTimeKey] doubleValue]/1000.0;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (![[taskInfo objectForKey:MGMFAudio] boolValue]) {
|
||||||
|
for (int i=0; i<[[taskInfo objectForKey:MGMMediaInfoKey] count]; i++) {
|
||||||
|
if ([[[[taskInfo objectForKey:MGMMediaInfoKey] objectAtIndex:i] objectForKey:MGMTypeKey] isEqual:MGMTypeVideoKey]) {
|
||||||
|
videoFrameRate = [[[[taskInfo objectForKey:MGMMediaInfoKey] objectAtIndex:i] objectForKey:MGMFrameRateKey] doubleValue];
|
||||||
|
[arguments addObjectsFromArray:[NSArray arrayWithObjects:@"-r", [[[taskInfo objectForKey:MGMMediaInfoKey] objectAtIndex:i] objectForKey:MGMFrameRateKey], nil]];
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for (int i=0; i<[[taskInfo objectForKey:MGMMediaInfoKey] count]; i++) {
|
||||||
|
if ([[[[taskInfo objectForKey:MGMMediaInfoKey] objectAtIndex:i] objectForKey:MGMTypeKey] isEqual:MGMTypeAudioKey]) {
|
||||||
|
[arguments addObjectsFromArray:[NSArray arrayWithObjects:@"-ar", [[[taskInfo objectForKey:MGMMediaInfoKey] objectAtIndex:i] objectForKey:MGMSamplingRateKey], @"-ab", [[[taskInfo objectForKey:MGMMediaInfoKey] objectAtIndex:i] objectForKey:MGMBitrateKey], nil]];
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if ([taskInfo objectForKey:MGMCustomWidthKey]!=nil && [taskInfo objectForKey:MGMCustomHeightKey]!=nil) {
|
||||||
|
[arguments addObjectsFromArray:[NSArray arrayWithObjects:@"-s", [NSString stringWithFormat:@"%@x%@", [taskInfo objectForKey:MGMCustomWidthKey], [taskInfo objectForKey:MGMCustomHeightKey]], nil]];
|
||||||
|
} else if ([taskInfo objectForKey:MGMCustomWidthKey]!=nil) {
|
||||||
|
[arguments addObjectsFromArray:[NSArray arrayWithObjects:@"-s", [NSString stringWithFormat:@"%@x%@", [taskInfo objectForKey:MGMCustomWidthKey], [taskInfo objectForKey:MGMHeightKey]], nil]];
|
||||||
|
} else if ([taskInfo objectForKey:MGMCustomHeightKey]!=nil) {
|
||||||
|
[arguments addObjectsFromArray:[NSArray arrayWithObjects:@"-s", [NSString stringWithFormat:@"%@x%@", [taskInfo objectForKey:MGMWidthKey], [taskInfo objectForKey:MGMCustomHeightKey]], nil]];
|
||||||
|
} else if ([taskInfo objectForKey:MGMCustomBitrateKey]==nil) {
|
||||||
|
for (int i=0; i<[[taskInfo objectForKey:MGMMediaInfoKey] count]; i++) {
|
||||||
|
if ([[[[taskInfo objectForKey:MGMMediaInfoKey] objectAtIndex:i] objectForKey:MGMTypeKey] isEqual:MGMTypeVideoKey]) {
|
||||||
|
[arguments addObjectsFromArray:[NSArray arrayWithObjects:@"-b", [[[taskInfo objectForKey:MGMMediaInfoKey] objectAtIndex:i] objectForKey:MGMBitrateKey], nil]];
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
[arguments addObject:[taskInfo objectForKey:MGMConvertPath]];
|
||||||
|
#if youviewdebug
|
||||||
|
NSLog(@"Task Info: %@", taskInfo);
|
||||||
|
NSLog(@"Arugments: %@", arguments);
|
||||||
|
#endif
|
||||||
|
[aTask setArguments:arguments];
|
||||||
|
taskPipe = [NSPipe new];
|
||||||
|
[aTask setStandardError:[taskPipe fileHandleForWriting]];
|
||||||
|
[aTask setStandardOutput:[taskPipe fileHandleForWriting]];
|
||||||
|
|
||||||
|
int runTime = [[NSDate date] timeIntervalSince1970]-startTime;
|
||||||
|
[taskInfo setObject:[NSNumber numberWithInt:runTime] forKey:MGMTime];
|
||||||
|
[info setStringValue:[NSString stringWithFormat:@"%@ downloaded at %@/sec and took %@", [self bytesToString:receivedContentLength], receivedSec, [self secsToString:runTime]]];
|
||||||
|
|
||||||
|
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(convertProcessFinish:) name:NSTaskDidTerminateNotification object:nil];
|
||||||
|
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(convertProcessRead:) name:NSFileHandleReadCompletionNotification object:[taskPipe fileHandleForReading]];
|
||||||
|
[[taskPipe fileHandleForReading] readInBackgroundAndNotify];
|
||||||
|
[aTask launch];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)setDownloading:(BOOL)isDownloading {
|
||||||
|
if (working != isDownloading) {
|
||||||
|
working = isDownloading;
|
||||||
|
[secCheckTimer invalidate];
|
||||||
|
[secCheckTimer release];
|
||||||
|
secCheckTimer = nil;
|
||||||
|
if (!working) {
|
||||||
|
if ([taskInfo objectForKey:MGMConvertFormat]!=nil) {
|
||||||
|
working = YES;
|
||||||
|
if ([manager ableToConvert:self]) {
|
||||||
|
[self startConverson];
|
||||||
|
} else {
|
||||||
|
[info setStringValue:@"Waiting my turn to convert."];
|
||||||
|
waitingToConvert = YES;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
[[NSFileManager defaultManager] moveItemAtPath:[taskInfo objectForKey:MGMFilePath] toPath:[taskInfo objectForKey:MGMDonePath]];
|
||||||
|
[[NSFileManager defaultManager] removeItemAtPath:[taskInfo objectForKey:MGMYVTaskPath]];
|
||||||
|
[taskInfo setObject:[taskInfo objectForKey:MGMDonePath] forKey:MGMRevealPath];
|
||||||
|
int runTime = [[NSDate date] timeIntervalSince1970]-startTime;
|
||||||
|
[info setStringValue:[NSString stringWithFormat:@"%@ downloaded at %@/sec and took %@", [self bytesToString:receivedContentLength], receivedSec, [self secsToString:runTime]]];
|
||||||
|
[progress setHidden:YES];
|
||||||
|
[progress stopAnimation:self];
|
||||||
|
[stop setHidden:YES];
|
||||||
|
NSSound *done = [[NSSound soundNamed:@"Glass"] retain];
|
||||||
|
[done setDelegate:self];
|
||||||
|
[done play];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
- (void)secCheck {
|
||||||
|
[receivedSec release];
|
||||||
|
receivedSec = [[self bytesToString:(double)bytesReceived] retain];
|
||||||
|
bytesReceivedSec = (bytesReceived==0 ? 1 : bytesReceived);
|
||||||
|
bytesReceived = 0;
|
||||||
|
int secs = (expectedContentLength-receivedContentLength)/bytesReceivedSec;
|
||||||
|
[info setStringValue:[NSString stringWithFormat:@"%@ of %@ (%@/sec) - %@", [self bytesToString:(double)receivedContentLength], [self bytesToString:(double)expectedContentLength], receivedSec, [self secsToString:secs]]];
|
||||||
|
}
|
||||||
|
- (void)fileDownload:(MGMURLBasicHandler *)theHandler didReceiveResponse:(NSHTTPURLResponse *)theResponse {
|
||||||
|
//NSLog(@"Got response %d", [theResponse statusCode]);
|
||||||
|
[self setDownloading:YES];
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)fileDownload:(MGMURLBasicHandler *)theHandler receivedBytes:(unsigned long)theBytes totalBytes:(unsigned long)theTotalBytes expectedBytes:(unsigned long)theExpectedBytes {
|
||||||
|
expectedContentLength = theExpectedBytes;
|
||||||
|
receivedContentLength = theTotalBytes;
|
||||||
|
bytesReceived += theBytes;
|
||||||
|
[progress setDoubleValue:(double)theTotalBytes/(double)theExpectedBytes];
|
||||||
|
}
|
||||||
|
- (void)fileDownload:(MGMURLBasicHandler *)theHandler didFailWithError:(NSError *)error {
|
||||||
|
NSLog(@"%@", error);
|
||||||
|
[self saveInfo];
|
||||||
|
if ([[videoFinder version] isEqual:@"FLV"]) {
|
||||||
|
[info setStringValue:@"Error"];
|
||||||
|
NSAlert *theAlert = [[NSAlert new] autorelease];
|
||||||
|
[theAlert addButtonWithTitle:@"OK"];
|
||||||
|
[theAlert setMessageText:@"Video Downloading"];
|
||||||
|
[theAlert setInformativeText:@"YouView was unable to download the video.\nPlease contact support via the help menu."];
|
||||||
|
[theAlert runModal];
|
||||||
|
} else {
|
||||||
|
[videoFinder shouldLoadFlash];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
- (void)fileDownloadDidFinish:(MGMURLBasicHandler *)theHandler {
|
||||||
|
[self setDownloading:NO];
|
||||||
|
}
|
||||||
|
@end
|
22
Classes/YouView/Views/MGMGradient.h
Normal file
22
Classes/YouView/Views/MGMGradient.h
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
//
|
||||||
|
// MGMGradient.h
|
||||||
|
// YouView
|
||||||
|
//
|
||||||
|
// Created by Mr. Gecko on 1/28/09.
|
||||||
|
// Copyright (c) 2015 Mr. Gecko's Media (James Coleman). https://mrgeckosmedia.com/ All rights reserved.
|
||||||
|
//
|
||||||
|
|
||||||
|
#import <Cocoa/Cocoa.h>
|
||||||
|
|
||||||
|
|
||||||
|
@interface MGMGradient : NSOutlineView {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@end
|
||||||
|
|
||||||
|
@interface MGMTextCell : NSTextFieldCell {
|
||||||
|
|
||||||
|
}
|
||||||
|
- (NSString*)truncateString:(NSString *)string forWidth:(double) inWidth andAttributes:(NSDictionary*)inAttributes;
|
||||||
|
@end
|
135
Classes/YouView/Views/MGMGradient.m
Normal file
135
Classes/YouView/Views/MGMGradient.m
Normal file
@ -0,0 +1,135 @@
|
|||||||
|
//
|
||||||
|
// MGMGradient.m
|
||||||
|
// YouView
|
||||||
|
//
|
||||||
|
// Created by Mr. Gecko on 1/28/09.
|
||||||
|
// Copyright (c) 2015 Mr. Gecko's Media (James Coleman). https://mrgeckosmedia.com/ All rights reserved.
|
||||||
|
//
|
||||||
|
|
||||||
|
#import "MGMGradient.h"
|
||||||
|
#import "MGMController.h"
|
||||||
|
#import <GeckoReporter/GeckoReporter.h>
|
||||||
|
|
||||||
|
@implementation MGMGradient
|
||||||
|
- (void)awakeFromNib {
|
||||||
|
[self setIntercellSpacing:NSMakeSize(0.0, 0.0)];
|
||||||
|
|
||||||
|
NSTableColumn *column = [[self tableColumns] objectAtIndex:0];
|
||||||
|
MGMTextCell *theTextCell = [[MGMTextCell new] autorelease];
|
||||||
|
[column setDataCell:theTextCell];
|
||||||
|
[[column dataCell] setFont:[NSFont labelFontOfSize:[NSFont labelFontSize]]];
|
||||||
|
}
|
||||||
|
- (void)dealloc {
|
||||||
|
#if releaseDebug
|
||||||
|
MGMLog(@"%s Releasing", __PRETTY_FUNCTION__);
|
||||||
|
#endif
|
||||||
|
[[NSNotificationCenter defaultCenter] removeObserver:self];
|
||||||
|
[super dealloc];
|
||||||
|
}
|
||||||
|
- (void)highlightSelectionInClipRect:(NSRect)clipRect {
|
||||||
|
int selectedRow = [self selectedRow];
|
||||||
|
if(selectedRow == -1)
|
||||||
|
return;
|
||||||
|
[self lockFocus];
|
||||||
|
NSImage *bgImg;
|
||||||
|
if([[self window] firstResponder]==self && [[self window] isKeyWindow]) {
|
||||||
|
bgImg = [NSImage imageNamed:@"gradient"];
|
||||||
|
} else {
|
||||||
|
bgImg = [NSImage imageNamed:@"gradientDeselected"];
|
||||||
|
}
|
||||||
|
|
||||||
|
NSRect drawingRect = [self rectOfRow:selectedRow];
|
||||||
|
NSSize bgSize = [bgImg size];
|
||||||
|
int i = 0;
|
||||||
|
for (i = drawingRect.origin.x; i < (drawingRect.origin.x + drawingRect.size.width); i += bgSize.width) {
|
||||||
|
[bgImg drawInRect:NSMakeRect(i, drawingRect.origin.y, bgSize.width, drawingRect.size.height)
|
||||||
|
fromRect:NSMakeRect(0, 0, bgSize.width, bgSize.height)
|
||||||
|
operation:NSCompositeSourceOver
|
||||||
|
fraction:1.0];
|
||||||
|
}
|
||||||
|
[self unlockFocus];
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)viewWillDraw {
|
||||||
|
if ([[self window] isKeyWindow]) {
|
||||||
|
[self setBackgroundColor:[NSColor colorWithCalibratedRed:0.863389 green:0.892058 blue:0.9205 alpha:1.0]];
|
||||||
|
} else {
|
||||||
|
[self setBackgroundColor:[NSColor colorWithCalibratedRed:0.929412 green:0.929412 blue:0.929412 alpha:1.0]];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@end
|
||||||
|
|
||||||
|
@implementation MGMTextCell
|
||||||
|
- (id)init {
|
||||||
|
self = [super init];
|
||||||
|
if (self != nil) {
|
||||||
|
[self setWraps:NO];
|
||||||
|
}
|
||||||
|
return self;
|
||||||
|
}
|
||||||
|
- (void)drawInteriorWithFrame:(NSRect)cellFrame inView:(NSView *)controlView {
|
||||||
|
[controlView lockFocus];
|
||||||
|
|
||||||
|
NSMutableDictionary *attrs = [NSMutableDictionary dictionaryWithDictionary:[[self attributedStringValue] attributesAtIndex:0 effectiveRange:NULL]];
|
||||||
|
|
||||||
|
cellFrame.size.width += 10;
|
||||||
|
|
||||||
|
if ([self isHighlighted]) {
|
||||||
|
[attrs setObject:[NSFont boldSystemFontOfSize:[[self font] pointSize]] forKey:NSFontAttributeName];
|
||||||
|
[attrs setValue:[NSColor whiteColor] forKey:@"NSColor"];
|
||||||
|
} else {
|
||||||
|
[attrs setObject:[self font] forKey:NSFontAttributeName];
|
||||||
|
}
|
||||||
|
|
||||||
|
NSRect inset = [self drawingRectForBounds:cellFrame];
|
||||||
|
inset.origin.x += 4;
|
||||||
|
inset.size.width-=inset.origin.x;
|
||||||
|
NSString *displayString = [self truncateString:[self stringValue] forWidth:inset.size.width andAttributes:attrs];
|
||||||
|
|
||||||
|
[displayString drawAtPoint:inset.origin withAttributes:attrs];
|
||||||
|
|
||||||
|
[controlView unlockFocus];
|
||||||
|
}
|
||||||
|
|
||||||
|
- (NSString*)truncateString:(NSString *)string forWidth:(double) inWidth andAttributes:(NSDictionary*)inAttributes {
|
||||||
|
unichar ellipsesCharacter = 0x2026;
|
||||||
|
NSString* ellipsisString = [NSString stringWithCharacters:&ellipsesCharacter length:1];
|
||||||
|
|
||||||
|
NSString* truncatedString = [NSString stringWithString:string];
|
||||||
|
int truncatedStringLength = [truncatedString length];
|
||||||
|
|
||||||
|
if ((truncatedStringLength > 2) && ([truncatedString sizeWithAttributes:inAttributes].width > inWidth)) {
|
||||||
|
double targetWidth = inWidth - [ellipsisString sizeWithAttributes:inAttributes].width;
|
||||||
|
NSCharacterSet* whiteSpaceCharacters = [NSCharacterSet
|
||||||
|
whitespaceAndNewlineCharacterSet];
|
||||||
|
|
||||||
|
while([truncatedString sizeWithAttributes:inAttributes].width > targetWidth && truncatedStringLength) {
|
||||||
|
truncatedStringLength--;
|
||||||
|
while ([whiteSpaceCharacters characterIsMember:[truncatedString characterAtIndex:(truncatedStringLength -1)]]) {
|
||||||
|
truncatedStringLength--;
|
||||||
|
}
|
||||||
|
|
||||||
|
truncatedString = [truncatedString substringToIndex:truncatedStringLength];
|
||||||
|
}
|
||||||
|
|
||||||
|
truncatedString = [truncatedString stringByAppendingString:ellipsisString];
|
||||||
|
}
|
||||||
|
|
||||||
|
return truncatedString;
|
||||||
|
}
|
||||||
|
|
||||||
|
- (NSRect)drawingRectForBounds:(NSRect)theRect {
|
||||||
|
NSRect newRect = [super drawingRectForBounds:theRect];
|
||||||
|
|
||||||
|
NSSize textSize = [self cellSizeForBounds:theRect];
|
||||||
|
|
||||||
|
float heightDelta = newRect.size.height - textSize.height;
|
||||||
|
if (heightDelta > 0) {
|
||||||
|
newRect.size.height -= heightDelta;
|
||||||
|
newRect.origin.y += (heightDelta / 2);
|
||||||
|
}
|
||||||
|
newRect.size.width -= 1;
|
||||||
|
|
||||||
|
return newRect;
|
||||||
|
}
|
||||||
|
@end
|
15
Classes/YouView/Views/MGMSplitView.h
Normal file
15
Classes/YouView/Views/MGMSplitView.h
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
//
|
||||||
|
// splitView.h
|
||||||
|
// YouView
|
||||||
|
//
|
||||||
|
// Created by Mr. Gecko on 1/23/09.
|
||||||
|
// Copyright (c) 2010 Mr. Gecko's Media (James Coleman). All rights reserved. https://mrgeckosmedia.com/
|
||||||
|
//
|
||||||
|
|
||||||
|
#import <Cocoa/Cocoa.h>
|
||||||
|
|
||||||
|
@interface MGMSplitView : NSSplitView {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@end
|
19
Classes/YouView/Views/MGMSplitView.m
Normal file
19
Classes/YouView/Views/MGMSplitView.m
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
//
|
||||||
|
// splitView.m
|
||||||
|
// YouView
|
||||||
|
//
|
||||||
|
// Created by Mr. Gecko on 1/23/09.
|
||||||
|
// Copyright (c) 2010 Mr. Gecko's Media (James Coleman). All rights reserved. https://mrgeckosmedia.com/
|
||||||
|
//
|
||||||
|
|
||||||
|
#import "MGMSplitView.h"
|
||||||
|
|
||||||
|
@implementation MGMSplitView
|
||||||
|
- (CGFloat)dividerThickness {
|
||||||
|
return 1.0;
|
||||||
|
}
|
||||||
|
- (void)drawDividerInRect:(NSRect)aRect {
|
||||||
|
[[NSColor lightGrayColor] set];
|
||||||
|
NSRectFill (aRect);
|
||||||
|
}
|
||||||
|
@end
|
21
Classes/YouView/Views/MGMViewCell.h
Normal file
21
Classes/YouView/Views/MGMViewCell.h
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
//
|
||||||
|
// MGMViewCell.h
|
||||||
|
// YouView
|
||||||
|
//
|
||||||
|
// Created by Mr. Gecko on 4/15/09.
|
||||||
|
// Copyright (c) 2015 Mr. Gecko's Media (James Coleman). https://mrgeckosmedia.com/ All rights reserved.
|
||||||
|
//
|
||||||
|
|
||||||
|
#import <Cocoa/Cocoa.h>
|
||||||
|
|
||||||
|
@protocol MGMViewCellController <NSObject>
|
||||||
|
- (NSView *)view;
|
||||||
|
- (NSString *)accessibilityDescription;
|
||||||
|
- (NSString *)accessibilityRole;
|
||||||
|
@end
|
||||||
|
|
||||||
|
@interface MGMViewCell : NSCell {
|
||||||
|
NSObject<MGMViewCellController> *subview;
|
||||||
|
}
|
||||||
|
- (void)addSubview:(NSObject<MGMViewCellController> *)view;
|
||||||
|
@end
|
68
Classes/YouView/Views/MGMViewCell.m
Normal file
68
Classes/YouView/Views/MGMViewCell.m
Normal file
@ -0,0 +1,68 @@
|
|||||||
|
//
|
||||||
|
// MGMViewCell.m
|
||||||
|
// YouView
|
||||||
|
//
|
||||||
|
// Created by Mr. Gecko on 4/15/09.
|
||||||
|
// Copyright (c) 2015 Mr. Gecko's Media (James Coleman). https://mrgeckosmedia.com/ All rights reserved.
|
||||||
|
//
|
||||||
|
|
||||||
|
#import "MGMViewCell.h"
|
||||||
|
#import "MGMController.h"
|
||||||
|
#import <GeckoReporter/GeckoReporter.h>
|
||||||
|
|
||||||
|
@implementation MGMViewCell
|
||||||
|
- (void)addSubview:(NSObject<MGMViewCellController> *)view {
|
||||||
|
subview = view;
|
||||||
|
}
|
||||||
|
- (void)dealloc {
|
||||||
|
#if releaseDebug
|
||||||
|
MGMLog(@"%s Releasing", __PRETTY_FUNCTION__);
|
||||||
|
#endif
|
||||||
|
subview = nil;
|
||||||
|
[super dealloc];
|
||||||
|
}
|
||||||
|
- (NSView *)view {
|
||||||
|
return [subview view];
|
||||||
|
}
|
||||||
|
|
||||||
|
- (NSArray *)accessibilityAttributeNames {
|
||||||
|
NSMutableArray *names = [[[super accessibilityAttributeNames] mutableCopy] autorelease];
|
||||||
|
[names addObject:NSAccessibilityEnabledAttribute];
|
||||||
|
[names addObject:NSAccessibilityDescriptionAttribute];
|
||||||
|
[names addObject:NSAccessibilityRoleAttribute];
|
||||||
|
return names;
|
||||||
|
}
|
||||||
|
|
||||||
|
- (id)accessibilityAttributeValue:(NSString *)attribute {
|
||||||
|
if ([attribute isEqualToString:NSAccessibilityDescriptionAttribute]) {
|
||||||
|
if ([subview respondsToSelector:@selector(accessibilityDescription)])
|
||||||
|
return [subview accessibilityDescription];
|
||||||
|
} else if ([attribute isEqualToString:NSAccessibilityRoleAttribute]) {
|
||||||
|
if ([subview respondsToSelector:@selector(accessibilityRole)])
|
||||||
|
return [subview accessibilityRole];
|
||||||
|
} else if ([attribute isEqualToString:NSAccessibilityEnabledAttribute]) {
|
||||||
|
return [NSNumber numberWithBool:YES];
|
||||||
|
} else {
|
||||||
|
return [super accessibilityAttributeValue:attribute];
|
||||||
|
}
|
||||||
|
return @"";
|
||||||
|
}
|
||||||
|
|
||||||
|
- (BOOL)accessibilityIsAttributeSettable:(NSString *)attribute {
|
||||||
|
if ([attribute isEqualToString:NSAccessibilityDescriptionAttribute] || [attribute isEqualToString:NSAccessibilityRoleAttribute]) {
|
||||||
|
return NO;
|
||||||
|
} else {
|
||||||
|
return [super accessibilityIsAttributeSettable:attribute];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)drawWithFrame:(NSRect)cellFrame inView:(NSView *)controlView {
|
||||||
|
[super drawWithFrame:cellFrame inView:controlView];
|
||||||
|
|
||||||
|
[[self view] setFrame:cellFrame];
|
||||||
|
|
||||||
|
if ([[self view] superview] != controlView) {
|
||||||
|
[controlView addSubview:[self view]];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@end
|
25
Classes/YouView/XML/MGMXML.h
Normal file
25
Classes/YouView/XML/MGMXML.h
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
//
|
||||||
|
// MGMXML.h
|
||||||
|
// MGMXML
|
||||||
|
//
|
||||||
|
// Created by Mr. Gecko on 9/22/10.
|
||||||
|
// Copyright (c) 2011 Mr. Gecko's Media (James Coleman). https://mrgeckosmedia.com/
|
||||||
|
//
|
||||||
|
// Permission to use, copy, modify, and/or distribute this software for any purpose
|
||||||
|
// with or without fee is hereby granted, provided that the above copyright notice
|
||||||
|
// and this permission notice appear in all copies.
|
||||||
|
//
|
||||||
|
// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
|
||||||
|
// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
|
||||||
|
// FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT,
|
||||||
|
// OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE,
|
||||||
|
// DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS
|
||||||
|
// ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||||
|
//
|
||||||
|
|
||||||
|
#import "MGMXMLNode.h"
|
||||||
|
#import "MGMXMLElement.h"
|
||||||
|
#import "MGMXMLDocument.h"
|
||||||
|
#import "MGMXMLDTD.h"
|
||||||
|
#import "MGMXMLDTDNode.h"
|
||||||
|
#import "MGMXMLNodeOptions.h"
|
32
Classes/YouView/XML/MGMXMLAddons.h
Normal file
32
Classes/YouView/XML/MGMXMLAddons.h
Normal file
@ -0,0 +1,32 @@
|
|||||||
|
//
|
||||||
|
// MGMXMLAddons.h
|
||||||
|
// MGMXML
|
||||||
|
//
|
||||||
|
// Created by Mr. Gecko on 9/22/10.
|
||||||
|
// Copyright (c) 2011 Mr. Gecko's Media (James Coleman). https://mrgeckosmedia.com/
|
||||||
|
//
|
||||||
|
// Permission to use, copy, modify, and/or distribute this software for any purpose
|
||||||
|
// with or without fee is hereby granted, provided that the above copyright notice
|
||||||
|
// and this permission notice appear in all copies.
|
||||||
|
//
|
||||||
|
// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
|
||||||
|
// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
|
||||||
|
// FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT,
|
||||||
|
// OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE,
|
||||||
|
// DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS
|
||||||
|
// ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||||
|
//
|
||||||
|
|
||||||
|
#import <Foundation/Foundation.h>
|
||||||
|
#import <libxml/parser.h>
|
||||||
|
#import <libxml/tree.h>
|
||||||
|
|
||||||
|
@interface NSValue (MGMXMLAddons)
|
||||||
|
+ (id)valueWithXMLError:(xmlErrorPtr)error;
|
||||||
|
- (xmlErrorPtr)xmlErrorValue;
|
||||||
|
@end
|
||||||
|
|
||||||
|
@interface NSString (MGMXMLAddons)
|
||||||
|
+ (NSString *)stringWithXMLString:(const xmlChar *)xmlString;
|
||||||
|
- (const xmlChar *)xmlString;
|
||||||
|
@end
|
40
Classes/YouView/XML/MGMXMLAddons.m
Normal file
40
Classes/YouView/XML/MGMXMLAddons.m
Normal file
@ -0,0 +1,40 @@
|
|||||||
|
//
|
||||||
|
// MGMXMLAddons.m
|
||||||
|
// MGMXML
|
||||||
|
//
|
||||||
|
// Created by Mr. Gecko on 9/22/10.
|
||||||
|
// Copyright (c) 2011 Mr. Gecko's Media (James Coleman). https://mrgeckosmedia.com/
|
||||||
|
//
|
||||||
|
// Permission to use, copy, modify, and/or distribute this software for any purpose
|
||||||
|
// with or without fee is hereby granted, provided that the above copyright notice
|
||||||
|
// and this permission notice appear in all copies.
|
||||||
|
//
|
||||||
|
// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
|
||||||
|
// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
|
||||||
|
// FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT,
|
||||||
|
// OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE,
|
||||||
|
// DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS
|
||||||
|
// ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||||
|
//
|
||||||
|
|
||||||
|
#import "MGMXMLAddons.h"
|
||||||
|
|
||||||
|
@implementation NSValue (MGMXMLAddons)
|
||||||
|
+ (id)valueWithXMLError:(xmlErrorPtr)error {
|
||||||
|
return [NSValue valueWithBytes:error objCType:@encode(xmlErrorPtr)];
|
||||||
|
}
|
||||||
|
- (xmlErrorPtr)xmlErrorValue {
|
||||||
|
xmlErrorPtr error;
|
||||||
|
[self getValue:&error];
|
||||||
|
return error;
|
||||||
|
}
|
||||||
|
@end
|
||||||
|
|
||||||
|
@implementation NSString (MGMXMLAddons)
|
||||||
|
+ (NSString *)stringWithXMLString:(const xmlChar *)xmlString {
|
||||||
|
return [[[NSString alloc] initWithBytes:(const char *)xmlString length:strlen((const char *)xmlString) encoding:NSUTF8StringEncoding] autorelease];
|
||||||
|
}
|
||||||
|
- (const xmlChar *)xmlString {
|
||||||
|
return (const xmlChar *)[self UTF8String];
|
||||||
|
}
|
||||||
|
@end
|
45
Classes/YouView/XML/MGMXMLDTD.h
Normal file
45
Classes/YouView/XML/MGMXMLDTD.h
Normal file
@ -0,0 +1,45 @@
|
|||||||
|
//
|
||||||
|
// MGMXMLDTD.h
|
||||||
|
// MGMXML
|
||||||
|
//
|
||||||
|
// Created by Mr. Gecko on 9/22/10.
|
||||||
|
// Copyright (c) 2011 Mr. Gecko's Media (James Coleman). https://mrgeckosmedia.com/
|
||||||
|
//
|
||||||
|
// Permission to use, copy, modify, and/or distribute this software for any purpose
|
||||||
|
// with or without fee is hereby granted, provided that the above copyright notice
|
||||||
|
// and this permission notice appear in all copies.
|
||||||
|
//
|
||||||
|
// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
|
||||||
|
// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
|
||||||
|
// FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT,
|
||||||
|
// OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE,
|
||||||
|
// DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS
|
||||||
|
// ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||||
|
//
|
||||||
|
|
||||||
|
#import <Foundation/Foundation.h>
|
||||||
|
#import "MGMXMLNode.h"
|
||||||
|
|
||||||
|
@interface MGMXMLDTD : MGMXMLNode {
|
||||||
|
|
||||||
|
}
|
||||||
|
//- (id)initWithContentsOfURL:(NSURL *)url options:(NSUInteger)mask error:(NSError **)error;
|
||||||
|
//- (id)initWithData:(NSData *)data options:(NSUInteger)mask error:(NSError **)error; //primitive
|
||||||
|
//- (void)setPublicID:(NSString *)publicID; //primitive
|
||||||
|
//- (NSString *)publicID; //primitive
|
||||||
|
//- (void)setSystemID:(NSString *)systemID; //primitive
|
||||||
|
//- (NSString *)systemID; //primitive
|
||||||
|
|
||||||
|
//- (void)insertChild:(MGMXMLNode *)child atIndex:(NSUInteger)index; //primitive
|
||||||
|
//- (void)insertChildren:(NSArray *)children atIndex:(NSUInteger)index;
|
||||||
|
//- (void)removeChildAtIndex:(NSUInteger)index; //primitive
|
||||||
|
//- (void)setChildren:(NSArray *)children; //primitive
|
||||||
|
//- (void)addChild:(MGMXMLNode *)child;
|
||||||
|
//- (void)replaceChildAtIndex:(NSUInteger)index withNode:(MGMXMLNode *)node;
|
||||||
|
|
||||||
|
//- (MGMXMLDTDNode *)entityDeclarationForName:(NSString *)name; //primitive
|
||||||
|
//- (MGMXMLDTDNode *)notationDeclarationForName:(NSString *)name; //primitive
|
||||||
|
//- (MGMXMLDTDNode *)elementDeclarationForName:(NSString *)name; //primitive
|
||||||
|
//- (MGMXMLDTDNode *)attributeDeclarationForName:(NSString *)name elementName:(NSString *)elementName; //primitive
|
||||||
|
//+ (MGMXMLDTDNode *)predefinedEntityDeclarationForName:(NSString *)name;
|
||||||
|
@end
|
24
Classes/YouView/XML/MGMXMLDTD.m
Normal file
24
Classes/YouView/XML/MGMXMLDTD.m
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
//
|
||||||
|
// MGMXMLDTD.m
|
||||||
|
// MGMXML
|
||||||
|
//
|
||||||
|
// Created by Mr. Gecko on 9/22/10.
|
||||||
|
// Copyright (c) 2011 Mr. Gecko's Media (James Coleman). https://mrgeckosmedia.com/
|
||||||
|
//
|
||||||
|
// Permission to use, copy, modify, and/or distribute this software for any purpose
|
||||||
|
// with or without fee is hereby granted, provided that the above copyright notice
|
||||||
|
// and this permission notice appear in all copies.
|
||||||
|
//
|
||||||
|
// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
|
||||||
|
// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
|
||||||
|
// FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT,
|
||||||
|
// OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE,
|
||||||
|
// DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS
|
||||||
|
// ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||||
|
//
|
||||||
|
|
||||||
|
#import "MGMXMLDTD.h"
|
||||||
|
|
||||||
|
@implementation MGMXMLDTD
|
||||||
|
|
||||||
|
@end
|
36
Classes/YouView/XML/MGMXMLDTDNode.h
Normal file
36
Classes/YouView/XML/MGMXMLDTDNode.h
Normal file
@ -0,0 +1,36 @@
|
|||||||
|
//
|
||||||
|
// MGMXMLDTDNode.h
|
||||||
|
// MGMXML
|
||||||
|
//
|
||||||
|
// Created by Mr. Gecko on 9/22/10.
|
||||||
|
// Copyright (c) 2011 Mr. Gecko's Media (James Coleman). https://mrgeckosmedia.com/
|
||||||
|
//
|
||||||
|
// Permission to use, copy, modify, and/or distribute this software for any purpose
|
||||||
|
// with or without fee is hereby granted, provided that the above copyright notice
|
||||||
|
// and this permission notice appear in all copies.
|
||||||
|
//
|
||||||
|
// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
|
||||||
|
// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
|
||||||
|
// FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT,
|
||||||
|
// OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE,
|
||||||
|
// DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS
|
||||||
|
// ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||||
|
//
|
||||||
|
|
||||||
|
#import <Foundation/Foundation.h>
|
||||||
|
#import "MGMXMLNode.h"
|
||||||
|
|
||||||
|
@interface MGMXMLDTDNode : MGMXMLNode {
|
||||||
|
|
||||||
|
}
|
||||||
|
//- (id)initWithXMLString:(NSString *)string; //primitive
|
||||||
|
//- (void)setDTDKind:(MGMXMLDTDNodeKind)kind; //primitive
|
||||||
|
//- (MGMXMLDTDNodeKind)DTDKind; //primitive
|
||||||
|
//- (BOOL)isExternal; //primitive
|
||||||
|
//- (void)setPublicID:(NSString *)publicID; //primitive
|
||||||
|
//- (NSString *)publicID; //primitive
|
||||||
|
//- (void)setSystemID:(NSString *)systemID; //primitive
|
||||||
|
//- (NSString *)systemID; //primitive
|
||||||
|
//- (void)setNotationName:(NSString *)notationName; //primitive
|
||||||
|
//- (NSString *)notationName; //primitive
|
||||||
|
@end
|
24
Classes/YouView/XML/MGMXMLDTDNode.m
Normal file
24
Classes/YouView/XML/MGMXMLDTDNode.m
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
//
|
||||||
|
// MGMXMLDTDNode.m
|
||||||
|
// MGMXML
|
||||||
|
//
|
||||||
|
// Created by Mr. Gecko on 9/22/10.
|
||||||
|
// Copyright (c) 2011 Mr. Gecko's Media (James Coleman). https://mrgeckosmedia.com/
|
||||||
|
//
|
||||||
|
// Permission to use, copy, modify, and/or distribute this software for any purpose
|
||||||
|
// with or without fee is hereby granted, provided that the above copyright notice
|
||||||
|
// and this permission notice appear in all copies.
|
||||||
|
//
|
||||||
|
// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
|
||||||
|
// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
|
||||||
|
// FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT,
|
||||||
|
// OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE,
|
||||||
|
// DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS
|
||||||
|
// ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||||
|
//
|
||||||
|
|
||||||
|
#import "MGMXMLDTDNode.h"
|
||||||
|
|
||||||
|
@implementation MGMXMLDTDNode
|
||||||
|
|
||||||
|
@end
|
64
Classes/YouView/XML/MGMXMLDocument.h
Normal file
64
Classes/YouView/XML/MGMXMLDocument.h
Normal file
@ -0,0 +1,64 @@
|
|||||||
|
//
|
||||||
|
// MGMXMLDocument.h
|
||||||
|
// MGMXML
|
||||||
|
//
|
||||||
|
// Created by Mr. Gecko on 9/22/10.
|
||||||
|
// Copyright (c) 2011 Mr. Gecko's Media (James Coleman). https://mrgeckosmedia.com/
|
||||||
|
//
|
||||||
|
// Permission to use, copy, modify, and/or distribute this software for any purpose
|
||||||
|
// with or without fee is hereby granted, provided that the above copyright notice
|
||||||
|
// and this permission notice appear in all copies.
|
||||||
|
//
|
||||||
|
// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
|
||||||
|
// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
|
||||||
|
// FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT,
|
||||||
|
// OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE,
|
||||||
|
// DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS
|
||||||
|
// ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||||
|
//
|
||||||
|
|
||||||
|
#import <Foundation/Foundation.h>
|
||||||
|
#import "MGMXMLNode.h"
|
||||||
|
|
||||||
|
#define MGMXMLDocPtr (xmlDocPtr)commonXML
|
||||||
|
|
||||||
|
@interface MGMXMLDocument : MGMXMLNode {
|
||||||
|
|
||||||
|
}
|
||||||
|
- (id)initWithXMLString:(NSString *)string options:(NSUInteger)mask error:(NSError **)error;
|
||||||
|
- (id)initWithContentsOfURL:(NSURL *)url options:(NSUInteger)mask error:(NSError **)error;
|
||||||
|
- (id)initWithData:(NSData *)data options:(NSUInteger)mask error:(NSError **)error; //primitive
|
||||||
|
- (id)initWithRootElement:(MGMXMLElement *)element;
|
||||||
|
|
||||||
|
//+ (Class)replacementClassForClass:(Class)cls;
|
||||||
|
//- (void)setCharacterEncoding:(NSString *)encoding; //primitive
|
||||||
|
//- (NSString *)characterEncoding; //primitive
|
||||||
|
//- (void)setVersion:(NSString *)version; //primitive
|
||||||
|
//- (NSString *)version; //primitive
|
||||||
|
//- (void)setStandalone:(BOOL)standalone; //primitive
|
||||||
|
//- (BOOL)isStandalone; //primitive
|
||||||
|
//- (void)setDocumentContentKind:(MGMXMLDocumentContentKind)kind; //primitive
|
||||||
|
//- (MGMXMLDocumentContentKind)documentContentKind; //primitive
|
||||||
|
//- (void)setMIMEType:(NSString *)MIMEType; //primitive
|
||||||
|
//- (NSString *)MIMEType; //primitive
|
||||||
|
//- (void)setDTD:(MGMXMLDTD *)documentTypeDeclaration; //primitive
|
||||||
|
//- (MGMXMLDTD *)DTD; //primitive
|
||||||
|
- (void)setRootElement:(MGMXMLNode *)root;
|
||||||
|
- (MGMXMLElement *)rootElement; //primitive
|
||||||
|
|
||||||
|
//- (void)insertChild:(MGMXMLNode *)child atIndex:(NSUInteger)index; //primitive
|
||||||
|
//- (void)insertChildren:(NSArray *)children atIndex:(NSUInteger)index;
|
||||||
|
//- (void)removeChildAtIndex:(NSUInteger)index; //primitive
|
||||||
|
//- (void)setChildren:(NSArray *)children; //primitive
|
||||||
|
//- (void)addChild:(MGMXMLNode *)child;
|
||||||
|
//- (void)replaceChildAtIndex:(NSUInteger)index withNode:(MGMXMLNode *)node;
|
||||||
|
|
||||||
|
- (NSData *)XMLData;
|
||||||
|
- (NSData *)XMLDataWithOptions:(NSUInteger)options;
|
||||||
|
|
||||||
|
//- (id)objectByApplyingXSLT:(NSData *)xslt arguments:(NSDictionary *)arguments error:(NSError **)error;
|
||||||
|
//- (id)objectByApplyingXSLTString:(NSString *)xslt arguments:(NSDictionary *)arguments error:(NSError **)error;
|
||||||
|
//- (id)objectByApplyingXSLTAtURL:(NSURL *)xsltURL arguments:(NSDictionary *)argument error:(NSError **)error;
|
||||||
|
|
||||||
|
//- (BOOL)validateAndReturnError:(NSError **)error;
|
||||||
|
@end
|
94
Classes/YouView/XML/MGMXMLDocument.m
Normal file
94
Classes/YouView/XML/MGMXMLDocument.m
Normal file
@ -0,0 +1,94 @@
|
|||||||
|
//
|
||||||
|
// MGMXMLDocument.m
|
||||||
|
// MGMXML
|
||||||
|
//
|
||||||
|
// Created by Mr. Gecko on 9/22/10.
|
||||||
|
// Copyright (c) 2011 Mr. Gecko's Media (James Coleman). https://mrgeckosmedia.com/
|
||||||
|
//
|
||||||
|
// Permission to use, copy, modify, and/or distribute this software for any purpose
|
||||||
|
// with or without fee is hereby granted, provided that the above copyright notice
|
||||||
|
// and this permission notice appear in all copies.
|
||||||
|
//
|
||||||
|
// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
|
||||||
|
// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
|
||||||
|
// FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT,
|
||||||
|
// OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE,
|
||||||
|
// DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS
|
||||||
|
// ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||||
|
//
|
||||||
|
|
||||||
|
#import "MGMXMLDocument.h"
|
||||||
|
#import "MGMXMLElement.h"
|
||||||
|
#import <libxml/HTMLtree.h>
|
||||||
|
#import <libxml/HTMLparser.h>
|
||||||
|
|
||||||
|
@interface MGMXMLNode (MGMPrivate)
|
||||||
|
- (void)setDocument:(MGMXMLDocument *)theDocument;
|
||||||
|
@end
|
||||||
|
|
||||||
|
@implementation MGMXMLDocument
|
||||||
|
- (id)initWithXMLString:(NSString *)string options:(NSUInteger)mask error:(NSError **)error {
|
||||||
|
NSData *data = [string dataUsingEncoding:NSUTF8StringEncoding];
|
||||||
|
return [self initWithData:data options:mask error:error];
|
||||||
|
}
|
||||||
|
- (id)initWithContentsOfURL:(NSURL *)url options:(NSUInteger)mask error:(NSError **)error {
|
||||||
|
NSData *data = [NSData dataWithContentsOfURL:url];
|
||||||
|
return [self initWithData:data options:mask error:error];
|
||||||
|
}
|
||||||
|
- (id)initWithData:(NSData *)data options:(NSUInteger)mask error:(NSError **)error {
|
||||||
|
if ((self = [super init])) {
|
||||||
|
if (data==nil || [data length]<=0) {
|
||||||
|
if (error!=nil) *error = [NSError errorWithDomain:MGMXMLErrorDomain code:0 userInfo:[NSDictionary dictionaryWithObject:@"Data has no length." forKey:NSLocalizedDescriptionKey]];
|
||||||
|
[self release];
|
||||||
|
self = nil;
|
||||||
|
} else {
|
||||||
|
xmlKeepBlanksDefault(0);
|
||||||
|
|
||||||
|
xmlDocPtr document = NULL;
|
||||||
|
if (mask & MGMXMLDocumentTidyHTML) {
|
||||||
|
document = htmlReadMemory([data bytes], [data length], NULL, NULL, XML_PARSE_RECOVER | HTML_PARSE_NONET);
|
||||||
|
} else {
|
||||||
|
document = xmlParseMemory([data bytes], [data length]);
|
||||||
|
}
|
||||||
|
if (document==NULL) {
|
||||||
|
if (error!=nil) *error = [NSError errorWithDomain:MGMXMLErrorDomain code:1 userInfo:[NSDictionary dictionaryWithObject:@"Unable to parse document." forKey:NSLocalizedDescriptionKey]];
|
||||||
|
[self release];
|
||||||
|
self = nil;
|
||||||
|
} else {
|
||||||
|
[self setTypeXMLPtr:(xmlTypPtr)document];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return self;
|
||||||
|
}
|
||||||
|
- (id)initWithRootElement:(MGMXMLElement *)element {
|
||||||
|
if ((self = [super init])) {
|
||||||
|
xmlDocPtr document = xmlNewDoc(NULL);
|
||||||
|
if (element!=nil) {
|
||||||
|
xmlDocSetRootElement(document, (xmlNodePtr)[element commonXML]);
|
||||||
|
[element setDocument:self];
|
||||||
|
}
|
||||||
|
[self setTypeXMLPtr:(xmlTypPtr)document];
|
||||||
|
}
|
||||||
|
return self;
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)setRootElement:(MGMXMLNode *)root {
|
||||||
|
if ([root kind]==MGMXMLNamespaceKind)
|
||||||
|
return;
|
||||||
|
xmlDocSetRootElement(MGMXMLDocPtr, (xmlNodePtr)[root commonXML]);
|
||||||
|
}
|
||||||
|
- (MGMXMLElement *)rootElement {
|
||||||
|
xmlNodePtr element = xmlDocGetRootElement(MGMXMLDocPtr);
|
||||||
|
if (element!=NULL)
|
||||||
|
return [MGMXMLElement nodeWithTypeXMLPtr:(xmlTypPtr)element];
|
||||||
|
return nil;
|
||||||
|
}
|
||||||
|
|
||||||
|
- (NSData *)XMLData {
|
||||||
|
return [[self XMLStringWithOptions:0] dataUsingEncoding:NSUTF8StringEncoding];
|
||||||
|
}
|
||||||
|
- (NSData *)XMLDataWithOptions:(NSUInteger)options {
|
||||||
|
return [[self XMLStringWithOptions:options] dataUsingEncoding:NSUTF8StringEncoding];
|
||||||
|
}
|
||||||
|
@end
|
57
Classes/YouView/XML/MGMXMLElement.h
Normal file
57
Classes/YouView/XML/MGMXMLElement.h
Normal file
@ -0,0 +1,57 @@
|
|||||||
|
//
|
||||||
|
// MGMXMLElement.h
|
||||||
|
// MGMXML
|
||||||
|
//
|
||||||
|
// Created by Mr. Gecko on 9/22/10.
|
||||||
|
// Copyright (c) 2011 Mr. Gecko's Media (James Coleman). https://mrgeckosmedia.com/
|
||||||
|
//
|
||||||
|
// Permission to use, copy, modify, and/or distribute this software for any purpose
|
||||||
|
// with or without fee is hereby granted, provided that the above copyright notice
|
||||||
|
// and this permission notice appear in all copies.
|
||||||
|
//
|
||||||
|
// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
|
||||||
|
// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
|
||||||
|
// FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT,
|
||||||
|
// OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE,
|
||||||
|
// DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS
|
||||||
|
// ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||||
|
//
|
||||||
|
|
||||||
|
#import <Foundation/Foundation.h>
|
||||||
|
#import "MGMXMLNode.h"
|
||||||
|
|
||||||
|
@interface MGMXMLElement : MGMXMLNode {
|
||||||
|
|
||||||
|
}
|
||||||
|
- (id)initWithName:(NSString *)name;
|
||||||
|
//- (id)initWithName:(NSString *)name URI:(NSString *)URI; //primitive
|
||||||
|
- (id)initWithName:(NSString *)name stringValue:(NSString *)string;
|
||||||
|
- (id)initWithXMLString:(NSString *)string error:(NSError **)error;
|
||||||
|
|
||||||
|
- (NSArray *)elementsForName:(NSString *)name;
|
||||||
|
- (NSArray *)elementsForLocalName:(NSString *)localName URI:(NSString *)URI;
|
||||||
|
|
||||||
|
- (void)addAttribute:(MGMXMLNode *)attribute; //primitive
|
||||||
|
- (void)removeAttributeForName:(NSString *)name; //primitive
|
||||||
|
//- (void)setAttributes:(NSArray *)attributes; //primitive
|
||||||
|
//- (void)setAttributesAsDictionary:(NSDictionary *)attributes;
|
||||||
|
- (NSArray *)attributes; //primitive
|
||||||
|
- (MGMXMLNode *)attributeForName:(NSString *)name;
|
||||||
|
//- (MGMXMLNode *)attributeForLocalName:(NSString *)localName URI:(NSString *)URI; //primitive
|
||||||
|
|
||||||
|
//- (void)addNamespace:(MGMXMLNode *)aNamespace; //primitive
|
||||||
|
//- (void)removeNamespaceForPrefix:(NSString *)name; //primitive
|
||||||
|
//- (void)setNamespaces:(NSArray *)namespaces; //primitive
|
||||||
|
//- (NSArray *)namespaces; //primitive
|
||||||
|
//- (MGMXMLNode *)namespaceForPrefix:(NSString *)name;
|
||||||
|
//- (MGMXMLNode *)resolveNamespaceForName:(NSString *)name;
|
||||||
|
- (NSString *)resolvePrefixForNamespaceURI:(NSString *)namespaceURI;
|
||||||
|
|
||||||
|
//- (void)insertChild:(MGMXMLNode *)child atIndex:(NSUInteger)index; //primitive
|
||||||
|
//- (void)insertChildren:(NSArray *)children atIndex:(NSUInteger)index;
|
||||||
|
//- (void)removeChildAtIndex:(NSUInteger)index; //primitive
|
||||||
|
//- (void)setChildren:(NSArray *)children; //primitive
|
||||||
|
//- (void)addChild:(MGMXMLNode *)child;
|
||||||
|
//- (void)replaceChildAtIndex:(NSUInteger)index withNode:(MGMXMLNode *)node;
|
||||||
|
//- (void)normalizeAdjacentTextNodesPreservingCDATA:(BOOL)preserve;
|
||||||
|
@end
|
154
Classes/YouView/XML/MGMXMLElement.m
Normal file
154
Classes/YouView/XML/MGMXMLElement.m
Normal file
@ -0,0 +1,154 @@
|
|||||||
|
//
|
||||||
|
// MGMXMLElement.m
|
||||||
|
// MGMXML
|
||||||
|
//
|
||||||
|
// Created by Mr. Gecko on 9/22/10.
|
||||||
|
// Copyright (c) 2011 Mr. Gecko's Media (James Coleman). https://mrgeckosmedia.com/
|
||||||
|
//
|
||||||
|
// Permission to use, copy, modify, and/or distribute this software for any purpose
|
||||||
|
// with or without fee is hereby granted, provided that the above copyright notice
|
||||||
|
// and this permission notice appear in all copies.
|
||||||
|
//
|
||||||
|
// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
|
||||||
|
// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
|
||||||
|
// FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT,
|
||||||
|
// OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE,
|
||||||
|
// DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS
|
||||||
|
// ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||||
|
//
|
||||||
|
|
||||||
|
#import "MGMXMLElement.h"
|
||||||
|
#import "MGMXMLDocument.h"
|
||||||
|
#import "MGMXMLAddons.h"
|
||||||
|
|
||||||
|
@implementation MGMXMLElement
|
||||||
|
- (id)initWithName:(NSString *)name {
|
||||||
|
return [self initWithName:name stringValue:nil];
|
||||||
|
}
|
||||||
|
- (id)initWithName:(NSString *)name stringValue:(NSString *)string {
|
||||||
|
if ((self = [super init])) {
|
||||||
|
xmlNodePtr node = xmlNewNode(NULL, [name xmlString]);
|
||||||
|
if (string!=nil)
|
||||||
|
xmlNodeSetContent(node, [string xmlString]);
|
||||||
|
[self setTypeXMLPtr:(xmlTypPtr)node];
|
||||||
|
}
|
||||||
|
return self;
|
||||||
|
}
|
||||||
|
- (id)initWithXMLString:(NSString *)string error:(NSError **)error {
|
||||||
|
[super release];
|
||||||
|
MGMXMLDocument *document = [[MGMXMLDocument alloc] initWithXMLString:string options:0 error:error];
|
||||||
|
MGMXMLElement *element = nil;
|
||||||
|
if (document!=nil) {
|
||||||
|
element = [[document rootElement] retain];
|
||||||
|
if (element!=nil)
|
||||||
|
[element detach];
|
||||||
|
[document release];
|
||||||
|
}
|
||||||
|
return element;
|
||||||
|
}
|
||||||
|
|
||||||
|
- (NSArray *)elementsForName:(NSString *)name URI:(NSString *)URI resolvingNamespacePrefix:(BOOL)resolveNamespacePrefix {
|
||||||
|
NSMutableArray *elements = [NSMutableArray array];
|
||||||
|
|
||||||
|
if (resolveNamespacePrefix && URI!=nil) {
|
||||||
|
NSString *prefix = [self resolvePrefixForNamespaceURI:URI];
|
||||||
|
if (prefix!=nil)
|
||||||
|
name = [NSString stringWithFormat:@"%@:%@", prefix, name];
|
||||||
|
}
|
||||||
|
NSString *localName = [[self class] localNameForName:name];
|
||||||
|
BOOL hasPrefix = ([[self class] prefixForName:localName]!=nil);
|
||||||
|
|
||||||
|
xmlNodePtr child = MGMXMLNodePtr->children;
|
||||||
|
while (child!=NULL) {
|
||||||
|
if (child->type==MGMXMLElementKind) {
|
||||||
|
BOOL shouldAddElement = NO;
|
||||||
|
if (URI==nil) {
|
||||||
|
shouldAddElement = xmlStrEqual(child->name, [name xmlString]);
|
||||||
|
} else {
|
||||||
|
if (hasPrefix && xmlStrEqual(child->name, [name xmlString]))
|
||||||
|
shouldAddElement = YES;
|
||||||
|
else if (child->ns!=NULL)
|
||||||
|
shouldAddElement = xmlStrEqual(child->name, [(hasPrefix ? localName : name) xmlString]) && xmlStrEqual(child->ns->href, [URI xmlString]);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (shouldAddElement)
|
||||||
|
[elements addObject:[MGMXMLElement nodeWithTypeXMLPtr:(xmlTypPtr)child]];
|
||||||
|
}
|
||||||
|
|
||||||
|
child = child->next;
|
||||||
|
}
|
||||||
|
return elements;
|
||||||
|
}
|
||||||
|
- (NSArray *)elementsForName:(NSString *)name {
|
||||||
|
NSString *localName = [[self class] localNameForName:name];
|
||||||
|
NSString *prefix = [[self class] prefixForName:name];
|
||||||
|
NSString *uri = nil;
|
||||||
|
if (prefix!=nil) {
|
||||||
|
if (MGMXMLNodePtr->doc==NULL) {
|
||||||
|
NSLog(@"You may not search for elements with a namespace if there is no document.");
|
||||||
|
return nil;
|
||||||
|
}
|
||||||
|
xmlNsPtr namespace = xmlSearchNs(MGMXMLNodePtr->doc, MGMXMLNodePtr, [prefix xmlString]);
|
||||||
|
if (namespace!=NULL)
|
||||||
|
uri = [NSString stringWithXMLString:namespace->href];
|
||||||
|
}
|
||||||
|
|
||||||
|
return [self elementsForName:localName URI:uri resolvingNamespacePrefix:NO];
|
||||||
|
}
|
||||||
|
- (NSArray *)elementsForLocalName:(NSString *)localName URI:(NSString *)URI {
|
||||||
|
return [self elementsForName:localName URI:URI resolvingNamespacePrefix:YES];
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)addAttribute:(MGMXMLNode *)attribute {
|
||||||
|
if ([attribute kind]==MGMXMLAttributeKind && [attribute commonXML]->parent!=NULL)
|
||||||
|
[self removeAttributeForName:[attribute name]];
|
||||||
|
xmlAddChild(MGMXMLNodePtr, (xmlNodePtr)[attribute commonXML]);
|
||||||
|
}
|
||||||
|
- (void)removeAttributeForName:(NSString *)name {
|
||||||
|
xmlAttrPtr attribute = MGMXMLNodePtr->properties;
|
||||||
|
while (attribute!=NULL) {
|
||||||
|
if (xmlStrEqual(attribute->name, [name xmlString])) {
|
||||||
|
[[self class] detatchAttribute:attribute fromNode:MGMXMLNodePtr];
|
||||||
|
|
||||||
|
if(attribute->_private == NULL)
|
||||||
|
xmlFreeProp(attribute);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
attribute = attribute->next;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
- (NSArray *)attributes {
|
||||||
|
NSMutableArray *attributes = [NSMutableArray array];
|
||||||
|
xmlAttrPtr attribute = MGMXMLNodePtr->properties;
|
||||||
|
while (attribute!=NULL) {
|
||||||
|
[attributes addObject:[MGMXMLNode nodeWithTypeXMLPtr:(xmlTypPtr)attribute]];
|
||||||
|
attribute = attribute->next;
|
||||||
|
}
|
||||||
|
return attributes;
|
||||||
|
}
|
||||||
|
- (MGMXMLNode *)attributeForName:(NSString *)name {
|
||||||
|
xmlAttrPtr attribute = MGMXMLNodePtr->properties;
|
||||||
|
while (attribute!=NULL) {
|
||||||
|
if (xmlStrEqual(attribute->name, [name xmlString]))
|
||||||
|
return [MGMXMLNode nodeWithTypeXMLPtr:(xmlTypPtr)attribute];
|
||||||
|
attribute = attribute->next;
|
||||||
|
}
|
||||||
|
return nil;
|
||||||
|
}
|
||||||
|
|
||||||
|
- (NSString *)resolvePrefixForNamespaceURI:(NSString *)namespaceURI {
|
||||||
|
xmlNodePtr child = MGMXMLNodePtr;
|
||||||
|
while (child!=NULL) {
|
||||||
|
xmlNsPtr namespace = child->nsDef;
|
||||||
|
while (namespace!=NULL) {
|
||||||
|
if (xmlStrEqual(namespace->href, [namespaceURI xmlString])) {
|
||||||
|
if (namespace->prefix!=NULL)
|
||||||
|
return [NSString stringWithXMLString:namespace->prefix];
|
||||||
|
}
|
||||||
|
namespace = namespace->next;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil;
|
||||||
|
}
|
||||||
|
@end
|
155
Classes/YouView/XML/MGMXMLNode.h
Normal file
155
Classes/YouView/XML/MGMXMLNode.h
Normal file
@ -0,0 +1,155 @@
|
|||||||
|
//
|
||||||
|
// MGMXMLNode.h
|
||||||
|
// MGMXML
|
||||||
|
//
|
||||||
|
// Created by Mr. Gecko on 9/22/10.
|
||||||
|
// Copyright (c) 2011 Mr. Gecko's Media (James Coleman). https://mrgeckosmedia.com/
|
||||||
|
//
|
||||||
|
// Permission to use, copy, modify, and/or distribute this software for any purpose
|
||||||
|
// with or without fee is hereby granted, provided that the above copyright notice
|
||||||
|
// and this permission notice appear in all copies.
|
||||||
|
//
|
||||||
|
// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
|
||||||
|
// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
|
||||||
|
// FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT,
|
||||||
|
// OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE,
|
||||||
|
// DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS
|
||||||
|
// ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||||
|
//
|
||||||
|
|
||||||
|
#import <Foundation/Foundation.h>
|
||||||
|
#import "MGMXMLNodeOptions.h"
|
||||||
|
#import <libxml/parser.h>
|
||||||
|
#import <libxml/tree.h>
|
||||||
|
#import <libxml/xpath.h>
|
||||||
|
#import <libxml/xpathInternals.h>
|
||||||
|
#import <libxml/HTMLtree.h>
|
||||||
|
#import <libxml/HTMLparser.h>
|
||||||
|
|
||||||
|
#define MGMXMLNodePtr ((xmlNodePtr)commonXML)
|
||||||
|
#define MGMXMLAttrPtr ((xmlAttrPtr)commonXML)
|
||||||
|
#define MGMXMLDTDPtr ((xmlDtdPtr)commonXML)
|
||||||
|
|
||||||
|
@class MGMXMLDocument, MGMXMLElement;
|
||||||
|
|
||||||
|
extern NSString * const MGMXMLErrorDomain;
|
||||||
|
|
||||||
|
typedef enum {
|
||||||
|
MGMXMLInvalidKind = 0,
|
||||||
|
MGMXMLDocumentKind = XML_DOCUMENT_NODE,
|
||||||
|
MGMXMLElementKind = XML_ELEMENT_NODE,
|
||||||
|
MGMXMLAttributeKind = XML_ATTRIBUTE_NODE,
|
||||||
|
MGMXMLNamespaceKind = XML_NAMESPACE_DECL,
|
||||||
|
MGMXMLProcessingInstructionKind = XML_PI_NODE,
|
||||||
|
MGMXMLCommentKind = XML_COMMENT_NODE,
|
||||||
|
MGMXMLTextKind = XML_TEXT_NODE,
|
||||||
|
MGMXMLDTDKind = XML_DTD_NODE,
|
||||||
|
MGMXMLEntityDeclarationKind = XML_ENTITY_DECL,
|
||||||
|
MGMXMLAttributeDeclarationKind = XML_ATTRIBUTE_DECL,
|
||||||
|
MGMXMLElementDeclarationKind = XML_ELEMENT_DECL,
|
||||||
|
MGMXMLNotationDeclarationKind = XML_NOTATION_NODE
|
||||||
|
} MGMXMLNodeKind;
|
||||||
|
|
||||||
|
//The common XML structure that has type.
|
||||||
|
typedef struct _xmlTyp xmlTyp;
|
||||||
|
typedef xmlTyp *xmlTypPtr;
|
||||||
|
struct _xmlTyp {
|
||||||
|
void *trash;
|
||||||
|
xmlElementType type;
|
||||||
|
};
|
||||||
|
|
||||||
|
//The common XML structure.
|
||||||
|
typedef struct _xmlCom xmlCom;
|
||||||
|
typedef xmlCom *xmlComPtr;
|
||||||
|
struct _xmlCom {
|
||||||
|
void *_private;
|
||||||
|
xmlElementType type;
|
||||||
|
char *name;
|
||||||
|
struct _xmlNode *children;
|
||||||
|
struct _xmlNode *last;
|
||||||
|
struct _xmlNode *parent;
|
||||||
|
struct _xmlNode *next;
|
||||||
|
struct _xmlNode *prev;
|
||||||
|
struct _xmlDoc *doc;
|
||||||
|
};
|
||||||
|
|
||||||
|
@interface MGMXMLNode : NSObject {
|
||||||
|
xmlComPtr commonXML;
|
||||||
|
xmlNsPtr namespaceXML;
|
||||||
|
xmlNodePtr parentNode;
|
||||||
|
MGMXMLNodeKind type;
|
||||||
|
MGMXMLDocument *documentNode;
|
||||||
|
}
|
||||||
|
+ (id)nodeWithTypeXMLPtr:(xmlTypPtr)theXMLPtr;
|
||||||
|
- (id)initWithTypeXMLPtr:(xmlTypPtr)theXMLPtr;
|
||||||
|
+ (void)stripDocumentFromAttribute:(xmlAttrPtr)theAttribute;
|
||||||
|
+ (void)stripDocumentFromNode:(xmlNodePtr)theNode;
|
||||||
|
+ (void)removeAttributesFromNode:(xmlNodePtr)theNode;
|
||||||
|
+ (void)removeNamespacesFromNode:(xmlNodePtr)theNode;
|
||||||
|
+ (void)removeChildrenFromNode:(xmlNodePtr)theNode;
|
||||||
|
+ (void)freeNode:(xmlNodePtr)theNode;
|
||||||
|
- (void)setTypeXMLPtr:(xmlTypPtr)theXMLPtr;
|
||||||
|
- (void)releaseDocument;
|
||||||
|
+ (BOOL)isNode:(MGMXMLNodeKind)theType;
|
||||||
|
- (BOOL)isNode;
|
||||||
|
+ (NSError *)lastError;
|
||||||
|
- (NSError *)lastError;
|
||||||
|
- (id)initWithKind:(MGMXMLNodeKind)kind;
|
||||||
|
//- (id)initWithKind:(MGMXMLNodeKind)kind options:(NSUInteger)options; //primitive
|
||||||
|
//+ (id)document;
|
||||||
|
//+ (id)documentWithRootElement:(MGMXMLElement *)element;
|
||||||
|
//+ (id)elementWithName:(NSString *)name;
|
||||||
|
//+ (id)elementWithName:(NSString *)name URI:(NSString *)URI;
|
||||||
|
//+ (id)elementWithName:(NSString *)name stringValue:(NSString *)string;
|
||||||
|
//+ (id)elementWithName:(NSString *)name children:(NSArray *)children attributes:(NSArray *)attributes;
|
||||||
|
//+ (id)attributeWithName:(NSString *)name stringValue:(NSString *)stringValue;
|
||||||
|
//+ (id)attributeWithName:(NSString *)name URI:(NSString *)URI stringValue:(NSString *)stringValue;
|
||||||
|
//+ (id)namespaceWithName:(NSString *)name stringValue:(NSString *)stringValue;
|
||||||
|
//+ (id)processingInstructionWithName:(NSString *)name stringValue:(NSString *)stringValue;
|
||||||
|
//+ (id)commentWithStringValue:(NSString *)stringValue;
|
||||||
|
//+ (id)textWithStringValue:(NSString *)stringValue;
|
||||||
|
//+ (id)DTDNodeWithXMLString:(NSString *)string;
|
||||||
|
|
||||||
|
- (MGMXMLNodeKind)kind;
|
||||||
|
- (xmlComPtr)commonXML;
|
||||||
|
- (xmlNsPtr)nameSpaceXML;
|
||||||
|
- (void)setName:(NSString *)name;
|
||||||
|
- (NSString *)name;
|
||||||
|
//- (void)setObjectValue:(id)value; //primitive
|
||||||
|
//- (id)objectValue; //primitive
|
||||||
|
- (void)setStringValue:(NSString *)string;
|
||||||
|
//- (void)setStringValue:(NSString *)string resolvingEntities:(BOOL)resolve; //primitive
|
||||||
|
- (NSString *)stringValue; //primitive
|
||||||
|
|
||||||
|
//- (NSUInteger)index; //primitive
|
||||||
|
//- (NSUInteger)level;
|
||||||
|
- (MGMXMLDocument *)rootDocument;
|
||||||
|
- (MGMXMLNode *)parent; //primitive
|
||||||
|
//- (NSUInteger)childCount; //primitive
|
||||||
|
//- (NSArray *)children; //primitive
|
||||||
|
- (MGMXMLNode *)childAtIndex:(NSUInteger)index; //primitive
|
||||||
|
//- (MGMXMLNode *)previousSibling;
|
||||||
|
//- (MGMXMLNode *)nextSibling;
|
||||||
|
//- (MGMXMLNode *)previousNode;
|
||||||
|
//- (MGMXMLNode *)nextNode;
|
||||||
|
+ (void)detatchAttribute:(xmlAttrPtr)theAttribute fromNode:(xmlNodePtr)theNode;
|
||||||
|
- (void)detach; //primitive
|
||||||
|
//- (NSString *)XPath;
|
||||||
|
|
||||||
|
//- (NSString *)localName; //primitive
|
||||||
|
//- (NSString *)prefix; //primitive
|
||||||
|
//- (void)setURI:(NSString *)URI; //primitive
|
||||||
|
//- (NSString *)URI; //primitive
|
||||||
|
+ (NSString *)localNameForName:(NSString *)name;
|
||||||
|
+ (NSString *)prefixForName:(NSString *)name;
|
||||||
|
//+ (MGMXMLNode *)predefinedNamespaceForPrefix:(NSString *)name;
|
||||||
|
|
||||||
|
- (NSString *)description;
|
||||||
|
- (NSString *)XMLString;
|
||||||
|
- (NSString *)XMLStringWithOptions:(NSUInteger)options;
|
||||||
|
//- (NSString *)canonicalXMLStringPreservingComments:(BOOL)comments;
|
||||||
|
|
||||||
|
- (NSArray *)nodesForXPath:(NSString *)xpath error:(NSError **)error;
|
||||||
|
//- (NSArray *)objectsForXQuery:(NSString *)xquery constants:(NSDictionary *)constants error:(NSError **)error;
|
||||||
|
//- (NSArray *)objectsForXQuery:(NSString *)xquery error:(NSError **)error;
|
||||||
|
@end
|
547
Classes/YouView/XML/MGMXMLNode.m
Normal file
547
Classes/YouView/XML/MGMXMLNode.m
Normal file
@ -0,0 +1,547 @@
|
|||||||
|
//
|
||||||
|
// MGMXMLNode.m
|
||||||
|
// MGMXML
|
||||||
|
//
|
||||||
|
// Created by Mr. Gecko on 9/22/10.
|
||||||
|
// Copyright (c) 2011 Mr. Gecko's Media (James Coleman). https://mrgeckosmedia.com/
|
||||||
|
//
|
||||||
|
// Permission to use, copy, modify, and/or distribute this software for any purpose
|
||||||
|
// with or without fee is hereby granted, provided that the above copyright notice
|
||||||
|
// and this permission notice appear in all copies.
|
||||||
|
//
|
||||||
|
// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
|
||||||
|
// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
|
||||||
|
// FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT,
|
||||||
|
// OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE,
|
||||||
|
// DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS
|
||||||
|
// ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||||
|
//
|
||||||
|
|
||||||
|
#import "MGMXMLNode.h"
|
||||||
|
#import "MGMXMLAddons.h"
|
||||||
|
#import "MGMXMLDocument.h"
|
||||||
|
#import "MGMXMLElement.h"
|
||||||
|
#import "MGMXMLNodeOptions.h"
|
||||||
|
|
||||||
|
NSString * const MGMXMLErrorDomain = @"MGMXMLErrorDomain";
|
||||||
|
NSString * const MGMXMLLastError = @"MGMXMLLastError";
|
||||||
|
|
||||||
|
static NSMutableDictionary *MGMXMLInfo = nil;
|
||||||
|
|
||||||
|
static void MGMXMLErrorHandler(void *userData, xmlErrorPtr error) {
|
||||||
|
NSAutoreleasePool *pool = [NSAutoreleasePool new];;
|
||||||
|
if (error==NULL) {
|
||||||
|
[MGMXMLInfo removeObjectForKey:MGMXMLLastError];
|
||||||
|
} else {
|
||||||
|
[MGMXMLInfo setObject:[NSValue valueWithXMLError:error] forKey:MGMXMLLastError];
|
||||||
|
}
|
||||||
|
[pool drain];
|
||||||
|
}
|
||||||
|
|
||||||
|
@implementation MGMXMLNode
|
||||||
|
+ (void)initialize {
|
||||||
|
if (MGMXMLInfo==nil) {
|
||||||
|
MGMXMLInfo = [NSMutableDictionary new];
|
||||||
|
|
||||||
|
initGenericErrorDefaultFunc(NULL);
|
||||||
|
xmlSetStructuredErrorFunc(NULL, MGMXMLErrorHandler);
|
||||||
|
xmlKeepBlanksDefault(0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
- (id)init {
|
||||||
|
if ((self = [super init])) {
|
||||||
|
commonXML = NULL;
|
||||||
|
namespaceXML = NULL;
|
||||||
|
parentNode = NULL;
|
||||||
|
type = MGMXMLInvalidKind;
|
||||||
|
}
|
||||||
|
return self;
|
||||||
|
}
|
||||||
|
+ (id)nodeWithTypeXMLPtr:(xmlTypPtr)theXMLPtr {
|
||||||
|
return [[[self alloc] initWithTypeXMLPtr:theXMLPtr] autorelease];
|
||||||
|
}
|
||||||
|
- (id)initWithTypeXMLPtr:(xmlTypPtr)theXMLPtr {
|
||||||
|
if ((self = [self init])) {
|
||||||
|
if (theXMLPtr==NULL) {
|
||||||
|
//NSLog(@"NULL XMLPtr");
|
||||||
|
[self release];
|
||||||
|
self = nil;
|
||||||
|
} else {
|
||||||
|
if (theXMLPtr->type==MGMXMLNamespaceKind) {
|
||||||
|
xmlNsPtr xmlPtr = (xmlNsPtr)theXMLPtr;
|
||||||
|
if (xmlPtr->_private!=NULL) {
|
||||||
|
[self release];
|
||||||
|
self = nil;
|
||||||
|
return [(MGMXMLNode *)xmlPtr->_private retain];
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
xmlComPtr comXML = (xmlComPtr)theXMLPtr;
|
||||||
|
if (comXML->_private!=NULL) {
|
||||||
|
[self release];
|
||||||
|
self = nil;
|
||||||
|
return [(MGMXMLNode *)comXML->_private retain];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
[self setTypeXMLPtr:theXMLPtr];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return self;
|
||||||
|
}
|
||||||
|
- (void)clearParent {
|
||||||
|
parentNode = NULL;
|
||||||
|
}
|
||||||
|
- (void)freeXML {
|
||||||
|
if (namespaceXML!=NULL) {
|
||||||
|
namespaceXML->_private = NULL;
|
||||||
|
|
||||||
|
if (parentNode==NULL)
|
||||||
|
xmlFreeNs(namespaceXML);
|
||||||
|
namespaceXML = NULL;
|
||||||
|
parentNode = NULL;
|
||||||
|
}
|
||||||
|
if (commonXML!=NULL) {
|
||||||
|
commonXML->_private = NULL;
|
||||||
|
if (type!=MGMXMLDocumentKind)
|
||||||
|
[self releaseDocument];
|
||||||
|
|
||||||
|
if (commonXML->parent==NULL) {
|
||||||
|
if (type==MGMXMLDocumentKind) {
|
||||||
|
xmlNodePtr child = commonXML->children;
|
||||||
|
while (child!=NULL) {
|
||||||
|
xmlNodePtr nextChild = child->next;
|
||||||
|
if (child->type==MGMXMLElementKind) {
|
||||||
|
if (child->prev!=NULL)
|
||||||
|
child->prev->next = child->next;
|
||||||
|
if (child->next!=NULL)
|
||||||
|
child->next->prev = child->prev;
|
||||||
|
if (commonXML->children==child)
|
||||||
|
commonXML->children = child->next;
|
||||||
|
if (commonXML->last==child)
|
||||||
|
commonXML->last = child->prev;
|
||||||
|
[[self class] freeNode:child];
|
||||||
|
}
|
||||||
|
child = nextChild;
|
||||||
|
}
|
||||||
|
xmlFreeDoc(MGMXMLDocPtr);
|
||||||
|
} else if (type==MGMXMLAttributeKind) {
|
||||||
|
xmlFreeProp(MGMXMLAttrPtr);
|
||||||
|
} else if (type==MGMXMLDTDKind) {
|
||||||
|
xmlFreeDtd(MGMXMLDTDPtr);
|
||||||
|
} else {
|
||||||
|
[[self class] freeNode:MGMXMLNodePtr];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
commonXML = NULL;
|
||||||
|
}
|
||||||
|
type = MGMXMLInvalidKind;
|
||||||
|
}
|
||||||
|
- (void)dealloc {
|
||||||
|
[self freeXML];
|
||||||
|
[super dealloc];
|
||||||
|
}
|
||||||
|
+ (void)stripDocumentFromAttribute:(xmlAttrPtr)theAttribute {
|
||||||
|
xmlNodePtr child = theAttribute->children;
|
||||||
|
while (child!=NULL) {
|
||||||
|
child->doc = NULL;
|
||||||
|
child = child->next;
|
||||||
|
}
|
||||||
|
theAttribute->doc = NULL;
|
||||||
|
}
|
||||||
|
+ (void)stripDocumentFromNode:(xmlNodePtr)theNode {
|
||||||
|
xmlAttrPtr attribute = theNode->properties;
|
||||||
|
while (attribute!=NULL) {
|
||||||
|
[self stripDocumentFromAttribute:attribute];
|
||||||
|
attribute = attribute->next;
|
||||||
|
}
|
||||||
|
|
||||||
|
xmlNodePtr child = theNode->children;
|
||||||
|
while (child!=NULL) {
|
||||||
|
[self stripDocumentFromNode:child];
|
||||||
|
child = child->next;
|
||||||
|
}
|
||||||
|
theNode->doc = NULL;
|
||||||
|
}
|
||||||
|
+ (void)removeAttributesFromNode:(xmlNodePtr)theNode {
|
||||||
|
xmlAttrPtr attribute = theNode->properties;
|
||||||
|
while (attribute!=NULL) {
|
||||||
|
xmlAttrPtr nextAttribute = attribute->next;
|
||||||
|
|
||||||
|
if (attribute->_private==NULL) {
|
||||||
|
xmlFreeProp(attribute);
|
||||||
|
} else {
|
||||||
|
attribute->parent = NULL;
|
||||||
|
attribute->prev = NULL;
|
||||||
|
attribute->next = NULL;
|
||||||
|
if (attribute->doc!=NULL) [self stripDocumentFromAttribute:attribute];
|
||||||
|
}
|
||||||
|
attribute = nextAttribute;
|
||||||
|
}
|
||||||
|
theNode->properties = NULL;
|
||||||
|
}
|
||||||
|
+ (void)removeNamespacesFromNode:(xmlNodePtr)theNode {
|
||||||
|
xmlNsPtr namespace = theNode->nsDef;
|
||||||
|
while (namespace!=NULL){
|
||||||
|
xmlNsPtr nextNamespace = namespace->next;
|
||||||
|
if (namespace->_private!=NULL) {
|
||||||
|
[(MGMXMLNode *)namespace->_private clearParent];
|
||||||
|
namespace->next = NULL;
|
||||||
|
} else {
|
||||||
|
xmlFreeNs(namespace);
|
||||||
|
}
|
||||||
|
namespace = nextNamespace;
|
||||||
|
}
|
||||||
|
theNode->nsDef = NULL;
|
||||||
|
theNode->ns = NULL;
|
||||||
|
}
|
||||||
|
+ (void)removeChildrenFromNode:(xmlNodePtr)theNode {
|
||||||
|
xmlNodePtr child = theNode->children;
|
||||||
|
while (child!=NULL) {
|
||||||
|
xmlNodePtr nextChild = child->next;
|
||||||
|
[self freeNode:child];
|
||||||
|
child = nextChild;
|
||||||
|
}
|
||||||
|
theNode->children = NULL;
|
||||||
|
theNode->last = NULL;
|
||||||
|
}
|
||||||
|
+ (void)freeNode:(xmlNodePtr)theNode {
|
||||||
|
if (![[self class] isNode:theNode->type]) {
|
||||||
|
NSLog(@"Cannot free node as it is the wrong type %d.", theNode->type);
|
||||||
|
} else {
|
||||||
|
if (theNode->_private==NULL) {
|
||||||
|
[self removeAttributesFromNode:theNode];
|
||||||
|
[self removeNamespacesFromNode:theNode];
|
||||||
|
[self removeChildrenFromNode:theNode];
|
||||||
|
|
||||||
|
xmlFreeNode(theNode);
|
||||||
|
} else {
|
||||||
|
theNode->parent = NULL;
|
||||||
|
theNode->prev = NULL;
|
||||||
|
theNode->next = NULL;
|
||||||
|
if (theNode->doc!=NULL) {
|
||||||
|
[(MGMXMLNode *)theNode->_private releaseDocument];
|
||||||
|
[self stripDocumentFromNode:theNode];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)setTypeXMLPtr:(xmlTypPtr)theXMLPtr {
|
||||||
|
[self freeXML];
|
||||||
|
type = theXMLPtr->type;
|
||||||
|
if (type==XML_HTML_DOCUMENT_NODE)
|
||||||
|
type = MGMXMLDocumentKind;
|
||||||
|
if (type==MGMXMLNamespaceKind) {
|
||||||
|
namespaceXML = (xmlNsPtr)theXMLPtr;
|
||||||
|
namespaceXML->_private = self;
|
||||||
|
} else {
|
||||||
|
commonXML = (xmlComPtr)theXMLPtr;
|
||||||
|
commonXML->_private = self;
|
||||||
|
if (type==MGMXMLDocumentKind && [self isMemberOfClass:[MGMXMLNode class]])
|
||||||
|
self->isa = [MGMXMLDocument class];
|
||||||
|
else if (type==MGMXMLElementKind && [self isMemberOfClass:[MGMXMLNode class]])
|
||||||
|
self->isa = [MGMXMLElement class];
|
||||||
|
if (type!=MGMXMLDocumentKind)
|
||||||
|
documentNode = [[MGMXMLDocument alloc] initWithTypeXMLPtr:(xmlTypPtr)commonXML->doc];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
- (void)setDocument:(MGMXMLDocument *)theDocument {
|
||||||
|
[documentNode release];
|
||||||
|
documentNode = [theDocument retain];
|
||||||
|
}
|
||||||
|
- (void)releaseDocument {
|
||||||
|
[documentNode release];
|
||||||
|
documentNode = nil;
|
||||||
|
}
|
||||||
|
+ (BOOL)isNode:(MGMXMLNodeKind)theType {
|
||||||
|
switch (theType) {
|
||||||
|
case MGMXMLElementKind:
|
||||||
|
case MGMXMLProcessingInstructionKind:
|
||||||
|
case MGMXMLCommentKind:
|
||||||
|
case MGMXMLTextKind:
|
||||||
|
case XML_CDATA_SECTION_NODE:
|
||||||
|
return YES;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
return NO;
|
||||||
|
}
|
||||||
|
- (BOOL)isNode {
|
||||||
|
return [[self class] isNode:type];
|
||||||
|
}
|
||||||
|
+ (NSError *)lastError {
|
||||||
|
if ([MGMXMLInfo objectForKey:MGMXMLLastError]!=nil) {
|
||||||
|
xmlErrorPtr lastError = [[MGMXMLInfo objectForKey:MGMXMLLastError] xmlErrorValue];
|
||||||
|
NSString *description = [[NSString stringWithUTF8String:lastError->message] stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceCharacterSet]];
|
||||||
|
return [NSError errorWithDomain:MGMXMLErrorDomain code:lastError->code userInfo:[NSDictionary dictionaryWithObject:description forKey:NSLocalizedDescriptionKey]];
|
||||||
|
}
|
||||||
|
return nil;
|
||||||
|
}
|
||||||
|
- (NSError *)lastError {
|
||||||
|
return [[self class] lastError];
|
||||||
|
}
|
||||||
|
|
||||||
|
- (id)initWithKind:(MGMXMLNodeKind)kind {
|
||||||
|
if (kind==MGMXMLDocumentKind) {
|
||||||
|
[self release];
|
||||||
|
return [[MGMXMLDocument alloc] initWithRootElement:nil];
|
||||||
|
} else if (kind==MGMXMLElementKind) {
|
||||||
|
return [self initWithTypeXMLPtr:(xmlTypPtr)xmlNewNode(NULL, NULL)];
|
||||||
|
} else if (kind==MGMXMLAttributeKind) {
|
||||||
|
return [self initWithTypeXMLPtr:(xmlTypPtr)xmlNewProp(NULL, (const xmlChar *)"", NULL)];
|
||||||
|
} else if (kind==MGMXMLNamespaceKind) {
|
||||||
|
return [self initWithTypeXMLPtr:(xmlTypPtr)xmlNewNs(NULL, NULL, NULL)];
|
||||||
|
} else if (kind==MGMXMLProcessingInstructionKind) {
|
||||||
|
return [self initWithTypeXMLPtr:(xmlTypPtr)xmlNewPI(NULL, NULL)];
|
||||||
|
} else if (kind==MGMXMLCommentKind) {
|
||||||
|
return [self initWithTypeXMLPtr:(xmlTypPtr)xmlNewComment(NULL)];
|
||||||
|
} else if (kind==MGMXMLTextKind) {
|
||||||
|
return [self initWithTypeXMLPtr:(xmlTypPtr)xmlNewText(NULL)];
|
||||||
|
}
|
||||||
|
[self release];
|
||||||
|
return nil;
|
||||||
|
}
|
||||||
|
|
||||||
|
- (MGMXMLNodeKind)kind {
|
||||||
|
return type;
|
||||||
|
}
|
||||||
|
- (xmlComPtr)commonXML {
|
||||||
|
return commonXML;
|
||||||
|
}
|
||||||
|
- (xmlNsPtr)nameSpaceXML {
|
||||||
|
return namespaceXML;
|
||||||
|
}
|
||||||
|
- (void)setName:(NSString *)name {
|
||||||
|
if (type==MGMXMLNamespaceKind) {
|
||||||
|
if (namespaceXML->prefix!=NULL)
|
||||||
|
xmlFree((xmlChar *)namespaceXML->prefix);
|
||||||
|
namespaceXML->prefix = xmlStrdup([name xmlString]);
|
||||||
|
} else {
|
||||||
|
xmlNodeSetName(MGMXMLNodePtr, [name xmlString]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
- (NSString *)name {
|
||||||
|
if (type==MGMXMLNamespaceKind) {
|
||||||
|
if (namespaceXML->prefix!=NULL)
|
||||||
|
return [NSString stringWithXMLString:namespaceXML->prefix];
|
||||||
|
} else {
|
||||||
|
if (MGMXMLNodePtr->name!=NULL)
|
||||||
|
return [NSString stringWithXMLString:(xmlChar *)MGMXMLNodePtr->name];
|
||||||
|
}
|
||||||
|
return nil;
|
||||||
|
}
|
||||||
|
- (void)setStringValue:(NSString *)string {
|
||||||
|
if (type==MGMXMLNamespaceKind) {
|
||||||
|
namespaceXML->href = [string xmlString];
|
||||||
|
} else if ([self isNode] || type==MGMXMLAttributeKind) {
|
||||||
|
xmlChar *contentString = xmlEncodeEntitiesReentrant(commonXML->doc, [string xmlString]);
|
||||||
|
xmlNodeSetContent(MGMXMLNodePtr, contentString);
|
||||||
|
xmlFree(contentString);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
- (NSString *)stringValue {
|
||||||
|
if (type==MGMXMLNamespaceKind) {
|
||||||
|
return [NSString stringWithXMLString:namespaceXML->href];
|
||||||
|
} else if ([self isNode] || type==MGMXMLAttributeKind) {
|
||||||
|
xmlChar *contentString = xmlNodeGetContent(MGMXMLNodePtr);
|
||||||
|
NSString *stringValue = [NSString stringWithXMLString:contentString];
|
||||||
|
xmlFree(contentString);
|
||||||
|
return stringValue;
|
||||||
|
}
|
||||||
|
return nil;
|
||||||
|
}
|
||||||
|
|
||||||
|
- (MGMXMLDocument *)rootDocument {
|
||||||
|
if (MGMXMLNodePtr->doc!=NULL)
|
||||||
|
return [MGMXMLDocument nodeWithTypeXMLPtr:(xmlTypPtr)MGMXMLNodePtr->doc];
|
||||||
|
return nil;
|
||||||
|
}
|
||||||
|
- (MGMXMLNode *)parent {
|
||||||
|
return [MGMXMLNode nodeWithTypeXMLPtr:(xmlTypPtr)MGMXMLNodePtr->parent];
|
||||||
|
}
|
||||||
|
- (MGMXMLNode *)childAtIndex:(NSUInteger)index {
|
||||||
|
if (type==MGMXMLNamespaceKind)
|
||||||
|
return nil;
|
||||||
|
|
||||||
|
NSUInteger i = 0;
|
||||||
|
xmlNodePtr child = commonXML->children;
|
||||||
|
while (child!=NULL) {
|
||||||
|
if (i==index)
|
||||||
|
return [MGMXMLNode nodeWithTypeXMLPtr:(xmlTypPtr)child];
|
||||||
|
i++;
|
||||||
|
child = child->next;
|
||||||
|
}
|
||||||
|
return nil;
|
||||||
|
}
|
||||||
|
+ (void)detatchAttribute:(xmlAttrPtr)theAttribute fromNode:(xmlNodePtr)theNode {
|
||||||
|
if (theAttribute->prev==NULL && theAttribute->next==NULL) {
|
||||||
|
theNode->properties = NULL;
|
||||||
|
} else if (theAttribute->prev==NULL) {
|
||||||
|
theNode->properties = theAttribute->next;
|
||||||
|
theAttribute->next->prev = NULL;
|
||||||
|
} else if (theAttribute->next==NULL) {
|
||||||
|
theAttribute->prev->next = NULL;
|
||||||
|
} else {
|
||||||
|
theAttribute->prev->next = theAttribute->next;
|
||||||
|
theAttribute->next->prev = theAttribute->prev;
|
||||||
|
}
|
||||||
|
theAttribute->parent = NULL;
|
||||||
|
theAttribute->prev = NULL;
|
||||||
|
theAttribute->next = NULL;
|
||||||
|
if (theAttribute->doc!=NULL) [[self class] stripDocumentFromAttribute:theAttribute];
|
||||||
|
}
|
||||||
|
- (void)detach {
|
||||||
|
if (type==MGMXMLNamespaceKind) {
|
||||||
|
if (parentNode!=NULL) {
|
||||||
|
xmlNsPtr previousNamespace = NULL;
|
||||||
|
xmlNsPtr currentNamespace = parentNode->nsDef;
|
||||||
|
while (currentNamespace!=NULL) {
|
||||||
|
if (currentNamespace==namespaceXML) {
|
||||||
|
if (previousNamespace!=NULL)
|
||||||
|
previousNamespace->next = currentNamespace->next;
|
||||||
|
else
|
||||||
|
parentNode->nsDef = currentNamespace->next;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
previousNamespace = currentNamespace;
|
||||||
|
currentNamespace = currentNamespace->next;
|
||||||
|
}
|
||||||
|
namespaceXML->next = NULL;
|
||||||
|
if (parentNode->ns==namespaceXML)
|
||||||
|
parentNode->ns = NULL;
|
||||||
|
parentNode = NULL;
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (commonXML->parent==NULL) return;
|
||||||
|
|
||||||
|
if (type==MGMXMLAttributeKind) {
|
||||||
|
[[self class] detatchAttribute:MGMXMLAttrPtr fromNode:MGMXMLAttrPtr->parent];
|
||||||
|
} else if ([self isNode]) {
|
||||||
|
if (commonXML->prev==NULL && commonXML->next==NULL) {
|
||||||
|
commonXML->parent->children = NULL;
|
||||||
|
commonXML->parent->last = NULL;
|
||||||
|
} else if (commonXML->prev==NULL) {
|
||||||
|
commonXML->parent->children = commonXML->next;
|
||||||
|
commonXML->next->prev = NULL;
|
||||||
|
} else if (commonXML->next==NULL) {
|
||||||
|
commonXML->parent->last = commonXML->prev;
|
||||||
|
commonXML->prev->next = NULL;
|
||||||
|
} else {
|
||||||
|
commonXML->prev->next = commonXML->next;
|
||||||
|
commonXML->next->prev = commonXML->prev;
|
||||||
|
}
|
||||||
|
commonXML->parent = NULL;
|
||||||
|
commonXML->prev = NULL;
|
||||||
|
commonXML->next = NULL;
|
||||||
|
if (commonXML->doc!=NULL) [[self class] stripDocumentFromNode:MGMXMLNodePtr];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
+ (NSString *)localNameForName:(NSString *)name {
|
||||||
|
if (name!=nil && [name length]>0) {
|
||||||
|
NSRange range = [name rangeOfString:@":"];
|
||||||
|
if (range.location!=NSNotFound)
|
||||||
|
return [name substringFromIndex:range.location+range.length];
|
||||||
|
else
|
||||||
|
return name;
|
||||||
|
}
|
||||||
|
return nil;
|
||||||
|
}
|
||||||
|
+ (NSString *)prefixForName:(NSString *)name {
|
||||||
|
if (name!=nil && [name length]>0) {
|
||||||
|
NSRange range = [name rangeOfString:@":"];
|
||||||
|
if (range.location!=NSNotFound)
|
||||||
|
return [name substringToIndex:range.location];
|
||||||
|
}
|
||||||
|
return nil;
|
||||||
|
}
|
||||||
|
|
||||||
|
- (NSString *)description {
|
||||||
|
return [self XMLString];
|
||||||
|
}
|
||||||
|
- (NSString *)XMLString {
|
||||||
|
return [self XMLStringWithOptions:0];
|
||||||
|
}
|
||||||
|
- (NSString *)XMLStringWithOptions:(NSUInteger)options {
|
||||||
|
if(options & MGMXMLNodeCompactEmptyElement)
|
||||||
|
xmlSaveNoEmptyTags = 0;
|
||||||
|
else
|
||||||
|
xmlSaveNoEmptyTags = 1;
|
||||||
|
|
||||||
|
int format = 0;
|
||||||
|
if (options & MGMXMLNodePrettyPrint) {
|
||||||
|
format = 1;
|
||||||
|
xmlIndentTreeOutput = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
xmlBufferPtr bufferPtr = xmlBufferCreate();
|
||||||
|
if (type==MGMXMLNamespaceKind)
|
||||||
|
xmlNodeDump(bufferPtr, NULL, MGMXMLNodePtr, 0, format);
|
||||||
|
else
|
||||||
|
xmlNodeDump(bufferPtr, commonXML->doc, MGMXMLNodePtr, 0, format);
|
||||||
|
|
||||||
|
NSString *result = [NSString stringWithXMLString:bufferPtr->content];
|
||||||
|
if (type!=MGMXMLTextKind)
|
||||||
|
result = [result stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceCharacterSet]];
|
||||||
|
xmlBufferFree(bufferPtr);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
- (NSArray *)nodesForXPath:(NSString *)xpath error:(NSError **)error {
|
||||||
|
BOOL shouldRemoveDocument = NO;
|
||||||
|
xmlDocPtr document;
|
||||||
|
if ([self isNode]) {
|
||||||
|
document = MGMXMLNodePtr->doc;
|
||||||
|
if (document==NULL) {
|
||||||
|
shouldRemoveDocument = YES;
|
||||||
|
document = xmlNewDoc(NULL);
|
||||||
|
xmlDocSetRootElement(document, MGMXMLNodePtr);
|
||||||
|
}
|
||||||
|
} else if (type==MGMXMLDocumentKind) {
|
||||||
|
document = MGMXMLDocPtr;
|
||||||
|
} else {
|
||||||
|
return nil;
|
||||||
|
}
|
||||||
|
|
||||||
|
xmlXPathContextPtr xPathContext = xmlXPathNewContext(document);
|
||||||
|
xPathContext->node = MGMXMLNodePtr;
|
||||||
|
|
||||||
|
xmlNodePtr rootNode = document->children;
|
||||||
|
if (rootNode!=NULL) {
|
||||||
|
xmlNsPtr namespace = rootNode->nsDef;
|
||||||
|
while (namespace!=NULL) {
|
||||||
|
xmlXPathRegisterNs(xPathContext, namespace->prefix, namespace->href);
|
||||||
|
namespace = namespace->next;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
xmlXPathObjectPtr xPathObject = xmlXPathEvalExpression([xpath xmlString], xPathContext);
|
||||||
|
|
||||||
|
NSMutableArray *nodes = [NSMutableArray array];
|
||||||
|
|
||||||
|
if (xPathObject==NULL) {
|
||||||
|
if (error!=nil) *error = [self lastError];
|
||||||
|
nodes = nil;
|
||||||
|
} else {
|
||||||
|
int count = xmlXPathNodeSetGetLength(xPathObject->nodesetval);
|
||||||
|
if (count!=0) {
|
||||||
|
for (int i=0; i<count; i++)
|
||||||
|
[nodes addObject:[MGMXMLNode nodeWithTypeXMLPtr:(xmlTypPtr)xPathObject->nodesetval->nodeTab[i]]];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (xPathObject) xmlXPathFreeObject(xPathObject);
|
||||||
|
if (xPathContext) xmlXPathFreeContext(xPathContext);
|
||||||
|
|
||||||
|
if (shouldRemoveDocument) {
|
||||||
|
xmlUnlinkNode(MGMXMLNodePtr);
|
||||||
|
xmlFreeDoc(document);
|
||||||
|
[[self class] stripDocumentFromNode:MGMXMLNodePtr];
|
||||||
|
}
|
||||||
|
|
||||||
|
return nodes;
|
||||||
|
}
|
||||||
|
@end
|
63
Classes/YouView/XML/MGMXMLNodeOptions.h
Normal file
63
Classes/YouView/XML/MGMXMLNodeOptions.h
Normal file
@ -0,0 +1,63 @@
|
|||||||
|
//
|
||||||
|
// MGMXMLNodeOptions.m
|
||||||
|
// MGMXML
|
||||||
|
//
|
||||||
|
// Created by Mr. Gecko on 9/22/10.
|
||||||
|
// Copyright (c) 2011 Mr. Gecko's Media (James Coleman). https://mrgeckosmedia.com/
|
||||||
|
//
|
||||||
|
// Permission to use, copy, modify, and/or distribute this software for any purpose
|
||||||
|
// with or without fee is hereby granted, provided that the above copyright notice
|
||||||
|
// and this permission notice appear in all copies.
|
||||||
|
//
|
||||||
|
// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
|
||||||
|
// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
|
||||||
|
// FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT,
|
||||||
|
// OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE,
|
||||||
|
// DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS
|
||||||
|
// ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||||
|
//
|
||||||
|
|
||||||
|
enum {
|
||||||
|
MGMXMLNodeOptionsNone = 0,
|
||||||
|
|
||||||
|
MGMXMLNodeIsCDATA = 1 << 0,
|
||||||
|
MGMXMLNodeExpandEmptyElement = 1 << 1,
|
||||||
|
MGMXMLNodeCompactEmptyElement = 1 << 2,
|
||||||
|
MGMXMLNodeUseSingleQuotes = 1 << 3,
|
||||||
|
MGMXMLNodeUseDoubleQuotes = 1 << 4,
|
||||||
|
|
||||||
|
MGMXMLDocumentTidyHTML = 1 << 9,
|
||||||
|
MGMXMLDocumentTidyXML = 1 << 10,
|
||||||
|
|
||||||
|
MGMXMLDocumentValidate = 1 << 13,
|
||||||
|
|
||||||
|
MGMXMLDocumentXInclude = 1 << 16,
|
||||||
|
|
||||||
|
MGMXMLNodePrettyPrint = 1 << 17,
|
||||||
|
MGMXMLDocumentIncludeContentTypeDeclaration = 1 << 18,
|
||||||
|
|
||||||
|
MGMXMLNodePreserveNamespaceOrder = 1 << 20,
|
||||||
|
MGMXMLNodePreserveAttributeOrder = 1 << 21,
|
||||||
|
MGMXMLNodePreserveEntities = 1 << 22,
|
||||||
|
MGMXMLNodePreservePrefixes = 1 << 23,
|
||||||
|
MGMXMLNodePreserveCDATA = 1 << 24,
|
||||||
|
MGMXMLNodePreserveWhitespace = 1 << 25,
|
||||||
|
MGMXMLNodePreserveDTD = 1 << 26,
|
||||||
|
MGMXMLNodePreserveCharacterReferences = 1 << 27,
|
||||||
|
MGMXMLNodePreserveEmptyElements =
|
||||||
|
(MGMXMLNodeExpandEmptyElement | MGMXMLNodeCompactEmptyElement),
|
||||||
|
MGMXMLNodePreserveQuotes =
|
||||||
|
(MGMXMLNodeUseSingleQuotes | MGMXMLNodeUseDoubleQuotes),
|
||||||
|
MGMXMLNodePreserveAll = (
|
||||||
|
MGMXMLNodePreserveNamespaceOrder |
|
||||||
|
MGMXMLNodePreserveAttributeOrder |
|
||||||
|
MGMXMLNodePreserveEntities |
|
||||||
|
MGMXMLNodePreservePrefixes |
|
||||||
|
MGMXMLNodePreserveCDATA |
|
||||||
|
MGMXMLNodePreserveEmptyElements |
|
||||||
|
MGMXMLNodePreserveQuotes |
|
||||||
|
MGMXMLNodePreserveWhitespace |
|
||||||
|
MGMXMLNodePreserveDTD |
|
||||||
|
MGMXMLNodePreserveCharacterReferences |
|
||||||
|
0xFFF00000)
|
||||||
|
};
|
1
Frameworks/GeckoReporter.framework/GeckoReporter
Symbolic link
1
Frameworks/GeckoReporter.framework/GeckoReporter
Symbolic link
@ -0,0 +1 @@
|
|||||||
|
Versions/Current/GeckoReporter
|
1
Frameworks/GeckoReporter.framework/Headers
Symbolic link
1
Frameworks/GeckoReporter.framework/Headers
Symbolic link
@ -0,0 +1 @@
|
|||||||
|
Versions/Current/Headers
|
1
Frameworks/GeckoReporter.framework/Resources
Symbolic link
1
Frameworks/GeckoReporter.framework/Resources
Symbolic link
@ -0,0 +1 @@
|
|||||||
|
Versions/Current/Resources
|
BIN
Frameworks/GeckoReporter.framework/Versions/A/GeckoReporter
Executable file
BIN
Frameworks/GeckoReporter.framework/Versions/A/GeckoReporter
Executable file
Binary file not shown.
@ -0,0 +1,12 @@
|
|||||||
|
//
|
||||||
|
// GeckoReporter.h
|
||||||
|
// GeckoReporter
|
||||||
|
//
|
||||||
|
// Created by Mr. Gecko on 12/27/09.
|
||||||
|
// Copyright (c) 2010 Mr. Gecko's Media (James Coleman). All rights reserved. http://mrgeckosmedia.com/
|
||||||
|
//
|
||||||
|
|
||||||
|
#import "MGMReporter.h"
|
||||||
|
#import "MGMSender.h"
|
||||||
|
#import "MGMSystemInfo.h"
|
||||||
|
#import "MGMLog.h"
|
@ -0,0 +1,16 @@
|
|||||||
|
//
|
||||||
|
// MGMFeedback.h
|
||||||
|
// GeckoReporter
|
||||||
|
//
|
||||||
|
// Created by Mr. Gecko on 1/2/10.
|
||||||
|
// Copyright (c) 2010 Mr. Gecko's Media (James Coleman). All rights reserved. http://mrgeckosmedia.com/
|
||||||
|
//
|
||||||
|
|
||||||
|
#import <Cocoa/Cocoa.h>
|
||||||
|
|
||||||
|
@interface MGMFeedback : NSObject {
|
||||||
|
|
||||||
|
}
|
||||||
|
- (IBAction)openBugReport:(id)sender;
|
||||||
|
- (IBAction)openContact:(id)sender;
|
||||||
|
@end
|
@ -0,0 +1,12 @@
|
|||||||
|
//
|
||||||
|
// MGMLog.h
|
||||||
|
// GeckoReporter
|
||||||
|
//
|
||||||
|
// Created by Mr. Gecko on 1/1/10.
|
||||||
|
// Copyright (c) 2010 Mr. Gecko's Media (James Coleman). All rights reserved. http://mrgeckosmedia.com/
|
||||||
|
//
|
||||||
|
|
||||||
|
#import <Cocoa/Cocoa.h>
|
||||||
|
|
||||||
|
void MGMLog(NSString *format, ...);
|
||||||
|
void MGMLogv(NSString *format, va_list args);
|
@ -0,0 +1,28 @@
|
|||||||
|
//
|
||||||
|
// MGMReporter.h
|
||||||
|
// GeckoReporter
|
||||||
|
//
|
||||||
|
// Created by Mr. Gecko on 12/27/09.
|
||||||
|
// Copyright (c) 2010 Mr. Gecko's Media (James Coleman). All rights reserved. http://mrgeckosmedia.com/
|
||||||
|
//
|
||||||
|
|
||||||
|
#import <Cocoa/Cocoa.h>
|
||||||
|
|
||||||
|
extern NSString * const MGMGRDoneNotification;
|
||||||
|
extern NSString * const MGMGRUserEmail;
|
||||||
|
extern NSString * const MGMGRUserName;
|
||||||
|
extern NSString * const MGMGRLastCrashDate;
|
||||||
|
extern NSString * const MGMGRSendAll;
|
||||||
|
extern NSString * const MGMGRIgnoreAll;
|
||||||
|
|
||||||
|
#define MGMGRReleaseDebug 0
|
||||||
|
|
||||||
|
@class MGMSender;
|
||||||
|
|
||||||
|
@interface MGMReporter : NSObject {
|
||||||
|
BOOL foundReport;
|
||||||
|
NSDate *lastDate;
|
||||||
|
MGMSender *mailSender;
|
||||||
|
}
|
||||||
|
+ (id)sharedReporter;
|
||||||
|
@end
|
@ -0,0 +1,20 @@
|
|||||||
|
//
|
||||||
|
// MGMSender.h
|
||||||
|
// GeckoReporter
|
||||||
|
//
|
||||||
|
// Created by Mr. Gecko on 12/28/09.
|
||||||
|
// Copyright (c) 2010 Mr. Gecko's Media (James Coleman). All rights reserved. http://mrgeckosmedia.com/
|
||||||
|
//
|
||||||
|
|
||||||
|
#import <Cocoa/Cocoa.h>
|
||||||
|
#import "MGMSenderDelegate.h"
|
||||||
|
|
||||||
|
@interface MGMSender : NSObject {
|
||||||
|
id<MGMSenderDelegate> delegate;
|
||||||
|
NSMutableData *receivedData;
|
||||||
|
NSURLConnection *theConnection;
|
||||||
|
}
|
||||||
|
- (void)sendReport:(NSString *)theReportPath reportDate:(NSDate *)theReportDate userReport:(NSString *)theUserReport delegate:(id)theDelegate;
|
||||||
|
- (void)sendBug:(NSString *)theBug reproduce:(NSString *)theReproduce delegate:(id)theDelegate;
|
||||||
|
- (void)sendMessage:(NSString *)theMessage subject:(NSString *)theSubject delegate:(id)theDelegate;
|
||||||
|
@end
|
@ -0,0 +1,15 @@
|
|||||||
|
//
|
||||||
|
// MGMSenderDelegate.h
|
||||||
|
// GeckoReporter
|
||||||
|
//
|
||||||
|
// Created by Mr. Gecko on 1/2/10.
|
||||||
|
// Copyright (c) 2010 Mr. Gecko's Media (James Coleman). All rights reserved. http://mrgeckosmedia.com/
|
||||||
|
//
|
||||||
|
|
||||||
|
#import <Cocoa/Cocoa.h>
|
||||||
|
|
||||||
|
|
||||||
|
@protocol MGMSenderDelegate <NSObject>
|
||||||
|
- (void)sendError:(NSError *)error;
|
||||||
|
- (void)sendFinished:(NSString *)received;
|
||||||
|
@end
|
@ -0,0 +1,46 @@
|
|||||||
|
//
|
||||||
|
// MGMSystemInfo.h
|
||||||
|
// GeckoReporter
|
||||||
|
//
|
||||||
|
// Created by Mr. Gecko on 12/31/09.
|
||||||
|
// Copyright (c) 2010 Mr. Gecko's Media (James Coleman). All rights reserved. http://mrgeckosmedia.com/
|
||||||
|
//
|
||||||
|
|
||||||
|
#import <Cocoa/Cocoa.h>
|
||||||
|
|
||||||
|
|
||||||
|
@interface MGMSystemInfo : NSObject {
|
||||||
|
|
||||||
|
}
|
||||||
|
+ (MGMSystemInfo *)info;
|
||||||
|
- (NSString *)architecture;
|
||||||
|
- (BOOL)is64Bit;
|
||||||
|
- (NSString *)CPUFamily;
|
||||||
|
- (int)CPUCount;
|
||||||
|
- (NSString *)model;
|
||||||
|
- (NSString *)modelName;
|
||||||
|
- (int)CPUMHz;
|
||||||
|
- (int)RAMSize;
|
||||||
|
- (int)OSMajorVersion;
|
||||||
|
- (int)OSMinorVersion;
|
||||||
|
- (int)OSBugFixVersion;
|
||||||
|
- (NSString *)OSVersion;
|
||||||
|
- (NSString *)OSVersionName;
|
||||||
|
- (BOOL)isAfterCheetah;
|
||||||
|
- (BOOL)isAfterPuma;
|
||||||
|
- (BOOL)isAfterJaguar;
|
||||||
|
- (BOOL)isAfterPanther;
|
||||||
|
- (BOOL)isAfterTiger;
|
||||||
|
- (BOOL)isAfterLeopard;
|
||||||
|
- (BOOL)isAfterSnowLeopard;
|
||||||
|
- (NSString *)language;
|
||||||
|
- (NSString *)applicationIdentifier;
|
||||||
|
- (NSString *)applicationName;
|
||||||
|
- (NSString *)applicationEXECName;
|
||||||
|
- (NSString *)applicationVersion;
|
||||||
|
- (BOOL)isUIElement;
|
||||||
|
- (NSBundle *)frameworkBundle;
|
||||||
|
- (NSString *)frameworkVersion;
|
||||||
|
- (NSString *)useragentWithApplicationNameAndVersion:(NSString *)nameAndVersion;
|
||||||
|
- (NSString *)useragent;
|
||||||
|
@end
|
Binary file not shown.
BIN
Frameworks/GeckoReporter.framework/Versions/A/Resources/English.lproj/MGMBugWindow.nib
generated
Normal file
BIN
Frameworks/GeckoReporter.framework/Versions/A/Resources/English.lproj/MGMBugWindow.nib
generated
Normal file
Binary file not shown.
BIN
Frameworks/GeckoReporter.framework/Versions/A/Resources/English.lproj/MGMContactWindow.nib
generated
Normal file
BIN
Frameworks/GeckoReporter.framework/Versions/A/Resources/English.lproj/MGMContactWindow.nib
generated
Normal file
Binary file not shown.
BIN
Frameworks/GeckoReporter.framework/Versions/A/Resources/English.lproj/MGMReportWindow.nib
generated
Normal file
BIN
Frameworks/GeckoReporter.framework/Versions/A/Resources/English.lproj/MGMReportWindow.nib
generated
Normal file
Binary file not shown.
BIN
Frameworks/GeckoReporter.framework/Versions/A/Resources/English.lproj/MainMenu.nib
generated
Normal file
BIN
Frameworks/GeckoReporter.framework/Versions/A/Resources/English.lproj/MainMenu.nib
generated
Normal file
Binary file not shown.
@ -0,0 +1,26 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||||
|
<plist version="1.0">
|
||||||
|
<dict>
|
||||||
|
<key>CFBundleDevelopmentRegion</key>
|
||||||
|
<string>English</string>
|
||||||
|
<key>CFBundleExecutable</key>
|
||||||
|
<string>GeckoReporter</string>
|
||||||
|
<key>CFBundleIdentifier</key>
|
||||||
|
<string>com.MrGeckosMedia.GeckoReporter</string>
|
||||||
|
<key>CFBundleInfoDictionaryVersion</key>
|
||||||
|
<string>6.0</string>
|
||||||
|
<key>CFBundleName</key>
|
||||||
|
<string>GeckoReporter</string>
|
||||||
|
<key>CFBundlePackageType</key>
|
||||||
|
<string>FMWK</string>
|
||||||
|
<key>CFBundleShortVersionString</key>
|
||||||
|
<string>0.1</string>
|
||||||
|
<key>CFBundleSignature</key>
|
||||||
|
<string>????</string>
|
||||||
|
<key>CFBundleVersion</key>
|
||||||
|
<string>0.1</string>
|
||||||
|
<key>NSHumanReadableCopyright</key>
|
||||||
|
<string>Copyright (c) 2010 Mr. Gecko's Media (James Coleman). All rights reserved. http://mrgeckosmedia.com/</string>
|
||||||
|
</dict>
|
||||||
|
</plist>
|
@ -0,0 +1,27 @@
|
|||||||
|
Copyright (c) 2010 Mr. Gecko's Media (James Coleman). All rights reserved. http://mrgeckosmedia.com/
|
||||||
|
|
||||||
|
Permission is granted, to any person obtaining a copy of this framework, to
|
||||||
|
use, copy, modify, merge, or redistribute this framework under the following terms:
|
||||||
|
|
||||||
|
1. This file must be included in all copies of this framework unmodified in
|
||||||
|
GeckoReporter.framework/Resource/License.txt and/or GeckoReporter.framework/Versions/A/Resources/License.txt.
|
||||||
|
|
||||||
|
2. THIS FRAMEWORK IS PROVIDED "AS IS" BY JAMES COLEMAN, WITHOUT WARRANTY OF
|
||||||
|
ANY KIND. IF YOUR SOFTWARE/FRAMEWORK/COMPUTER CRASH OR FAILS TO WORK IN ANY
|
||||||
|
WAY SHAPE OR FORM BECAUSE OF THIS FRAMEWORK, I (JAMES COLEMAN) AM NOT IN ANYWAY
|
||||||
|
RESPONSIBLE FOR YOUR PROBLEM. BUT, I MAY BE WILLING TO HELP YOU BUT NO PROMISES.
|
||||||
|
|
||||||
|
3. Redistributions of source code included in this framework must retain the
|
||||||
|
copyright notice above this license file without modifications.
|
||||||
|
|
||||||
|
4. Redistributions of binary must contain the copyright above this license file
|
||||||
|
without modifications.
|
||||||
|
|
||||||
|
5. If you use the crash reporter in this framework, you are allowed to remove the
|
||||||
|
NSTextField that says, "GeckoReporter by Mr. Gecko's Media".
|
||||||
|
|
||||||
|
6. For the users convenience, you must retain the notice about anonymous system
|
||||||
|
information being sent.
|
||||||
|
|
||||||
|
7. Mr. Gecko's Media (James Coleman) is allowed to modify these terms without notice to you
|
||||||
|
or your customers.
|
@ -0,0 +1,206 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||||
|
<plist version="1.0">
|
||||||
|
<dict>
|
||||||
|
<key>ADP2,1</key>
|
||||||
|
<string>Developer Transition Kit</string>
|
||||||
|
<key>iMac1,1</key>
|
||||||
|
<string>iMac G3 (Rev A-D)</string>
|
||||||
|
<key>PowerMac2,1</key>
|
||||||
|
<string>iMac G3 (Slot-loading CD-ROM)</string>
|
||||||
|
<key>PowerMac2,2</key>
|
||||||
|
<string>iMac G3 (Summer 2000)</string>
|
||||||
|
<key>PowerMac4,1</key>
|
||||||
|
<string>iMac G3 (2001)</string>
|
||||||
|
<key>PowerMac4,2</key>
|
||||||
|
<string>iMac G4 (Flat Panel)</string>
|
||||||
|
<key>PowerMac4,5</key>
|
||||||
|
<string>iMac G4 (17" Flat Panel)</string>
|
||||||
|
<key>PowerMac6,1</key>
|
||||||
|
<string>iMac G4 (USB 2.0)</string>
|
||||||
|
<key>PowerMac6,3</key>
|
||||||
|
<string>iMac G4 (20" Flat Panel)</string>
|
||||||
|
<key>PowerMac8,1</key>
|
||||||
|
<string>iMac G5</string>
|
||||||
|
<key>PowerMac8,2</key>
|
||||||
|
<string>iMac G5 (Ambient Light Sensor)</string>
|
||||||
|
<key>PowerMac12,1</key>
|
||||||
|
<string>iMac G5 (iSight)</string>
|
||||||
|
<key>iMac4,1</key>
|
||||||
|
<string>iMac (Core Duo)</string>
|
||||||
|
<key>iMac4,2</key>
|
||||||
|
<string>iMac for Education (17" Core Duo)</string>
|
||||||
|
<key>iMac5,1</key>
|
||||||
|
<string>iMac (Core 2 Duo 17"/20" SuperDrive)</string>
|
||||||
|
<key>iMac5,2</key>
|
||||||
|
<string>iMac (Core 2 Duo 17" Combo Drive)</string>
|
||||||
|
<key>iMac6,1</key>
|
||||||
|
<string>iMac (Core 2 Duo 24" SuperDrive)</string>
|
||||||
|
<key>iMac7,1</key>
|
||||||
|
<string>iMac (AI)</string>
|
||||||
|
<key>iMac8,1</key>
|
||||||
|
<string>iMac (2008)</string>
|
||||||
|
<key>iMac9,1</key>
|
||||||
|
<string>iMac (2009)</string>
|
||||||
|
<key>iMac10,1</key>
|
||||||
|
<string>iMac (2009 Magic)</string>
|
||||||
|
<key>iMac11,1</key>
|
||||||
|
<string>iMac (2009 Core I5)</string>
|
||||||
|
<key>PowerMac4,4</key>
|
||||||
|
<string>eMac</string>
|
||||||
|
<key>PowerMac6,4</key>
|
||||||
|
<string>eMac (USB 2.0 2005)</string>
|
||||||
|
<key>PowerMac10,1</key>
|
||||||
|
<string>Mac Mini G4</string>
|
||||||
|
<key>PowerMac10,2</key>
|
||||||
|
<string>Mac Mini (2005)</string>
|
||||||
|
<key>Macmini1,1</key>
|
||||||
|
<string>Mac Mini (Core Solo/Duo)</string>
|
||||||
|
<key>Macmini2,1</key>
|
||||||
|
<string>Mac Mini (Core 2 Duo)</string>
|
||||||
|
<key>Macmini3,1</key>
|
||||||
|
<string>Mac Mini (2009)</string>
|
||||||
|
<key>PowerBook2,1</key>
|
||||||
|
<string>iBook G3</string>
|
||||||
|
<key>PowerBook2,2</key>
|
||||||
|
<string>iBook G3 (FireWire)</string>
|
||||||
|
<key>PowerBook2,3</key>
|
||||||
|
<string>iBook G3</string>
|
||||||
|
<key>PowerBook2,4</key>
|
||||||
|
<string>iBook G3</string>
|
||||||
|
<key>PowerBook4,1</key>
|
||||||
|
<string>iBook G3 (Dual USB 2001)</string>
|
||||||
|
<key>PowerBook4,2</key>
|
||||||
|
<string>iBook G3 (16MB VRAM)</string>
|
||||||
|
<key>PowerBook4,3</key>
|
||||||
|
<string>iBook G3 Opaque 16MB VRAM/32MB VRAM 2003)</string>
|
||||||
|
<key>PowerBook6,3</key>
|
||||||
|
<string>iBook G4</string>
|
||||||
|
<key>PowerBook6,5</key>
|
||||||
|
<string>iBook G4 (2004)</string>
|
||||||
|
<key>PowerBook6,7</key>
|
||||||
|
<string>iBook G4 (Mid 2005)</string>
|
||||||
|
<key>PowerBook1,1</key>
|
||||||
|
<string>PowerBook G3</string>
|
||||||
|
<key>PowerBook3,1</key>
|
||||||
|
<string>PowerBook G3 (FireWire)</string>
|
||||||
|
<key>PowerBook3,2</key>
|
||||||
|
<string>PowerBook G4</string>
|
||||||
|
<key>PowerBook3,3</key>
|
||||||
|
<string>PowerBook G4 (Gigabit Ethernet)</string>
|
||||||
|
<key>PowerBook3,4</key>
|
||||||
|
<string>PowerBook G4 (DVI)</string>
|
||||||
|
<key>PowerBook3,5</key>
|
||||||
|
<string>PowerBook G4 (1GHz/867MHz)</string>
|
||||||
|
<key>PowerBook5,1</key>
|
||||||
|
<string>PowerBook G4 (17")</string>
|
||||||
|
<key>PowerBook5,2</key>
|
||||||
|
<string>PowerBook G4 (15" FW 800)</string>
|
||||||
|
<key>PowerBook5,3</key>
|
||||||
|
<string>PowerBook G4 (17" 1.33GHz)</string>
|
||||||
|
<key>PowerBook5,4</key>
|
||||||
|
<string>PowerBook G4 (15" 1.5/1.33GHz)</string>
|
||||||
|
<key>PowerBook5,5</key>
|
||||||
|
<string>PowerBook G4 (17" 1.5GHz)</string>
|
||||||
|
<key>PowerBook5,6</key>
|
||||||
|
<string>PowerBook G4 (15" 1.67GHz/1.5GHz)</string>
|
||||||
|
<key>PowerBook5,7</key>
|
||||||
|
<string>PowerBook G4 (17" 1.67GHz)</string>
|
||||||
|
<key>PowerBook5,8</key>
|
||||||
|
<string>PowerBook G4 (Double layer SD 15")</string>
|
||||||
|
<key>PowerBook5,9</key>
|
||||||
|
<string>PowerBook G4 (Double layer SD 17")</string>
|
||||||
|
<key>PowerBook6,1</key>
|
||||||
|
<string>PowerBook G4 (12")</string>
|
||||||
|
<key>PowerBook6,2</key>
|
||||||
|
<string>PowerBook G4 (12" DVI)</string>
|
||||||
|
<key>PowerBook6,4</key>
|
||||||
|
<string>PowerBook G4 (12" 1.33GHz)</string>
|
||||||
|
<key>PowerBook6,8</key>
|
||||||
|
<string>PowerBook G4 (12" 1.5GHz)</string>
|
||||||
|
<key>MacBook1,1</key>
|
||||||
|
<string>MacBook (Core Duo)</string>
|
||||||
|
<key>MacBook2,1</key>
|
||||||
|
<string>MacBook (Core 2 Duo)</string>
|
||||||
|
<key>MacBook4,1</key>
|
||||||
|
<string>MacBook (2008)</string>
|
||||||
|
<key>MacBook5,1</key>
|
||||||
|
<string>MacBook (Unibody)</string>
|
||||||
|
<key>MacBook5,2</key>
|
||||||
|
<string>MacBook (White 2009)</string>
|
||||||
|
<key>MacBook6,1</key>
|
||||||
|
<string>MacBook (White Unibody)</string>
|
||||||
|
<key>MacBookAir1,1</key>
|
||||||
|
<string>MacBook Air (2008)</string>
|
||||||
|
<key>MacBookPro1,1</key>
|
||||||
|
<string>MacBook Pro Core Duo (15")</string>
|
||||||
|
<key>MacBookPro1,2</key>
|
||||||
|
<string>MacBook Pro Core Duo (17")</string>
|
||||||
|
<key>MacBookPro2,1</key>
|
||||||
|
<string>MacBook Pro Core 2 Duo (17")</string>
|
||||||
|
<key>MacBookPro2,2</key>
|
||||||
|
<string>MacBook Pro Core 2 Duo (15")</string>
|
||||||
|
<key>MacBookPro3,1</key>
|
||||||
|
<string>MacBook Pro Core 2 Duo (15" LED Core 2 Duo)</string>
|
||||||
|
<key>MacBookPro3,2</key>
|
||||||
|
<string>MacBook Pro Core 2 Duo (17" HD Core 2 Duo)</string>
|
||||||
|
<key>MacBookPro4,1</key>
|
||||||
|
<string>MacBook Pro (Core 2 Duo 2008)</string>
|
||||||
|
<key>MacBookPro5,1</key>
|
||||||
|
<string>MacBook Pro (Unibody)</string>
|
||||||
|
<key>MacBookPro5,2</key>
|
||||||
|
<string>MacBook Pro (Unibody 17" 2009)</string>
|
||||||
|
<key>MacBookPro5,3</key>
|
||||||
|
<string>MacBook Pro (Unibody 15" 2009)</string>
|
||||||
|
<key>MacBookPro5,4</key>
|
||||||
|
<string>MacBook Pro (Unibody 15" 2009)</string>
|
||||||
|
<key>MacBookPro5,5</key>
|
||||||
|
<string>MacBook Pro (Unibody 13" 2009)</string>
|
||||||
|
<key>PowerMac1,1</key>
|
||||||
|
<string>Power Macintosh G3 (Blue & White)</string>
|
||||||
|
<key>PowerMac1,2</key>
|
||||||
|
<string>Power Macintosh G4 (PCI Graphics)</string>
|
||||||
|
<key>PowerMac11,2</key>
|
||||||
|
<string>Power Macintosh G5 (2005)</string>
|
||||||
|
<key>PowerMac3,1</key>
|
||||||
|
<string>Power Macintosh G4 (AGP Graphics)</string>
|
||||||
|
<key>PowerMac3,2</key>
|
||||||
|
<string>Power Macintosh G4 (AGP Graphics)</string>
|
||||||
|
<key>PowerMac3,3</key>
|
||||||
|
<string>Power Macintosh G4 (Gigabit Ethernet)</string>
|
||||||
|
<key>PowerMac3,4</key>
|
||||||
|
<string>Power Macintosh G4 (Digital Audio)</string>
|
||||||
|
<key>PowerMac3,5</key>
|
||||||
|
<string>Power Macintosh G4 (Quick Silver)</string>
|
||||||
|
<key>PowerMac3,6</key>
|
||||||
|
<string>Power Macintosh G4 (Mirrored Drive Door)</string>
|
||||||
|
<key>PowerMac5,1</key>
|
||||||
|
<string>Power Macintosh G4 Cube</string>
|
||||||
|
<key>PowerMac7,2</key>
|
||||||
|
<string>Power Macintosh G5</string>
|
||||||
|
<key>PowerMac7,3</key>
|
||||||
|
<string>Power Macintosh G5</string>
|
||||||
|
<key>PowerMac9,1</key>
|
||||||
|
<string>Power Macintosh G5 (2005)</string>
|
||||||
|
<key>MacPro1,1</key>
|
||||||
|
<string>Mac Pro (Four-Core)</string>
|
||||||
|
<key>MacPro2,1</key>
|
||||||
|
<string>Mac Pro (Eight-Core)</string>
|
||||||
|
<key>MacPro3,1</key>
|
||||||
|
<string>Mac Pro (2008 4/8 core "Harpertown")</string>
|
||||||
|
<key>MacPro4,1</key>
|
||||||
|
<string>Mac Pro (2009 "Nehalem")</string>
|
||||||
|
<key>RackMac1,1</key>
|
||||||
|
<string>Xserve G4</string>
|
||||||
|
<key>RackMac1,2</key>
|
||||||
|
<string>Xserve G4 (Slot-loading cluster node)</string>
|
||||||
|
<key>RackMac3,1</key>
|
||||||
|
<string>Xserve G5</string>
|
||||||
|
<key>Xserve1,1</key>
|
||||||
|
<string>Xserve (Intel Xeon)</string>
|
||||||
|
<key>Xserve2,1</key>
|
||||||
|
<string>Xserve (2008 Quad-Core)</string>
|
||||||
|
<key>Xserve3,1</key>
|
||||||
|
<string>Xserve (2009 Quad-Core)</string>
|
||||||
|
</dict>
|
||||||
|
</plist>
|
Binary file not shown.
BIN
Frameworks/GeckoReporter.framework/Versions/A/Resources/es.lproj/MGMBugWindow.nib
generated
Normal file
BIN
Frameworks/GeckoReporter.framework/Versions/A/Resources/es.lproj/MGMBugWindow.nib
generated
Normal file
Binary file not shown.
BIN
Frameworks/GeckoReporter.framework/Versions/A/Resources/es.lproj/MGMContactWindow.nib
generated
Normal file
BIN
Frameworks/GeckoReporter.framework/Versions/A/Resources/es.lproj/MGMContactWindow.nib
generated
Normal file
Binary file not shown.
BIN
Frameworks/GeckoReporter.framework/Versions/A/Resources/es.lproj/MGMReportWindow.nib
generated
Normal file
BIN
Frameworks/GeckoReporter.framework/Versions/A/Resources/es.lproj/MGMReportWindow.nib
generated
Normal file
Binary file not shown.
BIN
Frameworks/GeckoReporter.framework/Versions/A/Resources/es.lproj/MainMenu.nib
generated
Normal file
BIN
Frameworks/GeckoReporter.framework/Versions/A/Resources/es.lproj/MainMenu.nib
generated
Normal file
Binary file not shown.
Binary file not shown.
BIN
Frameworks/GeckoReporter.framework/Versions/A/Resources/pt_PT.lproj/MGMBugWindow.nib
generated
Normal file
BIN
Frameworks/GeckoReporter.framework/Versions/A/Resources/pt_PT.lproj/MGMBugWindow.nib
generated
Normal file
Binary file not shown.
BIN
Frameworks/GeckoReporter.framework/Versions/A/Resources/pt_PT.lproj/MGMContactWindow.nib
generated
Normal file
BIN
Frameworks/GeckoReporter.framework/Versions/A/Resources/pt_PT.lproj/MGMContactWindow.nib
generated
Normal file
Binary file not shown.
BIN
Frameworks/GeckoReporter.framework/Versions/A/Resources/pt_PT.lproj/MGMReportWindow.nib
generated
Normal file
BIN
Frameworks/GeckoReporter.framework/Versions/A/Resources/pt_PT.lproj/MGMReportWindow.nib
generated
Normal file
Binary file not shown.
BIN
Frameworks/GeckoReporter.framework/Versions/A/Resources/pt_PT.lproj/MainMenu.nib
generated
Normal file
BIN
Frameworks/GeckoReporter.framework/Versions/A/Resources/pt_PT.lproj/MainMenu.nib
generated
Normal file
Binary file not shown.
1
Frameworks/GeckoReporter.framework/Versions/Current
Symbolic link
1
Frameworks/GeckoReporter.framework/Versions/Current
Symbolic link
@ -0,0 +1 @@
|
|||||||
|
A
|
1
Frameworks/MGMUsers.framework/Headers
Symbolic link
1
Frameworks/MGMUsers.framework/Headers
Symbolic link
@ -0,0 +1 @@
|
|||||||
|
Versions/Current/Headers
|
1
Frameworks/MGMUsers.framework/MGMUsers
Symbolic link
1
Frameworks/MGMUsers.framework/MGMUsers
Symbolic link
@ -0,0 +1 @@
|
|||||||
|
Versions/Current/MGMUsers
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user