@@ -115,145 +115,176 @@ static int getNotificationIconStyle() {
115115%end
116116%end
117117
118- /* LEGACY VERSION ⚠️
119- This method uses UIView accessibilityLabel to hide classic video player buttons.
120- It's less reliable for new YouTube UI and only works for connect/thanks/save/report.
121- Now fully commented out for the NEW VERSION.
122- */
123- /*
124- %hook _ASDisplayView
125- - (void)layoutSubviews {
126- %orig;
127- BOOL hideConnectButton = IS_ENABLED(@"hideConnectButton_enabled");
128- BOOL hideThanksButton = IS_ENABLED(@"hideThanksButton_enabled");
129- BOOL hideSaveToPlaylistButton = IS_ENABLED(@"hideSaveToPlaylistButton_enabled");
130- BOOL hideReportButton = IS_ENABLED(@"hideReportButton_enabled");
131-
132- for (UIView *subview in self.subviews) {
133- if ([subview.accessibilityLabel isEqualToString:@"connect account"]) {
134- subview.hidden = hideConnectButton;
135- } else if ([subview.accessibilityLabel isEqualToString:@"Thanks"]) {
136- subview.hidden = hideThanksButton;
137- } else if ([subview.accessibilityLabel isEqualToString:@"Save to playlist"]) {
138- subview.hidden = hideSaveToPlaylistButton;
139- } else if ([subview.accessibilityLabel isEqualToString:@"Report"]) {
140- subview.hidden = hideReportButton;
141- }
142- }
118+ // YTHidePlayerButtons 1.0.0 - made by @aricloverEXTRA
119+ static NSDictionary <NSString *, NSString *> *HideToggleMap (void ) {
120+ static NSDictionary <NSString *, NSString *> *map = nil ;
121+ static dispatch_once_t onceToken;
122+ dispatch_once (&onceToken, ^{
123+ map = @{
124+ // identifiers
125+ @" id.video.share.button" : @" hideShareButton_enabled" ,
126+ @" id.video.remix.button" : @" hideRemixButton_enabled" ,
127+ @" clip_button.eml" : @" hideClipButton_enabled" ,
128+ @" id.ui.add_to.offline.button" : @" hideDownloadButton_enabled" ,
129+ @" id.ui.carousel_header" : @" hideCommentSection_enabled" ,
130+ // fallbacks
131+ @" Share" : @" hideShareButton_enabled" ,
132+ @" Remix" : @" hideRemixButton_enabled" ,
133+ @" Clip" : @" hideClipButton_enabled" ,
134+ @" Download" : @" hideDownloadButton_enabled" ,
135+ @" Save to playlist" : @" hideSaveToPlaylistButton_enabled" ,
136+ @" Report" : @" hideReportButton_enabled" ,
137+ @" Thanks" : @" hideThanksButton_enabled" ,
138+ @" connect account" : @" hideConnectButton_enabled" ,
139+ @" Like" : @" hideLikeButton_enabled" ,
140+ @" Dislike" : @" hideDislikeButton_enabled"
141+ };
142+ });
143+ return map;
143144}
144- %end
145- */
146-
147- // --- NEW VERSION ---
148- // This method traverses ASNodeController/ELMNodeController to find and hide modern action bar buttons.
149- // It is more robust for YouTube v19+ and node-based UI.
150- // Now with crash prevention and robust checks.
151-
152- static BOOL shouldHideButton (NSString *buttonKey) {
153- if ([buttonKey isEqualToString: @" id.video.share.button" ]) return IS_ENABLED (@" hideShareButton_enabled" );
154- if ([buttonKey isEqualToString: @" id.video.remix.button" ]) return IS_ENABLED (@" hideRemixButton_enabled" );
155- if ([buttonKey isEqualToString: @" Thanks" ]) return IS_ENABLED (@" hideThanksButton_enabled" );
156- if ([buttonKey isEqualToString: @" clip_button.eml" ]) return IS_ENABLED (@" hideClipButton_enabled" );
157- if ([buttonKey isEqualToString: @" id.ui.add_to.offline.button" ]) return IS_ENABLED (@" hideDownloadButton_enabled" );
158- if ([buttonKey isEqualToString: @" id.ui.carousel_header" ]) return IS_ENABLED (@" hideCommentSection_enabled" );
159- // Add more as needed
160- return NO ;
145+ static BOOL shouldHideForKey (NSString *key) {
146+ if (!key) return NO ;
147+ NSString *pref = HideToggleMap ()[key];
148+ if (!pref) return NO ;
149+ return IS_ENABLED (pref);
161150}
162-
163- static void safeSetHidden (id node, BOOL hidden) {
164- if (!node) return ;
151+ static void safeHideView (id view) {
152+ if (!view) return ;
153+ dispatch_async (dispatch_get_main_queue (), ^{
154+ @try {
155+ if ([view respondsToSelector: @selector (setHidden: )]) {
156+ [view setHidden: YES ];
157+ return ;
158+ }
159+ if ([view isKindOfClass: [UIView class ]]) {
160+ ((UIView *)view).hidden = YES ;
161+ return ;
162+ }
163+ } @catch (NSException *ex) {
164+ NSLog (@" [HidePlayerButtons] safeHideView exception: %@ " , ex);
165+ }
166+ });
167+ }
168+ static BOOL inspectAndHideIfMatch (id view) {
169+ if (!view) return NO ;
165170 @try {
166- if ([node respondsToSelector: @selector (setHidden: )]) {
167- [node setHidden: hidden];
168- } else if ([node isKindOfClass: [ASDisplayNode class ]]) {
169- ((ASDisplayNode *)node).hidden = hidden;
171+ NSString *accId = nil ;
172+ if ([view respondsToSelector: @selector (accessibilityIdentifier )]) {
173+ @try { accId = [view accessibilityIdentifier ]; } @catch (NSException *e) { accId = nil ; }
174+ if (accId && shouldHideForKey (accId)) {
175+ safeHideView (view);
176+ return YES ;
177+ }
170178 }
171- } @catch (NSException *exception) {
172- NSLog (@" [HidePlayerButtons] Exception hiding node: %@ " , exception);
179+ NSString *accLabel = nil ;
180+ if ([view respondsToSelector: @selector (accessibilityLabel )]) {
181+ @try { accLabel = [view accessibilityLabel ]; } @catch (NSException *e) { accLabel = nil ; }
182+ if (accLabel && shouldHideForKey (accLabel)) {
183+ safeHideView (view);
184+ return YES ;
185+ }
186+ }
187+ NSString *desc = nil ;
188+ @try { desc = [[view description ] copy ]; } @catch (NSException *e) { desc = nil ; }
189+ if (desc) {
190+ for (NSString *key in HideToggleMap ().allKeys ) {
191+ if ([desc containsString: key] && shouldHideForKey (key)) {
192+ safeHideView (view);
193+ return YES ;
194+ }
195+ }
196+ }
197+ } @catch (NSException *ex) {
198+ NSLog (@" [HidePlayerButtons] inspectAndHideIfMatch exception: %@ " , ex);
173199 }
200+ return NO ;
174201}
175-
176- static BOOL findAndHideCell (ASNodeController *nodeController, NSArray <NSString *> *identifiers) {
177- if (!nodeController || ![nodeController respondsToSelector: @selector (children )]) return NO ;
202+ static void traverseAndHideViews (UIView *root) {
203+ if (!root) return ;
178204 @try {
179- for (id child in [nodeController children ]) {
180- // ELMNodeController
181- if ([child isKindOfClass: %c (ELMNodeController)]) {
182- NSArray <ELMComponent *> *elmChildren = [(ELMNodeController*)child children ];
183- for (ELMComponent *elmChild in elmChildren) {
184- for (NSString *identifier in identifiers) {
185- if ([[elmChild description ] containsString: identifier]) {
186- if (shouldHideButton (identifier)) {
187- safeSetHidden (elmChild, YES );
188- return YES ;
189- }
190- }
191- }
205+ inspectAndHideIfMatch (root);
206+ NSArray <UIView *> *subs = nil ;
207+ @try { subs = root.subviews ; } @catch (NSException *e) { subs = nil ; }
208+ if (subs && subs.count ) {
209+ for (UIView *sv in subs) {
210+ if ([sv isKindOfClass: [UIView class ]]) {
211+ traverseAndHideViews (sv);
192212 }
193213 }
194- // ASNodeController
195- if ([child isKindOfClass: %c (ASNodeController)]) {
196- ASDisplayNode *childNode = nil ;
214+ }
215+ } @catch (NSException *ex) {
216+ NSLog (@" [HidePlayerButtons] traverseAndHideViews exception: %@ " , ex);
217+ }
218+ }
219+ static void hideButtonsInActionBarIfNeeded (id collectionView) {
220+ if (!collectionView) return ;
221+ @try {
222+ // Ensure the collectionView has accessibilityIdentifier and we only operate on the action bar
223+ NSString *accId = nil ;
224+ if ([collectionView respondsToSelector: @selector (accessibilityIdentifier )]) {
225+ @try { accId = [collectionView accessibilityIdentifier ]; } @catch (NSException *e) { accId = nil ; }
226+ }
227+ if (!accId) return ;
228+ if (![accId isEqualToString: @" id.video.scrollable_action_bar" ]) return ;
229+ NSArray *cells = nil ;
230+ if ([collectionView respondsToSelector: @selector (visibleCells )]) {
231+ @try { cells = [collectionView visibleCells ]; } @catch (NSException *e) { cells = nil ; }
232+ }
233+ if (!cells || cells.count == 0 ) {
234+ @try { cells = [collectionView subviews ]; } @catch (NSException *e) { cells = nil ; }
235+ }
236+ if (!cells || cells.count == 0 ) return ;
237+ for (id cell in cells) {
238+ if ([cell isKindOfClass: [UIView class ]]) {
239+ traverseAndHideViews ((UIView *)cell);
240+ } else {
197241 @try {
198- childNode = ((ASNodeController*)child).node ;
199- } @catch (NSException *exception) {
200- NSLog (@" [HidePlayerButtons] Exception accessing child node: %@ " , exception);
201- continue ;
202- }
203- if ([childNode respondsToSelector: @selector (yogaChildren )]) {
204- NSArray <id > *yogaChildren = childNode.yogaChildren ;
205- for (ASDisplayNode *displayNode in yogaChildren) {
206- if ([displayNode respondsToSelector: @selector (accessibilityIdentifier )]) {
207- if (shouldHideButton (displayNode.accessibilityIdentifier )) {
208- safeSetHidden (displayNode, YES );
209- return YES ;
210- }
242+ if ([cell respondsToSelector: @selector (view )]) {
243+ id view = [cell performSelector: @selector (view )];
244+ if ([view isKindOfClass: [UIView class ]]) {
245+ traverseAndHideViews ((UIView *)view);
211246 }
212- if (findAndHideCell (child, identifiers)) {
213- return YES ;
247+ } else if ([cell respondsToSelector: @selector (node )]) {
248+ NSString *desc = nil ;
249+ @try { desc = [cell description ]; } @catch (NSException *e) { desc = nil ; }
250+ if (desc) {
251+ // Not ideal to act on description, but we keep this non-destructive: only log for debugging
252+ // Uncomment logging for debug builds if needed.
253+ // NSLog(@"[HidePlayerButtons] Non-UIView cell description: %@", desc);
214254 }
215255 }
256+ } @catch (NSException *ex) {
257+ NSLog (@" [HidePlayerButtons] Exception handling non-UIView cell: %@ " , ex);
216258 }
217259 }
218260 }
219261 } @catch (NSException *exception) {
220- NSLog (@" [HidePlayerButtons] Exception traversing nodeController : %@ " , exception);
262+ NSLog (@" [HidePlayerButtons] hideButtonsInActionBarIfNeeded exception : %@ " , exception);
221263 }
222- return NO ;
223264}
224-
225265%hook ASCollectionView
226- - (CGSize)sizeForElement:(ASCollectionElement * _Nullable)element {
227- // Only target the video action bar (player buttons bar)
228- if ([self .accessibilityIdentifier isEqualToString: @" id.video.scrollable_action_bar" ]) {
229- ASCellNode *node = nil ;
230- ASNodeController *nodeController = nil ;
266+ - (id )nodeForItemAtIndexPath:(NSIndexPath *)indexPath {
267+ id node = %orig ;
268+ id weakSelf = (id )self;
269+ dispatch_async (dispatch_get_main_queue (), ^{
231270 @try {
232- node = [element node ];
233- nodeController = [node controller ];
234- } @catch (NSException *exception) {
235- NSLog (@" [HidePlayerButtons] Exception accessing node/controller: %@ " , exception);
236- return %orig ;
271+ hideButtonsInActionBarIfNeeded (weakSelf);
272+ } @catch (NSException *e) {
273+ NSLog (@" [HidePlayerButtons] async hide exception: %@ " , e);
237274 }
238- NSArray <NSString *> *buttonIdentifiers = @[
239- @" id.video.share.button" ,
240- @" id.video.remix.button" ,
241- @" Thanks" ,
242- @" clip_button.eml" ,
243- @" id.ui.add_to.offline.button" ,
244- @" id.ui.carousel_header"
245- // Add/expand with other button keys as needed
246- ];
247- // Traverse and hide
248- findAndHideCell (nodeController, buttonIdentifiers);
249- // Optionally, if you want to remove layout space entirely:
250- for (NSString *identifier in buttonIdentifiers) {
251- if (shouldHideButton (identifier) && findAndHideCell (nodeController, @[identifier])) {
252- return CGSizeZero;
253- }
275+ });
276+ return node;
277+ }
278+ - (void )nodesDidRelayout:(NSArray *)nodes {
279+ %orig ;
280+ id weakSelf = (id )self;
281+ dispatch_async (dispatch_get_main_queue (), ^{
282+ @try {
283+ hideButtonsInActionBarIfNeeded (weakSelf);
284+ } @catch (NSException *e) {
285+ NSLog (@" [HidePlayerButtons] relayout hide exception: %@ " , e);
254286 }
255- }
256- return %orig ;
287+ });
257288}
258289%end
259290
0 commit comments