/* This file is part of the HandBrake source code. Homepage: . It may be used under the terms of the GNU General Public License. */ #import "HandBrakeXPCService.h" #import "HBOutputRedirect.h" @import HandBrakeKit; static void *HandBrakeXPCServiceContext = &HandBrakeXPCServiceContext; @interface HandBrakeXPCService () @property (nonatomic, readonly) HBCore *core; @property (nonatomic, readonly, copy) HBCoreProgressHandler progressHandler; @property (nonatomic, readonly, copy) HBCoreCompletionHandler completionHandler; @property (nonatomic, readwrite, copy) void (^reply)(HBCoreResult); @property (nonatomic, readonly, weak) NSXPCConnection *connection; @property (nonatomic, readonly) dispatch_queue_t queue; @property (nonatomic, readwrite) NSArray *urls; @end @implementation HandBrakeXPCService - (instancetype)initWithConnection:(NSXPCConnection *)connection { self = [super init]; if (self) { _connection = connection; dispatch_queue_attr_t attr; attr = dispatch_queue_attr_make_with_qos_class(DISPATCH_QUEUE_SERIAL, QOS_CLASS_DEFAULT, 0); _queue = dispatch_queue_create("fr.handbrake.CoreQueue", attr); // Add ourself as stderr/stdout listener [HBOutputRedirect.stderrRedirect addListener:self queue:_queue]; [HBOutputRedirect.stdoutRedirect addListener:self queue:_queue]; } return self; } - (void)setDVDNav:(BOOL)enabled { dispatch_sync(_queue, ^{ [HBCore setDVDNav:enabled]; }); } - (void)setUpWithLogLevel:(NSInteger)level name:(NSString *)name { [HBCore initGlobal]; _core = [[HBCore alloc] initWithLogLevel:level queue:_queue]; _core.name = name; _core.automaticallyPreventSleep = NO; // Completion handler void (^completionHandler)(HBCoreResult result) = ^(HBCoreResult result) { self->_progressHandler = nil; self.reply(result); self.reply = nil; }; _completionHandler = completionHandler; // Set up observers [self.core addObserver:self forKeyPath:@"state" options:NSKeyValueObservingOptionNew | NSKeyValueObservingOptionInitial context:HandBrakeXPCServiceContext]; [NSProcessInfo.processInfo disableAutomaticTermination:@"Core started"]; } - (void)tearDown { _core = nil; [HBCore closeGlobal]; [NSProcessInfo.processInfo enableSuddenTermination]; } - (void)setLogLevel:(NSInteger)logLevel { dispatch_sync(_queue, ^{ self.core.logLevel = logLevel; }); } - (void)provideResourceAccessWithBookmarks:(NSArray *)bookmarks { dispatch_sync(_queue, ^{ NSMutableArray *urls = [NSMutableArray array]; for (NSData *bookmark in bookmarks) { NSURL *url = [NSURL URLByResolvingBookmarkData:bookmark options:0 relativeToURL:nil bookmarkDataIsStale:NULL error:NULL]; if (url) { [urls addObject:url]; } } self.urls = urls; }); } - (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context { if (context == HandBrakeXPCServiceContext) { [self.connection.remoteObjectProxy updateState:self.core.state]; } else { [super observeValueForKeyPath:keyPath ofObject:object change:change context:context]; } } - (void)scanURL:(NSURL *)url titleIndex:(NSUInteger)index previews:(NSUInteger)previewsNum minDuration:(NSUInteger)seconds keepPreviews:(BOOL)keepPreviews withReply:(void (^)(HBCoreResult))reply { dispatch_sync(_queue, ^{ void (^progressHandler)(HBState state, HBProgress progress, NSString *info) = ^(HBState state, HBProgress progress, NSString *info) { [self.connection.remoteObjectProxy updateProgress:progress.percent hours:progress.hours minutes:progress.minutes seconds:progress.seconds state:state info:info]; }; self->_progressHandler = progressHandler; self.reply = reply; [self.core scanURL:url titleIndex:index previews:previewsNum minDuration:seconds keepPreviews:keepPreviews progressHandler:self.progressHandler completionHandler:self.completionHandler]; }); } - (void)cancelScan { dispatch_sync(_queue, ^{ [self.core cancelScan]; }); } - (void)encodeJob:(HBJob *)job withReply:(void (^)(HBCoreResult))reply { dispatch_sync(_queue, ^{ void (^progressHandler)(HBState state, HBProgress progress, NSString *info) = ^(HBState state, HBProgress progress, NSString *info) { [self.connection.remoteObjectProxy updateProgress:progress.percent hours:progress.hours minutes:progress.minutes seconds:progress.seconds state:state info:info]; }; self->_progressHandler = progressHandler; self.reply = reply; // Reset the title in the job. job.title = self.core.titles.firstObject; [self.core encodeJob:job progressHandler:self.progressHandler completionHandler:self.completionHandler]; }); } - (void)cancelEncode { dispatch_sync(_queue, ^{ [self.core cancelEncode]; }); } - (void)pauseEncode { dispatch_sync(_queue, ^{ [self.core pause]; }); } - (void)resumeEncode { dispatch_sync(_queue, ^{ [self.core resume]; }); } - (void)redirect:(nonnull NSString *)text type:(HBRedirectType)type { if (type == HBRedirectTypeOutput) { [self.connection.remoteObjectProxy forwardError:text]; } else { [self.connection.remoteObjectProxy forwardOutput:text]; } } @end