diff --git a/Header.h b/Header.h index 9032631..2146374 100644 --- a/Header.h +++ b/Header.h @@ -3,6 +3,8 @@ #define LOC(x) [tweakBundle localizedStringForKey:x value:nil table:nil] #define YT_BUNDLE_ID @"com.google.ios.youtube" #define YT_NAME @"YouTube" +#define UNSUPPORTED_DEVICES @[@"iPhone14,3", @"iPhone14,6", @"iPhone14,8"] // DontEatMycontent +#define THRESHOLD 1.99 // DontEatMycontent // IAmYouTube @interface SSOConfiguration : NSObject @@ -37,6 +39,28 @@ - (float)progress; @end +// DontEatMyContent +NSString* deviceName(); +BOOL isDeviceSupported(); +void activate(); +void deactivate(); +void center(); + +@interface YTPlayerView : UIView +- (BOOL)zoomToFill; +- (id)renderingView; +- (id)playerView; +@end + +@interface MLHAMSBDLSampleBufferRenderingView : UIView +@end + +@interface YTMainAppVideoPlayerOverlayViewController : UIViewController +- (BOOL)isFullscreen; +- (id)videoPlayerOverlayView; +- (id)activeVideoPlayerOverlay; +@end + // iOS16 fix @interface OBPrivacyLinkButton : UIButton - (instancetype)initWithCaption:(NSString *)caption @@ -63,6 +87,8 @@ // YTAutoFullScreen @interface YTPlayerViewController (YTAFS) - (void)autoFullscreen; +- (id)activeVideoPlayerOverlay; // DontEatMycontent +- (id)playerView; // DontEatMycontent @end // OLED Darkmode @@ -94,4 +120,4 @@ @end @interface UIPredictionViewController : UIViewController -@end \ No newline at end of file +@end diff --git a/Settings.xm b/Settings.xm index 7cd2bd8..611b0b2 100644 --- a/Settings.xm +++ b/Settings.xm @@ -25,6 +25,7 @@ extern BOOL hidePreviousAndNextButton(); extern BOOL hidePaidPromotionCard(); extern BOOL fixGoogleSignIn(); extern BOOL replacePreviousAndNextButton(); +extern BOOL dontEatMyContent(); // Settings %hook YTAppSettingsPresentationData @@ -53,6 +54,15 @@ extern BOOL replacePreviousAndNextButton(); exit(0); }]; + YTSettingsSectionItem *dontEatMyContent = [[%c(YTSettingsSectionItem) alloc] initWithTitle:LOC(@"DONT_EAT_MY_CONTENT") titleDescription:LOC(@"DONT_EAT_MY_CONTENT_DESC")]; + dontEatMyContent.hasSwitch = YES; + dontEatMyContent.switchVisible = YES; + dontEatMyContent.on = [[NSUserDefaults standardUserDefaults] boolForKey:@"dontEatMyContent_enabled"]; + dontEatMyContent.switchBlock = ^BOOL (YTSettingsCell *cell, BOOL enabled) { + [[NSUserDefaults standardUserDefaults] setBool:enabled forKey:@"dontEatMyContent_enabled"]; + return YES; + }; + YTSettingsSectionItem *replacePreviousAndNextButton = [[%c(YTSettingsSectionItem) alloc] initWithTitle:LOC(@"REPLACE_PREVIOUS_NEXT_BUTTON") titleDescription:LOC(@"REPLACE_PREVIOUS_NEXT_BUTTON_DESC")]; replacePreviousAndNextButton.hasSwitch = YES; replacePreviousAndNextButton.switchVisible = YES; @@ -188,7 +198,7 @@ extern BOOL replacePreviousAndNextButton(); return YES; }; - NSMutableArray *sectionItems = [NSMutableArray arrayWithArray:@[killApp, autoFull, castConfirm, ytMiniPlayer, fixGoogleSignIn, hideAutoplaySwitch, hideCC, hideHUD, hidePaidPromotionCard, hidePreviousAndNextButton, hideHoverCard, bigYTMiniPlayer, oledDarkMode, oledKeyBoard, replacePreviousAndNextButton, reExplore]]; + NSMutableArray *sectionItems = [NSMutableArray arrayWithArray:@[killApp, autoFull, castConfirm, ytMiniPlayer, fixGoogleSignIn, hideAutoplaySwitch, hideCC, hideHUD, hidePaidPromotionCard, hidePreviousAndNextButton, hideHoverCard, bigYTMiniPlayer, oledDarkMode, oledKeyBoard, dontEatMyContent, replacePreviousAndNextButton, reExplore]]; [delegate setSectionItems:sectionItems forCategory:uYouPlusSection title:@"uYouPlus" titleDescription:nil headerHidden:NO]; } @@ -199,4 +209,4 @@ extern BOOL replacePreviousAndNextButton(); } %orig; } -%end \ No newline at end of file +%end diff --git a/lang/uYouPlus.bundle/en.lproj/Localizable.strings b/lang/uYouPlus.bundle/en.lproj/Localizable.strings index dcaf18c..f6bc208 100644 --- a/lang/uYouPlus.bundle/en.lproj/Localizable.strings +++ b/lang/uYouPlus.bundle/en.lproj/Localizable.strings @@ -1,5 +1,8 @@ // Settings +"DONT_EAT_MY_CONTENT" = "Prevent Notch/Island on 2:1 Video Content" +"DONT_EAT_MY_CONTENT_DESC" = "Prevent the notch/Dynamic Island from munching on 2:1 video content in YouTube. App restart is required."; + "REPLACE_PREVIOUS_NEXT_BUTTON" = "Replace Previous and Next Button"; "REPLACE_PREVIOUS_NEXT_BUTTON_DESC" = "Replace Previous and Next Button with Fast forward and Rewind button. App restart is required."; diff --git a/uYouPlus.xm b/uYouPlus.xm index fe78e58..35ef8d5 100644 --- a/uYouPlus.xm +++ b/uYouPlus.xm @@ -2,6 +2,8 @@ #import #import #import +#import +#import #import "Header.h" #import "Tweaks/YouTubeHeader/YTVideoQualitySwitchOriginalController.h" #import "Tweaks/YouTubeHeader/YTPlayerViewController.h" @@ -98,6 +100,9 @@ BOOL fixGoogleSignIn() { BOOL replacePreviousAndNextButton() { return [[NSUserDefaults standardUserDefaults] boolForKey:@"replacePreviousAndNextButton_enabled"]; } +BOOL dontEatMyContent() { + return [[NSUserDefaults standardUserDefaults] boolForKey:@"dontEatMyContent_enabled"]; +} # pragma mark - Tweaks // Enable Reorder videos from playlist while on the Watch page - @PoomSmart @@ -876,6 +881,142 @@ static void replaceTab(YTIGuideResponse *response) { %end %end +// DontEatMyContent - @therealFoxster: https://github.com/therealFoxster/DontEatMyContent +double aspectRatio = 16/9; +bool zoomedToFill = false; + +MLHAMSBDLSampleBufferRenderingView *renderingView; +NSLayoutConstraint *widthConstraint, *heightConstraint, *centerXConstraint, *centerYConstraint; + +%group gDontEatMyContent +%hook YTPlayerViewController +- (void)viewDidAppear:(BOOL)animated { + YTPlayerView *playerView = [self playerView]; + UIView *renderingViewContainer = MSHookIvar(playerView, "_renderingViewContainer"); + renderingView = [playerView renderingView]; + + CGFloat constant = 23; // Make renderingView a bit larger since safe area has sizeable margins from the notch and side borders; tested on iPhone 13 mini + + widthConstraint = [renderingView.widthAnchor constraintEqualToAnchor:renderingViewContainer.safeAreaLayoutGuide.widthAnchor constant:constant]; + heightConstraint = [renderingView.heightAnchor constraintEqualToAnchor:renderingViewContainer.safeAreaLayoutGuide.heightAnchor constant:constant]; + centerXConstraint = [renderingView.centerXAnchor constraintEqualToAnchor:renderingViewContainer.centerXAnchor]; + centerYConstraint = [renderingView.centerYAnchor constraintEqualToAnchor:renderingViewContainer.centerYAnchor]; + + // playerView.backgroundColor = [UIColor greenColor]; + // renderingViewContainer.backgroundColor = [UIColor redColor]; + // renderingView.backgroundColor = [UIColor blueColor]; + + YTMainAppVideoPlayerOverlayViewController *activeVideoPlayerOverlay = [self activeVideoPlayerOverlay]; + + // Must check class since YTInlineMutedPlaybackPlayerOverlayViewController doesn't have -(BOOL)isFullscreen + if ([NSStringFromClass([activeVideoPlayerOverlay class]) isEqualToString:@"YTMainAppVideoPlayerOverlayViewController"] && [activeVideoPlayerOverlay isFullscreen]) { + activate(); + } else { + center(); + } + + %orig(animated); +} +- (void)didPressToggleFullscreen { + YTMainAppVideoPlayerOverlayViewController *activeVideoPlayerOverlay = [self activeVideoPlayerOverlay]; + + if (![activeVideoPlayerOverlay isFullscreen]) // Entering fullscreen + activate(); + else // Exiting fullscreen + deactivate(); + + %orig; +} +- (void)didSwipeToEnterFullscreen { + %orig; activate(); +} +- (void)didSwipeToExitFullscreen { + %orig; deactivate(); +} +- (void)singleVideo:(id)arg1 aspectRatioDidChange:(CGFloat)arg2 { + aspectRatio = arg2; + if (aspectRatio == 0.0) { + // App backgrounded + } else if (aspectRatio < THRESHOLD) { + deactivate(); + } else { + activate(); + } + %orig(arg1, arg2); +} +%end + +%hook YTVideoZoomOverlayView +- (void)didRecognizePinch:(UIPinchGestureRecognizer *)pinchGestureRecognizer { + // %log((CGFloat) [pinchGestureRecognizer scale], (CGFloat) [pinchGestureRecognizer velocity]); + if ([pinchGestureRecognizer velocity] <= 0.0) { // >>Zoom out<< + zoomedToFill = false; + activate(); + } else if ([pinchGestureRecognizer velocity] > 0.0) { // <> + zoomedToFill = true; + deactivate(); + } + %orig(pinchGestureRecognizer); +} +- (void)flashAndHideSnapIndicator {} +// https://github.com/lgariv/UniZoom/blob/master/Tweak.xm +- (void)setSnapIndicatorVisible:(bool)arg1 { + %orig(NO); +} +%end +%end // gDontEatMyContent + +// DontEatMycontent - detecting device model +// https://stackoverflow.com/a/11197770/19227228 +NSString* deviceName() { + struct utsname systemInfo; + uname(&systemInfo); + return [NSString stringWithCString:systemInfo.machine encoding:NSUTF8StringEncoding]; +} + +BOOL isDeviceSupported() { + NSString *identifier = deviceName(); + NSArray *unsupportedDevices = UNSUPPORTED_DEVICES; + + for (NSString *device in unsupportedDevices) { + if ([device isEqualToString:identifier]) { + return NO; + } + } + + if ([identifier containsString:@"iPhone"]) { + NSString *model = [identifier stringByReplacingOccurrencesOfString:@"iPhone" withString:@""]; + model = [model stringByReplacingOccurrencesOfString:@"," withString:@"."]; + if ([identifier isEqualToString:@"iPhone13,1"]) { // iPhone 12 mini + return YES; + } else if ([model floatValue] >= 14.0) { // iPhone 13 series and newer + return YES; + } else return NO; + } else return NO; +} + +void activate() { + if (aspectRatio < THRESHOLD || zoomedToFill) return; + // NSLog(@"activate"); + center(); + renderingView.translatesAutoresizingMaskIntoConstraints = NO; + widthConstraint.active = YES; + heightConstraint.active = YES; +} + +void deactivate() { + // NSLog(@"deactivate"); + center(); + renderingView.translatesAutoresizingMaskIntoConstraints = YES; + widthConstraint.active = NO; + heightConstraint.active = NO; +} + +void center() { + centerXConstraint.active = YES; + centerYConstraint.active = YES; +} + // iOS 16 uYou crash fix - @level3tjg: https://github.com/qnblackcat/uYouPlus/pull/224 %group iOS16 %hook OBPrivacyLinkButton @@ -919,6 +1060,9 @@ static void replaceTab(YTIGuideResponse *response) { if (replacePreviousAndNextButton()) { %init(gReplacePreviousAndNextButton); } + if (dontEatMyContent() && isDeviceSupported()) { + %init(gDontEatMyContent); + } if (@available(iOS 16, *)) { %init(iOS16); }