/* HBPresets.m $ This file is part of the HandBrake source code. Homepage: . It may be used under the terms of the GNU General Public License. */ #import "HBPresetsManager.h" #import "HBPreset.h" #import "HBUtilities.h" #import "NSJSONSerialization+HBAdditions.h" #include "preset.h" NSString *HBPresetsChangedNotification = @"HBPresetsChangedNotification"; @interface HBPresetsManager () @property (nonatomic, readonly, copy) NSURL *fileURL; @end @implementation HBPresetsManager - (instancetype)init { self = [super init]; if (self) { // Init the root of the tree, it won't never be shown in the UI _root = [[HBPreset alloc] initWithFolderName:@"Root" builtIn:YES]; _root.delegate = self; } return self; } - (instancetype)initWithURL:(NSURL *)url { self = [self init]; if (self) { _fileURL = [url copy]; [self loadPresetsFromURL:url]; } return self; } #pragma mark - HBTreeNode delegate - (void)nodeDidChange:(id)node { [[NSNotificationCenter defaultCenter] postNotificationName:HBPresetsChangedNotification object:nil]; } - (void)treeDidRemoveNode:(id)node { if (node == self.defaultPreset) { // Select a new default preset [self selectNewDefault]; } } #pragma mark - Load/Save /** * Loads the old presets format (0.10 and earlier) plist * * @param url the url of the plist */ - (void)loadOldPresetsFromURL:(NSURL *)url { HBPreset *oldPresets = [[HBPreset alloc] initWithContentsOfURL:url]; for (HBPreset *preset in oldPresets.children) { [self.root.children addObject:preset]; } } - (BOOL)checkIfOutOfDate:(NSDictionary *)dict { int major, minor, micro; hb_presets_current_version(&major, &minor, µ); if (major != [dict[@"VersionMajor"] intValue] || minor != [dict[@"VersionMinor"] intValue] || micro != [dict[@"VersionMicro"] intValue]) { return YES; } return NO; } - (BOOL)loadPresetsFromURL:(NSURL *)url { NSData *presetData = [[NSData alloc] initWithContentsOfURL:url]; // Try to load to load the old presets file // if the new one is empty if (presetData == nil) { [self loadOldPresetsFromURL:[url.URLByDeletingPathExtension URLByAppendingPathExtension:@"plist"]]; [self generateBuiltInPresets]; } else { const char *json = [[NSString alloc] initWithData:presetData encoding:NSUTF8StringEncoding].UTF8String; char *cleanedJson = hb_presets_clean_json(json); NSDictionary *presetsDict = [NSJSONSerialization HB_JSONObjectWithUTF8String:cleanedJson options:0 error:NULL]; if ([self checkIfOutOfDate:presetsDict]) { char *updatedJson = hb_presets_import_json(cleanedJson); presetsDict = [NSJSONSerialization HB_JSONObjectWithUTF8String:updatedJson options:0 error:NULL]; free(updatedJson); } free(cleanedJson); for (NSDictionary *child in presetsDict[@"PresetList"]) { [self.root.children addObject:[[HBPreset alloc] initWithDictionary:child]]; } if ([self checkIfOutOfDate:presetsDict]) { [self generateBuiltInPresets]; } } // If the preset list contains no leaf, // add back the built in presets. __block BOOL leafFound = NO; [self.root enumerateObjectsUsingBlock:^(id obj, NSIndexPath *idx, BOOL *stop) { if ([obj isLeaf]) { leafFound = YES; *stop = YES; } }]; if (!leafFound) { [self generateBuiltInPresets]; } [self selectNewDefault]; return YES; } - (BOOL)savePresetsToURL:(NSURL *)url { return [self.root writeToURL:url atomically:YES format:HBPresetFormatJson removeRoot:YES]; } - (BOOL)savePresets { return [self savePresetsToURL:self.fileURL]; } #pragma mark - Presets Management - (void)addPreset:(HBPreset *)preset { // Make sure no preset has the default flag enabled. [preset enumerateObjectsUsingBlock:^(id obj, NSIndexPath *idx, BOOL *stop) { [obj setIsDefault:NO]; }]; [self.root insertObject:preset inChildrenAtIndex:[self.root countOfChildren]]; [self savePresets]; } - (void)deletePresetAtIndexPath:(NSIndexPath *)idx { [self.root removeObjectAtIndexPath:idx]; } - (NSIndexPath *)indexPathOfPreset:(HBPreset *)preset { return [self.root indexPathOfObject:preset]; } #pragma mark - Default preset /** * Private method to select a new default after the default preset is deleted * or when the built-in presets are regenerated. */ - (void)selectNewDefault { __block HBPreset *normalPreset = nil; __block HBPreset *firstUserPreset = nil; __block HBPreset *firstBuiltInPreset = nil; __block BOOL defaultAlreadySetted = NO; // Search for a possibile new default preset // Try to use "Normal" or the first user preset. [self.root enumerateObjectsUsingBlock:^(id obj, NSIndexPath *idx, BOOL *stop) { if ([obj isBuiltIn] && [obj isLeaf]) { if ([[obj name] isEqualToString:@"Normal"]) { normalPreset = obj; } if (firstBuiltInPreset == nil) { firstBuiltInPreset = obj; } } else if ([obj isLeaf] && firstUserPreset == nil) { firstUserPreset = obj; *stop = YES; } if ([obj isDefault]) { self.defaultPreset = obj; defaultAlreadySetted = YES; } }]; if (defaultAlreadySetted) { return; } else if (normalPreset) { self.defaultPreset = normalPreset; normalPreset.isDefault = YES; } else if (firstUserPreset) { self.defaultPreset = firstUserPreset; firstUserPreset.isDefault = YES; } else if (firstBuiltInPreset) { self.defaultPreset = firstBuiltInPreset; firstBuiltInPreset.isDefault = YES; } } - (void)setDefaultPreset:(HBPreset *)defaultPreset { if (defaultPreset && defaultPreset.isLeaf) { if (_defaultPreset) { _defaultPreset.isDefault = NO; } defaultPreset.isDefault = YES; _defaultPreset = defaultPreset; } } #pragma mark - Built In Generation /** * Built-in preset folders at the root of the hierarchy * * Note: the built-in presets will *not* sort themselves alphabetically, * so they will appear in the order you create them. */ - (void)generateBuiltInPresets { // Load the built-in presets from libhb. const char *presets = hb_presets_builtin_get_json(); NSData *presetsData = [NSData dataWithBytes:presets length:strlen(presets)]; NSError *error = nil; NSArray *presetsArray = [NSJSONSerialization JSONObjectWithData:presetsData options:NSJSONReadingAllowFragments error:&error]; if (presetsArray.count == 0) { [HBUtilities writeToActivityLog:"failed to update built-in presets"]; if (error) { [HBUtilities writeToActivityLog:"Error raised:\n%s", error.localizedFailureReason]; } } else { [self deleteBuiltInPresets]; for (NSDictionary *child in presetsArray.reverseObjectEnumerator) { HBPreset *preset = [[HBPreset alloc] initWithDictionary:child]; [self.root insertObject:preset inChildrenAtIndex:0]; } // set a new Default preset [self selectNewDefault]; [HBUtilities writeToActivityLog: "built-in presets updated"]; } } - (void)deleteBuiltInPresets { [self willChangeValueForKey:@"root"]; NSMutableArray *nodeToRemove = [[NSMutableArray alloc] init]; for (HBPreset *node in self.root.children) { if (node.isBuiltIn) { [nodeToRemove addObject:node]; } } [self.root.children removeObjectsInArray:nodeToRemove]; [self didChangeValueForKey:@"root"]; } @end