#import "uYouPlusPatches.h" # pragma mark - YouTube patches // Fix Google Sign in Patch %group gGoogleSignInPatch %hook NSBundle - (NSDictionary *)infoDictionary { NSDictionary *orig = %orig; if ([self isEqual:NSBundle.mainBundle]) { NSArray *stack = [NSThread callStackSymbols]; BOOL needsSpoof = NO; 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"; return [patched copy]; } } return orig; } %end %end // Workaround for MiRO92/uYou-for-YouTube#12, qnblackcat/uYouPlus#263 %hook YTDataUtils + (NSMutableDictionary *)spamSignalsDictionary { return nil; } + (NSMutableDictionary *)spamSignalsDictionaryWithoutIDFA { return nil; } %end %hook YTHotConfig - (BOOL)disableAfmaIdfaCollection { return NO; } %end // Reposition "Create" Tab to the Center in the Pivot Bar - qnblackcat/uYouPlus#107 /* static void repositionCreateTab(YTIGuideResponse *response) { NSMutableArray *renderers = [response itemsArray]; for (YTIGuideResponseSupportedRenderers *guideRenderers in renderers) { YTIPivotBarRenderer *pivotBarRenderer = [guideRenderers pivotBarRenderer]; NSMutableArray *items = [pivotBarRenderer itemsArray]; NSUInteger createIndex = [items indexOfObjectPassingTest:^BOOL(YTIPivotBarSupportedRenderers *renderers, NSUInteger idx, BOOL *stop) { return [[[renderers pivotBarItemRenderer] pivotIdentifier] isEqualToString:@"FEuploads"]; }]; if (createIndex != NSNotFound) { YTIPivotBarSupportedRenderers *createTab = [items objectAtIndex:createIndex]; [items removeObjectAtIndex:createIndex]; NSUInteger centerIndex = items.count / 2; [items insertObject:createTab atIndex:centerIndex]; // Reposition the "Create" tab at the center } } } %hook YTGuideServiceCoordinator - (void)handleResponse:(YTIGuideResponse *)response withCompletion:(id)completion { repositionCreateTab(response); %orig(response, completion); } - (void)handleResponse:(YTIGuideResponse *)response error:(id)error completion:(id)completion { repositionCreateTab(response); %orig(response, error, completion); } %end */ // https://github.com/PoomSmart/YouTube-X/blob/1e62b68e9027fcb849a75f54a402a530385f2a51/Tweak.x#L27 // %hook YTAdsInnerTubeContextDecorator // - (void)decorateContext:(id)context {} // %end # pragma mark - uYou patches // Workaround for qnblackcat/uYouPlus#10 %hook UIViewController - (UITraitCollection *)traitCollection { @try { return %orig; } @catch(NSException *e) { return [UITraitCollection currentTraitCollection]; } } %end // Prevent uYou player bar from showing when not playing downloaded media %hook PlayerManager - (void)pause { if (isnan([self progress])) return; %orig; } %end // Workaround for issue #54 %hook YTMainAppVideoPlayerOverlayViewController - (void)updateRelatedVideos { if ([[NSUserDefaults standardUserDefaults] boolForKey:@"relatedVideosAtTheEndOfYTVideos"] == NO) {} else { return %orig; } } %end // YouTube Native Share - https://github.com/jkhsjdhjs/youtube-native-share - @jkhsjdhjs typedef NS_ENUM(NSInteger, ShareEntityType) { ShareEntityFieldVideo = 1, ShareEntityFieldPlaylist = 2, ShareEntityFieldChannel = 3, ShareEntityFieldPost = 6, ShareEntityFieldClip = 8, ShareEntityFieldShortFlag = 10 }; static inline NSString* extractIdWithFormat(GPBUnknownFields *fields, NSInteger fieldNumber, NSString *format) { NSArray *fieldArray = [fields fields:fieldNumber]; if (!fieldArray) return nil; if ([fieldArray count] != 1) return nil; NSString *id = [[NSString alloc] initWithData:[fieldArray firstObject].lengthDelimited encoding:NSUTF8StringEncoding]; return [NSString stringWithFormat:format, id]; } static BOOL showNativeShareSheet(NSString *serializedShareEntity, UIView *sourceView) { GPBMessage *shareEntity = [%c(GPBMessage) deserializeFromString:serializedShareEntity]; GPBUnknownFields *fields = [[%c(GPBUnknownFields) alloc] initFromMessage:shareEntity]; NSString *shareUrl; NSArray *shareEntityClip = [fields fields:ShareEntityFieldClip]; if (shareEntityClip) { if ([shareEntityClip count] != 1) return NO; GPBMessage *clipMessage = [%c(GPBMessage) parseFromData:[shareEntityClip firstObject].lengthDelimited error:nil]; shareUrl = extractIdWithFormat([[%c(GPBUnknownFields) alloc] initFromMessage:clipMessage], 1, @"https://youtube.com/clip/%@"); } if (!shareUrl) shareUrl = extractIdWithFormat(fields, ShareEntityFieldChannel, @"https://youtube.com/channel/%@"); if (!shareUrl) { shareUrl = extractIdWithFormat(fields, ShareEntityFieldPlaylist, @"%@"); if (shareUrl) { if (![shareUrl hasPrefix:@"PL"] && ![shareUrl hasPrefix:@"FL"]) shareUrl = [shareUrl stringByAppendingString:@"&playnext=1"]; shareUrl = [@"https://youtube.com/playlist?list=" stringByAppendingString:shareUrl]; } } if (!shareUrl) { NSString *format = @"https://youtube.com/watch?v=%@"; if ([fields fields:ShareEntityFieldShortFlag]) format = @"https://youtube.com/shorts/%@"; shareUrl = extractIdWithFormat(fields, ShareEntityFieldVideo, format); } if (!shareUrl) shareUrl = extractIdWithFormat(fields, ShareEntityFieldPost, @"https://youtube.com/post/%@"); if (!shareUrl) return NO; UIActivityViewController *activityViewController = [[UIActivityViewController alloc] initWithActivityItems:@[shareUrl] applicationActivities:nil]; activityViewController.excludedActivityTypes = @[UIActivityTypeAssignToContact, UIActivityTypePrint]; UIViewController *topViewController = [%c(YTUIUtils) topViewControllerForPresenting]; if (activityViewController.popoverPresentationController) { activityViewController.popoverPresentationController.sourceView = topViewController.view; activityViewController.popoverPresentationController.sourceRect = [sourceView convertRect:sourceView.bounds toView:topViewController.view]; } [topViewController presentViewController:activityViewController animated:YES completion:nil]; return YES; } /* -------------------- iPad Layout -------------------- */ // %group gYouTubeNativeShare // YouTube Native Share Option - 0.2.3 - Supports YouTube v17.33.2-v19.34.2 %hook YTAccountScopedCommandResponderEvent - (void)send { GPBExtensionDescriptor *shareEntityEndpointDescriptor = [%c(YTIShareEntityEndpoint) shareEntityEndpoint]; if (![self.command hasExtension:shareEntityEndpointDescriptor]) return %orig; YTIShareEntityEndpoint *shareEntityEndpoint = [self.command getExtension:shareEntityEndpointDescriptor]; if (!shareEntityEndpoint.hasSerializedShareEntity) return %orig; if (!showNativeShareSheet(shareEntityEndpoint.serializedShareEntity, self.fromView)) return %orig; } %end /* ------------------- iPhone Layout ------------------- */ %hook ELMPBShowActionSheetCommand - (void)executeWithCommandContext:(ELMCommandContext*)context handler:(id)_handler { if (!self.hasOnAppear) return %orig; GPBExtensionDescriptor *innertubeCommandDescriptor = [%c(YTIInnertubeCommandExtensionRoot) innertubeCommand]; if (![self.onAppear hasExtension:innertubeCommandDescriptor]) return %orig; YTICommand *innertubeCommand = [self.onAppear getExtension:innertubeCommandDescriptor]; GPBExtensionDescriptor *updateShareSheetCommandDescriptor = [%c(YTIUpdateShareSheetCommand) updateShareSheetCommand]; if(![innertubeCommand hasExtension:updateShareSheetCommandDescriptor]) return %orig; YTIUpdateShareSheetCommand *updateShareSheetCommand = [innertubeCommand getExtension:updateShareSheetCommandDescriptor]; if (!updateShareSheetCommand.hasSerializedShareEntity) return %orig; if (!showNativeShareSheet(updateShareSheetCommand.serializedShareEntity, context.context.fromView)) return %orig; } %end // %end // // iOS 16 uYou crash fix - @level3tjg: https://github.com/qnblackcat/uYouPlus/pull/224 // %group iOS16 // %hook OBPrivacyLinkButton // %new // - (instancetype)initWithCaption:(NSString *)caption // buttonText:(NSString *)buttonText // image:(UIImage *)image // imageSize:(CGSize)imageSize // useLargeIcon:(BOOL)useLargeIcon { // return [self initWithCaption:caption // buttonText:buttonText // image:image // imageSize:imageSize // useLargeIcon:useLargeIcon // displayLanguage:[NSLocale currentLocale].languageCode]; // } // %end // %end // Fix uYou playback speed crashes YT v18.49.3+, see https://github.com/iCrazeiOS/uYouCrashFix // %hook YTPlayerViewController // %new // -(float)currentPlaybackRateForVarispeedSwitchController:(id)arg1 { // return [[self activeVideo] playbackRate]; // } // %new // -(void)varispeedSwitchController:(id)arg1 didSelectRate:(float)arg2 { // [[self activeVideo] setPlaybackRate:arg2]; // } // %end // Fix streched artwork in uYou's player view - https://github.com/MiRO92/uYou-for-YouTube/issues/287 %hook ArtworkImageView - (id)imageView { UIImageView * imageView = %orig; imageView.contentMode = UIViewContentModeScaleAspectFit; // Make artwork a bit bigger UIView *artworkImageView = imageView.superview; if (artworkImageView != nil && !artworkImageView.translatesAutoresizingMaskIntoConstraints) { [artworkImageView.leftAnchor constraintEqualToAnchor:artworkImageView.superview.leftAnchor constant:16].active = YES; [artworkImageView.rightAnchor constraintEqualToAnchor:artworkImageView.superview.rightAnchor constant:-16].active = YES; } return imageView; } %end // Fix navigation bar showing a lighter grey with default dark mode - https://github.com/therealFoxster/uYouPlus/commit/8db8197 %hook YTCommonColorPalette - (UIColor *)brandBackgroundSolid { return self.pageStyle == 1 ? [UIColor colorWithRed:0.05882352941176471 green:0.05882352941176471 blue:0.05882352941176471 alpha:1.0] : %orig; } %end // Fix uYou's appearance not updating if the app is backgrounded static DownloadsPagerVC *downloadsPagerVC; static NSUInteger selectedTabIndex; %hook DownloadsPagerVC - (id)init { downloadsPagerVC = %orig; return downloadsPagerVC; } - (void)viewPager:(id)viewPager didChangeTabToIndex:(NSUInteger)arg1 fromTabIndex:(NSUInteger)arg2 { %orig; selectedTabIndex = arg1; } %end static void refreshUYouAppearance() { if (!downloadsPagerVC) return; // View pager [downloadsPagerVC updatePageStyles]; // Views for (UIViewController *vc in [downloadsPagerVC viewControllers]) { if ([vc isKindOfClass:%c(DownloadingVC)]) { // `Downloading` view [(DownloadingVC *)vc updatePageStyles]; for (UITableViewCell *cell in [(DownloadingVC *)vc tableView].visibleCells) if ([cell isKindOfClass:%c(DownloadingCell)]) [(DownloadingCell *)cell updatePageStyles]; } else if ([vc isKindOfClass:%c(DownloadedVC)]) { // `All`, `Audios`, `Videos`, `Shorts` views [(DownloadedVC *)vc updatePageStyles]; for (UITableViewCell *cell in [(DownloadedVC *)vc tableView].visibleCells) if ([cell isKindOfClass:%c(DownloadedCell)]) [(DownloadedCell *)cell updatePageStyles]; } } // View pager tabs for (UIView *subview in [downloadsPagerVC view].subviews) { if ([subview isKindOfClass:[UIScrollView class]]) { UIScrollView *tabs = (UIScrollView *)subview; NSUInteger i = 0; for (UIView *item in tabs.subviews) { if ([item isKindOfClass:[UILabel class]]) { // Tab label UILabel *tabLabel = (UILabel *)item; if (i == selectedTabIndex) {} // Selected tab should be excluded else [tabLabel setTextColor:[UILabel _defaultColor]]; i++; } } } } } %hook UIViewController - (void)traitCollectionDidChange:(UITraitCollection *)previousTraitCollection { %orig; dispatch_async(dispatch_get_main_queue(), ^{ refreshUYouAppearance(); }); } %end // Prevent uYou's playback from colliding with YouTube's %hook PlayerVC - (void)close { %orig; [[%c(PlayerManager) sharedInstance] setSource:nil]; } %end %hook HAMPlayerInternal - (void)play { dispatch_async(dispatch_get_main_queue(), ^{ [[%c(PlayerManager) sharedInstance] pause]; }); %orig; } %end // Temporarily disable uYou's bouncy animation cause it's buggy %hook SSBouncyButton - (void)beginShrinkAnimation {} - (void)beginEnlargeAnimation {} %end %hook GOODialogView - (id)imageView { UIImageView *imageView = %orig; if ([[self titleLabel].text containsString:@"uYou\n"]) { // // Invert uYou logo in download dialog if dark mode is enabled // if ([[NSUserDefaults standardUserDefaults] integerForKey:@"page_style"] == 0) // return imageView; // // https://gist.github.com/coryalder/3113a43734f5e0e4b497 // UIImage *image = [imageView image]; // CIImage *ciImage = [[CIImage alloc] initWithImage:image]; // CIFilter *filter = [CIFilter filterWithName:@"CIColorInvert"]; // [filter setDefaults]; // [filter setValue:ciImage forKey:kCIInputImageKey]; // CIContext *context = [CIContext contextWithOptions:nil]; // CIImage *output = [filter outputImage]; // CGImageRef cgImage = [context createCGImage:output fromRect:[output extent]]; // UIImage *icon = [UIImage imageWithCGImage:cgImage]; // CGImageRelease(cgImage); // Load icon_clipped.png from uYouBundle.bundle NSString *bundlePath = [[NSBundle mainBundle] pathForResource:@"uYouBundle" ofType:@"bundle"]; NSBundle *bundle = [NSBundle bundleWithPath:bundlePath]; NSString *iconPath = [bundle pathForResource:@"icon_clipped" ofType:@"png"]; UIImage *icon = [UIImage imageWithContentsOfFile:iconPath]; [imageView setImage:icon]; // Resize image to 30x30 // https://stackoverflow.com/a/2658801/19227228 CGSize size = CGSizeMake(30, 30); UIGraphicsBeginImageContextWithOptions(size, NO, 0.0); [icon drawInRect:CGRectMake(0, 0, size.width, size.height)]; UIImage *resizedImage = UIGraphicsGetImageFromCurrentImageContext(); UIGraphicsEndImageContext(); [imageView setImage:resizedImage]; } return imageView; } // Increase space between uYou label and video title - (id)titleLabel { UILabel *titleLabel = %orig; if ([titleLabel.text containsString:@"uYou\n"] && ![titleLabel.text containsString:@"uYou\n\n"] ) { NSString *text = [titleLabel.text stringByReplacingOccurrencesOfString:@"uYou\n" withString:@"uYou\n\n"]; [titleLabel setText:text]; } return titleLabel; } %end %hook YTPlayerViewController - (id)varispeedController { id controller = %orig; if (controller == nil && [self respondsToSelector:@selector(overlayManager)]) controller = [self.overlayManager varispeedController]; return controller; } %end %ctor { %init; if (IS_ENABLED(kGoogleSignInPatch)) { %init(gGoogleSignInPatch); } /* if (IS_ENABLED(kYouTubeNativeShare)) { %init(gYouTubeNativeShare); } */ // if (@available(iOS 16, *)) { // %init(iOS16); // } // Disable broken options // Disable uYou's auto updates [[NSUserDefaults standardUserDefaults] setBool:NO forKey:@"automaticallyCheckForUpdates"]; // Disable uYou's welcome screen (fix #1147) [[NSUserDefaults standardUserDefaults] setBool:YES forKey:@"showedWelcomeVC"]; // Disable uYou's disable age restriction [[NSUserDefaults standardUserDefaults] setBool:NO forKey:@"disableAgeRestriction"]; // Disable uYou's playback speed controls (prevent crash on video playback https://github.com/therealFoxster/uYouPlus/issues/2#issuecomment-1894912963) // [[NSUserDefaults standardUserDefaults] setBool:NO forKey:@"showPlaybackRate"]; }