mirror of
				https://github.com/SoPat712/YTLitePlus.git
				synced 2025-10-30 20:34:03 -04:00 
			
		
		
		
	added files via upload
This commit is contained in:
		
							
								
								
									
										600
									
								
								Tweaks/YouPiP/Tweak.x
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										600
									
								
								Tweaks/YouPiP/Tweak.x
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,600 @@ | ||||
| #import <version.h> | ||||
| #import <rootless.h> | ||||
| #import "Header.h" | ||||
| #import "../YouTubeHeader/GIMBindingBuilder.h" | ||||
| #import "../YouTubeHeader/GPBExtensionRegistry.h" | ||||
| #import "../YouTubeHeader/MLPIPController.h" | ||||
| #import "../YouTubeHeader/MLDefaultPlayerViewFactory.h" | ||||
| #import "../YouTubeHeader/YTBackgroundabilityPolicy.h" | ||||
| #import "../YouTubeHeader/YTMainAppControlsOverlayView.h" | ||||
| #import "../YouTubeHeader/YTMainAppVideoPlayerOverlayViewController.h" | ||||
| #import "../YouTubeHeader/YTHotConfig.h" | ||||
| #import "../YouTubeHeader/YTLocalPlaybackController.h" | ||||
| #import "../YouTubeHeader/YTPlayerPIPController.h" | ||||
| #import "../YouTubeHeader/YTSettingsSectionItem.h" | ||||
| #import "../YouTubeHeader/YTSettingsSectionItemManager.h" | ||||
| #import "../YouTubeHeader/YTIPictureInPictureRendererRoot.h" | ||||
| #import "../YouTubeHeader/YTColor.h" | ||||
| #import "../YouTubeHeader/QTMIcon.h" | ||||
| #import "../YouTubeHeader/YTSlimVideoScrollableActionBarCellController.h" | ||||
| #import "../YouTubeHeader/YTSlimVideoScrollableDetailsActionsView.h" | ||||
| #import "../YouTubeHeader/YTSlimVideoDetailsActionView.h" | ||||
| #import "../YouTubeHeader/YTISlimMetadataButtonSupportedRenderers.h" | ||||
| #import "../YouTubeHeader/YTPageStyleController.h" | ||||
| #import "../YouTubeHeader/YTPlayerStatus.h" | ||||
| #import "../YouTubeHeader/YTWatchViewController.h" | ||||
|  | ||||
| #define PiPButtonType 801 | ||||
|  | ||||
| @interface YTMainAppControlsOverlayView (YP) | ||||
| @property (retain, nonatomic) YTQTMButton *pipButton; | ||||
| - (void)didPressPiP:(id)arg; | ||||
| - (UIImage *)pipImage; | ||||
| @end | ||||
|  | ||||
| BOOL FromUser = NO; | ||||
| BOOL PiPDisabled = NO; | ||||
|  | ||||
| extern BOOL LegacyPiP(); | ||||
| extern YTHotConfig *(*InjectYTHotConfig)(void); | ||||
|  | ||||
| BOOL TweakEnabled() { | ||||
|     return [[NSUserDefaults standardUserDefaults] boolForKey:EnabledKey]; | ||||
| } | ||||
|  | ||||
| BOOL UsePiPButton() { | ||||
|     return [[NSUserDefaults standardUserDefaults] boolForKey:PiPActivationMethodKey]; | ||||
| } | ||||
|  | ||||
| BOOL NoMiniPlayerPiP() { | ||||
|     return [[NSUserDefaults standardUserDefaults] boolForKey:NoMiniPlayerPiPKey]; | ||||
| } | ||||
|  | ||||
| BOOL UseTabBarPiPButton() { | ||||
|     return [[NSUserDefaults standardUserDefaults] boolForKey:PiPActivationMethod2Key]; | ||||
| } | ||||
|  | ||||
| BOOL NonBackgroundable() { | ||||
|     return [[NSUserDefaults standardUserDefaults] boolForKey:NonBackgroundableKey]; | ||||
| } | ||||
|  | ||||
| BOOL FakeVersion() { | ||||
|     return [[NSUserDefaults standardUserDefaults] boolForKey:FakeVersionKey]; | ||||
| } | ||||
|  | ||||
| BOOL isPictureInPictureActive(MLPIPController *pip) { | ||||
|     return [pip respondsToSelector:@selector(pictureInPictureActive)] ? [pip pictureInPictureActive] : [pip isPictureInPictureActive]; | ||||
| } | ||||
|  | ||||
| static NSString *PiPIconPath; | ||||
| static NSString *TabBarPiPIconPath; | ||||
| static NSString *PiPVideoPath; | ||||
|  | ||||
| static void forcePictureInPicture(YTHotConfig *hotConfig, BOOL value) { | ||||
|     [hotConfig mediaHotConfig].enablePictureInPicture = value; | ||||
|     YTIIosMediaHotConfig *iosMediaHotConfig = hotConfig.hotConfigGroup.mediaHotConfig.iosMediaHotConfig; | ||||
|     iosMediaHotConfig.enablePictureInPicture = value; | ||||
|     if ([iosMediaHotConfig respondsToSelector:@selector(setEnablePipForNonBackgroundableContent:)]) | ||||
|         iosMediaHotConfig.enablePipForNonBackgroundableContent = value && NonBackgroundable(); | ||||
|     if ([iosMediaHotConfig respondsToSelector:@selector(setEnablePipForNonPremiumUsers:)]) | ||||
|         iosMediaHotConfig.enablePipForNonPremiumUsers = value; | ||||
| } | ||||
|  | ||||
| static void activatePiPBase(YTPlayerPIPController *controller, BOOL playPiP) { | ||||
|     MLPIPController *pip = [controller valueForKey:@"_pipController"]; | ||||
|     if ([controller respondsToSelector:@selector(maybeEnablePictureInPicture)]) | ||||
|         [controller maybeEnablePictureInPicture]; | ||||
|     else if ([controller respondsToSelector:@selector(maybeInvokePictureInPicture)]) | ||||
|         [controller maybeInvokePictureInPicture]; | ||||
|     else { | ||||
|         BOOL canPiP = [controller respondsToSelector:@selector(canEnablePictureInPicture)] && [controller canEnablePictureInPicture]; | ||||
|         if (!canPiP) | ||||
|             canPiP = [controller respondsToSelector:@selector(canInvokePictureInPicture)] && [controller canInvokePictureInPicture]; | ||||
|         if (canPiP) { | ||||
|             if ([pip respondsToSelector:@selector(activatePiPController)]) | ||||
|                 [pip activatePiPController]; | ||||
|             else | ||||
|                 [pip startPictureInPicture]; | ||||
|         } | ||||
|     } | ||||
|     AVPictureInPictureController *avpip = [pip valueForKey:@"_pictureInPictureController"]; | ||||
|     if (playPiP) { | ||||
|         if ([avpip isPictureInPicturePossible]) | ||||
|             [avpip startPictureInPicture]; | ||||
|     } else { | ||||
|         if ([pip respondsToSelector:@selector(deactivatePiPController)]) | ||||
|             [pip deactivatePiPController]; | ||||
|         else | ||||
|             [avpip stopPictureInPicture]; | ||||
|     } | ||||
| } | ||||
|  | ||||
| static void activatePiP(YTLocalPlaybackController *local, BOOL playPiP) { | ||||
|     if (![local isKindOfClass:%c(YTLocalPlaybackController)]) | ||||
|         return; | ||||
|     YTPlayerPIPController *controller = [local valueForKey:@"_playerPIPController"]; | ||||
|     activatePiPBase(controller, playPiP); | ||||
| } | ||||
|  | ||||
| static void bootstrapPiP(YTPlayerViewController *self, BOOL playPiP) { | ||||
|     YTHotConfig *hotConfig; | ||||
|     @try { | ||||
|         if (InjectYTHotConfig) | ||||
|             hotConfig = InjectYTHotConfig(); | ||||
|         else | ||||
|             hotConfig = [self valueForKey:@"_hotConfig"]; | ||||
|     } @catch (id ex) { | ||||
|         hotConfig = [[self gimme] instanceForType:%c(YTHotConfig)]; | ||||
|     } | ||||
|     forcePictureInPicture(hotConfig, YES); | ||||
|     YTLocalPlaybackController *local = [self valueForKey:@"_playbackController"]; | ||||
|     activatePiP(local, playPiP); | ||||
| } | ||||
|  | ||||
| #pragma mark - Video tab bar PiP Button | ||||
|  | ||||
| static YTISlimMetadataButtonSupportedRenderers *makeUnderPlayerButton(NSString *title, int iconType, NSString *browseId) { | ||||
|     YTISlimMetadataButtonSupportedRenderers *supportedRenderer = [[%c(YTISlimMetadataButtonSupportedRenderers) alloc] init]; | ||||
|     YTISlimMetadataButtonRenderer *metadataButtonRenderer = [[%c(YTISlimMetadataButtonRenderer) alloc] init]; | ||||
|     YTIButtonSupportedRenderers *buttonSupportedRenderer = [[%c(YTIButtonSupportedRenderers) alloc] init]; | ||||
|     YTIBrowseEndpoint *endPoint = [[%c(YTIBrowseEndpoint) alloc] init]; | ||||
|     YTICommand *command = [[%c(YTICommand) alloc] init]; | ||||
|     YTIButtonRenderer *button = [[%c(YTIButtonRenderer) alloc] init]; | ||||
|     YTIIcon *icon = [[%c(YTIIcon) alloc] init]; | ||||
|     endPoint.browseId = browseId; | ||||
|     command.browseEndpoint = endPoint; | ||||
|     icon.iconType = iconType; | ||||
|     button.style = 8; // Opacity style | ||||
|     button.tooltip = title; | ||||
|     button.size = 1; // Default size | ||||
|     button.isDisabled = NO; | ||||
|     button.text = [%c(YTIFormattedString) formattedStringWithString:title]; | ||||
|     button.icon = icon; | ||||
|     button.navigationEndpoint = command; | ||||
|     buttonSupportedRenderer.buttonRenderer = button; | ||||
|     metadataButtonRenderer.button = buttonSupportedRenderer; | ||||
|     supportedRenderer.slimMetadataButtonRenderer = metadataButtonRenderer; | ||||
|     return supportedRenderer; | ||||
| } | ||||
|  | ||||
| %hook YTIIcon | ||||
|  | ||||
| - (UIImage *)iconImageWithColor:(UIColor *)color { | ||||
|     if (self.iconType == PiPButtonType) { | ||||
|         UIImage *image = [%c(QTMIcon) tintImage:[UIImage imageWithContentsOfFile:TabBarPiPIconPath] color:[[%c(YTPageStyleController) currentColorPalette] textPrimary]]; | ||||
|         if ([image respondsToSelector:@selector(imageFlippedForRightToLeftLayoutDirection)]) | ||||
|             image = [image imageFlippedForRightToLeftLayoutDirection]; | ||||
|         return image; | ||||
|     } | ||||
|     return %orig; | ||||
| } | ||||
| %end | ||||
|  | ||||
| %hook YTSlimVideoScrollableDetailsActionsView | ||||
|  | ||||
| - (void)createActionViewsFromSupportedRenderers:(NSMutableArray *)renderers { // for old YouTube version | ||||
|     if (UseTabBarPiPButton()) { | ||||
|         YTISlimMetadataButtonSupportedRenderers *PiPButton = makeUnderPlayerButton(@"PiP", PiPButtonType, @"YouPiP.pip.command"); | ||||
|         if (![renderers containsObject:PiPButton]) | ||||
|             [renderers addObject:PiPButton]; | ||||
|     } | ||||
|     %orig; | ||||
| } | ||||
|  | ||||
| - (void)createActionViewsFromSupportedRenderers:(NSMutableArray *)renderers withElementsContextBlock:(id)arg2 { | ||||
|     if (UseTabBarPiPButton()) { | ||||
|         YTISlimMetadataButtonSupportedRenderers *PiPButton = makeUnderPlayerButton(@"PiP", PiPButtonType, @"YouPiP.pip.command"); | ||||
|         if (![renderers containsObject:PiPButton]) | ||||
|             [renderers addObject:PiPButton]; | ||||
|     } | ||||
|     %orig; | ||||
| } | ||||
|  | ||||
| %end | ||||
|  | ||||
| %hook YTSlimVideoDetailsActionView | ||||
|  | ||||
| - (void)didTapButton:(id)arg1 { | ||||
|     if ([self.label.attributedText.string isEqualToString:@"PiP"]) { | ||||
|         YTSlimVideoScrollableActionBarCellController *_delegate = self.delegate; | ||||
|         YTPlayerViewController *playerViewController = nil; | ||||
|         @try { | ||||
|             if ([[_delegate valueForKey:@"_metadataPanelStateProvider"] isKindOfClass:%c(YTWatchController)]) { | ||||
|                 id provider = [_delegate valueForKey:@"_metadataPanelStateProvider"]; | ||||
|                 @try { | ||||
|                     YTWatchViewController *watchViewController = [provider valueForKey:@"_watchViewController"]; | ||||
|                     playerViewController = [watchViewController valueForKey:@"_playerViewController"]; | ||||
|                 } @catch (id ex) { | ||||
|                     playerViewController = [provider valueForKey:@"_playerViewController"]; | ||||
|                 } | ||||
|             } | ||||
|         } @catch (id ex) { // for old YouTube version | ||||
|             if ([[_delegate valueForKey:@"_ngwMetadataPanelStateProvider"] isKindOfClass:%c(YTNGWatchController)]) { | ||||
|                 id provider = [_delegate valueForKey:@"_ngwMetadataPanelStateProvider"]; | ||||
|                 playerViewController = [provider valueForKey:@"_playerViewController"]; | ||||
|             } | ||||
|         } | ||||
|         if (playerViewController && [playerViewController isKindOfClass:%c(YTPlayerViewController)]) { | ||||
|             FromUser = YES; | ||||
|             bootstrapPiP(playerViewController, YES); | ||||
|         } | ||||
|         return; | ||||
|     } | ||||
|     %orig; | ||||
| } | ||||
|  | ||||
| %end | ||||
|  | ||||
| #pragma mark - Overlay PiP Button | ||||
|  | ||||
| %hook YTMainAppVideoPlayerOverlayViewController | ||||
|  | ||||
| - (void)updateTopRightButtonAvailability { | ||||
|     %orig; | ||||
|     YTMainAppVideoPlayerOverlayView *v = [self videoPlayerOverlayView]; | ||||
|     YTMainAppControlsOverlayView *c = [v valueForKey:@"_controlsOverlayView"]; | ||||
|     c.pipButton.hidden = !UsePiPButton(); | ||||
|     [c setNeedsLayout]; | ||||
| } | ||||
|  | ||||
| %end | ||||
|  | ||||
| static void createPiPButton(YTMainAppControlsOverlayView *self) { | ||||
|     if (self) { | ||||
|         CGFloat padding = [[self class] topButtonAdditionalPadding]; | ||||
|         UIImage *image = [self pipImage]; | ||||
|         self.pipButton = [self buttonWithImage:image accessibilityLabel:@"pip" verticalContentPadding:padding]; | ||||
|         self.pipButton.hidden = YES; | ||||
|         self.pipButton.alpha = 0; | ||||
|         [self.pipButton addTarget:self action:@selector(didPressPiP:) forControlEvents:UIControlEventTouchUpInside]; | ||||
|         @try { | ||||
|             [[self valueForKey:@"_topControlsAccessibilityContainerView"] addSubview:self.pipButton]; | ||||
|         } @catch (id ex) { | ||||
|             [self addSubview:self.pipButton]; | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  | ||||
| static NSMutableArray *topControls(YTMainAppControlsOverlayView *self, NSMutableArray *controls) { | ||||
|     if (UsePiPButton()) | ||||
|         [controls insertObject:self.pipButton atIndex:0]; | ||||
|     return controls; | ||||
| } | ||||
|  | ||||
| %hook YTMainAppControlsOverlayView | ||||
|  | ||||
| %property (retain, nonatomic) YTQTMButton *pipButton; | ||||
|  | ||||
| - (id)initWithDelegate:(id)delegate { | ||||
|     self = %orig; | ||||
|     createPiPButton(self); | ||||
|     return self; | ||||
| } | ||||
|  | ||||
| - (id)initWithDelegate:(id)delegate autoplaySwitchEnabled:(BOOL)autoplaySwitchEnabled { | ||||
|     self = %orig; | ||||
|     createPiPButton(self); | ||||
|     return self; | ||||
| } | ||||
|  | ||||
| - (NSMutableArray *)topButtonControls { | ||||
|     return topControls(self, %orig); | ||||
| } | ||||
|  | ||||
| - (NSMutableArray *)topControls { | ||||
|     return topControls(self, %orig); | ||||
| } | ||||
|  | ||||
| - (void)setTopOverlayVisible:(BOOL)visible isAutonavCanceledState:(BOOL)canceledState { | ||||
|     if (UsePiPButton()) | ||||
|         self.pipButton.alpha = canceledState || !visible ? 0.0 : 1.0; | ||||
|     %orig; | ||||
| } | ||||
|  | ||||
| %new(@:) | ||||
| - (UIImage *)pipImage { | ||||
|     static UIImage *image = nil; | ||||
|     static dispatch_once_t onceToken; | ||||
|     dispatch_once(&onceToken, ^{ | ||||
|         UIColor *color = [%c(YTColor) white1]; | ||||
|         image = [%c(QTMIcon) tintImage:[UIImage imageWithContentsOfFile:PiPIconPath] color:color]; | ||||
|         if ([image respondsToSelector:@selector(imageFlippedForRightToLeftLayoutDirection)]) | ||||
|             image = [image imageFlippedForRightToLeftLayoutDirection]; | ||||
|     }); | ||||
|     return image; | ||||
| } | ||||
|  | ||||
| %new(v@:@) | ||||
| - (void)didPressPiP:(id)arg { | ||||
|     YTMainAppVideoPlayerOverlayViewController *c = [self valueForKey:@"_eventsDelegate"]; | ||||
|     FromUser = YES; | ||||
|     bootstrapPiP([c delegate], YES); | ||||
| } | ||||
|  | ||||
| %end | ||||
|  | ||||
| #pragma mark - PiP Support | ||||
|  | ||||
| %hook AVPictureInPictureController | ||||
|  | ||||
| + (BOOL)isPictureInPictureSupported { | ||||
|     return YES; | ||||
| } | ||||
|  | ||||
| %end | ||||
|  | ||||
| %hook AVPlayerController | ||||
|  | ||||
| - (BOOL)isPictureInPictureSupported { | ||||
|     return YES; | ||||
| } | ||||
|  | ||||
| %end | ||||
|  | ||||
| %hook AVSampleBufferDisplayLayerPlayerController | ||||
|  | ||||
| - (void)setPictureInPictureAvailable:(BOOL)available { | ||||
|     %orig(YES); | ||||
| } | ||||
|  | ||||
| %end | ||||
|  | ||||
| %hook MLPIPController | ||||
|  | ||||
| - (void)activatePiPController { | ||||
|     %orig; | ||||
|     if (!IS_IOS_OR_NEWER(iOS_15_0) && !LegacyPiP()) { | ||||
|         MLHAMSBDLSampleBufferRenderingView *view = [self valueForKey:@"_HAMPlayerView"]; | ||||
|         CGSize size = [self renderSizeForView:view]; | ||||
|         AVPictureInPictureController *avpip = [self valueForKey:@"_pictureInPictureController"]; | ||||
|         [avpip sampleBufferDisplayLayerRenderSizeDidChangeToSize:size]; | ||||
|         [avpip sampleBufferDisplayLayerDidAppear]; | ||||
|     } | ||||
| } | ||||
|  | ||||
| - (BOOL)isPictureInPictureSupported { | ||||
|     return YES; | ||||
| } | ||||
|  | ||||
| %new(v@:@) | ||||
| - (BOOL)pictureInPictureControllerPlaybackPaused:(AVPictureInPictureController *)pictureInPictureController { | ||||
|     return [self pictureInPictureControllerIsPlaybackPaused:pictureInPictureController]; | ||||
| } | ||||
|  | ||||
| %new(v@:@) | ||||
| - (void)pictureInPictureControllerStartPlayback:(id)arg1 { | ||||
|     [self pictureInPictureControllerStartPlayback]; | ||||
| } | ||||
|  | ||||
| %new(v@:@) | ||||
| - (void)pictureInPictureControllerStopPlayback:(id)arg1 { | ||||
|     [self pictureInPictureControllerStopPlayback]; | ||||
| } | ||||
|  | ||||
| %new | ||||
| - (void)renderingViewSampleBufferFrameSizeDidChange:(CGSize)size { | ||||
|     if (!IS_IOS_OR_NEWER(iOS_15_0) && size.width && size.height) { | ||||
|         AVPictureInPictureController *avpip = [self valueForKey:@"_pictureInPictureController"]; | ||||
|         [avpip sampleBufferDisplayLayerRenderSizeDidChangeToSize:size]; | ||||
|     } | ||||
| } | ||||
|  | ||||
| %new(v@:@) | ||||
| - (void)appWillEnterForeground:(id)arg1 { | ||||
|     if (!IS_IOS_OR_NEWER(iOS_15_0) && !LegacyPiP()) { | ||||
|         AVPictureInPictureController *avpip = [self valueForKey:@"_pictureInPictureController"]; | ||||
|         [avpip sampleBufferDisplayLayerDidAppear]; | ||||
|     } | ||||
| } | ||||
|  | ||||
| %new(v@:@) | ||||
| - (void)appWillEnterBackground:(id)arg1 { | ||||
|     if (!IS_IOS_OR_NEWER(iOS_15_0) && !LegacyPiP()) { | ||||
|         AVPictureInPictureController *avpip = [self valueForKey:@"_pictureInPictureController"]; | ||||
|         [avpip sampleBufferDisplayLayerDidDisappear]; | ||||
|     } | ||||
| } | ||||
|  | ||||
| %end | ||||
|  | ||||
| %hook MLDefaultPlayerViewFactory | ||||
|  | ||||
| - (MLAVPlayerLayerView *)AVPlayerViewForVideo:(MLVideo *)video playerConfig:(MLInnerTubePlayerConfig *)playerConfig { | ||||
|     forcePictureInPicture([self valueForKey:@"_hotConfig"], YES); | ||||
|     return %orig; | ||||
| } | ||||
|  | ||||
| %end | ||||
|  | ||||
| #pragma mark - PiP Support, Backgroundable | ||||
|  | ||||
| %hook YTIHamplayerConfig | ||||
|  | ||||
| - (BOOL)enableBackgroundable { | ||||
|     return YES; | ||||
| } | ||||
|  | ||||
| %end | ||||
|  | ||||
| %hook YTIBackgroundOfflineSettingCategoryEntryRenderer | ||||
|  | ||||
| - (BOOL)isBackgroundEnabled { | ||||
|     return YES; | ||||
| } | ||||
|  | ||||
| %end | ||||
|  | ||||
| %hook YTBackgroundabilityPolicy | ||||
|  | ||||
| - (void)updateIsBackgroundableByUserSettings { | ||||
|     %orig; | ||||
|     [self setValue:@(YES) forKey:@"_backgroundableByUserSettings"]; | ||||
| } | ||||
|  | ||||
| %end | ||||
|  | ||||
| %hook YTSettingsSectionItemManager | ||||
|  | ||||
| - (YTSettingsSectionItem *)pictureInPictureSectionItem { | ||||
|     forcePictureInPicture([self valueForKey:@"_hotConfig"], YES); | ||||
|     return %orig; | ||||
| } | ||||
|  | ||||
| - (YTSettingsSectionItem *)pictureInPictureSectionItem:(id)arg1 { | ||||
|     forcePictureInPicture([self valueForKey:@"_hotConfig"], YES); | ||||
|     return %orig; | ||||
| } | ||||
|  | ||||
| %end | ||||
|  | ||||
| #pragma mark - Hacks | ||||
|  | ||||
| BOOL YTSingleVideo_isLivePlayback_override = NO; | ||||
|  | ||||
| %hook YTSingleVideo | ||||
|  | ||||
| - (BOOL)isLivePlayback { | ||||
|     return YTSingleVideo_isLivePlayback_override ? NO : %orig; | ||||
| } | ||||
|  | ||||
| %end | ||||
|  | ||||
| static YTHotConfig *getHotConfig(YTPlayerPIPController *self) { | ||||
|     @try { | ||||
|         return [self valueForKey:@"_hotConfig"]; | ||||
|     } @catch (id ex) { | ||||
|         return [[self valueForKey:@"_config"] valueForKey:@"_hotConfig"]; | ||||
|     } | ||||
| } | ||||
|  | ||||
| %hook YTPlayerPIPController | ||||
|  | ||||
| - (BOOL)canInvokePictureInPicture { | ||||
|     forcePictureInPicture(getHotConfig(self), YES); | ||||
|     YTSingleVideo_isLivePlayback_override = YES; | ||||
|     BOOL value = %orig; | ||||
|     YTSingleVideo_isLivePlayback_override = NO; | ||||
|     return value; | ||||
| } | ||||
|  | ||||
| - (BOOL)canEnablePictureInPicture { | ||||
|     forcePictureInPicture(getHotConfig(self), YES); | ||||
|     YTSingleVideo_isLivePlayback_override = YES; | ||||
|     BOOL value = %orig; | ||||
|     YTSingleVideo_isLivePlayback_override = NO; | ||||
|     return value; | ||||
| } | ||||
|  | ||||
| - (void)didStopPictureInPicture { | ||||
|     FromUser = NO; | ||||
|     %orig; | ||||
| } | ||||
|  | ||||
| - (void)appWillResignActive:(id)arg1 { | ||||
|     // If PiP button on, PiP doesn't activate on app resign unless it's from user | ||||
|     BOOL hasPiPButton = UsePiPButton() || UseTabBarPiPButton(); | ||||
|     BOOL disablePiP = hasPiPButton && !FromUser; | ||||
|     if (disablePiP) { | ||||
|         MLPIPController *pip = [self valueForKey:@"_pipController"]; | ||||
|         [pip setValue:nil forKey:@"_pictureInPictureController"]; | ||||
|     } else { | ||||
|         if (LegacyPiP()) | ||||
|             activatePiPBase(self, YES); | ||||
|         %orig; | ||||
|     } | ||||
| } | ||||
|  | ||||
| %end | ||||
|  | ||||
| %hook YTSingleVideoController | ||||
|  | ||||
| - (void)playerStatusDidChange:(YTPlayerStatus *)playerStatus { | ||||
|     %orig; | ||||
|     PiPDisabled = NoMiniPlayerPiP() && playerStatus.visibility == 1; | ||||
| } | ||||
|  | ||||
| %end | ||||
|  | ||||
| %hook AVPictureInPicturePlatformAdapter | ||||
|  | ||||
| - (BOOL)isSystemPictureInPicturePossible { | ||||
|     return PiPDisabled ? NO : %orig; | ||||
| } | ||||
|  | ||||
| %end | ||||
|  | ||||
| %hook YTIPlayabilityStatus | ||||
|  | ||||
| - (BOOL)isPlayableInBackground { | ||||
|     return YES; | ||||
| } | ||||
|  | ||||
| - (BOOL)isPlayableInPictureInPicture { | ||||
|     return YES; | ||||
| } | ||||
|  | ||||
| - (BOOL)hasPictureInPicture { | ||||
|     return YES; | ||||
| } | ||||
|  | ||||
| %end | ||||
|  | ||||
| #pragma mark - PiP Support, Binding | ||||
|  | ||||
| %hook YTAppModule | ||||
|  | ||||
| - (void)configureWithBinder:(GIMBindingBuilder *)binder { | ||||
|     %orig; | ||||
|     [[binder bindType:%c(MLPIPController)] initializedWith:^(id a) { | ||||
|         MLPIPController *pip = [%c(MLPIPController) alloc]; | ||||
|         if ([pip respondsToSelector:@selector(initWithPlaceholderPlayerItemResourcePath:)]) | ||||
|             pip = [pip initWithPlaceholderPlayerItemResourcePath:PiPVideoPath]; | ||||
|         else if ([pip respondsToSelector:@selector(initWithPlaceholderPlayerItem:)]) | ||||
|             pip = [pip initWithPlaceholderPlayerItem:[AVPlayerItem playerItemWithURL:[NSURL URLWithString:PiPVideoPath]]]; | ||||
|         return pip; | ||||
|     }]; | ||||
| } | ||||
|  | ||||
| %end | ||||
|  | ||||
| %hook YTIInnertubeResourcesIosRoot | ||||
|  | ||||
| - (GPBExtensionRegistry *)extensionRegistry { | ||||
|     GPBExtensionRegistry *registry = %orig; | ||||
|     [registry addExtension:[%c(YTIPictureInPictureRendererRoot) pictureInPictureRenderer]]; | ||||
|     return registry; | ||||
| } | ||||
|  | ||||
| %end | ||||
|  | ||||
| %hook GoogleGlobalExtensionRegistry | ||||
|  | ||||
| - (GPBExtensionRegistry *)extensionRegistry { | ||||
|     GPBExtensionRegistry *registry = %orig; | ||||
|     [registry addExtension:[%c(YTIPictureInPictureRendererRoot) pictureInPictureRenderer]]; | ||||
|     return registry; | ||||
| } | ||||
|  | ||||
| %end | ||||
|  | ||||
| NSBundle *YouPiPBundle() { | ||||
|     static NSBundle *bundle = nil; | ||||
|     static dispatch_once_t onceToken; | ||||
| 	dispatch_once(&onceToken, ^{ | ||||
|         NSString *tweakBundlePath = [[NSBundle mainBundle] pathForResource:@"YouPiP" ofType:@"bundle"]; | ||||
|         if (tweakBundlePath) | ||||
|             bundle = [NSBundle bundleWithPath:tweakBundlePath]; | ||||
|         else | ||||
|             bundle = [NSBundle bundleWithPath:ROOT_PATH_NS(@"/Library/Application Support/YouPiP.bundle")]; | ||||
|     }); | ||||
|     return bundle; | ||||
| } | ||||
|  | ||||
| %ctor { | ||||
|     if (!TweakEnabled()) return; | ||||
|     NSBundle *tweakBundle = YouPiPBundle(); | ||||
|     PiPVideoPath = [tweakBundle pathForResource:@"PiPPlaceholderAsset" ofType:@"mp4"]; | ||||
|     PiPIconPath = [tweakBundle pathForResource:@"yt-pip-overlay" ofType:@"png"]; | ||||
|     TabBarPiPIconPath = [tweakBundle pathForResource:@"yt-pip-tabbar" ofType:@"png"]; | ||||
|     %init; | ||||
| } | ||||
		Reference in New Issue
	
	Block a user
	 Balackburn
					Balackburn