This commit is contained in:
dayanch96
2024-02-17 16:00:29 +03:00
parent 5b24103c2e
commit 870e7ba5b4
14 changed files with 755 additions and 256 deletions
+371 -176
View File
@@ -32,11 +32,21 @@
- (NSData *)elementData {
if (self.hasCompatibilityOptions && self.compatibilityOptions.hasAdLoggingData && kNoAds) return nil;
NSArray *adDescriptions = @[@"brand_promo", @"product_carousel", @"product_engagement_panel", @"product_item", @"text_search_ad", @"text_image_button_layout", @"carousel_headered_layout", @"carousel_footered_layout", @"square_image_layout", @"landscape_image_wide_button_layout", @"feed_ad_metadata"];
NSString *description = [self description];
if (([adDescriptions containsObject:description] && kNoAds) || ([description containsString:@"inline_shorts"] && kHideShorts)) {
NSArray *ads = @[@"brand_promo", @"product_carousel", @"product_engagement_panel", @"product_item", @"text_search_ad", @"text_image_button_layout", @"carousel_headered_layout", @"carousel_footered_layout", @"square_image_layout", @"landscape_image_wide_button_layout", @"feed_ad_metadata"];
if (kNoAds && [ads containsObject:description]) {
return [NSData data];
} return %orig;
}
NSArray *shortsToRemove = @[@"shorts_shelf.eml", @"shorts_video_cell.eml", @"6Shorts"];
for (NSString *shorts in shortsToRemove) {
if (kHideShorts && [description containsString:shorts] && ![description containsString:@"history*"]) {
return nil;
}
}
return %orig;
}
%end
@@ -105,42 +115,29 @@
%hook YTRightNavigationButtons
- (void)layoutSubviews {
%orig;
if (kNoCast && self.subviews.count > 1 && [self.subviews[1].accessibilityIdentifier isEqualToString:@"id.mdx.playbackroute.button"]) self.subviews[1].hidden = YES; // Hide icon immediately
if (kNoNotifsButton) self.notificationButton.hidden = YES;
if (kNoSearchButton) self.searchButton.hidden = YES;
NSInteger moreButtonIndex = -1;
for (NSInteger i = 0; i < self.subviews.count; i++) {
UIView *subview = self.subviews[i];
if ([subview.accessibilityIdentifier isEqualToString:@"id.settings.overflow.button"]) {
moreButtonIndex = i;
break;
}
}
if (moreButtonIndex != -1 && moreButtonIndex < self.subviews.count - 1 && kNoVoiceSearchButton) {
UIView *voiceButton = self.subviews[moreButtonIndex + 1];
voiceButton.hidden = YES;
for (UIView *subview in self.subviews) {
if (kNoVoiceSearchButton && [subview.accessibilityLabel isEqualToString:NSLocalizedString(@"search.voice.access", nil)]) subview.hidden = YES;
if (kNoCast && [subview.accessibilityIdentifier isEqualToString:@"id.mdx.playbackroute.button"]) subview.hidden = YES;
}
}
%end
%hook YTSearchView
- (void)layoutSubviews {
%hook YTSearchViewController
- (void)viewDidLoad {
%orig;
// Hide Search History
if (kNoSearchHistory && self.subviews.count > 1) self.subviews[1].hidden = YES;
// Hide Voice Search Button
if (kNoVoiceSearchButton && self.subviews.count > 0) {
UIView *firstSubview = self.subviews.firstObject;
for (UIView *subview in firstSubview.subviews) {
if ([NSStringFromClass([subview class]) isEqualToString:@"UIView"]) {
[subview setValue:@(1) forKey:@"hidden"];
break;
}
}
}
if (kNoVoiceSearchButton) [self setValue:@(NO) forKey:@"_isVoiceSearchAllowed"];
}
- (void)setSuggestions:(id)arg1 { if (!kNoSearchHistory) %orig; }
%end
%hook YTPersonalizedSuggestionsCacheProvider
- (id)activeCache { return kNoSearchHistory ? nil : %orig; }
%end
// Remove Videos Section Under Player
@@ -151,14 +148,13 @@
}
%end
// Hide YouTube Logo
%hook YTNavigationBarTitleView
- (void)layoutSubviews { %orig; if (kNoYTLogo && self.subviews.count > 1 && [self.subviews[1].accessibilityIdentifier isEqualToString:@"id.yoodle.logo"]) self.subviews[1].hidden = YES; }
%end
// Stick Navigation bar
%hook YTHeaderView
- (BOOL)stickyNavHeaderEnabled { return kStickyNavbar ? YES : NO; }
// Stick Navigation bar
- (BOOL)stickyNavHeaderEnabled { return kStickyNavbar ? YES : %orig; }
// Hide YouTube Logo
- (void)setCustomTitleView:(UIView *)customTitleView { if (!kNoYTLogo) %orig; }
- (void)setTitle:(NSString *)title { kNoYTLogo ? %orig(@"") : %orig; }
%end
// Remove Subbar
@@ -190,7 +186,6 @@
- (id)initWithMessage:(id)arg1 dismissHandler:(id)arg2 { return kNoHUDMsgs ? nil : %orig; }
%end
%hook YTColdConfig
// Hide Next & Previous buttons
- (BOOL)removeNextPaddleForSingletonVideos { return kHidePrevNext ? YES : %orig; }
@@ -248,11 +243,19 @@
- (void)setPaidContentWithPlayerData:(id)data { if (!kNoPromotionCards) %orig; }
%end
%hook YTInlinePlayerBarContainerView
- (void)setPlayerBarAlpha:(CGFloat)alpha { kPersistentProgressBar ? %orig(1.0) : %orig; }
%end
// Remove Watermarks
%hook YTAnnotationsViewController
- (void)loadFeaturedChannelWatermark { if (!kNoWatermarks) %orig; }
%end
%hook YTMainAppVideoPlayerOverlayView
- (BOOL)isWatermarkEnabled { return kNoWatermarks ? NO : %orig; }
%end
// Forcibly Enable Miniplayer
%hook YTWatchMiniBarViewController
- (void)updateMiniBarPlayerStateFromRenderer { if (!kMiniplayer) %orig; }
@@ -270,7 +273,7 @@
// Skip Content Warning (https://github.com/qnblackcat/uYouPlus/blob/main/uYouPlus.xm#L452-L454)
%hook YTPlayabilityResolutionUserActionUIController
- (void)showConfirmAlert { if (kNoContentWarning) [self confirmAlertDidPressConfirm]; }
- (void)showConfirmAlert { kNoContentWarning ? [self confirmAlertDidPressConfirm] : %orig; }
%end
// Classic Video Quality (https://github.com/PoomSmart/YTClassicVideoQuality)
@@ -374,7 +377,10 @@
%hook YTPlayerViewController
- (void)loadWithPlayerTransition:(id)arg1 playbackConfig:(id)arg2 {
%orig;
if (kAutoFullscreen) [NSTimer scheduledTimerWithTimeInterval:0.75 target:self selector:@selector(autoFullscreen) userInfo:nil repeats:NO];
if (kShortsToRegular) [NSTimer scheduledTimerWithTimeInterval:0.75 target:self selector:@selector(shortsToRegular) userInfo:nil repeats:NO];
if (kDisableAutoCaptions) [NSTimer scheduledTimerWithTimeInterval:0.75 target:self selector:@selector(turnOffCaptions) userInfo:nil repeats:NO];
}
%new
@@ -382,6 +388,21 @@
YTWatchController *watchController = [self valueForKey:@"_UIDelegate"];
[watchController showFullScreen];
}
%new
- (void)shortsToRegular {
if (self.contentVideoID != nil && [self.parentViewController isKindOfClass:NSClassFromString(@"YTShortsPlayerViewController")]) {
NSString *vidLink = [NSString stringWithFormat:@"vnd.youtube://%@", self.contentVideoID];
if ([[UIApplication sharedApplication] canOpenURL:[NSURL URLWithString:vidLink]]) {
[[UIApplication sharedApplication] openURL:[NSURL URLWithString:vidLink] options:@{} completionHandler:nil];
}
}
}
%new
- (void)turnOffCaptions {
[self setActiveCaptionTrack:nil];
}
%end
// Exit Fullscreen on Finish
@@ -458,7 +479,7 @@
if ([cell respondsToSelector:@selector(node)]) {
NSString *idToRemove = [[cell node] accessibilityIdentifier];
if ([idToRemove isEqualToString:@"statement_banner.view"] ||
(([idToRemove isEqualToString:@"eml.shorts-grid"] || [idToRemove isEqualToString:@"eml.shorts-shelf"] || [idToRemove isEqualToString:@"eml.inline_shorts"]) && kHideShorts)) {
(([idToRemove isEqualToString:@"eml.shorts-grid"] || [idToRemove isEqualToString:@"eml.shorts-shelf"]) && kHideShorts)) {
[self removeCellsAtIndexPath:indexPath];
}
}
@@ -487,6 +508,11 @@
- (BOOL)shouldEnablePlayerBarOnlyOnPause { return kShortsProgress ? NO : YES; }
%end
%hook YTShortsPlayerViewController
- (BOOL)shouldAlwaysEnablePlayerBar { return kShortsProgress ? YES : NO; }
- (BOOL)shouldEnablePlayerBarOnlyOnPause { return kShortsProgress ? NO : YES; }
%end
%hook YTColdConfig
- (BOOL)iosEnableVideoPlayerScrubber { return kShortsProgress ? YES : NO; }
- (BOOL)mobileShortsTabInlined { return kShortsProgress ? YES : NO; }
@@ -512,16 +538,7 @@
- (void)setViewCommentButton:(id)arg1 { if (!kHideShortsComments) %orig; }
- (void)setRemixButton:(id)arg1 { if (!kHideShortsRemix) %orig; }
- (void)setShareButton:(id)arg1 { if (!kHideShortsShare) %orig; }
- (void)layoutSubviews {
%orig;
for (UIView *subview in self.subviews) {
if (kHideShortsAvatars && [NSStringFromClass([subview class]) isEqualToString:@"YTELMView"]) {
subview.hidden = YES;
break;
}
}
}
- (void)setNativePivotButton:(id)arg1 { if (!kHideShortsAvatars) %orig; }
%end
%hook YTReelHeaderView
@@ -531,31 +548,200 @@
%hook YTReelTransparentStackView
- (void)layoutSubviews {
%orig;
if (kHideShortsSearch && self.subviews.count >= 3 && [self.subviews[0].accessibilityIdentifier isEqualToString:@"id.ui.generic.button"]) self.subviews[0].hidden = YES;
if (kHideShortsCamera && self.subviews.count >= 3 && [self.subviews[1].accessibilityIdentifier isEqualToString:@"id.ui.generic.button"]) self.subviews[1].hidden = YES;
if (kHideShortsMore && self.subviews.count >= 3 && [self.subviews[2].accessibilityIdentifier isEqualToString:@"id.ui.generic.button"]) self.subviews[2].hidden = YES;
}
%end
%hook YTReelWatchHeaderView
- (void)layoutSubviews {
%orig;
if (kHideShortsDescription && [self.subviews[2].accessibilityIdentifier isEqualToString:@"id.reels_smv_player_title_label"]) self.subviews[2].hidden = YES;
if (kHideShortsChannelName) self.subviews[self.subviews.count - 2].hidden = YES;
if (kHideShortsAudioTrack) self.subviews.lastObject.hidden = YES;
for (UIView *subview in self.subviews) {
if (kHideShortsPromoCards && [NSStringFromClass([subview class]) isEqualToString:@"YTBadge"]) {
subview.hidden = YES;
for (YTQTMButton *button in self.subviews) {
if ([button respondsToSelector:@selector(buttonRenderer)]) {
if (kHideShortsSearch && button.buttonRenderer.icon.iconType == 1045) button.hidden = YES;
if (kHideShortsCamera && button.buttonRenderer.icon.iconType == 1046) button.hidden = YES;
if (kHideShortsMore && button.buttonRenderer.icon.iconType == 1047) button.hidden = YES;
}
}
}
%end
%hook YTReelWatchHeaderView
- (void)setChannelBarElementRenderer:(id)renderer { if (!kHideShortsChannelName) %orig; }
- (void)setHeaderRenderer:(id)renderer { if (!kHideShortsDescription) %orig; }
- (void)setSoundMetadataElementRenderer:(id)renderer { if (!kHideShortsAudioTrack) %orig; }
- (void)setActionElement:(id)renderer { if (!kHideShortsPromoCards) %orig; }
- (void)setBadgeRenderer:(id)renderer { if (!kHideShortsThanks) %orig; }
- (void)setMultiFormatLinkElementRenderer:(id)renderer { if (!kHideShortsSource) %orig; }
%end
static BOOL isOverlayShown = YES;
%hook YTPlayerView
- (void)didPinch:(UIPinchGestureRecognizer *)gesture {
%orig;
if (kPinchToFullscreenShorts && [self.playerViewDelegate.parentViewController isKindOfClass:NSClassFromString(@"YTShortsPlayerViewController")]) {
YTShortsPlayerViewController *shortsPlayerVC = (YTShortsPlayerViewController *)self.playerViewDelegate.parentViewController;
YTReelContentView *contentView = (YTReelContentView *)shortsPlayerVC.view;
if (gesture.scale > 1) {
if (!kShortsOnlyMode) [shortsPlayerVC.navigationController.parentViewController hidePivotBar];
[UIView animateWithDuration:0.3 animations:^{
contentView.playbackOverlay.alpha = 0;
isOverlayShown = contentView.playbackOverlay.alpha;
}];
} else {
if (!kShortsOnlyMode) [shortsPlayerVC.navigationController.parentViewController showPivotBar];
[UIView animateWithDuration:0.3 animations:^{
contentView.playbackOverlay.alpha = 1;
isOverlayShown = contentView.playbackOverlay.alpha;
}];
}
}
}
%hook _ASDisplayView
- (void)layoutSubviews {
%orig;
if (kHideShortsThanks && [self.accessibilityIdentifier isEqualToString:@"id.elements.components.suggested_action"]) self.hidden = YES;
if (kShortsOnlyMode && [self.playerViewDelegate.parentViewController isKindOfClass:NSClassFromString(@"YTShortsPlayerViewController")]) {
UILongPressGestureRecognizer *longPressGesture = [[UILongPressGestureRecognizer alloc] initWithTarget:self action:@selector(turnShortsOnlyModeOff:)];
longPressGesture.numberOfTouchesRequired = 2;
longPressGesture.minimumPressDuration = 0.5;
[self addGestureRecognizer:longPressGesture];
}
}
%new
- (void)turnShortsOnlyModeOff:(UILongPressGestureRecognizer *)gesture {
if (gesture.state == UIGestureRecognizerStateBegan) {
NSString *path = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES).firstObject stringByAppendingPathComponent:@"YTLite.plist"];
NSMutableDictionary *prefs = [NSMutableDictionary dictionaryWithContentsOfFile:path];
[prefs setObject:@NO forKey:@"shortsOnlyMode"];
[prefs writeToFile:path atomically:NO];
CFNotificationCenterPostNotification(CFNotificationCenterGetDarwinNotifyCenter(), CFSTR("com.dvntm.ytlite.prefschanged"), NULL, NULL, YES);
UIResponder *responder = self.nextResponder;
while (responder && ![responder isKindOfClass:[UIViewController class]]) responder = responder.nextResponder;
if (responder) [[%c(YTToastResponderEvent) eventWithMessage:LOC(@"ShortsModeTurnedOff") firstResponder:responder] send];
YTShortsPlayerViewController *shortsPlayerVC = (YTShortsPlayerViewController *)self.playerViewDelegate.parentViewController;
[shortsPlayerVC.navigationController.parentViewController performSelector:@selector(showPivotBar) withObject:nil afterDelay:1.0];
}
}
%end
%hook YTReelWatchPlaybackOverlayView
- (void)layoutSubviews {
%orig;
self.alpha = isOverlayShown;
}
%end
%hook _ASDisplayView
- (void)setKeepalive_node:(id)arg1 {
%orig;
NSString *description = [self description];
if (kCopyPostText && [description containsString:@"ELMExpandableTextNode-View"]) {
UILongPressGestureRecognizer *longPress = [[UILongPressGestureRecognizer alloc] initWithTarget:self action:@selector(copyText:)];
longPress.minimumPressDuration = 0.3;
[self addGestureRecognizer:longPress];
}
if (kSavePostImage && [description containsString:@"YTImageZoomNode-View"]) {
UILongPressGestureRecognizer *longPress = [[UILongPressGestureRecognizer alloc] initWithTarget:self action:@selector(saveImage:)];
longPress.minimumPressDuration = 0.3;
[self addGestureRecognizer:longPress];
}
if (kSaveProfilePhoto && [description containsString:@"ELMImageNode-View"] && [description containsString:@"eml.avatar"]) {
UILongPressGestureRecognizer *longPress = [[UILongPressGestureRecognizer alloc] initWithTarget:self action:@selector(savePFP:)];
longPress.minimumPressDuration = 0.3;
[self addGestureRecognizer:longPress];
}
}
%new
- (void)savePFP:(UILongPressGestureRecognizer *)sender {
if (sender.state == UIGestureRecognizerStateBegan) {
NSString *URLString = self.keepalive_node.URL.absoluteString;
if (URLString) {
NSRange sizeRange = [URLString rangeOfString:@"=s"];
if (sizeRange.location != NSNotFound) {
NSRange dashRange = [URLString rangeOfString:@"-" options:0 range:NSMakeRange(sizeRange.location, URLString.length - sizeRange.location)];
if (dashRange.location != NSNotFound) {
NSString *newURLString = [URLString stringByReplacingCharactersInRange:NSMakeRange(sizeRange.location + 2, dashRange.location - sizeRange.location - 2) withString:@"1024"];
NSURL *PFPURL = [NSURL URLWithString:newURLString];
UIImage *image = [UIImage imageWithData:[NSData dataWithContentsOfURL:PFPURL]];
if (image) {
UIImageWriteToSavedPhotosAlbum(image, nil, nil, nil);
UIResponder *responder = self.nextResponder;
while (responder && ![responder isKindOfClass:[UIViewController class]]) responder = responder.nextResponder;
if (responder) [[%c(YTToastResponderEvent) eventWithMessage:LOC(@"Saved") firstResponder:responder] send];
}
}
}
}
}
}
%new
- (void)copyText:(UILongPressGestureRecognizer *)sender {
if (sender.state == UIGestureRecognizerStateBegan) {
UIPasteboard *pasteboard = [UIPasteboard generalPasteboard];
pasteboard.string = self.accessibilityLabel;
UIResponder *responder = self.nextResponder;
while (responder && ![responder isKindOfClass:[UIViewController class]]) responder = responder.nextResponder;
if (responder) [[%c(YTToastResponderEvent) eventWithMessage:LOC(@"Copied") firstResponder:responder] send];
}
}
%new
- (void)saveImage:(UILongPressGestureRecognizer *)sender {
if (sender.state == UIGestureRecognizerStateBegan) {
NSURL *imageURL = self.keepalive_node.URL;
if (imageURL) {
NSString *URLString = imageURL.absoluteString;
if (kFixAlbums && [URLString hasPrefix:@"https://yt3."]) {
URLString = [URLString stringByReplacingOccurrencesOfString:@"https://yt3." withString:@"https://yt4."];
}
NSURL *downloadURL = nil;
if ([URLString containsString:@"c-fcrop"]) {
NSRange croppedURL = [URLString rangeOfString:@"c-fcrop"];
if (croppedURL.location != NSNotFound) {
NSString *newURL = [URLString stringByReplacingOccurrencesOfString:[URLString substringFromIndex:croppedURL.location] withString:@"nd-v1"];
downloadURL = [NSURL URLWithString:newURL];
}
} else {
downloadURL = imageURL;
}
UIResponder *responder = self.nextResponder;
while (responder && ![responder isKindOfClass:[UIViewController class]]) responder = responder.nextResponder;
NSURLSession *session = [NSURLSession sharedSession];
[[session dataTaskWithURL:downloadURL completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) {
if (data) {
[[PHPhotoLibrary sharedPhotoLibrary] performChanges:^{
PHAssetCreationRequest *request = [PHAssetCreationRequest creationRequestForAsset];
[request addResourceWithType:PHAssetResourceTypePhoto data:data options:nil];
} completionHandler:^(BOOL success, NSError *error) {
if (responder) [[%c(YTToastResponderEvent) eventWithMessage:success ? LOC(@"Saved") : [NSString stringWithFormat:LOC(@"%@: %@"), LOC(@"Error"), error.localizedDescription] firstResponder:responder] send];
}];
} else {
if (responder) [[%c(YTToastResponderEvent) eventWithMessage:[NSString stringWithFormat:LOC(@"%@: %@"), LOC(@"Error"), error.localizedDescription] firstResponder:responder] send];
}
}] resume];
}
}
}
%end
@@ -565,59 +751,39 @@
NSMutableArray <YTIPivotBarSupportedRenderers *> *items = [renderer itemsArray];
NSDictionary *identifiersToRemove = @{
@"FEshorts": @(kRemoveShorts),
@"FEsubscriptions": @(kRemoveSubscriptions),
@"FEuploads": @(kRemoveUploads),
@"FElibrary": @(kRemoveLibrary)
@"FEshorts": @[@(kRemoveShorts), @(kReExplore)],
@"FEsubscriptions": @[@(kRemoveSubscriptions)],
@"FEuploads": @[@(kRemoveUploads)],
@"FElibrary": @[@(kRemoveLibrary)]
};
for (NSString *identifier in identifiersToRemove) {
BOOL shouldRemoveItem = [identifiersToRemove[identifier] boolValue];
NSUInteger index = [items indexOfObjectPassingTest:^BOOL(YTIPivotBarSupportedRenderers *renderers, NSUInteger idx, BOOL *stop) {
NSArray *removeValues = identifiersToRemove[identifier];
BOOL shouldRemoveItem = [removeValues containsObject:@(YES)];
NSUInteger index = [items indexOfObjectPassingTest:^BOOL(YTIPivotBarSupportedRenderers *renderer, NSUInteger idx, BOOL *stop) {
if ([identifier isEqualToString:@"FEuploads"]) {
return shouldRemoveItem && [[[renderers pivotBarIconOnlyItemRenderer] pivotIdentifier] isEqualToString:identifier];
return shouldRemoveItem && [[[renderer pivotBarIconOnlyItemRenderer] pivotIdentifier] isEqualToString:identifier];
} else {
return shouldRemoveItem && [[[renderers pivotBarItemRenderer] pivotIdentifier] isEqualToString:identifier];
return shouldRemoveItem && [[[renderer pivotBarItemRenderer] pivotIdentifier] isEqualToString:identifier];
}
}];
if (index != NSNotFound) {
[items removeObjectAtIndex:index];
}
} %orig;
}
%end
// Replace Shorts with Explore tab (https://github.com/PoomSmart/YTReExplore)
static void replaceTab(YTIGuideResponse *response) {
NSMutableArray <YTIGuideResponseSupportedRenderers *> *renderers = [response itemsArray];
for (YTIGuideResponseSupportedRenderers *guideRenderers in renderers) {
YTIPivotBarRenderer *pivotBarRenderer = [guideRenderers pivotBarRenderer];
NSMutableArray <YTIPivotBarSupportedRenderers *> *items = [pivotBarRenderer itemsArray];
NSUInteger shortIndex = [items indexOfObjectPassingTest:^BOOL(YTIPivotBarSupportedRenderers *renderers, NSUInteger idx, BOOL *stop) {
return [[[renderers pivotBarItemRenderer] pivotIdentifier] isEqualToString:@"FEshorts"];
}];
if (shortIndex != NSNotFound) {
[items removeObjectAtIndex:shortIndex];
NSUInteger exploreIndex = [items indexOfObjectPassingTest:^BOOL(YTIPivotBarSupportedRenderers *renderers, NSUInteger idx, BOOL *stop) {
return [[[renderers pivotBarItemRenderer] pivotIdentifier] isEqualToString:[%c(YTIBrowseRequest) browseIDForExploreTab]];
}];
if (exploreIndex == NSNotFound) {
YTIPivotBarSupportedRenderers *exploreTab = [%c(YTIPivotBarRenderer) pivotSupportedRenderersWithBrowseId:[%c(YTIBrowseRequest) browseIDForExploreTab] title:LOC(@"Explore") iconType:292];
[items insertObject:exploreTab atIndex:1];
}
}
}
}
NSUInteger exploreIndex = [items indexOfObjectPassingTest:^BOOL(YTIPivotBarSupportedRenderers *renderers, NSUInteger idx, BOOL *stop) {
return [[[renderers pivotBarItemRenderer] pivotIdentifier] isEqualToString:[%c(YTIBrowseRequest) browseIDForExploreTab]];
}];
%hook YTGuideServiceCoordinator
- (void)handleResponse:(YTIGuideResponse *)response withCompletion:(id)completion {
if (kReExplore) replaceTab(response);
%orig(response, completion);
}
- (void)handleResponse:(YTIGuideResponse *)response error:(id)error completion:(id)completion {
if (kReExplore) replaceTab(response);
%orig(response, error, completion);
if (exploreIndex == NSNotFound && (kReExplore || kAddExplore)) {
YTIPivotBarSupportedRenderers *exploreTab = [%c(YTIPivotBarRenderer) pivotSupportedRenderersWithBrowseId:[%c(YTIBrowseRequest) browseIDForExploreTab] title:LOC(@"Explore") iconType:292];
[items insertObject:exploreTab atIndex:1];
}
%orig;
}
%end
@@ -628,76 +794,13 @@ static void replaceTab(YTIGuideResponse *response) {
%end
// Hide Tab Labels
BOOL hasHomeBar = NO;
CGFloat pivotBarViewHeight;
%hook YTPivotBarView
- (void)layoutSubviews {
%orig;
pivotBarViewHeight = self.frame.size.height;
}
%end
%hook YTPivotBarItemView
- (void)layoutSubviews {
- (void)setRenderer:(YTIPivotBarRenderer *)renderer {
%orig;
CGFloat pivotBarAccessibilityControlWidth;
if (kRemoveLabels) {
for (UIView *subview in self.subviews) {
if ([subview isKindOfClass:objc_lookUpClass("YTPivotBarItemViewAccessibilityControl")]) {
pivotBarAccessibilityControlWidth = CGRectGetWidth(subview.frame);
break;
}
}
for (UIView *subview in self.subviews) {
if ([subview isKindOfClass:objc_lookUpClass("YTQTMButton")]) {
for (UIView *buttonSubview in subview.subviews) {
if ([buttonSubview isKindOfClass:[UILabel class]]) {
[buttonSubview removeFromSuperview];
break;
}
}
UIImageView *imageView = nil;
for (UIView *buttonSubview in subview.subviews) {
if ([buttonSubview isKindOfClass:[UIImageView class]]) {
imageView = (UIImageView *)buttonSubview;
break;
}
}
if (imageView) {
CGFloat imageViewHeight = imageView.image.size.height;
CGFloat imageViewWidth = imageView.image.size.width;
CGRect buttonFrame = subview.frame;
if (@available(iOS 13.0, *)) {
UIWindowScene *mainWindowScene = (UIWindowScene *)[[[UIApplication sharedApplication] connectedScenes] anyObject];
if (mainWindowScene) {
UIEdgeInsets safeAreaInsets = mainWindowScene.windows.firstObject.safeAreaInsets;
if (safeAreaInsets.bottom > 0) {
hasHomeBar = YES;
}
}
}
CGFloat yOffset = hasHomeBar ? 15.0 : 0.0;
CGFloat xOffset = (pivotBarAccessibilityControlWidth - imageViewWidth) / 2.0;
buttonFrame.origin.y = (pivotBarViewHeight - imageViewHeight - yOffset) / 2.0;
buttonFrame.origin.x = xOffset;
buttonFrame.size.height = imageViewHeight;
buttonFrame.size.width = imageViewWidth;
subview.frame = buttonFrame;
subview.bounds = CGRectMake(0, 0, imageViewWidth, imageViewHeight);
}
}
}
[self.navigationButton setTitle:@"" forState:UIControlStateNormal];
[self.navigationButton setSizeWithPaddingAndInsets:NO];
}
}
%end
@@ -706,21 +809,24 @@ CGFloat pivotBarViewHeight;
BOOL isTabSelected = NO;
%hook YTPivotBarViewController
- (void)viewDidAppear:(BOOL)animated {
%orig();
%orig;
if (!isTabSelected) {
if (!isTabSelected && !kShortsOnlyMode) {
NSString *pivotIdentifier;
switch (kPivotIndex) {
case 0:
pivotIdentifier = @"FEwhat_to_watch";
break;
case 1:
pivotIdentifier = @"FEshorts";
pivotIdentifier = @"FEexplore";
break;
case 2:
pivotIdentifier = @"FEsubscriptions";
pivotIdentifier = @"FEshorts";
break;
case 3:
pivotIdentifier = @"FEsubscriptions";
break;
case 4:
pivotIdentifier = @"FElibrary";
break;
default:
@@ -729,6 +835,35 @@ BOOL isTabSelected = NO;
[self selectItemWithPivotIdentifier:pivotIdentifier];
isTabSelected = YES;
}
if (kShortsOnlyMode) {
[self selectItemWithPivotIdentifier:@"FEshorts"];
[self.parentViewController hidePivotBar];
}
}
%end
%hook YTTabsViewController
- (void)viewDidAppear:(BOOL)animated {
%orig;
if (kShortsOnlyMode) {
[self.navigationController.parentViewController hidePivotBar];
}
}
%end
%hook YTAppViewController
- (void)showPivotBar { if (!kShortsOnlyMode) %orig;} ;
%end
%hook YTReelWatchRootViewController
- (void)viewDidAppear:(BOOL)animated {
%orig;
if (kShortsOnlyMode) {
[self.navigationController.parentViewController hidePivotBar];
}
}
%end
@@ -738,6 +873,35 @@ BOOL isTabSelected = NO;
+ (NSWritingDirection)_defaultWritingDirection { return kDisableRTL ? NSWritingDirectionLeftToRight : %orig; }
%end
// Fix Albums For Russian Users
static NSURL *newCoverURL(NSURL *originalURL) {
NSDictionary <NSString *, NSString *> *hostsToReplace = @{
@"yt3.ggpht.com": @"yt4.ggpht.com",
@"yt3.googleusercontent.com": @"yt4.googleusercontent.com",
};
NSString *const replacement = hostsToReplace[originalURL.host];
if (kFixAlbums && replacement) {
NSURLComponents *components = [NSURLComponents componentsWithURL:originalURL resolvingAgainstBaseURL:NO];
components.host = replacement;
return components.URL;
}
return originalURL;
}
%hook ELMImageDownloader
- (id)downloadImageWithURL:(id)arg1 targetSize:(CGSize)arg2 callbackQueue:(id)arg3 downloadProgress:(id)arg4 completion:(id)arg5 {
return %orig(newCoverURL(arg1), arg2, arg3, arg4, arg5);
}
%end
// Not necessary but preferred
%hook ASBasicImageDownloader
- (id)downloadImageWithURL:(id)arg1 shouldRetry:(BOOL)arg2 callbackQueue:(id)arg3 downloadProgress:(id)arg4 completion:(id)arg5 {
return %orig(newCoverURL(arg1), arg2, arg3, arg4, arg5);
}
%end
static void reloadPrefs() {
NSString *path = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES).firstObject stringByAppendingPathComponent:@"YTLite.plist"];
NSDictionary *prefs = [NSDictionary dictionaryWithContentsOfFile:path];
@@ -759,6 +923,7 @@ static void reloadPrefs() {
kNoDarkBg = [prefs[@"noDarkBg"] boolValue] ?: NO;
kEndScreenCards = [prefs[@"endScreenCards"] boolValue] ?: NO;
kNoFullscreenActions = [prefs[@"noFullscreenActions"] boolValue] ?: NO;
kPersistentProgressBar = [prefs[@"persistentProgressBar"] boolValue] ?: NO;
kNoRelatedVids = [prefs[@"noRelatedVids"] boolValue] ?: NO;
kNoPromotionCards = [prefs[@"noPromotionCards"] boolValue] ?: NO;
kNoWatermarks = [prefs[@"noWatermarks"] boolValue] ?: NO;
@@ -766,6 +931,7 @@ static void reloadPrefs() {
kPortraitFullscreen = [prefs[@"portraitFullscreen"] boolValue] ?: NO;
kCopyWithTimestamp = [prefs[@"copyWithTimestamp"] boolValue] ?: NO;
kDisableAutoplay = [prefs[@"disableAutoplay"] boolValue] ?: NO;
kDisableAutoCaptions = [prefs[@"disableAutoCaptions"] boolValue] ?: NO;
kNoContentWarning = [prefs[@"noContentWarning"] boolValue] ?: NO;
kClassicQuality = [prefs[@"classicQuality"] boolValue] ?: NO;
kExtraSpeedOptions = [prefs[@"extraSpeedOptions"] boolValue] ?: NO;
@@ -776,8 +942,11 @@ static void reloadPrefs() {
kAutoFullscreen = [prefs[@"autoFullscreen"] boolValue] ?: NO;
kExitFullscreen = [prefs[@"exitFullscreen"] boolValue] ?: NO;
kNoDoubleTapToSeek = [prefs[@"noDoubleTapToSeek"] boolValue] ?: NO;
kShortsOnlyMode = [prefs[@"shortsOnlyMode"] boolValue] ?: NO;
kHideShorts = [prefs[@"hideShorts"] boolValue] ?: NO;
kShortsProgress = [prefs[@"shortsProgress"] boolValue] ?: NO;
kPinchToFullscreenShorts = [prefs[@"pinchToFullscreenShorts"] boolValue] ?: NO;
kShortsToRegular = [prefs[@"shortsToRegular"] boolValue] ?: NO;
kResumeShorts = [prefs[@"resumeShorts"] boolValue] ?: NO;
kHideShortsLogo = [prefs[@"hideShortsLogo"] boolValue] ?: NO;
kHideShortsSearch = [prefs[@"hideShortsSearch"] boolValue] ?: NO;
@@ -791,6 +960,7 @@ static void reloadPrefs() {
kHideShortsShare = [prefs[@"hideShortsShare"] boolValue] ?: NO;
kHideShortsAvatars = [prefs[@"hideShortsAvatars"] boolValue] ?: NO;
kHideShortsThanks = [prefs[@"hideShortsThanks"] boolValue] ?: NO;
kHideShortsSource = [prefs[@"hideShortsSource"] boolValue] ?: NO;
kHideShortsChannelName = [prefs[@"hideShortsChannelName"] boolValue] ?: NO;
kHideShortsDescription = [prefs[@"hideShortsDescription"] boolValue] ?: NO;
kHideShortsAudioTrack = [prefs[@"hideShortsAudioTrack"] boolValue] ?: NO;
@@ -798,10 +968,15 @@ static void reloadPrefs() {
kRemoveLabels = [prefs[@"removeLabels"] boolValue] ?: NO;
kRemoveIndicators = [prefs[@"removeIndicators"] boolValue] ?: NO;
kReExplore = [prefs[@"reExplore"] boolValue] ?: NO;
kAddExplore = [prefs[@"addExplore"] boolValue] ?: NO;
kRemoveShorts = [prefs[@"removeShorts"] boolValue] ?: NO;
kRemoveSubscriptions = [prefs[@"removeSubscriptions"] boolValue] ?: NO;
kRemoveUploads = (prefs[@"removeUploads"] != nil) ? [prefs[@"removeUploads"] boolValue] : YES;
kRemoveLibrary = [prefs[@"removeLibrary"] boolValue] ?: NO;
kCopyPostText = [prefs[@"copyPostText"] boolValue] ?: NO;
kSavePostImage = [prefs[@"savePostImage"] boolValue] ?: NO;
kSaveProfilePhoto = [prefs[@"savePostImage"] boolValue] ?: NO;
kFixAlbums = [prefs[@"fixAlbums"] boolValue] ?: NO;
kRemovePlayNext = [prefs[@"removePlayNext"] boolValue] ?: NO;
kNoContinueWatching = [prefs[@"noContinueWatching"] boolValue] ?: NO;
kNoSearchHistory = [prefs[@"noSearchHistory"] boolValue] ?: NO;
@@ -832,6 +1007,7 @@ static void reloadPrefs() {
@"noDarkBg" : @(kNoDarkBg),
@"endScreenCards" : @(kEndScreenCards),
@"noFullscreenActions" : @(kNoFullscreenActions),
@"persistentProgressBar" : @(kPersistentProgressBar),
@"noRelatedVids" : @(kNoRelatedVids),
@"noPromotionCards" : @(kNoPromotionCards),
@"noWatermarks" : @(kNoWatermarks),
@@ -839,6 +1015,7 @@ static void reloadPrefs() {
@"portraitFullscreen" : @(kPortraitFullscreen),
@"copyWithTimestamp" : @(kCopyWithTimestamp),
@"disableAutoplay" : @(kDisableAutoplay),
@"disableAutoCaptions" : @(kDisableAutoCaptions),
@"noContentWarning" : @(kNoContentWarning),
@"classicQuality" : @(kClassicQuality),
@"extraSpeedOptions" : @(kExtraSpeedOptions),
@@ -849,8 +1026,11 @@ static void reloadPrefs() {
@"autoFullscreen" : @(kAutoFullscreen),
@"exitFullscreen" : @(kExitFullscreen),
@"noDoubleTapToSeek" : @(kNoDoubleTapToSeek),
@"shortsOnlyMode" : @(kShortsOnlyMode),
@"hideShorts" : @(kHideShorts),
@"shortsProgress" : @(kShortsProgress),
@"pinchToFullscreenShorts" : @(kPinchToFullscreenShorts),
@"shortsToRegular" : @(kShortsToRegular),
@"resumeShorts" : @(kResumeShorts),
@"hideShortsLogo" : @(kHideShortsLogo),
@"hideShortsSearch" : @(kHideShortsSearch),
@@ -864,6 +1044,7 @@ static void reloadPrefs() {
@"hideShortsShare" : @(kHideShortsShare),
@"hideShortsAvatars" : @(kHideShortsAvatars),
@"hideShortsThanks" : @(kHideShortsThanks),
@"hideShortsSource" : @(kHideShortsSource),
@"hideShortsChannelName" : @(kHideShortsChannelName),
@"hideShortsDescription" : @(kHideShortsDescription),
@"hideShortsAudioTrack" : @(kHideShortsAudioTrack),
@@ -871,10 +1052,15 @@ static void reloadPrefs() {
@"removeLabels" : @(kRemoveLabels),
@"removeIndicators" : @(kRemoveIndicators),
@"reExplore" : @(kReExplore),
@"addExplore" : @(kAddExplore),
@"removeShorts" : @(kRemoveShorts),
@"removeSubscriptions" : @(kRemoveSubscriptions),
@"removeUploads" : @(kRemoveUploads),
@"removeLibrary" : @(kRemoveLibrary),
@"copyPostText" : @(kCopyPostText),
@"savePostImage" : @(kSavePostImage),
@"saveProfilePhoto" : @(kSaveProfilePhoto),
@"fixAlbums" : @(kFixAlbums),
@"removePlayNext" : @(kRemovePlayNext),
@"noContinueWatching" : @(kNoContinueWatching),
@"noSearchHistory" : @(kNoSearchHistory),
@@ -896,6 +1082,15 @@ static void prefsChanged(CFNotificationCenterRef center, void *observer, CFStrin
}
%ctor {
NSString *path = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES).firstObject stringByAppendingPathComponent:@"YTLite.plist"];
NSMutableDictionary *prefs = [NSMutableDictionary dictionaryWithContentsOfFile:path];
if ([prefs[@"shortsOnlyMode"] boolValue] && ([prefs[@"removeShorts"] boolValue] || [prefs[@"reExplore"] boolValue])) {
[prefs setObject:@NO forKey:@"removeShorts"];
[prefs setObject:@NO forKey:@"reExplore"];
[prefs writeToFile:path atomically:NO];
}
CFNotificationCenterAddObserver(CFNotificationCenterGetDarwinNotifyCenter(), NULL, (CFNotificationCallback)prefsChanged, CFSTR("com.dvntm.ytlite.prefschanged"), NULL, CFNotificationSuspensionBehaviorCoalesce);
reloadPrefs();
}