/* HBSubtitlesTrack.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 "HBSubtitlesTrack.h" #import "HBCodingUtilities.h" #import "HBTitle.h" #include "handbrake/common.h" #include "handbrake/lang.h" #define CHAR_CODE_DEFAULT_INDEX 28 static NSArray *charEncodingArray = nil; static NSArray *_languagesArray = nil; @interface HBSubtitlesTrack () @property (nonatomic, readwrite) BOOL validating; @end @implementation HBSubtitlesTrack + (void)initialize { if ([HBSubtitlesTrack class] == self) { // populate the charCodeArray. charEncodingArray = @[@"ANSI_X3.4-1968", @"ANSI_X3.4-1986", @"ANSI_X3.4", @"ANSI_X3.110-1983", @"ANSI_X3.110", @"ASCII", @"ECMA-114", @"ECMA-118", @"ECMA-128", @"ECMA-CYRILLIC", @"IEC_P27-1", @"ISO-8859-1", @"ISO-8859-2", @"ISO-8859-3", @"ISO-8859-4", @"ISO-8859-5", @"ISO-8859-6", @"ISO-8859-7", @"ISO-8859-8", @"ISO-8859-9", @"ISO-8859-9E", @"ISO-8859-10", @"ISO-8859-11", @"ISO-8859-13", @"ISO-8859-14", @"ISO-8859-15", @"ISO-8859-16", @"UTF-7", @"UTF-8", @"UTF-16", @"UTF-16LE", @"UTF-16BE", @"UTF-32", @"UTF-32LE", @"UTF-32BE"]; // populate the languages array NSMutableArray *languages = [[NSMutableArray alloc] init]; for (const iso639_lang_t *lang = lang_get_next(NULL); lang != NULL; lang = lang_get_next(lang)) { [languages addObject:@(lang->eng_name)]; } _languagesArray = [languages copy]; } } - (instancetype)initWithTrackIdx:(NSUInteger)index container:(int)container dataSource:(id)dataSource delegate:(id)delegate { self = [super init]; if (self) { _dataSource = dataSource; _sourceTrackIdx = index; _container = container; _title = [dataSource sourceTrackAtIndex:_sourceTrackIdx].title; [self validateSettings]; _delegate = delegate; } return self; } - (void)validateSettings { HBTitleSubtitlesTrack *sourceTrack = [_dataSource sourceTrackAtIndex:_sourceTrackIdx]; self.type = sourceTrack.type; if (!hb_subtitle_can_burn(_type)) { // the source track cannot be burned in, so uncheck the widget self.burnedIn = NO; } if (!hb_subtitle_can_force(_type)) { // the source track does not support forced flags, so uncheck the widget self.forcedOnly = NO; } if (!hb_subtitle_can_pass(self.type, self.container)) { self.burnedIn = YES; } if (_sourceTrackIdx == 1) { self.forcedOnly = YES; self.burnedIn = YES; } // check to see if we are an srt, in which case set our file path and source track type kvp's if (_type == IMPORTSRT || _type == IMPORTSSA) { self.fileURL = sourceTrack.fileURL; self.isoLanguage = @"eng"; self.charCode = charEncodingArray[CHAR_CODE_DEFAULT_INDEX]; } else { self.fileURL = nil; self.isoLanguage = nil; self.charCode = nil; } } #pragma mark - Track properties - (void)setSourceTrackIdx:(NSUInteger)sourceTrackIdx { if (sourceTrackIdx != _sourceTrackIdx) { [[self.undo prepareWithInvocationTarget:self] setSourceTrackIdx:_sourceTrackIdx]; } NSUInteger oldIdx = _sourceTrackIdx; _sourceTrackIdx = sourceTrackIdx; if (!(self.undo.isUndoing || self.undo.isRedoing)) { self.title = [self.dataSource sourceTrackAtIndex:_sourceTrackIdx].title; [self validateSettings]; if (oldIdx != sourceTrackIdx) { [self.delegate track:self didChangeSourceFrom:oldIdx]; } } } - (void)setType:(int)type { if (type != _type) { [[self.undo prepareWithInvocationTarget:self] setType:_type]; } _type = type; } - (void)setForcedOnly:(BOOL)forcedOnly { if (forcedOnly != _forcedOnly) { [[self.undo prepareWithInvocationTarget:self] setForcedOnly:_forcedOnly]; } _forcedOnly = forcedOnly; } - (void)setBurnedIn:(BOOL)burnedIn { if (burnedIn != _burnedIn) { [[self.undo prepareWithInvocationTarget:self] setBurnedIn:_burnedIn]; } _burnedIn = burnedIn; if (!(self.undo.isUndoing || self.undo.isRedoing) || !self.validating) { self.validating = YES; if (_burnedIn) { [self.delegate didSetBurnedInOption:self]; self.def = NO; }; self.validating = NO; } } - (BOOL)validateBurnedIn:(id *)ioValue error:(NSError * __autoreleasing *)outError { BOOL retval = YES; if (nil != *ioValue) { BOOL value = [*ioValue boolValue]; if (value && [self.delegate canSetBurnedInOption:self] == NO) { *ioValue = @NO; } } return retval; } - (void)setDef:(BOOL)def { if (def != _def) { [[self.undo prepareWithInvocationTarget:self] setDef:_def]; } _def = def; if (!(self.undo.isUndoing || self.undo.isRedoing) || !self.validating) { self.validating = YES; if (_def) { [self.delegate didSetDefaultOption:self]; self.burnedIn = NO; } self.validating = NO; } } #pragma mark - External subtitles track only properties - (void)setFileURL:(NSURL *)fileURL { if (fileURL != _fileURL) { [[self.undo prepareWithInvocationTarget:self] setFileURL:_fileURL]; } _fileURL = [fileURL copy]; } - (void)setIsoLanguage:(NSString *)isoLanguage { if (_isoLanguage != isoLanguage || (_isoLanguage && ![isoLanguage isEqualToString:_isoLanguage])) { [[self.undo prepareWithInvocationTarget:self] setIsoLanguage:_isoLanguage]; } _isoLanguage = [isoLanguage copy]; } - (void)setCharCode:(NSString *)charCode { if (_charCode != charCode || (_charCode && ![charCode isEqualToString:_charCode])) { [[self.undo prepareWithInvocationTarget:self] setCharCode:_charCode]; } _charCode = [charCode copy]; } - (void)setOffset:(int)offset { if (offset != _offset) { [[self.undo prepareWithInvocationTarget:self] setOffset:_offset]; } _offset = offset; } #pragma mark - - (NSArray *)sourceTracksArray { return [self.dataSource sourceTracksArray]; } - (BOOL)isExternal { return self.type == IMPORTSRT || self.type == IMPORTSSA; } - (BOOL)isEnabled { return self.sourceTrackIdx != 0; } - (BOOL)isForcedSupported { return hb_subtitle_can_force(self.type) && self.isEnabled; } - (BOOL)canPassthru { return hb_subtitle_can_pass(self.type, self.container) && self.isEnabled; } - (NSArray *)languages { return _languagesArray; } - (NSArray *)encodings { return charEncodingArray; } #pragma mark - KVO + (NSSet *)keyPathsForValuesAffectingValueForKey:(NSString *)key { NSSet *retval = nil; if ([key isEqualToString: @"isExternal"]) { retval = [NSSet setWithObjects: @"type", nil]; } else if ([key isEqualToString: @"isEnabled"]) { retval = [NSSet setWithObjects: @"sourceTrackIdx", nil]; } else if ([key isEqualToString: @"canPassthru"] || [key isEqualToString: @"isForcedSupported"] ) { retval = [NSSet setWithObjects: @"isEnabled", @"sourceTrackIdx", @"container", nil]; } else { retval = [NSSet set]; } return retval; } - (void)setNilValueForKey:(NSString *)key { if ([key isEqualToString:@"offset"]) { [self setValue:@0 forKey:key]; } } #pragma mark - NSCopying - (instancetype)copyWithZone:(NSZone *)zone { HBSubtitlesTrack *copy = [[[self class] alloc] init]; if (copy) { copy->_sourceTrackIdx = _sourceTrackIdx; copy->_type = _type; copy->_container = _container; copy->_forcedOnly = _forcedOnly; copy->_burnedIn = _burnedIn; copy->_def = _def; copy->_fileURL = [_fileURL copy]; copy->_isoLanguage = [_isoLanguage copy]; copy->_charCode = [_charCode copy]; copy->_offset = _offset; copy->_title = [_title copy]; } return copy; } #pragma mark - NSCoding + (BOOL)supportsSecureCoding { return YES; } - (void)encodeWithCoder:(NSCoder *)coder { [coder encodeInt:2 forKey:@"HBSubtitlesTrackVersion"]; encodeInteger(_sourceTrackIdx); encodeInt(_type); encodeInt(_container); encodeBool(_forcedOnly); encodeBool(_burnedIn); encodeBool(_def); encodeObject(_title); encodeObject(_fileURL); encodeObject(_isoLanguage); encodeObject(_charCode); encodeInt(_offset); } - (instancetype)initWithCoder:(NSCoder *)decoder { self = [super init]; decodeInteger(_sourceTrackIdx); if (_sourceTrackIdx < 0) { goto fail; } decodeInt(_type); if (_type < VOBSUB || _type > DVBSUB) { goto fail; } decodeInt(_container); if (_container != HB_MUX_MP4 && _container != HB_MUX_MKV && _container != HB_MUX_WEBM) { goto fail; } decodeBool(_forcedOnly); decodeBool(_burnedIn); decodeBool(_def); decodeObject(_title, NSString); decodeObject(_fileURL, NSURL); decodeObject(_isoLanguage, NSString); decodeObject(_charCode, NSString); decodeInt(_offset); return self; fail: return nil; } @end #pragma mark - Value Transformers @implementation HBIsoLanguageTransformer + (Class)transformedValueClass { return [NSString class]; } - (id)transformedValue:(id)value { if ([value length]) { iso639_lang_t *lang = lang_for_code2([value UTF8String]); if (lang) { return @(lang->eng_name); } } return nil; } + (BOOL)allowsReverseTransformation { return YES; } - (id)reverseTransformedValue:(id)value { if ([value length]) { iso639_lang_t *lang = lang_for_english([value UTF8String]); if (lang) { return @(lang->iso639_2); } } return nil; } @end