mirror of
https://github.com/SoPat712/YTLitePlus.git
synced 2025-08-22 02:58:45 -04:00
added files via upload
This commit is contained in:
2
Tweaks/YouMute/.gitattributes
vendored
Normal file
2
Tweaks/YouMute/.gitattributes
vendored
Normal file
@@ -0,0 +1,2 @@
|
||||
# Auto detect text files and perform LF normalization
|
||||
* text=auto
|
3
Tweaks/YouMute/.gitignore
vendored
Normal file
3
Tweaks/YouMute/.gitignore
vendored
Normal file
@@ -0,0 +1,3 @@
|
||||
.theos
|
||||
.DS_Store
|
||||
packages
|
21
Tweaks/YouMute/LICENSE
Normal file
21
Tweaks/YouMute/LICENSE
Normal file
@@ -0,0 +1,21 @@
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2022 - 2023 PoomSmart
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
14
Tweaks/YouMute/Makefile
Normal file
14
Tweaks/YouMute/Makefile
Normal file
@@ -0,0 +1,14 @@
|
||||
TARGET := iphone:clang:latest:11.0
|
||||
PACKAGE_VERSION = 1.1.1-1
|
||||
INSTALL_TARGET_PROCESSES = YouTube
|
||||
ARCHS = arm64
|
||||
|
||||
include $(THEOS)/makefiles/common.mk
|
||||
|
||||
TWEAK_NAME = YouMute
|
||||
|
||||
$(TWEAK_NAME)_FILES = Tweak.x
|
||||
$(TWEAK_NAME)_CFLAGS = -fobjc-arc
|
||||
$(TWEAK_NAME)_FRAMEWORKS = UIKit
|
||||
|
||||
include $(THEOS_MAKE_PATH)/tweak.mk
|
5
Tweaks/YouMute/README.md
Normal file
5
Tweaks/YouMute/README.md
Normal file
@@ -0,0 +1,5 @@
|
||||
# YouMute
|
||||
Mute/unmute videos in YouTube directly.
|
||||
|
||||
## Supported YouTube versions
|
||||
Version 16.42.3 and newer. Lower are either untested or unsupported.
|
320
Tweaks/YouMute/Tweak.x
Normal file
320
Tweaks/YouMute/Tweak.x
Normal file
@@ -0,0 +1,320 @@
|
||||
#import <rootless.h>
|
||||
#import "../YouTubeHeader/YTColor.h"
|
||||
#import "../YouTubeHeader/YTCommonUtils.h"
|
||||
#import "../YouTubeHeader/YTInlinePlayerBarContainerView.h"
|
||||
#import "../YouTubeHeader/YTMainAppControlsOverlayView.h"
|
||||
#import "../YouTubeHeader/YTMainAppVideoPlayerOverlayViewController.h"
|
||||
#import "../YouTubeHeader/YTSingleVideoController.h"
|
||||
#import "../YouTubeHeader/YTSettingsPickerViewController.h"
|
||||
#import "../YouTubeHeader/YTSettingsViewController.h"
|
||||
#import "../YouTubeHeader/YTSettingsSectionItem.h"
|
||||
#import "../YouTubeHeader/YTSettingsSectionItemManager.h"
|
||||
#import "../YouTubeHeader/YTQTMButton.h"
|
||||
#import "../YouTubeHeader/QTMIcon.h"
|
||||
#import "../YouTubeHeader/UIView+YouTube.h"
|
||||
|
||||
#define EnabledKey @"YouMuteEnabled"
|
||||
#define PositionKey @"YouMutePosition"
|
||||
|
||||
#define LOC(x) [tweakBundle localizedStringForKey:x value:nil table:nil]
|
||||
|
||||
static const NSInteger YouMuteSection = 521;
|
||||
|
||||
@interface YTSettingsSectionItemManager (YouMute)
|
||||
- (void)updateYouMuteSectionWithEntry:(id)entry;
|
||||
@end
|
||||
|
||||
@interface YTMainAppControlsOverlayView (YouMute)
|
||||
@property (retain, nonatomic) YTQTMButton *muteButton;
|
||||
- (void)didPressMute:(id)arg;
|
||||
@end
|
||||
|
||||
@interface YTInlinePlayerBarContainerView (YouMute)
|
||||
@property (retain, nonatomic) YTQTMButton *muteButton;
|
||||
- (void)didPressMute:(id)arg;
|
||||
@end
|
||||
|
||||
static BOOL TweakEnabled() {
|
||||
return [[NSUserDefaults standardUserDefaults] boolForKey:EnabledKey];
|
||||
}
|
||||
|
||||
static int MuteButtonPosition() {
|
||||
return [[NSUserDefaults standardUserDefaults] integerForKey:PositionKey];
|
||||
}
|
||||
|
||||
static BOOL UseTopMuteButton() {
|
||||
return TweakEnabled() && MuteButtonPosition() == 0;
|
||||
}
|
||||
|
||||
static BOOL UseBottomMuteButton() {
|
||||
return TweakEnabled() && MuteButtonPosition() == 1;
|
||||
}
|
||||
|
||||
static BOOL isMutedTop(YTMainAppControlsOverlayView *self) {
|
||||
YTMainAppVideoPlayerOverlayViewController *c = [self valueForKey:@"_eventsDelegate"];
|
||||
YTSingleVideoController *video = [c valueForKey:@"_currentSingleVideoObservable"];
|
||||
return [video isMuted];
|
||||
}
|
||||
|
||||
static BOOL isMutedBottom(YTInlinePlayerBarContainerView *self) {
|
||||
YTSingleVideoController *video = [self.delegate valueForKey:@"_currentSingleVideo"];
|
||||
return [video isMuted];
|
||||
}
|
||||
|
||||
static NSBundle *YTEditResourcesBundle() {
|
||||
Class YTCommonUtilsClass = %c(YTCommonUtils);
|
||||
return [YTCommonUtilsClass resourceBundleForModuleName:@"Edit" appBundle:[YTCommonUtilsClass bundleForClass:%c(YTEditBundleIdentifier)]];
|
||||
}
|
||||
|
||||
static UIImage *muteImage(BOOL muted) {
|
||||
return [%c(QTMIcon) tintImage:[UIImage imageNamed:muted ? @"ic_volume_off" : @"ic_volume_up" inBundle:YTEditResourcesBundle() compatibleWithTraitCollection:nil] color:[%c(YTColor) white1]];
|
||||
}
|
||||
|
||||
static void createMuteButtonTop(YTMainAppControlsOverlayView *self) {
|
||||
if (!self) return;
|
||||
CGFloat padding = [[self class] topButtonAdditionalPadding];
|
||||
UIImage *image = muteImage(isMutedTop(self));
|
||||
self.muteButton = [self buttonWithImage:image accessibilityLabel:@"Mute" verticalContentPadding:padding];
|
||||
self.muteButton.hidden = YES;
|
||||
self.muteButton.alpha = 0;
|
||||
[self.muteButton addTarget:self action:@selector(didPressMute:) forControlEvents:UIControlEventTouchUpInside];
|
||||
@try {
|
||||
[[self valueForKey:@"_topControlsAccessibilityContainerView"] addSubview:self.muteButton];
|
||||
} @catch (id ex) {
|
||||
[self addSubview:self.muteButton];
|
||||
}
|
||||
}
|
||||
|
||||
static void createMuteButtonBottom(YTInlinePlayerBarContainerView *self) {
|
||||
if (!self) return;
|
||||
UIImage *image = muteImage(isMutedBottom(self));
|
||||
self.muteButton = [%c(YTQTMButton) iconButton];
|
||||
self.muteButton.hidden = YES;
|
||||
self.muteButton.exclusiveTouch = YES;
|
||||
self.muteButton.alpha = 0;
|
||||
self.muteButton.minHitTargetSize = 60;
|
||||
self.muteButton.accessibilityLabel = @"Mute";
|
||||
[self.muteButton setImage:image forState:0];
|
||||
[self.muteButton sizeToFit];
|
||||
[self.muteButton addTarget:self action:@selector(didPressMute:) forControlEvents:UIControlEventTouchUpInside];
|
||||
[self addSubview:self.muteButton];
|
||||
}
|
||||
|
||||
static NSMutableArray *topControls(YTMainAppControlsOverlayView *self, NSMutableArray *controls) {
|
||||
if (UseTopMuteButton())
|
||||
[controls insertObject:self.muteButton atIndex:0];
|
||||
return controls;
|
||||
}
|
||||
|
||||
NSBundle *YouMuteBundle() {
|
||||
static NSBundle *bundle = nil;
|
||||
static dispatch_once_t onceToken;
|
||||
dispatch_once(&onceToken, ^{
|
||||
NSString *tweakBundlePath = [[NSBundle mainBundle] pathForResource:@"YouMute" ofType:@"bundle"];
|
||||
if (tweakBundlePath)
|
||||
bundle = [NSBundle bundleWithPath:tweakBundlePath];
|
||||
else
|
||||
bundle = [NSBundle bundleWithPath:ROOT_PATH_NS(@"/Library/Application Support/YouMute.bundle")];
|
||||
});
|
||||
return bundle;
|
||||
}
|
||||
|
||||
%group Top
|
||||
|
||||
%hook YTMainAppVideoPlayerOverlayViewController
|
||||
|
||||
- (void)updateTopRightButtonAvailability {
|
||||
%orig;
|
||||
YTMainAppVideoPlayerOverlayView *v = [self videoPlayerOverlayView];
|
||||
YTMainAppControlsOverlayView *c = [v valueForKey:@"_controlsOverlayView"];
|
||||
c.muteButton.hidden = !UseTopMuteButton();
|
||||
[c setNeedsLayout];
|
||||
}
|
||||
|
||||
%end
|
||||
|
||||
%hook YTMainAppControlsOverlayView
|
||||
|
||||
%property (retain, nonatomic) YTQTMButton *muteButton;
|
||||
|
||||
- (id)initWithDelegate:(id)delegate {
|
||||
self = %orig;
|
||||
createMuteButtonTop(self);
|
||||
return self;
|
||||
}
|
||||
|
||||
- (id)initWithDelegate:(id)delegate autoplaySwitchEnabled:(BOOL)autoplaySwitchEnabled {
|
||||
self = %orig;
|
||||
createMuteButtonTop(self);
|
||||
return self;
|
||||
}
|
||||
|
||||
- (NSMutableArray *)topButtonControls {
|
||||
return topControls(self, %orig);
|
||||
}
|
||||
|
||||
- (NSMutableArray *)topControls {
|
||||
return topControls(self, %orig);
|
||||
}
|
||||
|
||||
- (void)setTopOverlayVisible:(BOOL)visible isAutonavCanceledState:(BOOL)canceledState {
|
||||
if (UseTopMuteButton())
|
||||
self.muteButton.alpha = canceledState || !visible ? 0.0 : 1.0;
|
||||
%orig;
|
||||
}
|
||||
|
||||
%new(v@:@)
|
||||
- (void)didPressMute:(id)arg {
|
||||
YTMainAppVideoPlayerOverlayViewController *c = [self valueForKey:@"_eventsDelegate"];
|
||||
YTSingleVideoController *video = [c valueForKey:@"_currentSingleVideoObservable"];
|
||||
[video setMuted:![video isMuted]];
|
||||
[self.muteButton setImage:muteImage([video isMuted]) forState:0];
|
||||
}
|
||||
|
||||
%end
|
||||
|
||||
%end
|
||||
|
||||
%group Bottom
|
||||
|
||||
%hook YTInlinePlayerBarContainerView
|
||||
|
||||
%property (retain, nonatomic) YTQTMButton *muteButton;
|
||||
|
||||
- (id)init {
|
||||
self = %orig;
|
||||
createMuteButtonBottom(self);
|
||||
return self;
|
||||
}
|
||||
|
||||
- (NSMutableArray *)rightIcons {
|
||||
NSMutableArray *icons = %orig;
|
||||
if (UseBottomMuteButton() && ![icons containsObject:self.muteButton])
|
||||
[icons insertObject:self.muteButton atIndex:0];
|
||||
return icons;
|
||||
}
|
||||
|
||||
- (void)updateIconVisibility {
|
||||
%orig;
|
||||
if (UseBottomMuteButton())
|
||||
self.muteButton.hidden = NO;
|
||||
}
|
||||
|
||||
- (void)hideScrubber {
|
||||
%orig;
|
||||
if (UseBottomMuteButton())
|
||||
self.muteButton.alpha = 0;
|
||||
}
|
||||
|
||||
- (void)setPeekableViewVisible:(BOOL)visible fullscreenButtonVisibleShouldMatchPeekableView:(BOOL)match {
|
||||
%orig;
|
||||
if (UseBottomMuteButton())
|
||||
self.muteButton.alpha = visible ? 1 : 0;
|
||||
}
|
||||
|
||||
- (void)layoutSubviews {
|
||||
%orig;
|
||||
if (!UseBottomMuteButton()) return;
|
||||
CGFloat multiFeedWidth = [self respondsToSelector:@selector(multiFeedElementView)] ? [self multiFeedElementView].frame.size.width : 0;
|
||||
YTQTMButton *enter = [self enterFullscreenButton];
|
||||
if ([enter yt_isVisible]) {
|
||||
CGRect frame = enter.frame;
|
||||
frame.origin.x -= multiFeedWidth + enter.frame.size.width + 16;
|
||||
self.muteButton.frame = frame;
|
||||
} else {
|
||||
YTQTMButton *exit = [self exitFullscreenButton];
|
||||
if ([exit yt_isVisible]) {
|
||||
CGRect frame = exit.frame;
|
||||
frame.origin.x -= multiFeedWidth + exit.frame.size.width + 16;
|
||||
self.muteButton.frame = frame;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
%new(v@:@)
|
||||
- (void)didPressMute:(id)arg {
|
||||
YTSingleVideoController *video = [self.delegate valueForKey:@"_currentSingleVideo"];
|
||||
[video setMuted:![video isMuted]];
|
||||
[self.muteButton setImage:muteImage([video isMuted]) forState:0];
|
||||
}
|
||||
|
||||
%end
|
||||
|
||||
%end
|
||||
|
||||
%group Settings
|
||||
|
||||
%hook YTAppSettingsPresentationData
|
||||
|
||||
+ (NSArray *)settingsCategoryOrder {
|
||||
NSArray *order = %orig;
|
||||
NSMutableArray *mutableOrder = [order mutableCopy];
|
||||
NSUInteger insertIndex = [order indexOfObject:@(1)];
|
||||
if (insertIndex != NSNotFound)
|
||||
[mutableOrder insertObject:@(YouMuteSection) atIndex:insertIndex + 1];
|
||||
return mutableOrder;
|
||||
}
|
||||
|
||||
%end
|
||||
|
||||
%hook YTSettingsSectionItemManager
|
||||
|
||||
%new(v@:@)
|
||||
- (void)updateYouMuteSectionWithEntry:(id)entry {
|
||||
NSMutableArray *sectionItems = [NSMutableArray array];
|
||||
NSBundle *tweakBundle = YouMuteBundle();
|
||||
Class YTSettingsSectionItemClass = %c(YTSettingsSectionItem);
|
||||
YTSettingsViewController *settingsViewController = [self valueForKey:@"_settingsViewControllerDelegate"];
|
||||
YTSettingsSectionItem *master = [YTSettingsSectionItemClass switchItemWithTitle:LOC(@"ENABLED")
|
||||
titleDescription:nil
|
||||
accessibilityIdentifier:nil
|
||||
switchOn:TweakEnabled()
|
||||
switchBlock:^BOOL (YTSettingsCell *cell, BOOL enabled) {
|
||||
[[NSUserDefaults standardUserDefaults] setBool:enabled forKey:EnabledKey];
|
||||
return YES;
|
||||
}
|
||||
settingItemId:0];
|
||||
[sectionItems addObject:master];
|
||||
YTSettingsSectionItem *position = [YTSettingsSectionItemClass itemWithTitle:LOC(@"POSITION")
|
||||
accessibilityIdentifier:nil
|
||||
detailTextBlock:^NSString *() {
|
||||
return MuteButtonPosition() ? LOC(@"BOTTOM") : LOC(@"TOP");
|
||||
}
|
||||
selectBlock:^BOOL (YTSettingsCell *cell, NSUInteger arg1) {
|
||||
NSArray <YTSettingsSectionItem *> *rows = @[
|
||||
[YTSettingsSectionItemClass checkmarkItemWithTitle:LOC(@"TOP") titleDescription:LOC(@"TOP_DESC") selectBlock:^BOOL (YTSettingsCell *top, NSUInteger arg1) {
|
||||
[[NSUserDefaults standardUserDefaults] setInteger:0 forKey:PositionKey];
|
||||
[settingsViewController reloadData];
|
||||
return YES;
|
||||
}],
|
||||
[YTSettingsSectionItemClass checkmarkItemWithTitle:LOC(@"BOTTOM") titleDescription:LOC(@"BOTTOM_DESC") selectBlock:^BOOL (YTSettingsCell *bottom, NSUInteger arg1) {
|
||||
[[NSUserDefaults standardUserDefaults] setInteger:1 forKey:PositionKey];
|
||||
[settingsViewController reloadData];
|
||||
return YES;
|
||||
}]
|
||||
];
|
||||
YTSettingsPickerViewController *picker = [[%c(YTSettingsPickerViewController) alloc] initWithNavTitle:LOC(@"POSITION") pickerSectionTitle:nil rows:rows selectedItemIndex:MuteButtonPosition() parentResponder:[self parentResponder]];
|
||||
[settingsViewController pushViewController:picker];
|
||||
return YES;
|
||||
}];
|
||||
[sectionItems addObject:position];
|
||||
[settingsViewController setSectionItems:sectionItems forCategory:YouMuteSection title:@"YouMute" titleDescription:nil headerHidden:NO];
|
||||
}
|
||||
|
||||
- (void)updateSectionForCategory:(NSUInteger)category withEntry:(id)entry {
|
||||
if (category == YouMuteSection) {
|
||||
[self updateYouMuteSectionWithEntry:entry];
|
||||
return;
|
||||
}
|
||||
%orig;
|
||||
}
|
||||
|
||||
%end
|
||||
|
||||
%end
|
||||
|
||||
%ctor {
|
||||
%init(Settings);
|
||||
%init(Top);
|
||||
%init(Bottom);
|
||||
}
|
1
Tweaks/YouMute/YouMute.plist
Normal file
1
Tweaks/YouMute/YouMute.plist
Normal file
@@ -0,0 +1 @@
|
||||
{ Filter = { Bundles = ( "com.google.ios.youtube" ); }; }
|
11
Tweaks/YouMute/control
Normal file
11
Tweaks/YouMute/control
Normal file
@@ -0,0 +1,11 @@
|
||||
Package: com.ps.youmute
|
||||
Name: YouMute
|
||||
Version: 1.0.0
|
||||
Architecture: iphoneos-arm
|
||||
Description: Mute/unmute videos in YouTube directly.
|
||||
Maintainer: PoomSmart
|
||||
Author: PoomSmart
|
||||
Section: Tweaks
|
||||
Depends: mobilesubstrate (>= 0.9.5000), firmware (>= 11.0)
|
||||
Depiction: https://poomsmart.github.io/repo/depictions/youmute.html
|
||||
SileoDepiction: https://poomsmart.github.io/repo/sileodepictions/youmute.json
|
Binary file not shown.
@@ -0,0 +1,8 @@
|
||||
// Settings
|
||||
|
||||
"ENABLED" = "إظهار زر كتم الصوت";
|
||||
"POSITION" = "مكان زر كتم الصوت";
|
||||
"TOP" = "الأعلى";
|
||||
"TOP_DESC" = "في الجزء العلوي الأيمن، حيث يوجد زر التشغيل التلقائي وزر البث وغيرها.";
|
||||
"BOTTOM" = "الأسفل";
|
||||
"BOTTOM_DESC" = "في الجزء السفلي الأيمن، حيث يوجد زر ملء الشاشة.";
|
@@ -0,0 +1,8 @@
|
||||
// Settings
|
||||
|
||||
"ENABLED" = "Show mute button";
|
||||
"POSITION" = "Mute button position";
|
||||
"TOP" = "Top";
|
||||
"TOP_DESC" = "At the top-right, where autoplay, captions, cast and overflow menu are.";
|
||||
"BOTTOM" = "Bottom";
|
||||
"BOTTOM_DESC" = "At the bottom-right, where fullscreen button is.";
|
@@ -0,0 +1,8 @@
|
||||
// Settings
|
||||
|
||||
"ENABLED" = "Mostrar botón de silencio";
|
||||
"POSITION" = "Posición del botón Silencio";
|
||||
"TOP" = "Arriba";
|
||||
"TOP_DESC" = "En la parte superior derecha, donde están la reproducción automática, los subtítulos, el reparto y el menú de desbordamiento.";
|
||||
"BOTTOM" = "Abajo";
|
||||
"BOTTOM_DESC" = "En la parte inferior derecha, donde está el botón de pantalla completa.";
|
@@ -0,0 +1,8 @@
|
||||
// Settings
|
||||
|
||||
"ENABLED" = "ミュートボタンを表示";
|
||||
"POSITION" = "ミュートボタンの位置";
|
||||
"TOP" = "上部";
|
||||
"TOP_DESC" = "右上, 自動再生, キャプション, キャストとオーバーフローメニューがある場所.";
|
||||
"BOTTOM" = "下部";
|
||||
"BOTTOM_DESC" = "右下, フルスクリーンボタンがある場所.";
|
@@ -0,0 +1,8 @@
|
||||
// Settings
|
||||
|
||||
"ENABLED" = "Отображать кнопку «Без звука»";
|
||||
"POSITION" = "Место расположения кнопки";
|
||||
"TOP" = "Сверху";
|
||||
"TOP_DESC" = "В правом верхнем углу, рядом с кнопками Автовоспроизведения, Субтитров и выпадающего меню.";
|
||||
"BOTTOM" = "Снизу";
|
||||
"BOTTOM_DESC" = "В правом нижнем углу, рядом с кнопкой Полноэкранного режима.";
|
@@ -0,0 +1,8 @@
|
||||
// Settings
|
||||
|
||||
"ENABLED" = "顯示靜音按鈕";
|
||||
"POSITION" = "靜音按鈕位置";
|
||||
"TOP" = "頂部";
|
||||
"TOP_DESC" = "在右上方,也就是自動播放、字幕、投放和選項所在的位置。";
|
||||
"BOTTOM" = "底部";
|
||||
"BOTTOM_DESC" = "在右下方,也就是全螢幕按鈕所在的位置。";
|
@@ -0,0 +1,8 @@
|
||||
// Settings
|
||||
|
||||
"ENABLED" = "显示静音按钮";
|
||||
"POSITION" = "静音按钮位置";
|
||||
"TOP" = "顶部";
|
||||
"TOP_DESC" = "在右上角,自动播放、字幕等按钮所在位置。";
|
||||
"BOTTOM" = "底部";
|
||||
"BOTTOM_DESC" = "在右下角,全屏按钮所在的位置。";
|
Reference in New Issue
Block a user