uYouEnhanced v20.44.2: QOL UPDATE

This commit is contained in:
aricloverEXTRA 2026-02-24 17:51:57 -06:00 committed by GitHub
parent 8aca84cc4f
commit c56628e3f8
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
30 changed files with 1152 additions and 1106 deletions

View file

@ -62,6 +62,8 @@ jobs:
git add . git add .
git submodule update --init --recursive --remote Tweaks/YTVideoOverlay git submodule update --init --recursive --remote Tweaks/YTVideoOverlay
git add . git add .
git submodule update --init --recursive --remote Tweaks/YTweaks
git add .
git submodule update --init --recursive git submodule update --init --recursive
git commit -m "updated submodules" git commit -m "updated submodules"
git push git push

9
.gitmodules vendored
View file

@ -76,8 +76,7 @@
url = https://github.com/PoomSmart/YTIcons.git url = https://github.com/PoomSmart/YTIcons.git
[submodule "Tweaks/YTUHD"] [submodule "Tweaks/YTUHD"]
path = Tweaks/YTUHD path = Tweaks/YTUHD
url = https://github.com/aricloverEXTRA/YTUHD.git url = https://github.com/Tonwalter888/YTUHD.git
branch = master
[submodule "Tweaks/YTVideoOverlay"] [submodule "Tweaks/YTVideoOverlay"]
path = Tweaks/YTVideoOverlay path = Tweaks/YTVideoOverlay
url = https://github.com/PoomSmart/YTVideoOverlay.git url = https://github.com/PoomSmart/YTVideoOverlay.git
@ -92,3 +91,9 @@
[submodule "Tweaks/uYouLocalization"] [submodule "Tweaks/uYouLocalization"]
path = Tweaks/uYouLocalization path = Tweaks/uYouLocalization
url = https://github.com/dayanch96/uYouLocalization.git url = https://github.com/dayanch96/uYouLocalization.git
[submodule "Tweaks/libundirect"]
path = Tweaks/libundirect
url = https://github.com/opa334/libundirect.git
[submodule "Tweaks/YTweaks"]
path = Tweaks/YTweaks
url = https://github.com/fosterbarnes/YTweaks.git

1
Bundles/YTWKS.bundle Symbolic link
View file

@ -0,0 +1 @@
../Tweaks/YTweaks/layout/Library/Application Support/YTWKS.bundle

Binary file not shown.

After

Width:  |  Height:  |  Size: 825 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

View file

@ -16,7 +16,7 @@ MODULES = jailed
endif endif
ifndef YOUTUBE_VERSION ifndef YOUTUBE_VERSION
YOUTUBE_VERSION = 20.50.9 YOUTUBE_VERSION = 20.44.2
endif endif
ifndef UYOU_VERSION ifndef UYOU_VERSION
UYOU_VERSION = 3.0.4 UYOU_VERSION = 3.0.4
@ -33,8 +33,27 @@ $(TWEAK_NAME)_FILES := $(wildcard Sources/*.xm) $(wildcard Sources/*.x) $(wildca
$(TWEAK_NAME)_FRAMEWORKS = UIKit Foundation AVFoundation AVKit Photos Accelerate CoreMotion GameController VideoToolbox Security $(TWEAK_NAME)_FRAMEWORKS = UIKit Foundation AVFoundation AVKit Photos Accelerate CoreMotion GameController VideoToolbox Security
$(TWEAK_NAME)_LIBRARIES = bz2 c++ iconv z $(TWEAK_NAME)_LIBRARIES = bz2 c++ iconv z
$(TWEAK_NAME)_CFLAGS = -fobjc-arc -Wno-deprecated-declarations -Wno-unused-but-set-variable -DTWEAK_VERSION=\"$(PACKAGE_VERSION)\" $(TWEAK_NAME)_CFLAGS = -fobjc-arc -Wno-deprecated-declarations -Wno-unused-but-set-variable -DTWEAK_VERSION=\"$(PACKAGE_VERSION)\"
#uYouLocalization $(TWEAK_NAME)_INJECT_DYLIBS = Tweaks/uYou/Library/MobileSubstrate/DynamicLibraries/uYou.dylib $(THEOS_OBJ_DIR)/uYouLocalization.dylib $(TWEAK_NAME)_INJECT_DYLIBS = \
$(TWEAK_NAME)_INJECT_DYLIBS = Tweaks/uYou/Library/MobileSubstrate/DynamicLibraries/uYou.dylib $(THEOS_OBJ_DIR)/libFLEX.dylib $(THEOS_OBJ_DIR)/iSponsorBlock.dylib $(THEOS_OBJ_DIR)/YTABConfig.dylib $(THEOS_OBJ_DIR)/YTIcons.dylib $(THEOS_OBJ_DIR)/YouGroupSettings.dylib $(THEOS_OBJ_DIR)/YouLoop.dylib $(THEOS_OBJ_DIR)/YouMute.dylib $(THEOS_OBJ_DIR)/YouPiP.dylib $(THEOS_OBJ_DIR)/YouQuality.dylib $(THEOS_OBJ_DIR)/YouSlider.dylib $(THEOS_OBJ_DIR)/YouSpeed.dylib $(THEOS_OBJ_DIR)/YouTimeStamp.dylib $(THEOS_OBJ_DIR)/YouTubeDislikesReturn.dylib $(THEOS_OBJ_DIR)/DontEatMyContent.dylib $(THEOS_OBJ_DIR)/YTHoldForSpeed.dylib $(THEOS_OBJ_DIR)/YTUHD.dylib $(THEOS_OBJ_DIR)/YTVideoOverlay.dylib Tweaks/uYou/Library/MobileSubstrate/DynamicLibraries/uYou.dylib \
$(THEOS_OBJ_DIR)/libFLEX.dylib \
$(THEOS_OBJ_DIR)/iSponsorBlock.dylib \
$(THEOS_OBJ_DIR)/YTABConfig.dylib \
$(THEOS_OBJ_DIR)/YTIcons.dylib \
$(THEOS_OBJ_DIR)/YouGroupSettings.dylib \
$(THEOS_OBJ_DIR)/YouLoop.dylib \
$(THEOS_OBJ_DIR)/YouMute.dylib \
$(THEOS_OBJ_DIR)/YouPiP.dylib \
$(THEOS_OBJ_DIR)/YouQuality.dylib \
$(THEOS_OBJ_DIR)/YouSlider.dylib \
$(THEOS_OBJ_DIR)/YouSpeed.dylib \
$(THEOS_OBJ_DIR)/YouTimeStamp.dylib \
$(THEOS_OBJ_DIR)/YouTubeDislikesReturn.dylib \
$(THEOS_OBJ_DIR)/DontEatMyContent.dylib \
$(THEOS_OBJ_DIR)/YTHoldForSpeed.dylib \
$(THEOS_OBJ_DIR)/YTUHD.dylib \
$(THEOS_OBJ_DIR)/YTVideoOverlay.dylib \
$(THEOS_OBJ_DIR)/YTweaks.dylib
$(TWEAK_NAME)_EMBED_LIBRARIES = $(THEOS_OBJ_DIR)/libcolorpicker.dylib $(TWEAK_NAME)_EMBED_LIBRARIES = $(THEOS_OBJ_DIR)/libcolorpicker.dylib
$(TWEAK_NAME)_EMBED_FRAMEWORKS = $(_THEOS_LOCAL_DATA_DIR)/$(THEOS_OBJ_DIR_NAME)/install_Alderis.xcarchive/Products/var/jb/Library/Frameworks/Alderis.framework $(TWEAK_NAME)_EMBED_FRAMEWORKS = $(_THEOS_LOCAL_DATA_DIR)/$(THEOS_OBJ_DIR_NAME)/install_Alderis.xcarchive/Products/var/jb/Library/Frameworks/Alderis.framework
$(TWEAK_NAME)_EMBED_BUNDLES = $(wildcard Bundles/*.bundle) $(TWEAK_NAME)_EMBED_BUNDLES = $(wildcard Bundles/*.bundle)
@ -42,8 +61,7 @@ $(TWEAK_NAME)_EMBED_EXTENSIONS = $(wildcard Extensions/*.appex)
include $(THEOS)/makefiles/common.mk include $(THEOS)/makefiles/common.mk
ifneq ($(JAILBROKEN),1) ifneq ($(JAILBROKEN),1)
#uYouLocalization SUBPROJECTS += Tweaks/Alderis Tweaks/uYouLocalization SUBPROJECTS += Tweaks/Alderis Tweaks/DontEatMyContent Tweaks/FLEXing/libflex Tweaks/iSponsorBlock Tweaks/Return-YouTube-Dislikes Tweaks/YTABConfig Tweaks/YouGroupSettings Tweaks/YTIcons Tweaks/YouLoop Tweaks/YouMute Tweaks/YouPiP Tweaks/YouQuality Tweaks/YouSlider Tweaks/YouSpeed Tweaks/YouTimeStamp Tweaks/YTHoldForSpeed Tweaks/YTUHD Tweaks/YTVideoOverlay Tweaks/YTweaks
SUBPROJECTS += Tweaks/Alderis Tweaks/DontEatMyContent Tweaks/FLEXing/libflex Tweaks/iSponsorBlock Tweaks/Return-YouTube-Dislikes Tweaks/YTABConfig Tweaks/YouGroupSettings Tweaks/YTIcons Tweaks/YouLoop Tweaks/YouMute Tweaks/YouPiP Tweaks/YouQuality Tweaks/YouSlider Tweaks/YouSpeed Tweaks/YouTimeStamp Tweaks/YTHoldForSpeed Tweaks/YTUHD Tweaks/YTVideoOverlay
include $(THEOS_MAKE_PATH)/aggregate.mk include $(THEOS_MAKE_PATH)/aggregate.mk
endif endif
include $(THEOS_MAKE_PATH)/tweak.mk include $(THEOS_MAKE_PATH)/tweak.mk

View file

@ -1,6 +1,6 @@
#import <UIKit/UIKit.h> #import <UIKit/UIKit.h>
@interface AppIconOptionsController : UIViewController @interface AppIconOptionsController : UIViewController <UITableViewDataSource, UITableViewDelegate>
@property (strong, nonatomic) UIButton *backButton; @property (strong, nonatomic) UIButton *backButton;
@ -11,12 +11,3 @@
+ (UIImage *)customBackButtonImage; + (UIImage *)customBackButtonImage;
@end @end
@implementation UIImage (CustomImages)
+ (UIImage *)customBackButtonImage {
NSBundle *bundle = [NSBundle bundleWithPath:[[NSBundle mainBundle] pathForResource:@"uYouPlus" ofType:@"bundle"]];
return [UIImage imageNamed:@"Back.png" inBundle:bundle compatibleWithTraitCollection:nil];
}
@end

View file

@ -1,10 +1,32 @@
#import "AppIconOptionsController.h" #import "AppIconOptionsController.h"
#import <YouTubeHeader/YTAssetLoader.h> #import <notify.h>
static NSString *const kPrefDomain = @"com.arichornlover.uYouEnhanced";
static NSString *const kPrefEnableIconOverride = @"appIconCustomization_enabled";
static NSString *const kPrefIconName = @"customAppIcon_name";
static NSString *const kPrefNotifyName = @"com.arichornlover.uYouEnhanced.prefschanged";
static NSString *BundlePath(void) {
NSString *path = [[NSBundle mainBundle] pathForResource:@"uYouPlus" ofType:@"bundle"];
if (path) return path;
return @"/Library/Application Support/uYouEnhanced";
}
@interface AppIconOptionsController ()
@interface AppIconOptionsController () <UITableViewDataSource, UITableViewDelegate>
@property (strong, nonatomic) UITableView *tableView; @property (strong, nonatomic) UITableView *tableView;
@property (strong, nonatomic) NSArray<NSString *> *appIcons; @property (strong, nonatomic) NSArray<NSString *> *appIcons;
@property (assign, nonatomic) NSInteger selectedIconIndex; @property (assign, nonatomic) NSInteger selectedIconIndex;
@end
@implementation UIImage (CustomImages)
+ (UIImage *)customBackButtonImage {
NSBundle *bundle = [NSBundle bundleWithPath:BundlePath()];
return [UIImage imageNamed:@"Back.png" inBundle:bundle compatibleWithTraitCollection:nil];
}
@end @end
@implementation AppIconOptionsController @implementation AppIconOptionsController
@ -14,102 +36,266 @@
self.title = @"Change App Icon"; self.title = @"Change App Icon";
self.selectedIconIndex = -1; self.selectedIconIndex = -1;
self.view.backgroundColor = [UIColor systemBackgroundColor];
self.tableView = [[UITableView alloc] initWithFrame:self.view.bounds style:UITableViewStyleGrouped]; self.tableView = [[UITableView alloc] initWithFrame:CGRectZero style:UITableViewStyleInsetGrouped];
self.tableView.dataSource = self; self.tableView.dataSource = self;
self.tableView.delegate = self; self.tableView.delegate = self;
self.tableView.translatesAutoresizingMaskIntoConstraints = NO;
if (@available(iOS 15.0, *)) {
self.tableView.sectionHeaderTopPadding = 0;
}
[self.view addSubview:self.tableView]; [self.view addSubview:self.tableView];
self.backButton = [UIButton buttonWithType:UIButtonTypeCustom]; [NSLayoutConstraint activateConstraints:@[
[self.backButton setImage:[UIImage systemImageNamed:@"chevron.backward"] forState:UIControlStateNormal]; [self.tableView.topAnchor constraintEqualToAnchor:self.view.safeAreaLayoutGuide.topAnchor],
[self.tableView.bottomAnchor constraintEqualToAnchor:self.view.bottomAnchor],
[self.tableView.leadingAnchor constraintEqualToAnchor:self.view.leadingAnchor],
[self.tableView.trailingAnchor constraintEqualToAnchor:self.view.trailingAnchor]
]];
self.navigationItem.hidesBackButton = YES;
if (@available(iOS 14.0, *)) {
self.navigationItem.backButtonDisplayMode = UINavigationItemBackButtonDisplayModeMinimal;
}
self.backButton = [UIButton buttonWithType:UIButtonTypeSystem];
[self.backButton setImage:[UIImage customBackButtonImage] forState:UIControlStateNormal];
[self.backButton addTarget:self action:@selector(back) forControlEvents:UIControlEventTouchUpInside]; [self.backButton addTarget:self action:@selector(back) forControlEvents:UIControlEventTouchUpInside];
UIBarButtonItem *customBackButton = [[UIBarButtonItem alloc] initWithCustomView:self.backButton]; UIBarButtonItem *customBackButton = [[UIBarButtonItem alloc] initWithCustomView:self.backButton];
self.navigationItem.leftBarButtonItem = customBackButton; self.navigationItem.leftBarButtonItem = customBackButton;
NSString *path = [[NSBundle mainBundle] pathForResource:@"uYouPlus" ofType:@"bundle"]; NSMutableSet<NSString *> *iconNames = [NSMutableSet set];
NSBundle *bundle = [NSBundle bundleWithPath:path]; NSFileManager *fm = [NSFileManager defaultManager];
self.appIcons = [bundle pathsForResourcesOfType:@"png" inDirectory:@"AppIcons"];
if (![UIApplication sharedApplication].supportsAlternateIcons) { NSString *bundlePath = BundlePath();
NSLog(@"Alternate icons are not supported on this device."); NSBundle *bundle = [NSBundle bundleWithPath:bundlePath];
if (bundle) {
NSString *appIconsDir = [bundle.bundlePath stringByAppendingPathComponent:@"AppIcons"];
BOOL isDir = NO;
if ([fm fileExistsAtPath:appIconsDir isDirectory:&isDir] && isDir) {
NSArray *contents = [fm contentsOfDirectoryAtPath:appIconsDir error:nil] ?: @[];
for (NSString *entry in contents) {
NSString *full = [appIconsDir stringByAppendingPathComponent:entry];
BOOL entryIsDir = NO;
if ([fm fileExistsAtPath:full isDirectory:&entryIsDir]) {
if (entryIsDir) {
[iconNames addObject:entry];
} else {
NSString *ext = entry.pathExtension.lowercaseString;
if ([ext isEqualToString:@"png"]) {
NSString *name = [entry stringByDeletingPathExtension];
if (name.length > 0) {
[iconNames addObject:name];
}
}
}
}
}
}
}
NSString *supportBase = @"/Library/Application Support/uYouEnhanced/AppIcons";
BOOL supportIsDir = NO;
if ([fm fileExistsAtPath:supportBase isDirectory:&supportIsDir] && supportIsDir) {
NSArray *contents = [fm contentsOfDirectoryAtPath:supportBase error:nil] ?: @[];
for (NSString *entry in contents) {
NSString *full = [supportBase stringByAppendingPathComponent:entry];
BOOL isDir = NO;
if ([fm fileExistsAtPath:full isDirectory:&isDir]) {
if (isDir) {
[iconNames addObject:entry];
} else {
NSString *ext = entry.pathExtension.lowercaseString;
if ([ext isEqualToString:@"png"]) {
NSString *name = [entry stringByDeletingPathExtension];
if (name.length > 0) {
[iconNames addObject:name];
}
}
}
}
}
}
self.appIcons = [[iconNames allObjects] sortedArrayUsingSelector:@selector(localizedCaseInsensitiveCompare:)];
NSDictionary *prefs = [NSDictionary dictionaryWithContentsOfFile:[NSString stringWithFormat:@"/var/mobile/Library/Preferences/%@.plist", kPrefDomain]] ?: @{};
NSString *savedIcon = prefs[kPrefIconName];
if (savedIcon) {
NSInteger idx = [self.appIcons indexOfObject:savedIcon];
if (idx != NSNotFound) self.selectedIconIndex = idx;
}
if (self.appIcons.count == 0) {
UILabel *lbl = [[UILabel alloc] initWithFrame:CGRectInset(self.view.bounds, 20, 20)];
lbl.text = @"No custom icons found. Place PNGs or icon folders in uYouPlus.bundle/AppIcons/ or /Library/Application Support/uYouEnhanced/AppIcons/";
lbl.numberOfLines = 0;
lbl.textAlignment = NSTextAlignmentCenter;
lbl.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;
[self.view addSubview:lbl];
} }
} }
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section { - (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
return self.appIcons.count; return self.appIcons.count + 1;
} }
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath { - (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath {
return 80.0; return 80.0;
} }
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath { - (UITableViewCell *)tableView:(UITableView *)tv cellForRowAtIndexPath:(NSIndexPath *)indexPath {
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:@"Cell"]; static NSString *CellId = @"AppIconCell";
if (!cell) { UITableViewCell *cell = [tv dequeueReusableCellWithIdentifier:CellId];
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:@"Cell"]; if (!cell) cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:CellId];
cell.selectionStyle = UITableViewCellSelectionStyleNone;
if (indexPath.row == 0) {
cell.textLabel.text = @"Reset to default";
cell.detailTextLabel.text = @"Restore the original app icon";
cell.imageView.image = nil;
cell.accessoryType = (self.selectedIconIndex == -1) ? UITableViewCellAccessoryCheckmark : UITableViewCellAccessoryNone;
return cell;
} }
NSString *iconPath = self.appIcons[indexPath.row]; NSString *iconName = self.appIcons[indexPath.row - 1];
cell.textLabel.text = [iconPath.lastPathComponent stringByDeletingPathExtension]; cell.textLabel.text = iconName;
cell.detailTextLabel.text = @"Tap to apply this icon";
UIImage *iconImage = [UIImage imageWithContentsOfFile:iconPath]; UIImage *preview = nil;
cell.imageView.image = iconImage; NSArray<NSString *> *candidates = @[
cell.imageView.layer.cornerRadius = 16.0; @"AppIcon60x60@3x.png",
@"AppIcon60x60@2x.png",
@"Icon@3x.png",
@"Icon@2x.png",
@"Icon.png"
];
NSString *bundlePath = BundlePath();
NSBundle *bundle = [NSBundle bundleWithPath:bundlePath];
NSString *supportBase = @"/Library/Application Support/uYouEnhanced/AppIcons";
NSFileManager *fm = [NSFileManager defaultManager];
BOOL found = NO;
if (bundle) {
NSString *dir = [bundle.bundlePath stringByAppendingPathComponent:[NSString stringWithFormat:@"AppIcons/%@", iconName]];
BOOL isDir = NO;
if ([fm fileExistsAtPath:dir isDirectory:&isDir] && isDir) {
for (NSString *c in candidates) {
NSString *imagePath = [dir stringByAppendingPathComponent:c];
if ([fm fileExistsAtPath:imagePath]) {
preview = [UIImage imageWithContentsOfFile:imagePath];
found = YES;
break;
}
}
if (!found) {
NSArray *files = [fm contentsOfDirectoryAtPath:dir error:nil];
for (NSString *file in files) {
NSString *ext = file.pathExtension.lowercaseString;
if ([ext isEqualToString:@"png"]) {
NSString *path = [dir stringByAppendingPathComponent:file];
preview = [UIImage imageWithContentsOfFile:path];
found = YES;
break;
}
}
}
} else {
NSString *pngPath = [[bundle.bundlePath stringByAppendingPathComponent:@"AppIcons"] stringByAppendingPathComponent:[NSString stringWithFormat:@"%@.png", iconName]];
if ([fm fileExistsAtPath:pngPath]) {
preview = [UIImage imageWithContentsOfFile:pngPath];
found = YES;
}
}
}
if (!found) {
NSString *dir = [supportBase stringByAppendingPathComponent:iconName];
BOOL isDir = NO;
if ([fm fileExistsAtPath:dir isDirectory:&isDir] && isDir) {
for (NSString *c in candidates) {
NSString *imagePath = [dir stringByAppendingPathComponent:c];
if ([fm fileExistsAtPath:imagePath]) {
preview = [UIImage imageWithContentsOfFile:imagePath];
found = YES;
break;
}
}
if (!found) {
NSArray *files = [fm contentsOfDirectoryAtPath:dir error:nil];
for (NSString *file in files) {
NSString *ext = file.pathExtension.lowercaseString;
if ([ext isEqualToString:@"png"]) {
NSString *path = [dir stringByAppendingPathComponent:file];
preview = [UIImage imageWithContentsOfFile:path];
found = YES;
break;
}
}
}
} else {
NSString *pngPath = [supportBase stringByAppendingPathComponent:[NSString stringWithFormat:@"%@.png", iconName]];
if ([fm fileExistsAtPath:pngPath]) {
preview = [UIImage imageWithContentsOfFile:pngPath];
found = YES;
}
}
}
cell.imageView.image = preview;
cell.imageView.layer.cornerRadius = 12.0;
cell.imageView.clipsToBounds = YES; cell.imageView.clipsToBounds = YES;
cell.imageView.contentMode = UIViewContentModeScaleAspectFit; cell.imageView.contentMode = UIViewContentModeScaleAspectFit;
cell.accessoryType = ((indexPath.row - 1) == self.selectedIconIndex) ? UITableViewCellAccessoryCheckmark : UITableViewCellAccessoryNone;
cell.accessoryType = (indexPath.row == self.selectedIconIndex) ? UITableViewCellAccessoryCheckmark : UITableViewCellAccessoryNone;
return cell; return cell;
} }
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath { - (void)tableView:(UITableView *)tv didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
[tableView deselectRowAtIndexPath:indexPath animated:YES]; [tv deselectRowAtIndexPath:indexPath animated:YES];
self.selectedIconIndex = indexPath.row; NSString *prefsPath = [NSString stringWithFormat:@"/var/mobile/Library/Preferences/%@.plist", kPrefDomain];
[self saveIcon]; NSMutableDictionary *prefs = [[NSMutableDictionary alloc] initWithContentsOfFile:prefsPath] ?: [NSMutableDictionary dictionary];
[self.tableView reloadData];
}
- (void)resetIcon { if (indexPath.row == 0) {
[[UIApplication sharedApplication] setAlternateIconName:nil completionHandler:^(NSError * _Nullable error) { self.selectedIconIndex = -1;
if (error) { prefs[kPrefEnableIconOverride] = @NO;
NSLog(@"Error resetting icon: %@", error.localizedDescription); [prefs writeToFile:prefsPath atomically:YES];
[self showAlertWithTitle:@"Error" message:@"Failed to reset icon"]; notify_post([kPrefNotifyName UTF8String]);
} else { [self.tableView reloadData];
NSLog(@"Icon reset successfully"); [self showAlertWithTitle:@"Requested" message:@"Icon reset requested."];
[self showAlertWithTitle:@"Success" message:@"Icon reset successfully"];
[self.tableView reloadData];
}
}];
}
- (void)saveIcon {
if (self.selectedIconIndex < 0) {
[self showAlertWithTitle:@"Error" message:@"No icon selected"];
return; return;
} }
NSString *selectedIcon = self.appIcons[self.selectedIconIndex]; self.selectedIconIndex = indexPath.row - 1;
NSString *iconName = [selectedIcon.lastPathComponent stringByDeletingPathExtension]; NSString *iconName = self.appIcons[self.selectedIconIndex];
[[UIApplication sharedApplication] setAlternateIconName:iconName completionHandler:^(NSError * _Nullable error) { prefs[kPrefEnableIconOverride] = @YES;
if (error) { prefs[kPrefIconName] = iconName;
NSLog(@"Error setting alternate icon: %@", error.localizedDescription);
[self showAlertWithTitle:@"Error" message:@"Failed to set alternate icon"]; BOOL ok = [prefs writeToFile:prefsPath atomically:YES];
} else { if (!ok) {
NSLog(@"Alternate icon set successfully"); [self showAlertWithTitle:@"Error" message:@"Failed to save preference"];
[self showAlertWithTitle:@"Success" message:@"Alternate icon set successfully"]; return;
[self.tableView reloadData]; }
}
}]; notify_post([kPrefNotifyName UTF8String]);
[self.tableView reloadData];
[self showAlertWithTitle:@"Requested" message:@"Icon change requested."];
} }
- (void)showAlertWithTitle:(NSString *)title message:(NSString *)message { - (void)showAlertWithTitle:(NSString *)title message:(NSString *)message {
UIAlertController *alert = [UIAlertController alertControllerWithTitle:title message:message preferredStyle:UIAlertControllerStyleAlert]; UIAlertController *alert = [UIAlertController alertControllerWithTitle:title message:message preferredStyle:UIAlertControllerStyleAlert];
UIAlertAction *okAction = [UIAlertAction actionWithTitle:@"OK" style:UIAlertActionStyleDefault handler:nil]; UIAlertAction *ok = [UIAlertAction actionWithTitle:@"OK" style:UIAlertActionStyleDefault handler:nil];
[alert addAction:okAction]; [alert addAction:ok];
[self presentViewController:alert animated:YES completion:nil]; [self presentViewController:alert animated:YES completion:nil];
} }

View file

@ -18,12 +18,19 @@ static inline BOOL customContrastMode() {
return IS_ENABLED(@"lowContrastMode_enabled") && contrastMode() == 1; return IS_ENABLED(@"lowContrastMode_enabled") && contrastMode() == 1;
} }
// static inline UIColor *activeColor() { // Helper to get active contrast color
// return customContrastMode() && lcmHexColor ? lcmHexColor : kLowContrastColor; static inline UIColor *activeContrastColor() {
// } return customContrastMode() && lcmHexColor ? lcmHexColor : kLowContrastColor;
}
// Helper to check if dark mode is enabled
static inline BOOL isDarkMode() {
return UIScreen.mainScreen.traitCollection.userInterfaceStyle == UIUserInterfaceStyleDark;
}
// Low Contrast Mode v1.8.0 (Compatible with YouTube v19.21.2-v20.44.2)
%group gContrastModeShared
// Low Contrast Mode v1.7.1.1 (Compatible with only YouTube v19.01.1-v20.21.6)
%group gLowContrastMode
%hook UIColor %hook UIColor
+ (UIColor *)colorNamed:(NSString *)name { + (UIColor *)colorNamed:(NSString *)name {
NSArray<NSString *> *targetColors = @[ NSArray<NSString *> *targetColors = @[
@ -32,478 +39,294 @@ static inline BOOL customContrastMode() {
@"systemExtraLightGrayColor", @"labelColor", @"secondaryLabelColor", @"systemExtraLightGrayColor", @"labelColor", @"secondaryLabelColor",
@"tertiaryLabelColor", @"quaternaryLabelColor" @"tertiaryLabelColor", @"quaternaryLabelColor"
]; ];
return [targetColors containsObject:name] ? kLowContrastColor : %orig; return [targetColors containsObject:name] ? activeContrastColor() : %orig;
} }
+ (UIColor *)whiteColor { return kLowContrastColor; } + (UIColor *)whiteColor { return activeContrastColor(); }
+ (UIColor *)lightTextColor { return kLowContrastColor; } + (UIColor *)lightTextColor { return activeContrastColor(); }
+ (UIColor *)lightGrayColor { return kLowContrastColor; } + (UIColor *)lightGrayColor { return activeContrastColor(); }
%end %end
%hook YTCommonColorPalette %hook YTCommonColorPalette
+ (id)darkPalette {
id palette = %orig;
if (isDarkMode()) {
UIColor *contrastColor = activeContrastColor();
[palette setValue:contrastColor forKey:@"textPrimary"];
[palette setValue:contrastColor forKey:@"textSecondary"];
[palette setValue:contrastColor forKey:@"overlayTextPrimary"];
[palette setValue:contrastColor forKey:@"overlayTextSecondary"];
[palette setValue:contrastColor forKey:@"iconActive"];
[palette setValue:contrastColor forKey:@"iconActiveOther"];
[palette setValue:contrastColor forKey:@"brandIconActive"];
[palette setValue:contrastColor forKey:@"staticBrandWhite"];
[palette setValue:contrastColor forKey:@"overlayIconActiveOther"];
[palette setValue:[contrastColor colorWithAlphaComponent:0.7] forKey:@"overlayIconInactive"];
[palette setValue:[contrastColor colorWithAlphaComponent:0.3] forKey:@"overlayIconDisabled"];
[palette setValue:[contrastColor colorWithAlphaComponent:0.2] forKey:@"overlayFilledButtonActive"];
}
return palette;
}
+ (id)lightPalette {
return %orig;
}
- (UIColor *)textPrimary { - (UIColor *)textPrimary {
return self.pageStyle == 1 ? kDefaultTextColor : %orig; return isDarkMode() ? activeContrastColor() : %orig;
} }
- (UIColor *)textSecondary { - (UIColor *)textSecondary {
return self.pageStyle == 1 ? kDefaultTextColor : %orig; return isDarkMode() ? activeContrastColor() : %orig;
} }
- (UIColor *)overlayTextPrimary { - (UIColor *)overlayTextPrimary {
return self.pageStyle == 1 ? kDefaultTextColor : %orig; return isDarkMode() ? activeContrastColor() : %orig;
} }
- (UIColor *)overlayTextSecondary { - (UIColor *)overlayTextSecondary {
return self.pageStyle == 1 ? kDefaultTextColor : %orig; return isDarkMode() ? activeContrastColor() : %orig;
} }
- (UIColor *)iconActive { - (UIColor *)iconActive {
return self.pageStyle == 1 ? kDefaultTextColor : %orig; return isDarkMode() ? activeContrastColor() : %orig;
} }
- (UIColor *)iconActiveOther { - (UIColor *)iconActiveOther {
return self.pageStyle == 1 ? kDefaultTextColor : %orig; return isDarkMode() ? activeContrastColor() : %orig;
} }
- (UIColor *)brandIconActive { - (UIColor *)brandIconActive {
return self.pageStyle == 1 ? kDefaultTextColor : %orig; return isDarkMode() ? activeContrastColor() : %orig;
} }
- (UIColor *)staticBrandWhite { - (UIColor *)staticBrandWhite {
return self.pageStyle == 1 ? kDefaultTextColor : %orig; return isDarkMode() ? activeContrastColor() : %orig;
} }
- (UIColor *)overlayIconActiveOther { - (UIColor *)overlayIconActiveOther {
return self.pageStyle == 1 ? kDefaultTextColor : %orig; return isDarkMode() ? activeContrastColor() : %orig;
} }
- (UIColor *)overlayIconInactive { - (UIColor *)overlayIconInactive {
return self.pageStyle == 1 ? [kDefaultTextColor colorWithAlphaComponent:0.7] : %orig; return isDarkMode() ? [activeContrastColor() colorWithAlphaComponent:0.7] : %orig;
} }
- (UIColor *)overlayIconDisabled { - (UIColor *)overlayIconDisabled {
return self.pageStyle == 1 ? [kDefaultTextColor colorWithAlphaComponent:0.3] : %orig; return isDarkMode() ? [activeContrastColor() colorWithAlphaComponent:0.3] : %orig;
} }
- (UIColor *)overlayFilledButtonActive { - (UIColor *)overlayFilledButtonActive {
return self.pageStyle == 1 ? [kDefaultTextColor colorWithAlphaComponent:0.2] : %orig; return isDarkMode() ? [activeContrastColor() colorWithAlphaComponent:0.2] : %orig;
} }
%end %end
%hook YTColor %hook YTColor
+ (BOOL)darkerPaletteTextColorEnabled { return NO; } + (BOOL)darkerPaletteTextColorEnabled { return NO; }
+ (UIColor *)white1 { return kLowContrastColor; } + (UIColor *)white1 { return activeContrastColor(); }
+ (UIColor *)white2 { return kLowContrastColor; } + (UIColor *)white2 { return activeContrastColor(); }
+ (UIColor *)white3 { return kLowContrastColor; } + (UIColor *)white3 { return activeContrastColor(); }
+ (UIColor *)white4 { return kLowContrastColor; } + (UIColor *)white4 { return activeContrastColor(); }
+ (UIColor *)white5 { return kLowContrastColor; } + (UIColor *)white5 { return activeContrastColor(); }
+ (UIColor *)grey1 { return kLowContrastColor; } + (UIColor *)grey1 { return activeContrastColor(); }
+ (UIColor *)grey2 { return kLowContrastColor; } + (UIColor *)grey2 { return activeContrastColor(); }
%end %end
%hook _ASDisplayView %hook _ASDisplayView
- (void)layoutSubviews { - (void)layoutSubviews {
%orig; %orig;
NSArray<NSString *> *targetLabels = @[@"connect account", @"Thanks", @"Save to playlist", @"Report"]; if (isDarkMode()) {
for (UIView *subview in self.subviews) { NSArray<NSString *> *targetLabels = @[@"connect account", @"Thanks", @"Save to playlist", @"Report", @"Share", @"Like", @"Dislike"];
if ([targetLabels containsObject:subview.accessibilityLabel]) { UIColor *contrastColor = activeContrastColor();
subview.backgroundColor = kLowContrastColor; for (UIView *subview in self.subviews) {
if ([subview isKindOfClass:[UILabel class]]) { if ([targetLabels containsObject:subview.accessibilityLabel]) {
((UILabel *)subview).textColor = [UIColor blackColor]; subview.backgroundColor = contrastColor;
} if ([subview isKindOfClass:[UILabel class]]) {
} ((UILabel *)subview).textColor = [UIColor blackColor];
} }
} }
%end }
}
%hook QTMColorGroup }
- (UIColor *)tint100 { return kDefaultTextColor; } %end
- (UIColor *)tint300 { return kDefaultTextColor; }
- (UIColor *)tint500 { return kDefaultTextColor; } %hook QTMColorGroup
- (UIColor *)tint700 { return kDefaultTextColor; } - (UIColor *)tint100 { return kDefaultTextColor; }
- (UIColor *)accent200 { return kDefaultTextColor; } - (UIColor *)tint300 { return kDefaultTextColor; }
- (UIColor *)accent400 { return kDefaultTextColor; } - (UIColor *)tint500 { return kDefaultTextColor; }
- (UIColor *)accentColor { return kDefaultTextColor; } - (UIColor *)tint700 { return kDefaultTextColor; }
- (UIColor *)brightAccentColor { return kDefaultTextColor; } - (UIColor *)accent200 { return kDefaultTextColor; }
- (UIColor *)regularColor { return kDefaultTextColor; } - (UIColor *)accent400 { return kDefaultTextColor; }
- (UIColor *)darkerColor { return kDefaultTextColor; } - (UIColor *)accentColor { return kDefaultTextColor; }
- (UIColor *)bodyTextColor { return kDefaultTextColor; } - (UIColor *)brightAccentColor { return kDefaultTextColor; }
- (UIColor *)lightBodyTextColor { return kDefaultTextColor; } - (UIColor *)regularColor { return kDefaultTextColor; }
- (UIColor *)bodyTextColorOnRegularColor { return kDefaultTextColor; } - (UIColor *)darkerColor { return kDefaultTextColor; }
- (UIColor *)bodyTextColorOnLighterColor { return kDefaultTextColor; } - (UIColor *)bodyTextColor { return kDefaultTextColor; }
- (UIColor *)bodyTextColorOnDarkerColor { return kDefaultTextColor; } - (UIColor *)lightBodyTextColor { return kDefaultTextColor; }
- (UIColor *)bodyTextColorOnAccentColor { return kDefaultTextColor; } - (UIColor *)bodyTextColorOnRegularColor { return kDefaultTextColor; }
- (UIColor *)buttonBackgroundColor { return kDefaultTextColor; } - (UIColor *)bodyTextColorOnLighterColor { return kDefaultTextColor; }
- (UIColor *)Color { return kDefaultTextColor; } - (UIColor *)bodyTextColorOnDarkerColor { return kDefaultTextColor; }
%end - (UIColor *)bodyTextColorOnAccentColor { return kDefaultTextColor; }
- (UIColor *)buttonBackgroundColor { return kDefaultTextColor; }
%hook YTQTMButton - (UIColor *)Color { return kDefaultTextColor; }
- (void)setImage:(UIImage *)image { %end
UIImage *tintedImage = [image imageWithRenderingMode:UIImageRenderingModeAlwaysTemplate];
[self setTintColor:kDefaultTextColor]; %hook YTQTMButton
%orig(tintedImage); - (void)setImage:(UIImage *)image {
} if (isDarkMode()) {
%end UIImage *tintedImage = [image imageWithRenderingMode:UIImageRenderingModeAlwaysTemplate];
[self setTintColor:kDefaultTextColor];
%hook UIExtendedSRGColorSpace %orig(tintedImage);
- (void)setTextColor:(UIColor *)textColor { } else {
%orig([kDefaultTextColor colorWithAlphaComponent:0.9]); %orig;
} }
%end }
%end
%hook UIExtendedSRGBColorSpace
- (void)setTextColor:(UIColor *)textColor { %hook UIExtendedSRGColorSpace
%orig([kDefaultTextColor colorWithAlphaComponent:1.0]); - (void)setTextColor:(UIColor *)textColor {
} %orig([kDefaultTextColor colorWithAlphaComponent:0.9]);
%end }
%end
%hook UIExtendedGrayColorSpace
- (void)setTextColor:(UIColor *)textColor { %hook UIExtendedSRGBColorSpace
%orig([kDefaultTextColor colorWithAlphaComponent:1.0]); - (void)setTextColor:(UIColor *)textColor {
} %orig([kDefaultTextColor colorWithAlphaComponent:1.0]);
%end }
%end
%hook VideoTitleLabel
- (void)setTextColor:(UIColor *)textColor { %hook UIExtendedGrayColorSpace
%orig(kDefaultTextColor); - (void)setTextColor:(UIColor *)textColor {
} %orig([kDefaultTextColor colorWithAlphaComponent:1.0]);
%end }
%end
%hook UILabel
+ (void)load { %hook VideoTitleLabel
[[UILabel appearance] setTextColor:kDefaultTextColor]; - (void)setTextColor:(UIColor *)textColor {
} %orig(kDefaultTextColor);
- (void)setTextColor:(UIColor *)textColor { }
%orig(kDefaultTextColor); %end
}
%end %hook UILabel
+ (void)load {
%hook UITextField if (@available(iOS 16.0, *)) {
- (void)setTextColor:(UIColor *)textColor { [[UILabel appearance] setContentHuggingPriority:UILayoutPriorityRequired forAxis:UILayoutConstraintAxisHorizontal];
%orig(kDefaultTextColor); [[UILabel appearance] setContentCompressionResistancePriority:UILayoutPriorityRequired forAxis:UILayoutConstraintAxisHorizontal];
} }
%end if (isDarkMode()) {
[[UILabel appearance] setTextColor:kDefaultTextColor];
%hook UITextView }
- (void)setTextColor:(UIColor *)textColor { }
%orig(kDefaultTextColor); - (void)setTextColor:(UIColor *)textColor {
} %orig(isDarkMode() ? kDefaultTextColor : textColor);
%end }
%end
%hook UISearchBar
- (void)setTextColor:(UIColor *)textColor { %hook UITextField
%orig(kDefaultTextColor); - (void)setTextColor:(UIColor *)textColor {
} %orig(isDarkMode() ? kDefaultTextColor : textColor);
%end }
%end
%hook UISegmentedControl
- (void)setTitleTextAttributes:(NSDictionary *)attributes forState:(UIControlState)state { %hook UITextView
NSMutableDictionary *modifiedAttributes = [NSMutableDictionary dictionaryWithDictionary:attributes]; - (void)setTextColor:(UIColor *)textColor {
modifiedAttributes[NSForegroundColorAttributeName] = kDefaultTextColor; %orig(isDarkMode() ? kDefaultTextColor : textColor);
%orig(modifiedAttributes, state); }
} %end
%end
%hook UISearchBar
%hook UIButton - (void)setTextColor:(UIColor *)textColor {
- (void)setTitleColor:(UIColor *)color forState:(UIControlState)state { %orig(isDarkMode() ? kDefaultTextColor : textColor);
%orig(kDefaultTextColor, state); }
} %end
%end
%hook UISegmentedControl
%hook UIBarButtonItem - (void)setTitleTextAttributes:(NSDictionary *)attributes forState:(UIControlState)state {
- (void)setTitleTextAttributes:(NSDictionary *)attributes forState:(UIControlState)state { if (isDarkMode()) {
NSMutableDictionary *modifiedAttributes = [NSMutableDictionary dictionaryWithDictionary:attributes]; NSMutableDictionary *modifiedAttributes = [NSMutableDictionary dictionaryWithDictionary:attributes];
modifiedAttributes[NSForegroundColorAttributeName] = kDefaultTextColor; modifiedAttributes[NSForegroundColorAttributeName] = kDefaultTextColor;
%orig(modifiedAttributes, state); %orig(modifiedAttributes, state);
} } else {
%end %orig;
}
%hook NSAttributedString }
- (instancetype)initWithString:(NSString *)str attributes:(NSDictionary<NSAttributedStringKey, id> *)attrs { %end
NSMutableDictionary *modifiedAttributes = [NSMutableDictionary dictionaryWithDictionary:attrs];
modifiedAttributes[NSForegroundColorAttributeName] = kDefaultTextColor; %hook UIButton
return %orig(str, modifiedAttributes); - (void)setTitleColor:(UIColor *)color forState:(UIControlState)state {
} %orig(isDarkMode() ? kDefaultTextColor : color, state);
%end }
%end
%hook CATextLayer
- (void)setTextColor:(CGColorRef)textColor { %hook UIBarButtonItem
%orig(kDefaultTextColor.CGColor); - (void)setTitleTextAttributes:(NSDictionary *)attributes forState:(UIControlState)state {
} if (isDarkMode()) {
%end NSMutableDictionary *modifiedAttributes = [NSMutableDictionary dictionaryWithDictionary:attributes];
modifiedAttributes[NSForegroundColorAttributeName] = kDefaultTextColor;
%hook ASTextNode %orig(modifiedAttributes, state);
- (NSAttributedString *)attributedString { } else {
NSAttributedString *original = %orig; %orig;
NSMutableAttributedString *modified = [original mutableCopy]; }
[modified addAttribute:NSForegroundColorAttributeName value:kDefaultTextColor range:NSMakeRange(0, modified.length)]; }
return modified; %end
}
%end %hook NSAttributedString
- (instancetype)initWithString:(NSString *)str attributes:(NSDictionary<NSAttributedStringKey, id> *)attrs {
%hook ASTextFieldNode if (isDarkMode()) {
- (void)setTextColor:(UIColor *)textColor { NSMutableDictionary *modifiedAttributes = [NSMutableDictionary dictionaryWithDictionary:attrs];
%orig(kDefaultTextColor); modifiedAttributes[NSForegroundColorAttributeName] = kDefaultTextColor;
} return %orig(str, modifiedAttributes);
%end }
return %orig;
%hook ASTextView }
- (void)setTextColor:(UIColor *)textColor { %end
%orig(kDefaultTextColor);
} %hook CATextLayer
%end - (void)setTextColor:(CGColorRef)textColor {
%orig(isDarkMode() ? kDefaultTextColor.CGColor : textColor);
%hook ASButtonNode }
- (void)setTextColor:(UIColor *)textColor { %end
%orig(kDefaultTextColor);
} %hook ASTextNode
%end - (NSAttributedString *)attributedString {
NSAttributedString *original = %orig;
%hook UIControl if (isDarkMode()) {
- (UIColor *)backgroundColor { NSMutableAttributedString *modified = [original mutableCopy];
return [UIColor blackColor]; [modified addAttribute:NSForegroundColorAttributeName value:kDefaultTextColor range:NSMakeRange(0, modified.length)];
} return modified;
%end }
%end return original;
}
// Custom Contrast Mode %end
%group gCustomContrastMode
%hook UIColor %hook ASTextFieldNode
+ (UIColor *)colorNamed:(NSString *)name { - (void)setTextColor:(UIColor *)textColor {
NSArray<NSString *> *targetColors = @[ %orig(isDarkMode() ? kDefaultTextColor : textColor);
@"whiteColor", @"lightTextColor", @"lightGrayColor", @"ychGrey7", }
@"skt_chipBackgroundColor", @"placeholderTextColor", @"systemLightGrayColor", %end
@"systemExtraLightGrayColor", @"labelColor", @"secondaryLabelColor",
@"tertiaryLabelColor", @"quaternaryLabelColor" %hook ASTextView
]; - (void)setTextColor:(UIColor *)textColor {
return [targetColors containsObject:name] ? lcmHexColor : %orig; %orig(isDarkMode() ? kDefaultTextColor : textColor);
} }
%end
+ (UIColor *)whiteColor { return lcmHexColor; }
+ (UIColor *)lightTextColor { return lcmHexColor; } %hook ASButtonNode
+ (UIColor *)lightGrayColor { return lcmHexColor; } - (void)setTextColor:(UIColor *)textColor {
%end %orig(isDarkMode() ? kDefaultTextColor : textColor);
}
%hook YTCommonColorPalette %end
- (UIColor *)textPrimary {
return self.pageStyle == 1 ? kDefaultTextColor : %orig; %hook UIControl
} - (UIColor *)backgroundColor {
- (UIColor *)textSecondary { return isDarkMode() ? [UIColor blackColor] : %orig;
return self.pageStyle == 1 ? kDefaultTextColor : %orig; }
} %end
- (UIColor *)overlayTextPrimary {
return self.pageStyle == 1 ? kDefaultTextColor : %orig; %end
}
- (UIColor *)overlayTextSecondary { // Constructor
return self.pageStyle == 1 ? kDefaultTextColor : %orig; %ctor {
} %init;
- (UIColor *)iconActive { if (lowContrastMode() || customContrastMode()) {
return self.pageStyle == 1 ? kDefaultTextColor : %orig; %init(gContrastModeShared);
} if (customContrastMode()) {
- (UIColor *)iconActiveOther { NSData *colorData = [[NSUserDefaults standardUserDefaults] objectForKey:@"kCustomUIColor"];
return self.pageStyle == 1 ? kDefaultTextColor : %orig; if (colorData) {
} NSError *error = nil;
- (UIColor *)brandIconActive { NSKeyedUnarchiver *unarchiver = [[NSKeyedUnarchiver alloc] initForReadingFromData:colorData error:&error];
return self.pageStyle == 1 ? kDefaultTextColor : %orig; if (!error) {
} [unarchiver setRequiresSecureCoding:NO];
- (UIColor *)staticBrandWhite { lcmHexColor = [unarchiver decodeObjectForKey:NSKeyedArchiveRootObjectKey];
return self.pageStyle == 1 ? kDefaultTextColor : %orig;
}
- (UIColor *)overlayIconActiveOther {
return self.pageStyle == 1 ? kDefaultTextColor : %orig;
}
- (UIColor *)overlayIconInactive {
return self.pageStyle == 1 ? [kDefaultTextColor colorWithAlphaComponent:0.7] : %orig;
}
- (UIColor *)overlayIconDisabled {
return self.pageStyle == 1 ? [kDefaultTextColor colorWithAlphaComponent:0.3] : %orig;
}
- (UIColor *)overlayFilledButtonActive {
return self.pageStyle == 1 ? [kDefaultTextColor colorWithAlphaComponent:0.2] : %orig;
}
%end
%hook YTColor
+ (BOOL)darkerPaletteTextColorEnabled { return NO; }
+ (UIColor *)white1 { return lcmHexColor; }
+ (UIColor *)white2 { return lcmHexColor; }
+ (UIColor *)white3 { return lcmHexColor; }
+ (UIColor *)white4 { return lcmHexColor; }
+ (UIColor *)white5 { return lcmHexColor; }
+ (UIColor *)grey1 { return lcmHexColor; }
+ (UIColor *)grey2 { return lcmHexColor; }
%end
%hook _ASDisplayView
- (void)layoutSubviews {
%orig;
NSArray<NSString *> *targetLabels = @[@"connect account", @"Thanks", @"Save to playlist", @"Report"];
for (UIView *subview in self.subviews) {
if ([targetLabels containsObject:subview.accessibilityLabel]) {
subview.backgroundColor = lcmHexColor;
if ([subview isKindOfClass:[UILabel class]]) {
((UILabel *)subview).textColor = [UIColor blackColor];
}
}
}
}
%end
%hook QTMColorGroup
- (UIColor *)tint100 { return kDefaultTextColor; }
- (UIColor *)tint300 { return kDefaultTextColor; }
- (UIColor *)tint500 { return kDefaultTextColor; }
- (UIColor *)tint700 { return kDefaultTextColor; }
- (UIColor *)accent200 { return kDefaultTextColor; }
- (UIColor *)accent400 { return kDefaultTextColor; }
- (UIColor *)accentColor { return kDefaultTextColor; }
- (UIColor *)brightAccentColor { return kDefaultTextColor; }
- (UIColor *)regularColor { return kDefaultTextColor; }
- (UIColor *)darkerColor { return kDefaultTextColor; }
- (UIColor *)bodyTextColor { return kDefaultTextColor; }
- (UIColor *)lightBodyTextColor { return kDefaultTextColor; }
- (UIColor *)bodyTextColorOnRegularColor { return kDefaultTextColor; }
- (UIColor *)bodyTextColorOnLighterColor { return kDefaultTextColor; }
- (UIColor *)bodyTextColorOnDarkerColor { return kDefaultTextColor; }
- (UIColor *)bodyTextColorOnAccentColor { return kDefaultTextColor; }
- (UIColor *)buttonBackgroundColor { return kDefaultTextColor; }
- (UIColor *)Color { return kDefaultTextColor; }
%end
%hook YTQTMButton
- (void)setImage:(UIImage *)image {
UIImage *tintedImage = [image imageWithRenderingMode:UIImageRenderingModeAlwaysTemplate];
[self setTintColor:kDefaultTextColor];
%orig(tintedImage);
}
%end
%hook UIExtendedSRGColorSpace
- (void)setTextColor:(UIColor *)textColor {
%orig([kDefaultTextColor colorWithAlphaComponent:0.9]);
}
%end
%hook UIExtendedSRGBColorSpace
- (void)setTextColor:(UIColor *)textColor {
%orig([kDefaultTextColor colorWithAlphaComponent:1.0]);
}
%end
%hook UIExtendedGrayColorSpace
- (void)setTextColor:(UIColor *)textColor {
%orig([kDefaultTextColor colorWithAlphaComponent:1.0]);
}
%end
%hook VideoTitleLabel
- (void)setTextColor:(UIColor *)textColor {
%orig(kDefaultTextColor);
}
%end
%hook UILabel
+ (void)load {
[[UILabel appearance] setTextColor:kDefaultTextColor];
}
- (void)setTextColor:(UIColor *)textColor {
%orig(kDefaultTextColor);
}
%end
%hook UITextField
- (void)setTextColor:(UIColor *)textColor {
%orig(kDefaultTextColor);
}
%end
%hook UITextView
- (void)setTextColor:(UIColor *)textColor {
%orig(kDefaultTextColor);
}
%end
%hook UISearchBar
- (void)setTextColor:(UIColor *)textColor {
%orig(kDefaultTextColor);
}
%end
%hook UISegmentedControl
- (void)setTitleTextAttributes:(NSDictionary *)attributes forState:(UIControlState)state {
NSMutableDictionary *modifiedAttributes = [NSMutableDictionary dictionaryWithDictionary:attributes];
modifiedAttributes[NSForegroundColorAttributeName] = kDefaultTextColor;
%orig(modifiedAttributes, state);
}
%end
%hook UIButton
- (void)setTitleColor:(UIColor *)color forState:(UIControlState)state {
%orig(kDefaultTextColor, state);
}
%end
%hook UIBarButtonItem
- (void)setTitleTextAttributes:(NSDictionary *)attributes forState:(UIControlState)state {
NSMutableDictionary *modifiedAttributes = [NSMutableDictionary dictionaryWithDictionary:attributes];
modifiedAttributes[NSForegroundColorAttributeName] = kDefaultTextColor;
%orig(modifiedAttributes, state);
}
%end
%hook NSAttributedString
- (instancetype)initWithString:(NSString *)str attributes:(NSDictionary<NSAttributedStringKey, id> *)attrs {
NSMutableDictionary *modifiedAttributes = [NSMutableDictionary dictionaryWithDictionary:attrs];
modifiedAttributes[NSForegroundColorAttributeName] = kDefaultTextColor;
return %orig(str, modifiedAttributes);
}
%end
%hook CATextLayer
- (void)setTextColor:(CGColorRef)color {
%orig(kDefaultTextColor.CGColor);
}
%end
%hook ASTextNode
- (NSAttributedString *)attributedString {
NSAttributedString *original = %orig;
NSMutableAttributedString *modified = [original mutableCopy];
[modified addAttribute:NSForegroundColorAttributeName value:kDefaultTextColor range:NSMakeRange(0, modified.length)];
return modified;
}
%end
%hook ASTextFieldNode
- (void)setTextColor:(UIColor *)textColor {
%orig(kDefaultTextColor);
}
%end
%hook ASTextView
- (void)setTextColor:(UIColor *)textColor {
%orig(kDefaultTextColor);
}
%end
%hook ASButtonNode
- (void)setTextColor:(UIColor *)textColor {
%orig(kDefaultTextColor);
}
%end
%hook UIControl
- (UIColor *)backgroundColor {
return [UIColor blackColor];
}
%end
%end
// Constructor
%ctor {
%init;
if (lowContrastMode()) {
%init(gLowContrastMode);
}
if (customContrastMode()) {
NSData *colorData = [[NSUserDefaults standardUserDefaults] objectForKey:@"kCustomUIColor"];
if (colorData) {
NSError *error = nil;
NSKeyedUnarchiver *unarchiver = [[NSKeyedUnarchiver alloc] initForReadingFromData:colorData error:&error];
if (!error) {
[unarchiver setRequiresSecureCoding:NO];
lcmHexColor = [unarchiver decodeObjectForKey:NSKeyedArchiveRootObjectKey];
if (lcmHexColor) {
%init(gCustomContrastMode);
} }
} }
} }

View file

@ -37,20 +37,24 @@ static int getNotificationIconStyle() {
NSString *imageName; NSString *imageName;
UIColor *iconColor; UIColor *iconColor;
switch (getNotificationIconStyle()) { switch (getNotificationIconStyle()) {
case 1: // Thin outline style (2020+) case 1: // Bold outline style (2024+)
imageName = isSelected ? @"notifications_selected" : @"notifications_unselected";
iconColor = [%c(YTColor) white1];
break;
case 2: // Thin outline style (2020+)
imageName = isSelected ? @"notifications_selected" : @"notifications_24pt"; imageName = isSelected ? @"notifications_selected" : @"notifications_24pt";
iconColor = [%c(YTColor) white1]; iconColor = [%c(YTColor) white1];
break; break;
case 2: // Filled style (2018+) case 3: // Filled style (2018+)
imageName = @"notifications_selected"; imageName = @"notifications_selected";
iconColor = isSelected ? [%c(YTColor) white1] : [UIColor grayColor]; iconColor = isSelected ? [%c(YTColor) white1] : [UIColor grayColor];
break; break;
case 3: // Inbox style (2014+) case 4: // Inbox style (2014+)
imageName = @"inbox_selected"; imageName = @"inbox_selected";
iconColor = isSelected ? [%c(YTColor) white1] : [UIColor grayColor]; iconColor = isSelected ? [%c(YTColor) white1] : [UIColor grayColor];
break; break;
default: // Default style default: // Default style (2025+)
imageName = isSelected ? @"notifications_selected" : @"notifications_unselected"; imageName = isSelected ? @"notifications_selected_2025" : @"notifications_unselected_2025";
iconColor = [%c(YTColor) white1]; iconColor = [%c(YTColor) white1];
break; break;
} }
@ -115,99 +119,178 @@ static int getNotificationIconStyle() {
%end %end
%end %end
// LEGACY VERSION ⚠️ // YTHidePlayerButtons 1.0.0 - made by @aricloverEXTRA
// Hide the (Connect / Thanks / Save / Report) Buttons under the Video Player - 17.33.2 and up - @arichornlover (inspired by @PoomSmart's version) static NSDictionary<NSString *, NSString *> *HideToggleMap(void) {
%hook _ASDisplayView static NSDictionary<NSString *, NSString *> *map = nil;
- (void)layoutSubviews { static dispatch_once_t onceToken;
%orig; dispatch_once(&onceToken, ^{
BOOL hideConnectButton = IS_ENABLED(@"hideConnectButton_enabled"); map = @{
BOOL hideThanksButton = IS_ENABLED(@"hideThanksButton_enabled"); // identifiers
BOOL hideSaveToPlaylistButton = IS_ENABLED(@"hideSaveToPlaylistButton_enabled"); @"id.video.share.button": @"hideShareButton_enabled",
BOOL hideReportButton = IS_ENABLED(@"hideReportButton_enabled"); @"id.ui.add_to.offline.button": @"hideDownloadButton_enabled",
@"id.video.remix.button": @"hideRemixButton_enabled",
for (UIView *subview in self.subviews) { @"clip_button.eml": @"hideClipButton_enabled",
if ([subview.accessibilityLabel isEqualToString:@"connect account"]) { @"id.ui.carousel_header": @"hideCommentSection_enabled",
subview.hidden = hideConnectButton; // fallbacks
} else if ([subview.accessibilityLabel isEqualToString:@"Thanks"]) { @"Like": @"hideLikeButton_enabled", // unidentified identifier
subview.hidden = hideThanksButton; @"Dislike": @"hideDislikeButton_enabled", // unidentified identifier
} else if ([subview.accessibilityLabel isEqualToString:@"Save to playlist"]) { @"Share": @"hideShareButton_enabled", // Share Button
subview.hidden = hideSaveToPlaylistButton; @"Ask": @"hideAskButton_enabled", // unidentified identifier
} else if ([subview.accessibilityLabel isEqualToString:@"Report"]) { @"Download": @"hideDownloadButton_enabled", // Download Button
subview.hidden = hideReportButton; @"Hype": @"hideHypeButton_enabled", // unidentified identifier
} @"Thanks": @"hideThanksButton_enabled", // unidentified identifier
} @"Remix": @"hideRemixButton_enabled", // Remix Button
@"Clip": @"hideClipButton_enabled", // Clip Button
@"Save to playlist": @"hideSaveToPlaylistButton_enabled", // unidentified identifier
@"Report": @"hideReportButton_enabled", // unidentified identifier
@"connect account": @"hideConnectButton_enabled" // unidentified identifier
};
});
return map;
} }
%end static BOOL shouldHideForKey(NSString *key) {
if (!key) return NO;
// UPDATED VERSION NSString *pref = HideToggleMap()[key];
// Hide the (Connect / Share / Remix / Thanks / Download / Clip / Save / Report) Buttons under the Video Player - 17.33.2 and up - @PoomSmart (inspired by @arichornlover) - METHOD BROKE Server-Side on May 14th 2024 if (!pref) return NO;
static BOOL findCell(ASNodeController *nodeController, NSArray <NSString *> *identifiers) { return IS_ENABLED(pref);
for (id child in [nodeController children]) { }
NSLog(@"Child: %@", [child description]); static void safeHideView(id view) {
if (!view) return;
if ([child isKindOfClass:%c(ELMNodeController)]) { dispatch_async(dispatch_get_main_queue(), ^{
NSArray <ELMComponent *> *elmChildren = [(ELMNodeController * _Nullable)child children]; @try {
for (ELMComponent *elmChild in elmChildren) { if ([view respondsToSelector:@selector(setHidden:)]) {
for (NSString *identifier in identifiers) { [view setHidden:YES];
if ([[elmChild description] containsString:identifier]) { return;
NSLog(@"Found identifier: %@", identifier); }
return YES; if ([view isKindOfClass:[UIView class]]) {
} ((UIView *)view).hidden = YES;
} return;
}
} @catch (NSException *ex) {
NSLog(@"[HidePlayerButtons] safeHideView exception: %@", ex);
}
});
}
static BOOL inspectAndHideIfMatch(id view) {
if (!view) return NO;
@try {
NSString *accId = nil;
if ([view respondsToSelector:@selector(accessibilityIdentifier)]) {
@try { accId = [view accessibilityIdentifier]; } @catch (NSException *e) { accId = nil; }
if (accId && shouldHideForKey(accId)) {
safeHideView(view);
return YES;
} }
} }
NSString *accLabel = nil;
if ([child isKindOfClass:%c(ASNodeController)]) { if ([view respondsToSelector:@selector(accessibilityLabel)]) {
ASDisplayNode *childNode = ((ASNodeController * _Nullable)child).node; // ELMContainerNode @try { accLabel = [view accessibilityLabel]; } @catch (NSException *e) { accLabel = nil; }
NSArray<id> *yogaChildren = childNode.yogaChildren; if (accLabel && shouldHideForKey(accLabel)) {
for (ASDisplayNode *displayNode in yogaChildren) { safeHideView(view);
NSLog(@"Yoga Child: %@", displayNode.accessibilityIdentifier); return YES;
}
if ([identifiers containsObject:displayNode.accessibilityIdentifier]) { }
NSLog(@"Found identifier: %@", displayNode.accessibilityIdentifier); NSString *desc = nil;
return YES; @try { desc = [[view description] copy]; } @catch (NSException *e) { desc = nil; }
} if (desc) {
for (NSString *key in HideToggleMap().allKeys) {
if (findCell(child, identifiers)) { if ([desc containsString:key] && shouldHideForKey(key)) {
safeHideView(view);
return YES; return YES;
} }
} }
} }
} @catch (NSException *ex) {
NSLog(@"[HidePlayerButtons] inspectAndHideIfMatch exception: %@", ex);
} }
return NO; return NO;
} }
static void traverseAndHideViews(UIView *root) {
%hook ASCollectionView // This stopped working on May 14th 2024 due to a Server-Side Change from YouTube. if (!root) return;
- (CGSize)sizeForElement:(ASCollectionElement * _Nullable)element { @try {
if ([self.accessibilityIdentifier isEqualToString:@"id.video.scrollable_action_bar"]) { inspectAndHideIfMatch(root);
ASCellNode *node = [element node]; NSArray<UIView *> *subs = nil;
ASNodeController *nodeController = [node controller]; @try { subs = root.subviews; } @catch (NSException *e) { subs = nil; }
if (subs && subs.count) {
if (IS_ENABLED(@"hideShareButton_enabled") && findCell(nodeController, @[@"id.video.share.button"])) { for (UIView *sv in subs) {
return CGSizeZero; if ([sv isKindOfClass:[UIView class]]) {
} traverseAndHideViews(sv);
}
if (IS_ENABLED(@"hideRemixButton_enabled") && findCell(nodeController, @[@"id.video.remix.button"])) { }
return CGSizeZero;
}
if (IS_ENABLED(@"hideThanksButton_enabled") && findCell(nodeController, @[@"Thanks"])) {
return CGSizeZero;
}
if (IS_ENABLED(@"hideClipButton_enabled") && findCell(nodeController, @[@"clip_button.eml"])) {
return CGSizeZero;
}
if (IS_ENABLED(@"hideDownloadButton_enabled") && findCell(nodeController, @[@"id.ui.add_to.offline.button"])) {
return CGSizeZero;
}
if (IS_ENABLED(@"hideCommentSection_enabled") && findCell(nodeController, @[@"id.ui.carousel_header"])) {
return CGSizeZero;
} }
} @catch (NSException *ex) {
NSLog(@"[HidePlayerButtons] traverseAndHideViews exception: %@", ex);
} }
return %orig; }
static void hideButtonsInActionBarIfNeeded(id collectionView) {
if (!collectionView) return;
@try {
// Ensure the collectionView has accessibilityIdentifier and we only operate on the action bar
NSString *accId = nil;
if ([collectionView respondsToSelector:@selector(accessibilityIdentifier)]) {
@try { accId = [collectionView accessibilityIdentifier]; } @catch (NSException *e) { accId = nil; }
}
if (!accId) return;
if (![accId isEqualToString:@"id.video.scrollable_action_bar"]) return;
NSArray *cells = nil;
if ([collectionView respondsToSelector:@selector(visibleCells)]) {
@try { cells = [collectionView visibleCells]; } @catch (NSException *e) { cells = nil; }
}
if (!cells || cells.count == 0) {
@try { cells = [collectionView subviews]; } @catch (NSException *e) { cells = nil; }
}
if (!cells || cells.count == 0) return;
for (id cell in cells) {
if ([cell isKindOfClass:[UIView class]]) {
traverseAndHideViews((UIView *)cell);
} else {
@try {
if ([cell respondsToSelector:@selector(view)]) {
id view = [cell performSelector:@selector(view)];
if ([view isKindOfClass:[UIView class]]) {
traverseAndHideViews((UIView *)view);
}
} else if ([cell respondsToSelector:@selector(node)]) {
NSString *desc = nil;
@try { desc = [cell description]; } @catch (NSException *e) { desc = nil; }
if (desc) {
// Not ideal to act on description, but we keep this non-destructive: only log for debugging
// Uncomment logging for debug builds if needed.
// NSLog(@"[HidePlayerButtons] Non-UIView cell description: %@", desc);
}
}
} @catch (NSException *ex) {
NSLog(@"[HidePlayerButtons] Exception handling non-UIView cell: %@", ex);
}
}
}
} @catch (NSException *exception) {
NSLog(@"[HidePlayerButtons] hideButtonsInActionBarIfNeeded exception: %@", exception);
}
}
%hook ASCollectionView
- (id)nodeForItemAtIndexPath:(NSIndexPath *)indexPath {
id node = %orig;
id weakSelf = (id)self;
dispatch_async(dispatch_get_main_queue(), ^{
@try {
hideButtonsInActionBarIfNeeded(weakSelf);
} @catch (NSException *e) {
NSLog(@"[HidePlayerButtons] async hide exception: %@", e);
}
});
return node;
}
- (void)nodesDidRelayout:(NSArray *)nodes {
%orig;
id weakSelf = (id)self;
dispatch_async(dispatch_get_main_queue(), ^{
@try {
hideButtonsInActionBarIfNeeded(weakSelf);
} @catch (NSException *e) {
NSLog(@"[HidePlayerButtons] relayout hide exception: %@", e);
}
});
} }
%end %end
@ -873,13 +956,6 @@ static NSMutableArray <YTIItemSectionRenderer *> *filteredArray(NSArray <YTIItem
%end %end
%end %end
// Disable Modern/Rounded Buttons (_ASDisplayView Version's not supported) - @arichornlover
%group gDisableModernButtons
%hook YTQTMButton // Disable Modern/Rounded Buttons
+ (BOOL)buttonModernizationEnabled { return NO; }
%end
%end
// Disable Rounded Hints with no Rounded Corners - @arichornlover // Disable Rounded Hints with no Rounded Corners - @arichornlover
%group gDisableRoundedHints %group gDisableRoundedHints
%hook YTBubbleHintView // Disable Modern/Rounded Hints %hook YTBubbleHintView // Disable Modern/Rounded Hints
@ -887,61 +963,6 @@ static NSMutableArray <YTIItemSectionRenderer *> *filteredArray(NSArray <YTIItem
%end %end
%end %end
// Disable Modern Flags - @arichornlover
%group gDisableModernFlags
%hook YTColdConfig
// Disable Modern Content
- (BOOL)creatorClientConfigEnableStudioModernizedMdeThumbnailPickerForClient { return NO; }
- (BOOL)cxClientEnableModernizedActionSheet { return NO; }
- (BOOL)enableClientShortsSheetsModernization { return NO; }
- (BOOL)enableTimestampModernizationForNative { return NO; }
- (BOOL)mainAppCoreClientEnableModernIaFeedStretchBottom { return NO; }
- (BOOL)mainAppCoreClientEnableModernIaFrostedBottomBar { return NO; }
- (BOOL)mainAppCoreClientEnableModernIaFrostedPivotBar { return NO; }
- (BOOL)mainAppCoreClientEnableModernIaFrostedPivotBarUpdatedBackdrop { return NO; }
- (BOOL)mainAppCoreClientEnableModernIaFrostedTopBar { return NO; }
- (BOOL)mainAppCoreClientEnableModernIaOpacityPivotBar { return NO; }
- (BOOL)mainAppCoreClientEnableModernIaTopAndBottomBarIconRefresh { return NO; }
- (BOOL)mainAppCoreClientEnableModernizedBedtimeReminderU18DefaultSettings { return NO; }
- (BOOL)modernizeCameoNavbar { return NO; }
- (BOOL)modernizeCollectionLockups { return NO; }
- (BOOL)modernizeCollectionLockupsShowVideoCount { return NO; }
- (BOOL)modernizeElementsBgColor { return NO; }
- (BOOL)modernizeElementsTextColor { return NO; }
- (BOOL)postsCreatorClientEnableModernButtonsUi { return NO; }
- (BOOL)pullToFullModernEdu { return NO; }
- (BOOL)showModernMiniplayerRedesign { return NO; }
- (BOOL)uiSystemsClientGlobalConfigEnableModernButtonsForNative { return NO; }
- (BOOL)uiSystemsClientGlobalConfigIosEnableModernTabsForNative { return NO; }
- (BOOL)uiSystemsClientGlobalConfigIosEnableSnackbarModernization { return NO; }
- (BOOL)uiSystemsClientGlobalConfigModernizeNativeBgColor { return NO; }
- (BOOL)uiSystemsClientGlobalConfigModernizeNativeTextColor { return NO; }
// Disable Rounded Content
- (BOOL)enableIosFloatingMiniplayerRoundedCornerRadius { return YES; }
- (BOOL)iosDownloadsPageRoundedThumbs { return NO; }
- (BOOL)iosRoundedSearchBarSuggestZeroPadding { return NO; }
- (BOOL)uiSystemsClientGlobalConfigEnableRoundedDialogForNative { return NO; }
- (BOOL)uiSystemsClientGlobalConfigEnableRoundedThumbnailsForNative { return NO; }
- (BOOL)uiSystemsClientGlobalConfigEnableRoundedThumbnailsForNativeLongTail { return NO; }
- (BOOL)uiSystemsClientGlobalConfigEnableRoundedTimestampForNative { return NO; }
// Disable Optional Content
- (BOOL)elementsClientIosElementsEnableLayoutUpdateForIob { return NO; }
- (BOOL)supportElementsInMenuItemSupportedRenderers { return NO; }
- (BOOL)isNewRadioButtonStyleEnabled { return NO; }
- (BOOL)uiSystemsClientGlobalConfigEnableButtonSentenceCasingForNative { return NO; }
- (BOOL)mainAppCoreClientEnableClientYouTab { return NO; }
- (BOOL)mainAppCoreClientEnableClientYouLatency { return NO; }
- (BOOL)mainAppCoreClientEnableClientYouTabTablet { return NO; }
%end
%hook YTHotConfig
- (BOOL)liveChatIosUseModernRotationDetection { return NO; } // Disable Modern Content (YTHotConfig)
- (BOOL)liveChatModernizeClassicElementizeTextMessage { return NO; }
- (BOOL)iosShouldRepositionChannelBar { return NO; }
- (BOOL)enableElementRendererOnChannelCreation { return NO; }
%end
%end
// Disable Ambient Mode in Fullscreen - @arichornlover // Disable Ambient Mode in Fullscreen - @arichornlover
%group gDisableAmbientMode %group gDisableAmbientMode
%hook YTCinematicContainerView %hook YTCinematicContainerView
@ -1943,15 +1964,9 @@ static NSMutableArray <YTIItemSectionRenderer *> *filteredArray(NSArray <YTIItem
if (IS_ENABLED(kClassicVideoPlayer)) { if (IS_ENABLED(kClassicVideoPlayer)) {
%init(gClassicVideoPlayer); %init(gClassicVideoPlayer);
} }
if (IS_ENABLED(kDisableModernButtons)) {
%init(gDisableModernButtons);
}
if (IS_ENABLED(kDisableRoundedHints)) { if (IS_ENABLED(kDisableRoundedHints)) {
%init(gDisableRoundedHints); %init(gDisableRoundedHints);
} }
if (IS_ENABLED(kDisableModernFlags)) {
%init(gDisableModernFlags);
}
if (IS_ENABLED(kDisableAmbientMode)) { if (IS_ENABLED(kDisableAmbientMode)) {
%init(gDisableAmbientMode); %init(gDisableAmbientMode);
} }

View file

@ -8,18 +8,8 @@
- (NSDictionary *)infoDictionary { - (NSDictionary *)infoDictionary {
NSDictionary *orig = %orig; NSDictionary *orig = %orig;
if ([self isEqual:NSBundle.mainBundle]) { if ([self isEqual:NSBundle.mainBundle]) {
NSArray<NSString *> *stack = [NSThread callStackSymbols]; NSMutableDictionary *patched = [orig mutableCopy];
BOOL needsSpoof = NO; if (patched) {
for (NSString *frame in stack) {
if ([frame containsString:@"GIDSignIn"] ||
[frame containsString:@"GTMSessionFetcher"] ||
[frame containsString:@"GoogleSignIn"]) {
needsSpoof = YES;
break;
}
}
if (needsSpoof) {
NSMutableDictionary *patched = [orig mutableCopy];
patched[@"CFBundleIdentifier"] = @"com.google.ios.youtube"; patched[@"CFBundleIdentifier"] = @"com.google.ios.youtube";
return [patched copy]; return [patched copy];
} }

View file

@ -565,12 +565,26 @@ NSString *cacheDescription = [NSString stringWithFormat:@"%@", GetCacheSize()];
kLowContrastMode, kLowContrastMode,
({ ({
if (enable) { if (enable) {
if (@available(iOS 16.0, *)) {
} else {
UIAlertController *alert = [UIAlertController alertControllerWithTitle:@"Outdated iOS Version" message:@"LowContrastMode is designed for iOS 16 or higher. You are running an older iOS version. Enable anyway?" preferredStyle:UIAlertControllerStyleAlert];
UIAlertAction *okAction = [UIAlertAction actionWithTitle:@"Enable" style:UIAlertActionStyleDefault handler:^(UIAlertAction * _Nonnull action) {
[[NSUserDefaults standardUserDefaults] setBool:enable forKey:kLowContrastMode];
[settingsViewController reloadData];
SHOW_RELAUNCH_YT_SNACKBAR;
}];
UIAlertAction *cancelAction = [UIAlertAction actionWithTitle:@"Cancel" style:UIAlertActionStyleCancel handler:nil];
[alert addAction:okAction];
[alert addAction:cancelAction];
[settingsViewController presentViewController:alert animated:YES completion:nil];
return NO;
}
Class YTVersionUtilsClass = %c(YTVersionUtils); Class YTVersionUtilsClass = %c(YTVersionUtils);
NSString *appVersion = [YTVersionUtilsClass performSelector:@selector(appVersion)]; NSString *appVersion = [YTVersionUtilsClass performSelector:@selector(appVersion)];
NSComparisonResult result1 = [appVersion compare:@"17.33.2" options:NSNumericSearch]; NSComparisonResult result1 = [appVersion compare:@"19.01.1" options:NSNumericSearch];
NSComparisonResult result2 = [appVersion compare:@"17.38.10" options:NSNumericSearch]; NSComparisonResult result2 = [appVersion compare:@"20.33.2" options:NSNumericSearch];
if (result1 == NSOrderedAscending) { if (result1 == NSOrderedAscending) {
UIAlertController *alert = [UIAlertController alertControllerWithTitle:@"Discontinued Version" message:[NSString stringWithFormat:@"You are using v%@, a discontinued version of YouTube that may not work with LowContrastMode. Supported versions are v17.33.2-v17.38.10. Enable anyway?", appVersion] preferredStyle:UIAlertControllerStyleAlert]; UIAlertController *alert = [UIAlertController alertControllerWithTitle:@"Discontinued YouTube Version" message:[NSString stringWithFormat:@"You are using v%@, a discontinued version of YouTube that may not work with LowContrastMode. Supported versions are v19.01.1-v20.33.2. Enable anyway?", appVersion] preferredStyle:UIAlertControllerStyleAlert];
UIAlertAction *okAction = [UIAlertAction actionWithTitle:@"Enable" style:UIAlertActionStyleDefault handler:^(UIAlertAction * _Nonnull action) { UIAlertAction *okAction = [UIAlertAction actionWithTitle:@"Enable" style:UIAlertActionStyleDefault handler:^(UIAlertAction * _Nonnull action) {
[[NSUserDefaults standardUserDefaults] setBool:enable forKey:kLowContrastMode]; [[NSUserDefaults standardUserDefaults] setBool:enable forKey:kLowContrastMode];
[settingsViewController reloadData]; [settingsViewController reloadData];
@ -582,7 +596,7 @@ NSString *cacheDescription = [NSString stringWithFormat:@"%@", GetCacheSize()];
[settingsViewController presentViewController:alert animated:YES completion:nil]; [settingsViewController presentViewController:alert animated:YES completion:nil];
return NO; return NO;
} else if (result2 == NSOrderedDescending) { } else if (result2 == NSOrderedDescending) {
UIAlertController *alert = [UIAlertController alertControllerWithTitle:@"Incompatible Version" message:[NSString stringWithFormat:@"LowContrastMode is only available for app versions v17.33.2-v17.38.10. You are using v%@. Enable anyway?", appVersion] preferredStyle:UIAlertControllerStyleAlert]; UIAlertController *alert = [UIAlertController alertControllerWithTitle:@"Incompatible YouTube Version" message:[NSString stringWithFormat:@"LowContrastMode is only available for app versions v19.01.1-v20.33.2. You are using v%@. Enable anyway?", appVersion] preferredStyle:UIAlertControllerStyleAlert];
UIAlertAction *okAction = [UIAlertAction actionWithTitle:@"Enable" style:UIAlertActionStyleDefault handler:^(UIAlertAction * _Nonnull action) { UIAlertAction *okAction = [UIAlertAction actionWithTitle:@"Enable" style:UIAlertActionStyleDefault handler:^(UIAlertAction * _Nonnull action) {
[[NSUserDefaults standardUserDefaults] setBool:enable forKey:kLowContrastMode]; [[NSUserDefaults standardUserDefaults] setBool:enable forKey:kLowContrastMode];
[settingsViewController reloadData]; [settingsViewController reloadData];
@ -594,7 +608,7 @@ NSString *cacheDescription = [NSString stringWithFormat:@"%@", GetCacheSize()];
[settingsViewController presentViewController:alert animated:YES completion:nil]; [settingsViewController presentViewController:alert animated:YES completion:nil];
return NO; return NO;
} else if (UIScreen.mainScreen.traitCollection.userInterfaceStyle != UIUserInterfaceStyleDark) { } else if (UIScreen.mainScreen.traitCollection.userInterfaceStyle != UIUserInterfaceStyleDark) {
UIAlertController *alert = [UIAlertController alertControllerWithTitle:@"Light Mode Detected" message:@"LowContrastMode is only available in Dark Mode. Please switch to Dark Mode to be able to use LowContrastMode." preferredStyle:UIAlertControllerStyleAlert]; UIAlertController *alert = [UIAlertController alertControllerWithTitle:@"Light Mode Detected" message:@"LowContrastMode is only designed for Dark Mode in mind. Please enable Dark Mode to be able to use LowContrastMode." preferredStyle:UIAlertControllerStyleAlert];
UIAlertAction *okAction = [UIAlertAction actionWithTitle:@"OK" style:UIAlertActionStyleDefault handler:nil]; UIAlertAction *okAction = [UIAlertAction actionWithTitle:@"OK" style:UIAlertActionStyleDefault handler:nil];
[alert addAction:okAction]; [alert addAction:okAction];
[settingsViewController presentViewController:alert animated:YES completion:nil]; [settingsViewController presentViewController:alert animated:YES completion:nil];
@ -625,7 +639,7 @@ NSString *cacheDescription = [NSString stringWithFormat:@"%@", GetCacheSize()];
Class YTVersionUtilsClass = %c(YTVersionUtils); Class YTVersionUtilsClass = %c(YTVersionUtils);
NSString *appVersion = [YTVersionUtilsClass performSelector:@selector(appVersion)]; NSString *appVersion = [YTVersionUtilsClass performSelector:@selector(appVersion)];
// Alert the user that they need to enable the fix // Alert the user that they need to enable the fix
UIAlertController *alert = [UIAlertController alertControllerWithTitle:@"Incompatibile" message:[NSString stringWithFormat:@"LowContrastMode is only available for app versions v17.33.2-v17.38.10. \nYou are currently using v%@. \n\nWorkaround: if you want to use this then I recommend enabling \"Fix LowContrastMode\" Option.", appVersion] preferredStyle:UIAlertControllerStyleAlert]; UIAlertController *alert = [UIAlertController alertControllerWithTitle:@"Incompatible YouTube Version" message:[NSString stringWithFormat:@"LowContrastMode is only available for app versions v19.01.1-v20.33.2. You are using v%@. Enable anyway?", appVersion] preferredStyle:UIAlertControllerStyleAlert];
UIAlertAction *okAction = [UIAlertAction actionWithTitle:@"OK" style:UIAlertActionStyleDefault handler:nil]; UIAlertAction *okAction = [UIAlertAction actionWithTitle:@"OK" style:UIAlertActionStyleDefault handler:nil];
[alert addAction:okAction]; [alert addAction:okAction];
[settingsViewController presentViewController:alert animated:YES completion:nil]; [settingsViewController presentViewController:alert animated:YES completion:nil];
@ -642,7 +656,6 @@ NSString *cacheDescription = [NSString stringWithFormat:@"%@", GetCacheSize()];
[settingsViewController reloadData]; [settingsViewController reloadData];
return YES; return YES;
}] }]
]; ];
YTSettingsPickerViewController *picker = [[%c(YTSettingsPickerViewController) alloc] initWithNavTitle:LOC(@"Low Contrast Mode Selector") pickerSectionTitle:nil rows:rows selectedItemIndex:contrastMode() parentResponder:[self parentResponder]]; YTSettingsPickerViewController *picker = [[%c(YTSettingsPickerViewController) alloc] initWithNavTitle:LOC(@"Low Contrast Mode Selector") pickerSectionTitle:nil rows:rows selectedItemIndex:contrastMode() parentResponder:[self parentResponder]];
[settingsViewController pushViewController:picker]; [settingsViewController pushViewController:picker];
@ -652,16 +665,14 @@ NSString *cacheDescription = [NSString stringWithFormat:@"%@", GetCacheSize()];
]; ];
[sectionItems addObject:lowContrastModeButton]; [sectionItems addObject:lowContrastModeButton];
SWITCH2(LOC(@"CLASSIC_VIDEO_PLAYER"), LOC(@"CLASSIC_VIDEO_PLAYER_DESC"), kClassicVideoPlayer); SWITCH2(LOC(@"CLASSIC_VIDEO_PLAYER"), LOC(@"CLASSIC_VIDEO_PLAYER_DESC"), kClassicVideoPlayer);
SWITCH2(LOC(@"DISABLE_MODERN_BUTTONS"), LOC(@"DISABLE_MODERN_BUTTONS_DESC"), kDisableModernButtons);
SWITCH2(LOC(@"DISABLE_ROUNDED_CORNERS_ON_HINTS"), LOC(@"DISABLE_ROUNDED_CORNERS_ON_HINTS_DESC"), kDisableRoundedHints); SWITCH2(LOC(@"DISABLE_ROUNDED_CORNERS_ON_HINTS"), LOC(@"DISABLE_ROUNDED_CORNERS_ON_HINTS_DESC"), kDisableRoundedHints);
SWITCH2(LOC(@"DISABLE_MODERN_FLAGS"), LOC(@"DISABLE_MODERN_FLAGS_DESC"), kDisableModernFlags);
SWITCH3( SWITCH3(
LOC(@"YTNOMODERNUI"), LOC(@"YTNOMODERNUI"),
LOC(@"YTNOMODERNUI_DESC"), LOC(@"YTNOMODERNUI_DESC"),
kYTNoModernUI, kYTNoModernUI,
({ ({
if (enable) { if (enable) {
UIAlertController *alert = [UIAlertController alertControllerWithTitle:@"Warning" message:@"This will force-enable other settings on restart. To disable them, you must turn this setting off." preferredStyle:UIAlertControllerStyleAlert]; UIAlertController *alert = [UIAlertController alertControllerWithTitle:@"Warning" message:@"This will force-enable other settings on restart. To disable them, you must turn this setting off. (YTNoModernUI is Deprecated)" preferredStyle:UIAlertControllerStyleAlert];
UIAlertAction *okAction = [UIAlertAction actionWithTitle:@"OK" style:UIAlertActionStyleDefault handler:nil]; UIAlertAction *okAction = [UIAlertAction actionWithTitle:@"OK" style:UIAlertActionStyleDefault handler:nil];
[alert addAction:okAction]; [alert addAction:okAction];
[settingsViewController presentViewController:alert animated:YES completion:nil]; [settingsViewController presentViewController:alert animated:YES completion:nil];
@ -678,303 +689,267 @@ NSString *cacheDescription = [NSString stringWithFormat:@"%@", GetCacheSize()];
accessibilityIdentifier:nil accessibilityIdentifier:nil
detailTextBlock:^NSString *() { detailTextBlock:^NSString *() {
switch (appVersionSpoofer()) { switch (appVersionSpoofer()) {
case 0: case 0: return @"v21.08.3";
return @"v20.33.2"; case 1: return @"v21.07.4";
case 1: case 2: return @"v21.06.2";
return @"v20.32.5"; case 3: return @"v21.05.3";
case 2: case 4: return @"v21.04.2";
return @"v20.32.4"; case 5: return @"v21.03.2";
case 3: case 6: return @"v21.02.3";
return @"v20.31.6"; case 7: return @"v20.50.10";
case 4: case 8: return @"v20.50.9";
return @"v20.31.5"; case 9: return @"v20.50.6";
case 5: case 10: return @"v20.49.5";
return @"v20.30.5"; case 11: return @"v20.47.3";
case 6: case 12: return @"v20.46.3";
return @"v20.29.3"; case 13: return @"v20.46.2";
case 7: case 14: return @"v20.45.3";
return @"v20.28.2"; case 15: return @"v20.44.2";
case 8: case 16: return @"v20.43.3";
return @"v20.26.7"; case 17: return @"v20.42.3";
case 9: case 18: return @"v20.41.5";
return @"v20.25.4"; case 19: return @"v20.41.4";
case 10: case 20: return @"v20.40.4";
return @"v20.24.5"; case 21: return @"v20.39.6";
case 11: case 22: return @"v20.39.5";
return @"v20.24.4"; case 23: return @"v20.39.4";
case 12: case 24: return @"v20.38.4";
return @"v20.23.3"; case 25: return @"v20.38.3";
case 13: case 26: return @"v20.37.5";
return @"v20.22.1"; case 27: return @"v20.37.3";
case 14: case 28: return @"v20.36.3";
return @"v20.21.6"; case 29: return @"v20.35.2";
case 15: case 30: return @"v20.34.2";
return @"v20.20.7"; case 31: return @"v20.33.2";
case 16: case 32: return @"v20.32.5";
return @"v20.20.5"; case 33: return @"v20.32.4";
case 17: case 34: return @"v20.31.6";
return @"v20.19.3"; case 35: return @"v20.31.5";
case 18: case 36: return @"v20.30.5";
return @"v20.19.2"; case 37: return @"v20.29.3";
case 19: case 38: return @"v20.28.2";
return @"v20.18.5"; case 39: return @"v20.26.7";
case 20: case 40: return @"v20.25.4";
return @"v20.18.4"; case 41: return @"v20.24.5";
case 21: case 42: return @"v20.24.4";
return @"v20.16.7"; case 43: return @"v20.23.3";
case 22: case 44: return @"v20.22.1";
return @"v20.15.1"; case 45: return @"v20.21.6";
case 23: case 46: return @"v20.20.7";
return @"v20.14.2"; case 47: return @"v20.20.5";
case 24: case 48: return @"v20.19.3";
return @"v20.13.5"; case 49: return @"v20.19.2";
case 25: case 50: return @"v20.18.5";
return @"v20.12.4"; case 51: return @"v20.18.4";
case 26: case 52: return @"v20.16.7";
return @"v20.11.6"; case 53: return @"v20.15.1";
case 27: case 54: return @"v20.14.2";
return @"v20.10.4"; case 55: return @"v20.13.5";
case 28: case 56: return @"v20.12.4";
return @"v20.10.3"; case 57: return @"v20.11.6";
case 29: case 58: return @"v20.10.4";
return @"v20.09.3"; case 59: return @"v20.10.3";
case 30: case 60: return @"v20.09.3";
return @"v20.08.3"; case 61: return @"v20.08.3";
case 31: case 62: return @"v20.07.6";
return @"v20.07.6"; case 63: return @"v20.06.03";
case 32: case 64: return @"v20.05.4";
return @"v20.06.03"; case 65: return @"v20.03.1";
case 33: case 66: return @"v20.03.02";
return @"v20.05.4"; case 67: return @"v20.02.3";
case 34: case 68: return @"v19.49.7";
return @"v20.03.1"; case 69: return @"v19.49.5";
case 35: case 70: return @"v19.49.3";
return @"v20.03.02"; case 71: return @"v19.47.7";
case 36: case 72: return @"v19.46.3";
return @"v20.02.3"; case 73: return @"v19.45.4";
case 37: case 74: return @"v19.44.4";
return @"v19.49.7"; case 75: return @"v19.43.2";
case 38: case 76: return @"v19.42.1";
return @"v19.49.5"; case 77: return @"v19.41.3";
case 39: case 78: return @"v19.40.4";
return @"v19.49.3"; case 79: return @"v19.39.1";
case 40: case 80: return @"v19.38.2";
return @"v19.47.7"; case 81: return @"v19.37.2";
case 41: case 82: return @"v19.36.1";
return @"v19.46.3"; case 83: return @"v19.35.3";
case 42: case 84: return @"v19.34.2";
return @"v19.45.4"; case 85: return @"v19.33.2";
case 43: case 86: return @"v19.32.8";
return @"v19.44.4"; case 87: return @"v19.32.6";
case 44: case 88: return @"v19.31.4";
return @"v19.43.2"; case 89: return @"v19.30.2";
case 45: case 90: return @"v19.29.1";
return @"v19.42.1"; case 91: return @"v19.28.1";
case 46: case 92: return @"v19.26.5";
return @"v19.41.3"; case 93: return @"v19.25.4";
case 47: case 94: return @"v19.25.3";
return @"v19.40.4"; case 95: return @"v19.24.3";
case 48: case 96: return @"v19.24.2";
return @"v19.39.1"; case 97: return @"v19.23.3";
case 49: case 98: return @"v19.22.6";
return @"v19.38.2"; case 99: return @"v19.22.3";
case 50: case 100: return @"v19.21.3";
return @"v19.37.2"; case 101: return @"v19.21.2";
case 51: case 102: return @"v19.20.2 (Deprecated)";
return @"v19.36.1"; case 103: return @"v19.19.7 (Deprecated)";
case 52: case 104: return @"v19.19.5 (Deprecated)";
return @"v19.35.3"; case 105: return @"v19.18.2 (Deprecated)";
case 53: case 106: return @"v19.17.2 (Deprecated)";
return @"v19.34.2"; case 107: return @"v19.16.3 (Deprecated)";
case 54: case 108: return @"v19.15.1 (Deprecated)";
return @"v19.33.2"; case 109: return @"v19.14.3 (Deprecated)";
case 55: case 110: return @"v19.14.2 (Deprecated)";
return @"v19.32.8"; case 111: return @"v19.13.1 (Deprecated)";
case 56: case 112: return @"v19.12.3 (Deprecated)";
return @"v19.32.6"; case 113: return @"v19.10.7 (Deprecated)";
case 57: case 114: return @"v19.10.6 (Deprecated)";
return @"v19.31.4"; case 115: return @"v19.10.5 (Deprecated)";
case 58: case 116: return @"v19.09.4 (Deprecated)";
return @"v19.30.2"; case 117: return @"v19.09.3 (Deprecated)";
case 59: case 118: return @"v19.08.2 (Deprecated)";
return @"v19.29.1"; case 119: return @"v19.07.5 (Deprecated)";
case 60: case 120: return @"v19.07.4 (Deprecated)";
return @"v19.28.1"; case 121: return @"v19.06.2 (Deprecated)";
case 61: case 122: return @"v19.05.5 (Deprecated)";
return @"v19.26.5"; case 123: return @"v19.05.3 (Deprecated)";
case 62: case 124: return @"v19.04.3 (Deprecated)";
return @"v19.25.4"; case 125: return @"v19.03.2 (Deprecated)";
case 63: case 126: return @"v19.02.1 (Deprecated)";
return @"v19.25.3"; case 127: return @"v19.01.1 (Deprecated)";
case 64: default: return @"v21.08.3";
return @"v19.24.3";
case 65:
return @"v19.24.2";
case 66:
return @"v19.23.3";
case 67:
return @"v19.22.6";
case 68:
return @"v19.22.3";
case 69:
return @"v19.21.3";
case 70:
return @"v19.21.2";
case 71:
return @"v19.20.2";
case 72:
return @"v19.19.7";
case 73:
return @"v19.19.5";
case 74:
return @"v19.18.2";
case 75:
return @"v19.17.2";
case 76:
return @"v19.16.3";
case 77:
return @"v19.15.1";
case 78:
return @"v19.14.3";
case 79:
return @"v19.14.2";
case 80:
return @"v19.13.1";
case 81:
return @"v19.12.3";
case 82:
return @"v19.10.7";
case 83:
return @"v19.10.6";
case 84:
return @"v19.10.5";
case 85:
return @"v19.09.4";
case 86:
return @"v19.09.3";
case 87:
return @"v19.08.2";
case 88:
return @"v19.07.5";
case 89:
return @"v19.07.4";
case 90:
return @"v19.06.2";
case 91:
return @"v19.05.5";
case 92:
return @"v19.05.3";
case 93:
return @"v19.04.3";
case 94:
return @"v19.03.2";
case 95:
return @"v19.02.1";
case 96:
return @"v19.01.1";
default:
return @"v20.33.2";
} }
} }
selectBlock:^BOOL (YTSettingsCell *cell, NSUInteger arg1) { selectBlock:^BOOL (YTSettingsCell *cell, NSUInteger arg1) {
NSArray <YTSettingsSectionItem *> *rows = @[ NSArray <YTSettingsSectionItem *> *rows = @[
SPOOFER_VERSION(@"v20.33.2", 0), SPOOFER_VERSION(@"v21.08.3", 0),
SPOOFER_VERSION(@"v20.32.5", 1), SPOOFER_VERSION(@"v21.07.4", 1),
SPOOFER_VERSION(@"v20.32.4", 2), SPOOFER_VERSION(@"v21.06.2", 2),
SPOOFER_VERSION(@"v20.31.6", 3), SPOOFER_VERSION(@"v21.05.3", 3),
SPOOFER_VERSION(@"v20.31.5", 4), SPOOFER_VERSION(@"v21.04.2", 4),
SPOOFER_VERSION(@"v20.30.5", 5), SPOOFER_VERSION(@"v21.03.2", 5),
SPOOFER_VERSION(@"v20.29.3", 6), SPOOFER_VERSION(@"v21.02.3", 6),
SPOOFER_VERSION(@"v20.28.2", 7), SPOOFER_VERSION(@"v20.50.10", 7),
SPOOFER_VERSION(@"v20.26.7", 8), SPOOFER_VERSION(@"v20.50.9", 8),
SPOOFER_VERSION(@"v20.25.4", 9), SPOOFER_VERSION(@"v20.50.6", 9),
SPOOFER_VERSION(@"v20.24.5", 10), SPOOFER_VERSION(@"v20.49.5", 10),
SPOOFER_VERSION(@"v20.24.4", 11), SPOOFER_VERSION(@"v20.47.3", 11),
SPOOFER_VERSION(@"v20.23.3", 12), SPOOFER_VERSION(@"v20.46.3", 12),
SPOOFER_VERSION(@"v20.22.1", 13), SPOOFER_VERSION(@"v20.46.2", 13),
SPOOFER_VERSION(@"v20.21.6", 14), SPOOFER_VERSION(@"v20.45.3", 14),
SPOOFER_VERSION(@"v20.20.7", 15), SPOOFER_VERSION(@"v20.44.2", 15),
SPOOFER_VERSION(@"v20.20.5", 16), SPOOFER_VERSION(@"v20.43.3", 16),
SPOOFER_VERSION(@"v20.19.3", 17), SPOOFER_VERSION(@"v20.42.3", 17),
SPOOFER_VERSION(@"v20.19.2", 18), SPOOFER_VERSION(@"v20.41.5", 18),
SPOOFER_VERSION(@"v20.18.5", 19), SPOOFER_VERSION(@"v20.41.4", 19),
SPOOFER_VERSION(@"v20.18.4", 20), SPOOFER_VERSION(@"v20.40.4", 20),
SPOOFER_VERSION(@"v20.16.7", 21), SPOOFER_VERSION(@"v20.39.6", 21),
SPOOFER_VERSION(@"v20.15.1", 22), SPOOFER_VERSION(@"v20.39.5", 22),
SPOOFER_VERSION(@"v20.14.2", 23), SPOOFER_VERSION(@"v20.39.4", 23),
SPOOFER_VERSION(@"v20.13.5", 24), SPOOFER_VERSION(@"v20.38.4", 24),
SPOOFER_VERSION(@"v20.12.4", 25), SPOOFER_VERSION(@"v20.38.3", 25),
SPOOFER_VERSION(@"v20.11.6", 26), SPOOFER_VERSION(@"v20.37.5", 26),
SPOOFER_VERSION(@"v20.10.4", 27), SPOOFER_VERSION(@"v20.37.3", 27),
SPOOFER_VERSION(@"v20.10.3", 28), SPOOFER_VERSION(@"v20.36.3", 28),
SPOOFER_VERSION(@"v20.09.3", 29), SPOOFER_VERSION(@"v20.35.2", 29),
SPOOFER_VERSION(@"v20.08.3", 30), SPOOFER_VERSION(@"v20.34.2", 30),
SPOOFER_VERSION(@"v20.07.6", 31), SPOOFER_VERSION(@"v20.33.2", 31),
SPOOFER_VERSION(@"v20.06.03", 32), SPOOFER_VERSION(@"v20.32.5", 32),
SPOOFER_VERSION(@"v20.05.4", 33), SPOOFER_VERSION(@"v20.32.4", 33),
SPOOFER_VERSION(@"v20.03.1", 34), SPOOFER_VERSION(@"v20.31.6", 34),
SPOOFER_VERSION(@"v20.03.02", 35), SPOOFER_VERSION(@"v20.31.5", 35),
SPOOFER_VERSION(@"v20.02.3", 36), SPOOFER_VERSION(@"v20.30.5", 36),
SPOOFER_VERSION(@"v19.49.7", 37), SPOOFER_VERSION(@"v20.29.3", 37),
SPOOFER_VERSION(@"v19.49.5", 38), SPOOFER_VERSION(@"v20.28.2", 38),
SPOOFER_VERSION(@"v19.49.3", 39), SPOOFER_VERSION(@"v20.26.7", 39),
SPOOFER_VERSION(@"v19.47.7", 40), SPOOFER_VERSION(@"v20.25.4", 40),
SPOOFER_VERSION(@"v19.46.3", 41), SPOOFER_VERSION(@"v20.24.5", 41),
SPOOFER_VERSION(@"v19.45.4", 42), SPOOFER_VERSION(@"v20.24.4", 42),
SPOOFER_VERSION(@"v19.44.4", 43), SPOOFER_VERSION(@"v20.23.3", 43),
SPOOFER_VERSION(@"v19.43.2", 44), SPOOFER_VERSION(@"v20.22.1", 44),
SPOOFER_VERSION(@"v19.42.1", 45), SPOOFER_VERSION(@"v20.21.6", 45),
SPOOFER_VERSION(@"v19.41.3", 46), SPOOFER_VERSION(@"v20.20.7", 46),
SPOOFER_VERSION(@"v19.40.4", 47), SPOOFER_VERSION(@"v20.20.5", 47),
SPOOFER_VERSION(@"v19.39.1", 48), SPOOFER_VERSION(@"v20.19.3", 48),
SPOOFER_VERSION(@"v19.38.2", 49), SPOOFER_VERSION(@"v20.19.2", 49),
SPOOFER_VERSION(@"v19.37.2", 50), SPOOFER_VERSION(@"v20.18.5", 50),
SPOOFER_VERSION(@"v19.36.1", 51), SPOOFER_VERSION(@"v20.18.4", 51),
SPOOFER_VERSION(@"v19.35.3", 52), SPOOFER_VERSION(@"v20.16.7", 52),
SPOOFER_VERSION(@"v19.34.2", 53), SPOOFER_VERSION(@"v20.15.1", 53),
SPOOFER_VERSION(@"v19.33.2", 54), SPOOFER_VERSION(@"v20.14.2", 54),
SPOOFER_VERSION(@"v19.32.8", 55), SPOOFER_VERSION(@"v20.13.5", 55),
SPOOFER_VERSION(@"v19.32.6", 56), SPOOFER_VERSION(@"v20.12.4", 56),
SPOOFER_VERSION(@"v19.31.4", 57), SPOOFER_VERSION(@"v20.11.6", 57),
SPOOFER_VERSION(@"v19.30.2", 58), SPOOFER_VERSION(@"v20.10.4", 58),
SPOOFER_VERSION(@"v19.29.1", 59), SPOOFER_VERSION(@"v20.10.3", 59),
SPOOFER_VERSION(@"v19.28.1", 60), SPOOFER_VERSION(@"v20.09.3", 60),
SPOOFER_VERSION(@"v19.26.5", 61), SPOOFER_VERSION(@"v20.08.3", 61),
SPOOFER_VERSION(@"v19.25.4", 62), SPOOFER_VERSION(@"v20.07.6", 62),
SPOOFER_VERSION(@"v19.25.3", 63), SPOOFER_VERSION(@"v20.06.03", 63),
SPOOFER_VERSION(@"v19.24.3", 64), SPOOFER_VERSION(@"v20.05.4", 64),
SPOOFER_VERSION(@"v19.24.2", 65), SPOOFER_VERSION(@"v20.03.1", 65),
SPOOFER_VERSION(@"v19.23.3", 66), SPOOFER_VERSION(@"v20.03.02", 66),
SPOOFER_VERSION(@"v19.22.6", 67), SPOOFER_VERSION(@"v20.02.3", 67),
SPOOFER_VERSION(@"v19.22.3", 68), SPOOFER_VERSION(@"v19.49.7", 68),
SPOOFER_VERSION(@"v19.21.3", 69), SPOOFER_VERSION(@"v19.49.5", 69),
SPOOFER_VERSION(@"v19.21.2", 70), SPOOFER_VERSION(@"v19.49.3", 70),
SPOOFER_VERSION(@"v19.20.2", 71), SPOOFER_VERSION(@"v19.47.7", 71),
SPOOFER_VERSION(@"v19.19.7", 72), SPOOFER_VERSION(@"v19.46.3", 72),
SPOOFER_VERSION(@"v19.19.5", 73), SPOOFER_VERSION(@"v19.45.4", 73),
SPOOFER_VERSION(@"v19.18.2", 74), SPOOFER_VERSION(@"v19.44.4", 74),
SPOOFER_VERSION(@"v19.17.2", 75), SPOOFER_VERSION(@"v19.43.2", 75),
SPOOFER_VERSION(@"v19.16.3", 76), SPOOFER_VERSION(@"v19.42.1", 76),
SPOOFER_VERSION(@"v19.15.1", 77), SPOOFER_VERSION(@"v19.41.3", 77),
SPOOFER_VERSION(@"v19.14.3", 78), SPOOFER_VERSION(@"v19.40.4", 78),
SPOOFER_VERSION(@"v19.14.2", 79), SPOOFER_VERSION(@"v19.39.1", 79),
SPOOFER_VERSION(@"v19.13.1", 80), SPOOFER_VERSION(@"v19.38.2", 80),
SPOOFER_VERSION(@"v19.12.3", 81), SPOOFER_VERSION(@"v19.37.2", 81),
SPOOFER_VERSION(@"v19.10.7", 82), SPOOFER_VERSION(@"v19.36.1", 82),
SPOOFER_VERSION(@"v19.10.6", 83), SPOOFER_VERSION(@"v19.35.3", 83),
SPOOFER_VERSION(@"v19.10.5", 84), SPOOFER_VERSION(@"v19.34.2", 84),
SPOOFER_VERSION(@"v19.09.4", 85), SPOOFER_VERSION(@"v19.33.2", 85),
SPOOFER_VERSION(@"v19.09.3", 86), SPOOFER_VERSION(@"v19.32.8", 86),
SPOOFER_VERSION(@"v19.08.2", 87), SPOOFER_VERSION(@"v19.32.6", 87),
SPOOFER_VERSION(@"v19.07.5", 88), SPOOFER_VERSION(@"v19.31.4", 88),
SPOOFER_VERSION(@"v19.07.4", 89), SPOOFER_VERSION(@"v19.30.2", 89),
SPOOFER_VERSION(@"v19.06.2", 90), SPOOFER_VERSION(@"v19.29.1", 90),
SPOOFER_VERSION(@"v19.05.5", 91), SPOOFER_VERSION(@"v19.28.1", 91),
SPOOFER_VERSION(@"v19.05.3", 92), SPOOFER_VERSION(@"v19.26.5", 92),
SPOOFER_VERSION(@"v19.04.3", 93), SPOOFER_VERSION(@"v19.25.4", 93),
SPOOFER_VERSION(@"v19.03.2", 94), SPOOFER_VERSION(@"v19.25.3", 94),
SPOOFER_VERSION(@"v19.02.1", 95), SPOOFER_VERSION(@"v19.24.3", 95),
SPOOFER_VERSION(@"v19.01.1", 96) SPOOFER_VERSION(@"v19.24.2", 96),
SPOOFER_VERSION(@"v19.23.3", 97),
SPOOFER_VERSION(@"v19.22.6", 98),
SPOOFER_VERSION(@"v19.22.3", 99),
SPOOFER_VERSION(@"v19.21.3", 100),
SPOOFER_VERSION(@"v19.21.2", 101),
SPOOFER_VERSION(@"v19.20.2 (Deprecated)", 102),
SPOOFER_VERSION(@"v19.19.7 (Deprecated)", 103),
SPOOFER_VERSION(@"v19.19.5 (Deprecated)", 104),
SPOOFER_VERSION(@"v19.18.2 (Deprecated)", 105),
SPOOFER_VERSION(@"v19.17.2 (Deprecated)", 106),
SPOOFER_VERSION(@"v19.16.3 (Deprecated)", 107),
SPOOFER_VERSION(@"v19.15.1 (Deprecated)", 108),
SPOOFER_VERSION(@"v19.14.3 (Deprecated)", 109),
SPOOFER_VERSION(@"v19.14.2 (Deprecated)", 110),
SPOOFER_VERSION(@"v19.13.1 (Deprecated)", 111),
SPOOFER_VERSION(@"v19.12.3 (Deprecated)", 112),
SPOOFER_VERSION(@"v19.10.7 (Deprecated)", 113),
SPOOFER_VERSION(@"v19.10.6 (Deprecated)", 114),
SPOOFER_VERSION(@"v19.10.5 (Deprecated)", 115),
SPOOFER_VERSION(@"v19.09.4 (Deprecated)", 116),
SPOOFER_VERSION(@"v19.09.3 (Deprecated)", 117),
SPOOFER_VERSION(@"v19.08.2 (Deprecated)", 118),
SPOOFER_VERSION(@"v19.07.5 (Deprecated)", 119),
SPOOFER_VERSION(@"v19.07.4 (Deprecated)", 120),
SPOOFER_VERSION(@"v19.06.2 (Deprecated)", 121),
SPOOFER_VERSION(@"v19.05.5 (Deprecated)", 122),
SPOOFER_VERSION(@"v19.05.3 (Deprecated)", 123),
SPOOFER_VERSION(@"v19.04.3 (Deprecated)", 124),
SPOOFER_VERSION(@"v19.03.2 (Deprecated)", 125),
SPOOFER_VERSION(@"v19.02.1 (Deprecated)", 126),
SPOOFER_VERSION(@"v19.01.1 (Deprecated)", 127)
]; ];
YTSettingsPickerViewController *picker = [[%c(YTSettingsPickerViewController) alloc] initWithNavTitle:LOC(@"VERSION_SPOOFER_SELECTOR") pickerSectionTitle:nil rows:rows selectedItemIndex:appVersionSpoofer() parentResponder:[self parentResponder]]; YTSettingsPickerViewController *picker = [[%c(YTSettingsPickerViewController) alloc] initWithNavTitle:LOC(@"VERSION_SPOOFER_SELECTOR") pickerSectionTitle:nil rows:rows selectedItemIndex:appVersionSpoofer() parentResponder:[self parentResponder]];
[settingsViewController pushViewController:picker]; [settingsViewController pushViewController:picker];
@ -1029,37 +1004,44 @@ NSString *cacheDescription = [NSString stringWithFormat:@"%@", GetCacheSize()];
detailTextBlock:^NSString *() { detailTextBlock:^NSString *() {
switch (getNotificationIconStyle()) { switch (getNotificationIconStyle()) {
case 1: case 1:
return @"Thin Outline (2020+)"; return @"Bold Outline (2024+)";
case 2: case 2:
return @"Filled (2018+)"; return @"Thin Outline (2020+)";
case 3: case 3:
return @"Filled (2018+)";
case 4:
return @"Classic/Inbox (2014+)"; return @"Classic/Inbox (2014+)";
case 0: case 0:
default: default:
return @"Default"; return @"Default (2025+)";
} }
} }
selectBlock:^BOOL (YTSettingsCell *cell, NSUInteger arg1) { selectBlock:^BOOL (YTSettingsCell *cell, NSUInteger arg1) {
NSArray <YTSettingsSectionItem *> *rows = @[ NSArray <YTSettingsSectionItem *> *rows = @[
[YTSettingsSectionItemClass checkmarkItemWithTitle:@"Default" titleDescription:nil selectBlock:^BOOL (YTSettingsCell *cell, NSUInteger arg1) { [YTSettingsSectionItemClass checkmarkItemWithTitle:@"Default (2025+)" titleDescription:nil selectBlock:^BOOL (YTSettingsCell *cell, NSUInteger arg1) {
[[NSUserDefaults standardUserDefaults] setInteger:0 forKey:@"notificationIconStyle"]; [[NSUserDefaults standardUserDefaults] setInteger:0 forKey:@"notificationIconStyle"];
[settingsViewController reloadData]; [settingsViewController reloadData];
return YES; return YES;
}], }],
[YTSettingsSectionItemClass checkmarkItemWithTitle:@"Thin Outline (2020+)" titleDescription:nil selectBlock:^BOOL (YTSettingsCell *cell, NSUInteger arg1) { [YTSettingsSectionItemClass checkmarkItemWithTitle:@"Bold Outline (2024+)" titleDescription:nil selectBlock:^BOOL (YTSettingsCell *cell, NSUInteger arg1) {
[[NSUserDefaults standardUserDefaults] setInteger:1 forKey:@"notificationIconStyle"]; [[NSUserDefaults standardUserDefaults] setInteger:1 forKey:@"notificationIconStyle"];
[settingsViewController reloadData]; [settingsViewController reloadData];
return YES; return YES;
}], }],
[YTSettingsSectionItemClass checkmarkItemWithTitle:@"Filled (2018+)" titleDescription:nil selectBlock:^BOOL (YTSettingsCell *cell, NSUInteger arg1) { [YTSettingsSectionItemClass checkmarkItemWithTitle:@"Thin Outline (2020+)" titleDescription:nil selectBlock:^BOOL (YTSettingsCell *cell, NSUInteger arg1) {
[[NSUserDefaults standardUserDefaults] setInteger:2 forKey:@"notificationIconStyle"]; [[NSUserDefaults standardUserDefaults] setInteger:2 forKey:@"notificationIconStyle"];
[settingsViewController reloadData]; [settingsViewController reloadData];
return YES; return YES;
}], }],
[YTSettingsSectionItemClass checkmarkItemWithTitle:@"Classic/Inbox (2014+)" titleDescription:nil selectBlock:^BOOL (YTSettingsCell *cell, NSUInteger arg1) { [YTSettingsSectionItemClass checkmarkItemWithTitle:@"Filled (2018+)" titleDescription:nil selectBlock:^BOOL (YTSettingsCell *cell, NSUInteger arg1) {
[[NSUserDefaults standardUserDefaults] setInteger:3 forKey:@"notificationIconStyle"]; [[NSUserDefaults standardUserDefaults] setInteger:3 forKey:@"notificationIconStyle"];
[settingsViewController reloadData]; [settingsViewController reloadData];
return YES; return YES;
}],
[YTSettingsSectionItemClass checkmarkItemWithTitle:@"Classic/Inbox (2014+)" titleDescription:nil selectBlock:^BOOL (YTSettingsCell *cell, NSUInteger arg1) {
[[NSUserDefaults standardUserDefaults] setInteger:4 forKey:@"notificationIconStyle"];
[settingsViewController reloadData];
return YES;
}] }]
]; ];
YTSettingsPickerViewController *picker = [[%c(YTSettingsPickerViewController) alloc] initWithNavTitle:LOC(@"Notifications Tab nostalgic customization") pickerSectionTitle:nil rows:rows selectedItemIndex:getNotificationIconStyle() parentResponder:[self parentResponder]]; YTSettingsPickerViewController *picker = [[%c(YTSettingsPickerViewController) alloc] initWithNavTitle:LOC(@"Notifications Tab nostalgic customization") pickerSectionTitle:nil rows:rows selectedItemIndex:getNotificationIconStyle() parentResponder:[self parentResponder]];

View file

@ -6,103 +6,134 @@ typedef struct {
} VersionMapping; } VersionMapping;
static VersionMapping versionMappings[] = { static VersionMapping versionMappings[] = {
{0, @"20.33.2"}, {0, @"21.08.3"},
{1, @"20.32.5"}, {1, @"21.07.4"},
{2, @"20.32.4"}, {2, @"21.06.2"},
{3, @"20.31.6"}, {3, @"21.05.3"},
{4, @"20.31.5"}, {4, @"21.04.2"},
{5, @"20.30.5"}, {5, @"21.03.2"},
{6, @"20.29.3"}, {6, @"21.02.3"},
{7, @"20.28.2"}, {7, @"20.50.10"},
{8, @"20.26.7"}, {8, @"20.50.9"},
{9, @"20.25.4"}, {9, @"20.50.6"},
{10, @"20.24.5"}, {10, @"20.49.5"},
{11, @"20.24.4"}, {11, @"20.47.3"},
{12, @"20.23.3"}, {12, @"20.46.3"},
{13, @"20.22.1"}, {13, @"20.46.2"},
{14, @"20.21.6"}, {14, @"20.45.3"},
{15, @"20.20.7"}, {15, @"20.44.2"},
{16, @"20.20.5"}, {16, @"20.43.3"},
{17, @"20.19.3"}, {17, @"20.42.3"},
{18, @"20.19.2"}, {18, @"20.41.5"},
{19, @"20.18.5"}, {19, @"20.41.4"},
{20, @"20.18.4"}, {20, @"20.40.4"},
{21, @"20.16.7"}, {21, @"20.39.6"},
{22, @"20.15.1"}, {22, @"20.39.5"},
{23, @"20.14.2"}, {23, @"20.39.4"},
{24, @"20.13.5"}, {24, @"20.38.4"},
{25, @"20.12.4"}, {25, @"20.38.3"},
{26, @"20.11.6"}, {26, @"20.37.5"},
{27, @"20.10.4"}, {27, @"20.37.3"},
{28, @"20.10.3"}, {28, @"20.36.3"},
{29, @"20.09.3"}, {29, @"20.35.2"},
{30, @"20.08.3"}, {30, @"20.34.2"},
{31, @"20.07.6"}, {31, @"20.33.2"},
{32, @"20.06.03"}, {32, @"20.32.5"},
{33, @"20.05.4"}, {33, @"20.32.4"},
{34, @"20.03.1"}, {34, @"20.31.6"},
{35, @"20.03.02"}, {35, @"20.31.5"},
{36, @"20.02.3"}, {36, @"20.30.5"},
{37, @"19.49.7"}, {37, @"20.29.3"},
{38, @"19.49.5"}, {38, @"20.28.2"},
{39, @"19.49.3"}, {39, @"20.26.7"},
{40, @"19.47.7"}, {40, @"20.25.4"},
{41, @"19.46.3"}, {41, @"20.24.5"},
{42, @"19.45.4"}, {42, @"20.24.4"},
{43, @"19.44.4"}, {43, @"20.23.3"},
{44, @"19.43.2"}, {44, @"20.22.1"},
{45, @"19.42.1"}, {45, @"20.21.6"},
{46, @"19.41.3"}, {46, @"20.20.7"},
{47, @"19.40.4"}, {47, @"20.20.5"},
{48, @"19.39.1"}, {48, @"20.19.3"},
{49, @"19.38.2"}, {49, @"20.19.2"},
{50, @"19.37.2"}, {50, @"20.18.5"},
{51, @"19.36.1"}, {51, @"20.18.4"},
{52, @"19.35.3"}, {52, @"20.16.7"},
{53, @"19.34.2"}, {53, @"20.15.1"},
{54, @"19.33.2"}, {54, @"20.14.2"},
{55, @"19.32.8"}, {55, @"20.13.5"},
{56, @"19.32.6"}, {56, @"20.12.4"},
{57, @"19.31.4"}, {57, @"20.11.6"},
{58, @"19.30.2"}, {58, @"20.10.4"},
{59, @"19.29.1"}, {59, @"20.10.3"},
{60, @"19.28.1"}, {60, @"20.09.3"},
{61, @"19.26.5"}, {61, @"20.08.3"},
{62, @"19.25.4"}, {62, @"20.07.6"},
{63, @"19.25.3"}, {63, @"20.06.03"},
{64, @"19.24.3"}, {64, @"20.05.4"},
{65, @"19.24.2"}, {65, @"20.03.1"},
{66, @"19.23.3"}, {66, @"20.03.02"},
{67, @"19.22.6"}, {67, @"20.02.3"},
{68, @"19.22.3"}, {68, @"19.49.7"},
{69, @"19.21.3"}, {69, @"19.49.5"},
{70, @"19.21.2"}, {70, @"19.49.3"},
{71, @"19.20.2"}, {71, @"19.47.7"},
{72, @"19.19.7"}, {72, @"19.46.3"},
{73, @"19.19.5"}, {73, @"19.45.4"},
{74, @"19.18.2"}, {74, @"19.44.4"},
{75, @"19.17.2"}, {75, @"19.43.2"},
{76, @"19.16.3"}, {76, @"19.42.1"},
{77, @"19.15.1"}, {77, @"19.41.3"},
{78, @"19.14.3"}, {78, @"19.40.4"},
{79, @"19.14.2"}, {79, @"19.39.1"},
{80, @"19.13.1"}, {80, @"19.38.2"},
{81, @"19.12.3"}, {81, @"19.37.2"},
{82, @"19.10.7"}, {82, @"19.36.1"},
{83, @"19.10.6"}, {83, @"19.35.3"},
{84, @"19.10.5"}, {84, @"19.34.2"},
{85, @"19.09.4"}, {85, @"19.33.2"},
{86, @"19.09.3"}, {86, @"19.32.8"},
{87, @"19.08.2"}, {87, @"19.32.6"},
{88, @"19.07.5"}, {88, @"19.31.4"},
{89, @"19.07.4"}, {89, @"19.30.2"},
{90, @"19.06.2"}, {90, @"19.29.1"},
{91, @"19.05.5"}, {91, @"19.28.1"},
{92, @"19.05.3"}, {92, @"19.26.5"},
{93, @"19.04.3"}, {93, @"19.25.4"},
{94, @"19.03.2"}, {94, @"19.25.3"},
{95, @"19.02.1"}, {95, @"19.24.3"},
{96, @"19.01.1"} {96, @"19.24.2"},
{97, @"19.23.3"},
{98, @"19.22.6"},
{99, @"19.22.3"},
{100, @"19.21.3"},
{101, @"19.21.2"},
{102, @"19.20.2"},
{103, @"19.19.7"},
{104, @"19.19.5"},
{105, @"19.18.2"},
{106, @"19.17.2"},
{107, @"19.16.3"},
{108, @"19.15.1"},
{109, @"19.14.3"},
{110, @"19.14.2"},
{111, @"19.13.1"},
{112, @"19.12.3"},
{113, @"19.10.7"},
{114, @"19.10.6"},
{115, @"19.10.5"},
{116, @"19.09.4"},
{117, @"19.09.3"},
{118, @"19.08.2"},
{119, @"19.07.5"},
{120, @"19.07.4"},
{121, @"19.06.2"},
{122, @"19.05.5"},
{123, @"19.05.3"},
{124, @"19.04.3"},
{125, @"19.03.2"},
{126, @"19.02.1"},
{127, @"19.01.1"}
}; };
static int appVersionSpoofer() { static int appVersionSpoofer() {

@ -1 +1 @@
Subproject commit b9b0cd5784ef63922b9424c9c32b0e7f10898d49 Subproject commit 63c099f62eb479883a4f3df212fb495af49fca05

@ -1 +1 @@
Subproject commit f840113b3820fe5b90a8b614c1a0876c5c9cc5cb Subproject commit 842f93556427e208248c1f2bd94e432ce56405e5

@ -1 +1 @@
Subproject commit 634443f699950a1dbc2a4a4a1730a788c8683ae2 Subproject commit 6cd5adc700cd79de80f7b02d0d14f1e5a7ea64aa

@ -1 +1 @@
Subproject commit 0f59b38817d554ab692a0053456319847a6ab1a1 Subproject commit f799b70cb40e32de6ba779529ad60ecd8f601cfd

@ -1 +1 @@
Subproject commit 3060d66bfd86a85ab446ff9b04a1b6a2ddcb4d6f Subproject commit f428a716021fd3e389e0ada5f05b3f215e079e24

1
Tweaks/YTweaks Submodule

@ -0,0 +1 @@
Subproject commit 9a6b4df48981d69e36feb774852e46c49dd31b32

@ -1 +1 @@
Subproject commit 96c2a82845acc43f8473b6ef46c2385301fda04e Subproject commit 852a58dc2c53d1426557b25ac0ca53570338248b

@ -1 +1 @@
Subproject commit eb33ec3b685a2e974272b971a559da795a1f6972 Subproject commit 899a2fdd539ae308e5847bbb3d7212cfce30a3a6

@ -1 +1 @@
Subproject commit 0548225b0abd354f55d6b2e7f436504e6ce7bca8 Subproject commit 8d6726e569a194d3f1c1ae973b290c66301087f1

@ -1 +1 @@
Subproject commit 041f95f3bf795cbcf4c197a511821b2c574eb66c Subproject commit 49ece8b0fac8465ee4fd8bf0b54790106e50cb88

@ -1 +1 @@
Subproject commit 08e9ff49cfda6513d9f8823773692355902da944 Subproject commit d56dccd4336f0902f05813f4af177360c69fe33f

@ -1 +1 @@
Subproject commit 20eab9638b4c19b6e9fa507ee3df5dbcec9303ee Subproject commit cbd7e9daf765ecf5d9ceb27afac15cc3eec3ecc0

@ -1 +1 @@
Subproject commit d91d799245f503ef8816024b09234117af1a5b9a Subproject commit 79b9ed44958ad27fe75ee14ccc300aa99610c263

@ -1 +1 @@
Subproject commit d7253fd7fe9bc2d5c900b156aa0d613de3b867e8 Subproject commit 3be0cc98b300b7870abc99e1e01566e1c4ef6e07

@ -1 +1 @@
Subproject commit 3e4157c2a84576d175fc73ae422927e3d2531055 Subproject commit e0f97ef5fe47c5c3728ce2ff7a6fb02bc085e86b

1
Tweaks/libundirect Submodule

@ -0,0 +1 @@
Subproject commit 35234fc8004e756772a12e22dee90b0b8a536143

@ -1 +1 @@
Subproject commit 8efdbbb991b27bbf49b425a98be9db45dd3c3b8c Subproject commit 57093a8bd5cb44211f323519a0045b883475666f