diff --git a/LowContrastMode.xm b/LowContrastMode.xm new file mode 100644 index 0000000..54ceadd --- /dev/null +++ b/LowContrastMode.xm @@ -0,0 +1,460 @@ +#import "../YTLitePlus.h" + +// Low Contrast Mode +static int contrastMode() { + return [[NSUserDefaults standardUserDefaults] integerForKey:@"lcm"]; +} +static BOOL lowContrastMode() { + return IsEnabled(@"lowContrastMode_enabled") && contrastMode() == 0; +} +static BOOL customContrastMode() { + return IsEnabled(@"lowContrastMode_enabled") && contrastMode() == 1; +} + +UIColor *lcmHexColor; + +%group gLowContrastMode // Low Contrast Mode v1.6.0 (Compatible with only YouTube v16.46.5-v17.38.10) +%hook UIColor ++ (UIColor *)whiteColor { // Dark Theme Color + return [UIColor colorWithRed: 0.56 green: 0.56 blue: 0.56 alpha: 1.00]; +} ++ (UIColor *)lightTextColor { + return [UIColor colorWithRed: 0.56 green: 0.56 blue: 0.56 alpha: 1.00]; +} ++ (UIColor *)placeholderTextColor { + return [UIColor colorWithRed: 0.56 green: 0.56 blue: 0.56 alpha: 1.00]; +} ++ (UIColor *)labelColor { + return [UIColor colorWithRed: 0.56 green: 0.56 blue: 0.56 alpha: 1.00]; +} ++ (UIColor *)secondaryLabelColor { + return [UIColor colorWithRed: 0.56 green: 0.56 blue: 0.56 alpha: 1.00]; +} ++ (UIColor *)tertiaryLabelColor { + return [UIColor colorWithRed: 0.56 green: 0.56 blue: 0.56 alpha: 1.00]; +} ++ (UIColor *)quaternaryLabelColor { + return [UIColor colorWithRed: 0.56 green: 0.56 blue: 0.56 alpha: 1.00]; +} +%end +%hook YTCommonColorPalette +- (UIColor *)textPrimary { + NSLog(@"LowContrastMode: textPrimary called"); + return self.pageStyle == 1 ? [UIColor whiteColor] : %orig; +} +- (UIColor *)textSecondary { + NSLog(@"LowContrastMode: textSecondary called"); + return self.pageStyle == 1 ? [UIColor whiteColor] : %orig; +} +- (UIColor *)overlayTextPrimary { + NSLog(@"LowContrastMode: overlayTextPrimary called"); + return self.pageStyle == 1 ? [UIColor whiteColor] : %orig; +} +- (UIColor *)overlayTextSecondary { + NSLog(@"LowContrastMode: overlayTextSecondary called"); + return self.pageStyle == 1 ? [UIColor whiteColor] : %orig; +} +- (UIColor *)iconActive { + NSLog(@"LowContrastMode: iconActive called"); + return self.pageStyle == 1 ? [UIColor whiteColor] : %orig; +} +- (UIColor *)iconActiveOther { + NSLog(@"LowContrastMode: iconActiveOther called"); + return self.pageStyle == 1 ? [UIColor whiteColor] : %orig; +} +- (UIColor *)brandIconActive { + NSLog(@"LowContrastMode: brandIconActive called"); + return self.pageStyle == 1 ? [UIColor whiteColor] : %orig; +} +- (UIColor *)staticBrandWhite { + NSLog(@"LowContrastMode: staticBrandWhite called"); + return self.pageStyle == 1 ? [UIColor whiteColor] : %orig; +} +- (UIColor *)overlayIconActiveOther { + NSLog(@"LowContrastMode: overlayIconActiveOther called"); + return self.pageStyle == 1 ? [UIColor whiteColor] : %orig; +} +- (UIColor *)overlayIconInactive { + NSLog(@"LowContrastMode: overlayIconInactive called"); + return self.pageStyle == 1 ? [[UIColor whiteColor] colorWithAlphaComponent:0.7] : %orig; +} +- (UIColor *)overlayIconDisabled { + NSLog(@"LowContrastMode: overlayIconDisabled called"); + return self.pageStyle == 1 ? [[UIColor whiteColor] colorWithAlphaComponent:0.3] : %orig; +} +- (UIColor *)overlayFilledButtonActive { + NSLog(@"LowContrastMode: overlayFilledButtonActive called"); + return self.pageStyle == 1 ? [[UIColor whiteColor] colorWithAlphaComponent:0.2] : %orig; +} +%end +%hook YTColor ++ (UIColor *)white2 { + return [UIColor whiteColor]; +} ++ (UIColor *)white3 { + return [UIColor whiteColor]; +} ++ (UIColor *)white4 { + return [UIColor whiteColor]; +} ++ (UIColor *)white5 { + return [UIColor whiteColor]; +} +%end +%hook QTMColorGroup +- (UIColor *)tint100 { + return [UIColor whiteColor]; +} +- (UIColor *)tint300 { + return [UIColor whiteColor]; +} +- (UIColor *)bodyTextColor { + return [UIColor whiteColor]; +} +- (UIColor *)bodyTextColorOnLighterColor { + return [UIColor whiteColor]; +} +- (UIColor *)bodyTextColorOnRegularColor { + return [UIColor whiteColor]; +} +- (UIColor *)bodyTextColorOnDarkerColor { + return [UIColor whiteColor]; +} +- (UIColor *)bodyTextColorOnAccentColor { + return [UIColor whiteColor]; +} +- (UIColor *)bodyTextColorOnOnBrightAccentColor { + return [UIColor whiteColor]; +} +- (UIColor *)lightBodyTextColor { + return [UIColor whiteColor]; +} +- (UIColor *)buttonBackgroundColor { + return [UIColor whiteColor]; +} +%end +%hook YTQTMButton +- (void)setImage:(UIImage *)image { + UIImage *currentImage = [image imageWithRenderingMode:UIImageRenderingModeAlwaysTemplate]; + [self setTintColor:[UIColor whiteColor]]; + %orig(currentImage); +} +%end +%hook UIExtendedGrayColorSpace +- (void)setTextColor:(UIColor *)textColor { + textColor = [[UIColor whiteColor] colorWithAlphaComponent:1.0]; + %orig(); +} +%end +%hook VideoTitleLabel +- (void)setTextColor:(UIColor *)textColor { + textColor = [UIColor whiteColor]; + %orig(textColor); +} +%end +%hook UILabel ++ (void)load { + @autoreleasepool { + [[UILabel appearance] setTextColor:[UIColor whiteColor]]; + } +} +- (void)setTextColor:(UIColor *)textColor { + %log; + textColor = [UIColor whiteColor]; + %orig(textColor); +} +%end +%hook UITextField +- (void)setTextColor:(UIColor *)textColor { + %log; + textColor = [UIColor whiteColor]; + %orig(textColor); +} +%end +%hook UITextView +- (void)setTextColor:(UIColor *)textColor { + %log; + textColor = [UIColor whiteColor]; + %orig(textColor); +} +%end +%hook UISearchBar +- (void)setTextColor:(UIColor *)textColor { + textColor = [UIColor whiteColor]; + %orig(textColor); +} +%end +%hook UISegmentedControl +- (void)setTitleTextAttributes:(NSDictionary *)attributes forState:(UIControlState)state { + NSMutableDictionary *modifiedAttributes = [NSMutableDictionary dictionaryWithDictionary:attributes]; + [modifiedAttributes setObject:[UIColor whiteColor] forKey:NSForegroundColorAttributeName]; + %orig(modifiedAttributes, state); +} +%end +%hook UIButton +- (void)setTitleColor:(UIColor *)color forState:(UIControlState)state { + color = [UIColor whiteColor]; + %orig(color, state); +} +%end +%hook UIBarButtonItem +- (void)setTitleTextAttributes:(NSDictionary *)attributes forState:(UIControlState)state { + NSMutableDictionary *modifiedAttributes = [NSMutableDictionary dictionaryWithDictionary:attributes]; + [modifiedAttributes setObject:[UIColor whiteColor] forKey:NSForegroundColorAttributeName]; + %orig(modifiedAttributes, state); +} +%end +%hook NSAttributedString +- (instancetype)initWithString:(NSString *)str attributes:(NSDictionary *)attrs { + NSMutableDictionary *modifiedAttributes = [NSMutableDictionary dictionaryWithDictionary:attrs]; + [modifiedAttributes setObject:[UIColor whiteColor] forKey:NSForegroundColorAttributeName]; + return %orig(str, modifiedAttributes); +} +%end +%hook CATextLayer +- (void)setTextColor:(CGColorRef)textColor { + %orig([UIColor whiteColor].CGColor); +} +%end +%hook ASTextNode +- (NSAttributedString *)attributedString { + NSAttributedString *originalAttributedString = %orig; + NSMutableAttributedString *newAttributedString = [originalAttributedString mutableCopy]; + [newAttributedString addAttribute:NSForegroundColorAttributeName value:[UIColor whiteColor] range:NSMakeRange(0, newAttributedString.length)]; + return newAttributedString; +} +%end +%hook ASTextFieldNode +- (void)setTextColor:(UIColor *)textColor { + %orig([UIColor whiteColor]); +} +%end +%hook ASTextView +- (void)setTextColor:(UIColor *)textColor { + %orig([UIColor whiteColor]); +} +%end +%hook ASButtonNode +- (void)setTextColor:(UIColor *)textColor { + %orig([UIColor whiteColor]); +} +%end +%end + +%group gCustomContrastMode // Custom Contrast Mode (Hex Color) +%hook UIColor ++ (UIColor *)whiteColor { + return lcmHexColor; +} ++ (UIColor *)lightTextColor { + return lcmHexColor; +} ++ (UIColor *)placeholderTextColor { + return lcmHexColor; +} ++ (UIColor *)labelColor { + return lcmHexColor; +} ++ (UIColor *)secondaryLabelColor { + return lcmHexColor; +} ++ (UIColor *)tertiaryLabelColor { + return lcmHexColor; +} ++ (UIColor *)quaternaryLabelColor { + return lcmHexColor; +} +%end +%hook YTCommonColorPalette +- (UIColor *)textPrimary { + return self.pageStyle == 1 ? [UIColor whiteColor] : %orig; +} +- (UIColor *)textSecondary { + return self.pageStyle == 1 ? [UIColor whiteColor] : %orig; +} +- (UIColor *)overlayTextPrimary { + return self.pageStyle == 1 ? [UIColor whiteColor] : %orig; +} +- (UIColor *)overlayTextSecondary { + return self.pageStyle == 1 ? [UIColor whiteColor] : %orig; +} +- (UIColor *)iconActive { + return self.pageStyle == 1 ? [UIColor whiteColor] : %orig; +} +- (UIColor *)iconActiveOther { + return self.pageStyle == 1 ? [UIColor whiteColor] : %orig; +} +- (UIColor *)brandIconActive { + return self.pageStyle == 1 ? [UIColor whiteColor] : %orig; +} +- (UIColor *)staticBrandWhite { + return self.pageStyle == 1 ? [UIColor whiteColor] : %orig; +} +- (UIColor *)overlayIconActiveOther { + return self.pageStyle == 1 ? [UIColor whiteColor] : %orig; +} +%end +%hook YTColor ++ (UIColor *)white2 { + return [UIColor whiteColor]; +} ++ (UIColor *)white3 { + return [UIColor whiteColor]; +} ++ (UIColor *)white4 { + return [UIColor whiteColor]; +} ++ (UIColor *)white5 { + return [UIColor whiteColor]; +} +%end +%hook QTMColorGroup +- (UIColor *)tint100 { + return [UIColor whiteColor]; +} +- (UIColor *)tint300 { + return [UIColor whiteColor]; +} +- (UIColor *)bodyTextColor { + return [UIColor whiteColor]; +} +- (UIColor *)bodyTextColorOnLighterColor { + return [UIColor whiteColor]; +} +- (UIColor *)bodyTextColorOnRegularColor { + return [UIColor whiteColor]; +} +- (UIColor *)bodyTextColorOnDarkerColor { + return [UIColor whiteColor]; +} +- (UIColor *)bodyTextColorOnAccentColor { + return [UIColor whiteColor]; +} +- (UIColor *)bodyTextColorOnOnBrightAccentColor { + return [UIColor whiteColor]; +} +- (UIColor *)lightBodyTextColor { + return [UIColor whiteColor]; +} +- (UIColor *)buttonBackgroundColor { + return [UIColor whiteColor]; +} +%end +%hook YTQTMButton +- (void)setImage:(UIImage *)image { + UIImage *currentImage = [image imageWithRenderingMode:UIImageRenderingModeAlwaysTemplate]; + [self setTintColor:[UIColor whiteColor]]; + %orig(currentImage); +} +%end +%hook UIExtendedGrayColorSpace +- (void)setTextColor:(UIColor *)textColor { + textColor = [[UIColor whiteColor] colorWithAlphaComponent:1.0]; + %orig(); +} +%end +%hook VideoTitleLabel +- (void)setTextColor:(UIColor *)textColor { + textColor = [UIColor whiteColor]; + %orig(textColor); +} +%end +%hook UILabel ++ (void)load { + @autoreleasepool { + [[UILabel appearance] setTextColor:[UIColor whiteColor]]; + } +} +- (void)setTextColor:(UIColor *)textColor { + %log; + textColor = [UIColor whiteColor]; + %orig(textColor); +} +%end +%hook UITextField +- (void)setTextColor:(UIColor *)textColor { + %log; + textColor = [UIColor whiteColor]; + %orig(textColor); +} +%end +%hook UITextView +- (void)setTextColor:(UIColor *)textColor { + %log; + textColor = [UIColor whiteColor]; + %orig(textColor); +} +%end +%hook UISearchBar +- (void)setTextColor:(UIColor *)textColor { + textColor = [UIColor whiteColor]; + %orig(textColor); +} +%end +%hook UISegmentedControl +- (void)setTitleTextAttributes:(NSDictionary *)attributes forState:(UIControlState)state { + NSMutableDictionary *modifiedAttributes = [NSMutableDictionary dictionaryWithDictionary:attributes]; + [modifiedAttributes setObject:[UIColor whiteColor] forKey:NSForegroundColorAttributeName]; + %orig(modifiedAttributes, state); +} +%end +%hook UIButton +- (void)setTitleColor:(UIColor *)color forState:(UIControlState)state { + color = [UIColor whiteColor]; + %orig(color, state); +} +%end +%hook UIBarButtonItem +- (void)setTitleTextAttributes:(NSDictionary *)attributes forState:(UIControlState)state { + NSMutableDictionary *modifiedAttributes = [NSMutableDictionary dictionaryWithDictionary:attributes]; + [modifiedAttributes setObject:[UIColor whiteColor] forKey:NSForegroundColorAttributeName]; + %orig(modifiedAttributes, state); +} +%end +%hook NSAttributedString +- (instancetype)initWithString:(NSString *)str attributes:(NSDictionary *)attrs { + NSMutableDictionary *modifiedAttributes = [NSMutableDictionary dictionaryWithDictionary:attrs]; + [modifiedAttributes setObject:[UIColor whiteColor] forKey:NSForegroundColorAttributeName]; + return %orig(str, modifiedAttributes); +} +%end +%hook CATextLayer +- (void)setTextColor:(CGColorRef)textColor { + %orig([UIColor whiteColor].CGColor); +} +%end +%hook ASTextNode +- (NSAttributedString *)attributedString { + NSAttributedString *originalAttributedString = %orig; + NSMutableAttributedString *newAttributedString = [originalAttributedString mutableCopy]; + [newAttributedString addAttribute:NSForegroundColorAttributeName value:[UIColor whiteColor] range:NSMakeRange(0, newAttributedString.length)]; + return newAttributedString; +} +%end +%hook ASTextFieldNode +- (void)setTextColor:(UIColor *)textColor { + %orig([UIColor whiteColor]); +} +%end +%hook ASTextView +- (void)setTextColor:(UIColor *)textColor { + %orig([UIColor whiteColor]); +} +%end +%hook ASButtonNode +- (void)setTextColor:(UIColor *)textColor { + %orig([UIColor whiteColor]); +} +%end +%end + +# pragma mark - ctor +%ctor { + %init; + if (lowContrastMode()) { + %init(gLowContrastMode); + } + if (customContrastMode()) { + %init(gCustomContrastMode); + } +} diff --git a/Settings.xm b/Settings.xm new file mode 100644 index 0000000..864275b --- /dev/null +++ b/Settings.xm @@ -0,0 +1,734 @@ +#import "../YTLitePlus.h" +#import "../Tweaks/YouTubeHeader/YTSettingsViewController.h" +#import "../Tweaks/YouTubeHeader/YTSearchableSettingsViewController.h" +#import "../Tweaks/YouTubeHeader/YTSettingsSectionItem.h" +#import "../Tweaks/YouTubeHeader/YTSettingsSectionItemManager.h" +#import "../Tweaks/YouTubeHeader/YTUIUtils.h" +#import "../Tweaks/YouTubeHeader/YTSettingsPickerViewController.h" +#import "SettingsKeys.h" +// #import "AppIconOptionsController.h" + +// Basic switch item +#define BASIC_SWITCH(title, description, key) \ + [YTSettingsSectionItemClass switchItemWithTitle:title \ + titleDescription:description \ + accessibilityIdentifier:nil \ + switchOn:IsEnabled(key) \ + switchBlock:^BOOL (YTSettingsCell *cell, BOOL enabled) { \ + [[NSUserDefaults standardUserDefaults] setBool:enabled forKey:key]; \ + return YES; \ + } \ + settingItemId:0] + +/* +// Custom switch item that has customizable code +#define CUSTOM_SWITCH(title, description, key, code) \ + [YTSettingsSectionItemClass switchItemWithTitle:title \ + titleDescription:description \ + accessibilityIdentifier:nil \ + switchOn:IsEnabled(key) \ + switchBlock:^BOOL (YTSettingsCell *cell, BOOL enable) { \ + code \ + } \ + settingItemId:0] +*/ + +static int contrastMode() { + return [[NSUserDefaults standardUserDefaults] integerForKey:@"lcm"]; +} +static int appVersionSpoofer() { + return [[NSUserDefaults standardUserDefaults] integerForKey:@"versionSpoofer"]; +} + +@interface YTSettingsSectionItemManager (YTLitePlus) +- (void)updateYTLitePlusSectionWithEntry:(id)entry; +@end + +extern NSBundle *YTLitePlusBundle(); + +// Add both YTLite and YTLitePlus to YouGroupSettings +static const NSInteger YTLitePlusSection = 788; +static const NSInteger YTLiteSection = 789; +%hook YTSettingsGroupData ++ (NSMutableArray *)tweaks { + NSMutableArray *originalTweaks = %orig; + + static dispatch_once_t onceToken; + dispatch_once(&onceToken, ^{ + [originalTweaks addObject:@(YTLitePlusSection)]; + [originalTweaks addObject:@(YTLiteSection)]; + }); + + return originalTweaks; +} +%end + + +// Add YTLitePlus to the settings list +%hook YTAppSettingsPresentationData ++ (NSArray *)settingsCategoryOrder { + NSArray *order = %orig; + NSMutableArray *mutableOrder = [order mutableCopy]; + NSUInteger insertIndex = [order indexOfObject:@(1)]; + if (insertIndex != NSNotFound) + [mutableOrder insertObject:@(YTLitePlusSection) atIndex:insertIndex + 1]; + return mutableOrder; +} +%end + +%hook YTSettingsSectionController + +- (void)setSelectedItem:(NSUInteger)selectedItem { + if (selectedItem != NSNotFound) %orig; +} + +%end + +%hook YTSettingsSectionItemManager +%new(v@:@) +- (void)updateYTLitePlusSectionWithEntry:(id)entry { + NSMutableArray *sectionItems = [NSMutableArray array]; + NSBundle *tweakBundle = YTLitePlusBundle(); + Class YTSettingsSectionItemClass = %c(YTSettingsSectionItem); + YTSettingsViewController *settingsViewController = [self valueForKey:@"_settingsViewControllerDelegate"]; + + // Add item for going to the YTLitePlus GitHub page + YTSettingsSectionItem *main = [%c(YTSettingsSectionItem) + itemWithTitle:[NSString stringWithFormat:LOC(@"VERSION"), @(OS_STRINGIFY(TWEAK_VERSION))] + titleDescription:LOC(@"VERSION_CHECK") + accessibilityIdentifier:nil + detailTextBlock:nil + selectBlock:^BOOL (YTSettingsCell *cell, NSUInteger arg1) { + return [%c(YTUIUtils) openURL:[NSURL URLWithString:@"https://github.com/YTLitePlus/YTLitePlus/releases/latest"]]; + }]; + [sectionItems addObject:main]; + +# pragma mark - Copy and Paste Settings + YTSettingsSectionItem *copySettings = [%c(YTSettingsSectionItem) + itemWithTitle:IS_ENABLED(@"switchCopyandPasteFunctionality_enabled") ? LOC(@"EXPORT_SETTINGS") : LOC(@"COPY_SETTINGS") + titleDescription:IS_ENABLED(@"switchCopyandPasteFunctionality_enabled") ? LOC(@"EXPORT_SETTINGS_DESC") : LOC(@"COPY_SETTINGS_DESC") + accessibilityIdentifier:nil + detailTextBlock:nil + selectBlock:^BOOL (YTSettingsCell *cell, NSUInteger arg1) { + if (IS_ENABLED(@"switchCopyandPasteFunctionality_enabled")) { + // Export Settings functionality + NSURL *tempFileURL = [NSURL fileURLWithPath:[NSTemporaryDirectory() stringByAppendingPathComponent:@"YTLitePlusSettings.txt"]]; + NSMutableString *settingsString = [NSMutableString string]; + for (NSString *key in NSUserDefaultsCopyKeys) { + id value = [[NSUserDefaults standardUserDefaults] objectForKey:key]; + id defaultValue = NSUserDefaultsCopyKeysDefaults[key]; + + // Only include the setting if it is different from the default value + // If no default value is found, include it by default + if (value && (!defaultValue || ![value isEqual:defaultValue])) { + [settingsString appendFormat:@"%@: %@\n", key, value]; + } + } + [settingsString writeToURL:tempFileURL atomically:YES encoding:NSUTF8StringEncoding error:nil]; + UIDocumentPickerViewController *documentPicker = [[UIDocumentPickerViewController alloc] initWithURL:tempFileURL inMode:UIDocumentPickerModeExportToService]; + documentPicker.delegate = (id)self; + documentPicker.allowsMultipleSelection = NO; + [settingsViewController presentViewController:documentPicker animated:YES completion:nil]; + } else { + // Copy Settings functionality (DEFAULT - Copies to Clipboard) + NSUserDefaults *userDefaults = [NSUserDefaults standardUserDefaults]; + NSMutableString *settingsString = [NSMutableString string]; + for (NSString *key in NSUserDefaultsCopyKeys) { + id value = [userDefaults objectForKey:key]; + id defaultValue = NSUserDefaultsCopyKeysDefaults[key]; + + // Only include the setting if it is different from the default value + // If no default value is found, include it by default + if (value && (!defaultValue || ![value isEqual:defaultValue])) { + [settingsString appendFormat:@"%@: %@\n", key, value]; + } + } + [[UIPasteboard generalPasteboard] setString:settingsString]; + // Show a confirmation message or perform some other action here + [[%c(GOOHUDManagerInternal) sharedInstance] showMessageMainThread:[%c(YTHUDMessage) messageWithText:@"Settings copied"]]; + + // Show an option to export YouTube Plus settings + UIAlertController *exportAlert = [UIAlertController alertControllerWithTitle:@"Export Settings" + message:@"Note: This feature cannot save iSponsorBlock and most YouTube settings.\n\nWould you like to also export your YouTube Plus Settings?" + preferredStyle:UIAlertControllerStyleAlert]; + [exportAlert addAction:[UIAlertAction actionWithTitle:@"Cancel" style:UIAlertActionStyleCancel handler:nil]]; + [exportAlert addAction:[UIAlertAction actionWithTitle:@"Export" style:UIAlertActionStyleDefault handler:^(UIAlertAction * _Nonnull action) { + // Export YouTube Plus Settings functionality + [%c(YTLUserDefaults) exportYtlSettings]; + }]]; + // Present the alert from the root view controller + [settingsViewController presentViewController:exportAlert animated:YES completion:nil]; + } + + return YES; + } + ]; + [sectionItems addObject:copySettings]; + + YTSettingsSectionItem *pasteSettings = [%c(YTSettingsSectionItem) + itemWithTitle:IS_ENABLED(@"switchCopyandPasteFunctionality_enabled") ? LOC(@"IMPORT_SETTINGS") : LOC(@"PASTE_SETTINGS") + titleDescription:IS_ENABLED(@"switchCopyandPasteFunctionality_enabled") ? LOC(@"IMPORT_SETTINGS_DESC") : LOC(@"PASTE_SETTINGS_DESC") + accessibilityIdentifier:nil + detailTextBlock:nil + selectBlock:^BOOL (YTSettingsCell *cell, NSUInteger arg1) { + if (IS_ENABLED(@"switchCopyandPasteFunctionality_enabled")) { + // Paste Settings functionality (ALTERNATE - Pastes from ".txt") + UIDocumentPickerViewController *documentPicker = [[UIDocumentPickerViewController alloc] initWithDocumentTypes:@[@"public.text"] inMode:UIDocumentPickerModeImport]; + documentPicker.delegate = (id)self; + documentPicker.allowsMultipleSelection = NO; + [settingsViewController presentViewController:documentPicker animated:YES completion:nil]; + } else { + // Paste Settings functionality (DEFAULT - Pastes from Clipboard) + UIAlertController *confirmPasteAlert = [UIAlertController alertControllerWithTitle:LOC(@"PASTE_SETTINGS_ALERT") message:nil preferredStyle:UIAlertControllerStyleAlert]; + [confirmPasteAlert addAction:[UIAlertAction actionWithTitle:LOC(@"Cancel") style:UIAlertActionStyleCancel handler:nil]]; + [confirmPasteAlert addAction:[UIAlertAction actionWithTitle:LOC(@"Confirm") style:UIAlertActionStyleDefault handler:^(UIAlertAction * _Nonnull action) { + NSString *settingsString = [[UIPasteboard generalPasteboard] string]; + if (settingsString.length > 0) { + NSArray *lines = [settingsString componentsSeparatedByString:@"\n"]; + for (NSString *line in lines) { + NSArray *components = [line componentsSeparatedByString:@": "]; + if (components.count == 2) { + NSString *key = components[0]; + NSString *value = components[1]; + [[NSUserDefaults standardUserDefaults] setObject:value forKey:key]; + } + } + [settingsViewController reloadData]; + // Show a confirmation toast + [[%c(GOOHUDManagerInternal) sharedInstance] showMessageMainThread:[%c(YTHUDMessage) messageWithText:@"Settings applied"]]; + // Show a reminder to import YouTube Plus settings as well + UIAlertController *reminderAlert = [UIAlertController alertControllerWithTitle:@"Reminder" + message:@"Remember to import your YouTube Plus settings as well" + preferredStyle:UIAlertControllerStyleAlert]; + [reminderAlert addAction:[UIAlertAction actionWithTitle:@"OK" style:UIAlertActionStyleDefault handler:nil]]; + [settingsViewController presentViewController:reminderAlert animated:YES completion:nil]; + } + }]]; + [settingsViewController presentViewController:confirmPasteAlert animated:YES completion:nil]; + } + + return YES; + } + ]; + [sectionItems addObject:pasteSettings]; + +/* + YTSettingsSectionItem *appIcon = [%c(YTSettingsSectionItem) + itemWithTitle:LOC(@"CHANGE_APP_ICON") + titleDescription:nil + accessibilityIdentifier:nil + detailTextBlock:nil + selectBlock:^BOOL (YTSettingsCell *cell, NSUInteger arg1) { + AppIconOptionsController *appIconController = [[AppIconOptionsController alloc] init]; + [settingsViewController.navigationController pushViewController:appIconController animated:YES]; + return YES; + } + ]; + [sectionItems addObject:appIcon]; +*/ + +# pragma mark - Player Gestures - @bhackel + // Helper to get the selected gesture mode + static NSString* (^sectionGestureSelectedModeToString)(GestureMode) = ^(GestureMode sectionIndex) { + switch (sectionIndex) { + case GestureModeVolume: + return LOC(@"VOLUME"); + case GestureModeBrightness: + return LOC(@"BRIGHTNESS"); + case GestureModeSeek: + return LOC(@"SEEK"); + case GestureModeDisabled: + return LOC(@"DISABLED"); + default: + return @"Invalid index - Report bug"; + } + }; + + // Helper to generate checkmark setting items for selecting gesture modes + static YTSettingsSectionItem* (^gestureCheckmarkSettingItem)(NSInteger, NSString *) = ^(NSInteger idx, NSString *key) { + return [YTSettingsSectionItemClass + checkmarkItemWithTitle:sectionGestureSelectedModeToString(idx) + selectBlock:^BOOL (YTSettingsCell *cell, NSUInteger arg1) { + [[NSUserDefaults standardUserDefaults] setInteger:idx forKey:key]; + [settingsViewController reloadData]; + return YES; + } + ]; + }; + + // Helper to generate a section item for selecting a gesture mode + YTSettingsSectionItem *(^createSectionGestureSelector)(NSString *, NSString *) = ^YTSettingsSectionItem *(NSString *sectionLabel, NSString *sectionKey) { + return [YTSettingsSectionItemClass itemWithTitle:LOC(sectionLabel) + accessibilityIdentifier:nil + detailTextBlock:^NSString *() { + return sectionGestureSelectedModeToString(GetInteger(sectionKey)); + } + selectBlock:^BOOL (YTSettingsCell *cell, NSUInteger arg1) { + NSArray *rows = @[ + gestureCheckmarkSettingItem(0, sectionKey), // Volume + gestureCheckmarkSettingItem(1, sectionKey), // Brightness + gestureCheckmarkSettingItem(2, sectionKey), // Seek + gestureCheckmarkSettingItem(3, sectionKey) // Disabled + ]; + // Present picker when selecting this settings item + YTSettingsPickerViewController *picker = [[%c(YTSettingsPickerViewController) alloc] + initWithNavTitle:LOC(sectionLabel) + pickerSectionTitle:nil + rows:rows + selectedItemIndex:GetInteger(sectionKey) + parentResponder:[self parentResponder] + ]; + [settingsViewController pushViewController:picker]; + return YES; + } + ]; + }; + // Configuration picker for deadzone to pick from 0 to 100 pixels with interval of 10 + NSMutableArray *deadzoneValues = [NSMutableArray array]; + for (CGFloat value = 0; value <= 100; value += 10) { + [deadzoneValues addObject:@(value)]; + } + YTSettingsSectionItem *deadzonePicker = [YTSettingsSectionItemClass + itemWithTitle:LOC(@"DEADZONE") + titleDescription:LOC(@"DEADZONE_DESC") + accessibilityIdentifier:nil + detailTextBlock:^NSString *() { + return [NSString stringWithFormat:@"%ld px", (long)GetFloat(@"playerGesturesDeadzone")]; + } + selectBlock:^BOOL (YTSettingsCell *cell, NSUInteger arg1) { + // Generate rows for deadzone picker using the predefined array + NSMutableArray *deadzoneRows = [NSMutableArray array]; + for (NSNumber *deadzoneValue in deadzoneValues) { + CGFloat deadzone = [deadzoneValue floatValue]; + [deadzoneRows addObject:[YTSettingsSectionItemClass + checkmarkItemWithTitle:[NSString stringWithFormat:@"%ld px", (long)deadzone] + selectBlock:^BOOL (YTSettingsCell *cell, NSUInteger arg1) { + [[NSUserDefaults standardUserDefaults] setFloat:deadzone forKey:@"playerGesturesDeadzone"]; + [settingsViewController reloadData]; + return YES; + } + ]]; + } + // Determine the index of the currently selected deadzone + CGFloat currentDeadzone = GetFloat(@"playerGesturesDeadzone"); + NSUInteger selectedIndex = [deadzoneValues indexOfObject:@(currentDeadzone)]; + if (selectedIndex == NSNotFound) { + selectedIndex = 0; // Default to the first item if the current deadzone is not found + } + // Present deadzone picker when selecting this settings item + YTSettingsPickerViewController *picker = [[%c(YTSettingsPickerViewController) alloc] + initWithNavTitle:LOC(@"DEADZONE") + pickerSectionTitle:nil + rows:deadzoneRows + selectedItemIndex:selectedIndex + parentResponder:[self parentResponder] + ]; + [settingsViewController pushViewController:picker]; + return YES; + } + ]; + + // Configuration picker for sensitivity to pick from 0.5 to 2.0 with interval of 0.1 + NSMutableArray *sensitivityValues = [NSMutableArray array]; + for (CGFloat value = 0.5; value <= 2.0; value += 0.1) { + [sensitivityValues addObject:@(value)]; + } + YTSettingsSectionItem *sensitivityPicker = [YTSettingsSectionItemClass + itemWithTitle:LOC(@"SENSITIVITY") + titleDescription:LOC(@"SENSITIVITY_DESC") + accessibilityIdentifier:nil + detailTextBlock:^NSString *() { + return [NSString stringWithFormat:@"%.1f", GetFloat(@"playerGesturesSensitivity")]; + } + selectBlock:^BOOL (YTSettingsCell *cell, NSUInteger arg1) { + // Generate rows for sensitivity picker using the predefined array + NSMutableArray *sensitivityRows = [NSMutableArray array]; + for (NSNumber *sensitivityValue in sensitivityValues) { + CGFloat sensitivity = [sensitivityValue floatValue]; + [sensitivityRows addObject:[YTSettingsSectionItemClass + checkmarkItemWithTitle:[NSString stringWithFormat:@"%.1f", sensitivity] + selectBlock:^BOOL (YTSettingsCell *cell, NSUInteger arg1) { + [[NSUserDefaults standardUserDefaults] setFloat:sensitivity forKey:@"playerGesturesSensitivity"]; + [settingsViewController reloadData]; + return YES; + } + ]]; + } + // Determine the index of the currently selected sensitivity + CGFloat currentSensitivity = GetFloat(@"playerGesturesSensitivity"); + NSUInteger selectedIndex = [sensitivityValues indexOfObject:@(currentSensitivity)]; + if (selectedIndex == NSNotFound) { + selectedIndex = 0; // Default to the first item if the current sensitivity is not found + } + // Present sensitivity picker + YTSettingsPickerViewController *picker = [[%c(YTSettingsPickerViewController) alloc] + initWithNavTitle:LOC(@"SENSITIVITY") + pickerSectionTitle:nil + rows:sensitivityRows + selectedItemIndex:selectedIndex + parentResponder:[self parentResponder] + ]; + [settingsViewController pushViewController:picker]; + return YES; + } + ]; + + // Create and add items to the high level gestures menu + YTSettingsSectionItem *playerGesturesGroup = [YTSettingsSectionItemClass itemWithTitle:LOC(@"PLAYER_GESTURES_TITLE") accessibilityIdentifier:nil detailTextBlock:nil selectBlock:^BOOL (YTSettingsCell *cell, NSUInteger arg1) { + NSArray *rows = @[ + // Description header item + [YTSettingsSectionItemClass + itemWithTitle:nil + titleDescription:LOC(@"PLAYER_GESTURES_DESC") + accessibilityIdentifier:nil + detailTextBlock:nil + selectBlock:nil + ], + // Toggle for enabling gestures + BASIC_SWITCH(LOC(@"PLAYER_GESTURES_TOGGLE"), nil, @"playerGestures_enabled"), + // Pickers for each gesture section + createSectionGestureSelector(@"TOP_SECTION", @"playerGestureTopSelection"), + createSectionGestureSelector(@"MIDDLE_SECTION", @"playerGestureMiddleSelection"), + createSectionGestureSelector(@"BOTTOM_SECTION", @"playerGestureBottomSelection"), + // Pickers for configuration settings + deadzonePicker, + sensitivityPicker, + // Toggle for haptic feedback + BASIC_SWITCH(LOC(@"PLAYER_GESTURES_HAPTIC_FEEDBACK"), nil, @"playerGesturesHapticFeedback_enabled"), + ]; + YTSettingsPickerViewController *picker = [[%c(YTSettingsPickerViewController) alloc] initWithNavTitle:LOC(@"PLAYER_GESTURES_TITLE") pickerSectionTitle:nil rows:rows selectedItemIndex:NSNotFound parentResponder:[self parentResponder]]; + [settingsViewController pushViewController:picker]; + return YES; + }]; + [sectionItems addObject:playerGesturesGroup]; + +# pragma mark - Video Controls Overlay Options + YTSettingsSectionItem *videoControlOverlayGroup = [YTSettingsSectionItemClass itemWithTitle:LOC(@"VIDEO_CONTROLS_OVERLAY_OPTIONS") accessibilityIdentifier:nil detailTextBlock:nil selectBlock:^BOOL (YTSettingsCell *cell, NSUInteger arg1) { + NSArray *rows = @[ + BASIC_SWITCH(LOC(@"ENABLE_SHARE_BUTTON"), LOC(@"ENABLE_SHARE_BUTTON_DESC"), @"enableShareButton_enabled"), + BASIC_SWITCH(LOC(@"ENABLE_SAVE_TO_PLAYLIST_BUTTON"), LOC(@"ENABLE_SAVE_TO_PLAYLIST_BUTTON_DESC"), @"enableSaveToButton_enabled"), + BASIC_SWITCH(LOC(@"HIDE_SHADOW_OVERLAY_BUTTONS"), LOC(@"HIDE_SHADOW_OVERLAY_BUTTONS_DESC"), @"hideVideoPlayerShadowOverlayButtons_enabled"), + BASIC_SWITCH(LOC(@"HIDE_RIGHT_PANEL"), LOC(@"HIDE_RIGHT_PANEL_DESC"), @"hideRightPanel_enabled"), + BASIC_SWITCH(LOC(@"HIDE_HEATWAVES"), LOC(@"HIDE_HEATWAVES_DESC"), @"hideHeatwaves_enabled"), + BASIC_SWITCH(LOC(@"DISABLE_AMBIENT_PORTRAIT"), LOC(@"DISABLE_AMBIENT_PORTRAIT_DESC"), @"disableAmbientModePortrait_enabled"), + BASIC_SWITCH(LOC(@"DISABLE_AMBIENT_FULLSCREEN"), LOC(@"DISABLE_AMBIENT_FULLSCREEN_DESC"), @"disableAmbientModeFullscreen_enabled"), + BASIC_SWITCH(LOC(@"FULLSCREEN_TO_THE_RIGHT"), LOC(@"FULLSCREEN_TO_THE_RIGHT_DESC"), @"fullscreenToTheRight_enabled"), + BASIC_SWITCH(LOC(@"SEEK_ANYWHERE"), LOC(@"SEEK_ANYWHERE_DESC"), @"seekAnywhere_enabled"), + BASIC_SWITCH(LOC(@"ENABLE_TAP_TO_SEEK"), LOC(@"ENABLE_TAP_TO_SEEK_DESC"), @"YTTapToSeek_enabled"), + BASIC_SWITCH(LOC(@"DISABLE_PULL_TO_FULLSCREEN_GESTURE"), LOC(@"DISABLE_PULL_TO_FULLSCREEN_GESTURE_DESC"), @"disablePullToFull_enabled"), + BASIC_SWITCH(LOC(@"ALWAYS_USE_REMAINING_TIME"), LOC(@"ALWAYS_USE_REMAINING_TIME_DESC"), @"alwaysShowRemainingTime_enabled"), + BASIC_SWITCH(LOC(@"DISABLE_TOGGLE_TIME_REMAINING"), LOC(@"DISABLE_TOGGLE_TIME_REMAINING_DESC"), @"disableRemainingTime_enabled"), + BASIC_SWITCH(LOC(@"DISABLE_ENGAGEMENT_OVERLAY"), LOC(@"DISABLE_ENGAGEMENT_OVERLAY_DESC"), @"disableEngagementOverlay_enabled"), + BASIC_SWITCH(LOC(@"HIDE_COMMENT_PREVIEWS_UNDER_PLAYER"), LOC(@"HIDE_COMMENT_PREVIEWS_UNDER_PLAYER_DESC"), @"hidePreviewCommentSection_enabled"), + BASIC_SWITCH(LOC(@"HIDE_AUTOPLAY_MINI_PREVIEW"), LOC(@"HIDE_AUTOPLAY_MINI_PREVIEW_DESC"), @"hideAutoplayMiniPreview_enabled"), + BASIC_SWITCH(LOC(@"HIDE_HUD_MESSAGES"), LOC(@"HIDE_HUD_MESSAGES_DESC"), @"hideHUD_enabled"), + BASIC_SWITCH(LOC(@"HIDE_COLLAPSE_BUTTON"), LOC(@"HIDE_COLLAPSE_BUTTON_DESC"), @"disableCollapseButton_enabled"), + BASIC_SWITCH(LOC(@"HIDE_SPEED_TOAST"), LOC(@"HIDE_SPEED_TOAST_DESC"), @"hideSpeedToast_enabled"), + ]; + YTSettingsPickerViewController *picker = [[%c(YTSettingsPickerViewController) alloc] initWithNavTitle:LOC(@"VIDEO_CONTROLS_OVERLAY_OPTIONS") pickerSectionTitle:nil rows:rows selectedItemIndex:NSNotFound parentResponder:[self parentResponder]]; + [settingsViewController pushViewController:picker]; + return YES; + }]; + [sectionItems addObject:videoControlOverlayGroup]; + +# pragma mark - App Settings Overlay Options + YTSettingsSectionItem *appSettingsOverlayGroup = [YTSettingsSectionItemClass itemWithTitle:LOC(@"APP_SETTINGS_OVERLAY_OPTIONS") accessibilityIdentifier:nil detailTextBlock:nil selectBlock:^BOOL (YTSettingsCell *cell, NSUInteger arg1) { + NSArray *rows = @[ + BASIC_SWITCH(LOC(@"HIDE_ACCOUNT_SECTION"), LOC(@"APP_RESTART_DESC"), @"disableAccountSection_enabled"), + BASIC_SWITCH(LOC(@"HIDE_AUTOPLAY_SECTION"), LOC(@"APP_RESTART_DESC"), @"disableAutoplaySection_enabled"), + BASIC_SWITCH(LOC(@"HIDE_TRYNEWFEATURES_SECTION"), LOC(@"APP_RESTART_DESC"), @"disableTryNewFeaturesSection_enabled"), + BASIC_SWITCH(LOC(@"HIDE_VIDEOQUALITYPREFERENCES_SECTION"), LOC(@"APP_RESTART_DESC"), @"disableVideoQualityPreferencesSection_enabled"), + BASIC_SWITCH(LOC(@"HIDE_NOTIFICATIONS_SECTION"), LOC(@"APP_RESTART_DESC"), @"disableNotificationsSection_enabled"), + BASIC_SWITCH(LOC(@"HIDE_MANAGEALLHISTORY_SECTION"), LOC(@"APP_RESTART_DESC"), @"disableManageAllHistorySection_enabled"), + BASIC_SWITCH(LOC(@"HIDE_YOURDATAINYOUTUBE_SECTION"), LOC(@"APP_RESTART_DESC"), @"disableYourDataInYouTubeSection_enabled"), + BASIC_SWITCH(LOC(@"HIDE_PRIVACY_SECTION"), LOC(@"APP_RESTART_DESC"), @"disablePrivacySection_enabled"), + BASIC_SWITCH(LOC(@"HIDE_LIVECHAT_SECTION"), LOC(@"APP_RESTART_DESC"), @"disableLiveChatSection_enabled") + ]; + YTSettingsPickerViewController *picker = [[%c(YTSettingsPickerViewController) alloc] initWithNavTitle:LOC(@"APP_SETTINGS_OVERLAY_OPTIONS") pickerSectionTitle:nil rows:rows selectedItemIndex:NSNotFound parentResponder:[self parentResponder]]; + [settingsViewController pushViewController:picker]; + return YES; + }]; + [sectionItems addObject:appSettingsOverlayGroup]; + +# pragma mark - LowContrastMode + YTSettingsSectionItem *lowContrastModeSection = [YTSettingsSectionItemClass itemWithTitle:LOC(@"LOW_CONTRAST_MODE") + accessibilityIdentifier:nil + detailTextBlock:^NSString *() { + switch (contrastMode()) { + case 1: + return LOC(@"Hex Color"); + case 0: + default: + return LOC(@"Default"); + } + } + selectBlock:^BOOL (YTSettingsCell *cell, NSUInteger arg1) { + NSArray *rows = @[ + [YTSettingsSectionItemClass checkmarkItemWithTitle:LOC(@"Default") titleDescription:nil selectBlock:^BOOL (YTSettingsCell *cell, NSUInteger arg1) { + [[NSUserDefaults standardUserDefaults] setInteger:0 forKey:@"lcm"]; + [settingsViewController reloadData]; + return YES; + }], + [YTSettingsSectionItemClass checkmarkItemWithTitle:LOC(@"Hex Color") titleDescription:nil selectBlock:^BOOL (YTSettingsCell *cell, NSUInteger arg1) { + [[NSUserDefaults standardUserDefaults] setInteger:1 forKey:@"lcm"]; + [settingsViewController reloadData]; + return YES; + }] + ]; + YTSettingsPickerViewController *picker = [[%c(YTSettingsPickerViewController) alloc] initWithNavTitle:LOC(@"LOW_CONTRAST_MODE") pickerSectionTitle:nil rows:rows selectedItemIndex:contrastMode() parentResponder:[self parentResponder]]; + [settingsViewController pushViewController:picker]; + return YES; + }]; + +# pragma mark - VersionSpooferLite + YTSettingsSectionItem *versionSpooferSection = [YTSettingsSectionItemClass itemWithTitle:LOC(@"VERSION_SPOOFER_TITLE") + accessibilityIdentifier:nil + detailTextBlock:^NSString *() { + switch (appVersionSpoofer()) { + case 1: + return @"v18.34.5 (Enable Library Tab)"; + case 2: + return @"v18.33.3 (Removes Playables)"; + case 3: + return @"v18.18.2 (Fixes YTClassicVideoQuality & YTSpeed)"; + case 4: + return @"v18.01.2 (First v18 Version)"; + case 5: + return @"v17.49.6 (Removes Rounded Miniplayer)"; + case 6: + return @"v17.38.10 (Fixes LowContrastMode)"; + case 7: + return @"v17.33.2 (Oldest Supported Version)"; + case 0: + default: + return @"v18.49.3 (Last v18 Version)"; + } + } + selectBlock:^BOOL (YTSettingsCell *cell, NSUInteger arg1) { + NSArray *rows = @[ + [YTSettingsSectionItemClass checkmarkItemWithTitle:@"v18.49.3 (Last v18 Version)" titleDescription:nil selectBlock:^BOOL (YTSettingsCell *cell, NSUInteger arg1) { + [[NSUserDefaults standardUserDefaults] setInteger:0 forKey:@"versionSpoofer"]; + [settingsViewController reloadData]; + return YES; + }], + [YTSettingsSectionItemClass checkmarkItemWithTitle:@"v18.34.5 (Enable Library Tab)" titleDescription:nil selectBlock:^BOOL (YTSettingsCell *cell, NSUInteger arg1) { + [[NSUserDefaults standardUserDefaults] setInteger:1 forKey:@"versionSpoofer"]; + [settingsViewController reloadData]; + return YES; + }], + [YTSettingsSectionItemClass checkmarkItemWithTitle:@"v18.33.3 (Removes Playables)" titleDescription:nil selectBlock:^BOOL (YTSettingsCell *cell, NSUInteger arg1) { + [[NSUserDefaults standardUserDefaults] setInteger:2 forKey:@"versionSpoofer"]; + [settingsViewController reloadData]; + return YES; + }], + [YTSettingsSectionItemClass checkmarkItemWithTitle:@"v18.18.2 (Fixes YTClassicVideoQuality & YTSpeed)" titleDescription:nil selectBlock:^BOOL (YTSettingsCell *cell, NSUInteger arg1) { + [[NSUserDefaults standardUserDefaults] setInteger:3 forKey:@"versionSpoofer"]; + [settingsViewController reloadData]; + return YES; + }], + [YTSettingsSectionItemClass checkmarkItemWithTitle:@"v18.01.2 (First v18 Version)" titleDescription:nil selectBlock:^BOOL (YTSettingsCell *cell, NSUInteger arg1) { + [[NSUserDefaults standardUserDefaults] setInteger:4 forKey:@"versionSpoofer"]; + [settingsViewController reloadData]; + return YES; + }], + [YTSettingsSectionItemClass checkmarkItemWithTitle:@"v17.49.6 (Removes Rounded Miniplayer)" titleDescription:nil selectBlock:^BOOL (YTSettingsCell *cell, NSUInteger arg1) { + [[NSUserDefaults standardUserDefaults] setInteger:5 forKey:@"versionSpoofer"]; + [settingsViewController reloadData]; + return YES; + }], + [YTSettingsSectionItemClass checkmarkItemWithTitle:@"v17.38.10 (Fixes LowContrastMode)" titleDescription:nil selectBlock:^BOOL (YTSettingsCell *cell, NSUInteger arg1) { + [[NSUserDefaults standardUserDefaults] setInteger:6 forKey:@"versionSpoofer"]; + [settingsViewController reloadData]; + return YES; + }], + [YTSettingsSectionItemClass checkmarkItemWithTitle:@"v17.33.2 (Oldest Supported Version)" titleDescription:nil selectBlock:^BOOL (YTSettingsCell *cell, NSUInteger arg1) { + [[NSUserDefaults standardUserDefaults] setInteger:7 forKey:@"versionSpoofer"]; + [settingsViewController reloadData]; + return YES; + }] + ]; + YTSettingsPickerViewController *picker = [[%c(YTSettingsPickerViewController) alloc] initWithNavTitle:@"VERSION_SPOOFER_TITLE" pickerSectionTitle:nil rows:rows selectedItemIndex:appVersionSpoofer() parentResponder:[self parentResponder]]; + [settingsViewController pushViewController:picker]; + return YES; + }]; + +# pragma mark - Theme + YTSettingsSectionItem *themeGroup = [YTSettingsSectionItemClass itemWithTitle:LOC(@"THEME_OPTIONS") + accessibilityIdentifier:nil + detailTextBlock:^NSString *() { + switch (GetInteger(@"appTheme")) { + case 1: + return LOC(@"OLD_DARK_THEME"); + case 0: + default: + return LOC(@"DEFAULT_THEME"); + } + } + selectBlock:^BOOL (YTSettingsCell *cell, NSUInteger arg1) { + NSArray *rows = @[ + [YTSettingsSectionItemClass checkmarkItemWithTitle:LOC(@"DEFAULT_THEME") titleDescription:LOC(@"DEFAULT_THEME_DESC") selectBlock:^BOOL (YTSettingsCell *cell, NSUInteger arg1) { + [[NSUserDefaults standardUserDefaults] setInteger:0 forKey:@"appTheme"]; + [settingsViewController reloadData]; + return YES; + }], + [YTSettingsSectionItemClass checkmarkItemWithTitle:LOC(@"OLD_DARK_THEME") titleDescription:LOC(@"OLD_DARK_THEME_DESC") selectBlock:^BOOL (YTSettingsCell *cell, NSUInteger arg1) { + [[NSUserDefaults standardUserDefaults] setInteger:1 forKey:@"appTheme"]; + [settingsViewController reloadData]; + return YES; + }], + BASIC_SWITCH(LOC(@"OLED_KEYBOARD"), LOC(@"OLED_KEYBOARD_DESC"), @"oledKeyBoard_enabled"), + BASIC_SWITCH(LOC(@"LOW_CONTRAST_MODE"), LOC(@"LOW_CONTRAST_MODE_DESC"), @"lowContrastMode_enabled"), + lowContrastModeSection + ]; + YTSettingsPickerViewController *picker = [[%c(YTSettingsPickerViewController) alloc] initWithNavTitle:LOC(@"THEME_OPTIONS") pickerSectionTitle:nil rows:rows selectedItemIndex:GetInteger(@"appTheme") parentResponder:[self parentResponder]]; + [settingsViewController pushViewController:picker]; + return YES; + }]; + [sectionItems addObject:themeGroup]; + +# pragma mark - Copy of Playback in feeds section - @bhackel + // This section is hidden in vanilla YouTube when using the new settings UI, so + // we can recreate it here + YTSettingsSectionItem *playbackInFeedsGroup = [YTSettingsSectionItemClass itemWithTitle:LOC(@"PLAYBACK_IN_FEEDS") + accessibilityIdentifier:nil + detailTextBlock:^NSString *() { + // The specific values were gathered by checking the value for each setting + switch (GetInteger(@"inline_muted_playback_enabled")) { + case 3: + return LOC(@"PLAYBACK_IN_FEEDS_WIFI_ONLY"); + case 1: + return LOC(@"PLAYBACK_IN_FEEDS_OFF"); + case 2: + default: + return LOC(@"PLAYBACK_IN_FEEDS_ALWAYS_ON"); + } + } + selectBlock:^BOOL (YTSettingsCell *cell, NSUInteger arg1) { + NSArray *rows = @[ + [YTSettingsSectionItemClass checkmarkItemWithTitle:LOC(@"PLAYBACK_IN_FEEDS_OFF") selectBlock:^BOOL (YTSettingsCell *cell, NSUInteger arg1) { + [[NSUserDefaults standardUserDefaults] setInteger:1 forKey:@"inline_muted_playback_enabled"]; + [settingsViewController reloadData]; + return YES; + }], + [YTSettingsSectionItemClass checkmarkItemWithTitle:LOC(@"PLAYBACK_IN_FEEDS_ALWAYS_ON") selectBlock:^BOOL (YTSettingsCell *cell, NSUInteger arg1) { + [[NSUserDefaults standardUserDefaults] setInteger:2 forKey:@"inline_muted_playback_enabled"]; + [settingsViewController reloadData]; + return YES; + }], + [YTSettingsSectionItemClass checkmarkItemWithTitle:LOC(@"PLAYBACK_IN_FEEDS_WIFI_ONLY") selectBlock:^BOOL (YTSettingsCell *cell, NSUInteger arg1) { + [[NSUserDefaults standardUserDefaults] setInteger:3 forKey:@"inline_muted_playback_enabled"]; + [settingsViewController reloadData]; + return YES; + }], + ]; + // It seems values greater than 3 act the same as Always On (Index 1) + // Convert the stored value to an index for a picker (0 to 2) + NSInteger (^getInlineSelection)(void) = ^NSInteger(void) { + NSInteger selection = GetInteger(@"inline_muted_playback_enabled") - 1; + // Check if selection is within the valid bounds [0, 1, 2] + if (selection < 0 || selection > 2) { + return 1; + } + return selection; + }; + YTSettingsPickerViewController *picker = [[%c(YTSettingsPickerViewController) alloc] initWithNavTitle:LOC(@"PLAYBACK_IN_FEEDS") pickerSectionTitle:nil rows:rows selectedItemIndex:getInlineSelection() parentResponder:[self parentResponder]]; + [settingsViewController pushViewController:picker]; + return YES; + } + ]; + +# pragma mark - Miscellaneous + YTSettingsSectionItem *miscellaneousGroup = [YTSettingsSectionItemClass itemWithTitle:LOC(@"MISCELLANEOUS") accessibilityIdentifier:nil detailTextBlock:nil selectBlock:^BOOL (YTSettingsCell *cell, NSUInteger arg1) { + NSArray *rows = @[ + playbackInFeedsGroup, + // BASIC_SWITCH(LOC(@"NEW_SETTINGS_UI"), LOC(@"NEW_SETTINGS_UI_DESC"), @"newSettingsUI_enabled"), // disabled because YTLite is probably forcing it to NO + BASIC_SWITCH(LOC(@"ENABLE_YT_STARTUP_ANIMATION"), LOC(@"ENABLE_YT_STARTUP_ANIMATION_DESC"), @"ytStartupAnimation_enabled"), + BASIC_SWITCH(LOC(@"HIDE_MODERN_INTERFACE"), LOC(@"HIDE_MODERN_INTERFACE_DESC"), @"ytNoModernUI_enabled"), + BASIC_SWITCH(LOC(@"IPAD_LAYOUT"), LOC(@"IPAD_LAYOUT_DESC"), @"iPadLayout_enabled"), + BASIC_SWITCH(LOC(@"IPHONE_LAYOUT"), LOC(@"IPHONE_LAYOUT_DESC"), @"iPhoneLayout_enabled"), + BASIC_SWITCH(LOC(@"CAST_CONFIRM"), LOC(@"CAST_CONFIRM_DESC"), @"castConfirm_enabled"), + BASIC_SWITCH(LOC(@"NEW_MINIPLAYER_STYLE"), LOC(@"NEW_MINIPLAYER_STYLE_DESC"), @"bigYTMiniPlayer_enabled"), + BASIC_SWITCH(LOC(@"HIDE_CAST_BUTTON"), LOC(@"HIDE_CAST_BUTTON_DESC"), @"hideCastButton_enabled"), + BASIC_SWITCH(LOC(@"VIDEO_PLAYER_BUTTON"), LOC(@"VIDEO_PLAYER_BUTTON_DESC"), @"videoPlayerButton_enabled"), + BASIC_SWITCH(LOC(@"HIDE_SPONSORBLOCK_BUTTON"), LOC(@"HIDE_SPONSORBLOCK_BUTTON_DESC"), @"hideSponsorBlockButton_enabled"), + BASIC_SWITCH(LOC(@"HIDE_HOME_TAB"), LOC(@"HIDE_HOME_TAB_DESC"), @"hideHomeTab_enabled"), + BASIC_SWITCH(LOC(@"FIX_CASTING"), LOC(@"FIX_CASTING_DESC"), @"fixCasting_enabled"), + BASIC_SWITCH(LOC(@"REPLACE_COPY_AND_PASTE_BUTTONS"), LOC(@"REPLACE_COPY_AND_PASTE_BUTTONS_DESC"), @"switchCopyandPasteFunctionality_enabled"), + BASIC_SWITCH(LOC(@"ENABLE_FLEX"), LOC(@"ENABLE_FLEX_DESC"), @"flex_enabled"), + BASIC_SWITCH(LOC(@"APP_VERSION_SPOOFER_LITE"), LOC(@"APP_VERSION_SPOOFER_LITE_DESC"), @"enableVersionSpoofer_enabled"), + versionSpooferSection + ]; + YTSettingsPickerViewController *picker = [[%c(YTSettingsPickerViewController) alloc] initWithNavTitle:LOC(@"MISCELLANEOUS") pickerSectionTitle:nil rows:rows selectedItemIndex:NSNotFound parentResponder:[self parentResponder]]; + [settingsViewController pushViewController:picker]; + return YES; + }]; + [sectionItems addObject:miscellaneousGroup]; + + if ([settingsViewController respondsToSelector:@selector(setSectionItems:forCategory:title:icon:titleDescription:headerHidden:)]) + [settingsViewController setSectionItems:sectionItems forCategory:YTLitePlusSection title:@"YTLitePlus" icon:nil titleDescription:LOC(@"TITLE DESCRIPTION") headerHidden:YES]; + else + [settingsViewController setSectionItems:sectionItems forCategory:YTLitePlusSection title:@"YTLitePlus" titleDescription:LOC(@"TITLE DESCRIPTION") headerHidden:YES];} + +- (void)updateSectionForCategory:(NSUInteger)category withEntry:(id)entry { + if (category == YTLitePlusSection) { + [self updateYTLitePlusSectionWithEntry:entry]; + return; + } + %orig; +} + +// Implement the delegate method for document picker +%new +- (void)documentPicker:(UIDocumentPickerViewController *)controller didPickDocumentsAtURLs:(NSArray *)urls { + if (urls.count > 0) { + NSURL *pickedURL = [urls firstObject]; + NSError *error; + // Check which mode the document picker is in + if (controller.documentPickerMode == UIDocumentPickerModeImport) { + // Import mode: Handle the import of settings from a text file + NSString *fileType = [pickedURL resourceValuesForKeys:@[NSURLTypeIdentifierKey] error:&error][NSURLTypeIdentifierKey]; + + if (UTTypeConformsTo((__bridge CFStringRef)fileType, kUTTypePlainText)) { + NSString *fileContents = [NSString stringWithContentsOfURL:pickedURL encoding:NSUTF8StringEncoding error:nil]; + NSArray *lines = [fileContents componentsSeparatedByString:@"\n"]; + for (NSString *line in lines) { + NSArray *components = [line componentsSeparatedByString:@": "]; + if (components.count == 2) { + NSString *key = components[0]; + NSString *value = components[1]; + [[NSUserDefaults standardUserDefaults] setObject:value forKey:key]; + } + } + // Reload settings view after importing + YTSettingsViewController *settingsViewController = [self valueForKey:@"_settingsViewControllerDelegate"]; + [settingsViewController reloadData]; + // Show a confirmation message or perform some other action here + [[%c(GOOHUDManagerInternal) sharedInstance] showMessageMainThread:[%c(YTHUDMessage) messageWithText:@"Settings applied"]]; + // Show a reminder to import YouTube Plus settings as well + UIAlertController *reminderAlert = [UIAlertController alertControllerWithTitle:@"Reminder" + message:@"Remember to import your YouTube Plus settings as well" + preferredStyle:UIAlertControllerStyleAlert]; + [reminderAlert addAction:[UIAlertAction actionWithTitle:@"OK" style:UIAlertActionStyleDefault handler:nil]]; + [settingsViewController presentViewController:reminderAlert animated:YES completion:nil]; + } + + } else if (controller.documentPickerMode == UIDocumentPickerModeExportToService || controller.documentPickerMode == UIDocumentPickerModeMoveToService) { + [[%c(GOOHUDManagerInternal) sharedInstance] showMessageMainThread:[%c(YTHUDMessage) messageWithText:@"Settings saved"]]; + // Export mode: Display a reminder to save YouTube Plus settings + UIAlertController *exportAlert = [UIAlertController alertControllerWithTitle:@"Export Settings" + message:@"Note: This feature cannot save iSponsorBlock and most YouTube settings.\n\nWould you like to also export your YouTube Plus Settings?" + preferredStyle:UIAlertControllerStyleAlert]; + [exportAlert addAction:[UIAlertAction actionWithTitle:@"Cancel" style:UIAlertActionStyleCancel handler:nil]]; + [exportAlert addAction:[UIAlertAction actionWithTitle:@"Export" style:UIAlertActionStyleDefault handler:^(UIAlertAction * _Nonnull action) { + // Export YouTube Plus Settings functionality + [%c(YTLUserDefaults) exportYtlSettings]; + }]]; + YTSettingsViewController *settingsViewController = [self valueForKey:@"_settingsViewControllerDelegate"]; + // Present the alert from the root view controller + [settingsViewController presentViewController:exportAlert animated:YES completion:nil]; + } + } +} + +%end + diff --git a/SettingsKeys.h b/SettingsKeys.h new file mode 100644 index 0000000..d18b4e8 --- /dev/null +++ b/SettingsKeys.h @@ -0,0 +1,55 @@ +#import "../YTLitePlus.h" + +// Keys for "Copy Settings" button (for: YTLitePlus) +// In alphabetical order for tweaks after YTLitePlus +NSArray *NSUserDefaultsCopyKeys = @[ + // YTLitePlus - gathered using get_keys.py + @"YTTapToSeek_enabled", @"alwaysShowRemainingTime_enabled", @"bigYTMiniPlayer_enabled", @"castConfirm_enabled", + @"disableAccountSection_enabled", @"disableAmbientModeFullscreen_enabled", + @"disableAmbientModePortrait_enabled", @"disableAutoplaySection_enabled", @"disableCollapseButton_enabled", + @"disableEngagementOverlay_enabled", @"disableLiveChatSection_enabled", + @"disableManageAllHistorySection_enabled", @"disableNotificationsSection_enabled", + @"disablePrivacySection_enabled", @"disablePullToFull_enabled", @"disableRemainingTime_enabled", + @"disableTryNewFeaturesSection_enabled", @"disableVideoQualityPreferencesSection_enabled", + @"disableYourDataInYouTubeSection_enabled", @"enableSaveToButton_enabled", @"enableShareButton_enabled", + @"enableVersionSpoofer_enabled", @"fixCasting_enabled", @"flex_enabled", @"fullscreenToTheRight_enabled", + @"hideAutoplayMiniPreview_enabled", @"hideCastButton_enabled", @"hideHUD_enabled", @"hideHeatwaves_enabled", + @"hideHomeTab_enabled", @"hidePreviewCommentSection_enabled", @"hideRightPanel_enabled", + @"hideSpeedToast_enabled", @"hideSponsorBlockButton_enabled", @"hideVideoPlayerShadowOverlayButtons_enabled", + @"iPadLayout_enabled", @"iPhoneLayout_enabled", @"inline_muted_playback_enabled", @"lowContrastMode_enabled", + @"newSettingsUI_enabled", @"oledKeyBoard_enabled", @"playerGesturesHapticFeedback_enabled", + @"playerGestures_enabled", @"seekAnywhere_enabled", @"switchCopyandPasteFunctionality_enabled", + @"videoPlayerButton_enabled", @"ytNoModernUI_enabled", @"ytStartupAnimation_enabled", + // DEMC - https://github.com/therealFoxster/DontEatMyContent/blob/master/Tweak.h + @"DEMC_enabled", @"DEMC_colorViewsEnabled", @"DEMC_safeAreaConstant", @"DEMC_disableAmbientMode", + @"DEMC_limitZoomToFill", @"DEMC_enableForAllVideos", + // iSponsorBlock cannot be exported using this method - it is also being removed in v5 + // Return-YouTube-Dislike - https://github.com/PoomSmart/Return-YouTube-Dislikes/blob/main/TweakSettings.h + @"RYD-ENABLED", @"RYD-VOTE-SUBMISSION", @"RYD-EXACT-LIKE-NUMBER", @"RYD-EXACT-NUMBER", + // All YTVideoOverlay Tweaks - https://github.com/PoomSmart/YTVideoOverlay/blob/0fc6d29d1aa9e75f8c13d675daec9365f753d45e/Tweak.x#L28C1-L41C84 + @"YTVideoOverlay-YouLoop-Enabled", @"YTVideoOverlay-YouTimeStamp-Enabled", @"YTVideoOverlay-YouMute-Enabled", + @"YTVideoOverlay-YouQuality-Enabled", @"YTVideoOverlay-YouLoop-Position", @"YTVideoOverlay-YouTimeStamp-Position", + @"YTVideoOverlay-YouMute-Position", @"YTVideoOverlay-YouQuality-Position", + // YouPiP - https://github.com/PoomSmart/YouPiP/blob/main/Header.h + @"YouPiPPosition", @"CompatibilityModeKey", @"PiPActivationMethodKey", @"PiPActivationMethod2Key", + @"NoMiniPlayerPiPKey", @"NonBackgroundableKey", + // YTABConfig cannot be reasonably exported using this method + // YTHoldForSpeed will be removed in v5 + // YouTube Plus / YTLite cannot be exported using this method + // YTUHD - https://github.com/PoomSmart/YTUHD/blob/master/Header.h + @"EnableVP9", @"AllVP9", + // Useful YouTube Keys + @"inline_muted_playback_enabled", +]; + + +// Some default values to ignore when exporting settings +NSDictionary *NSUserDefaultsCopyKeysDefaults = @{ + @"fixCasting_enabled": @1, + @"inline_muted_playback_enabled": @5, + @"newSettingsUI_enabled": @1, + @"DEMC_safeAreaConstant": @21.5, + @"RYD-ENABLED": @1, + @"RYD-VOTE-SUBMISSION": @1, + // Duplicate keys are not allowed in NSDictionary. If present, only the last one will be kept. +}; \ No newline at end of file diff --git a/Themes.xm b/Themes.xm new file mode 100644 index 0000000..50457ff --- /dev/null +++ b/Themes.xm @@ -0,0 +1,838 @@ +#import "../YTLitePlus.h" + +static BOOL isDarkMode() { + return ([[NSUserDefaults standardUserDefaults] integerForKey:@"page_style"] == 1); +} +static BOOL oldDarkTheme() { + return ([[NSUserDefaults standardUserDefaults] integerForKey:@"appTheme"] == 1); +} + +// Themes.xm - Theme Options +// Old dark theme (gray) +%group gOldDarkTheme +UIColor *customColor = [UIColor colorWithRed:0.129 green:0.129 blue:0.129 alpha:1.0]; +%hook YTCommonColorPalette +- (UIColor *)background1 { + return self.pageStyle == 1 ? customColor : %orig; +} +- (UIColor *)background2 { + return self.pageStyle == 1 ? customColor : %orig; +} +- (UIColor *)background3 { + return self.pageStyle == 1 ? customColor : %orig; +} +- (UIColor *)brandBackgroundSolid { + return self.pageStyle == 1 ? customColor : %orig; +} +- (UIColor *)brandBackgroundPrimary { + return self.pageStyle == 1 ? customColor : %orig; +} +- (UIColor *)brandBackgroundSecondary { + return self.pageStyle == 1 ? [customColor colorWithAlphaComponent:0.9] : %orig; +} +- (UIColor *)raisedBackground { + return self.pageStyle == 1 ? customColor : %orig; +} +- (UIColor *)staticBrandBlack { + return self.pageStyle == 1 ? customColor : %orig; +} +- (UIColor *)generalBackgroundA { + return self.pageStyle == 1 ? customColor : %orig; +} +- (UIColor *)generalBackgroundB { + return self.pageStyle == 1 ? customColor : %orig; +} +- (UIColor *)baseBackground { + return self.pageStyle == 1 ? customColor : %orig; +} +- (UIColor *)menuBackground { + return self.pageStyle == 1 ? customColor : %orig; +} +%end +%hook SponsorBlockSettingsController +- (void)viewDidLoad { + if (self.traitCollection.userInterfaceStyle == UIUserInterfaceStyleDark) { + %orig; + self.tableView.backgroundColor = customColor; + } else { return %orig; } +} +%end +%hook SponsorBlockViewController +- (void)viewDidLoad { + if (self.traitCollection.userInterfaceStyle == UIUserInterfaceStyleDark) { + %orig; + self.view.backgroundColor = customColor; + } else { return %orig; } +} +%end +%hook YTAsyncCollectionView +- (void)setBackgroundColor:(UIColor *)color { + if ([self.nextResponder isKindOfClass:NSClassFromString(@"YTRelatedVideosCollectionViewController")]) { + color = [UIColor clearColor]; + } else if ([self.nextResponder isKindOfClass:NSClassFromString(@"YTFullscreenMetadataHighlightsCollectionViewController")]) { + color = [UIColor clearColor]; + } else { + return isDarkMode() ? %orig(customColor) : %orig; + } + %orig; +} +- (UIColor *)darkBackgroundColor { + return isDarkMode() ? customColor : %orig; +} +- (void)setDarkBackgroundColor:(UIColor *)color { + return isDarkMode() ? %orig(customColor) : %orig; +} +- (void)layoutSubviews { + %orig(); + if ([self.nextResponder isKindOfClass:NSClassFromString(@"YTWatchNextResultsViewController")]) { + if (isDarkMode()) { + self.subviews[0].subviews[0].backgroundColor = customColor; + } + } +} +%end + +// Hide separators +%hook YTCollectionSeparatorView +- (void)setHidden:(BOOL)arg1 { + %orig(YES); +} +%end + +%hook ASScrollView +- (void)setBackgroundColor:(UIColor *)color { + return isDarkMode() ? %orig(customColor) : %orig; +} +%end + +%hook YTPivotBarView +- (void)setBackgroundColor:(UIColor *)color { + return isDarkMode() ? %orig(customColor) : %orig; +} +%end +%hook YTHeaderView +- (void)setBackgroundColor:(UIColor *)color { + return isDarkMode() ? %orig(customColor) : %orig; +} +%end +%hook YTSubheaderContainerView +- (void)setBackgroundColor:(UIColor *)color { + return isDarkMode() ? %orig(customColor) : %orig; +} +%end +%hook YTAppView +- (void)setBackgroundColor:(UIColor *)color { + return isDarkMode() ? %orig(customColor) : %orig; +} +%end +%hook YTCollectionView +- (void)setBackgroundColor:(UIColor *)color { + return isDarkMode() ? %orig(customColor) : %orig; +} +%end +%hook YTChannelListSubMenuView +- (void)setBackgroundColor:(UIColor *)color { + return isDarkMode() ? %orig(customColor) : %orig; +} +%end +%hook YTSettingsCell +- (void)setBackgroundColor:(UIColor *)color { + return isDarkMode() ? %orig(customColor) : %orig; +} +%end +%hook YTSlideForActionsView +- (void)setBackgroundColor:(UIColor *)color { + return isDarkMode() ? %orig(customColor) : %orig; +} +%end +%hook YTPageView +- (void)setBackgroundColor:(UIColor *)color { + return isDarkMode() ? %orig(customColor) : %orig; +} +%end +%hook YTWatchView +- (void)setBackgroundColor:(UIColor *)color { + return isDarkMode() ? %orig(customColor) : %orig; +} +%end +%hook YTPlaylistMiniBarView +- (void)setBackgroundColor:(UIColor *)color { + return isDarkMode() ? %orig(customColor) : %orig; +} +%end +%hook YTEngagementPanelView +- (void)setBackgroundColor:(UIColor *)color { + return isDarkMode() ? %orig(customColor) : %orig; +} +%end +%hook YTEngagementPanelHeaderView +- (void)setBackgroundColor:(UIColor *)color { + return isDarkMode() ? %orig(customColor) : %orig; +} +%end +%hook YTPlaylistPanelControlsView +- (void)setBackgroundColor:(UIColor *)color { + return isDarkMode() ? %orig(customColor) : %orig; +} +%end +%hook YTHorizontalCardListView +- (void)setBackgroundColor:(UIColor *)color { + return isDarkMode() ? %orig(customColor) : %orig; +} +%end +%hook YTWatchMiniBarView +- (void)setBackgroundColor:(UIColor *)color { + return isDarkMode() ? %orig(customColor) : %orig; +} +%end +%hook YTCreateCommentAccessoryView +- (void)setBackgroundColor:(UIColor *)color { + return isDarkMode() ? %orig(customColor) : %orig; +} +%end +%hook YTCreateCommentTextView +- (void)setBackgroundColor:(UIColor *)color { + return isDarkMode() ? %orig(customColor) : %orig; +} +%end +%hook YTSearchView +- (void)setBackgroundColor:(UIColor *)color { + return isDarkMode() ? %orig(customColor) : %orig; +} +%end +%hook YTSearchBoxView +- (void)setBackgroundColor:(UIColor *)color { + return isDarkMode() ? %orig(customColor) : %orig; +} +%end +%hook YTTabTitlesView +- (void)setBackgroundColor:(UIColor *)color { + return isDarkMode() ? %orig(customColor) : %orig; +} +%end +%hook YTPrivacyTosFooterView +- (void)setBackgroundColor:(UIColor *)color { + return isDarkMode() ? %orig(customColor) : %orig; +} +%end +%hook YTOfflineStorageUsageView +- (void)setBackgroundColor:(UIColor *)color { + return isDarkMode() ? %orig(customColor) : %orig; +} +%end +%hook YTInlineSignInView +- (void)setBackgroundColor:(UIColor *)color { + return isDarkMode() ? %orig(customColor) : %orig; +} +%end +%hook YTFeedChannelFilterHeaderView +- (void)setBackgroundColor:(UIColor *)color { + return isDarkMode() ? %orig(customColor) : %orig; +} +%end +%hook YCHLiveChatView +- (void)setBackgroundColor:(UIColor *)color { + return isDarkMode() ? %orig(customColor) : %orig; +} +%end +%hook YCHLiveChatActionPanelView +- (void)setBackgroundColor:(UIColor *)color { + return isDarkMode() ? %orig(customColor) : %orig; +} +%end +%hook YTEmojiTextView +- (void)setBackgroundColor:(UIColor *)color { + return isDarkMode() ? %orig(customColor) : %orig; +} +%end +%hook YTTopAlignedView +- (void)setBackgroundColor:(UIColor *)color { + return isDarkMode() ? %orig(customColor) : %orig; +} +- (void)layoutSubviews { + %orig(); + if (isDarkMode()) { + MSHookIvar(self, "_contentView").backgroundColor = customColor; + } +} +%end +%hook GOODialogView +- (void)setBackgroundColor:(UIColor *)color { + return isDarkMode() ? %orig(customColor) : %orig; +} +%end +%hook YTNavigationBar +- (void)setBackgroundColor:(UIColor *)color { + return isDarkMode() ? %orig(customColor) : %orig; +} +- (void)setBarTintColor:(UIColor *)color { + return isDarkMode() ? %orig(customColor) : %orig; +} +%end +%hook YTChannelMobileHeaderView +- (void)setBackgroundColor:(UIColor *)color { + return isDarkMode() ? %orig(customColor) : %orig; +} +%end +%hook YTChannelSubMenuView +- (void)setBackgroundColor:(UIColor *)color { + return isDarkMode() ? %orig(customColor) : %orig; +} +%end +%hook YTWrapperSplitView +- (void)setBackgroundColor:(UIColor *)color { + return isDarkMode() ? %orig(customColor) : %orig; +} +%end +%hook YTReelShelfCell +- (void)setBackgroundColor:(UIColor *)color { + return isDarkMode() ? %orig(customColor) : %orig; +} +%end +%hook YTReelShelfItemView +- (void)setBackgroundColor:(UIColor *)color { + return isDarkMode() ? %orig(customColor) : %orig; +} +%end +%hook YTReelShelfView +- (void)setBackgroundColor:(UIColor *)color { + return isDarkMode() ? %orig(customColor) : %orig; +} +%end +%hook YTCommentView +- (void)setBackgroundColor:(UIColor *)color { + return isDarkMode() ? %orig(customColor) : %orig; +} +%end +%hook YTChannelListSubMenuAvatarView +- (void)setBackgroundColor:(UIColor *)color { + return isDarkMode() ? %orig(customColor) : %orig; +} +%end +%hook YTSearchBarView +- (void)setBackgroundColor:(UIColor *)color { + return isDarkMode() ? %orig(customColor) : %orig; +} +%end +%hook YTDialogContainerScrollView +- (void)setBackgroundColor:(UIColor *)color { + return isDarkMode() ? %orig(customColor) : %orig; +} +%end +%hook YTShareTitleView +- (void)setBackgroundColor:(UIColor *)color { + return isDarkMode() ? %orig(customColor) : %orig; +} +%end +%hook YTShareBusyView +- (void)setBackgroundColor:(UIColor *)color { + return isDarkMode() ? %orig(customColor) : %orig; +} +%end +%hook YTELMView +- (void)setBackgroundColor:(UIColor *)color { + return isDarkMode() ? %orig(customColor) : %orig; +} +%end +%hook YTActionSheetHeaderView +- (void)setBackgroundColor:(UIColor *)color { + return isDarkMode() ? %orig(customColor) : %orig; +} +%end +%hook YTShareMainView +- (void)layoutSubviews { + %orig(); + if (isDarkMode()) { + MSHookIvar(self, "_cancelButton").backgroundColor = customColor; + MSHookIvar(self, "_safeArea").backgroundColor = customColor; + } +} +%end +%hook _ASDisplayView +- (void)layoutSubviews { + %orig; + if (isDarkMode()) { + UIResponder *responder = [self nextResponder]; + while (responder != nil) { + if ([responder isKindOfClass:NSClassFromString(@"YTActionSheetDialogViewController")]) { + self.backgroundColor = customColor; + } + if ([responder isKindOfClass:NSClassFromString(@"YTPanelLoadingStrategyViewController")]) { + self.backgroundColor = customColor; + } + if ([responder isKindOfClass:NSClassFromString(@"YTTabHeaderElementsViewController")]) { + self.backgroundColor = customColor; + } + if ([responder isKindOfClass:NSClassFromString(@"YTEditSheetControllerElementsContentViewController")]) { + self.backgroundColor = customColor; + } + responder = [responder nextResponder]; + } + } +} +- (void)didMoveToWindow { + %orig; + if (isDarkMode()) { + if ([self.nextResponder isKindOfClass:%c(ASScrollView)]) { self.backgroundColor = [UIColor clearColor]; } + if ([self.accessibilityIdentifier isEqualToString:@"eml.cvr"]) { self.backgroundColor = customColor; } + if ([self.accessibilityIdentifier isEqualToString:@"rich_header"]) { self.backgroundColor = customColor; } + if ([self.accessibilityIdentifier isEqualToString:@"id.ui.comment_cell"]) { self.backgroundColor = customColor; } + if ([self.accessibilityIdentifier isEqualToString:@"id.ui.cancel.button"]) { self.superview.backgroundColor = [UIColor clearColor]; } + if ([self.accessibilityIdentifier isEqualToString:@"id.elements.components.comment_composer"]) { self.backgroundColor = customColor; } + if ([self.accessibilityIdentifier isEqualToString:@"id.elements.components.filter_chip_bar"]) { self.backgroundColor = customColor; } + if ([self.accessibilityIdentifier isEqualToString:@"id.elements.components.video_list_entry"]) { self.backgroundColor = customColor; } + if ([self.accessibilityIdentifier isEqualToString:@"id.comment.guidelines_text"]) { self.superview.backgroundColor = customColor; } + if ([self.accessibilityIdentifier isEqualToString:@"id.comment.channel_guidelines_bottom_sheet_container"]) { self.backgroundColor = customColor; } + if ([self.accessibilityIdentifier isEqualToString:@"id.comment.channel_guidelines_entry_banner_container"]) { self.backgroundColor = customColor; } + if ([self.accessibilityIdentifier isEqualToString:@"id.comment.comment_group_detail_container"]) { self.backgroundColor = [UIColor clearColor]; } + } +} +%end +%hook YTCinematicContainerView +- (void)setHidden:(BOOL)arg1 { + %orig(YES); +} +%end +%end + +// OLED dark mode by @BandarHL and modified by @arichorn +/* +%group gOLED +%hook YTCommonColorPalette +- (UIColor *)background1 { + return self.pageStyle == 1 ? [UIColor blackColor] : %orig; +} +- (UIColor *)background2 { + return self.pageStyle == 1 ? [UIColor blackColor] : %orig; +} +- (UIColor *)background3 { + return self.pageStyle == 1 ? [UIColor blackColor] : %orig; +} +- (UIColor *)brandBackgroundSolid { + return self.pageStyle == 1 ? [UIColor blackColor] : %orig; +} +- (UIColor *)brandBackgroundPrimary { + return self.pageStyle == 1 ? [UIColor blackColor] : %orig; +} +- (UIColor *)brandBackgroundSecondary { + return self.pageStyle == 1 ? [[UIColor blackColor] colorWithAlphaComponent:0.9] : %orig; +} +- (UIColor *)raisedBackground { + return self.pageStyle == 1 ? [UIColor blackColor] : %orig; +} +- (UIColor *)staticBrandBlack { + return self.pageStyle == 1 ? [UIColor blackColor] : %orig; +} +- (UIColor *)generalBackgroundA { + return self.pageStyle == 1 ? [UIColor blackColor] : %orig; +} +- (UIColor *)generalBackgroundB { + return self.pageStyle == 1 ? [UIColor blackColor] : %orig; +} +- (UIColor *)baseBackground { + return self.pageStyle == 1 ? [UIColor blackColor] : %orig; +} +- (UIColor *)menuBackground { + return self.pageStyle == 1 ? [UIColor blackColor] : %orig; +} +%end +%hook SponsorBlockSettingsController +- (void)viewDidLoad { + if (self.traitCollection.userInterfaceStyle == UIUserInterfaceStyleDark) { + %orig; + self.tableView.backgroundColor = [UIColor blackColor]; + } else { return %orig; } +} +%end +%hook SponsorBlockViewController +- (void)viewDidLoad { + if (self.traitCollection.userInterfaceStyle == UIUserInterfaceStyleDark) { + %orig; + self.view.backgroundColor = [UIColor blackColor]; + } else { return %orig; } +} +%end +%hook YTAsyncCollectionView +- (void)setBackgroundColor:(UIColor *)color { + if ([self.nextResponder isKindOfClass:NSClassFromString(@"YTRelatedVideosCollectionViewController")]) { + color = [UIColor clearColor]; + } else if ([self.nextResponder isKindOfClass:NSClassFromString(@"YTFullscreenMetadataHighlightsCollectionViewController")]) { + color = [UIColor clearColor]; + } else { + return isDarkMode() ? %orig([UIColor blackColor]) : %orig; + } + %orig; +} +- (UIColor *)darkBackgroundColor { + return isDarkMode() ? [UIColor blackColor] : %orig; +} +- (void)setDarkBackgroundColor:(UIColor *)color { + return isDarkMode() ? %orig([UIColor blackColor]) : %orig; +} +- (void)layoutSubviews { + %orig(); + if ([self.nextResponder isKindOfClass:NSClassFromString(@"YTWatchNextResultsViewController")]) { + if (isDarkMode()) { + self.subviews[0].subviews[0].backgroundColor = [UIColor blackColor]; + } + } +} +%end + +// Hide separators +%hook YTCollectionSeparatorView +- (void)setHidden:(BOOL)arg1 { + %orig(YES); +} +%end + +%hook YTPivotBarView +- (void)setBackgroundColor:(UIColor *)color { + return isDarkMode() ? %orig([UIColor blackColor]) : %orig; +} +%end + +%hook ASScrollView +- (void)setBackgroundColor:(UIColor *)color { + return isDarkMode() ? %orig([UIColor blackColor]) : %orig; +} +%end + +%hook YTHeaderView +- (void)setBackgroundColor:(UIColor *)color { + return isDarkMode() ? %orig([UIColor blackColor]) : %orig; +} +%end +%hook YTSubheaderContainerView +- (void)setBackgroundColor:(UIColor *)color { + return isDarkMode() ? %orig([UIColor blackColor]) : %orig; +} +%end +%hook YTAppView +- (void)setBackgroundColor:(UIColor *)color { + return isDarkMode() ? %orig([UIColor blackColor]) : %orig; +} +%end +%hook YTCollectionView +- (void)setBackgroundColor:(UIColor *)color { + return isDarkMode() ? %orig([UIColor blackColor]) : %orig; +} +%end +%hook YTChannelListSubMenuView +- (void)setBackgroundColor:(UIColor *)color { + return isDarkMode() ? %orig([UIColor blackColor]) : %orig; +} +%end +%hook YTSettingsCell +- (void)setBackgroundColor:(UIColor *)color { + return isDarkMode() ? %orig([UIColor blackColor]) : %orig; +} +%end +%hook YTSlideForActionsView +- (void)setBackgroundColor:(UIColor *)color { + return isDarkMode() ? %orig([UIColor blackColor]) : %orig; +} +%end +%hook YTPageView +- (void)setBackgroundColor:(UIColor *)color { + return isDarkMode() ? %orig([UIColor blackColor]) : %orig; +} +%end +%hook YTWatchView +- (void)setBackgroundColor:(UIColor *)color { + return isDarkMode() ? %orig([UIColor blackColor]) : %orig; +} +%end +%hook YTPlaylistMiniBarView +- (void)setBackgroundColor:(UIColor *)color { + return isDarkMode() ? %orig([UIColor blackColor]) : %orig; +} +%end +%hook YTEngagementPanelView +- (void)setBackgroundColor:(UIColor *)color { + return isDarkMode() ? %orig([UIColor blackColor]) : %orig; +} +%end +%hook YTEngagementPanelHeaderView +- (void)setBackgroundColor:(UIColor *)color { + return isDarkMode() ? %orig([UIColor blackColor]) : %orig; +} +%end +%hook YTPlaylistPanelControlsView +- (void)setBackgroundColor:(UIColor *)color { + return isDarkMode() ? %orig([UIColor blackColor]) : %orig; +} +%end +%hook YTHorizontalCardListView +- (void)setBackgroundColor:(UIColor *)color { + return isDarkMode() ? %orig([UIColor blackColor]) : %orig; +} +%end +%hook YTWatchMiniBarView +- (void)setBackgroundColor:(UIColor *)color { + return isDarkMode() ? %orig([UIColor blackColor]) : %orig; +} +%end +%hook YTCreateCommentAccessoryView +- (void)setBackgroundColor:(UIColor *)color { + return isDarkMode() ? %orig([UIColor blackColor]) : %orig; +} +%end +%hook YTCreateCommentTextView +- (void)setBackgroundColor:(UIColor *)color { + return isDarkMode() ? %orig([UIColor blackColor]) : %orig; +} +%end +%hook YTSearchView +- (void)setBackgroundColor:(UIColor *)color { + return isDarkMode() ? %orig([UIColor blackColor]) : %orig; +} +%end +%hook YTSearchBoxView +- (void)setBackgroundColor:(UIColor *)color { + return isDarkMode() ? %orig([UIColor blackColor]) : %orig; +} +%end +%hook YTTabTitlesView +- (void)setBackgroundColor:(UIColor *)color { + return isDarkMode() ? %orig([UIColor blackColor]) : %orig; +} +%end +%hook YTPrivacyTosFooterView +- (void)setBackgroundColor:(UIColor *)color { + return isDarkMode() ? %orig([UIColor blackColor]) : %orig; +} +%end +%hook YTOfflineStorageUsageView +- (void)setBackgroundColor:(UIColor *)color { + return isDarkMode() ? %orig([UIColor blackColor]) : %orig; +} +%end +%hook YTInlineSignInView +- (void)setBackgroundColor:(UIColor *)color { + return isDarkMode() ? %orig([UIColor blackColor]) : %orig; +} +%end +%hook YTFeedChannelFilterHeaderView +- (void)setBackgroundColor:(UIColor *)color { + return isDarkMode() ? %orig([UIColor blackColor]) : %orig; +} +%end +%hook YCHLiveChatView +- (void)setBackgroundColor:(UIColor *)color { + return isDarkMode() ? %orig([UIColor blackColor]) : %orig; +} +%end +%hook YCHLiveChatActionPanelView +- (void)setBackgroundColor:(UIColor *)color { + return isDarkMode() ? %orig([UIColor blackColor]) : %orig; +} +%end +%hook YTEmojiTextView +- (void)setBackgroundColor:(UIColor *)color { + return isDarkMode() ? %orig([UIColor blackColor]) : %orig; +} +%end +%hook YTTopAlignedView +- (void)setBackgroundColor:(UIColor *)color { + return isDarkMode() ? %orig([UIColor blackColor]) : %orig; +} +- (void)layoutSubviews { + %orig(); + if (isDarkMode()) { + MSHookIvar(self, "_contentView").backgroundColor = [UIColor blackColor]; + } +} +%end +%hook GOODialogView +- (void)setBackgroundColor:(UIColor *)color { + return isDarkMode() ? %orig([UIColor blackColor]) : %orig; +} +%end +%hook YTNavigationBar +- (void)setBackgroundColor:(UIColor *)color { + return isDarkMode() ? %orig([UIColor blackColor]) : %orig; +} +- (void)setBarTintColor:(UIColor *)color { + return isDarkMode() ? %orig([UIColor blackColor]) : %orig; +} +%end +%hook YTChannelMobileHeaderView +- (void)setBackgroundColor:(UIColor *)color { + return isDarkMode() ? %orig([UIColor blackColor]) : %orig; +} +%end +%hook YTChannelSubMenuView +- (void)setBackgroundColor:(UIColor *)color { + return isDarkMode() ? %orig([UIColor blackColor]) : %orig; +} +%end +%hook YTWrapperSplitView +- (void)setBackgroundColor:(UIColor *)color { + return isDarkMode() ? %orig([UIColor blackColor]) : %orig; +} +%end +%hook YTReelShelfCell +- (void)setBackgroundColor:(UIColor *)color { + return isDarkMode() ? %orig([UIColor blackColor]) : %orig; +} +%end +%hook YTReelShelfItemView +- (void)setBackgroundColor:(UIColor *)color { + return isDarkMode() ? %orig([UIColor blackColor]) : %orig; +} +%end +%hook YTReelShelfView +- (void)setBackgroundColor:(UIColor *)color { + return isDarkMode() ? %orig([UIColor blackColor]) : %orig; +} +%end +%hook YTCommentView +- (void)setBackgroundColor:(UIColor *)color { + return isDarkMode() ? %orig([UIColor blackColor]) : %orig; +} +%end +%hook YTChannelListSubMenuAvatarView +- (void)setBackgroundColor:(UIColor *)color { + return isDarkMode() ? %orig([UIColor blackColor]) : %orig; +} +%end +%hook YTSearchBarView +- (void)setBackgroundColor:(UIColor *)color { + return isDarkMode() ? %orig([UIColor blackColor]) : %orig; +} +%end +%hook YTDialogContainerScrollView +- (void)setBackgroundColor:(UIColor *)color { + return isDarkMode() ? %orig([UIColor blackColor]) : %orig; +} +%end +%hook YTShareTitleView +- (void)setBackgroundColor:(UIColor *)color { + return isDarkMode() ? %orig([UIColor blackColor]) : %orig; +} +%end +%hook YTShareBusyView +- (void)setBackgroundColor:(UIColor *)color { + return isDarkMode() ? %orig([UIColor blackColor]) : %orig; +} +%end +%hook YTELMView +- (void)setBackgroundColor:(UIColor *)color { + return isDarkMode() ? %orig([UIColor blackColor]) : %orig; +} +%end +%hook YTActionSheetHeaderView +- (void)setBackgroundColor:(UIColor *)color { + return isDarkMode() ? %orig([UIColor blackColor]) : %orig; +} +%end +%hook YTShareMainView +- (void)layoutSubviews { + %orig(); + if (isDarkMode()) { + MSHookIvar(self, "_cancelButton").backgroundColor = [UIColor blackColor]; + MSHookIvar(self, "_safeArea").backgroundColor = [UIColor blackColor]; + } +} +%end +%hook _ASDisplayView +- (void)layoutSubviews { + %orig; + if (isDarkMode()) { + UIResponder *responder = [self nextResponder]; + while (responder != nil) { + if ([responder isKindOfClass:NSClassFromString(@"YTActionSheetDialogViewController")]) { + self.backgroundColor = [UIColor blackColor]; + } + if ([responder isKindOfClass:NSClassFromString(@"YTPanelLoadingStrategyViewController")]) { + self.backgroundColor = [UIColor blackColor]; + } + if ([responder isKindOfClass:NSClassFromString(@"YTTabHeaderElementsViewController")]) { + self.backgroundColor = [UIColor blackColor]; + } + if ([responder isKindOfClass:NSClassFromString(@"YTEditSheetControllerElementsContentViewController")]) { + self.backgroundColor = [UIColor blackColor]; + } + responder = [responder nextResponder]; + } + } +} +- (void)didMoveToWindow { + %orig; + if (isDarkMode()) { + if ([self.nextResponder isKindOfClass:%c(ASScrollView)]) { self.backgroundColor = [UIColor clearColor]; } + if ([self.accessibilityIdentifier isEqualToString:@"eml.cvr"]) { self.backgroundColor = [UIColor blackColor]; } + if ([self.accessibilityIdentifier isEqualToString:@"rich_header"]) { self.backgroundColor = [UIColor blackColor]; } + if ([self.accessibilityIdentifier isEqualToString:@"id.ui.comment_cell"]) { self.backgroundColor = [UIColor blackColor]; } + if ([self.accessibilityIdentifier isEqualToString:@"id.ui.cancel.button"]) { self.superview.backgroundColor = [UIColor clearColor]; } + if ([self.accessibilityIdentifier isEqualToString:@"id.elements.components.comment_composer"]) { self.backgroundColor = [UIColor blackColor]; } + if ([self.accessibilityIdentifier isEqualToString:@"id.elements.components.filter_chip_bar"]) { self.backgroundColor = [UIColor blackColor]; } + if ([self.accessibilityIdentifier isEqualToString:@"id.elements.components.video_list_entry"]) { self.backgroundColor = [UIColor blackColor]; } + if ([self.accessibilityIdentifier isEqualToString:@"id.comment.guidelines_text"]) { self.superview.backgroundColor = [UIColor blackColor]; } + if ([self.accessibilityIdentifier isEqualToString:@"id.comment.channel_guidelines_bottom_sheet_container"]) { self.backgroundColor = [UIColor blackColor]; } + if ([self.accessibilityIdentifier isEqualToString:@"id.comment.channel_guidelines_entry_banner_container"]) { self.backgroundColor = [UIColor blackColor]; } + if ([self.accessibilityIdentifier isEqualToString:@"id.comment.comment_group_detail_container"]) { self.backgroundColor = [UIColor clearColor]; } + } +} +%end +%hook YTCinematicContainerView +- (void)setHidden:(BOOL)arg1 { + %orig(YES); +} +%end +%end +*/ + +// OLED keyboard by @ichitaso <3 - http://gist.github.com/ichitaso/935100fd53a26f18a9060f7195a1be0e +%group gOLEDKB +%hook TUIEmojiSearchView +- (void)didMoveToWindow { + %orig; + self.backgroundColor = [UIColor blackColor]; +} +%end + +%hook UIPredictionViewController +- (void)loadView { + %orig; + [self.view setBackgroundColor:[UIColor blackColor]]; +} +%end + +%hook UICandidateViewController +- (void)loadView { + %orig; + [self.view setBackgroundColor:[UIColor blackColor]]; +} +%end + +%hook UIKeyboardDockView +- (void)didMoveToWindow { + %orig; + self.backgroundColor = [UIColor blackColor]; +} +%end + +%hook UIKeyboardLayoutStar +- (void)didMoveToWindow { + %orig; + self.backgroundColor = [UIColor blackColor]; +} +%end + +%hook UIKBRenderConfig // Prediction text color +- (void)setLightKeyboard:(BOOL)arg1 { %orig(NO); } +%end +%end + +# pragma mark - ctor +%ctor { + %init; + if (IsEnabled(@"oledKeyBoard_enabled")) { + %init(gOLEDKB); + } + if (oldDarkTheme()) { + %init(gOldDarkTheme); + } +} diff --git a/VersionSpooferLite.xm b/VersionSpooferLite.xm new file mode 100644 index 0000000..78f2889 --- /dev/null +++ b/VersionSpooferLite.xm @@ -0,0 +1,107 @@ +#import "../YTLitePlus.h" + + +static int appVersionSpoofer() { + return [[NSUserDefaults standardUserDefaults] integerForKey:@"versionSpoofer"]; +} +static BOOL version0() { + return IsEnabled(@"enableVersionSpoofer_enabled") && appVersionSpoofer() == 0; +} +static BOOL version1() { + return IsEnabled(@"enableVersionSpoofer_enabled") && appVersionSpoofer() == 1; +} +static BOOL version2() { + return IsEnabled(@"enableVersionSpoofer_enabled") && appVersionSpoofer() == 2; +} +static BOOL version3() { + return IsEnabled(@"enableVersionSpoofer_enabled") && appVersionSpoofer() == 3; +} +static BOOL version4() { + return IsEnabled(@"enableVersionSpoofer_enabled") && appVersionSpoofer() == 4; +} +static BOOL version5() { + return IsEnabled(@"enableVersionSpoofer_enabled") && appVersionSpoofer() == 5; +} +static BOOL version6() { + return IsEnabled(@"enableVersionSpoofer_enabled") && appVersionSpoofer() == 6; +} +static BOOL version7() { + return IsEnabled(@"enableVersionSpoofer_enabled") && appVersionSpoofer() == 7; +} + +%group gVersion0 +%hook YTVersionUtils // Last v18 App Version ++ (NSString *)appVersion { return @"18.49.3"; } +%end +%end + +%group gVersion1 +%hook YTVersionUtils // Brings back Library Tab ++ (NSString *)appVersion { return @"18.34.5"; } +%end +%end + +%group gVersion2 +%hook YTVersionUtils // Removes Playables in Explore ++ (NSString *)appVersion { return @"18.33.3"; } +%end +%end + +%group gVersion3 +%hook YTVersionUtils // Fixes YTClassicVideoQuality + YTSpeed ++ (NSString *)appVersion { return @"18.18.2"; } +%end +%end + +%group gVersion4 +%hook YTVersionUtils // First v18 App Version ++ (NSString *)appVersion { return @"18.01.2"; } +%end +%end + +%group gVersion5 +%hook YTVersionUtils // Last v17 App Version ++ (NSString *)appVersion { return @"17.49.6"; } +%end +%end + +%group gVersion6 +%hook YTVersionUtils // v17.38.10 Fixes LowContrastMode + No Rounded Thumbnails ++ (NSString *)appVersion { return @"17.38.10"; } +%end +%end + +%group gVersion7 +%hook YTVersionUtils // Oldest Supported App Version (v17) ++ (NSString *)appVersion { return @"17.33.2"; } +%end +%end + +# pragma mark - ctor +%ctor { + %init; + if (version0()) { // 0 + %init(gVersion0); + } + if (version1()) { // 1 + %init(gVersion1); + } + if (version2()) { // 2 + %init(gVersion2); + } + if (version3()) { // 3 + %init(gVersion3); + } + if (version4()) { // 4 + %init(gVersion4); + } + if (version5()) { // 5 + %init(gVersion5); + } + if (version6()) { // 6 + %init(gVersion6); + } + if (version7()) { // 7 + %init(gVersion7); + } +} diff --git a/get_keys.py b/get_keys.py new file mode 100644 index 0000000..dd6f221 --- /dev/null +++ b/get_keys.py @@ -0,0 +1,100 @@ +import re +import os + +def extract_values_from_file(file_path): + """ + Extracts keys that match the pattern @\"_enabled\" from the given file. + + Args: + file_path (str): The path to the file to be searched. + + Returns: + list: A list of matching keys found in the file. + """ + # Define the regex pattern to match the strings that resemble the given examples + pattern = r'@\"[a-zA-Z0-9_]+_enabled\"' + matches = [] + + try: + # Read the content of the file + with open(file_path, 'r') as file: + file_content = file.read() + + # Find all matches + matches = re.findall(pattern, file_content) + except Exception as e: + print(f"Error reading {file_path}: {e}") + + return matches + +def format_output(keys): + """ + Formats the keys with indentation and line breaks if the segment exceeds 120 characters (116 excluding indentation). + + Args: + keys (list): The list of keys to be formatted. + + Returns: + str: A formatted string with the keys. + """ + indent = " " * 4 + line_length_limit = 116 # Limit excluding indentation + current_line = indent + formatted_output = "" + + for key in keys: + # Check if adding the next key would exceed the line length limit + if len(current_line) + len(key) + 2 > line_length_limit: # +2 accounts for the comma and space + # Add the current line to the formatted output and start a new line + formatted_output += current_line.rstrip(", ") + ",\n" + current_line = indent # Start a new indented line + + # Add the key to the current line + current_line += key + ", " + + # Add the last line to the output + formatted_output += current_line.rstrip(", ") # Remove trailing comma and space from the final line + return formatted_output + +def find_and_extract_keys(): + """ + Recursively searches for .xm and .h files in the parent directory and extracts keys + that match the pattern @\"_enabled\". The matching keys are then printed + with indentation and line breaks if the line exceeds 120 characters. + Ignores SettingsKeys.h + + Usage: + 1. Place this script in the desired directory. + 2. Run the script with the command: python extract_keys.py + 3. The script will search for all .xm and .h files in the parent directory and + print any matching keys it finds. + + Note: + - The script searches the directory where it is located (the parent directory). + - It only looks for files with extensions .xm and .h. + """ + # Get the parent directory + parent_directory = os.path.dirname(os.path.abspath(__file__)) + + # Store the found keys + found_keys = set() # Use a set to automatically remove duplicates + + # Walk through the parent directory and find all .xm and .h files + for root, dirs, files in os.walk(parent_directory): + for file in files: + if file.endswith(('.xm', '.h')): + # Skip SettingsKeys.h + if file == "SettingsKeys.h": + continue + file_path = os.path.join(root, file) + found_keys.update(extract_values_from_file(file_path)) + + # Print the found keys with formatting + if found_keys: + sorted_keys = sorted(found_keys) + print(format_output(sorted_keys)) + else: + print("No keys found.") + +if __name__ == "__main__": + find_and_extract_keys()