/* This file is part of the HandBrake source code. Homepage: . It may be used under the terms of the GNU General Public License. */ #import "HBRemoteCore.h" #import "HBRemoteCoreProtocol.h" #import "HBPreferencesKeys.h" @import HandBrakeKit; @interface HBRemoteCore () @property (nonatomic, readonly) NSXPCConnection *connection; @property (nonatomic, readonly) id proxy; @property (nonatomic, readwrite) HBState state; @property (nonatomic, readonly) NSInteger level; @property (nonatomic, readonly, copy) NSString *name; @property (nonatomic, readonly, copy) NSString *serviceName; @property (nonatomic, readwrite, copy) HBCoreProgressHandler progressHandler; @property (nonatomic, readwrite, copy) HBCoreCompletionHandler completionHandler; @end @implementation HBRemoteCore - (instancetype)init { self = [super init]; if (self) { _state = HBStateIdle; _stdoutRedirect = HBRedirect.stdoutRedirect; _stderrRedirect = HBRedirect.stderrRedirect; _level = 1; _name = @"HandBrakeXPC"; _serviceName = @"fr.handbrake.HandBrakeXPCService"; } return self; } - (instancetype)initWithLogLevel:(NSInteger)level name:(NSString *)name serviceName:(NSString *)serviceName { self = [self init]; if (self) { _level = level; _name = [name copy]; _serviceName = [serviceName copy]; } return self; } - (void)connect { _connection = [[NSXPCConnection alloc] initWithServiceName:self.serviceName]; _connection.remoteObjectInterface = [NSXPCInterface interfaceWithProtocol:@protocol(HBRemoteCoreProtocol)]; _connection.exportedInterface = [NSXPCInterface interfaceWithProtocol:@protocol(HBRemoteProgressProtocol)]; _connection.exportedObject = self; __weak HBRemoteCore *weakSelf = self; _connection.interruptionHandler = ^{ dispatch_sync(dispatch_get_main_queue(), ^{ [weakSelf handleInterruption]; }); }; _connection.invalidationHandler = ^{ dispatch_sync(dispatch_get_main_queue(), ^{ [weakSelf forwardError:@"XPC: Service connection was invalidated\n"]; }); }; _proxy = [_connection remoteObjectProxyWithErrorHandler:^(NSError * _Nonnull error) { dispatch_sync(dispatch_get_main_queue(), ^{ [self forwardError:@"XPC: Service did report an error\n"]; [HBUtilities writeErrorToActivityLog:error]; }); }]; [_connection resume]; [_proxy setDVDNav:[NSUserDefaults.standardUserDefaults boolForKey:HBUseDvdNav]]; [_proxy setUpWithLogLevel:self.level name:self.name]; [_proxy setAutomaticallyPreventSleep:self.automaticallyPreventSleep]; } - (void)invalidate { [[_connection synchronousRemoteObjectProxyWithErrorHandler:^(NSError * _Nonnull error) {}] tearDown]; [_connection invalidate]; _connection = nil; _proxy = nil; } - (void)handleInterruption { [_proxy setDVDNav:[NSUserDefaults.standardUserDefaults boolForKey:HBUseDvdNav]]; [_proxy setUpWithLogLevel:self.level name:self.name]; [_proxy setAutomaticallyPreventSleep:self.automaticallyPreventSleep]; HBCoreCompletionHandler handler = self.completionHandler; self.progressHandler = nil; self.completionHandler = nil; self.state = HBStateIdle; if (handler) { handler(HBCoreResultFailed); } [self forwardError:@"XPC: Service did crash\n"]; } - (void)updateState:(HBState)state { dispatch_sync(dispatch_get_main_queue(), ^{ self.state = state; }); } - (void)setLogLevel:(NSInteger)logLevel { _logLevel = logLevel; [_proxy setLogLevel:logLevel]; } - (void)setAutomaticallyPreventSleep:(BOOL)automaticallyPreventSleep { _automaticallyPreventSleep = automaticallyPreventSleep; [_proxy setAutomaticallyPreventSleep:automaticallyPreventSleep]; } - (void)allowSleep { [_proxy allowSleep]; } - (void)preventSleep { [_proxy preventSleep]; } - (void)scanURL:(NSURL *)url titleIndex:(NSUInteger)index previews:(NSUInteger)previewsNum minDuration:(NSUInteger)seconds keepPreviews:(BOOL)keepPreviews progressHandler:(nonnull HBCoreProgressHandler)progressHandler completionHandler:(nonnull HBCoreCompletionHandler)completionHandler { if (!_connection) { [self connect]; } #ifdef __SANDBOX_ENABLED__ __block HBSecurityAccessToken *token = [HBSecurityAccessToken tokenWithObject:url]; NSData *bookmark = [url bookmarkDataWithOptions:0 includingResourceValuesForKeys:nil relativeToURL:nil error:NULL]; if (bookmark) { [_proxy provideResourceAccessWithBookmarks:@[bookmark]]; } #endif self.progressHandler = progressHandler; self.completionHandler = completionHandler; self.state = HBStateScanning; __weak HBRemoteCore *weakSelf = self; [_proxy scanURL:url titleIndex:index previews:previewsNum minDuration:seconds keepPreviews:keepPreviews withReply:^(HBCoreResult result) { dispatch_sync(dispatch_get_main_queue(), ^{ HBCoreCompletionHandler handler = weakSelf.completionHandler; weakSelf.completionHandler = nil; weakSelf.progressHandler = nil; #ifdef __SANDBOX_ENABLED__ token = nil; #endif handler(result); }); }]; } - (void)cancelScan { [_proxy cancelScan]; } - (void)encodeJob:(HBJob *)job progressHandler:(HBCoreProgressHandler)progressHandler completionHandler:(HBCoreCompletionHandler)completionHandler { #ifdef __SANDBOX_ENABLED__ __block HBSecurityAccessToken *token = [HBSecurityAccessToken tokenWithObject:job]; NSMutableArray *bookmarks = [NSMutableArray array]; for (HBSubtitlesTrack *track in job.subtitles.tracks) { if (track.fileURL) { NSData *subtitlesBookmark = [track.fileURL bookmarkDataWithOptions:0 includingResourceValuesForKeys:nil relativeToURL:nil error:NULL]; if (subtitlesBookmark) { [bookmarks addObject:subtitlesBookmark]; } } } NSData *bookmark = [job.outputURL bookmarkDataWithOptions:0 includingResourceValuesForKeys:nil relativeToURL:nil error:NULL]; if (bookmark) { [bookmarks addObject:bookmark]; } [_proxy provideResourceAccessWithBookmarks:bookmarks]; #endif self.progressHandler = progressHandler; self.completionHandler = completionHandler; self.state = HBStateWorking; __weak HBRemoteCore *weakSelf = self; [_proxy encodeJob:job withReply:^(HBCoreResult result) { dispatch_sync(dispatch_get_main_queue(), ^{ HBCoreCompletionHandler handler = weakSelf.completionHandler; weakSelf.completionHandler = nil; weakSelf.progressHandler = nil; #ifdef __SANDBOX_ENABLED__ token = nil; #endif handler(result); }); }]; } - (void)cancelEncode { [_proxy cancelEncode]; } - (void)updateProgress:(double)currentProgress hours:(int)hours minutes:(int)minutes seconds:(int)seconds state:(HBState)state info:(NSString *)info { __weak HBRemoteCore *weakSelf = self; dispatch_sync(dispatch_get_main_queue(), ^{ HBProgress progress = {currentProgress , hours, minutes, seconds}; weakSelf.progressHandler(state, progress, info); }); } - (void)forwardOutput:(NSString *)text { [_stdoutRedirect forwardOutput:text]; [HBUtilities writeToActivityLogWithNoHeader:text]; } - (void)forwardError:(NSString *)text { [_stdoutRedirect forwardOutput:text]; [HBUtilities writeToActivityLogWithNoHeader:text]; } - (void)pause { [_proxy pauseEncode]; } - (void)resume { [_proxy resumeEncode]; } @end