diff --git a/Header.h b/Header.h index 385230c..b2dfc93 100644 --- a/Header.h +++ b/Header.h @@ -4,8 +4,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 +#define DEMC_UNSUPPORTED_DEVICES @[@"iPhone14,3", @"iPhone14,6", @"iPhone14,8"] // DontEatMycontent +#define DEMC_THRESHOLD 1.99 // DontEatMycontent #define DEFAULT_RATE 2.0f // YTSpeed // IAmYouTube @@ -42,25 +42,33 @@ @end // DontEatMyContent -NSString* DEMC_getDeviceModelIdentifier(); BOOL DEMC_deviceIsSupported(); void DEMC_activate(); void DEMC_deactivate(); void DEMC_centerRenderingView(); @interface YTPlayerView : UIView -- (BOOL)zoomToFill; - (id)renderingView; -- (id)playerView; -@end - -@interface MLHAMSBDLSampleBufferRenderingView : UIView @end @interface YTMainAppVideoPlayerOverlayViewController : UIViewController - (BOOL)isFullscreen; -- (id)videoPlayerOverlayView; -- (id)activeVideoPlayerOverlay; +@end + +@interface HAMSBDLSampleBufferRenderingView : UIView +@end + +@interface MLHAMSBDLSampleBufferRenderingView : HAMSBDLSampleBufferRenderingView +@end + +@interface YTMainAppEngagementPanelViewController : UIViewController +- (BOOL)isLandscapeEngagementPanel; +- (BOOL)isPeekingSupported; +@end + +@interface YTEngagementPanelContainerViewController : UIViewController +- (BOOL)isLandscapeEngagementPanel; +- (BOOL)isPeekingSupported; @end // YTSpeed diff --git a/uYouPlus.xm b/uYouPlus.xm index 3192fad..ef0a29b 100644 --- a/uYouPlus.xm +++ b/uYouPlus.xm @@ -896,7 +896,8 @@ static void replaceTab(YTIGuideResponse *response) { // DontEatMyContent - @therealFoxster: https://github.com/therealFoxster/DontEatMyContent static double videoAspectRatio = 16/9; -static bool isZoomedToFill = false, isFullscreen = false, isNewVideo = true; +static bool zoomedToFill = false; +static bool engagementPanelIsVisible = false, removeEngagementPanelViewControllerWithIdentifierCalled = false; static MLHAMSBDLSampleBufferRenderingView *renderingView; static NSLayoutConstraint *widthConstraint, *heightConstraint, *centerXConstraint, *centerYConstraint; @@ -917,36 +918,34 @@ static NSLayoutConstraint *widthConstraint, *heightConstraint, *centerXConstrain 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 + // Making renderingView a bit larger since constraining to safe area leaves a gap between the notch and video + CGFloat constant = 24.5; // 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]; + // playerView.backgroundColor = [UIColor blueColor]; + // renderingViewContainer.backgroundColor = [UIColor greenColor]; + // renderingView.backgroundColor = [UIColor redColor]; YTMainAppVideoPlayerOverlayViewController *activeVideoPlayerOverlay = [self activeVideoPlayerOverlay]; // Must check class since YTInlineMutedPlaybackPlayerOverlayViewController doesn't have -(BOOL)isFullscreen - if ([NSStringFromClass([activeVideoPlayerOverlay class]) isEqualToString:@"YTMainAppVideoPlayerOverlayViewController"] + if ([NSStringFromClass([activeVideoPlayerOverlay class]) isEqualToString:@"YTMainAppVideoPlayerOverlayViewController"] // isKindOfClass doesn't work for some reason && [activeVideoPlayerOverlay isFullscreen]) { - if (!isZoomedToFill) DEMC_activate(); - isFullscreen = true; + if (!zoomedToFill && !engagementPanelIsVisible) DEMC_activate(); } else { DEMC_centerRenderingView(); - isFullscreen = false; } %orig(animated); } - (void)didPressToggleFullscreen { %orig; - YTMainAppVideoPlayerOverlayViewController *activeVideoPlayerOverlay = [self activeVideoPlayerOverlay]; - if (![activeVideoPlayerOverlay isFullscreen]) { // Entering full screen - if (!isZoomedToFill) DEMC_activate(); + if (![[self activeVideoPlayerOverlay] isFullscreen]) { // Entering full screen + if (!zoomedToFill && !engagementPanelIsVisible) DEMC_activate(); } else { // Exiting full screen DEMC_deactivate(); } @@ -955,90 +954,127 @@ static NSLayoutConstraint *widthConstraint, *heightConstraint, *centerXConstrain } - (void)didSwipeToEnterFullscreen { %orig; - if (!isZoomedToFill) DEMC_activate(); + if (!zoomedToFill && !engagementPanelIsVisible) DEMC_activate(); } - (void)didSwipeToExitFullscreen { %orig; DEMC_deactivate(); } -%end - -%hook MLHAMSBDLSampleBufferRenderingView -- (void)setVideo:(id)video playerConfig:(id)playerConfig { - %orig(video, playerConfig); - isNewVideo = true; +// New video played +-(void)playbackController:(id)playbackController didActivateVideo:(id)video withPlaybackData:(id)playbackData { + %orig(playbackController, video, playbackData); + if ([[self activeVideoPlayerOverlay] isFullscreen]) // New video played while in full screen (landscape) + // Activate since new videos played in full screen aren't zoomed-to-fill by default + // (i.e. the notch/Dynamic Island will cut into content when playing a new video in full screen) + DEMC_activate(); + engagementPanelIsVisible = false; + removeEngagementPanelViewControllerWithIdentifierCalled = false; } %end +// Pinch to zoom %hook YTVideoFreeZoomOverlayView - (void)didRecognizePinch:(UIPinchGestureRecognizer *)pinchGestureRecognizer { - // Pinched to zoom in/out DEMC_deactivate(); %orig(pinchGestureRecognizer); } // Detect zoom to fill - (void)showLabelForSnapState:(NSInteger)snapState { if (snapState == 0) { // Original - isZoomedToFill = false; + zoomedToFill = false; DEMC_activate(); } else if (snapState == 1) { // Zoomed to fill - isZoomedToFill = true; + zoomedToFill = true; // No need to deactivate constraints as it's already done in -(void)didRecognizePinch:(UIPinchGestureRecognizer *) } %orig(snapState); } -- (void)setEnabled:(BOOL)enabled { - %orig(enabled); - if (enabled && isNewVideo && isFullscreen) { // New video played while in full screen (landscape) - DEMC_activate(); // Activate since new videos played in full screen aren't zoomed-to-fill for first play (i.e. the notch/Dynamic Island will cut into content when playing a new video in full screen) - isNewVideo = false; - } -} %end +// Mini bar dismiss %hook YTWatchMiniBarViewController - (void)dismissMiniBarWithVelocity:(CGFloat)velocity gestureType:(int)gestureType { %orig(velocity, gestureType); - isZoomedToFill = false; // Setting to false since YouTube undoes zoom-to-fill when mini bar is dismissed + zoomedToFill = false; // Setting to false since YouTube undoes zoom-to-fill when mini bar is dismissed } - (void)dismissMiniBarWithVelocity:(CGFloat)velocity gestureType:(int)gestureType skipShouldDismissCheck:(BOOL)skipShouldDismissCheck { %orig(velocity, gestureType, skipShouldDismissCheck); - isZoomedToFill = false; + zoomedToFill = false; } %end -%end// gDontEatMyContent - -// https://stackoverflow.com/a/11197770/19227228 -NSString* DEMC_getDeviceModelIdentifier() { - struct utsname systemInfo; - uname(&systemInfo); - return [NSString stringWithCString:systemInfo.machine encoding:NSUTF8StringEncoding]; +%hook YTMainAppEngagementPanelViewController +// Engagement panel (comment, description, etc.) about to show up +- (void)viewWillAppear:(BOOL)animated { + if ([self isPeekingSupported]) { + // Shorts (only Shorts support peeking, I think) + } else { + // Everything else + engagementPanelIsVisible = true; + if ([self isLandscapeEngagementPanel]) { + DEMC_deactivate(); + } + } + %orig(animated); } +// Engagement panel about to dismiss +// - (void)viewDidDisappear:(BOOL)animated { %orig; %log; } // Called too late & isn't reliable so sometimes constraints aren't activated even when engagement panel is closed +%end + +%hook YTEngagementPanelContainerViewController +// Engagement panel about to dismiss +- (void)notifyEngagementPanelContainerControllerWillHideFinalPanel { // Called in time but crashes if plays new video while in full screen causing engagement panel dismissal + // Must check if engagement panel was dismissed because new video played + // (i.e. if -(void)removeEngagementPanelViewControllerWithIdentifier:(id) was called prior) + if (![self isPeekingSupported] && !removeEngagementPanelViewControllerWithIdentifierCalled) { + engagementPanelIsVisible = false; + if ([self isLandscapeEngagementPanel] && !zoomedToFill) { + DEMC_activate(); + } + } + %orig; +} +- (void)removeEngagementPanelViewControllerWithIdentifier:(id)identifier { + // Usually called when engagement panel is open & new video is played or mini bar is dismissed + removeEngagementPanelViewControllerWithIdentifierCalled = true; + %orig(identifier); +} +%end + +%end// group gDontEatMyContent BOOL DEMC_deviceIsSupported() { - NSString *identifier = DEMC_getDeviceModelIdentifier(); - NSArray *unsupportedDevices = UNSUPPORTED_DEVICES; + // Get device model identifier (e.g. iPhone14,4) + // https://stackoverflow.com/a/11197770/19227228 + struct utsname systemInfo; + uname(&systemInfo); + NSString *deviceModelID = [NSString stringWithCString:systemInfo.machine encoding:NSUTF8StringEncoding]; - for (NSString *device in unsupportedDevices) { - if ([device isEqualToString:identifier]) { + NSArray *unsupportedModelIDs = DEMC_UNSUPPORTED_DEVICES; + for (NSString *identifier in unsupportedModelIDs) { + if ([deviceModelID 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 + if ([deviceModelID containsString:@"iPhone"]) { + if ([deviceModelID isEqualToString:@"iPhone13,1"]) { + // iPhone 12 mini return YES; - } else if ([model floatValue] >= 14.0) { // iPhone 13 series and newer + } + NSString *modelNumber = [[deviceModelID stringByReplacingOccurrencesOfString:@"iPhone" withString:@""] stringByReplacingOccurrencesOfString:@"," withString:@"."]; + if ([modelNumber floatValue] >= 14.0) { + // iPhone 13 series and newer return YES; } else return NO; } else return NO; } void DEMC_activate() { - if (videoAspectRatio < THRESHOLD) DEMC_deactivate(); + if (videoAspectRatio < DEMC_THRESHOLD) { + DEMC_deactivate(); + return; + } // NSLog(@"activate"); DEMC_centerRenderingView(); renderingView.translatesAutoresizingMaskIntoConstraints = NO;