Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions Files/Headers.h
Original file line number Diff line number Diff line change
Expand Up @@ -208,6 +208,7 @@ typedef NS_ENUM(NSUInteger, GestureSection) {
- (void)YouModAutoFullscreen;
- (void)YouModTurnOffCaptions;
- (void)setActiveCaptionTrack:(id)arg1 source:(long long)arg2;
- (void)seekToTime:(CGFloat)time;
- (void)setPlaybackRate:(float)rate;
@end

Expand Down
213 changes: 213 additions & 0 deletions Files/Player.x
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,122 @@ extern void YouModDownloadSetCurrentPlayer(YTPlayerViewController *player);

float playbackRate = 1.0;

static const NSTimeInterval YouModRemoteSkipInterval = 10.0;
static __weak YTPlayerViewController *YouModCurrentPlayerViewController;
static NSTimeInterval YouModCurrentPlaybackTime = 0;
static NSTimeInterval YouModCurrentDuration = 0;
static BOOL YouModHasPlaybackTime = NO;
static id YouModSkipBackwardCommandTarget;
static id YouModSkipForwardCommandTarget;
static NSMutableArray *YouModRemoteCommandTargetProxies;
typedef MPRemoteCommandHandlerStatus (^YouModRemoteCommandHandler)(MPRemoteCommandEvent *event);

static void YouModUpdateCurrentPlayer(YTPlayerViewController *player) {
YouModCurrentPlayerViewController = player;
}

static void YouModUpdatePlaybackTime(YTPlayerViewController *player, YTSingleVideoController *video, YTSingleVideoTime *time) {
YouModUpdateCurrentPlayer(player);
YouModCurrentPlaybackTime = time.time;
YouModCurrentDuration = video.totalMediaTime;
YouModHasPlaybackTime = YES;
}

static BOOL YouModIsPreviousNextCommand(MPRemoteCommand *command) {
MPRemoteCommandCenter *commandCenter = [MPRemoteCommandCenter sharedCommandCenter];
return command == commandCenter.previousTrackCommand || command == commandCenter.nextTrackCommand;
}

static BOOL YouModIsSkipCommand(MPRemoteCommand *command) {
MPRemoteCommandCenter *commandCenter = [MPRemoteCommandCenter sharedCommandCenter];
return command == commandCenter.skipBackwardCommand || command == commandCenter.skipForwardCommand;
}

static BOOL YouModSeekByInterval(NSTimeInterval interval) {
if (!IS_ENABLED(ReplacePrevNextButtons) || !YouModCurrentPlayerViewController || !YouModHasPlaybackTime) return NO;
if (![YouModCurrentPlayerViewController respondsToSelector:@selector(seekToTime:)]) return NO;

NSTimeInterval targetTime = YouModCurrentPlaybackTime + interval;
if (YouModCurrentDuration > 0) targetTime = fmin(targetTime, YouModCurrentDuration);
targetTime = fmax(targetTime, 0);

[YouModCurrentPlayerViewController seekToTime:(CGFloat)targetTime];
YouModCurrentPlaybackTime = targetTime;
return YES;
}

static MPRemoteCommandHandlerStatus YouModStatusForSeek(BOOL handled) {
return handled ? MPRemoteCommandHandlerStatusSuccess : MPRemoteCommandHandlerStatusNoActionableNowPlayingItem;
}

static BOOL YouModHandlePreviousNextRemoteCommand(MPRemoteCommandEvent *event) {
MPRemoteCommandCenter *commandCenter = [MPRemoteCommandCenter sharedCommandCenter];
if (event.command == commandCenter.previousTrackCommand) return YouModSeekByInterval(-YouModRemoteSkipInterval);
if (event.command == commandCenter.nextTrackCommand) return YouModSeekByInterval(YouModRemoteSkipInterval);
return NO;
}

static BOOL YouModIsPreviousNextRemoteCommandEvent(MPRemoteCommandEvent *event) {
return YouModIsPreviousNextCommand(event.command);
}

@interface YouModRemoteCommandTargetProxy : NSObject
@property (nonatomic, weak) MPRemoteCommand *command;
@property (nonatomic, weak) id target;
@property (nonatomic, assign) SEL action;
- (MPRemoteCommandHandlerStatus)youModHandleRemoteCommandEvent:(MPRemoteCommandEvent *)event;
@end

@implementation YouModRemoteCommandTargetProxy
- (MPRemoteCommandHandlerStatus)youModHandleRemoteCommandEvent:(MPRemoteCommandEvent *)event {
if (IS_ENABLED(ReplacePrevNextButtons) && YouModIsPreviousNextRemoteCommandEvent(event)) {
return YouModStatusForSeek(YouModHandlePreviousNextRemoteCommand(event));
}

if (!self.target || !self.action) return MPRemoteCommandHandlerStatusCommandFailed;

NSMethodSignature *signature = [self.target methodSignatureForSelector:self.action];
if (!signature) return MPRemoteCommandHandlerStatusCommandFailed;

NSInvocation *invocation = [NSInvocation invocationWithMethodSignature:signature];
invocation.target = self.target;
invocation.selector = self.action;
if (signature.numberOfArguments > 2) [invocation setArgument:&event atIndex:2];
[invocation invoke];

if (signature.methodReturnLength == 0) return MPRemoteCommandHandlerStatusSuccess;

MPRemoteCommandHandlerStatus status = MPRemoteCommandHandlerStatusSuccess;
[invocation getReturnValue:&status];
return status;
}
@end

static void YouModConfigureRemoteSkipCommands(void) {
MPRemoteCommandCenter *commandCenter = [MPRemoteCommandCenter sharedCommandCenter];
BOOL enabled = IS_ENABLED(ReplacePrevNextButtons);
NSArray *intervals = @[@(YouModRemoteSkipInterval)];

commandCenter.skipBackwardCommand.enabled = enabled;
commandCenter.skipBackwardCommand.preferredIntervals = intervals;
commandCenter.skipForwardCommand.enabled = enabled;
commandCenter.skipForwardCommand.preferredIntervals = intervals;
commandCenter.previousTrackCommand.enabled = !enabled;
commandCenter.nextTrackCommand.enabled = !enabled;

if (!YouModSkipBackwardCommandTarget) {
YouModSkipBackwardCommandTarget = [commandCenter.skipBackwardCommand addTargetWithHandler:^MPRemoteCommandHandlerStatus(MPRemoteCommandEvent *event) {
return YouModStatusForSeek(YouModSeekByInterval(-YouModRemoteSkipInterval));
}];
}

if (!YouModSkipForwardCommandTarget) {
YouModSkipForwardCommandTarget = [commandCenter.skipForwardCommand addTargetWithHandler:^MPRemoteCommandHandlerStatus(MPRemoteCommandEvent *event) {
return YouModStatusForSeek(YouModSeekByInterval(YouModRemoteSkipInterval));
}];
}
}

/*
static void YouModAddEndTime(YTPlayerViewController *self, YTSingleVideoController *video, YTSingleVideoTime *time) {
if (!IS_ENABLED(ShowExtraTimeRemaining)) return;
Expand Down Expand Up @@ -204,6 +320,88 @@ static void YouModAddEndTime(YTPlayerViewController *self, YTSingleVideoControll
- (BOOL)replacePreviousPaddleWithRewindButtonForSingletonVods { return IS_ENABLED(ReplacePrevNextButtons) ? YES : %orig; }
%end

%hook MPRemoteCommand
- (void)addTarget:(id)target action:(SEL)action {
if (YouModIsPreviousNextCommand(self)) {
if (!YouModRemoteCommandTargetProxies) YouModRemoteCommandTargetProxies = [NSMutableArray array];

YouModRemoteCommandTargetProxy *proxy = [[YouModRemoteCommandTargetProxy alloc] init];
proxy.command = self;
proxy.target = target;
proxy.action = action;
[YouModRemoteCommandTargetProxies addObject:proxy];
%orig(proxy, @selector(youModHandleRemoteCommandEvent:));
YouModConfigureRemoteSkipCommands();
return;
}

%orig;
}

- (id)addTargetWithHandler:(YouModRemoteCommandHandler)handler {
if (YouModIsPreviousNextCommand(self)) {
YouModRemoteCommandHandler wrappedHandler = ^MPRemoteCommandHandlerStatus(MPRemoteCommandEvent *event) {
if (IS_ENABLED(ReplacePrevNextButtons) && YouModIsPreviousNextRemoteCommandEvent(event)) {
return YouModStatusForSeek(YouModHandlePreviousNextRemoteCommand(event));
}

return handler(event);
};
id commandTarget = %orig(wrappedHandler);
YouModConfigureRemoteSkipCommands();
return commandTarget;
}

return %orig;
}

- (void)setEnabled:(BOOL)enabled {
if (IS_ENABLED(ReplacePrevNextButtons)) {
if (YouModIsPreviousNextCommand(self)) {
%orig(NO);
return;
}

if (YouModIsSkipCommand(self)) {
%orig(YES);
return;
}
}

%orig;
}

- (void)removeTarget:(id)target action:(SEL)action {
if (YouModIsPreviousNextCommand(self) && YouModRemoteCommandTargetProxies.count > 0) {
NSArray *proxies = [YouModRemoteCommandTargetProxies copy];
for (YouModRemoteCommandTargetProxy *proxy in proxies) {
BOOL targetMatches = !target || proxy.target == target;
BOOL actionMatches = !action || proxy.action == action;
if (proxy.command == self && targetMatches && actionMatches) {
%orig(proxy, @selector(youModHandleRemoteCommandEvent:));
[YouModRemoteCommandTargetProxies removeObject:proxy];
}
}
}

%orig;
}

- (void)removeTarget:(id)target {
if (YouModIsPreviousNextCommand(self) && YouModRemoteCommandTargetProxies.count > 0) {
NSArray *proxies = [YouModRemoteCommandTargetProxies copy];
for (YouModRemoteCommandTargetProxy *proxy in proxies) {
if (proxy.command == self && (!target || proxy.target == target)) {
%orig(proxy);
[YouModRemoteCommandTargetProxies removeObject:proxy];
}
}
}

%orig;
}
%end

%group ForceMiniPlayer
%hook YTIMiniplayerRenderer
%new
Expand Down Expand Up @@ -313,6 +511,8 @@ static void YouModAddEndTime(YTPlayerViewController *self, YTSingleVideoControll
%hook YTPlayerViewController
- (void)loadWithPlayerTransition:(id)arg1 playbackConfig:(id)arg2 {
%orig;
YouModUpdateCurrentPlayer(self);
YouModConfigureRemoteSkipCommands();
YouModDownloadSetCurrentPlayer(self);
if (IS_ENABLED(AutoFullScreen)) [self performSelector:@selector(YouModAutoFullscreen) withObject:nil afterDelay:0.75];
// if (ytlBool(@"shortsToRegular")) [self performSelector:@selector(shortsToRegular) withObject:nil afterDelay:0.75];
Expand All @@ -321,6 +521,8 @@ static void YouModAddEndTime(YTPlayerViewController *self, YTSingleVideoControll

- (void)prepareToLoadWithPlayerTransition:(id)arg1 expectedLayout:(id)arg2 {
%orig;
YouModUpdateCurrentPlayer(self);
YouModConfigureRemoteSkipCommands();
YouModDownloadSetCurrentPlayer(self);
if (IS_ENABLED(AutoFullScreen)) [self performSelector:@selector(YouModAutoFullscreen) withObject:nil afterDelay:0.75];
// if (ytlBool(@"shortsToRegular")) [self performSelector:@selector(shortsToRegular) withObject:nil afterDelay:0.75];
Expand Down Expand Up @@ -352,6 +554,16 @@ static void YouModAddEndTime(YTPlayerViewController *self, YTSingleVideoControll
}
*/

- (void)singleVideo:(YTSingleVideoController *)video currentVideoTimeDidChange:(YTSingleVideoTime *)time {
%orig;
YouModUpdatePlaybackTime(self, video, time);
}

- (void)potentiallyMutatedSingleVideo:(YTSingleVideoController *)video currentVideoTimeDidChange:(YTSingleVideoTime *)time {
%orig;
YouModUpdatePlaybackTime(self, video, time);
}

- (void)setPlaybackRate:(float)rate {
playbackRate = rate;
%orig;
Expand Down Expand Up @@ -653,6 +865,7 @@ static void YouModAddEndTime(YTPlayerViewController *self, YTSingleVideoControll

%ctor {
%init;
YouModConfigureRemoteSkipCommands();
if (IS_ENABLED(OldQualityPicker)) {
%init(OldVideoQuality);
}
Expand Down
2 changes: 1 addition & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ TARGET := iphone:clang:latest:14.0
include $(THEOS)/makefiles/common.mk

TWEAK_NAME = YouMod
$(TWEAK_NAME)_FRAMEWORKS = UIKit Foundation
$(TWEAK_NAME)_FRAMEWORKS = UIKit Foundation MediaPlayer
$(TWEAK_NAME)_CFLAGS = -fobjc-arc
$(TWEAK_NAME)_FILES = $(wildcard Files/*.x)

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -88,8 +88,8 @@
"HIDE_PREV_BUTTON_DESC" = "Hide previous/back button in the video overlay.";
"HIDE_NEXT_BUTTON" = "Hide next/skip button";
"HIDE_NEXT_BUTTON_DESC" = "Hide next/skip button in the video overlay.";
"REPLACE_PREVNEXT_BUTTONS" = "Replace Previous/Next buttons";
"REPLACE_PREVNEXT_BUTTONS_DESC" = "Replaces the Previous and Next video buttons in the player with Rewind and Fast Forward buttons.";
"REPLACE_PREVNEXT_BUTTONS" = "Use Rewind/Fast Forward controls";
"REPLACE_PREVNEXT_BUTTONS_DESC" = "Replaces Previous and Next controls in the player and iOS media controls with Rewind and Fast Forward.";
"REMOVE_DARK_OVERLAY" = "Remove dark overlay";
"REMOVE_DARK_OVERLAY_DESC" = "Remove dark overlay from the video overlay.";
"HIDE_END_SCREEN" = "Hide endscreen cards";
Expand Down