Added YTHidePlayerButtons 1.0.0

Replaces my old code with a completely new YouTube Tweak that does its job. Some code was based off of YouPiP, Credits to PoomSmart & NguyenASang
This commit is contained in:
aricloverEXTRA 2025-09-21 13:19:18 -05:00 committed by GitHub
parent 7c46e2db90
commit d2eae0b1a1
No known key found for this signature in database
GPG key ID: B5690EEEBB952194

View file

@ -115,145 +115,176 @@ static int getNotificationIconStyle() {
%end %end
%end %end
/* LEGACY VERSION ⚠️ // YTHidePlayerButtons 1.0.0 - made by @aricloverEXTRA
This method uses UIView accessibilityLabel to hide classic video player buttons. static NSDictionary<NSString *, NSString *> *HideToggleMap(void) {
It's less reliable for new YouTube UI and only works for connect/thanks/save/report. static NSDictionary<NSString *, NSString *> *map = nil;
Now fully commented out for the NEW VERSION. static dispatch_once_t onceToken;
*/ dispatch_once(&onceToken, ^{
/* map = @{
%hook _ASDisplayView // identifiers
- (void)layoutSubviews { @"id.video.share.button": @"hideShareButton_enabled",
%orig; @"id.video.remix.button": @"hideRemixButton_enabled",
BOOL hideConnectButton = IS_ENABLED(@"hideConnectButton_enabled"); @"clip_button.eml": @"hideClipButton_enabled",
BOOL hideThanksButton = IS_ENABLED(@"hideThanksButton_enabled"); @"id.ui.add_to.offline.button": @"hideDownloadButton_enabled",
BOOL hideSaveToPlaylistButton = IS_ENABLED(@"hideSaveToPlaylistButton_enabled"); @"id.ui.carousel_header": @"hideCommentSection_enabled",
BOOL hideReportButton = IS_ENABLED(@"hideReportButton_enabled"); // fallbacks
@"Share": @"hideShareButton_enabled",
for (UIView *subview in self.subviews) { @"Remix": @"hideRemixButton_enabled",
if ([subview.accessibilityLabel isEqualToString:@"connect account"]) { @"Clip": @"hideClipButton_enabled",
subview.hidden = hideConnectButton; @"Download": @"hideDownloadButton_enabled",
} else if ([subview.accessibilityLabel isEqualToString:@"Thanks"]) { @"Save to playlist": @"hideSaveToPlaylistButton_enabled",
subview.hidden = hideThanksButton; @"Report": @"hideReportButton_enabled",
} else if ([subview.accessibilityLabel isEqualToString:@"Save to playlist"]) { @"Thanks": @"hideThanksButton_enabled",
subview.hidden = hideSaveToPlaylistButton; @"connect account": @"hideConnectButton_enabled",
} else if ([subview.accessibilityLabel isEqualToString:@"Report"]) { @"Like": @"hideLikeButton_enabled",
subview.hidden = hideReportButton; @"Dislike": @"hideDislikeButton_enabled"
} };
} });
return map;
} }
%end static BOOL shouldHideForKey(NSString *key) {
*/ if (!key) return NO;
NSString *pref = HideToggleMap()[key];
// --- NEW VERSION --- if (!pref) return NO;
// This method traverses ASNodeController/ELMNodeController to find and hide modern action bar buttons. return IS_ENABLED(pref);
// It is more robust for YouTube v19+ and node-based UI.
// Now with crash prevention and robust checks.
static BOOL shouldHideButton(NSString *buttonKey) {
if ([buttonKey isEqualToString:@"id.video.share.button"]) return IS_ENABLED(@"hideShareButton_enabled");
if ([buttonKey isEqualToString:@"id.video.remix.button"]) return IS_ENABLED(@"hideRemixButton_enabled");
if ([buttonKey isEqualToString:@"Thanks"]) return IS_ENABLED(@"hideThanksButton_enabled");
if ([buttonKey isEqualToString:@"clip_button.eml"]) return IS_ENABLED(@"hideClipButton_enabled");
if ([buttonKey isEqualToString:@"id.ui.add_to.offline.button"]) return IS_ENABLED(@"hideDownloadButton_enabled");
if ([buttonKey isEqualToString:@"id.ui.carousel_header"]) return IS_ENABLED(@"hideCommentSection_enabled");
// Add more as needed
return NO;
} }
static void safeHideView(id view) {
static void safeSetHidden(id node, BOOL hidden) { if (!view) return;
if (!node) return; dispatch_async(dispatch_get_main_queue(), ^{
@try {
if ([node respondsToSelector:@selector(setHidden:)]) {
[node setHidden:hidden];
} else if ([node isKindOfClass:[ASDisplayNode class]]) {
((ASDisplayNode *)node).hidden = hidden;
}
} @catch (NSException *exception) {
NSLog(@"[HidePlayerButtons] Exception hiding node: %@", exception);
}
}
static BOOL findAndHideCell(ASNodeController *nodeController, NSArray<NSString *> *identifiers) {
if (!nodeController || ![nodeController respondsToSelector:@selector(children)]) return NO;
@try {
for (id child in [nodeController children]) {
// ELMNodeController
if ([child isKindOfClass:%c(ELMNodeController)]) {
NSArray <ELMComponent *> *elmChildren = [(ELMNodeController*)child children];
for (ELMComponent *elmChild in elmChildren) {
for (NSString *identifier in identifiers) {
if ([[elmChild description] containsString:identifier]) {
if (shouldHideButton(identifier)) {
safeSetHidden(elmChild, YES);
return YES;
}
}
}
}
}
// ASNodeController
if ([child isKindOfClass:%c(ASNodeController)]) {
ASDisplayNode *childNode = nil;
@try {
childNode = ((ASNodeController*)child).node;
} @catch (NSException *exception) {
NSLog(@"[HidePlayerButtons] Exception accessing child node: %@", exception);
continue;
}
if ([childNode respondsToSelector:@selector(yogaChildren)]) {
NSArray<id> *yogaChildren = childNode.yogaChildren;
for (ASDisplayNode *displayNode in yogaChildren) {
if ([displayNode respondsToSelector:@selector(accessibilityIdentifier)]) {
if (shouldHideButton(displayNode.accessibilityIdentifier)) {
safeSetHidden(displayNode, YES);
return YES;
}
}
if (findAndHideCell(child, identifiers)) {
return YES;
}
}
}
}
}
} @catch (NSException *exception) {
NSLog(@"[HidePlayerButtons] Exception traversing nodeController: %@", exception);
}
return NO;
}
%hook ASCollectionView
- (CGSize)sizeForElement:(ASCollectionElement * _Nullable)element {
// Only target the video action bar (player buttons bar)
if ([self.accessibilityIdentifier isEqualToString:@"id.video.scrollable_action_bar"]) {
ASCellNode *node = nil;
ASNodeController *nodeController = nil;
@try { @try {
node = [element node]; if ([view respondsToSelector:@selector(setHidden:)]) {
nodeController = [node controller]; [view setHidden:YES];
} @catch (NSException *exception) { return;
NSLog(@"[HidePlayerButtons] Exception accessing node/controller: %@", exception); }
return %orig; if ([view isKindOfClass:[UIView class]]) {
((UIView *)view).hidden = YES;
return;
}
} @catch (NSException *ex) {
NSLog(@"[HidePlayerButtons] safeHideView exception: %@", ex);
} }
NSArray<NSString *> *buttonIdentifiers = @[ });
@"id.video.share.button", }
@"id.video.remix.button", static BOOL inspectAndHideIfMatch(id view) {
@"Thanks", if (!view) return NO;
@"clip_button.eml", @try {
@"id.ui.add_to.offline.button", NSString *accId = nil;
@"id.ui.carousel_header" if ([view respondsToSelector:@selector(accessibilityIdentifier)]) {
// Add/expand with other button keys as needed @try { accId = [view accessibilityIdentifier]; } @catch (NSException *e) { accId = nil; }
]; if (accId && shouldHideForKey(accId)) {
// Traverse and hide safeHideView(view);
findAndHideCell(nodeController, buttonIdentifiers); return YES;
// Optionally, if you want to remove layout space entirely:
for (NSString *identifier in buttonIdentifiers) {
if (shouldHideButton(identifier) && findAndHideCell(nodeController, @[identifier])) {
return CGSizeZero;
} }
} }
NSString *accLabel = nil;
if ([view respondsToSelector:@selector(accessibilityLabel)]) {
@try { accLabel = [view accessibilityLabel]; } @catch (NSException *e) { accLabel = nil; }
if (accLabel && shouldHideForKey(accLabel)) {
safeHideView(view);
return YES;
}
}
NSString *desc = nil;
@try { desc = [[view description] copy]; } @catch (NSException *e) { desc = nil; }
if (desc) {
for (NSString *key in HideToggleMap().allKeys) {
if ([desc containsString:key] && shouldHideForKey(key)) {
safeHideView(view);
return YES;
}
}
}
} @catch (NSException *ex) {
NSLog(@"[HidePlayerButtons] inspectAndHideIfMatch exception: %@", ex);
} }
return %orig; return NO;
}
static void traverseAndHideViews(UIView *root) {
if (!root) return;
@try {
inspectAndHideIfMatch(root);
NSArray<UIView *> *subs = nil;
@try { subs = root.subviews; } @catch (NSException *e) { subs = nil; }
if (subs && subs.count) {
for (UIView *sv in subs) {
if ([sv isKindOfClass:[UIView class]]) {
traverseAndHideViews(sv);
}
}
}
} @catch (NSException *ex) {
NSLog(@"[HidePlayerButtons] traverseAndHideViews exception: %@", ex);
}
}
static void hideButtonsInActionBarIfNeeded(id collectionView) {
if (!collectionView) return;
@try {
// Ensure the collectionView has accessibilityIdentifier and we only operate on the action bar
NSString *accId = nil;
if ([collectionView respondsToSelector:@selector(accessibilityIdentifier)]) {
@try { accId = [collectionView accessibilityIdentifier]; } @catch (NSException *e) { accId = nil; }
}
if (!accId) return;
if (![accId isEqualToString:@"id.video.scrollable_action_bar"]) return;
NSArray *cells = nil;
if ([collectionView respondsToSelector:@selector(visibleCells)]) {
@try { cells = [collectionView visibleCells]; } @catch (NSException *e) { cells = nil; }
}
if (!cells || cells.count == 0) {
@try { cells = [collectionView subviews]; } @catch (NSException *e) { cells = nil; }
}
if (!cells || cells.count == 0) return;
for (id cell in cells) {
if ([cell isKindOfClass:[UIView class]]) {
traverseAndHideViews((UIView *)cell);
} else {
@try {
if ([cell respondsToSelector:@selector(view)]) {
id view = [cell performSelector:@selector(view)];
if ([view isKindOfClass:[UIView class]]) {
traverseAndHideViews((UIView *)view);
}
} else if ([cell respondsToSelector:@selector(node)]) {
NSString *desc = nil;
@try { desc = [cell description]; } @catch (NSException *e) { desc = nil; }
if (desc) {
// Not ideal to act on description, but we keep this non-destructive: only log for debugging
// Uncomment logging for debug builds if needed.
// NSLog(@"[HidePlayerButtons] Non-UIView cell description: %@", desc);
}
}
} @catch (NSException *ex) {
NSLog(@"[HidePlayerButtons] Exception handling non-UIView cell: %@", ex);
}
}
}
} @catch (NSException *exception) {
NSLog(@"[HidePlayerButtons] hideButtonsInActionBarIfNeeded exception: %@", exception);
}
}
%hook ASCollectionView
- (id)nodeForItemAtIndexPath:(NSIndexPath *)indexPath {
id node = %orig;
id weakSelf = (id)self;
dispatch_async(dispatch_get_main_queue(), ^{
@try {
hideButtonsInActionBarIfNeeded(weakSelf);
} @catch (NSException *e) {
NSLog(@"[HidePlayerButtons] async hide exception: %@", e);
}
});
return node;
}
- (void)nodesDidRelayout:(NSArray *)nodes {
%orig;
id weakSelf = (id)self;
dispatch_async(dispatch_get_main_queue(), ^{
@try {
hideButtonsInActionBarIfNeeded(weakSelf);
} @catch (NSException *e) {
NSLog(@"[HidePlayerButtons] relayout hide exception: %@", e);
}
});
} }
%end %end