mirror of
https://github.com/SoPat712/YTLitePlus.git
synced 2026-02-11 01:08:37 -05:00
added files via upload
This commit is contained in:
32
Tweaks/iSponsorBlock/.github/ISSUE_TEMPLATE/bug_report.md
vendored
Normal file
32
Tweaks/iSponsorBlock/.github/ISSUE_TEMPLATE/bug_report.md
vendored
Normal file
@@ -0,0 +1,32 @@
|
||||
---
|
||||
name: Bug report
|
||||
about: Create a report to help us improve
|
||||
title: ''
|
||||
labels: bug
|
||||
assignees: Galactic-Dev
|
||||
|
||||
---
|
||||
|
||||
**Describe the bug**
|
||||
A clear and concise description of what the bug is.
|
||||
|
||||
**To Reproduce**
|
||||
Steps to reproduce the behavior:
|
||||
1. Go to '...'
|
||||
2. Click on '....'
|
||||
3. See error
|
||||
|
||||
**Expected behavior**
|
||||
A clear and concise description of what you expected to happen.
|
||||
|
||||
**Screenshots**
|
||||
If applicable, add screenshots to help explain your problem.
|
||||
|
||||
**Please complete the following information:**
|
||||
- iOS Version: [e.g. 14.3]
|
||||
- Device: [e.g. iPhone X]
|
||||
- YouTube Version [e.g. 16.11.3]
|
||||
- iSponsorBlock Version [e.g. 1.0-7]
|
||||
|
||||
**Additional context**
|
||||
Add any other context about the problem here.
|
||||
20
Tweaks/iSponsorBlock/.github/ISSUE_TEMPLATE/feature_request.md
vendored
Normal file
20
Tweaks/iSponsorBlock/.github/ISSUE_TEMPLATE/feature_request.md
vendored
Normal file
@@ -0,0 +1,20 @@
|
||||
---
|
||||
name: Feature request
|
||||
about: Suggest an idea for this project
|
||||
title: ''
|
||||
labels: enhancement
|
||||
assignees: ''
|
||||
|
||||
---
|
||||
|
||||
**Is your feature request related to a problem? Please describe.**
|
||||
A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]
|
||||
|
||||
**Describe the solution you'd like**
|
||||
A clear and concise description of what you want to happen.
|
||||
|
||||
**Describe alternatives you've considered**
|
||||
A clear and concise description of any alternative solutions or features you've considered.
|
||||
|
||||
**Additional context**
|
||||
Add any other context or screenshots about the feature request here.
|
||||
16
Tweaks/iSponsorBlock/.gitignore
vendored
Normal file
16
Tweaks/iSponsorBlock/.gitignore
vendored
Normal file
@@ -0,0 +1,16 @@
|
||||
# Created by https://www.toptal.com/developers/gitignore/api/theos-tweak
|
||||
# Edit at https://www.toptal.com/developers/gitignore?templates=theos-tweak
|
||||
|
||||
### THEOS-Tweak ###
|
||||
._*
|
||||
*.deb
|
||||
.debmake
|
||||
_
|
||||
obj
|
||||
.theos
|
||||
.DS_Store
|
||||
|
||||
### XCode stuff
|
||||
*.xcworkspace
|
||||
|
||||
# End of https://www.toptal.com/developers/gitignore/api/theos-tweak
|
||||
3
Tweaks/iSponsorBlock/.gitmodules
vendored
Normal file
3
Tweaks/iSponsorBlock/.gitmodules
vendored
Normal file
@@ -0,0 +1,3 @@
|
||||
[submodule "Headers/YouTubeHeader"]
|
||||
path = Headers/YouTubeHeader
|
||||
url = https://github.com/PoomSmart/YouTubeHeader
|
||||
37
Tweaks/iSponsorBlock/Headers/ColorFunctions.h
Normal file
37
Tweaks/iSponsorBlock/Headers/ColorFunctions.h
Normal file
@@ -0,0 +1,37 @@
|
||||
#import <Foundation/Foundation.h>
|
||||
#import <UIKit/UIKit.h>
|
||||
//https://stackoverflow.com/a/26341062
|
||||
static NSString *hexFromUIColor(UIColor *color) {
|
||||
const CGFloat *components = CGColorGetComponents(color.CGColor);
|
||||
|
||||
CGFloat r = components[0];
|
||||
CGFloat g = components[1];
|
||||
CGFloat b = components[2];
|
||||
|
||||
return [NSString stringWithFormat:@"#%02lX%02lX%02lX",
|
||||
lroundf(r * 255),
|
||||
lroundf(g * 255),
|
||||
lroundf(b * 255)];
|
||||
}
|
||||
|
||||
static CGFloat colorComponentFrom(NSString *string, NSUInteger start, NSUInteger length) {
|
||||
NSString *substring = [string substringWithRange: NSMakeRange(start, length)];
|
||||
NSString *fullHex = length == 2 ? substring : [NSString stringWithFormat: @"%@%@", substring, substring];
|
||||
unsigned hexComponent;
|
||||
[[NSScanner scannerWithString: fullHex] scanHexInt: &hexComponent];
|
||||
return hexComponent / 255.0;
|
||||
}
|
||||
|
||||
static UIColor *colorWithHexString(NSString *hexString) {
|
||||
NSString *colorString = [[hexString stringByReplacingOccurrencesOfString: @"#" withString: @""] uppercaseString];
|
||||
|
||||
CGFloat alpha, red, blue, green;
|
||||
|
||||
// #RGB
|
||||
alpha = 1.0f;
|
||||
red = colorComponentFrom(colorString,0,2);
|
||||
green = colorComponentFrom(colorString,2,2);
|
||||
blue = colorComponentFrom(colorString,4,2);
|
||||
|
||||
return [UIColor colorWithRed: red green: green blue: blue alpha: alpha];
|
||||
}
|
||||
8
Tweaks/iSponsorBlock/Headers/Localization.h
Normal file
8
Tweaks/iSponsorBlock/Headers/Localization.h
Normal file
@@ -0,0 +1,8 @@
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
extern NSBundle *iSponsorBlockBundle();
|
||||
|
||||
static inline NSString *LOC(NSString *key) {
|
||||
NSBundle *tweakBundle = iSponsorBlockBundle();
|
||||
return [tweakBundle localizedStringForKey:key value:nil table:nil];
|
||||
}
|
||||
411
Tweaks/iSponsorBlock/Headers/MBProgressHUD.h
Normal file
411
Tweaks/iSponsorBlock/Headers/MBProgressHUD.h
Normal file
@@ -0,0 +1,411 @@
|
||||
//
|
||||
// MBProgressHUD.h
|
||||
// Version 1.2.0
|
||||
// Created by Matej Bukovinski on 2.4.09.
|
||||
//
|
||||
|
||||
// This code is distributed under the terms and conditions of the MIT license.
|
||||
|
||||
// Copyright © 2009-2020 Matej Bukovinski
|
||||
//
|
||||
// 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.
|
||||
#import <Foundation/Foundation.h>
|
||||
#import <UIKit/UIKit.h>
|
||||
#import <CoreGraphics/CoreGraphics.h>
|
||||
|
||||
@class MBBackgroundView;
|
||||
@protocol MBProgressHUDDelegate;
|
||||
|
||||
|
||||
extern CGFloat const MBProgressMaxOffset;
|
||||
|
||||
typedef NS_ENUM(NSInteger, MBProgressHUDMode) {
|
||||
/// UIActivityIndicatorView.
|
||||
MBProgressHUDModeIndeterminate,
|
||||
/// A round, pie-chart like, progress view.
|
||||
MBProgressHUDModeDeterminate,
|
||||
/// Horizontal progress bar.
|
||||
MBProgressHUDModeDeterminateHorizontalBar,
|
||||
/// Ring-shaped progress view.
|
||||
MBProgressHUDModeAnnularDeterminate,
|
||||
/// Shows a custom view.
|
||||
MBProgressHUDModeCustomView,
|
||||
/// Shows only labels.
|
||||
MBProgressHUDModeText
|
||||
};
|
||||
|
||||
typedef NS_ENUM(NSInteger, MBProgressHUDAnimation) {
|
||||
/// Opacity animation
|
||||
MBProgressHUDAnimationFade,
|
||||
/// Opacity + scale animation (zoom in when appearing zoom out when disappearing)
|
||||
MBProgressHUDAnimationZoom,
|
||||
/// Opacity + scale animation (zoom out style)
|
||||
MBProgressHUDAnimationZoomOut,
|
||||
/// Opacity + scale animation (zoom in style)
|
||||
MBProgressHUDAnimationZoomIn
|
||||
};
|
||||
|
||||
typedef NS_ENUM(NSInteger, MBProgressHUDBackgroundStyle) {
|
||||
/// Solid color background
|
||||
MBProgressHUDBackgroundStyleSolidColor,
|
||||
/// UIVisualEffectView or UIToolbar.layer background view
|
||||
MBProgressHUDBackgroundStyleBlur
|
||||
};
|
||||
|
||||
typedef void (^MBProgressHUDCompletionBlock)(void);
|
||||
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
|
||||
/**
|
||||
* Displays a simple HUD window containing a progress indicator and two optional labels for short messages.
|
||||
*
|
||||
* This is a simple drop-in class for displaying a progress HUD view similar to Apple's private UIProgressHUD class.
|
||||
* The MBProgressHUD window spans over the entire space given to it by the initWithFrame: constructor and catches all
|
||||
* user input on this region, thereby preventing the user operations on components below the view.
|
||||
*
|
||||
* @note To still allow touches to pass through the HUD, you can set hud.userInteractionEnabled = NO.
|
||||
* @attention MBProgressHUD is a UI class and should therefore only be accessed on the main thread.
|
||||
*/
|
||||
@interface MBProgressHUD : UIView
|
||||
|
||||
/**
|
||||
* Creates a new HUD, adds it to provided view and shows it. The counterpart to this method is hideHUDForView:animated:.
|
||||
*
|
||||
* @note This method sets removeFromSuperViewOnHide. The HUD will automatically be removed from the view hierarchy when hidden.
|
||||
*
|
||||
* @param view The view that the HUD will be added to
|
||||
* @param animated If set to YES the HUD will appear using the current animationType. If set to NO the HUD will not use
|
||||
* animations while appearing.
|
||||
* @return A reference to the created HUD.
|
||||
*
|
||||
* @see hideHUDForView:animated:
|
||||
* @see animationType
|
||||
*/
|
||||
+ (instancetype)showHUDAddedTo:(UIView *)view animated:(BOOL)animated;
|
||||
|
||||
/// @name Showing and hiding
|
||||
|
||||
/**
|
||||
* Finds the top-most HUD subview that hasn't finished and hides it. The counterpart to this method is showHUDAddedTo:animated:.
|
||||
*
|
||||
* @note This method sets removeFromSuperViewOnHide. The HUD will automatically be removed from the view hierarchy when hidden.
|
||||
*
|
||||
* @param view The view that is going to be searched for a HUD subview.
|
||||
* @param animated If set to YES the HUD will disappear using the current animationType. If set to NO the HUD will not use
|
||||
* animations while disappearing.
|
||||
* @return YES if a HUD was found and removed, NO otherwise.
|
||||
*
|
||||
* @see showHUDAddedTo:animated:
|
||||
* @see animationType
|
||||
*/
|
||||
+ (BOOL)hideHUDForView:(UIView *)view animated:(BOOL)animated;
|
||||
|
||||
/**
|
||||
* Finds the top-most HUD subview that hasn't finished and returns it.
|
||||
*
|
||||
* @param view The view that is going to be searched.
|
||||
* @return A reference to the last HUD subview discovered.
|
||||
*/
|
||||
+ (nullable MBProgressHUD *)HUDForView:(UIView *)view NS_SWIFT_NAME(forView(_:));
|
||||
|
||||
/**
|
||||
* A convenience constructor that initializes the HUD with the view's bounds. Calls the designated constructor with
|
||||
* view.bounds as the parameter.
|
||||
*
|
||||
* @param view The view instance that will provide the bounds for the HUD. Should be the same instance as
|
||||
* the HUD's superview (i.e., the view that the HUD will be added to).
|
||||
*/
|
||||
- (instancetype)initWithView:(UIView *)view;
|
||||
|
||||
/**
|
||||
* Displays the HUD.
|
||||
*
|
||||
* @note You need to make sure that the main thread completes its run loop soon after this method call so that
|
||||
* the user interface can be updated. Call this method when your task is already set up to be executed in a new thread
|
||||
* (e.g., when using something like NSOperation or making an asynchronous call like NSURLRequest).
|
||||
*
|
||||
* @param animated If set to YES the HUD will appear using the current animationType. If set to NO the HUD will not use
|
||||
* animations while appearing.
|
||||
*
|
||||
* @see animationType
|
||||
*/
|
||||
- (void)showAnimated:(BOOL)animated;
|
||||
|
||||
/**
|
||||
* Hides the HUD. This still calls the hudWasHidden: delegate. This is the counterpart of the show: method. Use it to
|
||||
* hide the HUD when your task completes.
|
||||
*
|
||||
* @param animated If set to YES the HUD will disappear using the current animationType. If set to NO the HUD will not use
|
||||
* animations while disappearing.
|
||||
*
|
||||
* @see animationType
|
||||
*/
|
||||
- (void)hideAnimated:(BOOL)animated;
|
||||
|
||||
/**
|
||||
* Hides the HUD after a delay. This still calls the hudWasHidden: delegate. This is the counterpart of the show: method. Use it to
|
||||
* hide the HUD when your task completes.
|
||||
*
|
||||
* @param animated If set to YES the HUD will disappear using the current animationType. If set to NO the HUD will not use
|
||||
* animations while disappearing.
|
||||
* @param delay Delay in seconds until the HUD is hidden.
|
||||
*
|
||||
* @see animationType
|
||||
*/
|
||||
- (void)hideAnimated:(BOOL)animated afterDelay:(NSTimeInterval)delay;
|
||||
|
||||
/**
|
||||
* The HUD delegate object. Receives HUD state notifications.
|
||||
*/
|
||||
@property (weak, nonatomic) id<MBProgressHUDDelegate> delegate;
|
||||
|
||||
/**
|
||||
* Called after the HUD is hidden.
|
||||
*/
|
||||
@property (copy, nullable) MBProgressHUDCompletionBlock completionBlock;
|
||||
|
||||
/**
|
||||
* Grace period is the time (in seconds) that the invoked method may be run without
|
||||
* showing the HUD. If the task finishes before the grace time runs out, the HUD will
|
||||
* not be shown at all.
|
||||
* This may be used to prevent HUD display for very short tasks.
|
||||
* Defaults to 0 (no grace time).
|
||||
* @note The graceTime needs to be set before the hud is shown. You thus can't use `showHUDAddedTo:animated:`,
|
||||
* but instead need to alloc / init the HUD, configure the grace time and than show it manually.
|
||||
*/
|
||||
@property (assign, nonatomic) NSTimeInterval graceTime;
|
||||
|
||||
/**
|
||||
* The minimum time (in seconds) that the HUD is shown.
|
||||
* This avoids the problem of the HUD being shown and than instantly hidden.
|
||||
* Defaults to 0 (no minimum show time).
|
||||
*/
|
||||
@property (assign, nonatomic) NSTimeInterval minShowTime;
|
||||
|
||||
/**
|
||||
* Removes the HUD from its parent view when hidden.
|
||||
* Defaults to NO.
|
||||
*/
|
||||
@property (assign, nonatomic) BOOL removeFromSuperViewOnHide;
|
||||
|
||||
/// @name Appearance
|
||||
|
||||
/**
|
||||
* MBProgressHUD operation mode. The default is MBProgressHUDModeIndeterminate.
|
||||
*/
|
||||
@property (assign, nonatomic) MBProgressHUDMode mode;
|
||||
|
||||
/**
|
||||
* A color that gets forwarded to all labels and supported indicators. Also sets the tintColor
|
||||
* for custom views on iOS 7+. Set to nil to manage color individually.
|
||||
* Defaults to semi-translucent black on iOS 7 and later and white on earlier iOS versions.
|
||||
*/
|
||||
@property (strong, nonatomic, nullable) UIColor *contentColor UI_APPEARANCE_SELECTOR;
|
||||
|
||||
/**
|
||||
* The animation type that should be used when the HUD is shown and hidden.
|
||||
*/
|
||||
@property (assign, nonatomic) MBProgressHUDAnimation animationType UI_APPEARANCE_SELECTOR;
|
||||
|
||||
/**
|
||||
* The bezel offset relative to the center of the view. You can use MBProgressMaxOffset
|
||||
* and -MBProgressMaxOffset to move the HUD all the way to the screen edge in each direction.
|
||||
* E.g., CGPointMake(0.f, MBProgressMaxOffset) would position the HUD centered on the bottom edge.
|
||||
*/
|
||||
@property (assign, nonatomic) CGPoint offset UI_APPEARANCE_SELECTOR;
|
||||
|
||||
/**
|
||||
* The amount of space between the HUD edge and the HUD elements (labels, indicators or custom views).
|
||||
* This also represents the minimum bezel distance to the edge of the HUD view.
|
||||
* Defaults to 20.f
|
||||
*/
|
||||
@property (assign, nonatomic) CGFloat margin UI_APPEARANCE_SELECTOR;
|
||||
|
||||
/**
|
||||
* The minimum size of the HUD bezel. Defaults to CGSizeZero (no minimum size).
|
||||
*/
|
||||
@property (assign, nonatomic) CGSize minSize UI_APPEARANCE_SELECTOR;
|
||||
|
||||
/**
|
||||
* Force the HUD dimensions to be equal if possible.
|
||||
*/
|
||||
@property (assign, nonatomic, getter = isSquare) BOOL square UI_APPEARANCE_SELECTOR;
|
||||
|
||||
/**
|
||||
* When enabled, the bezel center gets slightly affected by the device accelerometer data.
|
||||
* Defaults to NO.
|
||||
*
|
||||
* @note This can cause main thread checker assertions on certain devices. https://github.com/jdg/MBProgressHUD/issues/552
|
||||
*/
|
||||
@property (assign, nonatomic, getter=areDefaultMotionEffectsEnabled) BOOL defaultMotionEffectsEnabled UI_APPEARANCE_SELECTOR;
|
||||
|
||||
/// @name Progress
|
||||
|
||||
/**
|
||||
* The progress of the progress indicator, from 0.0 to 1.0. Defaults to 0.0.
|
||||
*/
|
||||
@property (assign, nonatomic) float progress;
|
||||
|
||||
/// @name ProgressObject
|
||||
|
||||
/**
|
||||
* The NSProgress object feeding the progress information to the progress indicator.
|
||||
*/
|
||||
@property (strong, nonatomic, nullable) NSProgress *progressObject;
|
||||
|
||||
/// @name Views
|
||||
|
||||
/**
|
||||
* The view containing the labels and indicator (or customView).
|
||||
*/
|
||||
@property (strong, nonatomic, readonly) MBBackgroundView *bezelView;
|
||||
|
||||
/**
|
||||
* View covering the entire HUD area, placed behind bezelView.
|
||||
*/
|
||||
@property (strong, nonatomic, readonly) MBBackgroundView *backgroundView;
|
||||
|
||||
/**
|
||||
* The UIView (e.g., a UIImageView) to be shown when the HUD is in MBProgressHUDModeCustomView.
|
||||
* The view should implement intrinsicContentSize for proper sizing. For best results use approximately 37 by 37 pixels.
|
||||
*/
|
||||
@property (strong, nonatomic, nullable) UIView *customView;
|
||||
|
||||
/**
|
||||
* A label that holds an optional short message to be displayed below the activity indicator. The HUD is automatically resized to fit
|
||||
* the entire text.
|
||||
*/
|
||||
@property (strong, nonatomic, readonly) UILabel *label;
|
||||
|
||||
/**
|
||||
* A label that holds an optional details message displayed below the labelText message. The details text can span multiple lines.
|
||||
*/
|
||||
@property (strong, nonatomic, readonly) UILabel *detailsLabel;
|
||||
|
||||
/**
|
||||
* A button that is placed below the labels. Visible only if a target / action is added and a title is assigned..
|
||||
*/
|
||||
@property (strong, nonatomic, readonly) UIButton *button;
|
||||
@property (strong, nonatomic, readonly) UIButton *cancelButton;
|
||||
|
||||
@end
|
||||
|
||||
|
||||
@protocol MBProgressHUDDelegate <NSObject>
|
||||
|
||||
@optional
|
||||
|
||||
/**
|
||||
* Called after the HUD was fully hidden from the screen.
|
||||
*/
|
||||
- (void)hudWasHidden:(MBProgressHUD *)hud;
|
||||
|
||||
@end
|
||||
|
||||
|
||||
/**
|
||||
* A progress view for showing definite progress by filling up a circle (pie chart).
|
||||
*/
|
||||
@interface MBRoundProgressView : UIView
|
||||
|
||||
/**
|
||||
* Progress (0.0 to 1.0)
|
||||
*/
|
||||
@property (nonatomic, assign) float progress;
|
||||
|
||||
/**
|
||||
* Indicator progress color.
|
||||
* Defaults to white [UIColor whiteColor].
|
||||
*/
|
||||
@property (nonatomic, strong) UIColor *progressTintColor;
|
||||
|
||||
/**
|
||||
* Indicator background (non-progress) color.
|
||||
* Only applicable on iOS versions older than iOS 7.
|
||||
* Defaults to translucent white (alpha 0.1).
|
||||
*/
|
||||
@property (nonatomic, strong) UIColor *backgroundTintColor;
|
||||
|
||||
/*
|
||||
* Display mode - NO = round or YES = annular. Defaults to round.
|
||||
*/
|
||||
@property (nonatomic, assign, getter = isAnnular) BOOL annular;
|
||||
|
||||
@end
|
||||
|
||||
|
||||
/**
|
||||
* A flat bar progress view.
|
||||
*/
|
||||
@interface MBBarProgressView : UIView
|
||||
|
||||
/**
|
||||
* Progress (0.0 to 1.0)
|
||||
*/
|
||||
@property (nonatomic, assign) float progress;
|
||||
|
||||
/**
|
||||
* Bar border line color.
|
||||
* Defaults to white [UIColor whiteColor].
|
||||
*/
|
||||
@property (nonatomic, strong) UIColor *lineColor;
|
||||
|
||||
/**
|
||||
* Bar background color.
|
||||
* Defaults to clear [UIColor clearColor];
|
||||
*/
|
||||
@property (nonatomic, strong) UIColor *progressRemainingColor;
|
||||
|
||||
/**
|
||||
* Bar progress color.
|
||||
* Defaults to white [UIColor whiteColor].
|
||||
*/
|
||||
@property (nonatomic, strong) UIColor *progressColor;
|
||||
|
||||
@end
|
||||
|
||||
|
||||
@interface MBBackgroundView : UIView
|
||||
|
||||
/**
|
||||
* The background style.
|
||||
* Defaults to MBProgressHUDBackgroundStyleBlur.
|
||||
*/
|
||||
@property (nonatomic) MBProgressHUDBackgroundStyle style;
|
||||
|
||||
/**
|
||||
* The blur effect style, when using MBProgressHUDBackgroundStyleBlur.
|
||||
* Defaults to UIBlurEffectStyleLight.
|
||||
*/
|
||||
@property (nonatomic) UIBlurEffectStyle blurEffectStyle;
|
||||
|
||||
/**
|
||||
* The background color or the blur tint color.
|
||||
*
|
||||
* Defaults to nil on iOS 13 and later and
|
||||
* `[UIColor colorWithWhite:0.8f alpha:0.6f]`
|
||||
* on older systems.
|
||||
*/
|
||||
@property (nonatomic, strong, nullable) UIColor *color;
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
11
Tweaks/iSponsorBlock/Headers/SponsorBlockRequest.h
Normal file
11
Tweaks/iSponsorBlock/Headers/SponsorBlockRequest.h
Normal file
@@ -0,0 +1,11 @@
|
||||
#import <UIKit/UIKit.h>
|
||||
#import "iSponsorBlock.h"
|
||||
#import <objc/runtime.h>
|
||||
|
||||
@interface SponsorBlockRequest : NSObject
|
||||
+(void)getSponsorTimes:(NSString *)videoID completionTarget:(id)target completionSelector:(SEL)sel apiInstance:(NSString *)apiInstance;
|
||||
+(void)postSponsorTimes:(NSString *)videoID sponsorSegments:(NSArray <SponsorSegment *> *)segments userID:(NSString *)userID withViewController:(UIViewController *)viewController;
|
||||
+(void)normalVoteForSegment:(SponsorSegment *)segment userID:(NSString *)userID type:(BOOL)type withViewController:(UIViewController *)viewController;
|
||||
+(void)categoryVoteForSegment:(SponsorSegment *)segment userID:(NSString *)userID category:(NSString *)category withViewController:(UIViewController *)viewController;
|
||||
+(void)viewedVideoSponsorTime:(SponsorSegment *)segment;
|
||||
@end
|
||||
@@ -0,0 +1,52 @@
|
||||
#import <UIKit/UIKit.h>
|
||||
#import <objc/runtime.h>
|
||||
#import <rootless.h>
|
||||
#import "ColorFunctions.h"
|
||||
|
||||
@protocol HBColorPickerDelegate <NSObject>
|
||||
@optional -(void)colorPicker:(id)colorPicker didSelectColor:(UIColor *)color;
|
||||
@end
|
||||
|
||||
@interface UIView ()
|
||||
- (UIViewController *)_viewControllerForAncestor;
|
||||
@end
|
||||
|
||||
@interface UITableViewCell ()
|
||||
- (UITextField *)editableTextField;
|
||||
- (id)_indexPath;
|
||||
@end
|
||||
|
||||
@interface UISegment : UIView
|
||||
@end
|
||||
|
||||
@interface HBColorPickerConfiguration
|
||||
@property (nonatomic, assign) BOOL supportsAlpha;
|
||||
@end
|
||||
|
||||
@interface HBColorPickerViewController : UIViewController
|
||||
@property (strong, nonatomic) NSObject <HBColorPickerDelegate> *delegate;
|
||||
@property (strong, nonatomic) HBColorPickerConfiguration *configuration;
|
||||
@end
|
||||
|
||||
@interface HBColorWell : UIControl
|
||||
@property (nonatomic, assign) BOOL isDragInteractionEnabled;
|
||||
@property (nonatomic, assign) BOOL isDropInteractionEnabled;
|
||||
@property (strong, nonatomic) UIColor *color;
|
||||
@end
|
||||
|
||||
@interface SponsorBlockTableCell : UITableViewCell <HBColorPickerDelegate>
|
||||
@property (strong, nonatomic) NSString *category;
|
||||
@property (strong, nonatomic) UIColor *color;
|
||||
@property (strong, nonatomic) HBColorWell *colorWell;
|
||||
@end
|
||||
|
||||
@interface SponsorBlockSettingsController : UIViewController <UITableViewDelegate, UITableViewDataSource, UITextFieldDelegate>
|
||||
@property (nonatomic, strong) NSString *tweakTitle;
|
||||
@property (strong, nonatomic) UITableView *tableView;
|
||||
@property (strong, nonatomic) NSArray *sectionTitles;
|
||||
@property (strong, nonatomic) NSMutableDictionary *settings;
|
||||
@property (strong, nonatomic) NSString *settingsPath;
|
||||
- (void)enabledSwitchToggled:(UISwitch *)sender;
|
||||
- (void)switchToggled:(UISwitch *)sender;
|
||||
- (void)categorySegmentSelected:(UISegmentedControl *)segmentedControl;
|
||||
@end
|
||||
19
Tweaks/iSponsorBlock/Headers/SponsorBlockViewController.h
Normal file
19
Tweaks/iSponsorBlock/Headers/SponsorBlockViewController.h
Normal file
@@ -0,0 +1,19 @@
|
||||
#import <UIKit/UIKit.h>
|
||||
#import "SponsorSegmentView.h"
|
||||
#import "iSponsorBlock.h"
|
||||
|
||||
@interface SponsorBlockViewController : UIViewController <UIContextMenuInteractionDelegate>
|
||||
@property (strong, nonatomic) YTPlayerViewController *playerViewController;
|
||||
@property (strong, nonatomic) UIViewController *previousParentViewController;
|
||||
@property (strong, nonatomic) YTMainAppControlsOverlayView *overlayView;
|
||||
@property (strong, nonatomic) UIButton *startEndSegmentButton;
|
||||
@property (strong, nonatomic) UILabel *segmentsInDatabaseLabel;
|
||||
@property (strong, nonatomic) UILabel *userSegmentsLabel;
|
||||
@property (strong, nonatomic) UIButton *submitSegmentsButton;
|
||||
@property (strong, nonatomic) NSMutableArray <SponsorSegmentView *> *sponsorSegmentViews;
|
||||
@property (strong, nonatomic) NSMutableArray <SponsorSegmentView *> *userSponsorSegmentViews;
|
||||
@property (strong, nonatomic) UILabel *whitelistChannelLabel;
|
||||
- (void)startEndSegmentButtonPressed:(UIButton *)sender;
|
||||
- (NSMutableArray *)segmentViewsForSegments:(NSArray <SponsorSegment *> *)segments editable:(BOOL)editable;
|
||||
- (void)setupViews;
|
||||
@end
|
||||
10
Tweaks/iSponsorBlock/Headers/SponsorSegment.h
Normal file
10
Tweaks/iSponsorBlock/Headers/SponsorSegment.h
Normal file
@@ -0,0 +1,10 @@
|
||||
#import <Foundation/Foundation.h>
|
||||
#import <UIKit/UIKit.h>
|
||||
|
||||
@interface SponsorSegment : NSObject
|
||||
@property (nonatomic, assign) CGFloat startTime;
|
||||
@property (nonatomic, assign) CGFloat endTime;
|
||||
@property (strong, nonatomic) NSString *category;
|
||||
@property (strong, nonatomic) NSString *UUID;
|
||||
- (instancetype)initWithStartTime:(CGFloat)startTime endTime:(CGFloat)endTime category:(NSString *)category UUID:(NSString *)UUID;
|
||||
@end
|
||||
11
Tweaks/iSponsorBlock/Headers/SponsorSegmentView.h
Normal file
11
Tweaks/iSponsorBlock/Headers/SponsorSegmentView.h
Normal file
@@ -0,0 +1,11 @@
|
||||
#import <UIKit/UIKit.h>
|
||||
#import "SponsorSegment.h"
|
||||
#import "SponsorBlockRequest.h"
|
||||
|
||||
@interface SponsorSegmentView : UIView
|
||||
@property (strong, nonatomic) SponsorSegment *sponsorSegment;
|
||||
@property (nonatomic, assign) BOOL editable;
|
||||
@property (strong, nonatomic) UILabel *segmentLabel;
|
||||
@property (strong, nonatomic) UILabel *categoryLabel;
|
||||
- (instancetype)initWithFrame:(CGRect)frame sponsorSegment:(SponsorSegment *)segment editable:(BOOL)editable;
|
||||
@end
|
||||
2
Tweaks/iSponsorBlock/Headers/YouTubeHeader/.gitattributes
vendored
Normal file
2
Tweaks/iSponsorBlock/Headers/YouTubeHeader/.gitattributes
vendored
Normal file
@@ -0,0 +1,2 @@
|
||||
# Auto detect text files and perform LF normalization
|
||||
* text=auto
|
||||
2
Tweaks/iSponsorBlock/Headers/YouTubeHeader/.gitignore
vendored
Normal file
2
Tweaks/iSponsorBlock/Headers/YouTubeHeader/.gitignore
vendored
Normal file
@@ -0,0 +1,2 @@
|
||||
|
||||
.DS_Store
|
||||
@@ -0,0 +1,4 @@
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
@interface ASCollectionElement : NSObject
|
||||
@end
|
||||
@@ -0,0 +1,4 @@
|
||||
#import <UIKit/UIKit.h>
|
||||
|
||||
@interface ASCollectionView : UICollectionView
|
||||
@end
|
||||
@@ -0,0 +1,4 @@
|
||||
#import "ASDisplayNode.h"
|
||||
|
||||
@interface ASControlNode : ASDisplayNode
|
||||
@end
|
||||
21
Tweaks/iSponsorBlock/Headers/YouTubeHeader/ASDisplayNode.h
Normal file
21
Tweaks/iSponsorBlock/Headers/YouTubeHeader/ASDisplayNode.h
Normal file
@@ -0,0 +1,21 @@
|
||||
#import <UIKit/UIKit.h>
|
||||
#import "ASLayoutElementStyleYoga.h"
|
||||
|
||||
@interface ASDisplayNode : NSObject
|
||||
@property (atomic, copy, readwrite) NSArray *yogaChildren;
|
||||
@property (nonatomic, copy, readwrite) NSString *accessibilityIdentifier;
|
||||
@property (atomic, weak, readonly) ASDisplayNode *yogaParent;
|
||||
@property (atomic, strong, readwrite) id contents;
|
||||
@property (atomic, assign, readwrite) CGFloat alpha;
|
||||
@property (atomic, assign, readwrite) CGRect frame;
|
||||
@property (atomic, assign, readwrite) CGRect bounds;
|
||||
@property (atomic, assign, readonly) unsigned char interfaceState;
|
||||
@property (atomic, assign, readwrite, getter=isHidden) BOOL hidden;
|
||||
@property (atomic, assign, readwrite, getter=isLayerBacked) BOOL layerBacked;
|
||||
@property (atomic, assign, readwrite) BOOL automaticallyManagesSubnodes;
|
||||
- (id)controller;
|
||||
- (ASLayoutElementStyleYoga *)style;
|
||||
- (UIViewController *)closestViewController;
|
||||
- (UIView *)view;
|
||||
- (BOOL)isNodeLoaded;
|
||||
@end
|
||||
@@ -0,0 +1,8 @@
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
@interface ASLayoutElementStyleYoga : NSObject
|
||||
@property (nonatomic, assign, readwrite) CGFloat spacingBefore;
|
||||
@property (nonatomic, assign, readwrite) CGFloat spacingAfter;
|
||||
@property (nonatomic, assign, readwrite) CGFloat flexGrow;
|
||||
@property (nonatomic, assign, readwrite) CGFloat flexShrink;
|
||||
@end
|
||||
@@ -0,0 +1,6 @@
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
@interface ASNodeContext : NSObject
|
||||
- (instancetype)initWithOptions:(unsigned char)options;
|
||||
- (unsigned char)options;
|
||||
@end
|
||||
@@ -0,0 +1,4 @@
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
@interface ASNodeController : NSObject
|
||||
@end
|
||||
5
Tweaks/iSponsorBlock/Headers/YouTubeHeader/ASTextNode.h
Normal file
5
Tweaks/iSponsorBlock/Headers/YouTubeHeader/ASTextNode.h
Normal file
@@ -0,0 +1,5 @@
|
||||
#import "ASControlNode.h"
|
||||
|
||||
@interface ASTextNode : ASControlNode <UIGestureRecognizerDelegate>
|
||||
@property (atomic, copy, readwrite) NSAttributedString *attributedText;
|
||||
@end
|
||||
6
Tweaks/iSponsorBlock/Headers/YouTubeHeader/ELMCellNode.h
Normal file
6
Tweaks/iSponsorBlock/Headers/YouTubeHeader/ELMCellNode.h
Normal file
@@ -0,0 +1,6 @@
|
||||
#import "ASCellNode.h"
|
||||
#import "ELMElement.h"
|
||||
|
||||
@interface ELMCellNode : ASCellNode
|
||||
@property (atomic, strong, readwrite) ELMElement *element;
|
||||
@end
|
||||
@@ -0,0 +1,8 @@
|
||||
#import "ELMElement.h"
|
||||
#import "ASDisplayNode.h"
|
||||
|
||||
@interface ELMContainerNode : ASDisplayNode
|
||||
@property (atomic, strong, readwrite) ELMElement *element;
|
||||
- (void)addYogaChild:(id)child;
|
||||
- (void)addSubnode:(id)subnode;
|
||||
@end
|
||||
6
Tweaks/iSponsorBlock/Headers/YouTubeHeader/ELMElement.h
Normal file
6
Tweaks/iSponsorBlock/Headers/YouTubeHeader/ELMElement.h
Normal file
@@ -0,0 +1,6 @@
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
@interface ELMElement : NSObject
|
||||
- (id)newChildElementWithInstance:(const void *)instance;
|
||||
- (const void *)instance;
|
||||
@end
|
||||
@@ -0,0 +1,5 @@
|
||||
#import "ASNodeController.h"
|
||||
|
||||
@interface ELMNodeController : ASNodeController
|
||||
- (const void *)materializationContext;
|
||||
@end
|
||||
@@ -0,0 +1,6 @@
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
@interface ELMNodeFactory : NSObject
|
||||
+ (instancetype)sharedInstance;
|
||||
- (id)nodeWithElement:(id)element materializationContext:(const void *)context;
|
||||
@end
|
||||
7
Tweaks/iSponsorBlock/Headers/YouTubeHeader/ELMTextNode.h
Normal file
7
Tweaks/iSponsorBlock/Headers/YouTubeHeader/ELMTextNode.h
Normal file
@@ -0,0 +1,7 @@
|
||||
#import "ASTextNode.h"
|
||||
#import "ELMElement.h"
|
||||
|
||||
@interface ELMTextNode : ASTextNode
|
||||
@property (atomic, strong, readwrite) ELMElement *element;
|
||||
- (instancetype)initWithElement:(ELMElement *)element context:(id)context;
|
||||
@end
|
||||
@@ -0,0 +1,6 @@
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
@interface GIMBindingBuilder : NSObject
|
||||
- (instancetype)bindType:(Class)typeClass;
|
||||
- (instancetype)initializedWith:(id (^)(id))block;
|
||||
@end
|
||||
8
Tweaks/iSponsorBlock/Headers/YouTubeHeader/GIMMe.h
Normal file
8
Tweaks/iSponsorBlock/Headers/YouTubeHeader/GIMMe.h
Normal file
@@ -0,0 +1,8 @@
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
@interface GIMMe : NSObject
|
||||
+ (instancetype)gimme;
|
||||
- (instancetype)allocOf:(Class)cls;
|
||||
- (id)nullableInstanceForType:(id)type;
|
||||
- (id)instanceForType:(id)type;
|
||||
@end
|
||||
19
Tweaks/iSponsorBlock/Headers/YouTubeHeader/GOOAlertView.h
Normal file
19
Tweaks/iSponsorBlock/Headers/YouTubeHeader/GOOAlertView.h
Normal file
@@ -0,0 +1,19 @@
|
||||
#import "GOOModalView.h"
|
||||
|
||||
@interface GOOAlertView : GOOModalView
|
||||
@property (nonatomic, copy, readwrite) NSString *title;
|
||||
@property (nonatomic, readonly, strong) UILabel *titleLabel;
|
||||
@property (nonatomic, copy, readwrite) NSString *subtitle;
|
||||
@property (nonatomic, readonly, strong) UILabel *subtitleLabel;
|
||||
@property (nonatomic, readwrite, copy) UIImage *icon;
|
||||
+ (instancetype)dialog;
|
||||
+ (instancetype)infoDialog;
|
||||
+ (instancetype)confirmationDialog;
|
||||
+ (instancetype)confirmationDialogWithAction:(void (^)(void))action actionTitle:(NSString *)actionTitle;
|
||||
+ (instancetype)confirmationDialogWithAction:(void (^)(void))action actionTitle:(NSString *)actionTitle cancelTitle:(NSString *)cancelTitle;
|
||||
+ (instancetype)confirmationDialogWithAction:(void (^)(void))action actionTitle:(NSString *)actionTitle cancelAction:(void (^)(void))cancelAction cancelTitle:(NSString *)cancelTitle;
|
||||
+ (instancetype)confirmationDialogWithSelector:(SEL)selector actionTitle:(NSString *)actionTitle;
|
||||
+ (instancetype)confirmationDialogWithSelector:(SEL)selector actionTitle:(NSString *)actionTitle showsCancelButton:(BOOL)showsCancelButton;
|
||||
- (void)addCancelButton:(SEL)selector;
|
||||
- (void)addCancelButtonWithAction:(void (^)(void))action;
|
||||
@end
|
||||
18
Tweaks/iSponsorBlock/Headers/YouTubeHeader/GOOModalView.h
Normal file
18
Tweaks/iSponsorBlock/Headers/YouTubeHeader/GOOModalView.h
Normal file
@@ -0,0 +1,18 @@
|
||||
#import <UIKit/UIKit.h>
|
||||
|
||||
@interface GOOModalView : UIView
|
||||
@property (nonatomic, readwrite, weak) id target;
|
||||
@property (nonatomic, readwrite, assign) BOOL shouldDismissOnBackgroundTap;
|
||||
@property (nonatomic, readwrite, assign) BOOL shouldDismissOnApplicationBackground;
|
||||
- (instancetype)initWithTarget:(id)target;
|
||||
- (void)addTitle:(NSString *)title withAction:(void (^)(void))action;
|
||||
- (void)addTitle:(NSString *)title withDestructiveAction:(void (^)(void))action;
|
||||
- (void)addTitle:(NSString *)title withSelector:(SEL)selector;
|
||||
- (void)addTitle:(NSString *)title withCancelSelector:(SEL)cancelSelector;
|
||||
- (void)addTitle:(NSString *)title withDestructiveSelector:(SEL)cancelSelector;
|
||||
- (void)addTitle:(NSString *)title iconImage:(UIImage *)iconImage withAction:(void (^)(void))action;
|
||||
- (void)addTitle:(NSString *)title iconImage:(UIImage *)iconImage withSelector:(SEL)selector;
|
||||
- (void)show;
|
||||
- (void)cancel;
|
||||
- (void)dismiss;
|
||||
@end
|
||||
@@ -0,0 +1,5 @@
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
@interface GPBExtensionDescriptor : NSObject
|
||||
- (Class)msgClass;
|
||||
@end
|
||||
@@ -0,0 +1,5 @@
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
@interface GPBExtensionRegistry : NSObject
|
||||
- (void)addExtension:(id)extension;
|
||||
@end
|
||||
5
Tweaks/iSponsorBlock/Headers/YouTubeHeader/GPBMessage.h
Normal file
5
Tweaks/iSponsorBlock/Headers/YouTubeHeader/GPBMessage.h
Normal file
@@ -0,0 +1,5 @@
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
@interface GPBMessage : NSObject
|
||||
- (id)firstSubmessage;
|
||||
@end
|
||||
@@ -0,0 +1,5 @@
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
@interface HAMAsyncVTVideoDecoder : NSObject
|
||||
- (instancetype)initWithDelegate:(id)delegate delegateQueue:(id)delegateQueue decodeQueue:(id)decodeQueue formatDescription:(id)formatDescription pixelBufferAttributes:(id)pixelBufferAttributes;
|
||||
@end
|
||||
6
Tweaks/iSponsorBlock/Headers/YouTubeHeader/HAMMIMEType.h
Normal file
6
Tweaks/iSponsorBlock/Headers/YouTubeHeader/HAMMIMEType.h
Normal file
@@ -0,0 +1,6 @@
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
@interface HAMMIMEType : NSObject
|
||||
- (unsigned int)audioCodec;
|
||||
- (unsigned int)videoCodec;
|
||||
@end
|
||||
@@ -0,0 +1,2 @@
|
||||
@protocol HAMPixelBufferRenderingView
|
||||
@end
|
||||
@@ -0,0 +1,5 @@
|
||||
#import <AVKit/AVKit.h>
|
||||
|
||||
@interface HAMSBDLSampleBufferRenderingView : UIView
|
||||
@property (retain, nonatomic, readonly) AVSampleBufferDisplayLayer *displayLayer;
|
||||
@end
|
||||
21
Tweaks/iSponsorBlock/Headers/YouTubeHeader/LICENSE
Normal file
21
Tweaks/iSponsorBlock/Headers/YouTubeHeader/LICENSE
Normal file
@@ -0,0 +1,21 @@
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2021 - 2022 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.
|
||||
5
Tweaks/iSponsorBlock/Headers/YouTubeHeader/MLABRPolicy.h
Normal file
5
Tweaks/iSponsorBlock/Headers/YouTubeHeader/MLABRPolicy.h
Normal file
@@ -0,0 +1,5 @@
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
@interface MLABRPolicy : NSObject
|
||||
- (void)requestFormatReselection;
|
||||
@end
|
||||
@@ -0,0 +1,5 @@
|
||||
#import "MLFormat.h"
|
||||
|
||||
@interface MLABRPolicyFormatData : NSObject
|
||||
- (instancetype)initWithFormat:(MLFormat *)format;
|
||||
@end
|
||||
@@ -0,0 +1,5 @@
|
||||
#import "MLABRPolicy.h"
|
||||
|
||||
// YouTube 17.30.3 and higher
|
||||
@interface MLABRPolicyNew : MLABRPolicy
|
||||
@end
|
||||
@@ -0,0 +1,5 @@
|
||||
#import "MLABRPolicy.h"
|
||||
|
||||
// YouTube 17.30.3 and higher
|
||||
@interface MLABRPolicyOld : MLABRPolicy
|
||||
@end
|
||||
@@ -0,0 +1,5 @@
|
||||
#import <AVKit/AVKit.h>
|
||||
|
||||
@interface MLAVAssetPlayer : NSObject
|
||||
- (AVPlayerItem *)playerItem;
|
||||
@end
|
||||
@@ -0,0 +1,8 @@
|
||||
#import "MLAVPlayerLayerView.h"
|
||||
#import "MLAVPlayer.h"
|
||||
#import "MLAVPlayerViewDelegate.h"
|
||||
|
||||
@interface MLAVPIPPlayerLayerView : MLAVPlayerLayerView
|
||||
@property (nonatomic, readonly, strong) AVPlayerLayer *playerLayer;
|
||||
@property (nonatomic, readwrite, weak) NSObject <MLAVPlayerViewDelegate> *delegate;
|
||||
@end
|
||||
15
Tweaks/iSponsorBlock/Headers/YouTubeHeader/MLAVPlayer.h
Normal file
15
Tweaks/iSponsorBlock/Headers/YouTubeHeader/MLAVPlayer.h
Normal file
@@ -0,0 +1,15 @@
|
||||
#import "MLAVAssetPlayer.h"
|
||||
#import "MLInnerTubePlayerConfig.h"
|
||||
#import "MLPlayerViewProtocol.h"
|
||||
#import "MLPlayerStickySettings.h"
|
||||
|
||||
@interface MLAVPlayer : NSObject
|
||||
@property (nonatomic, readwrite, assign) BOOL active;
|
||||
@property (nonatomic, readonly, assign) BOOL externalPlaybackActive;
|
||||
@property (nonatomic, readwrite, assign) float rate;
|
||||
@property (nonatomic, readonly, strong) MLVideo *video;
|
||||
@property (nonatomic, readonly, strong) MLInnerTubePlayerConfig *config;
|
||||
@property (nonatomic, readonly, strong) MLAVAssetPlayer *assetPlayer;
|
||||
@property (nonatomic, readwrite, strong) UIView <MLPlayerViewProtocol> *renderingView;
|
||||
- (instancetype)initWithVideo:(MLVideo *)video playerConfig:(MLInnerTubePlayerConfig *)playerConfig stickySettings:(MLPlayerStickySettings *)stickySettings externalPlaybackActive:(BOOL)externalPlaybackActive;
|
||||
@end
|
||||
@@ -0,0 +1,5 @@
|
||||
#import "MLPlayerViewProtocol.h"
|
||||
#import "HAMPixelBufferRenderingView.h"
|
||||
|
||||
@interface MLAVPlayerLayerView : UIView <MLPlayerViewProtocol, HAMPixelBufferRenderingView>
|
||||
@end
|
||||
@@ -0,0 +1,6 @@
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
@protocol MLAVPlayerViewDelegate <NSObject>
|
||||
@required
|
||||
- (void)playerViewErrorDidOccur:(id)arg1;
|
||||
@end
|
||||
@@ -0,0 +1,10 @@
|
||||
#include "GIMMe.h"
|
||||
#import "MLVideo.h"
|
||||
#import "MLInnerTubePlayerConfig.h"
|
||||
#import "MLAVPlayerLayerView.h"
|
||||
|
||||
@interface MLDefaultPlayerViewFactory : NSObject
|
||||
@property (nonatomic, weak, readwrite) GIMMe *gimme;
|
||||
- (BOOL)canUsePlayerView:(UIView *)playerView forVideo:(MLVideo *)video playerConfig:(MLInnerTubePlayerConfig *)config;
|
||||
- (MLAVPlayerLayerView *)AVPlayerViewForVideo:(MLVideo *)video playerConfig:(MLInnerTubePlayerConfig *)config;
|
||||
@end
|
||||
17
Tweaks/iSponsorBlock/Headers/YouTubeHeader/MLFormat.h
Normal file
17
Tweaks/iSponsorBlock/Headers/YouTubeHeader/MLFormat.h
Normal file
@@ -0,0 +1,17 @@
|
||||
#import <CoreGraphics/CoreGraphics.h>
|
||||
#import "HAMMIMEType.h"
|
||||
#import "YTIFormatStream.h"
|
||||
|
||||
@interface MLFormat : NSObject <NSCopying>
|
||||
- (HAMMIMEType *)MIMEType;
|
||||
- (YTIFormatStream *)formatStream;
|
||||
- (NSURL *)URL;
|
||||
- (int)width;
|
||||
- (int)height;
|
||||
- (int)singleDimensionResolution;
|
||||
- (CGFloat)FPS;
|
||||
- (BOOL)isAudio;
|
||||
- (BOOL)isVideo;
|
||||
- (BOOL)isText;
|
||||
- (NSInteger)compareByQuality:(MLFormat *)format;
|
||||
@end
|
||||
7
Tweaks/iSponsorBlock/Headers/YouTubeHeader/MLHAMPlayer.h
Normal file
7
Tweaks/iSponsorBlock/Headers/YouTubeHeader/MLHAMPlayer.h
Normal file
@@ -0,0 +1,7 @@
|
||||
#import "MLVideo.h"
|
||||
#import "MLInnerTubePlayerConfig.h"
|
||||
#import "MLPlayerStickySettings.h"
|
||||
|
||||
@interface MLHAMPlayer : NSObject
|
||||
- (instancetype)initWithVideo:(MLVideo *)video playerConfig:(MLInnerTubePlayerConfig *)playerConfig stickySettings:(MLPlayerStickySettings *)stickySettings playerViewProvider:(id)playerViewProvider;
|
||||
@end
|
||||
@@ -0,0 +1,8 @@
|
||||
#import "MLABRPolicy.h"
|
||||
#import "MLFormat.h"
|
||||
#import "MLInnerTubePlayerConfig.h"
|
||||
|
||||
@interface MLHAMPlayerItem : NSObject
|
||||
@property (nonatomic, readonly, strong) MLInnerTubePlayerConfig *config;
|
||||
- (void)ABRPolicy:(MLABRPolicy *)policy selectableFormatsDidChange:(NSArray <MLFormat *> *)formats;
|
||||
@end
|
||||
@@ -0,0 +1,7 @@
|
||||
#import "MLVideo.h"
|
||||
#import "MLInnerTubePlayerConfig.h"
|
||||
|
||||
@protocol MLHAMPlayerViewProtocol
|
||||
- (void)makeActivePlayer;
|
||||
- (void)setVideo:(MLVideo *)video playerConfig:(MLInnerTubePlayerConfig *)playerConfig;
|
||||
@end
|
||||
@@ -0,0 +1,4 @@
|
||||
#import "MLHAMPlayer.h"
|
||||
|
||||
@interface MLHAMQueuePlayer : MLHAMPlayer
|
||||
@end
|
||||
@@ -0,0 +1,4 @@
|
||||
#import "HAMSBDLSampleBufferRenderingView.h"
|
||||
|
||||
@interface MLHAMSBDLSampleBufferRenderingView : HAMSBDLSampleBufferRenderingView
|
||||
@end
|
||||
@@ -0,0 +1,7 @@
|
||||
#import "YTIMediaCommonConfig.h"
|
||||
#import "YTIHamplayerConfig.h"
|
||||
|
||||
@interface MLInnerTubePlayerConfig : NSObject
|
||||
@property (nonatomic, readonly, strong) YTIMediaCommonConfig *mediaCommonConfig;
|
||||
@property (nonatomic, readonly, strong) YTIHamplayerConfig *hamplayerConfig;
|
||||
@end
|
||||
@@ -0,0 +1,4 @@
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
@interface MLOnesieVideoData : NSObject
|
||||
@end
|
||||
24
Tweaks/iSponsorBlock/Headers/YouTubeHeader/MLPIPController.h
Normal file
24
Tweaks/iSponsorBlock/Headers/YouTubeHeader/MLPIPController.h
Normal file
@@ -0,0 +1,24 @@
|
||||
#import <AVKit/AVKit.h>
|
||||
#import "MLAVPIPPlayerLayerView.h"
|
||||
#import "MLHAMSBDLSampleBufferRenderingView.h"
|
||||
|
||||
@interface MLPIPController : NSObject <AVPictureInPictureControllerDelegate, AVPictureInPictureSampleBufferPlaybackDelegate>
|
||||
@property (nonatomic, strong, readwrite) MLAVPIPPlayerLayerView *AVPlayerView;
|
||||
@property (nonatomic, strong, readwrite) MLHAMSBDLSampleBufferRenderingView *HAMPlayerView;
|
||||
- (instancetype)initWithPlaceholderPlayerItem:(AVPlayerItem *)playerItem; // Deprecated
|
||||
- (instancetype)initWithPlaceholderPlayerItemResourcePath:(NSString *)placeholderPath; // Deprecated
|
||||
- (AVPictureInPictureControllerContentSource *)newContentSource API_AVAILABLE(ios(15.0));
|
||||
- (BOOL)isPictureInPictureSupported;
|
||||
- (BOOL)isPictureInPictureActive; // Deprecated
|
||||
- (BOOL)pictureInPictureActive;
|
||||
- (BOOL)contentSourceNeedsRefresh;
|
||||
- (CGSize)renderSizeForView:(UIView *)view;
|
||||
- (BOOL)startPictureInPicture; // Deprecated
|
||||
- (void)stopPictureInPicture; // Deprecated
|
||||
- (void)addPIPControllerObserver:(id)observer;
|
||||
- (void)activatePiPController;
|
||||
- (void)deactivatePiPController;
|
||||
- (void)pictureInPictureControllerStartPlayback;
|
||||
- (void)pictureInPictureControllerStopPlayback;
|
||||
- (void)pause;
|
||||
@end
|
||||
@@ -0,0 +1,8 @@
|
||||
#import "GIMMe.h"
|
||||
#import "MLVideo.h"
|
||||
#import "MLInnerTubePlayerConfig.h"
|
||||
|
||||
@interface MLPlayerPool : NSObject
|
||||
@property (nonatomic, weak, readwrite) GIMMe *gimme;
|
||||
- (void)createHamResourcesForVideo:(MLVideo *)video playerConfig:(MLInnerTubePlayerConfig *)playerConfig;
|
||||
@end
|
||||
@@ -0,0 +1,4 @@
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
@interface MLPlayerPoolImpl : NSObject
|
||||
@end
|
||||
@@ -0,0 +1,5 @@
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
@interface MLPlayerStickySettings : NSObject
|
||||
@property (assign) float rate;
|
||||
@end
|
||||
@@ -0,0 +1,7 @@
|
||||
#import "MLVideo.h"
|
||||
#import "MLInnerTubePlayerConfig.h"
|
||||
|
||||
@protocol MLPlayerViewProtocol
|
||||
- (void)makeActivePlayer;
|
||||
- (void)setVideo:(MLVideo *)video playerConfig:(MLInnerTubePlayerConfig *)playerConfig;
|
||||
@end
|
||||
@@ -0,0 +1,4 @@
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
@interface MLQOEPingController : NSObject
|
||||
@end
|
||||
@@ -0,0 +1,6 @@
|
||||
#import "YTIFormatStream.h"
|
||||
#import "MLFormat.h"
|
||||
|
||||
@interface MLRemoteStream : MLFormat
|
||||
+ (instancetype)streamWithFormatStream:(YTIFormatStream *)formatStream;
|
||||
@end
|
||||
@@ -0,0 +1,5 @@
|
||||
#import "MLRemoteStream.h"
|
||||
|
||||
@interface MLStreamingData : NSObject
|
||||
- (NSArray <MLRemoteStream *> *)adaptiveStreams;
|
||||
@end
|
||||
7
Tweaks/iSponsorBlock/Headers/YouTubeHeader/MLVideo.h
Normal file
7
Tweaks/iSponsorBlock/Headers/YouTubeHeader/MLVideo.h
Normal file
@@ -0,0 +1,7 @@
|
||||
#import "MLStreamingData.h"
|
||||
#import "YTIVideoDetails.h"
|
||||
|
||||
@interface MLVideo : NSObject
|
||||
- (MLStreamingData *)streamingData;
|
||||
- (YTIVideoDetails *)videoDetails;
|
||||
@end
|
||||
@@ -0,0 +1,4 @@
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
@interface MLVideoDecoderFactory : NSObject
|
||||
@end
|
||||
6
Tweaks/iSponsorBlock/Headers/YouTubeHeader/QTMIcon.h
Normal file
6
Tweaks/iSponsorBlock/Headers/YouTubeHeader/QTMIcon.h
Normal file
@@ -0,0 +1,6 @@
|
||||
#import <UIKit/UIKit.h>
|
||||
|
||||
@interface QTMIcon : NSObject
|
||||
+ (UIImage *)imageWithName:(NSString *)name color:(UIColor *)color;
|
||||
+ (UIImage *)tintImage:(UIImage *)image color:(UIColor *)color;
|
||||
@end
|
||||
3
Tweaks/iSponsorBlock/Headers/YouTubeHeader/README.md
Normal file
3
Tweaks/iSponsorBlock/Headers/YouTubeHeader/README.md
Normal file
@@ -0,0 +1,3 @@
|
||||
# YouTubeHeader
|
||||
|
||||
Headers for iOS YouTube app.
|
||||
@@ -0,0 +1,5 @@
|
||||
#import "ASDisplayNode.h"
|
||||
|
||||
@interface UIView (AsyncDisplayKit)
|
||||
- (void)addSubnode:(ASDisplayNode *)subnode;
|
||||
@end
|
||||
@@ -0,0 +1,5 @@
|
||||
#import <UIKit/UIKit.h>
|
||||
|
||||
@interface UIView (YouTube)
|
||||
- (BOOL)yt_isVisible;
|
||||
@end
|
||||
@@ -0,0 +1,8 @@
|
||||
#import <UIKit/UIKit.h>
|
||||
|
||||
@interface YTActionSheetAction : NSObject
|
||||
+ (instancetype)actionWithTitle:(NSString *)title style:(NSInteger)style handler:(void (^)(YTActionSheetAction *))handler;
|
||||
+ (instancetype)actionWithTitle:(NSString *)title iconImage:(UIImage *)iconImage style:(NSInteger)style handler:(void (^)(YTActionSheetAction *))handler;
|
||||
+ (instancetype)actionWithTitle:(NSString *)title subtitle:(NSString *)subtitle iconImage:(UIImage *)iconImage handler:(void (^)(YTActionSheetAction *))handler;
|
||||
+ (instancetype)actionWithTitle:(NSString *)title subtitle:(NSString *)subtitle iconImage:(UIImage *)iconImage accessibilityIdentifier:(NSString *)accessibilityIdentifier handler:(void (^)(YTActionSheetAction *))handler;
|
||||
@end
|
||||
@@ -0,0 +1,7 @@
|
||||
#import <UIKit/UIKit.h>
|
||||
|
||||
@interface YTActionSheetController : NSObject
|
||||
+ (instancetype)actionSheetController;
|
||||
- (void)addCancelActionIfNeeded;
|
||||
- (void)presentFromViewController:(UIViewController *)viewController animated:(BOOL)animated completion:(void (^)(void))completion;
|
||||
@end
|
||||
6
Tweaks/iSponsorBlock/Headers/YouTubeHeader/YTAlertView.h
Normal file
6
Tweaks/iSponsorBlock/Headers/YouTubeHeader/YTAlertView.h
Normal file
@@ -0,0 +1,6 @@
|
||||
#import "GOOAlertView.h"
|
||||
|
||||
@interface YTAlertView : GOOAlertView
|
||||
+ (instancetype)yt_dialog;
|
||||
- (void)removeDefaultPadding;
|
||||
@end
|
||||
@@ -0,0 +1,4 @@
|
||||
#import <UIKit/UIKit.h>
|
||||
|
||||
@interface YTAppDelegate : UIResponder
|
||||
@end
|
||||
@@ -0,0 +1,4 @@
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
@interface YTAppSettingsSectionItemActionController : NSObject
|
||||
@end
|
||||
@@ -0,0 +1,9 @@
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
@interface YTAppSettingsStore : NSObject
|
||||
+ (NSUInteger)valueTypeForSetting:(int)setting;
|
||||
- (void)setValue:(NSNumber *)value forSetting:(int)setting;
|
||||
- (void)setBool:(BOOL)value forSetting:(int)setting;
|
||||
- (NSNumber *)valueForSetting:(int)setting;
|
||||
- (BOOL)boolForSetting:(int)setting;
|
||||
@end
|
||||
@@ -0,0 +1,4 @@
|
||||
#import "ASCollectionView.h"
|
||||
|
||||
@interface YTAsyncCollectionView : ASCollectionView
|
||||
@end
|
||||
@@ -0,0 +1,4 @@
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
@interface YTAutonavEndscreenController : NSObject
|
||||
@end
|
||||
@@ -0,0 +1,5 @@
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
@interface YTBackgroundabilityPolicy : NSObject
|
||||
- (void)addBackgroundabilityPolicyObserver:(id)observer;
|
||||
@end
|
||||
@@ -0,0 +1,6 @@
|
||||
#import <Foundation/Foundation.h>
|
||||
#import "YTCollectionViewCellProtocol.h"
|
||||
|
||||
@interface YTCellController : NSObject
|
||||
@property (nonatomic, weak, readwrite) UICollectionViewCell <YTCollectionViewCellProtocol> *cell;
|
||||
@end
|
||||
@@ -0,0 +1,4 @@
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
@interface YTColdConfig : NSObject
|
||||
@end
|
||||
@@ -0,0 +1,5 @@
|
||||
#import "YTSlideForActionsView.h"
|
||||
|
||||
@interface YTCollectionViewCell : UICollectionViewCell
|
||||
@property (nonatomic, strong, readwrite) YTSlideForActionsView *slideForActionsView;
|
||||
@end
|
||||
@@ -0,0 +1,2 @@
|
||||
@protocol YTCollectionViewCellProtocol
|
||||
@end
|
||||
8
Tweaks/iSponsorBlock/Headers/YouTubeHeader/YTColor.h
Normal file
8
Tweaks/iSponsorBlock/Headers/YouTubeHeader/YTColor.h
Normal file
@@ -0,0 +1,8 @@
|
||||
#import <UIKit/UIKit.h>
|
||||
|
||||
@interface YTColor : NSObject
|
||||
+ (UIColor *)white1;
|
||||
+ (UIColor *)black1;
|
||||
+ (UIColor *)black2;
|
||||
+ (UIColor *)black3;
|
||||
@end
|
||||
67
Tweaks/iSponsorBlock/Headers/YouTubeHeader/YTColorPalette.h
Normal file
67
Tweaks/iSponsorBlock/Headers/YouTubeHeader/YTColorPalette.h
Normal file
@@ -0,0 +1,67 @@
|
||||
#import <Foundation/Foundation.h>
|
||||
#import <UIKit/UIKit.h>
|
||||
|
||||
// Deprecated, use YTCommonColorPalette
|
||||
@interface YTColorPalette : NSObject
|
||||
+ (instancetype)lightPalette;
|
||||
+ (instancetype)darkPalette;
|
||||
+ (instancetype)colorPaletteForPageStyle:(NSInteger)pageStyle;
|
||||
- (NSInteger)pageStyle;
|
||||
- (UIColor *)background1;
|
||||
- (UIColor *)background2;
|
||||
- (UIColor *)background3;
|
||||
- (UIColor *)brandBackgroundSolid;
|
||||
- (UIColor *)brandBackgroundPrimary;
|
||||
- (UIColor *)brandBackgroundSecondary;
|
||||
- (UIColor *)generalBackgroundA;
|
||||
- (UIColor *)generalBackgroundB;
|
||||
- (UIColor *)generalBackgroundC;
|
||||
- (UIColor *)errorBackground;
|
||||
- (UIColor *)textPrimary;
|
||||
- (UIColor *)textSecondary;
|
||||
- (UIColor *)textDisabled;
|
||||
- (UIColor *)textPrimaryInverse;
|
||||
- (UIColor *)callToAction;
|
||||
- (UIColor *)iconActive;
|
||||
- (UIColor *)iconActiveOther;
|
||||
- (UIColor *)iconInactive;
|
||||
- (UIColor *)iconDisabled;
|
||||
- (UIColor *)badgeChipBackground;
|
||||
- (UIColor *)buttonChipBackgroundHover;
|
||||
- (UIColor *)touchResponse;
|
||||
- (UIColor *)callToActionInverse;
|
||||
- (UIColor *)brandIconActive;
|
||||
- (UIColor *)brandIconInactive;
|
||||
- (UIColor *)brandButtonBackground;
|
||||
- (UIColor *)brandLinkText;
|
||||
- (UIColor *)tenPercentLayer;
|
||||
- (UIColor *)snackbarBackground;
|
||||
- (UIColor *)themedBlue;
|
||||
- (UIColor *)themedGreen;
|
||||
- (UIColor *)staticBrandRed;
|
||||
- (UIColor *)staticBrandWhite;
|
||||
- (UIColor *)staticBrandBlack;
|
||||
- (UIColor *)staticClearColor;
|
||||
- (UIColor *)staticAdYellow;
|
||||
- (UIColor *)staticGrey;
|
||||
- (UIColor *)overlayBackgroundSolid;
|
||||
- (UIColor *)overlayBackgroundHeavy;
|
||||
- (UIColor *)overlayBackgroundMedium;
|
||||
- (UIColor *)overlayBackgroundMediumLight;
|
||||
- (UIColor *)overlayBackgroundLight;
|
||||
- (UIColor *)overlayTextPrimary;
|
||||
- (UIColor *)overlayTextSecondary;
|
||||
- (UIColor *)overlayTextTertiary;
|
||||
- (UIColor *)overlayIconActiveCallToAction;
|
||||
- (UIColor *)overlayIconActiveOther;
|
||||
- (UIColor *)overlayIconInactive;
|
||||
- (UIColor *)overlayIconDisabled;
|
||||
- (UIColor *)overlayFilledButtonActive;
|
||||
- (UIColor *)overlayButtonSecondary;
|
||||
- (UIColor *)overlayButtonPrimary;
|
||||
- (UIColor *)overlayBackgroundBrand;
|
||||
- (UIColor *)overlayBackgroundClear;
|
||||
- (UIColor *)verifiedBadgeBackground;
|
||||
- (UIColor *)themedOverlayBackground;
|
||||
- (UIColor *)adIndicator;
|
||||
@end
|
||||
@@ -0,0 +1,74 @@
|
||||
#import <Foundation/Foundation.h>
|
||||
#import <UIKit/UIKit.h>
|
||||
|
||||
// YouTube 17.19.2 and higher
|
||||
@interface YTCommonColorPalette : NSObject
|
||||
+ (instancetype)lightPalette;
|
||||
+ (instancetype)darkPalette;
|
||||
- (NSInteger)pageStyle;
|
||||
- (UIColor *)background1;
|
||||
- (UIColor *)background2;
|
||||
- (UIColor *)background3;
|
||||
- (UIColor *)staticBlue;
|
||||
- (UIColor *)brandBackgroundSolid;
|
||||
- (UIColor *)brandBackgroundPrimary;
|
||||
- (UIColor *)brandBackgroundSecondary;
|
||||
- (UIColor *)generalBackgroundA;
|
||||
- (UIColor *)generalBackgroundB;
|
||||
- (UIColor *)generalBackgroundC;
|
||||
- (UIColor *)errorBackground;
|
||||
- (UIColor *)textPrimary;
|
||||
- (UIColor *)textSecondary;
|
||||
- (UIColor *)textDisabled;
|
||||
- (UIColor *)textPrimaryInverse;
|
||||
- (UIColor *)callToAction;
|
||||
- (UIColor *)iconActive;
|
||||
- (UIColor *)iconActiveOther;
|
||||
- (UIColor *)iconInactive;
|
||||
- (UIColor *)iconDisabled;
|
||||
- (UIColor *)badgeChipBackground;
|
||||
- (UIColor *)buttonChipBackgroundHover;
|
||||
- (UIColor *)touchResponse;
|
||||
- (UIColor *)callToActionInverse;
|
||||
- (UIColor *)brandIconActive;
|
||||
- (UIColor *)brandIconInactive;
|
||||
- (UIColor *)brandButtonBackground;
|
||||
- (UIColor *)brandLinkText;
|
||||
- (UIColor *)tenPercentLayer;
|
||||
- (UIColor *)snackbarBackground;
|
||||
- (UIColor *)themedBlue;
|
||||
- (UIColor *)themedGreen;
|
||||
- (UIColor *)staticBrandRed;
|
||||
- (UIColor *)staticBrandWhite;
|
||||
- (UIColor *)staticBrandBlack;
|
||||
- (UIColor *)staticClearColor;
|
||||
- (UIColor *)staticAdYellow;
|
||||
- (UIColor *)staticGrey;
|
||||
- (UIColor *)overlayBackgroundSolid;
|
||||
- (UIColor *)overlayBackgroundHeavy;
|
||||
- (UIColor *)overlayBackgroundMedium;
|
||||
- (UIColor *)overlayBackgroundMediumLight;
|
||||
- (UIColor *)overlayBackgroundLight;
|
||||
- (UIColor *)overlayTextPrimary;
|
||||
- (UIColor *)overlayTextSecondary;
|
||||
- (UIColor *)overlayTextTertiary;
|
||||
- (UIColor *)overlayIconActiveCallToAction;
|
||||
- (UIColor *)overlayIconActiveOther;
|
||||
- (UIColor *)overlayIconInactive;
|
||||
- (UIColor *)overlayIconDisabled;
|
||||
- (UIColor *)overlayFilledButtonActive;
|
||||
- (UIColor *)overlayButtonSecondary;
|
||||
- (UIColor *)overlayButtonPrimary;
|
||||
- (UIColor *)overlayBackgroundBrand;
|
||||
- (UIColor *)overlayBackgroundClear;
|
||||
- (UIColor *)verifiedBadgeBackground;
|
||||
- (UIColor *)themedOverlayBackground;
|
||||
- (UIColor *)adIndicator;
|
||||
- (UIColor *)errorIndicator; // 17.52.1+
|
||||
- (UIColor *)baseBackground; // 17.52.1+
|
||||
- (UIColor *)raisedBackground; // 17.52.1+
|
||||
- (UIColor *)menuBackground; // 17.52.1+
|
||||
- (UIColor *)invertedBackground; // 17.52.1+
|
||||
- (UIColor *)additiveBackground; // 17.52.1+
|
||||
- (UIColor *)outline; // 17.52.1+
|
||||
@end
|
||||
14
Tweaks/iSponsorBlock/Headers/YouTubeHeader/YTCommonUtils.h
Normal file
14
Tweaks/iSponsorBlock/Headers/YouTubeHeader/YTCommonUtils.h
Normal file
@@ -0,0 +1,14 @@
|
||||
#import <Foundation/Foundation.h>
|
||||
#import <UIKit/UIKit.h>
|
||||
|
||||
@interface YTCommonUtils : NSObject
|
||||
+ (BOOL)isIPhoneWithNotch;
|
||||
+ (BOOL)isIPad;
|
||||
+ (BOOL)isSmallDevice;
|
||||
+ (BOOL)isAppRunningInFullScreen;
|
||||
+ (unsigned int)uniformRandomWithUpperBound:(unsigned int)upperBound;
|
||||
+ (UIWindow *)mainWindow; // YTMainWindow
|
||||
+ (NSBundle *)bundleForClass:(Class)cls;
|
||||
+ (NSBundle *)resourceBundleForModuleName:(NSString *)module appBundle:(NSBundle *)appBundle;
|
||||
+ (NSString *)hardwareModel;
|
||||
@end
|
||||
@@ -0,0 +1,4 @@
|
||||
#import "YTMainAppVideoPlayerOverlayView.h"
|
||||
|
||||
@interface YTContentVideoPlayerOverlayView : YTMainAppVideoPlayerOverlayView
|
||||
@end
|
||||
@@ -0,0 +1,5 @@
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
@interface YTELMContext : NSObject
|
||||
- (id)parentResponder;
|
||||
@end
|
||||
@@ -0,0 +1,5 @@
|
||||
#import <UIKit/UIKit.h>
|
||||
|
||||
@interface YTEditResources : NSObject
|
||||
+ (UIImage *)volumeControlsFeatureTabIconMuted:(BOOL)muted templateImage:(BOOL)templateImage;
|
||||
@end
|
||||
@@ -0,0 +1,6 @@
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
@interface YTFullscreenEngagementActionBarButtonRenderer : NSObject
|
||||
- (BOOL)isLikeButton;
|
||||
- (BOOL)isDislikeButton;
|
||||
@end
|
||||
@@ -0,0 +1,7 @@
|
||||
#import "Block.h"
|
||||
#import "YTIFormattedStringLabel.h"
|
||||
|
||||
@interface YTFullscreenEngagementActionBarButtonView : UIView
|
||||
@property (nonatomic, assign, readwrite, getter=isToggled) BOOL toggled;
|
||||
@property (nonatomic, strong, readwrite) YTIFormattedStringLabel *label;
|
||||
@end
|
||||
@@ -0,0 +1,4 @@
|
||||
#import <UIKit/UIKit.h>
|
||||
|
||||
@interface YTGlassContainerView : UIView
|
||||
@end
|
||||
@@ -0,0 +1,4 @@
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
@interface YTGlobalConfig : NSObject
|
||||
@end
|
||||
9
Tweaks/iSponsorBlock/Headers/YouTubeHeader/YTHotConfig.h
Normal file
9
Tweaks/iSponsorBlock/Headers/YouTubeHeader/YTHotConfig.h
Normal file
@@ -0,0 +1,9 @@
|
||||
#import "YTIHotConfigGroup.h"
|
||||
#import "YTIHamplayerHotConfig.h"
|
||||
|
||||
@interface YTHotConfig : NSObject
|
||||
@property (atomic, strong, readwrite) YTIHotConfigGroup *hotConfigGroup;
|
||||
- (YTIIosMediaHotConfig *)mediaHotConfig;
|
||||
- (YTIHamplayerHotConfig *)hamplayerHotConfig;
|
||||
- (BOOL)iosReleasePipControllerOnMain;
|
||||
@end
|
||||
@@ -0,0 +1,5 @@
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
@interface YTIAccessibilityData : NSObject
|
||||
@property (nonatomic, copy, readwrite) NSString *label;
|
||||
@end
|
||||
@@ -0,0 +1,5 @@
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
@interface YTIBrowseEndpoint : NSObject
|
||||
@property (nonatomic, copy, readwrite) NSString *browseId;
|
||||
@end
|
||||
@@ -0,0 +1,12 @@
|
||||
#import "GPBMessage.h"
|
||||
|
||||
@interface YTIBrowseRequest : GPBMessage
|
||||
+ (NSString *)browseIDForExploreTab;
|
||||
+ (NSString *)browseIDForAccountTab;
|
||||
+ (NSString *)browseIDForActivityTab;
|
||||
+ (NSString *)browseIDForHomeTab;
|
||||
+ (NSString *)browseIDForLibraryTab;
|
||||
+ (NSString *)browseIDForTrendingTab;
|
||||
+ (NSString *)browseIDForSubscriptionsTab;
|
||||
+ (NSString *)browseIDForWhatToWatch;
|
||||
@end
|
||||
@@ -0,0 +1,15 @@
|
||||
#import "YTICommand.h"
|
||||
#import "YTIFormattedString.h"
|
||||
#import "YTIIcon.h"
|
||||
|
||||
@interface YTIButtonRenderer : NSObject
|
||||
@property (nonatomic, strong, readwrite) YTICommand *command;
|
||||
@property (nonatomic, strong, readwrite) YTIIcon *icon;
|
||||
@property (nonatomic, strong, readwrite) YTICommand *navigationEndpoint;
|
||||
@property (nonatomic, copy, readwrite) NSString *targetId;
|
||||
@property (nonatomic, strong, readwrite) YTIFormattedString *text;
|
||||
@property (nonatomic, copy, readwrite) NSString *tooltip;
|
||||
@property (nonatomic, assign, readwrite) int size;
|
||||
@property (nonatomic, assign, readwrite) int style;
|
||||
@property (nonatomic, assign, readwrite) BOOL isDisabled;
|
||||
@end
|
||||
@@ -0,0 +1,7 @@
|
||||
#import "YTIToggleButtonRenderer.h"
|
||||
#import "YTIButtonRenderer.h"
|
||||
|
||||
@interface YTIButtonSupportedRenderers : NSObject
|
||||
@property (nonatomic, strong, readwrite) YTIToggleButtonRenderer *toggleButtonRenderer;
|
||||
@property (retain, nonatomic) YTIButtonRenderer *buttonRenderer;
|
||||
@end
|
||||
@@ -0,0 +1,7 @@
|
||||
#import "GPBMessage.h"
|
||||
#import "YTIFormattedString.h"
|
||||
|
||||
@interface YTIChapterRenderer : GPBMessage
|
||||
@property (nonatomic, readwrite, strong) YTIFormattedString *title;
|
||||
@property (nonatomic, readwrite, assign) int timeRangeStartMillis;
|
||||
@end
|
||||
@@ -0,0 +1,5 @@
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
@interface YTIColorInfo : NSObject
|
||||
@property (nonatomic, assign, readwrite) int transferCharacteristics;
|
||||
@end
|
||||
7
Tweaks/iSponsorBlock/Headers/YouTubeHeader/YTICommand.h
Normal file
7
Tweaks/iSponsorBlock/Headers/YouTubeHeader/YTICommand.h
Normal file
@@ -0,0 +1,7 @@
|
||||
#import "YTIReelWatchEndpoint.h"
|
||||
#import "YTIBrowseEndpoint.h"
|
||||
|
||||
@interface YTICommand : NSObject
|
||||
@property (nonatomic, readwrite, strong) YTIReelWatchEndpoint *reelWatchEndpoint;
|
||||
@property (nonatomic, readwrite, strong) YTIBrowseEndpoint *browseEndpoint;
|
||||
@end
|
||||
@@ -0,0 +1,6 @@
|
||||
#import "YTIElementRendererCompatibilityOptions.h"
|
||||
|
||||
@interface YTIElementRenderer : GPBMessage
|
||||
@property (nonatomic, strong, readwrite) YTIElementRendererCompatibilityOptions *compatibilityOptions;
|
||||
@property (nonatomic, assign, readwrite) BOOL hasCompatibilityOptions;
|
||||
@end
|
||||
@@ -0,0 +1,5 @@
|
||||
#import "GPBMessage.h"
|
||||
|
||||
@interface YTIElementRendererCompatibilityOptions : GPBMessage
|
||||
@property (nonatomic, assign, readwrite) BOOL hasAdLoggingData;
|
||||
@end
|
||||
@@ -0,0 +1,6 @@
|
||||
#import "YTIColorInfo.h"
|
||||
|
||||
@interface YTIFormatStream : NSObject
|
||||
@property (nonatomic, strong, readwrite) YTIColorInfo *colorInfo;
|
||||
@property (nonatomic, copy, readwrite) NSString *URL;
|
||||
@end
|
||||
@@ -0,0 +1,8 @@
|
||||
#import "YTIFormattedStringSupportedAccessibilityDatas.h"
|
||||
|
||||
@interface YTIFormattedString : NSObject
|
||||
+ (instancetype)formattedStringWithString:(NSString *)string;
|
||||
@property (nonatomic, strong, readwrite) NSMutableArray *runsArray;
|
||||
@property (nonatomic, strong, readwrite) YTIFormattedStringSupportedAccessibilityDatas *accessibility;
|
||||
- (NSString *)stringWithFormattingRemoved;
|
||||
@end
|
||||
@@ -0,0 +1,7 @@
|
||||
#import <UIKit/UIKit.h>
|
||||
#import "YTIFormattedString.h"
|
||||
|
||||
@interface YTIFormattedStringLabel : UILabel
|
||||
@property (nonatomic, copy, readwrite) NSAttributedString *attributedText;
|
||||
- (void)setFormattedString:(YTIFormattedString *)string;
|
||||
@end
|
||||
@@ -0,0 +1,5 @@
|
||||
#import "YTIAccessibilityData.h"
|
||||
|
||||
@interface YTIFormattedStringSupportedAccessibilityDatas : NSObject
|
||||
@property (nonatomic, strong, readwrite) YTIAccessibilityData *accessibilityData;
|
||||
@end
|
||||
@@ -0,0 +1,5 @@
|
||||
#import "YTIGuideResponseSupportedRenderers.h"
|
||||
|
||||
@interface YTIGuideResponse : NSObject
|
||||
- (NSMutableArray <YTIGuideResponseSupportedRenderers *> *)itemsArray;
|
||||
@end
|
||||
@@ -0,0 +1,5 @@
|
||||
#import "YTIPivotBarRenderer.h"
|
||||
|
||||
@interface YTIGuideResponseSupportedRenderers : NSObject
|
||||
- (YTIPivotBarRenderer *)pivotBarRenderer;
|
||||
@end
|
||||
@@ -0,0 +1,5 @@
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
@interface YTIHamplayerABRConfig : NSObject
|
||||
@property (nonatomic, assign, readwrite) BOOL preferSoftwareHdrOverHardwareSdr;
|
||||
@end
|
||||
@@ -0,0 +1,12 @@
|
||||
#import "YTIHamplayerStreamFilter.h"
|
||||
#import "YTIHamplayerABRConfig.h"
|
||||
|
||||
@interface YTIHamplayerConfig : NSObject
|
||||
@property (nonatomic, assign, readwrite) int renderViewType;
|
||||
@property (nonatomic, assign, readwrite) BOOL useSbdlRenderView;
|
||||
@property (nonatomic, assign, readwrite) BOOL useResolutionForHfrHdFormatFilter;
|
||||
@property (nonatomic, assign, readwrite) BOOL disableHfrHdFormatFilter;
|
||||
@property (nonatomic, assign, readwrite) BOOL disableResolveOverlappingQualitiesByCodec;
|
||||
@property (nonatomic, strong, readwrite) YTIHamplayerStreamFilter *streamFilter;
|
||||
@property (nonatomic, strong, readwrite) YTIHamplayerABRConfig *videoAbrConfig;
|
||||
@end
|
||||
@@ -0,0 +1,5 @@
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
@interface YTIHamplayerHotConfig : NSObject
|
||||
@property (nonatomic, assign, readwrite) int renderViewType;
|
||||
@end
|
||||
@@ -0,0 +1,6 @@
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
@interface YTIHamplayerSoftwareStreamFilter : NSObject
|
||||
@property int maxArea;
|
||||
@property int maxFps;
|
||||
@end
|
||||
@@ -0,0 +1,7 @@
|
||||
#import "YTIHamplayerSoftwareStreamFilter.h"
|
||||
|
||||
@interface YTIHamplayerStreamFilter : NSObject
|
||||
@property (nonatomic, strong, readwrite) YTIHamplayerSoftwareStreamFilter *av1;
|
||||
@property (nonatomic, strong, readwrite) YTIHamplayerSoftwareStreamFilter *vp9;
|
||||
@property (nonatomic, assign, readwrite) BOOL enableVideoCodecSplicing;
|
||||
@end
|
||||
@@ -0,0 +1,5 @@
|
||||
#import "YTIMediaHotConfig.h"
|
||||
|
||||
@interface YTIHotConfigGroup : NSObject
|
||||
@property (nonatomic, strong, readwrite) YTIMediaHotConfig *mediaHotConfig;
|
||||
@end
|
||||
5
Tweaks/iSponsorBlock/Headers/YouTubeHeader/YTIIcon.h
Normal file
5
Tweaks/iSponsorBlock/Headers/YouTubeHeader/YTIIcon.h
Normal file
@@ -0,0 +1,5 @@
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
@interface YTIIcon : NSObject
|
||||
@property (nonatomic, assign, readwrite) int iconType;
|
||||
@end
|
||||
@@ -0,0 +1,7 @@
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
@interface YTIIosMediaHotConfig : NSObject
|
||||
@property (nonatomic, assign, readwrite) BOOL enablePictureInPicture;
|
||||
@property (nonatomic, assign, readwrite) BOOL enablePipForNonBackgroundableContent;
|
||||
@property (nonatomic, assign, readwrite) BOOL enablePipForNonPremiumUsers;
|
||||
@end
|
||||
@@ -0,0 +1,5 @@
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
@interface YTIIosOnesieHotConfig : NSObject
|
||||
@property (nonatomic, assign, readwrite) BOOL prepareVideoDecoder;
|
||||
@end
|
||||
@@ -0,0 +1,5 @@
|
||||
#import "YTIItemSectionSupportedRenderers.h"
|
||||
|
||||
@interface YTIItemSectionRenderer : GPBMessage
|
||||
@property (nonatomic, strong, readwrite) NSMutableArray <YTIItemSectionSupportedRenderers *> *contentsArray;
|
||||
@end
|
||||
@@ -0,0 +1,8 @@
|
||||
#import "YTIElementRenderer.h"
|
||||
|
||||
@interface YTIItemSectionSupportedRenderers : GPBMessage
|
||||
@property (nonatomic, strong, readwrite) YTIElementRenderer *elementRenderer;
|
||||
@property (nonatomic, assign, readwrite) BOOL hasPromotedVideoRenderer;
|
||||
@property (nonatomic, assign, readwrite) BOOL hasPromotedVideoInlineMutedRenderer;
|
||||
@property (nonatomic, assign, readwrite) BOOL hasCompactPromotedVideoRenderer;
|
||||
@end
|
||||
@@ -0,0 +1,23 @@
|
||||
#import "YTLikeStatus.h"
|
||||
#import "YTILikeTarget.h"
|
||||
#import "YTIFormattedString.h"
|
||||
|
||||
@interface YTILikeButtonRenderer : NSObject
|
||||
@property (nonatomic, strong, readwrite) YTILikeTarget *target;
|
||||
@property (nonatomic, strong, readwrite) YTIFormattedString *likeCountText;
|
||||
@property (nonatomic, strong, readwrite) YTIFormattedString *likeCountWithLikeText;
|
||||
@property (nonatomic, strong, readwrite) YTIFormattedString *likeCountWithUnlikeText;
|
||||
@property (nonatomic, strong, readwrite) YTIFormattedString *dislikeCountText;
|
||||
@property (nonatomic, strong, readwrite) YTIFormattedString *dislikeCountWithDislikeText;
|
||||
@property (nonatomic, strong, readwrite) YTIFormattedString *dislikeCountWithUndislikeText;
|
||||
@property (nonatomic, assign, readwrite) BOOL hasLikeCountText;
|
||||
@property (nonatomic, assign, readwrite) BOOL hasLikeCountWithLikeText;
|
||||
@property (nonatomic, assign, readwrite) BOOL hasLikeCountWithUnlikeText;
|
||||
@property (nonatomic, assign, readwrite) BOOL hasDislikeCountText;
|
||||
@property (nonatomic, assign, readwrite) BOOL hasDislikeCountWithDislikeText;
|
||||
@property (nonatomic, assign, readwrite) BOOL hasDislikeCountWithUndislikeText;
|
||||
@property (nonatomic, assign, readwrite) BOOL likesAllowed;
|
||||
@property (nonatomic, assign, readwrite) YTLikeStatus likeStatus;
|
||||
@property (nonatomic, assign, readwrite) int likeCount;
|
||||
@property (nonatomic, assign, readwrite) int dislikeCount;
|
||||
@end
|
||||
@@ -0,0 +1,5 @@
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
@interface YTILikeTarget : NSObject
|
||||
@property (nonatomic, copy, readwrite) NSString *videoId;
|
||||
@end
|
||||
@@ -0,0 +1,5 @@
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
@interface YTIMediaCommonConfig : NSObject
|
||||
@property (nonatomic, assign, readwrite) BOOL useServerDrivenAbr;
|
||||
@end
|
||||
@@ -0,0 +1,7 @@
|
||||
#import "YTIIosMediaHotConfig.h"
|
||||
#import "YTIMediaQualitySettingsHotConfig.h"
|
||||
|
||||
@interface YTIMediaHotConfig : NSObject
|
||||
@property (nonatomic, strong, readwrite) YTIIosMediaHotConfig *iosMediaHotConfig;
|
||||
@property (nonatomic, strong, readwrite) YTIMediaQualitySettingsHotConfig *mediaQualitySettingsHotConfig;
|
||||
@end
|
||||
@@ -0,0 +1,5 @@
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
@interface YTIMediaQualitySettingsHotConfig : NSObject
|
||||
@property (nonatomic, assign, readwrite) BOOL enablePersistentVideoQualitySettings;
|
||||
@end
|
||||
@@ -0,0 +1,4 @@
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
@interface YTIOfflinePromoRenderer : NSObject
|
||||
@end
|
||||
@@ -0,0 +1,4 @@
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
@interface YTIOfflineabilityRenderer : NSObject
|
||||
@end
|
||||
@@ -0,0 +1,10 @@
|
||||
#import "YTIButtonRenderer.h"
|
||||
#import "YTIOfflinePromoRenderer.h"
|
||||
#import "YTIOfflineabilityRenderer.h"
|
||||
|
||||
@interface YTIOfflineabilitySupportedRenderers : NSObject
|
||||
@property (nonatomic, strong, readwrite) YTIOfflinePromoRenderer *offlinePromoRenderer;
|
||||
@property (nonatomic, strong, readwrite) YTIOfflineabilityRenderer *offlineabilityRenderer;
|
||||
@property (nonatomic, strong, readwrite) YTIButtonRenderer *buttonRenderer;
|
||||
- (int)rendererOneOfCase;
|
||||
@end
|
||||
@@ -0,0 +1,5 @@
|
||||
#import "YTIIosOnesieHotConfig.h"
|
||||
|
||||
@interface YTIOnesieHotConfig : NSObject
|
||||
@property (nonatomic, strong, readwrite) YTIIosOnesieHotConfig *iosConfig;
|
||||
@end
|
||||
@@ -0,0 +1,5 @@
|
||||
#import "YTICommand.h"
|
||||
|
||||
@interface YTIPaygatedQualityDetails : GPBMessage
|
||||
@property (nonatomic, strong, readwrite) YTICommand *endpoint;
|
||||
@end
|
||||
@@ -0,0 +1,5 @@
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
@interface YTIPictureInPictureRendererRoot : NSObject
|
||||
+ (id)pictureInPictureRenderer;
|
||||
@end
|
||||
@@ -0,0 +1,5 @@
|
||||
#import "GPBMessage.h"
|
||||
|
||||
@interface YTIPivotBarIconOnlyItemRenderer : GPBMessage
|
||||
- (NSString *)pivotIdentifier;
|
||||
@end
|
||||
@@ -0,0 +1,7 @@
|
||||
#import "YTICommand.h"
|
||||
|
||||
@interface YTIPivotBarItemRenderer : NSObject
|
||||
- (NSString *)pivotIdentifier;
|
||||
- (YTICommand *)navigationEndpoint;
|
||||
- (void)setNavigationEndpoint:(YTICommand *)navigationEndpoint;
|
||||
@end
|
||||
@@ -0,0 +1,6 @@
|
||||
#import "YTIPivotBarSupportedRenderers.h"
|
||||
|
||||
@interface YTIPivotBarRenderer : NSObject
|
||||
+ (YTIPivotBarSupportedRenderers *)pivotSupportedRenderersWithBrowseId:(NSString *)browseId title:(NSString *)title iconType:(int)iconType;
|
||||
- (NSMutableArray <YTIPivotBarSupportedRenderers *> *)itemsArray;
|
||||
@end
|
||||
@@ -0,0 +1,7 @@
|
||||
#import "YTIPivotBarItemRenderer.h"
|
||||
#import "YTIPivotBarIconOnlyItemRenderer.h"
|
||||
|
||||
@interface YTIPivotBarSupportedRenderers : NSObject
|
||||
- (YTIPivotBarItemRenderer *)pivotBarItemRenderer;
|
||||
- (YTIPivotBarIconOnlyItemRenderer *)pivotBarIconOnlyItemRenderer;
|
||||
@end
|
||||
@@ -0,0 +1,5 @@
|
||||
#import "YTIVideoDetails.h"
|
||||
|
||||
@interface YTIPlayerResponse : NSObject
|
||||
@property (nonatomic, strong, readwrite) YTIVideoDetails *videoDetails;
|
||||
@end
|
||||
@@ -0,0 +1,4 @@
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
@interface YTIReelWatchEndpoint : NSObject
|
||||
@end
|
||||
@@ -0,0 +1,5 @@
|
||||
#import "YTISectionListSupportedRenderers.h"
|
||||
|
||||
@interface YTISectionListRenderer : GPBMessage
|
||||
@property (nonatomic, strong, readwrite) NSMutableArray <YTISectionListSupportedRenderers *> *contentsArray;
|
||||
@end
|
||||
@@ -0,0 +1,5 @@
|
||||
#import "YTIItemSectionRenderer.h"
|
||||
|
||||
@interface YTISectionListSupportedRenderers : GPBMessage
|
||||
@property (nonatomic, strong, readwrite) YTIItemSectionRenderer *itemSectionRenderer;
|
||||
@end
|
||||
@@ -0,0 +1,5 @@
|
||||
#import "GPBExtensionDescriptor.h"
|
||||
|
||||
@interface YTIShowEngagementPanelEndpoint : NSObject
|
||||
+ (GPBExtensionDescriptor *)showEngagementPanelEndpoint;
|
||||
@end
|
||||
@@ -0,0 +1,5 @@
|
||||
#import "YTIButtonSupportedRenderers.h"
|
||||
|
||||
@interface YTISlimMetadataButtonRenderer : NSObject
|
||||
@property (retain, nonatomic) YTIButtonSupportedRenderers *button;
|
||||
@end
|
||||
@@ -0,0 +1,11 @@
|
||||
#import "YTISlimMetadataToggleButtonRenderer.h"
|
||||
#import "YTISlimMetadataButtonRenderer.h"
|
||||
|
||||
@interface YTISlimMetadataButtonSupportedRenderers : NSObject
|
||||
@property (nonatomic, strong, readwrite) YTISlimMetadataToggleButtonRenderer *slimMetadataToggleButtonRenderer;
|
||||
@property (retain, nonatomic) YTISlimMetadataButtonRenderer *slimMetadataButtonRenderer;
|
||||
- (BOOL)slimButton_isLikeButton;
|
||||
- (BOOL)slimButton_isDislikeButton;
|
||||
- (BOOL)slimButton_isOfflineButton;
|
||||
- (int)rendererOneOfCase;
|
||||
@end
|
||||
@@ -0,0 +1,7 @@
|
||||
#import "YTILikeTarget.h"
|
||||
#import "YTIButtonSupportedRenderers.h"
|
||||
|
||||
@interface YTISlimMetadataToggleButtonRenderer : NSObject
|
||||
@property (nonatomic, strong, readwrite) YTILikeTarget *target;
|
||||
@property (nonatomic, strong, readwrite) YTIButtonSupportedRenderers *button;
|
||||
@end
|
||||
@@ -0,0 +1,5 @@
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
@interface YTIStringRun : NSObject
|
||||
@property (nonatomic, copy, readwrite) NSString *text;
|
||||
@end
|
||||
@@ -0,0 +1,6 @@
|
||||
#import "YTIFormattedString.h"
|
||||
|
||||
@interface YTIToggleButtonRenderer : NSObject
|
||||
@property (nonatomic, strong, readwrite) YTIFormattedString *defaultText;
|
||||
@property (nonatomic, strong, readwrite) YTIFormattedString *toggledText;
|
||||
@end
|
||||
@@ -0,0 +1,8 @@
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
@interface YTIVideoDetails : NSObject
|
||||
@property (nonatomic, assign, readwrite) BOOL allowRatings;
|
||||
@property (nonatomic, assign, readwrite) float averageRating;
|
||||
@property (nonatomic, copy, readwrite) NSString *viewCount;
|
||||
@property (nonatomic, copy, readwrite) NSString *channelId;
|
||||
@end
|
||||
@@ -0,0 +1,5 @@
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
@interface YTIWatchNextResponse : NSObject
|
||||
@property (nonatomic, assign, readwrite) BOOL hasOnUiReady;
|
||||
@end
|
||||
@@ -0,0 +1,18 @@
|
||||
#import "YTGlassContainerView.h"
|
||||
#import "YTInlinePlayerBarView.h"
|
||||
#import "YTSegmentableInlinePlayerBarView.h"
|
||||
#import "YTLabel.h"
|
||||
#import "YTQTMButton.h"
|
||||
|
||||
@interface YTInlinePlayerBarContainerView : YTGlassContainerView
|
||||
@property (nonatomic, strong, readwrite) YTInlinePlayerBarView *playerBar; // Replaced by segmentablePlayerBar in newer versions
|
||||
@property (nonatomic, strong, readwrite) YTSegmentableInlinePlayerBarView *segmentablePlayerBar;
|
||||
@property (nonatomic, strong, readwrite) UIView *multiFeedElementView;
|
||||
@property (nonatomic, strong, readwrite) YTLabel *durationLabel;
|
||||
@property (nonatomic, assign, readwrite) BOOL showOnlyFullscreenButton;
|
||||
@property (nonatomic, assign, readwrite) int layout;
|
||||
@property (nonatomic, weak, readwrite) id delegate;
|
||||
- (YTQTMButton *)exitFullscreenButton;
|
||||
- (YTQTMButton *)enterFullscreenButton;
|
||||
- (void)setChapters:(NSArray *)chapters;
|
||||
@end
|
||||
@@ -0,0 +1,6 @@
|
||||
#import "YTPlayerViewController.h"
|
||||
|
||||
@interface YTInlinePlayerBarView : UIView
|
||||
@property (nonatomic, readonly, assign) CGFloat totalTime;
|
||||
@property (nonatomic, readwrite, strong) YTPlayerViewController *playerViewController;
|
||||
@end
|
||||
@@ -0,0 +1,4 @@
|
||||
#import "YTCellController.h"
|
||||
|
||||
@interface YTInnerTubeCellController : YTCellController
|
||||
@end
|
||||
4
Tweaks/iSponsorBlock/Headers/YouTubeHeader/YTLabel.h
Normal file
4
Tweaks/iSponsorBlock/Headers/YouTubeHeader/YTLabel.h
Normal file
@@ -0,0 +1,4 @@
|
||||
#import <UIKit/UIKit.h>
|
||||
|
||||
@interface YTLabel : UILabel
|
||||
@end
|
||||
@@ -0,0 +1,5 @@
|
||||
#import <UIKit/UIKit.h>
|
||||
|
||||
@interface YTLightweightQTMButton : UIButton
|
||||
@property (nonatomic, assign, readwrite, getter=isUppercaseTitle) BOOL uppercaseTitle;
|
||||
@end
|
||||
12
Tweaks/iSponsorBlock/Headers/YouTubeHeader/YTLikeStatus.h
Normal file
12
Tweaks/iSponsorBlock/Headers/YouTubeHeader/YTLikeStatus.h
Normal file
@@ -0,0 +1,12 @@
|
||||
#ifndef _YT_LIKESTATUS
|
||||
#define _YT_LIKESTATUS
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
typedef NS_ENUM(int, YTLikeStatus) {
|
||||
YTLikeStatusLike = 0,
|
||||
YTLikeStatusDislike = 1,
|
||||
YTLikeStatusNeutral = 2
|
||||
};
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,9 @@
|
||||
#import "GIMMe.h"
|
||||
#import "YTSingleVideoControllerDelegate.h"
|
||||
|
||||
@interface YTLocalPlaybackController : NSObject <YTSingleVideoControllerDelegate>
|
||||
- (GIMMe *)gimme; // Deprecated
|
||||
- (NSString *)currentVideoID;
|
||||
- (int)playerVisibility;
|
||||
- (void)setMuted:(BOOL)muted;
|
||||
@end
|
||||
@@ -0,0 +1,9 @@
|
||||
#import "YTQTMButton.h"
|
||||
#import "YTPlayerViewController.h"
|
||||
|
||||
@interface YTMainAppControlsOverlayView : UIView
|
||||
+ (CGFloat)topButtonAdditionalPadding;
|
||||
@property (nonatomic, assign, readwrite, getter=isOverlayVisible) BOOL overlayVisible;
|
||||
@property (nonatomic, strong, readwrite) YTPlayerViewController *playerViewController;
|
||||
- (YTQTMButton *)buttonWithImage:(UIImage *)image accessibilityLabel:(NSString *)accessibilityLabel verticalContentPadding:(CGFloat)verticalContentPadding;
|
||||
@end
|
||||
@@ -0,0 +1,5 @@
|
||||
#import <UIKit/UIKit.h>
|
||||
|
||||
@interface YTMainAppPlayerOverlayView : UIView
|
||||
+ (CGFloat)topButtonAdditionalPadding;
|
||||
@end
|
||||
@@ -0,0 +1,20 @@
|
||||
#import <UIKit/UIKit.h>
|
||||
|
||||
#ifdef LEGACY
|
||||
|
||||
#import "YTMainAppPlayerOverlayView.h"
|
||||
|
||||
@interface YTMainAppVideoPlayerOverlayView : YTMainAppPlayerOverlayView
|
||||
@end
|
||||
|
||||
#else
|
||||
|
||||
#import "YTInlinePlayerBarContainerView.h"
|
||||
#import "YTMainAppControlsOverlayView.h"
|
||||
|
||||
@interface YTMainAppVideoPlayerOverlayView : UIView
|
||||
@property (nonatomic, strong, readwrite) YTInlinePlayerBarContainerView *playerBar;
|
||||
- (YTMainAppControlsOverlayView *)controlsOverlayView;
|
||||
@end
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,7 @@
|
||||
#import "YTMainAppVideoPlayerOverlayView.h"
|
||||
#import "YTPlayerViewController.h"
|
||||
|
||||
@interface YTMainAppVideoPlayerOverlayViewController : UIViewController
|
||||
- (YTMainAppVideoPlayerOverlayView *)videoPlayerOverlayView;
|
||||
- (YTPlayerViewController *)delegate;
|
||||
@end
|
||||
@@ -0,0 +1,4 @@
|
||||
#import <UIKit/UIKit.h>
|
||||
|
||||
@interface YTMultiSizeViewController : UIViewController
|
||||
@end
|
||||
@@ -0,0 +1,4 @@
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
@interface YTNGWatchController : NSObject
|
||||
@end
|
||||
@@ -0,0 +1,5 @@
|
||||
#import "YTPlayerViewController.h"
|
||||
|
||||
@interface YTNGWatchLayerViewController : UIViewController
|
||||
- (YTPlayerViewController *)playerViewController;
|
||||
@end
|
||||
@@ -0,0 +1,5 @@
|
||||
#import <UIKit/UIKit.h>
|
||||
|
||||
@interface YTNGWatchMiniBarView : UIView
|
||||
@property (nonatomic, assign, readwrite) NSInteger watchMiniPlayerLayout;
|
||||
@end
|
||||
@@ -0,0 +1,7 @@
|
||||
#import <Foundation/Foundation.h>
|
||||
#import <UIKit/UIKit.h>
|
||||
|
||||
@interface YTOfflineButtonPressedResponderEvent : NSObject
|
||||
+ (instancetype)eventWithOfflineVideoID:(NSString *)videoID fromView:(UIView *)view firstResponder:(id)firstResponder;
|
||||
- (void)send;
|
||||
@end
|
||||
@@ -0,0 +1,8 @@
|
||||
#import "YTCommonColorPalette.h"
|
||||
|
||||
@interface YTPageStyleController : NSObject
|
||||
+ (YTCommonColorPalette *)currentColorPalette; // For YouTube older than 17.19.2, import/change type to YTColorPalette
|
||||
+ (NSInteger)pageStyle;
|
||||
@property (nonatomic, assign, readwrite) NSInteger appThemeSetting;
|
||||
@property (nonatomic, assign, readonly) NSInteger pageStyle;
|
||||
@end
|
||||
@@ -0,0 +1,5 @@
|
||||
#import <UIKit/UIKit.h>
|
||||
|
||||
@interface YTPivotBarItemView : UIView
|
||||
@property (strong, nonatomic) UIButton *navigationButton;
|
||||
@end
|
||||
@@ -0,0 +1,5 @@
|
||||
#import "YTSingleVideoController.h"
|
||||
|
||||
@protocol YTPlaybackController
|
||||
@property (strong, nonatomic) YTSingleVideoController *activeVideo;
|
||||
@end
|
||||
@@ -0,0 +1,6 @@
|
||||
#import "YTSingleVideoController.h"
|
||||
|
||||
@interface YTPlaybackControllerUIWrapper : NSObject
|
||||
- (YTSingleVideoController *)activeVideo;
|
||||
- (YTSingleVideoController *)contentVideo;
|
||||
@end
|
||||
@@ -0,0 +1,7 @@
|
||||
#import "MLVideo.h"
|
||||
#import "YTPlayerResponse.h"
|
||||
|
||||
@interface YTPlaybackData : NSObject
|
||||
- (MLVideo *)video;
|
||||
- (YTPlayerResponse *)playerResponse;
|
||||
@end
|
||||
@@ -0,0 +1,6 @@
|
||||
#import "YTWatchMetadataPanelStateResponderProvider.h"
|
||||
#import "YTWatchPlaybackController.h"
|
||||
|
||||
@interface YTPlaybackStrippedWatchController : NSObject <YTWatchMetadataPanelStateResponderProvider>
|
||||
@property (nonatomic, strong, readwrite) YTWatchPlaybackController *watchPlaybackController;
|
||||
@end
|
||||
@@ -0,0 +1,8 @@
|
||||
#import <UIKit/UIKit.h>
|
||||
|
||||
@interface YTPlayerBarSegmentMarkerView : UIView
|
||||
@property (nonatomic, readwrite, assign) CGFloat startTime;
|
||||
@property (nonatomic, readwrite, assign) CGFloat endTime;
|
||||
@property (nonatomic, readwrite, assign) CGFloat width;
|
||||
@property (nonatomic, readwrite, assign) NSInteger type;
|
||||
@end
|
||||
@@ -0,0 +1,11 @@
|
||||
#import <UIKit/UIKit.h>
|
||||
|
||||
@interface YTPlayerBarSegmentedProgressView : UIView
|
||||
@property (nonatomic, readwrite, assign) CGFloat totalTime;
|
||||
@property (nonatomic, readwrite, assign) int playerViewLayout;
|
||||
- (void)maybeCreateMarkerViews;
|
||||
- (void)setChapters:(NSArray *)chapters;
|
||||
- (void)createAndAddMarker:(CGFloat)arg1 type:(NSInteger)type width:(CGFloat)width;
|
||||
- (void)createAndAddMarker:(CGFloat)arg1 type:(NSInteger)type clusterType:(NSInteger)clusterType width:(CGFloat)width; // Deprecated
|
||||
- (NSMutableArray *)segmentViews;
|
||||
@end
|
||||
@@ -0,0 +1,6 @@
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
@interface YTPlayerOverlay : NSObject
|
||||
- (NSString *)overlayIdentifier;
|
||||
- (NSInteger)overlayZIndex;
|
||||
@end
|
||||
@@ -0,0 +1,4 @@
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
@interface YTPlayerOverlayProvider : NSObject
|
||||
@end
|
||||
@@ -0,0 +1,17 @@
|
||||
#import "GIMMe.h"
|
||||
#import "YTSingleVideoController.h"
|
||||
|
||||
@interface YTPlayerPIPController : NSObject
|
||||
@property (nonatomic, readonly, assign, getter=isPictureInPictureActive) BOOL pictureInPuctureActive;
|
||||
@property (nonatomic, readonly, assign, getter=isPictureInPicturePossible) BOOL pictureInPucturePossible;
|
||||
@property (retain, nonatomic) YTSingleVideoController *activeSingleVideo;
|
||||
- (instancetype)initWithPlayerView:(id)playerView delegate:(id)delegate; // Deprecated, use initWithDelegate:
|
||||
- (instancetype)initWithDelegate:(id)delegate;
|
||||
- (GIMMe *)gimme; // Deprecated
|
||||
- (BOOL)canInvokePictureInPicture; // Deprecated, use canEnablePictureInPicture
|
||||
- (BOOL)canEnablePictureInPicture;
|
||||
- (void)maybeInvokePictureInPicture; // Deprecated, use maybeEnablePictureInPicture
|
||||
- (void)maybeEnablePictureInPicture;
|
||||
- (void)play;
|
||||
- (void)pause;
|
||||
@end
|
||||
@@ -0,0 +1,4 @@
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
@interface YTPlayerResources : NSObject
|
||||
@end
|
||||
@@ -0,0 +1,5 @@
|
||||
#import "YTIPlayerResponse.h"
|
||||
|
||||
@interface YTPlayerResponse : NSObject
|
||||
- (YTIPlayerResponse *)playerData;
|
||||
@end
|
||||
10
Tweaks/iSponsorBlock/Headers/YouTubeHeader/YTPlayerStatus.h
Normal file
10
Tweaks/iSponsorBlock/Headers/YouTubeHeader/YTPlayerStatus.h
Normal file
@@ -0,0 +1,10 @@
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
@interface YTPlayerStatus : NSObject
|
||||
- (BOOL)externalPlayback;
|
||||
- (BOOL)backgroundPlayback;
|
||||
- (BOOL)isInlinePlaybackActive;
|
||||
- (BOOL)pictureInPicture;
|
||||
- (int)visibility;
|
||||
- (int)layout;
|
||||
@end
|
||||
@@ -0,0 +1,9 @@
|
||||
#import "MLAVPIPPlayerLayerView.h"
|
||||
#import "YTPlaybackControllerUIWrapper.h"
|
||||
|
||||
@interface YTPlayerView : UIView
|
||||
@property (retain, nonatomic) MLAVPIPPlayerLayerView *pipRenderingView; // Removed in newer versions
|
||||
@property (nonatomic, strong, readwrite) UIView *overlayView; // Usually YTMainAppVideoPlayerOverlayView
|
||||
- (YTPlaybackControllerUIWrapper *)playerViewDelegate;
|
||||
- (UIView *)renderingView;
|
||||
@end
|
||||
@@ -0,0 +1,21 @@
|
||||
#import <UIKit/UIKit.h>
|
||||
#import "GIMMe.h"
|
||||
#import "YTPlaybackController.h"
|
||||
#import "YTSingleVideoController.h"
|
||||
|
||||
@interface YTPlayerViewController : UIViewController <YTPlaybackController>
|
||||
@property (nonatomic, readonly, assign) BOOL isPlayingAd;
|
||||
@property (nonatomic, strong, readwrite) NSString *channelID;
|
||||
- (GIMMe *)gimme; // Deprecated
|
||||
- (NSString *)currentVideoID;
|
||||
- (YTSingleVideoController *)activeVideo;
|
||||
- (CGFloat)currentVideoMediaTime;
|
||||
- (CGFloat)currentVideoTotalMediaTime;
|
||||
- (int)playerViewLayout;
|
||||
- (BOOL)isMDXActive;
|
||||
- (void)didPressToggleFullscreen;
|
||||
- (void)setMuted:(BOOL)muted;
|
||||
- (void)setPlayerViewLayout:(int)layout;
|
||||
- (void)scrubToTime:(CGFloat)time; // Deprecated
|
||||
- (void)seekToTime:(CGFloat)time;
|
||||
@end
|
||||
@@ -0,0 +1,4 @@
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
@interface YTPlayerViewControllerConfig : NSObject
|
||||
@end
|
||||
@@ -0,0 +1,6 @@
|
||||
#import "YTCollectionViewCell.h"
|
||||
|
||||
@interface YTPlaylistPanelProminentThumbnailVideoCell : YTCollectionViewCell
|
||||
- (void)setSwipeButtonTarget:(id)target action:(SEL)action;
|
||||
- (void)setSwipeButtonActionsViewRightBlock:(void (^)(void))block;
|
||||
@end
|
||||
@@ -0,0 +1,5 @@
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
@interface YTPlaylistPanelProminentThumbnailVideoCellController : NSObject
|
||||
- (void)didPressSwipeToRevealButton;
|
||||
@end
|
||||
17
Tweaks/iSponsorBlock/Headers/YouTubeHeader/YTQTMButton.h
Normal file
17
Tweaks/iSponsorBlock/Headers/YouTubeHeader/YTQTMButton.h
Normal file
@@ -0,0 +1,17 @@
|
||||
#import "YTLightweightQTMButton.h"
|
||||
|
||||
@interface YTQTMButton : YTLightweightQTMButton
|
||||
+ (instancetype)button;
|
||||
+ (instancetype)closeButton;
|
||||
+ (instancetype)iconButton;
|
||||
+ (instancetype)textButton;
|
||||
@property (nonatomic, assign, readwrite) CGFloat buttonImageTitlePadding;
|
||||
@property (nonatomic, assign, readwrite) CGFloat minHitTargetSize;
|
||||
@property (nonatomic, assign, readwrite) CGFloat verticalContentPadding;
|
||||
@property (nonatomic, assign, readwrite) NSInteger buttonLayoutStyle;
|
||||
@property (nonatomic, assign, readwrite) BOOL refreshRendererAfterPageStyling;
|
||||
@property (nonatomic, assign, readwrite) BOOL sizeWithPaddingAndInsets;
|
||||
@property (nonatomic, copy, readwrite) NSString *accessibilityIdentifier;
|
||||
@property (nonatomic, copy, readwrite) NSString *accessibilityLabel;
|
||||
- (void)setTitleTypeKind:(NSInteger)titleTypeKind typeVariant:(NSInteger)typeVariant;
|
||||
@end
|
||||
@@ -0,0 +1,5 @@
|
||||
#import <UIKit/UIKit.h>
|
||||
|
||||
@interface YTReelContentView : UIView
|
||||
@property (nonatomic, assign, readwrite) BOOL alwaysShowShortsProgressBar;
|
||||
@end
|
||||
@@ -0,0 +1,8 @@
|
||||
#import "YTILikeButtonRenderer.h"
|
||||
|
||||
@interface YTReelLikeModel : NSObject
|
||||
@property (nonatomic, copy, readwrite) NSString *videoID;
|
||||
@property (nonatomic, strong, readwrite) YTILikeButtonRenderer *likeButtonRenderer;
|
||||
@property (nonatomic, assign, readwrite) int status;
|
||||
@property (nonatomic, assign, readwrite) int lastStatus;
|
||||
@end
|
||||
@@ -0,0 +1,5 @@
|
||||
#import "YTQTMButton.h"
|
||||
|
||||
@interface YTReelPlayerBottomButton : YTQTMButton
|
||||
@property (nonatomic, assign, readwrite) BOOL applyRightSideLayoutImageSize;
|
||||
@end
|
||||
@@ -0,0 +1,5 @@
|
||||
#import "YTReelContentView.h"
|
||||
|
||||
@interface YTReelPlayerViewController : UIViewController
|
||||
- (YTReelContentView *)contentView;
|
||||
@end
|
||||
@@ -0,0 +1,9 @@
|
||||
#import "YTQTMButton.h"
|
||||
#import "YTILikeButtonRenderer.h"
|
||||
|
||||
@interface YTReelWatchLikesController : NSObject
|
||||
@property (nonatomic, strong, readwrite) YTQTMButton *likeButton;
|
||||
@property (nonatomic, strong, readwrite) YTQTMButton *dislikeButton;
|
||||
- (id)likeModelForLikeButtonRenderer:(YTILikeButtonRenderer *)renderer;
|
||||
- (void)updateLikeButtonWithModel:(id)model animated:(BOOL)animated;
|
||||
@end
|
||||
@@ -0,0 +1,6 @@
|
||||
#import "YTQTMButton.h"
|
||||
|
||||
@interface YTReelWatchPlaybackOverlayView : UIView
|
||||
@property (nonatomic, assign, readonly) YTQTMButton *overflowButton;
|
||||
- (NSArray <YTQTMButton *> *)orderedRightSideButtons;
|
||||
@end
|
||||
@@ -0,0 +1,6 @@
|
||||
#import <UIKit/UIKit.h>
|
||||
|
||||
@interface YTRightNavigationButtons : UIView
|
||||
@property (nonatomic, readwrite, assign) CGFloat leadingPadding;
|
||||
@property (nonatomic, readwrite, assign) CGFloat tailingPadding; // Legitimate typo
|
||||
@end
|
||||
@@ -0,0 +1,5 @@
|
||||
#import <UIKit/UIKit.h>
|
||||
|
||||
@interface YTSearchableSettingsViewController : UIViewController
|
||||
- (void)storeCollectionViewSections:(NSArray *)sections;
|
||||
@end
|
||||
@@ -0,0 +1,4 @@
|
||||
#import <UIKit/UIKit.h>
|
||||
|
||||
@interface YTSectionListViewController : UIViewController
|
||||
@end
|
||||
@@ -0,0 +1,6 @@
|
||||
#import "YTPlayerViewController.h"
|
||||
|
||||
@interface YTSegmentableInlinePlayerBarView : UIView
|
||||
@property (nonatomic, readonly, assign) CGFloat totalTime;
|
||||
@property (nonatomic, readwrite, strong) YTPlayerViewController *playerViewController;
|
||||
@end
|
||||
@@ -0,0 +1,7 @@
|
||||
#import "YTCollectionViewCell.h"
|
||||
|
||||
@interface YTSettingsCell : YTCollectionViewCell
|
||||
@property (nonatomic, assign, readwrite) BOOL enabled;
|
||||
- (void)setSwitchOn:(BOOL)on animated:(BOOL)animated;
|
||||
- (void)toggleSwitch;
|
||||
@end
|
||||
@@ -0,0 +1,5 @@
|
||||
#import "YTStyledViewController.h"
|
||||
|
||||
@interface YTSettingsPickerViewController : YTStyledViewController
|
||||
- (instancetype)initWithNavTitle:(NSString *)navTitle pickerSectionTitle:(NSString *)pickerSectionTitle rows:(NSArray *)rows selectedItemIndex:(NSUInteger)selectedItemIndex parentResponder:(id)parentResponder;
|
||||
@end
|
||||
@@ -0,0 +1,4 @@
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
@interface YTSettingsSectionController : NSObject
|
||||
@end
|
||||
@@ -0,0 +1,21 @@
|
||||
#import "YTSettingsCell.h"
|
||||
|
||||
@interface YTSettingsSectionItem : NSObject
|
||||
@property (nonatomic, copy, readwrite) NSString *title;
|
||||
@property (nonatomic, assign, readwrite) BOOL hasSwitch;
|
||||
@property (nonatomic, assign, readwrite) BOOL switchVisible;
|
||||
@property (nonatomic, assign, readwrite) BOOL on;
|
||||
@property (nonatomic, assign, readwrite) BOOL enabled;
|
||||
@property (nonatomic, assign, readwrite) int settingItemId;
|
||||
@property (nonatomic, copy, readwrite) BOOL (^selectBlock)(YTSettingsCell *, NSUInteger);
|
||||
@property (nonatomic, copy, readwrite) BOOL (^switchBlock)(YTSettingsCell *, BOOL);
|
||||
+ (instancetype)itemWithTitle:(NSString *)title accessibilityIdentifier:(NSString *)accessibilityIdentifier detailTextBlock:(NSString *(^)(void))detailTextBlock selectBlock:(BOOL (^)(YTSettingsCell *, NSUInteger))selectBlock;
|
||||
+ (instancetype)itemWithTitle:(NSString *)title titleDescription:(NSString *)titleDescription accessibilityIdentifier:(NSString *)accessibilityIdentifier detailTextBlock:(id)detailTextBlock selectBlock:(BOOL (^)(YTSettingsCell *, NSUInteger))selectBlock;
|
||||
+ (instancetype)checkmarkItemWithTitle:(NSString *)title selectBlock:(BOOL (^)(YTSettingsCell *, NSUInteger))selectBlock;
|
||||
+ (instancetype)checkmarkItemWithTitle:(NSString *)title titleDescription:(NSString *)titleDescription selectBlock:(BOOL (^)(YTSettingsCell *, NSUInteger))selectBlock;
|
||||
+ (instancetype)checkmarkItemWithTitle:(NSString *)title titleDescription:(NSString *)titleDescription selectBlock:(BOOL (^)(YTSettingsCell *, NSUInteger))selectBlock disabledSelectBlock:(BOOL (^)(YTSettingsCell *, NSUInteger))disabledSelectBlock;
|
||||
+ (instancetype)switchItemWithTitle:(NSString *)title switchOn:(BOOL)switchOn switchBlock:(BOOL (^)(YTSettingsCell *, BOOL))switchBlock;
|
||||
+ (instancetype)switchItemWithTitle:(NSString *)title titleDescription:(NSString *)titleDescription accessibilityIdentifier:(NSString *)accessibilityIdentifier switchOn:(BOOL)switchOn switchBlock:(BOOL (^)(YTSettingsCell *, BOOL))switchBlock settingItemId:(int)settingItemId;
|
||||
+ (instancetype)switchItemWithTitle:(NSString *)title titleDescription:(NSString *)titleDescription accessibilityIdentifier:(NSString *)accessibilityIdentifier switchOn:(BOOL)switchOn switchBlock:(BOOL (^)(YTSettingsCell *, BOOL))switchBlock selectBlock:(BOOL (^)(YTSettingsCell *, NSUInteger))selectBlock settingItemId:(int)settingItemId;
|
||||
- (instancetype)initWithTitle:(NSString *)title titleDescription:(NSString *)titleDescription;
|
||||
@end
|
||||
@@ -0,0 +1,6 @@
|
||||
#import "GIMMe.h"
|
||||
|
||||
@interface YTSettingsSectionItemManager : NSObject
|
||||
@property (nonatomic, readwrite, weak) GIMMe *gimme;
|
||||
- (id)parentResponder;
|
||||
@end
|
||||
@@ -0,0 +1,9 @@
|
||||
#import "YTSettingsSectionItem.h"
|
||||
#import "YTSettingsSectionController.h"
|
||||
|
||||
@interface YTSettingsViewController : UIViewController
|
||||
- (NSMutableDictionary <NSNumber *, YTSettingsSectionController *> *)settingsSectionControllers;
|
||||
- (void)setSectionItems:(NSMutableArray <YTSettingsSectionItem *> *)sectionItems forCategory:(NSInteger)category title:(NSString *)title titleDescription:(NSString *)titleDescription headerHidden:(BOOL)headerHidden;
|
||||
- (void)pushViewController:(UIViewController *)viewController;
|
||||
- (void)reloadData;
|
||||
@end
|
||||
@@ -0,0 +1,8 @@
|
||||
#import "YTPlaybackData.h"
|
||||
#import "MLVideo.h"
|
||||
|
||||
@interface YTSingleVideo : NSObject
|
||||
- (MLVideo *)video; // Deprecated
|
||||
- (NSString *)videoId;
|
||||
- (YTPlaybackData *)playbackData;
|
||||
@end
|
||||
@@ -0,0 +1,13 @@
|
||||
#import "MLFormat.h"
|
||||
#import "YTSingleVideoControllerDelegate.h"
|
||||
#import "YTSingleVideo.h"
|
||||
|
||||
@interface YTSingleVideoController : NSObject
|
||||
@property (nonatomic, weak, readwrite) NSObject <YTSingleVideoControllerDelegate> *delegate;
|
||||
- (YTSingleVideo *)singleVideo;
|
||||
- (YTSingleVideo *)videoData;
|
||||
- (NSArray <MLFormat *> *)selectableVideoFormats;
|
||||
- (BOOL)isMuted;
|
||||
- (void)playerRateDidChange:(float)rate;
|
||||
- (void)setMuted:(BOOL)muted;
|
||||
@end
|
||||
@@ -0,0 +1,6 @@
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
@protocol YTSingleVideoControllerDelegate <NSObject>
|
||||
- (void)singleVideoController:(id)controller requiresReloadWithContext:(id)context;
|
||||
- (void)singleVideoController:(id)controller externalPlaybackActiveStateDidChange:(id)arg2;
|
||||
@end
|
||||
@@ -0,0 +1,10 @@
|
||||
#import <CoreGraphics/CoreGraphics.h>
|
||||
|
||||
@interface YTSingleVideoTime : NSObject
|
||||
@property (nonatomic, readonly, assign) CGFloat absoluteTime;
|
||||
@property (nonatomic, readonly, assign) CGFloat time;
|
||||
@property (nonatomic, readonly, assign) CGFloat productionTime;
|
||||
+ (instancetype)zero;
|
||||
+ (instancetype)timeWithTime:(CGFloat)time;
|
||||
+ (instancetype)timeWithTime:(CGFloat)time productionTime:(CGFloat)productionTime;
|
||||
@end
|
||||
@@ -0,0 +1,7 @@
|
||||
#import <UIKit/UIKit.h>
|
||||
|
||||
@interface YTSlideForActionsView : UIView
|
||||
@property (nonatomic, assign, readwrite) BOOL actionsEnabledRight;
|
||||
@property (nonatomic, assign, readwrite) BOOL enableMinSnap;
|
||||
@property (nonatomic, assign, readwrite) BOOL enableHapticFeedback;
|
||||
@end
|
||||
@@ -0,0 +1,10 @@
|
||||
#import "YTIFormattedStringLabel.h"
|
||||
#import "YTSlimVideoScrollableDetailsActionsView.h"
|
||||
|
||||
@interface YTSlimVideoDetailsActionView : UIView
|
||||
@property (nonatomic, strong, readwrite) YTIFormattedStringLabel *label;
|
||||
@property (nonatomic, weak, readwrite) YTSlimVideoScrollableDetailsActionsView *visibilityDelegate;
|
||||
@property (nonatomic) __weak id delegate;
|
||||
@property (nonatomic, assign, readwrite, getter=isToggled) BOOL toggled;
|
||||
- (instancetype)initWithSlimMetadataButtonSupportedRenderer:(id)renderer;
|
||||
@end
|
||||
@@ -0,0 +1,7 @@
|
||||
#include "YTQTMButton.h"
|
||||
#import "YTQTMButton.h"
|
||||
|
||||
@protocol YTSlimVideoDetailsActionViewDelegate <NSObject>
|
||||
- (void)didTapButton:(YTQTMButton *)button fromRect:(CGRect)rect inView:(UIView *)view;
|
||||
- (void)handleLongPressOnButton:(YTQTMButton *)button fromRect:(CGRect)rect inView:(UIView *)view;
|
||||
@end
|
||||
@@ -0,0 +1,5 @@
|
||||
#import "YTSlimVideoDetailsActionViewDelegate.h"
|
||||
|
||||
@interface YTSlimVideoDetailsActionsView : UIScrollView
|
||||
@property (nonatomic, weak, readwrite) NSObject <YTSlimVideoDetailsActionViewDelegate> *videoActionsDelegate;
|
||||
@end
|
||||
@@ -0,0 +1,4 @@
|
||||
#import "YTSlimVideoScrollableActionBarCellControllerDelegate.h"
|
||||
|
||||
@interface YTSlimVideoMetadataExpandingBehavior : NSObject <YTSlimVideoScrollableActionBarCellControllerDelegate>
|
||||
@end
|
||||
@@ -0,0 +1,5 @@
|
||||
#import "YTCollectionViewCellProtocol.h"
|
||||
#import "YTSlimVideoScrollableDetailsActionsProtocol.h"
|
||||
|
||||
@interface YTSlimVideoScrollableActionBarCell : UICollectionViewCell <YTSlimVideoScrollableDetailsActionsProtocol, YTCollectionViewCellProtocol>
|
||||
@end
|
||||
@@ -0,0 +1,6 @@
|
||||
#import "YTSlimVideoScrollableActionBarCellControllerDelegate.h"
|
||||
#import "YTInnerTubeCellController.h"
|
||||
|
||||
@interface YTSlimVideoScrollableActionBarCellController : YTInnerTubeCellController
|
||||
@property (nonatomic, weak, readwrite) NSObject <YTSlimVideoScrollableActionBarCellControllerDelegate> *delegate;
|
||||
@end
|
||||
@@ -0,0 +1,6 @@
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
@protocol YTSlimVideoScrollableActionBarCellControllerDelegate <NSObject>
|
||||
@required
|
||||
- (NSString *)videoId;
|
||||
@end
|
||||
@@ -0,0 +1,5 @@
|
||||
#import "YTSlimVideoDetailsActionView.h"
|
||||
|
||||
@protocol YTSlimVideoScrollableDetailsActionsProtocol
|
||||
- (YTSlimVideoDetailsActionView *)offlineActionView;
|
||||
@end
|
||||
@@ -0,0 +1,5 @@
|
||||
#import "YTSlimVideoDetailsActionViewDelegate.h"
|
||||
|
||||
@interface YTSlimVideoScrollableDetailsActionsView : UIScrollView
|
||||
@property (nonatomic, weak, readwrite) NSObject <YTSlimVideoDetailsActionViewDelegate> *videoActionsDelegate;
|
||||
@end
|
||||
@@ -0,0 +1,4 @@
|
||||
#import "YTMultiSizeViewController.h"
|
||||
|
||||
@interface YTStyledViewController : YTMultiSizeViewController
|
||||
@end
|
||||
@@ -0,0 +1,5 @@
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
@interface YTSystemNotifications : NSObject
|
||||
- (void)addSystemNotificationsObserver:(id)observer;
|
||||
@end
|
||||
@@ -0,0 +1,6 @@
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
@interface YTToastResponderEvent : NSObject
|
||||
+ (instancetype)eventWithMessage:(NSString *)message firstResponder:(id)firstResponder;
|
||||
- (void)send;
|
||||
@end
|
||||
@@ -0,0 +1,6 @@
|
||||
#import <UIKit/UIKit.h>
|
||||
|
||||
@interface YTUIResources : NSObject
|
||||
+ (UIImage *)iconCheckTemplateImage;
|
||||
+ (UIImage *)actionsheetDefaultImage;
|
||||
@end
|
||||
7
Tweaks/iSponsorBlock/Headers/YouTubeHeader/YTUIUtils.h
Normal file
7
Tweaks/iSponsorBlock/Headers/YouTubeHeader/YTUIUtils.h
Normal file
@@ -0,0 +1,7 @@
|
||||
#import <UIKit/UIKit.h>
|
||||
|
||||
@interface YTUIUtils : NSObject
|
||||
+ (BOOL)canOpenURL:(NSURL *)url;
|
||||
+ (BOOL)openURL:(NSURL *)url;
|
||||
+ (UIViewController *)topViewControllerForPresenting;
|
||||
@end
|
||||
@@ -0,0 +1,5 @@
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
@interface YTVersionUtils : NSObject
|
||||
+ (NSString *)appVersion;
|
||||
@end
|
||||
@@ -0,0 +1,5 @@
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
@interface YTVideoQualitySwitchOriginalController : NSObject
|
||||
- (instancetype)initWithParentResponder:(id)responder;
|
||||
@end
|
||||
@@ -0,0 +1,7 @@
|
||||
#import "YTWatchPlaybackController.h"
|
||||
|
||||
@interface YTWatchController : NSObject
|
||||
@property (nonatomic, strong, readwrite) YTWatchPlaybackController *watchPlaybackController;
|
||||
- (void)showFullScreen;
|
||||
- (void)showSmallScreen;
|
||||
@end
|
||||
@@ -0,0 +1,5 @@
|
||||
#import "YTPlayerViewController.h"
|
||||
|
||||
@interface YTWatchLayerViewController : UIViewController
|
||||
- (YTPlayerViewController *)playerViewController;
|
||||
@end
|
||||
@@ -0,0 +1,4 @@
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
@protocol YTWatchMetadataPanelStateResponderProvider <NSObject>
|
||||
@end
|
||||
@@ -0,0 +1,6 @@
|
||||
#import <UIKit/UIKit.h>
|
||||
|
||||
@interface YTWatchMiniBarViewController : UIViewController
|
||||
@property (nonatomic, assign, readwrite, getter=isActivated) BOOL activated;
|
||||
@property (nonatomic, assign, readwrite, getter=isVisible) BOOL visible;
|
||||
@end
|
||||
@@ -0,0 +1,4 @@
|
||||
#import <UIKit/UIKit.h>
|
||||
|
||||
@interface YTWatchNextResultsViewController : UIViewController
|
||||
@end
|
||||
@@ -0,0 +1,4 @@
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
@interface YTWatchPlaybackController : NSObject
|
||||
@end
|
||||
@@ -0,0 +1,5 @@
|
||||
#import "YTPlayerViewController.h"
|
||||
|
||||
@interface YTWatchViewController : UIViewController
|
||||
@property (nonatomic, weak, readwrite) YTPlayerViewController *playerViewController;
|
||||
@end
|
||||
@@ -0,0 +1,8 @@
|
||||
#import <UIKit/UIKit.h>
|
||||
#import "ASCollectionElement.h"
|
||||
#import "ASCellNode.h"
|
||||
|
||||
@interface _ASCollectionViewCell : UICollectionViewCell
|
||||
@property (nonatomic, strong, readwrite) ASCollectionElement *element;
|
||||
- (ASCellNode *)node;
|
||||
@end
|
||||
@@ -0,0 +1,6 @@
|
||||
#import "ASDisplayNode.h"
|
||||
|
||||
@interface _ASDisplayView : UIView
|
||||
@property (nonatomic, copy, readwrite) NSString *accessibilityLabel;
|
||||
@property (nonatomic) ASDisplayNode *keepalive_node;
|
||||
@end
|
||||
138
Tweaks/iSponsorBlock/Headers/iSponsorBlock.h
Normal file
138
Tweaks/iSponsorBlock/Headers/iSponsorBlock.h
Normal file
@@ -0,0 +1,138 @@
|
||||
#import <AVFoundation/AVFoundation.h>
|
||||
#import <AVKit/AVKit.h>
|
||||
#import <dlfcn.h>
|
||||
#import "YouTubeHeader/YTInlinePlayerBarView.h"
|
||||
#import "YouTubeHeader/YTMainAppControlsOverlayView.h"
|
||||
#import "YouTubeHeader/YTMainAppVideoPlayerOverlayViewController.h"
|
||||
#import "YouTubeHeader/YTRightNavigationButtons.h"
|
||||
#import "YouTubeHeader/YTPageStyleController.h"
|
||||
#import "YouTubeHeader/YTPlayerView.h"
|
||||
#import "YouTubeHeader/YTPlayerViewController.h"
|
||||
#import "YouTubeHeader/YTPlayerBarSegmentMarkerView.h"
|
||||
#import "YouTubeHeader/YTPlayerBarSegmentedProgressView.h"
|
||||
#import "YouTubeHeader/YTSegmentableInlinePlayerBarView.h"
|
||||
#import "YouTubeHeader/YTSingleVideoTime.h"
|
||||
#import "YouTubeHeader/YTNGWatchLayerViewController.h"
|
||||
#import "YouTubeHeader/YTWatchLayerViewController.h"
|
||||
#import "YouTubeHeader/YTIChapterRenderer.h"
|
||||
#import "MBProgressHUD.h"
|
||||
#import "SponsorSegment.h"
|
||||
#include <math.h>
|
||||
|
||||
// prefs
|
||||
BOOL kIsEnabled;
|
||||
NSString *kUserID;
|
||||
NSString *kAPIInstance;
|
||||
NSDictionary *kCategorySettings;
|
||||
CGFloat kMinimumDuration;
|
||||
BOOL kShowSkipNotice;
|
||||
BOOL kShowButtonsInPlayer;
|
||||
BOOL kHideStartEndButtonInPlayer;
|
||||
BOOL kShowModifiedTime;
|
||||
BOOL kSkipAudioNotification;
|
||||
BOOL kEnableSkipCountTracking;
|
||||
CGFloat kSkipNoticeDuration;
|
||||
NSMutableArray <NSString *> *kWhitelistedChannels;
|
||||
|
||||
@interface YTPlayerViewController (iSB)
|
||||
@property (strong, nonatomic) NSMutableArray <SponsorSegment *> *skipSegments;
|
||||
@property (nonatomic, assign) NSInteger currentSponsorSegment;
|
||||
@property (strong, nonatomic) MBProgressHUD *hud;
|
||||
@property (nonatomic, assign) NSInteger unskippedSegment;
|
||||
@property (strong, nonatomic) NSMutableArray <SponsorSegment *> *userSkipSegments;
|
||||
@property (nonatomic, assign) BOOL hudDisplayed;
|
||||
- (void)isb_scrubToTime:(CGFloat)time;
|
||||
- (void)isb_fixVisualGlitch;
|
||||
@end
|
||||
|
||||
@interface YTMainAppControlsOverlayView (iSB)
|
||||
- (void)sponsorBlockButtonPressed:(YTQTMButton *)sender;
|
||||
- (void)sponsorStartedButtonPressed:(YTQTMButton *)sender;
|
||||
- (void)sponsorEndedButtonPressed:(YTQTMButton *)sender;
|
||||
- (void)presentSponsorBlockViewController;
|
||||
@property (retain, nonatomic) YTQTMButton *sponsorBlockButton;
|
||||
@property (retain, nonatomic) YTQTMButton *sponsorStartedEndedButton;
|
||||
@property (nonatomic, assign) BOOL isDisplayingSponsorBlockViewController;
|
||||
@end
|
||||
|
||||
// Old class
|
||||
@interface YTIChapterRendererWrapper : NSObject
|
||||
- (instancetype)initWithStartTime:(CGFloat)arg1 endTime:(CGFloat)arg2 title:(NSString *)arg3;
|
||||
+ (instancetype)chapterRendererWrapperWithRenderer:(YTIChapterRenderer *)arg1;
|
||||
@property (nonatomic, assign) CGFloat endTime;
|
||||
@end
|
||||
|
||||
@interface YTPlayerBarSegmentedProgressView (iSB)
|
||||
@property (strong, nonatomic) NSMutableArray *sponsorMarkerViews;
|
||||
@property (nonatomic, retain) NSMutableArray <SponsorSegment *> *skipSegments;
|
||||
- (void)removeSponsorMarkers;
|
||||
@end
|
||||
|
||||
@interface YTPlayerBarSegmentMarkerView (iSB)
|
||||
@property (nonatomic, assign) BOOL isSponsorMarker;
|
||||
@end
|
||||
|
||||
@interface YTRightNavigationButtons (iSB)
|
||||
@property (strong, nonatomic) YTQTMButton *sponsorBlockButton;
|
||||
@end
|
||||
|
||||
@interface YTInlinePlayerBarView (iSB)
|
||||
@property (strong, nonatomic) NSMutableArray *sponsorMarkerViews;
|
||||
@property (strong, nonatomic) NSMutableArray *skipSegments;
|
||||
- (void)removeSponsorMarkers;
|
||||
- (void)maybeCreateMarkerViewsISB;
|
||||
@end
|
||||
|
||||
@interface YTSegmentableInlinePlayerBarView (iSB)
|
||||
@property (strong, nonatomic) NSMutableArray *sponsorMarkerViews;
|
||||
@property (strong, nonatomic) NSMutableArray *skipSegments;
|
||||
- (void)removeSponsorMarkers;
|
||||
- (void)maybeCreateMarkerViewsISB;
|
||||
@end
|
||||
|
||||
// Cercube
|
||||
@interface CADownloadObject : NSObject
|
||||
@property(readonly, nonatomic) NSString *filePath;
|
||||
@property(copy, nonatomic) NSString *fileName; // @dynamic fileName;
|
||||
@property(copy, nonatomic) NSString *videoId;
|
||||
@end
|
||||
|
||||
@interface CADownloadObject_CADownloadObject_ : CADownloadObject
|
||||
@end
|
||||
|
||||
@interface AVPlayerItem (Private)
|
||||
- (NSURL *)_URL;
|
||||
@end
|
||||
|
||||
@interface AVQueuePlayer (iSB)
|
||||
@property (strong, nonatomic) NSMutableArray <SponsorSegment *> *skipSegments;
|
||||
@property (nonatomic, assign) NSInteger currentSponsorSegment;
|
||||
@property (strong, nonatomic) MBProgressHUD *hud;
|
||||
@property (nonatomic, assign) NSInteger unskippedSegment;
|
||||
@property (nonatomic, assign) BOOL isSeeking;
|
||||
@property (nonatomic, assign) NSInteger currentPlayerItem;
|
||||
@property (strong, nonatomic) id timeObserver;
|
||||
@property (strong, nonatomic) AVPlayerViewController *playerViewController;
|
||||
@property (strong, nonatomic) NSMutableArray *markerViews;
|
||||
@property (nonatomic, assign) BOOL hudDisplayed;
|
||||
- (void)sponsorBlockSetup;
|
||||
- (void)updateMarkerViews;
|
||||
@end
|
||||
|
||||
@interface AVScrubber : UIView
|
||||
@end
|
||||
|
||||
@interface AVPlaybackControlsView : UIView
|
||||
@property (strong, nonatomic) AVScrubber *scrubber;
|
||||
@end
|
||||
|
||||
@interface AVPlayerViewControllerContentView : UIView
|
||||
@property (strong, nonatomic) AVPlaybackControlsView *playbackControlsView;
|
||||
@end
|
||||
|
||||
@interface AVPlayerViewController ()
|
||||
@property (strong, nonatomic) AVPlayerViewControllerContentView *contentView;
|
||||
@end
|
||||
|
||||
@interface AVContentOverlayView : UIView
|
||||
@end
|
||||
674
Tweaks/iSponsorBlock/LICENSE
Normal file
674
Tweaks/iSponsorBlock/LICENSE
Normal file
@@ -0,0 +1,674 @@
|
||||
GNU GENERAL PUBLIC LICENSE
|
||||
Version 3, 29 June 2007
|
||||
|
||||
Copyright (C) 2007 Free Software Foundation, Inc. <https://fsf.org/>
|
||||
Everyone is permitted to copy and distribute verbatim copies
|
||||
of this license document, but changing it is not allowed.
|
||||
|
||||
Preamble
|
||||
|
||||
The GNU General Public License is a free, copyleft license for
|
||||
software and other kinds of works.
|
||||
|
||||
The licenses for most software and other practical works are designed
|
||||
to take away your freedom to share and change the works. By contrast,
|
||||
the GNU General Public License is intended to guarantee your freedom to
|
||||
share and change all versions of a program--to make sure it remains free
|
||||
software for all its users. We, the Free Software Foundation, use the
|
||||
GNU General Public License for most of our software; it applies also to
|
||||
any other work released this way by its authors. You can apply it to
|
||||
your programs, too.
|
||||
|
||||
When we speak of free software, we are referring to freedom, not
|
||||
price. Our General Public Licenses are designed to make sure that you
|
||||
have the freedom to distribute copies of free software (and charge for
|
||||
them if you wish), that you receive source code or can get it if you
|
||||
want it, that you can change the software or use pieces of it in new
|
||||
free programs, and that you know you can do these things.
|
||||
|
||||
To protect your rights, we need to prevent others from denying you
|
||||
these rights or asking you to surrender the rights. Therefore, you have
|
||||
certain responsibilities if you distribute copies of the software, or if
|
||||
you modify it: responsibilities to respect the freedom of others.
|
||||
|
||||
For example, if you distribute copies of such a program, whether
|
||||
gratis or for a fee, you must pass on to the recipients the same
|
||||
freedoms that you received. You must make sure that they, too, receive
|
||||
or can get the source code. And you must show them these terms so they
|
||||
know their rights.
|
||||
|
||||
Developers that use the GNU GPL protect your rights with two steps:
|
||||
(1) assert copyright on the software, and (2) offer you this License
|
||||
giving you legal permission to copy, distribute and/or modify it.
|
||||
|
||||
For the developers' and authors' protection, the GPL clearly explains
|
||||
that there is no warranty for this free software. For both users' and
|
||||
authors' sake, the GPL requires that modified versions be marked as
|
||||
changed, so that their problems will not be attributed erroneously to
|
||||
authors of previous versions.
|
||||
|
||||
Some devices are designed to deny users access to install or run
|
||||
modified versions of the software inside them, although the manufacturer
|
||||
can do so. This is fundamentally incompatible with the aim of
|
||||
protecting users' freedom to change the software. The systematic
|
||||
pattern of such abuse occurs in the area of products for individuals to
|
||||
use, which is precisely where it is most unacceptable. Therefore, we
|
||||
have designed this version of the GPL to prohibit the practice for those
|
||||
products. If such problems arise substantially in other domains, we
|
||||
stand ready to extend this provision to those domains in future versions
|
||||
of the GPL, as needed to protect the freedom of users.
|
||||
|
||||
Finally, every program is threatened constantly by software patents.
|
||||
States should not allow patents to restrict development and use of
|
||||
software on general-purpose computers, but in those that do, we wish to
|
||||
avoid the special danger that patents applied to a free program could
|
||||
make it effectively proprietary. To prevent this, the GPL assures that
|
||||
patents cannot be used to render the program non-free.
|
||||
|
||||
The precise terms and conditions for copying, distribution and
|
||||
modification follow.
|
||||
|
||||
TERMS AND CONDITIONS
|
||||
|
||||
0. Definitions.
|
||||
|
||||
"This License" refers to version 3 of the GNU General Public License.
|
||||
|
||||
"Copyright" also means copyright-like laws that apply to other kinds of
|
||||
works, such as semiconductor masks.
|
||||
|
||||
"The Program" refers to any copyrightable work licensed under this
|
||||
License. Each licensee is addressed as "you". "Licensees" and
|
||||
"recipients" may be individuals or organizations.
|
||||
|
||||
To "modify" a work means to copy from or adapt all or part of the work
|
||||
in a fashion requiring copyright permission, other than the making of an
|
||||
exact copy. The resulting work is called a "modified version" of the
|
||||
earlier work or a work "based on" the earlier work.
|
||||
|
||||
A "covered work" means either the unmodified Program or a work based
|
||||
on the Program.
|
||||
|
||||
To "propagate" a work means to do anything with it that, without
|
||||
permission, would make you directly or secondarily liable for
|
||||
infringement under applicable copyright law, except executing it on a
|
||||
computer or modifying a private copy. Propagation includes copying,
|
||||
distribution (with or without modification), making available to the
|
||||
public, and in some countries other activities as well.
|
||||
|
||||
To "convey" a work means any kind of propagation that enables other
|
||||
parties to make or receive copies. Mere interaction with a user through
|
||||
a computer network, with no transfer of a copy, is not conveying.
|
||||
|
||||
An interactive user interface displays "Appropriate Legal Notices"
|
||||
to the extent that it includes a convenient and prominently visible
|
||||
feature that (1) displays an appropriate copyright notice, and (2)
|
||||
tells the user that there is no warranty for the work (except to the
|
||||
extent that warranties are provided), that licensees may convey the
|
||||
work under this License, and how to view a copy of this License. If
|
||||
the interface presents a list of user commands or options, such as a
|
||||
menu, a prominent item in the list meets this criterion.
|
||||
|
||||
1. Source Code.
|
||||
|
||||
The "source code" for a work means the preferred form of the work
|
||||
for making modifications to it. "Object code" means any non-source
|
||||
form of a work.
|
||||
|
||||
A "Standard Interface" means an interface that either is an official
|
||||
standard defined by a recognized standards body, or, in the case of
|
||||
interfaces specified for a particular programming language, one that
|
||||
is widely used among developers working in that language.
|
||||
|
||||
The "System Libraries" of an executable work include anything, other
|
||||
than the work as a whole, that (a) is included in the normal form of
|
||||
packaging a Major Component, but which is not part of that Major
|
||||
Component, and (b) serves only to enable use of the work with that
|
||||
Major Component, or to implement a Standard Interface for which an
|
||||
implementation is available to the public in source code form. A
|
||||
"Major Component", in this context, means a major essential component
|
||||
(kernel, window system, and so on) of the specific operating system
|
||||
(if any) on which the executable work runs, or a compiler used to
|
||||
produce the work, or an object code interpreter used to run it.
|
||||
|
||||
The "Corresponding Source" for a work in object code form means all
|
||||
the source code needed to generate, install, and (for an executable
|
||||
work) run the object code and to modify the work, including scripts to
|
||||
control those activities. However, it does not include the work's
|
||||
System Libraries, or general-purpose tools or generally available free
|
||||
programs which are used unmodified in performing those activities but
|
||||
which are not part of the work. For example, Corresponding Source
|
||||
includes interface definition files associated with source files for
|
||||
the work, and the source code for shared libraries and dynamically
|
||||
linked subprograms that the work is specifically designed to require,
|
||||
such as by intimate data communication or control flow between those
|
||||
subprograms and other parts of the work.
|
||||
|
||||
The Corresponding Source need not include anything that users
|
||||
can regenerate automatically from other parts of the Corresponding
|
||||
Source.
|
||||
|
||||
The Corresponding Source for a work in source code form is that
|
||||
same work.
|
||||
|
||||
2. Basic Permissions.
|
||||
|
||||
All rights granted under this License are granted for the term of
|
||||
copyright on the Program, and are irrevocable provided the stated
|
||||
conditions are met. This License explicitly affirms your unlimited
|
||||
permission to run the unmodified Program. The output from running a
|
||||
covered work is covered by this License only if the output, given its
|
||||
content, constitutes a covered work. This License acknowledges your
|
||||
rights of fair use or other equivalent, as provided by copyright law.
|
||||
|
||||
You may make, run and propagate covered works that you do not
|
||||
convey, without conditions so long as your license otherwise remains
|
||||
in force. You may convey covered works to others for the sole purpose
|
||||
of having them make modifications exclusively for you, or provide you
|
||||
with facilities for running those works, provided that you comply with
|
||||
the terms of this License in conveying all material for which you do
|
||||
not control copyright. Those thus making or running the covered works
|
||||
for you must do so exclusively on your behalf, under your direction
|
||||
and control, on terms that prohibit them from making any copies of
|
||||
your copyrighted material outside their relationship with you.
|
||||
|
||||
Conveying under any other circumstances is permitted solely under
|
||||
the conditions stated below. Sublicensing is not allowed; section 10
|
||||
makes it unnecessary.
|
||||
|
||||
3. Protecting Users' Legal Rights From Anti-Circumvention Law.
|
||||
|
||||
No covered work shall be deemed part of an effective technological
|
||||
measure under any applicable law fulfilling obligations under article
|
||||
11 of the WIPO copyright treaty adopted on 20 December 1996, or
|
||||
similar laws prohibiting or restricting circumvention of such
|
||||
measures.
|
||||
|
||||
When you convey a covered work, you waive any legal power to forbid
|
||||
circumvention of technological measures to the extent such circumvention
|
||||
is effected by exercising rights under this License with respect to
|
||||
the covered work, and you disclaim any intention to limit operation or
|
||||
modification of the work as a means of enforcing, against the work's
|
||||
users, your or third parties' legal rights to forbid circumvention of
|
||||
technological measures.
|
||||
|
||||
4. Conveying Verbatim Copies.
|
||||
|
||||
You may convey verbatim copies of the Program's source code as you
|
||||
receive it, in any medium, provided that you conspicuously and
|
||||
appropriately publish on each copy an appropriate copyright notice;
|
||||
keep intact all notices stating that this License and any
|
||||
non-permissive terms added in accord with section 7 apply to the code;
|
||||
keep intact all notices of the absence of any warranty; and give all
|
||||
recipients a copy of this License along with the Program.
|
||||
|
||||
You may charge any price or no price for each copy that you convey,
|
||||
and you may offer support or warranty protection for a fee.
|
||||
|
||||
5. Conveying Modified Source Versions.
|
||||
|
||||
You may convey a work based on the Program, or the modifications to
|
||||
produce it from the Program, in the form of source code under the
|
||||
terms of section 4, provided that you also meet all of these conditions:
|
||||
|
||||
a) The work must carry prominent notices stating that you modified
|
||||
it, and giving a relevant date.
|
||||
|
||||
b) The work must carry prominent notices stating that it is
|
||||
released under this License and any conditions added under section
|
||||
7. This requirement modifies the requirement in section 4 to
|
||||
"keep intact all notices".
|
||||
|
||||
c) You must license the entire work, as a whole, under this
|
||||
License to anyone who comes into possession of a copy. This
|
||||
License will therefore apply, along with any applicable section 7
|
||||
additional terms, to the whole of the work, and all its parts,
|
||||
regardless of how they are packaged. This License gives no
|
||||
permission to license the work in any other way, but it does not
|
||||
invalidate such permission if you have separately received it.
|
||||
|
||||
d) If the work has interactive user interfaces, each must display
|
||||
Appropriate Legal Notices; however, if the Program has interactive
|
||||
interfaces that do not display Appropriate Legal Notices, your
|
||||
work need not make them do so.
|
||||
|
||||
A compilation of a covered work with other separate and independent
|
||||
works, which are not by their nature extensions of the covered work,
|
||||
and which are not combined with it such as to form a larger program,
|
||||
in or on a volume of a storage or distribution medium, is called an
|
||||
"aggregate" if the compilation and its resulting copyright are not
|
||||
used to limit the access or legal rights of the compilation's users
|
||||
beyond what the individual works permit. Inclusion of a covered work
|
||||
in an aggregate does not cause this License to apply to the other
|
||||
parts of the aggregate.
|
||||
|
||||
6. Conveying Non-Source Forms.
|
||||
|
||||
You may convey a covered work in object code form under the terms
|
||||
of sections 4 and 5, provided that you also convey the
|
||||
machine-readable Corresponding Source under the terms of this License,
|
||||
in one of these ways:
|
||||
|
||||
a) Convey the object code in, or embodied in, a physical product
|
||||
(including a physical distribution medium), accompanied by the
|
||||
Corresponding Source fixed on a durable physical medium
|
||||
customarily used for software interchange.
|
||||
|
||||
b) Convey the object code in, or embodied in, a physical product
|
||||
(including a physical distribution medium), accompanied by a
|
||||
written offer, valid for at least three years and valid for as
|
||||
long as you offer spare parts or customer support for that product
|
||||
model, to give anyone who possesses the object code either (1) a
|
||||
copy of the Corresponding Source for all the software in the
|
||||
product that is covered by this License, on a durable physical
|
||||
medium customarily used for software interchange, for a price no
|
||||
more than your reasonable cost of physically performing this
|
||||
conveying of source, or (2) access to copy the
|
||||
Corresponding Source from a network server at no charge.
|
||||
|
||||
c) Convey individual copies of the object code with a copy of the
|
||||
written offer to provide the Corresponding Source. This
|
||||
alternative is allowed only occasionally and noncommercially, and
|
||||
only if you received the object code with such an offer, in accord
|
||||
with subsection 6b.
|
||||
|
||||
d) Convey the object code by offering access from a designated
|
||||
place (gratis or for a charge), and offer equivalent access to the
|
||||
Corresponding Source in the same way through the same place at no
|
||||
further charge. You need not require recipients to copy the
|
||||
Corresponding Source along with the object code. If the place to
|
||||
copy the object code is a network server, the Corresponding Source
|
||||
may be on a different server (operated by you or a third party)
|
||||
that supports equivalent copying facilities, provided you maintain
|
||||
clear directions next to the object code saying where to find the
|
||||
Corresponding Source. Regardless of what server hosts the
|
||||
Corresponding Source, you remain obligated to ensure that it is
|
||||
available for as long as needed to satisfy these requirements.
|
||||
|
||||
e) Convey the object code using peer-to-peer transmission, provided
|
||||
you inform other peers where the object code and Corresponding
|
||||
Source of the work are being offered to the general public at no
|
||||
charge under subsection 6d.
|
||||
|
||||
A separable portion of the object code, whose source code is excluded
|
||||
from the Corresponding Source as a System Library, need not be
|
||||
included in conveying the object code work.
|
||||
|
||||
A "User Product" is either (1) a "consumer product", which means any
|
||||
tangible personal property which is normally used for personal, family,
|
||||
or household purposes, or (2) anything designed or sold for incorporation
|
||||
into a dwelling. In determining whether a product is a consumer product,
|
||||
doubtful cases shall be resolved in favor of coverage. For a particular
|
||||
product received by a particular user, "normally used" refers to a
|
||||
typical or common use of that class of product, regardless of the status
|
||||
of the particular user or of the way in which the particular user
|
||||
actually uses, or expects or is expected to use, the product. A product
|
||||
is a consumer product regardless of whether the product has substantial
|
||||
commercial, industrial or non-consumer uses, unless such uses represent
|
||||
the only significant mode of use of the product.
|
||||
|
||||
"Installation Information" for a User Product means any methods,
|
||||
procedures, authorization keys, or other information required to install
|
||||
and execute modified versions of a covered work in that User Product from
|
||||
a modified version of its Corresponding Source. The information must
|
||||
suffice to ensure that the continued functioning of the modified object
|
||||
code is in no case prevented or interfered with solely because
|
||||
modification has been made.
|
||||
|
||||
If you convey an object code work under this section in, or with, or
|
||||
specifically for use in, a User Product, and the conveying occurs as
|
||||
part of a transaction in which the right of possession and use of the
|
||||
User Product is transferred to the recipient in perpetuity or for a
|
||||
fixed term (regardless of how the transaction is characterized), the
|
||||
Corresponding Source conveyed under this section must be accompanied
|
||||
by the Installation Information. But this requirement does not apply
|
||||
if neither you nor any third party retains the ability to install
|
||||
modified object code on the User Product (for example, the work has
|
||||
been installed in ROM).
|
||||
|
||||
The requirement to provide Installation Information does not include a
|
||||
requirement to continue to provide support service, warranty, or updates
|
||||
for a work that has been modified or installed by the recipient, or for
|
||||
the User Product in which it has been modified or installed. Access to a
|
||||
network may be denied when the modification itself materially and
|
||||
adversely affects the operation of the network or violates the rules and
|
||||
protocols for communication across the network.
|
||||
|
||||
Corresponding Source conveyed, and Installation Information provided,
|
||||
in accord with this section must be in a format that is publicly
|
||||
documented (and with an implementation available to the public in
|
||||
source code form), and must require no special password or key for
|
||||
unpacking, reading or copying.
|
||||
|
||||
7. Additional Terms.
|
||||
|
||||
"Additional permissions" are terms that supplement the terms of this
|
||||
License by making exceptions from one or more of its conditions.
|
||||
Additional permissions that are applicable to the entire Program shall
|
||||
be treated as though they were included in this License, to the extent
|
||||
that they are valid under applicable law. If additional permissions
|
||||
apply only to part of the Program, that part may be used separately
|
||||
under those permissions, but the entire Program remains governed by
|
||||
this License without regard to the additional permissions.
|
||||
|
||||
When you convey a copy of a covered work, you may at your option
|
||||
remove any additional permissions from that copy, or from any part of
|
||||
it. (Additional permissions may be written to require their own
|
||||
removal in certain cases when you modify the work.) You may place
|
||||
additional permissions on material, added by you to a covered work,
|
||||
for which you have or can give appropriate copyright permission.
|
||||
|
||||
Notwithstanding any other provision of this License, for material you
|
||||
add to a covered work, you may (if authorized by the copyright holders of
|
||||
that material) supplement the terms of this License with terms:
|
||||
|
||||
a) Disclaiming warranty or limiting liability differently from the
|
||||
terms of sections 15 and 16 of this License; or
|
||||
|
||||
b) Requiring preservation of specified reasonable legal notices or
|
||||
author attributions in that material or in the Appropriate Legal
|
||||
Notices displayed by works containing it; or
|
||||
|
||||
c) Prohibiting misrepresentation of the origin of that material, or
|
||||
requiring that modified versions of such material be marked in
|
||||
reasonable ways as different from the original version; or
|
||||
|
||||
d) Limiting the use for publicity purposes of names of licensors or
|
||||
authors of the material; or
|
||||
|
||||
e) Declining to grant rights under trademark law for use of some
|
||||
trade names, trademarks, or service marks; or
|
||||
|
||||
f) Requiring indemnification of licensors and authors of that
|
||||
material by anyone who conveys the material (or modified versions of
|
||||
it) with contractual assumptions of liability to the recipient, for
|
||||
any liability that these contractual assumptions directly impose on
|
||||
those licensors and authors.
|
||||
|
||||
All other non-permissive additional terms are considered "further
|
||||
restrictions" within the meaning of section 10. If the Program as you
|
||||
received it, or any part of it, contains a notice stating that it is
|
||||
governed by this License along with a term that is a further
|
||||
restriction, you may remove that term. If a license document contains
|
||||
a further restriction but permits relicensing or conveying under this
|
||||
License, you may add to a covered work material governed by the terms
|
||||
of that license document, provided that the further restriction does
|
||||
not survive such relicensing or conveying.
|
||||
|
||||
If you add terms to a covered work in accord with this section, you
|
||||
must place, in the relevant source files, a statement of the
|
||||
additional terms that apply to those files, or a notice indicating
|
||||
where to find the applicable terms.
|
||||
|
||||
Additional terms, permissive or non-permissive, may be stated in the
|
||||
form of a separately written license, or stated as exceptions;
|
||||
the above requirements apply either way.
|
||||
|
||||
8. Termination.
|
||||
|
||||
You may not propagate or modify a covered work except as expressly
|
||||
provided under this License. Any attempt otherwise to propagate or
|
||||
modify it is void, and will automatically terminate your rights under
|
||||
this License (including any patent licenses granted under the third
|
||||
paragraph of section 11).
|
||||
|
||||
However, if you cease all violation of this License, then your
|
||||
license from a particular copyright holder is reinstated (a)
|
||||
provisionally, unless and until the copyright holder explicitly and
|
||||
finally terminates your license, and (b) permanently, if the copyright
|
||||
holder fails to notify you of the violation by some reasonable means
|
||||
prior to 60 days after the cessation.
|
||||
|
||||
Moreover, your license from a particular copyright holder is
|
||||
reinstated permanently if the copyright holder notifies you of the
|
||||
violation by some reasonable means, this is the first time you have
|
||||
received notice of violation of this License (for any work) from that
|
||||
copyright holder, and you cure the violation prior to 30 days after
|
||||
your receipt of the notice.
|
||||
|
||||
Termination of your rights under this section does not terminate the
|
||||
licenses of parties who have received copies or rights from you under
|
||||
this License. If your rights have been terminated and not permanently
|
||||
reinstated, you do not qualify to receive new licenses for the same
|
||||
material under section 10.
|
||||
|
||||
9. Acceptance Not Required for Having Copies.
|
||||
|
||||
You are not required to accept this License in order to receive or
|
||||
run a copy of the Program. Ancillary propagation of a covered work
|
||||
occurring solely as a consequence of using peer-to-peer transmission
|
||||
to receive a copy likewise does not require acceptance. However,
|
||||
nothing other than this License grants you permission to propagate or
|
||||
modify any covered work. These actions infringe copyright if you do
|
||||
not accept this License. Therefore, by modifying or propagating a
|
||||
covered work, you indicate your acceptance of this License to do so.
|
||||
|
||||
10. Automatic Licensing of Downstream Recipients.
|
||||
|
||||
Each time you convey a covered work, the recipient automatically
|
||||
receives a license from the original licensors, to run, modify and
|
||||
propagate that work, subject to this License. You are not responsible
|
||||
for enforcing compliance by third parties with this License.
|
||||
|
||||
An "entity transaction" is a transaction transferring control of an
|
||||
organization, or substantially all assets of one, or subdividing an
|
||||
organization, or merging organizations. If propagation of a covered
|
||||
work results from an entity transaction, each party to that
|
||||
transaction who receives a copy of the work also receives whatever
|
||||
licenses to the work the party's predecessor in interest had or could
|
||||
give under the previous paragraph, plus a right to possession of the
|
||||
Corresponding Source of the work from the predecessor in interest, if
|
||||
the predecessor has it or can get it with reasonable efforts.
|
||||
|
||||
You may not impose any further restrictions on the exercise of the
|
||||
rights granted or affirmed under this License. For example, you may
|
||||
not impose a license fee, royalty, or other charge for exercise of
|
||||
rights granted under this License, and you may not initiate litigation
|
||||
(including a cross-claim or counterclaim in a lawsuit) alleging that
|
||||
any patent claim is infringed by making, using, selling, offering for
|
||||
sale, or importing the Program or any portion of it.
|
||||
|
||||
11. Patents.
|
||||
|
||||
A "contributor" is a copyright holder who authorizes use under this
|
||||
License of the Program or a work on which the Program is based. The
|
||||
work thus licensed is called the contributor's "contributor version".
|
||||
|
||||
A contributor's "essential patent claims" are all patent claims
|
||||
owned or controlled by the contributor, whether already acquired or
|
||||
hereafter acquired, that would be infringed by some manner, permitted
|
||||
by this License, of making, using, or selling its contributor version,
|
||||
but do not include claims that would be infringed only as a
|
||||
consequence of further modification of the contributor version. For
|
||||
purposes of this definition, "control" includes the right to grant
|
||||
patent sublicenses in a manner consistent with the requirements of
|
||||
this License.
|
||||
|
||||
Each contributor grants you a non-exclusive, worldwide, royalty-free
|
||||
patent license under the contributor's essential patent claims, to
|
||||
make, use, sell, offer for sale, import and otherwise run, modify and
|
||||
propagate the contents of its contributor version.
|
||||
|
||||
In the following three paragraphs, a "patent license" is any express
|
||||
agreement or commitment, however denominated, not to enforce a patent
|
||||
(such as an express permission to practice a patent or covenant not to
|
||||
sue for patent infringement). To "grant" such a patent license to a
|
||||
party means to make such an agreement or commitment not to enforce a
|
||||
patent against the party.
|
||||
|
||||
If you convey a covered work, knowingly relying on a patent license,
|
||||
and the Corresponding Source of the work is not available for anyone
|
||||
to copy, free of charge and under the terms of this License, through a
|
||||
publicly available network server or other readily accessible means,
|
||||
then you must either (1) cause the Corresponding Source to be so
|
||||
available, or (2) arrange to deprive yourself of the benefit of the
|
||||
patent license for this particular work, or (3) arrange, in a manner
|
||||
consistent with the requirements of this License, to extend the patent
|
||||
license to downstream recipients. "Knowingly relying" means you have
|
||||
actual knowledge that, but for the patent license, your conveying the
|
||||
covered work in a country, or your recipient's use of the covered work
|
||||
in a country, would infringe one or more identifiable patents in that
|
||||
country that you have reason to believe are valid.
|
||||
|
||||
If, pursuant to or in connection with a single transaction or
|
||||
arrangement, you convey, or propagate by procuring conveyance of, a
|
||||
covered work, and grant a patent license to some of the parties
|
||||
receiving the covered work authorizing them to use, propagate, modify
|
||||
or convey a specific copy of the covered work, then the patent license
|
||||
you grant is automatically extended to all recipients of the covered
|
||||
work and works based on it.
|
||||
|
||||
A patent license is "discriminatory" if it does not include within
|
||||
the scope of its coverage, prohibits the exercise of, or is
|
||||
conditioned on the non-exercise of one or more of the rights that are
|
||||
specifically granted under this License. You may not convey a covered
|
||||
work if you are a party to an arrangement with a third party that is
|
||||
in the business of distributing software, under which you make payment
|
||||
to the third party based on the extent of your activity of conveying
|
||||
the work, and under which the third party grants, to any of the
|
||||
parties who would receive the covered work from you, a discriminatory
|
||||
patent license (a) in connection with copies of the covered work
|
||||
conveyed by you (or copies made from those copies), or (b) primarily
|
||||
for and in connection with specific products or compilations that
|
||||
contain the covered work, unless you entered into that arrangement,
|
||||
or that patent license was granted, prior to 28 March 2007.
|
||||
|
||||
Nothing in this License shall be construed as excluding or limiting
|
||||
any implied license or other defenses to infringement that may
|
||||
otherwise be available to you under applicable patent law.
|
||||
|
||||
12. No Surrender of Others' Freedom.
|
||||
|
||||
If conditions are imposed on you (whether by court order, agreement or
|
||||
otherwise) that contradict the conditions of this License, they do not
|
||||
excuse you from the conditions of this License. If you cannot convey a
|
||||
covered work so as to satisfy simultaneously your obligations under this
|
||||
License and any other pertinent obligations, then as a consequence you may
|
||||
not convey it at all. For example, if you agree to terms that obligate you
|
||||
to collect a royalty for further conveying from those to whom you convey
|
||||
the Program, the only way you could satisfy both those terms and this
|
||||
License would be to refrain entirely from conveying the Program.
|
||||
|
||||
13. Use with the GNU Affero General Public License.
|
||||
|
||||
Notwithstanding any other provision of this License, you have
|
||||
permission to link or combine any covered work with a work licensed
|
||||
under version 3 of the GNU Affero General Public License into a single
|
||||
combined work, and to convey the resulting work. The terms of this
|
||||
License will continue to apply to the part which is the covered work,
|
||||
but the special requirements of the GNU Affero General Public License,
|
||||
section 13, concerning interaction through a network will apply to the
|
||||
combination as such.
|
||||
|
||||
14. Revised Versions of this License.
|
||||
|
||||
The Free Software Foundation may publish revised and/or new versions of
|
||||
the GNU General Public License from time to time. Such new versions will
|
||||
be similar in spirit to the present version, but may differ in detail to
|
||||
address new problems or concerns.
|
||||
|
||||
Each version is given a distinguishing version number. If the
|
||||
Program specifies that a certain numbered version of the GNU General
|
||||
Public License "or any later version" applies to it, you have the
|
||||
option of following the terms and conditions either of that numbered
|
||||
version or of any later version published by the Free Software
|
||||
Foundation. If the Program does not specify a version number of the
|
||||
GNU General Public License, you may choose any version ever published
|
||||
by the Free Software Foundation.
|
||||
|
||||
If the Program specifies that a proxy can decide which future
|
||||
versions of the GNU General Public License can be used, that proxy's
|
||||
public statement of acceptance of a version permanently authorizes you
|
||||
to choose that version for the Program.
|
||||
|
||||
Later license versions may give you additional or different
|
||||
permissions. However, no additional obligations are imposed on any
|
||||
author or copyright holder as a result of your choosing to follow a
|
||||
later version.
|
||||
|
||||
15. Disclaimer of Warranty.
|
||||
|
||||
THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY
|
||||
APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT
|
||||
HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY
|
||||
OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,
|
||||
THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
||||
PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM
|
||||
IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF
|
||||
ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
|
||||
|
||||
16. Limitation of Liability.
|
||||
|
||||
IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
|
||||
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS
|
||||
THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY
|
||||
GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE
|
||||
USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF
|
||||
DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD
|
||||
PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),
|
||||
EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
|
||||
SUCH DAMAGES.
|
||||
|
||||
17. Interpretation of Sections 15 and 16.
|
||||
|
||||
If the disclaimer of warranty and limitation of liability provided
|
||||
above cannot be given local legal effect according to their terms,
|
||||
reviewing courts shall apply local law that most closely approximates
|
||||
an absolute waiver of all civil liability in connection with the
|
||||
Program, unless a warranty or assumption of liability accompanies a
|
||||
copy of the Program in return for a fee.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
How to Apply These Terms to Your New Programs
|
||||
|
||||
If you develop a new program, and you want it to be of the greatest
|
||||
possible use to the public, the best way to achieve this is to make it
|
||||
free software which everyone can redistribute and change under these terms.
|
||||
|
||||
To do so, attach the following notices to the program. It is safest
|
||||
to attach them to the start of each source file to most effectively
|
||||
state the exclusion of warranty; and each file should have at least
|
||||
the "copyright" line and a pointer to where the full notice is found.
|
||||
|
||||
<one line to give the program's name and a brief idea of what it does.>
|
||||
Copyright (C) <year> <name of author>
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
Also add information on how to contact you by electronic and paper mail.
|
||||
|
||||
If the program does terminal interaction, make it output a short
|
||||
notice like this when it starts in an interactive mode:
|
||||
|
||||
<program> Copyright (C) <year> <name of author>
|
||||
This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
|
||||
This is free software, and you are welcome to redistribute it
|
||||
under certain conditions; type `show c' for details.
|
||||
|
||||
The hypothetical commands `show w' and `show c' should show the appropriate
|
||||
parts of the General Public License. Of course, your program's commands
|
||||
might be different; for a GUI interface, you would use an "about box".
|
||||
|
||||
You should also get your employer (if you work as a programmer) or school,
|
||||
if any, to sign a "copyright disclaimer" for the program, if necessary.
|
||||
For more information on this, and how to apply and follow the GNU GPL, see
|
||||
<https://www.gnu.org/licenses/>.
|
||||
|
||||
The GNU General Public License does not permit incorporating your program
|
||||
into proprietary programs. If your program is a subroutine library, you
|
||||
may consider it more useful to permit linking proprietary applications with
|
||||
the library. If this is what you want to do, use the GNU Lesser General
|
||||
Public License instead of this License. But first, please read
|
||||
<https://www.gnu.org/licenses/why-not-lgpl.html>.
|
||||
1201
Tweaks/iSponsorBlock/MBProgressHUD.m
Normal file
1201
Tweaks/iSponsorBlock/MBProgressHUD.m
Normal file
@@ -0,0 +1,1201 @@
|
||||
//
|
||||
// MBProgressHUD.m
|
||||
// Version 1.2.0
|
||||
// Created by Matej Bukovinski on 2.4.09.
|
||||
//
|
||||
|
||||
#import "Headers/MBProgressHUD.h"
|
||||
#import <tgmath.h>
|
||||
|
||||
#define MBMainThreadAssert() NSAssert([NSThread isMainThread], @"MBProgressHUD needs to be accessed on the main thread.");
|
||||
|
||||
CGFloat const MBProgressMaxOffset = 1000000.f;
|
||||
|
||||
static const CGFloat MBDefaultPadding = 4.f;
|
||||
static const CGFloat MBDefaultLabelFontSize = 16.f;
|
||||
static const CGFloat MBDefaultDetailsLabelFontSize = 12.f;
|
||||
|
||||
|
||||
@interface MBProgressHUD ()
|
||||
|
||||
@property (nonatomic, assign) BOOL useAnimation;
|
||||
@property (nonatomic, assign, getter=hasFinished) BOOL finished;
|
||||
@property (nonatomic, strong) UIView *indicator;
|
||||
@property (nonatomic, strong) NSDate *showStarted;
|
||||
@property (nonatomic, strong) NSArray *paddingConstraints;
|
||||
@property (nonatomic, strong) NSArray *bezelConstraints;
|
||||
@property (nonatomic, strong) UIView *topSpacer;
|
||||
@property (nonatomic, strong) UIView *bottomSpacer;
|
||||
@property (nonatomic, strong) UIMotionEffectGroup *bezelMotionEffects;
|
||||
@property (nonatomic, weak) NSTimer *graceTimer;
|
||||
@property (nonatomic, weak) NSTimer *minShowTimer;
|
||||
@property (nonatomic, weak) NSTimer *hideDelayTimer;
|
||||
@property (nonatomic, weak) CADisplayLink *progressObjectDisplayLink;
|
||||
|
||||
@end
|
||||
|
||||
@interface MBProgressHUDRoundedButton : UIButton
|
||||
@end
|
||||
|
||||
|
||||
@implementation MBProgressHUD
|
||||
|
||||
#pragma mark - Class methods
|
||||
|
||||
+ (instancetype)showHUDAddedTo:(UIView *)view animated:(BOOL)animated {
|
||||
MBProgressHUD *hud = [[self alloc] initWithView:view];
|
||||
hud.removeFromSuperViewOnHide = YES;
|
||||
[view addSubview:hud];
|
||||
[hud showAnimated:animated];
|
||||
return hud;
|
||||
}
|
||||
|
||||
+ (BOOL)hideHUDForView:(UIView *)view animated:(BOOL)animated {
|
||||
MBProgressHUD *hud = [self HUDForView:view];
|
||||
if (hud != nil) {
|
||||
hud.removeFromSuperViewOnHide = YES;
|
||||
[hud hideAnimated:animated];
|
||||
return YES;
|
||||
}
|
||||
return NO;
|
||||
}
|
||||
|
||||
+ (MBProgressHUD *)HUDForView:(UIView *)view {
|
||||
NSEnumerator *subviewsEnum = [view.subviews reverseObjectEnumerator];
|
||||
for (UIView *subview in subviewsEnum) {
|
||||
if ([subview isKindOfClass:self]) {
|
||||
MBProgressHUD *hud = (MBProgressHUD *)subview;
|
||||
if (hud.hasFinished == NO) {
|
||||
return hud;
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil;
|
||||
}
|
||||
|
||||
#pragma mark - Lifecycle
|
||||
|
||||
- (void)commonInit {
|
||||
// Set default values for properties
|
||||
_animationType = MBProgressHUDAnimationFade;
|
||||
_mode = MBProgressHUDModeIndeterminate;
|
||||
_margin = 20.0f;
|
||||
_defaultMotionEffectsEnabled = NO;
|
||||
|
||||
if (@available(iOS 13.0, tvOS 13, *)) {
|
||||
_contentColor = [[UIColor labelColor] colorWithAlphaComponent:0.7f];
|
||||
} else {
|
||||
_contentColor = [UIColor colorWithWhite:0.f alpha:0.7f];
|
||||
}
|
||||
|
||||
// Transparent background
|
||||
self.opaque = NO;
|
||||
self.backgroundColor = [UIColor clearColor];
|
||||
// Make it invisible for now
|
||||
self.alpha = 0.0f;
|
||||
self.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;
|
||||
self.layer.allowsGroupOpacity = NO;
|
||||
|
||||
[self setupViews];
|
||||
[self updateIndicators];
|
||||
[self registerForNotifications];
|
||||
}
|
||||
|
||||
- (instancetype)initWithFrame:(CGRect)frame {
|
||||
if ((self = [super initWithFrame:frame])) {
|
||||
[self commonInit];
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
- (instancetype)initWithCoder:(NSCoder *)aDecoder {
|
||||
if ((self = [super initWithCoder:aDecoder])) {
|
||||
[self commonInit];
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
- (id)initWithView:(UIView *)view {
|
||||
NSAssert(view, @"View must not be nil.");
|
||||
return [self initWithFrame:view.bounds];
|
||||
}
|
||||
|
||||
- (void)dealloc {
|
||||
[self unregisterFromNotifications];
|
||||
}
|
||||
|
||||
#pragma mark - Show & hide
|
||||
|
||||
- (void)showAnimated:(BOOL)animated {
|
||||
MBMainThreadAssert();
|
||||
[self.minShowTimer invalidate];
|
||||
self.useAnimation = animated;
|
||||
self.finished = NO;
|
||||
// If the grace time is set, postpone the HUD display
|
||||
if (self.graceTime > 0.0) {
|
||||
NSTimer *timer = [NSTimer timerWithTimeInterval:self.graceTime target:self selector:@selector(handleGraceTimer:) userInfo:nil repeats:NO];
|
||||
[[NSRunLoop currentRunLoop] addTimer:timer forMode:NSRunLoopCommonModes];
|
||||
self.graceTimer = timer;
|
||||
}
|
||||
// ... otherwise show the HUD immediately
|
||||
else {
|
||||
[self showUsingAnimation:self.useAnimation];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)hideAnimated:(BOOL)animated {
|
||||
MBMainThreadAssert();
|
||||
[self.graceTimer invalidate];
|
||||
self.useAnimation = animated;
|
||||
self.finished = YES;
|
||||
// If the minShow time is set, calculate how long the HUD was shown,
|
||||
// and postpone the hiding operation if necessary
|
||||
if (self.minShowTime > 0.0 && self.showStarted) {
|
||||
NSTimeInterval interv = [[NSDate date] timeIntervalSinceDate:self.showStarted];
|
||||
if (interv < self.minShowTime) {
|
||||
NSTimer *timer = [NSTimer timerWithTimeInterval:(self.minShowTime - interv) target:self selector:@selector(handleMinShowTimer:) userInfo:nil repeats:NO];
|
||||
[[NSRunLoop currentRunLoop] addTimer:timer forMode:NSRunLoopCommonModes];
|
||||
self.minShowTimer = timer;
|
||||
return;
|
||||
}
|
||||
}
|
||||
// ... otherwise hide the HUD immediately
|
||||
[self hideUsingAnimation:self.useAnimation];
|
||||
}
|
||||
|
||||
- (void)hideAnimated:(BOOL)animated afterDelay:(NSTimeInterval)delay {
|
||||
// Cancel any scheduled hideAnimated:afterDelay: calls
|
||||
[self.hideDelayTimer invalidate];
|
||||
|
||||
NSTimer *timer = [NSTimer timerWithTimeInterval:delay target:self selector:@selector(handleHideTimer:) userInfo:@(animated) repeats:NO];
|
||||
[[NSRunLoop currentRunLoop] addTimer:timer forMode:NSRunLoopCommonModes];
|
||||
self.hideDelayTimer = timer;
|
||||
}
|
||||
|
||||
#pragma mark - Timer callbacks
|
||||
|
||||
- (void)handleGraceTimer:(NSTimer *)theTimer {
|
||||
// Show the HUD only if the task is still running
|
||||
if (!self.hasFinished) {
|
||||
[self showUsingAnimation:self.useAnimation];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)handleMinShowTimer:(NSTimer *)theTimer {
|
||||
[self hideUsingAnimation:self.useAnimation];
|
||||
}
|
||||
|
||||
- (void)handleHideTimer:(NSTimer *)timer {
|
||||
[self hideAnimated:[timer.userInfo boolValue]];
|
||||
}
|
||||
|
||||
#pragma mark - View Hierrarchy
|
||||
|
||||
- (void)didMoveToSuperview {
|
||||
[self updateForCurrentOrientationAnimated:NO];
|
||||
}
|
||||
|
||||
#pragma mark - Internal show & hide operations
|
||||
|
||||
- (void)showUsingAnimation:(BOOL)animated {
|
||||
// Cancel any previous animations
|
||||
[self.bezelView.layer removeAllAnimations];
|
||||
[self.backgroundView.layer removeAllAnimations];
|
||||
|
||||
// Cancel any scheduled hideAnimated:afterDelay: calls
|
||||
[self.hideDelayTimer invalidate];
|
||||
|
||||
self.showStarted = [NSDate date];
|
||||
self.alpha = 1.f;
|
||||
|
||||
// Needed in case we hide and re-show with the same NSProgress object attached.
|
||||
[self setNSProgressDisplayLinkEnabled:YES];
|
||||
|
||||
// Set up motion effects only at this point to avoid needlessly
|
||||
// creating the effect if it was disabled after initialization.
|
||||
[self updateBezelMotionEffects];
|
||||
|
||||
if (animated) {
|
||||
[self animateIn:YES withType:self.animationType completion:NULL];
|
||||
} else {
|
||||
self.bezelView.alpha = 1.f;
|
||||
self.backgroundView.alpha = 1.f;
|
||||
}
|
||||
}
|
||||
|
||||
- (void)hideUsingAnimation:(BOOL)animated {
|
||||
// Cancel any scheduled hideAnimated:afterDelay: calls.
|
||||
// This needs to happen here instead of in done,
|
||||
// to avoid races if another hideAnimated:afterDelay:
|
||||
// call comes in while the HUD is animating out.
|
||||
[self.hideDelayTimer invalidate];
|
||||
|
||||
if (animated && self.showStarted) {
|
||||
self.showStarted = nil;
|
||||
[self animateIn:NO withType:self.animationType completion:^(BOOL finished) {
|
||||
[self done];
|
||||
}];
|
||||
} else {
|
||||
self.showStarted = nil;
|
||||
self.bezelView.alpha = 0.f;
|
||||
self.backgroundView.alpha = 1.f;
|
||||
[self done];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)animateIn:(BOOL)animatingIn withType:(MBProgressHUDAnimation)type completion:(void(^)(BOOL finished))completion {
|
||||
// Automatically determine the correct zoom animation type
|
||||
if (type == MBProgressHUDAnimationZoom) {
|
||||
type = animatingIn ? MBProgressHUDAnimationZoomIn : MBProgressHUDAnimationZoomOut;
|
||||
}
|
||||
|
||||
CGAffineTransform small = CGAffineTransformMakeScale(0.5f, 0.5f);
|
||||
CGAffineTransform large = CGAffineTransformMakeScale(1.5f, 1.5f);
|
||||
|
||||
// Set starting state
|
||||
UIView *bezelView = self.bezelView;
|
||||
if (animatingIn && bezelView.alpha == 0.f && type == MBProgressHUDAnimationZoomIn) {
|
||||
bezelView.transform = small;
|
||||
} else if (animatingIn && bezelView.alpha == 0.f && type == MBProgressHUDAnimationZoomOut) {
|
||||
bezelView.transform = large;
|
||||
}
|
||||
|
||||
// Perform animations
|
||||
dispatch_block_t animations = ^{
|
||||
if (animatingIn) {
|
||||
bezelView.transform = CGAffineTransformIdentity;
|
||||
} else if (!animatingIn && type == MBProgressHUDAnimationZoomIn) {
|
||||
bezelView.transform = large;
|
||||
} else if (!animatingIn && type == MBProgressHUDAnimationZoomOut) {
|
||||
bezelView.transform = small;
|
||||
}
|
||||
CGFloat alpha = animatingIn ? 1.f : 0.f;
|
||||
bezelView.alpha = alpha;
|
||||
self.backgroundView.alpha = alpha;
|
||||
};
|
||||
[UIView animateWithDuration:0.3 delay:0. usingSpringWithDamping:1.f initialSpringVelocity:0.f options:UIViewAnimationOptionBeginFromCurrentState animations:animations completion:completion];
|
||||
}
|
||||
|
||||
- (void)done {
|
||||
[self setNSProgressDisplayLinkEnabled:NO];
|
||||
|
||||
if (self.hasFinished) {
|
||||
self.alpha = 0.0f;
|
||||
if (self.removeFromSuperViewOnHide) {
|
||||
[self removeFromSuperview];
|
||||
}
|
||||
}
|
||||
MBProgressHUDCompletionBlock completionBlock = self.completionBlock;
|
||||
if (completionBlock) {
|
||||
completionBlock();
|
||||
}
|
||||
id<MBProgressHUDDelegate> delegate = self.delegate;
|
||||
if ([delegate respondsToSelector:@selector(hudWasHidden:)]) {
|
||||
[delegate performSelector:@selector(hudWasHidden:) withObject:self];
|
||||
}
|
||||
}
|
||||
|
||||
#pragma mark - UI
|
||||
|
||||
- (void)setupViews {
|
||||
UIColor *defaultColor = self.contentColor;
|
||||
|
||||
MBBackgroundView *backgroundView = [[MBBackgroundView alloc] initWithFrame:self.bounds];
|
||||
backgroundView.style = MBProgressHUDBackgroundStyleSolidColor;
|
||||
backgroundView.backgroundColor = [UIColor clearColor];
|
||||
backgroundView.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;
|
||||
backgroundView.alpha = 0.f;
|
||||
[self addSubview:backgroundView];
|
||||
_backgroundView = backgroundView;
|
||||
|
||||
MBBackgroundView *bezelView = [MBBackgroundView new];
|
||||
bezelView.translatesAutoresizingMaskIntoConstraints = NO;
|
||||
bezelView.layer.cornerRadius = 5.f;
|
||||
bezelView.alpha = 0.f;
|
||||
[self addSubview:bezelView];
|
||||
_bezelView = bezelView;
|
||||
|
||||
UILabel *label = [UILabel new];
|
||||
label.adjustsFontSizeToFitWidth = NO;
|
||||
label.textAlignment = NSTextAlignmentCenter;
|
||||
label.textColor = defaultColor;
|
||||
label.font = [UIFont boldSystemFontOfSize:MBDefaultLabelFontSize];
|
||||
label.opaque = NO;
|
||||
label.backgroundColor = [UIColor clearColor];
|
||||
_label = label;
|
||||
|
||||
UILabel *detailsLabel = [UILabel new];
|
||||
detailsLabel.adjustsFontSizeToFitWidth = NO;
|
||||
detailsLabel.textAlignment = NSTextAlignmentCenter;
|
||||
detailsLabel.textColor = defaultColor;
|
||||
detailsLabel.numberOfLines = 0;
|
||||
detailsLabel.font = [UIFont boldSystemFontOfSize:MBDefaultDetailsLabelFontSize];
|
||||
detailsLabel.opaque = NO;
|
||||
detailsLabel.backgroundColor = [UIColor clearColor];
|
||||
_detailsLabel = detailsLabel;
|
||||
|
||||
UIButton *button = [MBProgressHUDRoundedButton buttonWithType:UIButtonTypeCustom];
|
||||
button.titleLabel.textAlignment = NSTextAlignmentCenter;
|
||||
button.titleLabel.font = [UIFont boldSystemFontOfSize:MBDefaultDetailsLabelFontSize];
|
||||
[button setTitleColor:defaultColor forState:UIControlStateNormal];
|
||||
_button = button;
|
||||
|
||||
for (UIView *view in @[label, detailsLabel, button]) {
|
||||
view.translatesAutoresizingMaskIntoConstraints = NO;
|
||||
[view setContentCompressionResistancePriority:998.f forAxis:UILayoutConstraintAxisHorizontal];
|
||||
[view setContentCompressionResistancePriority:998.f forAxis:UILayoutConstraintAxisVertical];
|
||||
[bezelView addSubview:view];
|
||||
}
|
||||
|
||||
UIView *topSpacer = [UIView new];
|
||||
topSpacer.translatesAutoresizingMaskIntoConstraints = NO;
|
||||
topSpacer.hidden = YES;
|
||||
[bezelView addSubview:topSpacer];
|
||||
_topSpacer = topSpacer;
|
||||
|
||||
UIView *bottomSpacer = [UIView new];
|
||||
bottomSpacer.translatesAutoresizingMaskIntoConstraints = NO;
|
||||
bottomSpacer.hidden = YES;
|
||||
[bezelView addSubview:bottomSpacer];
|
||||
_bottomSpacer = bottomSpacer;
|
||||
}
|
||||
|
||||
- (void)updateIndicators {
|
||||
UIView *indicator = self.indicator;
|
||||
BOOL isActivityIndicator = [indicator isKindOfClass:[UIActivityIndicatorView class]];
|
||||
BOOL isRoundIndicator = [indicator isKindOfClass:[MBRoundProgressView class]];
|
||||
|
||||
MBProgressHUDMode mode = self.mode;
|
||||
if (mode == MBProgressHUDModeIndeterminate) {
|
||||
if (!isActivityIndicator) {
|
||||
// Update to indeterminate indicator
|
||||
UIActivityIndicatorView *activityIndicator;
|
||||
[indicator removeFromSuperview];
|
||||
#if !TARGET_OS_MACCATALYST
|
||||
if (@available(iOS 13.0, tvOS 13.0, *)) {
|
||||
#endif
|
||||
activityIndicator = [[UIActivityIndicatorView alloc] initWithActivityIndicatorStyle:UIActivityIndicatorViewStyleLarge];
|
||||
activityIndicator.color = [UIColor whiteColor];
|
||||
#if !TARGET_OS_MACCATALYST
|
||||
} else {
|
||||
activityIndicator = [[UIActivityIndicatorView alloc] initWithActivityIndicatorStyle:UIActivityIndicatorViewStyleWhiteLarge];
|
||||
}
|
||||
#endif
|
||||
[activityIndicator startAnimating];
|
||||
indicator = activityIndicator;
|
||||
[self.bezelView addSubview:indicator];
|
||||
}
|
||||
}
|
||||
else if (mode == MBProgressHUDModeDeterminateHorizontalBar) {
|
||||
// Update to bar determinate indicator
|
||||
[indicator removeFromSuperview];
|
||||
indicator = [[MBBarProgressView alloc] init];
|
||||
[self.bezelView addSubview:indicator];
|
||||
}
|
||||
else if (mode == MBProgressHUDModeDeterminate || mode == MBProgressHUDModeAnnularDeterminate) {
|
||||
if (!isRoundIndicator) {
|
||||
// Update to determinante indicator
|
||||
[indicator removeFromSuperview];
|
||||
indicator = [[MBRoundProgressView alloc] init];
|
||||
[self.bezelView addSubview:indicator];
|
||||
}
|
||||
if (mode == MBProgressHUDModeAnnularDeterminate) {
|
||||
[(MBRoundProgressView *)indicator setAnnular:YES];
|
||||
}
|
||||
}
|
||||
else if (mode == MBProgressHUDModeCustomView && self.customView != indicator) {
|
||||
// Update custom view indicator
|
||||
[indicator removeFromSuperview];
|
||||
indicator = self.customView;
|
||||
[self.bezelView addSubview:indicator];
|
||||
}
|
||||
else if (mode == MBProgressHUDModeText) {
|
||||
[indicator removeFromSuperview];
|
||||
indicator = nil;
|
||||
}
|
||||
indicator.translatesAutoresizingMaskIntoConstraints = NO;
|
||||
self.indicator = indicator;
|
||||
|
||||
if ([indicator respondsToSelector:@selector(setProgress:)]) {
|
||||
[(id)indicator setValue:@(self.progress) forKey:@"progress"];
|
||||
}
|
||||
|
||||
[indicator setContentCompressionResistancePriority:998.f forAxis:UILayoutConstraintAxisHorizontal];
|
||||
[indicator setContentCompressionResistancePriority:998.f forAxis:UILayoutConstraintAxisVertical];
|
||||
|
||||
[self updateViewsForColor:self.contentColor];
|
||||
[self setNeedsUpdateConstraints];
|
||||
}
|
||||
|
||||
- (void)updateViewsForColor:(UIColor *)color {
|
||||
if (!color) return;
|
||||
|
||||
self.label.textColor = color;
|
||||
self.detailsLabel.textColor = color;
|
||||
[self.button setTitleColor:color forState:UIControlStateNormal];
|
||||
|
||||
// UIAppearance settings are prioritized. If they are preset the set color is ignored.
|
||||
|
||||
UIView *indicator = self.indicator;
|
||||
if ([indicator isKindOfClass:[UIActivityIndicatorView class]]) {
|
||||
UIActivityIndicatorView *appearance = nil;
|
||||
#if __IPHONE_OS_VERSION_MIN_REQUIRED < 90000
|
||||
appearance = [UIActivityIndicatorView appearanceWhenContainedIn:[MBProgressHUD class], nil];
|
||||
#else
|
||||
// For iOS 9+
|
||||
appearance = [UIActivityIndicatorView appearanceWhenContainedInInstancesOfClasses:@[[MBProgressHUD class]]];
|
||||
#endif
|
||||
|
||||
if (appearance.color == nil) {
|
||||
((UIActivityIndicatorView *)indicator).color = color;
|
||||
}
|
||||
} else if ([indicator isKindOfClass:[MBRoundProgressView class]]) {
|
||||
MBRoundProgressView *appearance = nil;
|
||||
#if __IPHONE_OS_VERSION_MIN_REQUIRED < 90000
|
||||
appearance = [MBRoundProgressView appearanceWhenContainedIn:[MBProgressHUD class], nil];
|
||||
#else
|
||||
appearance = [MBRoundProgressView appearanceWhenContainedInInstancesOfClasses:@[[MBProgressHUD class]]];
|
||||
#endif
|
||||
if (appearance.progressTintColor == nil) {
|
||||
((MBRoundProgressView *)indicator).progressTintColor = color;
|
||||
}
|
||||
if (appearance.backgroundTintColor == nil) {
|
||||
((MBRoundProgressView *)indicator).backgroundTintColor = [color colorWithAlphaComponent:0.1];
|
||||
}
|
||||
} else if ([indicator isKindOfClass:[MBBarProgressView class]]) {
|
||||
MBBarProgressView *appearance = nil;
|
||||
#if __IPHONE_OS_VERSION_MIN_REQUIRED < 90000
|
||||
appearance = [MBBarProgressView appearanceWhenContainedIn:[MBProgressHUD class], nil];
|
||||
#else
|
||||
appearance = [MBBarProgressView appearanceWhenContainedInInstancesOfClasses:@[[MBProgressHUD class]]];
|
||||
#endif
|
||||
if (appearance.progressColor == nil) {
|
||||
((MBBarProgressView *)indicator).progressColor = color;
|
||||
}
|
||||
if (appearance.lineColor == nil) {
|
||||
((MBBarProgressView *)indicator).lineColor = color;
|
||||
}
|
||||
} else {
|
||||
[indicator setTintColor:color];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)updateBezelMotionEffects {
|
||||
MBBackgroundView *bezelView = self.bezelView;
|
||||
UIMotionEffectGroup *bezelMotionEffects = self.bezelMotionEffects;
|
||||
|
||||
if (self.defaultMotionEffectsEnabled && !bezelMotionEffects) {
|
||||
CGFloat effectOffset = 10.f;
|
||||
UIInterpolatingMotionEffect *effectX = [[UIInterpolatingMotionEffect alloc] initWithKeyPath:@"center.x" type:UIInterpolatingMotionEffectTypeTiltAlongHorizontalAxis];
|
||||
effectX.maximumRelativeValue = @(effectOffset);
|
||||
effectX.minimumRelativeValue = @(-effectOffset);
|
||||
|
||||
UIInterpolatingMotionEffect *effectY = [[UIInterpolatingMotionEffect alloc] initWithKeyPath:@"center.y" type:UIInterpolatingMotionEffectTypeTiltAlongVerticalAxis];
|
||||
effectY.maximumRelativeValue = @(effectOffset);
|
||||
effectY.minimumRelativeValue = @(-effectOffset);
|
||||
|
||||
UIMotionEffectGroup *group = [[UIMotionEffectGroup alloc] init];
|
||||
group.motionEffects = @[effectX, effectY];
|
||||
|
||||
self.bezelMotionEffects = group;
|
||||
[bezelView addMotionEffect:group];
|
||||
} else if (bezelMotionEffects) {
|
||||
self.bezelMotionEffects = nil;
|
||||
[bezelView removeMotionEffect:bezelMotionEffects];
|
||||
}
|
||||
}
|
||||
|
||||
#pragma mark - Layout
|
||||
|
||||
- (void)updateConstraints {
|
||||
UIView *bezel = self.bezelView;
|
||||
UIView *topSpacer = self.topSpacer;
|
||||
UIView *bottomSpacer = self.bottomSpacer;
|
||||
CGFloat margin = self.margin;
|
||||
NSMutableArray *bezelConstraints = [NSMutableArray array];
|
||||
NSDictionary *metrics = @{@"margin": @(margin)};
|
||||
|
||||
NSMutableArray *subviews = [NSMutableArray arrayWithObjects:self.topSpacer, self.label, self.detailsLabel, self.button, self.bottomSpacer, nil];
|
||||
if (self.indicator) [subviews insertObject:self.indicator atIndex:1];
|
||||
|
||||
// Remove existing constraints
|
||||
[self removeConstraints:self.constraints];
|
||||
[topSpacer removeConstraints:topSpacer.constraints];
|
||||
[bottomSpacer removeConstraints:bottomSpacer.constraints];
|
||||
if (self.bezelConstraints) {
|
||||
[bezel removeConstraints:self.bezelConstraints];
|
||||
self.bezelConstraints = nil;
|
||||
}
|
||||
|
||||
// Center bezel in container (self), applying the offset if set
|
||||
CGPoint offset = self.offset;
|
||||
NSMutableArray *centeringConstraints = [NSMutableArray array];
|
||||
[centeringConstraints addObject:[NSLayoutConstraint constraintWithItem:bezel attribute:NSLayoutAttributeCenterX relatedBy:NSLayoutRelationEqual toItem:self attribute:NSLayoutAttributeCenterX multiplier:1.f constant:offset.x]];
|
||||
[centeringConstraints addObject:[NSLayoutConstraint constraintWithItem:bezel attribute:NSLayoutAttributeCenterY relatedBy:NSLayoutRelationEqual toItem:self attribute:NSLayoutAttributeCenterY multiplier:1.f constant:offset.y]];
|
||||
[self applyPriority:998.f toConstraints:centeringConstraints];
|
||||
[self addConstraints:centeringConstraints];
|
||||
|
||||
// Ensure minimum side margin is kept
|
||||
NSMutableArray *sideConstraints = [NSMutableArray array];
|
||||
[sideConstraints addObjectsFromArray:[NSLayoutConstraint constraintsWithVisualFormat:@"|-(>=margin)-[bezel]-(>=margin)-|" options:0 metrics:metrics views:NSDictionaryOfVariableBindings(bezel)]];
|
||||
[sideConstraints addObjectsFromArray:[NSLayoutConstraint constraintsWithVisualFormat:@"V:|-(>=margin)-[bezel]-(>=margin)-|" options:0 metrics:metrics views:NSDictionaryOfVariableBindings(bezel)]];
|
||||
[self applyPriority:999.f toConstraints:sideConstraints];
|
||||
[self addConstraints:sideConstraints];
|
||||
|
||||
// Minimum bezel size, if set
|
||||
CGSize minimumSize = self.minSize;
|
||||
if (!CGSizeEqualToSize(minimumSize, CGSizeZero)) {
|
||||
NSMutableArray *minSizeConstraints = [NSMutableArray array];
|
||||
[minSizeConstraints addObject:[NSLayoutConstraint constraintWithItem:bezel attribute:NSLayoutAttributeWidth relatedBy:NSLayoutRelationGreaterThanOrEqual toItem:nil attribute:NSLayoutAttributeNotAnAttribute multiplier:1.f constant:minimumSize.width]];
|
||||
[minSizeConstraints addObject:[NSLayoutConstraint constraintWithItem:bezel attribute:NSLayoutAttributeHeight relatedBy:NSLayoutRelationGreaterThanOrEqual toItem:nil attribute:NSLayoutAttributeNotAnAttribute multiplier:1.f constant:minimumSize.height]];
|
||||
[self applyPriority:997.f toConstraints:minSizeConstraints];
|
||||
[bezelConstraints addObjectsFromArray:minSizeConstraints];
|
||||
}
|
||||
|
||||
// Square aspect ratio, if set
|
||||
if (self.square) {
|
||||
NSLayoutConstraint *square = [NSLayoutConstraint constraintWithItem:bezel attribute:NSLayoutAttributeHeight relatedBy:NSLayoutRelationEqual toItem:bezel attribute:NSLayoutAttributeWidth multiplier:1.f constant:0];
|
||||
square.priority = 997.f;
|
||||
[bezelConstraints addObject:square];
|
||||
}
|
||||
|
||||
// Top and bottom spacing
|
||||
[topSpacer addConstraint:[NSLayoutConstraint constraintWithItem:topSpacer attribute:NSLayoutAttributeHeight relatedBy:NSLayoutRelationGreaterThanOrEqual toItem:nil attribute:NSLayoutAttributeNotAnAttribute multiplier:1.f constant:margin]];
|
||||
[bottomSpacer addConstraint:[NSLayoutConstraint constraintWithItem:bottomSpacer attribute:NSLayoutAttributeHeight relatedBy:NSLayoutRelationGreaterThanOrEqual toItem:nil attribute:NSLayoutAttributeNotAnAttribute multiplier:1.f constant:margin]];
|
||||
// Top and bottom spaces should be equal
|
||||
[bezelConstraints addObject:[NSLayoutConstraint constraintWithItem:topSpacer attribute:NSLayoutAttributeHeight relatedBy:NSLayoutRelationEqual toItem:bottomSpacer attribute:NSLayoutAttributeHeight multiplier:1.f constant:0.f]];
|
||||
|
||||
// Layout subviews in bezel
|
||||
NSMutableArray *paddingConstraints = [NSMutableArray new];
|
||||
[subviews enumerateObjectsUsingBlock:^(UIView *view, NSUInteger idx, BOOL *stop) {
|
||||
// Center in bezel
|
||||
[bezelConstraints addObject:[NSLayoutConstraint constraintWithItem:view attribute:NSLayoutAttributeCenterX relatedBy:NSLayoutRelationEqual toItem:bezel attribute:NSLayoutAttributeCenterX multiplier:1.f constant:0.f]];
|
||||
// Ensure the minimum edge margin is kept
|
||||
[bezelConstraints addObjectsFromArray:[NSLayoutConstraint constraintsWithVisualFormat:@"|-(>=margin)-[view]-(>=margin)-|" options:0 metrics:metrics views:NSDictionaryOfVariableBindings(view)]];
|
||||
// Element spacing
|
||||
if (idx == 0) {
|
||||
// First, ensure spacing to bezel edge
|
||||
[bezelConstraints addObject:[NSLayoutConstraint constraintWithItem:view attribute:NSLayoutAttributeTop relatedBy:NSLayoutRelationEqual toItem:bezel attribute:NSLayoutAttributeTop multiplier:1.f constant:0.f]];
|
||||
} else if (idx == subviews.count - 1) {
|
||||
// Last, ensure spacing to bezel edge
|
||||
[bezelConstraints addObject:[NSLayoutConstraint constraintWithItem:view attribute:NSLayoutAttributeBottom relatedBy:NSLayoutRelationEqual toItem:bezel attribute:NSLayoutAttributeBottom multiplier:1.f constant:0.f]];
|
||||
}
|
||||
if (idx > 0) {
|
||||
// Has previous
|
||||
NSLayoutConstraint *padding = [NSLayoutConstraint constraintWithItem:view attribute:NSLayoutAttributeTop relatedBy:NSLayoutRelationEqual toItem:subviews[idx - 1] attribute:NSLayoutAttributeBottom multiplier:1.f constant:0.f];
|
||||
[bezelConstraints addObject:padding];
|
||||
[paddingConstraints addObject:padding];
|
||||
}
|
||||
}];
|
||||
|
||||
[bezel addConstraints:bezelConstraints];
|
||||
self.bezelConstraints = bezelConstraints;
|
||||
|
||||
self.paddingConstraints = [paddingConstraints copy];
|
||||
[self updatePaddingConstraints];
|
||||
|
||||
[super updateConstraints];
|
||||
}
|
||||
|
||||
- (void)layoutSubviews {
|
||||
// There is no need to update constraints if they are going to
|
||||
// be recreated in [super layoutSubviews] due to needsUpdateConstraints being set.
|
||||
// This also avoids an issue on iOS 8, where updatePaddingConstraints
|
||||
// would trigger a zombie object access.
|
||||
if (!self.needsUpdateConstraints) {
|
||||
[self updatePaddingConstraints];
|
||||
}
|
||||
[super layoutSubviews];
|
||||
}
|
||||
|
||||
- (void)updatePaddingConstraints {
|
||||
// Set padding dynamically, depending on whether the view is visible or not
|
||||
__block BOOL hasVisibleAncestors = NO;
|
||||
[self.paddingConstraints enumerateObjectsUsingBlock:^(NSLayoutConstraint *padding, NSUInteger idx, BOOL *stop) {
|
||||
UIView *firstView = (UIView *)padding.firstItem;
|
||||
UIView *secondView = (UIView *)padding.secondItem;
|
||||
BOOL firstVisible = !firstView.hidden && !CGSizeEqualToSize(firstView.intrinsicContentSize, CGSizeZero);
|
||||
BOOL secondVisible = !secondView.hidden && !CGSizeEqualToSize(secondView.intrinsicContentSize, CGSizeZero);
|
||||
// Set if both views are visible or if there's a visible view on top that doesn't have padding
|
||||
// added relative to the current view yet
|
||||
padding.constant = (firstVisible && (secondVisible || hasVisibleAncestors)) ? MBDefaultPadding : 0.f;
|
||||
hasVisibleAncestors |= secondVisible;
|
||||
}];
|
||||
}
|
||||
|
||||
- (void)applyPriority:(UILayoutPriority)priority toConstraints:(NSArray *)constraints {
|
||||
for (NSLayoutConstraint *constraint in constraints) {
|
||||
constraint.priority = priority;
|
||||
}
|
||||
}
|
||||
|
||||
#pragma mark - Properties
|
||||
|
||||
- (void)setMode:(MBProgressHUDMode)mode {
|
||||
if (mode != _mode) {
|
||||
_mode = mode;
|
||||
[self updateIndicators];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)setCustomView:(UIView *)customView {
|
||||
if (customView != _customView) {
|
||||
_customView = customView;
|
||||
if (self.mode == MBProgressHUDModeCustomView) {
|
||||
[self updateIndicators];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
- (void)setOffset:(CGPoint)offset {
|
||||
if (!CGPointEqualToPoint(offset, _offset)) {
|
||||
_offset = offset;
|
||||
[self setNeedsUpdateConstraints];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)setMargin:(CGFloat)margin {
|
||||
if (margin != _margin) {
|
||||
_margin = margin;
|
||||
[self setNeedsUpdateConstraints];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)setMinSize:(CGSize)minSize {
|
||||
if (!CGSizeEqualToSize(minSize, _minSize)) {
|
||||
_minSize = minSize;
|
||||
[self setNeedsUpdateConstraints];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)setSquare:(BOOL)square {
|
||||
if (square != _square) {
|
||||
_square = square;
|
||||
[self setNeedsUpdateConstraints];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)setProgressObjectDisplayLink:(CADisplayLink *)progressObjectDisplayLink {
|
||||
if (progressObjectDisplayLink != _progressObjectDisplayLink) {
|
||||
[_progressObjectDisplayLink invalidate];
|
||||
|
||||
_progressObjectDisplayLink = progressObjectDisplayLink;
|
||||
|
||||
[_progressObjectDisplayLink addToRunLoop:[NSRunLoop mainRunLoop] forMode:NSDefaultRunLoopMode];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)setProgressObject:(NSProgress *)progressObject {
|
||||
if (progressObject != _progressObject) {
|
||||
_progressObject = progressObject;
|
||||
[self setNSProgressDisplayLinkEnabled:YES];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)setProgress:(float)progress {
|
||||
if (progress != _progress) {
|
||||
_progress = progress;
|
||||
UIView *indicator = self.indicator;
|
||||
if ([indicator respondsToSelector:@selector(setProgress:)]) {
|
||||
[(id)indicator setValue:@(self.progress) forKey:@"progress"];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
- (void)setContentColor:(UIColor *)contentColor {
|
||||
if (contentColor != _contentColor && ![contentColor isEqual:_contentColor]) {
|
||||
_contentColor = contentColor;
|
||||
[self updateViewsForColor:contentColor];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)setDefaultMotionEffectsEnabled:(BOOL)defaultMotionEffectsEnabled {
|
||||
if (defaultMotionEffectsEnabled != _defaultMotionEffectsEnabled) {
|
||||
_defaultMotionEffectsEnabled = defaultMotionEffectsEnabled;
|
||||
[self updateBezelMotionEffects];
|
||||
}
|
||||
}
|
||||
|
||||
#pragma mark - NSProgress
|
||||
|
||||
- (void)setNSProgressDisplayLinkEnabled:(BOOL)enabled {
|
||||
// We're using CADisplayLink, because NSProgress can change very quickly and observing it may starve the main thread,
|
||||
// so we're refreshing the progress only every frame draw
|
||||
if (enabled && self.progressObject) {
|
||||
// Only create if not already active.
|
||||
if (!self.progressObjectDisplayLink) {
|
||||
self.progressObjectDisplayLink = [CADisplayLink displayLinkWithTarget:self selector:@selector(updateProgressFromProgressObject)];
|
||||
}
|
||||
} else {
|
||||
self.progressObjectDisplayLink = nil;
|
||||
}
|
||||
}
|
||||
|
||||
- (void)updateProgressFromProgressObject {
|
||||
self.progress = self.progressObject.fractionCompleted;
|
||||
}
|
||||
|
||||
#pragma mark - Notifications
|
||||
|
||||
- (void)registerForNotifications {
|
||||
#if !TARGET_OS_TV && !TARGET_OS_MACCATALYST
|
||||
NSNotificationCenter *nc = [NSNotificationCenter defaultCenter];
|
||||
|
||||
[nc addObserver:self selector:@selector(statusBarOrientationDidChange:)
|
||||
name:UIApplicationDidChangeStatusBarOrientationNotification object:nil];
|
||||
#endif
|
||||
}
|
||||
|
||||
- (void)unregisterFromNotifications {
|
||||
#if !TARGET_OS_TV && !TARGET_OS_MACCATALYST
|
||||
NSNotificationCenter *nc = [NSNotificationCenter defaultCenter];
|
||||
[nc removeObserver:self name:UIApplicationDidChangeStatusBarOrientationNotification object:nil];
|
||||
#endif
|
||||
}
|
||||
|
||||
#if !TARGET_OS_TV && !TARGET_OS_MACCATALYST
|
||||
- (void)statusBarOrientationDidChange:(NSNotification *)notification {
|
||||
UIView *superview = self.superview;
|
||||
if (!superview) {
|
||||
return;
|
||||
} else {
|
||||
[self updateForCurrentOrientationAnimated:YES];
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
- (void)updateForCurrentOrientationAnimated:(BOOL)animated {
|
||||
// Stay in sync with the superview in any case
|
||||
if (self.superview) {
|
||||
self.frame = self.superview.bounds;
|
||||
}
|
||||
|
||||
// Not needed on iOS 8+, compile out when the deployment target allows,
|
||||
// to avoid sharedApplication problems on extension targets
|
||||
#if __IPHONE_OS_VERSION_MIN_REQUIRED < 80000
|
||||
// Only needed pre iOS 8 when added to a window
|
||||
BOOL iOS8OrLater = kCFCoreFoundationVersionNumber >= kCFCoreFoundationVersionNumber_iOS_8_0;
|
||||
if (iOS8OrLater || ![self.superview isKindOfClass:[UIWindow class]]) return;
|
||||
|
||||
// Make extension friendly. Will not get called on extensions (iOS 8+) due to the above check.
|
||||
// This just ensures we don't get a warning about extension-unsafe API.
|
||||
Class UIApplicationClass = NSClassFromString(@"UIApplication");
|
||||
if (!UIApplicationClass || ![UIApplicationClass respondsToSelector:@selector(sharedApplication)]) return;
|
||||
|
||||
UIApplication *application = [UIApplication performSelector:@selector(sharedApplication)];
|
||||
UIInterfaceOrientation orientation = application.statusBarOrientation;
|
||||
CGFloat radians = 0;
|
||||
|
||||
if (UIInterfaceOrientationIsLandscape(orientation)) {
|
||||
radians = orientation == UIInterfaceOrientationLandscapeLeft ? -(CGFloat)M_PI_2 : (CGFloat)M_PI_2;
|
||||
// Window coordinates differ!
|
||||
self.bounds = CGRectMake(0, 0, self.bounds.size.height, self.bounds.size.width);
|
||||
} else {
|
||||
radians = orientation == UIInterfaceOrientationPortraitUpsideDown ? (CGFloat)M_PI : 0.f;
|
||||
}
|
||||
|
||||
if (animated) {
|
||||
[UIView animateWithDuration:0.3 animations:^{
|
||||
self.transform = CGAffineTransformMakeRotation(radians);
|
||||
}];
|
||||
} else {
|
||||
self.transform = CGAffineTransformMakeRotation(radians);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
- (id)hitTest:(CGPoint)point withEvent:(UIEvent *)event {
|
||||
id hitView = [super hitTest:point withEvent:event];
|
||||
if (hitView != self && ![hitView isKindOfClass:[MBBackgroundView class]]) {
|
||||
return hitView;
|
||||
}
|
||||
return nil;
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
|
||||
@implementation MBRoundProgressView
|
||||
|
||||
#pragma mark - Lifecycle
|
||||
|
||||
- (id)init {
|
||||
return [self initWithFrame:CGRectMake(0.f, 0.f, 37.f, 37.f)];
|
||||
}
|
||||
|
||||
- (id)initWithFrame:(CGRect)frame {
|
||||
self = [super initWithFrame:frame];
|
||||
if (self) {
|
||||
self.backgroundColor = [UIColor clearColor];
|
||||
self.opaque = NO;
|
||||
_progress = 0.f;
|
||||
_annular = NO;
|
||||
_progressTintColor = [[UIColor alloc] initWithWhite:1.f alpha:1.f];
|
||||
_backgroundTintColor = [[UIColor alloc] initWithWhite:1.f alpha:.1f];
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
#pragma mark - Layout
|
||||
|
||||
- (CGSize)intrinsicContentSize {
|
||||
return CGSizeMake(37.f, 37.f);
|
||||
}
|
||||
|
||||
#pragma mark - Properties
|
||||
|
||||
- (void)setProgress:(float)progress {
|
||||
if (progress != _progress) {
|
||||
_progress = progress;
|
||||
[self setNeedsDisplay];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)setProgressTintColor:(UIColor *)progressTintColor {
|
||||
NSAssert(progressTintColor, @"The color should not be nil.");
|
||||
if (progressTintColor != _progressTintColor && ![progressTintColor isEqual:_progressTintColor]) {
|
||||
_progressTintColor = progressTintColor;
|
||||
[self setNeedsDisplay];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)setBackgroundTintColor:(UIColor *)backgroundTintColor {
|
||||
NSAssert(backgroundTintColor, @"The color should not be nil.");
|
||||
if (backgroundTintColor != _backgroundTintColor && ![backgroundTintColor isEqual:_backgroundTintColor]) {
|
||||
_backgroundTintColor = backgroundTintColor;
|
||||
[self setNeedsDisplay];
|
||||
}
|
||||
}
|
||||
|
||||
#pragma mark - Drawing
|
||||
|
||||
- (void)drawRect:(CGRect)rect {
|
||||
CGContextRef context = UIGraphicsGetCurrentContext();
|
||||
|
||||
if (_annular) {
|
||||
// Draw background
|
||||
CGFloat lineWidth = 2.f;
|
||||
UIBezierPath *processBackgroundPath = [UIBezierPath bezierPath];
|
||||
processBackgroundPath.lineWidth = lineWidth;
|
||||
processBackgroundPath.lineCapStyle = kCGLineCapButt;
|
||||
CGPoint center = CGPointMake(CGRectGetMidX(self.bounds), CGRectGetMidY(self.bounds));
|
||||
CGFloat radius = (self.bounds.size.width - lineWidth)/2;
|
||||
CGFloat startAngle = - ((float)M_PI / 2); // 90 degrees
|
||||
CGFloat endAngle = (2 * (float)M_PI) + startAngle;
|
||||
[processBackgroundPath addArcWithCenter:center radius:radius startAngle:startAngle endAngle:endAngle clockwise:YES];
|
||||
[_backgroundTintColor set];
|
||||
[processBackgroundPath stroke];
|
||||
// Draw progress
|
||||
UIBezierPath *processPath = [UIBezierPath bezierPath];
|
||||
processPath.lineCapStyle = kCGLineCapSquare;
|
||||
processPath.lineWidth = lineWidth;
|
||||
endAngle = (self.progress * 2 * (float)M_PI) + startAngle;
|
||||
[processPath addArcWithCenter:center radius:radius startAngle:startAngle endAngle:endAngle clockwise:YES];
|
||||
[_progressTintColor set];
|
||||
[processPath stroke];
|
||||
} else {
|
||||
// Draw background
|
||||
CGFloat lineWidth = 2.f;
|
||||
CGRect allRect = self.bounds;
|
||||
CGRect circleRect = CGRectInset(allRect, lineWidth/2.f, lineWidth/2.f);
|
||||
CGPoint center = CGPointMake(CGRectGetMidX(self.bounds), CGRectGetMidY(self.bounds));
|
||||
[_progressTintColor setStroke];
|
||||
[_backgroundTintColor setFill];
|
||||
CGContextSetLineWidth(context, lineWidth);
|
||||
CGContextStrokeEllipseInRect(context, circleRect);
|
||||
// 90 degrees
|
||||
CGFloat startAngle = - ((float)M_PI / 2.f);
|
||||
// Draw progress
|
||||
UIBezierPath *processPath = [UIBezierPath bezierPath];
|
||||
processPath.lineCapStyle = kCGLineCapButt;
|
||||
processPath.lineWidth = lineWidth * 2.f;
|
||||
CGFloat radius = (CGRectGetWidth(self.bounds) / 2.f) - (processPath.lineWidth / 2.f);
|
||||
CGFloat endAngle = (self.progress * 2.f * (float)M_PI) + startAngle;
|
||||
[processPath addArcWithCenter:center radius:radius startAngle:startAngle endAngle:endAngle clockwise:YES];
|
||||
// Ensure that we don't get color overlapping when _progressTintColor alpha < 1.f.
|
||||
CGContextSetBlendMode(context, kCGBlendModeCopy);
|
||||
[_progressTintColor set];
|
||||
[processPath stroke];
|
||||
}
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
|
||||
@implementation MBBarProgressView
|
||||
|
||||
#pragma mark - Lifecycle
|
||||
|
||||
- (id)init {
|
||||
return [self initWithFrame:CGRectMake(.0f, .0f, 120.0f, 20.0f)];
|
||||
}
|
||||
|
||||
- (id)initWithFrame:(CGRect)frame {
|
||||
self = [super initWithFrame:frame];
|
||||
if (self) {
|
||||
_progress = 0.f;
|
||||
_lineColor = [UIColor whiteColor];
|
||||
_progressColor = [UIColor whiteColor];
|
||||
_progressRemainingColor = [UIColor clearColor];
|
||||
self.backgroundColor = [UIColor clearColor];
|
||||
self.opaque = NO;
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
#pragma mark - Layout
|
||||
|
||||
- (CGSize)intrinsicContentSize {
|
||||
return CGSizeMake(120.f, 10.f);
|
||||
}
|
||||
|
||||
#pragma mark - Properties
|
||||
|
||||
- (void)setProgress:(float)progress {
|
||||
if (progress != _progress) {
|
||||
_progress = progress;
|
||||
[self setNeedsDisplay];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)setProgressColor:(UIColor *)progressColor {
|
||||
NSAssert(progressColor, @"The color should not be nil.");
|
||||
if (progressColor != _progressColor && ![progressColor isEqual:_progressColor]) {
|
||||
_progressColor = progressColor;
|
||||
[self setNeedsDisplay];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)setProgressRemainingColor:(UIColor *)progressRemainingColor {
|
||||
NSAssert(progressRemainingColor, @"The color should not be nil.");
|
||||
if (progressRemainingColor != _progressRemainingColor && ![progressRemainingColor isEqual:_progressRemainingColor]) {
|
||||
_progressRemainingColor = progressRemainingColor;
|
||||
[self setNeedsDisplay];
|
||||
}
|
||||
}
|
||||
|
||||
#pragma mark - Drawing
|
||||
|
||||
- (void)drawRect:(CGRect)rect {
|
||||
CGContextRef context = UIGraphicsGetCurrentContext();
|
||||
|
||||
CGContextSetLineWidth(context, 2);
|
||||
CGContextSetStrokeColorWithColor(context,[_lineColor CGColor]);
|
||||
CGContextSetFillColorWithColor(context, [_progressRemainingColor CGColor]);
|
||||
|
||||
// Draw background and Border
|
||||
CGFloat radius = (rect.size.height / 2) - 2;
|
||||
CGContextMoveToPoint(context, 2, rect.size.height/2);
|
||||
CGContextAddArcToPoint(context, 2, 2, radius + 2, 2, radius);
|
||||
CGContextAddArcToPoint(context, rect.size.width - 2, 2, rect.size.width - 2, rect.size.height / 2, radius);
|
||||
CGContextAddArcToPoint(context, rect.size.width - 2, rect.size.height - 2, rect.size.width - radius - 2, rect.size.height - 2, radius);
|
||||
CGContextAddArcToPoint(context, 2, rect.size.height - 2, 2, rect.size.height/2, radius);
|
||||
CGContextDrawPath(context, kCGPathFillStroke);
|
||||
|
||||
CGContextSetFillColorWithColor(context, [_progressColor CGColor]);
|
||||
radius = radius - 2;
|
||||
CGFloat amount = self.progress * rect.size.width;
|
||||
|
||||
// Progress in the middle area
|
||||
if (amount >= radius + 4 && amount <= (rect.size.width - radius - 4)) {
|
||||
CGContextMoveToPoint(context, 4, rect.size.height/2);
|
||||
CGContextAddArcToPoint(context, 4, 4, radius + 4, 4, radius);
|
||||
CGContextAddLineToPoint(context, amount, 4);
|
||||
CGContextAddLineToPoint(context, amount, radius + 4);
|
||||
|
||||
CGContextMoveToPoint(context, 4, rect.size.height/2);
|
||||
CGContextAddArcToPoint(context, 4, rect.size.height - 4, radius + 4, rect.size.height - 4, radius);
|
||||
CGContextAddLineToPoint(context, amount, rect.size.height - 4);
|
||||
CGContextAddLineToPoint(context, amount, radius + 4);
|
||||
|
||||
CGContextFillPath(context);
|
||||
}
|
||||
|
||||
// Progress in the right arc
|
||||
else if (amount > radius + 4) {
|
||||
CGFloat x = amount - (rect.size.width - radius - 4);
|
||||
|
||||
CGContextMoveToPoint(context, 4, rect.size.height/2);
|
||||
CGContextAddArcToPoint(context, 4, 4, radius + 4, 4, radius);
|
||||
CGContextAddLineToPoint(context, rect.size.width - radius - 4, 4);
|
||||
CGFloat angle = -acos(x/radius);
|
||||
if (isnan(angle)) angle = 0;
|
||||
CGContextAddArc(context, rect.size.width - radius - 4, rect.size.height/2, radius, M_PI, angle, 0);
|
||||
CGContextAddLineToPoint(context, amount, rect.size.height/2);
|
||||
|
||||
CGContextMoveToPoint(context, 4, rect.size.height/2);
|
||||
CGContextAddArcToPoint(context, 4, rect.size.height - 4, radius + 4, rect.size.height - 4, radius);
|
||||
CGContextAddLineToPoint(context, rect.size.width - radius - 4, rect.size.height - 4);
|
||||
angle = acos(x/radius);
|
||||
if (isnan(angle)) angle = 0;
|
||||
CGContextAddArc(context, rect.size.width - radius - 4, rect.size.height/2, radius, -M_PI, angle, 1);
|
||||
CGContextAddLineToPoint(context, amount, rect.size.height/2);
|
||||
|
||||
CGContextFillPath(context);
|
||||
}
|
||||
|
||||
// Progress is in the left arc
|
||||
else if (amount < radius + 4 && amount > 0) {
|
||||
CGContextMoveToPoint(context, 4, rect.size.height/2);
|
||||
CGContextAddArcToPoint(context, 4, 4, radius + 4, 4, radius);
|
||||
CGContextAddLineToPoint(context, radius + 4, rect.size.height/2);
|
||||
|
||||
CGContextMoveToPoint(context, 4, rect.size.height/2);
|
||||
CGContextAddArcToPoint(context, 4, rect.size.height - 4, radius + 4, rect.size.height - 4, radius);
|
||||
CGContextAddLineToPoint(context, radius + 4, rect.size.height/2);
|
||||
|
||||
CGContextFillPath(context);
|
||||
}
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
|
||||
@interface MBBackgroundView ()
|
||||
|
||||
@property UIVisualEffectView *effectView;
|
||||
|
||||
@end
|
||||
|
||||
|
||||
@implementation MBBackgroundView
|
||||
|
||||
#pragma mark - Lifecycle
|
||||
|
||||
- (instancetype)initWithFrame:(CGRect)frame {
|
||||
if ((self = [super initWithFrame:frame])) {
|
||||
_style = MBProgressHUDBackgroundStyleBlur;
|
||||
if (@available(iOS 13.0, *)) {
|
||||
#if TARGET_OS_TV
|
||||
_blurEffectStyle = UIBlurEffectStyleRegular;
|
||||
#else
|
||||
_blurEffectStyle = UIBlurEffectStyleSystemThickMaterial;
|
||||
#endif
|
||||
// Leaving the color unassigned yields best results.
|
||||
} else {
|
||||
_blurEffectStyle = UIBlurEffectStyleLight;
|
||||
_color = [UIColor colorWithWhite:0.8f alpha:0.6f];
|
||||
}
|
||||
|
||||
self.clipsToBounds = YES;
|
||||
|
||||
[self updateForBackgroundStyle];
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
#pragma mark - Layout
|
||||
|
||||
- (CGSize)intrinsicContentSize {
|
||||
// Smallest size possible. Content pushes against this.
|
||||
return CGSizeZero;
|
||||
}
|
||||
|
||||
#pragma mark - Appearance
|
||||
|
||||
- (void)setStyle:(MBProgressHUDBackgroundStyle)style {
|
||||
if (_style != style) {
|
||||
_style = style;
|
||||
[self updateForBackgroundStyle];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)setColor:(UIColor *)color {
|
||||
NSAssert(color, @"The color should not be nil.");
|
||||
if (color != _color && ![color isEqual:_color]) {
|
||||
_color = color;
|
||||
[self updateViewsForColor:color];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)setBlurEffectStyle:(UIBlurEffectStyle)blurEffectStyle {
|
||||
if (_blurEffectStyle == blurEffectStyle) {
|
||||
return;
|
||||
}
|
||||
|
||||
_blurEffectStyle = blurEffectStyle;
|
||||
|
||||
[self updateForBackgroundStyle];
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
#pragma mark - Views
|
||||
|
||||
- (void)updateForBackgroundStyle {
|
||||
[self.effectView removeFromSuperview];
|
||||
self.effectView = nil;
|
||||
|
||||
MBProgressHUDBackgroundStyle style = self.style;
|
||||
if (style == MBProgressHUDBackgroundStyleBlur) {
|
||||
UIBlurEffect *effect = [UIBlurEffect effectWithStyle:self.blurEffectStyle];
|
||||
UIVisualEffectView *effectView = [[UIVisualEffectView alloc] initWithEffect:effect];
|
||||
[self insertSubview:effectView atIndex:0];
|
||||
effectView.frame = self.bounds;
|
||||
effectView.autoresizingMask = UIViewAutoresizingFlexibleHeight | UIViewAutoresizingFlexibleWidth;
|
||||
self.backgroundColor = self.color;
|
||||
self.layer.allowsGroupOpacity = NO;
|
||||
self.effectView = effectView;
|
||||
} else {
|
||||
self.backgroundColor = self.color;
|
||||
}
|
||||
}
|
||||
|
||||
- (void)updateViewsForColor:(UIColor *)color {
|
||||
if (self.style == MBProgressHUDBackgroundStyleBlur) {
|
||||
self.backgroundColor = self.color;
|
||||
} else {
|
||||
self.backgroundColor = self.color;
|
||||
}
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
|
||||
@implementation MBProgressHUDRoundedButton
|
||||
|
||||
#pragma mark - Lifecycle
|
||||
|
||||
- (instancetype)initWithFrame:(CGRect)frame {
|
||||
self = [super initWithFrame:frame];
|
||||
if (self) {
|
||||
CALayer *layer = self.layer;
|
||||
layer.borderWidth = 1.f;
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
#pragma mark - Layout
|
||||
|
||||
- (void)layoutSubviews {
|
||||
[super layoutSubviews];
|
||||
// Fully rounded corners
|
||||
CGFloat height = CGRectGetHeight(self.bounds);
|
||||
self.layer.cornerRadius = ceil(height / 2.f);
|
||||
}
|
||||
|
||||
- (CGSize)intrinsicContentSize {
|
||||
// Only show if we have associated control events and a title
|
||||
if ((self.allControlEvents == 0) || ([self titleForState:UIControlStateNormal].length == 0))
|
||||
return CGSizeZero;
|
||||
CGSize size = [super intrinsicContentSize];
|
||||
// Add some side padding
|
||||
size.width += 20.f;
|
||||
return size;
|
||||
}
|
||||
|
||||
#pragma mark - Color
|
||||
|
||||
- (void)setTitleColor:(UIColor *)color forState:(UIControlState)state {
|
||||
[super setTitleColor:color forState:state];
|
||||
// Update related colors
|
||||
[self setHighlighted:self.highlighted];
|
||||
self.layer.borderColor = color.CGColor;
|
||||
}
|
||||
|
||||
- (void)setHighlighted:(BOOL)highlighted {
|
||||
[super setHighlighted:highlighted];
|
||||
UIColor *baseColor = [self titleColorForState:UIControlStateSelected];
|
||||
self.backgroundColor = highlighted ? [baseColor colorWithAlphaComponent:0.1f] : [UIColor clearColor];
|
||||
}
|
||||
|
||||
@end
|
||||
17
Tweaks/iSponsorBlock/Makefile
Normal file
17
Tweaks/iSponsorBlock/Makefile
Normal file
@@ -0,0 +1,17 @@
|
||||
ifeq ($(ROOTLESS),1)
|
||||
THEOS_PACKAGE_SCHEME=rootless
|
||||
endif
|
||||
|
||||
export ARCHS = arm64
|
||||
TARGET := iphone:clang:latest:13.0
|
||||
INSTALL_TARGET_PROCESSES = YouTube
|
||||
|
||||
include $(THEOS)/makefiles/common.mk
|
||||
|
||||
TWEAK_NAME = iSponsorBlock
|
||||
|
||||
iSponsorBlock_FILES = iSponsorBlock.xm $(wildcard *.m)
|
||||
iSponsorBlock_LIBRARIES = colorpicker
|
||||
iSponsorBlock_CFLAGS = -fobjc-arc -Wno-deprecated-declarations
|
||||
|
||||
include $(THEOS_MAKE_PATH)/tweak.mk
|
||||
13
Tweaks/iSponsorBlock/README.md
Normal file
13
Tweaks/iSponsorBlock/README.md
Normal file
@@ -0,0 +1,13 @@
|
||||
# iSponsorBlock
|
||||
A jailbreak tweak that implements the SponsorBlock API to skip sponsorships in youtube vidoes.
|
||||
|
||||
More info about SponsorBlock and the API used can be found [here](https://sponsor.ajay.app).
|
||||
|
||||
This tweak has been tested on the latest YouTube version (18.20.3) and supports, at least, down to version 17.30.1.
|
||||
|
||||
# Installation
|
||||
|
||||
Add the following repository: https://repo.icrazeios.com
|
||||
|
||||
# Customization
|
||||
To access this tweaks settings open the YouTube app, and there should be a button in the top right, like in this image: https://imgur.com/lLpfXue.
|
||||
156
Tweaks/iSponsorBlock/SponsorBlockRequest.m
Normal file
156
Tweaks/iSponsorBlock/SponsorBlockRequest.m
Normal file
@@ -0,0 +1,156 @@
|
||||
#import "Headers/SponsorBlockRequest.h"
|
||||
#import "Headers/Localization.h"
|
||||
|
||||
@implementation SponsorBlockRequest
|
||||
+ (void)getSponsorTimes:(NSString *)videoID completionTarget:(id)target completionSelector:(SEL)sel apiInstance:(NSString *)apiInstance {
|
||||
__block NSMutableArray *skipSegments = [NSMutableArray array];
|
||||
NSMutableURLRequest *request = [[NSMutableURLRequest alloc] init];
|
||||
NSString *categories = @"[%22sponsor%22,%20%22intro%22,%20%22outro%22,%20%22interaction%22,%20%22selfpromo%22,%20%22music_offtopic%22]";
|
||||
//NSString *categories = @"[%22sponsor%22,%20%22intro%22,%20%22outro%22,%20%22interaction%22,%20%22selfpromo%22,%20%22music_offtopic%22,%20%22preview%22,%20%22filler%22]";
|
||||
|
||||
[request setURL:[NSURL URLWithString:[NSString stringWithFormat:@"%@/skipSegments?videoID=%@&categories=%@", apiInstance, videoID, categories]]];
|
||||
request.HTTPMethod = @"GET";
|
||||
NSURLSessionDataTask *dataTask = [[NSURLSession sharedSession] dataTaskWithRequest:request completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) {
|
||||
if (data != nil && error == nil) {
|
||||
NSArray *jsonData = [NSJSONSerialization JSONObjectWithData:data options:0 error:&error];
|
||||
NSMutableArray *segments = [NSMutableArray array];
|
||||
for (NSDictionary *dict in jsonData) {
|
||||
SponsorSegment *segment = [[SponsorSegment alloc] initWithStartTime:[[dict objectForKey:@"segment"][0] floatValue] endTime:[[dict objectForKey:@"segment"][1] floatValue] category:(NSString *)[dict objectForKey:@"category"] UUID:(NSString *)[dict objectForKey:@"UUID"]];
|
||||
[segments addObject:segment];
|
||||
}
|
||||
skipSegments = [segments sortedArrayUsingComparator:^NSComparisonResult(SponsorSegment *a, SponsorSegment *b) {
|
||||
NSNumber *first = @(a.startTime);
|
||||
NSNumber *second = @(b.startTime);
|
||||
return [first compare:second];
|
||||
}].mutableCopy;
|
||||
NSMutableArray *seekBarSegments = skipSegments.mutableCopy;
|
||||
for (SponsorSegment *segment in skipSegments.copy) {
|
||||
NSInteger setting = [[kCategorySettings objectForKey:segment.category] intValue];
|
||||
switch (setting) {
|
||||
case 0:
|
||||
[skipSegments removeObject:segment];
|
||||
[seekBarSegments removeObject:segment];
|
||||
break;
|
||||
case 2:
|
||||
[skipSegments removeObject:segment];
|
||||
break;
|
||||
//only leaves the object in seekBarSegments so it appears in the seek bar but doesn't get skipped
|
||||
default:
|
||||
break;
|
||||
}
|
||||
if (segment.endTime - segment.startTime < kMinimumDuration) {
|
||||
[skipSegments removeObject:segment];
|
||||
[seekBarSegments removeObject:segment];
|
||||
}
|
||||
|
||||
}
|
||||
[target performSelectorOnMainThread:sel withObject:skipSegments waitUntilDone:NO];
|
||||
|
||||
if ([target isKindOfClass:objc_getClass("YTPlayerViewController")]) {
|
||||
YTPlayerViewController *playerViewController = (YTPlayerViewController *)target;
|
||||
YTPlayerView *playerView = (YTPlayerView *)playerViewController.view;
|
||||
YTMainAppVideoPlayerOverlayView *overlayView = (YTMainAppVideoPlayerOverlayView *)playerView.overlayView;
|
||||
if ([overlayView isKindOfClass:objc_getClass("YTMainAppVideoPlayerOverlayView")]) {
|
||||
if (overlayView.playerBar.playerBar) {
|
||||
[overlayView.playerBar.playerBar performSelectorOnMainThread:@selector(setSkipSegments:) withObject:seekBarSegments waitUntilDone:NO];
|
||||
}
|
||||
else {
|
||||
[overlayView.playerBar.segmentablePlayerBar performSelectorOnMainThread:@selector(setSkipSegments:) withObject:seekBarSegments waitUntilDone:NO];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}];
|
||||
[dataTask resume];
|
||||
}
|
||||
+ (void)postSponsorTimes:(NSString *)videoID sponsorSegments:(NSArray <SponsorSegment *> *)segments userID:(NSString *)userID withViewController:(UIViewController *)viewController {
|
||||
for (SponsorSegment *segment in segments) {
|
||||
NSMutableURLRequest *request = [[NSMutableURLRequest alloc] init];
|
||||
[request setURL:[NSURL URLWithString:[NSString stringWithFormat:@"https://sponsor.ajay.app/api/skipSegments?videoID=%@&startTime=%f&endTime=%f&category=%@&userID=%@", videoID, segment.startTime, segment.endTime, segment.category, userID]]];
|
||||
request.HTTPMethod = @"POST";
|
||||
NSURLSessionDataTask *dataTask = [[NSURLSession sharedSession] dataTaskWithRequest:request completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) {
|
||||
NSHTTPURLResponse *URLResponse = (NSHTTPURLResponse *)response;
|
||||
if (URLResponse.statusCode != 200) {
|
||||
dispatch_async(dispatch_get_main_queue(), ^{
|
||||
UIAlertController *alert = [UIAlertController alertControllerWithTitle:LOC(@"Error") message:[NSString stringWithFormat:@"%@: %ld %@", LOC(@"ErrorCode"), URLResponse.statusCode, [NSHTTPURLResponse localizedStringForStatusCode:URLResponse.statusCode]] preferredStyle:UIAlertControllerStyleAlert];
|
||||
UIAlertAction* defaultAction = [UIAlertAction actionWithTitle:LOC(@"OK") style:UIAlertActionStyleDefault
|
||||
handler:^(UIAlertAction * action) {}];
|
||||
[alert addAction:defaultAction];
|
||||
[viewController presentViewController:alert animated:YES completion:nil];
|
||||
});
|
||||
return;
|
||||
}
|
||||
else {
|
||||
dispatch_async(dispatch_get_main_queue(), ^{
|
||||
UIAlertController *alert = [UIAlertController alertControllerWithTitle:LOC(@"Success") message:LOC(@"SuccessfullySubmittedSegments") preferredStyle:UIAlertControllerStyleAlert];
|
||||
UIAlertAction* defaultAction = [UIAlertAction actionWithTitle:LOC(@"OK") style:UIAlertActionStyleDefault
|
||||
handler:^(UIAlertAction * action) {}];
|
||||
[alert addAction:defaultAction];
|
||||
[viewController presentViewController:alert animated:YES completion:nil];
|
||||
});
|
||||
}
|
||||
}];
|
||||
[dataTask resume];
|
||||
}
|
||||
}
|
||||
+ (void)normalVoteForSegment:(SponsorSegment *)segment userID:(NSString *)userID type:(BOOL)type withViewController:(UIViewController *)viewController {
|
||||
NSMutableURLRequest *request = [[NSMutableURLRequest alloc] init];
|
||||
[request setURL:[NSURL URLWithString:[NSString stringWithFormat:@"https://sponsor.ajay.app/api/voteOnSponsorTime?UUID=%@&userID=%@&type=%d", segment.UUID, userID, type]]];
|
||||
request.HTTPMethod = @"POST";
|
||||
NSURLSessionDataTask *dataTask = [[NSURLSession sharedSession] dataTaskWithRequest:request completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) {
|
||||
NSHTTPURLResponse *URLResponse = (NSHTTPURLResponse *)response;
|
||||
NSString *title;
|
||||
CGFloat delay;
|
||||
if (URLResponse.statusCode != 200) {
|
||||
title = [NSString stringWithFormat:@"%@: (%ld %@)", LOC(@"ErrorVoting"), URLResponse.statusCode, [NSHTTPURLResponse localizedStringForStatusCode:URLResponse.statusCode]];
|
||||
delay = 3.0f;
|
||||
}
|
||||
else {
|
||||
title = LOC(@"SuccessfullyVoted");
|
||||
delay = 1.0f;
|
||||
}
|
||||
dispatch_async(dispatch_get_main_queue(), ^{
|
||||
MBProgressHUD *hud = [MBProgressHUD showHUDAddedTo:viewController.view animated:YES];
|
||||
hud.mode = MBProgressHUDModeText;
|
||||
hud.label.text = title;
|
||||
hud.offset = CGPointMake(0.f, 50);
|
||||
[hud hideAnimated:YES afterDelay:delay];
|
||||
});
|
||||
}];
|
||||
[dataTask resume];
|
||||
}
|
||||
+ (void)categoryVoteForSegment:(SponsorSegment *)segment userID:(NSString *)userID category:(NSString *)category withViewController:(UIViewController *)viewController {
|
||||
NSMutableURLRequest *request = [[NSMutableURLRequest alloc] init];
|
||||
[request setURL:[NSURL URLWithString:[NSString stringWithFormat:@"https://sponsor.ajay.app/api/voteOnSponsorTime?UUID=%@&userID=%@&category=%@", segment.UUID, userID, category]]];
|
||||
request.HTTPMethod = @"POST";
|
||||
NSURLSessionDataTask *dataTask = [[NSURLSession sharedSession] dataTaskWithRequest:request completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) {
|
||||
NSHTTPURLResponse *URLResponse = (NSHTTPURLResponse *)response;
|
||||
NSString *title;
|
||||
CGFloat delay;
|
||||
if (URLResponse.statusCode != 200) {
|
||||
title = [NSString stringWithFormat:@"%@: (%ld %@)", LOC(@"ErrorVoting"), URLResponse.statusCode, [NSHTTPURLResponse localizedStringForStatusCode:URLResponse.statusCode]];
|
||||
delay = 3.0f;
|
||||
}
|
||||
else {
|
||||
title = LOC(@"SuccessfullyVoted");
|
||||
delay = 1.0f;
|
||||
}
|
||||
dispatch_async(dispatch_get_main_queue(), ^{
|
||||
MBProgressHUD *hud = [MBProgressHUD showHUDAddedTo:viewController.view animated:YES];
|
||||
hud.mode = MBProgressHUDModeText;
|
||||
hud.label.text = title;
|
||||
hud.offset = CGPointMake(0.f, 50);
|
||||
[hud hideAnimated:YES afterDelay:delay];
|
||||
});
|
||||
}];
|
||||
[dataTask resume];
|
||||
}
|
||||
+ (void)viewedVideoSponsorTime:(SponsorSegment *)segment {
|
||||
NSMutableURLRequest *request = [[NSMutableURLRequest alloc] init];
|
||||
[request setURL:[NSURL URLWithString:[NSString stringWithFormat:@"https://sponsor.ajay.app/api/viewedVideoSponsorTime?UUID=%@", segment.UUID]]];
|
||||
request.HTTPMethod = @"POST";
|
||||
NSURLSessionDataTask *dataTask = [[NSURLSession sharedSession] dataTaskWithRequest:request completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) {
|
||||
}];
|
||||
[dataTask resume];
|
||||
}
|
||||
@end
|
||||
367
Tweaks/iSponsorBlock/SponsorBlockSettingsController.m
Normal file
367
Tweaks/iSponsorBlock/SponsorBlockSettingsController.m
Normal file
@@ -0,0 +1,367 @@
|
||||
#import "Headers/SponsorBlockSettingsController.h"
|
||||
#import "Headers/Localization.h"
|
||||
|
||||
@implementation SponsorBlockTableCell
|
||||
- (void)colorPicker:(id)colorPicker didSelectColor:(UIColor *)color {
|
||||
self.colorWell.color = color;
|
||||
NSString *hexString = hexFromUIColor(color);
|
||||
|
||||
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
|
||||
NSString *documentsDirectory = [paths objectAtIndex:0];
|
||||
NSString *settingsPath = [documentsDirectory stringByAppendingPathComponent:@"iSponsorBlock.plist"];
|
||||
NSMutableDictionary *settings = [NSMutableDictionary dictionary];
|
||||
[settings addEntriesFromDictionary:[NSDictionary dictionaryWithContentsOfFile:settingsPath]];
|
||||
NSDictionary *categorySettings = [settings objectForKey:@"categorySettings"];
|
||||
|
||||
[categorySettings setValue:hexString forKey:[NSString stringWithFormat:@"%@Color", self.category]];
|
||||
[settings setValue:categorySettings forKey:@"categorySettings"];
|
||||
[settings writeToURL:[NSURL fileURLWithPath:settingsPath isDirectory:NO] error:nil];
|
||||
CFNotificationCenterPostNotification(CFNotificationCenterGetDarwinNotifyCenter(), CFSTR("com.galacticdev.isponsorblockprefs.changed"), NULL, NULL, YES);
|
||||
}
|
||||
|
||||
- (void)presentColorPicker:(UITableViewCell *)sender {
|
||||
HBColorPickerViewController *viewController = [[objc_getClass("HBColorPickerViewController") alloc] init];
|
||||
viewController.delegate = self;
|
||||
viewController.popoverPresentationController.sourceView = self;
|
||||
|
||||
HBColorPickerConfiguration *configuration = [[objc_getClass("HBColorPickerConfiguration") alloc] initWithColor:self.colorWell.color];
|
||||
configuration.supportsAlpha = NO;
|
||||
viewController.configuration = configuration;
|
||||
|
||||
UIViewController *rootViewController = self._viewControllerForAncestor;
|
||||
[rootViewController presentViewController:viewController animated:YES completion:nil];
|
||||
|
||||
//fixes the bottom of the color picker from getting cut off
|
||||
viewController.view.frame = CGRectMake(0,-50, viewController.view.frame.size.width, viewController.view.frame.size.height);
|
||||
}
|
||||
@end
|
||||
|
||||
@implementation SponsorBlockSettingsController
|
||||
- (void)viewDidLoad {
|
||||
[super viewDidLoad];
|
||||
|
||||
UIBarButtonItem *dismissButton;
|
||||
|
||||
dismissButton = [[UIBarButtonItem alloc] initWithImage:[UIImage systemImageNamed:@"xmark"]
|
||||
style:UIBarButtonItemStylePlain
|
||||
target:self
|
||||
action:@selector(dismissButtonTapped:)];
|
||||
|
||||
dismissButton.tintColor = [UIColor blackColor];
|
||||
self.navigationItem.leftBarButtonItem = dismissButton;
|
||||
|
||||
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
|
||||
NSString *documentsDirectory = [paths objectAtIndex:0];
|
||||
self.settingsPath = [documentsDirectory stringByAppendingPathComponent:@"iSponsorBlock.plist"];
|
||||
self.settings = [NSMutableDictionary dictionary];
|
||||
[self.settings addEntriesFromDictionary:[NSDictionary dictionaryWithContentsOfFile:self.settingsPath]];
|
||||
|
||||
self.view.backgroundColor = UIColor.systemBackgroundColor;
|
||||
|
||||
//detects if device is an se gen 1 or not, crude fix for text getting cut off
|
||||
if ([UIScreen mainScreen].bounds.size.width > 320) {
|
||||
self.tableView = [[UITableView alloc] initWithFrame:self.view.frame style:UITableViewStyleInsetGrouped];
|
||||
}
|
||||
else {
|
||||
self.tableView = [[UITableView alloc] initWithFrame:self.view.frame style:UITableViewStyleGrouped];
|
||||
}
|
||||
|
||||
[self.view addSubview:self.tableView];
|
||||
self.tableView.translatesAutoresizingMaskIntoConstraints = NO;
|
||||
[self.tableView.heightAnchor constraintEqualToAnchor:self.view.heightAnchor].active = YES;
|
||||
[self.tableView.widthAnchor constraintEqualToAnchor:self.view.widthAnchor].active = YES;
|
||||
self.tableView.delegate = self;
|
||||
self.tableView.dataSource = self;
|
||||
|
||||
NSBundle *tweakBundle = iSponsorBlockBundle();
|
||||
UIImageView *imageView = [[UIImageView alloc] initWithImage:[UIImage imageWithContentsOfFile:[tweakBundle pathForResource:@"LogoSponsorBlocker128px" ofType:@"png"]]];
|
||||
UILabel *label = [[UILabel alloc] initWithFrame:CGRectMake(0,0,0,0)];
|
||||
label.text = @"iSponsorBlock";
|
||||
label.font = [UIFont boldSystemFontOfSize:48];
|
||||
self.tableView.tableHeaderView = [[UIView alloc] initWithFrame:CGRectMake(0,0,0,200)];
|
||||
[self.tableView.tableHeaderView addSubview:imageView];
|
||||
[self.tableView.tableHeaderView addSubview:label];
|
||||
|
||||
self.tweakTitle = label.text;
|
||||
|
||||
imageView.translatesAutoresizingMaskIntoConstraints = NO;
|
||||
label.translatesAutoresizingMaskIntoConstraints = NO;
|
||||
[imageView.centerXAnchor constraintEqualToAnchor:self.view.centerXAnchor].active = YES;
|
||||
[imageView.topAnchor constraintEqualToAnchor:self.tableView.tableHeaderView.topAnchor constant:5].active = YES;
|
||||
[label.centerXAnchor constraintEqualToAnchor:imageView.centerXAnchor].active = YES;
|
||||
[label.topAnchor constraintEqualToAnchor:imageView.bottomAnchor constant:5].active = YES;
|
||||
|
||||
//for dismissing num pad when tapping anywhere on the string
|
||||
UITapGestureRecognizer *tap = [[UITapGestureRecognizer alloc] initWithTarget:self.view action:@selector(endEditing:)];
|
||||
tap.cancelsTouchesInView = NO;
|
||||
[self.view addGestureRecognizer:tap];
|
||||
|
||||
self.sectionTitles = @[LOC(@"Sponsor"), LOC(@"Intermission/IntroAnimation"), LOC(@"Endcards/Credits"), LOC(@"InteractionReminder"), LOC(@"Unpaid/SelfPromotion"), LOC(@"Non-MusicSection"), LOC(@"SponsorBlockUserID"), LOC(@"SponsorBlockAPIInstance")];
|
||||
}
|
||||
|
||||
//Add iSponsorBlock text to Navbar label if header text out of screen
|
||||
- (void)scrollViewDidScroll:(UIScrollView *)scrollView {
|
||||
CGRect labelCellRect = [self.tableView rectForRowAtIndexPath:[NSIndexPath indexPathForRow:0 inSection:0]];
|
||||
CGRect visibleRect = CGRectMake(self.tableView.contentOffset.x,
|
||||
self.tableView.contentOffset.y + self.navigationController.navigationBar.frame.size.height,
|
||||
self.tableView.bounds.size.width,
|
||||
self.tableView.bounds.size.height - self.navigationController.navigationBar.frame.size.height);
|
||||
|
||||
if (!CGRectContainsRect(visibleRect, labelCellRect)) {
|
||||
self.title = self.tweakTitle;
|
||||
[UIView animateWithDuration:0.3 animations:^{
|
||||
self.navigationItem.titleView.alpha = 1.0;
|
||||
}];
|
||||
} else {
|
||||
self.title = nil;
|
||||
[UIView animateWithDuration:0.3 animations:^{
|
||||
self.navigationItem.titleView.alpha = 0.0;
|
||||
}];
|
||||
}
|
||||
}
|
||||
|
||||
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
|
||||
return 18;
|
||||
}
|
||||
|
||||
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
|
||||
if (section == 0) return 1;
|
||||
else if (section <= 6 || section == 17) return 2;
|
||||
return 1;
|
||||
}
|
||||
|
||||
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
|
||||
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:@"SponsorBlockCell"];
|
||||
if (!cell) {
|
||||
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:@"SponsorBlocKCell"];
|
||||
}
|
||||
|
||||
if (indexPath.section == 0) {
|
||||
cell.textLabel.text = LOC(@"Enabled");
|
||||
UISwitch *enabledSwitch = [[UISwitch alloc] initWithFrame:CGRectMake(0,0,51,31)];
|
||||
cell.accessoryView = enabledSwitch;
|
||||
[enabledSwitch addTarget:self action:@selector(enabledSwitchToggled:) forControlEvents:UIControlEventValueChanged];
|
||||
if ([self.settings valueForKey:@"enabled"]) {
|
||||
[enabledSwitch setOn:[[self.settings valueForKey:@"enabled"] boolValue] animated:NO];
|
||||
}
|
||||
else {
|
||||
[enabledSwitch setOn:YES animated:NO];
|
||||
[self enabledSwitchToggled:enabledSwitch];
|
||||
}
|
||||
return cell;
|
||||
}
|
||||
|
||||
if (indexPath.section <= 6) {
|
||||
SponsorBlockTableCell *tableCell = [[SponsorBlockTableCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:@"SponsorBlockCell2"];
|
||||
NSDictionary *categorySettings = [self.settings objectForKey:@"categorySettings"];
|
||||
UISegmentedControl *segmentedControl = [[UISegmentedControl alloc] initWithItems:@[LOC(@"Disable"), LOC(@"AutoSkip"), LOC(@"ShowInSeekBar"), LOC(@"ManualSkip")]];
|
||||
|
||||
//make it so "Show in Seek Bar" text won't be cut off on certain devices
|
||||
NSMutableArray *segments = [segmentedControl valueForKey:@"_segments"];
|
||||
UISegment *segment = segments[2];
|
||||
UILabel *label = [segment valueForKey:@"_info"];
|
||||
label.adjustsFontSizeToFitWidth = YES;
|
||||
|
||||
switch (indexPath.section) {
|
||||
case 1:
|
||||
segmentedControl.selectedSegmentIndex = [[categorySettings objectForKey:@"sponsor"] intValue];
|
||||
tableCell.category = @"sponsor";
|
||||
break;
|
||||
case 2:
|
||||
segmentedControl.selectedSegmentIndex = [[categorySettings objectForKey:@"intro"] intValue];
|
||||
tableCell.category = @"intro";
|
||||
break;
|
||||
case 3:
|
||||
segmentedControl.selectedSegmentIndex = [[categorySettings objectForKey:@"outro"] intValue];
|
||||
tableCell.category = @"outro";
|
||||
break;
|
||||
case 4:
|
||||
segmentedControl.selectedSegmentIndex = [[categorySettings objectForKey:@"interaction"] intValue];
|
||||
tableCell.category = @"interaction";
|
||||
break;
|
||||
case 5:
|
||||
segmentedControl.selectedSegmentIndex = [[categorySettings objectForKey:@"selfpromo"] intValue];
|
||||
tableCell.category = @"selfpromo";
|
||||
break;
|
||||
case 6:
|
||||
segmentedControl.selectedSegmentIndex = [[categorySettings objectForKey:@"music_offtopic"] intValue];
|
||||
tableCell.category = @"music_offtopic";
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
if (indexPath.row == 0) {
|
||||
[segmentedControl addTarget:self action:@selector(categorySegmentSelected:) forControlEvents:UIControlEventValueChanged];
|
||||
segmentedControl.apportionsSegmentWidthsByContent = YES;
|
||||
[tableCell.contentView addSubview:segmentedControl];
|
||||
segmentedControl.translatesAutoresizingMaskIntoConstraints = NO;
|
||||
[segmentedControl.centerYAnchor constraintEqualToAnchor:tableCell.contentView.centerYAnchor].active = YES;
|
||||
[segmentedControl.widthAnchor constraintEqualToAnchor:tableCell.contentView.widthAnchor].active = YES;
|
||||
}
|
||||
else {
|
||||
tableCell.textLabel.text = LOC(@"SetColorToShowInSeekBar");
|
||||
tableCell.textLabel.adjustsFontSizeToFitWidth = YES;
|
||||
HBColorWell *colorWell = [[objc_getClass("HBColorWell") alloc] initWithFrame:CGRectMake(0,0,32,32)];
|
||||
[colorWell addTarget:tableCell action:@selector(presentColorPicker:) forControlEvents:UIControlEventTouchUpInside];
|
||||
[colorWell addTarget:tableCell action:@selector(colorWellValueChanged:) forControlEvents:UIControlEventValueChanged];
|
||||
UIColor *color = colorWithHexString([categorySettings objectForKey:[NSString stringWithFormat:@"%@Color", tableCell.category]]);
|
||||
colorWell.color = color;
|
||||
tableCell.accessoryView = colorWell;
|
||||
tableCell.colorWell = colorWell;
|
||||
}
|
||||
return tableCell;
|
||||
}
|
||||
if (indexPath.section == 7) {
|
||||
UITableViewCell *textCell = [[UITableViewCell alloc] initWithStyle:1000 reuseIdentifier:@"SponsorBlockTextCell"];
|
||||
textCell.textLabel.text = LOC(@"UserID");
|
||||
textCell.textLabel.adjustsFontSizeToFitWidth = YES;
|
||||
[textCell editableTextField].text = [self.settings valueForKey:@"userID"];
|
||||
[textCell editableTextField].delegate = self;
|
||||
return textCell;
|
||||
}
|
||||
if (indexPath.section == 8) {
|
||||
UITableViewCell *textCell = [[UITableViewCell alloc] initWithStyle:1000 reuseIdentifier:@"SponsorBlockTextCell"];
|
||||
textCell.textLabel.text = LOC(@"API_URL");
|
||||
textCell.textLabel.adjustsFontSizeToFitWidth = YES;
|
||||
[textCell editableTextField].text = [self.settings valueForKey:@"apiInstance"];
|
||||
[textCell editableTextField].delegate = self;
|
||||
return textCell;
|
||||
}
|
||||
if (indexPath.section == 9) {
|
||||
UITableViewCell *textCell = [[UITableViewCell alloc] initWithStyle:1000 reuseIdentifier:@"SponsorBlockTextCell"];
|
||||
textCell.textLabel.text = LOC(@"MinimumSegmentDuration");
|
||||
textCell.textLabel.adjustsFontSizeToFitWidth = YES;
|
||||
[textCell editableTextField].text = [NSString stringWithFormat:@"%.1f", [[self.settings valueForKey:@"minimumDuration"] floatValue]];
|
||||
[textCell editableTextField].keyboardType = UIKeyboardTypeDecimalPad;
|
||||
[textCell editableTextField].delegate = self;
|
||||
return textCell;
|
||||
}
|
||||
if (indexPath.section == 10) {
|
||||
UITableViewCell *textCell = [[UITableViewCell alloc] initWithStyle:1000 reuseIdentifier:@"SponsorBlockTextCell"];
|
||||
textCell.textLabel.text = LOC(@"HowLongNoticeWillAppear");
|
||||
textCell.textLabel.adjustsFontSizeToFitWidth = YES;
|
||||
[textCell editableTextField].text = [NSString stringWithFormat:@"%.1f", [[self.settings valueForKey:@"skipNoticeDuration"] floatValue]];
|
||||
[textCell editableTextField].keyboardType = UIKeyboardTypeDecimalPad;
|
||||
[textCell editableTextField].delegate = self;
|
||||
return textCell;
|
||||
}
|
||||
if (indexPath.section >= 11 && indexPath.section < 17) {
|
||||
NSArray *titles = @[LOC(@"ShowSkipNotice"), LOC(@"ShowButtonsInPlayer"), LOC(@"HideStartEndButtonInPlayer"), LOC(@"ShowModifiedTime"), LOC(@"AudioNotificationOnSkip"), LOC(@"EnableSkipCountTracking")];
|
||||
NSArray *titlesNames = @[@"showSkipNotice", @"showButtonsInPlayer", @"hideStartEndButtonInPlayer", @"showModifiedTime", @"skipAudioNotification", @"enableSkipCountTracking"];
|
||||
UITableViewCell *tableCell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:@"SponsorBlockCell3"];
|
||||
|
||||
tableCell.textLabel.text = titles[indexPath.section-11];
|
||||
tableCell.textLabel.adjustsFontSizeToFitWidth = YES;
|
||||
|
||||
UISwitch *toggleSwitch = [[UISwitch alloc] initWithFrame:CGRectMake(0,0,51,31)];
|
||||
tableCell.accessoryView = toggleSwitch;
|
||||
[toggleSwitch addTarget:self action:@selector(switchToggled:) forControlEvents:UIControlEventValueChanged];
|
||||
if ([self.settings valueForKey:titlesNames[indexPath.section-11]]) {
|
||||
[toggleSwitch setOn:[[self.settings valueForKey:titlesNames[indexPath.section-11]] boolValue] animated:NO];
|
||||
} else {
|
||||
[toggleSwitch setOn:YES animated:NO];
|
||||
[self switchToggled:toggleSwitch];
|
||||
}
|
||||
return tableCell;
|
||||
}
|
||||
if (indexPath.section == 17) {
|
||||
UITableViewCell *cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:@"SponsorBlockDonationCell"];
|
||||
cell.textLabel.text = indexPath.row == 0 ? LOC(@"DonateOnVenmo") : LOC(@"DonateOnPayPal");
|
||||
cell.imageView.image = [UIImage systemImageNamed:@"dollarsign.circle.fill"];
|
||||
return cell;
|
||||
}
|
||||
return nil;
|
||||
}
|
||||
|
||||
- (NSString *)tableView:(UITableView *)tableView titleForHeaderInSection:(NSInteger)section {
|
||||
if (section == 0) return nil;
|
||||
if (section <= 8) return self.sectionTitles[section-1];
|
||||
return nil;
|
||||
}
|
||||
|
||||
- (NSString *)tableView:(UITableView *)tableView titleForFooterInSection:(NSInteger)section {
|
||||
if (section == 0) return LOC(@"RestartFooter");
|
||||
if (section == 7) return LOC(@"UserIDFooter");
|
||||
if (section == 8) return LOC(@"APIFooter");
|
||||
if (section == 15) return LOC(@"AudioFooter");
|
||||
return nil;
|
||||
}
|
||||
|
||||
//To allow highlights only for certain sections
|
||||
- (BOOL)tableView:(UITableView *)tableView shouldHighlightRowAtIndexPath:(NSIndexPath *)indexPath {
|
||||
if (indexPath.section == 17) {
|
||||
return YES;
|
||||
} else {
|
||||
return NO;
|
||||
}
|
||||
}
|
||||
|
||||
- (void)dismissButtonTapped:(id)sender {
|
||||
[self dismissViewControllerAnimated:YES completion:nil];
|
||||
}
|
||||
|
||||
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
|
||||
if (indexPath.section == 17) {
|
||||
if (indexPath.row == 0) {
|
||||
if ([[UIApplication sharedApplication] canOpenURL:[NSURL URLWithString:@"venmo://"]]) {
|
||||
[[UIApplication sharedApplication] openURL:[NSURL URLWithString:@"venmo://venmo.com/code?user_id=3178620965093376215"] options:@{} completionHandler:nil];
|
||||
} else {
|
||||
[[UIApplication sharedApplication] openURL:[NSURL URLWithString:@"https://venmo.com/code?user_id=3178620965093376215"] options:@{} completionHandler:nil];
|
||||
}
|
||||
|
||||
} else {
|
||||
[[UIApplication sharedApplication] openURL:[NSURL URLWithString:@"https://paypal.me/DBrett684"] options:@{} completionHandler:nil];
|
||||
}
|
||||
}
|
||||
[tableView deselectRowAtIndexPath:indexPath animated:YES]; //To prevent highlight sticking after pressing on buttons
|
||||
}
|
||||
|
||||
- (void)enabledSwitchToggled:(UISwitch *)sender {
|
||||
[self.settings setValue:@(sender.on) forKey:@"enabled"];
|
||||
[self writeSettings];
|
||||
}
|
||||
|
||||
- (void)switchToggled:(UISwitch *)sender {
|
||||
UITableViewCell *cell = (UITableViewCell *)sender.superview;
|
||||
NSArray *titlesNames = @[@"showSkipNotice", @"showButtonsInPlayer", @"hideStartEndButtonInPlayer", @"showModifiedTime", @"skipAudioNotification", @"enableSkipCountTracking"];
|
||||
NSIndexPath *indexPath = [self.tableView indexPathForCell:cell];
|
||||
[self.settings setValue:@(sender.on) forKey:titlesNames[indexPath.section-11]];
|
||||
[self writeSettings];
|
||||
}
|
||||
|
||||
- (void)categorySegmentSelected:(UISegmentedControl *)segmentedControl {
|
||||
NSMutableDictionary *categorySettings = [self.settings valueForKey:@"categorySettings"];
|
||||
[categorySettings setValue:@(segmentedControl.selectedSegmentIndex) forKey:[(SponsorBlockTableCell *)segmentedControl.superview.superview category]];
|
||||
|
||||
[self.settings setValue:categorySettings forKey:@"categorySettings"];
|
||||
[self writeSettings];
|
||||
}
|
||||
|
||||
- (void)textFieldDidEndEditing:(UITextField *)textField {
|
||||
UITableViewCell *cell = (UITableViewCell *)textField.superview.superview;
|
||||
NSNumberFormatter *f = [[NSNumberFormatter alloc] init];
|
||||
f.numberStyle = NSNumberFormatterDecimalStyle;
|
||||
|
||||
NSString *minimumDurationTitle = LOC(@"MinimumSegmentDuration");
|
||||
NSString *skipNoticeDurationTitle = LOC(@"HowLongNoticeWillAppear");
|
||||
NSString *userIDTitle = LOC(@"UserID");
|
||||
NSString *apiURLTitle = LOC(@"API_URL");
|
||||
|
||||
if ([cell.textLabel.text isEqualToString:minimumDurationTitle]) {
|
||||
[self.settings setValue:[f numberFromString:textField.text] forKey:@"minimumDuration"];
|
||||
} else if ([cell.textLabel.text isEqualToString:skipNoticeDurationTitle]) {
|
||||
[self.settings setValue:[f numberFromString:textField.text] forKey:@"skipNoticeDuration"];
|
||||
} else if ([cell.textLabel.text isEqualToString:userIDTitle]) {
|
||||
[self.settings setValue:textField.text forKey:@"userID"];
|
||||
} else if ([cell.textLabel.text isEqualToString:apiURLTitle]) {
|
||||
[self.settings setValue:textField.text forKey:@"apiInstance"];
|
||||
}
|
||||
[self writeSettings];
|
||||
}
|
||||
|
||||
- (void)writeSettings {
|
||||
[self.settings writeToURL:[NSURL fileURLWithPath:self.settingsPath isDirectory:NO] error:nil];
|
||||
CFNotificationCenterPostNotification(CFNotificationCenterGetDarwinNotifyCenter(), CFSTR("com.galacticdev.isponsorblockprefs.changed"), NULL, NULL, YES);
|
||||
}
|
||||
@end
|
||||
427
Tweaks/iSponsorBlock/SponsorBlockViewController.m
Normal file
427
Tweaks/iSponsorBlock/SponsorBlockViewController.m
Normal file
@@ -0,0 +1,427 @@
|
||||
#import "Headers/SponsorBlockViewController.h"
|
||||
#import "Headers/Localization.h"
|
||||
|
||||
@implementation SponsorBlockViewController
|
||||
|
||||
- (void)viewDidLoad {
|
||||
[super viewDidLoad];
|
||||
self.view.backgroundColor = [UIColor systemBackgroundColor];
|
||||
[self addChildViewController:self.playerViewController];
|
||||
[self.view addSubview:self.playerViewController.view];
|
||||
[self setupViews];
|
||||
}
|
||||
|
||||
- (void)setupViews {
|
||||
[self.segmentsInDatabaseLabel removeFromSuperview];
|
||||
[self.userSegmentsLabel removeFromSuperview];
|
||||
[self.submitSegmentsButton removeFromSuperview];
|
||||
[self.whitelistChannelLabel removeFromSuperview];
|
||||
|
||||
self.sponsorSegmentViews = [NSMutableArray array];
|
||||
self.userSponsorSegmentViews = [NSMutableArray array];
|
||||
|
||||
if (!self.startEndSegmentButton) {
|
||||
self.startEndSegmentButton = [UIButton buttonWithType:UIButtonTypeCustom];
|
||||
self.startEndSegmentButton.backgroundColor = UIColor.systemBlueColor;
|
||||
[self.startEndSegmentButton addTarget:self action:@selector(startEndSegmentButtonPressed:) forControlEvents:UIControlEventTouchUpInside];
|
||||
|
||||
if (self.playerViewController.userSkipSegments.lastObject.endTime != -1) [self.startEndSegmentButton setTitle:LOC(@"SegmentStartsNow") forState:UIControlStateNormal];
|
||||
else [self.startEndSegmentButton setTitle:LOC(@"SegmentEndsNow") forState:UIControlStateNormal];
|
||||
self.startEndSegmentButton.titleLabel.adjustsFontSizeToFitWidth = YES;
|
||||
|
||||
[self.playerViewController.view addSubview:self.startEndSegmentButton];
|
||||
|
||||
self.startEndSegmentButton.layer.cornerRadius = 12;
|
||||
self.startEndSegmentButton.frame = CGRectMake(0,0,512,50);
|
||||
self.startEndSegmentButton.translatesAutoresizingMaskIntoConstraints = NO;
|
||||
|
||||
[self.startEndSegmentButton.topAnchor constraintEqualToAnchor:self.playerViewController.view.bottomAnchor constant:10].active = YES;
|
||||
[self.startEndSegmentButton.centerXAnchor constraintEqualToAnchor:self.view.centerXAnchor].active = YES;
|
||||
[self.startEndSegmentButton.widthAnchor constraintEqualToConstant:self.view.frame.size.width/2].active = YES;
|
||||
[self.startEndSegmentButton.heightAnchor constraintEqualToConstant:50].active = YES;
|
||||
self.startEndSegmentButton.clipsToBounds = YES;
|
||||
}
|
||||
|
||||
self.whitelistChannelLabel = [[UILabel alloc] initWithFrame:CGRectZero];
|
||||
self.whitelistChannelLabel.text = LOC(@"WhitelistChannel");
|
||||
[self.playerViewController.view addSubview:self.whitelistChannelLabel];
|
||||
self.whitelistChannelLabel.translatesAutoresizingMaskIntoConstraints = NO;
|
||||
[self.whitelistChannelLabel.topAnchor constraintEqualToAnchor:self.startEndSegmentButton.bottomAnchor constant:10].active = YES;
|
||||
[self.whitelistChannelLabel.centerXAnchor constraintEqualToAnchor:self.startEndSegmentButton.centerXAnchor].active = YES;
|
||||
[self.whitelistChannelLabel.widthAnchor constraintEqualToConstant:185].active = YES;
|
||||
[self.whitelistChannelLabel.heightAnchor constraintEqualToConstant:31].active = YES;
|
||||
self.whitelistChannelLabel.userInteractionEnabled = YES;
|
||||
|
||||
UISwitch *whitelistSwitch = [[UISwitch alloc] initWithFrame:CGRectMake(0,0,51,31)];
|
||||
[whitelistSwitch addTarget:self action:@selector(whitelistSwitchToggled:) forControlEvents:UIControlEventValueChanged];
|
||||
[self.whitelistChannelLabel addSubview:whitelistSwitch];
|
||||
whitelistSwitch.translatesAutoresizingMaskIntoConstraints = NO;
|
||||
[whitelistSwitch.leadingAnchor constraintEqualToAnchor:self.whitelistChannelLabel.trailingAnchor constant:-51].active = YES;
|
||||
[whitelistSwitch.centerYAnchor constraintEqualToAnchor:self.whitelistChannelLabel.centerYAnchor].active = YES;
|
||||
|
||||
if ([kWhitelistedChannels containsObject:self.playerViewController.channelID]) {
|
||||
[whitelistSwitch setOn:YES animated:NO];
|
||||
}
|
||||
else {
|
||||
[whitelistSwitch setOn:NO animated:NO];
|
||||
}
|
||||
|
||||
// I'm using the playerBar skipSegments instead of the playerViewController ones because of the show in seek bar option
|
||||
YTPlayerView *playerView = (YTPlayerView *)self.playerViewController.view;
|
||||
YTMainAppVideoPlayerOverlayView *overlayView = (YTMainAppVideoPlayerOverlayView *)playerView.overlayView;
|
||||
if ([overlayView.playerBar.playerBar skipSegments].count > 0 || overlayView.playerBar.segmentablePlayerBar.skipSegments.count > 0) {
|
||||
self.segmentsInDatabaseLabel = [[UILabel alloc] initWithFrame:CGRectZero];
|
||||
self.segmentsInDatabaseLabel.userInteractionEnabled = YES;
|
||||
|
||||
self.segmentsInDatabaseLabel.text = LOC(@"SegmentsInDatabase");
|
||||
self.segmentsInDatabaseLabel.numberOfLines = 1;
|
||||
self.segmentsInDatabaseLabel.adjustsFontSizeToFitWidth = YES;
|
||||
self.segmentsInDatabaseLabel.textAlignment = NSTextAlignmentCenter;
|
||||
|
||||
[playerView addSubview:self.segmentsInDatabaseLabel];
|
||||
self.segmentsInDatabaseLabel.translatesAutoresizingMaskIntoConstraints = NO;
|
||||
|
||||
[self.segmentsInDatabaseLabel.topAnchor constraintEqualToAnchor:self.whitelistChannelLabel.bottomAnchor constant:-15].active = YES;
|
||||
[self.segmentsInDatabaseLabel.centerXAnchor constraintEqualToAnchor:playerView.centerXAnchor].active = YES;
|
||||
[self.segmentsInDatabaseLabel.widthAnchor constraintEqualToAnchor:playerView.widthAnchor].active = YES;
|
||||
[self.segmentsInDatabaseLabel.heightAnchor constraintEqualToConstant:75.0f].active = YES;
|
||||
|
||||
NSArray *segmentViewsForSegments;
|
||||
if (overlayView.playerBar.playerBar) {
|
||||
segmentViewsForSegments = overlayView.playerBar.playerBar.skipSegments;
|
||||
}
|
||||
else {
|
||||
segmentViewsForSegments = overlayView.playerBar.segmentablePlayerBar.skipSegments;
|
||||
}
|
||||
self.sponsorSegmentViews = [self segmentViewsForSegments:segmentViewsForSegments editable:NO];
|
||||
|
||||
for (int i = 0; i < self.sponsorSegmentViews.count; i++) {
|
||||
[self.segmentsInDatabaseLabel addSubview:self.sponsorSegmentViews[i]];
|
||||
[self.sponsorSegmentViews[i] addInteraction:[[UIContextMenuInteraction alloc] initWithDelegate:self]];
|
||||
|
||||
self.sponsorSegmentViews[i].translatesAutoresizingMaskIntoConstraints = NO;
|
||||
[self.sponsorSegmentViews[i].widthAnchor constraintEqualToConstant:playerView.frame.size.width/self.sponsorSegmentViews.count-10].active = YES;
|
||||
[self.sponsorSegmentViews[i].heightAnchor constraintEqualToConstant:30].active = YES;
|
||||
[self.sponsorSegmentViews[i].topAnchor constraintEqualToAnchor:self.segmentsInDatabaseLabel.bottomAnchor constant:-25].active = YES;
|
||||
|
||||
if (self.sponsorSegmentViews.count == 1) {
|
||||
[self.sponsorSegmentViews[i].centerXAnchor constraintEqualToAnchor:self.segmentsInDatabaseLabel.centerXAnchor].active = YES;
|
||||
break;
|
||||
}
|
||||
|
||||
if (i > 0) {
|
||||
[self.sponsorSegmentViews[i].leftAnchor constraintEqualToAnchor:self.sponsorSegmentViews[i-1].rightAnchor constant:5].active = YES;
|
||||
}
|
||||
else {
|
||||
[self.sponsorSegmentViews[i].leftAnchor constraintEqualToAnchor:self.segmentsInDatabaseLabel.leftAnchor constant:5*(self.sponsorSegmentViews.count / 2)].active = YES;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if (self.playerViewController.userSkipSegments.count > 0) {
|
||||
self.userSegmentsLabel = [[UILabel alloc] initWithFrame:CGRectZero];
|
||||
self.userSegmentsLabel.userInteractionEnabled = YES;
|
||||
|
||||
self.userSegmentsLabel.text = LOC(@"YourSegments");
|
||||
|
||||
self.userSponsorSegmentViews = [self segmentViewsForSegments:self.playerViewController.userSkipSegments editable:YES];
|
||||
for (int i = 0; i < self.userSponsorSegmentViews.count; i++) {
|
||||
[self.userSegmentsLabel addSubview:self.userSponsorSegmentViews[i]];
|
||||
[self.userSponsorSegmentViews[i] addInteraction:[[UIContextMenuInteraction alloc] initWithDelegate:self]];
|
||||
|
||||
self.userSponsorSegmentViews[i].translatesAutoresizingMaskIntoConstraints = NO;
|
||||
[self.userSponsorSegmentViews[i].widthAnchor constraintEqualToConstant:playerView.frame.size.width/self.userSponsorSegmentViews.count-10].active = YES;
|
||||
[self.userSponsorSegmentViews[i].heightAnchor constraintEqualToConstant:30].active = YES;
|
||||
[self.userSponsorSegmentViews[i].topAnchor constraintEqualToAnchor:self.userSegmentsLabel.bottomAnchor constant:-25].active = YES;
|
||||
|
||||
if (self.userSponsorSegmentViews.count == 1) {
|
||||
[self.userSponsorSegmentViews[i].centerXAnchor constraintEqualToAnchor:self.userSegmentsLabel.centerXAnchor].active = YES;
|
||||
break;
|
||||
}
|
||||
|
||||
if (i > 0) {
|
||||
[self.userSponsorSegmentViews[i].leftAnchor constraintEqualToAnchor:self.userSponsorSegmentViews[i-1].rightAnchor constant:5].active = YES;
|
||||
}
|
||||
else {
|
||||
[self.userSponsorSegmentViews[i].leftAnchor constraintEqualToAnchor:self.userSegmentsLabel.leftAnchor constant:5*(self.userSponsorSegmentViews.count / 2)].active = YES;
|
||||
}
|
||||
}
|
||||
self.userSegmentsLabel.numberOfLines = 2;
|
||||
self.userSegmentsLabel.adjustsFontSizeToFitWidth = YES;
|
||||
self.userSegmentsLabel.textAlignment = NSTextAlignmentCenter;
|
||||
|
||||
[playerView addSubview:self.userSegmentsLabel];
|
||||
self.userSegmentsLabel.translatesAutoresizingMaskIntoConstraints = NO;
|
||||
|
||||
if ([overlayView.playerBar.playerBar skipSegments].count > 0 || overlayView.playerBar.segmentablePlayerBar.skipSegments.count > 0) [self.userSegmentsLabel.topAnchor constraintEqualToAnchor:self.segmentsInDatabaseLabel.bottomAnchor constant:-10].active = YES;
|
||||
else [self.userSegmentsLabel.topAnchor constraintEqualToAnchor:self.whitelistChannelLabel.bottomAnchor constant:-10].active = YES;
|
||||
|
||||
[self.userSegmentsLabel.centerXAnchor constraintEqualToAnchor:playerView.centerXAnchor].active = YES;
|
||||
[self.userSegmentsLabel.widthAnchor constraintEqualToAnchor:playerView.widthAnchor].active = YES;
|
||||
[self.userSegmentsLabel.heightAnchor constraintEqualToConstant:75.0f].active = YES;
|
||||
|
||||
self.submitSegmentsButton = [UIButton buttonWithType:UIButtonTypeCustom];
|
||||
self.submitSegmentsButton.backgroundColor = UIColor.systemBlueColor;
|
||||
|
||||
[self.submitSegmentsButton addTarget:self action:@selector(submitSegmentsButtonPressed:) forControlEvents:UIControlEventTouchUpInside];
|
||||
[self.submitSegmentsButton setTitle:LOC(@"SubmitSegments") forState:UIControlStateNormal];
|
||||
|
||||
[playerView addSubview:self.submitSegmentsButton];
|
||||
self.submitSegmentsButton.layer.cornerRadius = 12;
|
||||
self.submitSegmentsButton.frame = CGRectMake(0,0,512,50);
|
||||
|
||||
self.submitSegmentsButton.translatesAutoresizingMaskIntoConstraints = NO;
|
||||
[self.submitSegmentsButton.topAnchor constraintEqualToAnchor:self.userSegmentsLabel.bottomAnchor constant:15].active = YES;
|
||||
[self.submitSegmentsButton.centerXAnchor constraintEqualToAnchor:playerView.centerXAnchor].active = YES;
|
||||
[self.submitSegmentsButton.widthAnchor constraintEqualToConstant:self.view.frame.size.width/2].active = YES;
|
||||
[self.submitSegmentsButton.heightAnchor constraintEqualToConstant:50].active = YES;
|
||||
self.submitSegmentsButton.clipsToBounds = YES;
|
||||
}
|
||||
}
|
||||
|
||||
- (void)whitelistSwitchToggled:(UISwitch *)sender {
|
||||
if (sender.isOn) {
|
||||
[kWhitelistedChannels addObject:self.playerViewController.channelID];
|
||||
}
|
||||
else {
|
||||
[kWhitelistedChannels removeObject:self.playerViewController.channelID];
|
||||
}
|
||||
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
|
||||
NSString *documentsDirectory = [paths objectAtIndex:0];
|
||||
NSString *settingsPath = [documentsDirectory stringByAppendingPathComponent:@"iSponsorBlock.plist"];
|
||||
NSMutableDictionary *settings = [NSMutableDictionary dictionary];
|
||||
[settings addEntriesFromDictionary:[NSDictionary dictionaryWithContentsOfFile:settingsPath]];
|
||||
|
||||
[settings setValue:kWhitelistedChannels forKey:@"whitelistedChannels"];
|
||||
[settings writeToURL:[NSURL fileURLWithPath:settingsPath isDirectory:NO] error:nil];
|
||||
CFNotificationCenterPostNotification(CFNotificationCenterGetDarwinNotifyCenter(), CFSTR("com.galacticdev.isponsorblockprefs.changed"), NULL, NULL, YES);
|
||||
}
|
||||
|
||||
- (void)viewDidDisappear:(BOOL)animated {
|
||||
[super viewDidDisappear:animated];
|
||||
[self.startEndSegmentButton removeFromSuperview];
|
||||
[self.segmentsInDatabaseLabel removeFromSuperview];
|
||||
[self.userSegmentsLabel removeFromSuperview];
|
||||
[self.submitSegmentsButton removeFromSuperview];
|
||||
[self.whitelistChannelLabel removeFromSuperview];
|
||||
|
||||
[self.previousParentViewController addChildViewController:self.playerViewController];
|
||||
[self.previousParentViewController.view addSubview:self.playerViewController.view];
|
||||
|
||||
self.overlayView.isDisplayingSponsorBlockViewController = NO;
|
||||
self.overlayView.sponsorBlockButton.hidden = NO;
|
||||
self.overlayView.sponsorStartedEndedButton.hidden = NO;
|
||||
[self.overlayView setOverlayVisible:YES];
|
||||
}
|
||||
|
||||
- (void)startEndSegmentButtonPressed:(UIButton *)sender {
|
||||
NSString *segmentStartsNowTitle = LOC(@"SegmentStartsNow");
|
||||
NSString *segmentEndsNowTitle = LOC(@"SegmentEndsNow");
|
||||
NSString *errorTitle = LOC(@"Error");
|
||||
NSString *okTitle = LOC(@"OK");
|
||||
NSInteger minutes = lroundf(self.playerViewController.userSkipSegments.lastObject.startTime)/60;
|
||||
NSInteger seconds = lroundf(self.playerViewController.userSkipSegments.lastObject.startTime)%60;
|
||||
NSString *errorMessage = [NSString stringWithFormat:@"%@ %ld:%02ld", LOC(@"EndTimeLessThanStartTime"), minutes, seconds];
|
||||
|
||||
if ([sender.titleLabel.text isEqualToString:segmentStartsNowTitle]) {
|
||||
[self.playerViewController.userSkipSegments addObject:[[SponsorSegment alloc] initWithStartTime:self.playerViewController.currentVideoMediaTime endTime:-1 category:nil UUID:nil]];
|
||||
[sender setTitle:segmentEndsNowTitle forState:UIControlStateNormal];
|
||||
} else {
|
||||
self.playerViewController.userSkipSegments.lastObject.endTime = self.playerViewController.currentVideoMediaTime;
|
||||
if (self.playerViewController.userSkipSegments.lastObject.endTime != self.playerViewController.currentVideoMediaTime) {
|
||||
UIAlertController *alert = [UIAlertController alertControllerWithTitle:errorTitle message:errorMessage preferredStyle:UIAlertControllerStyleAlert];
|
||||
UIAlertAction* defaultAction = [UIAlertAction actionWithTitle:okTitle style:UIAlertActionStyleDefault handler:^(UIAlertAction * action) {}];
|
||||
[alert addAction:defaultAction];
|
||||
[self presentViewController:alert animated:YES completion:nil];
|
||||
return;
|
||||
}
|
||||
[sender setTitle:segmentStartsNowTitle forState:UIControlStateNormal];
|
||||
}
|
||||
[self setupViews];
|
||||
}
|
||||
|
||||
- (void)submitSegmentsButtonPressed:(UIButton *)sender {
|
||||
for (SponsorSegment *segment in self.playerViewController.userSkipSegments) {
|
||||
if (segment.endTime == -1 || !segment.category) {
|
||||
UIAlertController *alert = [UIAlertController alertControllerWithTitle:LOC(@"Error") message:LOC(@"UnfinishedSegments") preferredStyle:UIAlertControllerStyleAlert];
|
||||
UIAlertAction* defaultAction = [UIAlertAction actionWithTitle:LOC(@"OK") style:UIAlertActionStyleDefault
|
||||
handler:^(UIAlertAction * action) {}];
|
||||
[alert addAction:defaultAction];
|
||||
[self presentViewController:alert animated:YES completion:nil];
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
|
||||
NSString *documentsDirectory = [paths objectAtIndex:0];
|
||||
NSString *settingsPath = [documentsDirectory stringByAppendingPathComponent:@"iSponsorBlock.plist"];
|
||||
NSMutableDictionary *settings = [NSMutableDictionary dictionary];
|
||||
[settings addEntriesFromDictionary:[NSDictionary dictionaryWithContentsOfFile:settingsPath]];
|
||||
|
||||
[SponsorBlockRequest postSponsorTimes:self.playerViewController.currentVideoID sponsorSegments:self.playerViewController.userSkipSegments userID:kUserID withViewController:self.previousParentViewController];
|
||||
[self.previousParentViewController dismissViewControllerAnimated:YES completion:nil];
|
||||
}
|
||||
|
||||
- (NSMutableArray *)segmentViewsForSegments:(NSArray <SponsorSegment *> *)segments editable:(BOOL)editable {
|
||||
for (SponsorSegment *segment in segments) {
|
||||
if (!editable) {
|
||||
[self.sponsorSegmentViews addObject:[[SponsorSegmentView alloc] initWithFrame:CGRectMake(0,0,50,30) sponsorSegment:segment editable:editable]];
|
||||
}
|
||||
else {
|
||||
[self.userSponsorSegmentViews addObject:[[SponsorSegmentView alloc] initWithFrame:CGRectMake(0,0,50,30) sponsorSegment:segment editable:editable]];
|
||||
}
|
||||
}
|
||||
if (!editable) return self.sponsorSegmentViews;
|
||||
return self.userSponsorSegmentViews;
|
||||
}
|
||||
|
||||
|
||||
- (UIContextMenuConfiguration *)contextMenuInteraction:(UIContextMenuInteraction *)interaction
|
||||
configurationForMenuAtLocation:(CGPoint)location {
|
||||
SponsorSegmentView *sponsorSegmentView = interaction.view;
|
||||
|
||||
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
|
||||
NSString *documentsDirectory = [paths objectAtIndex:0];
|
||||
NSString *path = [documentsDirectory stringByAppendingPathComponent:@"iSponsorBlock.plist"];
|
||||
NSMutableDictionary *settings = [NSMutableDictionary dictionary];
|
||||
[settings addEntriesFromDictionary:[NSDictionary dictionaryWithContentsOfFile:path]];
|
||||
|
||||
UIContextMenuConfiguration *config = [UIContextMenuConfiguration configurationWithIdentifier:nil
|
||||
previewProvider:nil
|
||||
actionProvider:^UIMenu* _Nullable(NSArray<UIMenuElement*>* _Nonnull suggestedActions) {
|
||||
NSMutableArray *categoryActions = [NSMutableArray array];
|
||||
[categoryActions addObject:[UIAction actionWithTitle:LOC(@"Sponsor") image:nil identifier:nil handler:^(__kindof UIAction* _Nonnull action) {
|
||||
if (sponsorSegmentView.editable) {
|
||||
sponsorSegmentView.sponsorSegment.category = @"sponsor";
|
||||
[self setupViews];
|
||||
return;
|
||||
}
|
||||
[SponsorBlockRequest categoryVoteForSegment:sponsorSegmentView.sponsorSegment userID:[settings objectForKey:@"userID"] category:@"sponsor" withViewController:self];
|
||||
}]];
|
||||
|
||||
[categoryActions addObject:[UIAction actionWithTitle:LOC(@"Intermission/IntroAnimation") image:nil identifier:nil handler:^(__kindof UIAction* _Nonnull action) {
|
||||
if (sponsorSegmentView.editable) {
|
||||
sponsorSegmentView.sponsorSegment.category = @"intro";
|
||||
[self setupViews];
|
||||
return;
|
||||
}
|
||||
[SponsorBlockRequest categoryVoteForSegment:sponsorSegmentView.sponsorSegment userID:[settings objectForKey:@"userID"] category:@"intro" withViewController:self];
|
||||
}]];
|
||||
|
||||
[categoryActions addObject:[UIAction actionWithTitle:LOC(@"Outro") image:nil identifier:nil handler:^(__kindof UIAction* _Nonnull action) {
|
||||
if (sponsorSegmentView.editable) {
|
||||
sponsorSegmentView.sponsorSegment.category = @"outro";
|
||||
[self setupViews];
|
||||
return;
|
||||
}
|
||||
[SponsorBlockRequest categoryVoteForSegment:sponsorSegmentView.sponsorSegment userID:[settings objectForKey:@"userID"] category:@"outro" withViewController:self];
|
||||
}]];
|
||||
|
||||
[categoryActions addObject:[UIAction actionWithTitle:LOC(@"InteractionReminder_Subcribe/Like") image:nil identifier:nil handler:^(__kindof UIAction* _Nonnull action) {
|
||||
if (sponsorSegmentView.editable) {
|
||||
sponsorSegmentView.sponsorSegment.category = @"interaction";
|
||||
[self setupViews];
|
||||
return;
|
||||
}
|
||||
[SponsorBlockRequest categoryVoteForSegment:sponsorSegmentView.sponsorSegment userID:[settings objectForKey:@"userID"] category:@"interaction" withViewController:self];
|
||||
}]];
|
||||
|
||||
[categoryActions addObject:[UIAction actionWithTitle:LOC(@"Unpaid/SelfPromotion") image:nil identifier:nil handler:^(__kindof UIAction* _Nonnull action) {
|
||||
if (sponsorSegmentView.editable) {
|
||||
sponsorSegmentView.sponsorSegment.category = @"selfpromo";
|
||||
[self setupViews];
|
||||
return;
|
||||
}
|
||||
[SponsorBlockRequest categoryVoteForSegment:sponsorSegmentView.sponsorSegment userID:[settings objectForKey:@"userID"] category:@"selfpromo" withViewController:self];
|
||||
}]];
|
||||
|
||||
[categoryActions addObject:[UIAction actionWithTitle:LOC(@"Non-MusicSection") image:nil identifier:nil handler:^(__kindof UIAction* _Nonnull action) {
|
||||
if (sponsorSegmentView.editable) {
|
||||
sponsorSegmentView.sponsorSegment.category = @"music_offtopic";
|
||||
[self setupViews];
|
||||
return;
|
||||
}
|
||||
[SponsorBlockRequest categoryVoteForSegment:sponsorSegmentView.sponsorSegment userID:[settings objectForKey:@"userID"] category:@"music_offtopic" withViewController:self];
|
||||
}]];
|
||||
NSMutableArray* actions = [NSMutableArray array];
|
||||
if (sponsorSegmentView.editable) {
|
||||
[actions addObject:[UIAction actionWithTitle:LOC(@"EditStartTime") image:[UIImage systemImageNamed:@"arrow.left.to.line"] identifier:nil handler:^(__kindof UIAction* _Nonnull action) {
|
||||
UIAlertController *alert = [UIAlertController alertControllerWithTitle:LOC(@"Edit") message:LOC(@"EditStartTime_Desc") preferredStyle:UIAlertControllerStyleAlert];
|
||||
UIAlertAction* defaultAction = [UIAlertAction actionWithTitle:LOC(@"OK") style:UIAlertActionStyleDefault
|
||||
handler:^(UIAlertAction * action) {
|
||||
NSNumberFormatter *f = [[NSNumberFormatter alloc] init];
|
||||
f.numberStyle = NSNumberFormatterDecimalStyle;
|
||||
|
||||
NSArray *strings = [alert.textFields[0].text componentsSeparatedByString:@":"];
|
||||
if (strings.count != 2) return;
|
||||
NSString *minutesString = strings[0];
|
||||
NSString *secondsString = strings[1];
|
||||
|
||||
CGFloat minutes = [[f numberFromString:minutesString] floatValue];
|
||||
CGFloat seconds = [[f numberFromString:secondsString] floatValue];
|
||||
sponsorSegmentView.sponsorSegment.startTime = (minutes*60)+seconds;
|
||||
[self setupViews];
|
||||
}];
|
||||
UIAlertAction *cancelAction = [UIAlertAction actionWithTitle:LOC(@"Cancel") style:UIAlertActionStyleDefault handler:^(UIAlertAction *action) {
|
||||
|
||||
}];
|
||||
[alert addAction:defaultAction];
|
||||
[alert addAction:cancelAction];
|
||||
[alert addTextFieldWithConfigurationHandler:nil];
|
||||
[self presentViewController:alert animated:YES completion:nil];
|
||||
}]];
|
||||
|
||||
[actions addObject:[UIAction actionWithTitle:LOC(@"EditEndTime") image:[UIImage systemImageNamed:@"arrow.right.to.line"] identifier:nil handler:^(__kindof UIAction* _Nonnull action) {
|
||||
UIAlertController *alert = [UIAlertController alertControllerWithTitle:LOC(@"Edit") message:LOC(@"EditEndTime_Desc") preferredStyle:UIAlertControllerStyleAlert];
|
||||
UIAlertAction* defaultAction = [UIAlertAction actionWithTitle:LOC(@"OK") style:UIAlertActionStyleDefault
|
||||
handler:^(UIAlertAction * action) {
|
||||
NSNumberFormatter *f = [[NSNumberFormatter alloc] init];
|
||||
f.numberStyle = NSNumberFormatterDecimalStyle;
|
||||
|
||||
NSArray *strings = [alert.textFields[0].text componentsSeparatedByString:@":"];
|
||||
if (strings.count != 2) return;
|
||||
NSString *minutesString = strings[0];
|
||||
NSString *secondsString = strings[1];
|
||||
|
||||
CGFloat minutes = [[f numberFromString:minutesString] floatValue];
|
||||
CGFloat seconds = [[f numberFromString:secondsString] floatValue];
|
||||
sponsorSegmentView.sponsorSegment.endTime = (minutes*60)+seconds;
|
||||
[self setupViews];
|
||||
}];
|
||||
UIAlertAction *cancelAction = [UIAlertAction actionWithTitle:LOC(@"Cancel") style:UIAlertActionStyleDefault handler:^(UIAlertAction *action) {
|
||||
|
||||
}];
|
||||
[alert addAction:defaultAction];
|
||||
[alert addAction:cancelAction];
|
||||
[alert addTextFieldWithConfigurationHandler:nil];
|
||||
[self presentViewController:alert animated:YES completion:nil];
|
||||
}]];
|
||||
|
||||
UIMenu *categoriesMenu = [UIMenu menuWithTitle:LOC(@"EditCategory") image:[UIImage systemImageNamed:@"square.grid.2x2"] identifier:nil options:0 children:categoryActions];
|
||||
[actions addObject:categoriesMenu];
|
||||
[actions addObject:[UIAction actionWithTitle:LOC(@"Delete") image:[UIImage systemImageNamed:@"trash"] identifier:nil handler:^(__kindof UIAction* _Nonnull action) {
|
||||
[self.playerViewController.userSkipSegments removeObject:sponsorSegmentView.sponsorSegment];
|
||||
[self setupViews];
|
||||
}]];
|
||||
|
||||
UIMenu* menu = [UIMenu menuWithTitle:LOC(@"EditSegment") children:actions];
|
||||
return menu;
|
||||
}
|
||||
else {
|
||||
[actions addObject:[UIAction actionWithTitle:LOC(@"Upvote") image:[UIImage systemImageNamed:@"hand.thumbsup.fill"] identifier:nil handler:^(__kindof UIAction* _Nonnull action) {
|
||||
[SponsorBlockRequest normalVoteForSegment:sponsorSegmentView.sponsorSegment userID:[settings objectForKey:@"userID"] type:YES withViewController:self];
|
||||
}]];
|
||||
|
||||
[actions addObject:[UIAction actionWithTitle:LOC(@"Downvote") image:[UIImage systemImageNamed:@"hand.thumbsdown.fill"] identifier:nil handler:^(__kindof UIAction* _Nonnull action) {
|
||||
[SponsorBlockRequest normalVoteForSegment:sponsorSegmentView.sponsorSegment userID:[settings objectForKey:@"userID"] type:NO withViewController:self];
|
||||
}]];
|
||||
|
||||
UIMenu *categoriesMenu = [UIMenu menuWithTitle:LOC(@"VoteToChangeCategory") image:[UIImage systemImageNamed:@"square.grid.2x2"] identifier:nil options:0 children:categoryActions];
|
||||
UIMenu* menu = [UIMenu menuWithTitle:LOC(@"VoteOnSegment") children:[actions arrayByAddingObject:categoriesMenu]];
|
||||
return menu;
|
||||
}
|
||||
}];
|
||||
return config;
|
||||
}
|
||||
@end
|
||||
21
Tweaks/iSponsorBlock/SponsorSegment.m
Normal file
21
Tweaks/iSponsorBlock/SponsorSegment.m
Normal file
@@ -0,0 +1,21 @@
|
||||
#import "Headers/SponsorSegment.h"
|
||||
|
||||
@implementation SponsorSegment
|
||||
- (instancetype)initWithStartTime:(CGFloat)startTime endTime:(CGFloat)endTime category:(NSString *)category UUID:(NSString *)UUID {
|
||||
self = [super init];
|
||||
if (self) {
|
||||
self.startTime = startTime;
|
||||
self.endTime = endTime;
|
||||
self.category = category;
|
||||
self.UUID = UUID;
|
||||
}
|
||||
return self;
|
||||
}
|
||||
- (void)setEndTime:(CGFloat)endTime {
|
||||
if (endTime < self.startTime) {
|
||||
_endTime = -1;
|
||||
return;
|
||||
}
|
||||
_endTime = endTime;
|
||||
}
|
||||
@end
|
||||
86
Tweaks/iSponsorBlock/SponsorSegmentView.m
Normal file
86
Tweaks/iSponsorBlock/SponsorSegmentView.m
Normal file
@@ -0,0 +1,86 @@
|
||||
#import "Headers/SponsorSegmentView.h"
|
||||
#import "Headers/Localization.h"
|
||||
|
||||
@implementation SponsorSegmentView
|
||||
- (instancetype)initWithFrame:(CGRect)frame sponsorSegment:(SponsorSegment *)segment editable:(BOOL)editable {
|
||||
self = [super initWithFrame:frame];
|
||||
if (self) {
|
||||
self.sponsorSegment = segment;
|
||||
self.editable = editable;
|
||||
|
||||
NSString *category;
|
||||
if ([segment.category isEqualToString:@"sponsor"]) {
|
||||
category = LOC(@"Sponsor");
|
||||
}
|
||||
else if ([segment.category isEqualToString:@"intro"]) {
|
||||
category = LOC(@"Intermission");
|
||||
}
|
||||
else if ([segment.category isEqualToString:@"outro"]) {
|
||||
category = LOC(@"Outro");
|
||||
}
|
||||
else if ([segment.category isEqualToString:@"interaction"]) {
|
||||
category = LOC(@"Interaction");
|
||||
}
|
||||
else if ([segment.category isEqualToString:@"selfpromo"]) {
|
||||
category = LOC(@"SelfPromo");
|
||||
}
|
||||
else if ([segment.category isEqualToString:@"music_offtopic"]) {
|
||||
category = LOC(@"Non-Music");
|
||||
}
|
||||
self.categoryLabel = [[UILabel alloc] initWithFrame:self.frame];
|
||||
self.segmentLabel = [[UILabel alloc] initWithFrame:self.frame];
|
||||
self.categoryLabel.text = category;
|
||||
|
||||
NSInteger startSeconds = lroundf(segment.startTime);
|
||||
NSInteger startHours = startSeconds / 3600;
|
||||
NSInteger startMinutes = (startSeconds - (startHours * 3600)) / 60;
|
||||
startSeconds = startSeconds %60;
|
||||
NSString *startTime;
|
||||
if (startHours >= 1) {
|
||||
startTime = [NSString stringWithFormat:@"%ld:%02ld:%02ld", startHours, startMinutes, startSeconds];
|
||||
}
|
||||
else {
|
||||
startTime = [NSString stringWithFormat:@"%ld:%02ld", startMinutes, startSeconds];
|
||||
}
|
||||
|
||||
NSInteger endSeconds = lroundf(segment.endTime);
|
||||
NSInteger endHours = endSeconds / 3600;
|
||||
NSInteger endMinutes = (endSeconds - (endHours * 3600)) / 60;
|
||||
endSeconds = endSeconds %60;
|
||||
NSString *endTime;
|
||||
if (endHours >= 1) {
|
||||
endTime = [NSString stringWithFormat:@"%ld:%02ld:%02ld", endHours, endMinutes, endSeconds];
|
||||
}
|
||||
else {
|
||||
endTime = [NSString stringWithFormat:@"%ld:%02ld", endMinutes, endSeconds];
|
||||
}
|
||||
|
||||
self.segmentLabel.text = [NSString stringWithFormat:@"%@ %@ %@ %@", LOC(@"From"), startTime, LOC(@"to"), endTime];
|
||||
|
||||
[self addSubview:self.categoryLabel];
|
||||
self.categoryLabel.adjustsFontSizeToFitWidth = YES;
|
||||
self.categoryLabel.font = [UIFont systemFontOfSize:12];
|
||||
self.categoryLabel.textAlignment = NSTextAlignmentCenter;
|
||||
self.categoryLabel.translatesAutoresizingMaskIntoConstraints = NO;
|
||||
|
||||
[self addSubview:self.segmentLabel];
|
||||
self.segmentLabel.adjustsFontSizeToFitWidth = YES;
|
||||
self.segmentLabel.font = [UIFont systemFontOfSize:12];
|
||||
self.segmentLabel.textAlignment = NSTextAlignmentCenter;
|
||||
self.segmentLabel.translatesAutoresizingMaskIntoConstraints = NO;
|
||||
|
||||
[self.segmentLabel.widthAnchor constraintEqualToAnchor:self.widthAnchor].active = YES;
|
||||
[self.segmentLabel.heightAnchor constraintEqualToConstant:self.frame.size.height/2].active = YES;
|
||||
|
||||
[self.categoryLabel.widthAnchor constraintEqualToAnchor:self.widthAnchor].active = YES;
|
||||
[self.categoryLabel.heightAnchor constraintEqualToConstant:self.frame.size.height/2].active = YES;
|
||||
[self.categoryLabel.topAnchor constraintEqualToAnchor:self.segmentLabel.bottomAnchor].active = YES;
|
||||
|
||||
self.backgroundColor = UIColor.systemGray4Color;
|
||||
self.layer.cornerRadius = 10;
|
||||
self.segmentLabel.layer.cornerRadius = 10;
|
||||
self.categoryLabel.layer.cornerRadius = 10;
|
||||
}
|
||||
return self;
|
||||
}
|
||||
@end
|
||||
11
Tweaks/iSponsorBlock/control
Normal file
11
Tweaks/iSponsorBlock/control
Normal file
@@ -0,0 +1,11 @@
|
||||
Package: com.galacticdev.isponsorblock
|
||||
Name: iSponsorBlock
|
||||
Version: 1.2
|
||||
Architecture: iphoneos-arm
|
||||
Description: SponsorBlock port for iOS
|
||||
Maintainer: Galactic Dev
|
||||
Author: Galactic Dev
|
||||
Section: Tweaks
|
||||
Depends: mobilesubstrate (>= 0.9.5000), ws.hbang.alderis (>= 1.1)
|
||||
Icon: https://raw.githubusercontent.com/ajayyy/SponsorBlock/master/public/icons/LogoSponsorBlocker64px.png
|
||||
Depiction: https://galacticdev.me/iSponsorBlock
|
||||
1
Tweaks/iSponsorBlock/iSponsorBlock.plist
Normal file
1
Tweaks/iSponsorBlock/iSponsorBlock.plist
Normal file
@@ -0,0 +1 @@
|
||||
{ Filter = { Bundles = ( "com.google.ios.youtube" ); }; }
|
||||
1047
Tweaks/iSponsorBlock/iSponsorBlock.xm
Normal file
1047
Tweaks/iSponsorBlock/iSponsorBlock.xm
Normal file
@@ -0,0 +1,1047 @@
|
||||
#import "Headers/iSponsorBlock.h"
|
||||
#import <AudioToolbox/AudioToolbox.h>
|
||||
#import <rootless.h>
|
||||
#import "Headers/ColorFunctions.h"
|
||||
#import "Headers/SponsorBlockSettingsController.h"
|
||||
#import "Headers/SponsorBlockRequest.h"
|
||||
#import "Headers/SponsorBlockViewController.h"
|
||||
|
||||
#define LOC(x) [tweakBundle localizedStringForKey:x value:nil table:nil]
|
||||
|
||||
extern "C" NSBundle *iSponsorBlockBundle() {
|
||||
static NSBundle *bundle = nil;
|
||||
static dispatch_once_t onceToken;
|
||||
dispatch_once(&onceToken, ^{
|
||||
NSString *tweakBundlePath = [[NSBundle mainBundle] pathForResource:@"iSponsorBlock" ofType:@"bundle"];
|
||||
if (tweakBundlePath)
|
||||
bundle = [NSBundle bundleWithPath:tweakBundlePath];
|
||||
else
|
||||
bundle = [NSBundle bundleWithPath:ROOT_PATH_NS("/Library/Application Support/iSponsorBlock.bundle")];
|
||||
});
|
||||
return bundle;
|
||||
}
|
||||
|
||||
NSBundle *tweakBundle = iSponsorBlockBundle();
|
||||
|
||||
// Sound effect for skip segments
|
||||
static void playSponsorAudio() {
|
||||
NSString *audioFilePath = [tweakBundle pathForResource:@"SponsorAudio" ofType:@"m4a"];
|
||||
NSURL *audioFileURL = [NSURL fileURLWithPath:audioFilePath];
|
||||
SystemSoundID soundID;
|
||||
AudioServicesCreateSystemSoundID((__bridge CFURLRef)audioFileURL, &soundID);
|
||||
AudioServicesPlaySystemSound(soundID);
|
||||
}
|
||||
|
||||
// Check and translate segment title for HUD
|
||||
NSDictionary *categoryLocalization = @{
|
||||
@"sponsor": LOC(@"sponsor"),
|
||||
@"intro": LOC(@"intro"),
|
||||
@"outro": LOC(@"outro"),
|
||||
@"interaction": LOC(@"interaction"),
|
||||
@"selfpromo": LOC(@"selfpromo"),
|
||||
@"music_offtopic": LOC(@"music_offtopic")
|
||||
};
|
||||
|
||||
%group Main
|
||||
NSString *modifiedTimeString;
|
||||
|
||||
%hook YTPlayerViewController
|
||||
%property (strong, nonatomic) NSMutableArray *skipSegments;
|
||||
%property (nonatomic, assign) NSInteger currentSponsorSegment;
|
||||
%property (strong, nonatomic) MBProgressHUD *hud;
|
||||
%property (nonatomic, assign) NSInteger unskippedSegment;
|
||||
%property (strong, nonatomic) NSMutableArray *userSkipSegments;
|
||||
%property (strong, nonatomic) NSString *channelID;
|
||||
%property (nonatomic, assign) BOOL hudDisplayed;
|
||||
|
||||
// used to keep support for older versions, as seekToTime is new
|
||||
%new
|
||||
- (void)isb_scrubToTime:(CGFloat)time {
|
||||
// YT v17.30.1 switched scrubToTime to seekToTime
|
||||
[self respondsToSelector:@selector(scrubToTime:)] ? [self scrubToTime:time] : [self seekToTime:time];
|
||||
}
|
||||
|
||||
- (void)singleVideo:(id)arg1 currentVideoTimeDidChange:(YTSingleVideoTime *)arg2 {
|
||||
%orig;
|
||||
YTPlayerView *playerView = (YTPlayerView *)self.view;
|
||||
YTMainAppVideoPlayerOverlayView *overlayView = (YTMainAppVideoPlayerOverlayView *)playerView.overlayView;
|
||||
if (!self.channelID) self.channelID = @"";
|
||||
if (self.skipSegments.count > 0 && [overlayView isKindOfClass:%c(YTMainAppVideoPlayerOverlayView)] && ![kWhitelistedChannels containsObject:self.channelID]) {
|
||||
if (kShowModifiedTime) {
|
||||
UILabel *durationLabel = overlayView.playerBar.durationLabel;
|
||||
if (![durationLabel.text containsString:modifiedTimeString]) durationLabel.text = [NSString stringWithFormat:@"%@ (%@)", durationLabel.text, modifiedTimeString];
|
||||
[durationLabel sizeToFit];
|
||||
}
|
||||
|
||||
SponsorSegment *sponsorSegment = [[SponsorSegment alloc] initWithStartTime:-1 endTime:-1 category:nil UUID:nil];
|
||||
if (self.currentSponsorSegment <= self.skipSegments.count-1) {
|
||||
sponsorSegment = self.skipSegments[self.currentSponsorSegment];
|
||||
} else if (self.unskippedSegment != self.currentSponsorSegment-1) {
|
||||
sponsorSegment = self.skipSegments[self.currentSponsorSegment-1];
|
||||
}
|
||||
|
||||
if ((lroundf(arg2.time) == ceil(sponsorSegment.startTime) && arg2.time >= sponsorSegment.startTime) || (lroundf(arg2.time) >= ceil(sponsorSegment.startTime) && arg2.time < sponsorSegment.endTime)) {
|
||||
|
||||
if ([[kCategorySettings objectForKey:sponsorSegment.category] intValue] == 3) {
|
||||
if (self.hud.superview != self.view && self.hudDisplayed == NO) {
|
||||
self.hud = [MBProgressHUD showHUDAddedTo:self.view animated:YES];
|
||||
self.hudDisplayed = YES; // Set yes to make sure that HUD is not persistent (Issue #62)
|
||||
self.hud.mode = MBProgressHUDModeCustomView;
|
||||
NSString *localizedSegment = categoryLocalization[sponsorSegment.category] ?: sponsorSegment.category;
|
||||
NSString *localizedManualSkip = LOC(@"ManuallySkipReminder");
|
||||
NSString *formattedManualSkip = [NSString stringWithFormat:localizedManualSkip, localizedSegment, lroundf(sponsorSegment.startTime)/60, lroundf(sponsorSegment.startTime)%60, lroundf(sponsorSegment.endTime)/60, lroundf(sponsorSegment.endTime)%60];
|
||||
self.hud.label.text = formattedManualSkip;
|
||||
self.hud.label.numberOfLines = 0;
|
||||
[self.hud.button setTitle:LOC(@"Skip") forState:UIControlStateNormal];
|
||||
[self.hud.button addTarget:self action:@selector(manuallySkipSegment:) forControlEvents:UIControlEventTouchUpInside];
|
||||
// Add custom button to hide HUD
|
||||
UIButton *cancelButton = [UIButton buttonWithType:UIButtonTypeSystem];
|
||||
UIImage *cancelImage = [[UIImage systemImageNamed:@"x.circle"] imageWithRenderingMode:UIImageRenderingModeAlwaysTemplate];
|
||||
[cancelButton setImage:cancelImage forState:UIControlStateNormal];
|
||||
[cancelButton setTintColor:[[UIColor blackColor] colorWithAlphaComponent:0.7]];
|
||||
[cancelButton addTarget:self action:@selector(cancelHUD:) forControlEvents:UIControlEventTouchUpInside];
|
||||
|
||||
UIView *buttonSuperview = self.hud.button.superview;
|
||||
[buttonSuperview addSubview:cancelButton];
|
||||
|
||||
CGFloat buttonSpacing = 10.0;
|
||||
cancelButton.translatesAutoresizingMaskIntoConstraints = NO;
|
||||
[NSLayoutConstraint activateConstraints:@[
|
||||
[cancelButton.topAnchor constraintEqualToAnchor:self.hud.button.topAnchor],
|
||||
[cancelButton.leadingAnchor constraintEqualToAnchor:self.hud.button.trailingAnchor constant:buttonSpacing],
|
||||
[cancelButton.heightAnchor constraintEqualToAnchor:self.hud.button.heightAnchor]
|
||||
]];
|
||||
self.hud.offset = CGPointMake(self.view.frame.size.width, -MBProgressMaxOffset);
|
||||
|
||||
// Use a delay equal to the length of the sponsored segment to avoid HUD call
|
||||
double delayInSeconds = sponsorSegment.endTime - sponsorSegment.startTime;
|
||||
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(delayInSeconds * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
|
||||
[MBProgressHUD hideHUDForView:self.view animated:YES]; // Hide HUD if user is not interacting with buttons
|
||||
self.hudDisplayed = NO; // Reset flag to make it work for the next segment
|
||||
});
|
||||
}
|
||||
}
|
||||
//edge case where segment end time is longer than the video
|
||||
else if (sponsorSegment.endTime > self.currentVideoTotalMediaTime) {
|
||||
[self isb_scrubToTime:self.currentVideoTotalMediaTime];
|
||||
if (kEnableSkipCountTracking) [SponsorBlockRequest viewedVideoSponsorTime:sponsorSegment];
|
||||
}
|
||||
else {
|
||||
[self isb_scrubToTime:sponsorSegment.endTime];
|
||||
if (kEnableSkipCountTracking) [SponsorBlockRequest viewedVideoSponsorTime:sponsorSegment];
|
||||
}
|
||||
if ([[kCategorySettings objectForKey:sponsorSegment.category] intValue] == 1) {
|
||||
if (self.hud.superview != self.view && kShowSkipNotice) {
|
||||
[MBProgressHUD hideHUDForView:self.view animated:YES];
|
||||
self.hud = [MBProgressHUD showHUDAddedTo:self.view animated:YES];
|
||||
self.hud.mode = MBProgressHUDModeCustomView;
|
||||
// Translate and add segment name to the skipped HUD (issue #70)
|
||||
NSString *localizedSegment = categoryLocalization[sponsorSegment.category] ?: sponsorSegment.category;
|
||||
self.hud.label.text = [NSString stringWithFormat:LOC(@"SkippedSegment"), localizedSegment];
|
||||
self.hud.label.numberOfLines = 0;
|
||||
[self.hud.button setTitle:LOC(@"Unskip") forState:UIControlStateNormal];
|
||||
[self.hud.button addTarget:self action:@selector(unskipSegment:) forControlEvents:UIControlEventTouchUpInside];
|
||||
self.hud.offset = CGPointMake(self.view.frame.size.width, -MBProgressMaxOffset);
|
||||
[self.hud hideAnimated:YES afterDelay:kSkipNoticeDuration];
|
||||
|
||||
// Play sound effect if option enabled
|
||||
if (kSkipAudioNotification) {
|
||||
playSponsorAudio();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (self.currentSponsorSegment <= self.skipSegments.count-1 && [[kCategorySettings objectForKey:sponsorSegment.category] intValue] != 3) self.currentSponsorSegment ++;
|
||||
}
|
||||
else if (lroundf(arg2.time) > sponsorSegment.startTime && self.currentSponsorSegment != self.skipSegments.count && self.currentSponsorSegment != self.skipSegments.count-1) {
|
||||
self.currentSponsorSegment ++;
|
||||
}
|
||||
else if (self.currentSponsorSegment == 0 && self.unskippedSegment != -1) {
|
||||
self.currentSponsorSegment ++;
|
||||
}
|
||||
else if (self.currentSponsorSegment > 0 && lroundf(arg2.time) < self.skipSegments[self.currentSponsorSegment-1].startTime-0.01) {
|
||||
if ([self isMDXActive]) {
|
||||
|
||||
}
|
||||
else if (self.unskippedSegment != self.currentSponsorSegment-1) {
|
||||
self.currentSponsorSegment--;
|
||||
}
|
||||
else if (arg2.time < self.skipSegments[self.currentSponsorSegment-1].startTime-0.01) {
|
||||
self.unskippedSegment = -1;
|
||||
}
|
||||
}
|
||||
}
|
||||
if ([overlayView isKindOfClass:%c(YTMainAppVideoPlayerOverlayView)]) {
|
||||
YTSegmentableInlinePlayerBarView *playerBarView = overlayView.playerBar.segmentablePlayerBar;
|
||||
|
||||
[playerBarView maybeCreateMarkerViewsISB];
|
||||
|
||||
for (UIView *markerView in playerBarView.subviews) {
|
||||
if (![playerBarView.sponsorMarkerViews containsObject:markerView] && playerBarView.skipSegments.count == 0) {
|
||||
[playerBarView maybeCreateMarkerViewsISB];
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
- (void)playbackController:(id)arg1 didActivateVideo:(id)arg2 withPlaybackData:(id)arg3 {
|
||||
%orig;
|
||||
if (self.isPlayingAd) return;
|
||||
YTPlayerView *playerView = (YTPlayerView *)self.view;
|
||||
YTMainAppVideoPlayerOverlayView *overlayView = (YTMainAppVideoPlayerOverlayView *)playerView.overlayView;
|
||||
if ([overlayView isKindOfClass:%c(YTMainAppVideoPlayerOverlayView)]) {
|
||||
[MBProgressHUD hideHUDForView:playerView animated:YES]; //fix manual skip popup not disappearing when changing videos
|
||||
self.hudDisplayed = NO; // Reset flag when changing videos
|
||||
|
||||
self.skipSegments = [NSMutableArray array];
|
||||
self.userSkipSegments = [NSMutableArray array];
|
||||
[SponsorBlockRequest getSponsorTimes:self.currentVideoID completionTarget:self completionSelector:@selector(setSkipSegments:) apiInstance:kAPIInstance];
|
||||
self.currentSponsorSegment = 0;
|
||||
self.unskippedSegment = -1;
|
||||
overlayView.controlsOverlayView.playerViewController = self;
|
||||
overlayView.controlsOverlayView.isDisplayingSponsorBlockViewController = NO;
|
||||
|
||||
YTSingleVideoController *activeVideo = self.activeVideo;
|
||||
if ([activeVideo isKindOfClass:%c(YTSingleVideoController)]) {
|
||||
if ([self.activeVideo.singleVideo respondsToSelector:@selector(video)]) self.channelID = self.activeVideo.singleVideo.video.videoDetails.channelId;
|
||||
else self.channelID = self.activeVideo.singleVideo.playbackData.video.videoDetails.channelId;
|
||||
}
|
||||
}
|
||||
}
|
||||
- (void)setSkipSegments:(NSMutableArray <SponsorSegment *> *)arg1 {
|
||||
%orig;
|
||||
NSInteger totalSavedTime = 0;
|
||||
for (SponsorSegment *segment in arg1) totalSavedTime += lroundf(segment.endTime) - lroundf(segment.startTime);
|
||||
if (arg1.count > 0) {
|
||||
NSInteger seconds = lroundf(self.currentVideoTotalMediaTime - totalSavedTime);
|
||||
NSInteger hours = seconds / 3600;
|
||||
NSInteger minutes = (seconds - (hours * 3600)) / 60;
|
||||
seconds = seconds % 60;
|
||||
|
||||
if (hours >= 1) modifiedTimeString = [NSString stringWithFormat:@"%ld:%02ld:%02ld",hours, minutes, seconds];
|
||||
else modifiedTimeString = [NSString stringWithFormat:@"%ld:%02ld", minutes, seconds];
|
||||
}
|
||||
|
||||
else {
|
||||
modifiedTimeString = nil;
|
||||
}
|
||||
}
|
||||
|
||||
%new
|
||||
- (void)isb_fixVisualGlitch {
|
||||
if (!self.isPlayingAd) {
|
||||
YTPlayerView *playerView = (YTPlayerView *)self.view;
|
||||
YTMainAppVideoPlayerOverlayView *overlayView = (YTMainAppVideoPlayerOverlayView *)playerView.overlayView;
|
||||
if ([overlayView isKindOfClass:%c(YTMainAppVideoPlayerOverlayView)]) {
|
||||
YTSegmentableInlinePlayerBarView *playerBarView = overlayView.playerBar.segmentablePlayerBar;
|
||||
[playerBarView maybeCreateMarkerViewsISB];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
- (void)scrubToTime:(CGFloat)arg1 {
|
||||
%orig;
|
||||
[self isb_fixVisualGlitch];
|
||||
}
|
||||
|
||||
- (void)seekToTime:(CGFloat)arg1 {
|
||||
%orig;
|
||||
[self isb_fixVisualGlitch];
|
||||
}
|
||||
|
||||
%new
|
||||
- (void)unskipSegment:(UIButton *)sender {
|
||||
if (self.currentSponsorSegment > 0) {
|
||||
[self isb_scrubToTime:self.skipSegments[self.currentSponsorSegment-1].startTime];
|
||||
self.unskippedSegment = self.currentSponsorSegment-1;
|
||||
} else {
|
||||
[self isb_scrubToTime:self.skipSegments[self.currentSponsorSegment].startTime];
|
||||
self.unskippedSegment = self.currentSponsorSegment;
|
||||
}
|
||||
[MBProgressHUD hideHUDForView:self.view animated:YES];
|
||||
}
|
||||
|
||||
%new
|
||||
- (void)manuallySkipSegment:(UIButton *)sender {
|
||||
SponsorSegment *sponsorSegment = [[SponsorSegment alloc] initWithStartTime:-1 endTime:-1 category:nil UUID:nil];
|
||||
if (self.currentSponsorSegment <= self.skipSegments.count-1) {
|
||||
sponsorSegment = self.skipSegments[self.currentSponsorSegment];
|
||||
} else if (self.unskippedSegment != self.currentSponsorSegment-1) {
|
||||
sponsorSegment = self.skipSegments[self.currentSponsorSegment-1];
|
||||
}
|
||||
|
||||
if (sponsorSegment.endTime > self.currentVideoTotalMediaTime) {
|
||||
[self isb_scrubToTime:self.currentVideoTotalMediaTime];
|
||||
if (kEnableSkipCountTracking) [SponsorBlockRequest viewedVideoSponsorTime:sponsorSegment];
|
||||
}
|
||||
else {
|
||||
[self isb_scrubToTime:sponsorSegment.endTime];
|
||||
if (kEnableSkipCountTracking) [SponsorBlockRequest viewedVideoSponsorTime:sponsorSegment];
|
||||
}
|
||||
[MBProgressHUD hideHUDForView:self.view animated:YES];
|
||||
// Prevent app crashing if segment was already skipped once
|
||||
if (self.currentSponsorSegment < 0) {
|
||||
self.currentSponsorSegment++;
|
||||
}
|
||||
|
||||
// Reset flag immediately if segment was skipped
|
||||
if (self.hudDisplayed != NO) {
|
||||
self.hudDisplayed = NO;
|
||||
}
|
||||
|
||||
// Play sound effect if option enabled
|
||||
if (kSkipAudioNotification) {
|
||||
playSponsorAudio();
|
||||
}
|
||||
}
|
||||
|
||||
%new
|
||||
- (void)cancelHUD:(UIButton *)sender {
|
||||
[MBProgressHUD hideHUDForView:self.view animated:YES];
|
||||
}
|
||||
|
||||
- (void)setPlayerViewLayout:(NSInteger)arg1 {
|
||||
%orig;
|
||||
YTPlayerView *playerView = (YTPlayerView *)self.view;
|
||||
YTMainAppVideoPlayerOverlayView *overlayView = (YTMainAppVideoPlayerOverlayView *)playerView.overlayView;
|
||||
if ([overlayView isKindOfClass:%c(YTMainAppVideoPlayerOverlayView)]) {
|
||||
YTSegmentableInlinePlayerBarView *playerBarView = overlayView.playerBar.segmentablePlayerBar;
|
||||
[playerBarView maybeCreateMarkerViewsISB];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)updateViewportSizeProvider {
|
||||
%orig;
|
||||
YTPlayerView *playerView = (YTPlayerView *)self.view;
|
||||
YTMainAppVideoPlayerOverlayView *overlayView = (YTMainAppVideoPlayerOverlayView *)playerView.overlayView;
|
||||
if ([overlayView isKindOfClass:%c(YTMainAppVideoPlayerOverlayView)]) {
|
||||
YTSegmentableInlinePlayerBarView *playerBarView = overlayView.playerBar.segmentablePlayerBar;
|
||||
[playerBarView maybeCreateMarkerViewsISB];
|
||||
}
|
||||
}
|
||||
%end
|
||||
|
||||
%hook YTMainAppVideoPlayerOverlayViewController
|
||||
|
||||
- (void)updateTopRightButtonAvailability {
|
||||
%orig;
|
||||
YTMainAppVideoPlayerOverlayView *v = [self videoPlayerOverlayView];
|
||||
YTMainAppControlsOverlayView *c = [v valueForKey:@"_controlsOverlayView"];
|
||||
c.sponsorBlockButton.hidden = !kShowButtonsInPlayer;
|
||||
c.sponsorStartedEndedButton.hidden = !kShowButtonsInPlayer || kHideStartEndButtonInPlayer;
|
||||
[c setNeedsLayout];
|
||||
}
|
||||
|
||||
%end
|
||||
|
||||
%hook YTMainAppControlsOverlayView
|
||||
%property (retain, nonatomic) YTQTMButton *sponsorBlockButton;
|
||||
%property (retain, nonatomic) YTQTMButton *sponsorStartedEndedButton;
|
||||
%property (retain, nonatomic) YTPlayerViewController *playerViewController;
|
||||
%property (nonatomic, assign) BOOL isDisplayingSponsorBlockViewController;
|
||||
|
||||
- (id)initWithDelegate:(id)delegate {
|
||||
self = %orig;
|
||||
if (kShowButtonsInPlayer) {
|
||||
CGFloat padding = [[self class] topButtonAdditionalPadding];
|
||||
self.sponsorBlockButton = [self buttonWithImage:[UIImage imageWithContentsOfFile:[tweakBundle pathForResource:@"PlayerInfoIconSponsorBlocker256px-20@2x" ofType:@"png"]] accessibilityLabel:@"iSponsorBlock" verticalContentPadding:padding];
|
||||
[self.sponsorBlockButton addTarget:self action:@selector(sponsorBlockButtonPressed:) forControlEvents:UIControlEventTouchUpInside];
|
||||
self.sponsorBlockButton.hidden = YES;
|
||||
self.sponsorBlockButton.alpha = 0;
|
||||
|
||||
if (!kHideStartEndButtonInPlayer) {
|
||||
BOOL isStart = self.playerViewController.userSkipSegments.lastObject.endTime != -1;
|
||||
NSString *startedEndedImagePath = isStart ? [tweakBundle pathForResource:@"sponsorblockstart-20@2x" ofType:@"png"] : [tweakBundle pathForResource:@"sponsorblockend-20@2x" ofType:@"png"];
|
||||
self.sponsorStartedEndedButton = [self buttonWithImage:[UIImage imageWithContentsOfFile:startedEndedImagePath] accessibilityLabel:isStart ? @"iSponsorBlock start" : @"iSponsorBlock end" verticalContentPadding:padding];
|
||||
[self.sponsorStartedEndedButton addTarget:self action:@selector(sponsorStartedEndedButtonPressed:) forControlEvents:UIControlEventTouchUpInside];
|
||||
self.sponsorStartedEndedButton.hidden = YES;
|
||||
self.sponsorStartedEndedButton.alpha = 0;
|
||||
}
|
||||
|
||||
@try {
|
||||
UIView *containerView = [self valueForKey:@"_topControlsAccessibilityContainerView"];
|
||||
[containerView addSubview:self.sponsorBlockButton];
|
||||
if (!kHideStartEndButtonInPlayer) {
|
||||
[containerView addSubview:self.sponsorStartedEndedButton];
|
||||
}
|
||||
} @catch (id ex) {
|
||||
[self addSubview:self.sponsorBlockButton];
|
||||
if (!kHideStartEndButtonInPlayer) {
|
||||
[self addSubview:self.sponsorStartedEndedButton];
|
||||
}
|
||||
}
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
- (NSMutableArray *)topControls {
|
||||
NSMutableArray <UIView *> *topControls = %orig;
|
||||
if (kShowButtonsInPlayer) {
|
||||
[topControls insertObject:self.sponsorBlockButton atIndex:0];
|
||||
if (!kHideStartEndButtonInPlayer) {
|
||||
[topControls insertObject:self.sponsorStartedEndedButton atIndex:0];
|
||||
}
|
||||
}
|
||||
return topControls;
|
||||
}
|
||||
|
||||
- (void)setTopOverlayVisible:(BOOL)visible isAutonavCanceledState:(BOOL)canceledState {
|
||||
if (self.isDisplayingSponsorBlockViewController) {
|
||||
%orig(NO, canceledState);
|
||||
self.sponsorBlockButton.imageView.hidden = YES;
|
||||
self.sponsorStartedEndedButton.imageView.hidden = YES;
|
||||
return;
|
||||
}
|
||||
|
||||
self.sponsorBlockButton.alpha = canceledState || !visible ? 0:1;
|
||||
self.sponsorStartedEndedButton.alpha = canceledState || !visible ? 0:1;
|
||||
%orig;
|
||||
}
|
||||
|
||||
%new
|
||||
- (void)sponsorBlockButtonPressed:(YTQTMButton *)sender {
|
||||
self.isDisplayingSponsorBlockViewController = YES;
|
||||
self.sponsorBlockButton.hidden = YES;
|
||||
self.sponsorStartedEndedButton.hidden = YES;
|
||||
if ([self.playerViewController playerViewLayout] == 3) [self.playerViewController didPressToggleFullscreen];
|
||||
[self presentSponsorBlockViewController];
|
||||
}
|
||||
%new
|
||||
- (void)sponsorStartedEndedButtonPressed:(YTQTMButton *)sender {
|
||||
if (self.playerViewController.userSkipSegments.lastObject.endTime != -1) {
|
||||
[self.playerViewController.userSkipSegments addObject:[[SponsorSegment alloc] initWithStartTime:self.playerViewController.currentVideoMediaTime endTime:-1 category:nil UUID:nil]];
|
||||
[self.sponsorStartedEndedButton setImage:[UIImage imageWithContentsOfFile:[tweakBundle pathForResource:@"sponsorblockend-20@2x" ofType:@"png"]] forState:UIControlStateNormal];
|
||||
}
|
||||
else {
|
||||
self.playerViewController.userSkipSegments.lastObject.endTime = self.playerViewController.currentVideoMediaTime;
|
||||
if (self.playerViewController.userSkipSegments.lastObject.endTime != self.playerViewController.currentVideoMediaTime) {
|
||||
UIAlertController *alert = [UIAlertController alertControllerWithTitle:@"Error" message:[NSString stringWithFormat:@"End Time That You Set Was Less Than the Start Time, Please Select a Time After %ld:%02ld",lroundf(self.playerViewController.userSkipSegments.lastObject.startTime)/60, lroundf(self.playerViewController.userSkipSegments.lastObject.startTime)%60] preferredStyle:UIAlertControllerStyleAlert];
|
||||
UIAlertAction* defaultAction = [UIAlertAction actionWithTitle:@"OK" style:UIAlertActionStyleDefault
|
||||
handler:^(UIAlertAction * action) {}];
|
||||
[alert addAction:defaultAction];
|
||||
[[[UIApplication sharedApplication] delegate].window.rootViewController presentViewController:alert animated:YES completion:nil];
|
||||
return;
|
||||
}
|
||||
[self.sponsorStartedEndedButton setImage:[UIImage imageWithContentsOfFile:[tweakBundle pathForResource:@"sponsorblockstart-20@2x" ofType:@"png"]] forState:UIControlStateNormal];
|
||||
}
|
||||
}
|
||||
%new
|
||||
- (void)presentSponsorBlockViewController {
|
||||
SponsorBlockViewController *addSponsorViewController = [[SponsorBlockViewController alloc] init];
|
||||
addSponsorViewController.playerViewController = self.playerViewController;
|
||||
addSponsorViewController.previousParentViewController = self.playerViewController.parentViewController;
|
||||
addSponsorViewController.overlayView = self;
|
||||
addSponsorViewController.preferredContentSize = CGSizeMake(CGRectGetWidth(self.playerViewController.view.frame), 0.9 * CGRectGetHeight(UIScreen.mainScreen.bounds));
|
||||
[[[UIApplication sharedApplication] delegate].window.rootViewController presentViewController:addSponsorViewController animated:YES completion:nil];
|
||||
self.isDisplayingSponsorBlockViewController = YES;
|
||||
[self setOverlayVisible:NO];
|
||||
|
||||
}
|
||||
%end
|
||||
|
||||
%hook YTInlinePlayerBarView
|
||||
%property (strong, nonatomic) NSMutableArray *sponsorMarkerViews;
|
||||
%property (strong, nonatomic) NSMutableArray *skipSegments;
|
||||
%property (strong, nonatomic) YTPlayerViewController *playerViewController;
|
||||
%new
|
||||
- (void)maybeCreateMarkerViewsISB {
|
||||
[self removeSponsorMarkers];
|
||||
self.skipSegments = self.skipSegments;
|
||||
}
|
||||
- (void)setSkipSegments:(NSMutableArray <SponsorSegment *> *)arg1 {
|
||||
%orig;
|
||||
[self removeSponsorMarkers];
|
||||
if ([kWhitelistedChannels containsObject:self.playerViewController.channelID]) {
|
||||
return;
|
||||
}
|
||||
self.sponsorMarkerViews = [NSMutableArray array];
|
||||
for (SponsorSegment *segment in arg1) {
|
||||
CGFloat startTime = segment.startTime;
|
||||
CGFloat endTime = segment.endTime;
|
||||
CGFloat beginX = (startTime * self.frame.size.width) / self.totalTime;
|
||||
CGFloat endX = (endTime * self.frame.size.width) / self.totalTime;
|
||||
CGFloat markerWidth = MAX(endX - beginX, 0);
|
||||
|
||||
UIColor *color;
|
||||
if ([segment.category isEqualToString:@"sponsor"]) color = colorWithHexString([kCategorySettings objectForKey:@"sponsorColor"]);
|
||||
else if ([segment.category isEqualToString:@"intro"]) color = colorWithHexString([kCategorySettings objectForKey:@"introColor"]);
|
||||
else if ([segment.category isEqualToString:@"outro"]) color = colorWithHexString([kCategorySettings objectForKey:@"outroColor"]);
|
||||
else if ([segment.category isEqualToString:@"interaction"]) color = colorWithHexString([kCategorySettings objectForKey:@"interactionColor"]);
|
||||
else if ([segment.category isEqualToString:@"selfpromo"]) color = colorWithHexString([kCategorySettings objectForKey:@"selfpromoColor"]);
|
||||
else if ([segment.category isEqualToString:@"music_offtopic"]) color = colorWithHexString([kCategorySettings objectForKey:@"music_offtopicColor"]);
|
||||
UIView *newMarkerView = [[UIView alloc] initWithFrame:CGRectZero];
|
||||
newMarkerView.backgroundColor = color;
|
||||
[self addSubview:newMarkerView];
|
||||
newMarkerView.translatesAutoresizingMaskIntoConstraints = NO;
|
||||
if (isnan(markerWidth) || !isfinite(beginX)) {
|
||||
return;
|
||||
}
|
||||
[newMarkerView.widthAnchor constraintEqualToConstant:markerWidth].active = YES;
|
||||
[newMarkerView.heightAnchor constraintEqualToConstant:2].active = YES;
|
||||
[newMarkerView.leadingAnchor constraintEqualToAnchor:self.leadingAnchor constant:beginX].active = YES;
|
||||
[newMarkerView.bottomAnchor constraintEqualToAnchor:self.bottomAnchor].active = YES;
|
||||
|
||||
[self.sponsorMarkerViews addObject:newMarkerView];
|
||||
}
|
||||
}
|
||||
|
||||
%new
|
||||
- (void)removeSponsorMarkers {
|
||||
for (UIView *markerView in self.sponsorMarkerViews) {
|
||||
[markerView removeFromSuperview];
|
||||
}
|
||||
self.sponsorMarkerViews = [NSMutableArray array];
|
||||
}
|
||||
%end
|
||||
|
||||
%hook YTSegmentableInlinePlayerBarView
|
||||
%property (strong, nonatomic) NSMutableArray *sponsorMarkerViews;
|
||||
%property (strong, nonatomic) NSMutableArray *skipSegments;
|
||||
%property (strong, nonatomic) YTPlayerViewController *playerViewController;
|
||||
%new
|
||||
- (void)maybeCreateMarkerViewsISB {
|
||||
[self removeSponsorMarkers];
|
||||
self.skipSegments = self.skipSegments;
|
||||
}
|
||||
- (void)setSkipSegments:(NSMutableArray <SponsorSegment *> *)arg1 {
|
||||
%orig;
|
||||
[self removeSponsorMarkers];
|
||||
if ([kWhitelistedChannels containsObject:self.playerViewController.channelID]) {
|
||||
return;
|
||||
}
|
||||
self.sponsorMarkerViews = [NSMutableArray array];
|
||||
UIView *scrubber = [self valueForKey:@"_scrubberCircle"];
|
||||
UIView *referenceView = [[self valueForKey:@"_segmentViews"] firstObject];
|
||||
if (referenceView == nil) return;
|
||||
CGFloat originY = referenceView.frame.origin.y;
|
||||
for (SponsorSegment *segment in arg1) {
|
||||
CGFloat startTime = segment.startTime;
|
||||
CGFloat endTime = segment.endTime;
|
||||
CGFloat beginX = (startTime * self.frame.size.width) / self.totalTime;
|
||||
CGFloat endX = (endTime * self.frame.size.width) / self.totalTime;
|
||||
CGFloat markerWidth = MAX(endX - beginX, 0);
|
||||
|
||||
UIColor *color;
|
||||
if ([segment.category isEqualToString:@"sponsor"]) color = colorWithHexString([kCategorySettings objectForKey:@"sponsorColor"]);
|
||||
else if ([segment.category isEqualToString:@"intro"]) color = colorWithHexString([kCategorySettings objectForKey:@"introColor"]);
|
||||
else if ([segment.category isEqualToString:@"outro"]) color = colorWithHexString([kCategorySettings objectForKey:@"outroColor"]);
|
||||
else if ([segment.category isEqualToString:@"interaction"]) color = colorWithHexString([kCategorySettings objectForKey:@"interactionColor"]);
|
||||
else if ([segment.category isEqualToString:@"selfpromo"]) color = colorWithHexString([kCategorySettings objectForKey:@"selfpromoColor"]);
|
||||
else if ([segment.category isEqualToString:@"music_offtopic"]) color = colorWithHexString([kCategorySettings objectForKey:@"music_offtopicColor"]);
|
||||
|
||||
if (isnan(markerWidth) || !isfinite(beginX)) {
|
||||
return;
|
||||
}
|
||||
|
||||
UIView *newMarkerView = [[UIView alloc] initWithFrame:CGRectMake(beginX, originY, markerWidth, 2)];
|
||||
newMarkerView.userInteractionEnabled = NO;
|
||||
newMarkerView.backgroundColor = color;
|
||||
[self insertSubview:newMarkerView belowSubview:scrubber];
|
||||
[self.sponsorMarkerViews addObject:newMarkerView];
|
||||
}
|
||||
}
|
||||
|
||||
%new
|
||||
- (void)removeSponsorMarkers {
|
||||
for (UIView *markerView in self.sponsorMarkerViews) {
|
||||
[markerView removeFromSuperview];
|
||||
}
|
||||
self.sponsorMarkerViews = [NSMutableArray array];
|
||||
}
|
||||
%end
|
||||
|
||||
|
||||
%hook YTInlinePlayerBarContainerView
|
||||
- (instancetype)initWithScrubbedTimeLabelsDisplayBelowStoryboard:(BOOL)arg1 enableSegmentedProgressView:(BOOL)arg2 {
|
||||
return %orig(arg1, YES);
|
||||
}
|
||||
//does the same thing as the method above on youtube v. 16.0x
|
||||
- (instancetype)initWithEnableSegmentedProgressView:(BOOL)arg1 {
|
||||
return %orig(YES);
|
||||
}
|
||||
- (BOOL)alwaysEnableSegmentedProgressView {
|
||||
return YES;
|
||||
}
|
||||
|
||||
- (void)setPeekableViewVisible:(BOOL)arg1 {
|
||||
%orig;
|
||||
if (kShowModifiedTime && modifiedTimeString && ![self.durationLabel.text containsString:modifiedTimeString]) {
|
||||
NSString *text = [NSString stringWithFormat:@"%@ (%@)", self.durationLabel.text, modifiedTimeString];
|
||||
self.durationLabel.text = text;
|
||||
[self.durationLabel sizeToFit];
|
||||
}
|
||||
}
|
||||
|
||||
//thanks @iCraze >>
|
||||
%new
|
||||
- (id)playerBar {
|
||||
return [self segmentablePlayerBar];
|
||||
}
|
||||
%end
|
||||
|
||||
%hook YTNGWatchLayerViewController
|
||||
|
||||
- (void)didCompleteFullscreenDismissAnimation {
|
||||
%orig;
|
||||
YTPlayerView *playerView = (YTPlayerView *)self.playerViewController.view;
|
||||
YTMainAppVideoPlayerOverlayView *overlayView = (YTMainAppVideoPlayerOverlayView *)playerView.overlayView;
|
||||
if (!self.playerViewController.isPlayingAd && overlayView.controlsOverlayView.isDisplayingSponsorBlockViewController && [overlayView isKindOfClass:%c(YTMainAppVideoPlayerOverlayView)]) {
|
||||
[overlayView.controlsOverlayView presentSponsorBlockViewController];
|
||||
}
|
||||
}
|
||||
%end
|
||||
|
||||
//For newer versions of YT the class name changed
|
||||
%hook YTWatchLayerViewController
|
||||
|
||||
- (void)didCompleteFullscreenDismissAnimation {
|
||||
%orig;
|
||||
YTPlayerView *playerView = (YTPlayerView *)self.playerViewController.view;
|
||||
YTMainAppVideoPlayerOverlayView *overlayView = (YTMainAppVideoPlayerOverlayView *)playerView.overlayView;
|
||||
if (!self.playerViewController.isPlayingAd && overlayView.controlsOverlayView.isDisplayingSponsorBlockViewController && [overlayView isKindOfClass:%c(YTMainAppVideoPlayerOverlayView)]) {
|
||||
[overlayView.controlsOverlayView presentSponsorBlockViewController];
|
||||
}
|
||||
}
|
||||
%end
|
||||
|
||||
|
||||
%hook YTPlayerView
|
||||
//https://stackoverflow.com/questions/11770743/capturing-touches-on-a-subview-outside-the-frame-of-its-superview-using-hittest
|
||||
- (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event {
|
||||
if (self.clipsToBounds || self.hidden || self.alpha == 0) {
|
||||
return nil;
|
||||
}
|
||||
|
||||
for (UIView *subview in self.subviews.reverseObjectEnumerator) {
|
||||
CGPoint subPoint = [subview convertPoint:point fromView:self];
|
||||
UIView *result = [subview hitTest:subPoint withEvent:event];
|
||||
if (result) return result;
|
||||
}
|
||||
return nil;
|
||||
}
|
||||
%end
|
||||
%end
|
||||
|
||||
%group Cercube
|
||||
//ew global variables
|
||||
NSArray <SponsorSegment *> *skipSegments;
|
||||
AVQueuePlayer *queuePlayer;
|
||||
|
||||
%hook CADownloadObject
|
||||
+ (id)modelWithMetadata:(id)arg1 format:(id)arg2 context:(id)arg3 type:(id)arg4 audioOnly:(_Bool)arg5 directory:(id)arg6 {
|
||||
CADownloadObject *downloadObject = %orig;
|
||||
[SponsorBlockRequest getSponsorTimes:downloadObject.videoId completionTarget:downloadObject completionSelector:@selector(setSkipSegments:) apiInstance:kAPIInstance];
|
||||
return downloadObject;
|
||||
}
|
||||
|
||||
%new
|
||||
- (void)setSkipSegments:(NSMutableArray <SponsorSegment *> *)skipSegments {
|
||||
NSString *path = [self.filePath.stringByDeletingLastPathComponent stringByAppendingPathComponent:[[self.fileName stringByDeletingPathExtension] stringByAppendingPathExtension:@"plist"]];
|
||||
[[NSFileManager defaultManager] createFileAtPath:path contents:nil attributes:nil];
|
||||
NSMutableArray *segments = [NSMutableArray array];
|
||||
for (SponsorSegment *segment in skipSegments) {
|
||||
[segments addObject:@{
|
||||
@"startTime" : @(segment.startTime),
|
||||
@"endTime" : @(segment.endTime),
|
||||
@"category" : segment.category,
|
||||
@"UUID" : segment.UUID
|
||||
}];
|
||||
}
|
||||
NSDictionary *dict = @{
|
||||
@"skipSegments" : segments
|
||||
};
|
||||
[dict writeToURL:[NSURL fileURLWithPath:path isDirectory:NO] error:nil];
|
||||
}
|
||||
%end
|
||||
%hook AVPlayerViewController
|
||||
- (void)viewDidLoad {
|
||||
%orig;
|
||||
[(AVQueuePlayer *)self.player setPlayerViewController:self];
|
||||
}
|
||||
%end
|
||||
|
||||
%hook AVScrubber
|
||||
//this is bad but i don't feel like finding a better way
|
||||
- (void)layoutSubviews {
|
||||
%orig;
|
||||
[queuePlayer updateMarkerViews];
|
||||
}
|
||||
%end
|
||||
|
||||
%hook AVQueuePlayer
|
||||
%property (strong, nonatomic) NSMutableArray *skipSegments;
|
||||
%property (nonatomic, assign) NSInteger currentSponsorSegment;
|
||||
%property (strong, nonatomic) MBProgressHUD *hud;
|
||||
%property (nonatomic, assign) NSInteger unskippedSegment;
|
||||
%property (nonatomic, assign) BOOL isSeeking;
|
||||
%property (nonatomic, assign) NSInteger currentPlayerItem;
|
||||
%property (strong, nonatomic) id timeObserver;
|
||||
%property (strong, nonatomic) AVPlayerViewController *playerViewController;
|
||||
%property (strong, nonatomic) NSMutableArray *markerViews;
|
||||
%property (nonatomic, assign) BOOL hudDisplayed;
|
||||
- (instancetype)initWithItems:(NSArray<AVPlayerItem *> *)items {
|
||||
self.currentPlayerItem = 0;
|
||||
queuePlayer = self;
|
||||
return %orig;
|
||||
}
|
||||
- (void)seekToTime:(CMTime)time {
|
||||
%orig;
|
||||
self.isSeeking = YES;
|
||||
[NSTimer scheduledTimerWithTimeInterval:1.0f repeats:NO block:^(NSTimer *timer) {
|
||||
self.isSeeking = NO;
|
||||
}];
|
||||
}
|
||||
- (void)_itemIsReadyToPlay:(id)arg1 {
|
||||
%orig;
|
||||
self.isSeeking = NO;
|
||||
[self sponsorBlockSetup];
|
||||
}
|
||||
- (void)_advanceCurrentItemAccordingToFigPlaybackItem:(id)arg1 {
|
||||
%orig;
|
||||
if (self.currentPlayerItem + 1 < [self items].count) self.currentPlayerItem ++;
|
||||
}
|
||||
- (void)_removeItem:(id)arg1 {
|
||||
%orig;
|
||||
[self removeTimeObserver:self.timeObserver];
|
||||
self.timeObserver = nil;
|
||||
if (self.currentPlayerItem != 0) self.currentPlayerItem --;
|
||||
[self sponsorBlockSetup];
|
||||
}
|
||||
%new
|
||||
- (void)updateMarkerViews {
|
||||
if (self.skipSegments.count > 0) {
|
||||
CGFloat totalTime = [@([self items][self.currentPlayerItem].duration.value) floatValue] / [self items][self.currentPlayerItem].duration.timescale;
|
||||
for (UIView *markerView in self.markerViews) {
|
||||
AVScrubber *scrubber = self.playerViewController.contentView.playbackControlsView.scrubber;
|
||||
CGFloat startTime = self.skipSegments[[self.markerViews indexOfObject:markerView]].startTime;
|
||||
CGFloat endTime = self.skipSegments[[self.markerViews indexOfObject:markerView]].endTime;
|
||||
CGFloat beginX = (startTime * scrubber.frame.size.width) / totalTime;
|
||||
CGFloat endX = (endTime * scrubber.frame.size.width) / totalTime;
|
||||
CGFloat markerWidth = endX - beginX;
|
||||
markerView.frame = CGRectMake(beginX, scrubber.frame.size.height/2-2, markerWidth, 5);
|
||||
}
|
||||
}
|
||||
}
|
||||
%new
|
||||
- (void)sponsorBlockSetup {
|
||||
if ([self items].count <= 0) return;
|
||||
NSString *path = [[[[self items][self.currentPlayerItem] _URL].path stringByDeletingPathExtension] stringByAppendingPathExtension:@"plist"];
|
||||
NSDictionary *segmentsDict = [NSDictionary dictionaryWithContentsOfFile:path];
|
||||
NSDictionary *segments = [segmentsDict objectForKey:@"skipSegments"];
|
||||
self.skipSegments = [NSMutableArray array];
|
||||
CGFloat totalTime = [@([self items][self.currentPlayerItem].duration.value) floatValue] / [self items][self.currentPlayerItem].duration.timescale;
|
||||
for (UIView *markerView in self.markerViews) {
|
||||
[markerView removeFromSuperview];
|
||||
}
|
||||
self.markerViews = [NSMutableArray array];
|
||||
for (NSDictionary *dict in segments) {
|
||||
SponsorSegment *segment = [[SponsorSegment alloc] initWithStartTime:[[dict objectForKey:@"startTime"] floatValue] endTime:[[dict objectForKey:@"endTime"] floatValue] category:[dict objectForKey:@"category"] UUID:[dict objectForKey:@"UUID"]];
|
||||
[self.skipSegments addObject:segment];
|
||||
AVScrubber *scrubber = self.playerViewController.contentView.playbackControlsView.scrubber;
|
||||
CGFloat startTime = segment.startTime;
|
||||
CGFloat endTime = segment.endTime;
|
||||
CGFloat beginX = (startTime * scrubber.frame.size.width) / totalTime;
|
||||
CGFloat endX = (endTime * scrubber.frame.size.width) / totalTime;
|
||||
CGFloat markerWidth = endX - beginX;
|
||||
UIView *markerView = [[UIView alloc] initWithFrame:CGRectMake(beginX, 2, markerWidth, 5)];
|
||||
|
||||
if ([segment.category isEqualToString:@"sponsor"]) markerView.backgroundColor = colorWithHexString([kCategorySettings objectForKey:@"sponsorColor"]);
|
||||
else if ([segment.category isEqualToString:@"intro"]) markerView.backgroundColor = colorWithHexString([kCategorySettings objectForKey:@"introColor"]);
|
||||
else if ([segment.category isEqualToString:@"outro"]) markerView.backgroundColor = colorWithHexString([kCategorySettings objectForKey:@"outroColor"]);
|
||||
else if ([segment.category isEqualToString:@"interaction"]) markerView.backgroundColor = colorWithHexString([kCategorySettings objectForKey:@"interactionColor"]);
|
||||
else if ([segment.category isEqualToString:@"selfpromo"]) markerView.backgroundColor = colorWithHexString([kCategorySettings objectForKey:@"selfpromoColor"]);
|
||||
else if ([segment.category isEqualToString:@"music_offtopic"]) markerView.backgroundColor = colorWithHexString([kCategorySettings objectForKey:@"music_offtopicColor"]);
|
||||
[scrubber addSubview:markerView];
|
||||
[self.markerViews addObject:markerView];
|
||||
}
|
||||
skipSegments = self.skipSegments;
|
||||
self.currentSponsorSegment = 0;
|
||||
self.unskippedSegment = -1;
|
||||
CMTime timeInterval = CMTimeMake(1,10);
|
||||
__weak AVQueuePlayer *weakSelf = self;
|
||||
[self removeTimeObserver:self.timeObserver];
|
||||
self.timeObserver = nil;
|
||||
|
||||
self.timeObserver = [self addPeriodicTimeObserverForInterval:timeInterval queue:nil usingBlock:^(CMTime time) {
|
||||
CGFloat timeFloat = [@(time.value) floatValue] / time.timescale;
|
||||
if (weakSelf.skipSegments.count > 0) {
|
||||
SponsorSegment *sponsorSegment = [[SponsorSegment alloc] initWithStartTime:-1 endTime:-1 category:nil UUID:nil];
|
||||
if (weakSelf.currentSponsorSegment <= weakSelf.skipSegments.count-1) {
|
||||
sponsorSegment = weakSelf.skipSegments[weakSelf.currentSponsorSegment];
|
||||
}
|
||||
else if (weakSelf.unskippedSegment != weakSelf.currentSponsorSegment-1) {
|
||||
sponsorSegment = weakSelf.skipSegments[weakSelf.currentSponsorSegment-1];
|
||||
}
|
||||
|
||||
if ((lroundf(timeFloat) == ceil(sponsorSegment.startTime) && timeFloat >= sponsorSegment.startTime) || (lroundf(timeFloat) >= ceil(sponsorSegment.startTime) && timeFloat < sponsorSegment.endTime)) {
|
||||
if ([[kCategorySettings objectForKey:sponsorSegment.category] intValue] == 3) {
|
||||
if (weakSelf.hud.superview != weakSelf.playerViewController.view && weakSelf.hudDisplayed == NO) {
|
||||
weakSelf.hud = [MBProgressHUD showHUDAddedTo:weakSelf.playerViewController.view animated:YES];
|
||||
weakSelf.hudDisplayed = YES; // Set yes to make sure that HUD is not persistent (Issue #62)
|
||||
weakSelf.hud.mode = MBProgressHUDModeCustomView;
|
||||
NSString *localizedSegment = categoryLocalization[sponsorSegment.category] ?: sponsorSegment.category;
|
||||
NSString *localizedManualSkip = LOC(@"ManuallySkipReminder");
|
||||
NSString *formattedManualSkip = [NSString stringWithFormat:localizedManualSkip, localizedSegment, lroundf(sponsorSegment.startTime)/60, lroundf(sponsorSegment.startTime)%60, lroundf(sponsorSegment.endTime)/60, lroundf(sponsorSegment.endTime)%60];
|
||||
weakSelf.hud.label.text = formattedManualSkip;
|
||||
weakSelf.hud.label.numberOfLines = 0;
|
||||
[weakSelf.hud.button setTitle:LOC(@"Skip") forState:UIControlStateNormal];
|
||||
[weakSelf.hud.button addTarget:weakSelf action:@selector(manuallySkipSegment:) forControlEvents:UIControlEventTouchUpInside];
|
||||
// Add custom button to hide HUD
|
||||
UIButton *cancelButton = [UIButton buttonWithType:UIButtonTypeSystem];
|
||||
UIImage *cancelImage = [[UIImage systemImageNamed:@"x.circle"] imageWithRenderingMode:UIImageRenderingModeAlwaysTemplate];
|
||||
[cancelButton setImage:cancelImage forState:UIControlStateNormal];
|
||||
[cancelButton setTintColor:[[UIColor blackColor] colorWithAlphaComponent:0.7]];
|
||||
[cancelButton addTarget:weakSelf action:@selector(cancelHUD:) forControlEvents:UIControlEventTouchUpInside];
|
||||
|
||||
UIView *buttonSuperview = weakSelf.hud.button.superview;
|
||||
[buttonSuperview addSubview:cancelButton];
|
||||
|
||||
CGFloat buttonSpacing = 10.0;
|
||||
cancelButton.translatesAutoresizingMaskIntoConstraints = NO;
|
||||
[NSLayoutConstraint activateConstraints:@[
|
||||
[cancelButton.topAnchor constraintEqualToAnchor:weakSelf.hud.button.topAnchor],
|
||||
[cancelButton.leadingAnchor constraintEqualToAnchor:weakSelf.hud.button.trailingAnchor constant:buttonSpacing],
|
||||
[cancelButton.heightAnchor constraintEqualToAnchor:weakSelf.hud.button.heightAnchor]
|
||||
]];
|
||||
weakSelf.hud.offset = CGPointMake(weakSelf.playerViewController.view.frame.size.width, -MBProgressMaxOffset);
|
||||
double delayInSeconds = sponsorSegment.endTime - sponsorSegment.startTime;
|
||||
|
||||
// Use a delay equal to the length of the sponsored segment to avoid HUD call
|
||||
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(delayInSeconds * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
|
||||
[MBProgressHUD hideHUDForView:weakSelf.playerViewController.view animated:YES]; // Hide HUD if user is not interacting with buttons
|
||||
weakSelf.hudDisplayed = NO; // Reset flag to make it work for the next segment
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
else if (sponsorSegment.endTime > totalTime) {
|
||||
[weakSelf seekToTime:CMTimeMake(totalTime,1) toleranceBefore:kCMTimeZero toleranceAfter:kCMTimeZero];
|
||||
}
|
||||
else {
|
||||
[weakSelf seekToTime:CMTimeMake(sponsorSegment.endTime,1)];
|
||||
}
|
||||
|
||||
if ([[kCategorySettings objectForKey:sponsorSegment.category] intValue] == 1) {
|
||||
if (weakSelf.hud.superview != weakSelf.playerViewController.view && kShowSkipNotice) {
|
||||
[MBProgressHUD hideHUDForView:weakSelf.playerViewController.view animated:YES];
|
||||
weakSelf.hud = [MBProgressHUD showHUDAddedTo:weakSelf.playerViewController.view animated:YES];
|
||||
weakSelf.hud.mode = MBProgressHUDModeCustomView;
|
||||
// Translate and add segment name to the skipped HUD (issue #70)
|
||||
NSString *localizedSegment = categoryLocalization[sponsorSegment.category] ?: sponsorSegment.category;
|
||||
weakSelf.hud.label.text = [NSString stringWithFormat:LOC(@"SkippedSegment"), localizedSegment];
|
||||
weakSelf.hud.label.numberOfLines = 0;
|
||||
[weakSelf.hud.button setTitle:LOC(@"Unskip") forState:UIControlStateNormal];
|
||||
[weakSelf.hud.button addTarget:weakSelf action:@selector(unskipSegment:) forControlEvents:UIControlEventTouchUpInside];
|
||||
weakSelf.hud.offset = CGPointMake(weakSelf.playerViewController.view.frame.size.width, -MBProgressMaxOffset);
|
||||
[weakSelf.hud hideAnimated:YES afterDelay:kSkipNoticeDuration];
|
||||
|
||||
// Play sound effect if option enabled
|
||||
if (kSkipAudioNotification) {
|
||||
playSponsorAudio();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (weakSelf.currentSponsorSegment <= weakSelf.skipSegments.count-1) weakSelf.currentSponsorSegment ++;
|
||||
}
|
||||
else if (lroundf(timeFloat) > sponsorSegment.startTime && weakSelf.currentSponsorSegment < weakSelf.skipSegments.count-1) {
|
||||
weakSelf.currentSponsorSegment ++;
|
||||
}
|
||||
else if (weakSelf.currentSponsorSegment == 0 && weakSelf.unskippedSegment != -1) {
|
||||
weakSelf.currentSponsorSegment ++;
|
||||
}
|
||||
else if (weakSelf.currentSponsorSegment > 0 && lroundf(timeFloat) < weakSelf.skipSegments[weakSelf.currentSponsorSegment-1].startTime-0.01) {
|
||||
if (weakSelf.unskippedSegment != weakSelf.currentSponsorSegment-1) {
|
||||
weakSelf.currentSponsorSegment--;
|
||||
}
|
||||
else if (timeFloat < weakSelf.skipSegments[weakSelf.currentSponsorSegment-1].startTime-0.01) {
|
||||
weakSelf.unskippedSegment = -1;
|
||||
}
|
||||
}
|
||||
}
|
||||
}];
|
||||
}
|
||||
%new
|
||||
- (void)unskipSegment:(UIButton *)sender {
|
||||
if (self.currentSponsorSegment > 0) {
|
||||
[self seekToTime:CMTimeMake(self.skipSegments[self.currentSponsorSegment-1].startTime,1)];
|
||||
self.unskippedSegment = self.currentSponsorSegment-1;
|
||||
} else {
|
||||
[self seekToTime:CMTimeMake(self.skipSegments[self.currentSponsorSegment].startTime,1)];
|
||||
self.unskippedSegment = self.currentSponsorSegment;
|
||||
}
|
||||
[MBProgressHUD hideHUDForView:self.playerViewController.view animated:YES];
|
||||
}
|
||||
%end
|
||||
%end
|
||||
|
||||
%group JustSettings
|
||||
NSInteger pageStyle = 0;
|
||||
%hook YTRightNavigationButtons
|
||||
%property (retain, nonatomic) YTQTMButton *sponsorBlockButton;
|
||||
- (NSMutableArray *)buttons {
|
||||
NSMutableArray *retVal = %orig.mutableCopy;
|
||||
[self.sponsorBlockButton removeFromSuperview];
|
||||
[self addSubview:self.sponsorBlockButton];
|
||||
if (!self.sponsorBlockButton || pageStyle != [%c(YTPageStyleController) pageStyle]) {
|
||||
self.sponsorBlockButton = [%c(YTQTMButton) iconButton];
|
||||
self.sponsorBlockButton.frame = CGRectMake(0, 0, 40, 40);
|
||||
|
||||
if ([%c(YTPageStyleController) pageStyle]) { //dark mode
|
||||
[self.sponsorBlockButton setImage:[UIImage imageWithContentsOfFile:[tweakBundle pathForResource:@"sponsorblocksettings-20@2x" ofType:@"png"]] forState:UIControlStateNormal];
|
||||
}
|
||||
else { //light mode
|
||||
UIImage *image = [UIImage imageWithContentsOfFile:[tweakBundle pathForResource:@"sponsorblocksettings-20@2x" ofType:@"png"]];
|
||||
image = [image imageWithTintColor:UIColor.blackColor renderingMode:UIImageRenderingModeAlwaysTemplate];
|
||||
[self.sponsorBlockButton setImage:image forState:UIControlStateNormal];
|
||||
[self.sponsorBlockButton setTintColor:UIColor.blackColor];
|
||||
}
|
||||
pageStyle = [%c(YTPageStyleController) pageStyle];
|
||||
|
||||
[self.sponsorBlockButton addTarget:self action:@selector(sponsorBlockButtonPressed:) forControlEvents:UIControlEventTouchUpInside];
|
||||
[retVal insertObject:self.sponsorBlockButton atIndex:0];
|
||||
}
|
||||
return retVal;
|
||||
}
|
||||
- (NSMutableArray *)visibleButtons {
|
||||
NSMutableArray *retVal = %orig.mutableCopy;
|
||||
|
||||
//fixes button overlapping yt logo on smaller devices
|
||||
[self setLeadingPadding:-10];
|
||||
if (self.sponsorBlockButton) {
|
||||
[self.sponsorBlockButton removeFromSuperview];
|
||||
[self addSubview:self.sponsorBlockButton];
|
||||
[retVal insertObject:self.sponsorBlockButton atIndex:0];
|
||||
}
|
||||
return retVal;
|
||||
}
|
||||
%new
|
||||
- (void)sponsorBlockButtonPressed:(UIButton *)sender {
|
||||
SponsorBlockSettingsController *settingsController = [[SponsorBlockSettingsController alloc] init];
|
||||
UINavigationController *navigationController = [[UINavigationController alloc] initWithRootViewController:settingsController];
|
||||
[[[UIApplication sharedApplication] delegate].window.rootViewController presentViewController:navigationController animated:YES completion:nil];
|
||||
}
|
||||
%end
|
||||
%end
|
||||
|
||||
static void loadPrefs() {
|
||||
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
|
||||
NSString *documentsDirectory = [paths objectAtIndex:0];
|
||||
NSString *path = [documentsDirectory stringByAppendingPathComponent:@"iSponsorBlock.plist"];
|
||||
NSMutableDictionary *settings = [NSMutableDictionary dictionary];
|
||||
[settings addEntriesFromDictionary:[NSDictionary dictionaryWithContentsOfFile:path]];
|
||||
kIsEnabled = [settings objectForKey:@"enabled"] ? [[settings objectForKey:@"enabled"] boolValue] : YES;
|
||||
|
||||
kUserID = [settings objectForKey:@"userID"] ? [settings objectForKey:@"userID"] : [[NSUUID UUID] UUIDString];
|
||||
// reset to uuid if user set to an empty string
|
||||
if ([kUserID isEqualToString:@""]) kUserID = [[NSUUID UUID] UUIDString];
|
||||
|
||||
kAPIInstance = [settings objectForKey:@"apiInstance"] ? [settings objectForKey:@"apiInstance"] : @"https://sponsor.ajay.app/api";
|
||||
// reset to official if user set to an empty string
|
||||
if ([kAPIInstance isEqualToString:@""]) kAPIInstance = @"https://sponsor.ajay.app/api";
|
||||
|
||||
kCategorySettings = [settings objectForKey:@"categorySettings"] ? [settings objectForKey:@"categorySettings"] : @{
|
||||
@"sponsor" : @1,
|
||||
@"sponsorColor" : hexFromUIColor(UIColor.greenColor),
|
||||
@"intro" : @0,
|
||||
@"introColor" : hexFromUIColor(UIColor.systemTealColor),
|
||||
@"outro" : @0,
|
||||
@"outroColor" : hexFromUIColor(UIColor.blueColor),
|
||||
@"interaction" : @0,
|
||||
@"interactionColor" : hexFromUIColor(UIColor.systemPinkColor),
|
||||
@"selfpromo" : @0,
|
||||
@"selfpromoColor" : hexFromUIColor(UIColor.yellowColor),
|
||||
@"music_offtopic" : @0,
|
||||
@"music_offtopicColor" : hexFromUIColor(UIColor.orangeColor)
|
||||
};
|
||||
kMinimumDuration = [settings objectForKey:@"minimumDuration"] ? [[settings objectForKey:@"minimumDuration"] floatValue] : 0.0f;
|
||||
kShowSkipNotice = [settings objectForKey:@"showSkipNotice"] ? [[settings objectForKey:@"showSkipNotice"] boolValue] : YES;
|
||||
kShowButtonsInPlayer = [settings objectForKey:@"showButtonsInPlayer"] ? [[settings objectForKey:@"showButtonsInPlayer"] boolValue] : YES;
|
||||
kHideStartEndButtonInPlayer = [settings objectForKey:@"hideStartEndButtonInPlayer"] ? [[settings objectForKey:@"hideStartEndButtonInPlayer"] boolValue] : NO;
|
||||
kShowModifiedTime = [settings objectForKey:@"showModifiedTime"] ? [[settings objectForKey:@"showModifiedTime"] boolValue] : YES;
|
||||
kSkipAudioNotification = [settings objectForKey:@"skipAudioNotification"] ? [[settings objectForKey:@"skipAudioNotification"] boolValue] : NO;
|
||||
kEnableSkipCountTracking = [settings objectForKey:@"enableSkipCountTracking"] ? [[settings objectForKey:@"enableSkipCountTracking"] boolValue] : YES;
|
||||
kSkipNoticeDuration = [settings objectForKey:@"skipNoticeDuration"] ? [[settings objectForKey:@"skipNoticeDuration"] floatValue] : 3.0f;
|
||||
kWhitelistedChannels = [settings objectForKey:@"whitelistedChannels"] ? [(NSArray *)[settings objectForKey:@"whitelistedChannels"] mutableCopy] : [NSMutableArray array];
|
||||
|
||||
NSDictionary *newSettings = @{
|
||||
@"enabled" : @(kIsEnabled),
|
||||
@"userID" : kUserID,
|
||||
@"apiInstance" : kAPIInstance,
|
||||
@"categorySettings" : kCategorySettings,
|
||||
@"minimumDuration" : @(kMinimumDuration),
|
||||
@"showSkipNotice" : @(kShowSkipNotice),
|
||||
@"showButtonsInPlayer" : @(kShowButtonsInPlayer),
|
||||
@"hideStartEndButtonInPlayer" : @(kHideStartEndButtonInPlayer),
|
||||
@"showModifiedTime" : @(kShowModifiedTime),
|
||||
@"skipAudioNotification" : @(kSkipAudioNotification),
|
||||
@"enableSkipCountTracking" : @(kEnableSkipCountTracking),
|
||||
@"skipNoticeDuration" : @(kSkipNoticeDuration),
|
||||
@"whitelistedChannels" : kWhitelistedChannels
|
||||
};
|
||||
if (![newSettings isEqualToDictionary:settings]) {
|
||||
[newSettings writeToURL:[NSURL fileURLWithPath:path isDirectory:NO] error:nil];
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
%group LateLoad
|
||||
|
||||
%hook YTAppDelegate
|
||||
|
||||
- (BOOL)application:(id)arg1 didFinishLaunchingWithOptions:(id)arg2 {
|
||||
BOOL orig = %orig;
|
||||
loadPrefs();
|
||||
if (kIsEnabled) {
|
||||
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
|
||||
NSString *documentsDirectory = [paths objectAtIndex:0];
|
||||
if (dlopen(ROOT_PATH("/Library/MobileSubstrate/DynamicLibraries/Cercube.dylib"), RTLD_LAZY)) {
|
||||
%init(Cercube)
|
||||
NSString *downloadsDirectory = [documentsDirectory stringByAppendingPathComponent:@"Carida_Files"];
|
||||
NSArray *files = [[NSFileManager defaultManager] contentsOfDirectoryAtPath:downloadsDirectory error:nil];
|
||||
for (NSString *path in files) {
|
||||
if ([path.pathExtension isEqualToString:@"plist"]) {
|
||||
NSString *mp4Path = [downloadsDirectory stringByAppendingPathComponent:[[path stringByDeletingPathExtension] stringByAppendingPathExtension:@"mp4"]];
|
||||
BOOL fileExists = [[NSFileManager defaultManager] fileExistsAtPath:mp4Path];
|
||||
if (!fileExists) {
|
||||
[[NSFileManager defaultManager] removeItemAtPath:[downloadsDirectory stringByAppendingPathComponent:path] error:nil];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
%init(Main);
|
||||
}
|
||||
return orig;
|
||||
}
|
||||
|
||||
%end
|
||||
|
||||
%end
|
||||
|
||||
static void prefsChanged(CFNotificationCenterRef center, void *observer, CFStringRef name, const void *object, CFDictionaryRef userInfo) {
|
||||
loadPrefs();
|
||||
}
|
||||
|
||||
%ctor {
|
||||
CFNotificationCenterAddObserver(CFNotificationCenterGetDarwinNotifyCenter(), NULL, (CFNotificationCallback)prefsChanged, CFSTR("com.galacticdev.isponsorblockprefs.changed"), NULL, CFNotificationSuspensionBehaviorCoalesce);
|
||||
%init(LateLoad);
|
||||
%init(JustSettings);
|
||||
}
|
||||
|
||||
%dtor {
|
||||
if (dlopen(ROOT_PATH("/Library/MobileSubstrate/DynamicLibraries/Cercube.dylib"), RTLD_LAZY)) {
|
||||
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
|
||||
NSString *documentsDirectory = [paths objectAtIndex:0];
|
||||
NSString *downloadsDirectory = [documentsDirectory stringByAppendingPathComponent:@"Carida_Files"];
|
||||
NSArray *files = [[NSFileManager defaultManager] contentsOfDirectoryAtPath:downloadsDirectory error:nil];
|
||||
for (NSString *path in files) {
|
||||
if ([path.pathExtension isEqualToString:@"plist"]) {
|
||||
NSString *mp4Path = [downloadsDirectory stringByAppendingPathComponent:[[path stringByDeletingPathExtension] stringByAppendingPathExtension:@"mp4"]];
|
||||
BOOL fileExists = [[NSFileManager defaultManager] fileExistsAtPath:mp4Path];
|
||||
if (!fileExists) {
|
||||
[[NSFileManager defaultManager] removeItemAtPath:[downloadsDirectory stringByAppendingPathComponent:path] error:nil];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Binary file not shown.
Binary file not shown.
|
After Width: | Height: | Size: 6.2 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 1.7 KiB |
Binary file not shown.
@@ -0,0 +1,86 @@
|
||||
/* iSponsorBlock Settings */
|
||||
"Enabled" = "Enabled";
|
||||
"RestartFooter" = "Restart YouTube for changes to take effect";
|
||||
|
||||
"Sponsor" = "Sponsor";
|
||||
"Intermission/IntroAnimation" = "Intermission/Intro Animation";
|
||||
"Intermission" = "Intermission";
|
||||
"Endcards/Credits" = "Endcards/Credits";
|
||||
"Outro" = "Outro";
|
||||
"InteractionReminder" = "Interaction Reminder (Subscribe)";
|
||||
"InteractionReminder_Subcribe/Like" = "Interaction Reminder (Subcribe/Like)";
|
||||
"Interaction" = "Interaction";
|
||||
"Unpaid/SelfPromotion" = "Unpaid/Self Promotion";
|
||||
"SelfPromo" = "Self Promo";
|
||||
"Non-MusicSection" = "Music: Non-Music Section";
|
||||
"Non-Music" = "Non-Music";
|
||||
"SponsorBlockUserID" = "SponsorBlock User ID";
|
||||
"SponsorBlockAPIInstance" = "SponsorBlock API Instance";
|
||||
|
||||
"Disable" = "Disable";
|
||||
"AutoSkip" = "Auto Skip";
|
||||
"ShowInSeekBar" = "Show in Seek Bar";
|
||||
"ManualSkip" = "Manual Skip";
|
||||
"SetColorToShowInSeekBar" = "Set Color To Show in Seek Bar";
|
||||
|
||||
"UserID" = "User ID:";
|
||||
"UserIDFooter" = "If you want to use a custom SponsorBlock user ID, you can enter it here. If you don't know what this is, leave it as default.";
|
||||
"API_URL" = "API URL:";
|
||||
"APIFooter" = "If you want to use a custom SponsorBlock API instance, you can enter it here. If you don't know what this is, leave it as default. Example:https://sponsor.ajay.app/api";
|
||||
"MinimumSegmentDuration" = "Set Minimum Segment Duration:";
|
||||
"HowLongNoticeWillAppear" = "Set How Long Skip Notice Will Appear:";
|
||||
"ShowSkipNotice" = "Show Skip Notice";
|
||||
"ShowButtonsInPlayer" = "Show iSponsorBlock Buttons in Video Player";
|
||||
"HideStartEndButtonInPlayer" = "Hide Start/End Button in Video Player";
|
||||
"ShowModifiedTime" = "Show Modified Time in Seek Bar";
|
||||
"AudioNotificationOnSkip" = "Audio Notification on Skip";
|
||||
"AudioFooter" = "Unavailable in silent mode";
|
||||
"EnableSkipCountTracking" = "Enable Skip Count Tracking";
|
||||
"DonateOnVenmo" = "Donate on Venmo";
|
||||
"DonateOnPayPal" = "Donate on PayPal";
|
||||
|
||||
/* Segments stuff */
|
||||
"SegmentStartsNow" = "Segment Starts Now";
|
||||
"SegmentEndsNow" = "Segment Ends Now";
|
||||
"WhitelistChannel" = "Whitelist Channel";
|
||||
"SegmentsInDatabase" = "There are already segments in the database:";
|
||||
"YourSegments" = "Your Segments:";
|
||||
"SubmitSegments" = "Submit Segments";
|
||||
"SuccessfullySubmittedSegments" = "Successfully Submitted Segments";
|
||||
"EndTimeLessThanStartTime" = "End Time That You Set Was Less Than the Start Time, Please Select a Time After"; //Example: ..., Please Select a Time After 10:15
|
||||
"UnfinishedSegments" = "You Have Unfinished Segments\n Please Add a Category and/or End Time to Your Segments";
|
||||
"EditStartTime" = "Edit Start Time";
|
||||
"EditStartTime_Desc" = "Edit Start Time: (ex. type 1:15)";
|
||||
"EditEndTime" = "Edit End Time";
|
||||
"EditEndTime_Desc" = "Edit End Time: (ex. type 1:15)";
|
||||
"From" = "From"; // Time. Example: From 1:15 to 3:00. If you dont need it, just leave empty
|
||||
"to" = "to"; // Time. Example: From 1:15 to 3:00
|
||||
"EditCategory" = "Edit Category";
|
||||
"EditSegment" = "Edit Segment";
|
||||
"Edit" = "Edit";
|
||||
"Error" = "Error";
|
||||
"ErrorCode" = "Error Code";
|
||||
"Success" = "Success";
|
||||
"OK" = "OK";
|
||||
"Cancel" = "Cancel";
|
||||
"Delete" = "Delete";
|
||||
"Upvote" = "Upvote";
|
||||
"Downvote" = "Downvote";
|
||||
"VoteToChangeCategory" = "Vote to Change Category";
|
||||
"VoteOnSegment" = "Vote on Segment";
|
||||
"SuccessfullyVoted" = "Successfully Voted";
|
||||
"ErrorVoting" = "Error Voting";
|
||||
|
||||
/* Pop-ups*/
|
||||
"ManuallySkipReminder" = "Manually Skip %@ segment\nfrom %ld:%02ld to %ld:%02ld?";
|
||||
"Skip" = "Skip";
|
||||
"SkippedSegment" = "Skipped %@ Segment";
|
||||
"Unskip" = "Unskip";
|
||||
|
||||
/* Segments in pop-ups*/
|
||||
"sponsor" = "sponsor";
|
||||
"intro" = "intro";
|
||||
"outro" = "outro";
|
||||
"interaction" = "interaction";
|
||||
"selfpromo" = "self promo";
|
||||
"music_offtopic" = "non-music";
|
||||
@@ -0,0 +1,86 @@
|
||||
/* iSponsorBlock Settings */
|
||||
"Enabled" = "Активировать iSponsorBlock";
|
||||
"RestartFooter" = "Для применения настроек перезапустите YouTube";
|
||||
|
||||
"Sponsor" = "Спонсорская реклама";
|
||||
"Intermission/IntroAnimation" = "Вступление/Интервалы";
|
||||
"Intermission" = "Вступление";
|
||||
"Endcards/Credits" = "Конечные заставки/Титры";
|
||||
"Outro" = "Концовка";
|
||||
"InteractionReminder" = "Взаимодействие (Подписаться)";
|
||||
"InteractionReminder_Subcribe/Like" = "Взаимодействие (Лайк/Подписка)";
|
||||
"Interaction" = "Взаимодействие";
|
||||
"Unpaid/SelfPromotion" = "Самореклама";
|
||||
"SelfPromo" = "Самореклама";
|
||||
"Non-MusicSection" = "Музыка: Сегмент без музыки";
|
||||
"Non-Music" = "Сегмент без музыки";
|
||||
"SponsorBlockUserID" = "Идентификатор SponsorBlock";
|
||||
"SponsorBlockAPIInstance" = "Используемый API";
|
||||
|
||||
"Disable" = "Выкл";
|
||||
"AutoSkip" = "Перематывать";
|
||||
"ShowInSeekBar" = "Игнорировать";
|
||||
"ManualSkip" = "Вручную";
|
||||
"SetColorToShowInSeekBar" = "Цвет сегмента, отображаемый в плеере";
|
||||
|
||||
"UserID" = "Ваш ID:";
|
||||
"UserIDFooter" = "Если вы хотите использовать другой идентификатор пользователя SponsorBlock, то можете ввести его в окне выше. Если же вы не знаете, что это такое, то оставьте как есть.";
|
||||
"API_URL" = "Ссылка на API:";
|
||||
"APIFooter" = "Если вы хотите использовать альтернативый API, то можете ввести его в окне выше. Если же вы не знаете, что это такое, то оставьте адрес по умолчанию. Адрес по умолчанию: https://sponsor.ajay.app/api";
|
||||
"MinimumSegmentDuration" = "Минимальная длина рекламы:";
|
||||
"HowLongNoticeWillAppear" = "Длительность уведомлений:";
|
||||
"ShowSkipNotice" = "Отображать уведомления о перемотке";
|
||||
"ShowButtonsInPlayer" = "Отображать кнопки твика в плеере";
|
||||
"HideStartEndButtonInPlayer" = "Скрыть кнопку создания меток";
|
||||
"ShowModifiedTime" = "Отображать сокращенное время в плеере";
|
||||
"AudioNotificationOnSkip" = "Аудиоуведомление о перемотке";
|
||||
"AudioFooter" = "Недоступно в беззвучном режиме";
|
||||
"EnableSkipCountTracking" = "Включить подсчет пропущенной рекламы";
|
||||
"DonateOnVenmo" = "Задонатить в Venmo";
|
||||
"DonateOnPayPal" = "Задонатить в PayPal";
|
||||
|
||||
/* Segments stuff */
|
||||
"SegmentStartsNow" = "Начало сегмента";
|
||||
"SegmentEndsNow" = "Конец сегмента";
|
||||
"WhitelistChannel" = "В белый список";
|
||||
"SegmentsInDatabase" = "Сегменты в базе данных:";
|
||||
"YourSegments" = "Ваши сегменты:";
|
||||
"SubmitSegments" = "Отправить";
|
||||
"SuccessfullySubmittedSegments" = "Успешно отправлено";
|
||||
"EndTimeLessThanStartTime" = "Выбранное вами время конца сегмента находится раньше начала сегмента. Выберите время после"; //Example: ..., Please Select a Time After 10:15
|
||||
"UnfinishedSegments" = "Имеются неполноценные сегменты.\nУбедитесь, что вы выбрали категорию и/или время окончания сегмента";
|
||||
"EditStartTime" = "Изменить начало сегмента";
|
||||
"EditStartTime_Desc" = "Время начала: (Например: 1:15)";
|
||||
"EditEndTime" = "Изменить окончание сегмента";
|
||||
"EditEndTime_Desc" = "Время окончания: (Например: 1:15)";
|
||||
"From" = "С"; // Time. Example: From 1:15 to 3:00. If you dont need it, just leave empty
|
||||
"to" = "по"; // Time. Example: From 1:15 to 3:00
|
||||
"EditCategory" = "Изменить категорию";
|
||||
"EditSegment" = "Изменить сегмент";
|
||||
"Edit" = "Изменить";
|
||||
"Error" = "Ошибка";
|
||||
"ErrorCode" = "Код ошибки";
|
||||
"Success" = "Готово";
|
||||
"OK" = "OK";
|
||||
"Cancel" = "Отмена";
|
||||
"Delete" = "Удалить";
|
||||
"Upvote" = "Проголосовать за";
|
||||
"Downvote" = "Проголосовать против";
|
||||
"VoteToChangeCategory" = "Проголосовать за смену категории";
|
||||
"VoteOnSegment" = "Проголосовать за сегмент";
|
||||
"SuccessfullyVoted" = "Голос отправлен";
|
||||
"ErrorVoting" = "Ошибка голосования";
|
||||
|
||||
/* Pop-ups*/
|
||||
"ManuallySkipReminder" = "Перемотать %@ с %ld:%02ld до %ld:%02ld?";
|
||||
"Skip" = "Да";
|
||||
"SkippedSegment" = "Перемотан\n%@";
|
||||
"Unskip" = "Отмена";
|
||||
|
||||
/* Segments in pop-ups*/
|
||||
"sponsor" = "спонсорский\nсегмент";
|
||||
"intro" = "сегмент со\nвступлением";
|
||||
"outro" = "сегмент\nс титрами";
|
||||
"interaction" = "сегмент со\nвзаимодействием";
|
||||
"selfpromo" = "сегмент с\nсаморекламой";
|
||||
"music_offtopic" = "сегмент\nбез музыки";
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 1.1 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 1.2 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 1.4 KiB |
Reference in New Issue
Block a user