diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml
index fd82285e..a0436b5c 100644
--- a/.github/workflows/build.yml
+++ b/.github/workflows/build.yml
@@ -4,14 +4,14 @@ on:
branches: [ "main" ]
workflow_dispatch:
inputs:
- branch:
- description: 'Branch to build from'
- required: true
- default: 'main'
repo:
description: 'Repository to build from'
required: true
default: 'Tonwalter888/YouMod'
+ branch:
+ description: 'Branch to build from'
+ required: true
+ default: 'main'
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
@@ -28,8 +28,8 @@ jobs:
- name: Checkout YouMod
uses: actions/checkout@v7
with:
- repository: ${{ inputs.repo }}
ref: ${{ inputs.branch }}
+ repository: ${{ inputs.repo }}
path: YouMod
- name: Install dependencies
diff --git a/.github/workflows/ipa.yml b/.github/workflows/ipa.yml
index 41677a15..2a0e041a 100644
--- a/.github/workflows/ipa.yml
+++ b/.github/workflows/ipa.yml
@@ -60,30 +60,12 @@ on:
required: false
type: boolean
- youspeed:
- description: "Integrate YouSpeed"
- default: true
- required: false
- type: boolean
-
demc:
description: "Integrate DontEatMyContent"
default: true
required: false
type: boolean
- youmute:
- description: "Integrate YouMute"
- default: true
- required: false
- type: boolean
-
- youloop:
- description: "Integrate YouLoop"
- default: true
- required: false
- type: boolean
-
ytweaks:
description: "Integrate YTweaks"
default: true
@@ -96,24 +78,6 @@ on:
required: false
type: boolean
- ytholdspeed:
- description: "Integrate YTHoldForSpeed"
- default: true
- required: false
- type: boolean
-
- youchoose:
- description: "Integrate YouChooseQuality"
- default: true
- required: false
- type: boolean
-
- youshare:
- description: "Integrate YouShare"
- default: true
- required: false
- type: boolean
-
gonerino:
description: "Integrate Gonerino"
default: true
@@ -153,17 +117,19 @@ jobs:
- name: Hide Sensitive Inputs
uses: levibostian/action-hide-sensitive-inputs@v1
with:
- exclude_inputs: bundle_id,display_name,youpip,ytuhd,ryd,abconfig,youquality,youspeed,youmute,youloop,ytweaks,youslider,ytholdspeed,youchoose,youshare,gonerino,getcap,demc,ytshare,volboost
+ exclude_inputs: bundle_id,display_name,youpip,ytuhd,ryd,abconfig,youquality,ytweaks,youslider,gonerino,getcap,demc,volboost
- name: Install MEGA downloader
if: ${{ startsWith(inputs.ipa_url, 'https://mega.nz/') }}
run: brew install megatools
+
- name: Download IPA from Mega share link
if: ${{ startsWith(inputs.ipa_url, 'https://mega.nz/') }}
env:
IPA_URL: ${{ inputs.ipa_url }}
run: |
megadl --no-progress --path youtube.ipa "$IPA_URL"
+
- name: Download IPA from direct URL
if: ${{ !startsWith(inputs.ipa_url, 'https://mega.nz/') }}
env:
@@ -186,7 +152,7 @@ jobs:
echo "YT_VERSION=$(grep -A 1 'CFBundleVersion' ytextracted/Payload/YouTube.app/Info.plist | grep '' | awk -F'[><]' '{print $3}')" >> $GITHUB_ENV
- name: Install Dependencies
- run: brew install make ldid swiftlint
+ run: brew install make ldid
- name: Set PATH Environment Variables
run: |
@@ -249,10 +215,7 @@ jobs:
run: sed -i '' 's/od -c "$i" | head/od -c "$i" 2>\/dev\/null | head/g' $THEOS/bin/convert_xml_plist.sh || true
- name: Install cyan and tbd
- run: |
- pipx install https://github.com/asdfzxcvbn/pyzule-rw/archive/main.zip
- wget --quiet --no-verbose "https://github.com/inoahdev/tbd/releases/download/2.2/tbd-mac" -O /usr/local/bin/tbd
- chmod +x /usr/local/bin/tbd
+ run: pipx install https://github.com/asdfzxcvbn/pyzule-rw/archive/main.zip
- name: Checkout YouMod
uses: actions/checkout@v7
@@ -275,7 +238,7 @@ jobs:
- name: Clone YTUHD
if: ${{ inputs.ytuhd }}
- run: git clone --quiet --depth=1 https://github.com/Tonwalter888/YTUHD.git
+ run: git clone --quiet --depth=1 --recurse-submodules https://github.com/PoomSmart/YTUHD.git
- name: Clone Return-YouTube-Dislikes
if: ${{ inputs.ryd }}
@@ -285,7 +248,7 @@ jobs:
run: git clone --quiet --depth=1 https://github.com/PoomSmart/YouGroupSettings.git
- name: Clone YTVideoOverlay
- if: ${{ inputs.youpip || inputs.ytuhd || inputs.youquality || inputs.youspeed || inputs.youmute || inputs.youloop || inputs.youshare || inputs.getcap }}
+ if: ${{ inputs.youpip || inputs.ytuhd || inputs.youquality || inputs.getcap }}
run: git clone --quiet --depth=1 https://github.com/PoomSmart/YTVideoOverlay.git
- name: Clone YTABConfig
@@ -296,40 +259,19 @@ jobs:
if: ${{ inputs.youquality }}
run: git clone --quiet --depth=1 https://github.com/PoomSmart/YouQuality.git
- - name: Clone YouSpeed
- if: ${{ inputs.youspeed }}
- run: git clone --quiet --depth=1 https://github.com/PoomSmart/YouSpeed.git
+ - name: Clone UncappedAVPlayer
+ run: git clone --quiet --depth=1 https://github.com/PoomSmart/UncappedAVPlayer.git
- name: Clone DontEatMyContent
if: ${{ inputs.demc }}
run: git clone --quiet --depth=1 https://github.com/therealFoxster/DontEatMyContent.git
- - name: Clone YouMute
- if: ${{ inputs.youmute }}
- run: git clone --quiet --depth=1 https://github.com/PoomSmart/YouMute.git
-
- - name: Clone YouLoop
- if: ${{ inputs.youloop }}
- run: git clone --quiet --depth=1 https://github.com/bhackel/YouLoop.git
-
- name: Clone YouSlider
if: ${{ inputs.youslider }}
run: |
git clone --quiet --depth=1 https://github.com/PoomSmart/YouSlider.git
wget --no-verbose --quiet https://github.com/Tonwalter888/Tonwalter888.github.io/raw/refs/heads/main/deb/alderis.deb
- - name: Clone YTHoldForSpeed
- if: ${{ inputs.ytholdspeed }}
- run: git clone --quiet --depth=1 https://github.com/joshuaseltzer/YTHoldForSpeed.git
-
- - name: Clone YouChooseQuality
- if: ${{ inputs.youchoose }}
- run: git clone --quiet https://github.com/PoomSmart/YouChooseQuality.git
-
- - name: Clone YouShare
- if: ${{ inputs.youshare }}
- run: git clone --quiet --depth=1 https://github.com/Tonwalter888/YouShare.git
-
- name: Clone YTweaks
if: ${{ inputs.ytweaks }}
run: git clone --quiet --depth=1 https://github.com/fosterbarnes/YTweaks.git
@@ -368,7 +310,7 @@ jobs:
if: ${{ inputs.ytuhd }}
run: |
cd YTUHD
- make clean package DEBUG=0 FINALPACKAGE=1 THEOS_PACKAGE_SCHEME=rootless
+ make clean package DEBUG=0 FINALPACKAGE=1 THEOS_PACKAGE_SCHEME=rootless SIDELOAD=1
mv packages/*.deb ${{ github.workspace }}/ytuhd.deb
- name: Build Return-YouTube-Dislikes
@@ -385,7 +327,7 @@ jobs:
mv packages/*.deb ${{ github.workspace }}/ygs.deb
- name: Build YTVideoOverlay
- if: ${{ inputs.youpip || inputs.ytuhd || inputs.youquality || inputs.youspeed || inputs.youmute || inputs.youloop || inputs.youshare || inputs.getcap }}
+ if: ${{ inputs.youpip || inputs.ytuhd || inputs.youquality || inputs.getcap }}
run: |
cd YTVideoOverlay
make clean package DEBUG=0 FINALPACKAGE=1 THEOS_PACKAGE_SCHEME=rootless
@@ -405,12 +347,11 @@ jobs:
make clean package DEBUG=0 FINALPACKAGE=1 THEOS_PACKAGE_SCHEME=rootless
mv packages/*.deb ${{ github.workspace }}/youquality.deb
- - name: Build YouSpeed
- if: ${{ inputs.youspeed }}
+ - name: Build UncappedAVPlayer
run: |
- cd YouSpeed
+ cd UncappedAVPlayer
make clean package DEBUG=0 FINALPACKAGE=1 THEOS_PACKAGE_SCHEME=rootless
- mv packages/*.deb ${{ github.workspace }}/youspeed.deb
+ mv packages/*.deb ${{ github.workspace }}/uncapavplayer.deb
- name: Build DontEatMyContent
if: ${{ inputs.demc }}
@@ -419,20 +360,6 @@ jobs:
make clean package DEBUG=0 FINALPACKAGE=1 THEOS_PACKAGE_SCHEME=rootless
mv packages/*.deb ${{ github.workspace }}/demc.deb
- - name: Build YouMute
- if: ${{ inputs.youmute }}
- run: |
- cd YouMute
- make clean package DEBUG=0 FINALPACKAGE=1 THEOS_PACKAGE_SCHEME=rootless
- mv packages/*.deb ${{ github.workspace }}/ym.deb
-
- - name: Build YouLoop
- if: ${{ inputs.youloop }}
- run: |
- cd YouLoop
- make clean package DEBUG=0 FINALPACKAGE=1 THEOS_PACKAGE_SCHEME=rootless
- mv packages/*.deb ${{ github.workspace }}/youloop.deb
-
- name: Build YouSlider
if: ${{ inputs.youslider }}
run: |
@@ -440,28 +367,6 @@ jobs:
make clean package DEBUG=0 FINALPACKAGE=1 THEOS_PACKAGE_SCHEME=rootless
mv packages/*.deb ${{ github.workspace }}/youslider.deb
- - name: Build YTHoldForSpeed
- if: ${{ inputs.ytholdspeed }}
- run: |
- cd YTHoldForSpeed
- make clean package DEBUG=0 FINALPACKAGE=1 THEOS_PACKAGE_SCHEME=rootless TARGET=iphone:clang:latest:15.0 ARCHS=arm64
- mv packages/*.deb ${{ github.workspace }}/ytholdspeed.deb
-
- - name: Build YouChooseQuality
- if: ${{ inputs.youchoose }}
- run: |
- cd YouChooseQuality
- git checkout 1585a3691b2ef0b59d42c40c31639fd8b79e2cd4
- make clean package DEBUG=0 FINALPACKAGE=1 THEOS_PACKAGE_SCHEME=rootless
- mv packages/*.deb ${{ github.workspace }}/youchoose.deb
-
- - name: Build YouShare
- if: ${{ inputs.youshare }}
- run: |
- cd YouShare
- make clean package DEBUG=0 FINALPACKAGE=1 THEOS_PACKAGE_SCHEME=rootless
- mv packages/*.deb ${{ github.workspace }}/youshare.deb
-
- name: Build YTweaks
if: ${{ inputs.ytweaks }}
run: |
diff --git a/Files/Ads.x b/Files/Ads.x
index 6b19953e..02bec830 100644
--- a/Files/Ads.x
+++ b/Files/Ads.x
@@ -12,12 +12,15 @@ static BOOL isProductList(YTICommand *command) {
NSString *getAdString(NSString *description) {
for (NSString *str in @[
@"brand_promo",
+ @"brand_video_shelf",
+ @"brand_video_singleton",
@"carousel_footered_layout",
@"carousel_headered_layout",
@"eml.expandable_metadata",
@"feed_ad_metadata",
@"full_width_portrait_image_layout",
@"full_width_square_image_layout",
+ @"grid_ads_image_layout",
@"landscape_image_wide_button_layout",
@"post_shelf",
@"product_carousel",
@@ -51,7 +54,20 @@ static BOOL isAdRenderer(YTIElementRenderer *elementRenderer, int kind) {
static NSMutableArray *filteredArray(NSArray *array) {
NSMutableArray *newArray = [array mutableCopy];
NSIndexSet *removeIndexes = [newArray indexesOfObjectsPassingTest:^BOOL(YTIItemSectionRenderer *sectionRenderer, NSUInteger idx, BOOL *stop) {
+ // Filter shelf renderer items (ads and shorts)
if ([sectionRenderer isKindOfClass:%c(YTIShelfRenderer)]) {
+ NSString *description = [sectionRenderer description];
+ if ([description containsString:@"community-tab-chip-posts-section"]) return NO;
+ // Filter shorts
+ if (IS_ENABLED(HideShortsShelf)) {
+ if (IS_ENABLED(KeepShortsSubscript) && [description containsString:@"subscriptions-shorts-shelf-item"]) return NO;
+ if ([description containsString:@"shorts_video_cell.eml"]) return YES;
+ if ([description containsString:@"shelf_header.eml"] && [description containsString:@"youtube_shorts_24_cairo"]) return YES;
+ }
+ // Filter feed posts
+ if (IS_ENABLED(HideFeedPost) && ([description containsString:@"poll_post_root.eml"] || [description containsString:@"options_post_root.eml"] || [description containsString:@"images_post_root_slim.eml"] || [description containsString:@"images_post_responsive_root.eml"] || [description containsString:@"options_post_responsive_root.eml"] || [description containsString:@"post_base_wrapper_slim.eml"] || [description containsString:@"text_post_root_slim.eml"])) {
+ return YES;
+ }
YTIShelfSupportedRenderers *content = ((YTIShelfRenderer *)sectionRenderer).content;
YTIHorizontalListRenderer *horizontalListRenderer = content.horizontalListRenderer;
NSMutableArray *itemsArray = horizontalListRenderer.itemsArray;
@@ -61,8 +77,55 @@ static NSMutableArray *filteredArray(NSArray *contentsArray = sectionRenderer.contentsArray;
if (contentsArray.count > 1) {
NSIndexSet *removeContentsArrayIndexes = [contentsArray indexesOfObjectsPassingTest:^BOOL(YTIItemSectionSupportedRenderers *sectionSupportedRenderers, NSUInteger idx2, BOOL *stop2) {
@@ -73,7 +136,10 @@ static NSMutableArray *filteredArray(NSArray *filteredArray(NSArray *filteredArray(NSArray *filteredArray(NSArray *)reels {
[reels removeObjectsAtIndexes:[reels indexesOfObjectsPassingTest:^BOOL(YTReelModel *obj, NSUInteger idx, BOOL *stop) {
- return [obj respondsToSelector:@selector(videoType)] ? obj.videoType == 3 : NO;
+ if ([obj respondsToSelector:@selector(videoType)] && obj.videoType == 3) return YES;
+ if ([obj isKindOfClass:%c(YTReelNonVideoContentModel)]) return YES;
+ if ([obj respondsToSelector:@selector(videoType)] && obj.videoType == 10 && IS_ENABLED(RemoveShortsPosts)) return YES;
+ if ([obj respondsToSelector:@selector(videoType)] && (obj.videoType == 4 || obj.videoType == 7) && IS_ENABLED(RemoveShortsLive)) return YES;
+ return NO;
}]];
%orig;
}
@@ -164,6 +268,7 @@ static NSMutableArray *filteredArray(NSArray *filteredArray(NSArray *filteredArray(NSArray
#import
#import
-#import
-#import
-#import
-#import
-#import
-
-@interface YTDefaultSheetController (YouModDownload)
-+ (instancetype)sheetControllerWithParentResponder:(id)parentResponder;
-- (void)addAction:(YTActionSheetAction *)action;
-- (void)presentFromView:(UIView *)view animated:(BOOL)animated completion:(void (^)(void))completion;
-- (void)presentFromViewController:(UIViewController *)vc animated:(BOOL)animated completion:(void (^)(void))completion;
-@end
-@interface YTPlayerViewController (YouModDownload)
-- (YTPlayerResponse *)contentPlayerResponse;
-@end
+#define TweakName @"YouMod"
-@interface YTIPlayerResponse (YouModDownload)
-- (id)streamingData;
-@end
-
-@interface YTIFormatStream (YouModDownload)
-- (NSString *)mimeType;
-- (BOOL)hasContentLength;
-- (unsigned long long)contentLength;
-- (unsigned long long)approxDurationMs;
-@end
+static NSBundle *YouModBundle() {
+ static NSBundle *bundle = nil;
+ static dispatch_once_t onceToken;
+ dispatch_once(&onceToken, ^{
+ NSString *tweakBundlePath = [[NSBundle mainBundle] pathForResource:TweakName ofType:@"bundle"];
+ if (tweakBundlePath)
+ bundle = [NSBundle bundleWithPath:tweakBundlePath];
+ else
+ bundle = [NSBundle bundleWithPath:[NSString stringWithFormat:PS_ROOT_PATH_NS(@"/Library/Application Support/%@.bundle"), TweakName]];
+ });
+ return bundle;
+}
-@interface YTIVideoDetails (YouModDownload)
-- (NSString *)title;
-- (NSString *)author;
-- (NSString *)shortDescription;
-@end
+#define LOC(x) [YouModBundle() localizedStringForKey:x value:nil table:nil]
static UIImage *YouModIconImage(NSInteger iconType) {
YTIIcon *icon = [%c(YTIIcon) new];
@@ -54,14 +38,10 @@ static UIImage *YouModIconImage(NSInteger iconType) {
@property (nonatomic, copy) NSString *subtitle;
@property (nonatomic, strong) UIImage *iconImage;
@property (nonatomic, copy) void (^handler)(void);
-+ (instancetype)itemWithTitle:(NSString *)title subtitle:(NSString *)subtitle handler:(void (^)(void))handler;
+ (instancetype)itemWithTitle:(NSString *)title subtitle:(NSString *)subtitle icon:(UIImage *)icon handler:(void (^)(void))handler;
@end
@implementation YouModMenuItem
-+ (instancetype)itemWithTitle:(NSString *)title subtitle:(NSString *)subtitle handler:(void (^)(void))handler {
- return [self itemWithTitle:title subtitle:subtitle icon:nil handler:handler];
-}
+ (instancetype)itemWithTitle:(NSString *)title subtitle:(NSString *)subtitle icon:(UIImage *)icon handler:(void (^)(void))handler {
YouModMenuItem *item = [YouModMenuItem new];
item.title = title;
@@ -73,36 +53,22 @@ static UIImage *YouModIconImage(NSInteger iconType) {
@end
@interface YouModMediaFormat : NSObject
-@property (nonatomic, strong) id source;
+@property (nonatomic, strong) YTIFormatStream *source;
@property (nonatomic, copy) NSString *urlString;
@property (nonatomic, copy) NSString *qualityLabel;
@property (nonatomic, copy) NSString *mimeType;
-@property (nonatomic, copy) NSDictionary *httpHeaders;
-@property (nonatomic, assign) unsigned long long contentLength;
-@property (nonatomic, assign) unsigned long long durationMs;
-@property (nonatomic, assign) NSInteger fps;
+@property (nonatomic, copy) NSString *idp;
+@property (nonatomic, assign) NSInteger contentLength;
+@property (nonatomic, assign) NSUInteger durationMs;
+@property (nonatomic, assign) int fps;
+@property (nonatomic, assign) int itag;
+@property (nonatomic, assign) int resolution;
@property (nonatomic, assign) BOOL video;
-@property (nonatomic, copy) NSString *languageCode;
-@property (nonatomic, copy) NSString *languageName;
-@property (nonatomic, assign) BOOL drcAudio;
@end
@implementation YouModMediaFormat
@end
-@interface YouModAudioOutputFormat : NSObject
-@property (nonatomic, copy) NSString *identifier;
-@property (nonatomic, copy) NSString *title;
-@property (nonatomic, copy) NSString *subtitle;
-@property (nonatomic, copy) NSString *fileExtension;
-@property (nonatomic, copy) NSArray *ffmpegArguments;
-@property (nonatomic, assign) BOOL passthroughWhenCompatible;
-@property (nonatomic, assign) BOOL supported;
-@end
-
-@implementation YouModAudioOutputFormat
-@end
-
typedef void (^YouModFileDownloadCompletion)(NSURL *fileURL, NSError *error);
typedef void (^YouModMergeCompletion)(BOOL success, NSError *error);
typedef void (^YouModRangeDownloadProgress)(unsigned long long completedBytes);
@@ -144,8 +110,8 @@ typedef void (^YouModRangeDownloadProgress)(unsigned long long completedBytes);
@property (nonatomic, strong) NSURLSessionDownloadTask *task;
@property (nonatomic, strong) NSURLSessionDataTask *metadataTask;
@property (nonatomic, strong) YouModRangeDownloader *rangeDownloader;
-@property (nonatomic, strong) UIAlertController *progressAlert;
-@property (nonatomic, strong) UIProgressView *progressView;
+@property (nonatomic, strong) AVAssetExportSession *exporter;
+@property (nonatomic, strong) YMDownloadProgressView *progressPill;
@property (nonatomic, weak) UIViewController *presenter;
@property (nonatomic, copy) YouModFileDownloadCompletion fileCompletion;
@property (nonatomic, strong) NSURL *destinationURL;
@@ -161,18 +127,17 @@ typedef void (^YouModRangeDownloadProgress)(unsigned long long completedBytes);
@property (nonatomic, assign) BOOL cancelled;
@property (nonatomic, copy) NSString *baseProgressTitle;
@property (nonatomic, assign) NSTimeInterval downloadStartTime;
+@property (nonatomic, copy) void (^downloadCompletionBlock)(NSURL *localURL, NSString *errorMsg);
+@property (nonatomic, strong) NSURL *downloadedFileURL;
+@property (nonatomic, strong) NSString *downloadErrorStr;
+ (instancetype)sharedCoordinator;
-- (void)startVideoDownloadWithVideoFormat:(YouModMediaFormat *)videoFormat audioFormat:(YouModMediaFormat *)audioFormat fileName:(NSString *)fileName videoID:(NSString *)videoID presenter:(UIViewController *)presenter;
-- (void)startAudioDownloadWithAudioFormat:(YouModMediaFormat *)audioFormat fileName:(NSString *)fileName videoID:(NSString *)videoID presenter:(UIViewController *)presenter;
-- (void)startAudioDownloadWithAudioFormat:(YouModMediaFormat *)audioFormat fileName:(NSString *)fileName videoID:(NSString *)videoID outputFormat:(YouModAudioOutputFormat *)outputFormat presenter:(UIViewController *)presenter;
-- (void)startDirectVideoDownloadWithVideoFormat:(YouModMediaFormat *)videoFormat audioFormat:(YouModMediaFormat *)audioFormat fileName:(NSString *)fileName videoID:(NSString *)videoID presenter:(UIViewController *)presenter;
-- (void)startDirectSingleVideoDownloadWithFormat:(YouModMediaFormat *)format fileName:(NSString *)fileName videoID:(NSString *)videoID presenter:(UIViewController *)presenter;
-- (void)startDirectAudioDownloadWithAudioFormat:(YouModMediaFormat *)audioFormat fileName:(NSString *)fileName videoID:(NSString *)videoID presenter:(UIViewController *)presenter;
-- (void)startDirectAudioDownloadWithAudioFormat:(YouModMediaFormat *)audioFormat fileName:(NSString *)fileName videoID:(NSString *)videoID outputFormat:(YouModAudioOutputFormat *)outputFormat presenter:(UIViewController *)presenter;
+- (void)startVideoDownloadWithVideoFormat:(YouModMediaFormat *)videoFormat audioFormat:(YouModMediaFormat *)audioFormat fileName:(NSString *)fileName presenter:(UIViewController *)presenter videoID:(NSString *)vidID;
+- (void)startAudioDownloadWithAudioFormat:(YouModMediaFormat *)audioFormat fileName:(NSString *)fileName presenter:(UIViewController *)presenter videoID:(NSString *)vidID;
+- (void)startDirectVideoDownloadWithVideoFormat:(YouModMediaFormat *)videoFormat audioFormat:(YouModMediaFormat *)audioFormat fileName:(NSString *)fileName presenter:(UIViewController *)presenter videoID:(NSString *)vidID;
+- (void)startDirectAudioDownloadWithAudioFormat:(YouModMediaFormat *)audioFormat fileName:(NSString *)fileName presenter:(UIViewController *)presenter videoID:(NSString *)vidID;
+- (void)trimAudioToHalfLengthAtURL:(NSURL *)inputURL toURL:(NSURL *)outputURL completion:(void (^)(NSError *error))completion;
- (void)mergeVideoURL:(NSURL *)videoURL audioURL:(NSURL *)audioURL fileName:(NSString *)fileName outputExtension:(NSString *)outputExtension durationMs:(unsigned long long)durationMs presenter:(UIViewController *)presenter;
- (void)mergeVideoWithAVFoundationVideoURL:(NSURL *)videoURL audioURL:(NSURL *)audioURL outputURL:(NSURL *)outputURL durationMs:(unsigned long long)durationMs presenter:(UIViewController *)presenter fallbackError:(NSError *)fallbackError;
-- (void)trimSingleVideoURL:(NSURL *)inputURL outputURL:(NSURL *)outputURL durationMs:(unsigned long long)durationMs presenter:(UIViewController *)presenter;
-- (void)convertAudioURL:(NSURL *)inputURL outputURL:(NSURL *)outputURL outputFormat:(YouModAudioOutputFormat *)outputFormat durationMs:(unsigned long long)durationMs presenter:(UIViewController *)presenter;
@end
static const unsigned long long YouModFastDownloadMinimumBytes = 256ULL * 1024ULL;
@@ -199,9 +164,8 @@ static NSString *YouModYouTubeCookiesString(void) {
}
static NSString *YouModNativeUserAgent(void) {
- NSString *version = @"21.18.4";
- NSString *sysVersion = [[UIDevice currentDevice].systemVersion stringByReplacingOccurrencesOfString:@"." withString:@"_"] ?: @"18_7";
- return [NSString stringWithFormat:@"com.google.ios.youtube/%@ (iPhone; CPU iPhone OS %@ like Mac OS X)", version, sysVersion];
+ NSString *device = UIDevice.currentDevice.userInterfaceIdiom == UIUserInterfaceIdiomPad ? @"iPad" : @"iPhone";
+ return [NSString stringWithFormat:@"com.google.ios.youtube/21.26.4 (%@; CPU OS 18_7 like Mac OS X)", device];
}
static void YouModApplyDownloadHeaders(NSMutableURLRequest *request, NSDictionary *headers) {
@@ -313,7 +277,8 @@ static void YouModApplyDownloadHeaders(NSMutableURLRequest *request, NSDictionar
return;
}
- unsigned long long chunkSize = self.expectedBytes / YouModFastDownloadConcurrency;
+ // Aim for ~100 chunks (≈1% per chunk) but respect min/max bounds.
+ unsigned long long chunkSize = self.expectedBytes / 100ULL;
if (chunkSize < 256ULL * 1024ULL) chunkSize = 256ULL * 1024ULL;
if (chunkSize > YouModFastDownloadChunkBytes) chunkSize = YouModFastDownloadChunkBytes;
@@ -486,22 +451,8 @@ void YouModDownloadSetCurrentPlayer(YTPlayerViewController *player) {
YouModCurrentPlayerViewController = player;
}
-static NSString *YouModStringFromSelector(id object, SEL selector) {
- if (!object) return nil;
- id value = nil;
- if ([object respondsToSelector:selector]) {
- value = ((id (*)(id, SEL))objc_msgSend)(object, selector);
- } else {
- @try {
- value = [object valueForKey:NSStringFromSelector(selector)];
- } @catch (__unused NSException *exception) {
- value = nil;
- }
- }
- if ([value isKindOfClass:NSString.class]) return value;
- if ([value isKindOfClass:NSURL.class]) return [(NSURL *)value absoluteString];
- if ([value respondsToSelector:@selector(stringValue)]) return [value stringValue];
- return [value respondsToSelector:@selector(description)] ? [value description] : nil;
+YTPlayerViewController *YouModDownloadGetCurrentPlayer(void) {
+ return YouModCurrentPlayerViewController;
}
static id YouModObjectFromSelector(id object, SEL selector) {
@@ -516,48 +467,6 @@ static id YouModObjectFromSelector(id object, SEL selector) {
}
}
-static unsigned long long YouModUnsignedLongLongFromSelector(id object, SEL selector) {
- if (!object) return 0;
- if ([object respondsToSelector:selector]) {
- return ((unsigned long long (*)(id, SEL))objc_msgSend)(object, selector);
- }
- @try {
- id value = [object valueForKey:NSStringFromSelector(selector)];
- if ([value respondsToSelector:@selector(unsignedLongLongValue)])
- return [value unsignedLongLongValue];
- } @catch (__unused NSException *exception) {
- }
- return 0;
-}
-
-static BOOL YouModBoolFromSelector(id object, SEL selector) {
- if (!object) return NO;
- if ([object respondsToSelector:selector]) {
- return ((BOOL (*)(id, SEL))objc_msgSend)(object, selector);
- }
- @try {
- id value = [object valueForKey:NSStringFromSelector(selector)];
- if ([value respondsToSelector:@selector(boolValue)])
- return [value boolValue];
- } @catch (__unused NSException *exception) {
- }
- return NO;
-}
-
-static NSInteger YouModIntegerFromSelector(id object, SEL selector) {
- if (!object) return 0;
- if ([object respondsToSelector:selector]) {
- return ((NSInteger (*)(id, SEL))objc_msgSend)(object, selector);
- }
- @try {
- id value = [object valueForKey:NSStringFromSelector(selector)];
- if ([value respondsToSelector:@selector(integerValue)])
- return [value integerValue];
- } @catch (__unused NSException *exception) {
- }
- return 0;
-}
-
static UIViewController *YouModTopViewController(UIViewController *root) {
if (!root) {
UIWindow *keyWindow = nil;
@@ -577,22 +486,19 @@ static UIViewController *YouModTopViewController(UIViewController *root) {
return root;
}
-static void YouModSendToast(NSString *message, id responder) {
- Class toastClass = NSClassFromString(@"YTToastResponderEvent");
- id event = [toastClass eventWithMessage:message firstResponder:responder ?: YouModTopViewController(nil)];
- if ([event respondsToSelector:@selector(send)]) {
- [event send];
- return;
- }
+static void YouModSendToast(NSString *message) {
+ UIView *parent = sbGetNotificationParent();
+ [SBSkipNotificationView showInView:parent message:message buttonTitle:nil action:nil duration:3.0];
+}
- UIViewController *presenter = YouModTopViewController([responder isKindOfClass:UIViewController.class] ? responder : nil);
- if (!presenter) return;
- UIAlertController *alert = [UIAlertController alertControllerWithTitle:nil message:message preferredStyle:UIAlertControllerStyleAlert];
- [presenter presentViewController:alert animated:YES completion:^{
- dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(1.2 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
- [alert dismissViewControllerAnimated:YES completion:nil];
- });
- }];
+static void YouModSendSuccess(NSString *message) {
+ UIView *parent = sbGetNotificationParent();
+ [SBSkipNotificationView showSuccessInView:parent message:message duration:3.0];
+}
+
+static void YouModSendError(NSString *message) {
+ UIView *parent = sbGetNotificationParent();
+ [SBSkipNotificationView showErrorInView:parent message:message duration:4.0];
}
static NSString *YouModByteCount(unsigned long long bytes) {
@@ -602,14 +508,6 @@ static NSString *YouModByteCount(unsigned long long bytes) {
return [formatter stringFromByteCount:(long long)bytes];
}
-static NSString *YouModGenerateCPN(void) {
- static NSString *const alphabet = @"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_";
- NSMutableString *nonce = [NSMutableString stringWithCapacity:16];
- for (NSUInteger i = 0; i < 16; i++)
- [nonce appendFormat:@"%C", [alphabet characterAtIndex:arc4random_uniform((uint32_t)alphabet.length)]];
- return nonce;
-}
-
static NSString *YouModURLStringBypassingThrottle(NSString *urlString) {
if (urlString.length == 0) return urlString;
NSURLComponents *components = [NSURLComponents componentsWithString:urlString];
@@ -637,10 +535,7 @@ static NSString *YouModURLStringWithCPN(NSString *urlString) {
if (urlString.length == 0) return urlString;
urlString = YouModURLStringBypassingThrottle(urlString);
if ([urlString containsString:@"cpn="]) return urlString;
- Class ytDataUtils = NSClassFromString(@"YTDataUtils");
- NSString *cpn = ((id (*)(Class, SEL))objc_msgSend)(ytDataUtils, @selector(generateClientSideNonce));
- if (![cpn isKindOfClass:NSString.class] || cpn.length == 0)
- cpn = YouModGenerateCPN();
+ NSString *cpn = [%c(YTDataUtils) generateClientSideNonce];
NSString *separator = [urlString containsString:@"?"] ? @"&" : @"?";
return [NSString stringWithFormat:@"%@%@cpn=%@", urlString, separator, cpn];
}
@@ -658,60 +553,11 @@ static NSString *YouModSanitizedFileName(NSString *name) {
static NSURL *YouModDownloadsDirectoryURL(void) {
NSURL *documentsURL = [NSFileManager.defaultManager URLsForDirectory:NSDocumentDirectory inDomains:NSUserDomainMask].firstObject;
- NSURL *downloadsURL = [documentsURL URLByAppendingPathComponent:@"YouMod Downloads" isDirectory:YES];
+ NSURL *downloadsURL = [documentsURL URLByAppendingPathComponent:@"YouMod_Downloads" isDirectory:YES];
[NSFileManager.defaultManager createDirectoryAtURL:downloadsURL withIntermediateDirectories:YES attributes:nil error:nil];
return downloadsURL;
}
-static NSString *YouModLastDownloadDiagnostic;
-
-static NSURL *YouModDiagnosticLogURL(void) {
- return [YouModDownloadsDirectoryURL() URLByAppendingPathComponent:@"youmod-download-diagnostics.txt"];
-}
-
-static void YouModRecordDownloadDiagnostic(NSString *context, NSString *details) {
- if (context.length == 0 && details.length == 0) return;
-
- NSDateFormatter *formatter = [NSDateFormatter new];
- formatter.locale = [NSLocale localeWithLocaleIdentifier:@"en_US_POSIX"];
- formatter.dateFormat = @"yyyy-MM-dd HH:mm:ss ZZZZZ";
- NSString *timestamp = [formatter stringFromDate:NSDate.date];
- NSString *entry = [NSString stringWithFormat:@"[%@]\n%@\n%@\n\n", timestamp ?: @"", context ?: @"", details ?: @""];
- YouModLastDownloadDiagnostic = entry;
-
- NSURL *logURL = YouModDiagnosticLogURL();
- NSData *data = [entry dataUsingEncoding:NSUTF8StringEncoding];
- if (![NSFileManager.defaultManager fileExistsAtPath:logURL.path])
- [NSFileManager.defaultManager createFileAtPath:logURL.path contents:nil attributes:nil];
-
- NSFileHandle *handle = [NSFileHandle fileHandleForWritingAtPath:logURL.path];
- if (!handle) return;
- @try {
- [handle seekToEndOfFile];
- [handle writeData:data];
- [handle closeFile];
- } @catch (__unused NSException *exception) {
- }
-}
-
-static NSString *YouModDownloadDiagnosticText(void) {
- if (YouModLastDownloadDiagnostic.length) return YouModLastDownloadDiagnostic;
- NSString *log = [NSString stringWithContentsOfURL:YouModDiagnosticLogURL() encoding:NSUTF8StringEncoding error:nil];
- if (log.length == 0) return nil;
- NSUInteger maxLength = 12000;
- return log.length > maxLength ? [log substringFromIndex:log.length - maxLength] : log;
-}
-
-static void YouModCopyDownloadDiagnostics(UIViewController *presenter) {
- NSString *diagnostic = YouModDownloadDiagnosticText();
- if (diagnostic.length == 0) {
- YouModSendToast(@"No download diagnostics yet.", presenter);
- return;
- }
- UIPasteboard.generalPasteboard.string = diagnostic;
- YouModSendToast(@"Copied download diagnostics", presenter);
-}
-
static NSURL *YouModUniqueFileURL(NSString *fileName, NSString *extension) {
NSString *safeName = YouModSanitizedFileName(fileName);
NSURL *directoryURL = YouModDownloadsDirectoryURL();
@@ -729,13 +575,6 @@ static NSURL *YouModTemporaryFileURL(NSString *extension) {
return [NSURL fileURLWithPath:[NSTemporaryDirectory() stringByAppendingPathComponent:name]];
}
-static NSInteger YouModResolutionFromQuality(NSString *quality);
-static NSInteger YouModFPSFromQuality(NSString *quality);
-static NSInteger YouModNormalizedFPS(NSInteger fps);
-static NSInteger YouModDisplayHeightForVideoHeight(NSInteger height);
-static NSString *YouModQualityLabel(NSInteger height, NSInteger fps, NSString *fallback);
-static BOOL YouModFFmpegKitAvailable(void);
-
static unsigned long long YouModDurationMsForURL(NSURL *url) {
AVURLAsset *asset = [AVURLAsset URLAssetWithURL:url options:nil];
if (!CMTIME_IS_NUMERIC(asset.duration) || !CMTIME_IS_VALID(asset.duration)) return 0;
@@ -744,10 +583,6 @@ static unsigned long long YouModDurationMsForURL(NSURL *url) {
return (unsigned long long)llround(seconds * 1000.0);
}
-static NSString *YouModDurationSecondsArgument(unsigned long long durationMs) {
- return [NSString stringWithFormat:@"%.3f", (double)durationMs / 1000.0];
-}
-
static BOOL YouModCMTimeIsUsable(CMTime time) {
if (!CMTIME_IS_VALID(time) || !CMTIME_IS_NUMERIC(time) || CMTIME_IS_INDEFINITE(time)) return NO;
Float64 seconds = CMTimeGetSeconds(time);
@@ -778,321 +613,33 @@ static CMTime YouModExportDuration(AVAsset *videoAsset, AVAsset *audioAsset, uns
return duration;
}
-static NSMutableArray *YouModFFmpegKitLoadEntries(void) {
- static NSMutableArray *entries = nil;
- static dispatch_once_t onceToken;
- dispatch_once(&onceToken, ^{
- entries = [NSMutableArray array];
- });
- return entries;
-}
-
-static void YouModAppendFFmpegKitLoadEntry(NSString *format, ...) {
- if (format.length == 0) return;
-
- va_list arguments;
- va_start(arguments, format);
- NSString *entry = [[NSString alloc] initWithFormat:format arguments:arguments];
- va_end(arguments);
- if (entry.length == 0) return;
-
- NSMutableArray *entries = YouModFFmpegKitLoadEntries();
- @synchronized(entries) {
- [entries addObject:entry];
- if (entries.count > 220)
- [entries removeObjectsInRange:NSMakeRange(0, entries.count - 220)];
- }
-}
-
-static NSArray *YouModFFmpegKitSearchDirectories(void) {
- NSMutableOrderedSet *directories = [NSMutableOrderedSet orderedSet];
-
- // Path to YouMod.bundle/Frameworks inside the main app bundle
- NSString *bundlePath = [[NSBundle.mainBundle resourcePath] stringByAppendingPathComponent:@"YouMod.bundle"];
- NSString *frameworksInsideBundle = [bundlePath stringByAppendingPathComponent:@"Frameworks"];
-
- // Safety check: only add if the directory actually exists
- BOOL isDir = NO;
- if ([[NSFileManager defaultManager] fileExistsAtPath:frameworksInsideBundle isDirectory:&isDir] && isDir) {
- [directories addObject:frameworksInsideBundle];
- }
-
- return directories.array;
-}
-
-static void YouModDlopenPath(NSString *path, BOOL requireExistingFile) {
- if (path.length == 0) return;
- if (requireExistingFile && ![NSFileManager.defaultManager fileExistsAtPath:path]) {
- YouModAppendFFmpegKitLoadEntry(@"missing %@", path);
- return;
- }
-
- dlerror();
- void *handle = dlopen(path.fileSystemRepresentation, RTLD_NOW | RTLD_GLOBAL);
- const char *error = dlerror();
- if (handle) {
- YouModAppendFFmpegKitLoadEntry(@"loaded %@", path);
- } else {
- YouModAppendFFmpegKitLoadEntry(@"failed %@\n dlerror=%@", path, error ? [NSString stringWithUTF8String:error] : @"unknown");
- }
-}
-
-static void YouModDlopenPathIfPresent(NSString *path) {
- YouModDlopenPath(path, YES);
-}
-
-static void YouModLoadFrameworkBinary(NSString *directory, NSString *frameworkName, NSString *binaryName) {
- if (directory.length == 0 || frameworkName.length == 0 || binaryName.length == 0) return;
- YouModDlopenPathIfPresent([[directory stringByAppendingPathComponent:[frameworkName stringByAppendingString:@".framework"]] stringByAppendingPathComponent:binaryName]);
- YouModDlopenPathIfPresent([[directory stringByAppendingPathComponent:[frameworkName stringByAppendingString:@".framework"]] stringByAppendingPathComponent:frameworkName]);
-}
-
-static void YouModLoadFFmpegKitIfNeeded(void) {
- static BOOL attempted = NO;
- if (NSClassFromString(@"FFmpegKit")) return;
- if (attempted) return;
- attempted = YES;
-
- YouModAppendFFmpegKitLoadEntry(@"[YouMod] Starting bundled FFmpegKit load...");
-
- // Order is important: load dependencies (avutil, etc.) before the main toolkit
- NSArray *> *frameworks = @[
- @[@"libavutil", @"libavutil"],
- @[@"libswresample", @"libswresample"],
- @[@"libswscale", @"libswscale"],
- @[@"libavcodec", @"libavcodec"],
- @[@"libavformat", @"libavformat"],
- @[@"libavfilter", @"libavfilter"],
- @[@"libavdevice", @"libavdevice"],
- @[@"ffmpegkit", @"ffmpegkit"],
- @[@"FFmpegKit", @"FFmpegKit"],
- ];
-
- NSArray *searchDirs = YouModFFmpegKitSearchDirectories();
- if (searchDirs.count == 0) {
- YouModAppendFFmpegKitLoadEntry(@"[YouMod] Error: Bundled Frameworks directory not found.");
- return;
- }
-
- // Only iterate through our controlled bundle directory
- for (NSString *directory in searchDirs) {
- for (NSArray *framework in frameworks) {
- // This helper uses dlopen on the direct path within our bundle
- YouModLoadFrameworkBinary(directory, framework.firstObject, framework.lastObject);
- }
-
- if (NSClassFromString(@"FFmpegKit")) {
- YouModAppendFFmpegKitLoadEntry(@"[YouMod] Success: FFmpegKit loaded from bundle.");
- return;
- }
- }
-
- YouModAppendFFmpegKitLoadEntry(@"[YouMod] Critical: FFmpegKit could not be found in YouMod.bundle.");
-}
-
-static Class YouModFFmpegKitClass(void) {
- Class ffmpegKitClass = NSClassFromString(@"FFmpegKit");
- if (!ffmpegKitClass) {
- YouModLoadFFmpegKitIfNeeded();
- ffmpegKitClass = NSClassFromString(@"FFmpegKit");
- }
- return ffmpegKitClass;
-}
-
-static BOOL YouModFFmpegKitAvailable(void) {
- Class ffmpegKitClass = YouModFFmpegKitClass();
- return ffmpegKitClass && [ffmpegKitClass respondsToSelector:@selector(executeWithArgumentsAsync:withCompleteCallback:withLogCallback:withStatisticsCallback:)];
-}
-
-static NSString *YouModFFmpegKitDiagnosticText(YouModAudioOutputFormat *outputFormat, YouModMediaFormat *sourceFormat, NSString *videoID) {
- YouModLoadFFmpegKitIfNeeded();
-
- Class ffmpegKitClass = NSClassFromString(@"FFmpegKit");
- SEL executeSelector = @selector(executeWithArgumentsAsync:withCompleteCallback:withLogCallback:withStatisticsCallback:);
- NSMutableArray *lines = [NSMutableArray array];
- NSBundle *mainBundle = NSBundle.mainBundle;
- NSString *resourcePath = mainBundle.resourcePath ?: @"";
- NSString *privateFrameworksPath = mainBundle.privateFrameworksPath ?: @"";
- NSString *executablePath = mainBundle.executablePath ?: @"";
- NSString *bundlePath = [resourcePath stringByAppendingPathComponent:@"YouMod.bundle"];
- NSString *packageFrameworkPath = [resourcePath stringByAppendingPathComponent:@"YouMod.bundle/Frameworks"];
-
- [lines addObject:@"FFmpegKit lookup"];
- [lines addObject:[NSString stringWithFormat:@"videoID=%@", videoID ?: @""]];
- [lines addObject:[NSString stringWithFormat:@"requestedFormat=%@ (%@)", outputFormat.title ?: @"", outputFormat.identifier ?: @""]];
- [lines addObject:[NSString stringWithFormat:@"sourceMime=%@", sourceFormat.mimeType ?: @""]];
- [lines addObject:[NSString stringWithFormat:@"sourceQuality=%@", sourceFormat.qualityLabel ?: @""]];
- [lines addObject:[NSString stringWithFormat:@"sourceBytes=%llu", sourceFormat.contentLength]];
- [lines addObject:[NSString stringWithFormat:@"mainBundle=%@", mainBundle.bundlePath ?: @""]];
- [lines addObject:[NSString stringWithFormat:@"resourcePath=%@", resourcePath]];
- [lines addObject:[NSString stringWithFormat:@"privateFrameworksPath=%@", privateFrameworksPath]];
- [lines addObject:[NSString stringWithFormat:@"executablePath=%@", executablePath]];
- [lines addObject:[NSString stringWithFormat:@"YouMod.bundle exists=%@", [NSFileManager.defaultManager fileExistsAtPath:bundlePath] ? @"YES" : @"NO"]];
- [lines addObject:[NSString stringWithFormat:@"YouMod.bundle/Frameworks exists=%@", [NSFileManager.defaultManager fileExistsAtPath:packageFrameworkPath] ? @"YES" : @"NO"]];
- [lines addObject:[NSString stringWithFormat:@"FFmpegKit class=%@", ffmpegKitClass ? @"YES" : @"NO"]];
- [lines addObject:[NSString stringWithFormat:@"FFmpegKit execute selector=%@", [ffmpegKitClass respondsToSelector:executeSelector] ? @"YES" : @"NO"]];
- [lines addObject:[NSString stringWithFormat:@"ReturnCode class=%@", NSClassFromString(@"ReturnCode") ? @"YES" : @"NO"]];
- [lines addObject:@"searchDirectories:"];
- for (NSString *directory in YouModFFmpegKitSearchDirectories()) {
- BOOL isDirectory = NO;
- BOOL exists = [NSFileManager.defaultManager fileExistsAtPath:directory isDirectory:&isDirectory];
- [lines addObject:[NSString stringWithFormat:@" %@ exists=%@ directory=%@", directory, exists ? @"YES" : @"NO", isDirectory ? @"YES" : @"NO"]];
- }
-
- NSMutableArray *entries = YouModFFmpegKitLoadEntries();
- [lines addObject:@"dlopenAttempts:"];
- @synchronized(entries) {
- [lines addObjectsFromArray:entries];
- }
- return [lines componentsJoinedByString:@"\n"];
-}
-
-static void YouModCancelFFmpegKit(void) {
- Class ffmpegKitClass = YouModFFmpegKitClass();
- if ([ffmpegKitClass respondsToSelector:@selector(cancel)])
- ((void (*)(Class, SEL))objc_msgSend)(ffmpegKitClass, @selector(cancel));
-}
-
-static NSError *YouModFFmpegErrorFromSession(id session) {
- NSString *failure = YouModStringFromSelector(session, @selector(getFailStackTrace));
- NSString *message = failure.length ? failure : @"FFmpeg failed";
- return [NSError errorWithDomain:@"YouMod" code:7 userInfo:@{NSLocalizedDescriptionKey: message}];
-}
-
static BOOL YouModPathExtensionIsPhotosVideo(NSString *extension) {
- NSString *lower = extension.lowercaseString ?: @"";
- return [@[@"mp4", @"m4v", @"mov"] containsObject:lower];
-}
-
-static BOOL YouModStartFFmpegKitMerge(NSURL *videoURL, NSURL *audioURL, NSURL *outputURL, unsigned long long durationMs, void (^progress)(float progress), YouModMergeCompletion completion) {
- Class ffmpegKitClass = YouModFFmpegKitClass();
- SEL executeSelector = @selector(executeWithArgumentsAsync:withCompleteCallback:withLogCallback:withStatisticsCallback:);
- if (![ffmpegKitClass respondsToSelector:executeSelector]) return NO;
-
- NSMutableArray *arguments = [@[
- @"-y",
- @"-i", videoURL.path,
- @"-i", audioURL.path,
- @"-map", @"0:v:0",
- @"-map", @"1:a:0",
- ] mutableCopy];
- if (durationMs > 0)
- [arguments addObjectsFromArray:@[@"-t", YouModDurationSecondsArgument(durationMs)]];
- [arguments addObjectsFromArray:@[
- @"-c", @"copy",
- @"-shortest",
- @"-avoid_negative_ts", @"make_zero",
- ]];
- if (YouModPathExtensionIsPhotosVideo(outputURL.pathExtension))
- [arguments addObjectsFromArray:@[@"-movflags", @"+faststart"]];
- [arguments addObject:outputURL.path];
-
- id completeBlock = [^(id session) {
- Class returnCodeClass = NSClassFromString(@"ReturnCode");
- id returnCode = YouModObjectFromSelector(session, @selector(getReturnCode));
- BOOL success = NO;
- if ([returnCodeClass respondsToSelector:@selector(isSuccess:)])
- success = ((BOOL (*)(Class, SEL, id))objc_msgSend)(returnCodeClass, @selector(isSuccess:), returnCode);
-
- NSError *error = success ? nil : YouModFFmpegErrorFromSession(session);
- dispatch_async(dispatch_get_main_queue(), ^{
- if (success && [NSFileManager.defaultManager fileExistsAtPath:outputURL.path]) {
- completion(YES, nil);
- } else {
- completion(NO, error ?: [NSError errorWithDomain:@"YouMod" code:7 userInfo:@{NSLocalizedDescriptionKey: @"Merge failed"}]);
- }
- });
- } copy];
-
- id statisticsBlock = durationMs ? [^(id statistics) {
- if (!progress || ![statistics respondsToSelector:@selector(getTime)]) return;
- double timeMs = ((double (*)(id, SEL))objc_msgSend)(statistics, @selector(getTime));
- if (!isfinite(timeMs) || timeMs <= 0.0) return;
- float mergeProgress = 0.985f + (0.01f * fminf((float)(timeMs / (double)durationMs), 1.0f));
- dispatch_async(dispatch_get_main_queue(), ^{
- progress(mergeProgress);
- });
- } copy] : nil;
-
- ((id (*)(Class, SEL, NSArray *, id, id, id))objc_msgSend)(ffmpegKitClass, executeSelector, arguments, completeBlock, nil, statisticsBlock);
- return YES;
-}
-
-static BOOL YouModStartFFmpegKitAudioConvert(NSURL *inputURL, NSURL *outputURL, YouModAudioOutputFormat *outputFormat, unsigned long long durationMs, void (^progress)(float progress), YouModMergeCompletion completion) {
- Class ffmpegKitClass = YouModFFmpegKitClass();
- SEL executeSelector = @selector(executeWithArgumentsAsync:withCompleteCallback:withLogCallback:withStatisticsCallback:);
- if (![ffmpegKitClass respondsToSelector:executeSelector] || outputFormat.ffmpegArguments.count == 0) return NO;
-
- NSMutableArray *arguments = [@[@"-y", @"-i", inputURL.path] mutableCopy];
- [arguments addObjectsFromArray:outputFormat.ffmpegArguments];
- [arguments addObject:outputURL.path];
-
- id completeBlock = [^(id session) {
- Class returnCodeClass = NSClassFromString(@"ReturnCode");
- id returnCode = YouModObjectFromSelector(session, @selector(getReturnCode));
- BOOL success = NO;
- if ([returnCodeClass respondsToSelector:@selector(isSuccess:)])
- success = ((BOOL (*)(Class, SEL, id))objc_msgSend)(returnCodeClass, @selector(isSuccess:), returnCode);
-
- NSError *error = success ? nil : YouModFFmpegErrorFromSession(session);
- dispatch_async(dispatch_get_main_queue(), ^{
- if (success && [NSFileManager.defaultManager fileExistsAtPath:outputURL.path]) {
- completion(YES, nil);
- } else {
- completion(NO, error ?: [NSError errorWithDomain:@"YouMod" code:13 userInfo:@{NSLocalizedDescriptionKey: @"Conversion failed"}]);
- }
- });
- } copy];
-
- id statisticsBlock = durationMs ? [^(id statistics) {
- if (!progress || ![statistics respondsToSelector:@selector(getTime)]) return;
- double timeMs = ((double (*)(id, SEL))objc_msgSend)(statistics, @selector(getTime));
- if (!isfinite(timeMs) || timeMs <= 0.0) return;
- float convertProgress = 0.985f + (0.01f * fminf((float)(timeMs / (double)durationMs), 1.0f));
- dispatch_async(dispatch_get_main_queue(), ^{
- progress(convertProgress);
- });
- } copy] : nil;
-
- ((id (*)(Class, SEL, NSArray *, id, id, id))objc_msgSend)(ffmpegKitClass, executeSelector, arguments, completeBlock, nil, statisticsBlock);
- return YES;
+ NSString *lower = extension.lowercaseString;
+ return [@[@"mp4"] containsObject:lower];
}
static NSString *YouModMimeDetail(NSString *mimeType) {
- NSString *lower = mimeType.lowercaseString ?: @"";
+ NSString *lower = mimeType.lowercaseString;
if ([lower containsString:@"mp4"]) return @"MP4";
- if ([lower containsString:@"webm"]) return @"WebM";
- if ([lower containsString:@"mp3"]) return @"MP3";
- if ([lower containsString:@"aac"]) return @"AAC";
- return mimeType.length ? mimeType : @"Stream";
-}
-
-static NSString *YouModFileExtensionForFormat(YouModMediaFormat *format, NSString *fallbackExtension) {
- NSString *lower = format.mimeType.lowercaseString ?: @"";
- if ([lower containsString:@"webm"]) return @"webm";
- if ([lower containsString:@"matroska"]) return @"mkv";
- if ([lower containsString:@"quicktime"]) return @"mov";
- if ([lower containsString:@"m4a"]) return @"m4a";
- if ([lower containsString:@"mp4"]) return @"mp4";
- return fallbackExtension ?: @"mp4";
+ return mimeType;
}
-static BOOL YouModFormatLooksMP4Family(YouModMediaFormat *format) {
- NSString *mime = format.mimeType.lowercaseString ?: @"";
- NSString *extension = YouModFileExtensionForFormat(format, @"").lowercaseString ?: @"";
- return [mime containsString:@"mp4"] || [mime containsString:@"m4a"] || [mime containsString:@"quicktime"] || [@[@"mp4", @"m4a", @"m4v", @"mov"] containsObject:extension];
+static NSString *YouModFileExtensionForFormat(YouModMediaFormat *format) {
+ NSString *lower = format.mimeType.lowercaseString;
+ if ([lower containsString:@"mp4a"]) return @"m4a";
+ if ([lower containsString:@"mp4"]) return @"mp4";
+ return nil;
}
-static BOOL YouModFormatLooksWebM(YouModMediaFormat *format) {
- NSString *mime = format.mimeType.lowercaseString ?: @"";
- NSString *extension = YouModFileExtensionForFormat(format, @"").lowercaseString ?: @"";
- return [mime containsString:@"webm"] || [extension isEqualToString:@"webm"];
+static BOOL YouModFormatLooksMP4Family(YouModMediaFormat *format) {
+ NSString *mime = format.mimeType.lowercaseString;
+ NSString *extension = YouModFileExtensionForFormat(format);
+ return [mime containsString:@"mp4"] || [mime containsString:@"mp4a"] || [@[@"mp4", @"mp4a"] containsObject:extension];
}
static NSString *YouModMergedVideoOutputExtension(YouModMediaFormat *videoFormat, YouModMediaFormat *audioFormat) {
if (YouModFormatLooksMP4Family(videoFormat) && YouModFormatLooksMP4Family(audioFormat)) return @"mp4";
- if (YouModFormatLooksWebM(videoFormat) && YouModFormatLooksWebM(audioFormat)) return @"webm";
- return @"mkv";
+ return nil;
}
static BOOL YouModVideoFileCanUseAVFoundation(NSURL *fileURL) {
@@ -1103,324 +650,121 @@ static BOOL YouModVideoFileCanSaveToPhotos(NSURL *fileURL) {
return YouModPathExtensionIsPhotosVideo(fileURL.pathExtension);
}
-static YouModAudioOutputFormat *YouModAudioOutputFormatMake(NSString *identifier, NSString *title, NSString *subtitle, NSString *fileExtension, NSArray *ffmpegArguments, BOOL passthroughWhenCompatible, BOOL supported) {
- YouModAudioOutputFormat *format = [YouModAudioOutputFormat new];
- format.identifier = identifier;
- format.title = title;
- format.subtitle = subtitle;
- format.fileExtension = fileExtension;
- format.ffmpegArguments = ffmpegArguments;
- format.passthroughWhenCompatible = passthroughWhenCompatible;
- format.supported = supported;
- return format;
-}
-
-static NSArray *YouModAudioOutputFormats(void) {
- static NSArray *formats = nil;
- static dispatch_once_t onceToken;
- dispatch_once(&onceToken, ^{
- formats = @[
- YouModAudioOutputFormatMake(@"m4a", @"M4A", @"AAC container, passthrough when possible", @"m4a", @[@"-map", @"0:a:0", @"-vn", @"-c:a", @"aac", @"-b:a", @"192k", @"-movflags", @"+faststart"], YES, YES),
- YouModAudioOutputFormatMake(@"aac", @"AAC", @"Lossy (192k)", @"aac", @[@"-map", @"0:a:0", @"-vn", @"-c:a", @"aac", @"-b:a", @"192k", @"-f", @"adts"], YES, YES),
- YouModAudioOutputFormatMake(@"mp3", @"MP3", @"Lossy, widely compatible", @"mp3", @[@"-map", @"0:a:0", @"-vn", @"-c:a", @"libmp3lame", @"-q:a", @"2"], NO, YES),
- YouModAudioOutputFormatMake(@"opus", @"Opus", @"Lossy, small file size", @"opus", @[@"-map", @"0:a:0", @"-vn", @"-c:a", @"libopus", @"-b:a", @"160k", @"-vbr", @"on"], NO, YES),
- YouModAudioOutputFormatMake(@"ogg", @"OGG", @"Vorbis lossy", @"ogg", @[@"-map", @"0:a:0", @"-vn", @"-c:a", @"libvorbis", @"-q:a", @"6"], NO, YES),
- YouModAudioOutputFormatMake(@"flac", @"FLAC", @"Lossless compressed", @"flac", @[@"-map", @"0:a:0", @"-vn", @"-c:a", @"flac", @"-compression_level", @"8"], NO, YES),
- YouModAudioOutputFormatMake(@"alac", @"ALAC", @"Apple lossless (M4A)", @"m4a", @[@"-map", @"0:a:0", @"-vn", @"-c:a", @"alac", @"-movflags", @"+faststart"], NO, YES),
- YouModAudioOutputFormatMake(@"wav", @"WAV", @"Uncompressed PCM", @"wav", @[@"-map", @"0:a:0", @"-vn", @"-c:a", @"pcm_s24le"], NO, YES),
- YouModAudioOutputFormatMake(@"aiff", @"AIFF", @"Apple PCM", @"aiff", @[@"-map", @"0:a:0", @"-vn", @"-c:a", @"pcm_s24be"], NO, YES),
- ];
- });
- return formats;
-}
-
-static YouModAudioOutputFormat *YouModDefaultAudioOutputFormat(void) {
- return [YouModAudioOutputFormats() firstObject];
-}
-
-static BOOL YouModAudioOutputFormatCanPassthrough(YouModAudioOutputFormat *outputFormat, YouModMediaFormat *sourceFormat) {
- if (!outputFormat.passthroughWhenCompatible) return NO;
- NSString *identifier = outputFormat.identifier.lowercaseString ?: @"";
- NSString *mime = sourceFormat.mimeType.lowercaseString ?: @"";
- NSString *extension = YouModFileExtensionForFormat(sourceFormat, @"").lowercaseString ?: @"";
- if ([identifier isEqualToString:@"m4a"] || [identifier isEqualToString:@"aac"])
- return [extension isEqualToString:@"m4a"] || [mime containsString:@"mp4"] || [mime containsString:@"m4a"];
- return NO;
-}
-
-static NSString *YouModAudioOutputFileExtension(YouModAudioOutputFormat *outputFormat, YouModMediaFormat *sourceFormat, BOOL passthrough) {
- NSString *identifier = outputFormat.identifier.lowercaseString ?: @"";
- NSString *mime = sourceFormat.mimeType.lowercaseString ?: @"";
- if (passthrough && ([identifier isEqualToString:@"m4a"] || [identifier isEqualToString:@"aac"]) && ([mime containsString:@"mp4"] || [mime containsString:@"m4a"]))
- return @"m4a";
- return outputFormat.fileExtension ?: YouModFileExtensionForFormat(sourceFormat, @"m4a");
-}
-
-static NSString *YouModAudioOutputSubtitle(YouModAudioOutputFormat *outputFormat) {
- return [NSString stringWithFormat:@"%@", outputFormat.subtitle];
-}
-
-static NSString *YouModFormatSubtitle(YouModMediaFormat *format) {
- NSMutableArray *parts = [NSMutableArray array];
- NSString *language = format.languageName.length ? format.languageName : format.languageCode;
- if (language.length) [parts addObject:language];
- if (format.drcAudio) [parts addObject:@"DRC"];
- NSString *detail = YouModMimeDetail(format.mimeType);
- if (detail.length) [parts addObject:detail];
- NSString *size = YouModByteCount(format.contentLength);
- if (size.length) [parts addObject:size];
- return [parts componentsJoinedByString:@" - "];
-}
-
-static NSString *YouModVideoIDForPlayer(YTPlayerViewController *player) {
- NSString *videoID = [player contentVideoID];
- if (videoID.length == 0)
- videoID = [player currentVideoID];
- return videoID;
+static NSString *YouModFormatSubtitle(YouModMediaFormat *format, BOOL video) {
+ if (video) {
+ NSMutableArray *parts = [NSMutableArray array];
+ NSString *detail = YouModMimeDetail(format.mimeType);
+ if (detail.length) [parts addObject:detail];
+ NSString *size = YouModByteCount(format.contentLength);
+ if (size.length) [parts addObject:size];
+ return [parts componentsJoinedByString:@" - "];
+ }
+ NSString *cut = [[format.idp componentsSeparatedByString:@"."] firstObject];
+ return cut;
}
-static NSArray *YouModPlayerResponsesForPlayer(YTPlayerViewController *player) {
- NSMutableArray *responses = [NSMutableArray array];
- id response = YouModObjectFromSelector(player, @selector(contentPlayerResponse));
- if (response) [responses addObject:response];
-
- id activeVideo = YouModObjectFromSelector(player, @selector(activeVideo));
- response = YouModObjectFromSelector(activeVideo, @selector(contentPlayerResponse));
- if (response && ![responses containsObject:response]) [responses addObject:response];
- return responses.copy;
+static YTIPlayerResponse *YouModPlayerDataForPlayer(YTPlayerViewController *player) {
+ YTPlayerResponse *response;
+ if ([player respondsToSelector:@selector(contentPlayerResponse)]) {
+ response = player.contentPlayerResponse;
+ } else {
+ response = player.playerResponse;
+ }
+ YTIPlayerResponse *playerData = response.playerData;
+ return playerData;
}
-// Where is this going to?
static NSArray *YouModCaptionTracksForPlayer(YTPlayerViewController *player) {
- for (id response in YouModPlayerResponsesForPlayer(player)) {
- id playerData = YouModObjectFromSelector(response, @selector(playerData)) ?: response;
- id captions = YouModObjectFromSelector(playerData, @selector(captions));
- id tracklistRenderer = YouModObjectFromSelector(captions, @selector(playerCaptionsTracklistRenderer));
- NSArray *tracks = YouModObjectFromSelector(tracklistRenderer, @selector(captionTracksArray));
- if (tracks.count > 0) return tracks;
- }
+ YTIPlayerResponse *playerData = YouModPlayerDataForPlayer(player);
+ YTICaptionsSupportedRenderers *captions = playerData.captions;
+ YTIPlayerCaptionsTrackListRenderer *tracklistRenderer = captions.playerCaptionsTracklistRenderer;
+ NSArray *tracks = tracklistRenderer.captionTracksArray;
+ if (tracks.count > 0) return tracks;
return nil;
}
-static id YouModPlayerDataForPlayer(YTPlayerViewController *player) {
- id response = YouModPlayerResponsesForPlayer(player).firstObject;
- id playerData = YouModObjectFromSelector(response, @selector(playerData));
- return playerData ?: response;
+static NSString *YouModAuthorForPlayer(YTPlayerViewController *player) {
+ YTIPlayerResponse *playerData = YouModPlayerDataForPlayer(player);
+ YTIVideoDetails *details = playerData.videoDetails;
+ return details.author;
}
static NSString *YouModTitleForPlayer(YTPlayerViewController *player) {
- id playerData = YouModPlayerDataForPlayer(player);
- id details = YouModObjectFromSelector(playerData, @selector(videoDetails));
- NSString *title = YouModStringFromSelector(details, @selector(title));
- NSString *author = YouModStringFromSelector(details, @selector(author));
- if (author.length && title.length) {
- return [NSString stringWithFormat:@"%@ - %@", author, title];
- } else if (title.length) {
- return title;
- }
- NSString *videoID = YouModVideoIDForPlayer(player);
- return videoID.length ? [NSString stringWithFormat:@"YouTube %@", videoID] : @"YouTube Video";
+ YTIPlayerResponse *playerData = YouModPlayerDataForPlayer(player);
+ YTIVideoDetails *details = playerData.videoDetails;
+ return details.title;
}
-static NSArray *YouModAdaptiveFormatObjectsForPlayer(YTPlayerViewController *player) {
- NSMutableArray *formats = [NSMutableArray array];
- NSMutableSet *seenPointers = [NSMutableSet set];
-
- void (^appendFormats)(NSArray *) = ^(NSArray *candidateFormats) {
- if (![candidateFormats isKindOfClass:NSArray.class]) return;
- for (id format in candidateFormats) {
- NSString *key = [NSString stringWithFormat:@"%p", format];
- if ([seenPointers containsObject:key]) continue;
- [seenPointers addObject:key];
- [formats addObject:format];
- }
- };
-
- id activeVideo = YouModObjectFromSelector(player, @selector(activeVideo));
- id streamingData = YouModObjectFromSelector(activeVideo, @selector(streamingData));
- appendFormats(YouModObjectFromSelector(streamingData, @selector(adaptiveStreams)));
- appendFormats(YouModObjectFromSelector(activeVideo, @selector(selectableVideoFormats)));
-
- for (id response in YouModPlayerResponsesForPlayer(player)) {
- id playerData = YouModObjectFromSelector(response, @selector(playerData)) ?: response;
- id responseStreamingData = YouModObjectFromSelector(playerData, @selector(streamingData));
- appendFormats(YouModObjectFromSelector(responseStreamingData, @selector(adaptiveFormatsArray)));
- }
-
- return formats.copy;
+static NSString *YouModDescriptionForPlayer(YTPlayerViewController *player) {
+ YTIPlayerResponse *playerData = YouModPlayerDataForPlayer(player);
+ YTIVideoDetails *details = playerData.videoDetails;
+ NSString *description = details.shortDescription;
+ return description;
}
-static YouModMediaFormat *YouModMediaFormatFromStream(id stream, BOOL video) {
- id formatStream = YouModObjectFromSelector(stream, @selector(formatStream));
- NSString *url = YouModStringFromSelector(stream, @selector(URL));
- if (url.length == 0) url = YouModStringFromSelector(formatStream, @selector(URL));
- if (url.length == 0) url = YouModStringFromSelector(stream, @selector(url));
- if (url.length == 0) url = YouModStringFromSelector(formatStream, @selector(url));
- if (url.length == 0) return nil;
-
- NSString *mimeType = YouModStringFromSelector(stream, @selector(mimeType));
- if (mimeType.length == 0) mimeType = YouModStringFromSelector(formatStream, @selector(mimeType));
- NSString *lowerMime = mimeType.lowercaseString ?: @"";
- BOOL streamSaysVideo = YouModBoolFromSelector(stream, @selector(isVideo)) || YouModBoolFromSelector(formatStream, @selector(isVideo));
- BOOL streamSaysAudio = YouModBoolFromSelector(stream, @selector(isAudio)) || YouModBoolFromSelector(formatStream, @selector(isAudio));
- NSInteger itag = YouModIntegerFromSelector(stream, @selector(itag));
- if (itag == 0) itag = YouModIntegerFromSelector(formatStream, @selector(itag));
+static NSArray *YouModAdaptiveFormatObjectsForPlayer(YTPlayerViewController *player) {
+ YTIPlayerResponse *playerData = YouModPlayerDataForPlayer(player);
+ YTIStreamingData *streamingData = playerData.streamingData;
+ return streamingData.adaptiveFormatsArray;
+}
- NSSet *mp4VideoItags = [NSSet setWithObjects:@18, @22, @37, @38, @59, @78, @133, @134, @135, @136, @137, @160, @212, @264, @266, @298, @299, nil];
- NSSet *m4aAudioItags = [NSSet setWithObjects:@139, @140, @141, @256, @258, @325, @328, nil];
- BOOL itagMatches = video ? [mp4VideoItags containsObject:@(itag)] : [m4aAudioItags containsObject:@(itag)];
- BOOL typeMatches = video ? ([lowerMime containsString:@"video/"] || streamSaysVideo || itagMatches) : ([lowerMime containsString:@"audio/"] || streamSaysAudio || itagMatches);
+static YouModMediaFormat *YouModMediaFormatFromStream(YTIFormatStream *stream, BOOL video) {
+ NSString *url = stream.URL;
+ NSString *mimeType = stream.mimeType;
+ NSString *lowerMime = mimeType.lowercaseString;
+ BOOL typeMatches = video ? ([lowerMime containsString:@"video/"]) : ([lowerMime containsString:@"audio/"]);
if (!typeMatches) return nil;
- BOOL mimeLooksMP4 = [lowerMime containsString:@"mp4"] || [lowerMime containsString:@"m4a"];
- BOOL canRemuxWithFFmpeg = YouModFFmpegKitAvailable();
- if (mimeType.length && !mimeLooksMP4 && !itagMatches && !canRemuxWithFFmpeg) return nil;
+ BOOL mimeLooksMP4 = [lowerMime containsString:@"mp4"] && ([lowerMime containsString:@"avc1"] || ([lowerMime containsString:@"mp4a"] && stream.itag == 140));
+ if (mimeType.length && !mimeLooksMP4) return nil;
YouModMediaFormat *format = [YouModMediaFormat new];
format.source = stream;
format.video = video;
format.urlString = YouModURLStringWithCPN(url);
- format.mimeType = mimeType.length ? mimeType : (video ? @"video/mp4" : @"audio/mp4");
- NSInteger height = YouModIntegerFromSelector(stream, @selector(height));
- if (height == 0) height = YouModIntegerFromSelector(formatStream, @selector(height));
- NSInteger fps = YouModIntegerFromSelector(stream, @selector(fps));
- if (fps == 0) fps = YouModIntegerFromSelector(formatStream, @selector(fps));
- if (fps == 0) fps = YouModIntegerFromSelector(stream, @selector(framesPerSecond));
- if (fps == 0) fps = YouModIntegerFromSelector(formatStream, @selector(framesPerSecond));
- if (fps == 0) fps = YouModIntegerFromSelector(stream, @selector(frameRate));
- if (fps == 0) fps = YouModIntegerFromSelector(formatStream, @selector(frameRate));
- fps = YouModNormalizedFPS(fps);
- format.fps = fps;
- format.qualityLabel = YouModStringFromSelector(stream, @selector(qualityLabel));
- if (format.qualityLabel.length == 0) format.qualityLabel = YouModStringFromSelector(formatStream, @selector(qualityLabel));
- if (video) {
- NSInteger labelHeight = YouModResolutionFromQuality(format.qualityLabel);
- NSInteger labelFPS = YouModFPSFromQuality(format.qualityLabel);
- if (labelHeight == 960) format.qualityLabel = YouModQualityLabel(labelHeight, fps ?: labelFPS, nil);
- else if (labelFPS == 0 && fps > 0) format.qualityLabel = YouModQualityLabel(height, fps, format.qualityLabel);
- if (format.qualityLabel.length == 0) format.qualityLabel = YouModQualityLabel(height, fps, nil);
- }
- if (format.qualityLabel.length == 0 && !video) format.qualityLabel = @"Audio";
+ format.mimeType = mimeType;
+ int height = stream.height;
+ if (video && height > 1080) return nil;
+ format.resolution = height;
+ format.fps = stream.fps;
+ format.qualityLabel = stream.qualityLabel;
+ if ([stream.qualityLabel hasSuffix:@"HDR"]) return nil;
if (!video) {
- NSString *languageCode = YouModStringFromSelector(stream, @selector(languageCode));
- if (languageCode.length == 0) languageCode = YouModStringFromSelector(formatStream, @selector(languageCode));
- if (languageCode.length == 0) languageCode = YouModStringFromSelector(stream, @selector(language));
- if (languageCode.length == 0) languageCode = YouModStringFromSelector(formatStream, @selector(language));
- format.languageCode = languageCode;
-
- NSString *languageName = YouModStringFromSelector(stream, @selector(languageName));
- if (languageName.length == 0) languageName = YouModStringFromSelector(formatStream, @selector(languageName));
- if (languageName.length == 0) languageName = YouModStringFromSelector(stream, @selector(displayName));
- if (languageName.length == 0) languageName = YouModStringFromSelector(formatStream, @selector(displayName));
- format.languageName = languageName.length ? languageName : languageCode;
-
- NSMutableArray *audioTraits = [NSMutableArray array];
- for (NSString *value in @[
- mimeType ?: @"",
- format.qualityLabel ?: @"",
- YouModStringFromSelector(stream, @selector(audioTrack)) ?: @"",
- YouModStringFromSelector(formatStream, @selector(audioTrack)) ?: @"",
- YouModStringFromSelector(stream, @selector(audioTrackType)) ?: @"",
- YouModStringFromSelector(formatStream, @selector(audioTrackType)) ?: @"",
- YouModStringFromSelector(stream, @selector(audioTrackDisplayName)) ?: @"",
- YouModStringFromSelector(formatStream, @selector(audioTrackDisplayName)) ?: @"",
- ]) {
- if (value.length) [audioTraits addObject:value];
- }
- format.drcAudio = [[audioTraits componentsJoinedByString:@" "] localizedCaseInsensitiveContainsString:@"drc"];
- }
- if (YouModBoolFromSelector(stream, @selector(hasContentLength)) || [stream respondsToSelector:@selector(contentLength)])
- format.contentLength = YouModUnsignedLongLongFromSelector(stream, @selector(contentLength));
- if (format.contentLength == 0 && (YouModBoolFromSelector(formatStream, @selector(hasContentLength)) || [formatStream respondsToSelector:@selector(contentLength)]))
- format.contentLength = YouModUnsignedLongLongFromSelector(formatStream, @selector(contentLength));
- format.durationMs = YouModUnsignedLongLongFromSelector(stream, @selector(approxDurationMs));
- if (format.durationMs == 0) format.durationMs = YouModUnsignedLongLongFromSelector(formatStream, @selector(approxDurationMs));
-
- NSMutableDictionary *headers = [NSMutableDictionary dictionary];
- NSDictionary *streamHeaders = YouModObjectFromSelector(stream, @selector(httpHeaders));
- if (![streamHeaders isKindOfClass:NSDictionary.class]) streamHeaders = YouModObjectFromSelector(formatStream, @selector(httpHeaders));
- if (![streamHeaders isKindOfClass:NSDictionary.class]) streamHeaders = YouModObjectFromSelector(stream, @selector(headers));
- if (![streamHeaders isKindOfClass:NSDictionary.class]) streamHeaders = YouModObjectFromSelector(formatStream, @selector(headers));
- if ([streamHeaders isKindOfClass:NSDictionary.class]) {
- for (id key in streamHeaders) {
- id value = streamHeaders[key];
- if ([key isKindOfClass:NSString.class] && [value isKindOfClass:NSString.class])
- headers[key] = value;
+ YTIAudioTrack *audio = stream.audioTrack;
+ NSString *audioidp = audio.id_p;
+ if (audio.hasId_p) {
+ if (IS_ENABLED(UseOrigAudio) && ![audioidp hasSuffix:@".4"]) return nil;
+ format.qualityLabel = audio.displayName;
+ format.idp = audioidp;
}
}
- if (!YouModHTTPHeadersContainField(headers, @"Origin"))
- headers[@"Origin"] = @"https://www.youtube.com";
- if (!YouModHTTPHeadersContainField(headers, @"Referer"))
- headers[@"Referer"] = @"https://www.youtube.com/";
- format.httpHeaders = headers;
+ format.contentLength = stream.contentLength;
+ format.durationMs = stream.approxDurationMs;
+ format.itag = stream.itag;
return format;
}
-static NSInteger YouModResolutionFromQuality(NSString *quality) {
- NSScanner *scanner = [NSScanner scannerWithString:quality ?: @""];
- NSInteger value = 0;
- [scanner scanInteger:&value];
- return value;
-}
-
-static NSInteger YouModFPSFromQuality(NSString *quality) {
- NSString *lower = quality.lowercaseString ?: @"";
- NSRange pRange = [lower rangeOfString:@"p"];
- if (pRange.location != NSNotFound && pRange.location + 1 < lower.length) {
- NSString *afterP = [lower substringFromIndex:pRange.location + 1];
- NSScanner *scanner = [NSScanner scannerWithString:afterP];
- NSInteger fps = 0;
- if ([scanner scanInteger:&fps] && fps > 0) return fps;
- }
- if ([lower containsString:@"60fps"] || [lower containsString:@"60 fps"]) return 60;
- if ([lower containsString:@"30fps"] || [lower containsString:@"30 fps"]) return 30;
- return 0;
-}
-
-static NSInteger YouModNormalizedFPS(NSInteger fps) {
- if (fps >= 50 && fps <= 61) return 60;
- if (fps >= 24 && fps <= 31) return 30;
- return fps;
-}
-
-static NSInteger YouModDisplayHeightForVideoHeight(NSInteger height) {
- if (height >= 900 && height < 1080) return 1080;
- return height;
-}
-
-static NSString *YouModQualityLabel(NSInteger height, NSInteger fps, NSString *fallback) {
- height = YouModDisplayHeightForVideoHeight(height);
- fps = YouModNormalizedFPS(fps);
- if (height > 0 && fps > 0) return [NSString stringWithFormat:@"%ldp%ld", (long)height, (long)fps];
- if (height > 0) return [NSString stringWithFormat:@"%ldp", (long)height];
- if (fallback.length && fps > 0 && ![fallback.lowercaseString containsString:@"fps"])
- return [NSString stringWithFormat:@"%@ %ldfps", fallback, (long)fps];
- return fallback;
-}
-
static NSArray *YouModFormatsForPlayer(YTPlayerViewController *player, BOOL video) {
NSMutableArray *formats = [NSMutableArray array];
- for (id stream in YouModAdaptiveFormatObjectsForPlayer(player)) {
+ for (YTIFormatStream *stream in YouModAdaptiveFormatObjectsForPlayer(player)) {
YouModMediaFormat *format = YouModMediaFormatFromStream(stream, video);
if (format) [formats addObject:format];
}
[formats sortUsingComparator:^NSComparisonResult(YouModMediaFormat *left, YouModMediaFormat *right) {
if (video) {
- NSInteger leftRes = YouModResolutionFromQuality(left.qualityLabel);
- NSInteger rightRes = YouModResolutionFromQuality(right.qualityLabel);
+ NSInteger leftRes = left.resolution;
+ NSInteger rightRes = right.resolution;
if (leftRes != rightRes) return leftRes > rightRes ? NSOrderedAscending : NSOrderedDescending;
- NSInteger leftFPS = left.fps ?: YouModFPSFromQuality(left.qualityLabel);
- NSInteger rightFPS = right.fps ?: YouModFPSFromQuality(right.qualityLabel);
+ NSInteger leftFPS = left.fps;
+ NSInteger rightFPS = right.fps;
if (leftFPS != rightFPS) return leftFPS > rightFPS ? NSOrderedAscending : NSOrderedDescending;
}
BOOL leftMP4 = YouModFormatLooksMP4Family(left);
BOOL rightMP4 = YouModFormatLooksMP4Family(right);
if (leftMP4 != rightMP4) return leftMP4 ? NSOrderedAscending : NSOrderedDescending;
-
- if (!video && IS_ENABLED(DownloadPreferDRCAudio) && left.drcAudio != right.drcAudio)
- return left.drcAudio ? NSOrderedAscending : NSOrderedDescending;
+
if (left.contentLength != right.contentLength)
return left.contentLength > right.contentLength ? NSOrderedAscending : NSOrderedDescending;
return NSOrderedSame;
@@ -1429,10 +773,10 @@ static NSArray *YouModFormatsForPlayer(YTPlayerViewControl
NSMutableArray *unique = [NSMutableArray array];
NSMutableSet *seen = [NSMutableSet set];
for (YouModMediaFormat *format in formats) {
- NSInteger fps = format.fps ?: YouModFPSFromQuality(format.qualityLabel);
+ NSInteger fps = format.fps;
NSString *key = video
- ? [NSString stringWithFormat:@"%@-%ld-%@", format.qualityLabel ?: @"", (long)fps, YouModMimeDetail(format.mimeType)]
- : [NSString stringWithFormat:@"%@-%@-%@-%@", format.qualityLabel ?: @"", format.languageCode ?: @"", format.drcAudio ? @"drc" : @"std", YouModMimeDetail(format.mimeType)];
+ ? [NSString stringWithFormat:@"%@-%ld-%@", format.qualityLabel, (long)fps, YouModMimeDetail(format.mimeType)]
+ : [NSString stringWithFormat:@"%@-%@", format.qualityLabel, YouModMimeDetail(format.mimeType)];
if ([seen containsObject:key]) continue;
[seen addObject:key];
[unique addObject:format];
@@ -1440,11 +784,6 @@ static NSArray *YouModFormatsForPlayer(YTPlayerViewControl
return unique.copy;
}
-static YouModMediaFormat *YouModBestAudioFormatForPlayer(YTPlayerViewController *player) {
- NSArray *audioFormats = YouModFormatsForPlayer(player, NO);
- return audioFormats.firstObject;
-}
-
static UIViewController *YouModPresenterForSender(UIView *sender, YTPlayerViewController *player) {
UIViewController *presenter = nil;
if ([sender respondsToSelector:@selector(_viewControllerForAncestor)])
@@ -1467,20 +806,14 @@ static YTPlayerViewController *YouModPlayerFromViewController(UIViewController *
static NSURL *YouModThumbnailURLForVideoID(NSString *videoID) {
if (videoID.length == 0) return nil;
- NSString *urlString = [NSString stringWithFormat:@"https://i.ytimg.com/vi/%@/maxresdefault.jpg", videoID];
+ NSString *urlString = [NSString stringWithFormat:@"https://i.ytimg.com/vi/%@/hqdefault.jpg", videoID];
return [NSURL URLWithString:urlString];
}
static void YouModRequestPhotoAccess(void (^completion)(BOOL granted)) {
- if (@available(iOS 14.0, *)) {
- [PHPhotoLibrary requestAuthorizationForAccessLevel:PHAccessLevelAddOnly handler:^(PHAuthorizationStatus status) {
- completion(status == PHAuthorizationStatusAuthorized || status == PHAuthorizationStatusLimited);
- }];
- } else {
- [PHPhotoLibrary requestAuthorization:^(PHAuthorizationStatus status) {
- completion(status == PHAuthorizationStatusAuthorized);
- }];
- }
+ [PHPhotoLibrary requestAuthorizationForAccessLevel:PHAccessLevelAddOnly handler:^(PHAuthorizationStatus status) {
+ completion(status == PHAuthorizationStatusAuthorized || status == PHAuthorizationStatusLimited);
+ }];
}
static void YouModSaveVideoToPhotos(NSURL *fileURL, UIViewController *presenter, void (^completion)(BOOL success, NSError *error)) {
@@ -1518,40 +851,25 @@ static void YouModShareFile(NSURL *fileURL, UIViewController *presenter) {
static void YouModPresentMenu(NSString *title, NSArray *items, UIViewController *presenter, UIView *sender) {
presenter = YouModTopViewController(presenter);
- Class sheetClass = NSClassFromString(@"YTDefaultSheetController");
- if (sheetClass && [sheetClass respondsToSelector:@selector(sheetControllerWithParentResponder:)]) {
- YTDefaultSheetController *sheet = [sheetClass sheetControllerWithParentResponder:presenter];
- Class actionClass = NSClassFromString(@"YTActionSheetAction");
- for (YouModMenuItem *item in items) {
- id action = nil;
- if ([actionClass respondsToSelector:@selector(actionWithTitle:subtitle:iconImage:handler:)]) {
- action = ((id (*)(Class, SEL, NSString *, NSString *, UIImage *, id))objc_msgSend)(actionClass, @selector(actionWithTitle:subtitle:iconImage:handler:), item.title, item.subtitle, item.iconImage, ^(__unused id action) {
- if (item.handler) item.handler();
- });
- } else {
- action = ((id (*)(Class, SEL, NSString *, NSInteger, id))objc_msgSend)(actionClass, @selector(actionWithTitle:style:handler:), item.title, 0, ^(__unused id action) {
- if (item.handler) item.handler();
- });
- }
- if (action) [sheet addAction:action];
+ YTDefaultSheetController *sheet = [%c(YTDefaultSheetController) sheetControllerWithParentResponder:presenter];
+ for (YouModMenuItem *item in items) {
+ YTActionSheetAction *action;
+ if (item.subtitle == nil) {
+ action = [%c(YTActionSheetAction) actionWithTitle:item.title iconImage:item.iconImage style:0 handler:^(__unused YTActionSheetAction *action) {
+ item.handler();
+ }];
+ } else {
+ action = [%c(YTActionSheetAction) actionWithTitle:item.title subtitle:item.subtitle iconImage:item.iconImage handler:^(__unused YTActionSheetAction *action) {
+ item.handler();
+ }];
}
- if (sender && [sheet respondsToSelector:@selector(presentFromView:animated:completion:)])
- [sheet presentFromView:sender animated:YES completion:nil];
- else
- [sheet presentFromViewController:presenter animated:YES completion:nil];
- return;
+ [sheet addAction:action];
}
-
- UIAlertController *alert = [UIAlertController alertControllerWithTitle:title message:nil preferredStyle:UIAlertControllerStyleActionSheet];
- for (YouModMenuItem *item in items) {
- NSString *rowTitle = item.subtitle.length ? [NSString stringWithFormat:@"%@\n%@", item.title, item.subtitle] : item.title;
- [alert addAction:[UIAlertAction actionWithTitle:rowTitle style:UIAlertActionStyleDefault handler:^(__unused UIAlertAction *action) {
- if (item.handler) item.handler();
- }]];
+ if (sender) {
+ [sheet presentFromView:sender animated:YES completion:nil];
+ } else {
+ [sheet presentFromViewController:presenter animated:YES completion:nil];
}
- [alert addAction:[UIAlertAction actionWithTitle:@"Cancel" style:UIAlertActionStyleCancel handler:nil]];
- alert.popoverPresentationController.sourceView = sender ?: presenter.view;
- [presenter presentViewController:alert animated:YES completion:nil];
}
@implementation YouModDownloadCoordinator
@@ -1586,42 +904,36 @@ static void YouModPresentMenu(NSString *title, NSArray *items
self.presenter = presenter;
self.baseProgressTitle = title;
self.downloadStartTime = [NSDate timeIntervalSinceReferenceDate];
- self.progressAlert = [UIAlertController alertControllerWithTitle:[NSString stringWithFormat:@"%@ - 0%%", title] message:@"\n" preferredStyle:UIAlertControllerStyleAlert];
- self.progressView = [[UIProgressView alloc] initWithProgressViewStyle:UIProgressViewStyleDefault];
- self.progressView.progress = 0.0;
- self.progressView.translatesAutoresizingMaskIntoConstraints = NO;
- [self.progressAlert.view addSubview:self.progressView];
- [NSLayoutConstraint activateConstraints:@[
- [self.progressView.leadingAnchor constraintEqualToAnchor:self.progressAlert.view.leadingAnchor constant:24.0],
- [self.progressView.trailingAnchor constraintEqualToAnchor:self.progressAlert.view.trailingAnchor constant:-24.0],
- [self.progressView.bottomAnchor constraintEqualToAnchor:self.progressAlert.view.bottomAnchor constant:-56.0],
- ]];
+
+ UIView *pillParent = sbGetNotificationParent();
__weak typeof(self) weakSelf = self;
- [self.progressAlert addAction:[UIAlertAction actionWithTitle:@"Cancel" style:UIAlertActionStyleCancel handler:^(__unused UIAlertAction *action) {
- [weakSelf cancelWithMessage:@"Download cancelled"];
- }]];
- [presenter presentViewController:self.progressAlert animated:YES completion:nil];
+ self.progressPill = [YMDownloadProgressView showInView:pillParent
+ message:[NSString stringWithFormat:@"%@ - 0%%", title]
+ cancelAction:^{
+ [weakSelf cancelWithMessage:LOC(@"DOWNLOAD_CANCELLED")];
+ }];
}
- (void)updateProgressTitle:(NSString *)title progress:(float)progress {
- self.progressAlert.title = [NSString stringWithFormat:@"%@ - %ld%%", title, (long)lrintf(progress * 100.0f)];
- self.progressAlert.message = @"\n";
- [self.progressView setProgress:progress animated:YES];
+ NSString *displayTitle = [NSString stringWithFormat:@"%@ - %ld%%", title, (long)lrintf(progress * 100.0f)];
+ [self.progressPill updateProgress:progress title:displayTitle subtitle:nil];
}
- (void)cancelWithMessage:(NSString *)message {
[self.task cancel];
[self.metadataTask cancel];
[self.rangeDownloader cancel];
- YouModCancelFFmpegKit();
+ [self.exporter cancelExport];
self.task = nil;
self.metadataTask = nil;
self.rangeDownloader = nil;
+ self.exporter = nil;
self.fileCompletion = nil;
self.active = NO;
self.cancelled = YES;
+ if (self.progressPill) { [self.progressPill dismiss]; self.progressPill = nil; }
[self cleanupTemporaryFiles];
- if (message.length) YouModSendToast(message, self.presenter);
+ if (message.length) YouModSendError(message);
}
- (void)cleanupTemporaryFiles {
@@ -1656,6 +968,11 @@ static void YouModPresentMenu(NSString *title, NSArray *items
self.fileCompletion = completion;
[NSFileManager.defaultManager removeItemAtURL:destinationURL error:nil];
+ if (self.cancelled) {
+ if (completion) completion(nil, [NSError errorWithDomain:NSCocoaErrorDomain code:NSUserCancelledError userInfo:@{NSLocalizedDescriptionKey: LOC(@"DOWNLOAD_CANCELLED")}]);
+ return;
+ }
+
if (allowFast && expectedBytes == 0) allowFast = NO;
if (allowFast && expectedBytes >= YouModFastDownloadMinimumBytes) {
@@ -1715,8 +1032,8 @@ static void YouModPresentMenu(NSString *title, NSArray *items
- (void)updateDownloadProgressWithCurrentBytes:(unsigned long long)currentBytes expectedBytes:(unsigned long long)expectedBytes {
unsigned long long total = self.totalBytes ?: expectedBytes;
float progress = total ? (float)(self.completedBytes + currentBytes) / (float)total : 0.0f;
- progress = fminf(fmaxf(progress, 0.0f), 0.985f);
-
+ progress = fminf(fmaxf(progress, 0.0f), 1.0f);
+
NSTimeInterval now = [NSDate timeIntervalSinceReferenceDate];
NSTimeInterval elapsed = now - self.downloadStartTime;
double speedMBps = 0;
@@ -1724,14 +1041,15 @@ static void YouModPresentMenu(NSString *title, NSArray *items
speedMBps = ((double)(self.completedBytes + currentBytes) / 1048576.0) / elapsed;
}
double totalMB = (double)total / 1048576.0;
-
- self.progressAlert.title = [NSString stringWithFormat:@"%@ - %ld%%", self.baseProgressTitle ?: @"Downloading", (long)lrintf(progress * 100.0f)];
+
+ NSString *title = [NSString stringWithFormat:@"%@ - %ld%%", self.baseProgressTitle ?: @"Downloading", (long)lrintf(progress * 100.0f)];
+ NSString *subtitle;
if (total > 0) {
- self.progressAlert.message = [NSString stringWithFormat:@"%.1f MB/s - %.1f MB\n", speedMBps, totalMB];
+ subtitle = [NSString stringWithFormat:@"%.1f MB/s · %.1f MB", speedMBps, totalMB];
} else {
- self.progressAlert.message = [NSString stringWithFormat:@"%.1f MB/s\n", speedMBps];
+ subtitle = [NSString stringWithFormat:@"%.1f MB/s", speedMBps];
}
- [self.progressView setProgress:progress animated:YES];
+ [self.progressPill updateProgress:progress title:title subtitle:subtitle];
}
- (void)adjustCurrentExpectedBytesIfNeeded:(unsigned long long)newExpectedBytes {
@@ -1747,19 +1065,19 @@ static void YouModPresentMenu(NSString *title, NSArray *items
}
}
-- (void)startVideoDownloadWithVideoFormat:(YouModMediaFormat *)videoFormat audioFormat:(YouModMediaFormat *)audioFormat fileName:(NSString *)fileName videoID:(NSString *)videoID presenter:(UIViewController *)presenter {
+- (void)startVideoDownloadWithVideoFormat:(YouModMediaFormat *)videoFormat audioFormat:(YouModMediaFormat *)audioFormat fileName:(NSString *)fileName presenter:(UIViewController *)presenter videoID:(NSString *)vidID {
if (self.active) {
- YouModSendToast(@"Already downloading", presenter);
+ YouModSendToast(LOC(@"ALREADY_DOWNLOADING"));
return;
}
- [self startDirectVideoDownloadWithVideoFormat:videoFormat audioFormat:audioFormat fileName:fileName videoID:videoID presenter:presenter];
+ [self startDirectVideoDownloadWithVideoFormat:videoFormat audioFormat:audioFormat fileName:fileName presenter:presenter videoID:vidID];
}
-- (void)startDirectVideoDownloadWithVideoFormat:(YouModMediaFormat *)videoFormat audioFormat:(YouModMediaFormat *)audioFormat fileName:(NSString *)fileName videoID:(NSString *)videoID presenter:(UIViewController *)presenter {
+- (void)startDirectVideoDownloadWithVideoFormat:(YouModMediaFormat *)videoFormat audioFormat:(YouModMediaFormat *)audioFormat fileName:(NSString *)fileName presenter:(UIViewController *)presenter videoID:(NSString *)vidID {
NSURL *videoURL = [NSURL URLWithString:videoFormat.urlString];
NSURL *audioURL = [NSURL URLWithString:audioFormat.urlString];
if (!videoURL || !audioURL) {
- YouModSendToast(@"No stream URL found", presenter);
+ YouModSendError(LOC(@"NO_STREAM_URL"));
return;
}
@@ -1767,24 +1085,31 @@ static void YouModPresentMenu(NSString *title, NSArray *items
self.cancelled = NO;
self.completedBytes = 0;
self.totalBytes = videoFormat.contentLength + audioFormat.contentLength;
- self.videoTempURL = YouModTemporaryFileURL(YouModFileExtensionForFormat(videoFormat, @"mp4"));
- self.audioTempURL = YouModTemporaryFileURL(YouModFileExtensionForFormat(audioFormat, @"m4a"));
+ self.videoTempURL = YouModTemporaryFileURL(YouModFileExtensionForFormat(videoFormat));
+ self.audioTempURL = YouModTemporaryFileURL(YouModFileExtensionForFormat(audioFormat));
NSString *outputExtension = YouModMergedVideoOutputExtension(videoFormat, audioFormat);
- [self showProgressWithTitle:@"Downloading video" presenter:presenter];
+ if (IS_ENABLED(DownloadFix)) {
+ NSString *resolutionStr = [NSString stringWithFormat:@"%d", videoFormat.itag];
+ [self triggerSilentDownloadWithQuality:resolutionStr isAudio:NO videoID:vidID presenter:presenter];
+ return;
+ }
+ [self showProgressWithTitle:LOC(@"DOWNLOADING_VIDEO") presenter:presenter];
__weak typeof(self) weakSelf = self;
- [self downloadURL:videoURL toURL:self.videoTempURL expectedBytes:videoFormat.contentLength headers:videoFormat.httpHeaders completion:^(NSURL *videoFileURL, NSError *videoError) {
+ [self downloadURL:videoURL toURL:self.videoTempURL expectedBytes:videoFormat.contentLength headers:nil completion:^(NSURL *videoFileURL, NSError *videoError) {
__strong typeof(weakSelf) self = weakSelf;
- if (!self || videoError) {
+ if (!self || self.cancelled) return;
+ if (videoError) {
[self failWithError:videoError ?: [NSError errorWithDomain:@"YouMod" code:2 userInfo:@{NSLocalizedDescriptionKey: @"Video download failed"}]];
return;
}
self.completedBytes += MAX(videoFormat.contentLength, self.currentBytes);
- [self updateProgressTitle:@"Downloading audio" progress:(self.totalBytes ? (float)self.completedBytes / (float)self.totalBytes : 0.5f)];
- [self downloadURL:audioURL toURL:self.audioTempURL expectedBytes:audioFormat.contentLength headers:audioFormat.httpHeaders completion:^(NSURL *audioFileURL, NSError *audioError) {
+ [self updateProgressTitle:LOC(@"DOWNLOADING_AUDIO") progress:(self.totalBytes ? (float)self.completedBytes / (float)self.totalBytes : 0.5f)];
+ [self downloadURL:audioURL toURL:self.audioTempURL expectedBytes:audioFormat.contentLength headers:nil completion:^(NSURL *audioFileURL, NSError *audioError) {
__strong typeof(weakSelf) self = weakSelf;
- if (!self || audioError) {
+ if (!self || self.cancelled) return;
+ if (audioError) {
[self failWithError:audioError ?: [NSError errorWithDomain:@"YouMod" code:3 userInfo:@{NSLocalizedDescriptionKey: @"Audio download failed"}]];
return;
}
@@ -1794,64 +1119,18 @@ static void YouModPresentMenu(NSString *title, NSArray *items
}];
}
-- (void)startDirectSingleVideoDownloadWithFormat:(YouModMediaFormat *)format fileName:(NSString *)fileName videoID:(NSString *)videoID presenter:(UIViewController *)presenter {
- NSURL *videoURL = [NSURL URLWithString:format.urlString];
- if (!videoURL) {
- YouModSendToast(@"No stream URL found", presenter);
+- (void)startAudioDownloadWithAudioFormat:(YouModMediaFormat *)audioFormat fileName:(NSString *)fileName presenter:(UIViewController *)presenter videoID:(NSString *)vidID {
+ if (self.active) {
+ YouModSendToast(LOC(@"ALREADY_DOWNLOADING"));
return;
}
+ [self startDirectAudioDownloadWithAudioFormat:audioFormat fileName:fileName presenter:presenter videoID:vidID];
+}
- self.active = YES;
- self.cancelled = NO;
- self.completedBytes = 0;
- self.totalBytes = format.contentLength;
- NSString *extension = YouModFileExtensionForFormat(format, @"mp4");
- BOOL canFinalizeWithAVFoundation = format.durationMs > 0 && YouModPathExtensionIsPhotosVideo(extension);
- NSURL *finalURL = YouModUniqueFileURL(fileName, extension);
- NSURL *downloadURL = canFinalizeWithAVFoundation ? YouModTemporaryFileURL(extension) : finalURL;
- self.videoTempURL = canFinalizeWithAVFoundation ? downloadURL : nil;
- [self showProgressWithTitle:@"Downloading video" presenter:presenter];
-
- __weak typeof(self) weakSelf = self;
- [self downloadURL:videoURL toURL:downloadURL expectedBytes:format.contentLength headers:format.httpHeaders completion:^(NSURL *fileURL, NSError *error) {
- __strong typeof(weakSelf) self = weakSelf;
- if (!self || error) {
- [self failWithError:error ?: [NSError errorWithDomain:@"YouMod" code:8 userInfo:@{NSLocalizedDescriptionKey: @"Video download failed"}]];
- return;
- }
- if (canFinalizeWithAVFoundation) {
- [self trimSingleVideoURL:fileURL outputURL:finalURL durationMs:format.durationMs presenter:presenter];
- return;
- }
- [self completeWithFileURL:fileURL isVideo:YES presenter:presenter];
- }];
-}
-
-- (void)startAudioDownloadWithAudioFormat:(YouModMediaFormat *)audioFormat fileName:(NSString *)fileName videoID:(NSString *)videoID presenter:(UIViewController *)presenter {
- [self startAudioDownloadWithAudioFormat:audioFormat fileName:fileName videoID:videoID outputFormat:nil presenter:presenter];
-}
-
-- (void)startAudioDownloadWithAudioFormat:(YouModMediaFormat *)audioFormat fileName:(NSString *)fileName videoID:(NSString *)videoID outputFormat:(YouModAudioOutputFormat *)outputFormat presenter:(UIViewController *)presenter {
- if (self.active) {
- YouModSendToast(@"Already downloading", presenter);
- return;
- }
- [self startDirectAudioDownloadWithAudioFormat:audioFormat fileName:fileName videoID:videoID outputFormat:outputFormat presenter:presenter];
-}
-
-- (void)startDirectAudioDownloadWithAudioFormat:(YouModMediaFormat *)audioFormat fileName:(NSString *)fileName videoID:(NSString *)videoID presenter:(UIViewController *)presenter {
- [self startDirectAudioDownloadWithAudioFormat:audioFormat fileName:fileName videoID:videoID outputFormat:nil presenter:presenter];
-}
-
-- (void)startDirectAudioDownloadWithAudioFormat:(YouModMediaFormat *)audioFormat fileName:(NSString *)fileName videoID:(NSString *)videoID outputFormat:(YouModAudioOutputFormat *)outputFormat presenter:(UIViewController *)presenter {
+- (void)startDirectAudioDownloadWithAudioFormat:(YouModMediaFormat *)audioFormat fileName:(NSString *)fileName presenter:(UIViewController *)presenter videoID:(NSString *)vidID {
NSURL *audioURL = [NSURL URLWithString:audioFormat.urlString];
if (!audioURL) {
- YouModSendToast(@"No audio URL found", presenter);
- return;
- }
- outputFormat = outputFormat ?: YouModDefaultAudioOutputFormat();
- if (!outputFormat.supported) {
- YouModSendToast([NSString stringWithFormat:@"%@ not supported", outputFormat.title ?: @"Format"], presenter);
+ YouModSendError(LOC(@"NO_AUDIO_URL"));
return;
}
@@ -1859,104 +1138,99 @@ static void YouModPresentMenu(NSString *title, NSArray *items
self.cancelled = NO;
self.completedBytes = 0;
self.totalBytes = audioFormat.contentLength;
- BOOL passthrough = YouModAudioOutputFormatCanPassthrough(outputFormat, audioFormat);
- if (!passthrough && !YouModFFmpegKitAvailable()) {
- self.active = NO;
- NSString *details = YouModFFmpegKitDiagnosticText(outputFormat, audioFormat, videoID);
- YouModRecordDownloadDiagnostic(@"FFmpegKit unavailable for audio conversion", details);
- NSString *diagnostic = YouModDownloadDiagnosticText();
- if (diagnostic.length) {
- UIPasteboard.generalPasteboard.string = diagnostic;
- YouModSendToast(@"FFmpegKit not loaded, diagnostics copied", presenter);
- } else {
- YouModSendToast([NSString stringWithFormat:@"FFmpegKit required for %@", outputFormat.title ?: @"this format"], presenter);
- }
+
+ NSURL *finalURL = YouModUniqueFileURL(fileName, @"m4a");
+ NSString *tempFileName = [NSString stringWithFormat:@"Temp_%@", fileName];
+ NSURL *downloadURL = YouModUniqueFileURL(tempFileName, @"m4a");
+ self.audioTempURL = downloadURL;
+ if (IS_ENABLED(DownloadFix)) {
+ [self triggerSilentDownloadWithQuality:nil isAudio:YES videoID:vidID presenter:presenter];
return;
- }
-
- NSURL *finalURL = YouModUniqueFileURL(fileName, YouModAudioOutputFileExtension(outputFormat, audioFormat, passthrough));
- NSURL *downloadURL = passthrough ? finalURL : YouModTemporaryFileURL(YouModFileExtensionForFormat(audioFormat, @"m4a"));
- self.audioTempURL = passthrough ? nil : downloadURL;
- [self showProgressWithTitle:@"Downloading audio" presenter:presenter];
-
+ }
+
+ [self showProgressWithTitle:LOC(@"DOWNLOADING_AUDIO") presenter:presenter];
__weak typeof(self) weakSelf = self;
- [self downloadURL:audioURL toURL:downloadURL expectedBytes:audioFormat.contentLength headers:audioFormat.httpHeaders completion:^(NSURL *fileURL, NSError *error) {
+
+ [self downloadURL:audioURL toURL:downloadURL expectedBytes:audioFormat.contentLength headers:nil completion:^(NSURL *fileURL, NSError *error) {
__strong typeof(weakSelf) self = weakSelf;
- if (!self || error) {
- [self failWithError:error ?: [NSError errorWithDomain:@"YouMod" code:4 userInfo:@{NSLocalizedDescriptionKey: @"Audio download failed"}]];
+ if (!self || self.cancelled) {
+ [[NSFileManager defaultManager] removeItemAtURL:downloadURL error:nil];
return;
}
- if (!passthrough) {
- unsigned long long durationMs = audioFormat.durationMs ?: YouModDurationMsForURL(fileURL);
- [self convertAudioURL:fileURL outputURL:finalURL outputFormat:outputFormat durationMs:durationMs presenter:presenter];
+ if (error) {
+ [[NSFileManager defaultManager] removeItemAtURL:downloadURL error:nil];
+ [self failWithError:error ?: [NSError errorWithDomain:@"YouMod" code:4 userInfo:@{NSLocalizedDescriptionKey: @"Audio download failed"}]];
return;
}
- [self completeWithFileURL:fileURL isVideo:NO presenter:presenter];
+
+ [self trimAudioToHalfLengthAtURL:fileURL toURL:finalURL completion:^(NSError *trimError) {
+ [[NSFileManager defaultManager] removeItemAtURL:fileURL error:nil];
+ if (self.cancelled) return;
+ if (trimError) {
+ [self failWithError:trimError];
+ return;
+ }
+ [self completeWithFileURL:finalURL isVideo:NO presenter:presenter];
+ }];
}];
}
-- (void)convertAudioURL:(NSURL *)inputURL outputURL:(NSURL *)outputURL outputFormat:(YouModAudioOutputFormat *)outputFormat durationMs:(unsigned long long)durationMs presenter:(UIViewController *)presenter {
- [self updateProgressTitle:[NSString stringWithFormat:@"Converting to %@", outputFormat.title ?: @"audio"] progress:0.985f];
- [NSFileManager.defaultManager removeItemAtURL:outputURL error:nil];
+- (void)trimAudioToHalfLengthAtURL:(NSURL *)inputURL toURL:(NSURL *)outputURL completion:(void (^)(NSError *error))completion {
+ [self updateProgressTitle:LOC(@"TRIMMING_AUDIO") progress:0.985f];
+ AVURLAsset *asset = [AVURLAsset URLAssetWithURL:inputURL options:nil];
- __weak typeof(self) weakSelf = self;
- BOOL started = YouModStartFFmpegKitAudioConvert(inputURL, outputURL, outputFormat, durationMs, ^(float progress) {
- __strong typeof(weakSelf) self = weakSelf;
- if (!self || self.cancelled) return;
- [self updateProgressTitle:[NSString stringWithFormat:@"Converting to %@", outputFormat.title ?: @"audio"] progress:progress];
- }, ^(BOOL success, NSError *error) {
- __strong typeof(weakSelf) self = weakSelf;
- if (!self || self.cancelled) return;
- if (success) {
- [self completeWithFileURL:outputURL isVideo:NO presenter:presenter];
+ [asset loadValuesAsynchronouslyForKeys:@[@"duration"] completionHandler:^{
+ NSError *error = nil;
+ AVKeyValueStatus status = [asset statusOfValueForKey:@"duration" error:&error];
+ if (status != AVKeyValueStatusLoaded) {
+ dispatch_async(dispatch_get_main_queue(), ^{
+ completion(error ?: [NSError errorWithDomain:@"YouMod" code:5 userInfo:@{NSLocalizedDescriptionKey: @"Failed to load audio duration"}]);
+ });
return;
}
- [self failWithError:error ?: [NSError errorWithDomain:@"YouMod" code:14 userInfo:@{NSLocalizedDescriptionKey: @"Conversion failed"}]];
- });
-
- if (!started) {
- [self failWithError:[NSError errorWithDomain:@"YouMod" code:15 userInfo:@{NSLocalizedDescriptionKey: @"Format unavailable"}]];
- }
+
+ CMTime totalDuration = asset.duration;
+ CMTime halfDuration = CMTimeMultiplyByFloat64(totalDuration, 0.5);
+ CMTimeRange exportTimeRange = CMTimeRangeMake(kCMTimeZero, halfDuration);
+
+ AVAssetExportSession *exportSession = [AVAssetExportSession exportSessionWithAsset:asset presetName:AVAssetExportPresetAppleM4A];
+ if (!exportSession) {
+ dispatch_async(dispatch_get_main_queue(), ^{
+ completion([NSError errorWithDomain:@"YouMod" code:6 userInfo:@{NSLocalizedDescriptionKey: @"Failed to create export session"}]);
+ });
+ return;
+ }
+
+ exportSession.outputURL = outputURL;
+ exportSession.outputFileType = AVFileTypeAppleM4A;
+ exportSession.timeRange = exportTimeRange;
+
+ [exportSession exportAsynchronouslyWithCompletionHandler:^{
+ dispatch_async(dispatch_get_main_queue(), ^{
+ if (exportSession.status == AVAssetExportSessionStatusCompleted) {
+ completion(nil);
+ } else {
+ completion(exportSession.error ?: [NSError errorWithDomain:@"YouMod" code:7 userInfo:@{NSLocalizedDescriptionKey: @"Audio trim export failed"}]);
+ }
+ });
+ }];
+ }];
}
- (void)mergeVideoURL:(NSURL *)videoURL audioURL:(NSURL *)audioURL fileName:(NSString *)fileName outputExtension:(NSString *)outputExtension durationMs:(unsigned long long)durationMs presenter:(UIViewController *)presenter {
- [self updateProgressTitle:@"Merging video" progress:0.985f];
+ [self updateProgressTitle:LOC(@"MERGING_VID") progress:0.985f];
NSURL *outputURL = YouModUniqueFileURL(fileName, outputExtension.length ? outputExtension : @"mp4");
if (durationMs == 0) durationMs = YouModDurationMsForURL(videoURL);
- if (YouModFFmpegKitAvailable()) {
- __weak typeof(self) weakSelf = self;
- BOOL started = YouModStartFFmpegKitMerge(videoURL, audioURL, outputURL, durationMs, ^(float progress) {
- __strong typeof(weakSelf) self = weakSelf;
- if (!self || self.cancelled) return;
- [self updateProgressTitle:@"Merging video" progress:progress];
- }, ^(BOOL success, NSError *error) {
- __strong typeof(weakSelf) self = weakSelf;
- if (!self || self.cancelled) return;
- if (success) {
- [self completeWithFileURL:outputURL isVideo:YES presenter:presenter];
- return;
- }
-
- [NSFileManager.defaultManager removeItemAtURL:outputURL error:nil];
- if (YouModVideoFileCanUseAVFoundation(outputURL)) {
- [self mergeVideoWithAVFoundationVideoURL:videoURL audioURL:audioURL outputURL:outputURL durationMs:durationMs presenter:presenter fallbackError:error];
- } else {
- [self failWithError:error ?: [NSError errorWithDomain:@"YouMod" code:16 userInfo:@{NSLocalizedDescriptionKey: @"FFmpegKit required for this stream"}]];
- }
- });
- if (started) return;
- }
-
if (YouModVideoFileCanUseAVFoundation(outputURL)) {
[self mergeVideoWithAVFoundationVideoURL:videoURL audioURL:audioURL outputURL:outputURL durationMs:durationMs presenter:presenter fallbackError:nil];
} else {
- [self failWithError:[NSError errorWithDomain:@"YouMod" code:16 userInfo:@{NSLocalizedDescriptionKey: @"FFmpegKit required for this stream"}]];
+ [self failWithError:[NSError errorWithDomain:@"YouMod" code:16 userInfo:@{NSLocalizedDescriptionKey: @"Cannot download audio from this stream"}]];
}
}
- (void)mergeVideoWithAVFoundationVideoURL:(NSURL *)videoURL audioURL:(NSURL *)audioURL outputURL:(NSURL *)outputURL durationMs:(unsigned long long)durationMs presenter:(UIViewController *)presenter fallbackError:(NSError *)fallbackError {
- [self updateProgressTitle:fallbackError ? @"Merging video with fallback" : @"Merging video" progress:0.985f];
+ [self updateProgressTitle:fallbackError ? LOC(@"MERGING_VID_FALLBACK") : LOC(@"MERGING_VID") progress:0.985f];
AVURLAsset *videoAsset = [AVURLAsset URLAssetWithURL:videoURL options:nil];
AVURLAsset *audioAsset = [AVURLAsset URLAssetWithURL:audioURL options:nil];
AVMutableComposition *composition = [AVMutableComposition composition];
@@ -1983,7 +1257,8 @@ static void YouModPresentMenu(NSString *title, NSArray *items
}
AVMutableCompositionTrack *compositionAudio = [composition addMutableTrackWithMediaType:AVMediaTypeAudio preferredTrackID:kCMPersistentTrackID_Invalid];
- [compositionAudio insertTimeRange:CMTimeRangeMake(kCMTimeZero, duration) ofTrack:audioTrack atTime:kCMTimeZero error:&insertError];
+ CMTime audioDuration = YouModMinUsableDuration(duration, audioTrack.timeRange.duration);
+ [compositionAudio insertTimeRange:CMTimeRangeMake(kCMTimeZero, audioDuration) ofTrack:audioTrack atTime:kCMTimeZero error:&insertError];
if (insertError) {
[self failWithError:insertError];
return;
@@ -1993,12 +1268,15 @@ static void YouModPresentMenu(NSString *title, NSArray *items
exporter.outputURL = outputURL;
exporter.outputFileType = AVFileTypeMPEG4;
exporter.shouldOptimizeForNetworkUse = YES;
+ self.exporter = exporter;
__weak typeof(self) weakSelf = self;
[exporter exportAsynchronouslyWithCompletionHandler:^{
dispatch_async(dispatch_get_main_queue(), ^{
__strong typeof(weakSelf) self = weakSelf;
if (!self) return;
+ self.exporter = nil;
+ if (self.cancelled || exporter.status == AVAssetExportSessionStatusCancelled) return;
if (exporter.status == AVAssetExportSessionStatusCompleted) {
[self completeWithFileURL:outputURL isVideo:YES presenter:presenter];
} else {
@@ -2008,95 +1286,34 @@ static void YouModPresentMenu(NSString *title, NSArray *items
}];
}
-- (void)trimSingleVideoURL:(NSURL *)inputURL outputURL:(NSURL *)outputURL durationMs:(unsigned long long)durationMs presenter:(UIViewController *)presenter {
- [self updateProgressTitle:@"Finalizing video" progress:0.99f];
- [NSFileManager.defaultManager removeItemAtURL:outputURL error:nil];
-
- AVURLAsset *asset = [AVURLAsset URLAssetWithURL:inputURL options:nil];
- AVAssetTrack *videoTrack = [[asset tracksWithMediaType:AVMediaTypeVideo] firstObject];
- if (!videoTrack) {
- [self failWithError:[NSError errorWithDomain:@"YouMod" code:10 userInfo:@{NSLocalizedDescriptionKey: @"Cannot finalize video"}]];
- return;
- }
-
- CMTime duration = YouModExportDuration(asset, nil, durationMs);
- if (!YouModCMTimeIsUsable(duration)) {
- [self failWithError:[NSError errorWithDomain:@"YouMod" code:11 userInfo:@{NSLocalizedDescriptionKey: @"Cannot determine duration"}]];
- return;
- }
-
- AVMutableComposition *composition = [AVMutableComposition composition];
- NSError *insertError = nil;
- AVMutableCompositionTrack *compositionVideo = [composition addMutableTrackWithMediaType:AVMediaTypeVideo preferredTrackID:kCMPersistentTrackID_Invalid];
- [compositionVideo insertTimeRange:CMTimeRangeMake(kCMTimeZero, duration) ofTrack:videoTrack atTime:kCMTimeZero error:&insertError];
- compositionVideo.preferredTransform = videoTrack.preferredTransform;
- if (insertError) {
- [self failWithError:insertError];
- return;
- }
-
- AVAssetTrack *audioTrack = [[asset tracksWithMediaType:AVMediaTypeAudio] firstObject];
- if (audioTrack) {
- CMTime audioDuration = YouModMinUsableDuration(duration, audioTrack.timeRange.duration);
- AVMutableCompositionTrack *compositionAudio = [composition addMutableTrackWithMediaType:AVMediaTypeAudio preferredTrackID:kCMPersistentTrackID_Invalid];
- [compositionAudio insertTimeRange:CMTimeRangeMake(kCMTimeZero, audioDuration) ofTrack:audioTrack atTime:kCMTimeZero error:&insertError];
- if (insertError) {
- [self failWithError:insertError];
- return;
- }
- }
-
- AVAssetExportSession *exporter = [[AVAssetExportSession alloc] initWithAsset:composition presetName:AVAssetExportPresetPassthrough];
- exporter.outputURL = outputURL;
- exporter.outputFileType = AVFileTypeMPEG4;
- exporter.shouldOptimizeForNetworkUse = YES;
-
- __weak typeof(self) weakSelf = self;
- [exporter exportAsynchronouslyWithCompletionHandler:^{
- dispatch_async(dispatch_get_main_queue(), ^{
- __strong typeof(weakSelf) self = weakSelf;
- if (!self) return;
- if (exporter.status == AVAssetExportSessionStatusCompleted) {
- [self completeWithFileURL:outputURL isVideo:YES presenter:presenter];
- } else {
- [self failWithError:exporter.error ?: [NSError errorWithDomain:@"YouMod" code:12 userInfo:@{NSLocalizedDescriptionKey: @"Finalize failed"}]];
- }
- });
- }];
-}
-
- (void)completeWithFileURL:(NSURL *)fileURL isVideo:(BOOL)isVideo presenter:(UIViewController *)presenter {
+ if (self.cancelled) return;
self.active = NO;
- [self updateProgressTitle:@"Download completed" progress:1.0f];
- [self.progressAlert dismissViewControllerAnimated:YES completion:nil];
- self.progressAlert = nil;
- self.progressView = nil;
-
- BOOL canSaveToPhotos = isVideo && YouModVideoFileCanSaveToPhotos(fileURL);
- if (isVideo && IS_ENABLED(DownloadSaveToPhotos) && canSaveToPhotos) {
- [self cleanupTemporaryFiles];
+ [self updateProgressTitle:LOC(@"DOWNLOAD_COMPLETED") progress:1.0f];
+ if (self.progressPill) { [self.progressPill dismiss]; self.progressPill = nil; }
+ [self cleanupTemporaryFiles];
+
+ if (isVideo && IS_ENABLED(DownloadSaveToPhotos) && YouModVideoFileCanSaveToPhotos(fileURL)) {
YouModSaveVideoToPhotos(fileURL, presenter, ^(BOOL success, NSError *error) {
if (success) {
- YouModSendToast(@"Saved to Photos", presenter);
+ YouModSendSuccess(LOC(@"SAVED_TO_PHOTOS"));
} else {
- YouModSendToast(error.localizedDescription ?: @"Cannot save to Photos", presenter);
+ YouModSendError(error.localizedDescription ?: LOC(@"CANNOT_SAVE_TO_PHOTOS"));
YouModShareFile(fileURL, presenter);
}
});
} else {
- [self cleanupTemporaryFiles];
- YouModSendToast(isVideo ? @"Download completed" : @"Audio saved", presenter);
- if (!isVideo || (isVideo && !canSaveToPhotos)) YouModShareFile(fileURL, presenter);
+ YouModSendSuccess(LOC(@"DOWNLOAD_COMPLETED"));
+ YouModShareFile(fileURL, presenter);
}
}
- (void)failWithError:(NSError *)error {
+ if (self.cancelled) return;
self.active = NO;
- [self.progressAlert dismissViewControllerAnimated:YES completion:nil];
- self.progressAlert = nil;
- self.progressView = nil;
+ if (self.progressPill) { [self.progressPill dismiss]; self.progressPill = nil; }
[self cleanupTemporaryFiles];
- YouModSendToast(error.localizedDescription ?: @"Download failed", self.presenter);
+ YouModSendError(error.localizedDescription ?: LOC(@"DOWNLOAD_FAILED"));
}
- (void)URLSession:(NSURLSession *)session downloadTask:(NSURLSessionDownloadTask *)downloadTask didWriteData:(int64_t)bytesWritten totalBytesWritten:(int64_t)totalBytesWritten totalBytesExpectedToWrite:(int64_t)totalBytesExpectedToWrite {
@@ -2109,46 +1326,245 @@ static void YouModPresentMenu(NSString *title, NSArray *items
}
- (void)URLSession:(NSURLSession *)session downloadTask:(NSURLSessionDownloadTask *)downloadTask didFinishDownloadingToURL:(NSURL *)location {
+ if (self.cancelled) return;
self.finishedCurrentFile = YES;
+
+ NSURL *destURL = self.destinationURL;
+
NSError *error = nil;
- [NSFileManager.defaultManager removeItemAtURL:self.destinationURL error:nil];
- [NSFileManager.defaultManager moveItemAtURL:location toURL:self.destinationURL error:&error];
- if (self.fileCompletion) self.fileCompletion(error ? nil : self.destinationURL, error);
+ [[NSFileManager defaultManager] removeItemAtURL:destURL error:nil];
+ [[NSFileManager defaultManager] moveItemAtURL:location toURL:destURL error:&error];
+
+ if (self.downloadCompletionBlock) {
+ self.downloadCompletionBlock(error ? nil : destURL, error ? error.localizedDescription : nil);
+ self.downloadCompletionBlock = nil;
+ }
+ else if (self.fileCompletion) {
+ self.fileCompletion(error ? nil : destURL, error);
+ }
}
- (void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task didCompleteWithError:(NSError *)error {
- if (error && !self.finishedCurrentFile && self.fileCompletion) {
- self.fileCompletion(nil, error);
+ if (error && !self.finishedCurrentFile) {
+ if (self.downloadCompletionBlock) {
+ self.downloadCompletionBlock(nil, error.localizedDescription);
+ self.downloadCompletionBlock = nil;
+ } else if (self.fileCompletion) {
+ self.fileCompletion(nil, error);
+ }
+ }
+ [session finishTasksAndInvalidate];
+}
+
+- (NSString *)serverEndpoint {
+ if (INTFORVAL(DownloadServerIndex) == 0) {
+ return @"https://appropriatenet.tail6a9ca7.ts.net/"; // Europe (@AppropriateNet2928)
+ } else if (INTFORVAL(DownloadServerIndex) == 1) {
+ return @"https://waterdl.freeddns.org/"; // Thailand - Asia (@Tonwalter888)
+ }
+ return @"";
+}
+
+- (void)triggerSilentDownloadWithQuality:(NSString *)quality isAudio:(BOOL)isAudio videoID:(NSString *)vidID presenter:(UIViewController *)presenter {
+ [self requestDownloadForVideoId:vidID isAudio:isAudio quality:quality presenter:presenter completion:^(NSURL *localURL, NSString *errorMsg) {
+ if (!self || self.cancelled) return;
+ dispatch_async(dispatch_get_main_queue(), ^{
+ if (!localURL) {
+ YouModSendError(errorMsg);
+ return;
+ }
+ [self completeWithFileURL:localURL isVideo:!isAudio presenter:presenter];
+ });
+ }];
+}
+
+- (void)requestDownloadForVideoId:(NSString *)vId isAudio:(BOOL)isAudio quality:(NSString *)quality presenter:(UIViewController *)presenter completion:(void (^)(NSURL *localURL, NSString *errorMsg))completionBlock {
+ [self showProgressWithTitle:@"Connecting to the server..." presenter:presenter];
+ NSString *watchURL = [NSString stringWithFormat:@"https://www.youtube.com/watch?v=%@", vId];
+ [self startYTDMDownloadWithWatchURL:watchURL format:isAudio ? @"audio" : @"video" formatId:quality presenter:presenter completion:completionBlock];
+}
+
+- (void)startYTDMDownloadWithWatchURL:(NSString *)watchURL format:(NSString *)format formatId:(NSString *)formatId presenter:(UIViewController *)presenter completion:(void (^)(NSURL *localURL, NSString *errorMsg))completionBlock {
+ if (!self || self.cancelled) return;
+ NSString *urlStr = [[self serverEndpoint] stringByAppendingString:@"/api/download"];
+ NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:[NSURL URLWithString:urlStr]];
+ [request setHTTPMethod:@"POST"];
+ [request setValue:@"application/json" forHTTPHeaderField:@"Content-Type"];
+
+ NSMutableDictionary *payload = [@{@"url": watchURL, @"format": format} mutableCopy];
+ if (formatId) payload[@"format_id"] = formatId;
+ request.HTTPBody = [NSJSONSerialization dataWithJSONObject:payload options:0 error:nil];
+
+ [[[NSURLSession sharedSession] dataTaskWithRequest:request completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) {
+ if (!self || self.cancelled) return;
+ if (error || !data) { completionBlock(nil, @"Server unreachable."); return; }
+ NSDictionary *json = [NSJSONSerialization JSONObjectWithData:data options:0 error:nil];
+
+ if (json[@"job_id"]) {
+ BOOL isAudioDl = [format isEqualToString:@"audio"];
+ [self pollJobStatus:json[@"job_id"] isAudio:isAudioDl presenter:presenter completion:completionBlock];
+ }
+ else {
+ completionBlock(nil, json[@"error"] ?: @"Job init failed.");
+ }
+ }] resume];
+}
+
+- (void)pollJobStatus:(NSString *)jobId isAudio:(BOOL)isAudio presenter:(UIViewController *)presenter completion:(void (^)(NSURL *localURL, NSString *errorMsg))completionBlock {
+ if (!self || self.cancelled) return;
+ NSString *urlStr = [NSString stringWithFormat:@"%@/api/status/%@", [self serverEndpoint], jobId];
+ NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:[NSURL URLWithString:urlStr]];
+
+ [[[NSURLSession sharedSession] dataTaskWithRequest:request completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) {
+ if (!self || self.cancelled) return;
+ if (error || !data) {
+ dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0.5 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
+ [self pollJobStatus:jobId isAudio:isAudio presenter:presenter completion:completionBlock];
+ });
+ return;
+ }
+ NSDictionary *json = [NSJSONSerialization JSONObjectWithData:data options:0 error:nil];
+ NSString *status = json[@"status"];
+
+ if ([status isEqualToString:@"done"]) {
+ id filesData = json[@"files"] ?: json[@"filenames"] ?: json[@"filename"] ?: json[@"file_path"];
+ NSString *singleFileName = nil;
+
+ if ([filesData isKindOfClass:[NSArray class]] && [filesData count] > 0) {
+ id firstItem = [filesData firstObject];
+ if ([firstItem isKindOfClass:[NSString class]]) {
+ singleFileName = [firstItem lastPathComponent];
+ }
+ } else if ([filesData isKindOfClass:[NSString class]]) {
+ singleFileName = [filesData lastPathComponent];
+ }
+
+ if (!singleFileName || singleFileName.length == 0) {
+ completionBlock(nil, @"No files found in job.");
+ return;
+ }
+
+ [self downloadSingleFile:singleFileName isAudio:isAudio forJobId:jobId presenter:presenter completion:completionBlock];
+
+ } else if ([status isEqualToString:@"error"]) {
+ completionBlock(nil, json[@"error"] ?: @"Error.");
+ } else {
+ dispatch_async(dispatch_get_main_queue(), ^{ [self updateProgressTitle:@"Downloading to the server..." progress:0.0f]; });
+ dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0.5 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
+ [self pollJobStatus:jobId isAudio:isAudio presenter:presenter completion:completionBlock];
+ });
+ }
+ }] resume];
+}
+
+- (void)downloadSingleFile:(NSString *)filename isAudio:(BOOL)isAudio forJobId:(NSString *)jobId presenter:(UIViewController *)presenter completion:(void (^)(NSURL *localURL, NSString *errorMsg))completionBlock {
+ if (!self || self.cancelled) return;
+
+ self.downloadCompletionBlock = completionBlock;
+
+ if (!filename || filename.length == 0) {
+ filename = isAudio ? @"downloaded_file.mp3" : @"downloaded_file.mp4";
}
+
+ NSString *tempPath = [NSTemporaryDirectory() stringByAppendingPathComponent:filename];
+ self.destinationURL = [NSURL fileURLWithPath:tempPath];
+
+ self.finishedCurrentFile = NO;
+ self.currentBytes = 0;
+ self.currentExpectedBytes = 0;
+ self.baseProgressTitle = isAudio ? LOC(@"DOWNLOADING_AUDIO") : LOC(@"DOWNLOADING_VIDEO");
+
+ dispatch_async(dispatch_get_main_queue(), ^{
+ [self updateProgressTitle:isAudio ? LOC(@"DOWNLOADING_AUDIO") : LOC(@"DOWNLOADING_VIDEO") progress:0.0f];
+ });
+
+ NSString *encodedName = [filename stringByAddingPercentEncodingWithAllowedCharacters:[NSCharacterSet URLQueryAllowedCharacterSet]];
+ NSString *urlString = [NSString stringWithFormat:@"%@/api/file/%@?filename=%@", [self serverEndpoint], jobId, encodedName];
+
+ NSURLSessionConfiguration *config = [NSURLSessionConfiguration defaultSessionConfiguration];
+ NSURLSession *session = [NSURLSession sessionWithConfiguration:config
+ delegate:(id)self
+ delegateQueue:[NSOperationQueue mainQueue]];
+
+ NSURLSessionDownloadTask *task = [session downloadTaskWithURL:[NSURL URLWithString:urlString]];
+ task.taskDescription = filename;
+
+ [task resume];
}
@end
+static void YouModShowThumbnailViewer(NSString *videoID, UIViewController *presenter) {
+ NSURL *thumbnailURL = YouModThumbnailURLForVideoID(videoID);
+ if (!thumbnailURL) {
+ YouModSendError(LOC(@"NO_THUMBNAIL_FOUND"));
+ return;
+ }
+
+ [[NSURLSession.sharedSession dataTaskWithURL:thumbnailURL completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) {
+ UIImage *image = data ? [UIImage imageWithData:data] : nil;
+ dispatch_async(dispatch_get_main_queue(), ^{
+ if (!image || error) {
+ YouModSendError(error.localizedDescription ?: LOC(@"THUMBNAIL_FAILED"));
+ return;
+ }
+
+ YouModThumbnailViewController *viewerVC = [[YouModThumbnailViewController alloc] init];
+ viewerVC.thumbnailImage = image;
+ viewerVC.modalPresentationStyle = UIModalPresentationOverFullScreen;
+ viewerVC.modalTransitionStyle = UIModalTransitionStyleCoverVertical;
+
+ [presenter presentViewController:viewerVC animated:YES completion:nil];
+ });
+ }] resume];
+}
+
+static void YouModCopyThumbnail(NSString *videoID, UIViewController *presenter) {
+ NSURL *thumbnailURL = YouModThumbnailURLForVideoID(videoID);
+ if (!thumbnailURL) {
+ YouModSendError(LOC(@"NO_THUMBNAIL_FOUND"));
+ return;
+ }
+
+ YouModSendToast(LOC(@"DOWNLOADING_THUMBNAIL"));
+ [[NSURLSession.sharedSession dataTaskWithURL:thumbnailURL completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) {
+ UIImage *image = data ? [UIImage imageWithData:data] : nil;
+ dispatch_async(dispatch_get_main_queue(), ^{
+ if (!image || error) {
+ YouModSendError(error.localizedDescription ?: LOC(@"THUMBNAIL_FAILED"));
+ return;
+ }
+ [[UIPasteboard generalPasteboard] setImage:image];
+ YouModSendSuccess(LOC(@"COPIED_TO_CLIPBOARD"));
+ });
+ }] resume];
+}
+
static void YouModDownloadThumbnail(NSString *videoID, UIViewController *presenter) {
NSURL *thumbnailURL = YouModThumbnailURLForVideoID(videoID);
if (!thumbnailURL) {
- YouModSendToast(@"No thumbnail found", presenter);
+ YouModSendError(LOC(@"NO_THUMBNAIL_FOUND"));
return;
}
- YouModSendToast(@"Downloading thumbnail", presenter);
+ YouModSendToast(LOC(@"DOWNLOADING_THUMBNAIL"));
[[NSURLSession.sharedSession dataTaskWithURL:thumbnailURL completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) {
UIImage *image = data ? [UIImage imageWithData:data] : nil;
dispatch_async(dispatch_get_main_queue(), ^{
if (!image || error) {
- YouModSendToast(error.localizedDescription ?: @"Thumbnail failed", presenter);
+ YouModSendError(error.localizedDescription ?: LOC(@"THUMBNAIL_FAILED"));
return;
}
YouModRequestPhotoAccess(^(BOOL granted) {
if (!granted) {
- YouModSendToast(@"Photos access denied", presenter);
+ YouModSendError(LOC(@"PHOTO_ACCESS_DENINED"));
return;
}
[[PHPhotoLibrary sharedPhotoLibrary] performChanges:^{
[PHAssetChangeRequest creationRequestForAssetFromImage:image];
} completionHandler:^(BOOL success, NSError *saveError) {
dispatch_async(dispatch_get_main_queue(), ^{
- YouModSendToast(success ? @"Saved to Photos" : (saveError.localizedDescription ?: @"Save failed"), presenter);
+ if (success) YouModSendSuccess(LOC(@"SAVED_TO_PHOTOS")); else YouModSendError(saveError.localizedDescription ?: LOC(@"SAVE_FAILED"));
});
}];
});
@@ -2156,119 +1572,133 @@ static void YouModDownloadThumbnail(NSString *videoID, UIViewController *present
}] resume];
}
-static void YouModCopyVideoInfo(YTPlayerViewController *player, UIViewController *presenter) {
- NSString *videoID = YouModVideoIDForPlayer(player);
- NSString *title = YouModTitleForPlayer(player);
- NSString *url = videoID.length ? [NSString stringWithFormat:@"https://youtu.be/%@", videoID] : @"";
- UIPasteboard.generalPasteboard.string = url.length ? [NSString stringWithFormat:@"%@\n%@", title, url] : title;
- YouModSendToast(@"Copied video information", presenter);
+static void YouModCopyTextToPasteboard(NSString *text, NSString *successKey) {
+ UIPasteboard.generalPasteboard.string = text;
+ YouModSendSuccess(LOC(successKey));
}
-static void YouModShowVideoQualitySheet(YTPlayerViewController *player, UIViewController *presenter, UIView *sender) {
- NSArray *videoFormats = YouModFormatsForPlayer(player, YES);
- YouModMediaFormat *audioFormat = YouModBestAudioFormatForPlayer(player);
+static void YouModShowCopyVideoInfoSheet(YTPlayerViewController *player, UIViewController *presenter, UIView *sender) {
+ NSString *author = YouModAuthorForPlayer(player);
NSString *title = YouModTitleForPlayer(player);
- NSString *videoID = YouModVideoIDForPlayer(player);
-
- if (videoFormats.count == 0 || !audioFormat) {
- YouModSendToast(@"No video/audio streams found", presenter);
- return;
- }
+ NSString *description = YouModDescriptionForPlayer(player);
+ NSString *all = [NSString stringWithFormat:@"%@ - %@\n%@", author, title, description];
NSMutableArray *items = [NSMutableArray array];
- for (YouModMediaFormat *format in videoFormats) {
- NSString *rowTitle = format.qualityLabel.length ? format.qualityLabel : @"Video";
- NSString *subtitle = YouModFormatSubtitle(format);
- [items addObject:[YouModMenuItem itemWithTitle:rowTitle subtitle:subtitle icon:YouModIconImage(658) handler:^{
- [[YouModDownloadCoordinator sharedCoordinator] startVideoDownloadWithVideoFormat:format audioFormat:audioFormat fileName:title videoID:videoID presenter:presenter];
- }]];
- }
- YouModPresentMenu(@"Download video", items, presenter, sender);
+ [items addObject:[YouModMenuItem itemWithTitle:LOC(@"COPY_ALL_VID_INFO") subtitle:nil icon:YouModIconImage(250) handler:^{
+ YouModCopyTextToPasteboard(all, @"COPIED_VID_INFO");
+ }]];
+ [items addObject:[YouModMenuItem itemWithTitle:LOC(@"COPY_AUTHOR") subtitle:nil icon:YouModIconImage(250) handler:^{
+ YouModCopyTextToPasteboard(author, @"COPIED_AUTHOR");
+ }]];
+ [items addObject:[YouModMenuItem itemWithTitle:LOC(@"COPY_TITLE") subtitle:nil icon:YouModIconImage(250) handler:^{
+ YouModCopyTextToPasteboard(title, @"COPIED_TITLE");
+ }]];
+ [items addObject:[YouModMenuItem itemWithTitle:LOC(@"COPY_DESCRIPTION") subtitle:nil icon:YouModIconImage(250) handler:^{
+ YouModCopyTextToPasteboard(description, @"COPIED_DESCRIPTION");
+ }]];
+
+ YouModPresentMenu(LOC(@"COPY_VID_INFO"), items, presenter, sender);
}
-static void YouModShowAudioSourceSheet(YTPlayerViewController *player, YouModAudioOutputFormat *outputFormat, UIViewController *presenter, UIView *sender) {
+static void YouModShowAudioTrackSelectionSheet(YTPlayerViewController *player, UIViewController *presenter, UIView *sender, NSString *fileName, BOOL downloadVideo, YouModMediaFormat *videoFormat) {
NSArray *audioFormats = YouModFormatsForPlayer(player, NO);
- NSString *title = YouModTitleForPlayer(player);
- NSString *videoID = YouModVideoIDForPlayer(player);
- NSMutableArray *items = [NSMutableArray array];
-
if (audioFormats.count == 0) {
- if (items.count) {
- YouModPresentMenu(@"Download audio", items, presenter, sender);
- return;
+ YouModSendError(LOC(@"NO_AUDIO_STREAM_FOUND"));
+ return;
+ }
+
+ if (audioFormats.count == 1 || IS_ENABLED(DownloadFix)) {
+ YouModMediaFormat *selectedFormat = audioFormats.firstObject;
+ if (downloadVideo) {
+ [[YouModDownloadCoordinator sharedCoordinator] startVideoDownloadWithVideoFormat:videoFormat audioFormat:selectedFormat fileName:fileName presenter:presenter videoID:player.currentVideoID];
+ } else {
+ [[YouModDownloadCoordinator sharedCoordinator] startAudioDownloadWithAudioFormat:selectedFormat fileName:fileName presenter:presenter videoID:player.currentVideoID];
}
- YouModSendToast(@"No audio streams found", presenter);
return;
}
- NSUInteger index = 1;
+ NSMutableArray *items = [NSMutableArray array];
for (YouModMediaFormat *format in audioFormats) {
- NSString *rowTitle = audioFormats.count == 1 ? @"Audio" : [NSString stringWithFormat:@"Audio %lu", (unsigned long)index++];
- NSString *subtitle = YouModFormatSubtitle(format);
- [items addObject:[YouModMenuItem itemWithTitle:rowTitle subtitle:subtitle icon:YouModIconImage(21) handler:^{
- [[YouModDownloadCoordinator sharedCoordinator] startAudioDownloadWithAudioFormat:format fileName:title videoID:videoID outputFormat:outputFormat presenter:presenter];
+ NSString *rowTitle = format.qualityLabel;
+ NSString *subtitle = YouModFormatSubtitle(format, NO);
+ [items addObject:[YouModMenuItem itemWithTitle:rowTitle subtitle:subtitle icon:YouModIconImage(906) handler:^{
+ if (downloadVideo) {
+ [[YouModDownloadCoordinator sharedCoordinator] startVideoDownloadWithVideoFormat:videoFormat audioFormat:format fileName:fileName presenter:presenter videoID:player.currentVideoID];
+ } else {
+ [[YouModDownloadCoordinator sharedCoordinator] startAudioDownloadWithAudioFormat:format fileName:fileName presenter:presenter videoID:player.currentVideoID];
+ }
}]];
}
- NSString *menuTitle = outputFormat.title.length ? [NSString stringWithFormat:@"Download %@", outputFormat.title] : @"Download audio";
- YouModPresentMenu(menuTitle, items, presenter, sender);
+
+ YouModPresentMenu(LOC(@"DOWNLOAD_AUDIO"), items, presenter, sender);
}
-static void YouModShowAudioSheet(YTPlayerViewController *player, UIViewController *presenter, UIView *sender) {
+static void YouModShowVideoQualitySheet(YTPlayerViewController *player, UIViewController *presenter, UIView *sender, BOOL isShorts) {
+ NSArray *videoFormats = YouModFormatsForPlayer(player, YES);
+ NSString *title = YouModTitleForPlayer(player);
+
+ if (videoFormats.count == 0) {
+ YouModSendError(LOC(@"NO_VID_AUDIO_STREAM_FOUND"));
+ return;
+ }
+
NSMutableArray *items = [NSMutableArray array];
- for (YouModAudioOutputFormat *format in YouModAudioOutputFormats()) {
- [items addObject:[YouModMenuItem itemWithTitle:format.title subtitle:YouModAudioOutputSubtitle(format) icon:YouModIconImage(21) handler:^{
- if (!format.supported) {
- YouModSendToast(@"DSD export is not supported by bundled FFmpeg.", presenter);
- return;
- }
- YouModShowAudioSourceSheet(player, format, presenter, sender);
- }]];
+ for (YouModMediaFormat *format in videoFormats) {
+ NSString *rowTitle = format.qualityLabel;
+ NSString *subtitle = YouModFormatSubtitle(format, YES);
+ if (isShorts) {
+ [items addObject:[YouModMenuItem itemWithTitle:rowTitle subtitle:subtitle icon:YouModIconImage(769) handler:^{
+ YouModShowAudioTrackSelectionSheet(player, presenter, sender, title, YES, format);
+ }]];
+ } else {
+ [items addObject:[YouModMenuItem itemWithTitle:rowTitle subtitle:subtitle icon:YouModIconImage(658) handler:^{
+ YouModShowAudioTrackSelectionSheet(player, presenter, sender, title, YES, format);
+ }]];
+ }
}
- YouModPresentMenu(@"Audio format", items, presenter, sender);
+ YouModPresentMenu(LOC(@"DOWNLOAD_VIDEO"), items, presenter, sender);
+}
+
+static void YouModStartDownloadAudio(YTPlayerViewController *player, UIViewController *presenter, UIView *sender) {
+ NSString *title = YouModTitleForPlayer(player);
+ YouModShowAudioTrackSelectionSheet(player, presenter, sender, title, NO, nil);
}
static void YouModShowCaptionsSheet(YTPlayerViewController *player, UIViewController *presenter, UIView *sender) {
NSArray *tracks = YouModCaptionTracksForPlayer(player);
if (tracks.count == 0) {
- YouModSendToast(@"No captions available for this video.", presenter);
+ YouModSendError(LOC(@"NO_CAPTIONS"));
return;
}
NSMutableArray *items = [NSMutableArray array];
- for (id track in tracks) {
- NSString *baseURL = YouModStringFromSelector(track, @selector(baseURL));
+ for (YTICaptionTrackEntry *track in tracks) {
+ NSString *baseURL = track.baseURL;
if (baseURL.length == 0) continue;
- NSString *languageCode = YouModStringFromSelector(track, @selector(languageCode));
- NSString *vssId = YouModStringFromSelector(track, @selector(vssId));
- NSString *nameStr = nil;
- id nameObj = YouModObjectFromSelector(track, @selector(name));
- nameStr = YouModStringFromSelector(nameObj, @selector(simpleText));
- if (!nameStr.length) {
- NSArray *runs = YouModObjectFromSelector(nameObj, @selector(runsArray));
- if (runs.count > 0) nameStr = YouModStringFromSelector(runs.firstObject, @selector(text));
- }
- if (!nameStr.length) nameStr = languageCode;
- if (!nameStr.length) nameStr = vssId;
+ NSString *languageCode = track.languageCode;
+ YTIFormattedString *nameObj = track.name;
+ NSString *nameStr = nameObj.dropdownOptionTitle;
- [items addObject:[YouModMenuItem itemWithTitle:nameStr subtitle:languageCode icon:YouModIconImage(637) handler:^{
+ [items addObject:[YouModMenuItem itemWithTitle:nameStr subtitle:languageCode icon:YouModIconImage(50) handler:^{
NSString *vttURL = [baseURL stringByAppendingString:@"&fmt=vtt"];
NSURL *url = [NSURL URLWithString:vttURL];
if (!url) {
- YouModSendToast(@"Invalid caption URL.", presenter);
+ YouModSendError(LOC(@"NO_CAPTIONS_URL"));
return;
}
- YouModSendToast(@"Downloading captions...", presenter);
+ YouModSendToast(LOC(@"DOWNLOADING_CAPTIONS"));
[[NSURLSession.sharedSession dataTaskWithURL:url completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) {
dispatch_async(dispatch_get_main_queue(), ^{
if (error || data.length == 0) {
- YouModSendToast(@"Failed to download captions.", presenter);
+ YouModSendError(LOC(@"CAPTIONS_FAILED"));
return;
}
- NSString *videoID = YouModVideoIDForPlayer(player) ?: @"video";
- NSString *filename = [NSString stringWithFormat:@"%@_%@.vtt", videoID, languageCode ?: @"captions"];
+ NSString *videoID = player.currentVideoID;
+ NSString *filename = [NSString stringWithFormat:@"%@_%@.vtt", videoID, languageCode];
NSURL *tempURL = [NSURL fileURLWithPath:[NSTemporaryDirectory() stringByAppendingPathComponent:filename]];
[data writeToURL:tempURL atomically:YES];
+ YouModSendSuccess(LOC(@"DOWNLOAD_COMPLETED"));
YouModShareFile(tempURL, presenter);
});
}] resume];
@@ -2276,68 +1706,61 @@ static void YouModShowCaptionsSheet(YTPlayerViewController *player, UIViewContro
}
if (items.count == 0) {
- YouModSendToast(@"No valid caption URLs found.", presenter);
+ YouModSendError(LOC(@"NO_CAPTIONS_URL"));
return;
}
- YouModPresentMenu(@"Download captions", items, presenter, sender);
+ YouModPresentMenu(LOC(@"DOWNLOAD_CAPTIONS"), items, presenter, sender);
}
-static void YouModShowDownloadManager(YTPlayerViewController *player, UIViewController *presenter, UIView *sender) {
- if (!player) {
- YouModSendToast(@"Open a video before using the download manager.", presenter);
- return;
- }
+static void YouModShowThumbnailSheet(YTPlayerViewController *player, UIViewController *presenter, UIView *sender) {
+ NSString *videoID = player.currentVideoID;
- NSString *videoID = YouModVideoIDForPlayer(player);
NSMutableArray *items = [NSMutableArray array];
- [items addObject:[YouModMenuItem itemWithTitle:@"Download video" subtitle:@"Choose quality" icon:YouModIconImage(658) handler:^{
- YouModShowVideoQualitySheet(player, presenter, sender);
- }]];
- [items addObject:[YouModMenuItem itemWithTitle:@"Download audio" subtitle:@"Choose format" icon:YouModIconImage(21) handler:^{
- YouModShowAudioSheet(player, presenter, sender);
- }]];
- [items addObject:[YouModMenuItem itemWithTitle:@"Download captions" subtitle:@"Save subtitles as VTT" icon:YouModIconImage(637) handler:^{
- YouModShowCaptionsSheet(player, presenter, sender);
- }]];
- [items addObject:[YouModMenuItem itemWithTitle:@"Copy diagnostics" subtitle:@"Copy last error log" icon:YouModIconImage(870) handler:^{
- YouModCopyDownloadDiagnostics(presenter);
- }]];
- [items addObject:[YouModMenuItem itemWithTitle:@"Save thumbnail" subtitle:@"Save to Photos" icon:YouModIconImage(367) handler:^{
+ [items addObject:[YouModMenuItem itemWithTitle:LOC(@"SAVE_THUMBNAIL") subtitle:nil icon:YouModIconImage(57) handler:^{
YouModDownloadThumbnail(videoID, presenter);
}]];
- [items addObject:[YouModMenuItem itemWithTitle:@"Copy video information" subtitle:@"Copy title and URL" icon:YouModIconImage(250) handler:^{
- YouModCopyVideoInfo(player, presenter);
+ [items addObject:[YouModMenuItem itemWithTitle:LOC(@"SHOW_THUMBNAIL") subtitle:nil icon:YouModIconImage(208) handler:^{
+ YouModShowThumbnailViewer(videoID, presenter);
+ }]];
+ [items addObject:[YouModMenuItem itemWithTitle:LOC(@"COPY_THUMBNAIL") subtitle:nil icon:YouModIconImage(250) handler:^{
+ YouModCopyThumbnail(videoID, presenter);
}]];
- YouModPresentMenu(@"Download manager", items, presenter, sender);
-}
-
-void YouModConfigureDownloadButton(_ASDisplayView *view) {
- if (![view.accessibilityIdentifier isEqualToString:@"id.ui.add_to.offline.button"]) return;
- if (!IS_ENABLED(DownloadManager) || IS_ENABLED(HideDownloadButton)) return;
- if (objc_getAssociatedObject(view, @selector(YouModDownloadButtonTapped:))) return;
- view.userInteractionEnabled = YES;
- UITapGestureRecognizer *tap = [[UITapGestureRecognizer alloc] initWithTarget:view action:@selector(YouModDownloadButtonTapped:)];
- tap.cancelsTouchesInView = YES;
- tap.delaysTouchesBegan = YES;
- tap.delaysTouchesEnded = YES;
- [view addGestureRecognizer:tap];
- objc_setAssociatedObject(view, @selector(YouModDownloadButtonTapped:), @YES, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
+ YouModPresentMenu(LOC(@"COPY_VID_INFO"), items, presenter, sender);
}
-%hook _ASDisplayView
+static void YouModShowDownloadManager(YTPlayerViewController *player, UIViewController *presenter, UIView *sender, BOOL isShorts) {
+ if (!player) {
+ YouModSendError(LOC(@"OPEN_VID_BEFORE"));
+ return;
+ }
+ NSMutableArray *items = [NSMutableArray array];
-%new
-- (void)YouModDownloadButtonTapped:(UITapGestureRecognizer *)sender {
- if (sender.state != UIGestureRecognizerStateEnded) return;
- UIViewController *presenter = YouModPresenterForSender(self, YouModCurrentPlayerViewController);
- YTPlayerViewController *player = YouModPlayerFromViewController(presenter);
- YouModShowDownloadManager(player, presenter, self);
+ if (isShorts) {
+ [items addObject:[YouModMenuItem itemWithTitle:LOC(@"DOWNLOAD_SHORTS") subtitle:nil icon:YouModIconImage(769) handler:^{
+ YouModShowVideoQualitySheet(player, presenter, sender, YES);
+ }]];
+ } else {
+ [items addObject:[YouModMenuItem itemWithTitle:LOC(@"DOWNLOAD_VIDEO") subtitle:nil icon:YouModIconImage(658) handler:^{
+ YouModShowVideoQualitySheet(player, presenter, sender, NO);
+ }]];
+ }
+ [items addObject:[YouModMenuItem itemWithTitle:LOC(@"DOWNLOAD_AUDIO") subtitle:nil icon:YouModIconImage(21) handler:^{
+ YouModStartDownloadAudio(player, presenter, sender);
+ }]];
+ [items addObject:[YouModMenuItem itemWithTitle:LOC(@"DOWNLOAD_CAPTIONS") subtitle:nil icon:YouModIconImage(50) handler:^{
+ YouModShowCaptionsSheet(player, presenter, sender);
+ }]];
+ [items addObject:[YouModMenuItem itemWithTitle:LOC(@"THUMBNAIL_OPTIONS") subtitle:nil icon:YouModIconImage(367) handler:^{
+ YouModShowThumbnailSheet(player, presenter, sender);
+ }]];
+ [items addObject:[YouModMenuItem itemWithTitle:LOC(@"COPY_VID_INFO") subtitle:nil icon:YouModIconImage(250) handler:^{
+ YouModShowCopyVideoInfoSheet(player, presenter, sender);
+ }]];
+ YouModPresentMenu(LOC(@"DOWNLOAD_MANAGER"), items, presenter, sender);
}
-%end
-
%hook YTPlayerViewController
- (void)viewDidAppear:(BOOL)animated {
@@ -2384,3 +1807,112 @@ NSString *YouModGlobalAuthHeader = nil;
return token;
}
%end
+
+void YouModConfigureDownloadButton(_ASDisplayView *view) {
+ if (!IS_ENABLED(DownloadManager)) return;
+ if (objc_getAssociatedObject(view, @selector(YouModDownloadButtonTapped:))) return;
+
+ // For iPad (Old ID)
+ if ([view.accessibilityIdentifier isEqualToString:@"id.ui.add_to.offline.button"]) {
+ view.userInteractionEnabled = YES;
+ UITapGestureRecognizer *tap = [[UITapGestureRecognizer alloc] initWithTarget:view action:@selector(YouModDownloadButtonTapped:)];
+ tap.cancelsTouchesInView = YES;
+ tap.delaysTouchesBegan = YES;
+ tap.delaysTouchesEnded = YES;
+ [view addGestureRecognizer:tap];
+ objc_setAssociatedObject(view, @selector(YouModDownloadButtonTapped:), @YES, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
+ }
+}
+
+%hook _ASDisplayView
+
+%new
+- (void)YouModDownloadButtonTapped:(UITapGestureRecognizer *)sender {
+ if (sender.state != UIGestureRecognizerStateEnded) return;
+ UIViewController *presenter = YouModPresenterForSender(self, YouModCurrentPlayerViewController);
+ YTPlayerViewController *player = YouModPlayerFromViewController(presenter);
+ YouModShowDownloadManager(player, presenter, self, NO);
+}
+
+%end
+
+// Download button in Shorts
+%hook YTReelWatchPlaybackOverlayView
+
+- (void)layoutSubviews {
+ %orig;
+ if (!IS_ENABLED(DownloadManager) || !IS_ENABLED(AddDownloadToShorts)) return;
+ UIView *likeButtonView = nil;
+ for (UIView *subview in self.subviews) {
+ if ([subview isKindOfClass:%c(YTReelPlayerButton)]) {
+ likeButtonView = subview;
+ break;
+ }
+ }
+
+ if (!likeButtonView) return;
+
+ YTQTMButton *downloadBtn = (YTQTMButton *)[self viewWithTag:1501];
+ if (!downloadBtn) {
+ UIImageSymbolConfiguration *config = [UIImageSymbolConfiguration configurationWithPointSize:20 weight:UIImageSymbolWeightMedium];
+ // Template rendering so YTQTMButton's tint colours the glyph reliably.
+ UIImage *icon = [[UIImage systemImageNamed:@"arrow.down.circle" withConfiguration:config] imageWithRenderingMode:UIImageRenderingModeAlwaysTemplate];
+ downloadBtn = [%c(YTQTMButton) iconButton];
+ [downloadBtn setImage:icon forState:UIControlStateNormal];
+ downloadBtn.tintColor = [UIColor whiteColor];
+ downloadBtn.exclusiveTouch = YES;
+ downloadBtn.tag = 1501;
+
+ [downloadBtn addTarget:self action:@selector(didTapYouModShortsDownload:) forControlEvents:UIControlEventTouchUpInside];
+ [self addSubview:downloadBtn];
+ }
+
+ CGFloat btnWidth = 64.0;
+ CGFloat btnHeight = 60.0;
+
+ CGFloat pX = likeButtonView.frame.origin.x;
+ CGFloat pY = likeButtonView.frame.origin.y + 85.0;
+
+ downloadBtn.frame = CGRectMake(pX, pY, btnWidth, btnHeight);
+
+ [self bringSubviewToFront:downloadBtn];
+}
+
+%new
+- (void)didTapYouModShortsDownload:(UIButton *)button {
+ // Loop to find playerview
+ UIResponder *responder = self.nextResponder;
+ while (responder && ![responder isKindOfClass:%c(YTShortsPlayerViewController)]) {
+ responder = responder.nextResponder;
+ }
+ if (responder) {
+ YTShortsPlayerViewController *shortsPlayerView = (YTShortsPlayerViewController *)responder;
+ YTPlayerViewController *player = (YTPlayerViewController *)shortsPlayerView.childViewControllers[0];
+ UIViewController *presenter = YouModPresenterForSender(button, player);
+ YouModShowDownloadManager(player, presenter, button, YES);
+ }
+}
+
+%end
+
+%ctor {
+ // An explicit constructor must call %init itself; without it Logos does not
+ // initialize the hooks in this file.
+ %init;
+ // Register the download button in the player overlay's custom button row.
+ // sortOrder 200 places it to the left of the SponsorBlock toggle (sortOrder 100).
+ YMOverlayButtonSpec *download = [[YMOverlayButtonSpec alloc] init];
+ download.identifier = @"download.video";
+ download.symbolName = @"arrow.down.circle";
+ download.tintColor = [UIColor whiteColor];
+ download.sortOrder = 200;
+ download.isVisible = ^BOOL(YTPlayerViewController *player) {
+ return IS_ENABLED(DownloadManager);
+ };
+ download.onTap = ^(YTPlayerViewController *player, UIButton *button) {
+ UIViewController *presenter = YouModPresenterForSender(button, player ?: YouModCurrentPlayerViewController);
+ YTPlayerViewController *resolved = YouModPlayerFromViewController(presenter) ?: player ?: YouModCurrentPlayerViewController;
+ YouModShowDownloadManager(resolved, presenter, button, NO);
+ };
+ YMRegisterOverlayButton(download);
+}
diff --git a/Files/Download_FFmpeg.txt b/Files/Download_FFmpeg.txt
new file mode 100644
index 00000000..eccd24af
--- /dev/null
+++ b/Files/Download_FFmpeg.txt
@@ -0,0 +1,2390 @@
+// HUGE thanks to @daisuke1227 for implementing all of this
+#import "Headers.h"
+#import
+#import
+#import
+#import
+#import
+#import
+#import
+#import
+#import
+#import
+#import
+#import
+#import
+
+@interface YTDefaultSheetController (YouModDownload)
++ (instancetype)sheetControllerWithParentResponder:(id)parentResponder;
+- (void)addAction:(YTActionSheetAction *)action;
+- (void)presentFromView:(UIView *)view animated:(BOOL)animated completion:(void (^)(void))completion;
+- (void)presentFromViewController:(UIViewController *)vc animated:(BOOL)animated completion:(void (^)(void))completion;
+@end
+
+@interface YTPlayerViewController (YouModDownload)
+- (YTPlayerResponse *)contentPlayerResponse;
+@end
+
+@interface YTIPlayerResponse (YouModDownload)
+- (id)streamingData;
+@end
+
+@interface YTIStreamingData : NSObject
+- (NSArray *)adaptiveFormatsArray;
+@end
+
+@interface YTIFormatStream (YouModDownload)
+- (NSString *)mimeType;
+- (BOOL)hasContentLength;
+- (unsigned long long)contentLength;
+- (unsigned long long)approxDurationMs;
+@end
+
+@interface YTIVideoDetails (YouModDownload)
+- (NSString *)title;
+- (NSString *)author;
+- (NSString *)shortDescription;
+@end
+
+static UIImage *YouModIconImage(NSInteger iconType) {
+ YTIIcon *icon = [%c(YTIIcon) new];
+ icon.iconType = iconType;
+ UIImage *image = [icon iconImageWithColor:[UIColor labelColor]];
+ return [image imageWithRenderingMode:UIImageRenderingModeAlwaysTemplate];
+}
+
+@interface YouModMenuItem : NSObject
+@property (nonatomic, copy) NSString *title;
+@property (nonatomic, copy) NSString *subtitle;
+@property (nonatomic, strong) UIImage *iconImage;
+@property (nonatomic, copy) void (^handler)(void);
++ (instancetype)itemWithTitle:(NSString *)title subtitle:(NSString *)subtitle handler:(void (^)(void))handler;
++ (instancetype)itemWithTitle:(NSString *)title subtitle:(NSString *)subtitle icon:(UIImage *)icon handler:(void (^)(void))handler;
+@end
+
+@implementation YouModMenuItem
++ (instancetype)itemWithTitle:(NSString *)title subtitle:(NSString *)subtitle handler:(void (^)(void))handler {
+ return [self itemWithTitle:title subtitle:subtitle icon:nil handler:handler];
+}
++ (instancetype)itemWithTitle:(NSString *)title subtitle:(NSString *)subtitle icon:(UIImage *)icon handler:(void (^)(void))handler {
+ YouModMenuItem *item = [YouModMenuItem new];
+ item.title = title;
+ item.subtitle = subtitle;
+ item.iconImage = icon;
+ item.handler = handler;
+ return item;
+}
+@end
+
+@interface YouModMediaFormat : NSObject
+@property (nonatomic, strong) id source;
+@property (nonatomic, copy) NSString *urlString;
+@property (nonatomic, copy) NSString *qualityLabel;
+@property (nonatomic, copy) NSString *mimeType;
+@property (nonatomic, copy) NSDictionary *httpHeaders;
+@property (nonatomic, assign) unsigned long long contentLength;
+@property (nonatomic, assign) unsigned long long durationMs;
+@property (nonatomic, assign) NSInteger fps;
+@property (nonatomic, assign) BOOL video;
+@property (nonatomic, copy) NSString *languageCode;
+@property (nonatomic, copy) NSString *languageName;
+@property (nonatomic, assign) BOOL drcAudio;
+@end
+
+@implementation YouModMediaFormat
+@end
+
+@interface YouModAudioOutputFormat : NSObject
+@property (nonatomic, copy) NSString *identifier;
+@property (nonatomic, copy) NSString *title;
+@property (nonatomic, copy) NSString *subtitle;
+@property (nonatomic, copy) NSString *fileExtension;
+@property (nonatomic, copy) NSArray *ffmpegArguments;
+@property (nonatomic, assign) BOOL passthroughWhenCompatible;
+@property (nonatomic, assign) BOOL supported;
+@end
+
+@implementation YouModAudioOutputFormat
+@end
+
+typedef void (^YouModFileDownloadCompletion)(NSURL *fileURL, NSError *error);
+typedef void (^YouModMergeCompletion)(BOOL success, NSError *error);
+typedef void (^YouModRangeDownloadProgress)(unsigned long long completedBytes);
+
+@interface YouModDownloadChunk : NSObject
+@property (nonatomic, assign) unsigned long long offset;
+@property (nonatomic, assign) unsigned long long length;
+@property (nonatomic, assign) NSUInteger attempts;
+@end
+
+@implementation YouModDownloadChunk
+@end
+
+@interface YouModRangeDownloader : NSObject
+@property (nonatomic, strong) NSURL *url;
+@property (nonatomic, strong) NSURL *destinationURL;
+@property (nonatomic, copy) NSDictionary *httpHeaders;
+@property (nonatomic, assign) unsigned long long expectedBytes;
+@property (nonatomic, copy) YouModRangeDownloadProgress progress;
+@property (nonatomic, copy) YouModFileDownloadCompletion completion;
+@property (nonatomic, strong) NSURLSession *session;
+@property (nonatomic, strong) NSFileHandle *fileHandle;
+@property (nonatomic, strong) NSMutableArray *pendingChunks;
+@property (nonatomic, strong) NSMutableSet *tasks;
+@property (nonatomic, strong) dispatch_queue_t stateQueue;
+@property (nonatomic, strong) dispatch_queue_t fileQueue;
+@property (nonatomic, assign) NSUInteger activeTaskCount;
+@property (nonatomic, assign) NSUInteger totalChunkCount;
+@property (nonatomic, assign) unsigned long long completedBytes;
+@property (nonatomic, assign) BOOL cancelled;
+@property (nonatomic, assign) BOOL finished;
+- (instancetype)initWithURL:(NSURL *)url destinationURL:(NSURL *)destinationURL expectedBytes:(unsigned long long)expectedBytes headers:(NSDictionary *)headers progress:(YouModRangeDownloadProgress)progress completion:(YouModFileDownloadCompletion)completion;
+- (void)start;
+- (void)cancel;
+@end
+
+@interface YouModDownloadCoordinator : NSObject
+@property (nonatomic, strong) NSURLSession *session;
+@property (nonatomic, strong) NSURLSessionDownloadTask *task;
+@property (nonatomic, strong) NSURLSessionDataTask *metadataTask;
+@property (nonatomic, strong) YouModRangeDownloader *rangeDownloader;
+@property (nonatomic, strong) UIAlertController *progressAlert;
+@property (nonatomic, strong) UIProgressView *progressView;
+@property (nonatomic, weak) UIViewController *presenter;
+@property (nonatomic, copy) YouModFileDownloadCompletion fileCompletion;
+@property (nonatomic, strong) NSURL *destinationURL;
+@property (nonatomic, strong) NSURL *videoTempURL;
+@property (nonatomic, strong) NSURL *audioTempURL;
+@property (nonatomic, assign) unsigned long long completedBytes;
+@property (nonatomic, assign) unsigned long long totalBytes;
+@property (nonatomic, assign) unsigned long long currentBytes;
+@property (nonatomic, assign) unsigned long long currentExpectedBytes;
+@property (nonatomic, assign) BOOL currentResolvedSizeAddedToTotal;
+@property (nonatomic, assign) BOOL active;
+@property (nonatomic, assign) BOOL finishedCurrentFile;
+@property (nonatomic, assign) BOOL cancelled;
+@property (nonatomic, copy) NSString *baseProgressTitle;
+@property (nonatomic, assign) NSTimeInterval downloadStartTime;
++ (instancetype)sharedCoordinator;
+- (void)startVideoDownloadWithVideoFormat:(YouModMediaFormat *)videoFormat audioFormat:(YouModMediaFormat *)audioFormat fileName:(NSString *)fileName videoID:(NSString *)videoID presenter:(UIViewController *)presenter;
+- (void)startAudioDownloadWithAudioFormat:(YouModMediaFormat *)audioFormat fileName:(NSString *)fileName videoID:(NSString *)videoID presenter:(UIViewController *)presenter;
+- (void)startAudioDownloadWithAudioFormat:(YouModMediaFormat *)audioFormat fileName:(NSString *)fileName videoID:(NSString *)videoID outputFormat:(YouModAudioOutputFormat *)outputFormat presenter:(UIViewController *)presenter;
+- (void)startDirectVideoDownloadWithVideoFormat:(YouModMediaFormat *)videoFormat audioFormat:(YouModMediaFormat *)audioFormat fileName:(NSString *)fileName videoID:(NSString *)videoID presenter:(UIViewController *)presenter;
+- (void)startDirectSingleVideoDownloadWithFormat:(YouModMediaFormat *)format fileName:(NSString *)fileName videoID:(NSString *)videoID presenter:(UIViewController *)presenter;
+- (void)startDirectAudioDownloadWithAudioFormat:(YouModMediaFormat *)audioFormat fileName:(NSString *)fileName videoID:(NSString *)videoID presenter:(UIViewController *)presenter;
+- (void)startDirectAudioDownloadWithAudioFormat:(YouModMediaFormat *)audioFormat fileName:(NSString *)fileName videoID:(NSString *)videoID outputFormat:(YouModAudioOutputFormat *)outputFormat presenter:(UIViewController *)presenter;
+- (void)mergeVideoURL:(NSURL *)videoURL audioURL:(NSURL *)audioURL fileName:(NSString *)fileName outputExtension:(NSString *)outputExtension durationMs:(unsigned long long)durationMs presenter:(UIViewController *)presenter;
+- (void)mergeVideoWithAVFoundationVideoURL:(NSURL *)videoURL audioURL:(NSURL *)audioURL outputURL:(NSURL *)outputURL durationMs:(unsigned long long)durationMs presenter:(UIViewController *)presenter fallbackError:(NSError *)fallbackError;
+- (void)trimSingleVideoURL:(NSURL *)inputURL outputURL:(NSURL *)outputURL durationMs:(unsigned long long)durationMs presenter:(UIViewController *)presenter;
+- (void)convertAudioURL:(NSURL *)inputURL outputURL:(NSURL *)outputURL outputFormat:(YouModAudioOutputFormat *)outputFormat durationMs:(unsigned long long)durationMs presenter:(UIViewController *)presenter;
+@end
+
+static const unsigned long long YouModFastDownloadMinimumBytes = 256ULL * 1024ULL;
+static const unsigned long long YouModFastDownloadChunkBytes = 4ULL * 1024ULL * 1024ULL;
+static const NSUInteger YouModFastDownloadConcurrency = 8;
+static const NSUInteger YouModFastDownloadMaxAttempts = 3;
+
+static BOOL YouModHTTPHeadersContainField(NSDictionary *headers, NSString *field) {
+ for (id key in headers) {
+ if ([key isKindOfClass:NSString.class] && [(NSString *)key caseInsensitiveCompare:field] == NSOrderedSame)
+ return YES;
+ }
+ return NO;
+}
+
+static NSString *YouModYouTubeCookiesString(void) {
+ NSMutableArray *cookieStrings = [NSMutableArray array];
+ for (NSHTTPCookie *cookie in [[NSHTTPCookieStorage sharedHTTPCookieStorage] cookies]) {
+ if ([cookie.domain containsString:@"youtube.com"]) {
+ [cookieStrings addObject:[NSString stringWithFormat:@"%@=%@", cookie.name, cookie.value]];
+ }
+ }
+ return [cookieStrings componentsJoinedByString:@"; "];
+}
+
+static NSString *YouModNativeUserAgent(void) {
+ NSString *version = @"21.18.4";
+ NSString *sysVersion = [[UIDevice currentDevice].systemVersion stringByReplacingOccurrencesOfString:@"." withString:@"_"] ?: @"18_7";
+ return [NSString stringWithFormat:@"com.google.ios.youtube/%@ (iPhone; CPU iPhone OS %@ like Mac OS X)", version, sysVersion];
+}
+
+static void YouModApplyDownloadHeaders(NSMutableURLRequest *request, NSDictionary *headers) {
+ for (id key in headers) {
+ id value = headers[key];
+ if ([key isKindOfClass:NSString.class] && [value isKindOfClass:NSString.class])
+ [request setValue:value forHTTPHeaderField:key];
+ }
+ if (!YouModHTTPHeadersContainField(headers, @"User-Agent"))
+ [request setValue:YouModNativeUserAgent() forHTTPHeaderField:@"User-Agent"];
+ if (!YouModHTTPHeadersContainField(headers, @"Origin"))
+ [request setValue:@"https://www.youtube.com" forHTTPHeaderField:@"Origin"];
+ if (!YouModHTTPHeadersContainField(headers, @"Referer"))
+ [request setValue:@"https://www.youtube.com/" forHTTPHeaderField:@"Referer"];
+ if (!YouModHTTPHeadersContainField(headers, @"Cookie")) {
+ NSString *cookies = YouModYouTubeCookiesString();
+ if (cookies.length > 0) [request setValue:cookies forHTTPHeaderField:@"Cookie"];
+ }
+ extern NSString *YouModGlobalAuthHeader;
+ if (YouModGlobalAuthHeader && !YouModHTTPHeadersContainField(headers, @"Authorization")) {
+ [request setValue:YouModGlobalAuthHeader forHTTPHeaderField:@"Authorization"];
+ }
+ [request setValue:@"identity" forHTTPHeaderField:@"Accept-Encoding"];
+}
+
+@implementation YouModRangeDownloader
+
+- (instancetype)initWithURL:(NSURL *)url destinationURL:(NSURL *)destinationURL expectedBytes:(unsigned long long)expectedBytes headers:(NSDictionary *)headers progress:(YouModRangeDownloadProgress)progress completion:(YouModFileDownloadCompletion)completion {
+ self = [super init];
+ if (self) {
+ _url = url;
+ _destinationURL = destinationURL;
+ _httpHeaders = [headers copy];
+ _expectedBytes = expectedBytes;
+ _progress = [progress copy];
+ _completion = [completion copy];
+ _pendingChunks = [NSMutableArray array];
+ _tasks = [NSMutableSet set];
+ _stateQueue = dispatch_queue_create("com.youmod.download.range.state", DISPATCH_QUEUE_SERIAL);
+ _fileQueue = dispatch_queue_create("com.youmod.download.range.file", DISPATCH_QUEUE_SERIAL);
+
+ NSURLSessionConfiguration *configuration = [NSURLSessionConfiguration defaultSessionConfiguration];
+ configuration.HTTPMaximumConnectionsPerHost = YouModFastDownloadConcurrency;
+ configuration.requestCachePolicy = NSURLRequestReloadIgnoringLocalCacheData;
+ configuration.timeoutIntervalForResource = 300;
+ NSMutableDictionary *additionalHeaders = [NSMutableDictionary dictionary];
+ for (id key in headers) {
+ id value = headers[key];
+ if ([key isKindOfClass:NSString.class] && [value isKindOfClass:NSString.class])
+ additionalHeaders[key] = value;
+ }
+ if (!YouModHTTPHeadersContainField(additionalHeaders, @"User-Agent"))
+ additionalHeaders[@"User-Agent"] = YouModNativeUserAgent();
+ if (!YouModHTTPHeadersContainField(additionalHeaders, @"Origin"))
+ additionalHeaders[@"Origin"] = @"https://www.youtube.com";
+ if (!YouModHTTPHeadersContainField(additionalHeaders, @"Referer"))
+ additionalHeaders[@"Referer"] = @"https://www.youtube.com/";
+ if (!YouModHTTPHeadersContainField(additionalHeaders, @"Cookie")) {
+ NSString *cookies = YouModYouTubeCookiesString();
+ if (cookies.length > 0) additionalHeaders[@"Cookie"] = cookies;
+ }
+ extern NSString *YouModGlobalAuthHeader;
+ if (YouModGlobalAuthHeader && !YouModHTTPHeadersContainField(additionalHeaders, @"Authorization")) {
+ additionalHeaders[@"Authorization"] = YouModGlobalAuthHeader;
+ }
+ additionalHeaders[@"Accept-Encoding"] = @"identity";
+ configuration.HTTPAdditionalHeaders = additionalHeaders;
+ _session = [NSURLSession sessionWithConfiguration:configuration];
+ }
+ return self;
+}
+
+- (NSError *)errorWithCode:(NSInteger)code message:(NSString *)message {
+ return [NSError errorWithDomain:@"YouMod" code:code userInfo:@{NSLocalizedDescriptionKey: message ?: @"Download failed"}];
+}
+
+- (BOOL)prepareDestinationWithError:(NSError **)error {
+ [NSFileManager.defaultManager removeItemAtURL:self.destinationURL error:nil];
+ if (![NSFileManager.defaultManager createFileAtPath:self.destinationURL.path contents:nil attributes:nil]) {
+ if (error) *error = [self errorWithCode:20 message:@"Cannot create file"];
+ return NO;
+ }
+
+ self.fileHandle = [NSFileHandle fileHandleForWritingAtPath:self.destinationURL.path];
+ if (!self.fileHandle) {
+ if (error) *error = [self errorWithCode:21 message:@"Cannot open file"];
+ return NO;
+ }
+
+ @try {
+ [self.fileHandle truncateFileAtOffset:self.expectedBytes];
+ } @catch (NSException *exception) {
+ if (error) *error = [self errorWithCode:22 message:exception.reason ?: @"Cannot allocate file"];
+ return NO;
+ }
+ return YES;
+}
+
+- (void)start {
+ dispatch_async(self.stateQueue, ^{
+ if (self.expectedBytes == 0) {
+ [self finishWithErrorLocked:[self errorWithCode:23 message:@"Unknown stream size"]];
+ return;
+ }
+
+ NSError *error = nil;
+ if (![self prepareDestinationWithError:&error]) {
+ [self finishWithErrorLocked:error];
+ return;
+ }
+
+ unsigned long long chunkSize = self.expectedBytes / YouModFastDownloadConcurrency;
+ if (chunkSize < 256ULL * 1024ULL) chunkSize = 256ULL * 1024ULL;
+ if (chunkSize > YouModFastDownloadChunkBytes) chunkSize = YouModFastDownloadChunkBytes;
+
+ for (unsigned long long offset = 0; offset < self.expectedBytes; offset += chunkSize) {
+ YouModDownloadChunk *chunk = [YouModDownloadChunk new];
+ chunk.offset = offset;
+ unsigned long long remaining = self.expectedBytes - offset;
+ chunk.length = remaining < chunkSize ? remaining : chunkSize;
+ [self.pendingChunks addObject:chunk];
+ }
+ self.totalChunkCount = self.pendingChunks.count;
+ [self scheduleChunksLocked];
+ });
+}
+
+- (void)cancel {
+ dispatch_async(self.stateQueue, ^{
+ if (self.finished) return;
+ self.cancelled = YES;
+ self.finished = YES;
+ for (NSURLSessionDataTask *task in self.tasks) [task cancel];
+ [self.tasks removeAllObjects];
+ [self.session invalidateAndCancel];
+ dispatch_async(self.fileQueue, ^{
+ @try {
+ [self.fileHandle closeFile];
+ } @catch (__unused NSException *exception) {
+ }
+ [NSFileManager.defaultManager removeItemAtURL:self.destinationURL error:nil];
+ });
+ });
+}
+
+- (void)scheduleChunksLocked {
+ if (self.finished || self.cancelled) return;
+ while (self.activeTaskCount < YouModFastDownloadConcurrency && self.pendingChunks.count > 0) {
+ YouModDownloadChunk *chunk = self.pendingChunks.firstObject;
+ [self.pendingChunks removeObjectAtIndex:0];
+ [self startChunkLocked:chunk];
+ }
+
+ if (self.activeTaskCount == 0 && self.pendingChunks.count == 0) {
+ [self finishSuccessfullyLocked];
+ }
+}
+
+- (void)startChunkLocked:(YouModDownloadChunk *)chunk {
+ unsigned long long end = chunk.offset + chunk.length - 1;
+ NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:self.url cachePolicy:NSURLRequestReloadIgnoringLocalCacheData timeoutInterval:60.0];
+ YouModApplyDownloadHeaders(request, self.httpHeaders);
+ [request setValue:[NSString stringWithFormat:@"bytes=%llu-%llu", chunk.offset, end] forHTTPHeaderField:@"Range"];
+
+ __weak typeof(self) weakSelf = self;
+ __block NSURLSessionDataTask *task = nil;
+ task = [self.session dataTaskWithRequest:request completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) {
+ __strong typeof(weakSelf) self = weakSelf;
+ if (!self) return;
+ [self completeChunk:chunk task:task data:data response:response error:error];
+ }];
+ [self.tasks addObject:task];
+ self.activeTaskCount++;
+ [task resume];
+}
+
+- (NSError *)validationErrorForChunk:(YouModDownloadChunk *)chunk data:(NSData *)data response:(NSURLResponse *)response error:(NSError *)error {
+ if (error) return error;
+
+ NSHTTPURLResponse *httpResponse = [response isKindOfClass:NSHTTPURLResponse.class] ? (NSHTTPURLResponse *)response : nil;
+ NSInteger statusCode = httpResponse.statusCode;
+ BOOL statusOK = statusCode == 206 || (self.totalChunkCount == 1 && statusCode == 200);
+ if (httpResponse && !statusOK)
+ return [self errorWithCode:24 message:@"Range request rejected by server"];
+
+ if (data.length != chunk.length)
+ return [self errorWithCode:25 message:@"Incomplete chunk"];
+
+ return nil;
+}
+
+- (void)completeChunk:(YouModDownloadChunk *)chunk task:(NSURLSessionDataTask *)task data:(NSData *)data response:(NSURLResponse *)response error:(NSError *)error {
+ dispatch_async(self.stateQueue, ^{
+ if (self.activeTaskCount > 0) self.activeTaskCount--;
+ if (task) [self.tasks removeObject:task];
+ if (self.finished || self.cancelled) return;
+
+ NSError *validationError = [self validationErrorForChunk:chunk data:data response:response error:error];
+ if (validationError) {
+ if (validationError.code == 24) {
+ [self finishWithErrorLocked:validationError];
+ return;
+ }
+ if (chunk.attempts + 1 < YouModFastDownloadMaxAttempts) {
+ chunk.attempts++;
+ [self.pendingChunks insertObject:chunk atIndex:0];
+ [self scheduleChunksLocked];
+ } else {
+ [self finishWithErrorLocked:validationError];
+ }
+ return;
+ }
+
+ NSData *chunkData = [data copy];
+ dispatch_async(self.fileQueue, ^{
+ NSError *writeError = nil;
+ @try {
+ [self.fileHandle seekToFileOffset:chunk.offset];
+ [self.fileHandle writeData:chunkData];
+ } @catch (NSException *exception) {
+ writeError = [self errorWithCode:26 message:exception.reason ?: @"Write failed"];
+ }
+
+ dispatch_async(self.stateQueue, ^{
+ if (self.finished || self.cancelled) return;
+ if (writeError) {
+ [self finishWithErrorLocked:writeError];
+ return;
+ }
+
+ self.completedBytes += chunkData.length;
+ if (self.progress) {
+ unsigned long long completed = self.completedBytes;
+ dispatch_async(dispatch_get_main_queue(), ^{
+ self.progress(completed);
+ });
+ }
+ [self scheduleChunksLocked];
+ });
+ });
+ });
+}
+
+- (void)finishSuccessfullyLocked {
+ if (self.finished) return;
+ self.finished = YES;
+ [self.session finishTasksAndInvalidate];
+ dispatch_async(self.fileQueue, ^{
+ @try {
+ [self.fileHandle closeFile];
+ } @catch (__unused NSException *exception) {
+ }
+ dispatch_async(dispatch_get_main_queue(), ^{
+ if (self.completion) self.completion(self.destinationURL, nil);
+ });
+ });
+}
+
+- (void)finishWithErrorLocked:(NSError *)error {
+ if (self.finished) return;
+ self.finished = YES;
+ for (NSURLSessionDataTask *task in self.tasks) [task cancel];
+ [self.tasks removeAllObjects];
+ [self.session invalidateAndCancel];
+ dispatch_async(self.fileQueue, ^{
+ @try {
+ [self.fileHandle closeFile];
+ } @catch (__unused NSException *exception) {
+ }
+ [NSFileManager.defaultManager removeItemAtURL:self.destinationURL error:nil];
+ dispatch_async(dispatch_get_main_queue(), ^{
+ if (self.completion) self.completion(nil, error ?: [self errorWithCode:27 message:@"Download failed"]);
+ });
+ });
+}
+
+@end
+
+static __weak YTPlayerViewController *YouModCurrentPlayerViewController;
+
+void YouModDownloadSetCurrentPlayer(YTPlayerViewController *player) {
+ YouModCurrentPlayerViewController = player;
+}
+
+static NSString *YouModStringFromSelector(id object, SEL selector) {
+ if (!object) return nil;
+ id value = nil;
+ if ([object respondsToSelector:selector]) {
+ value = ((id (*)(id, SEL))objc_msgSend)(object, selector);
+ } else {
+ @try {
+ value = [object valueForKey:NSStringFromSelector(selector)];
+ } @catch (__unused NSException *exception) {
+ value = nil;
+ }
+ }
+ if ([value isKindOfClass:NSString.class]) return value;
+ if ([value isKindOfClass:NSURL.class]) return [(NSURL *)value absoluteString];
+ if ([value respondsToSelector:@selector(stringValue)]) return [value stringValue];
+ return [value respondsToSelector:@selector(description)] ? [value description] : nil;
+}
+
+static id YouModObjectFromSelector(id object, SEL selector) {
+ if (!object) return nil;
+ if ([object respondsToSelector:selector]) {
+ return ((id (*)(id, SEL))objc_msgSend)(object, selector);
+ }
+ @try {
+ return [object valueForKey:NSStringFromSelector(selector)];
+ } @catch (__unused NSException *exception) {
+ return nil;
+ }
+}
+
+static unsigned long long YouModUnsignedLongLongFromSelector(id object, SEL selector) {
+ if (!object) return 0;
+ if ([object respondsToSelector:selector]) {
+ return ((unsigned long long (*)(id, SEL))objc_msgSend)(object, selector);
+ }
+ @try {
+ id value = [object valueForKey:NSStringFromSelector(selector)];
+ if ([value respondsToSelector:@selector(unsignedLongLongValue)])
+ return [value unsignedLongLongValue];
+ } @catch (__unused NSException *exception) {
+ }
+ return 0;
+}
+
+static BOOL YouModBoolFromSelector(id object, SEL selector) {
+ if (!object) return NO;
+ if ([object respondsToSelector:selector]) {
+ return ((BOOL (*)(id, SEL))objc_msgSend)(object, selector);
+ }
+ @try {
+ id value = [object valueForKey:NSStringFromSelector(selector)];
+ if ([value respondsToSelector:@selector(boolValue)])
+ return [value boolValue];
+ } @catch (__unused NSException *exception) {
+ }
+ return NO;
+}
+
+static NSInteger YouModIntegerFromSelector(id object, SEL selector) {
+ if (!object) return 0;
+ if ([object respondsToSelector:selector]) {
+ return ((NSInteger (*)(id, SEL))objc_msgSend)(object, selector);
+ }
+ @try {
+ id value = [object valueForKey:NSStringFromSelector(selector)];
+ if ([value respondsToSelector:@selector(integerValue)])
+ return [value integerValue];
+ } @catch (__unused NSException *exception) {
+ }
+ return 0;
+}
+
+static UIViewController *YouModTopViewController(UIViewController *root) {
+ if (!root) {
+ UIWindow *keyWindow = nil;
+ for (UIWindow *window in UIApplication.sharedApplication.windows) {
+ if (window.isKeyWindow) {
+ keyWindow = window;
+ break;
+ }
+ }
+ root = keyWindow.rootViewController;
+ }
+ while (root.presentedViewController) root = root.presentedViewController;
+ if ([root isKindOfClass:UINavigationController.class])
+ return YouModTopViewController(((UINavigationController *)root).topViewController);
+ if ([root isKindOfClass:UITabBarController.class])
+ return YouModTopViewController(((UITabBarController *)root).selectedViewController);
+ return root;
+}
+
+static void YouModSendToast(NSString *message, id responder) {
+ Class toastClass = NSClassFromString(@"YTToastResponderEvent");
+ id event = [toastClass eventWithMessage:message firstResponder:responder ?: YouModTopViewController(nil)];
+ if ([event respondsToSelector:@selector(send)]) {
+ [event send];
+ return;
+ }
+
+ UIViewController *presenter = YouModTopViewController([responder isKindOfClass:UIViewController.class] ? responder : nil);
+ if (!presenter) return;
+ UIAlertController *alert = [UIAlertController alertControllerWithTitle:nil message:message preferredStyle:UIAlertControllerStyleAlert];
+ [presenter presentViewController:alert animated:YES completion:^{
+ dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(1.2 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
+ [alert dismissViewControllerAnimated:YES completion:nil];
+ });
+ }];
+}
+
+static NSString *YouModByteCount(unsigned long long bytes) {
+ if (bytes == 0) return nil;
+ NSByteCountFormatter *formatter = [NSByteCountFormatter new];
+ formatter.countStyle = NSByteCountFormatterCountStyleFile;
+ return [formatter stringFromByteCount:(long long)bytes];
+}
+
+static NSString *YouModGenerateCPN(void) {
+ static NSString *const alphabet = @"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_";
+ NSMutableString *nonce = [NSMutableString stringWithCapacity:16];
+ for (NSUInteger i = 0; i < 16; i++)
+ [nonce appendFormat:@"%C", [alphabet characterAtIndex:arc4random_uniform((uint32_t)alphabet.length)]];
+ return nonce;
+}
+
+static NSString *YouModURLStringBypassingThrottle(NSString *urlString) {
+ if (urlString.length == 0) return urlString;
+ NSURLComponents *components = [NSURLComponents componentsWithString:urlString];
+ if (components) {
+ NSMutableArray *queryItems = [components.queryItems mutableCopy] ?: [NSMutableArray array];
+ NSMutableArray *filtered = [NSMutableArray array];
+ for (NSURLQueryItem *item in queryItems) {
+ if (![item.name isEqualToString:@"n"])
+ [filtered addObject:item];
+ }
+ BOOL hasRateBypass = NO;
+ for (NSURLQueryItem *item in filtered) {
+ if ([item.name isEqualToString:@"ratebypass"]) { hasRateBypass = YES; break; }
+ }
+ if (!hasRateBypass)
+ [filtered addObject:[NSURLQueryItem queryItemWithName:@"ratebypass" value:@"yes"]];
+ components.queryItems = filtered;
+ NSString *result = components.string;
+ if (result.length > 0) return result;
+ }
+ return urlString;
+}
+
+static NSString *YouModURLStringWithCPN(NSString *urlString) {
+ if (urlString.length == 0) return urlString;
+ urlString = YouModURLStringBypassingThrottle(urlString);
+ if ([urlString containsString:@"cpn="]) return urlString;
+ Class ytDataUtils = NSClassFromString(@"YTDataUtils");
+ NSString *cpn = ((id (*)(Class, SEL))objc_msgSend)(ytDataUtils, @selector(generateClientSideNonce));
+ if (![cpn isKindOfClass:NSString.class] || cpn.length == 0)
+ cpn = YouModGenerateCPN();
+ NSString *separator = [urlString containsString:@"?"] ? @"&" : @"?";
+ return [NSString stringWithFormat:@"%@%@cpn=%@", urlString, separator, cpn];
+}
+
+static NSString *YouModSanitizedFileName(NSString *name) {
+ if (name.length == 0) return @"YouTube Video";
+ NSMutableCharacterSet *invalid = [NSMutableCharacterSet characterSetWithCharactersInString:@"/\\?%*|\"<>:"];
+ [invalid formUnionWithCharacterSet:NSCharacterSet.newlineCharacterSet];
+ NSArray *parts = [name componentsSeparatedByCharactersInSet:invalid];
+ NSString *clean = [[parts componentsJoinedByString:@" "] stringByTrimmingCharactersInSet:NSCharacterSet.whitespaceAndNewlineCharacterSet];
+ while ([clean containsString:@" "]) clean = [clean stringByReplacingOccurrencesOfString:@" " withString:@" "];
+ if (clean.length > 120) clean = [clean substringToIndex:120];
+ return clean.length ? clean : @"YouTube Video";
+}
+
+static NSURL *YouModDownloadsDirectoryURL(void) {
+ NSURL *documentsURL = [NSFileManager.defaultManager URLsForDirectory:NSDocumentDirectory inDomains:NSUserDomainMask].firstObject;
+ NSURL *downloadsURL = [documentsURL URLByAppendingPathComponent:@"YouMod Downloads" isDirectory:YES];
+ [NSFileManager.defaultManager createDirectoryAtURL:downloadsURL withIntermediateDirectories:YES attributes:nil error:nil];
+ return downloadsURL;
+}
+
+static NSString *YouModLastDownloadDiagnostic;
+
+static NSURL *YouModDiagnosticLogURL(void) {
+ return [YouModDownloadsDirectoryURL() URLByAppendingPathComponent:@"youmod-download-diagnostics.txt"];
+}
+
+static void YouModRecordDownloadDiagnostic(NSString *context, NSString *details) {
+ if (context.length == 0 && details.length == 0) return;
+
+ NSDateFormatter *formatter = [NSDateFormatter new];
+ formatter.locale = [NSLocale localeWithLocaleIdentifier:@"en_US_POSIX"];
+ formatter.dateFormat = @"yyyy-MM-dd HH:mm:ss ZZZZZ";
+ NSString *timestamp = [formatter stringFromDate:NSDate.date];
+ NSString *entry = [NSString stringWithFormat:@"[%@]\n%@\n%@\n\n", timestamp ?: @"", context ?: @"", details ?: @""];
+ YouModLastDownloadDiagnostic = entry;
+
+ NSURL *logURL = YouModDiagnosticLogURL();
+ NSData *data = [entry dataUsingEncoding:NSUTF8StringEncoding];
+ if (![NSFileManager.defaultManager fileExistsAtPath:logURL.path])
+ [NSFileManager.defaultManager createFileAtPath:logURL.path contents:nil attributes:nil];
+
+ NSFileHandle *handle = [NSFileHandle fileHandleForWritingAtPath:logURL.path];
+ if (!handle) return;
+ @try {
+ [handle seekToEndOfFile];
+ [handle writeData:data];
+ [handle closeFile];
+ } @catch (__unused NSException *exception) {
+ }
+}
+
+static NSString *YouModDownloadDiagnosticText(void) {
+ if (YouModLastDownloadDiagnostic.length) return YouModLastDownloadDiagnostic;
+ NSString *log = [NSString stringWithContentsOfURL:YouModDiagnosticLogURL() encoding:NSUTF8StringEncoding error:nil];
+ if (log.length == 0) return nil;
+ NSUInteger maxLength = 12000;
+ return log.length > maxLength ? [log substringFromIndex:log.length - maxLength] : log;
+}
+
+static void YouModCopyDownloadDiagnostics(UIViewController *presenter) {
+ NSString *diagnostic = YouModDownloadDiagnosticText();
+ if (diagnostic.length == 0) {
+ YouModSendToast(@"No download diagnostics yet.", presenter);
+ return;
+ }
+ UIPasteboard.generalPasteboard.string = diagnostic;
+ YouModSendToast(@"Copied download diagnostics", presenter);
+}
+
+static NSURL *YouModUniqueFileURL(NSString *fileName, NSString *extension) {
+ NSString *safeName = YouModSanitizedFileName(fileName);
+ NSURL *directoryURL = YouModDownloadsDirectoryURL();
+ NSURL *candidate = [directoryURL URLByAppendingPathComponent:[safeName stringByAppendingPathExtension:extension]];
+ NSUInteger index = 2;
+ while ([NSFileManager.defaultManager fileExistsAtPath:candidate.path]) {
+ NSString *indexed = [NSString stringWithFormat:@"%@ %lu", safeName, (unsigned long)index++];
+ candidate = [directoryURL URLByAppendingPathComponent:[indexed stringByAppendingPathExtension:extension]];
+ }
+ return candidate;
+}
+
+static NSURL *YouModTemporaryFileURL(NSString *extension) {
+ NSString *name = [[NSUUID UUID].UUIDString stringByAppendingPathExtension:extension];
+ return [NSURL fileURLWithPath:[NSTemporaryDirectory() stringByAppendingPathComponent:name]];
+}
+
+static NSInteger YouModResolutionFromQuality(NSString *quality);
+static NSInteger YouModFPSFromQuality(NSString *quality);
+static NSInteger YouModNormalizedFPS(NSInteger fps);
+static NSInteger YouModDisplayHeightForVideoHeight(NSInteger height);
+static NSString *YouModQualityLabel(NSInteger height, NSInteger fps, NSString *fallback);
+static BOOL YouModFFmpegKitAvailable(void);
+
+static unsigned long long YouModDurationMsForURL(NSURL *url) {
+ AVURLAsset *asset = [AVURLAsset URLAssetWithURL:url options:nil];
+ if (!CMTIME_IS_NUMERIC(asset.duration) || !CMTIME_IS_VALID(asset.duration)) return 0;
+ Float64 seconds = CMTimeGetSeconds(asset.duration);
+ if (!isfinite(seconds) || seconds <= 0.0) return 0;
+ return (unsigned long long)llround(seconds * 1000.0);
+}
+
+static NSString *YouModDurationSecondsArgument(unsigned long long durationMs) {
+ return [NSString stringWithFormat:@"%.3f", (double)durationMs / 1000.0];
+}
+
+static BOOL YouModCMTimeIsUsable(CMTime time) {
+ if (!CMTIME_IS_VALID(time) || !CMTIME_IS_NUMERIC(time) || CMTIME_IS_INDEFINITE(time)) return NO;
+ Float64 seconds = CMTimeGetSeconds(time);
+ return isfinite(seconds) && seconds > 0.0;
+}
+
+static CMTime YouModMinUsableDuration(CMTime first, CMTime second) {
+ BOOL firstOK = YouModCMTimeIsUsable(first);
+ BOOL secondOK = YouModCMTimeIsUsable(second);
+ if (firstOK && secondOK) return CMTIME_COMPARE_INLINE(first, <, second) ? first : second;
+ if (firstOK) return first;
+ if (secondOK) return second;
+ return kCMTimeInvalid;
+}
+
+static CMTime YouModExportDuration(AVAsset *videoAsset, AVAsset *audioAsset, unsigned long long expectedDurationMs) {
+ CMTime duration = kCMTimeInvalid;
+ if (expectedDurationMs > 0)
+ duration = CMTimeMakeWithSeconds((double)expectedDurationMs / 1000.0, 600);
+
+ CMTime videoDuration = YouModMinUsableDuration(videoAsset.duration, [[videoAsset tracksWithMediaType:AVMediaTypeVideo] firstObject].timeRange.duration);
+ CMTime audioDuration = audioAsset ? YouModMinUsableDuration(audioAsset.duration, [[audioAsset tracksWithMediaType:AVMediaTypeAudio] firstObject].timeRange.duration) : kCMTimeInvalid;
+ CMTime mediaDuration = audioAsset ? YouModMinUsableDuration(videoDuration, audioDuration) : videoDuration;
+
+ if (!YouModCMTimeIsUsable(duration)) return mediaDuration;
+ if (YouModCMTimeIsUsable(mediaDuration) && CMTIME_COMPARE_INLINE(duration, >, mediaDuration))
+ return mediaDuration;
+ return duration;
+}
+
+static NSMutableArray *YouModFFmpegKitLoadEntries(void) {
+ static NSMutableArray *entries = nil;
+ static dispatch_once_t onceToken;
+ dispatch_once(&onceToken, ^{
+ entries = [NSMutableArray array];
+ });
+ return entries;
+}
+
+static void YouModAppendFFmpegKitLoadEntry(NSString *format, ...) {
+ if (format.length == 0) return;
+
+ va_list arguments;
+ va_start(arguments, format);
+ NSString *entry = [[NSString alloc] initWithFormat:format arguments:arguments];
+ va_end(arguments);
+ if (entry.length == 0) return;
+
+ NSMutableArray *entries = YouModFFmpegKitLoadEntries();
+ @synchronized(entries) {
+ [entries addObject:entry];
+ if (entries.count > 220)
+ [entries removeObjectsInRange:NSMakeRange(0, entries.count - 220)];
+ }
+}
+
+static NSArray *YouModFFmpegKitSearchDirectories(void) {
+ NSMutableOrderedSet *directories = [NSMutableOrderedSet orderedSet];
+
+ // Path to YouMod.bundle/Frameworks inside the main app bundle
+ NSString *bundlePath = [[NSBundle.mainBundle resourcePath] stringByAppendingPathComponent:@"YouMod.bundle"];
+ NSString *frameworksInsideBundle = [bundlePath stringByAppendingPathComponent:@"Frameworks"];
+
+ // Safety check: only add if the directory actually exists
+ BOOL isDir = NO;
+ if ([[NSFileManager defaultManager] fileExistsAtPath:frameworksInsideBundle isDirectory:&isDir] && isDir) {
+ [directories addObject:frameworksInsideBundle];
+ }
+
+ return directories.array;
+}
+
+static void YouModDlopenPath(NSString *path, BOOL requireExistingFile) {
+ if (path.length == 0) return;
+ if (requireExistingFile && ![NSFileManager.defaultManager fileExistsAtPath:path]) {
+ YouModAppendFFmpegKitLoadEntry(@"missing %@", path);
+ return;
+ }
+
+ dlerror();
+ void *handle = dlopen(path.fileSystemRepresentation, RTLD_NOW | RTLD_GLOBAL);
+ const char *error = dlerror();
+ if (handle) {
+ YouModAppendFFmpegKitLoadEntry(@"loaded %@", path);
+ } else {
+ YouModAppendFFmpegKitLoadEntry(@"failed %@\n dlerror=%@", path, error ? [NSString stringWithUTF8String:error] : @"unknown");
+ }
+}
+
+static void YouModDlopenPathIfPresent(NSString *path) {
+ YouModDlopenPath(path, YES);
+}
+
+static void YouModLoadFrameworkBinary(NSString *directory, NSString *frameworkName, NSString *binaryName) {
+ if (directory.length == 0 || frameworkName.length == 0 || binaryName.length == 0) return;
+ YouModDlopenPathIfPresent([[directory stringByAppendingPathComponent:[frameworkName stringByAppendingString:@".framework"]] stringByAppendingPathComponent:binaryName]);
+ YouModDlopenPathIfPresent([[directory stringByAppendingPathComponent:[frameworkName stringByAppendingString:@".framework"]] stringByAppendingPathComponent:frameworkName]);
+}
+
+static void YouModLoadFFmpegKitIfNeeded(void) {
+ static BOOL attempted = NO;
+ if (NSClassFromString(@"FFmpegKit")) return;
+ if (attempted) return;
+ attempted = YES;
+
+ YouModAppendFFmpegKitLoadEntry(@"[YouMod] Starting bundled FFmpegKit load...");
+
+ // Order is important: load dependencies (avutil, etc.) before the main toolkit
+ NSArray *> *frameworks = @[
+ @[@"libavutil", @"libavutil"],
+ @[@"libswresample", @"libswresample"],
+ @[@"libswscale", @"libswscale"],
+ @[@"libavcodec", @"libavcodec"],
+ @[@"libavformat", @"libavformat"],
+ @[@"libavfilter", @"libavfilter"],
+ @[@"libavdevice", @"libavdevice"],
+ @[@"ffmpegkit", @"ffmpegkit"],
+ @[@"FFmpegKit", @"FFmpegKit"],
+ ];
+
+ NSArray *searchDirs = YouModFFmpegKitSearchDirectories();
+ if (searchDirs.count == 0) {
+ YouModAppendFFmpegKitLoadEntry(@"[YouMod] Error: Bundled Frameworks directory not found.");
+ return;
+ }
+
+ // Only iterate through our controlled bundle directory
+ for (NSString *directory in searchDirs) {
+ for (NSArray *framework in frameworks) {
+ // This helper uses dlopen on the direct path within our bundle
+ YouModLoadFrameworkBinary(directory, framework.firstObject, framework.lastObject);
+ }
+
+ if (NSClassFromString(@"FFmpegKit")) {
+ YouModAppendFFmpegKitLoadEntry(@"[YouMod] Success: FFmpegKit loaded from bundle.");
+ return;
+ }
+ }
+
+ YouModAppendFFmpegKitLoadEntry(@"[YouMod] Critical: FFmpegKit could not be found in YouMod.bundle.");
+}
+
+static Class YouModFFmpegKitClass(void) {
+ Class ffmpegKitClass = NSClassFromString(@"FFmpegKit");
+ if (!ffmpegKitClass) {
+ YouModLoadFFmpegKitIfNeeded();
+ ffmpegKitClass = NSClassFromString(@"FFmpegKit");
+ }
+ return ffmpegKitClass;
+}
+
+static BOOL YouModFFmpegKitAvailable(void) {
+ Class ffmpegKitClass = YouModFFmpegKitClass();
+ return ffmpegKitClass && [ffmpegKitClass respondsToSelector:@selector(executeWithArgumentsAsync:withCompleteCallback:withLogCallback:withStatisticsCallback:)];
+}
+
+static NSString *YouModFFmpegKitDiagnosticText(YouModAudioOutputFormat *outputFormat, YouModMediaFormat *sourceFormat, NSString *videoID) {
+ YouModLoadFFmpegKitIfNeeded();
+
+ Class ffmpegKitClass = NSClassFromString(@"FFmpegKit");
+ SEL executeSelector = @selector(executeWithArgumentsAsync:withCompleteCallback:withLogCallback:withStatisticsCallback:);
+ NSMutableArray *lines = [NSMutableArray array];
+ NSBundle *mainBundle = NSBundle.mainBundle;
+ NSString *resourcePath = mainBundle.resourcePath ?: @"";
+ NSString *privateFrameworksPath = mainBundle.privateFrameworksPath ?: @"";
+ NSString *executablePath = mainBundle.executablePath ?: @"";
+ NSString *bundlePath = [resourcePath stringByAppendingPathComponent:@"YouMod.bundle"];
+ NSString *packageFrameworkPath = [resourcePath stringByAppendingPathComponent:@"YouMod.bundle/Frameworks"];
+
+ [lines addObject:@"FFmpegKit lookup"];
+ [lines addObject:[NSString stringWithFormat:@"videoID=%@", videoID ?: @""]];
+ [lines addObject:[NSString stringWithFormat:@"requestedFormat=%@ (%@)", outputFormat.title ?: @"", outputFormat.identifier ?: @""]];
+ [lines addObject:[NSString stringWithFormat:@"sourceMime=%@", sourceFormat.mimeType ?: @""]];
+ [lines addObject:[NSString stringWithFormat:@"sourceQuality=%@", sourceFormat.qualityLabel ?: @""]];
+ [lines addObject:[NSString stringWithFormat:@"sourceBytes=%llu", sourceFormat.contentLength]];
+ [lines addObject:[NSString stringWithFormat:@"mainBundle=%@", mainBundle.bundlePath ?: @""]];
+ [lines addObject:[NSString stringWithFormat:@"resourcePath=%@", resourcePath]];
+ [lines addObject:[NSString stringWithFormat:@"privateFrameworksPath=%@", privateFrameworksPath]];
+ [lines addObject:[NSString stringWithFormat:@"executablePath=%@", executablePath]];
+ [lines addObject:[NSString stringWithFormat:@"YouMod.bundle exists=%@", [NSFileManager.defaultManager fileExistsAtPath:bundlePath] ? @"YES" : @"NO"]];
+ [lines addObject:[NSString stringWithFormat:@"YouMod.bundle/Frameworks exists=%@", [NSFileManager.defaultManager fileExistsAtPath:packageFrameworkPath] ? @"YES" : @"NO"]];
+ [lines addObject:[NSString stringWithFormat:@"FFmpegKit class=%@", ffmpegKitClass ? @"YES" : @"NO"]];
+ [lines addObject:[NSString stringWithFormat:@"FFmpegKit execute selector=%@", [ffmpegKitClass respondsToSelector:executeSelector] ? @"YES" : @"NO"]];
+ [lines addObject:[NSString stringWithFormat:@"ReturnCode class=%@", NSClassFromString(@"ReturnCode") ? @"YES" : @"NO"]];
+ [lines addObject:@"searchDirectories:"];
+ for (NSString *directory in YouModFFmpegKitSearchDirectories()) {
+ BOOL isDirectory = NO;
+ BOOL exists = [NSFileManager.defaultManager fileExistsAtPath:directory isDirectory:&isDirectory];
+ [lines addObject:[NSString stringWithFormat:@" %@ exists=%@ directory=%@", directory, exists ? @"YES" : @"NO", isDirectory ? @"YES" : @"NO"]];
+ }
+
+ NSMutableArray *entries = YouModFFmpegKitLoadEntries();
+ [lines addObject:@"dlopenAttempts:"];
+ @synchronized(entries) {
+ [lines addObjectsFromArray:entries];
+ }
+ return [lines componentsJoinedByString:@"\n"];
+}
+
+static void YouModCancelFFmpegKit(void) {
+ Class ffmpegKitClass = YouModFFmpegKitClass();
+ if ([ffmpegKitClass respondsToSelector:@selector(cancel)])
+ ((void (*)(Class, SEL))objc_msgSend)(ffmpegKitClass, @selector(cancel));
+}
+
+static NSError *YouModFFmpegErrorFromSession(id session) {
+ NSString *failure = YouModStringFromSelector(session, @selector(getFailStackTrace));
+ NSString *message = failure.length ? failure : @"FFmpeg failed";
+ return [NSError errorWithDomain:@"YouMod" code:7 userInfo:@{NSLocalizedDescriptionKey: message}];
+}
+
+static BOOL YouModPathExtensionIsPhotosVideo(NSString *extension) {
+ NSString *lower = extension.lowercaseString ?: @"";
+ return [@[@"mp4", @"m4v", @"mov"] containsObject:lower];
+}
+
+static BOOL YouModStartFFmpegKitMerge(NSURL *videoURL, NSURL *audioURL, NSURL *outputURL, unsigned long long durationMs, void (^progress)(float progress), YouModMergeCompletion completion) {
+ Class ffmpegKitClass = YouModFFmpegKitClass();
+ SEL executeSelector = @selector(executeWithArgumentsAsync:withCompleteCallback:withLogCallback:withStatisticsCallback:);
+ if (![ffmpegKitClass respondsToSelector:executeSelector]) return NO;
+
+ NSMutableArray *arguments = [@[
+ @"-y",
+ @"-i", videoURL.path,
+ @"-i", audioURL.path,
+ @"-map", @"0:v:0",
+ @"-map", @"1:a:0",
+ ] mutableCopy];
+ if (durationMs > 0)
+ [arguments addObjectsFromArray:@[@"-t", YouModDurationSecondsArgument(durationMs)]];
+ [arguments addObjectsFromArray:@[
+ @"-c", @"copy",
+ @"-shortest",
+ @"-avoid_negative_ts", @"make_zero",
+ ]];
+ if (YouModPathExtensionIsPhotosVideo(outputURL.pathExtension))
+ [arguments addObjectsFromArray:@[@"-movflags", @"+faststart"]];
+ [arguments addObject:outputURL.path];
+
+ id completeBlock = [^(id session) {
+ Class returnCodeClass = NSClassFromString(@"ReturnCode");
+ id returnCode = YouModObjectFromSelector(session, @selector(getReturnCode));
+ BOOL success = NO;
+ if ([returnCodeClass respondsToSelector:@selector(isSuccess:)])
+ success = ((BOOL (*)(Class, SEL, id))objc_msgSend)(returnCodeClass, @selector(isSuccess:), returnCode);
+
+ NSError *error = success ? nil : YouModFFmpegErrorFromSession(session);
+ dispatch_async(dispatch_get_main_queue(), ^{
+ if (success && [NSFileManager.defaultManager fileExistsAtPath:outputURL.path]) {
+ completion(YES, nil);
+ } else {
+ completion(NO, error ?: [NSError errorWithDomain:@"YouMod" code:7 userInfo:@{NSLocalizedDescriptionKey: @"Merge failed"}]);
+ }
+ });
+ } copy];
+
+ id statisticsBlock = durationMs ? [^(id statistics) {
+ if (!progress || ![statistics respondsToSelector:@selector(getTime)]) return;
+ double timeMs = ((double (*)(id, SEL))objc_msgSend)(statistics, @selector(getTime));
+ if (!isfinite(timeMs) || timeMs <= 0.0) return;
+ float mergeProgress = 0.985f + (0.01f * fminf((float)(timeMs / (double)durationMs), 1.0f));
+ dispatch_async(dispatch_get_main_queue(), ^{
+ progress(mergeProgress);
+ });
+ } copy] : nil;
+
+ ((id (*)(Class, SEL, NSArray *, id, id, id))objc_msgSend)(ffmpegKitClass, executeSelector, arguments, completeBlock, nil, statisticsBlock);
+ return YES;
+}
+
+static BOOL YouModStartFFmpegKitAudioConvert(NSURL *inputURL, NSURL *outputURL, YouModAudioOutputFormat *outputFormat, unsigned long long durationMs, void (^progress)(float progress), YouModMergeCompletion completion) {
+ Class ffmpegKitClass = YouModFFmpegKitClass();
+ SEL executeSelector = @selector(executeWithArgumentsAsync:withCompleteCallback:withLogCallback:withStatisticsCallback:);
+ if (![ffmpegKitClass respondsToSelector:executeSelector] || outputFormat.ffmpegArguments.count == 0) return NO;
+
+ NSMutableArray *arguments = [@[@"-y", @"-i", inputURL.path] mutableCopy];
+ [arguments addObjectsFromArray:outputFormat.ffmpegArguments];
+ [arguments addObject:outputURL.path];
+
+ id completeBlock = [^(id session) {
+ Class returnCodeClass = NSClassFromString(@"ReturnCode");
+ id returnCode = YouModObjectFromSelector(session, @selector(getReturnCode));
+ BOOL success = NO;
+ if ([returnCodeClass respondsToSelector:@selector(isSuccess:)])
+ success = ((BOOL (*)(Class, SEL, id))objc_msgSend)(returnCodeClass, @selector(isSuccess:), returnCode);
+
+ NSError *error = success ? nil : YouModFFmpegErrorFromSession(session);
+ dispatch_async(dispatch_get_main_queue(), ^{
+ if (success && [NSFileManager.defaultManager fileExistsAtPath:outputURL.path]) {
+ completion(YES, nil);
+ } else {
+ completion(NO, error ?: [NSError errorWithDomain:@"YouMod" code:13 userInfo:@{NSLocalizedDescriptionKey: @"Conversion failed"}]);
+ }
+ });
+ } copy];
+
+ id statisticsBlock = durationMs ? [^(id statistics) {
+ if (!progress || ![statistics respondsToSelector:@selector(getTime)]) return;
+ double timeMs = ((double (*)(id, SEL))objc_msgSend)(statistics, @selector(getTime));
+ if (!isfinite(timeMs) || timeMs <= 0.0) return;
+ float convertProgress = 0.985f + (0.01f * fminf((float)(timeMs / (double)durationMs), 1.0f));
+ dispatch_async(dispatch_get_main_queue(), ^{
+ progress(convertProgress);
+ });
+ } copy] : nil;
+
+ ((id (*)(Class, SEL, NSArray *, id, id, id))objc_msgSend)(ffmpegKitClass, executeSelector, arguments, completeBlock, nil, statisticsBlock);
+ return YES;
+}
+
+static NSString *YouModMimeDetail(NSString *mimeType) {
+ NSString *lower = mimeType.lowercaseString ?: @"";
+ if ([lower containsString:@"mp4"]) return @"MP4";
+ if ([lower containsString:@"webm"]) return @"WebM";
+ if ([lower containsString:@"mp3"]) return @"MP3";
+ if ([lower containsString:@"aac"]) return @"AAC";
+ return mimeType.length ? mimeType : @"Stream";
+}
+
+static NSString *YouModFileExtensionForFormat(YouModMediaFormat *format, NSString *fallbackExtension) {
+ NSString *lower = format.mimeType.lowercaseString ?: @"";
+ if ([lower containsString:@"webm"]) return @"webm";
+ if ([lower containsString:@"matroska"]) return @"mkv";
+ if ([lower containsString:@"quicktime"]) return @"mov";
+ if ([lower containsString:@"m4a"]) return @"m4a";
+ if ([lower containsString:@"mp4"]) return @"mp4";
+ return fallbackExtension ?: @"mp4";
+}
+
+static BOOL YouModFormatLooksMP4Family(YouModMediaFormat *format) {
+ NSString *mime = format.mimeType.lowercaseString ?: @"";
+ NSString *extension = YouModFileExtensionForFormat(format, @"").lowercaseString ?: @"";
+ return [mime containsString:@"mp4"] || [mime containsString:@"m4a"] || [mime containsString:@"quicktime"] || [@[@"mp4", @"m4a", @"m4v", @"mov"] containsObject:extension];
+}
+
+static BOOL YouModFormatLooksWebM(YouModMediaFormat *format) {
+ NSString *mime = format.mimeType.lowercaseString ?: @"";
+ NSString *extension = YouModFileExtensionForFormat(format, @"").lowercaseString ?: @"";
+ return [mime containsString:@"webm"] || [extension isEqualToString:@"webm"];
+}
+
+static NSString *YouModMergedVideoOutputExtension(YouModMediaFormat *videoFormat, YouModMediaFormat *audioFormat) {
+ if (YouModFormatLooksMP4Family(videoFormat) && YouModFormatLooksMP4Family(audioFormat)) return @"mp4";
+ if (YouModFormatLooksWebM(videoFormat) && YouModFormatLooksWebM(audioFormat)) return @"webm";
+ return @"mkv";
+}
+
+static BOOL YouModVideoFileCanUseAVFoundation(NSURL *fileURL) {
+ return YouModPathExtensionIsPhotosVideo(fileURL.pathExtension);
+}
+
+static BOOL YouModVideoFileCanSaveToPhotos(NSURL *fileURL) {
+ return YouModPathExtensionIsPhotosVideo(fileURL.pathExtension);
+}
+
+static YouModAudioOutputFormat *YouModAudioOutputFormatMake(NSString *identifier, NSString *title, NSString *subtitle, NSString *fileExtension, NSArray *ffmpegArguments, BOOL passthroughWhenCompatible, BOOL supported) {
+ YouModAudioOutputFormat *format = [YouModAudioOutputFormat new];
+ format.identifier = identifier;
+ format.title = title;
+ format.subtitle = subtitle;
+ format.fileExtension = fileExtension;
+ format.ffmpegArguments = ffmpegArguments;
+ format.passthroughWhenCompatible = passthroughWhenCompatible;
+ format.supported = supported;
+ return format;
+}
+
+static NSArray *YouModAudioOutputFormats(void) {
+ static NSArray *formats = nil;
+ static dispatch_once_t onceToken;
+ dispatch_once(&onceToken, ^{
+ formats = @[
+ YouModAudioOutputFormatMake(@"m4a", @"M4A", @"AAC container, passthrough when possible", @"m4a", @[@"-map", @"0:a:0", @"-vn", @"-c:a", @"aac", @"-b:a", @"192k", @"-movflags", @"+faststart"], YES, YES),
+ YouModAudioOutputFormatMake(@"aac", @"AAC", @"Lossy (192k)", @"aac", @[@"-map", @"0:a:0", @"-vn", @"-c:a", @"aac", @"-b:a", @"192k", @"-f", @"adts"], YES, YES),
+ YouModAudioOutputFormatMake(@"mp3", @"MP3", @"Lossy, widely compatible", @"mp3", @[@"-map", @"0:a:0", @"-vn", @"-c:a", @"libmp3lame", @"-q:a", @"2"], NO, YES),
+ YouModAudioOutputFormatMake(@"opus", @"Opus", @"Lossy, small file size", @"opus", @[@"-map", @"0:a:0", @"-vn", @"-c:a", @"libopus", @"-b:a", @"160k", @"-vbr", @"on"], NO, YES),
+ YouModAudioOutputFormatMake(@"ogg", @"OGG", @"Vorbis lossy", @"ogg", @[@"-map", @"0:a:0", @"-vn", @"-c:a", @"libvorbis", @"-q:a", @"6"], NO, YES),
+ YouModAudioOutputFormatMake(@"flac", @"FLAC", @"Lossless compressed", @"flac", @[@"-map", @"0:a:0", @"-vn", @"-c:a", @"flac", @"-compression_level", @"8"], NO, YES),
+ YouModAudioOutputFormatMake(@"alac", @"ALAC", @"Apple lossless (M4A)", @"m4a", @[@"-map", @"0:a:0", @"-vn", @"-c:a", @"alac", @"-movflags", @"+faststart"], NO, YES),
+ YouModAudioOutputFormatMake(@"wav", @"WAV", @"Uncompressed PCM", @"wav", @[@"-map", @"0:a:0", @"-vn", @"-c:a", @"pcm_s24le"], NO, YES),
+ YouModAudioOutputFormatMake(@"aiff", @"AIFF", @"Apple PCM", @"aiff", @[@"-map", @"0:a:0", @"-vn", @"-c:a", @"pcm_s24be"], NO, YES),
+ ];
+ });
+ return formats;
+}
+
+static YouModAudioOutputFormat *YouModDefaultAudioOutputFormat(void) {
+ return [YouModAudioOutputFormats() firstObject];
+}
+
+static BOOL YouModAudioOutputFormatCanPassthrough(YouModAudioOutputFormat *outputFormat, YouModMediaFormat *sourceFormat) {
+ if (!outputFormat.passthroughWhenCompatible) return NO;
+ NSString *identifier = outputFormat.identifier.lowercaseString ?: @"";
+ NSString *mime = sourceFormat.mimeType.lowercaseString ?: @"";
+ NSString *extension = YouModFileExtensionForFormat(sourceFormat, @"").lowercaseString ?: @"";
+ if ([identifier isEqualToString:@"m4a"] || [identifier isEqualToString:@"aac"])
+ return [extension isEqualToString:@"m4a"] || [mime containsString:@"mp4"] || [mime containsString:@"m4a"];
+ return NO;
+}
+
+static NSString *YouModAudioOutputFileExtension(YouModAudioOutputFormat *outputFormat, YouModMediaFormat *sourceFormat, BOOL passthrough) {
+ NSString *identifier = outputFormat.identifier.lowercaseString ?: @"";
+ NSString *mime = sourceFormat.mimeType.lowercaseString ?: @"";
+ if (passthrough && ([identifier isEqualToString:@"m4a"] || [identifier isEqualToString:@"aac"]) && ([mime containsString:@"mp4"] || [mime containsString:@"m4a"]))
+ return @"m4a";
+ return outputFormat.fileExtension ?: YouModFileExtensionForFormat(sourceFormat, @"m4a");
+}
+
+static NSString *YouModAudioOutputSubtitle(YouModAudioOutputFormat *outputFormat) {
+ return [NSString stringWithFormat:@"%@", outputFormat.subtitle];
+}
+
+static NSString *YouModFormatSubtitle(YouModMediaFormat *format) {
+ NSMutableArray *parts = [NSMutableArray array];
+ NSString *language = format.languageName.length ? format.languageName : format.languageCode;
+ if (language.length) [parts addObject:language];
+ if (format.drcAudio) [parts addObject:@"DRC"];
+ NSString *detail = YouModMimeDetail(format.mimeType);
+ if (detail.length) [parts addObject:detail];
+ NSString *size = YouModByteCount(format.contentLength);
+ if (size.length) [parts addObject:size];
+ return [parts componentsJoinedByString:@" - "];
+}
+
+static NSString *YouModVideoIDForPlayer(YTPlayerViewController *player) {
+ NSString *videoID = [player contentVideoID];
+ if (videoID.length == 0)
+ videoID = [player currentVideoID];
+ return videoID;
+}
+
+static NSArray *YouModPlayerResponsesForPlayer(YTPlayerViewController *player) {
+ NSMutableArray *responses = [NSMutableArray array];
+ id response = YouModObjectFromSelector(player, @selector(contentPlayerResponse));
+ if (response) [responses addObject:response];
+
+ id activeVideo = YouModObjectFromSelector(player, @selector(activeVideo));
+ response = YouModObjectFromSelector(activeVideo, @selector(contentPlayerResponse));
+ if (response && ![responses containsObject:response]) [responses addObject:response];
+ return responses.copy;
+}
+
+// Where is this going to?
+static NSArray *YouModCaptionTracksForPlayer(YTPlayerViewController *player) {
+ for (id response in YouModPlayerResponsesForPlayer(player)) {
+ id playerData = YouModObjectFromSelector(response, @selector(playerData)) ?: response;
+ id captions = YouModObjectFromSelector(playerData, @selector(captions));
+ id tracklistRenderer = YouModObjectFromSelector(captions, @selector(playerCaptionsTracklistRenderer));
+ NSArray *tracks = YouModObjectFromSelector(tracklistRenderer, @selector(captionTracksArray));
+ if (tracks.count > 0) return tracks;
+ }
+ return nil;
+}
+
+static id YouModPlayerDataForPlayer(YTPlayerViewController *player) {
+ id response = YouModPlayerResponsesForPlayer(player).firstObject;
+ id playerData = YouModObjectFromSelector(response, @selector(playerData));
+ return playerData ?: response;
+}
+
+static NSString *YouModTitleForPlayer(YTPlayerViewController *player) {
+ id playerData = YouModPlayerDataForPlayer(player);
+ id details = YouModObjectFromSelector(playerData, @selector(videoDetails));
+ NSString *title = YouModStringFromSelector(details, @selector(title));
+ NSString *author = YouModStringFromSelector(details, @selector(author));
+ if (author.length && title.length) {
+ return [NSString stringWithFormat:@"%@ - %@", author, title];
+ } else if (title.length) {
+ return title;
+ }
+ NSString *videoID = YouModVideoIDForPlayer(player);
+ return videoID.length ? [NSString stringWithFormat:@"YouTube %@", videoID] : @"YouTube Video";
+}
+
+static NSArray *YouModAdaptiveFormatObjectsForPlayer(YTPlayerViewController *player) {
+ NSMutableArray *formats = [NSMutableArray array];
+ NSMutableSet *seenPointers = [NSMutableSet set];
+
+ void (^appendFormats)(NSArray *) = ^(NSArray *candidateFormats) {
+ if (![candidateFormats isKindOfClass:NSArray.class]) return;
+ for (id format in candidateFormats) {
+ NSString *key = [NSString stringWithFormat:@"%p", format];
+ if ([seenPointers containsObject:key]) continue;
+ [seenPointers addObject:key];
+ [formats addObject:format];
+ }
+ };
+
+ id activeVideo = YouModObjectFromSelector(player, @selector(activeVideo));
+ id streamingData = YouModObjectFromSelector(activeVideo, @selector(streamingData));
+ appendFormats(YouModObjectFromSelector(streamingData, @selector(adaptiveStreams)));
+ appendFormats(YouModObjectFromSelector(activeVideo, @selector(selectableVideoFormats)));
+
+ for (id response in YouModPlayerResponsesForPlayer(player)) {
+ id playerData = YouModObjectFromSelector(response, @selector(playerData)) ?: response;
+ id responseStreamingData = YouModObjectFromSelector(playerData, @selector(streamingData));
+ appendFormats(YouModObjectFromSelector(responseStreamingData, @selector(adaptiveFormatsArray)));
+ }
+
+ return formats.copy;
+}
+
+static YouModMediaFormat *YouModMediaFormatFromStream(id stream, BOOL video) {
+ id formatStream = YouModObjectFromSelector(stream, @selector(formatStream));
+ NSString *url = YouModStringFromSelector(stream, @selector(URL));
+ if (url.length == 0) url = YouModStringFromSelector(formatStream, @selector(URL));
+ if (url.length == 0) url = YouModStringFromSelector(stream, @selector(url));
+ if (url.length == 0) url = YouModStringFromSelector(formatStream, @selector(url));
+ if (url.length == 0) return nil;
+
+ NSString *mimeType = YouModStringFromSelector(stream, @selector(mimeType));
+ if (mimeType.length == 0) mimeType = YouModStringFromSelector(formatStream, @selector(mimeType));
+ NSString *lowerMime = mimeType.lowercaseString ?: @"";
+ BOOL streamSaysVideo = YouModBoolFromSelector(stream, @selector(isVideo)) || YouModBoolFromSelector(formatStream, @selector(isVideo));
+ BOOL streamSaysAudio = YouModBoolFromSelector(stream, @selector(isAudio)) || YouModBoolFromSelector(formatStream, @selector(isAudio));
+ NSInteger itag = YouModIntegerFromSelector(stream, @selector(itag));
+ if (itag == 0) itag = YouModIntegerFromSelector(formatStream, @selector(itag));
+
+ NSSet *mp4VideoItags = [NSSet setWithObjects:@18, @22, @37, @38, @59, @78, @133, @134, @135, @136, @137, @160, @212, @264, @266, @298, @299, nil];
+ NSSet *m4aAudioItags = [NSSet setWithObjects:@139, @140, @141, @256, @258, @325, @328, nil];
+ BOOL itagMatches = video ? [mp4VideoItags containsObject:@(itag)] : [m4aAudioItags containsObject:@(itag)];
+ BOOL typeMatches = video ? ([lowerMime containsString:@"video/"] || streamSaysVideo || itagMatches) : ([lowerMime containsString:@"audio/"] || streamSaysAudio || itagMatches);
+ if (!typeMatches) return nil;
+
+ BOOL mimeLooksMP4 = [lowerMime containsString:@"mp4"] || [lowerMime containsString:@"m4a"];
+ BOOL canRemuxWithFFmpeg = YouModFFmpegKitAvailable();
+ if (mimeType.length && !mimeLooksMP4 && !itagMatches && !canRemuxWithFFmpeg) return nil;
+
+ YouModMediaFormat *format = [YouModMediaFormat new];
+ format.source = stream;
+ format.video = video;
+ format.urlString = YouModURLStringWithCPN(url);
+ format.mimeType = mimeType.length ? mimeType : (video ? @"video/mp4" : @"audio/mp4");
+ NSInteger height = YouModIntegerFromSelector(stream, @selector(height));
+ if (height == 0) height = YouModIntegerFromSelector(formatStream, @selector(height));
+ NSInteger fps = YouModIntegerFromSelector(stream, @selector(fps));
+ if (fps == 0) fps = YouModIntegerFromSelector(formatStream, @selector(fps));
+ if (fps == 0) fps = YouModIntegerFromSelector(stream, @selector(framesPerSecond));
+ if (fps == 0) fps = YouModIntegerFromSelector(formatStream, @selector(framesPerSecond));
+ if (fps == 0) fps = YouModIntegerFromSelector(stream, @selector(frameRate));
+ if (fps == 0) fps = YouModIntegerFromSelector(formatStream, @selector(frameRate));
+ fps = YouModNormalizedFPS(fps);
+ format.fps = fps;
+ format.qualityLabel = YouModStringFromSelector(stream, @selector(qualityLabel));
+ if (format.qualityLabel.length == 0) format.qualityLabel = YouModStringFromSelector(formatStream, @selector(qualityLabel));
+ if (video) {
+ NSInteger labelHeight = YouModResolutionFromQuality(format.qualityLabel);
+ NSInteger labelFPS = YouModFPSFromQuality(format.qualityLabel);
+ if (labelHeight == 960) format.qualityLabel = YouModQualityLabel(labelHeight, fps ?: labelFPS, nil);
+ else if (labelFPS == 0 && fps > 0) format.qualityLabel = YouModQualityLabel(height, fps, format.qualityLabel);
+ if (format.qualityLabel.length == 0) format.qualityLabel = YouModQualityLabel(height, fps, nil);
+ }
+ if (format.qualityLabel.length == 0 && !video) format.qualityLabel = @"Audio";
+ if (!video) {
+ NSString *languageCode = YouModStringFromSelector(stream, @selector(languageCode));
+ if (languageCode.length == 0) languageCode = YouModStringFromSelector(formatStream, @selector(languageCode));
+ if (languageCode.length == 0) languageCode = YouModStringFromSelector(stream, @selector(language));
+ if (languageCode.length == 0) languageCode = YouModStringFromSelector(formatStream, @selector(language));
+ format.languageCode = languageCode;
+
+ NSString *languageName = YouModStringFromSelector(stream, @selector(languageName));
+ if (languageName.length == 0) languageName = YouModStringFromSelector(formatStream, @selector(languageName));
+ if (languageName.length == 0) languageName = YouModStringFromSelector(stream, @selector(displayName));
+ if (languageName.length == 0) languageName = YouModStringFromSelector(formatStream, @selector(displayName));
+ format.languageName = languageName.length ? languageName : languageCode;
+
+ NSMutableArray *audioTraits = [NSMutableArray array];
+ for (NSString *value in @[
+ mimeType ?: @"",
+ format.qualityLabel ?: @"",
+ YouModStringFromSelector(stream, @selector(audioTrack)) ?: @"",
+ YouModStringFromSelector(formatStream, @selector(audioTrack)) ?: @"",
+ YouModStringFromSelector(stream, @selector(audioTrackType)) ?: @"",
+ YouModStringFromSelector(formatStream, @selector(audioTrackType)) ?: @"",
+ YouModStringFromSelector(stream, @selector(audioTrackDisplayName)) ?: @"",
+ YouModStringFromSelector(formatStream, @selector(audioTrackDisplayName)) ?: @"",
+ ]) {
+ if (value.length) [audioTraits addObject:value];
+ }
+ format.drcAudio = [[audioTraits componentsJoinedByString:@" "] localizedCaseInsensitiveContainsString:@"drc"];
+ }
+ if (YouModBoolFromSelector(stream, @selector(hasContentLength)) || [stream respondsToSelector:@selector(contentLength)])
+ format.contentLength = YouModUnsignedLongLongFromSelector(stream, @selector(contentLength));
+ if (format.contentLength == 0 && (YouModBoolFromSelector(formatStream, @selector(hasContentLength)) || [formatStream respondsToSelector:@selector(contentLength)]))
+ format.contentLength = YouModUnsignedLongLongFromSelector(formatStream, @selector(contentLength));
+ format.durationMs = YouModUnsignedLongLongFromSelector(stream, @selector(approxDurationMs));
+ if (format.durationMs == 0) format.durationMs = YouModUnsignedLongLongFromSelector(formatStream, @selector(approxDurationMs));
+
+ NSMutableDictionary *headers = [NSMutableDictionary dictionary];
+ NSDictionary *streamHeaders = YouModObjectFromSelector(stream, @selector(httpHeaders));
+ if (![streamHeaders isKindOfClass:NSDictionary.class]) streamHeaders = YouModObjectFromSelector(formatStream, @selector(httpHeaders));
+ if (![streamHeaders isKindOfClass:NSDictionary.class]) streamHeaders = YouModObjectFromSelector(stream, @selector(headers));
+ if (![streamHeaders isKindOfClass:NSDictionary.class]) streamHeaders = YouModObjectFromSelector(formatStream, @selector(headers));
+ if ([streamHeaders isKindOfClass:NSDictionary.class]) {
+ for (id key in streamHeaders) {
+ id value = streamHeaders[key];
+ if ([key isKindOfClass:NSString.class] && [value isKindOfClass:NSString.class])
+ headers[key] = value;
+ }
+ }
+ if (!YouModHTTPHeadersContainField(headers, @"Origin"))
+ headers[@"Origin"] = @"https://www.youtube.com";
+ if (!YouModHTTPHeadersContainField(headers, @"Referer"))
+ headers[@"Referer"] = @"https://www.youtube.com/";
+ format.httpHeaders = headers;
+ return format;
+}
+
+static NSInteger YouModResolutionFromQuality(NSString *quality) {
+ NSScanner *scanner = [NSScanner scannerWithString:quality ?: @""];
+ NSInteger value = 0;
+ [scanner scanInteger:&value];
+ return value;
+}
+
+static NSInteger YouModFPSFromQuality(NSString *quality) {
+ NSString *lower = quality.lowercaseString ?: @"";
+ NSRange pRange = [lower rangeOfString:@"p"];
+ if (pRange.location != NSNotFound && pRange.location + 1 < lower.length) {
+ NSString *afterP = [lower substringFromIndex:pRange.location + 1];
+ NSScanner *scanner = [NSScanner scannerWithString:afterP];
+ NSInteger fps = 0;
+ if ([scanner scanInteger:&fps] && fps > 0) return fps;
+ }
+ if ([lower containsString:@"60fps"] || [lower containsString:@"60 fps"]) return 60;
+ if ([lower containsString:@"30fps"] || [lower containsString:@"30 fps"]) return 30;
+ return 0;
+}
+
+static NSInteger YouModNormalizedFPS(NSInteger fps) {
+ if (fps >= 50 && fps <= 61) return 60;
+ if (fps >= 24 && fps <= 31) return 30;
+ return fps;
+}
+
+static NSInteger YouModDisplayHeightForVideoHeight(NSInteger height) {
+ if (height >= 900 && height < 1080) return 1080;
+ return height;
+}
+
+static NSString *YouModQualityLabel(NSInteger height, NSInteger fps, NSString *fallback) {
+ height = YouModDisplayHeightForVideoHeight(height);
+ fps = YouModNormalizedFPS(fps);
+ if (height > 0 && fps > 0) return [NSString stringWithFormat:@"%ldp%ld", (long)height, (long)fps];
+ if (height > 0) return [NSString stringWithFormat:@"%ldp", (long)height];
+ if (fallback.length && fps > 0 && ![fallback.lowercaseString containsString:@"fps"])
+ return [NSString stringWithFormat:@"%@ %ldfps", fallback, (long)fps];
+ return fallback;
+}
+
+static NSArray *YouModFormatsForPlayer(YTPlayerViewController *player, BOOL video) {
+ NSMutableArray *formats = [NSMutableArray array];
+ for (id stream in YouModAdaptiveFormatObjectsForPlayer(player)) {
+ YouModMediaFormat *format = YouModMediaFormatFromStream(stream, video);
+ if (format) [formats addObject:format];
+ }
+
+ [formats sortUsingComparator:^NSComparisonResult(YouModMediaFormat *left, YouModMediaFormat *right) {
+ if (video) {
+ NSInteger leftRes = YouModResolutionFromQuality(left.qualityLabel);
+ NSInteger rightRes = YouModResolutionFromQuality(right.qualityLabel);
+ if (leftRes != rightRes) return leftRes > rightRes ? NSOrderedAscending : NSOrderedDescending;
+ NSInteger leftFPS = left.fps ?: YouModFPSFromQuality(left.qualityLabel);
+ NSInteger rightFPS = right.fps ?: YouModFPSFromQuality(right.qualityLabel);
+ if (leftFPS != rightFPS) return leftFPS > rightFPS ? NSOrderedAscending : NSOrderedDescending;
+ }
+
+ BOOL leftMP4 = YouModFormatLooksMP4Family(left);
+ BOOL rightMP4 = YouModFormatLooksMP4Family(right);
+ if (leftMP4 != rightMP4) return leftMP4 ? NSOrderedAscending : NSOrderedDescending;
+
+ if (!video && IS_ENABLED(DownloadPreferDRCAudio) && left.drcAudio != right.drcAudio)
+ return left.drcAudio ? NSOrderedAscending : NSOrderedDescending;
+ if (left.contentLength != right.contentLength)
+ return left.contentLength > right.contentLength ? NSOrderedAscending : NSOrderedDescending;
+ return NSOrderedSame;
+ }];
+
+ NSMutableArray *unique = [NSMutableArray array];
+ NSMutableSet *seen = [NSMutableSet set];
+ for (YouModMediaFormat *format in formats) {
+ NSInteger fps = format.fps ?: YouModFPSFromQuality(format.qualityLabel);
+ NSString *key = video
+ ? [NSString stringWithFormat:@"%@-%ld-%@", format.qualityLabel ?: @"", (long)fps, YouModMimeDetail(format.mimeType)]
+ : [NSString stringWithFormat:@"%@-%@-%@-%@", format.qualityLabel ?: @"", format.languageCode ?: @"", format.drcAudio ? @"drc" : @"std", YouModMimeDetail(format.mimeType)];
+ if ([seen containsObject:key]) continue;
+ [seen addObject:key];
+ [unique addObject:format];
+ }
+ return unique.copy;
+}
+
+static YouModMediaFormat *YouModBestAudioFormatForPlayer(YTPlayerViewController *player) {
+ NSArray *audioFormats = YouModFormatsForPlayer(player, NO);
+ return audioFormats.firstObject;
+}
+
+static UIViewController *YouModPresenterForSender(UIView *sender, YTPlayerViewController *player) {
+ UIViewController *presenter = nil;
+ if ([sender respondsToSelector:@selector(_viewControllerForAncestor)])
+ presenter = [sender _viewControllerForAncestor];
+ if (!presenter) presenter = player;
+ return YouModTopViewController(presenter);
+}
+
+static YTPlayerViewController *YouModPlayerFromViewController(UIViewController *vc) {
+ Class playerClass = NSClassFromString(@"YTPlayerViewController");
+ UIViewController *cursor = vc;
+ while (cursor) {
+ if (playerClass && [cursor isKindOfClass:playerClass]) return (YTPlayerViewController *)cursor;
+ id player = YouModObjectFromSelector(cursor, @selector(playerViewController));
+ if (playerClass && [player isKindOfClass:playerClass]) return (YTPlayerViewController *)player;
+ cursor = cursor.parentViewController;
+ }
+ return YouModCurrentPlayerViewController;
+}
+
+static NSURL *YouModThumbnailURLForVideoID(NSString *videoID) {
+ if (videoID.length == 0) return nil;
+ NSString *urlString = [NSString stringWithFormat:@"https://i.ytimg.com/vi/%@/maxresdefault.jpg", videoID];
+ return [NSURL URLWithString:urlString];
+}
+
+static void YouModRequestPhotoAccess(void (^completion)(BOOL granted)) {
+ if (@available(iOS 14.0, *)) {
+ [PHPhotoLibrary requestAuthorizationForAccessLevel:PHAccessLevelAddOnly handler:^(PHAuthorizationStatus status) {
+ completion(status == PHAuthorizationStatusAuthorized || status == PHAuthorizationStatusLimited);
+ }];
+ } else {
+ [PHPhotoLibrary requestAuthorization:^(PHAuthorizationStatus status) {
+ completion(status == PHAuthorizationStatusAuthorized);
+ }];
+ }
+}
+
+static void YouModSaveVideoToPhotos(NSURL *fileURL, UIViewController *presenter, void (^completion)(BOOL success, NSError *error)) {
+ YouModRequestPhotoAccess(^(BOOL granted) {
+ if (!granted) {
+ NSError *error = [NSError errorWithDomain:@"YouMod" code:1 userInfo:@{NSLocalizedDescriptionKey: @"Photos access denied"}];
+ completion(NO, error);
+ return;
+ }
+ [[PHPhotoLibrary sharedPhotoLibrary] performChanges:^{
+ [PHAssetCreationRequest creationRequestForAssetFromVideoAtFileURL:fileURL];
+ } completionHandler:^(BOOL success, NSError *error) {
+ dispatch_async(dispatch_get_main_queue(), ^{
+ completion(success, error);
+ });
+ }];
+ });
+}
+
+static void YouModShareFile(NSURL *fileURL, UIViewController *presenter) {
+ if (!fileURL || !presenter) return;
+ UIActivityViewController *activity = [[UIActivityViewController alloc] initWithActivityItems:@[fileURL] applicationActivities:nil];
+ // Fix for iPad and specific presentation alignment
+ if (UIDevice.currentDevice.userInterfaceIdiom == UIUserInterfaceIdiomPad) {
+ activity.popoverPresentationController.sourceView = presenter.view;
+ // Position at the bottom center of the screen
+ activity.popoverPresentationController.sourceRect = CGRectMake(presenter.view.bounds.size.width / 2, presenter.view.bounds.size.height, 0, 0);
+ activity.popoverPresentationController.permittedArrowDirections = 0; // No arrow pointing to a button
+ } else {
+ // On iPhone, UIActivityViewController naturally comes from the bottom center
+ activity.popoverPresentationController.sourceView = presenter.view;
+ }
+ [presenter presentViewController:activity animated:YES completion:nil];
+}
+
+static void YouModPresentMenu(NSString *title, NSArray *items, UIViewController *presenter, UIView *sender) {
+ presenter = YouModTopViewController(presenter);
+ Class sheetClass = NSClassFromString(@"YTDefaultSheetController");
+ if (sheetClass && [sheetClass respondsToSelector:@selector(sheetControllerWithParentResponder:)]) {
+ YTDefaultSheetController *sheet = [sheetClass sheetControllerWithParentResponder:presenter];
+ Class actionClass = NSClassFromString(@"YTActionSheetAction");
+ for (YouModMenuItem *item in items) {
+ id action = nil;
+ if ([actionClass respondsToSelector:@selector(actionWithTitle:subtitle:iconImage:handler:)]) {
+ action = ((id (*)(Class, SEL, NSString *, NSString *, UIImage *, id))objc_msgSend)(actionClass, @selector(actionWithTitle:subtitle:iconImage:handler:), item.title, item.subtitle, item.iconImage, ^(__unused id action) {
+ if (item.handler) item.handler();
+ });
+ } else {
+ action = ((id (*)(Class, SEL, NSString *, NSInteger, id))objc_msgSend)(actionClass, @selector(actionWithTitle:style:handler:), item.title, 0, ^(__unused id action) {
+ if (item.handler) item.handler();
+ });
+ }
+ if (action) [sheet addAction:action];
+ }
+ if (sender && [sheet respondsToSelector:@selector(presentFromView:animated:completion:)])
+ [sheet presentFromView:sender animated:YES completion:nil];
+ else
+ [sheet presentFromViewController:presenter animated:YES completion:nil];
+ return;
+ }
+
+ UIAlertController *alert = [UIAlertController alertControllerWithTitle:title message:nil preferredStyle:UIAlertControllerStyleActionSheet];
+ for (YouModMenuItem *item in items) {
+ NSString *rowTitle = item.subtitle.length ? [NSString stringWithFormat:@"%@\n%@", item.title, item.subtitle] : item.title;
+ [alert addAction:[UIAlertAction actionWithTitle:rowTitle style:UIAlertActionStyleDefault handler:^(__unused UIAlertAction *action) {
+ if (item.handler) item.handler();
+ }]];
+ }
+ [alert addAction:[UIAlertAction actionWithTitle:@"Cancel" style:UIAlertActionStyleCancel handler:nil]];
+ alert.popoverPresentationController.sourceView = sender ?: presenter.view;
+ [presenter presentViewController:alert animated:YES completion:nil];
+}
+
+@implementation YouModDownloadCoordinator
+
++ (instancetype)sharedCoordinator {
+ static YouModDownloadCoordinator *coordinator;
+ static dispatch_once_t onceToken;
+ dispatch_once(&onceToken, ^{
+ coordinator = [YouModDownloadCoordinator new];
+ });
+ return coordinator;
+}
+
+- (instancetype)init {
+ self = [super init];
+ if (self) {
+ NSURLSessionConfiguration *configuration = [NSURLSessionConfiguration defaultSessionConfiguration];
+ configuration.HTTPAdditionalHeaders = @{
+ @"User-Agent": @"Mozilla/5.0",
+ @"Origin": @"https://www.youtube.com",
+ @"Referer": @"https://www.youtube.com/",
+ };
+ configuration.HTTPMaximumConnectionsPerHost = YouModFastDownloadConcurrency;
+ configuration.requestCachePolicy = NSURLRequestReloadIgnoringLocalCacheData;
+ configuration.timeoutIntervalForResource = 300;
+ _session = [NSURLSession sessionWithConfiguration:configuration delegate:self delegateQueue:[NSOperationQueue mainQueue]];
+ }
+ return self;
+}
+
+- (void)showProgressWithTitle:(NSString *)title presenter:(UIViewController *)presenter {
+ self.presenter = presenter;
+ self.baseProgressTitle = title;
+ self.downloadStartTime = [NSDate timeIntervalSinceReferenceDate];
+ self.progressAlert = [UIAlertController alertControllerWithTitle:[NSString stringWithFormat:@"%@ - 0%%", title] message:@"\n" preferredStyle:UIAlertControllerStyleAlert];
+ self.progressView = [[UIProgressView alloc] initWithProgressViewStyle:UIProgressViewStyleDefault];
+ self.progressView.progress = 0.0;
+ self.progressView.translatesAutoresizingMaskIntoConstraints = NO;
+ [self.progressAlert.view addSubview:self.progressView];
+ [NSLayoutConstraint activateConstraints:@[
+ [self.progressView.leadingAnchor constraintEqualToAnchor:self.progressAlert.view.leadingAnchor constant:24.0],
+ [self.progressView.trailingAnchor constraintEqualToAnchor:self.progressAlert.view.trailingAnchor constant:-24.0],
+ [self.progressView.bottomAnchor constraintEqualToAnchor:self.progressAlert.view.bottomAnchor constant:-56.0],
+ ]];
+ __weak typeof(self) weakSelf = self;
+ [self.progressAlert addAction:[UIAlertAction actionWithTitle:@"Cancel" style:UIAlertActionStyleCancel handler:^(__unused UIAlertAction *action) {
+ [weakSelf cancelWithMessage:@"Download cancelled"];
+ }]];
+ [presenter presentViewController:self.progressAlert animated:YES completion:nil];
+}
+
+- (void)updateProgressTitle:(NSString *)title progress:(float)progress {
+ self.progressAlert.title = [NSString stringWithFormat:@"%@ - %ld%%", title, (long)lrintf(progress * 100.0f)];
+ self.progressAlert.message = @"\n";
+ [self.progressView setProgress:progress animated:YES];
+}
+
+- (void)cancelWithMessage:(NSString *)message {
+ [self.task cancel];
+ [self.metadataTask cancel];
+ [self.rangeDownloader cancel];
+ YouModCancelFFmpegKit();
+ self.task = nil;
+ self.metadataTask = nil;
+ self.rangeDownloader = nil;
+ self.fileCompletion = nil;
+ self.active = NO;
+ self.cancelled = YES;
+ [self cleanupTemporaryFiles];
+ if (message.length) YouModSendToast(message, self.presenter);
+}
+
+- (void)cleanupTemporaryFiles {
+ if (self.videoTempURL) [NSFileManager.defaultManager removeItemAtURL:self.videoTempURL error:nil];
+ if (self.audioTempURL) [NSFileManager.defaultManager removeItemAtURL:self.audioTempURL error:nil];
+ self.videoTempURL = nil;
+ self.audioTempURL = nil;
+}
+
+- (void)downloadURL:(NSURL *)url toURL:(NSURL *)destinationURL expectedBytes:(unsigned long long)expectedBytes headers:(NSDictionary *)headers completion:(YouModFileDownloadCompletion)completion {
+ self.currentResolvedSizeAddedToTotal = NO;
+ self.currentExpectedBytes = expectedBytes;
+ self.currentBytes = 0;
+ if (expectedBytes == 0) {
+ __weak typeof(self) weakSelf = self;
+ [self resolveExpectedBytesForURL:url headers:headers completion:^(unsigned long long bytes) {
+ __strong typeof(weakSelf) self = weakSelf;
+ if (!self || self.cancelled) return;
+ if (bytes > 0) [self adjustCurrentExpectedBytesIfNeeded:bytes];
+ [self beginDownloadURL:url toURL:destinationURL expectedBytes:bytes headers:headers allowFast:YES completion:completion];
+ }];
+ return;
+ }
+ [self beginDownloadURL:url toURL:destinationURL expectedBytes:expectedBytes headers:headers allowFast:YES completion:completion];
+}
+
+- (void)beginDownloadURL:(NSURL *)url toURL:(NSURL *)destinationURL expectedBytes:(unsigned long long)expectedBytes headers:(NSDictionary *)headers allowFast:(BOOL)allowFast completion:(YouModFileDownloadCompletion)completion {
+ self.destinationURL = destinationURL;
+ self.currentExpectedBytes = expectedBytes;
+ self.currentBytes = 0;
+ self.finishedCurrentFile = NO;
+ self.fileCompletion = completion;
+ [NSFileManager.defaultManager removeItemAtURL:destinationURL error:nil];
+
+ if (allowFast && expectedBytes == 0) allowFast = NO;
+
+ if (allowFast && expectedBytes >= YouModFastDownloadMinimumBytes) {
+ __weak typeof(self) weakSelf = self;
+ self.rangeDownloader = [[YouModRangeDownloader alloc] initWithURL:url destinationURL:destinationURL expectedBytes:expectedBytes headers:headers progress:^(unsigned long long completedBytes) {
+ __strong typeof(weakSelf) self = weakSelf;
+ if (!self || self.cancelled) return;
+ self.currentBytes = completedBytes;
+ [self updateDownloadProgressWithCurrentBytes:completedBytes expectedBytes:expectedBytes];
+ } completion:^(NSURL *fileURL, NSError *error) {
+ __strong typeof(weakSelf) self = weakSelf;
+ if (!self || self.cancelled) return;
+ self.rangeDownloader = nil;
+ if (error) {
+ [self beginDownloadURL:url toURL:destinationURL expectedBytes:expectedBytes headers:headers allowFast:NO completion:completion];
+ return;
+ }
+ if (completion) completion(fileURL, nil);
+ }];
+ [self.rangeDownloader start];
+ return;
+ }
+
+ NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url cachePolicy:NSURLRequestReloadIgnoringLocalCacheData timeoutInterval:60.0];
+ YouModApplyDownloadHeaders(request, headers);
+ self.task = [self.session downloadTaskWithRequest:request];
+ [self.task resume];
+}
+
+- (void)resolveExpectedBytesForURL:(NSURL *)url headers:(NSDictionary *)headers completion:(void (^)(unsigned long long bytes))completion {
+ NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url cachePolicy:NSURLRequestReloadIgnoringLocalCacheData timeoutInterval:15.0];
+ request.HTTPMethod = @"HEAD";
+ YouModApplyDownloadHeaders(request, headers);
+
+ __weak typeof(self) weakSelf = self;
+ self.metadataTask = [NSURLSession.sharedSession dataTaskWithRequest:request completionHandler:^(__unused NSData *data, NSURLResponse *response, __unused NSError *error) {
+ __strong typeof(weakSelf) self = weakSelf;
+ if (!self) return;
+
+ unsigned long long bytes = 0;
+ if (response.expectedContentLength > 0) {
+ bytes = (unsigned long long)response.expectedContentLength;
+ } else if ([response isKindOfClass:NSHTTPURLResponse.class]) {
+ id header = ((NSHTTPURLResponse *)response).allHeaderFields[@"Content-Length"];
+ if ([header respondsToSelector:@selector(unsignedLongLongValue)])
+ bytes = [header unsignedLongLongValue];
+ }
+
+ dispatch_async(dispatch_get_main_queue(), ^{
+ self.metadataTask = nil;
+ completion(bytes);
+ });
+ }];
+ [self.metadataTask resume];
+}
+
+- (void)updateDownloadProgressWithCurrentBytes:(unsigned long long)currentBytes expectedBytes:(unsigned long long)expectedBytes {
+ unsigned long long total = self.totalBytes ?: expectedBytes;
+ float progress = total ? (float)(self.completedBytes + currentBytes) / (float)total : 0.0f;
+ progress = fminf(fmaxf(progress, 0.0f), 0.985f);
+
+ NSTimeInterval now = [NSDate timeIntervalSinceReferenceDate];
+ NSTimeInterval elapsed = now - self.downloadStartTime;
+ double speedMBps = 0;
+ if (elapsed > 0) {
+ speedMBps = ((double)(self.completedBytes + currentBytes) / 1048576.0) / elapsed;
+ }
+ double totalMB = (double)total / 1048576.0;
+
+ self.progressAlert.title = [NSString stringWithFormat:@"%@ - %ld%%", self.baseProgressTitle ?: @"Downloading", (long)lrintf(progress * 100.0f)];
+ if (total > 0) {
+ self.progressAlert.message = [NSString stringWithFormat:@"%.1f MB/s - %.1f MB\n", speedMBps, totalMB];
+ } else {
+ self.progressAlert.message = [NSString stringWithFormat:@"%.1f MB/s\n", speedMBps];
+ }
+ [self.progressView setProgress:progress animated:YES];
+}
+
+- (void)adjustCurrentExpectedBytesIfNeeded:(unsigned long long)newExpectedBytes {
+ unsigned long long oldExpectedBytes = self.currentExpectedBytes;
+ if (newExpectedBytes <= oldExpectedBytes) return;
+
+ self.currentExpectedBytes = newExpectedBytes;
+ if (oldExpectedBytes > 0) {
+ self.totalBytes += newExpectedBytes - oldExpectedBytes;
+ } else if (!self.currentResolvedSizeAddedToTotal) {
+ self.totalBytes += newExpectedBytes;
+ self.currentResolvedSizeAddedToTotal = YES;
+ }
+}
+
+- (void)startVideoDownloadWithVideoFormat:(YouModMediaFormat *)videoFormat audioFormat:(YouModMediaFormat *)audioFormat fileName:(NSString *)fileName videoID:(NSString *)videoID presenter:(UIViewController *)presenter {
+ if (self.active) {
+ YouModSendToast(@"Already downloading", presenter);
+ return;
+ }
+ [self startDirectVideoDownloadWithVideoFormat:videoFormat audioFormat:audioFormat fileName:fileName videoID:videoID presenter:presenter];
+}
+
+- (void)startDirectVideoDownloadWithVideoFormat:(YouModMediaFormat *)videoFormat audioFormat:(YouModMediaFormat *)audioFormat fileName:(NSString *)fileName videoID:(NSString *)videoID presenter:(UIViewController *)presenter {
+ NSURL *videoURL = [NSURL URLWithString:videoFormat.urlString];
+ NSURL *audioURL = [NSURL URLWithString:audioFormat.urlString];
+ if (!videoURL || !audioURL) {
+ YouModSendToast(@"No stream URL found", presenter);
+ return;
+ }
+
+ self.active = YES;
+ self.cancelled = NO;
+ self.completedBytes = 0;
+ self.totalBytes = videoFormat.contentLength + audioFormat.contentLength;
+ self.videoTempURL = YouModTemporaryFileURL(YouModFileExtensionForFormat(videoFormat, @"mp4"));
+ self.audioTempURL = YouModTemporaryFileURL(YouModFileExtensionForFormat(audioFormat, @"m4a"));
+ NSString *outputExtension = YouModMergedVideoOutputExtension(videoFormat, audioFormat);
+ [self showProgressWithTitle:@"Downloading video" presenter:presenter];
+
+ __weak typeof(self) weakSelf = self;
+ [self downloadURL:videoURL toURL:self.videoTempURL expectedBytes:videoFormat.contentLength headers:videoFormat.httpHeaders completion:^(NSURL *videoFileURL, NSError *videoError) {
+ __strong typeof(weakSelf) self = weakSelf;
+ if (!self || videoError) {
+ [self failWithError:videoError ?: [NSError errorWithDomain:@"YouMod" code:2 userInfo:@{NSLocalizedDescriptionKey: @"Video download failed"}]];
+ return;
+ }
+
+ self.completedBytes += MAX(videoFormat.contentLength, self.currentBytes);
+ [self updateProgressTitle:@"Downloading audio" progress:(self.totalBytes ? (float)self.completedBytes / (float)self.totalBytes : 0.5f)];
+ [self downloadURL:audioURL toURL:self.audioTempURL expectedBytes:audioFormat.contentLength headers:audioFormat.httpHeaders completion:^(NSURL *audioFileURL, NSError *audioError) {
+ __strong typeof(weakSelf) self = weakSelf;
+ if (!self || audioError) {
+ [self failWithError:audioError ?: [NSError errorWithDomain:@"YouMod" code:3 userInfo:@{NSLocalizedDescriptionKey: @"Audio download failed"}]];
+ return;
+ }
+ unsigned long long durationMs = videoFormat.durationMs ?: audioFormat.durationMs;
+ [self mergeVideoURL:videoFileURL audioURL:audioFileURL fileName:fileName outputExtension:outputExtension durationMs:durationMs presenter:presenter];
+ }];
+ }];
+}
+
+- (void)startDirectSingleVideoDownloadWithFormat:(YouModMediaFormat *)format fileName:(NSString *)fileName videoID:(NSString *)videoID presenter:(UIViewController *)presenter {
+ NSURL *videoURL = [NSURL URLWithString:format.urlString];
+ if (!videoURL) {
+ YouModSendToast(@"No stream URL found", presenter);
+ return;
+ }
+
+ self.active = YES;
+ self.cancelled = NO;
+ self.completedBytes = 0;
+ self.totalBytes = format.contentLength;
+ NSString *extension = YouModFileExtensionForFormat(format, @"mp4");
+ BOOL canFinalizeWithAVFoundation = format.durationMs > 0 && YouModPathExtensionIsPhotosVideo(extension);
+ NSURL *finalURL = YouModUniqueFileURL(fileName, extension);
+ NSURL *downloadURL = canFinalizeWithAVFoundation ? YouModTemporaryFileURL(extension) : finalURL;
+ self.videoTempURL = canFinalizeWithAVFoundation ? downloadURL : nil;
+ [self showProgressWithTitle:@"Downloading video" presenter:presenter];
+
+ __weak typeof(self) weakSelf = self;
+ [self downloadURL:videoURL toURL:downloadURL expectedBytes:format.contentLength headers:format.httpHeaders completion:^(NSURL *fileURL, NSError *error) {
+ __strong typeof(weakSelf) self = weakSelf;
+ if (!self || error) {
+ [self failWithError:error ?: [NSError errorWithDomain:@"YouMod" code:8 userInfo:@{NSLocalizedDescriptionKey: @"Video download failed"}]];
+ return;
+ }
+ if (canFinalizeWithAVFoundation) {
+ [self trimSingleVideoURL:fileURL outputURL:finalURL durationMs:format.durationMs presenter:presenter];
+ return;
+ }
+ [self completeWithFileURL:fileURL isVideo:YES presenter:presenter];
+ }];
+}
+
+- (void)startAudioDownloadWithAudioFormat:(YouModMediaFormat *)audioFormat fileName:(NSString *)fileName videoID:(NSString *)videoID presenter:(UIViewController *)presenter {
+ [self startAudioDownloadWithAudioFormat:audioFormat fileName:fileName videoID:videoID outputFormat:nil presenter:presenter];
+}
+
+- (void)startAudioDownloadWithAudioFormat:(YouModMediaFormat *)audioFormat fileName:(NSString *)fileName videoID:(NSString *)videoID outputFormat:(YouModAudioOutputFormat *)outputFormat presenter:(UIViewController *)presenter {
+ if (self.active) {
+ YouModSendToast(@"Already downloading", presenter);
+ return;
+ }
+ [self startDirectAudioDownloadWithAudioFormat:audioFormat fileName:fileName videoID:videoID outputFormat:outputFormat presenter:presenter];
+}
+
+- (void)startDirectAudioDownloadWithAudioFormat:(YouModMediaFormat *)audioFormat fileName:(NSString *)fileName videoID:(NSString *)videoID presenter:(UIViewController *)presenter {
+ [self startDirectAudioDownloadWithAudioFormat:audioFormat fileName:fileName videoID:videoID outputFormat:nil presenter:presenter];
+}
+
+- (void)startDirectAudioDownloadWithAudioFormat:(YouModMediaFormat *)audioFormat fileName:(NSString *)fileName videoID:(NSString *)videoID outputFormat:(YouModAudioOutputFormat *)outputFormat presenter:(UIViewController *)presenter {
+ NSURL *audioURL = [NSURL URLWithString:audioFormat.urlString];
+ if (!audioURL) {
+ YouModSendToast(@"No audio URL found", presenter);
+ return;
+ }
+ outputFormat = outputFormat ?: YouModDefaultAudioOutputFormat();
+ if (!outputFormat.supported) {
+ YouModSendToast([NSString stringWithFormat:@"%@ not supported", outputFormat.title ?: @"Format"], presenter);
+ return;
+ }
+
+ self.active = YES;
+ self.cancelled = NO;
+ self.completedBytes = 0;
+ self.totalBytes = audioFormat.contentLength;
+ BOOL passthrough = YouModAudioOutputFormatCanPassthrough(outputFormat, audioFormat);
+ if (!passthrough && !YouModFFmpegKitAvailable()) {
+ self.active = NO;
+ NSString *details = YouModFFmpegKitDiagnosticText(outputFormat, audioFormat, videoID);
+ YouModRecordDownloadDiagnostic(@"FFmpegKit unavailable for audio conversion", details);
+ NSString *diagnostic = YouModDownloadDiagnosticText();
+ if (diagnostic.length) {
+ UIPasteboard.generalPasteboard.string = diagnostic;
+ YouModSendToast(@"FFmpegKit not loaded, diagnostics copied", presenter);
+ } else {
+ YouModSendToast([NSString stringWithFormat:@"FFmpegKit required for %@", outputFormat.title ?: @"this format"], presenter);
+ }
+ return;
+ }
+
+ NSURL *finalURL = YouModUniqueFileURL(fileName, YouModAudioOutputFileExtension(outputFormat, audioFormat, passthrough));
+ NSURL *downloadURL = passthrough ? finalURL : YouModTemporaryFileURL(YouModFileExtensionForFormat(audioFormat, @"m4a"));
+ self.audioTempURL = passthrough ? nil : downloadURL;
+ [self showProgressWithTitle:@"Downloading audio" presenter:presenter];
+
+ __weak typeof(self) weakSelf = self;
+ [self downloadURL:audioURL toURL:downloadURL expectedBytes:audioFormat.contentLength headers:audioFormat.httpHeaders completion:^(NSURL *fileURL, NSError *error) {
+ __strong typeof(weakSelf) self = weakSelf;
+ if (!self || error) {
+ [self failWithError:error ?: [NSError errorWithDomain:@"YouMod" code:4 userInfo:@{NSLocalizedDescriptionKey: @"Audio download failed"}]];
+ return;
+ }
+ if (!passthrough) {
+ unsigned long long durationMs = audioFormat.durationMs ?: YouModDurationMsForURL(fileURL);
+ [self convertAudioURL:fileURL outputURL:finalURL outputFormat:outputFormat durationMs:durationMs presenter:presenter];
+ return;
+ }
+ [self completeWithFileURL:fileURL isVideo:NO presenter:presenter];
+ }];
+}
+
+- (void)convertAudioURL:(NSURL *)inputURL outputURL:(NSURL *)outputURL outputFormat:(YouModAudioOutputFormat *)outputFormat durationMs:(unsigned long long)durationMs presenter:(UIViewController *)presenter {
+ [self updateProgressTitle:[NSString stringWithFormat:@"Converting to %@", outputFormat.title ?: @"audio"] progress:0.985f];
+ [NSFileManager.defaultManager removeItemAtURL:outputURL error:nil];
+
+ __weak typeof(self) weakSelf = self;
+ BOOL started = YouModStartFFmpegKitAudioConvert(inputURL, outputURL, outputFormat, durationMs, ^(float progress) {
+ __strong typeof(weakSelf) self = weakSelf;
+ if (!self || self.cancelled) return;
+ [self updateProgressTitle:[NSString stringWithFormat:@"Converting to %@", outputFormat.title ?: @"audio"] progress:progress];
+ }, ^(BOOL success, NSError *error) {
+ __strong typeof(weakSelf) self = weakSelf;
+ if (!self || self.cancelled) return;
+ if (success) {
+ [self completeWithFileURL:outputURL isVideo:NO presenter:presenter];
+ return;
+ }
+ [self failWithError:error ?: [NSError errorWithDomain:@"YouMod" code:14 userInfo:@{NSLocalizedDescriptionKey: @"Conversion failed"}]];
+ });
+
+ if (!started) {
+ [self failWithError:[NSError errorWithDomain:@"YouMod" code:15 userInfo:@{NSLocalizedDescriptionKey: @"Format unavailable"}]];
+ }
+}
+
+- (void)mergeVideoURL:(NSURL *)videoURL audioURL:(NSURL *)audioURL fileName:(NSString *)fileName outputExtension:(NSString *)outputExtension durationMs:(unsigned long long)durationMs presenter:(UIViewController *)presenter {
+ [self updateProgressTitle:@"Merging video" progress:0.985f];
+ NSURL *outputURL = YouModUniqueFileURL(fileName, outputExtension.length ? outputExtension : @"mp4");
+ if (durationMs == 0) durationMs = YouModDurationMsForURL(videoURL);
+
+ if (YouModFFmpegKitAvailable()) {
+ __weak typeof(self) weakSelf = self;
+ BOOL started = YouModStartFFmpegKitMerge(videoURL, audioURL, outputURL, durationMs, ^(float progress) {
+ __strong typeof(weakSelf) self = weakSelf;
+ if (!self || self.cancelled) return;
+ [self updateProgressTitle:@"Merging video" progress:progress];
+ }, ^(BOOL success, NSError *error) {
+ __strong typeof(weakSelf) self = weakSelf;
+ if (!self || self.cancelled) return;
+ if (success) {
+ [self completeWithFileURL:outputURL isVideo:YES presenter:presenter];
+ return;
+ }
+
+ [NSFileManager.defaultManager removeItemAtURL:outputURL error:nil];
+ if (YouModVideoFileCanUseAVFoundation(outputURL)) {
+ [self mergeVideoWithAVFoundationVideoURL:videoURL audioURL:audioURL outputURL:outputURL durationMs:durationMs presenter:presenter fallbackError:error];
+ } else {
+ [self failWithError:error ?: [NSError errorWithDomain:@"YouMod" code:16 userInfo:@{NSLocalizedDescriptionKey: @"FFmpegKit required for this stream"}]];
+ }
+ });
+ if (started) return;
+ }
+
+ if (YouModVideoFileCanUseAVFoundation(outputURL)) {
+ [self mergeVideoWithAVFoundationVideoURL:videoURL audioURL:audioURL outputURL:outputURL durationMs:durationMs presenter:presenter fallbackError:nil];
+ } else {
+ [self failWithError:[NSError errorWithDomain:@"YouMod" code:16 userInfo:@{NSLocalizedDescriptionKey: @"FFmpegKit required for this stream"}]];
+ }
+}
+
+- (void)mergeVideoWithAVFoundationVideoURL:(NSURL *)videoURL audioURL:(NSURL *)audioURL outputURL:(NSURL *)outputURL durationMs:(unsigned long long)durationMs presenter:(UIViewController *)presenter fallbackError:(NSError *)fallbackError {
+ [self updateProgressTitle:fallbackError ? @"Merging video with fallback" : @"Merging video" progress:0.985f];
+ AVURLAsset *videoAsset = [AVURLAsset URLAssetWithURL:videoURL options:nil];
+ AVURLAsset *audioAsset = [AVURLAsset URLAssetWithURL:audioURL options:nil];
+ AVMutableComposition *composition = [AVMutableComposition composition];
+
+ AVAssetTrack *videoTrack = [[videoAsset tracksWithMediaType:AVMediaTypeVideo] firstObject];
+ AVAssetTrack *audioTrack = [[audioAsset tracksWithMediaType:AVMediaTypeAudio] firstObject];
+ if (!videoTrack || !audioTrack) {
+ [self failWithError:fallbackError ?: [NSError errorWithDomain:@"YouMod" code:5 userInfo:@{NSLocalizedDescriptionKey: @"Merge failed"}]];
+ return;
+ }
+
+ CMTime duration = YouModExportDuration(videoAsset, audioAsset, durationMs);
+ if (!YouModCMTimeIsUsable(duration)) {
+ [self failWithError:fallbackError ?: [NSError errorWithDomain:@"YouMod" code:9 userInfo:@{NSLocalizedDescriptionKey: @"Cannot determine duration"}]];
+ return;
+ }
+ NSError *insertError = nil;
+ AVMutableCompositionTrack *compositionVideo = [composition addMutableTrackWithMediaType:AVMediaTypeVideo preferredTrackID:kCMPersistentTrackID_Invalid];
+ [compositionVideo insertTimeRange:CMTimeRangeMake(kCMTimeZero, duration) ofTrack:videoTrack atTime:kCMTimeZero error:&insertError];
+ compositionVideo.preferredTransform = videoTrack.preferredTransform;
+ if (insertError) {
+ [self failWithError:insertError];
+ return;
+ }
+
+ AVMutableCompositionTrack *compositionAudio = [composition addMutableTrackWithMediaType:AVMediaTypeAudio preferredTrackID:kCMPersistentTrackID_Invalid];
+ [compositionAudio insertTimeRange:CMTimeRangeMake(kCMTimeZero, duration) ofTrack:audioTrack atTime:kCMTimeZero error:&insertError];
+ if (insertError) {
+ [self failWithError:insertError];
+ return;
+ }
+
+ AVAssetExportSession *exporter = [[AVAssetExportSession alloc] initWithAsset:composition presetName:AVAssetExportPresetPassthrough];
+ exporter.outputURL = outputURL;
+ exporter.outputFileType = AVFileTypeMPEG4;
+ exporter.shouldOptimizeForNetworkUse = YES;
+
+ __weak typeof(self) weakSelf = self;
+ [exporter exportAsynchronouslyWithCompletionHandler:^{
+ dispatch_async(dispatch_get_main_queue(), ^{
+ __strong typeof(weakSelf) self = weakSelf;
+ if (!self) return;
+ if (exporter.status == AVAssetExportSessionStatusCompleted) {
+ [self completeWithFileURL:outputURL isVideo:YES presenter:presenter];
+ } else {
+ [self failWithError:exporter.error ?: [NSError errorWithDomain:@"YouMod" code:6 userInfo:@{NSLocalizedDescriptionKey: @"Merge failed"}]];
+ }
+ });
+ }];
+}
+
+- (void)trimSingleVideoURL:(NSURL *)inputURL outputURL:(NSURL *)outputURL durationMs:(unsigned long long)durationMs presenter:(UIViewController *)presenter {
+ [self updateProgressTitle:@"Finalizing video" progress:0.99f];
+ [NSFileManager.defaultManager removeItemAtURL:outputURL error:nil];
+
+ AVURLAsset *asset = [AVURLAsset URLAssetWithURL:inputURL options:nil];
+ AVAssetTrack *videoTrack = [[asset tracksWithMediaType:AVMediaTypeVideo] firstObject];
+ if (!videoTrack) {
+ [self failWithError:[NSError errorWithDomain:@"YouMod" code:10 userInfo:@{NSLocalizedDescriptionKey: @"Cannot finalize video"}]];
+ return;
+ }
+
+ CMTime duration = YouModExportDuration(asset, nil, durationMs);
+ if (!YouModCMTimeIsUsable(duration)) {
+ [self failWithError:[NSError errorWithDomain:@"YouMod" code:11 userInfo:@{NSLocalizedDescriptionKey: @"Cannot determine duration"}]];
+ return;
+ }
+
+ AVMutableComposition *composition = [AVMutableComposition composition];
+ NSError *insertError = nil;
+ AVMutableCompositionTrack *compositionVideo = [composition addMutableTrackWithMediaType:AVMediaTypeVideo preferredTrackID:kCMPersistentTrackID_Invalid];
+ [compositionVideo insertTimeRange:CMTimeRangeMake(kCMTimeZero, duration) ofTrack:videoTrack atTime:kCMTimeZero error:&insertError];
+ compositionVideo.preferredTransform = videoTrack.preferredTransform;
+ if (insertError) {
+ [self failWithError:insertError];
+ return;
+ }
+
+ AVAssetTrack *audioTrack = [[asset tracksWithMediaType:AVMediaTypeAudio] firstObject];
+ if (audioTrack) {
+ CMTime audioDuration = YouModMinUsableDuration(duration, audioTrack.timeRange.duration);
+ AVMutableCompositionTrack *compositionAudio = [composition addMutableTrackWithMediaType:AVMediaTypeAudio preferredTrackID:kCMPersistentTrackID_Invalid];
+ [compositionAudio insertTimeRange:CMTimeRangeMake(kCMTimeZero, audioDuration) ofTrack:audioTrack atTime:kCMTimeZero error:&insertError];
+ if (insertError) {
+ [self failWithError:insertError];
+ return;
+ }
+ }
+
+ AVAssetExportSession *exporter = [[AVAssetExportSession alloc] initWithAsset:composition presetName:AVAssetExportPresetPassthrough];
+ exporter.outputURL = outputURL;
+ exporter.outputFileType = AVFileTypeMPEG4;
+ exporter.shouldOptimizeForNetworkUse = YES;
+
+ __weak typeof(self) weakSelf = self;
+ [exporter exportAsynchronouslyWithCompletionHandler:^{
+ dispatch_async(dispatch_get_main_queue(), ^{
+ __strong typeof(weakSelf) self = weakSelf;
+ if (!self) return;
+ if (exporter.status == AVAssetExportSessionStatusCompleted) {
+ [self completeWithFileURL:outputURL isVideo:YES presenter:presenter];
+ } else {
+ [self failWithError:exporter.error ?: [NSError errorWithDomain:@"YouMod" code:12 userInfo:@{NSLocalizedDescriptionKey: @"Finalize failed"}]];
+ }
+ });
+ }];
+}
+
+- (void)completeWithFileURL:(NSURL *)fileURL isVideo:(BOOL)isVideo presenter:(UIViewController *)presenter {
+ self.active = NO;
+ [self updateProgressTitle:@"Download completed" progress:1.0f];
+ [self.progressAlert dismissViewControllerAnimated:YES completion:nil];
+ self.progressAlert = nil;
+ self.progressView = nil;
+
+ BOOL canSaveToPhotos = isVideo && YouModVideoFileCanSaveToPhotos(fileURL);
+ if (isVideo && IS_ENABLED(DownloadSaveToPhotos) && canSaveToPhotos) {
+ [self cleanupTemporaryFiles];
+ YouModSaveVideoToPhotos(fileURL, presenter, ^(BOOL success, NSError *error) {
+ if (success) {
+ YouModSendToast(@"Saved to Photos", presenter);
+ } else {
+ YouModSendToast(error.localizedDescription ?: @"Cannot save to Photos", presenter);
+ YouModShareFile(fileURL, presenter);
+ }
+ });
+ } else {
+ [self cleanupTemporaryFiles];
+ YouModSendToast(isVideo ? @"Download completed" : @"Audio saved", presenter);
+ if (!isVideo || (isVideo && !canSaveToPhotos)) YouModShareFile(fileURL, presenter);
+ }
+}
+
+- (void)failWithError:(NSError *)error {
+ self.active = NO;
+ [self.progressAlert dismissViewControllerAnimated:YES completion:nil];
+ self.progressAlert = nil;
+ self.progressView = nil;
+ [self cleanupTemporaryFiles];
+ YouModSendToast(error.localizedDescription ?: @"Download failed", self.presenter);
+}
+
+- (void)URLSession:(NSURLSession *)session downloadTask:(NSURLSessionDownloadTask *)downloadTask didWriteData:(int64_t)bytesWritten totalBytesWritten:(int64_t)totalBytesWritten totalBytesExpectedToWrite:(int64_t)totalBytesExpectedToWrite {
+ self.currentBytes = (unsigned long long)MAX(totalBytesWritten, 0);
+ if (totalBytesExpectedToWrite > 0)
+ [self adjustCurrentExpectedBytesIfNeeded:(unsigned long long)totalBytesExpectedToWrite];
+ if (self.currentBytes > self.currentExpectedBytes)
+ [self adjustCurrentExpectedBytesIfNeeded:self.currentBytes];
+ [self updateDownloadProgressWithCurrentBytes:self.currentBytes expectedBytes:self.currentExpectedBytes];
+}
+
+- (void)URLSession:(NSURLSession *)session downloadTask:(NSURLSessionDownloadTask *)downloadTask didFinishDownloadingToURL:(NSURL *)location {
+ self.finishedCurrentFile = YES;
+ NSError *error = nil;
+ [NSFileManager.defaultManager removeItemAtURL:self.destinationURL error:nil];
+ [NSFileManager.defaultManager moveItemAtURL:location toURL:self.destinationURL error:&error];
+ if (self.fileCompletion) self.fileCompletion(error ? nil : self.destinationURL, error);
+}
+
+- (void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task didCompleteWithError:(NSError *)error {
+ if (error && !self.finishedCurrentFile && self.fileCompletion) {
+ self.fileCompletion(nil, error);
+ }
+}
+
+@end
+
+static void YouModDownloadThumbnail(NSString *videoID, UIViewController *presenter) {
+ NSURL *thumbnailURL = YouModThumbnailURLForVideoID(videoID);
+ if (!thumbnailURL) {
+ YouModSendToast(@"No thumbnail found", presenter);
+ return;
+ }
+
+ YouModSendToast(@"Downloading thumbnail", presenter);
+ [[NSURLSession.sharedSession dataTaskWithURL:thumbnailURL completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) {
+ UIImage *image = data ? [UIImage imageWithData:data] : nil;
+ dispatch_async(dispatch_get_main_queue(), ^{
+ if (!image || error) {
+ YouModSendToast(error.localizedDescription ?: @"Thumbnail failed", presenter);
+ return;
+ }
+ YouModRequestPhotoAccess(^(BOOL granted) {
+ if (!granted) {
+ YouModSendToast(@"Photos access denied", presenter);
+ return;
+ }
+ [[PHPhotoLibrary sharedPhotoLibrary] performChanges:^{
+ [PHAssetChangeRequest creationRequestForAssetFromImage:image];
+ } completionHandler:^(BOOL success, NSError *saveError) {
+ dispatch_async(dispatch_get_main_queue(), ^{
+ YouModSendToast(success ? @"Saved to Photos" : (saveError.localizedDescription ?: @"Save failed"), presenter);
+ });
+ }];
+ });
+ });
+ }] resume];
+}
+
+static void YouModCopyVideoInfo(YTPlayerViewController *player, UIViewController *presenter) {
+ NSString *videoID = YouModVideoIDForPlayer(player);
+ NSString *title = YouModTitleForPlayer(player);
+ NSString *url = videoID.length ? [NSString stringWithFormat:@"https://youtu.be/%@", videoID] : @"";
+ UIPasteboard.generalPasteboard.string = url.length ? [NSString stringWithFormat:@"%@\n%@", title, url] : title;
+ YouModSendToast(@"Copied video information", presenter);
+}
+
+static void YouModShowVideoQualitySheet(YTPlayerViewController *player, UIViewController *presenter, UIView *sender) {
+ NSArray *videoFormats = YouModFormatsForPlayer(player, YES);
+ YouModMediaFormat *audioFormat = YouModBestAudioFormatForPlayer(player);
+ NSString *title = YouModTitleForPlayer(player);
+ NSString *videoID = YouModVideoIDForPlayer(player);
+
+ if (videoFormats.count == 0 || !audioFormat) {
+ YouModSendToast(@"No video/audio streams found", presenter);
+ return;
+ }
+
+ NSMutableArray *items = [NSMutableArray array];
+ for (YouModMediaFormat *format in videoFormats) {
+ NSString *rowTitle = format.qualityLabel.length ? format.qualityLabel : @"Video";
+ NSString *subtitle = YouModFormatSubtitle(format);
+ [items addObject:[YouModMenuItem itemWithTitle:rowTitle subtitle:subtitle icon:YouModIconImage(658) handler:^{
+ [[YouModDownloadCoordinator sharedCoordinator] startVideoDownloadWithVideoFormat:format audioFormat:audioFormat fileName:title videoID:videoID presenter:presenter];
+ }]];
+ }
+ YouModPresentMenu(@"Download video", items, presenter, sender);
+}
+
+static void YouModShowAudioSourceSheet(YTPlayerViewController *player, YouModAudioOutputFormat *outputFormat, UIViewController *presenter, UIView *sender) {
+ NSArray *audioFormats = YouModFormatsForPlayer(player, NO);
+ NSString *title = YouModTitleForPlayer(player);
+ NSString *videoID = YouModVideoIDForPlayer(player);
+ NSMutableArray *items = [NSMutableArray array];
+
+ if (audioFormats.count == 0) {
+ if (items.count) {
+ YouModPresentMenu(@"Download audio", items, presenter, sender);
+ return;
+ }
+ YouModSendToast(@"No audio streams found", presenter);
+ return;
+ }
+
+ NSUInteger index = 1;
+ for (YouModMediaFormat *format in audioFormats) {
+ NSString *rowTitle = audioFormats.count == 1 ? @"Audio" : [NSString stringWithFormat:@"Audio %lu", (unsigned long)index++];
+ NSString *subtitle = YouModFormatSubtitle(format);
+ [items addObject:[YouModMenuItem itemWithTitle:rowTitle subtitle:subtitle icon:YouModIconImage(21) handler:^{
+ [[YouModDownloadCoordinator sharedCoordinator] startAudioDownloadWithAudioFormat:format fileName:title videoID:videoID outputFormat:outputFormat presenter:presenter];
+ }]];
+ }
+ NSString *menuTitle = outputFormat.title.length ? [NSString stringWithFormat:@"Download %@", outputFormat.title] : @"Download audio";
+ YouModPresentMenu(menuTitle, items, presenter, sender);
+}
+
+static void YouModShowAudioSheet(YTPlayerViewController *player, UIViewController *presenter, UIView *sender) {
+ NSMutableArray *items = [NSMutableArray array];
+ for (YouModAudioOutputFormat *format in YouModAudioOutputFormats()) {
+ [items addObject:[YouModMenuItem itemWithTitle:format.title subtitle:YouModAudioOutputSubtitle(format) icon:YouModIconImage(21) handler:^{
+ if (!format.supported) {
+ YouModSendToast(@"DSD export is not supported by bundled FFmpeg.", presenter);
+ return;
+ }
+ YouModShowAudioSourceSheet(player, format, presenter, sender);
+ }]];
+ }
+ YouModPresentMenu(@"Audio format", items, presenter, sender);
+}
+
+static void YouModShowCaptionsSheet(YTPlayerViewController *player, UIViewController *presenter, UIView *sender) {
+ NSArray *tracks = YouModCaptionTracksForPlayer(player);
+ if (tracks.count == 0) {
+ YouModSendToast(@"No captions available for this video.", presenter);
+ return;
+ }
+
+ NSMutableArray *items = [NSMutableArray array];
+ for (id track in tracks) {
+ NSString *baseURL = YouModStringFromSelector(track, @selector(baseURL));
+ if (baseURL.length == 0) continue;
+
+ NSString *languageCode = YouModStringFromSelector(track, @selector(languageCode));
+ NSString *vssId = YouModStringFromSelector(track, @selector(vssId));
+ NSString *nameStr = nil;
+ id nameObj = YouModObjectFromSelector(track, @selector(name));
+ nameStr = YouModStringFromSelector(nameObj, @selector(simpleText));
+ if (!nameStr.length) {
+ NSArray *runs = YouModObjectFromSelector(nameObj, @selector(runsArray));
+ if (runs.count > 0) nameStr = YouModStringFromSelector(runs.firstObject, @selector(text));
+ }
+ if (!nameStr.length) nameStr = languageCode;
+ if (!nameStr.length) nameStr = vssId;
+
+ [items addObject:[YouModMenuItem itemWithTitle:nameStr subtitle:languageCode icon:YouModIconImage(637) handler:^{
+ NSString *vttURL = [baseURL stringByAppendingString:@"&fmt=vtt"];
+ NSURL *url = [NSURL URLWithString:vttURL];
+ if (!url) {
+ YouModSendToast(@"Invalid caption URL.", presenter);
+ return;
+ }
+ YouModSendToast(@"Downloading captions...", presenter);
+ [[NSURLSession.sharedSession dataTaskWithURL:url completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) {
+ dispatch_async(dispatch_get_main_queue(), ^{
+ if (error || data.length == 0) {
+ YouModSendToast(@"Failed to download captions.", presenter);
+ return;
+ }
+ NSString *videoID = YouModVideoIDForPlayer(player) ?: @"video";
+ NSString *filename = [NSString stringWithFormat:@"%@_%@.vtt", videoID, languageCode ?: @"captions"];
+ NSURL *tempURL = [NSURL fileURLWithPath:[NSTemporaryDirectory() stringByAppendingPathComponent:filename]];
+ [data writeToURL:tempURL atomically:YES];
+ YouModShareFile(tempURL, presenter);
+ });
+ }] resume];
+ }]];
+ }
+
+ if (items.count == 0) {
+ YouModSendToast(@"No valid caption URLs found.", presenter);
+ return;
+ }
+
+ YouModPresentMenu(@"Download captions", items, presenter, sender);
+}
+
+static void YouModShowDownloadManager(YTPlayerViewController *player, UIViewController *presenter, UIView *sender) {
+ if (!player) {
+ YouModSendToast(@"Open a video before using the download manager.", presenter);
+ return;
+ }
+
+ NSString *videoID = YouModVideoIDForPlayer(player);
+ NSMutableArray *items = [NSMutableArray array];
+ [items addObject:[YouModMenuItem itemWithTitle:@"Download video" subtitle:@"Choose quality" icon:YouModIconImage(658) handler:^{
+ YouModShowVideoQualitySheet(player, presenter, sender);
+ }]];
+ [items addObject:[YouModMenuItem itemWithTitle:@"Download audio" subtitle:@"Choose format" icon:YouModIconImage(21) handler:^{
+ YouModShowAudioSheet(player, presenter, sender);
+ }]];
+ [items addObject:[YouModMenuItem itemWithTitle:@"Download captions" subtitle:@"Save subtitles as VTT" icon:YouModIconImage(637) handler:^{
+ YouModShowCaptionsSheet(player, presenter, sender);
+ }]];
+ [items addObject:[YouModMenuItem itemWithTitle:@"Copy diagnostics" subtitle:@"Copy last error log" icon:YouModIconImage(870) handler:^{
+ YouModCopyDownloadDiagnostics(presenter);
+ }]];
+ [items addObject:[YouModMenuItem itemWithTitle:@"Save thumbnail" subtitle:@"Save to Photos" icon:YouModIconImage(367) handler:^{
+ YouModDownloadThumbnail(videoID, presenter);
+ }]];
+ [items addObject:[YouModMenuItem itemWithTitle:@"Copy video information" subtitle:@"Copy title and URL" icon:YouModIconImage(250) handler:^{
+ YouModCopyVideoInfo(player, presenter);
+ }]];
+ YouModPresentMenu(@"Download manager", items, presenter, sender);
+}
+
+void YouModConfigureDownloadButton(_ASDisplayView *view) {
+ if (![view.accessibilityIdentifier isEqualToString:@"id.ui.add_to.offline.button"]) return;
+ if (!IS_ENABLED(DownloadManager) || IS_ENABLED(HideDownloadButton)) return;
+ if (objc_getAssociatedObject(view, @selector(YouModDownloadButtonTapped:))) return;
+
+ view.userInteractionEnabled = YES;
+ UITapGestureRecognizer *tap = [[UITapGestureRecognizer alloc] initWithTarget:view action:@selector(YouModDownloadButtonTapped:)];
+ tap.cancelsTouchesInView = YES;
+ tap.delaysTouchesBegan = YES;
+ tap.delaysTouchesEnded = YES;
+ [view addGestureRecognizer:tap];
+ objc_setAssociatedObject(view, @selector(YouModDownloadButtonTapped:), @YES, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
+}
+
+%hook _ASDisplayView
+
+%new
+- (void)YouModDownloadButtonTapped:(UITapGestureRecognizer *)sender {
+ if (sender.state != UIGestureRecognizerStateEnded) return;
+ UIViewController *presenter = YouModPresenterForSender(self, YouModCurrentPlayerViewController);
+ YTPlayerViewController *player = YouModPlayerFromViewController(presenter);
+ YouModShowDownloadManager(player, presenter, self);
+}
+
+%end
+
+%hook YTPlayerViewController
+
+- (void)viewDidAppear:(BOOL)animated {
+ %orig;
+ YouModCurrentPlayerViewController = self;
+}
+
+- (void)viewWillDisappear:(BOOL)animated {
+ %orig;
+ if (YouModCurrentPlayerViewController == self)
+ YouModCurrentPlayerViewController = nil;
+}
+
+%end
+
+NSString *YouModGlobalAuthHeader = nil;
+
+%hook SSOAuthorization
+- (id)accessToken {
+ id token = %orig;
+ if ([token isKindOfClass:[NSString class]] && [(NSString *)token length] > 0) {
+ YouModGlobalAuthHeader = [NSString stringWithFormat:@"Bearer %@", token];
+ }
+ return token;
+}
+%end
+
+%hook SSOAuthorizationImpl
+- (id)accessToken {
+ id token = %orig;
+ if ([token isKindOfClass:[NSString class]] && [(NSString *)token length] > 0) {
+ YouModGlobalAuthHeader = [NSString stringWithFormat:@"Bearer %@", token];
+ }
+ return token;
+}
+%end
+
+%hook GNPSSOAuthorizationService
+- (id)authToken {
+ id token = %orig;
+ if ([token isKindOfClass:[NSString class]] && [(NSString *)token length] > 0) {
+ YouModGlobalAuthHeader = [NSString stringWithFormat:@"Bearer %@", token];
+ }
+ return token;
+}
+%end
\ No newline at end of file
diff --git a/Files/Feed.x b/Files/Feed.x
index 4670281c..311425e4 100644
--- a/Files/Feed.x
+++ b/Files/Feed.x
@@ -1,64 +1,11 @@
#import "Headers.h"
-// YTUnShorts (https://github.com/PoomSmart/YTUnShorts)
-static NSMutableArray *filteredArray(NSArray *array) {
- NSMutableArray *newArray = [array mutableCopy];
- NSIndexSet *removeIndexes = [newArray indexesOfObjectsPassingTest:^BOOL(YTIItemSectionRenderer *sectionRenderer, NSUInteger idx, BOOL *stop) {
- if ([sectionRenderer isKindOfClass:%c(YTIShelfRenderer)]) {
- YTIShelfSupportedRenderers *content = ((YTIShelfRenderer *)sectionRenderer).content;
- YTIHorizontalListRenderer *horizontalListRenderer = content.horizontalListRenderer;
- NSMutableArray *itemsArray = horizontalListRenderer.itemsArray;
- NSIndexSet *removeItemsArrayIndexes = [itemsArray indexesOfObjectsPassingTest:^BOOL(YTIHorizontalListSupportedRenderers *horizontalListSupportedRenderers, NSUInteger idx2, BOOL *stop2) {
- YTIElementRenderer *elementRenderer = horizontalListSupportedRenderers.elementRenderer;
- NSString *description = [elementRenderer description];
- BOOL hasShorts = [description containsString:@"shorts_video_cell"];
- if (hasShorts) *stop2 = YES;
- return hasShorts;
- }];
- return removeItemsArrayIndexes.count > 0;
- }
- if ([sectionRenderer isKindOfClass:%c(YTIItemSectionRenderer)]) {
- NSString *description = [sectionRenderer description];
- if ([description containsString:@"shorts_shelf.eml"])
- return YES;
- }
- return NO;
- }];
- [newArray removeObjectsAtIndexes:removeIndexes];
- return newArray;
-}
-
-%group Shorts
-%hook YTInnerTubeCollectionViewController
-
-- (void)displaySectionsWithReloadingSectionControllerByRenderer:(id)renderer {
- NSMutableArray *sectionRenderers = [self valueForKey:@"_sectionRenderers"];
- [self setValue:filteredArray(sectionRenderers) forKey:@"_sectionRenderers"];
- %orig;
-}
-
-- (void)addSectionsFromArray:(NSArray *)array {
- %orig(filteredArray(array));
-}
-
-%end
-%end
-
// Hide Subbar
-%hook YTMySubsFilterHeaderView
-- (void)setChipFilterView:(id)arg1 { if (!IS_ENABLED(HideSubbar)) %orig; }
-%end
-
%hook YTHeaderContentComboView
- (void)enableSubheaderBarWithView:(id)arg1 { if (!IS_ENABLED(HideSubbar)) %orig; }
-- (void)setFeedHeaderScrollMode:(int)arg1 { IS_ENABLED(HideSubbar) ? %orig(0) : %orig; }
-%end
-
-%hook YTChipCloudCell
-- (void)layoutSubviews {
- if (self.superview && IS_ENABLED(HideSubbar)) {
- [self removeFromSuperview];
- } %orig;
+- (void)setFeedHeaderScrollMode:(int)arg1 {
+ int temp = IS_ENABLED(HideSubbar) ? 0 : arg1;
+ %orig(temp);
}
%end
@@ -77,10 +24,3 @@ static NSMutableArray *filteredArray(NSArray
#import
#import
+#import
#import
#import
#import
@@ -26,35 +27,69 @@
#import
#import
#import
-#import
#import
-#import
#import
#import
#import
#import
+#import
+#import
#import
+#import
+#import
#import
+#import
+#import
+#import
#import
+#import
+#import
+#import
+#import
#import
+#import
+#import
+#import
+#import
+#import
+#import
+#import
+#import
+#import
+#import
+#import
+#import
+#import
+#import
+#import
+#import
-// For Settings.x
+// For Settings.x and SponsorBlockSettings.x
#import
#import
-#import
#import
-#import
#import
#import
-#import
#import
+#define DownloadFix @"YouModDownloadFix"
+#define DownloadServerIndex @"YouModDownloadServerIndex"
+
#define IS_ENABLED(k) [[NSUserDefaults standardUserDefaults] boolForKey:k]
#define INTFORVAL(v) [[NSUserDefaults standardUserDefaults] integerForKey:v]
+#define FixPlaybackIssues @"YouModFixPlaybackIssues"
+#define MuteButton @"YouModMuteButton"
+#define SpeedButton @"YouModSpeedButton"
+#define ShareButton @"YouModShareButton"
+#define LoopButton @"YouModLoopButton"
+#define CaptionButton @"YouModCaptionButton"
+#define KeepMutedKey @"YouModKeepMutedKey"
+#define KeepLoopKey @"YouModKeepLoopKey"
// Downloading
#define DownloadManager @"YouModDownloadManager"
#define DownloadSaveToPhotos @"YouModDownloadSaveToPhotos"
-#define DownloadPreferDRCAudio @"YouModDownloadPreferDRCAudio"
+#define AddDownloadToShorts @"YouModAddDownloadToShorts"
+#define UseOrigAudio @"YouModUseOrigAudio"
// Cache
#define AutoClearCache @"YouModAutoClearCache"
// Appearance
@@ -63,44 +98,66 @@
// Navigation bar
#define HideYTLogo @"YouModHideYTLogo"
#define YTPremiumLogo @"YouModYTPremiumLogo"
+#define StickyNavBar @"YouModStickyNavBar"
#define HideNoti @"YouModHideNotificationButton"
#define HideSearch @"YouModHideSearchButton"
#define HideVoiceSearch @"YouModHideVoiceSearchButton"
#define HideCastButtonNav @"YouModHideCastButtonNavigationBar"
// Feed
#define HideSubbar @"YouModHideSubbar"
+#define HideHoriShelf @"YouModHideHoriShelf"
#define HideGenMusicShelf @"YouModHideGenMusicShelf"
#define HideFeedPost @"YouModHideFeedPost"
+#define HidePlayables @"YouModHidePlayables"
#define HideShortsShelf @"YouModHideShortsShelf"
+#define KeepShortsSubscript @"YouModKeepShortsSubscript"
#define HideSearchHis @"YouModHideSearchHistoryAndSuggestions"
-#define HideSubButton @"YouModHideSubscribeButton"
-#define HideShoppingButton @"YouModHideShoppingButton"
-#define HideMemberButton @"YouModHideMemberButton"
+#define HideSurveys @"YouModHideSurveys"
// Player
+#define WifiQualityIndex @"YouModWifiQualityIndex"
+#define CellQualityIndex @"YouModCellQualityIndex"
+#define LowPowerQualityIndex @"YouModLowPowerQualityIndex"
+#define AudioTrack @"YouModAudioTrackSegment"
+#define AudioTrackLangIndex @"YouModAudioTrackLangIndex"
+#define NoDubbedAudioTrack @"YouModNoDubbedAudioTrack"
+#define CaptionTrack @"YouModCaptionTrack"
+#define CaptionTrackLangIndex @"YouModCaptionTrackLangIndex"
+#define DisablesCaptionTrack @"YouModDisablesCaptionTrack"
+#define AutoSpeedIndex @"YouModAutoSpeedIndex"
+#define HoldToSpeedIndex @"YouModHoldToSpeedIndex"
#define HideAutoPlayToggle @"YouModHideAutoPlayToggle"
#define HideCaptionsButton @"YouModHideCaptionsButton"
#define HideCastButtonPlayer @"YouModHideCastButtonPlayer"
-#define HidePrevButton @"YouModHidePrevButton"
-#define HideNextButton @"YouModHideNextButton"
+#define HideNextAndPrevButtons @"YouModHideNextAndPrevButtons"
#define ReplacePrevNextButtons @"YouModReplacePrevNextButtons"
+#define SkipBackwardEnabled @"YouModSkipBackwardEnabled"
+#define SkipForwardEnabled @"YouModSkipForwardEnabled"
+#define RewindSeconds @"YouModRewindSeconds"
+#define ForwardSeconds @"YouModForwardSeconds"
#define RemoveDarkOverlay @"YouModRemoveDarkOverlay"
#define RemoveAmbiant @"YouModRemoveAmbiantColors"
#define HideEndScreenCards @"YouModHideEndScreenCards"
#define HideSuggestedVideo @"YouModHideSuggestedVideoOnFinish"
#define HidePaidPromoOverlay @"YouModHidePaidPromoOverlay"
#define HideWaterMark @"YouModHideWaterMark"
+#define DisablesEngagementPanel @"YouModDisablesEngagementPanel"
+#define DontSnapToChapter @"YouModDontSnapToChapter"
+#define PauseOnOverlay @"YouModPauseOnOverlay"
#define GestureControls @"YouModEnableGesturesControls"
#define GestureActivationArea @"YouModGestureActivationArea"
#define LeftSideGesture @"YouModLeftSideGesture"
#define RightSideGesture @"YouModRightSideGesture"
#define GestureHUD @"YouModGestureHUD"
+#define GestureHUDSize @"YouModGestureHUDSize"
+#define GestureHUDPosition @"YouModGestureHUDPosition"
#define DisablesDoubleTap @"YouModDisablesDoubleTap"
#define DisablesLongHold @"YouModDisablesLongHold"
#define AutoExitFullScreen @"YouModAutoExitFullScreen"
-#define DisablesCaptions @"YouModAutoDisablesCaptions"
#define DisablesShowRemaining @"YouModDisablesShowRemainingTime"
#define AlwaysShowRemaining @"YouModAlwaysShowRemainingTime"
#define ShowExtraTimeRemaining @"YouModShowExtraTimeRemaining"
+#define Uses24HoursTime @"YouModUses24HoursTime"
+#define CopyWithTimestampOnPause @"YouModCopyWithTimestampOnPause"
#define HideFullAction @"YouModHideFullScreenAction"
#define HideFullvidTitle @"YouModHideFullscreenVideoTitle"
#define StopAutoplayVideo @"YouModStopAutoplayVideo"
@@ -109,57 +166,97 @@
#define PortFull @"YouModPortraitFullscreen"
#define OldQualityPicker @"YouModUseOldQualityPicker"
#define ExtraSpeed @"YouModAddExtraSpeed"
-#define DisableHints @"YouModDisableHints"
#define ForceMiniPlayer @"YouModForceMiniPlayer"
#define AlwaysShowSeekbar @"YouModAlwaysShowSeekbar"
-#define HideLikeButton @"YouModHideLikeButton"
-#define HideDisLikeButton @"YouModHideDisLikeButton"
-#define HideShareButton @"YouModHideShareButton"
-#define HideDownloadButton @"YouModHideDownloadButton"
-#define HideClipButton @"YouModHideClipButton"
-#define HideRemixButton @"YouModHideRemixButton"
-#define HideSaveButton @"YouModHideSaveButton"
+#define DisablesFreeZoom @"YouModDisablesFreeZoom"
+#define TapToSeek @"YouModTapToSeek"
+#define PauseTwoFingers @"YouModPauseTwoFingers"
+#define HideCommentsSection @"YouModHideCommentsSection"
+#define HideCommentsPreview @"YouModHideCommentsPreview"
+#define UseAnotherMiniplayer @"YouModUseAnotherMiniplayer"
// Shorts
-#define HideShortsLikeButton @"YouModHideShortsLikeButton"
-#define HideShortsDisLikeButton @"YouModHideShortsDisLikeButton"
-#define HideShortsCommentButton @"YouModHideShortsCommentButton"
-#define HideShortsShareButton @"YouModHideShortsShareButton"
-#define HideShortsRemixButton @"YouModHideShortsRemixButton"
-#define HideShortsMetaButton @"YouModHideShortsMetaButton"
+#define FullScreenShorts @"YouModFullScreenShorts"
+#define RemoveShortsLive @"YouModRemoveShortsLive"
+#define RemoveShortsPosts @"YouModRemoveShortsPosts"
#define HideShortsProducts @"YouModHideShortsProducts"
#define HideShortsRecbar @"YouModHideShortsRecbar"
-#define HideShortsCommit @"YouModHideShortsCommit"
-#define HideShortsSubscriptButton @"YouModHideShortsSubscriptButton"
-#define HideShortsLiveButton @"YouModHideShortsLiveButton"
-#define HideShortsLensButton @"YouModHideShortsLensButton"
-#define HideShortsTrendsButton @"YouModHideShortsTrendsButton"
-#define HideShortsToVideo @"YouModHideShortsToVideo"
#define EnablesShortsQuality @"YouModEnablesShortsQuality"
#define ShowShortsSeekbar @"YouModShowShortsSeekbar"
+#define ShortsActionIndex @"YouModMakeAShortsAction"
+#define ShortsOnly @"YouModShortsOnly"
// Tab bar
#define DefaultTab @"YouModDefaultStartupTab"
+#define TabOrder @"YouModTabOrder"
#define HideTabIndi @"YouModHideTabIndicators"
#define HideTabLabels @"YouModHideTabLabels"
-#define HideHomeTab @"YouModHideHomeTab"
-#define HideShortsTab @"YouModHideShortsTab"
-#define HideCreateButton @"YouModHideCreateButton"
-#define HideSubscriptTab @"YouModHideSubscriptionsTab"
+#define UseFrostedTabBar @"YouModUseFrostedTabBar"
// Miscellaneous
#define BackgroundPlayback @"YouModEnablesBackgroundPlayback"
#define DisablesShortsPiP @"YouModTrytoDisablesShortsPiP"
+#define DisableHints @"YouModDisableHints"
#define BlockUpgradeDialogs @"YouModBlockUpgradeDialogs"
#define HideAreYouThereDialog @"YouModHideAreYouThereDialog"
#define FixesSlowMiniPlayer @"YouModFixesSlowMiniPlayer"
#define DisablesNewMiniPlayer @"YouModDisablesNewMiniPlayer"
#define DisablesSnackBar @"YouModDisablesSnackBar"
#define HideStartupAni @"YouModHideStartupAnimations"
-#define HidePlayInNextQueue @"YouModHidePlayInNextQueue"
#define HideLikeDislikeVotes @"YouModHideLikeDislikeVotes"
+#define HideCommuGuide @"YouModHideCommuGuide"
+#define DisablesRTL @"YouModDisablesRTL"
+#define DeviceUIIndex @"YouModDeviceUIIndex"
// #define CustomStartup @"YouModUseCustomVideoStartup"
+// Flyout menu
+#define RemovePlayInNextQueueOption @"YouModRemovePlayInNextQueueOption"
+#define RemoveDownloadOption @"YouModRemoveDownloadOption"
+#define RemoveWatchLaterOption @"YouModRemoveWatchLaterOption"
+#define RemoveSaveOption @"YouModRemoveSaveOption"
+#define RemoveRemoveFromPlaylistOption @"YouModRemoveRemoveFromPlaylistOption"
+#define RemoveShareOption @"YouModRemoveShareOption"
+#define RemoveNotInterestedOption @"YouModRemoveNotInterestedOption"
+#define RemoveInfoOption @"YouModRemoveInfoOption"
+#define RemoveFilterOption @"YouModRemoveFilterOption"
+#define RemoveReportOption @"YouModRemoveReportOption"
+#define RemoveYouTubeMusicOption @"YouModRemoveYouTubeMusicOption"
+#define RemoveFeedBackOption @"YouModRemoveFeedBackOption"
+#define RemoveDontRecommendOption @"YouModRemoveDontRecommendOption"
+#define RemoveCastOption @"YouModRemoveCastOption"
+#define RemoveShuffleOption @"YouModRemoveShuffleOption"
+#define RemoveUnSubOption @"YouModRemoveUnSubOption"
+#define RemoveHideFromPlaylistOption @"YouModRemoveHideFromPlaylistOption"
+#define RemoveHelpOption @"YouModRemoveHelpOption"
+#define RemoveNotifyOption @"YouModRemoveNotifyOption"
+#define RemoveClearScreenOption @"YouModRemoveClearScreenOption"
+// SponsorBlock
+#define SBEnabled @"YouModSBEnabled"
+#define SBShowButton @"YouModSBShowButton"
+#define SBShowNotifications @"YouModSBShowNotifications"
+#define SBAudioNotification @"YouModSBAudioNotification"
+#define SBSegmentsInPlayer @"YouModSBSegmentsInPlayer"
+#define SBSegmentsInFeed @"YouModSBSegmentsInFeed"
+#define SBSegmentsInMiniPlayer @"YouModSBSegmentsInMiniPlayer"
+#define SBShowDuration @"YouModSBShowDuration"
+#define SBMinDuration @"YouModSBMinDuration"
+#define SBSkipAlertDuration @"YouModSBSkipAlertDuration"
+#define SBUnskipAlertDuration @"YouModSBUnskipAlertDuration"
+
+#define SB_ACTION_KEY(cat) [NSString stringWithFormat:@"YouModSBAction_%@", cat]
+#define SB_COLOR_KEY(cat) [NSString stringWithFormat:@"YouModSBColor_%@", cat]
+
+#define FLOAT_FOR_KEY(k) [[NSUserDefaults standardUserDefaults] floatForKey:k]
#define YT_BUNDLE_ID @"com.google.ios.youtube"
#define YT_NAME @"YouTube"
+@interface YTMenuItemMDCButton : UIButton
+@end
+
+@interface YTDefaultSheetController (YouMod)
++ (instancetype)sheetControllerWithParentResponder:(id)parentResponder;
+- (void)addAction:(YTActionSheetAction *)action;
+- (void)presentFromView:(UIView *)view animated:(BOOL)animated completion:(void (^)(void))completion;
+- (void)presentFromViewController:(UIViewController *)vc animated:(BOOL)animated completion:(void (^)(void))completion;
+@end
+
// Gesture Section Enum
typedef NS_ENUM(NSUInteger, GestureSection) {
GestureSectionTop,
@@ -167,6 +264,39 @@ typedef NS_ENUM(NSUInteger, GestureSection) {
GestureSectionInvalid
};
+@interface YTWatchController (YouMod)
+- (void)reload;
+@end
+
+@interface YTELMViewController : UIViewController
+- (YTIElementRenderer *)renderer;
+@end
+
+@interface YTInlineScrubGestureView : UIView
+@end
+
+@interface YTReelContainerViewController : UIViewController
+@end
+
+@interface YTAppReelWatchRootViewController : UIViewController
+@end
+
+@interface YTPivotBarView : UIView
+@end
+
+@interface YTContextualSheetView : UIView
+@end
+
+@interface YTShortsAdsPlayerViewController : YTReelPlayerViewController
+@end
+
+@interface YTIBrowseRequest (YouMod)
++ (NSString *)browseIDForGamingDestination;
++ (NSString *)browseIDForSportsDestination;
++ (NSString *)browseIDForNotificationsInbox;
++ (NSString *)browseIDForHistory;
+@end
+
@interface YTITopbarLogoRenderer : NSObject
@property(readonly, nonatomic) YTIIcon *iconImage;
@end
@@ -177,15 +307,13 @@ typedef NS_ENUM(NSUInteger, GestureSection) {
@end
@interface YTMainAppVideoPlayerOverlayView (YouMod)
+@property (nonatomic, weak, readwrite) YTMainAppVideoPlayerOverlayViewController *delegate;
@property (nonatomic, strong) YTQTMButton *playbackRouteButton;
@end
@interface YTNavigationBarTitleView : UIView
@end
-@interface YTChipCloudCell : UICollectionViewCell
-@end
-
@interface YTSearchViewController : UIViewController
@end
@@ -199,21 +327,121 @@ typedef NS_ENUM(NSUInteger, GestureSection) {
@interface YTPivotBarViewController : UIViewController
- (void)selectItemWithPivotIdentifier:(id)pivotIndentifier;
+- (void)YouModReloadTabBar:(id)arg;
+@end
+
+@interface YTAppViewController (YouMod)
+@property (nonatomic, assign, readonly) YTPivotBarViewController *pivotBarViewController;
+- (void)hidePivotBar;
+- (void)showPivotBar;
+- (void)refreshPivotBarWithTriggedByNotification:(BOOL)arg;
+- (BOOL)isPivotBarHidden;
+@end
+
+@interface YTAppViewControllerImpl (YouMod)
+@property (nonatomic, assign, readonly) YTPivotBarViewController *pivotBarViewController;
+- (void)hidePivotBar;
+- (void)showPivotBar;
+- (void)refreshPivotBarWithTriggedByNotification:(BOOL)arg;
+- (BOOL)isPivotBarHidden;
+@end
+
+@interface YTReelWatchPlaybackOverlayView : UIView
+@property (nonatomic, retain) UIPinchGestureRecognizer *YouModFullscreenGesture;
+- (void)YouModFullscrrenGestureHandler:(UIPinchGestureRecognizer *)gesture;
+@end
+
+@interface YTReelContentView (YouMod)
+- (YTReelWatchPlaybackOverlayView *)playbackOverlay;
+- (void)YouModTurnOffShortsOnly:(UILongPressGestureRecognizer *)gesture;
+@end
+
+@interface YTLanguages : NSObject
++ (instancetype)languageList;
+@end
+
+@interface YTICaptionTrackEntry : GPBMessage
+- (NSString *)baseURL;
+- (NSString *)vssId;
+- (NSString *)languageCode;
+- (YTIFormattedString *)name;
@end
@interface YTPlayerViewController (YouMod)
@property (nonatomic, retain) UIPanGestureRecognizer *YouModPanGesture;
+@property (nonatomic, retain) UITapGestureRecognizer *YouModTapGesture;
@property (nonatomic, retain) UILabel *YouModGestureHUD;
+@property (nonatomic, weak, readwrite) UIViewController *parentViewController;
+@property (nonatomic, assign, readonly) BOOL isInlinePlaybackActive;
+@property (nonatomic, assign, readonly) BOOL isPlayingAd;
+@property (nonatomic, strong) UIView *YouModSpeedToastView;
+@property (nonatomic, strong) UILabel *YouModSpeedToastLabel;
+@property (nonatomic, retain) UILongPressGestureRecognizer *YouModHoldGesture;
- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldRecognizeSimultaneouslyWithGestureRecognizer:(UIGestureRecognizer *)otherGestureRecognizer;
- (void)YouModAutoFullscreen;
-- (void)YouModTurnOffCaptions;
-- (void)setActiveCaptionTrack:(id)arg1 source:(long long)arg2;
+- (void)YouModSetAutoSpeed;
- (void)setPlaybackRate:(float)rate;
+- (void)setActiveCaptionTrack:(MLInnerTubeCaptionTrack *)arg1 source:(NSInteger)arg2;
+- (void)setActiveCaptionTrack:(MLInnerTubeCaptionTrack *)arg;
+- (void)play;
+- (void)pause;
+- (void)YouModAutoMute;
+- (void)YouModAutoAudioTrack;
+- (void)YouModAutoCaptions;
+- (void)YouModLoopButton;
+- (void)YouModCaptionsHelper:(MLInnerTubeCaptionTrack *)arg;
+- (void)YouModShareButton:(UIView *)sourceView;
+- (NSInteger)playerState;
+- (YTPlayerResponse *)contentPlayerResponse;
+- (YTPlayerResponse *)playerResponse;
+- (id)audioTrackController;
+- (void)setAudioTrack:(YTIAudioTrack *)arg1 source:(NSInteger)arg2;
+- (void)YouModHideSpeedToast;
+- (void)YouModShowSpeedToast:(CGFloat)speed;
+- (void)YouModHoldToSpeed:(UILongPressGestureRecognizer *)gesture;
+@end
+
+@interface YTPlayerBarController (YouMod)
+- (void)didScrub:(UIPanGestureRecognizer *)gesture;
+@end
+
+@interface YTAutoplayAutonavController : NSObject
+- (void)setLoopMode:(NSInteger)loopMode;
+@end
+
+@interface YTInlineMutedPlaybackPlayerOverlayViewController : UIViewController
+@end
+
+@interface YTInlineMutedPlaybackPlayerOverlayView : UIView
+@end
+
+@interface YTWatchFloatingMiniplayerViewController : UIViewController
+@end
+
+@interface YTWatchFloatingMiniplayerWithPersistentControlsView : UIView
+- (NSInteger)dockHandleStyle;
+@end
+
+@interface YTWatchFloatingMiniplayerProgressBarView : UIView
@end
@interface SSOConfiguration : NSObject
@end
+@interface YTIMySubsFilterHeaderRenderer : GPBMessage
+@end
+
+@interface YTMySubsFilterHeaderViewController : UIViewController
+@end
+
+@interface YTEngagementPanelView : UIView
+- (UIView *)footerView;
+@end
+
+@interface YTMainAppControlsOverlayView (YouMod)
+- (YTMainAppVideoPlayerOverlayViewController *)eventsDelegate;
+@end
+
@interface YTVideoQualitySwitchOriginalController (YouMod)
@property (retain, nonatomic) YTVideoQualitySwitchRedesignedController *redesignedController;
@end
@@ -241,6 +469,10 @@ typedef NS_ENUM(NSUInteger, GestureSection) {
- (void)YouModAutoClearCache;
@end
+@interface YTInlinePlayerBarContainerView (YouMod)
+@property (nonatomic, strong) NSString *endTimeString;
+@end
+
// Custom perferences logics
@interface YouModPrefsManager : NSObject
+ (instancetype)sharedManager;
@@ -249,34 +481,202 @@ typedef NS_ENUM(NSUInteger, GestureSection) {
- (void)restoreYouModDefaults;
@end
+@interface YTIAudioTrack (YouMod)
+@property (nonatomic, assign, readwrite) BOOL isAutoDubbed;
+- (BOOL)hasId_p;
+@end
+
+@interface MLInnerTubeCaptionTrack (YouMod)
+- (NSString *)languageCode;
+- (NSString *)VSSID;
+@end
+
+@interface YTCaptionTrackSwitchController : NSObject
+@end
+
// Player Gestures - @bhackel (YTLitePlus)
-@interface YTFineScrubberFilmstripView : UIView
+@interface YTMainAppVideoPlayerOverlayViewController (YouMod)
+@property (nonatomic, assign) YTPlayerViewController *parentViewController;
+- (YTCaptionTrackSwitchController *)captionTrackController;
+- (NSString *)videoID;
+- (CGFloat)mediaTime;
+- (void)setVideoFreeZoomOverlayController:(id)arg;
@end
-@interface YTFineScrubberFilmstripCollectionView : UICollectionView
+@interface YTSingleVideoController (YouMod)
+@property (nonatomic, assign, readonly) CGFloat totalMediaTime;
+- (YTSingleVideoTime *)currentVideoTime;
+- (void)setVideoFormatConstraint:(id)arg;
+- (void)YouModAutoQuality;
+- (NSArray *)availableCaptionTracks;
+- (MLInnerTubeCaptionTrack *)activeCaptionTrack;
@end
-@interface YTWatchFullscreenViewController : YTMultiSizeViewController
+@interface YTReelPlayerViewController (YouMod)
+- (void)reelContentViewRequestsAdvanceToNextVideo:(id)arg;
+- (void)reelContentViewRequestsPlayPauseToggle:(id)arg;
+- (id)audioTrackController;
+- (void)YouModAutoAudioTrack:(YTPlayerViewController *)pv;
+- (void)YouModOnlyShorts;
@end
-@interface YTPlayerBarController (YouMod)
-- (void)didScrub:(UIPanGestureRecognizer *)gestureRecognizer;
-- (void)startScrubbing;
-- (void)didScrubToPoint:(CGPoint)point;
-- (void)endScrubbingForSeekSource:(int)seekSource;
+@interface YTIPlayerCaptionsTrackListRenderer : GPBMessage
+- (NSMutableArray *)captionTracksArray;
@end
-@interface YTMainAppVideoPlayerOverlayViewController (YouMod)
-@property (nonatomic, strong, readwrite) YTPlayerBarController *playerBarController;
+@interface YTICaptionsSupportedRenderers : GPBMessage
+- (YTIPlayerCaptionsTrackListRenderer *)playerCaptionsTracklistRenderer;
@end
-@interface YTInlinePlayerBarContainerView (YouMod)
-@property UIPanGestureRecognizer *scrubGestureRecognizer;
-@property (nonatomic, strong, readwrite) YTFineScrubberFilmstripView *fineScrubberFilmstrip;
-@property (nonatomic, strong, readwrite) NSString *endTimeString;
-- (CGFloat)scrubXForScrubRange:(CGFloat)scrubRange;
+@interface YTIPlayerResponse (YouMod)
+- (YTIStreamingData *)streamingData;
+- (YTICaptionsSupportedRenderers *)captions;
@end
-@interface YTSingleVideoController (YouMod)
-@property (nonatomic, assign, readonly) CGFloat totalMediaTime;
+@interface YTIFormatStream (YouMod)
+- (NSString *)mimeType;
+- (NSInteger)contentLength;
+- (NSUInteger)approxDurationMs;
+- (int)height;
+- (int)fps;
+- (YTIAudioTrack *)audioTrack;
+- (int)itag;
+@end
+
+@interface YTIFormattedString (YouMod)
+- (NSString *)dropdownOptionTitle;
+@end
+
+@interface YTIVideoDetails (YouMod)
+- (NSString *)title;
+- (NSString *)author;
+- (NSString *)shortDescription;
+@end
+
+@interface YTDataUtils : NSObject
++ (instancetype)generateClientSideNonce;
+@end
+
+@interface YCHAsyncLiveChatCollectionViewController : UIViewController
+@end
+
+// SponsorBlock action modes
+typedef NS_ENUM(NSInteger, SBSegmentAction) {
+ SBSegmentActionDisable = 0,
+ SBSegmentActionAutoSkip = 1,
+ SBSegmentActionAsk = 2,
+ SBSegmentActionDisplay = 3,
+ SBSegmentActionSkipTo = 4
+};
+
+@interface SBSegment : NSObject
+@property (nonatomic, strong) NSString *UUID;
+@property (nonatomic, strong) NSString *category;
+@property (nonatomic, assign) float startTime;
+@property (nonatomic, assign) float endTime;
+@property (nonatomic, strong) NSString *actionType;
++ (instancetype)segmentWithUUID:(NSString *)UUID category:(NSString *)category start:(float)start end:(float)end action:(NSString *)actionType;
+- (SBSegmentAction)configuredAction;
+- (UIColor *)segmentColor;
+@end
+
+@interface SBRequest : NSObject
++ (void)fetchSegmentsForVideoID:(NSString *)videoID completion:(void (^)(NSArray *segments))completion;
+@end
+
+@interface SBSkipNotificationView : UIView
+@property (nonatomic, strong) UILabel *messageLabel;
+@property (nonatomic, strong) UIButton *actionButton;
+@property (nonatomic, strong) UIView *progressOverlay;
+@property (nonatomic, copy) void (^onAction)(void);
+@property (nonatomic, assign) NSTimeInterval totalDuration;
+@property (nonatomic, assign) NSTimeInterval remainingDuration;
+@property (nonatomic, assign) BOOL isPaused;
+@property (nonatomic, assign) BOOL isHighlightPill;
++ (instancetype)showInView:(UIView *)parentView message:(NSString *)message buttonTitle:(NSString *)buttonTitle action:(void (^)(void))action duration:(NSTimeInterval)duration;
++ (instancetype)showSuccessInView:(UIView *)parentView message:(NSString *)message duration:(NSTimeInterval)duration;
++ (instancetype)showErrorInView:(UIView *)parentView message:(NSString *)message duration:(NSTimeInterval)duration;
+- (void)dismiss;
+- (void)pauseProgress;
+- (void)resumeProgress;
+@end
+
+extern UIView *sbGetNotificationParent(void);
+extern void sbUpdateOverlayInsetForPivotBar(void);
+extern void YMPresentTabOrderModally(id parentResponder);
+
+// The ordered set of SponsorBlock categories YouMod supports. Both the core
+// (segment fetching / skipping) and the settings UI read from this single list,
+// so a category can never be fetchable without a control, or configurable
+// without being fetched.
+extern NSArray *sbAllCategories(void);
+
+// Tag stamped on every seek-bar segment marker view, used to find and remove
+// them across the player-bar layout hooks that don't hold a direct reference.
+static const NSInteger SBSegmentMarkerTag = 9900;
+
+// Supported range and default for the skip/unskip banner duration (seconds).
+// The settings sliders expose this range and the core clamps stored values to
+// it, so both read from one source and can never drift out of agreement.
+static const CGFloat SBAlertDurationMin = 2.0;
+static const CGFloat SBAlertDurationMax = 20.0;
+static const CGFloat SBAlertDurationDefault = 4.0;
+
+#pragma mark - Custom Overlay Button Registry
+
+// A registered button shown in the player's controls overlay (top-right, under
+// YouTube's settings gear). Features register a spec from their own %ctor; the
+// single YTMainAppControlsOverlayView hook in OverlayButtons.x lays them all out.
+@interface YMOverlayButtonSpec : NSObject
+@property (nonatomic, copy) NSString *identifier; // unique, e.g. @"sponsorblock.toggle"
+@property (nonatomic, copy) NSString *symbolName; // SF Symbol name (icon button)
+@property (nonatomic, copy) NSString *title; // text label; set this instead of symbolName for a text button
+@property (nonatomic, strong) UIColor *tintColor; // default tint (used if tintProvider is nil)
+@property (nonatomic, assign) NSInteger sortOrder; // ascending; lower = closer to gear (rightmost)
+@property (nonatomic, copy) void (^onTap)(YTPlayerViewController *player, YTQTMButton *button);
+@property (nonatomic, copy) BOOL (^isVisible)(YTPlayerViewController *player); // nil = always visible
+@property (nonatomic, copy) UIColor *(^tintProvider)(YTPlayerViewController *player); // nil = use tintColor
+@property (nonatomic, assign) NSInteger viewTag; // assigned by the registry; do not set
+@end
+
+extern void YMRegisterOverlayButton(YMOverlayButtonSpec *spec);
+extern NSArray *YMRegisteredOverlayButtons(void);
+
+@interface YMDownloadProgressView : UIView
+@property (nonatomic, strong) UILabel *titleLabel;
+@property (nonatomic, strong) UILabel *subtitleLabel;
+@property (nonatomic, strong) UIProgressView *progressBar;
+@property (nonatomic, strong) UIButton *cancelButton;
+@property (nonatomic, copy) void (^onCancel)(void);
++ (instancetype)showInView:(UIView *)parentView message:(NSString *)message cancelAction:(void (^)(void))cancelAction;
+- (void)updateProgress:(float)progress title:(NSString *)title subtitle:(NSString *)subtitle;
+- (void)dismiss;
+@end
+
+@interface YTPlayerViewController (SponsorBlock)
+@property (nonatomic, strong) NSString *sbLastVideoID;
+@property (nonatomic, strong) NSArray *sbSegments;
+@property (nonatomic, strong) NSMutableSet *sbSkippedSegments;
+@property (nonatomic, strong) SBSkipNotificationView *sbNotificationView;
+@property (nonatomic, assign) BOOL sbEnabledForVideo;
+- (void)sbCheckSegmentsAtCurrentTime;
+- (void)sbPerformSkip:(SBSegment *)segment;
+- (void)sbShowAskNotification:(SBSegment *)segment;
+- (void)sbShowHighlightBannerIfNeeded:(NSArray *)segments;
+- (void)sbSkipToHighlight;
+- (void)sbRefreshMarkers:(NSArray *)segments;
+@end
+
+@interface YTSegmentableInlinePlayerBarView : UIView
+@property (nonatomic, assign, readwrite) BOOL enableSnapToChapter;
+@property (nonatomic, strong) NSArray *sbMarkerViews;
+- (void)sbRenderSegments:(NSArray *)segments;
+- (void)sbClearSegments;
+- (void)sbRepositionMarkers;
+@end
+
+@interface YouModThumbnailViewController : UIViewController
+@property (nonatomic, strong) UIImage *thumbnailImage;
+@property (nonatomic, strong) UIScrollView *scrollView;
+@property (nonatomic, strong) UIImageView *imageView;
@end
\ No newline at end of file
diff --git a/Files/Navbar.x b/Files/Navbar.x
index 5316f291..58ace9d3 100644
--- a/Files/Navbar.x
+++ b/Files/Navbar.x
@@ -15,7 +15,10 @@
%orig(renderer);
}
// For when spoofing before 18.34.5
-- (void)setPremiumLogo:(BOOL)arg { IS_ENABLED(YTPremiumLogo) ? %orig(YES) : %orig; }
+- (void)setPremiumLogo:(BOOL)arg {
+ BOOL temp = IS_ENABLED(YTPremiumLogo) ? YES : arg;
+ %orig(temp);
+}
- (BOOL)isPremiumLogo { return IS_ENABLED(YTPremiumLogo) ? YES : %orig; }
%end
@@ -33,7 +36,10 @@
%orig(renderer);
}
// For when spoofing before 18.34.5
-- (void)setPremiumLogo:(BOOL)arg { IS_ENABLED(YTPremiumLogo) ? %orig(YES) : %orig; }
+- (void)setPremiumLogo:(BOOL)arg {
+ BOOL temp = IS_ENABLED(YTPremiumLogo) ? YES : arg;
+ %orig(temp);
+}
- (BOOL)isPremiumLogo { return IS_ENABLED(YTPremiumLogo) ? YES : %orig; }
%end
@@ -65,8 +71,16 @@
%hook YTNavigationBarTitleView
- (void)layoutSubviews {
%orig;
- if (self.subviews.count > 1 && [self.subviews[1].accessibilityIdentifier isEqualToString:@"id.yoodle.logo"] && IS_ENABLED(HideYTLogo)) {
+ if (self.subviews.count > 1 && [self.subviews[1].accessibilityIdentifier isEqualToString:@"id.youtube.logo"] && IS_ENABLED(HideYTLogo)) {
self.subviews[1].hidden = YES;
}
}
+%end
+
+%hook YTHeaderView
+- (BOOL)stickyNavHeaderEnabled { return IS_ENABLED(StickyNavBar) ? YES : %orig; }
+- (void)setStickyNavHeaderEnabled:(BOOL)arg {
+ BOOL temp = IS_ENABLED(StickyNavBar) ? YES : arg;
+ %orig(temp);
+}
%end
\ No newline at end of file
diff --git a/Files/Others.x b/Files/Others.x
index e8a2eebe..46f8dbec 100644
--- a/Files/Others.x
+++ b/Files/Others.x
@@ -1,15 +1,6 @@
#import "Headers.h"
-Class YTILikeResponseClass, YTIDislikeResponseClass, YTIRemoveLikeResponseClass;
-
// Background playback
-%group BackgroundPlayback
-%hook YTIBackgroundOfflineSettingCategoryEntryRenderer
-%new(B@:)
-- (BOOL)isBackgroundEnabled { return YES; }
-%end
-%end
-
%hook MLVideo
- (BOOL)playableInBackground { return IS_ENABLED(BackgroundPlayback) ? YES : %orig; }
%end
@@ -26,10 +17,18 @@ Class YTILikeResponseClass, YTIDislikeResponseClass, YTIRemoveLikeResponseClass;
- (BOOL)isPlayableInBackground { return IS_ENABLED(BackgroundPlayback) ? YES : %orig; }
%end
-// Try to disable Shorts PiP
%hook YTColdConfig
+// Try to disable Shorts PiP
- (BOOL)shortsPlayerGlobalConfigEnableReelsPictureInPicture { return IS_ENABLED(DisablesShortsPiP) ? NO : %orig; }
- (BOOL)shortsPlayerGlobalConfigEnableReelsPictureInPictureIos { return IS_ENABLED(DisablesShortsPiP) ? NO : %orig; }
+// Hide startup animations
+- (BOOL)mainAppCoreClientIosEnableStartupAnimation { return IS_ENABLED(HideStartupAni) ? NO : %orig; }
+// Prevent YouTube from asking "Are you there?"
+- (BOOL)enableYouthereCommandsOnIos { return IS_ENABLED(BlockUpgradeDialogs) ? NO : %orig; }
+// Fixes slow miniplayer
+- (BOOL)enableIosFloatingMiniplayerDoubleTapToResize { return IS_ENABLED(FixesSlowMiniPlayer) ? NO : %orig; }
+// Use old miniplayer
+- (BOOL)enableIosFloatingMiniplayer { return IS_ENABLED(DisablesNewMiniPlayer) ? NO : %orig; }
%end
%hook YTHotConfig
@@ -42,12 +41,38 @@ Class YTILikeResponseClass, YTIDislikeResponseClass, YTIRemoveLikeResponseClass;
%hook YTReelPlayerViewController
- (BOOL)isPictureInPictureAllowed { return IS_ENABLED(DisablesShortsPiP) ? NO : %orig; }
+- (void)setupPlayerForPiP { if (!IS_ENABLED(DisablesShortsPiP)) %orig; }
%end
%hook YTReelWatchRootViewController
- (void)switchToPictureInPicture { if (!IS_ENABLED(DisablesShortsPiP)) %orig; }
%end
+// Disable Hints
+%hook YTSettings
+- (BOOL)areHintsDisabled { return IS_ENABLED(DisableHints) ? YES : %orig; }
+- (void)setHintsDisabled:(BOOL)arg1 {
+ BOOL temp = IS_ENABLED(DisableHints) ? YES : arg1;
+ %orig(temp);
+}
+%end
+
+%hook YTSettingsImpl
+- (BOOL)areHintsDisabled { return IS_ENABLED(DisableHints) ? YES : %orig; }
+- (void)setHintsDisabled:(BOOL)arg1 {
+ BOOL temp = IS_ENABLED(DisableHints) ? YES : arg1;
+ %orig(temp);
+}
+%end
+
+%hook YTUserDefaults
+- (BOOL)areHintsDisabled { return IS_ENABLED(DisableHints) ? YES : %orig; }
+- (void)setHintsDisabled:(BOOL)arg1 {
+ BOOL temp = IS_ENABLED(DisableHints) ? YES : arg1;
+ %orig(temp);
+}
+%end
+
// Block upgrade dialogs
%hook YTGlobalConfig
- (BOOL)shouldBlockUpgradeDialog { return IS_ENABLED(BlockUpgradeDialogs) ? YES : %orig; }
@@ -56,11 +81,6 @@ Class YTILikeResponseClass, YTIDislikeResponseClass, YTIRemoveLikeResponseClass;
- (BOOL)shouldForceUpgrade { return IS_ENABLED(BlockUpgradeDialogs) ? NO : %orig; }
%end
-// Prevent YouTube from asking "Are you there?"
-%hook YTColdConfig
-- (BOOL)enableYouthereCommandsOnIos { return IS_ENABLED(BlockUpgradeDialogs) ? NO : %orig; }
-%end
-
%hook YTYouThereController
- (BOOL)shouldShowYouTherePrompt { return IS_ENABLED(HideAreYouThereDialog) ? NO : %orig; }
- (void)showYouTherePrompt { if (!IS_ENABLED(HideAreYouThereDialog)) %orig; }
@@ -71,16 +91,6 @@ Class YTILikeResponseClass, YTIDislikeResponseClass, YTIRemoveLikeResponseClass;
- (void)showYouTherePrompt { if (!IS_ENABLED(HideAreYouThereDialog)) %orig; }
%end
-// Fixes slow miniplayer
-%hook YTColdConfig
-- (BOOL)enableIosFloatingMiniplayerDoubleTapToResize { return IS_ENABLED(FixesSlowMiniPlayer) ? NO : %orig; }
-%end
-
-// Use old miniplayer
-%hook YTColdConfig
-- (BOOL)enableIosFloatingMiniplayer { return IS_ENABLED(DisablesNewMiniPlayer) ? NO : %orig; }
-%end
-
// Disables Snackbar
%hook GOOHUDManagerInternal
- (id)sharedInstance { return IS_ENABLED(DisablesSnackBar) ? nil : %orig; }
@@ -89,72 +99,104 @@ Class YTILikeResponseClass, YTIDislikeResponseClass, YTIRemoveLikeResponseClass;
- (void)displayHUDViewForMessage:(id)arg { if (!IS_ENABLED(DisablesSnackBar)) %orig; }
%end
-// Hide startup animations
-%hook YTColdConfig
-- (BOOL)mainAppCoreClientIosEnableStartupAnimation { return IS_ENABLED(HideStartupAni) ? NO : %orig; }
-%end
-
// Remove "Play next in queue" from the menu @PoomSmart (https://github.com/qnblackcat/uYouPlus/issues/1138#issuecomment-1606415080)
%hook YTMenuItemVisibilityHandler
- (BOOL)shouldShowServiceItemRenderer:(YTIMenuConditionalServiceItemRenderer *)renderer {
- if (renderer.icon.iconType == 251 && IS_ENABLED(HidePlayInNextQueue)) {
+ if (renderer.icon.iconType == 251 && IS_ENABLED(RemovePlayInNextQueueOption)) {
return NO;
- } return %orig;
+ }
+ return %orig;
}
%end
%hook YTMenuItemVisibilityHandlerImpl
- (BOOL)shouldShowServiceItemRenderer:(YTIMenuConditionalServiceItemRenderer *)renderer {
- if (renderer.icon.iconType == 251 && IS_ENABLED(HidePlayInNextQueue)) {
+ if (renderer.icon.iconType == 251 && IS_ENABLED(RemovePlayInNextQueueOption)) {
return NO;
- } return %orig;
+ }
+ return %orig;
}
%end
-/* untested
-// Remove Download button from the menu
+// Remove flyout menu options
%hook YTDefaultSheetController
- (void)addAction:(YTActionSheetAction *)action {
- NSString *identifier = [action valueForKey:@"_accessibilityIdentifier"];
+ if (![action.button isKindOfClass:%c(YTMenuItemMDCButton)]) {
+ %orig;
+ return;
+ }
+ YTMenuItemMDCButton *button = (YTMenuItemMDCButton *)action.button;
+ NSString *iden = button.accessibilityIdentifier;
+ NSString *imageName = [button.currentImage description];
+ // Method 1: Filter from accessibilityIdentifier
NSDictionary *actionsToRemove = @{
- @"7": @(ytlBool(@"removeDownloadMenu")),
- @"1": @(ytlBool(@"removeWatchLaterMenu")),
- @"3": @(ytlBool(@"removeSaveToPlaylistMenu")),
- @"5": @(ytlBool(@"removeShareMenu")),
- @"12": @(ytlBool(@"removeNotInterestedMenu")),
- @"31": @(ytlBool(@"removeDontRecommendMenu")),
- @"58": @(ytlBool(@"removeReportMenu"))
+ @"7": @(IS_ENABLED(RemoveDownloadOption)),
+ @"1": @(IS_ENABLED(RemoveWatchLaterOption)),
+ @"3": @(IS_ENABLED(RemoveSaveOption)),
+ @"4": @(IS_ENABLED(RemoveRemoveFromPlaylistOption)),
+ @"5": @(IS_ENABLED(RemoveShareOption)),
+ @"6": @(IS_ENABLED(RemoveShareOption)),
+ @"12": @(IS_ENABLED(RemoveNotInterestedOption)),
+ @"22": @(IS_ENABLED(RemoveInfoOption)),
+ @"36": @(IS_ENABLED(RemoveFilterOption)),
+ @"40": @(IS_ENABLED(RemoveNotifyOption)),
+ @"58": @(IS_ENABLED(RemoveReportOption))
};
-
- if (![actionsToRemove[identifier] boolValue]) {
- %orig;
+ if ([actionsToRemove[iden] boolValue]) return;
+
+ // Method 2: Filter from imageName
+ NSDictionary *imageNameToRemove = @{
+ @"youtube_music": @(IS_ENABLED(RemoveYouTubeMusicOption)),
+ @"flag": @(IS_ENABLED(RemoveReportOption)),
+ @"alert_bubble": @(IS_ENABLED(RemoveFeedBackOption)),
+ @"bookmark": @(IS_ENABLED(RemoveSaveOption)),
+ @"circle_slash": @(IS_ENABLED(RemoveNotInterestedOption)),
+ @"x_circle": @(IS_ENABLED(RemoveDontRecommendOption)),
+ @"chromecast": @(IS_ENABLED(RemoveCastOption)),
+ @"shuffle": @(IS_ENABLED(RemoveShuffleOption)),
+ @"person_x": @(IS_ENABLED(RemoveUnSubOption)),
+ @"help_circle": @(IS_ENABLED(RemoveHelpOption)),
+ @"eye_slash": @(IS_ENABLED(RemoveHideFromPlaylistOption)),
+ @"player_full_enter_alt": @(IS_ENABLED(RemoveClearScreenOption)),
+ @"info_circle": @(IS_ENABLED(RemoveInfoOption))
+ };
+ for (NSString *key in imageNameToRemove) {
+ if ([imageName containsString:key]) {
+ if ([imageNameToRemove[key] boolValue]) {
+ return;
+ }
+ break;
+ }
}
+ %orig;
}
%end
-*/
// YTSlientVote (https://github.com/PoomSmart/YTSilentVote)
-%group SlientVote
%hook YTInnerTubeResponseWrapper
- (id)initWithResponse:(id)response cacheContext:(id)arg2 requestStatistics:(id)arg3 mutableSharedData:(id)arg4 {
- if ([response isKindOfClass:YTILikeResponseClass]
- || [response isKindOfClass:YTIDislikeResponseClass]
- || [response isKindOfClass:YTIRemoveLikeResponseClass]) return nil;
+ if (!IS_ENABLED(HideLikeDislikeVotes)) return %orig;
+ if ([response isKindOfClass:%c(YTILikeResponse)]
+ || [response isKindOfClass:%c(YTIDislikeResponse)]
+ || [response isKindOfClass:%c(YTIRemoveLikeResponse)]) return nil;
return %orig;
}
%end
+
+%hook NSParagraphStyle
++ (NSWritingDirection)defaultWritingDirectionForLanguage:(id)lang { return IS_ENABLED(DisablesRTL) ? NSWritingDirectionLeftToRight : %orig; }
++ (NSWritingDirection)_defaultWritingDirection { return IS_ENABLED(DisablesRTL) ? NSWritingDirectionLeftToRight : %orig; }
%end
-%ctor {
- YTILikeResponseClass = %c(YTILikeResponse);
- YTIDislikeResponseClass = %c(YTIDislikeResponse);
- YTIRemoveLikeResponseClass = %c(YTIRemoveLikeResponse);
- %init;
- if (IS_ENABLED(HideLikeDislikeVotes)) {
- %init(SlientVote);
+%hook UIDevice
+- (UIUserInterfaceIdiom)userInterfaceIdiom {
+ if (INTFORVAL(DeviceUIIndex) == 1) {
+ return UIUserInterfaceIdiomPad;
}
- if (IS_ENABLED(BackgroundPlayback)) {
- %init(BackgroundPlayback);
+ if (INTFORVAL(DeviceUIIndex) == 2) {
+ return UIUserInterfaceIdiomPhone;
}
-}
\ No newline at end of file
+ return %orig;
+}
+%end
\ No newline at end of file
diff --git a/Files/OverlayButtons.x b/Files/OverlayButtons.x
new file mode 100644
index 00000000..b02185c5
--- /dev/null
+++ b/Files/OverlayButtons.x
@@ -0,0 +1,418 @@
+#import "Headers.h"
+
+#define TweakName @"YouMod"
+
+static NSBundle *YouModBundle() {
+ static NSBundle *bundle = nil;
+ static dispatch_once_t onceToken;
+ dispatch_once(&onceToken, ^{
+ NSString *tweakBundlePath = [[NSBundle mainBundle] pathForResource:TweakName ofType:@"bundle"];
+ if (tweakBundlePath)
+ bundle = [NSBundle bundleWithPath:tweakBundlePath];
+ else
+ bundle = [NSBundle bundleWithPath:[NSString stringWithFormat:PS_ROOT_PATH_NS(@"/Library/Application Support/%@.bundle"), TweakName]];
+ });
+ return bundle;
+}
+
+#define LOC(x) [YouModBundle() localizedStringForKey:x value:nil table:nil]
+
+// YouGetCaption (https://github.com/PoomSmart/YouGetCaption)
+static void showTranscript(YTFormat3CaptionViewController *cvc) {
+ UIView *parent = sbGetNotificationParent();
+ MLFormat3Captions *currentCaptions = [cvc valueForKey:@"_currentCaptions"];
+ YTIntervalTree *tree = currentCaptions.captions;
+ NSMutableString *transcript = [NSMutableString string];
+ [tree enumerateAllIntervalsWithBlock:^(YTInterval *interval) {
+ MLCaption *caption = (MLCaption *)interval;
+ NSArray *segments = caption.segments;
+ for (MLCaptionSegment *segment in segments) {
+ [transcript appendString:segment.text];
+ }
+ }];
+ if (transcript.length == 0) {
+ [SBSkipNotificationView showErrorInView:parent message:LOC(@"NO_CAPTIONS") duration:4.0];
+ return;
+ }
+ YTAlertView *alertView = [%c(YTAlertView) confirmationDialogWithAction:^{
+ UIPasteboard *pasteboard = [UIPasteboard generalPasteboard];
+ pasteboard.string = transcript;
+ [SBSkipNotificationView showSuccessInView:parent message:LOC(@"COPIED_TO_CLIPBOARD") duration:3.0];
+ } actionTitle:LOC(@"COPY_TO_CLIPBOARD")];
+ alertView.title = nil;
+ alertView.subtitle = transcript;
+ alertView.shouldDismissOnBackgroundTap = YES;
+ [alertView show];
+}
+
+#pragma mark - YMOverlayButtonSpec
+
+@implementation YMOverlayButtonSpec
+@end
+
+#pragma mark - Registry
+
+// Base of the view-tag range for registered overlay buttons. Chosen to avoid
+// colliding with other tagged views in the player overlay (e.g. the seek-bar
+// segment markers at 9900).
+static const NSInteger YMOverlayButtonBaseTag = 9910;
+
+// Button geometry. The top inset places the row just below YouTube's own
+// CC/gear row in the top-right corner of the player overlay.
+static const CGFloat YMOverlayButtonSize = 40.0;
+static const CGFloat YMOverlayButtonGap = 8.0;
+static const CGFloat YMOverlayButtonTopInset = 52.0; // fallback row top when the gear can't be located
+static const CGFloat YMOverlayButtonGearGap = 4.0; // vertical gap between YouTube's gear row and our row
+static const CGFloat YMOverlayButtonEdgePadding = 12.0; // fallback right padding when the gear isn't found
+
+// Point size of a text button's label. Tweak this to change how large the text renders.
+static const CGFloat YMOverlayTextButtonFontSize = 12.0;
+// Width of a text button. Tweak this to make text buttons wider or narrower; icon
+// buttons stay square at YMOverlayButtonSize.
+static const CGFloat YMOverlayTextButtonWidth = 40.0;
+
+static NSMutableArray *gOverlayButtons = nil;
+static NSInteger gOverlayButtonNextTag = YMOverlayButtonBaseTag;
+
+void YMRegisterOverlayButton(YMOverlayButtonSpec *spec) {
+ if (!spec || spec.identifier.length == 0) return;
+ static dispatch_once_t once;
+ dispatch_once(&once, ^{ gOverlayButtons = [NSMutableArray array]; });
+
+ // Replace any previous registration with the same identifier (idempotent).
+ for (YMOverlayButtonSpec *existing in [gOverlayButtons copy]) {
+ if ([existing.identifier isEqualToString:spec.identifier]) {
+ spec.viewTag = existing.viewTag;
+ [gOverlayButtons removeObject:existing];
+ }
+ }
+ if (spec.viewTag == 0) spec.viewTag = gOverlayButtonNextTag++;
+ [gOverlayButtons addObject:spec];
+}
+
+NSArray *YMRegisteredOverlayButtons(void) {
+ if (!gOverlayButtons) return @[];
+ return [gOverlayButtons sortedArrayUsingComparator:^NSComparisonResult(YMOverlayButtonSpec *a, YMOverlayButtonSpec *b) {
+ if (a.sortOrder == b.sortOrder) return [a.identifier compare:b.identifier];
+ return a.sortOrder < b.sortOrder ? NSOrderedAscending : NSOrderedDescending;
+ }];
+}
+
+#pragma mark - Helpers
+
+// The player view controller that owns this controls overlay, reached through the
+// overlay's events delegate. Button handlers use it to act on the current video.
+static YTPlayerViewController *YMPlayerVCFromOverlay(YTMainAppControlsOverlayView *overlay) {
+ YTMainAppVideoPlayerOverlayViewController *mainOverlayController = (YTMainAppVideoPlayerOverlayViewController *)overlay.eventsDelegate;
+ return mainOverlayController.parentViewController;
+}
+
+// Recursively find the right-most YTQTMButton in the overlay's top region. YouTube
+// nests the gear/CC/cast buttons inside a container, so a one-level scan would miss
+// them; recursion reaches the nested buttons wherever they sit.
+static void YMScanForGearFrame(UIView *view, YTMainAppControlsOverlayView *overlay, CGFloat topRegionMaxY, CGRect *bestFrame) {
+ for (UIView *sub in view.subviews) {
+ if ([sub isKindOfClass:%c(YTQTMButton)]) {
+ CGRect f = [sub convertRect:sub.bounds toView:overlay];
+ if (CGRectGetMidY(f) <= topRegionMaxY) { // in the top button row
+ // The CGRectIsNull check must stay first: CGRectGetMidX(CGRectNull) is
+ // infinite, so the > comparison alone would never accept the first match.
+ if (CGRectIsNull(*bestFrame) || CGRectGetMidX(f) > CGRectGetMidX(*bestFrame)) *bestFrame = f;
+ }
+ }
+ YMScanForGearFrame(sub, overlay, topRegionMaxY, bestFrame);
+ }
+}
+
+// Find YouTube's settings/overflow button so we can anchor our row directly beneath it.
+// Prefer the overlay's own overflowButton; otherwise take the right-most YTQTMButton in
+// the overlay's top region. Returns its frame in the overlay's coordinate space, or
+// CGRectNull if not found (the caller then falls back to the screen edge / top inset).
+static CGRect YMGearFrameInOverlay(YTMainAppControlsOverlayView *overlay) {
+ YTQTMButton *overflow = [overlay valueForKey:@"_overflowButton"];
+ if (overflow.window) {
+ return [overflow convertRect:overflow.bounds toView:overlay];
+ }
+
+ CGFloat topRegionMaxY = overlay.bounds.size.height * 0.25;
+ CGRect bestFrame = CGRectNull;
+ YMScanForGearFrame(overlay, overlay, topRegionMaxY, &bestFrame);
+ return bestFrame;
+}
+
+// The font for a text button's label, in YouTube Sans to match native controls,
+// with a plain system-font fallback on versions lacking the YouTube Sans style API.
+static UIFont *YMOverlayTextButtonFont(void) {
+ YTDefaultTypeStyle *typeStyle = [%c(YTTypeStyle) defaultTypeStyle];
+ if ([typeStyle respondsToSelector:@selector(ytSansFontOfSize:weight:)]) {
+ return [typeStyle ytSansFontOfSize:YMOverlayTextButtonFontSize weight:UIFontWeightSemibold];
+ }
+ return [UIFont systemFontOfSize:YMOverlayTextButtonFontSize weight:UIFontWeightSemibold];
+}
+
+static YTQTMButton *YMCreateOverlayButton(YTMainAppControlsOverlayView *overlay, YMOverlayButtonSpec *spec) {
+ YTQTMButton *button;
+ UIColor *tint = spec.tintColor ?: [UIColor whiteColor];
+
+ if (spec.title.length > 0) {
+ // Text button: a label instead of an icon. customTitleColor is YTQTMButton's
+ // own text-colour channel; sizeWithPaddingAndInsets is disabled so the width
+ // stays fixed rather than expanding to fit the text.
+ button = [%c(YTQTMButton) textButton];
+ [button setTitle:spec.title forState:UIControlStateNormal];
+ button.customTitleColor = tint;
+ button.titleLabel.font = YMOverlayTextButtonFont();
+ button.titleLabel.textAlignment = NSTextAlignmentCenter;
+ button.sizeWithPaddingAndInsets = NO;
+ } else {
+ UIImageSymbolConfiguration *config = [UIImageSymbolConfiguration configurationWithPointSize:20 weight:UIImageSymbolWeightMedium];
+ // Template rendering so YTQTMButton's tint colours the glyph reliably.
+ UIImage *icon = [[UIImage systemImageNamed:spec.symbolName withConfiguration:config] imageWithRenderingMode:UIImageRenderingModeAlwaysTemplate];
+ button = [%c(YTQTMButton) iconButton];
+ [button setImage:icon forState:UIControlStateNormal];
+ button.tintColor = tint;
+ }
+
+ button.exclusiveTouch = YES;
+ button.tag = spec.viewTag;
+ // The row's frame is assigned authoritatively in layoutSubviews.
+ button.frame = CGRectMake(0, 0, YMOverlayButtonSize, YMOverlayButtonSize);
+ [button addTarget:overlay action:@selector(ymOverlayButtonTapped:) forControlEvents:UIControlEventTouchUpInside];
+ [overlay addSubview:button];
+ return button;
+}
+
+#pragma mark - YTMainAppControlsOverlayView Hook
+
+%hook YTMainAppControlsOverlayView
+
+- (void)layoutSubviews {
+ %orig;
+ NSArray *specs = YMRegisteredOverlayButtons();
+ if (specs.count == 0) return;
+
+ YTPlayerViewController *player = YMPlayerVCFromOverlay(self);
+
+ // layoutSubviews is the high-frequency path and may (re)create buttons, so it
+ // owns the hidden state — otherwise a freshly created button shows on top of a
+ // faded-out overlay until the next setOverlayVisible: call.
+ BOOL overlayVisible = self.isOverlayVisible;
+
+ // Anchor the row to the gear: right-most button under the gear's centre-x, row top
+ // just below the gear's bottom edge; grow leftward. Deriving Y from the gear (rather
+ // than a fixed inset) keeps the row the same distance below the gear on every device —
+ // YouTube's gear sits at a different Y on iPhone vs iPad. Fall back to the screen edge
+ // and the fixed top inset when the gear can't be located, so buttons never collapse to
+ // x=0 or y=0. trailingCenterX is the center of the button placed in the previous
+ // iteration; each button steps left by half of both widths plus the gap, so text
+ // buttons (which may be wider than icon buttons) still pack without overlap.
+ CGRect gearFrame = YMGearFrameInOverlay(self);
+ BOOL hasGear = !CGRectIsNull(gearFrame);
+ CGFloat trailingCenterX = hasGear ? CGRectGetMidX(gearFrame) : self.bounds.size.width - YMOverlayButtonEdgePadding - YMOverlayButtonSize / 2.0;
+ CGFloat rowTop = hasGear ? CGRectGetMaxY(gearFrame) + YMOverlayButtonGearGap : YMOverlayButtonTopInset;
+ CGFloat prevHalfWidth = 0;
+
+ for (YMOverlayButtonSpec *spec in specs) {
+ BOOL visible = (spec.isVisible == nil) || spec.isVisible(player);
+ YTQTMButton *btn = (YTQTMButton *)[self viewWithTag:spec.viewTag];
+
+ if (!visible) {
+ if (btn) [btn removeFromSuperview];
+ continue;
+ }
+ if (!btn) btn = YMCreateOverlayButton(self, spec);
+
+ btn.hidden = !overlayVisible;
+ if (spec.tintProvider) {
+ // Text buttons colour their label through customTitleColor; icon buttons
+ // through tintColor. Route the dynamic colour to the right channel.
+ UIColor *dynamic = spec.tintProvider(player);
+ if (spec.title.length > 0) btn.customTitleColor = dynamic;
+ else btn.tintColor = dynamic;
+ }
+
+ CGFloat width = (spec.title.length > 0) ? YMOverlayTextButtonWidth : YMOverlayButtonSize;
+ CGFloat centerX = (prevHalfWidth == 0)
+ ? trailingCenterX
+ : trailingCenterX - prevHalfWidth - YMOverlayButtonGap - width / 2.0;
+
+ btn.frame = CGRectMake(centerX - width / 2.0,
+ rowTop,
+ width,
+ YMOverlayButtonSize);
+ trailingCenterX = centerX;
+ prevHalfWidth = width / 2.0;
+ }
+}
+
+- (void)setOverlayVisible:(BOOL)visible {
+ %orig;
+ for (YMOverlayButtonSpec *spec in YMRegisteredOverlayButtons()) {
+ YTQTMButton *btn = (YTQTMButton *)[self viewWithTag:spec.viewTag];
+ if (btn) btn.hidden = !visible;
+ }
+}
+
+%new
+- (void)ymOverlayButtonTapped:(YTQTMButton *)sender {
+ YMOverlayButtonSpec *matched = nil;
+ for (YMOverlayButtonSpec *spec in YMRegisteredOverlayButtons()) {
+ if (spec.viewTag == sender.tag) { matched = spec; break; }
+ }
+ if (!matched || !matched.onTap) return;
+
+ YTPlayerViewController *player = YMPlayerVCFromOverlay(self);
+ matched.onTap(player, sender);
+}
+
+%end
+
+static void YouModShowShareNotification(NSString *message, BOOL success) {
+ UIView *parent = sbGetNotificationParent();
+ if (success) {
+ [SBSkipNotificationView showSuccessInView:parent message:message duration:3.0];
+ } else {
+ [SBSkipNotificationView showErrorInView:parent message:message duration:4.0];
+ }
+}
+
+static UIImage *YouModIconImage(NSInteger iconType) {
+ YTIIcon *icon = [%c(YTIIcon) new];
+ icon.iconType = iconType;
+ UIImage *image = [icon iconImageWithColor:[UIColor labelColor]];
+ return [image imageWithRenderingMode:UIImageRenderingModeAlwaysTemplate];
+}
+
+%hook YTPlayerViewController
+%new
+- (void)YouModShareButton:(UIView *)sourceView {
+ if (!self.currentVideoID) {
+ YouModShowShareNotification(LOC(@"ERROR_VIDEOID"), NO);
+ return;
+ } else if (self.isPlayingAd) {
+ YouModShowShareNotification(LOC(@"ERROR_ADS"), NO);
+ return;
+ }
+
+ NSString *videoURL = [NSString stringWithFormat:@"https://youtube.com/watch?v=%@", self.currentVideoID];
+ NSInteger seconds = (NSInteger)floor(self.currentVideoMediaTime);
+ NSString *timestampURL = [NSString stringWithFormat:@"%@&t=%lds", videoURL, (long)seconds];
+
+ UIViewController *presenter = (UIViewController *)[self activeVideoPlayerOverlay];
+ YTDefaultSheetController *sheet = [%c(YTDefaultSheetController) sheetControllerWithParentResponder:presenter];
+
+ YTActionSheetAction *copyURL = [%c(YTActionSheetAction) actionWithTitle:LOC(@"COPY_URL") iconImage:YouModIconImage(250) style:0 handler:^(__unused YTActionSheetAction *action) {
+ UIPasteboard.generalPasteboard.string = videoURL;
+ YouModShowShareNotification(LOC(@"URL_COPIED"), YES);
+ }];
+
+ YTActionSheetAction *copyTimestamp = [%c(YTActionSheetAction) actionWithTitle:LOC(@"COPY_URL_TIMESTAMP") iconImage:YouModIconImage(250) style:0 handler:^(__unused YTActionSheetAction *action) {
+ UIPasteboard.generalPasteboard.string = timestampURL;
+ YouModShowShareNotification(LOC(@"URL_TIMESTAMP_COPIED"), YES);
+ }];
+
+ [sheet addAction:copyURL];
+ [sheet addAction:copyTimestamp];
+
+ [sheet presentFromView:sourceView animated:YES completion:nil];
+}
+%new
+- (void)YouModLoopButton {
+ YTMainAppVideoPlayerOverlayViewController *playerOverlay = self.activeVideoPlayerOverlay;
+ YTAutoplayAutonavController *autoplayController = [playerOverlay valueForKey:@"_autonavController"];
+ BOOL isLoopEnabled = !IS_ENABLED(KeepLoopKey);
+ [[NSUserDefaults standardUserDefaults] setBool:isLoopEnabled forKey:KeepLoopKey];
+ [autoplayController setLoopMode:isLoopEnabled ? 2 : 0];
+ [[%c(GOOHUDManagerInternal) sharedInstance] showMessageMainThread:[%c(YTHUDMessage) messageWithText:LOC(isLoopEnabled ? @"LOOP_ENABLED" : @"LOOP_DISABLED")]];
+}
+%end
+
+%hook YTAutoplayAutonavController
+- (id)initWithParentResponder:(id)arg {
+ self = %orig;
+ if (self && IS_ENABLED(KeepLoopKey)) {
+ [self setLoopMode:2];
+ }
+ return self;
+}
+- (void)setLoopMode:(NSInteger)arg {
+ NSInteger set = IS_ENABLED(KeepLoopKey) ? 2 : arg;
+ %orig(set);
+}
+%end
+
+%ctor {
+ YMOverlayButtonSpec *mute = [[YMOverlayButtonSpec alloc] init];
+ mute.identifier = @"mute.video";
+ mute.symbolName = IS_ENABLED(KeepMutedKey) ? @"speaker.slash" : @"speaker.wave.2";
+ mute.tintColor = [UIColor whiteColor];
+ mute.sortOrder = 300;
+ mute.isVisible = ^BOOL(YTPlayerViewController *player) {
+ return IS_ENABLED(MuteButton);
+ };
+ mute.onTap = ^(YTPlayerViewController *player, YTQTMButton *button) {
+ YTSingleVideoController *sgvid = player.activeVideo;
+ BOOL muteStatus = ![sgvid isMuted];
+ [[NSUserDefaults standardUserDefaults] setBool:muteStatus forKey:KeepMutedKey];
+ [sgvid setMuted:muteStatus];
+ UIImageSymbolConfiguration *config = [UIImageSymbolConfiguration configurationWithPointSize:20 weight:UIImageSymbolWeightMedium];
+ UIImage *newIcon = [UIImage systemImageNamed:muteStatus ? @"speaker.slash" : @"speaker.wave.2" withConfiguration:config];
+ [button setImage:newIcon forState:UIControlStateNormal];
+ };
+ YMRegisterOverlayButton(mute);
+ YMOverlayButtonSpec *speed = [[YMOverlayButtonSpec alloc] init];
+ speed.identifier = @"speed.video";
+ speed.symbolName = @"speedometer";
+ speed.tintColor = [UIColor whiteColor];
+ speed.sortOrder = 400;
+ speed.isVisible = ^BOOL(YTPlayerViewController *player) {
+ return IS_ENABLED(SpeedButton);
+ };
+ speed.onTap = ^(YTPlayerViewController *player, YTQTMButton *button) {
+ YTMainAppVideoPlayerOverlayViewController *ovcon = [player activeVideoPlayerOverlay];
+ [ovcon didPressVarispeed:button];
+ };
+ YMRegisterOverlayButton(speed);
+ YMOverlayButtonSpec *share = [[YMOverlayButtonSpec alloc] init];
+ share.identifier = @"share.video";
+ share.symbolName = @"arrowshape.turn.up.right";
+ share.tintColor = [UIColor whiteColor];
+ share.sortOrder = 500;
+ share.isVisible = ^BOOL(YTPlayerViewController *player) {
+ return IS_ENABLED(ShareButton);
+ };
+ share.onTap = ^(YTPlayerViewController *player, YTQTMButton *button) {
+ [player YouModShareButton:button];
+ };
+ YMRegisterOverlayButton(share);
+ YMOverlayButtonSpec *loop = [[YMOverlayButtonSpec alloc] init];
+ loop.identifier = @"loop.video";
+ loop.symbolName = IS_ENABLED(KeepLoopKey) ? @"repeat.1" : @"repeat";
+ loop.tintColor = [UIColor whiteColor];
+ loop.sortOrder = 600;
+ loop.isVisible = ^BOOL(YTPlayerViewController *player) {
+ return IS_ENABLED(LoopButton);
+ };
+ loop.onTap = ^(YTPlayerViewController *player, YTQTMButton *button) {
+ [player YouModLoopButton];
+ UIImageSymbolConfiguration *config = [UIImageSymbolConfiguration configurationWithPointSize:20 weight:UIImageSymbolWeightMedium];
+ UIImage *newIcon = [UIImage systemImageNamed:IS_ENABLED(KeepLoopKey) ? @"repeat.1" : @"repeat" withConfiguration:config];
+ [button setImage:newIcon forState:UIControlStateNormal];
+ };
+ YMRegisterOverlayButton(loop);
+ YMOverlayButtonSpec *caption = [[YMOverlayButtonSpec alloc] init];
+ caption.identifier = @"caption.video";
+ caption.symbolName = @"captions.bubble";
+ caption.tintColor = [UIColor whiteColor];
+ caption.sortOrder = 700;
+ caption.isVisible = ^BOOL(YTPlayerViewController *player) {
+ return IS_ENABLED(CaptionButton);
+ };
+ caption.onTap = ^(YTPlayerViewController *player, YTQTMButton *button) {
+ YTMainAppVideoPlayerOverlayViewController *c = [player activeVideoPlayerOverlay];
+ YTFormat3CaptionViewController *cvc = [c valueForKey:@"_captionOverlayViewController"];
+ showTranscript(cvc);
+ };
+ YMRegisterOverlayButton(caption);
+ %init;
+}
\ No newline at end of file
diff --git a/Files/Player.x b/Files/Player.x
index 63418794..ba157d53 100644
--- a/Files/Player.x
+++ b/Files/Player.x
@@ -1,53 +1,395 @@
#import "Headers.h"
+#define TweakName @"YouMod"
+
+static NSBundle *YouModBundle() {
+ static NSBundle *bundle = nil;
+ static dispatch_once_t onceToken;
+ dispatch_once(&onceToken, ^{
+ NSString *tweakBundlePath = [[NSBundle mainBundle] pathForResource:TweakName ofType:@"bundle"];
+ if (tweakBundlePath)
+ bundle = [NSBundle bundleWithPath:tweakBundlePath];
+ else
+ bundle = [NSBundle bundleWithPath:[NSString stringWithFormat:PS_ROOT_PATH_NS(@"/Library/Application Support/%@.bundle"), TweakName]];
+ });
+ return bundle;
+}
+
+#define LOC(x) [YouModBundle() localizedStringForKey:x value:nil table:nil]
+
+static BOOL isWiFiConnected() {
+ struct sockaddr_in zeroAddress;
+ bzero(&zeroAddress, sizeof(zeroAddress));
+ zeroAddress.sin_len = sizeof(zeroAddress);
+ zeroAddress.sin_family = AF_INET;
+
+ SCNetworkReachabilityRef reachability = SCNetworkReachabilityCreateWithAddress(kCFAllocatorDefault, (const struct sockaddr *)&zeroAddress);
+ if (!reachability) return NO;
+
+ SCNetworkReachabilityFlags flags;
+ BOOL retrievedFlags = SCNetworkReachabilityGetFlags(reachability, &flags);
+ CFRelease(reachability);
+
+ if (!retrievedFlags) return NO;
+
+ BOOL isReachable = (flags & kSCNetworkReachabilityFlagsReachable) != 0;
+ BOOL needsConnection = (flags & kSCNetworkReachabilityFlagsConnectionRequired) != 0;
+ BOOL canConnect = isReachable && !needsConnection;
+
+ if (!canConnect) return NO;
+
+ BOOL isCellular = (flags & kSCNetworkReachabilityFlagsIsWWAN) != 0;
+ return !isCellular;
+}
+
extern void YouModDownloadSetCurrentPlayer(YTPlayerViewController *player);
+extern YTPlayerViewController *YouModDownloadGetCurrentPlayer(void);
+
+#pragma mark - Rewind / Fast-forward on iOS media controls
+
+// The user-chosen skip amount for each direction, in seconds. Zero means the
+// preference was never set, in which case the seek falls back to 10 seconds.
+static CGFloat YouModRewindSecondsValue(void) {
+ CGFloat s = FLOAT_FOR_KEY(RewindSeconds);
+ return s > 0 ? s : 10.0;
+}
+
+static CGFloat YouModForwardSecondsValue(void) {
+ CGFloat s = FLOAT_FOR_KEY(ForwardSeconds);
+ return s > 0 ? s : 10.0;
+}
+
+// Seeks the active player by delta seconds, clamped to [0, duration]. Returns NO
+// when no active player is available (e.g. a media key is pressed after playback
+// ended), so the caller can report the command as having nothing to act on.
+//
+// The seek runs on the main queue because MPRemoteCommand handlers may be invoked
+// off the main thread (notably from Bluetooth and CarPlay), while seekToTime: and
+// the player's time accessors are main-thread-only.
+static BOOL YouModSeekByInterval(CGFloat delta) {
+ YTPlayerViewController *player = YouModDownloadGetCurrentPlayer();
+ if (!player || ![player respondsToSelector:@selector(seekToTime:)]) return NO;
+ dispatch_async(dispatch_get_main_queue(), ^{
+ CGFloat cur = [player currentVideoMediaTime];
+ CGFloat dur = [player currentVideoTotalMediaTime];
+ CGFloat target = cur + delta;
+ if (target < 0) target = 0;
+ if (dur > 0 && target > dur) target = dur;
+ [player seekToTime:target];
+ });
+ return YES;
+}
+
+// Retained handler tokens for the two skip commands. A non-nil token marks a
+// command whose handler is already installed, so it is installed only once.
+static id gYouModRewindTarget = nil;
+static id gYouModForwardTarget = nil;
+
+// Points the system now-playing skip controls (lock screen, Bluetooth, Control
+// Center, CarPlay) at our per-direction seek. When enabled, the OS previous/next
+// commands are turned off and skip-backward/forward take over with the user's
+// intervals; when disabled, previous/next are restored. Only these system
+// controls are configurable — the on-screen player rewind/fast-forward buttons
+// are rendered by YouTube with an amount it owns, so they are left alone.
+//
+// The handlers are installed once and thereafter only their enabled state and
+// preferred intervals are updated. Because the handlers read the seconds at press
+// time, a changed preference always seeks by the new amount immediately; the
+// interval shown on the OS controls reflects the value captured the last time this
+// ran (video change or launch).
+static void YouModConfigureRemoteSkipCommands(void) {
+ MPRemoteCommandCenter *cc = [MPRemoteCommandCenter sharedCommandCenter];
+ BOOL back = IS_ENABLED(SkipBackwardEnabled);
+ BOOL fwd = IS_ENABLED(SkipForwardEnabled);
+
+ // Each direction is independent: the previous/next track command is remapped
+ // to a skip only on the side the user enabled, so a mixed state (e.g. rewind
+ // on, fast-forward off) shows a skip-back control alongside the stock next
+ // track button.
+ cc.skipBackwardCommand.enabled = back;
+ cc.skipBackwardCommand.preferredIntervals = @[@(YouModRewindSecondsValue())];
+ cc.skipForwardCommand.enabled = fwd;
+ cc.skipForwardCommand.preferredIntervals = @[@(YouModForwardSecondsValue())];
+ cc.previousTrackCommand.enabled = !back;
+ cc.nextTrackCommand.enabled = !fwd;
+
+ if (!gYouModRewindTarget) {
+ gYouModRewindTarget = [cc.skipBackwardCommand addTargetWithHandler:^MPRemoteCommandHandlerStatus(MPRemoteCommandEvent *event) {
+ return YouModSeekByInterval(-YouModRewindSecondsValue()) ? MPRemoteCommandHandlerStatusSuccess : MPRemoteCommandHandlerStatusNoSuchContent;
+ }];
+ }
+ if (!gYouModForwardTarget) {
+ gYouModForwardTarget = [cc.skipForwardCommand addTargetWithHandler:^MPRemoteCommandHandlerStatus(MPRemoteCommandEvent *event) {
+ return YouModSeekByInterval(YouModForwardSecondsValue()) ? MPRemoteCommandHandlerStatusSuccess : MPRemoteCommandHandlerStatusNoSuchContent;
+ }];
+ }
+}
+
+// Audio track list
+static NSArray *getAllSystemLanguageTitles() {
+ NSMutableArray *titles = [NSMutableArray array];
+ NSArray *allLocales = [%c(YTLanguages) languageList];
+ NSMutableSet *seenLanguages = [NSMutableSet set];
+ NSLocale *currentLocale = [NSLocale currentLocale];
+
+ for (NSString *localeId in allLocales) {
+ NSDictionary *components = [NSLocale componentsFromLocaleIdentifier:localeId];
+ NSString *langCode = components[NSLocaleLanguageCode];
+
+ if (langCode && ![seenLanguages containsObject:langCode]) {
+ [seenLanguages addObject:langCode];
+ NSString *displayName = [currentLocale localizedStringForLocaleIdentifier:langCode];
+ if (displayName) [titles addObject:displayName];
+ }
+ }
+ return [titles sortedArrayUsingSelector:@selector(localizedCaseInsensitiveCompare:)];
+}
+
+static NSArray *getAllSystemLanguageValues() {
+ NSArray *sortedTitles = getAllSystemLanguageTitles();
+ NSMutableArray *sortedCodes = [NSMutableArray array];
+ NSArray *allLocales = [%c(YTLanguages) languageList];
+ NSLocale *currentLocale = [NSLocale currentLocale];
+
+ NSMutableDictionary *titleToCodeMap = [NSMutableDictionary dictionary];
+ for (NSString *localeId in allLocales) {
+ NSDictionary *components = [NSLocale componentsFromLocaleIdentifier:localeId];
+ NSString *langCode = components[NSLocaleLanguageCode];
+ if (langCode) {
+ NSString *displayName = [currentLocale localizedStringForLocaleIdentifier:langCode];
+ if (displayName) titleToCodeMap[displayName] = langCode;
+ }
+ }
+
+ for (NSString *title in sortedTitles) {
+ [sortedCodes addObject:titleToCodeMap[title] ? titleToCodeMap[title] : @"en"];
+ }
+ return [sortedCodes copy];
+}
-float playbackRate = 1.0;
+static float playbackRate = 1.0;
-/*
static void YouModAddEndTime(YTPlayerViewController *self, YTSingleVideoController *video, YTSingleVideoTime *time) {
- if (!IS_ENABLED(ShowExtraTimeRemaining)) return;
+ if (!IS_ENABLED(ShowExtraTimeRemaining) && !IS_ENABLED(SBShowDuration)) return;
CGFloat rate = playbackRate != 0 ? playbackRate : 1.0;
- NSTimeInterval remainingTime = (lround(video.totalMediaTime) - lround(time.time)) / rate;
-
- NSDate *estimatedEndTime = [NSDate dateWithTimeIntervalSinceNow:remainingTime];
+ NSTimeInterval remainingSeconds = (lround(video.totalMediaTime) - lround(time.time)) / rate;
+
+ NSString *remainingTimeText;
+ NSString *SBTimeRemaining = nil;
+ NSTimeInterval SBTotalTimeRemaining = 0.0;
+ if (IS_ENABLED(SBShowDuration)) {
+ if (self.sbSegments && self.sbSegments.count > 0) {
+ for (SBSegment *segment in self.sbSegments) {
+ SBSegmentAction action = [segment configuredAction];
+ if (action == SBSegmentActionDisable) continue;
+
+ CGFloat timeValue = segment.endTime - segment.startTime;
+ SBTotalTimeRemaining = SBTotalTimeRemaining + timeValue;
+ }
+ if (SBTotalTimeRemaining != 0.0) {
+ NSTimeInterval SBRemaining = video.totalMediaTime - SBTotalTimeRemaining;
+ int hours = (int)(SBRemaining / 3600);
+ int minutes = (int)(((int)SBRemaining % 3600) / 60);
+ int seconds = (int)((int)SBRemaining % 60);
+ if (hours > 0) {
+ SBTimeRemaining = [NSString stringWithFormat:@"%d:%02d:%02d", hours, minutes, seconds];
+ } else {
+ SBTimeRemaining = [NSString stringWithFormat:@"%d:%02d", minutes, seconds];
+ }
+ }
+ }
+ }
+ if (IS_ENABLED(ShowExtraTimeRemaining)) {
+ NSDate *estimatedEndTime = [NSDate dateWithTimeIntervalSinceNow:remainingSeconds];
- NSDateFormatter *dateFormatter = [[NSDateFormatter alloc] init];
- [dateFormatter setLocale:[[NSLocale alloc] initWithLocaleIdentifier:@"en_US_POSIX"]];
- [dateFormatter setDateFormat:@"HH:mm"];
- // [dateFormatter setDateFormat:ytlBool(@"24hrFormat") ? @"HH:mm" : @"h:mm a"];
+ NSDateFormatter *dateFormatter = [[NSDateFormatter alloc] init];
+ [dateFormatter setLocale:[[NSLocale alloc] initWithLocaleIdentifier:@"en_US_POSIX"]];
+ [dateFormatter setDateFormat:IS_ENABLED(Uses24HoursTime) ? @"HH:mm" : @"h:mm a"];
- NSString *formattedEndTime = [dateFormatter stringFromDate:estimatedEndTime];
+ remainingTimeText = [dateFormatter stringFromDate:estimatedEndTime];
+ }
+ NSString *safeRemainingTimeText = remainingTimeText ?: @"";
+ NSString *safeSBTimeRemaining = SBTimeRemaining ?: @"";
- YTPlayerView *playerView = (YTPlayerView *)self.view;
+ YTPlayerView *playerView = (YTPlayerView *)self.playerView;
if (![playerView.overlayView isKindOfClass:%c(YTMainAppVideoPlayerOverlayView)]) return;
YTMainAppVideoPlayerOverlayView *overlay = (YTMainAppVideoPlayerOverlayView*)playerView.overlayView;
YTLabel *durationLabel = overlay.playerBar.durationLabel;
- overlay.playerBar.endTimeString = formattedEndTime;
-
- if (![durationLabel.text containsString:formattedEndTime]) {
- durationLabel.text = [durationLabel.text stringByAppendingString:[NSString stringWithFormat:@" • %@", formattedEndTime]];
+
+ NSString *labelText = durationLabel.text ?: @"";
+
+ BOOL needsExtraUpdate = IS_ENABLED(ShowExtraTimeRemaining) && safeRemainingTimeText.length > 0 && ![labelText containsString:safeRemainingTimeText];
+ BOOL needsSBUpdate = IS_ENABLED(SBShowDuration) && SBTimeRemaining != nil && ![labelText containsString:safeSBTimeRemaining];
+
+ if (needsExtraUpdate || needsSBUpdate) {
+ if (IS_ENABLED(SBShowDuration) && SBTimeRemaining != nil && IS_ENABLED(ShowExtraTimeRemaining)) {
+ durationLabel.text = [labelText stringByAppendingString:[NSString stringWithFormat:@" (%@) • %@", safeSBTimeRemaining, safeRemainingTimeText]];
+ } else if (IS_ENABLED(ShowExtraTimeRemaining)) {
+ durationLabel.text = [labelText stringByAppendingString:[NSString stringWithFormat:@" • %@", safeRemainingTimeText]];
+ } else if (IS_ENABLED(SBShowDuration) && SBTimeRemaining != nil) {
+ durationLabel.text = [labelText stringByAppendingString:[NSString stringWithFormat:@" (%@)", safeSBTimeRemaining]];
+ }
+ overlay.playerBar.endTimeString = durationLabel.text;
[durationLabel sizeToFit];
}
}
-*/
+
+%hook YTInlinePlayerBarContainerView
+%property (nonatomic, strong) NSString *endTimeString;
+- (void)layoutSubviews {
+ %orig;
+ if (!IS_ENABLED(TapToSeek)) return;
+ for (UIView *subview in self.subviews) {
+ if ([subview isKindOfClass:%c(YTInlineScrubGestureView)]) {
+ BOOL hasCustomTap = NO;
+ for (UIGestureRecognizer *gesture in subview.gestureRecognizers) {
+ if ([gesture isKindOfClass:[UITapGestureRecognizer class]] &&
+ [gesture.name isEqualToString:@"YouModTapToSeek"]) {
+ hasCustomTap = YES;
+ break;
+ }
+ }
+ if (!hasCustomTap) {
+ UITapGestureRecognizer *tap = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(handleYouModScrubTap:)];
+ tap.name = @"YouModTapToSeek";
+ [subview addGestureRecognizer:tap];
+ }
+ break;
+ }
+ }
+}
+%new
+- (void)handleYouModScrubTap:(UITapGestureRecognizer *)gesture {
+ if (gesture.state == UIGestureRecognizerStateEnded) {
+ UIView *gestureView = gesture.view;
+ UIView *progressBar;
+
+ for (UIView *subview in self.subviews) {
+ if ([subview isKindOfClass:%c(YTModularPlayerBarView)]) {
+ progressBar = subview;
+ break;
+ }
+ }
+ if (!progressBar) return;
+
+ UIWindow *keyWindow = nil;
+ for (UIWindowScene *scene in [UIApplication sharedApplication].connectedScenes) {
+ if (scene.activationState == UISceneActivationStateForegroundActive) {
+ for (UIWindow *window in scene.windows) {
+ if (window.isKeyWindow) {
+ keyWindow = window;
+ break;
+ }
+ }
+ }
+ if (keyWindow) break;
+ }
+
+ CGPoint touchPointInWindow = [gesture locationInView:keyWindow];
+ CGFloat barStartX = 0.0;
+ CGFloat barWidth = gestureView.bounds.size.width;
+
+ if (progressBar) {
+ CGRect barFrameInWindow = [progressBar convertRect:progressBar.bounds toView:keyWindow];
+ barStartX = barFrameInWindow.origin.x;
+ barWidth = barFrameInWindow.size.width;
+ }
+
+ if (barWidth > 0) {
+ CGFloat relativeX = touchPointInWindow.x - barStartX;
+ CGFloat percentage = relativeX / barWidth;
+ CGFloat snapThreshold = 8.0;
+
+ if (relativeX <= snapThreshold) {
+ percentage = 0.0;
+ } else if (relativeX >= barWidth - snapThreshold) {
+ percentage = 1.0;
+ } else {
+ if (percentage < 0.0) percentage = 0.0;
+ if (percentage > 1.0) percentage = 1.0;
+ }
+
+ UIResponder *responder = self.nextResponder;
+ while (responder && ![responder isKindOfClass:%c(YTMainAppVideoPlayerOverlayViewController)]) {
+ responder = responder.nextResponder;
+ }
+
+ if (responder) {
+ YTMainAppVideoPlayerOverlayViewController *controller = (YTMainAppVideoPlayerOverlayViewController *)responder;
+ YTPlayerViewController *controller2 = controller.parentViewController;
+ CGFloat totalDuration = [controller2 currentVideoTotalMediaTime];
+ CGFloat targetTime = totalDuration * percentage;
+ [controller2 seekToTime:targetTime];
+ }
+ }
+ }
+}
+// Disable toggle time remaining - @bhackel
+- (void)setShouldDisplayTimeRemaining:(BOOL)arg1 {
+ BOOL temp;
+ if (IS_ENABLED(DisablesShowRemaining)) {
+ temp = NO;
+ } else if (IS_ENABLED(AlwaysShowRemaining)) {
+ temp = YES;
+ } else {
+ temp = arg1;
+ }
+ %orig(temp);
+}
+// Always show seekbar
+- (void)setPlayerBarAlpha:(CGFloat)alpha {
+ CGFloat temp = IS_ENABLED(AlwaysShowSeekbar) ? 1.0 : alpha;
+ %orig(temp);
+}
+// Disables snap to chapter
+- (void)inlinePlayerBarView:(id)arg1 didScrubToChapteredTime:(CGFloat)arg2 shouldSnap:(BOOL)arg3 {
+ BOOL temp = IS_ENABLED(DontSnapToChapter) ? NO : arg3;
+ %orig(arg1, arg2, temp);
+}
+- (void)setPeekableViewVisible:(BOOL)visible {
+ %orig;
+ if (!IS_ENABLED(ShowExtraTimeRemaining) && !IS_ENABLED(SBShowDuration)) return;
+
+ YTLabel *dLabel = self.durationLabel;
+ if (dLabel && self.endTimeString) {
+ if (![dLabel.text isEqualToString:self.endTimeString]) {
+ dLabel.text = self.endTimeString;
+ [dLabel sizeToFit];
+ }
+ }
+}
+%end
%hook YTMainAppControlsOverlayView
// Hide autoplay Switch
- (void)setAutoplaySwitchButtonRenderer:(id)arg1 { if (!IS_ENABLED(HideAutoPlayToggle)) %orig; }
// Hide captions Button
- (void)setClosedCaptionsOrSubtitlesButtonAvailable:(BOOL)arg1 { if (!IS_ENABLED(HideCaptionsButton)) %orig; }
-- (void)setPreviousButtonHidden:(BOOL)arg { IS_ENABLED(HidePrevButton) ? %orig(YES) : %orig; }
-- (void)setNextButtonHidden:(BOOL)arg { IS_ENABLED(HideNextButton) ? %orig(YES) : %orig; }
// Hide video title in full screen
- (BOOL)titleViewHidden { return IS_ENABLED(HideFullvidTitle) ? YES : %orig; }
+// Pause On Overlay
+- (void)setOverlayVisible:(BOOL)visible {
+ %orig;
+ if (!IS_ENABLED(PauseOnOverlay)) return;
+ YTMainAppVideoPlayerOverlayViewController *mainOverlayController = (YTMainAppVideoPlayerOverlayViewController *)self.eventsDelegate;
+ YTPlayerViewController *playerViewController = mainOverlayController.parentViewController;
+ visible ? [playerViewController pause] : [playerViewController play];
+}
%end
%hook YTAutonavEndscreenController
- (void)showEndscreen { if (!IS_ENABLED(HideSuggestedVideo)) %orig; }
-- (void)showEndscreenControlsInPlayerBar:(BOOL)arg { IS_ENABLED(HideSuggestedVideo) ? %orig(NO) : %orig; }
+- (void)showEndscreenControlsInPlayerBar:(BOOL)arg {
+ BOOL temp = IS_ENABLED(HideSuggestedVideo) ? NO : arg;
+ %orig(temp);
+}
+%end
+
+// YTAnotherMiniplayer (https://github.com/PoomSmart/YTAnotherMiniplayer)
+%hook YTWatchMiniplayerConstants
++ (NSInteger)miniplayerVariant { return IS_ENABLED(UseAnotherMiniplayer) ? 2 : %orig; }
%end
%hook YTSettings
@@ -58,38 +400,25 @@ static void YouModAddEndTime(YTPlayerViewController *self, YTSingleVideoControll
- (BOOL)isAutoplayEnabled { return IS_ENABLED(HideAutoPlayToggle) ? NO : %orig; }
%end
-/* idk what is this thing does
%hook YTColdConfig
-- (BOOL)isLandscapeEngagementPanelEnabled {
- return NO;
-}
-%end
-*/
-
-/*
-%hook YTHeaderView
-- (BOOL)stickyNavHeaderEnabled { return IS_ENABLED(YTPremiumLogo) ? YES : NO; } // idk what is this does, the nav is already sticky... Or this thing only happens in iPhone?
-- (void)setStickyNavHeaderEnabled:(BOOL)arg { IS_ENABLED(YTPremiumLogo) ? %orig(YES) : %orig(NO); }
-%end
-*/
-
-// Remove Dark Background in Overlay
-%hook YTMainAppVideoPlayerOverlayView
-- (void)setBackgroundVisible:(BOOL)arg1 isGradientBackground:(BOOL)arg2 { IS_ENABLED(RemoveDarkOverlay) ? %orig(NO, arg2) : %orig; }
-// Hide Watermarks
-- (BOOL)isWatermarkEnabled { return IS_ENABLED(HideWaterMark) ? NO : %orig; }
-- (void)setWatermarkEnabled:(BOOL)arg { IS_ENABLED(HideWaterMark) ? %orig(NO) : %orig; }
-- (void)layoutSubviews {
- %orig;
- if (IS_ENABLED(HideCastButtonPlayer)) self.playbackRouteButton.hidden = YES;
-}
-- (BOOL)isFullscreenActionsVisible { return IS_ENABLED(HideFullAction) ? NO : %orig; }
+- (BOOL)isLandscapeEngagementPanelEnabled { return IS_ENABLED(DisablesEngagementPanel) ? NO : %orig; }
+- (BOOL)removeNextPaddleForAllVideos { return IS_ENABLED(HideNextAndPrevButtons) ? YES : %orig; }
+- (BOOL)removePreviousPaddleForAllVideos { return IS_ENABLED(HideNextAndPrevButtons) ? YES : %orig; }
+// Replace previous/next buttons with back and forward
+- (BOOL)replaceNextPaddleWithFastForwardButtonForSingletonVods { return IS_ENABLED(ReplacePrevNextButtons) ? YES : %orig; }
+- (BOOL)replacePreviousPaddleWithRewindButtonForSingletonVods { return IS_ENABLED(ReplacePrevNextButtons) ? YES : %orig; }
%end
// No Endscreen Cards
%hook YTCreatorEndscreenView
-- (void)setHidden:(BOOL)arg1 { IS_ENABLED(HideEndScreenCards) ? %orig(YES) : %orig; }
-- (void)setHoverCardHidden:(BOOL)arg { IS_ENABLED(HideEndScreenCards) ? %orig(YES) : %orig; }
+- (void)setHidden:(BOOL)arg1 {
+ BOOL temp = IS_ENABLED(HideEndScreenCards) ? YES : arg1;
+ %orig(temp);
+}
+- (void)setHoverCardHidden:(BOOL)arg {
+ BOOL temp = IS_ENABLED(HideEndScreenCards) ? YES : arg;
+ %orig(temp);
+}
- (void)setHoverCardRenderer:(id)arg { if (!IS_ENABLED(HideEndScreenCards)) %orig; }
%end
@@ -98,26 +427,28 @@ static void YouModAddEndTime(YTPlayerViewController *self, YTSingleVideoControll
- (BOOL)allowDoubleTapToSeekGestureRecognizer { return IS_ENABLED(DisablesDoubleTap) ? NO : %orig; }
// Disable long hold
- (BOOL)allowLongPressGestureRecognizerInView:(id)arg { return IS_ENABLED(DisablesLongHold) ? NO : %orig; }
-%end
-
-// YTNoPaidPromo (https://github.com/PoomSmart/YTNoPaidPromo)
-%group PaidPromoOverlay
-%hook YTMainAppVideoPlayerOverlayViewController
-- (void)setPaidContentWithPlayerData:(id)data {}
-- (void)playerOverlayProvider:(YTPlayerOverlayProvider *)provider didInsertPlayerOverlay:(YTPlayerOverlay *)overlay {
- if ([[overlay overlayIdentifier] isEqualToString:@"player_overlay_paid_content"]) return;
+// Copy timestamp on pause
+- (void)didPressPause:(id)arg {
%orig;
+ if (!IS_ENABLED(CopyWithTimestampOnPause)) return;
+ CGFloat mediaTimeIn = self.mediaTime;
+ NSString *vidID = self.videoID;
+ if (vidID.length)
+ UIPasteboard.generalPasteboard.string = [NSString stringWithFormat:@"https://www.youtube.com/watch?v=%@&t=%lds", vidID, (long)mediaTimeIn];
}
+- (BOOL)isZoomEnabled { return IS_ENABLED(DisablesFreeZoom) ? NO : %orig; }
+- (void)setPaidContentWithPlayerData:(id)data { if (!IS_ENABLED(HidePaidPromoOverlay)) %orig; }
%end
+// YTNoPaidPromo (https://github.com/PoomSmart/YTNoPaidPromo)
%hook YTInlineMutedPlaybackPlayerOverlayViewController
-- (void)setPaidContentWithPlayerData:(id)data {}
-%end
+- (void)setPaidContentWithPlayerData:(id)data { if (!IS_ENABLED(HidePaidPromoOverlay)) %orig; }
%end
// Remove Watermarks
%hook YTAnnotationsViewController
- (void)loadFeaturedChannelWatermark { if (!IS_ENABLED(HideWaterMark)) %orig; }
+- (void)setWatermarkImage:(id)arg1 height:(unsigned long long)arg2 { if (!IS_ENABLED(HideWaterMark)) %orig; }
%end
// Exit Fullscreen on Finish
@@ -125,17 +456,6 @@ static void YouModAddEndTime(YTPlayerViewController *self, YTSingleVideoControll
- (BOOL)shouldExitFullScreenOnFinish { return IS_ENABLED(AutoExitFullScreen) ? YES : %orig; }
%end
-// Disable toggle time remaining - @bhackel
-%hook YTInlinePlayerBarContainerView
-- (void)setShouldDisplayTimeRemaining:(BOOL)arg1 {
- if (IS_ENABLED(DisablesShowRemaining)) {
- %orig(NO);
- return;
- }
- IS_ENABLED(AlwaysShowRemaining) ? %orig(YES) : %orig;
-}
-%end
-
// Always use remaining time in the video player - @bhackel
%hook YTPlayerBarController
// When a new video is played, enable time remaining flag
@@ -149,6 +469,16 @@ static void YouModAddEndTime(YTPlayerViewController *self, YTSingleVideoControll
playerBar.shouldDisplayTimeRemaining = YES;
}
}
+ YTSingleVideoController *sgvid = [self valueForKey:@"_currentSingleVideo"];
+ YTPlayerView *playerview = [sgvid valueForKey:@"_playerView"];
+ YTPlayerViewController *playerviewController = [playerview valueForKey:@"_playerViewDelegate"];
+ YouModDownloadSetCurrentPlayer(playerviewController);
+ YouModConfigureRemoteSkipCommands();
+ if (IS_ENABLED(MuteButton)) [playerviewController YouModAutoMute];
+ if (IS_ENABLED(AutoFullScreen)) [playerviewController performSelector:@selector(YouModAutoFullscreen) withObject:nil afterDelay:0.5];
+ if (INTFORVAL(CaptionTrack) != 0) [playerviewController performSelector:@selector(YouModAutoCaptions) withObject:nil afterDelay:0.5];
+ if (INTFORVAL(AutoSpeedIndex) != 0) [playerviewController performSelector:@selector(YouModSetAutoSpeed) withObject:nil afterDelay:0.5];
+ if (INTFORVAL(AudioTrack) != 0) [playerviewController performSelector:@selector(YouModAutoAudioTrack) withObject:nil afterDelay:0.5];
}
%end
@@ -166,7 +496,10 @@ static void YouModAddEndTime(YTPlayerViewController *self, YTSingleVideoControll
// Disable Autoplay
%hook YTPlaybackConfig
-- (void)setStartPlayback:(BOOL)arg1 { IS_ENABLED(StopAutoplayVideo) ? %orig(NO) : %orig; }
+- (void)setStartPlayback:(BOOL)arg1 {
+ BOOL temp = IS_ENABLED(StopAutoplayVideo) ? NO : arg1;
+ %orig(temp);
+}
%end
// Skip Content Warning (https://github.com/qnblackcat/uYouPlus/blob/main/uYouPlus.xm#L452-L454)
@@ -178,30 +511,17 @@ static void YouModAddEndTime(YTPlayerViewController *self, YTSingleVideoControll
- (void)showConfirmAlert { IS_ENABLED(HideContentWarning) ? [self confirmAlertDidPressConfirm] : %orig; }
%end
-// Always show seekbar
-%hook YTInlinePlayerBarContainerView
-- (void)setPlayerBarAlpha:(CGFloat)alpha { IS_ENABLED(AlwaysShowSeekbar) ? %orig(1.0) : %orig; }
-%end
-
// Portrait Fullscreen
%hook YTWatchViewController
- (unsigned long long)allowedFullScreenOrientations { return IS_ENABLED(PortFull) ? UIInterfaceOrientationMaskAllButUpsideDown : %orig; }
%end
-/* Disable Snap To Chapter (https://github.com/qnblackcat/uYouPlus/blob/main/uYouPlus.xm#L457-464) - GOT REMOVED
+// Disable Snap To Chapter (https://github.com/qnblackcat/uYouPlus/blob/main/uYouPlus.xm#L457-464) - GOT REMOVED
%hook YTSegmentableInlinePlayerBarView
-- (void)didMoveToWindow { %orig; if (ytlBool(@"dontSnapToChapter")) self.enableSnapToChapter = NO; }
-%end
-
-%hook YTModularPlayerBarController
-- (void)setEnableSnapToChapter:(BOOL)arg { %orig(NO); } // idk this works or not
-%end
-*/
-
-// Replace previous/next buttons with back and forward
-%hook YTColdConfig
-- (BOOL)replaceNextPaddleWithFastForwardButtonForSingletonVods { return IS_ENABLED(ReplacePrevNextButtons) ? YES : %orig; }
-- (BOOL)replacePreviousPaddleWithRewindButtonForSingletonVods { return IS_ENABLED(ReplacePrevNextButtons) ? YES : %orig; }
+- (void)didMoveToWindow {
+ %orig;
+ if (IS_ENABLED(DontSnapToChapter)) self.enableSnapToChapter = NO;
+}
%end
%group ForceMiniPlayer
@@ -258,23 +578,6 @@ static void YouModAddEndTime(YTPlayerViewController *self, YTSingleVideoControll
%end
-%hook YTVarispeedSwitchControllerImpl
-
-- (id)init {
- self = %orig;
- float speeds[] = {0.25, 0.5, 0.75, 1.0, 1.25, 1.5, 1.75, 2.0, 2.5, 3.0, 5.0, 7.5, 10.0};
- id options[itemCount];
- Class YTVarispeedSwitchControllerOptionClass = %c(YTVarispeedSwitchControllerOption);
- for (int i = 0; i < itemCount; ++i) {
- NSString *title = [NSString stringWithFormat:@"%.2fx", speeds[i]];
- options[i] = [[YTVarispeedSwitchControllerOptionClass alloc] initWithTitle:title rate:speeds[i]];
- }
- [self setValue:[NSArray arrayWithObjects:options count:itemCount] forKey:@"_options"];
- return self;
-}
-
-%end
-
%hook YTIPlayerHotConfig
%new(f@:)
@@ -294,90 +597,111 @@ static void YouModAddEndTime(YTPlayerViewController *self, YTSingleVideoControll
%end
%end
-// Disable Hints
-%hook YTSettings
-- (BOOL)areHintsDisabled { return IS_ENABLED(DisableHints) ? YES : %orig; }
-- (void)setHintsDisabled:(BOOL)arg1 { IS_ENABLED(DisableHints) ? %orig(YES) : %orig; }
-%end
-
-%hook YTSettingsImpl
-- (BOOL)areHintsDisabled { return IS_ENABLED(DisableHints) ? YES : %orig; }
-- (void)setHintsDisabled:(BOOL)arg1 { IS_ENABLED(DisableHints) ? %orig(YES) : %orig; }
-%end
+static CGFloat YouModRateBeforeHoldToSpeed = 1.0;
-%hook YTUserDefaults
-- (BOOL)areHintsDisabled { return IS_ENABLED(DisableHints) ? YES : %orig; }
-- (void)setHintsDisabled:(BOOL)arg1 { IS_ENABLED(DisableHints) ? %orig(YES) : %orig; }
-%end
+static NSArray *YouModHoldSpeedValues(void) {
+ return @[@0.0, @0.25, @0.5, @0.75, @1.0, @1.25, @1.5, @1.75, @2.0, @3.0, @4.0, @5.0];
+}
-%hook YTPlayerViewController
-- (void)loadWithPlayerTransition:(id)arg1 playbackConfig:(id)arg2 {
- %orig;
- YouModDownloadSetCurrentPlayer(self);
- if (IS_ENABLED(AutoFullScreen)) [self performSelector:@selector(YouModAutoFullscreen) withObject:nil afterDelay:0.75];
- // if (ytlBool(@"shortsToRegular")) [self performSelector:@selector(shortsToRegular) withObject:nil afterDelay:0.75];
- if (IS_ENABLED(DisablesCaptions)) [self performSelector:@selector(YouModTurnOffCaptions) withObject:nil afterDelay:1.0];
+static CGFloat YouModSpeedForHoldIndex(NSInteger index) {
+ NSArray *values = YouModHoldSpeedValues();
+ return [values[index] floatValue];
}
-- (void)prepareToLoadWithPlayerTransition:(id)arg1 expectedLayout:(id)arg2 {
+%hook YTMainAppVideoPlayerOverlayView
+- (void)setLongPressGestureRecognizer:(id)arg {
+ if (INTFORVAL(HoldToSpeedIndex) != 0) return;
%orig;
- YouModDownloadSetCurrentPlayer(self);
- if (IS_ENABLED(AutoFullScreen)) [self performSelector:@selector(YouModAutoFullscreen) withObject:nil afterDelay:0.75];
- // if (ytlBool(@"shortsToRegular")) [self performSelector:@selector(shortsToRegular) withObject:nil afterDelay:0.75];
- if (IS_ENABLED(DisablesCaptions)) [self performSelector:@selector(YouModTurnOffCaptions) withObject:nil afterDelay:1.0];
}
-
-%new
-- (void)YouModTurnOffCaptions {
- if ([self.view.superview isKindOfClass:NSClassFromString(@"YTWatchView")]) {
- [self setActiveCaptionTrack:nil source:0];
- }
+// Remove Dark Background in Overlay
+- (void)setBackgroundVisible:(BOOL)arg1 isGradientBackground:(BOOL)arg2 {
+ BOOL temp = IS_ENABLED(RemoveDarkOverlay) ? NO : arg1;
+ %orig(temp, arg2);
}
-
-%new
-- (void)YouModAutoFullscreen {
- YTWatchController *watchController = [self valueForKey:@"_UIDelegate"];
- [watchController showFullScreen];
+// Hide Watermarks
+- (BOOL)isWatermarkEnabled { return IS_ENABLED(HideWaterMark) ? NO : %orig; }
+- (void)setWatermarkEnabled:(BOOL)arg {
+ BOOL temp = IS_ENABLED(HideWaterMark) ? NO : arg;
+ %orig(temp);
}
-
-/*
-- (void)singleVideo:(YTSingleVideoController *)video currentVideoTimeDidChange:(YTSingleVideoTime *)time {
+- (void)layoutSubviews {
%orig;
- YouModAddEndTime(self, video, time);
+ if (IS_ENABLED(HideCastButtonPlayer)) self.playbackRouteButton.hidden = YES;
}
+- (BOOL)isFullscreenActionsVisible { return IS_ENABLED(HideFullAction) ? NO : %orig; }
+%end
-- (void)potentiallyMutatedSingleVideo:(YTSingleVideoController *)video currentVideoTimeDidChange:(YTSingleVideoTime *)time {
- %orig;
- YouModAddEndTime(self, video, time);
-}
-*/
+%hook YTSingleVideoController
-- (void)setPlaybackRate:(float)rate {
- playbackRate = rate;
+- (void)playerItem:(id)arg1 hasSelectableVideoFormats:(id)arg2 {
%orig;
+ if (!arg2) return;
+ [self YouModAutoQuality];
}
-%end
-/*
%new
-- (void)shortsToRegular {
- if (self.contentVideoID != nil && [self.parentViewController isKindOfClass:NSClassFromString(@"YTShortsPlayerViewController")]) {
- NSString *vidLink = [NSString stringWithFormat:@"vnd.youtube://%@", self.contentVideoID]; // idk about this
- if ([[UIApplication sharedApplication] canOpenURL:[NSURL URLWithString:vidLink]]) {
- [[UIApplication sharedApplication] openURL:[NSURL URLWithString:vidLink] options:@{} completionHandler:nil];
+- (void)YouModAutoQuality {
+ NSArray *videoFormats = self.selectableVideoFormats;
+ // Return early if there aren't any video formats available
+ // eg. Voice comments and others
+ if (!videoFormats || videoFormats.count == 0) return;
+ NSInteger kQualityIndex = isWiFiConnected() ? INTFORVAL(WifiQualityIndex) : INTFORVAL(CellQualityIndex);
+ if ([NSProcessInfo processInfo].lowPowerModeEnabled) kQualityIndex = INTFORVAL(LowPowerQualityIndex);
+ if (kQualityIndex == 0) return;
+
+ NSString *bestQualityLabel;
+ int highestResolution = 0;
+ for (MLFormat *format in videoFormats) {
+ int reso = format.singleDimensionResolution;
+ if (reso > highestResolution) {
+ highestResolution = reso;
+ bestQualityLabel = format.qualityLabel;
}
}
-}
-%end
-// Fix Playlist Mini-bar Height For Small Screens
-%hook YTPlaylistMiniBarView
-- (void)setFrame:(CGRect)frame {
- if (frame.size.height < 54.0) frame.size.height = 54.0; // what
- %orig(frame);
+ NSArray *qualityLabels = @[@"Default", bestQualityLabel, @"2160p60", @"2160p", @"1440p60", @"1440p", @"1080p60", @"1080p", @"720p60", @"720p", @"480p", @"360p", @"240p", @"144p"];
+ NSString *qualityLabel = qualityLabels[kQualityIndex];
+
+ if (![qualityLabel isEqualToString:bestQualityLabel]) {
+ BOOL exactMatch = NO;
+ NSString *closestQualityLabel = qualityLabel;
+
+ for (MLFormat *format in videoFormats) {
+ if ([format.qualityLabel isEqualToString:qualityLabel]) {
+ exactMatch = YES;
+ break;
+ }
+ }
+
+ if (!exactMatch) {
+ NSInteger bestQualityDifference = NSIntegerMax;
+
+ for (MLFormat *format in videoFormats) {
+ NSArray *formatСomponents = [format.qualityLabel componentsSeparatedByString:@"p"];
+ NSArray *targetComponents = [qualityLabel componentsSeparatedByString:@"p"];
+ if (formatСomponents.count == 2) {
+ NSInteger formatQuality = [formatСomponents.firstObject integerValue];
+ NSInteger targetQuality = [targetComponents.firstObject integerValue];
+ NSInteger difference = labs(formatQuality - targetQuality);
+ if (difference < bestQualityDifference) {
+ bestQualityDifference = difference;
+ closestQualityLabel = format.qualityLabel;
+ }
+ }
+ }
+
+ qualityLabel = closestQualityLabel;
+ }
+ }
+
+ MLQuickMenuVideoQualitySettingFormatConstraint *fc = [%c(MLQuickMenuVideoQualitySettingFormatConstraint) alloc];
+ if ([fc respondsToSelector:@selector(initWithVideoQualitySetting:formatSelectionReason:qualityLabel:resolutionCap:)]) {
+ [self setVideoFormatConstraint:[fc initWithVideoQualitySetting:3 formatSelectionReason:2 qualityLabel:qualityLabel resolutionCap:0]];
+ } else {
+ [self setVideoFormatConstraint:[fc initWithVideoQualitySetting:3 formatSelectionReason:2 qualityLabel:qualityLabel]];
+ }
}
%end
-*/
// YTClassicVideoQuality (https://github.com/PoomSmart/YTClassicVideoQuality)
%group OldVideoQuality
@@ -424,17 +748,27 @@ static void YouModAddEndTime(YTPlayerViewController *self, YTSingleVideoControll
%end
// Gestures - @bhackel (YTLitePlus)
-%group Gestures
%hook YTWatchLayerViewController
// invoked when the player view controller is either created or destroyed
- (void)watchController:(YTWatchController *)watchController didSetPlayerViewController:(YTPlayerViewController *)playerViewController {
if (playerViewController) {
// check to see if the pan gesture is already created
- if (!playerViewController.YouModPanGesture) {
+ if (!playerViewController.YouModPanGesture && IS_ENABLED(GestureControls)) {
playerViewController.YouModPanGesture = [[UIPanGestureRecognizer alloc] initWithTarget:playerViewController action:@selector(YouModHandlePanGesture:)];
playerViewController.YouModPanGesture.delegate = playerViewController;
[playerViewController.playerView addGestureRecognizer:playerViewController.YouModPanGesture];
- }
+ }
+ if (!playerViewController.YouModTapGesture && IS_ENABLED(PauseTwoFingers)) {
+ playerViewController.YouModTapGesture = [[UITapGestureRecognizer alloc] initWithTarget:playerViewController action:@selector(YouModHandleTapGesture:)];
+ playerViewController.YouModTapGesture.numberOfTouchesRequired = 2;
+ playerViewController.YouModTapGesture.delegate = playerViewController;
+ [playerViewController.playerView addGestureRecognizer:playerViewController.YouModTapGesture];
+ }
+ if (!playerViewController.YouModHoldGesture && INTFORVAL(HoldToSpeedIndex) != 0) {
+ playerViewController.YouModHoldGesture = [[UILongPressGestureRecognizer alloc] initWithTarget:playerViewController action:@selector(YouModHoldToSpeed:)];
+ playerViewController.YouModHoldGesture.minimumPressDuration = 0.4;
+ [playerViewController.playerView addGestureRecognizer:playerViewController.YouModHoldGesture];
+ }
}
%orig;
}
@@ -442,7 +776,11 @@ static void YouModAddEndTime(YTPlayerViewController *self, YTSingleVideoControll
%hook YTPlayerViewController
%property (nonatomic, retain) UIPanGestureRecognizer *YouModPanGesture;
+%property (nonatomic, retain) UITapGestureRecognizer *YouModTapGesture;
%property (nonatomic, retain) UILabel *YouModGestureHUD;
+%property (nonatomic, strong) UIView *YouModSpeedToastView;
+%property (nonatomic, strong) UILabel *YouModSpeedToastLabel;
+%property (nonatomic, retain) UILongPressGestureRecognizer *YouModHoldGesture;
%new
- (BOOL)gestureRecognizerShouldBegin:(UIGestureRecognizer *)gestureRecognizer {
if (gestureRecognizer == self.YouModPanGesture) {
@@ -552,7 +890,7 @@ static void YouModAddEndTime(YTPlayerViewController *self, YTSingleVideoControll
}
if (IS_ENABLED(GestureHUD)) {
- int sizeSetting = [[NSUserDefaults standardUserDefaults] objectForKey:@"GestureHUDSize"] ? (int)[[NSUserDefaults standardUserDefaults] integerForKey:@"GestureHUDSize"] : 1;
+ int sizeSetting = [[NSUserDefaults standardUserDefaults] objectForKey:GestureHUDSize] ? (int)[[NSUserDefaults standardUserDefaults] integerForKey:GestureHUDSize] : 1;
CGFloat fontSize = 14.0 + (sizeSetting * 2.0);
CGFloat hudWidth = 74.0 + (sizeSetting * 10.0);
CGFloat hudHeight = 30.0 + (sizeSetting * 4.0);
@@ -561,7 +899,7 @@ static void YouModAddEndTime(YTPlayerViewController *self, YTSingleVideoControll
self.YouModGestureHUD.layer.cornerRadius = hudHeight / 2.0;
self.YouModGestureHUD.font = [UIFont boldSystemFontOfSize:fontSize];
- int posSetting = [[NSUserDefaults standardUserDefaults] objectForKey:@"GestureHUDPosition"] ? (int)[[NSUserDefaults standardUserDefaults] integerForKey:@"GestureHUDPosition"] : 0;
+ int posSetting = [[NSUserDefaults standardUserDefaults] objectForKey:GestureHUDPosition] ? (int)[[NSUserDefaults standardUserDefaults] integerForKey:GestureHUDPosition] : 0;
CGFloat viewHeight = self.view.bounds.size.height;
CGFloat centerY = viewHeight / 6.0;
if (posSetting == 1) centerY = viewHeight / 2.0;
@@ -648,23 +986,237 @@ static void YouModAddEndTime(YTPlayerViewController *self, YTSingleVideoControll
}
return YES;
}
-%end
+// Pause using Two fingers
+%new
+- (void)YouModHandleTapGesture:(UITapGestureRecognizer *)tapGestureRecognizer {
+ if (tapGestureRecognizer.state == UIGestureRecognizerStateEnded) {
+ if (self.playerState == 3) {
+ [self pause];
+ } else if (self.playerState == 4) {
+ [self play];
+ }
+ }
+}
+
+%new
+- (void)YouModAutoFullscreen {
+ YTWatchController *watchController = [self valueForKey:@"_UIDelegate"];
+ [watchController showFullScreen];
+}
+
+%new
+- (void)YouModSetAutoSpeed {
+ NSArray *speedLabels = @[@0.01, @0.25, @0.5, @0.75, @1.0, @1.25, @1.5, @1.75, @2.0, @3.0, @4.0, @5.0];
+ [self setPlaybackRate:[speedLabels[INTFORVAL(AutoSpeedIndex)] floatValue]];
+}
+
+- (void)singleVideo:(YTSingleVideoController *)video currentVideoTimeDidChange:(YTSingleVideoTime *)time {
+ %orig;
+ YouModAddEndTime(self, video, time);
+}
+
+- (void)potentiallyMutatedSingleVideo:(YTSingleVideoController *)video currentVideoTimeDidChange:(YTSingleVideoTime *)time {
+ %orig;
+ YouModAddEndTime(self, video, time);
+}
+
+- (void)setPlaybackRate:(float)rate {
+ playbackRate = rate;
+ %orig;
+}
+
+%new
+- (void)YouModAutoMute {
+ YTSingleVideoController *sgvid = self.activeVideo;
+ BOOL muted = [sgvid isMuted];
+ [sgvid setMuted:[self isInlinePlaybackActive] ? muted : IS_ENABLED(KeepMutedKey)];
+}
+
+%new
+- (void)YouModAutoAudioTrack {
+ NSInteger selectedIndex = INTFORVAL(AudioTrackLangIndex);
+ NSArray *langCodes = getAllSystemLanguageValues();
+ NSString *userTargetLang = langCodes[selectedIndex];
+ id switchcon = self.audioTrackController;
+ NSArray *availableTracks = [switchcon valueForKey:@"_availableAudioTracks"];
+ if (!availableTracks || availableTracks.count == 0) return;
+ YTIAudioTrack *matchedTrack = nil;
+
+ if (INTFORVAL(AudioTrack) == 1) {
+ // Loop for all tracks
+ for (YTIAudioTrack *track in availableTracks) {
+ if ([track.id_p hasSuffix:@".4"]) {
+ matchedTrack = track;
+ break;
+ }
+ }
+ } else if (INTFORVAL(AudioTrack) == 2) {
+ // Loop for all tracks
+ for (YTIAudioTrack *track in availableTracks) {
+ if ([track.id_p hasPrefix:userTargetLang]) {
+ matchedTrack = track;
+ break;
+ }
+ }
+
+ // Check if it's dubbed
+ if (matchedTrack && [matchedTrack isAutoDubbed] && IS_ENABLED(NoDubbedAudioTrack)) {
+ matchedTrack = nil;
+ return;
+ }
+ }
+
+ // If found, change to it
+ if (matchedTrack) {
+ [self setAudioTrack:matchedTrack source:0];
+ } else if (!matchedTrack && IS_ENABLED(NoDubbedAudioTrack)) {
+ for (YTIAudioTrack *track in availableTracks) {
+ if ([track.id_p hasSuffix:@".4"]) {
+ [self setAudioTrack:track source:0];
+ break;
+ }
+ }
+ }
+}
+
+%new
+- (void)YouModAutoCaptions {
+ YTSingleVideoController *sgvid = self.activeVideo;
+ NSArray *allTracks = sgvid.availableCaptionTracks;
+ if (!allTracks || allTracks.count == 0) return;
+ NSInteger selectedIndex = INTFORVAL(CaptionTrackLangIndex);
+ NSArray *langCodes = getAllSystemLanguageValues();
+ NSString *userTargetLang = langCodes[selectedIndex];
+ MLInnerTubeCaptionTrack *currentTrack = sgvid.activeCaptionTrack;
+ MLInnerTubeCaptionTrack *matchedTrack;
+
+ if (INTFORVAL(CaptionTrack) == 1) {
+ if (currentTrack != nil) {
+ [self YouModCaptionsHelper:nil];
+ }
+ return;
+ }
+
+ for (MLInnerTubeCaptionTrack *track in allTracks) {
+ if ([track.languageCode isEqualToString:userTargetLang]) {
+ matchedTrack = track;
+ break;
+ }
+ }
+ if (matchedTrack && ([matchedTrack.VSSID hasPrefix:@"a."] || [matchedTrack.VSSID hasPrefix:@"ta."]) && IS_ENABLED(DisablesCaptionTrack)) {
+ matchedTrack = nil;
+ [self YouModCaptionsHelper:nil];
+ return;
+ } else if (!matchedTrack && IS_ENABLED(DisablesCaptionTrack)) {
+ [self YouModCaptionsHelper:nil];
+ return;
+ }
+ if (matchedTrack && matchedTrack != currentTrack) {
+ [self YouModCaptionsHelper:matchedTrack];
+ }
+}
+
+%new
+- (void)YouModCaptionsHelper:(MLInnerTubeCaptionTrack *)track {
+ if ([self respondsToSelector:@selector(setActiveCaptionTrack:source:)]) {
+ [self setActiveCaptionTrack:track source:0];
+ } else {
+ [self setActiveCaptionTrack:track];
+ }
+}
+%new
+- (void)YouModShowSpeedToast:(CGFloat)speed {
+ UIColor *themeTextColor = [UIColor labelColor];
+ UIColor *toastBgColor = [UIColor colorWithDynamicProvider:^UIColor * _Nonnull(UITraitCollection * _Nonnull traitCollection) {
+ if (traitCollection.userInterfaceStyle == UIUserInterfaceStyleDark) {
+ return [UIColor colorWithWhite:0.1 alpha:0.95];
+ } else {
+ return [UIColor colorWithWhite:0.95 alpha:0.95];
+ }
+ }];
+
+ if (!self.YouModSpeedToastView) {
+ self.YouModSpeedToastView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 160, 44)];
+ self.YouModSpeedToastView.layer.cornerRadius = 22;
+ self.YouModSpeedToastView.clipsToBounds = YES;
+ self.YouModSpeedToastView.alpha = 0.0;
+ self.YouModSpeedToastView.backgroundColor = toastBgColor;
+
+ self.YouModSpeedToastLabel = [[UILabel alloc] initWithFrame:self.YouModSpeedToastView.bounds];
+ self.YouModSpeedToastLabel.textAlignment = NSTextAlignmentCenter;
+ self.YouModSpeedToastLabel.textColor = themeTextColor;
+ self.YouModSpeedToastLabel.font = [UIFont systemFontOfSize:13 weight:UIFontWeightMedium];
+ self.YouModSpeedToastLabel.numberOfLines = 2;
+ [self.YouModSpeedToastView addSubview:self.YouModSpeedToastLabel];
+
+ [self.playerView addSubview:self.YouModSpeedToastView];
+ }
+
+ self.YouModSpeedToastView.backgroundColor = toastBgColor;
+ self.YouModSpeedToastLabel.textColor = themeTextColor;
+
+ CGRect toastFrame = self.YouModSpeedToastView.frame;
+ toastFrame.origin.y = 18;
+ toastFrame.origin.x = (self.playerView.bounds.size.width - toastFrame.size.width) / 2.0;
+ self.YouModSpeedToastView.frame = toastFrame;
+
+ self.YouModSpeedToastView.autoresizingMask = UIViewAutoresizingFlexibleLeftMargin | UIViewAutoresizingFlexibleRightMargin | UIViewAutoresizingFlexibleBottomMargin;
+ [self.playerView bringSubviewToFront:self.YouModSpeedToastView];
+
+ NSTextAttachment *attachment = [[NSTextAttachment alloc] init];
+ attachment.image = [[UIImage systemImageNamed:@"hare.fill"] imageWithTintColor:themeTextColor];
+ attachment.bounds = CGRectMake(0, -2, 14, 14);
+
+ NSString *localizedText = LOC(@"PLAYBACK_SPEED");
+ NSMutableAttributedString *attrString = [[NSMutableAttributedString alloc] initWithString:[NSString stringWithFormat:@" %@\n%gx", localizedText, speed]];
+
+ if (attachment.image) {
+ NSAttributedString *iconString = [NSAttributedString attributedStringWithAttachment:attachment];
+ [attrString insertAttributedString:iconString atIndex:0];
+ }
+
+ self.YouModSpeedToastLabel.attributedText = attrString;
+
+ [UIView animateWithDuration:0.2 animations:^{
+ self.YouModSpeedToastView.alpha = 1.0;
+ }];
+}
+%new
+- (void)YouModHideSpeedToast {
+ [UIView animateWithDuration:0.2 animations:^{
+ self.YouModSpeedToastView.alpha = 0.0;
+ }];
+}
+%new
+- (void)YouModHoldToSpeed:(UILongPressGestureRecognizer *)gesture {
+ if (self.playerState != 3) return;
+ NSInteger speedIndex = INTFORVAL(HoldToSpeedIndex);
+ CGFloat speed = YouModSpeedForHoldIndex(speedIndex);
+
+ if (gesture.state == UIGestureRecognizerStateBegan) {
+ YTMainAppVideoPlayerOverlayViewController *con = [self activeVideoPlayerOverlay];
+ YouModRateBeforeHoldToSpeed = [con currentPlaybackRate];
+ [self setPlaybackRate:speed];
+ [self YouModShowSpeedToast:speed];
+ } else if (gesture.state == UIGestureRecognizerStateEnded ||
+ gesture.state == UIGestureRecognizerStateCancelled ||
+ gesture.state == UIGestureRecognizerStateFailed) {
+
+ [self setPlaybackRate:YouModRateBeforeHoldToSpeed];
+ [self YouModHideSpeedToast];
+ }
+}
%end
%ctor {
%init;
+ YouModConfigureRemoteSkipCommands();
if (IS_ENABLED(OldQualityPicker)) {
%init(OldVideoQuality);
}
- if (IS_ENABLED(ExtraSpeed) || IS_ENABLED(GestureControls)) {
+ if (IS_ENABLED(ExtraSpeed) || IS_ENABLED(GestureControls) || INTFORVAL(HoldToSpeedIndex) >= 9 || INTFORVAL(AutoSpeedIndex) >= 9) {
%init(Speed);
}
- if (IS_ENABLED(HidePaidPromoOverlay)) {
- %init(PaidPromoOverlay);
- }
- if (IS_ENABLED(GestureControls)) {
- %init(Gestures);
- }
if (IS_ENABLED(ForceMiniPlayer)) {
%init(ForceMiniPlayer);
}
diff --git a/Files/Settings.x b/Files/Settings.x
index f2967084..ac2db7e0 100644
--- a/Files/Settings.x
+++ b/Files/Settings.x
@@ -10,8 +10,21 @@
static const NSInteger TweakSection = 'ytmo';
+@class YMSettingsItem;
+extern void YMPushSubSettings(NSString *title, NSArray *items, id settingsVC, id parentResponder);
+extern YMSettingsItem *YMToggle(NSString *title, NSString *subtitle, NSString *key);
+extern YMSettingsItem *YMSlider(NSString *title, NSString *subtitle, NSString *key, float min, float max, float step, float defaultValue);
+extern YMSettingsItem *YMPicker(NSString *title, NSString *subtitle, NSString *key, NSArray *options, NSInteger defaultValue);
+extern YMSettingsItem *YMAction(NSString *title, NSString *subtitle, void (^action)(UIViewController *vc));
+extern YMSettingsItem *YMHeader(NSString *title);
+extern YMSettingsItem *YMSegment(NSString *title, NSString *key, NSArray *icons, NSInteger defaultValue);
+extern YMSettingsItem *YMTextSegment(NSString *title, NSString *key, NSArray *labels, NSInteger defaultValue);
+extern YMSettingsItem *YMImageSegment(NSString *title, NSString *key, NSArray *images, NSInteger defaultValue);
+extern void YMPushTabOrder(id settingsVC, id parentResponder);
+
@interface YTSettingsSectionItemManager (YouMod)
- (void)updateYouModSectionWithEntry:(id)entry;
+- (void)updateSponsorBlockSectionWithEntry:(id)entry;
@end
static NSBundle *YouModBundle() {
@@ -41,34 +54,34 @@ static NSString *GetCacheSize() { // YTLite - @dayanch96
return [formatter stringFromByteCount:folderSize];
}
-// Basic switch item - YTLitePlus
-#define BASIC_SWITCH(title, description, key) \
- [YTSettingsSectionItemClass switchItemWithTitle:title \
- titleDescription:description \
- accessibilityIdentifier:nil \
- switchOn:IS_ENABLED(key) \
- switchBlock:^BOOL (YTSettingsCell *cell, BOOL enabled) { \
- [[NSUserDefaults standardUserDefaults] setBool:enabled forKey:key]; \
- return YES; \
- } \
- settingItemId:0]
-
-// Settings header
-#define SETTINGS_HEADER \
- [YTSettingsSectionItemClass itemWithTitle:nil \
- titleDescription:LOC(@"SETTINGS") \
- accessibilityIdentifier:nil \
- detailTextBlock:nil \
- selectBlock:^BOOL (YTSettingsCell *cell, NSUInteger arg1) { \
- return NO; \
- }]
+// Audio track list
+static NSArray *getAllSystemLanguageTitles() {
+ NSMutableArray *titles = [NSMutableArray array];
+ NSArray *allLocales = [%c(YTLanguages) languageList];
+ NSMutableSet *seenLanguages = [NSMutableSet set];
+ NSLocale *currentLocale = [NSLocale currentLocale];
+
+ for (NSString *localeId in allLocales) {
+ NSDictionary *components = [NSLocale componentsFromLocaleIdentifier:localeId];
+ NSString *langCode = components[NSLocaleLanguageCode];
+
+ if (langCode && ![seenLanguages containsObject:langCode]) {
+ [seenLanguages addObject:langCode];
+ NSString *displayName = [currentLocale localizedStringForLocaleIdentifier:langCode];
+ if (displayName) [titles addObject:displayName];
+ }
+ }
+ return [titles sortedArrayUsingSelector:@selector(localizedCaseInsensitiveCompare:)];
+}
%hook YTSettingsGroupData
- (NSArray *)orderedCategories {
- if (self.type != 1 || class_getClassMethod(objc_getClass("YTSettingsGroupData"), @selector(tweaks)))
+ if (self.type != 1 || class_getClassMethod(objc_getClass("YTSettingsGroupData"), @selector(tweaks))) {
return %orig;
- NSMutableArray *mutableCategories = %orig.mutableCopy;
+ }
+ NSArray *temp = %orig;
+ NSMutableArray *mutableCategories = temp.mutableCopy;
[mutableCategories insertObject:@(TweakSection) atIndex:0];
return mutableCategories.copy;
}
@@ -101,7 +114,7 @@ static NSString *GetCacheSize() { // YTLite - @dayanch96
// Tweak Version (at the top)
// Thanks to the original codes from YTweaks by fosterbarnes - https://github.com/fosterbarnes/YTweaks/blob/e921591a89b87256a2b37c4788bd99282f70d9c2/Settings.x
- YTSettingsSectionItem *tweakVersion = [YTSettingsSectionItemClass itemWithTitle:@"YouMod v1.3.0"
+ YTSettingsSectionItem *tweakVersion = [YTSettingsSectionItemClass itemWithTitle:@"YouMod v2.0.0"
titleDescription:nil
accessibilityIdentifier:nil
detailTextBlock:nil
@@ -110,16 +123,6 @@ static NSString *GetCacheSize() { // YTLite - @dayanch96
}];
[sectionItems addObject:tweakVersion];
- // Note
- YTSettingsSectionItem *note = [YTSettingsSectionItemClass itemWithTitle:LOC(@"NOTE")
- titleDescription:nil
- accessibilityIdentifier:nil
- detailTextBlock:nil
- selectBlock:^BOOL (YTSettingsCell *cell, NSUInteger arg1) {
- return NO;
- }];
- [sectionItems addObject:note];
-
// Section 0
// Github
YTSettingsSectionItem *github = [YTSettingsSectionItemClass itemWithTitle:nil
@@ -153,19 +156,30 @@ static NSString *GetCacheSize() { // YTLite - @dayanch96
];
[sectionItems addObject:sourceCodes];
- /*
- // Center YT logo
- YTSettingsSectionItem *centerytlogo = [YTSettingsSectionItemClass switchItemWithTitle:LOC(@"CENTER_YT_LOGO")
- titleDescription:LOC(@"CENTER_YT_LOGO_DESC") // Set center logo
+ // ?
+ YTSettingsSectionItem *blank = [YTSettingsSectionItemClass itemWithTitle:nil
+ titleDescription:LOC(@"EXTRA")
accessibilityIdentifier:nil
- switchOn:IS_ENABLED(CenterYTLogo)
+ detailTextBlock:nil
+ selectBlock:^BOOL (YTSettingsCell *cell, NSUInteger arg1) {
+ return NO;
+ }];
+ [sectionItems addObject:blank];
+
+ // Fix playback issues
+ YTSettingsSectionItem *fixPlaybackissues = [YTSettingsSectionItemClass switchItemWithTitle:LOC(@"FIX_PLAYBACK_ISSUES")
+ titleDescription:LOC(@"FIX_PLAYBACK_ISSUES_DESC")
+ accessibilityIdentifier:nil
+ switchOn:IS_ENABLED(FixPlaybackIssues)
switchBlock:^BOOL (YTSettingsCell *cell, BOOL enabled) {
- [[NSUserDefaults standardUserDefaults] setBool:enabled forKey:CenterYTLogo];
+ [[NSUserDefaults standardUserDefaults] setBool:enabled forKey:FixPlaybackIssues];
return YES;
}
settingItemId:0];
- [sectionItems addObject:centerytlogo];
- */
+ [sectionItems addObject:fixPlaybackissues];
+
+ // TODO: Center YT logo (not yet implemented)
+ // [sectionItems addObject: YMToggle(LOC(@"CENTER_YT_LOGO"), LOC(@"CENTER_YT_LOGO_DESC"), CenterYTLogo)];
// Settings
YTSettingsSectionItem *settings = [YTSettingsSectionItemClass itemWithTitle:nil
@@ -180,14 +194,14 @@ static NSString *GetCacheSize() { // YTLite - @dayanch96
// Section 1
// Downloading
YTSettingsSectionItem *downloadinggroup = [YTSettingsSectionItemClass itemWithTitle:LOC(@"DOWNLOADING") accessibilityIdentifier:nil detailTextBlock:nil selectBlock:^BOOL (YTSettingsCell *cell, NSUInteger arg1) {
- NSArray *rows = @[
- SETTINGS_HEADER,
- BASIC_SWITCH(LOC(@"DOWNLOAD_MANAGER"), LOC(@"DOWNLOAD_MANAGER_DESC"), DownloadManager),
- BASIC_SWITCH(LOC(@"DOWNLOAD_SAVE_PHOTOS"), LOC(@"DOWNLOAD_SAVE_PHOTOS_DESC"), DownloadSaveToPhotos),
- BASIC_SWITCH(LOC(@"DOWNLOAD_DRC_AUDIO"), LOC(@"DOWNLOAD_DRC_AUDIO"), DownloadPreferDRCAudio),
- ];
- YTSettingsPickerViewController *picker = [[%c(YTSettingsPickerViewController) alloc] initWithNavTitle:LOC(@"DOWNLOADING") pickerSectionTitle:nil rows:rows selectedItemIndex:0 parentResponder:[self parentResponder]];
- [settingsViewController pushViewController:picker];
+ YMPushSubSettings(LOC(@"DOWNLOADING"), @[
+ YMToggle(LOC(@"DOWNLOAD_MANAGER"), LOC(@"DOWNLOAD_MANAGER_DESC"), DownloadManager),
+ YMToggle(LOC(@"DOWNLOAD_SAVE_PHOTOS"), LOC(@"DOWNLOAD_SAVE_PHOTOS_DESC"), DownloadSaveToPhotos),
+ YMToggle(LOC(@"USE_ORIG_AUDIO"), LOC(@"USE_ORIG_AUDIO_DESC"), UseOrigAudio),
+ YMToggle(LOC(@"ADD_SHORTS_DOWNLOAD"), LOC(@"ADD_SHORTS_DOWNLOAD_DESC"), AddDownloadToShorts),
+ YMToggle(@"Fix download errors", @"Testing, and this doesn't support downloading other audio track languages", DownloadFix),
+ YMPicker(@"Download server", @"Choose server", DownloadServerIndex, (@[@"Europe", @"Thailand - Asia"]), 0),
+ ], settingsViewController, [self parentResponder]);
return YES;
}];
YTIIcon *downloadIcon = [%c(YTIIcon) new];
@@ -198,13 +212,10 @@ static NSString *GetCacheSize() { // YTLite - @dayanch96
// Section 2
// Appearance
YTSettingsSectionItem *appergroup = [YTSettingsSectionItemClass itemWithTitle:LOC(@"APPEARANCE") accessibilityIdentifier:nil detailTextBlock:nil selectBlock:^BOOL (YTSettingsCell *cell, NSUInteger arg1) {
- NSArray *rows = @[
- SETTINGS_HEADER,
- BASIC_SWITCH(LOC(@"OLED_THEME"), LOC(@"OLED_THEME_DESC"), OLEDTheme),
- BASIC_SWITCH(LOC(@"OLED_KEYBOARD"), LOC(@"OLED_KEYBOARD_DESC"), OLEDKeyboard),
- ];
- YTSettingsPickerViewController *picker = [[%c(YTSettingsPickerViewController) alloc] initWithNavTitle:LOC(@"APPEARANCE") pickerSectionTitle:nil rows:rows selectedItemIndex:0 parentResponder:[self parentResponder]];
- [settingsViewController pushViewController:picker];
+ YMPushSubSettings(LOC(@"APPEARANCE"), @[
+ YMToggle(LOC(@"OLED_THEME"), LOC(@"OLED_THEME_DESC"), OLEDTheme),
+ YMToggle(LOC(@"OLED_KEYBOARD"), LOC(@"OLED_KEYBOARD_DESC"), OLEDKeyboard),
+ ], settingsViewController, [self parentResponder]);
return YES;
}];
YTIIcon *icon0 = [%c(YTIIcon) new];
@@ -215,17 +226,15 @@ static NSString *GetCacheSize() { // YTLite - @dayanch96
// Section 3
// Navigation bar
YTSettingsSectionItem *navbargroup = [YTSettingsSectionItemClass itemWithTitle:LOC(@"NAVBAR") accessibilityIdentifier:nil detailTextBlock:nil selectBlock:^BOOL (YTSettingsCell *cell, NSUInteger arg1) {
- NSArray *rows = @[
- SETTINGS_HEADER,
- BASIC_SWITCH(LOC(@"HIDE_YT_LOGO"), LOC(@"HIDE_YT_LOGO_DESC"), HideYTLogo),
- BASIC_SWITCH(LOC(@"PREMIUM_LOGO"), LOC(@"PREMIUM_LOGO_DESC"), YTPremiumLogo),
- BASIC_SWITCH(LOC(@"HIDE_NOTIFICATION_BUTTON"), LOC(@"HIDE_NOTIFICATION_BUTTON_DESC"), HideNoti),
- BASIC_SWITCH(LOC(@"HIDE_SEARCH_BUTTON"), LOC(@"HIDE_SEARCH_BUTTON_DESC"), HideSearch),
- BASIC_SWITCH(LOC(@"HIDE_VOICE_SEARCH_BUTTON"), LOC(@"HIDE_VOICE_SEARCH_BUTTON_DESC"), HideVoiceSearch),
- BASIC_SWITCH(LOC(@"HIDE_CAST_BUTTON_NAVBAR"), LOC(@"HIDE_CAST_BUTTON_NAVBAR_DESC"), HideCastButtonNav),
- ];
- YTSettingsPickerViewController *picker = [[%c(YTSettingsPickerViewController) alloc] initWithNavTitle:LOC(@"NAVBAR") pickerSectionTitle:nil rows:rows selectedItemIndex:0 parentResponder:[self parentResponder]];
- [settingsViewController pushViewController:picker];
+ YMPushSubSettings(LOC(@"NAVBAR"), @[
+ YMToggle(LOC(@"STICKY_NAVBAR"), LOC(@"STICKY_NAVBAR_DESC"), StickyNavBar),
+ YMToggle(LOC(@"HIDE_YT_LOGO"), LOC(@"HIDE_YT_LOGO_DESC"), HideYTLogo),
+ YMToggle(LOC(@"PREMIUM_LOGO"), LOC(@"PREMIUM_LOGO_DESC"), YTPremiumLogo),
+ YMToggle(LOC(@"HIDE_NOTIFICATION_BUTTON"), LOC(@"HIDE_NOTIFICATION_BUTTON_DESC"), HideNoti),
+ YMToggle(LOC(@"HIDE_SEARCH_BUTTON"), LOC(@"HIDE_SEARCH_BUTTON_DESC"), HideSearch),
+ YMToggle(LOC(@"HIDE_VOICE_SEARCH_BUTTON"), LOC(@"HIDE_VOICE_SEARCH_BUTTON_DESC"), HideVoiceSearch),
+ YMToggle(LOC(@"HIDE_CAST_BUTTON_NAVBAR"), LOC(@"HIDE_CAST_BUTTON_NAVBAR_DESC"), HideCastButtonNav),
+ ], settingsViewController, [self parentResponder]);
return YES;
}];
YTIIcon *icon1 = [%c(YTIIcon) new];
@@ -236,19 +245,17 @@ static NSString *GetCacheSize() { // YTLite - @dayanch96
// Section 4
// Feed
YTSettingsSectionItem *feedgroup = [YTSettingsSectionItemClass itemWithTitle:LOC(@"FEED") accessibilityIdentifier:nil detailTextBlock:nil selectBlock:^BOOL (YTSettingsCell *cell, NSUInteger arg1) {
- NSArray *rows = @[
- SETTINGS_HEADER,
- BASIC_SWITCH(LOC(@"HIDE_SUBBAR"), LOC(@"HIDE_SUBBAR_DESC"), HideSubbar),
- BASIC_SWITCH(LOC(@"HIDE_MUSIC_PLAYLISTS"), LOC(@"HIDE_MUSIC_PLAYLISTS_DESC"), HideGenMusicShelf),
- BASIC_SWITCH(LOC(@"HIDE_FEED_POST"), LOC(@"HIDE_FEED_POST_DESC"), HideFeedPost),
- BASIC_SWITCH(LOC(@"HIDE_SHORTS_SHELF"), LOC(@"HIDE_SHORTS_SHELF_DESC"), HideShortsShelf),
- BASIC_SWITCH(LOC(@"HIDE_SEARCH_HISTORY"), LOC(@"HIDE_SEARCH_HISTORY_DESC"), HideSearchHis),
- BASIC_SWITCH(LOC(@"HIDE_SUB_BUTTON"), LOC(@"HIDE_SUB_BUTTON_DESC"), HideSubButton),
- BASIC_SWITCH(LOC(@"HIDE_SHOP_BUTTON"), LOC(@"HIDE_SHOP_BUTTON_DESC"), HideShoppingButton),
- BASIC_SWITCH(LOC(@"HIDE_MEMBER_BUTTON"), LOC(@"HIDE_MEMBER_BUTTON_DESC"), HideMemberButton),
- ];
- YTSettingsPickerViewController *picker = [[%c(YTSettingsPickerViewController) alloc] initWithNavTitle:LOC(@"FEED") pickerSectionTitle:nil rows:rows selectedItemIndex:0 parentResponder:[self parentResponder]];
- [settingsViewController pushViewController:picker];
+ YMPushSubSettings(LOC(@"FEED"), @[
+ YMToggle(LOC(@"HIDE_SUBBAR"), LOC(@"HIDE_SUBBAR_DESC"), HideSubbar),
+ YMToggle(LOC(@"HIDE_HORI_SHELF"), LOC(@"HIDE_HORI_SHELF_DESC"), HideHoriShelf),
+ YMToggle(LOC(@"HIDE_MUSIC_PLAYLISTS"), LOC(@"HIDE_MUSIC_PLAYLISTS_DESC"), HideGenMusicShelf),
+ YMToggle(LOC(@"HIDE_SURVEYS"), LOC(@"HIDE_SURVEYS_DESC"), HideSurveys),
+ YMToggle(LOC(@"HIDE_FEED_POST"), LOC(@"HIDE_FEED_POST_DESC"), HideFeedPost),
+ YMToggle(LOC(@"HIDE_PLAYABLES"), LOC(@"HIDE_PLAYABLES_DESC"), HidePlayables),
+ YMToggle(LOC(@"HIDE_SHORTS_SHELF"), LOC(@"HIDE_SHORTS_SHELF_DESC"), HideShortsShelf),
+ YMToggle(LOC(@"KEEP_SHORTS_SUBSCRIPT"), LOC(@"KEEP_SHORTS_SUBSCRIPT_DESC"), KeepShortsSubscript),
+ YMToggle(LOC(@"HIDE_SEARCH_HISTORY"), LOC(@"HIDE_SEARCH_HISTORY_DESC"), HideSearchHis),
+ ], settingsViewController, [self parentResponder]);
return YES;
}];
YTIIcon *icon2 = [%c(YTIIcon) new];
@@ -259,151 +266,78 @@ static NSString *GetCacheSize() { // YTLite - @dayanch96
// Section 5
// Player
YTSettingsSectionItem *playergroup = [YTSettingsSectionItemClass itemWithTitle:LOC(@"PLAYER") accessibilityIdentifier:nil detailTextBlock:nil selectBlock:^BOOL (YTSettingsCell *cell, NSUInteger arg1) {
- NSArray *rows = @[
- SETTINGS_HEADER,
- BASIC_SWITCH(LOC(@"HIDE_AUTOPLAY"), LOC(@"HIDE_AUTOPLAY_DESC"), HideAutoPlayToggle),
- BASIC_SWITCH(LOC(@"HIDE_CAPTIONS_BUTTON"), LOC(@"HIDE_CAPTIONS_BUTTON_DESC"), HideCaptionsButton),
- BASIC_SWITCH(LOC(@"HIDE_CAST_BUTTON_PLAYER"), LOC(@"HIDE_CAST_BUTTON_PLAYER_DESC"), HideCastButtonPlayer),
- BASIC_SWITCH(LOC(@"HIDE_PREV_BUTTON"), LOC(@"HIDE_PREV_BUTTON_DESC"), HidePrevButton),
- BASIC_SWITCH(LOC(@"HIDE_NEXT_BUTTON"), LOC(@"HIDE_NEXT_BUTTON_DESC"), HideNextButton),
- BASIC_SWITCH(LOC(@"REPLACE_PREVNEXT_BUTTONS"), LOC(@"REPLACE_PREVNEXT_BUTTONS_DESC"), ReplacePrevNextButtons),
- BASIC_SWITCH(LOC(@"REMOVE_DARK_OVERLAY"), LOC(@"REMOVE_DARK_OVERLAY_DESC"), RemoveDarkOverlay),
- BASIC_SWITCH(LOC(@"HIDE_END_SCREEN"), LOC(@"HIDE_END_SCREEN_DESC"), HideEndScreenCards),
- BASIC_SWITCH(LOC(@"REMOVE_AMBIANT"), LOC(@"REMOVE_AMBIANT_DESC"), RemoveAmbiant),
- BASIC_SWITCH(LOC(@"HIDE_SUGGESTED_VIDEO"), LOC(@"HIDE_SUGGESTED_VIDEO_DESC"), HideSuggestedVideo),
- BASIC_SWITCH(LOC(@"HIDE_PAID_OVERLAY"), LOC(@"HIDE_PAID_OVERLAY_DESC"), HidePaidPromoOverlay),
- BASIC_SWITCH(LOC(@"HIDE_WATERMARK"), LOC(@"HIDE_WATERMARK_DESC"), HideWaterMark),
- BASIC_SWITCH(LOC(@"GESTURES"), LOC(@"GESTURES_DESC"), GestureControls),
- [YTSettingsSectionItemClass itemWithTitle:LOC(@"GESTURE_AREA")
- titleDescription:LOC(@"GESTURE_AREA_DESC")
- accessibilityIdentifier:nil
- detailTextBlock:nil
- selectBlock:^BOOL (YTSettingsCell *cell, NSUInteger arg1) {
- int selectedIndex = 1;
- int currentVal = INTFORVAL(GestureActivationArea);
- if (currentVal == 0) selectedIndex = 0;
- else if (currentVal == 1) selectedIndex = 1;
- else if (currentVal == 2) selectedIndex = 2;
- else if (currentVal == 3) selectedIndex = 3;
- else if (currentVal == 4) selectedIndex = 4;
- else if (currentVal == 5) selectedIndex = 5;
- else if (currentVal == 6) selectedIndex = 6;
- else if (currentVal == 7) selectedIndex = 7;
- else if (currentVal == 8) selectedIndex = 8;
-
- NSArray *rows = @[
- [YTSettingsSectionItemClass checkmarkItemWithTitle:@"10%" titleDescription:nil selectBlock:^BOOL (YTSettingsCell *cell, NSUInteger arg1) { [[NSUserDefaults standardUserDefaults] setInteger:0 forKey:GestureActivationArea]; [[NSUserDefaults standardUserDefaults] synchronize]; [settingsViewController reloadData]; return YES; }],
- [YTSettingsSectionItemClass checkmarkItemWithTitle:@"15%" titleDescription:nil selectBlock:^BOOL (YTSettingsCell *cell, NSUInteger arg1) { [[NSUserDefaults standardUserDefaults] setInteger:1 forKey:GestureActivationArea]; [[NSUserDefaults standardUserDefaults] synchronize]; [settingsViewController reloadData]; return YES; }],
- [YTSettingsSectionItemClass checkmarkItemWithTitle:@"20%" titleDescription:nil selectBlock:^BOOL (YTSettingsCell *cell, NSUInteger arg1) { [[NSUserDefaults standardUserDefaults] setInteger:2 forKey:GestureActivationArea]; [[NSUserDefaults standardUserDefaults] synchronize]; [settingsViewController reloadData]; return YES; }],
- [YTSettingsSectionItemClass checkmarkItemWithTitle:@"25%" titleDescription:nil selectBlock:^BOOL (YTSettingsCell *cell, NSUInteger arg1) { [[NSUserDefaults standardUserDefaults] setInteger:3 forKey:GestureActivationArea]; [[NSUserDefaults standardUserDefaults] synchronize]; [settingsViewController reloadData]; return YES; }],
- [YTSettingsSectionItemClass checkmarkItemWithTitle:@"30%" titleDescription:nil selectBlock:^BOOL (YTSettingsCell *cell, NSUInteger arg1) { [[NSUserDefaults standardUserDefaults] setInteger:4 forKey:GestureActivationArea]; [[NSUserDefaults standardUserDefaults] synchronize]; [settingsViewController reloadData]; return YES; }],
- [YTSettingsSectionItemClass checkmarkItemWithTitle:@"35%" titleDescription:nil selectBlock:^BOOL (YTSettingsCell *cell, NSUInteger arg1) { [[NSUserDefaults standardUserDefaults] setInteger:5 forKey:GestureActivationArea]; [[NSUserDefaults standardUserDefaults] synchronize]; [settingsViewController reloadData]; return YES; }],
- [YTSettingsSectionItemClass checkmarkItemWithTitle:@"40%" titleDescription:nil selectBlock:^BOOL (YTSettingsCell *cell, NSUInteger arg1) { [[NSUserDefaults standardUserDefaults] setInteger:6 forKey:GestureActivationArea]; [[NSUserDefaults standardUserDefaults] synchronize]; [settingsViewController reloadData]; return YES; }],
- [YTSettingsSectionItemClass checkmarkItemWithTitle:@"45%" titleDescription:nil selectBlock:^BOOL (YTSettingsCell *cell, NSUInteger arg1) { [[NSUserDefaults standardUserDefaults] setInteger:7 forKey:GestureActivationArea]; [[NSUserDefaults standardUserDefaults] synchronize]; [settingsViewController reloadData]; return YES; }],
- [YTSettingsSectionItemClass checkmarkItemWithTitle:@"50%" titleDescription:nil selectBlock:^BOOL (YTSettingsCell *cell, NSUInteger arg1) { [[NSUserDefaults standardUserDefaults] setInteger:8 forKey:GestureActivationArea]; [[NSUserDefaults standardUserDefaults] synchronize]; [settingsViewController reloadData]; return YES; }]
- ];
- YTSettingsPickerViewController *picker = [[%c(YTSettingsPickerViewController) alloc] initWithNavTitle:LOC(@"GESTURE_AREA") pickerSectionTitle:nil rows:rows selectedItemIndex:selectedIndex parentResponder:[self parentResponder]];
- [settingsViewController pushViewController:picker];
- return YES;
- }
- ],
- [YTSettingsSectionItemClass itemWithTitle:LOC(@"LEFT_SIDE_GESTURE")
- titleDescription:nil
- accessibilityIdentifier:nil
- detailTextBlock:nil
- selectBlock:^BOOL (YTSettingsCell *cell, NSUInteger arg1) {
- int currentVal = [[NSUserDefaults standardUserDefaults] objectForKey:LeftSideGesture] ? INTFORVAL(LeftSideGesture) : 1;
- NSArray *rows = @[
- [YTSettingsSectionItemClass checkmarkItemWithTitle:LOC(@"GESTURE_NONE") titleDescription:nil selectBlock:^BOOL (YTSettingsCell *cell, NSUInteger arg1) { [[NSUserDefaults standardUserDefaults] setInteger:0 forKey:LeftSideGesture]; [[NSUserDefaults standardUserDefaults] synchronize]; [settingsViewController reloadData]; return YES; }],
- [YTSettingsSectionItemClass checkmarkItemWithTitle:LOC(@"GESTURE_BRIGHTNESS") titleDescription:nil selectBlock:^BOOL (YTSettingsCell *cell, NSUInteger arg1) { [[NSUserDefaults standardUserDefaults] setInteger:1 forKey:LeftSideGesture]; [[NSUserDefaults standardUserDefaults] synchronize]; [settingsViewController reloadData]; return YES; }],
- [YTSettingsSectionItemClass checkmarkItemWithTitle:LOC(@"GESTURE_VOLUME") titleDescription:nil selectBlock:^BOOL (YTSettingsCell *cell, NSUInteger arg1) { [[NSUserDefaults standardUserDefaults] setInteger:2 forKey:LeftSideGesture]; [[NSUserDefaults standardUserDefaults] synchronize]; [settingsViewController reloadData]; return YES; }],
- [YTSettingsSectionItemClass checkmarkItemWithTitle:LOC(@"GESTURE_SPEED") titleDescription:nil selectBlock:^BOOL (YTSettingsCell *cell, NSUInteger arg1) { [[NSUserDefaults standardUserDefaults] setInteger:3 forKey:LeftSideGesture]; [[NSUserDefaults standardUserDefaults] synchronize]; [settingsViewController reloadData]; return YES; }]
- ];
- YTSettingsPickerViewController *picker = [[%c(YTSettingsPickerViewController) alloc] initWithNavTitle:LOC(@"LEFT_SIDE_GESTURE") pickerSectionTitle:nil rows:rows selectedItemIndex:currentVal parentResponder:[self parentResponder]];
- [settingsViewController pushViewController:picker];
- return YES;
- }
- ],
- [YTSettingsSectionItemClass itemWithTitle:LOC(@"RIGHT_SIDE_GESTURE")
- titleDescription:nil
- accessibilityIdentifier:nil
- detailTextBlock:nil
- selectBlock:^BOOL (YTSettingsCell *cell, NSUInteger arg1) {
- int currentVal = [[NSUserDefaults standardUserDefaults] objectForKey:RightSideGesture] ? INTFORVAL(RightSideGesture) : 2;
- NSArray *rows = @[
- [YTSettingsSectionItemClass checkmarkItemWithTitle:LOC(@"GESTURE_NONE") titleDescription:nil selectBlock:^BOOL (YTSettingsCell *cell, NSUInteger arg1) { [[NSUserDefaults standardUserDefaults] setInteger:0 forKey:RightSideGesture]; [[NSUserDefaults standardUserDefaults] synchronize]; [settingsViewController reloadData]; return YES; }],
- [YTSettingsSectionItemClass checkmarkItemWithTitle:LOC(@"GESTURE_BRIGHTNESS") titleDescription:nil selectBlock:^BOOL (YTSettingsCell *cell, NSUInteger arg1) { [[NSUserDefaults standardUserDefaults] setInteger:1 forKey:RightSideGesture]; [[NSUserDefaults standardUserDefaults] synchronize]; [settingsViewController reloadData]; return YES; }],
- [YTSettingsSectionItemClass checkmarkItemWithTitle:LOC(@"GESTURE_VOLUME") titleDescription:nil selectBlock:^BOOL (YTSettingsCell *cell, NSUInteger arg1) { [[NSUserDefaults standardUserDefaults] setInteger:2 forKey:RightSideGesture]; [[NSUserDefaults standardUserDefaults] synchronize]; [settingsViewController reloadData]; return YES; }],
- [YTSettingsSectionItemClass checkmarkItemWithTitle:LOC(@"GESTURE_SPEED") titleDescription:nil selectBlock:^BOOL (YTSettingsCell *cell, NSUInteger arg1) { [[NSUserDefaults standardUserDefaults] setInteger:3 forKey:RightSideGesture]; [[NSUserDefaults standardUserDefaults] synchronize]; [settingsViewController reloadData]; return YES; }]
- ];
- YTSettingsPickerViewController *picker = [[%c(YTSettingsPickerViewController) alloc] initWithNavTitle:LOC(@"RIGHT_SIDE_GESTURE") pickerSectionTitle:nil rows:rows selectedItemIndex:currentVal parentResponder:[self parentResponder]];
- [settingsViewController pushViewController:picker];
- return YES;
- }
- ],
- BASIC_SWITCH(LOC(@"GESTURE_HUD"), LOC(@"GESTURE_HUD_DESC"), GestureHUD),
- [YTSettingsSectionItemClass itemWithTitle:LOC(@"GESTURE_HUD_SIZE")
- titleDescription:LOC(@"GESTURE_HUD_SIZE_DESC")
- accessibilityIdentifier:nil
- detailTextBlock:nil
- selectBlock:^BOOL (YTSettingsCell *cell, NSUInteger arg1) {
- int currentVal = [[NSUserDefaults standardUserDefaults] objectForKey:@"GestureHUDSize"] ? (int)[[NSUserDefaults standardUserDefaults] integerForKey:@"GestureHUDSize"] : 1;
- NSArray *rows = @[
- [YTSettingsSectionItemClass checkmarkItemWithTitle:LOC(@"SMALL") titleDescription:nil selectBlock:^BOOL (YTSettingsCell *cell, NSUInteger arg1) { [[NSUserDefaults standardUserDefaults] setInteger:0 forKey:@"GestureHUDSize"]; [[NSUserDefaults standardUserDefaults] synchronize]; [settingsViewController reloadData]; return YES; }],
- [YTSettingsSectionItemClass checkmarkItemWithTitle:LOC(@"NORMAL") titleDescription:nil selectBlock:^BOOL (YTSettingsCell *cell, NSUInteger arg1) { [[NSUserDefaults standardUserDefaults] setInteger:1 forKey:@"GestureHUDSize"]; [[NSUserDefaults standardUserDefaults] synchronize]; [settingsViewController reloadData]; return YES; }],
- [YTSettingsSectionItemClass checkmarkItemWithTitle:LOC(@"LARGE") titleDescription:nil selectBlock:^BOOL (YTSettingsCell *cell, NSUInteger arg1) { [[NSUserDefaults standardUserDefaults] setInteger:2 forKey:@"GestureHUDSize"]; [[NSUserDefaults standardUserDefaults] synchronize]; [settingsViewController reloadData]; return YES; }],
- [YTSettingsSectionItemClass checkmarkItemWithTitle:LOC(@"EXTRALARGE") titleDescription:nil selectBlock:^BOOL (YTSettingsCell *cell, NSUInteger arg1) { [[NSUserDefaults standardUserDefaults] setInteger:3 forKey:@"GestureHUDSize"]; [[NSUserDefaults standardUserDefaults] synchronize]; [settingsViewController reloadData]; return YES; }],
- [YTSettingsSectionItemClass checkmarkItemWithTitle:LOC(@"MAX") titleDescription:nil selectBlock:^BOOL (YTSettingsCell *cell, NSUInteger arg1) { [[NSUserDefaults standardUserDefaults] setInteger:4 forKey:@"GestureHUDSize"]; [[NSUserDefaults standardUserDefaults] synchronize]; [settingsViewController reloadData]; return YES; }]
- ];
- YTSettingsPickerViewController *picker = [[%c(YTSettingsPickerViewController) alloc] initWithNavTitle:LOC(@"GESTURE_HUD_SIZE") pickerSectionTitle:nil rows:rows selectedItemIndex:currentVal parentResponder:[self parentResponder]];
- [settingsViewController pushViewController:picker];
- return YES;
- }
- ],
- [YTSettingsSectionItemClass itemWithTitle:LOC(@"GESTURE_HUD_POSITION")
- titleDescription:LOC(@"GESTURE_HUD_POSITION_DESC")
- accessibilityIdentifier:nil
- detailTextBlock:nil
- selectBlock:^BOOL (YTSettingsCell *cell, NSUInteger arg1) {
- int currentVal = [[NSUserDefaults standardUserDefaults] objectForKey:@"GestureHUDPosition"] ? (int)[[NSUserDefaults standardUserDefaults] integerForKey:@"GestureHUDPosition"] : 0;
- NSArray *rows = @[
- [YTSettingsSectionItemClass checkmarkItemWithTitle:LOC(@"TOP") titleDescription:nil selectBlock:^BOOL (YTSettingsCell *cell, NSUInteger arg1) { [[NSUserDefaults standardUserDefaults] setInteger:0 forKey:@"GestureHUDPosition"]; [[NSUserDefaults standardUserDefaults] synchronize]; [settingsViewController reloadData]; return YES; }],
- [YTSettingsSectionItemClass checkmarkItemWithTitle:LOC(@"MIDDLE") titleDescription:nil selectBlock:^BOOL (YTSettingsCell *cell, NSUInteger arg1) { [[NSUserDefaults standardUserDefaults] setInteger:1 forKey:@"GestureHUDPosition"]; [[NSUserDefaults standardUserDefaults] synchronize]; [settingsViewController reloadData]; return YES; }],
- [YTSettingsSectionItemClass checkmarkItemWithTitle:LOC(@"BOTTOM") titleDescription:nil selectBlock:^BOOL (YTSettingsCell *cell, NSUInteger arg1) { [[NSUserDefaults standardUserDefaults] setInteger:2 forKey:@"GestureHUDPosition"]; [[NSUserDefaults standardUserDefaults] synchronize]; [settingsViewController reloadData]; return YES; }]
- ];
- YTSettingsPickerViewController *picker = [[%c(YTSettingsPickerViewController) alloc] initWithNavTitle:LOC(@"GESTURE_HUD_POSITION") pickerSectionTitle:nil rows:rows selectedItemIndex:currentVal parentResponder:[self parentResponder]];
- [settingsViewController pushViewController:picker];
- return YES;
- }
- ],
- BASIC_SWITCH(LOC(@"DISABLES_DOUBLE_TAP"), LOC(@"DISABLES_DOUBLE_TAP_DESC"), DisablesDoubleTap),
- BASIC_SWITCH(LOC(@"DISABLES_LONG_HOLD"), LOC(@"DISABLES_LONG_HOLD_DESC"), DisablesLongHold),
- BASIC_SWITCH(LOC(@"AUTO_EXIT_FULLSCREEN"), LOC(@"AUTO_EXIT_FULLSCREEN_DESC"), AutoExitFullScreen),
- BASIC_SWITCH(LOC(@"AUTO_DISABLES_CAPTION"), LOC(@"AUTO_DISABLES_CAPTION_DESC"), DisablesCaptions),
- BASIC_SWITCH(LOC(@"DISABLES_SHOW_REMAINING"), LOC(@"DISABLES_SHOW_REMAINING_DESC"), DisablesShowRemaining),
- BASIC_SWITCH(LOC(@"ALWAYS_SHOW_REMAINING"), LOC(@"ALWAYS_SHOW_REMAINING_DESC"), AlwaysShowRemaining),
- // BASIC_SWITCH(LOC(@"SHOW_REMAINING_EXTRA"), LOC(@"SHOW_REMAINING_EXTRA_DESC"), ShowExtraTimeRemaining),
- BASIC_SWITCH(LOC(@"HIDE_FULLSCREEN_ACTIONS"), LOC(@"HIDE_FULLSCREEN_ACTIONS_DESC"), HideFullAction),
- BASIC_SWITCH(LOC(@"HIDE_FULL_VID_TITLE"), LOC(@"HIDE_FULL_VID_TITLE_DESC"), HideFullvidTitle),
- BASIC_SWITCH(LOC(@"STOP_AUTOPLAY_VIDEO"), LOC(@"STOP_AUTOPLAY_VIDEO_DESC"), StopAutoplayVideo),
- BASIC_SWITCH(LOC(@"HIDE_CONTENT_WARNING"), LOC(@"HIDE_CONTENT_WARNING_DESC"), HideContentWarning),
- BASIC_SWITCH(LOC(@"AUTO_FULLSCREEN"), LOC(@"AUTO_FULLSCREEN_DESC"), AutoFullScreen),
- BASIC_SWITCH(LOC(@"PORTRAIT_FULLSCREEN"), LOC(@"PORTRAIT_FULLSCREEN_DESC"), PortFull),
- BASIC_SWITCH(LOC(@"OLD_QUALITY_PICKER"), LOC(@"OLD_QUALITY_PICKER_DESC"), OldQualityPicker),
- BASIC_SWITCH(LOC(@"EXTRA_SPEED"), LOC(@"EXTRA_SPEED_DESC"), ExtraSpeed),
- BASIC_SWITCH(LOC(@"DISABLE_HINTS"), LOC(@"DISABLE_HINTS_DESC"), DisableHints),
- BASIC_SWITCH(LOC(@"FORCE_MINIPLAYER"), LOC(@"FORCE_MINIPLAYER_DESC"), ForceMiniPlayer),
- BASIC_SWITCH(LOC(@"FORCE_SEEKBAR"), LOC(@"FORCE_SEEKBAR_DESC"), AlwaysShowSeekbar),
- BASIC_SWITCH(LOC(@"HIDE_LIKE_BUTTON"), LOC(@"HIDE_LIKE_BUTTON_DESC"), HideLikeButton),
- BASIC_SWITCH(LOC(@"HIDE_DISLIKE_BUTTON"), LOC(@"HIDE_DISLIKE_BUTTON_DESC"), HideDisLikeButton),
- BASIC_SWITCH(LOC(@"HIDE_SHARE_BUTTON"), LOC(@"HIDE_SHARE_BUTTON_DESC"), HideShareButton),
- BASIC_SWITCH(LOC(@"HIDE_DOWNLOAD_BUTTON"), LOC(@"HIDE_DOWNLOAD_BUTTON_DESC"), HideDownloadButton),
- BASIC_SWITCH(LOC(@"HIDE_CLIP_BUTTON"), LOC(@"HIDE_CLIP_BUTTON_DESC"), HideClipButton),
- BASIC_SWITCH(LOC(@"HIDE_REMIX_BUTTON"), LOC(@"HIDE_REMIX_BUTTON_DESC"), HideRemixButton),
- BASIC_SWITCH(LOC(@"HIDE_SAVE_BUTTON"), LOC(@"HIDE_SAVE_BUTTON_DESC"), HideSaveButton),
- ];
- YTSettingsPickerViewController *picker = [[%c(YTSettingsPickerViewController) alloc] initWithNavTitle:LOC(@"PLAYER") pickerSectionTitle:nil rows:rows selectedItemIndex:0 parentResponder:[self parentResponder]];
- [settingsViewController pushViewController:picker];
+ YMPushSubSettings(LOC(@"PLAYER"), @[
+ YMPicker(LOC(@"QUALITY_WIFI"), LOC(@"QUALITY_WIFI_DESC"), WifiQualityIndex, (@[LOC(@"DEFAULT"), LOC(@"BEST"), @"2160p60", @"2160p", @"1440p60", @"1440p", @"1080p60", @"1080p", @"720p60", @"720p", @"480p", @"360p", @"240p", @"144p"]), 0),
+ YMPicker(LOC(@"QUALITY_CELLULAR"), LOC(@"QUALITY_CELLULAR_DESC"), CellQualityIndex, (@[LOC(@"DEFAULT"), LOC(@"BEST"), @"2160p60", @"2160p", @"1440p60", @"1440p", @"1080p60", @"1080p", @"720p60", @"720p", @"480p", @"360p", @"240p", @"144p"]), 0),
+ YMPicker(LOC(@"QUALITY_LOW_POWER"), LOC(@"QUALITY_LOW_POWER_DESC"), LowPowerQualityIndex, (@[LOC(@"DEFAULT"), LOC(@"BEST"), @"2160p60", @"2160p", @"1440p60", @"1440p", @"1080p60", @"1080p", @"720p60", @"720p", @"480p", @"360p", @"240p", @"144p"]), 0),
+ YMTextSegment(LOC(@"AUDIO_TRACK"), AudioTrack, (@[LOC(@"DEFAULT"), LOC(@"ORIGINAL"), LOC(@"SELECT_MANUALLY")]), 0),
+ YMPicker(LOC(@"AUDIO_TRACK_SELECT"), LOC(@"AUDIO_TRACK_SELECT_DESC"), AudioTrackLangIndex, getAllSystemLanguageTitles(), 0),
+ YMToggle(LOC(@"NO_AUTO_DUBBED"), LOC(@"NO_AUTO_DUBBED_DESC"), NoDubbedAudioTrack),
+ YMTextSegment(LOC(@"CAPTION_TRACK"), CaptionTrack, (@[LOC(@"DEFAULT"), LOC(@"DISABLED"), LOC(@"SELECT_MANUALLY")]), 0),
+ YMPicker(LOC(@"CAPTION_TRACK_SELECT"), LOC(@"CAPTION_TRACK_SELECT_DESC"), CaptionTrackLangIndex, getAllSystemLanguageTitles(), 0),
+ YMToggle(LOC(@"DISABLES_CAPTION_TRACK"), LOC(@"DISABLES_CAPTION_TRACK_DESC"), DisablesCaptionTrack),
+ YMHeader(LOC(@"INTERFACE")),
+ YMPicker(LOC(@"HOLD_TO_SPEED"), LOC(@"HOLD_TO_SPEED_DESC"), HoldToSpeedIndex, (@[LOC(@"DEFAULT"), @"0.25x", @"0.5x", @"0.75x", @"1x", @"1.25x", @"1.5x", @"1.75x", @"2x", @"3x", @"4x", @"5x"]), 0),
+ YMToggle(LOC(@"MUTE_BUTTON"), LOC(@"MUTE_BUTTON_DESC"), MuteButton),
+ YMToggle(LOC(@"SPEED_BUTTON"), LOC(@"SPEED_BUTTON_DESC"), SpeedButton),
+ YMToggle(LOC(@"SHARE_BUTTON"), LOC(@"SHARE_BUTTON_DESC"), ShareButton),
+ YMToggle(LOC(@"LOOP_BUTTON"), LOC(@"LOOP_BUTTON_DESC"), LoopButton),
+ YMToggle(LOC(@"CAPTION_BUTTON"), LOC(@"CAPTION_BUTTON_DESC"), CaptionButton),
+ YMToggle(LOC(@"HIDE_AUTOPLAY"), LOC(@"HIDE_AUTOPLAY_DESC"), HideAutoPlayToggle),
+ YMToggle(LOC(@"HIDE_FULL_VID_TITLE"), LOC(@"HIDE_FULL_VID_TITLE_DESC"), HideFullvidTitle),
+ YMToggle(LOC(@"HIDE_CAPTIONS_BUTTON"), LOC(@"HIDE_CAPTIONS_BUTTON_DESC"), HideCaptionsButton),
+ YMToggle(LOC(@"HIDE_CAST_BUTTON_PLAYER"), LOC(@"HIDE_CAST_BUTTON_PLAYER_DESC"), HideCastButtonPlayer),
+ YMToggle(LOC(@"HIDE_NEXT_AND_PREV_BUTTON"), LOC(@"HIDE_NEXT_AND_PREV_BUTTON_DESC"), HideNextAndPrevButtons),
+ YMToggle(LOC(@"REPLACE_PREVNEXT_BUTTONS"), LOC(@"REPLACE_PREVNEXT_BUTTONS_DESC"), ReplacePrevNextButtons),
+ YMToggle(LOC(@"REMOVE_AMBIANT"), LOC(@"REMOVE_AMBIANT_DESC"), RemoveAmbiant),
+ YMToggle(LOC(@"REMOVE_DARK_OVERLAY"), LOC(@"REMOVE_DARK_OVERLAY_DESC"), RemoveDarkOverlay),
+ YMToggle(LOC(@"HIDE_END_SCREEN"), LOC(@"HIDE_END_SCREEN_DESC"), HideEndScreenCards),
+ YMToggle(LOC(@"HIDE_SUGGESTED_VIDEO"), LOC(@"HIDE_SUGGESTED_VIDEO_DESC"), HideSuggestedVideo),
+ YMToggle(LOC(@"HIDE_PAID_OVERLAY"), LOC(@"HIDE_PAID_OVERLAY_DESC"), HidePaidPromoOverlay),
+ YMToggle(LOC(@"HIDE_WATERMARK"), LOC(@"HIDE_WATERMARK_DESC"), HideWaterMark),
+ YMToggle(LOC(@"HIDE_FULLSCREEN_ACTIONS"), LOC(@"HIDE_FULLSCREEN_ACTIONS_DESC"), HideFullAction),
+ YMToggle(LOC(@"FORCE_SEEKBAR"), LOC(@"FORCE_SEEKBAR_DESC"), AlwaysShowSeekbar),
+ YMToggle(LOC(@"DISABLES_SHOW_REMAINING"), LOC(@"DISABLES_SHOW_REMAINING_DESC"), DisablesShowRemaining),
+ YMToggle(LOC(@"ALWAYS_SHOW_REMAINING"), LOC(@"ALWAYS_SHOW_REMAINING_DESC"), AlwaysShowRemaining),
+ YMToggle(LOC(@"SHOW_REMAINING_EXTRA"), LOC(@"SHOW_REMAINING_EXTRA_DESC"), ShowExtraTimeRemaining),
+ YMToggle(LOC(@"USES_24_HOURS_TIME"), LOC(@"USES_24_HOURS_TIME_DESC"), Uses24HoursTime),
+ YMToggle(LOC(@"OLD_QUALITY_PICKER"), LOC(@"OLD_QUALITY_PICKER_DESC"), OldQualityPicker),
+ YMToggle(LOC(@"EXTRA_SPEED"), LOC(@"EXTRA_SPEED_DESC"), ExtraSpeed),
+ YMToggle(LOC(@"USE_ANOTHER_MINIPLAYER"), LOC(@"USE_ANOTHER_MINIPLAYER_DESC"), UseAnotherMiniplayer),
+ YMToggle(LOC(@"PORTRAIT_FULLSCREEN"), LOC(@"PORTRAIT_FULLSCREEN_DESC"), PortFull),
+ YMToggle(LOC(@"HIDE_COMMENTS_SECTION"), LOC(@"HIDE_COMMENTS_SECTION_DESC"), HideCommentsSection),
+ YMToggle(LOC(@"HIDE_COMMENTS_PREVIEW"), LOC(@"HIDE_COMMENTS_PREVIEW_DESC"), HideCommentsPreview),
+ YMHeader(LOC(@"CONTROL_CENTER")),
+ YMToggle(LOC(@"SKIP_BACKWARD"), LOC(@"SKIP_BACKWARD_DESC"), SkipBackwardEnabled),
+ YMSlider(LOC(@"REWIND_SECONDS"), nil, RewindSeconds, 5, 60, 5, 10),
+ YMToggle(LOC(@"SKIP_FORWARD"), LOC(@"SKIP_FORWARD_DESC"), SkipForwardEnabled),
+ YMSlider(LOC(@"FORWARD_SECONDS"), nil, ForwardSeconds, 5, 60, 5, 10),
+ YMHeader(LOC(@"PLAYER_ACTIONS")),
+ YMPicker(LOC(@"DEFAULT_SPEED"), LOC(@"DEFAULT_SPEED_DESC"), AutoSpeedIndex, (@[LOC(@"DISABLED"), @"0.25x", @"0.5x", @"0.75x", @"1x", @"1.25x", @"1.5x", @"1.75x", @"2x", @"3x", @"4x", @"5x"]), 0),
+ YMToggle(LOC(@"FORCE_MINIPLAYER"), LOC(@"FORCE_MINIPLAYER_DESC"), ForceMiniPlayer),
+ YMToggle(LOC(@"HIDE_CONTENT_WARNING"), LOC(@"HIDE_CONTENT_WARNING_DESC"), HideContentWarning),
+ YMToggle(LOC(@"STOP_AUTOPLAY_VIDEO"), LOC(@"STOP_AUTOPLAY_VIDEO_DESC"), StopAutoplayVideo),
+ YMToggle(LOC(@"AUTO_FULLSCREEN"), LOC(@"AUTO_FULLSCREEN_DESC"), AutoFullScreen),
+ YMToggle(LOC(@"AUTO_EXIT_FULLSCREEN"), LOC(@"AUTO_EXIT_FULLSCREEN_DESC"), AutoExitFullScreen),
+ YMHeader(LOC(@"GESTURE_HEADER")),
+ YMToggle(LOC(@"GESTURES"), LOC(@"GESTURES_DESC"), GestureControls),
+ YMPicker(LOC(@"GESTURE_AREA"), LOC(@"GESTURE_AREA_DESC"), GestureActivationArea, (@[@"10%", @"15%", @"20%", @"25%", @"30%", @"35%", @"40%", @"45%", @"50%"]), 1),
+ YMPicker(LOC(@"LEFT_SIDE_GESTURE"), nil, LeftSideGesture, (@[LOC(@"GESTURE_NONE"), LOC(@"GESTURE_BRIGHTNESS"), LOC(@"GESTURE_VOLUME"), LOC(@"GESTURE_SPEED")]), 1),
+ YMPicker(LOC(@"RIGHT_SIDE_GESTURE"), nil, RightSideGesture, (@[LOC(@"GESTURE_NONE"), LOC(@"GESTURE_BRIGHTNESS"), LOC(@"GESTURE_VOLUME"), LOC(@"GESTURE_SPEED")]), 2),
+ YMToggle(LOC(@"GESTURE_HUD"), LOC(@"GESTURE_HUD_DESC"), GestureHUD),
+ YMPicker(LOC(@"GESTURE_HUD_SIZE"), LOC(@"GESTURE_HUD_SIZE_DESC"), GestureHUDSize, (@[LOC(@"SMALL"), LOC(@"NORMAL"), LOC(@"LARGE"), LOC(@"EXTRALARGE"), LOC(@"MAX")]), 1),
+ YMPicker(LOC(@"GESTURE_HUD_POSITION"), LOC(@"GESTURE_HUD_POSITION_DESC"), GestureHUDPosition, (@[LOC(@"TOP"), LOC(@"MIDDLE"), LOC(@"BOTTOM")]), 0),
+ YMHeader(@""),
+ YMToggle(LOC(@"TAP_TO_SEEK"), LOC(@"TAP_TO_SEEK_DESC"), TapToSeek),
+ YMToggle(LOC(@"PAUSE_TWO_FINGERS"), LOC(@"PAUSE_TWO_FINGERS_DESC"), PauseTwoFingers),
+ YMToggle(LOC(@"PAUSE_ON_OVERLAY"), LOC(@"PAUSE_ON_OVERLAY_DESC"), PauseOnOverlay),
+ YMToggle(LOC(@"COPY_TIMESTAMP_ON_PAUSE"), LOC(@"COPY_TIMESTAMP_ON_PAUSE_DESC"), CopyWithTimestampOnPause),
+ YMToggle(LOC(@"DISABLES_DOUBLE_TAP"), LOC(@"DISABLES_DOUBLE_TAP_DESC"), DisablesDoubleTap),
+ YMToggle(LOC(@"DISABLES_LONG_HOLD"), LOC(@"DISABLES_LONG_HOLD_DESC"), DisablesLongHold),
+ YMToggle(LOC(@"DISABLES_ZOOM"), LOC(@"DISABLES_ZOOM_DESC"), DisablesFreeZoom),
+ YMToggle(LOC(@"DISABLES_SNAP_TO_CHAPTER"), LOC(@"DISABLES_SNAP_TO_CHAPTER_DESC"), DontSnapToChapter),
+ YMToggle(LOC(@"DISABLES_ENGAGE_PANEL"), LOC(@"DISABLES_ENGAGE_PANEL_DESC"), DisablesEngagementPanel),
+ ], settingsViewController, [self parentResponder]);
return YES;
}];
YTIIcon *icon3 = [%c(YTIIcon) new];
@@ -414,27 +348,18 @@ static NSString *GetCacheSize() { // YTLite - @dayanch96
// Section 6
// Shorts
YTSettingsSectionItem *shortsgroup = [YTSettingsSectionItemClass itemWithTitle:LOC(@"SHORTS") accessibilityIdentifier:nil detailTextBlock:nil selectBlock:^BOOL (YTSettingsCell *cell, NSUInteger arg1) {
- NSArray *rows = @[
- SETTINGS_HEADER,
- BASIC_SWITCH(LOC(@"HIDE_SHORTS_LIKE_BUTTON"), LOC(@"HIDE_SHORTS_LIKE_BUTTON_DESC"), HideShortsLikeButton),
- BASIC_SWITCH(LOC(@"HIDE_SHORTS_DISLIKE_BUTTON"), LOC(@"HIDE_SHORTS_DISLIKE_BUTTON_DESC"), HideShortsDisLikeButton),
- BASIC_SWITCH(LOC(@"HIDE_SHORTS_COMMENT_BUTTON"), LOC(@"HIDE_SHORTS_COMMENT_BUTTON_DESC"), HideShortsCommentButton),
- BASIC_SWITCH(LOC(@"HIDE_SHORTS_SHARE_BUTTON"), LOC(@"HIDE_SHORTS_SHARE_BUTTON_DESC"), HideShortsShareButton),
- BASIC_SWITCH(LOC(@"HIDE_SHORTS_REMIX_BUTTON"), LOC(@"HIDE_SHORTS_REMIX_BUTTON_DESC"), HideShortsRemixButton),
- BASIC_SWITCH(LOC(@"HIDE_METADATA_BUTTON"), LOC(@"HIDE_METADATA_BUTTON_DESC"), HideShortsMetaButton),
- BASIC_SWITCH(LOC(@"HIDE_SHORTS_PRODUCT"), LOC(@"HIDE_SHORTS_PRODUCT_DESC"), HideShortsProducts),
- BASIC_SWITCH(LOC(@"HIDE_SHORTS_RECBAR"), LOC(@"HIDE_SHORTS_RECBAR_DESC"), HideShortsRecbar),
- BASIC_SWITCH(LOC(@"HIDE_SHORTS_COMMIT"), LOC(@"HIDE_SHORTS_COMMIT_DESC"), HideShortsCommit),
- BASIC_SWITCH(LOC(@"HIDE_SHORTS_SUBSCRIPT_BUTTON"), LOC(@"HIDE_SHORTS_SUBSCRIPT_BUTTON_DESC"), HideShortsSubscriptButton),
- BASIC_SWITCH(LOC(@"HIDE_SHORTS_LIVE_BUTTON"), LOC(@"HIDE_SHORTS_LIVE_BUTTON_DESC"), HideShortsLiveButton),
- BASIC_SWITCH(LOC(@"HIDE_SHORTS_LENS_BUTTON"), LOC(@"HIDE_SHORTS_LENS_BUTTON_DESC"), HideShortsLensButton),
- BASIC_SWITCH(LOC(@"HIDE_SHORTS_TRENDS_BUTTON"), LOC(@"HIDE_SHORTS_TRENDS_BUTTON_DESC"), HideShortsTrendsButton),
- BASIC_SWITCH(LOC(@"HIDE_SHORTS_TO_VIDEO"), LOC(@"HIDE_SHORTS_TO_VIDEO_DESC"), HideShortsToVideo),
- BASIC_SWITCH(LOC(@"ENABLES_SHORTS_QUALITY"), LOC(@"ENABLES_SHORTS_QUALITY_DESC"), EnablesShortsQuality),
- BASIC_SWITCH(LOC(@"SHOW_SHORTS_SEEKBAR"), LOC(@"SHOW_SHORTS_SEEKBAR_DESC"), ShowShortsSeekbar),
- ];
- YTSettingsPickerViewController *picker = [[%c(YTSettingsPickerViewController) alloc] initWithNavTitle:LOC(@"SHORTS") pickerSectionTitle:nil rows:rows selectedItemIndex:0 parentResponder:[self parentResponder]];
- [settingsViewController pushViewController:picker];
+ YMPushSubSettings(LOC(@"SHORTS"), @[
+ YMTextSegment(LOC(@"SHORTS_ACTION"), ShortsActionIndex, (@[LOC(@"LOOP"), LOC(@"SKIP_TO_NEXT_SHORTS"), LOC(@"PAUSE_SHORTS")]), 0),
+ YMToggle(LOC(@"ENABLES_SHORTS_QUALITY"), LOC(@"ENABLES_SHORTS_QUALITY_DESC"), EnablesShortsQuality),
+ YMToggle(LOC(@"SHOW_SHORTS_SEEKBAR"), LOC(@"SHOW_SHORTS_SEEKBAR_DESC"), ShowShortsSeekbar),
+ YMToggle(LOC(@"SHORTS_ONLY"), LOC(@"SHORTS_ONLY_DESC"), ShortsOnly),
+ YMToggle(LOC(@"SHORTS_FULLSCREEN"), LOC(@"SHORTS_FULLSCREEN_DESC"), FullScreenShorts),
+ YMToggle(LOC(@"REMOVE_LIVE_SHORTS"), LOC(@"REMOVE_LIVE_SHORTS_DESC"), RemoveShortsLive),
+ YMToggle(LOC(@"REMOVE_POSTS_SHORTS"), LOC(@"REMOVE_POSTS_SHORTS_DESC"), RemoveShortsPosts),
+ YMHeader(LOC(@"INTERFACE")),
+ YMToggle(LOC(@"HIDE_SHORTS_PRODUCT"), LOC(@"HIDE_SHORTS_PRODUCT_DESC"), HideShortsProducts),
+ YMToggle(LOC(@"HIDE_SHORTS_RECBAR"), LOC(@"HIDE_SHORTS_RECBAR_DESC"), HideShortsRecbar),
+ ], settingsViewController, [self parentResponder]);
return YES;
}];
YTIIcon *icon4 = [%c(YTIIcon) new];
@@ -445,48 +370,57 @@ static NSString *GetCacheSize() { // YTLite - @dayanch96
// Section 7
// Tab bar
YTSettingsSectionItem *tabgroup = [YTSettingsSectionItemClass itemWithTitle:LOC(@"TABBAR") accessibilityIdentifier:nil detailTextBlock:nil selectBlock:^BOOL (YTSettingsCell *cell, NSUInteger arg1) {
- NSArray *rows = @[
- SETTINGS_HEADER,
- [YTSettingsSectionItemClass itemWithTitle:LOC(@"DEFAULT_TAB")
- titleDescription:LOC(@"DEFAULT_TAB_DESC")
- accessibilityIdentifier:nil
- detailTextBlock:nil
- selectBlock:^BOOL (YTSettingsCell *cell, NSUInteger arg1) {
- NSArray *rows = @[
- [YTSettingsSectionItemClass checkmarkItemWithTitle:LOC(@"HOME_NAME") titleDescription:nil selectBlock:^BOOL (YTSettingsCell *cell, NSUInteger arg1) {
- [[NSUserDefaults standardUserDefaults] setInteger:0 forKey:DefaultTab];
- [settingsViewController reloadData];
- return YES;
- }],
- [YTSettingsSectionItemClass checkmarkItemWithTitle:LOC(@"Shorts") titleDescription:nil selectBlock:^BOOL (YTSettingsCell *cell, NSUInteger arg1) {
- [[NSUserDefaults standardUserDefaults] setInteger:1 forKey:DefaultTab];
- [settingsViewController reloadData];
- return YES;
- }],
- [YTSettingsSectionItemClass checkmarkItemWithTitle:LOC(@"SUBSCRIPT_NAME") titleDescription:nil selectBlock:^BOOL (YTSettingsCell *cell, NSUInteger arg1) {
- [[NSUserDefaults standardUserDefaults] setInteger:2 forKey:DefaultTab];
- [settingsViewController reloadData];
- return YES;
- }],
- [YTSettingsSectionItemClass checkmarkItemWithTitle:LOC(@"LIB_NAME") titleDescription:nil selectBlock:^BOOL (YTSettingsCell *cell, NSUInteger arg1) {
- [[NSUserDefaults standardUserDefaults] setInteger:3 forKey:DefaultTab];
- [settingsViewController reloadData];
- return YES;
- }]
- ];
- YTSettingsPickerViewController *picker = [[%c(YTSettingsPickerViewController) alloc] initWithNavTitle:LOC(@"DEFAULT_TAB") pickerSectionTitle:nil rows:rows selectedItemIndex:INTFORVAL(DefaultTab) parentResponder:[self parentResponder]];
- [settingsViewController pushViewController:picker];
- return YES;
- }],
- BASIC_SWITCH(LOC(@"HIDE_TAB_INDI"), LOC(@"HIDE_TAB_INDI_DESC"), HideTabIndi),
- BASIC_SWITCH(LOC(@"HIDE_TAB_LABELS"), LOC(@"HIDE_TAB_LABELS_DESC"), HideTabLabels),
- BASIC_SWITCH(LOC(@"HIDE_HOME_TAB"), LOC(@"HIDE_HOME_TAB_DESC"), HideHomeTab),
- BASIC_SWITCH(LOC(@"HIDE_SHORTS_TAB"), LOC(@"HIDE_SHORTS_TAB_DESC"), HideShortsTab),
- BASIC_SWITCH(LOC(@"HIDE_CREATE_BUTTON"), LOC(@"HIDE_CREATE_BUTTON_DESC"), HideCreateButton),
- BASIC_SWITCH(LOC(@"HIDE_SUBSCRIPT_TAB"), LOC(@"HIDE_SUBSCRIPT_TAB_DESC"), HideSubscriptTab),
- ];
- YTSettingsPickerViewController *picker = [[%c(YTSettingsPickerViewController) alloc] initWithNavTitle:LOC(@"TABBAR") pickerSectionTitle:nil rows:rows selectedItemIndex:0 parentResponder:[self parentResponder]];
- [settingsViewController pushViewController:picker];
+ // Build dynamic image list from enabled tabs (standard + custom)
+ NSDictionary *tabYTIconMap = @{@"home": @(65), @"shorts": @(769), @"subscriptions": @(66), @"library": @(61)};
+ NSDictionary *tabBundleIconMap = @{@"history": @"icons/history", @"gaming": @"icons/gaming", @"sports": @"icons/sports", @"notifications": @"icons/noti", @"news": @"icons/news", @"music": @"icons/music", @"watchlater": @"icons/watchlater", @"playlist": @"icons/playlist", @"like": @"icons/like", @"live": @"icons/live", @"post": @"icons/post", @"video": @"icons/video", @"movie": @"icons/movie", @"course": @"icons/course", @"minigame": @"icons/minigame"};
+ NSBundle *ymBundle = [NSBundle bundleWithPath:[[NSBundle mainBundle] pathForResource:@"YouMod" ofType:@"bundle"]];
+ YTAssetLoader *assetLoader = [[%c(YTAssetLoader) alloc] initWithBundle:ymBundle];
+
+ NSMutableArray *defaultTabImages = [NSMutableArray array];
+ NSArray *savedOrder = [[NSUserDefaults standardUserDefaults] arrayForKey:TabOrder];
+ if (savedOrder.count > 0) {
+ for (NSDictionary *entry in savedOrder) {
+ if (![entry[@"enabled"] boolValue]) continue;
+ NSString *tabID = entry[@"id"];
+ if ([tabID isEqualToString:@"create"]) continue;
+ NSNumber *ytIconType = tabYTIconMap[tabID];
+ if (ytIconType) {
+ YTIIcon *icon = [%c(YTIIcon) new];
+ icon.iconType = [ytIconType intValue];
+ UIImage *img = [icon iconImageWithColor:[UIColor whiteColor]];
+ if (img) [defaultTabImages addObject:img];
+ } else {
+ NSString *bundleName = tabBundleIconMap[tabID];
+ if (bundleName) {
+ UIImage *img = [assetLoader imageNamed:bundleName];
+ if (img) {
+ UIImage *whiteImg = [img imageWithTintColor:[UIColor whiteColor] renderingMode:UIImageRenderingModeAlwaysOriginal];
+ [defaultTabImages addObject:whiteImg];
+ }
+ }
+ }
+ }
+ }
+ if (defaultTabImages.count == 0) {
+ NSArray *fallbackIcons = @[@(65), @(769), @(66), @(61)];
+ for (NSNumber *iconType in fallbackIcons) {
+ YTIIcon *icon = [%c(YTIIcon) new];
+ icon.iconType = [iconType intValue];
+ UIImage *img = [icon iconImageWithColor:[UIColor whiteColor]];
+ if (img) [defaultTabImages addObject:img];
+ }
+ }
+
+ YMPushSubSettings(LOC(@"TABBAR"), @[
+ YMImageSegment(LOC(@"DEFAULT_TAB"), DefaultTab, defaultTabImages, 0),
+ YMTextSegment(LOC(@"FORSTED_TAB_BAR"), UseFrostedTabBar, (@[LOC(@"DEFAULT"),LOC(@"ENABLED"), LOC(@"DISABLED")]), 0),
+ YMToggle(LOC(@"HIDE_TAB_INDI"), LOC(@"HIDE_TAB_INDI_DESC"), HideTabIndi),
+ YMToggle(LOC(@"HIDE_TAB_LABELS"), LOC(@"HIDE_TAB_LABELS_DESC"), HideTabLabels),
+ YMAction(LOC(@"MANAGE_TABS"), LOC(@"MANAGE_TABS_DESC"), ^(UIViewController *vc) {
+ (void)vc;
+ YMPushTabOrder(settingsViewController, [self parentResponder]);
+ }),
+ ], settingsViewController, [self parentResponder]);
return YES;
}];
YTIIcon *icon5 = [%c(YTIIcon) new];
@@ -497,21 +431,42 @@ static NSString *GetCacheSize() { // YTLite - @dayanch96
// Section 8
// Miscellaneous
YTSettingsSectionItem *othergroup = [YTSettingsSectionItemClass itemWithTitle:LOC(@"MISCELLANEOUS") accessibilityIdentifier:nil detailTextBlock:nil selectBlock:^BOOL (YTSettingsCell *cell, NSUInteger arg1) {
- NSArray *rows = @[
- SETTINGS_HEADER,
- BASIC_SWITCH(LOC(@"BACKGROUND_PLAYBACK"), LOC(@"BACKGROUND_PLAYBACK_DESC"), BackgroundPlayback),
- BASIC_SWITCH(LOC(@"DISABLES_SHORTS_PIP"), LOC(@"DISABLES_SHORTS_PIP_DESC"), DisablesShortsPiP),
- BASIC_SWITCH(LOC(@"BLOCK_UPGRADE_DIALOGS"), LOC(@"BLOCK_UPGRADE_DIALOGS_DESC"), BlockUpgradeDialogs),
- BASIC_SWITCH(LOC(@"ARE_YOU_THERE_DIALOG"), LOC(@"ARE_YOU_THERE_DIALOG_DESC"), HideAreYouThereDialog),
- BASIC_SWITCH(LOC(@"FIXES_SLOW_MINIPLAYER"), LOC(@"FIXES_SLOW_MINIPLAYER_DESC"), FixesSlowMiniPlayer),
- BASIC_SWITCH(LOC(@"DISABLES_NEW_MINIPLAYER"), LOC(@"DISABLES_NEW_MINIPLAYER_DESC"), DisablesNewMiniPlayer),
- BASIC_SWITCH(LOC(@"DISABLES_SNACK_BAR"), LOC(@"DISABLES_SNACK_BAR_DESC"), DisablesSnackBar),
- BASIC_SWITCH(LOC(@"HIDE_STARTUP_ANIMATIONS"), LOC(@"HIDE_STARTUP_ANIMATIONS_DESC"), HideStartupAni),
- BASIC_SWITCH(LOC(@"HIDE_PLAY_IN_NEXT_QUEUE"), LOC(@"HIDE_PLAY_IN_NEXT_QUEUE_DESC"), HidePlayInNextQueue),
- BASIC_SWITCH(LOC(@"HIDE_LIKE_DISLIKE_VOTES"), LOC(@"HIDE_LIKE_DISLIKE_VOTES_DESC"), HideLikeDislikeVotes),
- ];
- YTSettingsPickerViewController *picker = [[%c(YTSettingsPickerViewController) alloc] initWithNavTitle:LOC(@"MISCELLANEOUS") pickerSectionTitle:nil rows:rows selectedItemIndex:0 parentResponder:[self parentResponder]];
- [settingsViewController pushViewController:picker];
+ YMPushSubSettings(LOC(@"MISCELLANEOUS"), @[
+ YMToggle(LOC(@"BACKGROUND_PLAYBACK"), LOC(@"BACKGROUND_PLAYBACK_DESC"), BackgroundPlayback),
+ YMToggle(LOC(@"DISABLES_SHORTS_PIP"), LOC(@"DISABLES_SHORTS_PIP_DESC"), DisablesShortsPiP),
+ YMToggle(LOC(@"DISABLE_HINTS"), LOC(@"DISABLE_HINTS_DESC"), DisableHints),
+ YMToggle(LOC(@"BLOCK_UPGRADE_DIALOGS"), LOC(@"BLOCK_UPGRADE_DIALOGS_DESC"), BlockUpgradeDialogs),
+ YMToggle(LOC(@"ARE_YOU_THERE_DIALOG"), LOC(@"ARE_YOU_THERE_DIALOG_DESC"), HideAreYouThereDialog),
+ YMToggle(LOC(@"FIXES_SLOW_MINIPLAYER"), LOC(@"FIXES_SLOW_MINIPLAYER_DESC"), FixesSlowMiniPlayer),
+ YMToggle(LOC(@"DISABLES_NEW_MINIPLAYER"), LOC(@"DISABLES_NEW_MINIPLAYER_DESC"), DisablesNewMiniPlayer),
+ YMToggle(LOC(@"DISABLES_SNACK_BAR"), LOC(@"DISABLES_SNACK_BAR_DESC"), DisablesSnackBar),
+ YMToggle(LOC(@"HIDE_STARTUP_ANIMATIONS"), LOC(@"HIDE_STARTUP_ANIMATIONS_DESC"), HideStartupAni),
+ YMToggle(LOC(@"HIDE_LIKE_DISLIKE_VOTES"), LOC(@"HIDE_LIKE_DISLIKE_VOTES_DESC"), HideLikeDislikeVotes),
+ YMToggle(LOC(@"HIDE_COMMU_GUIDE"), LOC(@"HIDE_COMMU_GUIDE_DESC"), HideCommuGuide),
+ YMToggle(LOC(@"DISABLES_RTL"), LOC(@"DISABLES_RTL_DESC"), DisablesRTL),
+ YMTextSegment(LOC(@"DEVICE_UI"), DeviceUIIndex, (@[LOC(@"DEFAULT"), LOC(@"iPad"), LOC(@"iPhone")]), 0),
+ YMHeader(LOC(@"FLYOUT_MENU")),
+ YMToggle(LOC(@"REMOVE_PLAY_IN_NEXT_QUEUE_OPTION"), LOC(@"REMOVE_PLAY_IN_NEXT_QUEUE_OPTION_DESC"), RemovePlayInNextQueueOption),
+ YMToggle(LOC(@"REMOVE_DOWNLOAD_OPTION"), LOC(@"REMOVE_DOWNLOAD_OPTION_DESC"), RemoveDownloadOption),
+ YMToggle(LOC(@"REMOVE_WATCH_LATER_OPTION"), LOC(@"REMOVE_WATCH_LATER_OPTION_DESC"), RemoveWatchLaterOption),
+ YMToggle(LOC(@"REMOVE_SAVE_OPTION"), LOC(@"REMOVE_SAVE_OPTION_DESC"), RemoveSaveOption),
+ YMToggle(LOC(@"REMOVE_REMOVE_FROM_PLAYLIST_OPTION"), LOC(@"REMOVE_REMOVE_FROM_PLAYLIST_OPTION_DESC"), RemoveRemoveFromPlaylistOption),
+ YMToggle(LOC(@"REMOVE_SHARE_OPTION"), LOC(@"REMOVE_SHARE_OPTION_DESC"), RemoveShareOption),
+ YMToggle(LOC(@"REMOVE_NOT_INTERESTED_OPTION"), LOC(@"REMOVE_NOT_INTERESTED_OPTION_DESC"), RemoveNotInterestedOption),
+ YMToggle(LOC(@"REMOVE_DONT_RECOMMEND_OPTION"), LOC(@"REMOVE_DONT_RECOMMEND_OPTION_DESC"), RemoveDontRecommendOption),
+ YMToggle(LOC(@"REMOVE_INFO_OPTION"), LOC(@"REMOVE_INFO_OPTION_DESC"), RemoveInfoOption),
+ YMToggle(LOC(@"REMOVE_FILTER_OPTION"), LOC(@"REMOVE_FILTER_OPTION_DESC"), RemoveFilterOption),
+ YMToggle(LOC(@"REMOVE_REPORT_OPTION"), LOC(@"REMOVE_REPORT_OPTION_DESC"), RemoveReportOption),
+ YMToggle(LOC(@"REMOVE_YOUTUBE_MUSIC_OPTION"), LOC(@"REMOVE_YOUTUBE_MUSIC_OPTION_DESC"), RemoveYouTubeMusicOption),
+ YMToggle(LOC(@"REMOVE_FEED_BACK_OPTION"), LOC(@"REMOVE_FEED_BACK_OPTION_DESC"), RemoveFeedBackOption),
+ YMToggle(LOC(@"REMOVE_CAST_OPTION"), LOC(@"REMOVE_CAST_OPTION_DESC"), RemoveCastOption),
+ YMToggle(LOC(@"REMOVE_SHUFFLE_OPTION"), LOC(@"REMOVE_SHUFFLE_OPTION_DESC"), RemoveShuffleOption),
+ YMToggle(LOC(@"REMOVE_UN_SUB_OPTION"), LOC(@"REMOVE_UN_SUB_OPTION_DESC"), RemoveUnSubOption),
+ YMToggle(LOC(@"REMOVE_HIDE_FROM_PLAYLIST_OPTION"), LOC(@"REMOVE_HIDE_FROM_PLAYLIST_OPTION_DESC"), RemoveHideFromPlaylistOption),
+ YMToggle(LOC(@"REMOVE_HELP_OPTION"), LOC(@"REMOVE_HELP_OPTION_DESC"), RemoveHelpOption),
+ YMToggle(LOC(@"REMOVE_NOTIFY_OPTION"), LOC(@"REMOVE_NOTIFY_OPTION_DESC"), RemoveNotifyOption),
+ YMToggle(LOC(@"REMOVE_CLEARSCREEN_OPTION"), LOC(@"REMOVE_CLEARSCREEN_OPTION_DESC"), RemoveClearScreenOption),
+ ], settingsViewController, [self parentResponder]);
return YES;
}];
YTIIcon *icon6 = [%c(YTIIcon) new];
@@ -519,77 +474,99 @@ static NSString *GetCacheSize() { // YTLite - @dayanch96
othergroup.settingIcon = icon6;
[sectionItems addObject:othergroup];
+ // Section: SponsorBlock
+ YTSettingsSectionItem *sponsorblockgroup = [YTSettingsSectionItemClass itemWithTitle:@"SponsorBlock" accessibilityIdentifier:nil detailTextBlock:nil selectBlock:^BOOL (YTSettingsCell *cell, NSUInteger arg1) {
+ [self updateSponsorBlockSectionWithEntry:entry];
+ return YES;
+ }];
+ YTIIcon *iconSB = [%c(YTIIcon) new];
+ iconSB.iconType = 610;
+ sponsorblockgroup.settingIcon = iconSB;
+ [sectionItems addObject:sponsorblockgroup];
+
// Section 9
// Perferences
YTSettingsSectionItem *perfgroup = [YTSettingsSectionItemClass itemWithTitle:LOC(@"PERFER_HEADER") accessibilityIdentifier:nil detailTextBlock:nil selectBlock:^BOOL (YTSettingsCell *cell, NSUInteger arg1) {
- NSArray *rows = @[
- [YTSettingsSectionItemClass itemWithTitle:nil
- titleDescription:LOC(@"PERFER")
- accessibilityIdentifier:nil
- detailTextBlock:nil
- selectBlock:^BOOL (YTSettingsCell *cell, NSUInteger arg1) {
- return NO;
- }
- ],
- [YTSettingsSectionItemClass itemWithTitle:LOC(@"IMPORT")
- titleDescription:LOC(@"IMPORT_DESC")
- accessibilityIdentifier:nil
- detailTextBlock:nil
- selectBlock:^BOOL (YTSettingsCell *cell, NSUInteger arg1) {
- YTAlertView *alertView = [%c(YTAlertView) confirmationDialogWithAction:^{
- [[YouModPrefsManager sharedManager] importYouModSettingsFromVC:settingsViewController];
- } actionTitle:LOC(@"YES")];
- alertView.title = LOC(@"WARNING");
- alertView.subtitle = LOC(@"OVERRIDE");
- [alertView show];
- return YES;
- }
- ],
- [YTSettingsSectionItemClass itemWithTitle:LOC(@"EXPORT")
- titleDescription:LOC(@"EXPORT_DESC")
- accessibilityIdentifier:nil
- detailTextBlock:nil
- selectBlock:^BOOL (YTSettingsCell *cell, NSUInteger arg1) {
- [[YouModPrefsManager sharedManager] exportYouModSettingsFromVC:settingsViewController];
- return YES;
- }
- ],
- [YTSettingsSectionItemClass itemWithTitle:LOC(@"RESTORE")
- titleDescription:LOC(@"RESTORE_DESC")
- accessibilityIdentifier:nil
- detailTextBlock:nil
- selectBlock:^BOOL (YTSettingsCell *cell, NSUInteger arg1) {
- [[YouModPrefsManager sharedManager] restoreYouModDefaults];
- return YES;
- }
- ],
- [YTSettingsSectionItemClass itemWithTitle:nil
- titleDescription:LOC(@"CACHE")
- accessibilityIdentifier:nil
- detailTextBlock:nil
- selectBlock:^BOOL (YTSettingsCell *cell, NSUInteger arg1) {
- return NO;
- }
- ],
- [YTSettingsSectionItemClass itemWithTitle:LOC(@"CLEARCACHE")
- titleDescription:GetCacheSize()
- accessibilityIdentifier:nil
- detailTextBlock:nil
- selectBlock:^BOOL (YTSettingsCell *cell, NSUInteger arg1) {
- dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
- NSString *cachePath = NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES).firstObject;
- [[NSFileManager defaultManager] removeItemAtPath:cachePath error:nil];
- dispatch_async(dispatch_get_main_queue(), ^{
- [[%c(YTToastResponderEvent) eventWithMessage:LOC(@"DONE") firstResponder:[self parentResponder]] send];
- });
+ YMPushSubSettings(LOC(@"PERFER_HEADER"), @[
+ YMHeader(LOC(@"PERFER")),
+ YMAction(LOC(@"IMPORT"), LOC(@"IMPORT_DESC"), ^(UIViewController *vc) {
+ Class alertClass = NSClassFromString(@"YTAlertView");
+ YTAlertView *alertView = [alertClass confirmationDialogWithAction:^{
+ [[YouModPrefsManager sharedManager] importYouModSettingsFromVC:vc];
+ } actionTitle:LOC(@"YES")];
+ alertView.title = LOC(@"WARNING");
+ alertView.subtitle = LOC(@"OVERRIDE");
+ [alertView show];
+ }),
+ YMAction(LOC(@"EXPORT"), LOC(@"EXPORT_DESC"), ^(UIViewController *vc) {
+ [[YouModPrefsManager sharedManager] exportYouModSettingsFromVC:vc];
+ }),
+ YMAction(LOC(@"RESTORE"), LOC(@"RESTORE_DESC"), ^(UIViewController *vc) {
+ [[YouModPrefsManager sharedManager] restoreYouModDefaults];
+ }),
+ YMHeader(LOC(@"CACHE")),
+ YMAction(LOC(@"CLEARCACHE"), GetCacheSize(), ^(UIViewController *vc) {
+ __weak UIViewController *weakVC = vc;
+ NSString *clearTitle = LOC(@"CLEARCACHE");
+ dispatch_async(dispatch_get_main_queue(), ^{
+ __strong UIViewController *strongVC = weakVC;
+ if (!strongVC) return;
+ if ([strongVC respondsToSelector:@selector(items)] && [strongVC respondsToSelector:@selector(tableView)]) {
+ NSArray *items = [(id)strongVC items];
+ for (id item in items) {
+ if ([[item title] isEqualToString:clearTitle]) {
+ [item setSubtitle:@""];
+ break;
+ }
+ }
+ UITableView *tableView = [(id)strongVC tableView];
+ [tableView reloadData];
+ for (UITableViewCell *cell in tableView.visibleCells) {
+ if ([cell.textLabel.text isEqualToString:clearTitle]) {
+ UIActivityIndicatorView *indicator = [cell viewWithTag:0xC0FFEE];
+ if (!indicator) {
+ indicator = [[UIActivityIndicatorView alloc] initWithActivityIndicatorStyle:UIActivityIndicatorViewStyleMedium];
+ indicator.tag = 0xC0FFEE;
+ [indicator startAnimating];
+ cell.accessoryView = indicator;
+ }
+ cell.detailTextLabel.text = @"";
+ break;
+ }
+ }
+ }
+ });
+
+ dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
+ NSString *cachePath = NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES).firstObject;
+ [[NSFileManager defaultManager] removeItemAtPath:cachePath error:nil];
+ dispatch_async(dispatch_get_main_queue(), ^{
+ __strong UIViewController *strongVC = weakVC;
+ if (!strongVC) return;
+ if ([strongVC respondsToSelector:@selector(tableView)]) {
+ UITableView *tableView = [(id)strongVC tableView];
+ for (UITableViewCell *cell in tableView.visibleCells) {
+ if ([cell.textLabel.text isEqualToString:LOC(@"CLEARCACHE")]) {
+ cell.accessoryView = nil;
+ break;
+ }
+ }
+ }
+ if ([strongVC respondsToSelector:@selector(items)] && [strongVC respondsToSelector:@selector(tableView)]) {
+ NSArray *items = [(id)strongVC items];
+ for (id item in items) {
+ if ([[item title] isEqualToString:clearTitle]) {
+ [item setSubtitle:@"0 KB"];
+ break;
+ }
+ }
+ [[(id)strongVC tableView] reloadData];
+ }
});
- return YES;
- }
- ],
- BASIC_SWITCH(LOC(@"AUTO_CLEARCACHE"), LOC(@"AUTO_CLEARCACHE_DESC"), AutoClearCache),
- ];
- YTSettingsPickerViewController *picker = [[%c(YTSettingsPickerViewController) alloc] initWithNavTitle:LOC(@"PERFER_HEADER") pickerSectionTitle:nil rows:rows selectedItemIndex:0 parentResponder:[self parentResponder]];
- [settingsViewController pushViewController:picker];
+ });
+ }),
+ YMToggle(LOC(@"AUTO_CLEARCACHE"), LOC(@"AUTO_CLEARCACHE_DESC"), AutoClearCache),
+ ], settingsViewController, [self parentResponder]);
return YES;
}];
YTIIcon *icon7 = [%c(YTIIcon) new];
@@ -619,13 +596,14 @@ static NSString *GetCacheSize() { // YTLite - @dayanch96
[[NSUserDefaults standardUserDefaults] registerDefaults:@{
AutoClearCache: @YES,
YTPremiumLogo: @YES,
- HideCreateButton: @YES,
HideCastButtonNav: @YES,
HideCastButtonPlayer: @YES,
BackgroundPlayback: @YES,
- OldQualityPicker: @YES,
DownloadManager: @YES,
DownloadSaveToPhotos: @YES,
+ DisableHints: @YES,
+ RewindSeconds: @10.0,
+ ForwardSeconds: @10.0,
}];
%init;
-}
+}
\ No newline at end of file
diff --git a/Files/Shorts.x b/Files/Shorts.x
index d73cbb24..faaf7c28 100644
--- a/Files/Shorts.x
+++ b/Files/Shorts.x
@@ -1,5 +1,62 @@
#import "Headers.h"
+static NSBundle *YouModBundle() {
+ static NSBundle *bundle = nil;
+ static dispatch_once_t onceToken;
+ dispatch_once(&onceToken, ^{
+ NSString *tweakBundlePath = [[NSBundle mainBundle] pathForResource:@"YouMod" ofType:@"bundle"];
+ if (tweakBundlePath)
+ bundle = [NSBundle bundleWithPath:tweakBundlePath];
+ else
+ bundle = [NSBundle bundleWithPath:[NSString stringWithFormat:PS_ROOT_PATH_NS(@"/Library/Application Support/%@.bundle"), @"YouMod"]];
+ });
+ return bundle;
+}
+
+#define LOC(x) [YouModBundle() localizedStringForKey:x value:nil table:nil]
+
+// Audio track list
+static NSArray *getAllSystemLanguageTitles() {
+ NSMutableArray *titles = [NSMutableArray array];
+ NSArray *allLocales = [%c(YTLanguages) languageList];
+ NSMutableSet *seenLanguages = [NSMutableSet set];
+ NSLocale *currentLocale = [NSLocale currentLocale];
+
+ for (NSString *localeId in allLocales) {
+ NSDictionary *components = [NSLocale componentsFromLocaleIdentifier:localeId];
+ NSString *langCode = components[NSLocaleLanguageCode];
+
+ if (langCode && ![seenLanguages containsObject:langCode]) {
+ [seenLanguages addObject:langCode];
+ NSString *displayName = [currentLocale localizedStringForLocaleIdentifier:langCode];
+ if (displayName) [titles addObject:displayName];
+ }
+ }
+ return [titles sortedArrayUsingSelector:@selector(localizedCaseInsensitiveCompare:)];
+}
+
+static NSArray *getAllSystemLanguageValues() {
+ NSArray *sortedTitles = getAllSystemLanguageTitles();
+ NSMutableArray *sortedCodes = [NSMutableArray array];
+ NSArray *allLocales = [%c(YTLanguages) languageList];
+ NSLocale *currentLocale = [NSLocale currentLocale];
+
+ NSMutableDictionary *titleToCodeMap = [NSMutableDictionary dictionary];
+ for (NSString *localeId in allLocales) {
+ NSDictionary *components = [NSLocale componentsFromLocaleIdentifier:localeId];
+ NSString *langCode = components[NSLocaleLanguageCode];
+ if (langCode) {
+ NSString *displayName = [currentLocale localizedStringForLocaleIdentifier:langCode];
+ if (displayName) titleToCodeMap[displayName] = langCode;
+ }
+ }
+
+ for (NSString *title in sortedTitles) {
+ [sortedCodes addObject:titleToCodeMap[title] ? titleToCodeMap[title] : @"en"];
+ }
+ return [sortedCodes copy];
+}
+
// Enables shorts quality - works best with YTClassicVideoQuality
%hook YTHotConfig
- (BOOL)enableOmitAdvancedMenuInShortsVideoQualityPicker { return IS_ENABLED(EnablesShortsQuality) ? YES : %orig; }
@@ -33,4 +90,207 @@
%hook YTHotConfig
- (BOOL)enablePlayerBarForVerticalVideoWhenControlsHiddenInFullscreen { return IS_ENABLED(ShowShortsSeekbar) ? YES : %orig; }
+%end
+
+static void YouModMakeAShortsAction(YTReelPlayerViewController *self, YTSingleVideoController *video, YTSingleVideoTime *time) {
+ if (INTFORVAL(ShortsActionIndex) == 0) return;
+
+ if (floor(time.time) >= floor(video.totalMediaTime)) {
+ if (INTFORVAL(ShortsActionIndex) == 1) {
+ [self reelContentViewRequestsAdvanceToNextVideo:nil];
+ } else if (INTFORVAL(ShortsActionIndex) == 2) {
+ [self reelContentViewRequestsPlayPauseToggle:nil];
+ }
+ }
+}
+
+static BOOL isShortsOnlyOn = YES;
+
+%hook YTReelPlayerViewController
+- (void)singleVideo:(YTSingleVideoController *)video currentVideoTimeDidChange:(YTSingleVideoTime *)time {
+ %orig;
+ YouModMakeAShortsAction(self, video, time);
+}
+- (void)loadPlayerBar {
+ %orig;
+ YTPlayerViewController *main = self.player;
+ if (INTFORVAL(CaptionTrack) != 0) [main performSelector:@selector(YouModAutoCaptions) withObject:nil afterDelay:0.5];
+ if (INTFORVAL(AutoSpeedIndex) != 0) [main performSelector:@selector(YouModSetAutoSpeed) withObject:nil afterDelay:0.5];
+ if (INTFORVAL(AudioTrack) != 0) [self performSelector:@selector(YouModAutoAudioTrack:) withObject:main afterDelay:0.5];
+ if (isShortsOnlyOn && IS_ENABLED(ShortsOnly)) [self YouModOnlyShorts];
+}
+%new
+- (void)YouModOnlyShorts {
+ id appconmain = [self valueForKey:@"_pivotBarProvider"];
+ if ([appconmain isKindOfClass:%c(YTAppViewControllerImpl)]) {
+ YTAppViewControllerImpl *appcon = (YTAppViewControllerImpl *)appconmain;
+ [appcon hidePivotBar];
+ } else {
+ YTAppViewController *appcon = (YTAppViewController *)appconmain;
+ [appcon hidePivotBar];
+ }
+}
+%new
+- (void)YouModAutoAudioTrack:(YTPlayerViewController *)pv {
+ NSInteger selectedIndex = INTFORVAL(AudioTrackLangIndex);
+ NSArray *langCodes = getAllSystemLanguageValues();
+ NSString *userTargetLang = langCodes[selectedIndex];
+ id switchcon = self.audioTrackController;
+ NSArray *availableTracks = [switchcon valueForKey:@"_availableAudioTracks"];
+ if (!availableTracks || availableTracks.count == 0) return;
+ YTIAudioTrack *matchedTrack = nil;
+
+ if (INTFORVAL(AudioTrack) == 1) {
+ // Loop for all tracks
+ for (YTIAudioTrack *track in availableTracks) {
+ if ([track.id_p hasSuffix:@".4"]) {
+ matchedTrack = track;
+ break;
+ }
+ }
+ } else if (INTFORVAL(AudioTrack) == 2) {
+ // Loop for all tracks
+ for (YTIAudioTrack *track in availableTracks) {
+ if ([track.id_p hasPrefix:userTargetLang]) {
+ matchedTrack = track;
+ break;
+ }
+ }
+
+ // Check if it's dubbed
+ if (matchedTrack && [matchedTrack isAutoDubbed] && IS_ENABLED(NoDubbedAudioTrack)) {
+ matchedTrack = nil;
+ return;
+ }
+ }
+
+ // If found, change to it
+ if (matchedTrack) {
+ [pv setAudioTrack:matchedTrack source:0];
+ } else if (!matchedTrack && IS_ENABLED(NoDubbedAudioTrack)) {
+ for (YTIAudioTrack *track in availableTracks) {
+ if ([track.id_p hasSuffix:@".4"]) {
+ [pv setAudioTrack:track source:0];
+ break;
+ }
+ }
+ }
+}
+%end
+
+extern void YouModConfigureDownloadButton(_ASDisplayView *view);
+
+// _ASDisplayView filters
+%hook _ASDisplayView
+- (void)didMoveToWindow {
+ %orig;
+ YouModConfigureDownloadButton(self);
+ NSDictionary *elements = @{
+ @"product_sticker.main_target": @(IS_ENABLED(HideShortsProducts)),
+ @"product_sticker.secondary_target": @(IS_ENABLED(HideShortsProducts)),
+ @"id.elements.components.suggested_action": @(IS_ENABLED(HideShortsRecbar))
+ };
+ if ([elements[self.accessibilityIdentifier] boolValue]) [self removeFromSuperview];
+}
+%end
+
+%hook YTReelWatchPlaybackOverlayView
+%property (nonatomic, retain) UIPinchGestureRecognizer *YouModFullscreenGesture;
+- (void)layoutSubviews {
+ %orig;
+ if (!IS_ENABLED(FullScreenShorts)) return;
+ if (!self.YouModFullscreenGesture) {
+ self.YouModFullscreenGesture = [[UIPinchGestureRecognizer alloc] initWithTarget:self action:@selector(YouModFullscrrenGestureHandler:)];
+ self.YouModFullscreenGesture.delegate = (id)self;
+
+ [self.superview addGestureRecognizer:self.YouModFullscreenGesture];
+ }
+}
+%new
+- (void)YouModFullscrrenGestureHandler:(UIPinchGestureRecognizer *)gesture {
+ if (gesture.state != UIGestureRecognizerStateBegan || (isShortsOnlyOn && IS_ENABLED(ShortsOnly))) return;
+ id appconmain = [self valueForKey:@"_pivotBarProvider"];
+ if ([appconmain isKindOfClass:%c(YTAppViewControllerImpl)]) {
+ YTAppViewControllerImpl *appcon = (YTAppViewControllerImpl *)appconmain;
+ BOOL isTabBarHidden = [appcon isPivotBarHidden];
+ if (gesture.scale > 1.0) {
+ if (!isTabBarHidden) {
+ [appcon hidePivotBar];
+ [UIView animateWithDuration:0.3 animations:^{
+ self.alpha = 0;
+ }];
+ }
+ } else if (gesture.scale < 1.0) {
+ if (isTabBarHidden) {
+ [appcon showPivotBar];
+ [UIView animateWithDuration:0.3 animations:^{
+ self.alpha = 1;
+ }];
+ }
+ }
+ } else {
+ YTAppViewController *appcon = (YTAppViewController *)appconmain;
+ BOOL isTabBarHidden = [appcon isPivotBarHidden];
+ if (gesture.scale > 1.0) {
+ if (!isTabBarHidden) {
+ [appcon hidePivotBar];
+ [UIView animateWithDuration:0.3 animations:^{
+ self.alpha = 0;
+ }];
+ }
+ } else if (gesture.scale < 1.0) {
+ if (isTabBarHidden) {
+ [appcon showPivotBar];
+ [UIView animateWithDuration:0.3 animations:^{
+ self.alpha = 1;
+ }];
+ }
+ }
+ }
+}
+%new
+- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldRecognizeSimultaneouslyWithGestureRecognizer:(UIGestureRecognizer *)otherGestureRecognizer {
+ if (gestureRecognizer == self.YouModFullscreenGesture) {
+ return YES;
+ }
+ return NO;
+}
+%end
+
+%hook YTReelContentView
+- (void)setPlaybackView:(id)arg1 {
+ %orig;
+ if (!IS_ENABLED(ShortsOnly)) return;
+ if (isShortsOnlyOn) {
+ UILongPressGestureRecognizer *longPressGesture = [[UILongPressGestureRecognizer alloc] initWithTarget:self action:@selector(YouModTurnOffShortsOnly:)];
+ longPressGesture.numberOfTouchesRequired = 2;
+ longPressGesture.minimumPressDuration = 0.5;
+
+ [self addGestureRecognizer:longPressGesture];
+ }
+}
+%new
+- (void)YouModTurnOffShortsOnly:(UILongPressGestureRecognizer *)gesture {
+ if (gesture.state != UIGestureRecognizerStateBegan) return;
+ isShortsOnlyOn = NO;
+ UIView *parent = sbGetNotificationParent();
+ [SBSkipNotificationView showSuccessInView:parent message:LOC(@"SHORTS_ONLY_DISABLED") duration:3.0];
+
+ YTReelContainerViewController *reelcon = [self valueForKey:@"_parentResponder"];
+ YTAppReelWatchRootViewController *watchroot = [reelcon valueForKey:@"_delegate"];
+ id appconmain = [watchroot valueForKey:@"_pivotBarProvider"];
+ if ([appconmain isKindOfClass:%c(YTAppViewControllerImpl)]) {
+ YTAppViewControllerImpl *appcon = (YTAppViewControllerImpl *)appconmain;
+ [appcon showPivotBar];
+ [UIView animateWithDuration:0.3 animations:^{
+ self.playbackOverlay.alpha = 1;
+ }];
+ } else {
+ YTAppViewController *appcon = (YTAppViewController *)appconmain;
+ [appcon showPivotBar];
+ [UIView animateWithDuration:0.3 animations:^{
+ self.playbackOverlay.alpha = 1;
+ }];
+ }
+}
%end
\ No newline at end of file
diff --git a/Files/Sideloading.x b/Files/Sideloading.x
index 9c9a2870..7e46717b 100644
--- a/Files/Sideloading.x
+++ b/Files/Sideloading.x
@@ -1,8 +1,6 @@
// All Codes are adapt from YTLite and uYouEnhanced + Some of my research
#import "Headers.h"
-extern void YouModConfigureDownloadButton(_ASDisplayView *view);
-
// AccessGroupID
static NSString *accessGroupID() {
NSDictionary *query = [NSDictionary dictionaryWithObjectsAndKeys:
@@ -23,45 +21,6 @@ static NSString *accessGroupID() {
return accessGroup;
}
-// _ASDisplayView filters
-// This hook can hide A LOT of things
-%hook _ASDisplayView
-
-- (void)didMoveToWindow {
- %orig;
- YouModConfigureDownloadButton(self);
- // if (IS_ENABLED(HideShortsShelf) && [self.accessibilityIdentifier isEqualToString:@"eml.shorts-shelf"]) self.hidden = YES;
- if (IS_ENABLED(HideGenMusicShelf) && [self.accessibilityIdentifier isEqualToString:@"feed_nudge.view"]) self.hidden = YES;
- if (IS_ENABLED(HideFeedPost) && [self.accessibilityIdentifier isEqualToString:@"id.ui.backstage.original_post"]) self.hidden = YES;
- if (IS_ENABLED(HideLikeButton) && [self.accessibilityIdentifier isEqualToString:@"id.video.like.button"]) self.hidden = YES;
- if (IS_ENABLED(HideDisLikeButton) && [self.accessibilityIdentifier isEqualToString:@"id.video.dislike.button"]) self.hidden = YES;
- if (IS_ENABLED(HideShareButton) && [self.accessibilityIdentifier isEqualToString:@"id.video.share.button"]) self.hidden = YES;
- if (IS_ENABLED(HideDownloadButton) && [self.accessibilityIdentifier isEqualToString:@"id.ui.add_to.offline.button"]) self.hidden = YES;
- if (IS_ENABLED(HideClipButton) && [self.accessibilityIdentifier isEqualToString:@"clip_button.eml"]) self.hidden = YES;
- if (IS_ENABLED(HideRemixButton) && [self.accessibilityIdentifier isEqualToString:@"id.video.remix.button"]) self.hidden = YES;
- if (IS_ENABLED(HideSaveButton) && [self.accessibilityIdentifier isEqualToString:@"id.video.add_to.button"]) self.hidden = YES;
- if (IS_ENABLED(HideShortsLikeButton) && [self.accessibilityIdentifier isEqualToString:@"id.reel_like_button"]) self.hidden = YES;
- if (IS_ENABLED(HideShortsDisLikeButton) && [self.accessibilityIdentifier isEqualToString:@"id.reel_dislike_button"]) self.hidden = YES;
- if (IS_ENABLED(HideShortsCommentButton) && [self.accessibilityIdentifier isEqualToString:@"id.reel_comment_button"]) self.hidden = YES;
- if (IS_ENABLED(HideShortsShareButton) && [self.accessibilityIdentifier isEqualToString:@"id.reel_share_button"]) self.hidden = YES;
- if (IS_ENABLED(HideShortsRemixButton) && [self.accessibilityIdentifier isEqualToString:@"id.reel_remix_button"]) self.hidden = YES;
- if (IS_ENABLED(HideShortsMetaButton) && [self.accessibilityIdentifier isEqualToString:@"id.reel_pivot_button"]) self.hidden = YES;
- if (IS_ENABLED(HideShortsProducts) && [self.accessibilityIdentifier isEqualToString:@"product_sticker.main_target"]) self.hidden = YES;
- if (IS_ENABLED(HideShortsProducts) && [self.accessibilityIdentifier isEqualToString:@"product_sticker.secondary_target"]) self.hidden = YES;
- if (IS_ENABLED(HideShortsRecbar) && [self.accessibilityIdentifier isEqualToString:@"id.elements.components.suggested_action"]) self.hidden = YES;
- if (IS_ENABLED(HideShortsCommit) && [self.accessibilityIdentifier isEqualToString:@"eml.shorts-disclosures"]) self.hidden = YES;
- if (IS_ENABLED(HideShortsSubscriptButton) && [self.accessibilityIdentifier isEqualToString:@"id.ui.shorts_paused_state.subscriptions_button"]) self.hidden = YES;
- if (IS_ENABLED(HideShortsLiveButton) && [self.accessibilityIdentifier isEqualToString:@"id.ui.shorts_paused_state.live_button"]) self.hidden = YES;
- if (IS_ENABLED(HideShortsLensButton) && [self.accessibilityIdentifier isEqualToString:@"id.ui.shorts_paused_state.lens_button"]) self.hidden = YES;
- if (IS_ENABLED(HideShortsTrendsButton) && [self.accessibilityIdentifier isEqualToString:@"id.ui.shorts_paused_state.trends_button"]) self.hidden = YES;
- if (IS_ENABLED(HideShortsToVideo) && [self.accessibilityIdentifier isEqualToString:@"id.reel_multi_format_link"]) self.hidden = YES;
- if (IS_ENABLED(HideSubButton) && [self.accessibilityIdentifier isEqualToString:@"eml.animated_subscribe_button"]) self.hidden = YES;
- if (IS_ENABLED(HideShoppingButton) && [self.accessibilityIdentifier isEqualToString:@"eml.header_store_button"]) self.hidden = YES;
- if (IS_ENABLED(HideMemberButton) && [self.accessibilityIdentifier isEqualToString:@"id.sponsor_button"]) self.hidden = YES;
-}
-
-%end
-
// IAmYouTube (https://github.com/PoomSmart/IAmYouTube)
%hook YTVersionUtils
+ (NSString *)appName { return YT_NAME; }
@@ -208,6 +167,6 @@ static NSString *accessGroupID() {
NSURL *documentsURL = [paths lastObject];
return [documentsURL URLByAppendingPathComponent:@"AppGroup"];
}
- return %orig(groupIdentifier);
+ return %orig;
}
-%end
\ No newline at end of file
+%end
diff --git a/Files/SleepTimer.txt b/Files/SleepTimer.txt
new file mode 100644
index 00000000..23c5423b
--- /dev/null
+++ b/Files/SleepTimer.txt
@@ -0,0 +1,115 @@
+#import "Headers.h"
+
+@interface YouModSleepMenuItem : NSObject
+@property (nonatomic, copy) NSString *title;
+@property (nonatomic, copy) NSString *subtitle;
+@property (nonatomic, strong) UIImage *iconImage;
+@property (nonatomic, copy) void (^handler)(void);
++ (instancetype)itemWithTitle:(NSString *)title subtitle:(NSString *)subtitle handler:(void (^)(void))handler;
++ (instancetype)itemWithTitle:(NSString *)title subtitle:(NSString *)subtitle icon:(UIImage *)icon handler:(void (^)(void))handler;
+@end
+
+static void YouModPresentMenu(NSString *title, NSString *desc, UIViewController *presenter, UIView *sender) {
+ presenter = YouModTopViewController(presenter);
+ YTDefaultSheetController *sheet = [%c(YTDefaultSheetController) sheetControllerWithParentResponder:presenter];;
+ for (YouModSleepMenuItem *item in items) {
+ YTActionSheetAction *action = [%c(YTActionSheetAction) actionWithTitle:title subtitle:desc iconImage:nil handler:^(__unused YTActionSheetAction *action) {
+ if (handler) item.handler();
+ }];
+ [sheet addAction:action];
+ }
+ if (sender) {
+ [sheet presentFromView:sender animated:YES completion:nil];
+ } else {
+ [sheet presentFromViewController:presenter animated:YES completion:nil];
+ }
+}
+
+static __weak YTPlayerViewController *YouModSleepTimerPlayer = nil;
+static NSTimer *YouModSleepTimerTimer = nil;
+static BOOL YouModSleepTimerAtEndOfVideo = NO;
+
+static void YouModCancelSleepTimer(void) {
+ [YouModSleepTimerTimer invalidate];
+ YouModSleepTimerTimer = nil;
+ YouModSleepTimerAtEndOfVideo = NO;
+}
+
+static void YouModPauseForSleepTimer(void) {
+ YTPlayerViewController *player = YouModSleepTimerPlayer;
+ [player pause];
+ YouModCancelSleepTimer();
+ YouModShowToast(@"Sleep timer ended", player);
+}
+
+static void YouModStartSleepTimer(NSTimeInterval seconds, YTPlayerViewController *player) {
+ YouModCancelSleepTimer();
+ YouModSleepTimerPlayer = player;
+ YouModSleepTimerTimer = [NSTimer scheduledTimerWithTimeInterval:seconds repeats:NO block:^(__unused NSTimer *timer) {
+ YouModPauseForSleepTimer();
+ }];
+ YouModShowToast([NSString stringWithFormat:@"Sleep timer: %.0f minutes", seconds / 60.0], player);
+}
+
+static void YouModPresentSleepTimerCustomMinutes(YTPlayerViewController *player) {
+ UIAlertController *alert = [UIAlertController alertControllerWithTitle:@"Sleep timer" message:@"Enter minutes" preferredStyle:UIAlertControllerStyleAlert];
+ [alert addTextFieldWithConfigurationHandler:^(UITextField *textField) {
+ textField.keyboardType = UIKeyboardTypeNumberPad;
+ textField.placeholder = @"Minutes";
+ }];
+ [alert addAction:[UIAlertAction actionWithTitle:@"Cancel" style:UIAlertActionStyleCancel handler:nil]];
+ [alert addAction:[UIAlertAction actionWithTitle:@"Start" style:UIAlertActionStyleDefault handler:^(__unused UIAlertAction *action) {
+ NSInteger minutes = alert.textFields.firstObject.text.integerValue;
+ if (minutes <= 0) {
+ YouModShowToast(@"Enter a valid number of minutes", player);
+ return;
+ }
+ YouModStartSleepTimer((NSTimeInterval)minutes * 60.0, player);
+ }]];
+ [YouModTopViewController() presentViewController:alert animated:YES completion:nil];
+}
+
+static NSDate *YouModSleepTimerDateFromClockText(NSString *text) {
+ NSString *trimmed = [text stringByTrimmingCharactersInSet:NSCharacterSet.whitespaceAndNewlineCharacterSet];
+ if (trimmed.length == 0) return nil;
+
+ NSArray *formats = @[@"H:mm", @"HH:mm", @"h:mm a", @"h:mma"];
+ NSDate *parsed = nil;
+ NSDateFormatter *formatter = [NSDateFormatter new];
+ formatter.locale = [NSLocale localeWithLocaleIdentifier:@"en_US_POSIX"];
+ for (NSString *format in formats) {
+ formatter.dateFormat = format;
+ parsed = [formatter dateFromString:trimmed];
+ if (parsed) break;
+ }
+ if (!parsed) return nil;
+
+ NSCalendar *calendar = NSCalendar.currentCalendar;
+ NSDateComponents *clock = [calendar components:NSCalendarUnitHour | NSCalendarUnitMinute fromDate:parsed];
+ NSDateComponents *today = [calendar components:NSCalendarUnitYear | NSCalendarUnitMonth | NSCalendarUnitDay fromDate:NSDate.date];
+ today.hour = clock.hour;
+ today.minute = clock.minute;
+ today.second = 0;
+ NSDate *target = [calendar dateFromComponents:today];
+ if (target.timeIntervalSinceNow <= 5.0)
+ target = [calendar dateByAddingUnit:NSCalendarUnitDay value:1 toDate:target options:0];
+ return target;
+}
+
+static void YouModPresentSleepTimerEndAtTime(YTPlayerViewController *player) {
+ UIAlertController *alert = [UIAlertController alertControllerWithTitle:@"Sleep timer" message:@"Enter a time" preferredStyle:UIAlertControllerStyleAlert];
+ [alert addTextFieldWithConfigurationHandler:^(UITextField *textField) {
+ textField.keyboardType = UIKeyboardTypeNumbersAndPunctuation;
+ textField.placeholder = @"23:30 or 11:30 PM";
+ }];
+ [alert addAction:[UIAlertAction actionWithTitle:@"Cancel" style:UIAlertActionStyleCancel handler:nil]];
+ [alert addAction:[UIAlertAction actionWithTitle:@"Start" style:UIAlertActionStyleDefault handler:^(__unused UIAlertAction *action) {
+ NSDate *target = YouModSleepTimerDateFromClockText(alert.textFields.firstObject.text);
+ if (!target) {
+ YouModShowToast(@"Enter a valid time", player);
+ return;
+ }
+ YouModStartSleepTimer(target.timeIntervalSinceNow, player);
+ }]];
+ [YouModTopViewController() presentViewController:alert animated:YES completion:nil];
+}
diff --git a/Files/SponsorBlock.x b/Files/SponsorBlock.x
new file mode 100644
index 00000000..d1c3dce8
--- /dev/null
+++ b/Files/SponsorBlock.x
@@ -0,0 +1,606 @@
+#import "Headers.h"
+#import
+
+BOOL useBackwardIconForButton;
+
+// System sound played on skip when haptic/audio feedback is enabled.
+static const SystemSoundID SBSkipHapticSoundID = 1519;
+
+// Skipping is suppressed within this many seconds of a segment's end, so a
+// segment already almost over isn't re-triggered by a late time-change callback.
+static const CGFloat SBSegmentEndGuardSeconds = 0.5;
+
+// The skip banner is shown after this delay so the seek completes first —
+// otherwise the following time-change callback dismisses it immediately.
+static const NSTimeInterval SBSkipNotificationDelaySeconds = 0.3;
+
+// The YouMod resource bundle, resolved once. Localized strings for the skip
+// banners are looked up here.
+static NSBundle *SBBundle(void) {
+ static NSBundle *bundle = nil;
+ static dispatch_once_t onceToken;
+ dispatch_once(&onceToken, ^{
+ bundle = [NSBundle bundleWithPath:[[NSBundle mainBundle] pathForResource:@"YouMod" ofType:@"bundle"]];
+ });
+ return bundle;
+}
+
+// Clamps a stored banner duration to the supported range, falling back to the
+// default when unset or out of range.
+static float SBClampedAlertDuration(NSString *key) {
+ float duration = FLOAT_FOR_KEY(key);
+ if (duration < SBAlertDurationMin || duration > SBAlertDurationMax) return SBAlertDurationDefault;
+ return duration;
+}
+
+@interface SBPassthroughView : UIView
+@end
+@implementation SBPassthroughView
+- (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event {
+ UIView *hit = [super hitTest:point withEvent:event];
+ return (hit == self) ? nil : hit;
+}
+@end
+
+@interface SBPassthroughWindow : UIWindow
+@end
+@implementation SBPassthroughWindow
+- (BOOL)_canBecomeKeyWindow { return NO; }
+- (BOOL)_canAffectStatusBarAppearance { return NO; }
+- (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event {
+ UIView *rootView = self.rootViewController.view;
+ if (!rootView) return nil;
+ CGPoint convertedPoint = [rootView convertPoint:point fromView:self];
+ UIView *hitView = [rootView hitTest:convertedPoint withEvent:event];
+ if (!hitView || hitView == rootView) return nil;
+ return hitView;
+}
+@end
+
+static SBPassthroughWindow *sbOverlayWindow = nil;
+
+void sbUpdateOverlayInsetForPivotBar() {
+ if (!sbOverlayWindow) return;
+ UIViewController *rootVC = sbOverlayWindow.rootViewController;
+ if (!rootVC) return;
+
+ // The overlay window's frame is set only at creation; the scene can change
+ // size afterward (rotation, iPhone fullscreen exit) and this plain-UIViewController
+ // window doesn't auto-resize with it. Re-syncing to the current scene bounds keeps
+ // the pill safe-area math on the correct (e.g. portrait) size rather than a stale
+ // landscape one — otherwise pills anchor to a mid-screen bottom edge.
+ UIWindowScene *scene = sbOverlayWindow.windowScene;
+ CGRect sceneBounds = scene ? scene.coordinateSpace.bounds : sbOverlayWindow.bounds;
+ if (scene && !CGRectEqualToRect(sbOverlayWindow.frame, sceneBounds)) {
+ sbOverlayWindow.frame = sceneBounds;
+ }
+
+ // Look up YouTube's root view controller in the SAME scene as our overlay
+ // window — on iPad multi-window the app delegate's window may belong to a
+ // different scene, so [delegate window] is not safe here.
+ UIWindow *ytWindow = nil;
+ for (UIWindow *win in sbOverlayWindow.windowScene.windows) {
+ if ([win.rootViewController isKindOfClass:NSClassFromString(@"YTAppViewController")] || [win.rootViewController isKindOfClass:NSClassFromString(@"YTAppViewControllerImpl")]) {
+ ytWindow = win;
+ break;
+ }
+ }
+ YTAppViewController *appVC = (YTAppViewController *)ytWindow.rootViewController;
+ YTAppViewControllerImpl *appVC2 = (YTAppViewControllerImpl *)ytWindow.rootViewController;
+ YTPivotBarViewController *pivotVC;
+ @try {
+ pivotVC = (YTPivotBarViewController *)appVC.pivotBarViewController;
+ } @catch (id ex) {
+ pivotVC = (YTPivotBarViewController *)appVC2.pivotBarViewController;
+ }
+ YTPivotBarView *pivot = (YTPivotBarView *)pivotVC.viewIfLoaded;
+
+ // Measure the pivot bar's visible top edge in our overlay window's coords
+ // and convert it into the inset our pills need above the device safe area.
+ // This avoids reading pivot.bounds.size.height directly — that value
+ // includes home-indicator padding on notched devices and would over-correct
+ // the safe area, leaving the pill floating too high above the tabbar.
+ CGFloat tabH = 0.0;
+ if (pivot && pivot.window != nil && !pivot.hidden && pivot.alpha > 0.01) {
+ UIView *overlayView = rootVC.view;
+ CGRect pivotInOverlay = [overlayView convertRect:pivot.bounds fromView:pivot];
+ CGFloat pivotTop = CGRectGetMinY(pivotInOverlay);
+ // The scene bounds are the authoritative overlay height. overlayView.bounds
+ // is avoided here because it only reflects a new window size once autoresizing
+ // has propagated, which may lag within the current runloop.
+ CGFloat overlayHeight = sceneBounds.size.height;
+ CGFloat deviceSafeBottom = sbOverlayWindow.safeAreaInsets.bottom;
+ CGFloat pivotHeight = pivot.bounds.size.height;
+ // Clamp to the pivot bar's own height: a legitimate inset can never exceed
+ // the bar it's clearing. If convertRect returns a stale value during a scene
+ // transition (e.g. fullscreen exit), the raw result balloons toward the full
+ // screen height — clamping to pivotHeight keeps the pill just above the safe
+ // area instead of letting it drift to the middle of the screen.
+ tabH = MAX(0.0, MIN(pivotHeight, overlayHeight - deviceSafeBottom - pivotTop));
+ }
+ UIEdgeInsets current = rootVC.additionalSafeAreaInsets;
+ if (current.bottom != tabH) {
+ rootVC.additionalSafeAreaInsets = UIEdgeInsetsMake(0, 0, tabH, 0);
+ }
+}
+
+static const NSTimeInterval SBOverlayRestoreFadeDuration = 0.15;
+
+// Hide the pill overlay instantly (non-animated): iOS captures the app-switcher
+// snapshot synchronously as the app deactivates, so an animated hide wouldn't
+// land in time and the pill would leak into the switcher card.
+static void sbHideOverlayForSnapshot(void) {
+ if (sbOverlayWindow) sbOverlayWindow.hidden = YES;
+}
+
+// Restore the overlay, fading it back in so the reappearance isn't a hard pop.
+// Guarded to the hidden state so the two "became active" notifications don't
+// each re-trigger the fade.
+static void sbRestoreOverlayAfterSnapshot(void) {
+ if (!sbOverlayWindow || !sbOverlayWindow.hidden) return;
+ sbOverlayWindow.alpha = 0.0;
+ sbOverlayWindow.hidden = NO;
+ [UIView animateWithDuration:SBOverlayRestoreFadeDuration animations:^{
+ sbOverlayWindow.alpha = 1.0;
+ }];
+}
+
+// Tracks which scene's lifecycle is currently observed. When sbOverlayWindow is
+// recreated for a different scene (after the original goes Unattached), we
+// re-bind observers to the new scene rather than leaving stale registrations.
+static UIWindowScene *sbObservedScene = nil;
+static id sbSceneDeactivateObserver = nil;
+static id sbSceneBackgroundObserver = nil;
+static id sbSceneActivateObserver = nil;
+static id sbAppResignObserver = nil;
+static id sbAppBackgroundObserver = nil;
+static id sbAppActivateObserver = nil;
+static id sbOrientationObserver = nil;
+
+static void sbRegisterOverlayLifecycleObservers(UIWindowScene *targetScene) {
+ if (!targetScene || sbObservedScene == targetScene) return;
+ NSNotificationCenter *nc = [NSNotificationCenter defaultCenter];
+
+ if (sbSceneDeactivateObserver) [nc removeObserver:sbSceneDeactivateObserver];
+ if (sbSceneBackgroundObserver) [nc removeObserver:sbSceneBackgroundObserver];
+ if (sbSceneActivateObserver) [nc removeObserver:sbSceneActivateObserver];
+ if (sbAppResignObserver) [nc removeObserver:sbAppResignObserver];
+ if (sbAppBackgroundObserver) [nc removeObserver:sbAppBackgroundObserver];
+ if (sbAppActivateObserver) [nc removeObserver:sbAppActivateObserver];
+ if (sbOrientationObserver) [nc removeObserver:sbOrientationObserver];
+
+ sbObservedScene = targetScene;
+
+ // Hide on deactivation, restore on activation. Deactivation is the trigger
+ // (not backgrounding) because invoking the app switcher only moves the app to
+ // the inactive state — a background-only observer misses that path. queue:nil
+ // runs the hide synchronously before the snapshot is captured. Scene
+ // notifications are filtered to targetScene so one scene's interruption
+ // doesn't hide another's overlay on iPad multi-window; the app-level
+ // notifications (object:nil) are an app-wide backstop.
+ sbSceneDeactivateObserver = [nc addObserverForName:UISceneWillDeactivateNotification object:targetScene queue:nil usingBlock:^(__unused NSNotification *note) {
+ sbHideOverlayForSnapshot();
+ }];
+ sbSceneBackgroundObserver = [nc addObserverForName:UISceneDidEnterBackgroundNotification object:targetScene queue:nil usingBlock:^(__unused NSNotification *note) {
+ sbHideOverlayForSnapshot();
+ }];
+ sbSceneActivateObserver = [nc addObserverForName:UISceneDidActivateNotification object:targetScene queue:nil usingBlock:^(__unused NSNotification *note) {
+ sbRestoreOverlayAfterSnapshot();
+ }];
+ sbAppResignObserver = [nc addObserverForName:UIApplicationWillResignActiveNotification object:nil queue:nil usingBlock:^(__unused NSNotification *note) {
+ sbHideOverlayForSnapshot();
+ }];
+ sbAppBackgroundObserver = [nc addObserverForName:UIApplicationDidEnterBackgroundNotification object:nil queue:nil usingBlock:^(__unused NSNotification *note) {
+ sbHideOverlayForSnapshot();
+ }];
+ sbAppActivateObserver = [nc addObserverForName:UIApplicationDidBecomeActiveNotification object:nil queue:nil usingBlock:^(__unused NSNotification *note) {
+ sbRestoreOverlayAfterSnapshot();
+ }];
+
+ // Recompute pivot-bar inset on rotation / dynamic tabbar height changes.
+ // UIDeviceOrientationDidChangeNotification only fires when device-orientation
+ // generation is enabled; this call is idempotent.
+ [[UIDevice currentDevice] beginGeneratingDeviceOrientationNotifications];
+ sbOrientationObserver = [nc addObserverForName:UIDeviceOrientationDidChangeNotification object:nil queue:[NSOperationQueue mainQueue] usingBlock:^(__unused NSNotification *note) {
+ sbUpdateOverlayInsetForPivotBar();
+ }];
+}
+
+UIView *sbGetNotificationParent(void) {
+ if (sbOverlayWindow && sbOverlayWindow.windowScene.activationState == UISceneActivationStateUnattached) {
+ sbOverlayWindow = nil;
+ }
+ if (!sbOverlayWindow) {
+ UIWindowScene *activeScene = nil;
+ for (UIWindowScene *scene in [UIApplication sharedApplication].connectedScenes) {
+ if (scene.activationState == UISceneActivationStateForegroundActive) {
+ activeScene = scene;
+ break;
+ }
+ }
+ if (!activeScene) {
+ activeScene = (UIWindowScene *)[[[UIApplication sharedApplication].connectedScenes allObjects] firstObject];
+ }
+ if (!activeScene) return nil;
+
+ sbOverlayWindow = [[SBPassthroughWindow alloc] initWithWindowScene:activeScene];
+ sbOverlayWindow.frame = activeScene.coordinateSpace.bounds;
+ sbOverlayWindow.windowLevel = UIWindowLevelAlert - 1;
+ sbOverlayWindow.backgroundColor = [UIColor clearColor];
+ sbOverlayWindow.hidden = NO;
+
+ UIViewController *rootVC = [[UIViewController alloc] init];
+ rootVC.view = [[SBPassthroughView alloc] initWithFrame:sbOverlayWindow.bounds];
+ rootVC.view.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;
+ rootVC.view.backgroundColor = [UIColor clearColor];
+ sbOverlayWindow.rootViewController = rootVC;
+
+ sbRegisterOverlayLifecycleObservers(activeScene);
+ sbUpdateOverlayInsetForPivotBar();
+ }
+ return sbOverlayWindow.rootViewController.view;
+}
+
+static NSMutableDictionary *> *sbSegmentCache;
+
+NSArray *sbAllCategories(void) {
+ static NSArray *cats;
+ static dispatch_once_t onceToken;
+ dispatch_once(&onceToken, ^{
+ cats = @[@"sponsor", @"intro", @"outro", @"interaction", @"selfpromo",
+ @"music_offtopic", @"preview", @"hook", @"poi_highlight", @"filler"];
+ });
+ return cats;
+}
+
+static NSArray *sbEnabledCategories() {
+ NSMutableArray *enabled = [NSMutableArray array];
+ for (NSString *cat in sbAllCategories()) {
+ NSInteger action = [[NSUserDefaults standardUserDefaults] integerForKey:SB_ACTION_KEY(cat)];
+ if (action != SBSegmentActionDisable) {
+ [enabled addObject:cat];
+ }
+ }
+ return enabled;
+}
+
+UIColor *SBColorFromHex(NSString *hexString) {
+ if (!hexString || hexString.length < 7) return [UIColor whiteColor];
+ unsigned int hex = 0;
+ NSScanner *scanner = [NSScanner scannerWithString:[hexString substringFromIndex:1]];
+ [scanner scanHexInt:&hex];
+ return [UIColor colorWithRed:((hex >> 16) & 0xFF) / 255.0
+ green:((hex >> 8) & 0xFF) / 255.0
+ blue:(hex & 0xFF) / 255.0
+ alpha:1.0];
+}
+
+#pragma mark - SBSegment Implementation
+
+@implementation SBSegment
+
++ (instancetype)segmentWithUUID:(NSString *)UUID category:(NSString *)category start:(float)start end:(float)end action:(NSString *)actionType {
+ SBSegment *seg = [[SBSegment alloc] init];
+ seg.UUID = UUID;
+ seg.category = category;
+ seg.startTime = start;
+ seg.endTime = end;
+ seg.actionType = actionType;
+ return seg;
+}
+
+- (SBSegmentAction)configuredAction {
+ return (SBSegmentAction)[[NSUserDefaults standardUserDefaults] integerForKey:SB_ACTION_KEY(self.category)];
+}
+
+- (UIColor *)segmentColor {
+ NSString *hex = [[NSUserDefaults standardUserDefaults] stringForKey:SB_COLOR_KEY(self.category)];
+ return SBColorFromHex(hex);
+}
+
+@end
+
+#pragma mark - SBRequest Implementation
+
+@implementation SBRequest
+
++ (void)fetchSegmentsForVideoID:(NSString *)videoID completion:(void (^)(NSArray *))completion {
+ if (!videoID || videoID.length == 0) {
+ if (completion) completion(@[]);
+ return;
+ }
+
+ @synchronized(sbSegmentCache) {
+ NSArray *cached = sbSegmentCache[videoID];
+ if (cached) {
+ if (completion) completion(cached);
+ return;
+ }
+ }
+
+ NSArray *categories = sbEnabledCategories();
+ if (categories.count == 0) {
+ if (completion) completion(@[]);
+ return;
+ }
+
+ NSData *catJSON = [NSJSONSerialization dataWithJSONObject:categories options:0 error:nil];
+ NSString *catString = [[NSString alloc] initWithData:catJSON encoding:NSUTF8StringEncoding];
+ NSString *encoded = [catString stringByAddingPercentEncodingWithAllowedCharacters:[NSCharacterSet URLQueryAllowedCharacterSet]];
+ NSString *urlStr = [NSString stringWithFormat:@"https://sponsor.ajay.app/api/skipSegments?videoID=%@&categories=%@", videoID, encoded];
+ NSURL *url = [NSURL URLWithString:urlStr];
+
+ NSURLSessionDataTask *task = [[NSURLSession sharedSession] dataTaskWithURL:url completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) {
+ NSMutableArray *segments = [NSMutableArray array];
+
+ if (!error && data) {
+ NSHTTPURLResponse *httpResp = (NSHTTPURLResponse *)response;
+ if (httpResp.statusCode == 200) {
+ NSArray *json = [NSJSONSerialization JSONObjectWithData:data options:0 error:nil];
+ if ([json isKindOfClass:[NSArray class]]) {
+ for (NSDictionary *item in json) {
+ NSArray *segment = item[@"segment"];
+ if (segment.count >= 2) {
+ SBSegment *seg = [SBSegment segmentWithUUID:item[@"UUID"] ?: @""
+ category:item[@"category"] ?: @""
+ start:[segment[0] floatValue]
+ end:[segment[1] floatValue]
+ action:item[@"actionType"] ?: @"skip"];
+ [segments addObject:seg];
+ }
+ }
+ }
+ }
+ }
+
+ dispatch_async(dispatch_get_main_queue(), ^{
+ @synchronized(sbSegmentCache) {
+ sbSegmentCache[videoID] = segments;
+ }
+ if (completion) completion(segments);
+ });
+ }];
+ [task resume];
+}
+
+@end
+
+#pragma mark - YTPlayerViewController Hooks
+
+%hook YTPlayerViewController
+%property (nonatomic, strong) NSString *sbLastVideoID;
+%property (nonatomic, strong) NSArray *sbSegments;
+%property (nonatomic, strong) NSMutableSet *sbSkippedSegments;
+%property (nonatomic, strong) SBSkipNotificationView *sbNotificationView;
+%property (nonatomic, assign) BOOL sbEnabledForVideo;
+
+- (void)playbackController:(id)playbackController didActivateVideo:(id)video withPlaybackData:(id)playbackData {
+ %orig;
+ if (!IS_ENABLED(SBEnabled) || self.isPlayingAd) return;
+
+ self.sbEnabledForVideo = YES;
+ self.sbSkippedSegments = [NSMutableSet set];
+ self.sbSegments = nil;
+
+ [self.sbNotificationView dismiss];
+
+ NSString *videoID = [self contentVideoID];
+ if ([self.sbLastVideoID isEqualToString:videoID] && self.sbSegments.count > 0) return;
+ self.sbLastVideoID = videoID;
+
+ __weak typeof(self) weakSelf = self;
+ [SBRequest fetchSegmentsForVideoID:videoID completion:^(NSArray *segments) {
+ __strong typeof(weakSelf) strongSelf = weakSelf;
+ if (!strongSelf) return;
+ strongSelf.sbSegments = segments;
+ [[NSNotificationCenter defaultCenter] postNotificationName:@"SBSegmentsDidLoad"
+ object:strongSelf
+ userInfo:@{@"segments": segments ?: @[]}];
+
+ [strongSelf sbShowHighlightBannerIfNeeded:segments];
+ }];
+}
+
+- (void)singleVideo:(id)video currentVideoTimeDidChange:(id)time {
+ %orig;
+ [self sbCheckSegmentsAtCurrentTime];
+}
+
+// Time-change hook for YouTube versions that use the renamed selector.
+- (void)potentiallyMutatedSingleVideo:(id)video currentVideoTimeDidChange:(id)time {
+ %orig;
+ [self sbCheckSegmentsAtCurrentTime];
+}
+
+// Evaluates the loaded segments against the current playback time and performs
+// the configured skip / ask action for the first matching segment. Shared by
+// both time-change hooks so the skip logic lives in one place.
+%new
+- (void)sbCheckSegmentsAtCurrentTime {
+ if (!IS_ENABLED(SBEnabled) || !self.sbEnabledForVideo || self.isPlayingAd) return;
+
+ CGFloat currentTime = [self currentVideoMediaTime];
+ float minDuration = FLOAT_FOR_KEY(SBMinDuration);
+
+ for (SBSegment *segment in self.sbSegments) {
+ SBSegmentAction action = [segment configuredAction];
+ if (action == SBSegmentActionDisable || action == SBSegmentActionDisplay) continue;
+ if (action == SBSegmentActionSkipTo) continue;
+
+ float duration = segment.endTime - segment.startTime;
+ if (duration < minDuration) continue;
+
+ if (currentTime >= segment.startTime && currentTime < segment.endTime - SBSegmentEndGuardSeconds) {
+ NSString *segID = segment.UUID;
+ if ([self.sbSkippedSegments containsObject:segID]) continue;
+
+ if (action == SBSegmentActionAutoSkip) {
+ [self sbPerformSkip:segment];
+ } else if (action == SBSegmentActionAsk) {
+ [self sbShowAskNotification:segment];
+ }
+ break;
+ }
+ }
+}
+
+%new
+- (void)sbPerformSkip:(SBSegment *)segment {
+ [self.sbSkippedSegments addObject:segment.UUID];
+ [self seekToTime:(CGFloat)segment.endTime];
+
+ if (IS_ENABLED(SBAudioNotification)) {
+ AudioServicesPlaySystemSound(SBSkipHapticSoundID);
+ }
+
+ if (IS_ENABLED(SBShowNotifications)) {
+ useBackwardIconForButton = YES;
+ NSBundle *bundle = SBBundle();
+ NSString *catName = [bundle localizedStringForKey:[NSString stringWithFormat:@"SB_CAT_%@", segment.category] value:segment.category table:nil];
+ NSString *message = [NSString stringWithFormat:[bundle localizedStringForKey:@"SB_SKIPPED" value:@"%@ skipped" table:nil], catName];
+ NSString *unskipTitle = [bundle localizedStringForKey:@"SB_UNSKIP" value:@"Unskip" table:nil];
+
+ float alertDuration = SBClampedAlertDuration(SBUnskipAlertDuration);
+
+ __weak typeof(self) weakSelf = self;
+ // Delay notification so the seek completes before the banner is shown,
+ // preventing the time-change callback from dismissing it immediately.
+ dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(SBSkipNotificationDelaySeconds * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
+ __strong typeof(weakSelf) strongSelf = weakSelf;
+ if (!strongSelf) return;
+ UIView *parentView = sbGetNotificationParent();
+ strongSelf.sbNotificationView = [SBSkipNotificationView showInView:parentView
+ message:message
+ buttonTitle:unskipTitle
+ action:^{
+ __strong typeof(weakSelf) ss = weakSelf;
+ if (ss) [ss seekToTime:(CGFloat)segment.startTime];
+ }
+ duration:alertDuration];
+ });
+ }
+}
+
+%new
+- (void)sbShowAskNotification:(SBSegment *)segment {
+ [self.sbSkippedSegments addObject:segment.UUID];
+
+ useBackwardIconForButton = NO;
+ NSBundle *bundle = SBBundle();
+ NSString *catName = [bundle localizedStringForKey:[NSString stringWithFormat:@"SB_CAT_%@", segment.category] value:segment.category table:nil];
+ NSString *message = [NSString stringWithFormat:[bundle localizedStringForKey:@"SB_DETECTED" value:@"%@ detected" table:nil], catName];
+
+ float alertDuration = SBClampedAlertDuration(SBSkipAlertDuration);
+
+ UIView *parentView = sbGetNotificationParent();
+ __weak typeof(self) weakSelf = self;
+ self.sbNotificationView = [SBSkipNotificationView showInView:parentView
+ message:message
+ buttonTitle:[bundle localizedStringForKey:@"SB_SKIP_NOW" value:@"Skip" table:nil]
+ action:^{
+ __strong typeof(weakSelf) ss = weakSelf;
+ if (ss) [ss seekToTime:(CGFloat)segment.endTime];
+ }
+ duration:alertDuration];
+}
+
+%new
+- (void)sbShowHighlightBannerIfNeeded:(NSArray *)segments {
+ for (SBSegment *seg in segments) {
+ if ([seg.category isEqualToString:@"poi_highlight"] && [seg configuredAction] == SBSegmentActionAsk) {
+ useBackwardIconForButton = NO;
+ NSBundle *bundle = SBBundle();
+ NSString *message = [bundle localizedStringForKey:@"SB_JUMP_TO_HIGHLIGHT" value:@"Highlight available. Jump to the point?" table:nil];
+ NSString *skipTitle = [bundle localizedStringForKey:@"SB_SKIP_NOW" value:@"Skip" table:nil];
+
+ float alertDuration = SBClampedAlertDuration(SBSkipAlertDuration);
+
+ UIView *parentView = sbGetNotificationParent();
+ SBSkipNotificationView *pill = [SBSkipNotificationView showInView:parentView
+ message:message
+ buttonTitle:skipTitle
+ action:^{ [self sbSkipToHighlight]; }
+ duration:alertDuration];
+ if (pill) {
+ pill.isHighlightPill = YES;
+ self.sbNotificationView = pill;
+ }
+ break;
+ }
+ }
+}
+
+%new
+- (void)sbSkipToHighlight {
+ self.sbNotificationView.isHighlightPill = NO;
+
+ for (SBSegment *segment in self.sbSegments) {
+ if ([segment.category isEqualToString:@"poi_highlight"]) {
+ CGFloat previousTime = [self currentVideoMediaTime];
+ [self seekToTime:(CGFloat)segment.startTime];
+
+ if (IS_ENABLED(SBShowNotifications)) {
+ useBackwardIconForButton = YES;
+ NSBundle *bundle = SBBundle();
+ NSString *message = [bundle localizedStringForKey:@"SB_JUMPED_TO_HIGHLIGHT" value:@"Jumped to highlight" table:nil];
+ NSString *unskipTitle = [bundle localizedStringForKey:@"SB_UNSKIP" value:@"Unskip" table:nil];
+
+ float alertDuration = SBClampedAlertDuration(SBUnskipAlertDuration);
+
+ __weak typeof(self) weakSelf = self;
+ SBSkipNotificationView *pill = [SBSkipNotificationView showInView:sbGetNotificationParent()
+ message:message
+ buttonTitle:unskipTitle
+ action:^{
+ __strong typeof(weakSelf) ss = weakSelf;
+ if (ss) [ss seekToTime:previousTime];
+ }
+ duration:alertDuration];
+ if (pill) {
+ pill.isHighlightPill = YES;
+ self.sbNotificationView = pill;
+ }
+ }
+ break;
+ }
+ }
+}
+
+%end
+
+// SponsorBlock's accent blue, reused for the toggle button's enabled state.
+static UIColor *SBAccentColor() {
+ return [UIColor colorWithRed:0.4 green:0.8 blue:1.0 alpha:1.0];
+}
+
+%ctor {
+ sbSegmentCache = [NSMutableDictionary dictionary];
+ %init;
+
+ // Register the SponsorBlock toggle in the player overlay's custom button row.
+ // sortOrder 100 keeps it right-most (directly under YouTube's settings gear).
+ YMOverlayButtonSpec *toggle = [[YMOverlayButtonSpec alloc] init];
+ toggle.identifier = @"sponsorblock.toggle";
+ toggle.symbolName = @"shield.fill";
+ toggle.tintColor = SBAccentColor();
+ toggle.sortOrder = 100;
+ toggle.isVisible = ^BOOL(YTPlayerViewController *player) {
+ return IS_ENABLED(SBEnabled) && IS_ENABLED(SBShowButton);
+ };
+ toggle.tintProvider = ^UIColor *(YTPlayerViewController *player) {
+ return (player && player.sbEnabledForVideo) ? SBAccentColor() : [UIColor grayColor];
+ };
+ toggle.onTap = ^(YTPlayerViewController *player, UIButton *button) {
+ if (!player) return;
+ BOOL newState = !player.sbEnabledForVideo;
+ player.sbEnabledForVideo = newState;
+ button.tintColor = newState ? SBAccentColor() : [UIColor grayColor];
+
+ NSArray *segments = newState ? (player.sbSegments ?: @[]) : @[];
+ if (newState && segments.count == 0) return;
+ [[NSNotificationCenter defaultCenter] postNotificationName:@"SBSegmentsDidLoad"
+ object:player
+ userInfo:@{@"segments": segments}];
+ };
+ YMRegisterOverlayButton(toggle);
+}
diff --git a/Files/SponsorBlockSettings.x b/Files/SponsorBlockSettings.x
new file mode 100644
index 00000000..57c84b02
--- /dev/null
+++ b/Files/SponsorBlockSettings.x
@@ -0,0 +1,630 @@
+// SponsorBlockSettings.x — Custom UITableViewController matching YTLite's SponsorBlock UI
+#import "Headers.h"
+#import
+#import
+
+extern UIColor *SBColorFromHex(NSString *hexString);
+
+static NSBundle *SBSettingsBundle() {
+ static NSBundle *bundle = nil;
+ static dispatch_once_t onceToken;
+ dispatch_once(&onceToken, ^{
+ NSString *tweakBundlePath = [[NSBundle mainBundle] pathForResource:@"YouMod" ofType:@"bundle"];
+ if (tweakBundlePath)
+ bundle = [NSBundle bundleWithPath:tweakBundlePath];
+ else
+ bundle = [NSBundle bundleWithPath:[NSString stringWithFormat:PS_ROOT_PATH_NS(@"/Library/Application Support/%@.bundle"), @"YouMod"]];
+ });
+ return bundle;
+}
+
+#define SB_LOC(x) [SBSettingsBundle() localizedStringForKey:x value:nil table:nil]
+
+// Purple accent used to tint the toggle switches and duration sliders on this
+// page. (Distinct from the player overlay button's accent in SponsorBlock.x.)
+static UIColor *SBControlTintColor(void) {
+ return [UIColor colorWithRed:0.6 green:0.2 blue:0.9 alpha:1.0];
+}
+
+// Tag base for a slider row's value label, offset by the row index so each
+// slider can find its own label via viewWithTag:.
+static const NSInteger SBSliderValueLabelTagBase = 100;
+
+// The localization key for a segment action's display name. Shared by the
+// selected-action label and the action picker menu so the two never disagree.
+static NSString *SBActionLocKey(SBSegmentAction action) {
+ switch (action) {
+ case SBSegmentActionAutoSkip: return @"SB_ACTION_AUTO_SKIP";
+ case SBSegmentActionAsk: return @"SB_ACTION_ASK";
+ case SBSegmentActionDisplay: return @"SB_ACTION_DISPLAY";
+ case SBSegmentActionSkipTo: return @"SB_ACTION_SKIP_TO";
+ default: return @"SB_ACTION_DISABLE";
+ }
+}
+
+// One toggle row: the defaults key it controls and its localized title/description
+// keys. This is the single source of truth for the toggle section — the row count,
+// each cell's contents, and the value written on change all read from it, so a row
+// can never display one setting while toggling another.
+@interface SBToggleRow : NSObject
+@property (nonatomic, copy) NSString *key;
+@property (nonatomic, copy) NSString *titleKey;
+@property (nonatomic, copy) NSString *descKey;
+@end
+@implementation SBToggleRow
++ (instancetype)key:(NSString *)key title:(NSString *)titleKey desc:(NSString *)descKey {
+ SBToggleRow *row = [SBToggleRow new];
+ row.key = key; row.titleKey = titleKey; row.descKey = descKey;
+ return row;
+}
+@end
+
+static NSArray *sbToggleRows() {
+ return @[
+ [SBToggleRow key:SBEnabled title:@"SB_ENABLE" desc:@"SB_ENABLE_DESC"],
+ [SBToggleRow key:SBShowButton title:@"SB_SHOW_BUTTON" desc:@"SB_SHOW_BUTTON_DESC"],
+ [SBToggleRow key:SBShowNotifications title:@"SB_SHOW_NOTIFICATIONS" desc:@"SB_SHOW_NOTIFICATIONS_DESC"],
+ [SBToggleRow key:SBSegmentsInPlayer title:@"SB_SEGMENTS_IN_PLAYER" desc:@"SB_SEGMENTS_IN_PLAYER_DESC"],
+ [SBToggleRow key:SBSegmentsInFeed title:@"SB_SEGMENTS_IN_FEED" desc:@"SB_SEGMENTS_IN_FEED_DESC"],
+ [SBToggleRow key:SBSegmentsInMiniPlayer title:@"SB_SEGMENTS_IN_MINIPLAYER" desc:@"SB_SEGMENTS_IN_MINIPLAYER_DESC"],
+ [SBToggleRow key:SBAudioNotification title:@"SB_HAPTIC_FEEDBACK" desc:@"SB_HAPTIC_FEEDBACK_DESC"],
+ [SBToggleRow key:SBShowDuration title:@"SB_SHOW_DURATION" desc:@"SB_SHOW_DURATION_DESC"],
+ ];
+}
+
+static NSString *SBActionName(NSInteger action) {
+ return SB_LOC(SBActionLocKey((SBSegmentAction)action));
+}
+
+static NSString *SBHexFromColor(UIColor *color) {
+ CGFloat r, g, b, a;
+ [color getRed:&r green:&g blue:&b alpha:&a];
+ return [NSString stringWithFormat:@"#%02X%02X%02X", (int)(r * 255), (int)(g * 255), (int)(b * 255)];
+}
+
+#pragma mark - Color Circle View (filled center + rainbow ring)
+
+@interface SBColorCircleView : UIView
+@property (nonatomic, strong) UIColor *fillColor;
+@end
+
+@implementation SBColorCircleView
+
+- (instancetype)initWithFrame:(CGRect)frame color:(UIColor *)color {
+ self = [super initWithFrame:frame];
+ if (self) {
+ self.fillColor = color;
+ self.backgroundColor = [UIColor clearColor];
+ }
+ return self;
+}
+
+- (void)drawRect:(CGRect)rect {
+ CGContextRef ctx = UIGraphicsGetCurrentContext();
+ CGFloat size = MIN(rect.size.width, rect.size.height);
+ CGRect square = CGRectMake((rect.size.width - size) / 2, (rect.size.height - size) / 2, size, size);
+ CGFloat cx = CGRectGetMidX(square), cy = CGRectGetMidY(square);
+ CGFloat ringWidth = 3.0;
+ CGFloat radius = (size - ringWidth) / 2.0;
+
+ // Draw rainbow ring by stroking arc segments at varying hues
+ NSInteger segments = 64;
+ CGFloat anglePerSegment = (2.0 * M_PI) / segments;
+ for (NSInteger i = 0; i < segments; i++) {
+ CGFloat startAngle = i * anglePerSegment - M_PI_2;
+ CGFloat endAngle = startAngle + anglePerSegment + 0.02; // slight overlap to avoid gaps
+ CGFloat hue = (CGFloat)i / segments;
+ UIColor *color = [UIColor colorWithHue:hue saturation:1.0 brightness:1.0 alpha:1.0];
+ CGContextSetStrokeColorWithColor(ctx, color.CGColor);
+ CGContextSetLineWidth(ctx, ringWidth);
+ CGContextAddArc(ctx, cx, cy, radius, startAngle, endAngle, 0);
+ CGContextStrokePath(ctx);
+ }
+
+ // Filled center circle
+ CGRect innerRect = CGRectInset(square, ringWidth + 2, ringWidth + 2);
+ UIBezierPath *innerPath = [UIBezierPath bezierPathWithOvalInRect:innerRect];
+ [self.fillColor setFill];
+ [innerPath fill];
+}
+
+- (void)setFillColor:(UIColor *)fillColor {
+ _fillColor = fillColor;
+ [self setNeedsDisplay];
+}
+
+@end
+
+#pragma mark - SBSettingsViewController
+
+@interface SBSettingsViewController : UIViewController
+- (UITableView *)tableView;
+- (void)setTableView:(UITableView *)tv;
+- (NSString *)activeColorKey;
+- (void)setActiveColorKey:(NSString *)key;
+- (NSIndexPath *)activeColorIndexPath;
+- (void)setActiveColorIndexPath:(NSIndexPath *)ip;
+- (UIColor *)sbTextColor;
+- (UIColor *)sbSecondaryTextColor;
+@end
+
+static const void *kSBTableViewKey = &kSBTableViewKey;
+static const void *kSBColorKeyKey = &kSBColorKeyKey;
+static const void *kSBColorIndexPathKey = &kSBColorIndexPathKey;
+
+@implementation SBSettingsViewController
+
+- (UITableView *)tableView { return objc_getAssociatedObject(self, kSBTableViewKey); }
+- (void)setTableView:(UITableView *)tv { objc_setAssociatedObject(self, kSBTableViewKey, tv, OBJC_ASSOCIATION_RETAIN_NONATOMIC); }
+- (NSString *)activeColorKey { return objc_getAssociatedObject(self, kSBColorKeyKey); }
+- (void)setActiveColorKey:(NSString *)key { objc_setAssociatedObject(self, kSBColorKeyKey, key, OBJC_ASSOCIATION_RETAIN_NONATOMIC); }
+- (NSIndexPath *)activeColorIndexPath { return objc_getAssociatedObject(self, kSBColorIndexPathKey); }
+- (void)setActiveColorIndexPath:(NSIndexPath *)ip { objc_setAssociatedObject(self, kSBColorIndexPathKey, ip, OBJC_ASSOCIATION_RETAIN_NONATOMIC); }
+
+- (void)viewDidLoad {
+ Class ytStyled = objc_getClass("YTStyledViewController");
+ struct objc_super superStruct = { self, ytStyled ?: [UIViewController class] };
+ ((void (*)(struct objc_super *, SEL))objc_msgSendSuper)(&superStruct, @selector(viewDidLoad));
+
+ self.title = @"SponsorBlock";
+
+ self.tableView = [[UITableView alloc] initWithFrame:self.view.bounds style:UITableViewStyleGrouped];
+ self.tableView.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;
+ self.tableView.delegate = self;
+ self.tableView.dataSource = self;
+ self.tableView.separatorStyle = UITableViewCellSeparatorStyleNone;
+ self.tableView.estimatedRowHeight = 60;
+ self.tableView.rowHeight = UITableViewAutomaticDimension;
+
+ if (self.traitCollection.userInterfaceStyle == UIUserInterfaceStyleDark) {
+ self.tableView.backgroundColor = [%c(YTColor) black3];
+ } else {
+ self.tableView.backgroundColor = [UIColor systemBackgroundColor];
+ }
+
+ [self.view addSubview:self.tableView];
+}
+
+- (void)traitCollectionDidChange:(UITraitCollection *)previousTraitCollection {
+ [super traitCollectionDidChange:previousTraitCollection];
+ if (previousTraitCollection.userInterfaceStyle != self.traitCollection.userInterfaceStyle) {
+ self.tableView.backgroundColor = (self.traitCollection.userInterfaceStyle == UIUserInterfaceStyleDark)
+ ? [%c(YTColor) black3]
+ : [UIColor systemBackgroundColor];
+ [self.tableView reloadData];
+ }
+}
+
+- (void)viewWillAppear:(BOOL)animated {
+ Class ytStyled = objc_getClass("YTStyledViewController");
+ struct objc_super superStruct = { self, ytStyled ?: [UIViewController class] };
+ ((void (*)(struct objc_super *, SEL, BOOL))objc_msgSendSuper)(&superStruct, @selector(viewWillAppear:), animated);
+}
+
+- (void)viewDidLayoutSubviews {
+ Class ytStyled = objc_getClass("YTStyledViewController");
+ struct objc_super superStruct = { self, ytStyled ?: [UIViewController class] };
+ ((void (*)(struct objc_super *, SEL))objc_msgSendSuper)(&superStruct, @selector(viewDidLayoutSubviews));
+ YTQTMButton *backButton = [self valueForKey:@"_backButton"];
+
+ if (self.traitCollection.userInterfaceStyle == UIUserInterfaceStyleDark) {
+ backButton.tintColor = [UIColor whiteColor];
+ } else {
+ backButton.tintColor = [UIColor blackColor];
+ }
+}
+
+- (UIColor *)navBarForegroundColor {
+ if (self.traitCollection.userInterfaceStyle == UIUserInterfaceStyleDark) {
+ return [UIColor whiteColor];
+ }
+ // Light mode: return a concrete colour rather than nil. nil lets YouTube tint the
+ // nav-bar foreground (back chevron + title) with its default appearance, which is
+ // system blue in light mode. labelColor keeps it black in light / white in dark.
+ return [UIColor labelColor];
+}
+
+- (UIColor *)sbTextColor {
+ return (self.traitCollection.userInterfaceStyle == UIUserInterfaceStyleDark)
+ ? [UIColor whiteColor] : [UIColor labelColor];
+}
+
+- (UIColor *)sbSecondaryTextColor {
+ return [UIColor colorWithWhite:0.55 alpha:1.0];
+}
+
+#pragma mark - Sections: 0=Main, 1=Sliders, 2=Segments
+
+- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView { return 3; }
+
+- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
+ if (section == 0) return sbToggleRows().count; // toggles
+ if (section == 1) return 2; // sliders
+ return sbAllCategories().count * 2; // action + color per category
+}
+
+- (UIView *)tableView:(UITableView *)tableView viewForHeaderInSection:(NSInteger)section {
+ NSString *title = nil;
+ if (section == 0) title = SB_LOC(@"SB_SECTION_MAIN");
+ else if (section == 2) title = SB_LOC(@"SB_CATEGORIES_HEADER");
+ if (!title) return nil;
+
+ UIView *header = [[UIView alloc] init];
+ UILabel *label = [[UILabel alloc] init];
+ label.text = title;
+ // Match the main YouMod settings section headers (ymSecondaryColor / size 14).
+ label.textColor = [UIColor colorWithWhite:0.55 alpha:1.0];
+ label.font = [UIFont systemFontOfSize:14 weight:UIFontWeightRegular];
+ label.translatesAutoresizingMaskIntoConstraints = NO;
+ [header addSubview:label];
+ [NSLayoutConstraint activateConstraints:@[
+ [label.leadingAnchor constraintEqualToAnchor:header.leadingAnchor constant:16],
+ [label.bottomAnchor constraintEqualToAnchor:header.bottomAnchor constant:-6],
+ ]];
+ return header;
+}
+
+- (CGFloat)tableView:(UITableView *)tableView heightForHeaderInSection:(NSInteger)section {
+ if (section == 1) return 16;
+ return 36;
+}
+
+- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath {
+ if (indexPath.section == 1) return 70;
+ if (indexPath.section == 2) {
+ BOOL isActionRow = (indexPath.row % 2 == 0);
+ NSInteger catIndex = indexPath.row / 2;
+ if (isActionRow && catIndex > 0) return 64; // extra top spacing between groups
+ return 48;
+ }
+ return UITableViewAutomaticDimension;
+}
+
+- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
+ if (indexPath.section == 0) return [self toggleCellForRow:indexPath.row tableView:tableView];
+ if (indexPath.section == 1) return [self sliderCellForRow:indexPath.row tableView:tableView];
+ return [self segmentCellForRow:indexPath.row tableView:tableView];
+}
+
+#pragma mark - Toggle Cells (Section 0)
+
+- (UITableViewCell *)toggleCellForRow:(NSInteger)row tableView:(UITableView *)tableView {
+ UITableViewCell *cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:nil];
+ cell.backgroundColor = [UIColor clearColor];
+ cell.selectionStyle = UITableViewCellSelectionStyleNone;
+ cell.textLabel.textColor = [self sbTextColor];
+ cell.textLabel.font = [UIFont systemFontOfSize:16 weight:UIFontWeightMedium];
+ cell.detailTextLabel.textColor = [self sbSecondaryTextColor];
+ cell.detailTextLabel.font = [UIFont systemFontOfSize:13];
+ cell.detailTextLabel.numberOfLines = 0;
+
+ SBToggleRow *def = sbToggleRows()[row];
+
+ cell.textLabel.text = SB_LOC(def.titleKey);
+ cell.detailTextLabel.text = SB_LOC(def.descKey);
+
+ UISwitch *sw = [[UISwitch alloc] init];
+ sw.on = [[NSUserDefaults standardUserDefaults] boolForKey:def.key];
+ sw.onTintColor = SBControlTintColor();
+ sw.tag = row;
+ [sw addTarget:self action:@selector(toggleChanged:) forControlEvents:UIControlEventValueChanged];
+ cell.accessoryView = sw;
+
+ return cell;
+}
+
+- (void)toggleChanged:(UISwitch *)sender {
+ NSArray *rows = sbToggleRows();
+ if (sender.tag < 0 || sender.tag >= (NSInteger)rows.count) return;
+ NSString *key = rows[sender.tag].key;
+ [[NSUserDefaults standardUserDefaults] setBool:sender.on forKey:key];
+}
+
+#pragma mark - Slider Cells (Section 1)
+
+- (UITableViewCell *)sliderCellForRow:(NSInteger)row tableView:(UITableView *)tableView {
+ UITableViewCell *cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:nil];
+ cell.backgroundColor = [UIColor clearColor];
+ cell.selectionStyle = UITableViewCellSelectionStyleNone;
+
+ NSString *title = (row == 0) ? SB_LOC(@"SB_SKIP_ALERT_DURATION") : SB_LOC(@"SB_UNSKIP_ALERT_DURATION");
+ NSString *key = (row == 0) ? SBSkipAlertDuration : SBUnskipAlertDuration;
+ float currentVal = [[NSUserDefaults standardUserDefaults] floatForKey:key];
+ if (currentVal <= 0) currentVal = SBAlertDurationDefault;
+
+ UILabel *titleLabel = [[UILabel alloc] init];
+ titleLabel.text = title;
+ titleLabel.textColor = [self sbSecondaryTextColor];
+ titleLabel.font = [UIFont systemFontOfSize:13];
+ titleLabel.translatesAutoresizingMaskIntoConstraints = NO;
+
+ UISlider *slider = [[UISlider alloc] init];
+ slider.minimumValue = SBAlertDurationMin;
+ slider.maximumValue = SBAlertDurationMax;
+ slider.value = currentVal;
+ slider.minimumTrackTintColor = SBControlTintColor();
+ slider.maximumTrackTintColor = [UIColor colorWithWhite:0.3 alpha:1.0];
+ slider.translatesAutoresizingMaskIntoConstraints = NO;
+ slider.tag = row;
+ [slider addTarget:self action:@selector(sliderChanged:) forControlEvents:UIControlEventValueChanged];
+
+ UILabel *valueLabel = [[UILabel alloc] init];
+ NSDateComponentsFormatter *formatter = [[NSDateComponentsFormatter alloc] init];
+ formatter.allowedUnits = NSCalendarUnitSecond;
+ formatter.unitsStyle = NSDateComponentsFormatterUnitsStyleAbbreviated;
+
+ valueLabel.text = [formatter stringFromTimeInterval:currentVal];
+ valueLabel.textColor = [self sbSecondaryTextColor];
+ valueLabel.font = [UIFont systemFontOfSize:13];
+ valueLabel.textAlignment = NSTextAlignmentRight;
+ valueLabel.translatesAutoresizingMaskIntoConstraints = NO;
+ valueLabel.tag = SBSliderValueLabelTagBase + row;
+
+ [cell.contentView addSubview:titleLabel];
+ [cell.contentView addSubview:slider];
+ [cell.contentView addSubview:valueLabel];
+
+ [NSLayoutConstraint activateConstraints:@[
+ [titleLabel.topAnchor constraintEqualToAnchor:cell.contentView.topAnchor constant:8],
+ [titleLabel.leadingAnchor constraintEqualToAnchor:cell.contentView.leadingAnchor constant:16],
+
+ [slider.topAnchor constraintEqualToAnchor:titleLabel.bottomAnchor constant:8],
+ [slider.leadingAnchor constraintEqualToAnchor:cell.contentView.leadingAnchor constant:16],
+ [slider.trailingAnchor constraintEqualToAnchor:valueLabel.leadingAnchor constant:-8],
+ [slider.bottomAnchor constraintEqualToAnchor:cell.contentView.bottomAnchor constant:-8],
+
+ [valueLabel.centerYAnchor constraintEqualToAnchor:slider.centerYAnchor],
+ [valueLabel.trailingAnchor constraintEqualToAnchor:cell.contentView.trailingAnchor constant:-16],
+ [valueLabel.widthAnchor constraintEqualToConstant:50],
+ ]];
+
+ return cell;
+}
+
+- (void)sliderChanged:(UISlider *)sender {
+ NSString *key = (sender.tag == 0) ? SBSkipAlertDuration : SBUnskipAlertDuration;
+ int rounded = (int)roundf(sender.value);
+ sender.value = rounded;
+ [[NSUserDefaults standardUserDefaults] setFloat:(float)rounded forKey:key];
+ [[NSUserDefaults standardUserDefaults] synchronize];
+
+ UILabel *valueLabel = (UILabel *)[sender.superview viewWithTag:SBSliderValueLabelTagBase + sender.tag];
+ NSDateComponentsFormatter *formatter = [[NSDateComponentsFormatter alloc] init];
+ formatter.allowedUnits = NSCalendarUnitSecond;
+ formatter.unitsStyle = NSDateComponentsFormatterUnitsStyleAbbreviated;
+
+ valueLabel.text = [formatter stringFromTimeInterval:rounded];
+}
+
+#pragma mark - Segment Cells (Section 2)
+
+- (UITableViewCell *)segmentCellForRow:(NSInteger)row tableView:(UITableView *)tableView {
+ NSInteger catIndex = row / 2;
+ BOOL isColorRow = (row % 2 == 1);
+ NSString *category = sbAllCategories()[catIndex];
+ NSBundle *bundle = SBSettingsBundle();
+ NSString *catLocKey = [NSString stringWithFormat:@"SB_CAT_%@", category];
+ NSString *catName = [bundle localizedStringForKey:catLocKey value:category table:nil];
+
+ if (isColorRow) {
+ return [self colorCellForCategory:category name:catName tableView:tableView];
+ } else {
+ return [self actionCellForCategory:category name:catName tableView:tableView];
+ }
+}
+
+- (UITableViewCell *)actionCellForCategory:(NSString *)category name:(NSString *)catName tableView:(UITableView *)tableView {
+ UITableViewCell *cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:nil];
+ cell.backgroundColor = [UIColor clearColor];
+ cell.selectionStyle = UITableViewCellSelectionStyleNone;
+ cell.textLabel.text = catName;
+ cell.textLabel.textColor = [self sbTextColor];
+ cell.textLabel.font = [UIFont systemFontOfSize:16 weight:UIFontWeightMedium];
+
+ BOOL isHighlight = [category isEqualToString:@"poi_highlight"];
+ NSString *actionKey = SB_ACTION_KEY(category);
+
+ UIButton *menuButton = [UIButton buttonWithType:UIButtonTypeSystem];
+ NSInteger currentAction = [[NSUserDefaults standardUserDefaults] integerForKey:actionKey];
+ [menuButton setTitle:SBActionName(currentAction) forState:UIControlStateNormal];
+ [menuButton setTitleColor:[self sbSecondaryTextColor] forState:UIControlStateNormal];
+ menuButton.titleLabel.font = [UIFont systemFontOfSize:15];
+ [menuButton setImage:[UIImage systemImageNamed:@"chevron.up.chevron.down"] forState:UIControlStateNormal];
+ menuButton.tintColor = [self sbSecondaryTextColor];
+
+ NSMutableArray *menuActions = [NSMutableArray array];
+ // Which actions a category may take differs by kind: a highlight can only be
+ // disabled, skipped-to, or displayed, while a regular segment offers
+ // auto-skip / ask instead of skip-to. These are separate option sets, not
+ // duplication — each action's label is resolved through SBActionLocKey.
+ NSArray *actionOptions;
+ if (isHighlight) {
+ actionOptions = @[@(SBSegmentActionDisable), @(SBSegmentActionAsk), @(SBSegmentActionDisplay)];
+ } else {
+ actionOptions = @[@(SBSegmentActionDisable), @(SBSegmentActionAutoSkip), @(SBSegmentActionAsk), @(SBSegmentActionDisplay)];
+ }
+
+ NSBundle *bundle = SBSettingsBundle();
+ for (NSNumber *option in actionOptions) {
+ NSInteger actionVal = [option integerValue];
+ NSString *actionTitle = [bundle localizedStringForKey:SBActionLocKey((SBSegmentAction)actionVal) value:nil table:nil];
+
+ UIAction *action = [UIAction actionWithTitle:actionTitle image:nil identifier:nil handler:^(__kindof UIAction *a) {
+ [[NSUserDefaults standardUserDefaults] setInteger:actionVal forKey:actionKey];
+ [self.tableView reloadData];
+ }];
+ if (actionVal == currentAction) action.state = UIMenuElementStateOn;
+ [menuActions addObject:action];
+ }
+
+ menuButton.menu = [UIMenu menuWithTitle:catName children:menuActions];
+ menuButton.showsMenuAsPrimaryAction = YES;
+ [menuButton sizeToFit];
+ cell.accessoryView = menuButton;
+
+ return cell;
+}
+
+- (UITableViewCell *)colorCellForCategory:(NSString *)category name:(NSString *)catName tableView:(UITableView *)tableView {
+ UITableViewCell *cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:nil];
+ cell.backgroundColor = [UIColor clearColor];
+ cell.selectionStyle = UITableViewCellSelectionStyleNone;
+ cell.textLabel.text = [NSString stringWithFormat:@"%@ %@", catName, SB_LOC(@"SB_SEGMENT_COLOR_SUFFIX")];
+ cell.textLabel.textColor = [self sbTextColor];
+ cell.textLabel.font = [UIFont systemFontOfSize:15];
+
+ NSString *colorKey = SB_COLOR_KEY(category);
+ NSString *hex = [[NSUserDefaults standardUserDefaults] stringForKey:colorKey];
+ UIColor *color = SBColorFromHex(hex);
+
+ SBColorCircleView *circle = [[SBColorCircleView alloc] initWithFrame:CGRectMake(0, 0, 34, 34) color:color];
+ cell.accessoryView = circle;
+
+ return cell;
+}
+
+- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
+ if (indexPath.section != 2) return;
+ if (indexPath.row % 2 != 1) return; // only color rows are tappable
+
+ NSInteger catIndex = indexPath.row / 2;
+ NSString *category = sbAllCategories()[catIndex];
+ NSString *colorKey = SB_COLOR_KEY(category);
+
+ self.activeColorKey = colorKey;
+ self.activeColorIndexPath = indexPath;
+
+ UIColorPickerViewController *picker = [[UIColorPickerViewController alloc] init];
+ NSString *catName = [SBSettingsBundle() localizedStringForKey:[NSString stringWithFormat:@"SB_CAT_%@", category] value:category table:nil];
+ picker.title = [NSString stringWithFormat:@"%@ %@", catName, SB_LOC(@"SB_SEGMENT_COLOR_SUFFIX")];
+ NSString *currentHex = [[NSUserDefaults standardUserDefaults] stringForKey:colorKey];
+ if (currentHex) picker.selectedColor = SBColorFromHex(currentHex);
+ picker.supportsAlpha = NO;
+ picker.delegate = self;
+
+ [self presentViewController:picker animated:YES completion:nil];
+}
+
+#pragma mark - UIColorPickerViewControllerDelegate
+
+- (void)colorPickerViewControllerDidFinish:(UIColorPickerViewController *)viewController {
+ UIColor *color = viewController.selectedColor;
+ NSString *hex = SBHexFromColor(color);
+ [[NSUserDefaults standardUserDefaults] setObject:hex forKey:self.activeColorKey];
+ [self.tableView reloadRowsAtIndexPaths:@[self.activeColorIndexPath] withRowAnimation:UITableViewRowAnimationNone];
+}
+
+- (void)colorPickerViewController:(UIColorPickerViewController *)viewController didSelectColor:(UIColor *)color continuously:(BOOL)continuously {
+ if (!continuously) {
+ NSString *hex = SBHexFromColor(color);
+ [[NSUserDefaults standardUserDefaults] setObject:hex forKey:self.activeColorKey];
+ [self.tableView reloadRowsAtIndexPaths:@[self.activeColorIndexPath] withRowAnimation:UITableViewRowAnimationNone];
+ }
+}
+
+#pragma mark - Footer spacing between category groups
+
+- (CGFloat)tableView:(UITableView *)tableView heightForFooterInSection:(NSInteger)section {
+ return (section == 2) ? 0 : 16;
+}
+
+- (UIView *)tableView:(UITableView *)tableView viewForFooterInSection:(NSInteger)section {
+ return [[UIView alloc] init];
+}
+
+@end
+
+#pragma mark - Hook entry point
+
+@interface YTSettingsSectionItemManager (SponsorBlock)
+- (void)updateSponsorBlockSectionWithEntry:(id)entry;
+@end
+
+%hook YTSettingsSectionItemManager
+
+%new(v@:@)
+- (void)updateSponsorBlockSectionWithEntry:(id)entry {
+ YTSettingsViewController *settingsVC = [self valueForKey:@"_settingsViewControllerDelegate"];
+ // Use runtime-registered subclass of YTStyledViewController for YouTube's nav styling
+ Class sbClass = objc_getClass("SBSettingsViewControllerStyled");
+ if (!sbClass) sbClass = [SBSettingsViewController class];
+ // initWithParentResponder: sets up YouTube's DI container (gimme) for nav bar theming
+ id allocated = [sbClass alloc];
+ SBSettingsViewController *sbVC = (SBSettingsViewController *)((id (*)(id, SEL, id))objc_msgSend)(allocated, @selector(initWithParentResponder:), settingsVC);
+ [settingsVC pushViewController:sbVC];
+}
+
+%end
+
+%ctor {
+ // Register SBSettingsViewControllerStyled as a runtime subclass of YTStyledViewController
+ // with all methods from SBSettingsViewController — gives us YouTube's nav bar styling
+ Class ytStyled = %c(YTStyledViewController);
+ if (ytStyled) {
+ Class sbStyled = objc_allocateClassPair(ytStyled, "SBSettingsViewControllerStyled", 0);
+ if (sbStyled) {
+ // Copy all instance methods from our compiled SBSettingsViewController
+ unsigned int count = 0;
+ Method *methods = class_copyMethodList([SBSettingsViewController class], &count);
+ for (unsigned int i = 0; i < count; i++) {
+ SEL sel = method_getName(methods[i]);
+ IMP imp = method_getImplementation(methods[i]);
+ const char *types = method_getTypeEncoding(methods[i]);
+ class_addMethod(sbStyled, sel, imp, types);
+ }
+ free(methods);
+
+ // Copy properties (for @synthesize ivars)
+ unsigned int propCount = 0;
+ objc_property_t *props = class_copyPropertyList([SBSettingsViewController class], &propCount);
+ for (unsigned int i = 0; i < propCount; i++) {
+ unsigned int attrCount = 0;
+ objc_property_attribute_t *attrs = property_copyAttributeList(props[i], &attrCount);
+ class_addProperty(sbStyled, property_getName(props[i]), attrs, attrCount);
+ free(attrs);
+ }
+ free(props);
+
+ // Copy ivars won't work after registration, but properties use associated objects
+ objc_registerClassPair(sbStyled);
+ }
+ }
+
+ // Per-category default action and seek-bar color. Sponsor is the only
+ // category that auto-skips out of the box; the rest are disabled until the
+ // user opts in. The action/color default entries are generated from
+ // sbAllCategories() below so this table stays the sole per-category source.
+ NSDictionary *categoryDefaults = @{
+ @"sponsor": @[@(SBSegmentActionAutoSkip), @"#00D400"],
+ @"intro": @[@(SBSegmentActionDisable), @"#00FFFF"],
+ @"outro": @[@(SBSegmentActionDisable), @"#0202ED"],
+ @"interaction": @[@(SBSegmentActionDisable), @"#FF00F7"],
+ @"selfpromo": @[@(SBSegmentActionDisable), @"#FFFF00"],
+ @"music_offtopic": @[@(SBSegmentActionDisable), @"#FF9900"],
+ @"preview": @[@(SBSegmentActionDisable), @"#0084D6"],
+ @"hook": @[@(SBSegmentActionDisable), @"#395699"],
+ @"poi_highlight": @[@(SBSegmentActionDisable), @"#FF006A"],
+ @"filler": @[@(SBSegmentActionDisable), @"#7300FF"],
+ };
+
+ NSMutableDictionary *defaults = [@{
+ SBEnabled: @YES,
+ SBShowButton: @YES,
+ SBShowNotifications: @YES,
+ SBSegmentsInPlayer: @YES,
+ SBSegmentsInFeed: @YES,
+ SBSegmentsInMiniPlayer: @YES,
+ SBSkipAlertDuration: @(SBAlertDurationDefault),
+ SBUnskipAlertDuration: @(SBAlertDurationDefault),
+ } mutableCopy];
+
+ for (NSString *category in sbAllCategories()) {
+ NSArray *def = categoryDefaults[category];
+ if (!def) continue;
+ defaults[SB_ACTION_KEY(category)] = def[0];
+ defaults[SB_COLOR_KEY(category)] = def[1];
+ }
+
+ [[NSUserDefaults standardUserDefaults] registerDefaults:defaults];
+ %init;
+}
diff --git a/Files/SponsorBlockUI.x b/Files/SponsorBlockUI.x
new file mode 100644
index 00000000..b5715aec
--- /dev/null
+++ b/Files/SponsorBlockUI.x
@@ -0,0 +1,892 @@
+#import "Headers.h"
+#import
+
+extern BOOL useBackwardIconForButton;
+
+// Range segments render as a filled bar at least this wide so very short segments
+// stay visible. Point segments (poi_highlight) have no duration, so they render as
+// a fixed-width tick centered on their position via the x-offset.
+static const CGFloat SBMarkerMinWidth = 2.0;
+static const CGFloat SBPoiMarkerWidth = 3.0;
+static const CGFloat SBPoiMarkerXOffset = 1.5;
+
+// Height of the segment markers drawn on the inline (thumbnail) player bar,
+// bottom-aligned so they sit flush with the bottom edge of the bar.
+static const CGFloat SBInlineMarkerHeight = 4.0;
+
+#pragma mark - SBSkipNotificationView Implementation
+
+@implementation SBSkipNotificationView
+
++ (instancetype)showInView:(UIView *)parentView message:(NSString *)message buttonTitle:(NSString *)buttonTitle action:(void (^)(void))action duration:(NSTimeInterval)duration {
+ if (!parentView) return nil;
+
+ for (UIView *sub in [parentView.subviews copy]) {
+ if ([sub isKindOfClass:[SBSkipNotificationView class]]) {
+ SBSkipNotificationView *existing = (SBSkipNotificationView *)sub;
+ if (existing.isHighlightPill) return nil;
+ [existing dismiss];
+ }
+ }
+
+ SBSkipNotificationView *view = [[SBSkipNotificationView alloc] initWithFrame:CGRectZero];
+ view.translatesAutoresizingMaskIntoConstraints = NO;
+ view.clipsToBounds = YES;
+ view.layer.cornerRadius = 22.0;
+ view.onAction = action;
+ view.totalDuration = duration;
+ view.remainingDuration = duration;
+ view.isPaused = NO;
+
+ // Base layer (revealed as progress depletes)
+ view.backgroundColor = [UIColor colorWithWhite:0.08 alpha:1.0];
+
+ // Progress overlay (shrinks from right to left)
+ UIView *progressOverlay = [[UIView alloc] initWithFrame:CGRectZero];
+ progressOverlay.translatesAutoresizingMaskIntoConstraints = YES;
+ progressOverlay.backgroundColor = [UIColor colorWithWhite:0.18 alpha:1.0];
+ progressOverlay.userInteractionEnabled = NO;
+ progressOverlay.layer.anchorPoint = CGPointMake(0, 0.5);
+ progressOverlay.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;
+ view.progressOverlay = progressOverlay;
+ [view addSubview:progressOverlay];
+
+ // Message label
+ UILabel *label = [[UILabel alloc] initWithFrame:CGRectZero];
+ label.translatesAutoresizingMaskIntoConstraints = NO;
+ label.text = message;
+ label.textColor = [UIColor whiteColor];
+ label.font = [UIFont systemFontOfSize:14.0 weight:UIFontWeightMedium];
+ label.numberOfLines = 2;
+ label.lineBreakMode = NSLineBreakByTruncatingTail;
+ view.messageLabel = label;
+ [view addSubview:label];
+
+ // Icon button (right side)
+ BOOL showButton = (buttonTitle != nil || action != nil);
+ UIButton *button = nil;
+
+ if (showButton) {
+ button = [UIButton buttonWithType:UIButtonTypeCustom];
+ button.translatesAutoresizingMaskIntoConstraints = NO;
+
+ NSString *iconName = useBackwardIconForButton ? @"backward.fill" : @"forward.end.fill";
+ UIImageSymbolConfiguration *config = [UIImageSymbolConfiguration configurationWithPointSize:14 weight:UIImageSymbolWeightMedium];
+ UIImage *icon = [UIImage systemImageNamed:iconName withConfiguration:config];
+ [button setImage:icon forState:UIControlStateNormal];
+ button.tintColor = [UIColor whiteColor];
+ button.backgroundColor = [UIColor colorWithWhite:1.0 alpha:0.15];
+ button.layer.cornerRadius = 16.0;
+ button.clipsToBounds = YES;
+ [button addTarget:view action:@selector(actionButtonTapped) forControlEvents:UIControlEventTouchUpInside];
+ view.actionButton = button;
+ [view addSubview:button];
+ }
+
+ [parentView addSubview:view];
+
+ // Layout: centered horizontally, anchored above tab bar via safe area
+ NSLayoutConstraint *maxWidth = [view.widthAnchor constraintLessThanOrEqualToAnchor:parentView.widthAnchor multiplier:0.85];
+ [NSLayoutConstraint activateConstraints:@[
+ [view.centerXAnchor constraintEqualToAnchor:parentView.centerXAnchor],
+ [view.bottomAnchor constraintEqualToAnchor:parentView.safeAreaLayoutGuide.bottomAnchor constant:-60.0],
+ [view.heightAnchor constraintEqualToConstant:44.0],
+ maxWidth
+ ]];
+
+ // Internal layout
+ if (showButton) {
+ [NSLayoutConstraint activateConstraints:@[
+ [label.leadingAnchor constraintEqualToAnchor:view.leadingAnchor constant:16.0],
+ [label.centerYAnchor constraintEqualToAnchor:view.centerYAnchor],
+ [label.trailingAnchor constraintEqualToAnchor:button.leadingAnchor constant:-10.0],
+
+ [button.trailingAnchor constraintEqualToAnchor:view.trailingAnchor constant:-8.0],
+ [button.centerYAnchor constraintEqualToAnchor:view.centerYAnchor],
+ [button.widthAnchor constraintEqualToConstant:32.0],
+ [button.heightAnchor constraintEqualToConstant:32.0]
+ ]];
+ } else {
+ [NSLayoutConstraint activateConstraints:@[
+ [label.leadingAnchor constraintEqualToAnchor:view.leadingAnchor constant:16.0],
+ [label.centerYAnchor constraintEqualToAnchor:view.centerYAnchor],
+ [label.trailingAnchor constraintEqualToAnchor:view.trailingAnchor constant:-40.0],
+ ]];
+ }
+
+ // Pan gesture for interactive dismissal
+ UIPanGestureRecognizer *pan = [[UIPanGestureRecognizer alloc] initWithTarget:view action:@selector(handlePan:)];
+ [view addGestureRecognizer:pan];
+
+ // Slide up from below
+ view.transform = CGAffineTransformMakeTranslation(0, 60);
+ view.alpha = 0.0;
+ [UIView animateWithDuration:0.4 delay:0 usingSpringWithDamping:0.85 initialSpringVelocity:0.5 options:UIViewAnimationOptionCurveEaseOut animations:^{
+ view.alpha = 1.0;
+ view.transform = CGAffineTransformIdentity;
+ } completion:^(BOOL finished) {
+ if (finished && duration > 0) {
+ [view startProgressAnimation];
+ }
+ }];
+
+ return view;
+}
+
+- (void)layoutSubviews {
+ [super layoutSubviews];
+ if (self.progressOverlay.layer.animationKeys.count == 0 || self.isPaused) {
+ self.progressOverlay.frame = CGRectMake(0, 0, self.bounds.size.width, self.bounds.size.height);
+ }
+}
+
+- (void)startProgressAnimation {
+ if (self.remainingDuration <= 0) return;
+
+ self.progressOverlay.frame = CGRectMake(0, 0, self.bounds.size.width, self.bounds.size.height);
+
+ [UIView animateWithDuration:self.remainingDuration delay:0 options:UIViewAnimationOptionCurveLinear animations:^{
+ self.progressOverlay.transform = CGAffineTransformMakeScale(0.001, 1.0);
+ self.progressOverlay.alpha = 0.0;
+ } completion:^(BOOL finished) {
+ if (finished && !self.isPaused && self.superview) {
+ [self dismiss];
+ }
+ }];
+}
+
+- (void)pauseProgress {
+ if (self.isPaused) return;
+ self.isPaused = YES;
+
+ CALayer *presentationLayer = self.progressOverlay.layer.presentationLayer;
+ CGFloat currentScaleX = 1.0;
+ if (presentationLayer) {
+ CATransform3D t = presentationLayer.transform;
+ currentScaleX = t.m11;
+ }
+
+ [self.progressOverlay.layer removeAllAnimations];
+ currentScaleX = MAX(0.001, MIN(currentScaleX, 1.0));
+ self.progressOverlay.transform = CGAffineTransformMakeScale(currentScaleX, 1.0);
+ self.progressOverlay.alpha = currentScaleX;
+ self.remainingDuration = self.totalDuration * currentScaleX;
+}
+
+- (void)resumeProgress {
+ if (!self.isPaused) return;
+ self.isPaused = NO;
+
+ if (self.remainingDuration <= 0) {
+ [self dismiss];
+ return;
+ }
+
+ [UIView animateWithDuration:self.remainingDuration delay:0 options:UIViewAnimationOptionCurveLinear animations:^{
+ self.progressOverlay.transform = CGAffineTransformMakeScale(0.001, 1.0);
+ self.progressOverlay.alpha = 0.0;
+ } completion:^(BOOL finished) {
+ if (finished && !self.isPaused && self.superview) {
+ [self dismiss];
+ }
+ }];
+}
+
+- (void)handlePan:(UIPanGestureRecognizer *)gesture {
+ if (self.alpha < 1.0) {
+ gesture.enabled = NO;
+ gesture.enabled = YES;
+ return;
+ }
+
+ CGPoint translation = [gesture translationInView:self.superview];
+ CGPoint velocity = [gesture velocityInView:self.superview];
+
+ switch (gesture.state) {
+ case UIGestureRecognizerStateBegan:
+ [self pauseProgress];
+ break;
+
+ case UIGestureRecognizerStateChanged:
+ self.transform = CGAffineTransformMakeTranslation(0, translation.y);
+ break;
+
+ case UIGestureRecognizerStateEnded:
+ case UIGestureRecognizerStateCancelled: {
+ CGFloat distanceThreshold = 30.0;
+ CGFloat velocityThreshold = 500.0;
+ BOOL shouldDismiss = (fabs(translation.y) > distanceThreshold) || (fabs(velocity.y) > velocityThreshold);
+
+ if (shouldDismiss) {
+ CGFloat direction = (translation.y < 0) ? -1.0 : 1.0;
+ [self dismissInDirection:direction velocity:fabs(velocity.y)];
+ } else {
+ // Snap back
+ [UIView animateWithDuration:0.3 delay:0 usingSpringWithDamping:0.8 initialSpringVelocity:0.5 options:UIViewAnimationOptionBeginFromCurrentState animations:^{
+ self.transform = CGAffineTransformIdentity;
+ } completion:^(BOOL finished) {
+ [self resumeProgress];
+ }];
+ }
+ break;
+ }
+ default:
+ break;
+ }
+}
+
+- (void)dismissInDirection:(CGFloat)direction velocity:(CGFloat)velocity {
+ CGFloat offscreenY = direction < 0 ? -(self.frame.size.height + 80) : (self.frame.size.height + 80);
+ CGFloat animDuration = velocity > 500 ? 0.2 : 0.35;
+
+ [UIView animateWithDuration:animDuration delay:0 options:UIViewAnimationOptionCurveEaseIn animations:^{
+ self.transform = CGAffineTransformMakeTranslation(0, offscreenY);
+ self.alpha = 0.0;
+ } completion:^(BOOL finished) {
+ [self removeFromSuperview];
+ }];
+}
+
+- (void)actionButtonTapped {
+ if (self.onAction) {
+ self.onAction();
+ }
+ [self dismiss];
+}
+
+- (void)dismiss {
+ [self.progressOverlay.layer removeAllAnimations];
+ [UIView animateWithDuration:0.25 delay:0 options:UIViewAnimationOptionCurveEaseIn animations:^{
+ self.transform = CGAffineTransformMakeTranslation(0, 60);
+ self.alpha = 0.0;
+ } completion:^(BOOL finished) {
+ [self removeFromSuperview];
+ }];
+}
+
++ (instancetype)showSuccessInView:(UIView *)parentView message:(NSString *)message duration:(NSTimeInterval)duration {
+ SBSkipNotificationView *view = [self showInView:parentView message:message buttonTitle:nil action:nil duration:duration];
+ if (view) {
+ UIImageSymbolConfiguration *config = [UIImageSymbolConfiguration configurationWithPointSize:16 weight:UIImageSymbolWeightMedium];
+ UIImageView *iconView = [[UIImageView alloc] initWithImage:[UIImage systemImageNamed:@"checkmark.circle.fill" withConfiguration:config]];
+ iconView.tintColor = [UIColor systemGreenColor];
+ iconView.translatesAutoresizingMaskIntoConstraints = NO;
+ [view addSubview:iconView];
+ [NSLayoutConstraint activateConstraints:@[
+ [iconView.trailingAnchor constraintEqualToAnchor:view.trailingAnchor constant:-12.0],
+ [iconView.centerYAnchor constraintEqualToAnchor:view.centerYAnchor],
+ ]];
+ }
+ return view;
+}
+
++ (instancetype)showErrorInView:(UIView *)parentView message:(NSString *)message duration:(NSTimeInterval)duration {
+ SBSkipNotificationView *view = [self showInView:parentView message:message buttonTitle:nil action:nil duration:duration];
+ if (view) {
+ UIImageSymbolConfiguration *config = [UIImageSymbolConfiguration configurationWithPointSize:16 weight:UIImageSymbolWeightMedium];
+ UIImageView *iconView = [[UIImageView alloc] initWithImage:[UIImage systemImageNamed:@"xmark.circle.fill" withConfiguration:config]];
+ iconView.tintColor = [UIColor systemRedColor];
+ iconView.translatesAutoresizingMaskIntoConstraints = NO;
+ [view addSubview:iconView];
+ [NSLayoutConstraint activateConstraints:@[
+ [iconView.trailingAnchor constraintEqualToAnchor:view.trailingAnchor constant:-12.0],
+ [iconView.centerYAnchor constraintEqualToAnchor:view.centerYAnchor],
+ ]];
+ }
+ return view;
+}
+
+@end
+
+#pragma mark - YMDownloadProgressView
+
+@implementation YMDownloadProgressView
+
++ (instancetype)showInView:(UIView *)parentView message:(NSString *)message cancelAction:(void (^)(void))cancelAction {
+ if (!parentView) return nil;
+
+ // Dismiss any existing download progress pill
+ for (UIView *sub in [parentView.subviews copy]) {
+ if ([sub isKindOfClass:[YMDownloadProgressView class]]) {
+ [(YMDownloadProgressView *)sub dismiss];
+ }
+ }
+
+ YMDownloadProgressView *view = [[YMDownloadProgressView alloc] init];
+ view.onCancel = cancelAction;
+ view.translatesAutoresizingMaskIntoConstraints = NO;
+ view.backgroundColor = [UIColor colorWithWhite:0.12 alpha:1.0];
+ view.layer.cornerRadius = 16.0;
+ view.clipsToBounds = YES;
+ view.layer.borderWidth = 0.5;
+ view.layer.borderColor = [UIColor colorWithWhite:0.25 alpha:1.0].CGColor;
+
+ // Title label
+ UILabel *titleLabel = [[UILabel alloc] init];
+ titleLabel.text = message;
+ titleLabel.textColor = [UIColor whiteColor];
+ titleLabel.font = [UIFont systemFontOfSize:14 weight:UIFontWeightSemibold];
+ titleLabel.translatesAutoresizingMaskIntoConstraints = NO;
+ view.titleLabel = titleLabel;
+ [view addSubview:titleLabel];
+
+ // Subtitle label (speed + size)
+ UILabel *subtitleLabel = [[UILabel alloc] init];
+ subtitleLabel.text = @"";
+ subtitleLabel.textColor = [UIColor colorWithWhite:0.55 alpha:1.0];
+ subtitleLabel.font = [UIFont systemFontOfSize:12 weight:UIFontWeightRegular];
+ subtitleLabel.translatesAutoresizingMaskIntoConstraints = NO;
+ view.subtitleLabel = subtitleLabel;
+ [view addSubview:subtitleLabel];
+
+ // Progress bar
+ UIProgressView *progressBar = [[UIProgressView alloc] initWithProgressViewStyle:UIProgressViewStyleDefault];
+ progressBar.progress = 0.0;
+ progressBar.trackTintColor = [UIColor colorWithWhite:0.22 alpha:1.0];
+ progressBar.progressTintColor = [UIColor colorWithRed:0.6 green:0.2 blue:0.9 alpha:1.0];
+ progressBar.translatesAutoresizingMaskIntoConstraints = NO;
+ progressBar.layer.cornerRadius = 3.0;
+ progressBar.clipsToBounds = YES;
+ view.progressBar = progressBar;
+ [view addSubview:progressBar];
+
+ // Cancel button
+ UIButton *cancelButton = [UIButton buttonWithType:UIButtonTypeSystem];
+ UIImageSymbolConfiguration *config = [UIImageSymbolConfiguration configurationWithPointSize:18 weight:UIImageSymbolWeightMedium];
+ [cancelButton setImage:[UIImage systemImageNamed:@"xmark.circle.fill" withConfiguration:config] forState:UIControlStateNormal];
+ cancelButton.tintColor = [UIColor colorWithWhite:0.45 alpha:1.0];
+ cancelButton.translatesAutoresizingMaskIntoConstraints = NO;
+ [cancelButton addTarget:view action:@selector(cancelButtonTapped) forControlEvents:UIControlEventTouchUpInside];
+ view.cancelButton = cancelButton;
+ [view addSubview:cancelButton];
+
+ // Layout
+ [NSLayoutConstraint activateConstraints:@[
+ [titleLabel.leadingAnchor constraintEqualToAnchor:view.leadingAnchor constant:16],
+ [titleLabel.topAnchor constraintEqualToAnchor:view.topAnchor constant:12],
+ [titleLabel.trailingAnchor constraintEqualToAnchor:cancelButton.leadingAnchor constant:-10],
+
+ [subtitleLabel.leadingAnchor constraintEqualToAnchor:view.leadingAnchor constant:16],
+ [subtitleLabel.topAnchor constraintEqualToAnchor:titleLabel.bottomAnchor constant:3],
+ [subtitleLabel.trailingAnchor constraintEqualToAnchor:cancelButton.leadingAnchor constant:-10],
+
+ [progressBar.leadingAnchor constraintEqualToAnchor:view.leadingAnchor constant:16],
+ [progressBar.trailingAnchor constraintEqualToAnchor:view.trailingAnchor constant:-16],
+ [progressBar.topAnchor constraintEqualToAnchor:subtitleLabel.bottomAnchor constant:10],
+ [progressBar.bottomAnchor constraintEqualToAnchor:view.bottomAnchor constant:-14],
+ [progressBar.heightAnchor constraintEqualToConstant:6],
+
+ [cancelButton.trailingAnchor constraintEqualToAnchor:view.trailingAnchor constant:-14],
+ [cancelButton.centerYAnchor constraintEqualToAnchor:titleLabel.centerYAnchor],
+ [cancelButton.widthAnchor constraintEqualToConstant:32],
+ [cancelButton.heightAnchor constraintEqualToConstant:32],
+ ]];
+
+ [parentView addSubview:view];
+
+ // Center horizontally with max width
+ NSLayoutConstraint *centerX = [view.centerXAnchor constraintEqualToAnchor:parentView.centerXAnchor];
+ NSLayoutConstraint *maxWidth = [view.widthAnchor constraintLessThanOrEqualToConstant:360];
+ NSLayoutConstraint *leadingFallback = [view.leadingAnchor constraintGreaterThanOrEqualToAnchor:parentView.leadingAnchor constant:16];
+ NSLayoutConstraint *trailingFallback = [view.trailingAnchor constraintLessThanOrEqualToAnchor:parentView.trailingAnchor constant:-16];
+ NSLayoutConstraint *preferredWidth = [view.widthAnchor constraintEqualToAnchor:parentView.widthAnchor constant:-32];
+ preferredWidth.priority = UILayoutPriorityDefaultHigh;
+
+ [NSLayoutConstraint activateConstraints:@[
+ centerX, maxWidth, leadingFallback, trailingFallback, preferredWidth,
+ [view.bottomAnchor constraintEqualToAnchor:parentView.safeAreaLayoutGuide.bottomAnchor constant:-12],
+ ]];
+
+ // Slide-up animation
+ view.transform = CGAffineTransformMakeTranslation(0, 80);
+ view.alpha = 0;
+ [UIView animateWithDuration:0.35 delay:0 usingSpringWithDamping:0.75 initialSpringVelocity:0.5 options:UIViewAnimationOptionCurveEaseOut animations:^{
+ view.transform = CGAffineTransformIdentity;
+ view.alpha = 1.0;
+ } completion:nil];
+
+ return view;
+}
+
+- (void)updateProgress:(float)progress title:(NSString *)title subtitle:(NSString *)subtitle {
+ self.titleLabel.text = title;
+ self.subtitleLabel.text = subtitle;
+ [self.progressBar setProgress:progress animated:YES];
+}
+
+- (void)cancelButtonTapped {
+ if (self.onCancel) {
+ self.onCancel();
+ [self dismiss];
+ }
+}
+
+- (void)dismiss {
+ if (!self.superview) return;
+ [UIView animateWithDuration:0.25 animations:^{
+ self.transform = CGAffineTransformMakeTranslation(0, 80);
+ self.alpha = 0;
+ } completion:^(BOOL finished) {
+ [self removeFromSuperview];
+ }];
+}
+
+@end
+
+#pragma mark - YTSegmentableInlinePlayerBarView Hook (Seek Bar Markers)
+
+%hook YTSegmentableInlinePlayerBarView
+%property (nonatomic, strong) NSArray *sbMarkerViews;
+
+- (void)layoutSubviews {
+ %orig;
+ if (self.sbMarkerViews.count > 0) {
+ [self sbRepositionMarkers];
+ }
+}
+
+%new
+- (void)sbRenderSegments:(NSArray *)segments {
+ [self sbClearSegments];
+
+ CGFloat totalTime = 0;
+ @try {
+ totalTime = [[self valueForKey:@"totalTime"] floatValue];
+ } @catch (NSException *e) { return; }
+ if (totalTime <= 0 || segments.count == 0) return;
+
+ CGFloat barWidth = self.bounds.size.width;
+ CGFloat barHeight = self.bounds.size.height;
+ if (barWidth <= 0) return;
+
+ NSMutableArray *markers = [NSMutableArray array];
+
+ for (SBSegment *segment in segments) {
+ SBSegmentAction action = [segment configuredAction];
+ if (action == SBSegmentActionDisable) continue;
+
+ CGFloat startFrac = segment.startTime / totalTime;
+ CGFloat endFrac = segment.endTime / totalTime;
+ CGFloat x = startFrac * barWidth;
+ CGFloat w = (endFrac - startFrac) * barWidth;
+ if (w < SBMarkerMinWidth) w = SBMarkerMinWidth;
+
+ UIView *marker = [[UIView alloc] initWithFrame:CGRectMake(x, barHeight - SBInlineMarkerHeight, w, SBInlineMarkerHeight)];
+ marker.backgroundColor = [segment segmentColor];
+ marker.userInteractionEnabled = NO;
+ marker.layer.name = [NSString stringWithFormat:@"%f|%f", segment.startTime, segment.endTime];
+
+ [self addSubview:marker];
+ [markers addObject:marker];
+ }
+
+ self.sbMarkerViews = [markers copy];
+}
+
+%new
+- (void)sbRepositionMarkers {
+ CGFloat totalTime = 0;
+ @try {
+ totalTime = [[self valueForKey:@"totalTime"] floatValue];
+ } @catch (NSException *e) { return; }
+ if (totalTime <= 0) return;
+
+ CGFloat barWidth = self.bounds.size.width;
+ CGFloat barHeight = self.bounds.size.height;
+ if (barWidth <= 0) return;
+
+ for (UIView *marker in self.sbMarkerViews) {
+ NSString *name = marker.layer.name;
+ NSArray *parts = [name componentsSeparatedByString:@"|"];
+ if (parts.count < 2) continue;
+
+ CGFloat startTime = [parts[0] floatValue];
+ CGFloat endTime = [parts[1] floatValue];
+ CGFloat startFrac = startTime / totalTime;
+ CGFloat endFrac = endTime / totalTime;
+ CGFloat x = startFrac * barWidth;
+ CGFloat w = (endFrac - startFrac) * barWidth;
+ if (w < SBMarkerMinWidth) w = SBMarkerMinWidth;
+
+ marker.frame = CGRectMake(x, barHeight - SBInlineMarkerHeight, w, SBInlineMarkerHeight);
+ }
+}
+
+%new
+- (void)sbClearSegments {
+ for (UIView *marker in self.sbMarkerViews) {
+ [marker removeFromSuperview];
+ }
+ self.sbMarkerViews = nil;
+}
+
+%end
+
+#pragma mark - Marker Repositioning Hooks
+
+// YTModularPlayerBarView - normal player
+%hook YTModularPlayerBarView
+
+- (void)layoutSubviews {
+ %orig;
+ CGFloat barWidth = self.bounds.size.width;
+ if (barWidth <= 0) return;
+
+ // Find reference view for Y
+ UIView *referenceView = nil;
+ for (UIView *sub in self.subviews) {
+ if ([sub isKindOfClass:%c(YTPlayerBarRectangleDecorationView)] ||
+ [sub isKindOfClass:%c(YTPlayerBarProgressDecorationView)]) {
+ referenceView = sub;
+ break;
+ }
+ }
+
+ for (UIView *sub in self.subviews) {
+ if (sub.tag != SBSegmentMarkerTag) continue;
+ NSArray *data = objc_getAssociatedObject(sub, @selector(sbSegmentData));
+ if (!data || data.count < 3) continue;
+
+ CGFloat startFrac = [data[0] floatValue];
+ CGFloat endFrac = [data[1] floatValue];
+ BOOL isPoi = [data[2] boolValue];
+
+ CGFloat x = startFrac * barWidth;
+ CGFloat w = (endFrac - startFrac) * barWidth;
+ if (isPoi) { w = SBPoiMarkerWidth; x = MAX(0, x - SBPoiMarkerXOffset); }
+ else if (w < SBMarkerMinWidth) w = SBMarkerMinWidth;
+
+ sub.frame = CGRectMake(x, referenceView.frame.origin.y, w, referenceView.frame.size.height);
+ }
+
+ // Keep the segment markers above YouTube's decoration views, and the scrubber
+ // dot above the markers, so the stack stays track < markers < dot. YouTube
+ // rebuilds its own decorations on top of ours on each layout (splitting the
+ // progress view per chapter), which would otherwise bury the markers.
+ NSArray *subs = self.subviews;
+ NSMutableArray *markers = [NSMutableArray array];
+ UIView *scrubberDot = nil;
+ @try {
+ scrubberDot = [self valueForKey:@"_scrubberCircle"];
+ } @catch (id ex) {}
+ for (UIView *sub in subs) {
+ if (sub.tag == SBSegmentMarkerTag) {
+ [markers addObject:sub];
+ } else if ([sub isKindOfClass:%c(YTPlayerBarScrubberDotDecorationView)]) {
+ if (!scrubberDot) scrubberDot = sub.subviews.firstObject;
+ }
+ }
+ if (markers.count == 0) return;
+
+ // Desired top group, front-most last: the markers followed by the dot. Reorder
+ // only when the subview tail doesn't already match it, so a settled layout is a
+ // no-op and doesn't trigger a fresh layout pass on every runloop cycle.
+ NSMutableArray *desiredTail = [markers mutableCopy];
+ if (scrubberDot) [desiredTail addObject:scrubberDot];
+ BOOL settled = subs.count >= desiredTail.count;
+ for (NSUInteger i = 0; settled && i < desiredTail.count; i++) {
+ if (subs[subs.count - desiredTail.count + i] != desiredTail[i]) settled = NO;
+ }
+ if (settled) return;
+
+ for (UIView *marker in markers) [self bringSubviewToFront:marker];
+ if (scrubberDot) [self bringSubviewToFront:scrubberDot];
+}
+
+%end
+
+// YTWatchFloatingMiniplayerWithPersistentControlsView - miniplayer
+%hook YTWatchFloatingMiniplayerWithPersistentControlsView
+- (void)layoutSubviews {
+ %orig;
+ if (self.dockHandleStyle == 2) {
+ for (UIView *sub in [self.subviews copy]) {
+ if (sub.tag == SBSegmentMarkerTag) sub.hidden = YES;
+ }
+ return;
+ }
+ for (UIView *sub in [self.subviews copy]) {
+ if (sub.tag == SBSegmentMarkerTag) sub.hidden = NO;
+ }
+ UIView *playerBar;
+ for (UIView *sub in self.subviews) {
+ for (UIView *sub2 in sub.subviews) {
+ if ([sub2 isKindOfClass:%c(YTWatchFloatingMiniplayerProgressBarView)]) {
+ playerBar = sub2;
+ break;
+ }
+ }
+ if (playerBar) break;
+ }
+ CGFloat barWidth = playerBar.bounds.size.width;
+
+ for (UIView *sub in self.subviews) {
+ if (sub.tag != SBSegmentMarkerTag) continue;
+ NSArray *data = objc_getAssociatedObject(sub, @selector(sbSegmentData));
+ if (!data || data.count < 3) continue;
+
+ CGFloat startFrac = [data[0] floatValue];
+ CGFloat endFrac = [data[1] floatValue];
+ BOOL isPoi = [data[2] boolValue];
+
+ CGFloat x = startFrac * barWidth;
+ CGFloat w = (endFrac - startFrac) * barWidth;
+ if (isPoi) { w = SBPoiMarkerWidth; x = MAX(0, x - SBPoiMarkerXOffset); }
+ else if (w < SBMarkerMinWidth) w = SBMarkerMinWidth;
+
+ sub.frame = CGRectMake(x, playerBar.frame.origin.y, w, playerBar.bounds.size.height);
+ }
+}
+%end
+
+#pragma mark - YTPlayerViewController Hook (Notification Observer)
+
+%group SBObserver
+%hook YTPlayerViewController
+
+- (void)viewDidLoad {
+ %orig;
+ [[NSNotificationCenter defaultCenter] removeObserver:self name:@"SBSegmentsDidLoad" object:self];
+ [[NSNotificationCenter defaultCenter] addObserver:self
+ selector:@selector(sbSegmentsDidLoad:)
+ name:@"SBSegmentsDidLoad"
+ object:self];
+}
+
+- (void)dealloc {
+ [[NSNotificationCenter defaultCenter] removeObserver:self name:@"SBSegmentsDidLoad" object:self];
+ %orig;
+}
+
+%new
+- (void)sbSegmentsDidLoad:(NSNotification *)notification {
+ [self sbRefreshMarkers:notification.userInfo[@"segments"]];
+}
+
+// Re-resolves the current player bar view fresh and re-creates segment markers
+// on it. Called whenever the player layout might have changed (initial load,
+// fullscreen enter/exit, viewport resize) so markers always live on the
+// currently-visible bar instead of an old detached one.
+%new
+- (void)sbRefreshMarkers:(NSArray *)segments {
+ if (!IS_ENABLED(SBSegmentsInPlayer) && !IS_ENABLED(SBSegmentsInMiniPlayer) && !IS_ENABLED(SBSegmentsInFeed)) return;
+ if (!segments) segments = self.sbSegments;
+
+ CGFloat totalTime = [self currentVideoTotalMediaTime];
+ if (totalTime <= 0) return;
+ CGFloat barWidth;
+ CGFloat h;
+ CGFloat y;
+ // Explicitly nil so the surface branches can rely on "unassigned == nil"
+ // when deciding marker ordering, without depending on ARC zero-init.
+ UIView *mainView = nil;
+ UIView *scrubberDot = nil;
+ UIView *referenceView = nil;
+
+ if ([self.parentViewController isKindOfClass:%c(YTWatchFloatingMiniplayerViewController)] && IS_ENABLED(SBSegmentsInMiniPlayer)) {
+ YTWatchFloatingMiniplayerViewController *miniplayercontroller = (YTWatchFloatingMiniplayerViewController *)self.parentViewController;
+ YTWatchFloatingMiniplayerWithPersistentControlsView *controlsview = (YTWatchFloatingMiniplayerWithPersistentControlsView *)miniplayercontroller.view;
+ mainView = controlsview;
+
+ for (UIView *sub in controlsview.subviews) {
+ for (UIView *sub2 in sub.subviews) {
+ if ([sub2 isKindOfClass:%c(YTWatchFloatingMiniplayerProgressBarView)]) {
+ referenceView = sub2;
+ break;
+ }
+ }
+ if (referenceView) break;
+ }
+ // Remove old markers
+ for (UIView *sub in [controlsview.subviews copy]) {
+ if (sub.tag == SBSegmentMarkerTag) [sub removeFromSuperview];
+ }
+ if (!segments || segments.count == 0) return;
+
+ barWidth = referenceView.bounds.size.width;
+ h = referenceView.bounds.size.height;
+ y = referenceView.frame.origin.y;
+ } else if ([[self activeVideoPlayerOverlay] isKindOfClass:%c(YTMainAppVideoPlayerOverlayViewController)] && IS_ENABLED(SBSegmentsInPlayer)) {
+ YTMainAppVideoPlayerOverlayViewController *overlay = [self activeVideoPlayerOverlay];
+ YTPlayerBarController *barController = [overlay playerBarController];
+ YTInlinePlayerBarContainerView *containerView = barController.playerBar;
+ UIView *playerBar;
+
+ for (UIView *subview in containerView.subviews) {
+ if ([subview isKindOfClass:%c(YTModularPlayerBarView)]) {
+ playerBar = subview;
+ mainView = subview;
+ break;
+ }
+ }
+ if (!playerBar) return;
+
+ // Remove old markers
+ for (UIView *sub in [playerBar.subviews copy]) {
+ if (sub.tag == SBSegmentMarkerTag) [sub removeFromSuperview];
+ }
+
+ if (!segments || segments.count == 0) return;
+
+ barWidth = playerBar.bounds.size.width;
+ if (barWidth <= 0) return;
+
+ @try {
+ scrubberDot = [playerBar valueForKey:@"_scrubberCircle"];
+ } @catch (id ex) {}
+ // Find reference track view for Y position and height
+ for (UIView *sub in playerBar.subviews) {
+ if ([sub isKindOfClass:%c(YTPlayerBarRectangleDecorationView)]) {
+ if (!referenceView) referenceView = sub;
+ } else if ([sub isKindOfClass:%c(YTPlayerBarProgressDecorationView)]) {
+ if (!referenceView) referenceView = sub;
+ } else if ([sub isKindOfClass:%c(YTPlayerBarScrubberDotDecorationView)]) {
+ if (!scrubberDot) scrubberDot = sub.subviews.firstObject;
+ }
+ if (referenceView && scrubberDot) break;
+ }
+ h = referenceView.bounds.size.height;
+ y = referenceView.frame.origin.y;
+ } else if ([[self activeVideoPlayerOverlay] isKindOfClass:%c(YTInlineMutedPlaybackPlayerOverlayViewController)] && IS_ENABLED(SBSegmentsInFeed)) {
+ YTInlineMutedPlaybackPlayerOverlayViewController *viewcon = [self activeVideoPlayerOverlay];
+ YTInlineMutedPlaybackPlayerOverlayView *view = (YTInlineMutedPlaybackPlayerOverlayView *)viewcon.view;
+ UIView *scrub;
+ UIView *playerBar;
+ for (UIView *sub in view.subviews) {
+ if ([sub isKindOfClass:%c(YTInlineMutedPlaybackScrubberView)]) {
+ scrub = sub;
+ mainView = sub;
+ break;
+ }
+ }
+
+ if (!segments || segments.count == 0) return;
+
+ for (UIView *sub in scrub.subviews) {
+ if ([sub isKindOfClass:%c(YTPlayerBarMarkerView)] && sub.frame.origin.y != 0) {
+ playerBar = sub;
+ } else if ([sub isKindOfClass:%c(YTModularPlayerBarView)] && sub.frame.origin.y != 0) {
+ playerBar = sub;
+ mainView = sub;
+ } else if ([sub isKindOfClass:%c(YTInlineMutedPlaybackScrubbingSlider)]) {
+ if ([sub.accessibilityIdentifier isEqualToString:@"id.player.scrubber.slider"]) {
+ scrubberDot = sub;
+ }
+ }
+ if (playerBar && scrubberDot) break;
+ }
+
+ if (!playerBar) return;
+
+ // Remove old markers
+ for (UIView *sub in [mainView.subviews copy]) {
+ if (sub.tag == SBSegmentMarkerTag) [sub removeFromSuperview];
+ }
+
+ if ([mainView isKindOfClass:%c(YTModularPlayerBarView)]) {
+ @try {
+ scrubberDot = [mainView valueForKey:@"_scrubberCircle"];
+ } @catch (id ex) {}
+ // Find reference track view for Y position and height
+ for (UIView *sub in mainView.subviews) {
+ if ([sub isKindOfClass:%c(YTPlayerBarRectangleDecorationView)]) {
+ if (!referenceView) referenceView = sub;
+ } else if ([sub isKindOfClass:%c(YTPlayerBarProgressDecorationView)]) {
+ if (!referenceView) referenceView = sub;
+ } else if ([sub isKindOfClass:%c(YTPlayerBarScrubberDotDecorationView)]) {
+ if (!scrubberDot) scrubberDot = sub.subviews.firstObject;
+ }
+ if (referenceView && scrubberDot) break;
+ }
+ barWidth = playerBar.bounds.size.width;
+ h = referenceView.bounds.size.height;
+ y = referenceView.frame.origin.y;
+ } else {
+ barWidth = playerBar.bounds.size.width;
+ h = playerBar.bounds.size.height;
+ y = playerBar.frame.origin.y;
+ }
+ } else {
+ return;
+ }
+
+ if (!self.sbEnabledForVideo) return;
+
+ for (SBSegment *segment in segments) {
+ SBSegmentAction action = [segment configuredAction];
+ if (action == SBSegmentActionDisable) continue;
+
+ CGFloat startFrac = segment.startTime / totalTime;
+ CGFloat endFrac;
+ if (segment.endTime > totalTime) {
+ endFrac = 1.0;
+ } else {
+ endFrac = segment.endTime / totalTime;
+ }
+ CGFloat x = startFrac * barWidth;
+ CGFloat w = (endFrac - startFrac) * barWidth;
+
+ // poi_highlight is a point, not a range — give it fixed width
+ BOOL isPoi = [segment.category isEqualToString:@"poi_highlight"];
+ if (isPoi) {
+ w = SBPoiMarkerWidth;
+ x = MAX(0, x - SBPoiMarkerXOffset);
+ } else {
+ if (w < SBMarkerMinWidth) w = SBMarkerMinWidth;
+ }
+
+ UIView *marker = [[UIView alloc] initWithFrame:CGRectMake(x, y, w, h)];
+ marker.backgroundColor = [segment segmentColor];
+ marker.userInteractionEnabled = NO;
+ marker.tag = SBSegmentMarkerTag;
+ objc_setAssociatedObject(marker, @selector(sbSegmentData), @[@(startFrac), @(endFrac), @(isPoi)], OBJC_ASSOCIATION_RETAIN_NONATOMIC);
+
+ // Insert above the track (main player bar) so the marker paints on it;
+ // the dot is re-fronted after the loop. Miniplayer/feed keep dot-relative
+ // or top ordering.
+ if (referenceView && referenceView.superview == mainView) {
+ [mainView insertSubview:marker aboveSubview:referenceView];
+ } else if (scrubberDot && scrubberDot.superview == mainView) {
+ [mainView insertSubview:marker belowSubview:scrubberDot];
+ } else {
+ [mainView addSubview:marker];
+ [mainView bringSubviewToFront:marker];
+ }
+ }
+ if (scrubberDot) {
+ [mainView bringSubviewToFront:scrubberDot];
+ }
+}
+
+// On fullscreen enter/exit and other layout transitions, YouTube swaps the
+// player bar instance. Re-render markers on the current bar (matches
+// iSponsorBlock's approach). Deferred to the next runloop so YouTube's own
+// layout pass finishes first — otherwise the new bar's bounds.size.width can
+// still be 0 and the refresh early-returns without inserting markers.
+- (void)setPlayerViewLayout:(NSInteger)layout {
+ %orig;
+ __weak typeof(self) weakSelf = self;
+ dispatch_async(dispatch_get_main_queue(), ^{
+ [weakSelf sbRefreshMarkers:nil];
+ sbUpdateOverlayInsetForPivotBar();
+ });
+}
+
+- (void)updateViewportSizeProvider {
+ %orig;
+ __weak typeof(self) weakSelf = self;
+ dispatch_async(dispatch_get_main_queue(), ^{
+ [weakSelf sbRefreshMarkers:nil];
+ sbUpdateOverlayInsetForPivotBar();
+ });
+}
+
+%end
+%end
+
+#pragma mark - Constructor
+
+%ctor {
+ %init;
+ %init(SBObserver);
+}
\ No newline at end of file
diff --git a/Files/Tabbar.x b/Files/Tabbar.x
index c12a9b49..47bafe97 100644
--- a/Files/Tabbar.x
+++ b/Files/Tabbar.x
@@ -1,40 +1,172 @@
#import "Headers.h"
+#import
+
+#define TweakName @"YouMod"
+
+static NSBundle *YouModBundle() {
+ static NSBundle *bundle = nil;
+ static dispatch_once_t onceToken;
+ dispatch_once(&onceToken, ^{
+ NSString *tweakBundlePath = [[NSBundle mainBundle] pathForResource:TweakName ofType:@"bundle"];
+ if (tweakBundlePath)
+ bundle = [NSBundle bundleWithPath:tweakBundlePath];
+ else
+ bundle = [NSBundle bundleWithPath:[NSString stringWithFormat:PS_ROOT_PATH_NS(@"/Library/Application Support/%@.bundle"), TweakName]];
+ });
+ return bundle;
+}
+
+#define LOC(x) [YouModBundle() localizedStringForKey:x value:nil table:nil]
+
+// Tab icons
+%hook YTAppPivotBarItemStyle
+- (UIImage *)pivotBarItemIconImageWithIconType:(int)type color:(UIColor *)color useNewIcons:(BOOL)isNew selected:(BOOL)isSelected {
+ if (type == 1 || type == 2 || type == 3 || type == 4 || type == 5 || type == 6 || type == 7 || type == 8 || type == 9 || type == 10 || type == 11 || type == 12 || type == 13 || type == 14 || type == 15) {
+ NSString *imageName;
+ if (type == 1) imageName = isSelected ? @"icons/history_selected" : @"icons/history";
+ else if (type == 2) imageName = isSelected ? @"icons/gaming_selected" : @"icons/gaming";
+ else if (type == 3) imageName = isSelected ? @"icons/sports_selected" : @"icons/sports";
+ else if (type == 4) imageName = isSelected ? @"icons/noti_selected" : @"icons/noti";
+ else if (type == 5) imageName = isSelected ? @"icons/news_selected" : @"icons/news";
+ else if (type == 6) imageName = isSelected ? @"icons/music_selected" : @"icons/music";
+ else if (type == 7) imageName = isSelected ? @"icons/watchlater_selected" : @"icons/watchlater";
+ else if (type == 8) imageName = isSelected ? @"icons/playlist_selected" : @"icons/playlist";
+ else if (type == 9) imageName = isSelected ? @"icons/like_selected" : @"icons/like";
+ else if (type == 10) imageName = isSelected ? @"icons/live_selected" : @"icons/live";
+ else if (type == 11) imageName = isSelected ? @"icons/post_selected" : @"icons/post";
+ else if (type == 12) imageName = isSelected ? @"icons/video_selected" : @"icons/video";
+ else if (type == 13) imageName = isSelected ? @"icons/movie_selected" : @"icons/movie";
+ else if (type == 14) imageName = isSelected ? @"icons/course_selected" : @"icons/course";
+ else if (type == 15) imageName = isSelected ? @"icons/minigame_selected" : @"icons/minigame";
+ YTAssetLoader *al = [[%c(YTAssetLoader) alloc] initWithBundle:YouModBundle()];
+ return [al imageNamed:imageName];
+ }
+ return %orig;
+}
+%end
+
+static NSString *ymPivotIDForTabID(NSString *tabID) {
+ if ([tabID isEqualToString:@"home"]) return @"FEwhat_to_watch";
+ if ([tabID isEqualToString:@"shorts"]) return @"FEshorts";
+ if ([tabID isEqualToString:@"create"]) return @"FEuploads";
+ if ([tabID isEqualToString:@"subscriptions"]) return @"FEsubscriptions";
+ if ([tabID isEqualToString:@"library"]) return @"FElibrary";
+ if ([tabID isEqualToString:@"history"]) return [%c(YTIBrowseRequest) browseIDForHistory];
+ if ([tabID isEqualToString:@"gaming"]) return [%c(YTIBrowseRequest) browseIDForGamingDestination];
+ if ([tabID isEqualToString:@"sports"]) return [%c(YTIBrowseRequest) browseIDForSportsDestination];
+ if ([tabID isEqualToString:@"notifications"]) return [%c(YTIBrowseRequest) browseIDForNotificationsInbox];
+ if ([tabID isEqualToString:@"news"]) return @"UCYfdidRxbB8Qhf0Nx7ioOYw"; // FEnews_destination
+ if ([tabID isEqualToString:@"music"]) return @"UC-9-kyTW8ZkZNDHQJ6FgpwQ";
+ if ([tabID isEqualToString:@"watchlater"]) return @"VLWL";
+ if ([tabID isEqualToString:@"playlist"]) return @"FEplaylist_aggregation";
+ if ([tabID isEqualToString:@"like"]) return @"VLLL";
+ if ([tabID isEqualToString:@"live"]) return @"UC4R8DWoMoI7CAwX8_LjQHig";
+ if ([tabID isEqualToString:@"post"]) return @"FEpost_home";
+ if ([tabID isEqualToString:@"video"]) return @"UC3qapbGAd2-S75NkBY3XWww";
+ if ([tabID isEqualToString:@"movie"]) return @"FEstorefront";
+ if ([tabID isEqualToString:@"course"]) return @"FEcourses";
+ if ([tabID isEqualToString:@"minigame"]) return @"FEmini_app_destination";
+ return nil;
+}
+
+static NSInteger ymIconTypeForTabID(NSString *tabID) {
+ if ([tabID isEqualToString:@"history"]) return 1;
+ if ([tabID isEqualToString:@"gaming"]) return 2;
+ if ([tabID isEqualToString:@"sports"]) return 3;
+ if ([tabID isEqualToString:@"notifications"]) return 4;
+ if ([tabID isEqualToString:@"news"]) return 5;
+ if ([tabID isEqualToString:@"music"]) return 6;
+ if ([tabID isEqualToString:@"watchlater"]) return 7;
+ if ([tabID isEqualToString:@"playlist"]) return 8;
+ if ([tabID isEqualToString:@"like"]) return 9;
+ if ([tabID isEqualToString:@"live"]) return 10;
+ if ([tabID isEqualToString:@"post"]) return 11;
+ if ([tabID isEqualToString:@"video"]) return 12;
+ if ([tabID isEqualToString:@"movie"]) return 13;
+ if ([tabID isEqualToString:@"course"]) return 14;
+ if ([tabID isEqualToString:@"minigame"]) return 15;
+ return 0;
+}
+
+static NSString *ymTitleForTabID(NSString *tabID) {
+ if ([tabID isEqualToString:@"history"]) return LOC(@"HISTORY_TAB");
+ if ([tabID isEqualToString:@"gaming"]) return LOC(@"GAMING_TAB");
+ if ([tabID isEqualToString:@"sports"]) return LOC(@"SPORTS_TAB");
+ if ([tabID isEqualToString:@"notifications"]) return LOC(@"NOTI_TAB");
+ if ([tabID isEqualToString:@"news"]) return LOC(@"NEWS_TAB");
+ if ([tabID isEqualToString:@"music"]) return LOC(@"MUSIC_TAB");
+ if ([tabID isEqualToString:@"watchlater"]) return LOC(@"WATCH_LATER_TAB");
+ if ([tabID isEqualToString:@"playlist"]) return LOC(@"PLAYLIST_TAB");
+ if ([tabID isEqualToString:@"like"]) return LOC(@"LIKE_TAB");
+ if ([tabID isEqualToString:@"live"]) return LOC(@"LIVE_TAB");
+ if ([tabID isEqualToString:@"post"]) return LOC(@"POST_TAB");
+ if ([tabID isEqualToString:@"video"]) return LOC(@"VIDEO_TAB");
+ if ([tabID isEqualToString:@"movie"]) return LOC(@"MOVIE_TAB");
+ if ([tabID isEqualToString:@"course"]) return LOC(@"COURSE_TAB");
+ if ([tabID isEqualToString:@"minigame"]) return LOC(@"MINIGAME_TAB");
+ return nil;
+}
%hook YTPivotBarView
- (void)setRenderer:(YTIPivotBarRenderer *)renderer {
- NSMutableArray *items = [renderer itemsArray];
- NSMutableIndexSet *indicesToRemove = [NSMutableIndexSet indexSet];
- // Loop through every item in the bar
- for (NSUInteger i = 0; i < items.count; i++) {
- YTIPivotBarSupportedRenderers *item = items[i];
- NSString *pID = [[item pivotBarItemRenderer] pivotIdentifier];
- NSString *pID2 = [[item pivotBarIconOnlyItemRenderer] pivotIdentifier];
- if ([pID isEqualToString:@"FEwhat_to_watch"] && IS_ENABLED(HideHomeTab)) {
- [indicesToRemove addIndex:i];
- }
- if ([pID isEqualToString:@"FEshorts"] && IS_ENABLED(HideShortsTab)) {
- [indicesToRemove addIndex:i];
- }
- if ([pID2 isEqualToString:@"FEuploads"] && IS_ENABLED(HideCreateButton)) {
- [indicesToRemove addIndex:i];
+ NSArray *savedOrder = [[NSUserDefaults standardUserDefaults] arrayForKey:TabOrder];
+ if (savedOrder.count > 0) {
+ NSMutableArray *items = [renderer itemsArray];
+
+ // Build lookup: pivotIdentifier -> renderer item
+ NSMutableDictionary *lookup = [NSMutableDictionary dictionary];
+ for (YTIPivotBarSupportedRenderers *item in items) {
+ NSString *pID = [[item pivotBarItemRenderer] pivotIdentifier];
+ NSString *pID2 = [[item pivotBarIconOnlyItemRenderer] pivotIdentifier];
+ if (pID) lookup[pID] = item;
+ if (pID2) lookup[pID2] = item;
}
- if ([pID isEqualToString:@"FEsubscriptions"] && IS_ENABLED(HideSubscriptTab)) {
- [indicesToRemove addIndex:i];
+
+ // Build ordered array from saved data
+ NSMutableArray *ordered = [NSMutableArray array];
+ for (NSDictionary *entry in savedOrder) {
+ NSString *tabID = entry[@"id"];
+ BOOL enabled = [entry[@"enabled"] boolValue];
+ if (!enabled) continue;
+
+ NSString *pivotID = ymPivotIDForTabID(tabID);
+ if (!pivotID) continue;
+
+ YTIPivotBarSupportedRenderers *existing = lookup[pivotID];
+ if (existing) {
+ [ordered addObject:existing];
+ } else {
+ // Custom tab not in YouTube's default items — create it
+ NSInteger iconType = ymIconTypeForTabID(tabID);
+ NSString *title = ymTitleForTabID(tabID);
+ if (iconType > 0 && title) {
+ YTIPivotBarSupportedRenderers *newTab = [%c(YTIPivotBarRenderer) pivotSupportedRenderersWithBrowseId:pivotID title:title iconType:iconType];
+ if (newTab) [ordered addObject:newTab];
+ }
+ }
}
+ // Replace items with ordered set
+ [items removeAllObjects];
+ [items addObjectsFromArray:ordered];
}
- // Remove them all at once so the layout doesn't break
- [items removeObjectsAtIndexes:indicesToRemove];
%orig(renderer);
}
%end
// Hide Tab Bar Indicators
%hook YTPivotBarIndicatorView
-- (void)setFillColor:(id)arg1 { IS_ENABLED(HideTabIndi) ? %orig([UIColor clearColor]) : %orig; }
-- (void)setBorderColor:(id)arg1 { IS_ENABLED(HideTabIndi) ? %orig([UIColor clearColor]) : %orig; }
+- (void)setFillColor:(UIColor *)arg1 {
+ UIColor *temp = IS_ENABLED(HideTabIndi) ? [UIColor clearColor] : arg1;
+ %orig(temp);
+}
+- (void)setBorderColor:(UIColor *)arg1 {
+ UIColor *temp = IS_ENABLED(HideTabIndi) ? [UIColor clearColor] : arg1;
+ %orig(temp);
+}
%end
-// Hide Tab Labels
+static BOOL isGestureRegistered = NO;
+// Hide Tab Labels + long-press on the first tab to open Manage Tabs
%hook YTPivotBarItemView
- (void)setRenderer:(YTIPivotBarRenderer *)renderer {
%orig;
@@ -42,18 +174,95 @@
[self.navigationButton setTitle:@"" forState:UIControlStateNormal];
[self.navigationButton setSizeWithPaddingAndInsets:NO];
}
+ // Attach long-press gesture once per view; the action handler checks the
+ // current pivotIdentifier at fire time, so cell reuse / pivot bar refresh
+ // can rebind the same view to a different tab safely.
+ static const void *kYMLongPressKey = &kYMLongPressKey;
+ if (!objc_getAssociatedObject(self, kYMLongPressKey) && !isGestureRegistered) {
+ UILongPressGestureRecognizer *longPress = [[UILongPressGestureRecognizer alloc]
+ initWithTarget:self action:@selector(ymOpenManageTabs:)];
+ longPress.minimumPressDuration = 0.4;
+ [self addGestureRecognizer:longPress];
+ objc_setAssociatedObject(self, kYMLongPressKey, longPress, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
+ isGestureRegistered = YES;
+ }
+}
+
+%new
+- (void)ymOpenManageTabs:(UILongPressGestureRecognizer *)gesture {
+ if (gesture.state != UIGestureRecognizerStateBegan) return;
+ YMPresentTabOrderModally(nil);
}
%end
// Startup Tab
-BOOL isTabSelected = NO;
+static BOOL isTabSelected = NO;
%hook YTPivotBarViewController
- (void)viewDidAppear:(BOOL)animated {
%orig;
+ sbUpdateOverlayInsetForPivotBar();
+ if (IS_ENABLED(ShortsOnly) && !isTabSelected) {
+ [self selectItemWithPivotIdentifier:@"FEshorts"];
+ isTabSelected = YES;
+ return;
+ }
if (!isTabSelected) {
- NSArray *pivotIdentifiers = @[@"FEwhat_to_watch", @"FEshorts", @"FEsubscriptions", @"FElibrary"];
- [self selectItemWithPivotIdentifier:pivotIdentifiers[INTFORVAL(DefaultTab)]]; // Set int here
+ // Build pivot identifiers from enabled tabs (skip Create — matches Settings.x segment logic)
+ NSMutableArray *pivotIdentifiers = [NSMutableArray array];
+ NSArray *savedOrder = [[NSUserDefaults standardUserDefaults] arrayForKey:TabOrder];
+ if (savedOrder.count > 0) {
+ for (NSDictionary *entry in savedOrder) {
+ if (![entry[@"enabled"] boolValue]) continue;
+ NSString *tabID = entry[@"id"];
+ if ([tabID isEqualToString:@"create"]) continue;
+ NSString *pivot = ymPivotIDForTabID(tabID);
+ if (pivot) [pivotIdentifiers addObject:pivot];
+ }
+ }
+ if (pivotIdentifiers.count == 0) {
+ pivotIdentifiers = [@[@"FEwhat_to_watch", @"FEshorts", @"FEsubscriptions", @"FElibrary"] mutableCopy];
+ }
+
+ NSInteger tabIndex = INTFORVAL(DefaultTab);
+ if (tabIndex < 0) tabIndex = 0;
+ if (tabIndex >= (NSInteger)pivotIdentifiers.count) tabIndex = MAX(0, (NSInteger)pivotIdentifiers.count - 1);
+ [self selectItemWithPivotIdentifier:pivotIdentifiers[tabIndex]];
isTabSelected = YES;
}
}
-%end
\ No newline at end of file
+// Translucent tab bar
+- (BOOL)isFrostedPivotBarPermitted {
+ if (INTFORVAL(UseFrostedTabBar) == 1) {
+ return YES;
+ } else if (INTFORVAL(UseFrostedTabBar) == 2) {
+ return NO;
+ }
+ return %orig;
+}
+%end
+
+// Recompute SB overlay safe-area inset whenever YouTube shows or hides the pivot bar
+// (e.g. entering/exiting fullscreen player). This keeps the SponsorBlock skip pill
+// and download progress pill anchored above the tabbar when visible, and at the
+// device safe-area bottom when the tabbar is hidden.
+%hook YTAppViewController
+- (void)hidePivotBar {
+ %orig;
+ sbUpdateOverlayInsetForPivotBar();
+}
+- (void)showPivotBar {
+ %orig;
+ sbUpdateOverlayInsetForPivotBar();
+}
+%end
+
+%hook YTAppViewControllerImpl
+- (void)hidePivotBar {
+ %orig;
+ sbUpdateOverlayInsetForPivotBar();
+}
+- (void)showPivotBar {
+ %orig;
+ sbUpdateOverlayInsetForPivotBar();
+}
+%end
diff --git a/Files/ThumbnailPreview.x b/Files/ThumbnailPreview.x
new file mode 100644
index 00000000..b2831636
--- /dev/null
+++ b/Files/ThumbnailPreview.x
@@ -0,0 +1,217 @@
+#import
+#import "Headers.h"
+
+static BOOL isPad() {
+ return UIDevice.currentDevice.userInterfaceIdiom == UIUserInterfaceIdiomPad;
+}
+
+@implementation YouModThumbnailViewController
+
+- (void)viewDidLoad {
+ [super viewDidLoad];
+
+ UIView *container = [[UIView alloc] init];
+ container.translatesAutoresizingMaskIntoConstraints = NO;
+ container.clipsToBounds = YES;
+ container.backgroundColor = [UIColor clearColor];
+ [self.view addSubview:container];
+
+ UIImageView *bgImageView = [[UIImageView alloc] initWithImage:self.thumbnailImage];
+ bgImageView.contentMode = UIViewContentModeScaleAspectFill;
+ bgImageView.translatesAutoresizingMaskIntoConstraints = NO;
+ [container addSubview:bgImageView];
+
+ UIBlurEffect *blurEffect = [UIBlurEffect effectWithStyle:UIBlurEffectStyleSystemMaterial];
+ UIVisualEffectView *blurView = [[UIVisualEffectView alloc] initWithEffect:blurEffect];
+ blurView.translatesAutoresizingMaskIntoConstraints = NO;
+ [container addSubview:blurView];
+
+ self.scrollView = [[UIScrollView alloc] init];
+ self.scrollView.delegate = self;
+ self.scrollView.minimumZoomScale = 1.0;
+ self.scrollView.maximumZoomScale = 4.0;
+ self.scrollView.showsVerticalScrollIndicator = NO;
+ self.scrollView.showsHorizontalScrollIndicator = NO;
+ self.scrollView.translatesAutoresizingMaskIntoConstraints = NO;
+ [container addSubview:self.scrollView];
+
+ self.imageView = [[UIImageView alloc] initWithImage:self.thumbnailImage];
+ self.imageView.contentMode = UIViewContentModeScaleAspectFit;
+ self.imageView.translatesAutoresizingMaskIntoConstraints = NO;
+ [self.scrollView addSubview:self.imageView];
+
+ UITapGestureRecognizer *doubleTap = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(handleDoubleTap:)];
+ doubleTap.numberOfTapsRequired = 2;
+ [self.scrollView addGestureRecognizer:doubleTap];
+
+ UIImageSymbolConfiguration *iconConfig = [UIImageSymbolConfiguration configurationWithPointSize:22 weight:UIImageSymbolWeightSemibold];
+ UIButton *closeBtn = [UIButton buttonWithType:UIButtonTypeCustom];
+ [closeBtn setImage:[UIImage systemImageNamed:@"xmark" withConfiguration:iconConfig] forState:UIControlStateNormal];
+ closeBtn.tintColor = [UIColor whiteColor];
+ closeBtn.backgroundColor = [UIColor colorWithWhite:0.0 alpha:0.4];
+ closeBtn.layer.cornerRadius = 25;
+ closeBtn.translatesAutoresizingMaskIntoConstraints = NO;
+ [closeBtn addTarget:self action:@selector(closeTapped) forControlEvents:UIControlEventTouchUpInside];
+ [container addSubview:closeBtn];
+
+ UIButton *moreBtn = [UIButton buttonWithType:UIButtonTypeCustom];
+ [moreBtn setImage:[UIImage systemImageNamed:@"ellipsis" withConfiguration:iconConfig] forState:UIControlStateNormal];
+ moreBtn.tintColor = [UIColor whiteColor];
+ moreBtn.backgroundColor = [UIColor colorWithWhite:0.0 alpha:0.4];
+ moreBtn.layer.cornerRadius = 25;
+ moreBtn.translatesAutoresizingMaskIntoConstraints = NO;
+ [moreBtn addTarget:self action:@selector(moreTapped:) forControlEvents:UIControlEventTouchUpInside];
+ [container addSubview:moreBtn];
+
+ NSMutableArray *constraints = [NSMutableArray array];
+
+ if (isPad()) {
+ self.view.backgroundColor = [UIColor colorWithWhite:0.0 alpha:0.5];
+ container.layer.cornerRadius = 16;
+
+ [constraints addObject:[container.centerXAnchor constraintEqualToAnchor:self.view.centerXAnchor]];
+ [constraints addObject:[container.centerYAnchor constraintEqualToAnchor:self.view.centerYAnchor]];
+ [constraints addObject:[container.widthAnchor constraintEqualToAnchor:container.heightAnchor]];
+ [constraints addObject:[container.widthAnchor constraintLessThanOrEqualToAnchor:self.view.safeAreaLayoutGuide.widthAnchor multiplier:0.9]];
+ [constraints addObject:[container.heightAnchor constraintLessThanOrEqualToAnchor:self.view.safeAreaLayoutGuide.heightAnchor multiplier:0.9]];
+
+ NSLayoutConstraint *expandBox = [container.widthAnchor constraintEqualToConstant:2000];
+ expandBox.priority = UILayoutPriorityDefaultHigh;
+ [constraints addObject:expandBox];
+
+ [constraints addObjectsFromArray:@[
+ [closeBtn.topAnchor constraintEqualToAnchor:container.topAnchor constant:12],
+ [closeBtn.leadingAnchor constraintEqualToAnchor:container.leadingAnchor constant:12],
+ [moreBtn.topAnchor constraintEqualToAnchor:container.topAnchor constant:12],
+ [moreBtn.trailingAnchor constraintEqualToAnchor:container.trailingAnchor constant:-12],
+ ]];
+ } else {
+ self.view.backgroundColor = [UIColor clearColor];
+ container.layer.cornerRadius = 0;
+
+ [constraints addObjectsFromArray:@[
+ [container.topAnchor constraintEqualToAnchor:self.view.topAnchor],
+ [container.bottomAnchor constraintEqualToAnchor:self.view.bottomAnchor],
+ [container.leadingAnchor constraintEqualToAnchor:self.view.leadingAnchor],
+ [container.trailingAnchor constraintEqualToAnchor:self.view.trailingAnchor]
+ ]];
+
+ [constraints addObjectsFromArray:@[
+ [closeBtn.topAnchor constraintEqualToAnchor:self.view.safeAreaLayoutGuide.topAnchor constant:12],
+ [closeBtn.leadingAnchor constraintEqualToAnchor:self.view.safeAreaLayoutGuide.leadingAnchor constant:12],
+ [moreBtn.topAnchor constraintEqualToAnchor:self.view.safeAreaLayoutGuide.topAnchor constant:12],
+ [moreBtn.trailingAnchor constraintEqualToAnchor:self.view.safeAreaLayoutGuide.trailingAnchor constant:-12],
+ ]];
+ }
+
+ [constraints addObjectsFromArray:@[
+ [bgImageView.topAnchor constraintEqualToAnchor:container.topAnchor],
+ [bgImageView.bottomAnchor constraintEqualToAnchor:container.bottomAnchor],
+ [bgImageView.leadingAnchor constraintEqualToAnchor:container.leadingAnchor],
+ [bgImageView.trailingAnchor constraintEqualToAnchor:container.trailingAnchor],
+ [blurView.topAnchor constraintEqualToAnchor:container.topAnchor],
+ [blurView.bottomAnchor constraintEqualToAnchor:container.bottomAnchor],
+ [blurView.leadingAnchor constraintEqualToAnchor:container.leadingAnchor],
+ [blurView.trailingAnchor constraintEqualToAnchor:container.trailingAnchor]
+ ]];
+
+ [constraints addObjectsFromArray:@[
+ [self.scrollView.centerXAnchor constraintEqualToAnchor:container.centerXAnchor],
+ [self.scrollView.centerYAnchor constraintEqualToAnchor:container.centerYAnchor],
+ [self.scrollView.widthAnchor constraintEqualToAnchor:self.scrollView.heightAnchor multiplier:16.0/9.0],
+ [self.scrollView.widthAnchor constraintLessThanOrEqualToAnchor:container.widthAnchor],
+ [self.scrollView.heightAnchor constraintLessThanOrEqualToAnchor:container.heightAnchor]
+ ]];
+
+ NSLayoutConstraint *expandSV = [self.scrollView.widthAnchor constraintEqualToConstant:2000];
+ expandSV.priority = UILayoutPriorityDefaultHigh;
+ [constraints addObject:expandSV];
+
+ [constraints addObjectsFromArray:@[
+ [self.imageView.topAnchor constraintEqualToAnchor:self.scrollView.contentLayoutGuide.topAnchor],
+ [self.imageView.bottomAnchor constraintEqualToAnchor:self.scrollView.contentLayoutGuide.bottomAnchor],
+ [self.imageView.leadingAnchor constraintEqualToAnchor:self.scrollView.contentLayoutGuide.leadingAnchor],
+ [self.imageView.trailingAnchor constraintEqualToAnchor:self.scrollView.contentLayoutGuide.trailingAnchor],
+ [self.imageView.widthAnchor constraintEqualToAnchor:self.scrollView.frameLayoutGuide.widthAnchor],
+ [self.imageView.heightAnchor constraintEqualToAnchor:self.scrollView.frameLayoutGuide.heightAnchor]
+ ]];
+
+ [constraints addObjectsFromArray:@[
+ [closeBtn.widthAnchor constraintEqualToConstant:50],
+ [closeBtn.heightAnchor constraintEqualToConstant:50],
+ [moreBtn.widthAnchor constraintEqualToConstant:50],
+ [moreBtn.heightAnchor constraintEqualToConstant:50],
+ ]];
+
+ [NSLayoutConstraint activateConstraints:constraints];
+
+ UIPanGestureRecognizer *panGesture = [[UIPanGestureRecognizer alloc] initWithTarget:self action:@selector(handlePanGesture:)];
+ panGesture.delegate = self;
+ [self.view addGestureRecognizer:panGesture];
+}
+
+- (UIView *)viewForZoomingInScrollView:(UIScrollView *)scrollView {
+ return self.imageView;
+}
+
+- (void)handleDoubleTap:(UITapGestureRecognizer *)gesture {
+ if (self.scrollView.zoomScale > self.scrollView.minimumZoomScale) {
+ [self.scrollView setZoomScale:self.scrollView.minimumZoomScale animated:YES];
+ } else {
+ CGFloat targetZoomScale = 3.0;
+ CGPoint touchPoint = [gesture locationInView:self.imageView];
+ CGFloat zoomWidth = self.scrollView.bounds.size.width / targetZoomScale;
+ CGFloat zoomHeight = self.scrollView.bounds.size.height / targetZoomScale;
+ CGFloat zoomX = touchPoint.x - (zoomWidth / 2.0);
+ CGFloat zoomY = touchPoint.y - (zoomHeight / 2.0);
+ [self.scrollView zoomToRect:CGRectMake(zoomX, zoomY, zoomWidth, zoomHeight) animated:YES];
+ }
+}
+
+- (BOOL)gestureRecognizerShouldBegin:(UIGestureRecognizer *)gestureRecognizer {
+ if ([gestureRecognizer isKindOfClass:[UIPanGestureRecognizer class]]) {
+ if (self.scrollView.zoomScale > 1.0) return NO;
+ }
+ return YES;
+}
+
+- (void)handlePanGesture:(UIPanGestureRecognizer *)gesture {
+ CGPoint translation = [gesture translationInView:self.view];
+ CGPoint velocity = [gesture velocityInView:self.view];
+
+ if (gesture.state == UIGestureRecognizerStateChanged) {
+ if (translation.y > 0) {
+ self.view.transform = CGAffineTransformMakeTranslation(0, translation.y);
+ if (isPad()) {
+ CGFloat alpha = MAX(0.0, 0.5 - (translation.y / self.view.bounds.size.height));
+ self.view.backgroundColor = [UIColor colorWithWhite:0.0 alpha:alpha];
+ }
+ } else {
+ self.view.transform = CGAffineTransformMakeTranslation(0, translation.y * 0.1);
+ }
+ } else if (gesture.state == UIGestureRecognizerStateEnded || gesture.state == UIGestureRecognizerStateCancelled) {
+ if (translation.y > 150 || velocity.y > 1000) {
+ [self dismissViewControllerAnimated:YES completion:nil];
+ } else {
+ [UIView animateWithDuration:0.3 delay:0 usingSpringWithDamping:0.8 initialSpringVelocity:velocity.y / 1000.0 options:UIViewAnimationOptionCurveEaseOut animations:^{
+ self.view.transform = CGAffineTransformIdentity;
+ if (isPad()) self.view.backgroundColor = [UIColor colorWithWhite:0.0 alpha:0.5];
+ } completion:nil];
+ }
+ }
+}
+
+- (void)closeTapped {
+ [self dismissViewControllerAnimated:YES completion:nil];
+}
+
+- (void)moreTapped:(UIButton *)sender {
+ UIActivityViewController *activityVC = [[UIActivityViewController alloc] initWithActivityItems:@[self.thumbnailImage] applicationActivities:nil];
+ if (isPad()) {
+ activityVC.popoverPresentationController.sourceView = sender;
+ activityVC.popoverPresentationController.sourceRect = sender.bounds;
+ }
+ [self presentViewController:activityVC animated:YES completion:nil];
+}
+
+@end
\ No newline at end of file
diff --git a/Files/YouModPerferences.x b/Files/YouModPerferences.x
index 361ce66c..6b7b138d 100644
--- a/Files/YouModPerferences.x
+++ b/Files/YouModPerferences.x
@@ -57,6 +57,7 @@ static NSBundle *YouModBundle() {
YTAlertView *alertView = [%c(YTAlertView) infoDialog];
alertView.title = LOC(@"ERROR");
alertView.subtitle = LOC(@"ERROR_INVALID_FILE");
+ alertView.shouldDismissOnBackgroundTap = YES;
[alertView show];
return;
}
@@ -81,16 +82,21 @@ static NSBundle *YouModBundle() {
YTAlertView *alertView = [%c(YTAlertView) infoDialog];
alertView.title = LOC(@"ERROR");
alertView.subtitle = LOC(@"ERROR_NO_KEYS_IMPORT");
+ alertView.shouldDismissOnBackgroundTap = YES;
[alertView show];
return;
}
[defaults synchronize];
// Success Alert with Restart
YTAlertView *alertView = [%c(YTAlertView) confirmationDialogWithAction:^{
- exit(0);
+ [[UIApplication sharedApplication] performSelector:@selector(suspend)];
+ dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0.3 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
+ exit(0);
+ });
} actionTitle:LOC(@"YES")];
alertView.title = LOC(@"IMPORT_DONE");
alertView.subtitle = LOC(@"APPLY_DESC"); // "Restart required"
+ alertView.shouldDismissOnBackgroundTap = YES;
[alertView show];
}
@@ -107,6 +113,7 @@ static NSBundle *YouModBundle() {
YTAlertView *alertView = [%c(YTAlertView) infoDialog];
alertView.title = LOC(@"ERROR");
alertView.subtitle = LOC(@"ERROR_NO_KEYS_EXPORT");
+ alertView.shouldDismissOnBackgroundTap = YES;
[alertView show];
return;
}
@@ -134,18 +141,23 @@ static NSBundle *YouModBundle() {
}
[defaults setBool:YES forKey:AutoClearCache];
[defaults setBool:YES forKey:YTPremiumLogo];
- [defaults setBool:YES forKey:HideCreateButton];
[defaults setBool:YES forKey:HideCastButtonNav];
[defaults setBool:YES forKey:HideCastButtonPlayer];
[defaults setBool:YES forKey:BackgroundPlayback];
- [defaults setBool:YES forKey:OldQualityPicker];
- [defaults setBool:YES forKey:DownloadManager];
- [defaults setBool:YES forKey:DownloadSaveToPhotos];
+ [defaults setBool:YES forKey:DisableHints];
+ [defaults setInteger:1 forKey:GestureActivationArea];
+ [defaults setInteger:1 forKey:LeftSideGesture];
+ [defaults setInteger:2 forKey:RightSideGesture];
+ [defaults setInteger:1 forKey:GestureHUDSize];
[defaults synchronize];
- exit(0);
+ [[UIApplication sharedApplication] performSelector:@selector(suspend)];
+ dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0.3 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
+ exit(0);
+ });
} actionTitle:LOC(@"YES")];
alertView.title = LOC(@"WARNING");
alertView.subtitle = LOC(@"RESETDEFAULT");
+ alertView.shouldDismissOnBackgroundTap = YES;
[alertView show];
}
diff --git a/Files/YouModSettings.x b/Files/YouModSettings.x
new file mode 100644
index 00000000..87c7bb88
--- /dev/null
+++ b/Files/YouModSettings.x
@@ -0,0 +1,1216 @@
+// YouModSettings.x — Reusable UIKit-based sub-page for YouMod settings sections
+#import "Headers.h"
+#import
+#import
+
+static NSBundle *YMSettingsBundle() {
+ static NSBundle *bundle = nil;
+ static dispatch_once_t onceToken;
+ dispatch_once(&onceToken, ^{
+ NSString *path = [[NSBundle mainBundle] pathForResource:@"YouMod" ofType:@"bundle"];
+ if (path) bundle = [NSBundle bundleWithPath:path];
+ });
+ return bundle;
+}
+#define YMLOC(x) [YMSettingsBundle() localizedStringForKey:x value:nil table:nil]
+
+#pragma mark - Data Model
+
+typedef NS_ENUM(NSInteger, YMRowType) {
+ YMRowTypeToggle = 0,
+ YMRowTypePicker,
+ YMRowTypeAction,
+ YMRowTypeHeader,
+ YMRowTypeSegment,
+ YMRowTypeTextSegment,
+ YMRowTypeImageSegment,
+ YMRowTypeSlider
+};
+
+@interface YMSettingsItem : NSObject
+@property (nonatomic, assign) YMRowType type;
+@property (nonatomic, strong) NSString *title;
+@property (nonatomic, strong) NSString *subtitle;
+@property (nonatomic, strong) NSString *key;
+@property (nonatomic, strong) NSArray *pickerOptions;
+@property (nonatomic, assign) NSInteger pickerDefault;
+@property (nonatomic, copy) void (^action)(UIViewController *vc);
+@property (nonatomic, strong) NSArray *segmentIcons;
+@property (nonatomic, strong) NSArray *segmentLabels;
+@property (nonatomic, strong) NSArray *segmentImages;
+@property (nonatomic, assign) float sliderMin;
+@property (nonatomic, assign) float sliderMax;
+@property (nonatomic, assign) float sliderStep;
+@property (nonatomic, assign) float sliderDefault;
++ (instancetype)toggleWithTitle:(NSString *)title subtitle:(NSString *)subtitle key:(NSString *)key;
++ (instancetype)sliderWithTitle:(NSString *)title subtitle:(NSString *)subtitle key:(NSString *)key min:(float)min max:(float)max step:(float)step defaultValue:(float)defaultValue;
++ (instancetype)pickerWithTitle:(NSString *)title subtitle:(NSString *)subtitle key:(NSString *)key options:(NSArray *)options defaultValue:(NSInteger)defaultValue;
++ (instancetype)actionWithTitle:(NSString *)title subtitle:(NSString *)subtitle action:(void (^)(UIViewController *vc))action;
++ (instancetype)headerWithTitle:(NSString *)title;
++ (instancetype)segmentWithTitle:(NSString *)title key:(NSString *)key icons:(NSArray *)icons defaultValue:(NSInteger)defaultValue;
++ (instancetype)textSegmentWithTitle:(NSString *)title key:(NSString *)key labels:(NSArray *)labels defaultValue:(NSInteger)defaultValue;
++ (instancetype)imageSegmentWithTitle:(NSString *)title key:(NSString *)key images:(NSArray *)images defaultValue:(NSInteger)defaultValue;
+@end
+
+@implementation YMSettingsItem
+
++ (instancetype)toggleWithTitle:(NSString *)title subtitle:(NSString *)subtitle key:(NSString *)key {
+ YMSettingsItem *item = [[YMSettingsItem alloc] init];
+ item.type = YMRowTypeToggle;
+ item.title = title;
+ item.subtitle = subtitle;
+ item.key = key;
+ return item;
+}
+
++ (instancetype)sliderWithTitle:(NSString *)title subtitle:(NSString *)subtitle key:(NSString *)key min:(float)min max:(float)max step:(float)step defaultValue:(float)defaultValue {
+ YMSettingsItem *item = [[YMSettingsItem alloc] init];
+ item.type = YMRowTypeSlider;
+ item.title = title;
+ item.subtitle = subtitle;
+ item.key = key;
+ item.sliderMin = min;
+ item.sliderMax = max;
+ item.sliderStep = step;
+ item.sliderDefault = defaultValue;
+ return item;
+}
+
++ (instancetype)pickerWithTitle:(NSString *)title subtitle:(NSString *)subtitle key:(NSString *)key options:(NSArray *)options defaultValue:(NSInteger)defaultValue {
+ YMSettingsItem *item = [[YMSettingsItem alloc] init];
+ item.type = YMRowTypePicker;
+ item.title = title;
+ item.subtitle = subtitle;
+ item.key = key;
+ item.pickerOptions = options;
+ item.pickerDefault = defaultValue;
+ return item;
+}
+
++ (instancetype)actionWithTitle:(NSString *)title subtitle:(NSString *)subtitle action:(void (^)(UIViewController *vc))action {
+ YMSettingsItem *item = [[YMSettingsItem alloc] init];
+ item.type = YMRowTypeAction;
+ item.title = title;
+ item.subtitle = subtitle;
+ item.action = action;
+ return item;
+}
+
++ (instancetype)headerWithTitle:(NSString *)title {
+ YMSettingsItem *item = [[YMSettingsItem alloc] init];
+ item.type = YMRowTypeHeader;
+ item.title = title;
+ return item;
+}
+
++ (instancetype)segmentWithTitle:(NSString *)title key:(NSString *)key icons:(NSArray *)icons defaultValue:(NSInteger)defaultValue {
+ YMSettingsItem *item = [[YMSettingsItem alloc] init];
+ item.type = YMRowTypeSegment;
+ item.title = title;
+ item.key = key;
+ item.segmentIcons = icons;
+ item.pickerDefault = defaultValue;
+ return item;
+}
+
++ (instancetype)textSegmentWithTitle:(NSString *)title key:(NSString *)key labels:(NSArray *)labels defaultValue:(NSInteger)defaultValue {
+ YMSettingsItem *item = [[YMSettingsItem alloc] init];
+ item.type = YMRowTypeTextSegment;
+ item.title = title;
+ item.key = key;
+ item.segmentLabels = labels;
+ item.pickerDefault = defaultValue;
+ return item;
+}
+
++ (instancetype)imageSegmentWithTitle:(NSString *)title key:(NSString *)key images:(NSArray *)images defaultValue:(NSInteger)defaultValue {
+ YMSettingsItem *item = [[YMSettingsItem alloc] init];
+ item.type = YMRowTypeImageSegment;
+ item.title = title;
+ item.key = key;
+ item.segmentImages = images;
+ item.pickerDefault = defaultValue;
+ return item;
+}
+
+@end
+
+#pragma mark - YMSubSettingsViewController
+
+@interface YMSubSettingsViewController : UIViewController
+- (UITableView *)tableView;
+- (void)setTableView:(UITableView *)tv;
+- (NSString *)navTitle;
+- (void)setNavTitle:(NSString *)t;
+- (NSArray *)items;
+- (void)setItems:(NSArray *)items;
+- (UIColor *)ymTextColor;
+- (UIColor *)ymSecondaryColor;
+@end
+
+static const void *kYMTableViewKey = &kYMTableViewKey;
+static const void *kYMNavTitleKey = &kYMNavTitleKey;
+static const void *kYMItemsKey = &kYMItemsKey;
+static const void *kYMSwitchKeyAssoc = &kYMSwitchKeyAssoc;
+static const void *kYMSliderKeyAssoc = &kYMSliderKeyAssoc;
+static const void *kYMSliderStepAssoc = &kYMSliderStepAssoc;
+static const void *kYMSliderLabelAssoc = &kYMSliderLabelAssoc;
+
+@implementation YMSubSettingsViewController
+
+- (UITableView *)tableView { return objc_getAssociatedObject(self, kYMTableViewKey); }
+- (void)setTableView:(UITableView *)tv { objc_setAssociatedObject(self, kYMTableViewKey, tv, OBJC_ASSOCIATION_RETAIN_NONATOMIC); }
+- (NSString *)navTitle { return objc_getAssociatedObject(self, kYMNavTitleKey); }
+- (void)setNavTitle:(NSString *)t { objc_setAssociatedObject(self, kYMNavTitleKey, t, OBJC_ASSOCIATION_RETAIN_NONATOMIC); }
+- (NSArray *)items { return objc_getAssociatedObject(self, kYMItemsKey); }
+- (void)setItems:(NSArray *)items { objc_setAssociatedObject(self, kYMItemsKey, items, OBJC_ASSOCIATION_RETAIN_NONATOMIC); }
+
+- (void)viewDidLoad {
+ Class ytStyled = objc_getClass("YTStyledViewController");
+ struct objc_super superStruct = { self, ytStyled ?: [UIViewController class] };
+ ((void (*)(struct objc_super *, SEL))objc_msgSendSuper)(&superStruct, @selector(viewDidLoad));
+
+ self.title = self.navTitle;
+
+ self.tableView = [[UITableView alloc] initWithFrame:self.view.bounds style:UITableViewStyleGrouped];
+ self.tableView.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;
+ self.tableView.delegate = self;
+ self.tableView.dataSource = self;
+ self.tableView.separatorStyle = UITableViewCellSeparatorStyleNone;
+ self.tableView.estimatedRowHeight = 60;
+ self.tableView.rowHeight = UITableViewAutomaticDimension;
+
+ if (self.traitCollection.userInterfaceStyle == UIUserInterfaceStyleDark) {
+ self.tableView.backgroundColor = [%c(YTColor) black3];
+ } else {
+ self.tableView.backgroundColor = [UIColor systemBackgroundColor];
+ }
+
+ [self.view addSubview:self.tableView];
+}
+
+- (void)traitCollectionDidChange:(UITraitCollection *)previousTraitCollection {
+ [super traitCollectionDidChange:previousTraitCollection];
+ if (previousTraitCollection.userInterfaceStyle != self.traitCollection.userInterfaceStyle) {
+ self.tableView.backgroundColor = (self.traitCollection.userInterfaceStyle == UIUserInterfaceStyleDark)
+ ? [%c(YTColor) black3]
+ : [UIColor systemBackgroundColor];
+ [self.tableView reloadData];
+ }
+}
+
+- (void)viewWillAppear:(BOOL)animated {
+ Class ytStyled = objc_getClass("YTStyledViewController");
+ struct objc_super superStruct = { self, ytStyled ?: [UIViewController class] };
+ ((void (*)(struct objc_super *, SEL, BOOL))objc_msgSendSuper)(&superStruct, @selector(viewWillAppear:), animated);
+}
+
+- (void)viewDidLayoutSubviews {
+ Class ytStyled = objc_getClass("YTStyledViewController");
+ struct objc_super superStruct = { self, ytStyled ?: [UIViewController class] };
+ ((void (*)(struct objc_super *, SEL))objc_msgSendSuper)(&superStruct, @selector(viewDidLayoutSubviews));
+ YTQTMButton *backButton = [self valueForKey:@"_backButton"];
+
+ if (self.traitCollection.userInterfaceStyle == UIUserInterfaceStyleDark) {
+ backButton.tintColor = [UIColor whiteColor];
+ } else {
+ backButton.tintColor = [UIColor blackColor];
+ }
+}
+
+- (UIColor *)navBarForegroundColor {
+ if (self.traitCollection.userInterfaceStyle == UIUserInterfaceStyleDark) {
+ return [UIColor whiteColor];
+ }
+ // Light mode: return a concrete colour rather than nil. nil lets YouTube tint the
+ // nav-bar foreground (back chevron + title) with its default appearance, which is
+ // system blue in light mode. labelColor keeps it black in light / white in dark.
+ return [UIColor labelColor];
+}
+
+- (UIColor *)ymTextColor {
+ return (self.traitCollection.userInterfaceStyle == UIUserInterfaceStyleDark)
+ ? [UIColor whiteColor] : [UIColor labelColor];
+}
+
+- (UIColor *)ymSecondaryColor {
+ return [UIColor colorWithWhite:0.55 alpha:1.0];
+}
+
+#pragma mark - UITableViewDataSource
+
+- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
+ return 1;
+}
+
+- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
+ return self.items.count;
+}
+
+- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
+ YMSettingsItem *item = self.items[indexPath.row];
+ if (item.type == YMRowTypeToggle) {
+ return [self toggleCellForItem:item tableView:tableView];
+ } else if (item.type == YMRowTypeAction) {
+ return [self actionCellForItem:item tableView:tableView];
+ } else if (item.type == YMRowTypeHeader) {
+ return [self headerCellForItem:item tableView:tableView];
+ } else if (item.type == YMRowTypeSegment) {
+ return [self segmentCellForItem:item tableView:tableView];
+ } else if (item.type == YMRowTypeTextSegment) {
+ return [self textSegmentCellForItem:item tableView:tableView];
+ } else if (item.type == YMRowTypeImageSegment) {
+ return [self imageSegmentCellForItem:item tableView:tableView];
+ } else if (item.type == YMRowTypeSlider) {
+ return [self sliderCellForItem:item tableView:tableView];
+ }
+ return [self pickerCellForItem:item tableView:tableView];
+}
+
+- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
+ [tableView deselectRowAtIndexPath:indexPath animated:YES];
+ YMSettingsItem *item = self.items[indexPath.row];
+ if (item.type == YMRowTypeAction && item.action) {
+ item.action(self);
+ }
+}
+
+#pragma mark - Toggle Cell
+
+- (UITableViewCell *)toggleCellForItem:(YMSettingsItem *)item tableView:(UITableView *)tableView {
+ UITableViewCell *cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:nil];
+ cell.backgroundColor = [UIColor clearColor];
+ cell.selectionStyle = UITableViewCellSelectionStyleNone;
+ cell.textLabel.text = item.title;
+ cell.textLabel.textColor = [self ymTextColor];
+ cell.textLabel.font = [UIFont systemFontOfSize:16 weight:UIFontWeightMedium];
+
+ if (item.subtitle.length > 0) {
+ cell.detailTextLabel.text = item.subtitle;
+ cell.detailTextLabel.textColor = [self ymSecondaryColor];
+ cell.detailTextLabel.font = [UIFont systemFontOfSize:13];
+ cell.detailTextLabel.numberOfLines = 0;
+ }
+
+ UISwitch *sw = [[UISwitch alloc] init];
+ sw.on = IS_ENABLED(item.key);
+ sw.onTintColor = [UIColor colorWithRed:0.6 green:0.2 blue:0.9 alpha:1.0];
+ objc_setAssociatedObject(sw, kYMSwitchKeyAssoc, item.key, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
+ [sw addTarget:self action:@selector(toggleChanged:) forControlEvents:UIControlEventValueChanged];
+ cell.accessoryView = sw;
+
+ return cell;
+}
+
+- (void)toggleChanged:(UISwitch *)sender {
+ NSString *key = objc_getAssociatedObject(sender, kYMSwitchKeyAssoc);
+ if (key) {
+ [[NSUserDefaults standardUserDefaults] setBool:sender.on forKey:key];
+ }
+}
+
+- (UITableViewCell *)sliderCellForItem:(YMSettingsItem *)item tableView:(UITableView *)tableView {
+ UITableViewCell *cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:nil];
+ cell.backgroundColor = [UIColor clearColor];
+ cell.selectionStyle = UITableViewCellSelectionStyleNone;
+
+ // A stored value of zero is treated as "no value yet" and shown as the
+ // default. Slider ranges are therefore expected to be positive.
+ float stored = [[NSUserDefaults standardUserDefaults] floatForKey:item.key];
+ if (stored <= 0) stored = item.sliderDefault;
+
+ UILabel *titleLabel = [[UILabel alloc] init];
+ titleLabel.translatesAutoresizingMaskIntoConstraints = NO;
+ titleLabel.text = item.title;
+ titleLabel.textColor = [self ymTextColor];
+ titleLabel.font = [UIFont systemFontOfSize:16 weight:UIFontWeightMedium];
+ [cell.contentView addSubview:titleLabel];
+
+ UILabel *valueLabel = [[UILabel alloc] init];
+ valueLabel.translatesAutoresizingMaskIntoConstraints = NO;
+ valueLabel.textColor = [self ymSecondaryColor];
+ valueLabel.font = [UIFont systemFontOfSize:14 weight:UIFontWeightMedium];
+ valueLabel.textAlignment = NSTextAlignmentRight;
+ NSDateComponentsFormatter *formatter = [[NSDateComponentsFormatter alloc] init];
+ formatter.allowedUnits = NSCalendarUnitSecond;
+ formatter.unitsStyle = NSDateComponentsFormatterUnitsStyleAbbreviated;
+
+ valueLabel.text = [formatter stringFromTimeInterval:stored];
+ [cell.contentView addSubview:valueLabel];
+
+ UISlider *slider = [[UISlider alloc] init];
+ slider.translatesAutoresizingMaskIntoConstraints = NO;
+ slider.minimumValue = item.sliderMin;
+ slider.maximumValue = item.sliderMax;
+ slider.value = stored;
+ slider.minimumTrackTintColor = [UIColor colorWithRed:0.6 green:0.2 blue:0.9 alpha:1.0];
+ slider.maximumTrackTintColor = [UIColor colorWithWhite:0.3 alpha:1.0];
+ objc_setAssociatedObject(slider, kYMSliderKeyAssoc, item.key, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
+ objc_setAssociatedObject(slider, kYMSliderStepAssoc, @(item.sliderStep), OBJC_ASSOCIATION_RETAIN_NONATOMIC);
+ objc_setAssociatedObject(slider, kYMSliderLabelAssoc, valueLabel, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
+ [slider addTarget:self action:@selector(sliderChanged:) forControlEvents:UIControlEventValueChanged];
+ [cell.contentView addSubview:slider];
+
+ [NSLayoutConstraint activateConstraints:@[
+ [titleLabel.leadingAnchor constraintEqualToAnchor:cell.contentView.leadingAnchor constant:16],
+ [titleLabel.topAnchor constraintEqualToAnchor:cell.contentView.topAnchor constant:12],
+ [valueLabel.trailingAnchor constraintEqualToAnchor:cell.contentView.trailingAnchor constant:-16],
+ [valueLabel.centerYAnchor constraintEqualToAnchor:titleLabel.centerYAnchor],
+ [valueLabel.widthAnchor constraintGreaterThanOrEqualToConstant:60],
+ [slider.leadingAnchor constraintEqualToAnchor:cell.contentView.leadingAnchor constant:16],
+ [slider.trailingAnchor constraintEqualToAnchor:cell.contentView.trailingAnchor constant:-16],
+ [slider.topAnchor constraintEqualToAnchor:titleLabel.bottomAnchor constant:8],
+ [slider.bottomAnchor constraintEqualToAnchor:cell.contentView.bottomAnchor constant:-12],
+ ]];
+
+ return cell;
+}
+
+- (void)sliderChanged:(UISlider *)sender {
+ NSString *key = objc_getAssociatedObject(sender, kYMSliderKeyAssoc);
+ if (!key) return;
+ float step = [objc_getAssociatedObject(sender, kYMSliderStepAssoc) floatValue];
+ if (step <= 0) step = 1;
+ float snapped = roundf(sender.value / step) * step;
+ sender.value = snapped;
+ [[NSUserDefaults standardUserDefaults] setFloat:snapped forKey:key];
+ UILabel *valueLabel = objc_getAssociatedObject(sender, kYMSliderLabelAssoc);
+ NSDateComponentsFormatter *formatter = [[NSDateComponentsFormatter alloc] init];
+ formatter.allowedUnits = NSCalendarUnitSecond;
+ formatter.unitsStyle = NSDateComponentsFormatterUnitsStyleAbbreviated;
+
+ valueLabel.text = [formatter stringFromTimeInterval:snapped];
+}
+
+#pragma mark - Action Cell
+
+- (UITableViewCell *)actionCellForItem:(YMSettingsItem *)item tableView:(UITableView *)tableView {
+ UITableViewCell *cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:nil];
+ cell.backgroundColor = [UIColor clearColor];
+ cell.selectionStyle = UITableViewCellSelectionStyleDefault;
+ cell.textLabel.text = item.title;
+ cell.textLabel.textColor = [self ymTextColor];
+ cell.textLabel.font = [UIFont systemFontOfSize:16 weight:UIFontWeightMedium];
+
+ if (item.subtitle.length > 0) {
+ cell.detailTextLabel.text = item.subtitle;
+ cell.detailTextLabel.textColor = [self ymSecondaryColor];
+ cell.detailTextLabel.font = [UIFont systemFontOfSize:13];
+ cell.detailTextLabel.numberOfLines = 0;
+ }
+
+ return cell;
+}
+
+#pragma mark - Header Cell
+
+- (UITableViewCell *)headerCellForItem:(YMSettingsItem *)item tableView:(UITableView *)tableView {
+ UITableViewCell *cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:nil];
+ cell.backgroundColor = [UIColor clearColor];
+ cell.selectionStyle = UITableViewCellSelectionStyleNone;
+ cell.textLabel.text = item.title;
+ cell.textLabel.textColor = [self ymSecondaryColor];
+ cell.textLabel.font = [UIFont systemFontOfSize:14 weight:UIFontWeightRegular];
+ return cell;
+}
+
+#pragma mark - Segment Cell
+
+- (UITableViewCell *)segmentCellForItem:(YMSettingsItem *)item tableView:(UITableView *)tableView {
+ UITableViewCell *cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:nil];
+ cell.backgroundColor = [UIColor clearColor];
+ cell.selectionStyle = UITableViewCellSelectionStyleNone;
+
+ UILabel *titleLabel = [[UILabel alloc] init];
+ titleLabel.text = item.title;
+ titleLabel.textColor = [self ymTextColor];
+ titleLabel.font = [UIFont systemFontOfSize:16 weight:UIFontWeightMedium];
+ titleLabel.translatesAutoresizingMaskIntoConstraints = NO;
+ [cell.contentView addSubview:titleLabel];
+
+ NSMutableArray *items = [NSMutableArray array];
+ for (NSUInteger i = 0; i < item.segmentIcons.count; i++) {
+ [items addObject:@""];
+ }
+ UISegmentedControl *segment = [[UISegmentedControl alloc] initWithItems:items];
+
+ for (NSInteger i = 0; i < (NSInteger)item.segmentIcons.count; i++) {
+ YTIIcon *ytIcon = [%c(YTIIcon) new];
+ if (ytIcon) {
+ ((void (*)(id, SEL, int))objc_msgSend)(ytIcon, @selector(setIconType:), [item.segmentIcons[i] intValue]);
+ UIImage *iconImage = nil;
+ if ([ytIcon respondsToSelector:@selector(iconImageWithColor:)]) {
+ iconImage = [ytIcon iconImageWithColor:[UIColor whiteColor]];
+ } else if ([ytIcon respondsToSelector:@selector(iconImageWithSelected:)]) {
+ iconImage = [ytIcon iconImageWithSelected:NO];
+ }
+ if (iconImage) {
+ [segment setImage:iconImage forSegmentAtIndex:i];
+ }
+ }
+ }
+
+ id storedSegVal = [[NSUserDefaults standardUserDefaults] objectForKey:item.key];
+ NSInteger segIdx = storedSegVal ? [storedSegVal integerValue] : item.pickerDefault;
+ segment.selectedSegmentIndex = MAX(0, MIN(segIdx, segment.numberOfSegments - 1));
+ segment.backgroundColor = [UIColor colorWithRed:0.13 green:0.13 blue:0.13 alpha:1.0];
+ segment.selectedSegmentTintColor = [UIColor colorWithRed:0.25 green:0.25 blue:0.25 alpha:1.0];
+ segment.layer.cornerRadius = 8.0;
+ segment.clipsToBounds = YES;
+
+ objc_setAssociatedObject(segment, kYMSwitchKeyAssoc, item.key, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
+ [segment addTarget:self action:@selector(segmentChanged:) forControlEvents:UIControlEventValueChanged];
+
+ segment.translatesAutoresizingMaskIntoConstraints = NO;
+ [cell.contentView addSubview:segment];
+
+ [NSLayoutConstraint activateConstraints:@[
+ [titleLabel.leadingAnchor constraintEqualToAnchor:cell.contentView.leadingAnchor constant:16],
+ [titleLabel.topAnchor constraintEqualToAnchor:cell.contentView.topAnchor constant:12],
+
+ [segment.leadingAnchor constraintEqualToAnchor:cell.contentView.leadingAnchor constant:16],
+ [segment.trailingAnchor constraintEqualToAnchor:cell.contentView.trailingAnchor constant:-16],
+ [segment.topAnchor constraintEqualToAnchor:titleLabel.bottomAnchor constant:10],
+ [segment.bottomAnchor constraintEqualToAnchor:cell.contentView.bottomAnchor constant:-12],
+ [segment.heightAnchor constraintEqualToConstant:36]
+ ]];
+
+ return cell;
+}
+
+- (void)segmentChanged:(UISegmentedControl *)sender {
+ NSString *key = objc_getAssociatedObject(sender, kYMSwitchKeyAssoc);
+ if (key) {
+ [[NSUserDefaults standardUserDefaults] setInteger:sender.selectedSegmentIndex forKey:key];
+ }
+}
+
+#pragma mark - Text Segment Cell
+
+- (UITableViewCell *)textSegmentCellForItem:(YMSettingsItem *)item tableView:(UITableView *)tableView {
+ UITableViewCell *cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:nil];
+ cell.backgroundColor = [UIColor clearColor];
+ cell.selectionStyle = UITableViewCellSelectionStyleNone;
+
+ UILabel *titleLabel = [[UILabel alloc] init];
+ titleLabel.text = item.title;
+ titleLabel.textColor = [self ymTextColor];
+ titleLabel.font = [UIFont systemFontOfSize:16 weight:UIFontWeightMedium];
+ titleLabel.translatesAutoresizingMaskIntoConstraints = NO;
+ [cell.contentView addSubview:titleLabel];
+
+ UISegmentedControl *segment = [[UISegmentedControl alloc] initWithItems:item.segmentLabels];
+
+ id storedVal = [[NSUserDefaults standardUserDefaults] objectForKey:item.key];
+ NSInteger txtSegIdx = storedVal ? [storedVal integerValue] : item.pickerDefault;
+ segment.selectedSegmentIndex = MAX(0, MIN(txtSegIdx, segment.numberOfSegments - 1));
+ segment.backgroundColor = [UIColor colorWithRed:0.13 green:0.13 blue:0.13 alpha:1.0];
+ segment.selectedSegmentTintColor = [UIColor colorWithRed:0.25 green:0.25 blue:0.25 alpha:1.0];
+ segment.layer.cornerRadius = 8.0;
+ segment.clipsToBounds = YES;
+
+ NSDictionary *textAttrs = @{NSForegroundColorAttributeName: [UIColor whiteColor], NSFontAttributeName: [UIFont systemFontOfSize:13 weight:UIFontWeightMedium]};
+ [segment setTitleTextAttributes:textAttrs forState:UIControlStateNormal];
+ [segment setTitleTextAttributes:textAttrs forState:UIControlStateSelected];
+
+ objc_setAssociatedObject(segment, kYMSwitchKeyAssoc, item.key, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
+ [segment addTarget:self action:@selector(segmentChanged:) forControlEvents:UIControlEventValueChanged];
+
+ segment.translatesAutoresizingMaskIntoConstraints = NO;
+ [cell.contentView addSubview:segment];
+
+ [NSLayoutConstraint activateConstraints:@[
+ [titleLabel.leadingAnchor constraintEqualToAnchor:cell.contentView.leadingAnchor constant:16],
+ [titleLabel.topAnchor constraintEqualToAnchor:cell.contentView.topAnchor constant:12],
+
+ [segment.leadingAnchor constraintEqualToAnchor:cell.contentView.leadingAnchor constant:16],
+ [segment.trailingAnchor constraintEqualToAnchor:cell.contentView.trailingAnchor constant:-16],
+ [segment.topAnchor constraintEqualToAnchor:titleLabel.bottomAnchor constant:10],
+ [segment.bottomAnchor constraintEqualToAnchor:cell.contentView.bottomAnchor constant:-12],
+ [segment.heightAnchor constraintEqualToConstant:36]
+ ]];
+
+ return cell;
+}
+
+#pragma mark - Image Segment Cell
+
+- (UITableViewCell *)imageSegmentCellForItem:(YMSettingsItem *)item tableView:(UITableView *)tableView {
+ UITableViewCell *cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:nil];
+ cell.backgroundColor = [UIColor clearColor];
+ cell.selectionStyle = UITableViewCellSelectionStyleNone;
+
+ UILabel *titleLabel = [[UILabel alloc] init];
+ titleLabel.text = item.title;
+ titleLabel.textColor = [self ymTextColor];
+ titleLabel.font = [UIFont systemFontOfSize:16 weight:UIFontWeightMedium];
+ titleLabel.translatesAutoresizingMaskIntoConstraints = NO;
+ [cell.contentView addSubview:titleLabel];
+
+ NSMutableArray *segItems = [NSMutableArray array];
+ for (NSUInteger i = 0; i < item.segmentImages.count; i++) {
+ [segItems addObject:@""];
+ }
+ UISegmentedControl *segment = [[UISegmentedControl alloc] initWithItems:segItems];
+
+ for (NSInteger i = 0; i < (NSInteger)item.segmentImages.count; i++) {
+ UIImage *img = item.segmentImages[i];
+ if (img) [segment setImage:img forSegmentAtIndex:i];
+ }
+
+ id storedVal = [[NSUserDefaults standardUserDefaults] objectForKey:item.key];
+ NSInteger idx = storedVal ? [storedVal integerValue] : item.pickerDefault;
+ segment.selectedSegmentIndex = MAX(0, MIN(idx, segment.numberOfSegments - 1));
+ segment.backgroundColor = [UIColor colorWithRed:0.13 green:0.13 blue:0.13 alpha:1.0];
+ segment.selectedSegmentTintColor = [UIColor colorWithRed:0.25 green:0.25 blue:0.25 alpha:1.0];
+ segment.layer.cornerRadius = 8.0;
+ segment.clipsToBounds = YES;
+
+ objc_setAssociatedObject(segment, kYMSwitchKeyAssoc, item.key, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
+ [segment addTarget:self action:@selector(segmentChanged:) forControlEvents:UIControlEventValueChanged];
+
+ segment.translatesAutoresizingMaskIntoConstraints = NO;
+ [cell.contentView addSubview:segment];
+
+ [NSLayoutConstraint activateConstraints:@[
+ [titleLabel.leadingAnchor constraintEqualToAnchor:cell.contentView.leadingAnchor constant:16],
+ [titleLabel.topAnchor constraintEqualToAnchor:cell.contentView.topAnchor constant:12],
+
+ [segment.leadingAnchor constraintEqualToAnchor:cell.contentView.leadingAnchor constant:16],
+ [segment.trailingAnchor constraintEqualToAnchor:cell.contentView.trailingAnchor constant:-16],
+ [segment.topAnchor constraintEqualToAnchor:titleLabel.bottomAnchor constant:10],
+ [segment.bottomAnchor constraintEqualToAnchor:cell.contentView.bottomAnchor constant:-12],
+ [segment.heightAnchor constraintEqualToConstant:36]
+ ]];
+
+ return cell;
+}
+
+#pragma mark - Picker Cell
+
+- (UITableViewCell *)pickerCellForItem:(YMSettingsItem *)item tableView:(UITableView *)tableView {
+ UITableViewCell *cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:nil];
+ cell.backgroundColor = [UIColor clearColor];
+ cell.selectionStyle = UITableViewCellSelectionStyleNone;
+ cell.textLabel.text = item.title;
+ cell.textLabel.textColor = [self ymTextColor];
+ cell.textLabel.font = [UIFont systemFontOfSize:16 weight:UIFontWeightMedium];
+
+ if (item.subtitle.length > 0) {
+ cell.detailTextLabel.text = item.subtitle;
+ cell.detailTextLabel.textColor = [self ymSecondaryColor];
+ cell.detailTextLabel.font = [UIFont systemFontOfSize:13];
+ cell.detailTextLabel.numberOfLines = 0;
+ }
+
+ NSInteger safeDefault = (item.pickerDefault >= 0 && item.pickerDefault < (NSInteger)item.pickerOptions.count)
+ ? item.pickerDefault : 0;
+ id storedValue = [[NSUserDefaults standardUserDefaults] objectForKey:item.key];
+ NSInteger currentValue = storedValue ? [storedValue integerValue] : safeDefault;
+ NSString *currentTitle = (currentValue >= 0 && currentValue < (NSInteger)item.pickerOptions.count)
+ ? item.pickerOptions[currentValue]
+ : item.pickerOptions[safeDefault];
+
+ UIButton *menuButton = [UIButton buttonWithType:UIButtonTypeSystem];
+ [menuButton setTitle:currentTitle forState:UIControlStateNormal];
+ [menuButton setTitleColor:[self ymSecondaryColor] forState:UIControlStateNormal];
+ menuButton.titleLabel.font = [UIFont systemFontOfSize:15];
+ [menuButton setImage:[UIImage systemImageNamed:@"chevron.up.chevron.down"] forState:UIControlStateNormal];
+ menuButton.tintColor = [self ymSecondaryColor];
+
+ NSMutableArray *menuActions = [NSMutableArray array];
+ __weak typeof(self) weakSelf = self;
+ for (NSInteger i = 0; i < (NSInteger)item.pickerOptions.count; i++) {
+ NSString *optionTitle = item.pickerOptions[i];
+ NSString *itemKey = item.key;
+ UIAction *action = [UIAction actionWithTitle:optionTitle image:nil identifier:nil handler:^(__kindof UIAction *a) {
+ [[NSUserDefaults standardUserDefaults] setInteger:i forKey:itemKey];
+ [weakSelf.tableView reloadData];
+ }];
+ if (i == currentValue) {
+ action.state = UIMenuElementStateOn;
+ }
+ [menuActions addObject:action];
+ }
+
+ menuButton.menu = [UIMenu menuWithTitle:item.title children:menuActions];
+ menuButton.showsMenuAsPrimaryAction = YES;
+ [menuButton sizeToFit];
+ cell.accessoryView = menuButton;
+
+ return cell;
+}
+
+#pragma mark - Table View Footer
+
+- (CGFloat)tableView:(UITableView *)tableView heightForFooterInSection:(NSInteger)section {
+ return 0;
+}
+
+- (UIView *)tableView:(UITableView *)tableView viewForFooterInSection:(NSInteger)section {
+ return [[UIView alloc] init];
+}
+
+- (CGFloat)tableView:(UITableView *)tableView heightForHeaderInSection:(NSInteger)section {
+ return 16;
+}
+
+- (UIView *)tableView:(UITableView *)tableView viewForHeaderInSection:(NSInteger)section {
+ return [[UIView alloc] init];
+}
+
+@end
+
+#pragma mark - Convenience Factory Functions
+
+YMSettingsItem *YMToggle(NSString *title, NSString *subtitle, NSString *key) {
+ return [YMSettingsItem toggleWithTitle:title subtitle:subtitle key:key];
+}
+
+YMSettingsItem *YMSlider(NSString *title, NSString *subtitle, NSString *key, float min, float max, float step, float defaultValue) {
+ return [YMSettingsItem sliderWithTitle:title subtitle:subtitle key:key min:min max:max step:step defaultValue:defaultValue];
+}
+
+YMSettingsItem *YMPicker(NSString *title, NSString *subtitle, NSString *key, NSArray *options, NSInteger defaultValue) {
+ return [YMSettingsItem pickerWithTitle:title subtitle:subtitle key:key options:options defaultValue:defaultValue];
+}
+
+YMSettingsItem *YMAction(NSString *title, NSString *subtitle, void (^action)(UIViewController *vc)) {
+ return [YMSettingsItem actionWithTitle:title subtitle:subtitle action:action];
+}
+
+YMSettingsItem *YMHeader(NSString *title) {
+ return [YMSettingsItem headerWithTitle:title];
+}
+
+YMSettingsItem *YMSegment(NSString *title, NSString *key, NSArray *icons, NSInteger defaultValue) {
+ return [YMSettingsItem segmentWithTitle:title key:key icons:icons defaultValue:defaultValue];
+}
+
+YMSettingsItem *YMTextSegment(NSString *title, NSString *key, NSArray *labels, NSInteger defaultValue) {
+ return [YMSettingsItem textSegmentWithTitle:title key:key labels:labels defaultValue:defaultValue];
+}
+
+YMSettingsItem *YMImageSegment(NSString *title, NSString *key, NSArray *images, NSInteger defaultValue) {
+ return [YMSettingsItem imageSegmentWithTitle:title key:key images:images defaultValue:defaultValue];
+}
+
+#pragma mark - YMTabOrderViewController
+
+static NSString * const kYMTabIDs[] = {
+ @"home", @"shorts", @"create", @"subscriptions", @"library", @"history", @"gaming", @"sports", @"notifications", @"news", @"music", @"watchlater", @"playlist", @"like", @"live", @"post", @"video", @"movie", @"course", @"minigame"
+};
+static const NSInteger kYMTabCount = 20;
+static const NSInteger kYMTabMaxEnabled = 6;
+static const NSInteger kYMTabMinEnabled = 0;
+
+@interface YMTabOrderViewController : UIViewController
+- (UITableView *)tableView;
+- (void)setTableView:(UITableView *)tv;
+- (NSMutableArray *)tabData;
+- (void)setTabData:(NSMutableArray *)data;
+- (NSArray *)initialSnapshot;
+- (void)setInitialSnapshot:(NSArray *)snap;
+@end
+
+static const void *kYMTabTableViewKey = &kYMTabTableViewKey;
+static const void *kYMTabDataKey = &kYMTabDataKey;
+static const void *kYMTabSnapshotKey = &kYMTabSnapshotKey;
+// Saved copies of the shared navigation bar's appearance, restored when this
+// screen is dismissed. This screen installs its own opaque navigation bar
+// appearance; because that appearance is shared, leaving it in place would repaint
+// the previous screen's back button with the appearance's default (blue) tint.
+static const void *kYMTabSavedStdAppearanceKey = &kYMTabSavedStdAppearanceKey;
+static const void *kYMTabSavedScrollEdgeAppearanceKey = &kYMTabSavedScrollEdgeAppearanceKey;
+
+@implementation YMTabOrderViewController
+
+- (UITableView *)tableView { return objc_getAssociatedObject(self, kYMTabTableViewKey); }
+- (void)setTableView:(UITableView *)tv { objc_setAssociatedObject(self, kYMTabTableViewKey, tv, OBJC_ASSOCIATION_RETAIN_NONATOMIC); }
+- (NSMutableArray *)tabData { return objc_getAssociatedObject(self, kYMTabDataKey); }
+- (void)setTabData:(NSMutableArray *)data { objc_setAssociatedObject(self, kYMTabDataKey, data, OBJC_ASSOCIATION_RETAIN_NONATOMIC); }
+- (NSArray *)initialSnapshot { return objc_getAssociatedObject(self, kYMTabSnapshotKey); }
+- (void)setInitialSnapshot:(NSArray *)snap { objc_setAssociatedObject(self, kYMTabSnapshotKey, snap, OBJC_ASSOCIATION_RETAIN_NONATOMIC); }
+
+- (NSString *)localizedNameForTabID:(NSString *)tabID {
+ if ([tabID isEqualToString:@"home"]) return YMLOC(@"HOME_TAB");
+ if ([tabID isEqualToString:@"shorts"]) return YMLOC(@"SHORTS_TAB");
+ if ([tabID isEqualToString:@"create"]) return YMLOC(@"CREATE_TAB");
+ if ([tabID isEqualToString:@"subscriptions"]) return YMLOC(@"SUBSCRIPTIONS_TAB");
+ if ([tabID isEqualToString:@"library"]) return YMLOC(@"LIBRARY_TAB");
+ if ([tabID isEqualToString:@"history"]) return YMLOC(@"HISTORY_TAB");
+ if ([tabID isEqualToString:@"gaming"]) return YMLOC(@"GAMING_TAB");
+ if ([tabID isEqualToString:@"sports"]) return YMLOC(@"SPORTS_TAB");
+ if ([tabID isEqualToString:@"notifications"]) return YMLOC(@"NOTI_TAB");
+ if ([tabID isEqualToString:@"news"]) return YMLOC(@"NEWS_TAB");
+ if ([tabID isEqualToString:@"music"]) return YMLOC(@"MUSIC_TAB");
+ if ([tabID isEqualToString:@"watchlater"]) return YMLOC(@"WATCH_LATER_TAB");
+ if ([tabID isEqualToString:@"playlist"]) return YMLOC(@"PLAYLIST_TAB");
+ if ([tabID isEqualToString:@"like"]) return YMLOC(@"LIKE_TAB");
+ if ([tabID isEqualToString:@"live"]) return YMLOC(@"LIVE_TAB");
+ if ([tabID isEqualToString:@"post"]) return YMLOC(@"POST_TAB");
+ if ([tabID isEqualToString:@"video"]) return YMLOC(@"VIDEO_TAB");
+ if ([tabID isEqualToString:@"movie"]) return YMLOC(@"MOVIE_TAB");
+ if ([tabID isEqualToString:@"course"]) return YMLOC(@"COURSE_TAB");
+ if ([tabID isEqualToString:@"minigame"]) return YMLOC(@"MINIGAME_TAB");
+ return tabID;
+}
+
+- (UIImage *)iconForTabID:(NSString *)tabID {
+ static YTAssetLoader *cachedLoader = nil;
+ static dispatch_once_t onceToken;
+ dispatch_once(&onceToken, ^{
+ cachedLoader = [[%c(YTAssetLoader) alloc] initWithBundle:YMSettingsBundle()];
+ });
+
+ if ([tabID isEqualToString:@"create"]) {
+ UIImageSymbolConfiguration *config = [UIImageSymbolConfiguration configurationWithPointSize:18 weight:UIImageSymbolWeightMedium];
+ return [[UIImage systemImageNamed:@"plus" withConfiguration:config] imageWithRenderingMode:UIImageRenderingModeAlwaysTemplate];
+ }
+ NSDictionary *ytIconTypes = @{@"home": @(65), @"shorts": @(769), @"subscriptions": @(66), @"library": @(61)};
+ NSDictionary *bundleIcons = @{@"history": @"icons/history", @"gaming": @"icons/gaming", @"sports": @"icons/sports", @"notifications": @"icons/noti", @"news": @"icons/news", @"music": @"icons/music", @"watchlater": @"icons/watchlater", @"playlist": @"icons/playlist", @"like": @"icons/like", @"live": @"icons/live", @"post": @"icons/post", @"video": @"icons/video", @"movie": @"icons/movie", @"course": @"icons/course", @"minigame": @"icons/minigame"};
+
+ NSNumber *iconType = ytIconTypes[tabID];
+ if (iconType) {
+ YTIIcon *icon = [%c(YTIIcon) new];
+ if (icon) {
+ ((void (*)(id, SEL, int))objc_msgSend)(icon, @selector(setIconType:), [iconType intValue]);
+ if ([icon respondsToSelector:@selector(iconImageWithColor:)]) {
+ return [[icon iconImageWithColor:[UIColor whiteColor]] imageWithRenderingMode:UIImageRenderingModeAlwaysTemplate];
+ }
+ }
+ }
+
+ NSString *bundleName = bundleIcons[tabID];
+ if (bundleName && cachedLoader) {
+ UIImage *img = [cachedLoader imageNamed:bundleName];
+ if (img) return [img imageWithRenderingMode:UIImageRenderingModeAlwaysTemplate];
+ }
+
+ return nil;
+}
+
+- (void)viewDidLoad {
+ Class ytStyled = objc_getClass("YTStyledViewController");
+ struct objc_super superStruct = { self, ytStyled ?: [UIViewController class] };
+ ((void (*)(struct objc_super *, SEL))objc_msgSendSuper)(&superStruct, @selector(viewDidLoad));
+
+ self.title = YMLOC(@"MANAGE_TABS");
+ [self loadTabData];
+ [self takeSnapshot];
+
+ // The previous screen's navigation bar appearance is saved before this screen
+ // installs its own opaque appearance, and restored on dismissal so that screen
+ // keeps its own back-button tint.
+ UINavigationBar *sharedNavBar = self.navigationController.navigationBar;
+ objc_setAssociatedObject(self, kYMTabSavedStdAppearanceKey, sharedNavBar.standardAppearance, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
+ objc_setAssociatedObject(self, kYMTabSavedScrollEdgeAppearanceKey, sharedNavBar.scrollEdgeAppearance, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
+
+ // Configure navigation bar appearance with solid color
+ UINavigationBarAppearance *appearance = [[UINavigationBarAppearance alloc] init];
+ [appearance configureWithDefaultBackground];
+ if (self.traitCollection.userInterfaceStyle == UIUserInterfaceStyleDark) {
+ appearance.backgroundColor = [%c(YTColor) black3];
+ } else {
+ appearance.backgroundColor = [UIColor systemBackgroundColor];
+ }
+ self.navigationController.navigationBar.standardAppearance = appearance;
+ self.navigationController.navigationBar.scrollEdgeAppearance = appearance;
+
+ self.tableView = [[UITableView alloc] initWithFrame:self.view.bounds style:UITableViewStyleGrouped];
+ self.tableView.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;
+ self.tableView.delegate = self;
+ self.tableView.dataSource = self;
+ self.tableView.editing = YES;
+ self.tableView.allowsSelectionDuringEditing = NO;
+ self.tableView.estimatedRowHeight = 56;
+ self.tableView.rowHeight = UITableViewAutomaticDimension;
+
+ if (self.traitCollection.userInterfaceStyle == UIUserInterfaceStyleDark) {
+ self.tableView.backgroundColor = [%c(YTColor) black3];
+ } else {
+ self.tableView.backgroundColor = [UIColor systemBackgroundColor];
+ }
+
+ [self.view addSubview:self.tableView];
+}
+
+- (void)traitCollectionDidChange:(UITraitCollection *)previousTraitCollection {
+ [super traitCollectionDidChange:previousTraitCollection];
+ if (previousTraitCollection.userInterfaceStyle != self.traitCollection.userInterfaceStyle) {
+ self.tableView.backgroundColor = (self.traitCollection.userInterfaceStyle == UIUserInterfaceStyleDark)
+ ? [%c(YTColor) black3]
+ : [UIColor systemBackgroundColor];
+
+ // Update navigation bar appearance for dark/light mode
+ UINavigationBarAppearance *appearance = [[UINavigationBarAppearance alloc] init];
+ [appearance configureWithDefaultBackground];
+ if (self.traitCollection.userInterfaceStyle == UIUserInterfaceStyleDark) {
+ appearance.backgroundColor = [%c(YTColor) black3];
+ } else {
+ appearance.backgroundColor = [UIColor systemBackgroundColor];
+ }
+ self.navigationController.navigationBar.standardAppearance = appearance;
+ self.navigationController.navigationBar.scrollEdgeAppearance = appearance;
+
+ [self.tableView reloadData];
+ }
+}
+
+- (void)viewWillAppear:(BOOL)animated {
+ Class ytStyled = objc_getClass("YTStyledViewController");
+ struct objc_super superStruct = { self, ytStyled ?: [UIViewController class] };
+ ((void (*)(struct objc_super *, SEL, BOOL))objc_msgSendSuper)(&superStruct, @selector(viewWillAppear:), animated);
+}
+
+- (void)viewWillDisappear:(BOOL)animated {
+ Class ytStyled = objc_getClass("YTStyledViewController");
+ struct objc_super superStruct = { self, ytStyled ?: [UIViewController class] };
+ ((void (*)(struct objc_super *, SEL, BOOL))objc_msgSendSuper)(&superStruct, @selector(viewWillDisappear:), animated);
+
+ // Restore the previous screen's saved navigation bar appearance so it keeps
+ // its own back-button tint rather than this screen's opaque appearance.
+ UINavigationBar *navBar = self.navigationController.navigationBar;
+ navBar.standardAppearance = objc_getAssociatedObject(self, kYMTabSavedStdAppearanceKey);
+ navBar.scrollEdgeAppearance = objc_getAssociatedObject(self, kYMTabSavedScrollEdgeAppearanceKey);
+}
+
+- (void)viewDidLayoutSubviews {
+ Class ytStyled = objc_getClass("YTStyledViewController");
+ struct objc_super superStruct = { self, ytStyled ?: [UIViewController class] };
+ ((void (*)(struct objc_super *, SEL))objc_msgSendSuper)(&superStruct, @selector(viewDidLayoutSubviews));
+ YTQTMButton *backButton = [self valueForKey:@"_backButton"];
+
+ if (self.traitCollection.userInterfaceStyle == UIUserInterfaceStyleDark) {
+ backButton.tintColor = [UIColor whiteColor];
+ } else {
+ backButton.tintColor = [UIColor blackColor];
+ }
+}
+
+- (void)loadTabData {
+ NSArray *savedOrder = [[NSUserDefaults standardUserDefaults] arrayForKey:TabOrder];
+ NSMutableArray *data = [NSMutableArray array];
+
+ if (savedOrder.count > 0) {
+ for (NSDictionary *entry in savedOrder) {
+ NSString *tabID = entry[@"id"];
+ BOOL enabled = [entry[@"enabled"] boolValue];
+ if (tabID) {
+ [data addObject:[@{@"id": tabID, @"enabled": @(enabled)} mutableCopy]];
+ }
+ }
+ // Add any new tabs not in saved data
+ for (NSInteger i = 0; i < kYMTabCount; i++) {
+ NSString *tabID = kYMTabIDs[i];
+ BOOL found = NO;
+ for (NSDictionary *d in data) {
+ if ([d[@"id"] isEqualToString:tabID]) { found = YES; break; }
+ }
+ if (!found) {
+ [data addObject:[@{@"id": tabID, @"enabled": @NO} mutableCopy]];
+ }
+ }
+ } else {
+ // Default: Home, Shorts, Create, Subscriptions, Library enabled
+ for (NSInteger i = 0; i < kYMTabCount; i++) {
+ BOOL defaultEnabled = i < 5;
+ [data addObject:[@{@"id": kYMTabIDs[i], @"enabled": @(defaultEnabled)} mutableCopy]];
+ }
+ }
+
+ self.tabData = data;
+}
+
+- (void)saveTabData {
+ NSMutableArray *toSave = [NSMutableArray array];
+ for (NSMutableDictionary *entry in self.tabData) {
+ [toSave addObject:@{@"id": entry[@"id"], @"enabled": entry[@"enabled"]}];
+ }
+ [[NSUserDefaults standardUserDefaults] setObject:toSave forKey:TabOrder];
+ [[NSUserDefaults standardUserDefaults] synchronize];
+ [[NSNotificationCenter defaultCenter] postNotificationName:@"YouModUpdateTabBar" object:nil];
+}
+
+- (void)takeSnapshot {
+ NSMutableArray *snap = [NSMutableArray array];
+ for (NSDictionary *entry in self.tabData) {
+ [snap addObject:@{@"id": entry[@"id"], @"enabled": entry[@"enabled"]}];
+ }
+ self.initialSnapshot = [snap copy];
+}
+
+- (NSInteger)enabledCount {
+ NSInteger count = 0;
+ for (NSDictionary *entry in self.tabData) {
+ if ([entry[@"enabled"] boolValue]) count++;
+ }
+ return count;
+}
+
+#pragma mark - UITableViewDataSource
+
+- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView { return 1; }
+
+- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
+ return self.tabData.count;
+}
+
+- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
+ static NSString *cellID = @"YMTabCell";
+ UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:cellID];
+ UISwitch *sw;
+
+ if (!cell) {
+ cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:cellID];
+ cell.backgroundColor = [UIColor clearColor];
+ cell.selectionStyle = UITableViewCellSelectionStyleNone;
+
+ sw = [[UISwitch alloc] init];
+ sw.onTintColor = [UIColor colorWithRed:0.6 green:0.2 blue:0.9 alpha:1.0];
+ [sw addTarget:self action:@selector(tabToggleChanged:) forControlEvents:UIControlEventValueChanged];
+ sw.translatesAutoresizingMaskIntoConstraints = NO;
+ sw.tag = 999;
+ [cell.contentView addSubview:sw];
+
+ [NSLayoutConstraint activateConstraints:@[
+ [sw.centerYAnchor constraintEqualToAnchor:cell.contentView.centerYAnchor],
+ [sw.trailingAnchor constraintEqualToAnchor:cell.contentView.trailingAnchor constant:-16]
+ ]];
+ } else {
+ sw = [cell.contentView viewWithTag:999];
+ }
+
+ NSMutableDictionary *entry = self.tabData[indexPath.row];
+ NSString *tabID = entry[@"id"];
+ BOOL enabled = [entry[@"enabled"] boolValue];
+
+ cell.textLabel.text = [self localizedNameForTabID:tabID];
+ cell.textLabel.textColor = [UIColor labelColor];
+ cell.textLabel.font = [UIFont systemFontOfSize:16 weight:UIFontWeightMedium];
+
+ UIImage *tabIcon = [self iconForTabID:tabID];
+ cell.imageView.image = tabIcon;
+ cell.imageView.tintColor = [UIColor labelColor];
+
+ sw.on = enabled;
+ objc_setAssociatedObject(sw, kYMSwitchKeyAssoc, tabID, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
+
+ return cell;
+}
+
+- (void)tabToggleChanged:(UISwitch *)sender {
+ NSString *tabID = objc_getAssociatedObject(sender, kYMSwitchKeyAssoc);
+ if (!tabID) return;
+
+ NSMutableDictionary *entry = nil;
+ for (NSMutableDictionary *d in self.tabData) {
+ if ([d[@"id"] isEqualToString:tabID]) { entry = d; break; }
+ }
+ if (!entry) return;
+
+ BOOL wantsEnabled = sender.on;
+
+ if (wantsEnabled && [self enabledCount] >= kYMTabMaxEnabled) {
+ sender.on = NO;
+ YTAlertView *alert = [%c(YTAlertView) infoDialog];
+ alert.title = YMLOC(@"TAB_LIMIT");
+ alert.subtitle = YMLOC(@"TAB_LIMIT_DESC");
+ [alert show];
+ return;
+ } else if (!wantsEnabled && [self enabledCount] <= kYMTabMinEnabled) {
+ sender.on = YES;
+ YTAlertView *alert = [%c(YTAlertView) infoDialog];
+ alert.title = YMLOC(@"WARNING");
+ alert.subtitle = YMLOC(@"ZERO_TAB_DESC");
+ [alert show];
+ return;
+ }
+
+ entry[@"enabled"] = @(wantsEnabled);
+ [self saveTabData];
+}
+
+#pragma mark - Reordering
+
+- (BOOL)tableView:(UITableView *)tableView canMoveRowAtIndexPath:(NSIndexPath *)indexPath { return YES; }
+
+- (void)tableView:(UITableView *)tableView moveRowAtIndexPath:(NSIndexPath *)from toIndexPath:(NSIndexPath *)to {
+ NSMutableDictionary *item = self.tabData[from.row];
+ [self.tabData removeObjectAtIndex:from.row];
+ [self.tabData insertObject:item atIndex:to.row];
+ [self saveTabData];
+}
+
+- (UITableViewCellEditingStyle)tableView:(UITableView *)tableView editingStyleForRowAtIndexPath:(NSIndexPath *)indexPath {
+ return UITableViewCellEditingStyleNone;
+}
+
+- (BOOL)tableView:(UITableView *)tableView shouldIndentWhileEditingRowAtIndexPath:(NSIndexPath *)indexPath {
+ return NO;
+}
+
+#pragma mark - Section Header/Footer
+
+- (CGFloat)tableView:(UITableView *)tableView heightForHeaderInSection:(NSInteger)section {
+ return UITableViewAutomaticDimension;
+}
+
+- (UIColor *)ymSecondaryColor {
+ return [UIColor colorWithWhite:0.55 alpha:1.0];
+}
+
+- (UIView *)tableView:(UITableView *)tableView viewForHeaderInSection:(NSInteger)section {
+ UIView *headerView = [[UIView alloc] init];
+ headerView.backgroundColor = [UIColor clearColor];
+
+ UILabel *hintLabel = [[UILabel alloc] init];
+ hintLabel.text = YMLOC(@"TAB_REORDER_HINT");
+ hintLabel.textColor = [self ymSecondaryColor];
+ hintLabel.font = [UIFont systemFontOfSize:13];
+ hintLabel.numberOfLines = 0;
+ hintLabel.translatesAutoresizingMaskIntoConstraints = NO;
+ [headerView addSubview:hintLabel];
+
+ [NSLayoutConstraint activateConstraints:@[
+ [hintLabel.leadingAnchor constraintEqualToAnchor:headerView.leadingAnchor constant:16],
+ [hintLabel.trailingAnchor constraintEqualToAnchor:headerView.trailingAnchor constant:-16],
+ [hintLabel.topAnchor constraintEqualToAnchor:headerView.topAnchor constant:12],
+ [hintLabel.bottomAnchor constraintEqualToAnchor:headerView.bottomAnchor constant:-12]
+ ]];
+
+ return headerView;
+}
+
+- (CGFloat)tableView:(UITableView *)tableView heightForFooterInSection:(NSInteger)section { return 0; }
+- (UIView *)tableView:(UITableView *)tableView viewForFooterInSection:(NSInteger)section { return [[UIView alloc] init]; }
+
+@end
+
+void YMPushTabOrder(id settingsVC, id parentResponder) {
+ Class styledClass = objc_getClass("YMTabOrderViewControllerStyled");
+ if (!styledClass) styledClass = [YMTabOrderViewController class];
+
+ YMTabOrderViewController *vc = (YMTabOrderViewController *)((id (*)(id, SEL, id))objc_msgSend)([styledClass alloc], @selector(initWithParentResponder:), parentResponder);
+ if (!vc) vc = [[styledClass alloc] init];
+ [settingsVC pushViewController:vc];
+}
+
+// Modal entry point for opening Manage Tabs without a YTSettingsViewController nav stack
+// (used by the long-press gesture on the Home tab). Wraps the standard tab-order VC in
+// a UINavigationController with a Done button and presents from the topmost VC.
+void YMPresentTabOrderModally(id parentResponder) {
+ Class styledClass = objc_getClass("YMTabOrderViewControllerStyled");
+ if (!styledClass) styledClass = [YMTabOrderViewController class];
+
+ YMTabOrderViewController *vc = (YMTabOrderViewController *)((id (*)(id, SEL, id))objc_msgSend)([styledClass alloc], @selector(initWithParentResponder:), parentResponder);
+ if (!vc) vc = [[styledClass alloc] init];
+
+ UINavigationController *nav = [[UINavigationController alloc] initWithRootViewController:vc];
+ nav.modalPresentationStyle = UIModalPresentationFormSheet;
+
+ __weak UINavigationController *weakNav = nav;
+ UIAction *doneAction = [UIAction actionWithTitle:@"" image:nil identifier:nil handler:^(__unused UIAction *action) {
+ [weakNav dismissViewControllerAnimated:YES completion:nil];
+ }];
+ vc.navigationItem.rightBarButtonItem = [[UIBarButtonItem alloc]
+ initWithBarButtonSystemItem:UIBarButtonSystemItemDone
+ primaryAction:doneAction];
+
+ UIViewController *presenter = [%c(YTUIUtils) topViewControllerForPresenting];
+ if (!presenter) return;
+ [presenter presentViewController:nav animated:YES completion:nil];
+}
+
+#pragma mark - Entry Point
+
+void YMPushSubSettings(NSString *title, NSArray *items, id settingsVC, id parentResponder) {
+ Class styledClass = objc_getClass("YMSubSettingsViewControllerStyled");
+ if (!styledClass) styledClass = [YMSubSettingsViewController class];
+
+ YMSubSettingsViewController *vc = (YMSubSettingsViewController *)((id (*)(id, SEL, id))objc_msgSend)([styledClass alloc], @selector(initWithParentResponder:), parentResponder);
+ if (!vc) vc = [[styledClass alloc] init];
+ vc.navTitle = title;
+ vc.items = items;
+ [settingsVC pushViewController:vc];
+}
+
+#pragma mark - Runtime Class Registration
+
+static void ymRegisterStyledSubclass(Class sourceClass, const char *name) {
+ Class ytStyled = %c(YTStyledViewController);
+ Class newClass = objc_allocateClassPair(ytStyled, name, 0);
+ if (!newClass) return;
+
+ unsigned int count = 0;
+ Method *methods = class_copyMethodList(sourceClass, &count);
+ for (unsigned int i = 0; i < count; i++) {
+ class_addMethod(newClass, method_getName(methods[i]), method_getImplementation(methods[i]), method_getTypeEncoding(methods[i]));
+ }
+ free(methods);
+
+ unsigned int propCount = 0;
+ objc_property_t *props = class_copyPropertyList(sourceClass, &propCount);
+ for (unsigned int i = 0; i < propCount; i++) {
+ unsigned int attrCount = 0;
+ objc_property_attribute_t *attrs = property_copyAttributeList(props[i], &attrCount);
+ class_addProperty(newClass, property_getName(props[i]), attrs, attrCount);
+ free(attrs);
+ }
+ free(props);
+
+ objc_registerClassPair(newClass);
+}
+
+%hook YTQTMButton
+- (void)layoutSubviews {
+ %orig;
+ if ([self.accessibilityIdentifier isEqualToString:@"id.ui.title.tab.button"]) {
+ UIColor *customTitle = [self valueForKey:@"_desiredCustomTitleColor"];
+
+ if (self.traitCollection.userInterfaceStyle == UIUserInterfaceStyleDark) {
+ self.titleLabel.textColor = [UIColor whiteColor];
+ if (customTitle) {
+ [self setValue:[UIColor whiteColor] forKey:@"_desiredCustomTitleColor"];
+ }
+ } else {
+ self.titleLabel.textColor = [UIColor blackColor];
+ if (customTitle) {
+ [self setValue:[UIColor blackColor] forKey:@"_desiredCustomTitleColor"];
+ }
+ }
+ } else if ([self.accessibilityIdentifier isEqualToString:@"id.ui.browse.back.button"]) {
+ if (self.traitCollection.userInterfaceStyle == UIUserInterfaceStyleDark) {
+ self.tintColor = [UIColor whiteColor];
+ } else {
+ self.tintColor = [UIColor blackColor];
+ }
+ }
+}
+%end
+
+%hook YTPivotBarViewController
+- (void)viewDidLoad {
+ %orig;
+ [[NSNotificationCenter defaultCenter] removeObserver:self name:@"YouModUpdateTabBar" object:nil];
+ [[NSNotificationCenter defaultCenter] addObserver:self
+ selector:@selector(YouModReloadTabBar:)
+ name:@"YouModUpdateTabBar"
+ object:nil];
+}
+%new
+- (void)YouModReloadTabBar:(id)arg {
+ if ([self.parentViewController isKindOfClass:%c(YTAppViewControllerImpl)]) {
+ YTAppViewControllerImpl *appcon = (YTAppViewControllerImpl *)self.parentViewController;
+ [appcon refreshPivotBarWithTriggedByNotification:YES];
+ } else {
+ YTAppViewController *appcon = (YTAppViewController *)self.parentViewController;
+ [appcon refreshPivotBarWithTriggedByNotification:YES];
+ }
+}
+%end
+
+%ctor {
+ ymRegisterStyledSubclass([YMSubSettingsViewController class], "YMSubSettingsViewControllerStyled");
+ ymRegisterStyledSubclass([YMTabOrderViewController class], "YMTabOrderViewControllerStyled");
+}
diff --git a/Makefile b/Makefile
index 2718ca0a..c93851ce 100644
--- a/Makefile
+++ b/Makefile
@@ -1,4 +1,5 @@
# Original Makefile from YTLite
+DEBUG = 0
FINALPACKAGE = 1
ARCHS = arm64
TARGET := iphone:clang:latest:14.0
@@ -6,7 +7,7 @@ TARGET := iphone:clang:latest:14.0
include $(THEOS)/makefiles/common.mk
TWEAK_NAME = YouMod
-$(TWEAK_NAME)_FRAMEWORKS = UIKit Foundation
+$(TWEAK_NAME)_FRAMEWORKS = UIKit Foundation AudioToolbox MediaPlayer
$(TWEAK_NAME)_CFLAGS = -fobjc-arc
$(TWEAK_NAME)_FILES = $(wildcard Files/*.x)
diff --git a/README.md b/README.md
index 0736c3bb..e2a4d61f 100644
--- a/README.md
+++ b/README.md
@@ -1,25 +1,33 @@
# YouMod
-A tweak that's try to recreate features like in YTLite as much as possible. Contributions are welcome! ^^
-Thanks for 200 stars and forks! This might be the best project I've done.
+TODO: Cleanup download things
+
+
+
+
+
+A tweak that's try to recreate features like in YTLite as much as possible. Contributors are welcome! ^^
+
+Thanks for 300 stars and 500 forks! This might be the best project I've done.
+
+If you want to contribute this project, scroll down below for how to do it.
-If you want to contribute this project, scroll down to `How to contribute this project?` part.
## Why I created this project?
The reason is because of YTLite no longer free. So I'll try my best to recreate all the features as much as I can, plus alternative tweaks combined.
-## Features
-This tweak add a lot of QoL feature and can hide elements in YouTube. Such as:
-- Downloading (video, audio, select quality)
-- Feed modifications (hide shorts, hide post)
-- Appearance modification (OLED theme, keyboard, add/remove buttons)
-- Navigation bar (hide buttons)
-- Player (add/remove buttons and elements)
-- Shorts (add/remove buttons and elements)
-- Tab bar (reorder, add/remove buttons)
+## Features
+This tweak can hide, remove elements and others in YouTube. Such as:
+- Downloading (Video, Audio, Captions, Thumbnail, Video datails, Currently supports up to 1080p60)
+- Appearance (OLED theme and keyboard)
+- Navigation bar (Hiding buttons)
+- Player (Hiding elements, buttons, and Add custom actions)
+- Shorts (Hiding elements and Add custom actions)
+- Tab bar (Reorder tabs and Hiding things)
+- Built-in SponsorBlock
- And much more!
## Supported YouTube version
-Up to the latest, this is the goal of this tweak. Any versions starting from 19.x - latest.
+Up to the latest, this is the goal of this tweak. Any versions starting from 19.x - latest and running at least iOS 14.
## How to build the IPA?
1. Click on **Sync fork**, and if your branch is out-of-date, click on **Update branch**.
@@ -50,7 +58,10 @@ This project uses GPLv3 license. See [LICENSE](https://github.com/Tonwalter888/Y
- [YTLitePlus](https://github.com/YTLitePlus/YTLitePlus)
- [uYouEnhanced](https://github.com/arichornlover/uYouEnhanced) - arichornlover
- [YTweaks](https://github.com/fosterbarnes/YTweaks) - fosterbarnes
-- [daisuke1227](https://github.com/daisuke1227) - Implementing download features
+- [jefemagril](https://github.com/jefemagril) - Implemented rewind/fast forward buttons to system Control Center
+- [grohit1810](https://github.com/grohit1810) - Implemented built-in SponsorBlock, whole new settings logic, and others
+- [SponsorBlock](https://sponsor.ajay.app) - SponsorBlock segments API
+- [Google Fonts](https://fonts.google.com/icons) - Icons for custom tabs
- And [PoomSmart](https://github.com/PoomSmart)'s tweaks
-Also thanks to all the contributors in the past!
+Also thanks to the other contributors in the past!
\ No newline at end of file
diff --git a/control b/control
index 579c5b7a..0d1f04fd 100644
--- a/control
+++ b/control
@@ -1,6 +1,6 @@
Package: dev.water888.youmod
Name: YouMod
-Version: 1.3.0
+Version: 2.0.0
Architecture: iphoneos-arm
Description: A lightweight YouTube enhancer
Maintainer: Tonwalter888
diff --git a/layout/Library/Application Support/YouMod.bundle/Info.plist b/layout/Library/Application Support/YouMod.bundle/Info.plist
index fe35029e..53ba69a4 100644
--- a/layout/Library/Application Support/YouMod.bundle/Info.plist
+++ b/layout/Library/Application Support/YouMod.bundle/Info.plist
@@ -13,11 +13,11 @@
CFBundlePackageType
BNDL
CFBundleShortVersionString
- 1.2.0
+ 1.3.0
CFBundleSignature
????
CFBundleVersion
- 1.2.0
+ 1.3.0
NSPrincipalClass
YouMod
diff --git a/layout/Library/Application Support/YouMod.bundle/ar.lproj/Localizable.strings b/layout/Library/Application Support/YouMod.bundle/ar.lproj/Localizable.strings
index bccaf550..2c1a8a0f 100644
--- a/layout/Library/Application Support/YouMod.bundle/ar.lproj/Localizable.strings
+++ b/layout/Library/Application Support/YouMod.bundle/ar.lproj/Localizable.strings
@@ -1,11 +1,18 @@
-"NOTE" = "NOTE: Some hiding features may leave a blank box.";
"SETTINGS" = "Settings";
+"INTERFACE" = "Interface";
+
+"LOOP_DISABLED" = "Loop disabled";
+"LOOP_ENABLED" = "Loop enabled";
"NEW_ISSUES" = "Report issues or request new features";
"NEW_ISSUES_DESC" = "Found bugs or want to request new features? Tap here.";
"SOURCE_CODES" = "Source codes";
"SOURCE_CODES_DESC" = "Take a look at the codes!";
+"EXTRA" = "Extra";
+"FIX_PLAYBACK_ISSUES" = "Fix playback issues (Temporary)";
+"FIX_PLAYBACK_ISSUES_DESC" = "Try to fix video playback issues for some users. Turning this on will make the player automatically reloads itself everytime it errors or fails to play the video.";
+
"PERFER_HEADER" = "YouMod perferences";
"PERFER" = "Perferences";
"IMPORT" = "Import settings";
@@ -24,6 +31,7 @@
"ERROR_NO_KEYS_IMPORT" = "No available keys to import";
"ERROR_NO_KEYS_EXPORT" = "No available keys to export";
"OVERRIDE" = "This will override the current settings. Do you want to continue?";
+"DISABLED" = "Disabled";
"CACHE" = "Cache management";
"CLEARCACHE" = "Clear cache";
@@ -32,12 +40,80 @@
"AUTO_CLEARCACHE_DESC" = "Automatically clears the app cache on startup.";
"DOWNLOADING" = "Downloading";
-"DOWNLOAD_MANAGER" = "Download Manager";
-"DOWNLOAD_MANAGER_DESC" = "Replaces YouTube's video downloader with YouMod one. With ablity to downloading videos, audios, captions, and thumbnails";
+"DOWNLOAD_MANAGER" = "Download manager";
+"DOWNLOAD_MANAGER_DESC" = "Replaces YouTube's video downloader with YouMod one. With ablity to downloading videos, audios, captions, and thumbnails.";
"DOWNLOAD_SAVE_PHOTOS" = "Automatically save to Photos";
"DOWNLOAD_SAVE_PHOTOS_DESC" = "Automatically saves downloaded videos and images directly to your Photos app.";
-"DOWNLOAD_DRC_AUDIO" = "Prefer DRC Audio";
-"DOWNLOAD_DRC_AUDIO_DESC" = "Prioritizes downloading audio tracks with Dynamic Range Compression for more consistent volume levels.";
+"ADD_SHORTS_DOWNLOAD" = "Adds a download button to Shorts";
+"ADD_SHORTS_DOWNLOAD_DESC" = "Adds a download button to Shorts video overlay.";
+"USE_ORIG_AUDIO" = "Automatically uses original audio track";
+"USE_ORIG_AUDIO_DESC" = "Automatically uses original audio track instead of showing a dialog.";
+"DOWNLOAD_SHORTS" = "Download Shorts";
+"DOWNLOAD_SHORTS_DESC" = "Choose Shorts quality";
+"COPY_VID_INFO" = "Copy video informations";
+"COPY_VID_INFO_DESC" = "Choose what to copy";
+"COPY_ALL_VID_INFO" = "Copy all";
+"COPY_ALL_VID_INFO_DESC" = "Copy all video informations";
+"COPY_AUTHOR" = "Copy author";
+"COPY_AUTHOR_DESC" = "Copy the video author";
+"COPY_TITLE" = "Copy title";
+"COPY_TITLE_DESC" = "Copy the video title";
+"COPY_DESCRIPTION" = "Copy description";
+"COPY_DESCRIPTION_DESC" = "Copy the video description";
+"COPY_URL" = "Copy video link";
+"COPY_URL_TIMESTAMP" = "Copy video link with timestamp";
+"URL_COPIED" = "Copied video link";
+"URL_TIMESTAMP_COPIED" = "Copied video link with timestamp";
+"ERROR_VIDEOID" = "Cannot get the video ID";
+"ERROR_ADS" = "Cannot copy video link while playing ads";
+"COPIED_AUTHOR" = "Copied video author";
+"COPIED_TITLE" = "Copied video title";
+"COPIED_DESCRIPTION" = "Copied video description";
+"SAVE_THUMBNAIL" = "Save thumbnail";
+"SAVE_THUMBNAIL_DESC" = "Save to Photos";
+"DOWNLOAD_CAPTIONS" = "Download captions";
+"DOWNLOAD_CAPTIONS_DESC" = "Save subtitles as .VTT file";
+"DOWNLOAD_AUDIO" = "Download audio";
+"DOWNLOAD_AUDIO_DESC" = "Download audio as .m4a file";
+"DOWNLOAD_VIDEO" = "Download video";
+"DOWNLOAD_VIDEO_DESC" = "Choose video quality";
+"OPEN_VID_BEFORE" = "Open a video before using the download manager";
+"NO_CAPTIONS_URL" = "No valid caption URLs found";
+"NO_AUDIO_URL" = "No audio URL found";
+"NO_STREAM_URL" = "No stream URL found";
+"CAPTIONS_FAILED" = "Failed to download captions";
+"NO_CAPTIONS" = "No captions available for this video";
+"NO_AUDIO_STREAM_FOUND" = "No audio streams found";
+"NO_VID_AUDIO_STREAM_FOUND" = "No video/audio streams found";
+"COPIED_VID_INFO" = "Copied all video informations";
+"SAVED_TO_PHOTOS" = "Saved to Photos";
+"SAVE_FAILED" = "Save failed";
+"PHOTO_ACCESS_DENINED" = "Photos access denied";
+"THUMBNAIL_FAILED" = "Thumbnail failed";
+"DOWNLOADING_THUMBNAIL" = "Downloading thumbnail...";
+"DOWNLOADING_AUDIO" = "Downloading audio...";
+"DOWNLOADING_VIDEO" = "Downloading video...";
+"DOWNLOADING_CAPTIONS" = "Downloading captions...";
+"NO_THUMBNAIL_FOUND" = "No thumbnail found";
+"DOWNLOAD_FAILED" = "Download failed";
+"COPY_THUMBNAIL" = "Copy thumbnail";
+"COPY_THUMBNAIL_DESC" = "Copy to clipboard";
+"SHOW_THUMBNAIL" = "Show thumbnail";
+"SHOW_THUMBNAIL_DESC" = "Show in a dialog";
+"THUMBNAIL_OPTIONS" = "Download thumbnail";
+"THUMBNAIL_OPTIONS_DESC" = "Show more options";
+"COPIED_TO_CLIPBOARD" = "Copied to clipboard";
+"DOWNLOAD_COMPLETED" = "Download completed";
+"CANNOT_SAVE_TO_PHOTOS" = "Cannot save to Photos";
+"FINA_VIDEO" = "Finalizing video...";
+"MERGING_VID_FALLBACK" = "Merging video with fallback...";
+"MERGING_VID" = "Merging video...";
+"CONVERTING" = "Converting to";
+"ALREADY_DOWNLOADING" = "Already downloading";
+"DOWNLOAD_CANCELLED" = "Download cancelled";
+"CANCEL" = "Cancel";
+"TRIMMING_AUDIO" = "Trimming audio...";
+"COPY" = "Copy";
"APPEARANCE" = "Appearance";
"OLED_THEME" = "OLED dark mode";
@@ -46,6 +122,8 @@
"OLED_KEYBOARD_DESC" = "Make the keyboard uses a pure black (OLED) color for dark theme.";
"NAVBAR" = "Navigation bar";
+"STICKY_NAVBAR" = "Sticky Navigation bar";
+"STICKY_NAVBAR_DESC" = "Pins the Navigation bar so that it remains visible when scrolling down.";
"HIDE_YT_LOGO" = "Hide YouTube logo";
"HIDE_YT_LOGO_DESC" = "Hide YouTube logo in the navigation bar.";
"PREMIUM_LOGO" = "Use YouTube Premium logo";
@@ -62,34 +140,78 @@
"FEED" = "Feed";
"HIDE_SUBBAR" = "Hide subbar";
"HIDE_SUBBAR_DESC" = "Hide subbar at the top of the feed.";
+"HIDE_HORI_SHELF" = "Remove horizontal feeds";
+"HIDE_HORI_SHELF_DESC" = "Removes all horizontal feeds (News, Continue Watching, Posts collection, etc.) from the feed, except Playables.";
"HIDE_MUSIC_PLAYLISTS" = "Hide music playlist generator";
"HIDE_MUSIC_PLAYLISTS_DESC" = "Hide music playlist generator (Select music tunes in three) in the feed.";
"HIDE_FEED_POST" = "Hide posts";
"HIDE_FEED_POST_DESC" = "Hide posts in feed.";
+"HIDE_SURVEYS" = "Hide surveys";
+"HIDE_SURVEYS_DESC" = "Hide video surveys that YouTube shows sometimes.";
"HIDE_SHORTS_SHELF" = "Hide Shorts shelf";
"HIDE_SHORTS_SHELF_DESC" = "Hide Shorts shelf in the feed, including in the Subscriptions tab.";
+"KEEP_SHORTS_SUBSCRIPT" = "Keep Shorts in Subscriptions";
+"KEEP_SHORTS_SUBSCRIPT_DESC" = "Prevents removing Shorts from Subscriptions tab.";
"HIDE_SEARCH_HISTORY" = "Hide search history";
"HIDE_SEARCH_HISTORY_DESC" = "Hide previous search history and suggestions when using the search bar.\nNOTE: Your search history will still be accessible in your other YouTube clients.";
-"HIDE_SUB_BUTTON" = "Hide Subscribe button";
-"HIDE_SUB_BUTTON_DESC" = "Hides the subscribe button in channels and video descriptions.";
-"HIDE_SHOP_BUTTON" = "Hide Shopping button";
-"HIDE_SHOP_BUTTON_DESC" = "Hides the shopping/store button in channels descriptions.";
-"HIDE_MEMBER_BUTTON" = "Hide Membership button";
-"HIDE_MEMBER_BUTTON_DESC" = "Hides the 'Join' button in channels and video descriptions.";
+"HIDE_PLAYABLES" = "Remove playables";
+"HIDE_PLAYABLES_DESC" = "Removes playables (mini-games) from the feed.";
"PLAYER" = "Player";
+"PLAYBACK_SPEED" = "Playback speed";
+"CONTROL_CENTER" = "Control Center";
+"PLAYER_ACTIONS" = "Player actions";
+"GESTURE_HEADER" = "Gestures";
+"QUALITY_WIFI" = "Playback quality on WiFi";
+"QUALITY_WIFI_DESC" = "Automatically applies the selected quality when streaming over Wi-Fi.";
+"QUALITY_CELLULAR" = "Playback quality on Cellular";
+"QUALITY_CELLULAR_DESC" = "Automatically applies the selected quality when streaming over mobile data.";
+"QUALITY_LOW_POWER" = "Playback quality on Power saver mode";
+"QUALITY_LOW_POWER_DESC" = "Automatically applies the selected quality when when Low power mode is enabled on your device.";
+"DEFAULT" = "Default";
+"BEST" = "Best";
+"AUDIO_TRACK" = "Preferred audio track";
+"ORIGINAL" = "Original";
+"SELECT_MANUALLY" = "Select manually";
+"AUDIO_TRACK_SELECT" = "Selected audio track";
+"AUDIO_TRACK_SELECT_DESC" = "Specifies the preferred audio track language to use for playback, if available.";
+"NO_AUTO_DUBBED" = "Uses original audio track";
+"NO_AUTO_DUBBED_DESC" = "Uses original audio track if the preferred language track is auto-dubbed or not found.";
+"CAPTION_TRACK" = "Preferred captions";
+"CAPTION_TRACK_SELECT" = "Selected captions";
+"CAPTION_TRACK_SELECT_DESC" = "Specifies the preferred captions language to display, if available.";
+"DISABLES_CAPTION_TRACK" = "Automatically disable captions";
+"DISABLES_CAPTION_TRACK_DESC" = "Automatically disable captions if the preferred language is auto-generated or not found.";
+"DEFAULT_SPEED" = "Default playback rate";
+"DEFAULT_SPEED_DESC" = "Overrides the default playback speed.";
+"SPEED_BUTTON" = "Speed controls button";
+"SPEED_BUTTON_DESC" = "Displays a speed controls button on overlay.";
+"MUTE_BUTTON" = "Mute button";
+"MUTE_BUTTON_DESC" = "Displays a button to mute or unmute the volume on the overlay.";
+"SHARE_BUTTON" = "Share button";
+"SHARE_BUTTON_DESC" = "Displays a button to share the video link on the overlay.";
+"LOOP_BUTTON" = "Loop button";
+"LOOP_BUTTON_DESC" = "Displays a button to loop the video on the overlay.";
+"CAPTION_BUTTON" = "Captions button";
+"CAPTION_BUTTON_DESC" = "Displays a button to copy the video captions on the overlay.";
+"HOLD_TO_SPEED" = "Hold to speed";
+"HOLD_TO_SPEED_DESC" = "Uses custom hold to speed playback rate.";
"HIDE_AUTOPLAY" = "Hide autoplay switch";
"HIDE_AUTOPLAY_DESC" = "Hide autoplay switch in the video overlay.";
"HIDE_CAPTIONS_BUTTON" = "Hide captions button";
"HIDE_CAPTIONS_BUTTON_DESC" = "Hide captions button in the video overlay.";
"HIDE_CAST_BUTTON_PLAYER" = "Hide cast button";
"HIDE_CAST_BUTTON_PLAYER_DESC" = "Hide cast button in the video overlay.";
-"HIDE_PREV_BUTTON" = "Hide previous/back button";
-"HIDE_PREV_BUTTON_DESC" = "Hide previous/back button in the video overlay.";
-"HIDE_NEXT_BUTTON" = "Hide next/skip button";
-"HIDE_NEXT_BUTTON_DESC" = "Hide next/skip button in the video overlay.";
+"HIDE_NEXT_AND_PREV_BUTTON" = "Hide previous and next buttons";
+"HIDE_NEXT_AND_PREV_BUTTON_DESC" = "Hide previous and next buttons in the video overlay.";
"REPLACE_PREVNEXT_BUTTONS" = "Replace Previous/Next buttons";
"REPLACE_PREVNEXT_BUTTONS_DESC" = "Replaces the Previous and Next video buttons in the player with Rewind and Fast Forward buttons.";
+"SKIP_BACKWARD" = "Skip backward";
+"SKIP_BACKWARD_DESC" = "Replaces the previous track button in Control Center with a skip backward button by a set number of seconds.";
+"SKIP_FORWARD" = "Skip forward";
+"SKIP_FORWARD_DESC" = "Replaces the next track button in Control Center with a skip forward button by a set number of seconds.";
+"REWIND_SECONDS" = "Rewind seconds";
+"FORWARD_SECONDS" = "Fast-forward seconds";
"REMOVE_DARK_OVERLAY" = "Remove dark overlay";
"REMOVE_DARK_OVERLAY_DESC" = "Remove dark overlay from the video overlay.";
"HIDE_END_SCREEN" = "Hide endscreen cards";
@@ -102,6 +224,12 @@
"HIDE_PAID_OVERLAY_DESC" = "Hide 'Includes paid promotion' in the video overlay.";
"HIDE_WATERMARK" = "Hide watermark";
"HIDE_WATERMARK_DESC" = "Hide channel watermark at the bottom of the video overlay.";
+"PAUSE_ON_OVERLAY" = "Pause on overlay";
+"PAUSE_ON_OVERLAY_DESC" = "Sets playback on pause if overlay appears.";
+"DISABLES_SNAP_TO_CHAPTER" = "Disable snap to chapters";
+"DISABLES_SNAP_TO_CHAPTER_DESC" = "Prevents the scrubbar stick to the chapters.";
+"DISABLES_ENGAGE_PANEL" = "Disables engagement panel in fullscreen";
+"DISABLES_ENGAGE_PANEL_DESC" = "Prevent engagement panel showing in fullscreen.";
"GESTURES" = "Enable gestures controls";
"GESTURES_DESC" = "Swipe up or down on the left/right edges of the video screen to adjust brightness, volume, or playback speed.";
"GESTURE_AREA" = "Gesture activation area";
@@ -136,6 +264,8 @@
"DISABLES_SHOW_REMAINING_DESC" = "Disable the feature that shows how much time is left in a video when tapping the timestamp.";
"ALWAYS_SHOW_REMAINING" = "Always show remaining time";
"ALWAYS_SHOW_REMAINING_DESC" = "Ensure the remaining time is always displayed by default in the player overlay.";
+"COPY_TIMESTAMP_ON_PAUSE" = "Copy timestamped links";
+"COPY_TIMESTAMP_ON_PAUSE_DESC" = "Allows to copy timestamped link to the clipboard by pressing pause button.";
"SHOW_REMAINING_EXTRA" = "Show extra remaining time";
"SHOW_REMAINING_EXTRA_DESC" = "Adds the estimated finish time to the label in the video overlay.";
"HIDE_FULLSCREEN_ACTIONS" = "Hide fullscreen actions";
@@ -148,93 +278,95 @@
"HIDE_CONTENT_WARNING_DESC" = "Automatically skip the sensitive content warning alert in some vidoes.";
"AUTO_FULLSCREEN" = "Auto fullscreen";
"AUTO_FULLSCREEN_DESC" = "Automatically enter fullscreen mode when starting video playback.";
-"AUTO_DISABLES_CAPTION" = "Auto disable captions";
-"AUTO_DISABLES_CAPTION_DESC" = "Automatically turns off closed captions (CC) whenever a new video starts playing.";
"PORTRAIT_FULLSCREEN" = "Portrait fullscren mode";
"PORTRAIT_FULLSCREEN_DESC" = "Enables portrait fullscreen mode support.";
"OLD_QUALITY_PICKER" = "Use old quality picker";
"OLD_QUALITY_PICKER_DESC" = "Reverts the video quality selection menu to the classic version (lists all resolutions immediately).";
"EXTRA_SPEED" = "Extra speed options";
"EXTRA_SPEED_DESC" = "Adds more video playback speed options up to 10x.";
-"DISABLE_HINTS" = "Disable hints";
-"DISABLE_HINTS_DESC" = "Disable the tutorial hints and interactive pop-ups.";
"FORCE_MINIPLAYER" = "Force miniplayer";
"FORCE_MINIPLAYER_DESC" = "Allows all videos, including those made for kids, to play in the miniplayer when dismissing the player.";
"FORCE_SEEKBAR" = "Always show progress bar";
"FORCE_SEEKBAR_DESC" = "Makes the progress bar (seekbar) remains visible at all times while in fullscreen mode.";
-"HIDE_LIKE_BUTTON" = "Hide Like button";
-"HIDE_LIKE_BUTTON_DESC" = "Hide Like button in the action bar under the video overlay.";
-"HIDE_DISLIKE_BUTTON" = "Hide Dislike button";
-"HIDE_DISLIKE_BUTTON_DESC" = "Hide Dislike button in the action bar under the video overlay.";
-"HIDE_SHARE_BUTTON" = "Hide Share button";
-"HIDE_SHARE_BUTTON_DESC" = "Hide Share button in the action bar under the video overlay.";
-"HIDE_DOWNLOAD_BUTTON" = "Hide Download button";
-"HIDE_DOWNLOAD_BUTTON_DESC" = "Hide Download button in the action bar under the video overlay.";
-"HIDE_CLIP_BUTTON" = "Hide Clip button";
-"HIDE_CLIP_BUTTON_DESC" = "Hide Clip button in the action bar under the video overlay.";
-"HIDE_REMIX_BUTTON" = "Hide Remix button";
-"HIDE_REMIX_BUTTON_DESC" = "Hide Remix button in the action bar under the video overlay.";
-"HIDE_SAVE_BUTTON" = "Hide Save button";
-"HIDE_SAVE_BUTTON_DESC" = "Hide Save button in the action bar under the video overlay.";
+"TAP_TO_SEEK" = "Tap to seek";
+"TAP_TO_SEEK_DESC" = "Quickly jump to any point by tapping the progress bar.";
+"DISABLES_ZOOM" = "Disable free zoom gesture";
+"DISABLES_ZOOM_DESC" = "Disables new free zoom gestures.";
+"USES_24_HOURS_TIME" = "24-hour format";
+"USES_24_HOURS_TIME_DESC" = "Shows end time in 24-hour format.";
+"PAUSE_TWO_FINGERS" = "Pause using two fingers";
+"PAUSE_TWO_FINGERS_DESC" = "Pause/Resume playback by tapping on the screen using two fingers.";
+"HIDE_COMMENTS_SECTION" = "Hide comments section";
+"HIDE_COMMENTS_SECTION_DESC" = "Completely removes the entire comments section.";
+"HIDE_COMMENTS_PREVIEW" = "Hide preview comments";
+"HIDE_COMMENTS_PREVIEW_DESC" = "Removes the preview box for the top comment while keeping the full section expandable.";
+"USE_ANOTHER_MINIPLAYER" = "Uses another miniplayer style";
+"USE_ANOTHER_MINIPLAYER_DESC" = "Uses another miniplayer style redesigned from older YT versions.";
"SHORTS" = "Shorts";
-"HIDE_SHORTS_LIKE_BUTTON" = "Hide Like button";
-"HIDE_SHORTS_LIKE_BUTTON_DESC" = "Hide the Like button in the Shorts player.";
-"HIDE_SHORTS_DISLIKE_BUTTON" = "Hide Dislike button";
-"HIDE_SHORTS_DISLIKE_BUTTON_DESC" = "Hide the Dislike button in the Shorts player.";
-"HIDE_SHORTS_COMMENT_BUTTON" = "Hide Comment button";
-"HIDE_SHORTS_COMMENT_BUTTON_DESC" = "Hide the Comment button in the Shorts player.";
-"HIDE_SHORTS_SHARE_BUTTON" = "Hide Share button";
-"HIDE_SHORTS_SHARE_BUTTON_DESC" = "Hide the Share button in the Shorts player.";
-"HIDE_SHORTS_REMIX_BUTTON" = "Hide Remix button";
-"HIDE_SHORTS_REMIX_BUTTON_DESC" = "Hide the Remix button in the Shorts player.";
-"HIDE_METADATA_BUTTON" = "Hide sound metadata button";
-"HIDE_METADATA_BUTTON_DESC" = "Hide the button that shows the sound source or music metadata in the Shorts player.";
+"SHORTS_ONLY" = "Shorts only mode";
+"SHORTS_ONLY_DESC" = "Limits YouTube functionality to viewing Shorts only.";
+"SHORTS_ACTION" = "Shorts action after the video ends";
+"LOOP" = "Loop video";
+"SKIP_TO_NEXT_SHORTS" = "Skip to next video";
+"PAUSE_SHORTS" = "Stop video";
+"REMOVE_LIVE_SHORTS" = "Remove live videos";
+"REMOVE_LIVE_SHORTS_DESC" = "Removes live videos from the feed.";
"HIDE_SHORTS_PRODUCT" = "Hide products";
"HIDE_SHORTS_PRODUCT_DESC" = "Hide tagged products and shopping links in the Shorts overlay.";
"HIDE_SHORTS_RECBAR" = "Hide recommendation bar";
"HIDE_SHORTS_RECBAR_DESC" = "Hide the suggested action bar in the Shorts player (e.g. Try this sound, Shopping, Preview comment).";
-"HIDE_SHORTS_COMMIT" = "Hide commissions bar";
-"HIDE_SHORTS_COMMIT_DESC" = "Hide the bar that shows the video got commissions in the Shorts player.";
-"HIDE_SHORTS_SUBSCRIPT_BUTTON" = "Hide subscriptions button";
-"HIDE_SHORTS_SUBSCRIPT_BUTTON_DESC" = "Hide the Subscriptions button at the top of the Shorts player.";
-"HIDE_SHORTS_LIVE_BUTTON" = "Hide live button";
-"HIDE_SHORTS_LIVE_BUTTON_DESC" = "Hide the Live button at the top of the Shorts player.";
-"HIDE_SHORTS_LENS_BUTTON" = "Hide lens button";
-"HIDE_SHORTS_LENS_BUTTON_DESC" = "Hide the Lens button at the top of the Shorts player.";
-"HIDE_SHORTS_TRENDS_BUTTON" = "Hide trends button";
-"HIDE_SHORTS_TRENDS_BUTTON_DESC" = "Hide the Trends button at the top of the Shorts player.";
-"HIDE_SHORTS_TO_VIDEO" = "Hide link to full video";
-"HIDE_SHORTS_TO_VIDEO_DESC" = "Hide the link that redirects in a Short to a full video in the Shorts player.";
"ENABLES_SHORTS_QUALITY" = "Enable Shorts quality picker";
"ENABLES_SHORTS_QUALITY_DESC" = "Force enable the video quality selection menu in Shorts.";
"SHOW_SHORTS_SEEKBAR" = "Always show seekbar";
"SHOW_SHORTS_SEEKBAR_DESC" = "Ensure the progress/seek bar is always visible at the bottom of the Shorts player.";
+"REMOVE_POSTS_SHORTS" = "Remove posts";
+"REMOVE_POSTS_SHORTS_DESC" = "Remove posts from the feed.";
+"SHORTS_FULLSCREEN" = "Pinch to Fullscreen";
+"SHORTS_FULLSCREEN_DESC" = "Manages visibility of the overlay with pinch in and pinch out gestures, displaying Shorts in fullscreen mode.";
"TABBAR" = "Tab bar";
"DEFAULT_TAB" = "Default startup tab";
-"DEFAULT_TAB_DESC" = "You can select the tab you want to open when opening the app.";
-"HOME_NAME" = "Home";
-"SUBSCRIPT_NAME" = "Subscriptions";
-"LIB_NAME" = "Library (You)";
+"FORSTED_TAB_BAR" = "Translucent tab bar";
+"ENABLED" = "Enabled";
"HIDE_TAB_INDI" = "Hide tab indicators";
"HIDE_TAB_INDI_DESC" = "Hide all tab indicators in the tab bar.";
"HIDE_TAB_LABELS" = "Hide tab labels";
"HIDE_TAB_LABELS_DESC" = "Hide all tab labels in the tab bar.";
-"HIDE_HOME_TAB" = "Hide Home tab";
-"HIDE_HOME_TAB_DESC" = "Hide Home tab in the tab bar.";
-"HIDE_SHORTS_TAB" = "Hide Shorts tab";
-"HIDE_SHORTS_TAB_DESC" = "Hide Shorts tab in the tab bar.";
-"HIDE_CREATE_BUTTON" = "Hide Create button";
-"HIDE_CREATE_BUTTON_DESC" = "Hide Create button in the tab bar.";
-"HIDE_SUBSCRIPT_TAB" = "Hide Subscriptions tab";
-"HIDE_SUBSCRIPT_TAB_DESC" = "Hide Subscriptions tab in the tab bar.";
+"HOME_TAB" = "Home";
+"SHORTS_TAB" = "Shorts";
+"CREATE_TAB" = "Create";
+"SUBSCRIPTIONS_TAB" = "Subscriptions";
+"LIBRARY_TAB" = "Library (You)";
+"HISTORY_TAB" = "History";
+"GAMING_TAB" = "Gaming";
+"SPORTS_TAB" = "Sports";
+"NOTI_TAB" = "Notifications";
+"NEWS_TAB" = "News";
+"MUSIC_TAB" = "Music";
+"WATCH_LATER_TAB" = "Watch later";
+"PLAYLIST_TAB" = "Playlists";
+"LIKE_TAB" = "Liked videos";
+"LIVE_TAB" = "Live";
+"POST_TAB" = "Posts";
+"VIDEO_TAB" = "Your videos";
+"MOVIE_TAB" = "Movies";
+"COURSE_TAB" = "Courses";
+"MINIGAME_TAB" = "Playables";
+"MANAGE_TABS" = "Manage tabs";
+"MANAGE_TABS_DESC" = "Reorder and toggle tabs";
+"TAB_REORDER_HINT" = "Drag to reorder. Toggle to show/hide.\nTab settings are also accessible by long-pressing the first tab in the tab bar.";
+"TAB_LIMIT" = "Tab limit";
+"TAB_LIMIT_DESC" = "Only 6 tabs are allowed.";
+"ZERO_TAB_DESC" = "At least 1 tab has to be enabled.";
"MISCELLANEOUS" = "Miscellaneous";
"BACKGROUND_PLAYBACK" = "Background playback";
"BACKGROUND_PLAYBACK_DESC" = "Enables background playback for videos.";
"DISABLES_SHORTS_PIP" = "Disables Shorts PiP";
"DISABLES_SHORTS_PIP_DESC" = "Try to disables Shorts PiP, this might not 100% disables it.";
+"DISABLE_HINTS" = "Disable hints";
+"DISABLE_HINTS_DESC" = "Disable the tutorial hints and interactive pop-ups.";
"BLOCK_UPGRADE_DIALOGS" = "Block upgrade dialogs";
"BLOCK_UPGRADE_DIALOGS_DESC" = "Block the YouTube dialogs that ask you to upgrade the app.";
"ARE_YOU_THERE_DIALOG" = "Hide 'Are you there?' dialog";
@@ -247,7 +379,109 @@
"DISABLES_SNACK_BAR_DESC" = "Disables snack bar when pressing some buttons or doing something in the app.";
"HIDE_STARTUP_ANIMATIONS" = "Hide YouTube's startup animations";
"HIDE_STARTUP_ANIMATIONS_DESC" = "Hide YouTube's startup animations when opening the app.";
-"HIDE_PLAY_IN_NEXT_QUEUE" = "Hide 'Play next in queue'";
-"HIDE_PLAY_IN_NEXT_QUEUE_DESC" = "Hide the 'Play next in queue' option in the flyout menu.";
"HIDE_LIKE_DISLIKE_VOTES" = "Hide like/dislike votes";
-"HIDE_LIKE_DISLIKE_VOTES_DESC" = "Hide messages that pop up when you like/dislike videos.";
\ No newline at end of file
+"HIDE_LIKE_DISLIKE_VOTES_DESC" = "Hide messages that pop up when you like/dislike videos.";
+"FLYOUT_MENU" = "Flyout menu";
+"REMOVE_PLAY_IN_NEXT_QUEUE_OPTION" = "Removes 'Play next in queue' option";
+"REMOVE_PLAY_IN_NEXT_QUEUE_OPTION_DESC" = "Removes 'Play next in queue' option from the flyout menu.";
+"REMOVE_DOWNLOAD_OPTION" = "Removes 'Download video' option";
+"REMOVE_DOWNLOAD_OPTION_DESC" = "Removes 'Download video' option from the flyout menu.";
+"REMOVE_WATCH_LATER_OPTION" = "Removes 'Save to Watch later' option";
+"REMOVE_WATCH_LATER_OPTION_DESC" = "Removes 'Save to Watch Later' option from the flyout menu.";
+"REMOVE_SAVE_OPTION" = "Removes 'Save to playlist' option";
+"REMOVE_SAVE_OPTION_DESC" = "Removes 'Save to playlist' option from the flyout menu.";
+"REMOVE_REMOVE_FROM_PLAYLIST_OPTION" = "Removes 'Remove from playlist' option";
+"REMOVE_REMOVE_FROM_PLAYLIST_OPTION_DESC" = "Removes 'Remove from playlist' option from the flyout menu.";
+"REMOVE_SHARE_OPTION" = "Removes 'Share' option";
+"REMOVE_SHARE_OPTION_DESC" = "Removes 'Share' option from the flyout menu.";
+"REMOVE_NOT_INTERESTED_OPTION" = "Removes 'Not interested' option";
+"REMOVE_NOT_INTERESTED_OPTION_DESC" = "Removes 'Not interested' option from the flyout menu.";
+"REMOVE_DONT_RECOMMEND_OPTION" = "Removes 'Don't recommend channel' option";
+"REMOVE_DONT_RECOMMEND_OPTION_DESC" = "Removes 'Don't recommend channel' option from the flyout menu.";
+"REMOVE_INFO_OPTION" = "Removes 'Why are you seeing this?' option";
+"REMOVE_INFO_OPTION_DESC" = "Removes 'Why are you seeing this?' option from the flyout menu.";
+"REMOVE_FILTER_OPTION" = "Removes 'Search filters' option";
+"REMOVE_FILTER_OPTION_DESC" = "Removes 'Search filters' option from the flyout menu.";
+"REMOVE_REPORT_OPTION" = "Removes 'Report' option";
+"REMOVE_REPORT_OPTION_DESC" = "Removes 'Report' option from the flyout menu.";
+"REMOVE_YOUTUBE_MUSIC_OPTION" = "Removes 'Listen with YouTube Music' option";
+"REMOVE_YOUTUBE_MUSIC_OPTION_DESC" = "Removes 'Listen with YouTube Music' option from the flyout menu.";
+"REMOVE_FEED_BACK_OPTION" = "Removes 'Send feedback' option";
+"REMOVE_FEED_BACK_OPTION_DESC" = "Removes 'Send feedback' option from the flyout menu.";
+"REMOVE_CAST_OPTION" = "Removes 'Watch on TV' option";
+"REMOVE_CAST_OPTION_DESC" = "Removes 'Watch on TV' option from the flyout menu.";
+"REMOVE_SHUFFLE_OPTION" = "Removes 'Shuffle' option";
+"REMOVE_SHUFFLE_OPTION_DESC" = "Removes 'Shuffle' option from the flyout menu.";
+"REMOVE_UN_SUB_OPTION" = "Removes 'Unsubscribe' option";
+"REMOVE_UN_SUB_OPTION_DESC" = "Removes 'Unsubscribe' option from the flyout menu.";
+"REMOVE_HIDE_FROM_PLAYLIST_OPTION" = "Removes 'Hide video from playlist' option";
+"REMOVE_HIDE_FROM_PLAYLIST_OPTION_DESC" = "Removes 'Hide video from playlist' option from the flyout menu.";
+"REMOVE_HELP_OPTION" = "Removes 'Help & feedback' option";
+"REMOVE_HELP_OPTION_DESC" = "Removes 'Help & feedback' option from the flyout menu.";
+"REMOVE_NOTIFY_OPTION" = "Removes 'Notify me' option";
+"REMOVE_NOTIFY_OPTION_DESC" = "Removes 'Notify me' option from the flyout menu.";
+"REMOVE_CLEARSCREEN_OPTION" = "Removes 'Clear Screen' option";
+"REMOVE_CLEARSCREEN_OPTION_DESC" = "Removes 'Clear Screen' option from the flyout menu.";
+"HIDE_COMMU_GUIDE" = "Hide community guidelines";
+"HIDE_COMMU_GUIDE_DESC" = "Hide community guidelines header.";
+"DISABLES_RTL" = "Disable RTL formatting";
+"DISABLES_RTL_DESC" = "Forcefully displays text in left-to-right (LTR) format for languages that are initially displayed in right-to-left (RTL).";
+"DEVICE_UI" = "Interface style";
+
+"SB_DESCRIPTION" = "Skip sponsored segments automatically using community-submitted data.";
+"SB_ENABLE" = "Enable SponsorBlock";
+"SB_ENABLE_DESC" = "Activate the SponsorBlock extension to skip sponsored segments.";
+"SB_SHOW_BUTTON" = "Show overlay button";
+"SB_SHOW_BUTTON_DESC" = "Display a SponsorBlock toggle button in the player overlay.";
+"SB_SHOW_NOTIFICATIONS" = "Show notifications";
+"SB_SHOW_NOTIFICATIONS_DESC" = "Display notifications when segments are skipped. Notifications for manual-skip categories are always shown.";
+"SB_HAPTIC_FEEDBACK" = "Haptic feedback";
+"SB_HAPTIC_FEEDBACK_DESC" = "Provide haptic feedback when a segment is skipped.";
+"SB_SEGMENTS_IN_PLAYER" = "Segments in player";
+"SB_SEGMENTS_IN_PLAYER_DESC" = "Display colored segments on the progress bar of the main video player.";
+"SB_SEGMENTS_IN_FEED" = "Segments in feed";
+"SB_SEGMENTS_IN_FEED_DESC" = "Display colored segments on the progress bar of players in the feed.";
+"SB_SEGMENTS_IN_MINIPLAYER" = "Segments in mini-player";
+"SB_SEGMENTS_IN_MINIPLAYER_DESC" = "Display colored segments on the progress bar of the mini-player.";
+"SB_SHOW_DURATION" = "Show duration without segments";
+"SB_SHOW_DURATION_DESC" = "Display the total video duration excluding skippable segments.";
+"SB_CATEGORIES_HEADER" = "Segment categories";
+"SB_SEGMENT_COLOR" = "Segment color";
+
+"SB_ACTION_DISABLE" = "Disable";
+"SB_ACTION_AUTO_SKIP" = "Auto-skip";
+"SB_ACTION_ASK" = "Ask before skipping";
+"SB_ACTION_DISPLAY" = "Show on seek bar only";
+"SB_ACTION_SKIP_TO" = "Skip to segment";
+
+"SB_CAT_sponsor" = "Sponsor";
+"SB_CAT_sponsor_DESC" = "Paid promotion for a product or service not directly related to the creator.";
+"SB_CAT_intro" = "Intro";
+"SB_CAT_intro_DESC" = "Intermission or intro animation typically seen at the start of a video.";
+"SB_CAT_outro" = "Endcards";
+"SB_CAT_outro_DESC" = "Endcards, credits, or outros at the end of the video.";
+"SB_CAT_interaction" = "Interaction";
+"SB_CAT_interaction_DESC" = "Reminders to like, subscribe, or follow on social media.";
+"SB_CAT_selfpromo" = "Self-promotion";
+"SB_CAT_selfpromo_DESC" = "Unpaid promotion of the creator's own products, merchandise, or other content.";
+"SB_CAT_music_offtopic" = "Non-music";
+"SB_CAT_music_offtopic_DESC" = "Non-music sections in music videos (e.g. talking, introductions).";
+"SB_CAT_preview" = "Preview";
+"SB_CAT_preview_DESC" = "Recap or preview of content from another video or previous episode.";
+"SB_CAT_hook" = "Hook/Greetings";
+"SB_CAT_hook_DESC" = "Narrated trailers for the upcoming video, greetings and goodbyes.";
+"SB_CAT_poi_highlight" = "Highlight";
+"SB_CAT_poi_highlight_DESC" = "The key moment or highlight point of the video. Use 'Skip to' to jump directly to it.";
+"SB_CAT_filler" = "Filler";
+"SB_CAT_filler_DESC" = "Tangential content, jokes, or filler that is not directly related to the main topic.";
+
+"SB_SKIPPED" = "%@ segment has been skipped";
+"SB_DETECTED" = "%@ segment detected.\nWould you like to skip the segment?";
+"SB_SKIP_NOW" = "Skip";
+"SB_UNSKIP" = "Unskip";
+"SB_JUMPED_TO_HIGHLIGHT" = "Jumped to highlight";
+"SB_JUMP_TO_HIGHLIGHT" = "Highlight available. Jump to the point?";
+"SB_SEGMENT_COLOR_SUFFIX" = "Segment color";
+"SB_SECTION_MAIN" = "Main";
+"SB_SKIP_ALERT_DURATION" = "Skip alert duration";
+"SB_UNSKIP_ALERT_DURATION" = "Unskip alert duration";
\ No newline at end of file
diff --git a/layout/Library/Application Support/YouMod.bundle/cs.lproj/Localizable.strings b/layout/Library/Application Support/YouMod.bundle/cs.lproj/Localizable.strings
index bccaf550..2c1a8a0f 100644
--- a/layout/Library/Application Support/YouMod.bundle/cs.lproj/Localizable.strings
+++ b/layout/Library/Application Support/YouMod.bundle/cs.lproj/Localizable.strings
@@ -1,11 +1,18 @@
-"NOTE" = "NOTE: Some hiding features may leave a blank box.";
"SETTINGS" = "Settings";
+"INTERFACE" = "Interface";
+
+"LOOP_DISABLED" = "Loop disabled";
+"LOOP_ENABLED" = "Loop enabled";
"NEW_ISSUES" = "Report issues or request new features";
"NEW_ISSUES_DESC" = "Found bugs or want to request new features? Tap here.";
"SOURCE_CODES" = "Source codes";
"SOURCE_CODES_DESC" = "Take a look at the codes!";
+"EXTRA" = "Extra";
+"FIX_PLAYBACK_ISSUES" = "Fix playback issues (Temporary)";
+"FIX_PLAYBACK_ISSUES_DESC" = "Try to fix video playback issues for some users. Turning this on will make the player automatically reloads itself everytime it errors or fails to play the video.";
+
"PERFER_HEADER" = "YouMod perferences";
"PERFER" = "Perferences";
"IMPORT" = "Import settings";
@@ -24,6 +31,7 @@
"ERROR_NO_KEYS_IMPORT" = "No available keys to import";
"ERROR_NO_KEYS_EXPORT" = "No available keys to export";
"OVERRIDE" = "This will override the current settings. Do you want to continue?";
+"DISABLED" = "Disabled";
"CACHE" = "Cache management";
"CLEARCACHE" = "Clear cache";
@@ -32,12 +40,80 @@
"AUTO_CLEARCACHE_DESC" = "Automatically clears the app cache on startup.";
"DOWNLOADING" = "Downloading";
-"DOWNLOAD_MANAGER" = "Download Manager";
-"DOWNLOAD_MANAGER_DESC" = "Replaces YouTube's video downloader with YouMod one. With ablity to downloading videos, audios, captions, and thumbnails";
+"DOWNLOAD_MANAGER" = "Download manager";
+"DOWNLOAD_MANAGER_DESC" = "Replaces YouTube's video downloader with YouMod one. With ablity to downloading videos, audios, captions, and thumbnails.";
"DOWNLOAD_SAVE_PHOTOS" = "Automatically save to Photos";
"DOWNLOAD_SAVE_PHOTOS_DESC" = "Automatically saves downloaded videos and images directly to your Photos app.";
-"DOWNLOAD_DRC_AUDIO" = "Prefer DRC Audio";
-"DOWNLOAD_DRC_AUDIO_DESC" = "Prioritizes downloading audio tracks with Dynamic Range Compression for more consistent volume levels.";
+"ADD_SHORTS_DOWNLOAD" = "Adds a download button to Shorts";
+"ADD_SHORTS_DOWNLOAD_DESC" = "Adds a download button to Shorts video overlay.";
+"USE_ORIG_AUDIO" = "Automatically uses original audio track";
+"USE_ORIG_AUDIO_DESC" = "Automatically uses original audio track instead of showing a dialog.";
+"DOWNLOAD_SHORTS" = "Download Shorts";
+"DOWNLOAD_SHORTS_DESC" = "Choose Shorts quality";
+"COPY_VID_INFO" = "Copy video informations";
+"COPY_VID_INFO_DESC" = "Choose what to copy";
+"COPY_ALL_VID_INFO" = "Copy all";
+"COPY_ALL_VID_INFO_DESC" = "Copy all video informations";
+"COPY_AUTHOR" = "Copy author";
+"COPY_AUTHOR_DESC" = "Copy the video author";
+"COPY_TITLE" = "Copy title";
+"COPY_TITLE_DESC" = "Copy the video title";
+"COPY_DESCRIPTION" = "Copy description";
+"COPY_DESCRIPTION_DESC" = "Copy the video description";
+"COPY_URL" = "Copy video link";
+"COPY_URL_TIMESTAMP" = "Copy video link with timestamp";
+"URL_COPIED" = "Copied video link";
+"URL_TIMESTAMP_COPIED" = "Copied video link with timestamp";
+"ERROR_VIDEOID" = "Cannot get the video ID";
+"ERROR_ADS" = "Cannot copy video link while playing ads";
+"COPIED_AUTHOR" = "Copied video author";
+"COPIED_TITLE" = "Copied video title";
+"COPIED_DESCRIPTION" = "Copied video description";
+"SAVE_THUMBNAIL" = "Save thumbnail";
+"SAVE_THUMBNAIL_DESC" = "Save to Photos";
+"DOWNLOAD_CAPTIONS" = "Download captions";
+"DOWNLOAD_CAPTIONS_DESC" = "Save subtitles as .VTT file";
+"DOWNLOAD_AUDIO" = "Download audio";
+"DOWNLOAD_AUDIO_DESC" = "Download audio as .m4a file";
+"DOWNLOAD_VIDEO" = "Download video";
+"DOWNLOAD_VIDEO_DESC" = "Choose video quality";
+"OPEN_VID_BEFORE" = "Open a video before using the download manager";
+"NO_CAPTIONS_URL" = "No valid caption URLs found";
+"NO_AUDIO_URL" = "No audio URL found";
+"NO_STREAM_URL" = "No stream URL found";
+"CAPTIONS_FAILED" = "Failed to download captions";
+"NO_CAPTIONS" = "No captions available for this video";
+"NO_AUDIO_STREAM_FOUND" = "No audio streams found";
+"NO_VID_AUDIO_STREAM_FOUND" = "No video/audio streams found";
+"COPIED_VID_INFO" = "Copied all video informations";
+"SAVED_TO_PHOTOS" = "Saved to Photos";
+"SAVE_FAILED" = "Save failed";
+"PHOTO_ACCESS_DENINED" = "Photos access denied";
+"THUMBNAIL_FAILED" = "Thumbnail failed";
+"DOWNLOADING_THUMBNAIL" = "Downloading thumbnail...";
+"DOWNLOADING_AUDIO" = "Downloading audio...";
+"DOWNLOADING_VIDEO" = "Downloading video...";
+"DOWNLOADING_CAPTIONS" = "Downloading captions...";
+"NO_THUMBNAIL_FOUND" = "No thumbnail found";
+"DOWNLOAD_FAILED" = "Download failed";
+"COPY_THUMBNAIL" = "Copy thumbnail";
+"COPY_THUMBNAIL_DESC" = "Copy to clipboard";
+"SHOW_THUMBNAIL" = "Show thumbnail";
+"SHOW_THUMBNAIL_DESC" = "Show in a dialog";
+"THUMBNAIL_OPTIONS" = "Download thumbnail";
+"THUMBNAIL_OPTIONS_DESC" = "Show more options";
+"COPIED_TO_CLIPBOARD" = "Copied to clipboard";
+"DOWNLOAD_COMPLETED" = "Download completed";
+"CANNOT_SAVE_TO_PHOTOS" = "Cannot save to Photos";
+"FINA_VIDEO" = "Finalizing video...";
+"MERGING_VID_FALLBACK" = "Merging video with fallback...";
+"MERGING_VID" = "Merging video...";
+"CONVERTING" = "Converting to";
+"ALREADY_DOWNLOADING" = "Already downloading";
+"DOWNLOAD_CANCELLED" = "Download cancelled";
+"CANCEL" = "Cancel";
+"TRIMMING_AUDIO" = "Trimming audio...";
+"COPY" = "Copy";
"APPEARANCE" = "Appearance";
"OLED_THEME" = "OLED dark mode";
@@ -46,6 +122,8 @@
"OLED_KEYBOARD_DESC" = "Make the keyboard uses a pure black (OLED) color for dark theme.";
"NAVBAR" = "Navigation bar";
+"STICKY_NAVBAR" = "Sticky Navigation bar";
+"STICKY_NAVBAR_DESC" = "Pins the Navigation bar so that it remains visible when scrolling down.";
"HIDE_YT_LOGO" = "Hide YouTube logo";
"HIDE_YT_LOGO_DESC" = "Hide YouTube logo in the navigation bar.";
"PREMIUM_LOGO" = "Use YouTube Premium logo";
@@ -62,34 +140,78 @@
"FEED" = "Feed";
"HIDE_SUBBAR" = "Hide subbar";
"HIDE_SUBBAR_DESC" = "Hide subbar at the top of the feed.";
+"HIDE_HORI_SHELF" = "Remove horizontal feeds";
+"HIDE_HORI_SHELF_DESC" = "Removes all horizontal feeds (News, Continue Watching, Posts collection, etc.) from the feed, except Playables.";
"HIDE_MUSIC_PLAYLISTS" = "Hide music playlist generator";
"HIDE_MUSIC_PLAYLISTS_DESC" = "Hide music playlist generator (Select music tunes in three) in the feed.";
"HIDE_FEED_POST" = "Hide posts";
"HIDE_FEED_POST_DESC" = "Hide posts in feed.";
+"HIDE_SURVEYS" = "Hide surveys";
+"HIDE_SURVEYS_DESC" = "Hide video surveys that YouTube shows sometimes.";
"HIDE_SHORTS_SHELF" = "Hide Shorts shelf";
"HIDE_SHORTS_SHELF_DESC" = "Hide Shorts shelf in the feed, including in the Subscriptions tab.";
+"KEEP_SHORTS_SUBSCRIPT" = "Keep Shorts in Subscriptions";
+"KEEP_SHORTS_SUBSCRIPT_DESC" = "Prevents removing Shorts from Subscriptions tab.";
"HIDE_SEARCH_HISTORY" = "Hide search history";
"HIDE_SEARCH_HISTORY_DESC" = "Hide previous search history and suggestions when using the search bar.\nNOTE: Your search history will still be accessible in your other YouTube clients.";
-"HIDE_SUB_BUTTON" = "Hide Subscribe button";
-"HIDE_SUB_BUTTON_DESC" = "Hides the subscribe button in channels and video descriptions.";
-"HIDE_SHOP_BUTTON" = "Hide Shopping button";
-"HIDE_SHOP_BUTTON_DESC" = "Hides the shopping/store button in channels descriptions.";
-"HIDE_MEMBER_BUTTON" = "Hide Membership button";
-"HIDE_MEMBER_BUTTON_DESC" = "Hides the 'Join' button in channels and video descriptions.";
+"HIDE_PLAYABLES" = "Remove playables";
+"HIDE_PLAYABLES_DESC" = "Removes playables (mini-games) from the feed.";
"PLAYER" = "Player";
+"PLAYBACK_SPEED" = "Playback speed";
+"CONTROL_CENTER" = "Control Center";
+"PLAYER_ACTIONS" = "Player actions";
+"GESTURE_HEADER" = "Gestures";
+"QUALITY_WIFI" = "Playback quality on WiFi";
+"QUALITY_WIFI_DESC" = "Automatically applies the selected quality when streaming over Wi-Fi.";
+"QUALITY_CELLULAR" = "Playback quality on Cellular";
+"QUALITY_CELLULAR_DESC" = "Automatically applies the selected quality when streaming over mobile data.";
+"QUALITY_LOW_POWER" = "Playback quality on Power saver mode";
+"QUALITY_LOW_POWER_DESC" = "Automatically applies the selected quality when when Low power mode is enabled on your device.";
+"DEFAULT" = "Default";
+"BEST" = "Best";
+"AUDIO_TRACK" = "Preferred audio track";
+"ORIGINAL" = "Original";
+"SELECT_MANUALLY" = "Select manually";
+"AUDIO_TRACK_SELECT" = "Selected audio track";
+"AUDIO_TRACK_SELECT_DESC" = "Specifies the preferred audio track language to use for playback, if available.";
+"NO_AUTO_DUBBED" = "Uses original audio track";
+"NO_AUTO_DUBBED_DESC" = "Uses original audio track if the preferred language track is auto-dubbed or not found.";
+"CAPTION_TRACK" = "Preferred captions";
+"CAPTION_TRACK_SELECT" = "Selected captions";
+"CAPTION_TRACK_SELECT_DESC" = "Specifies the preferred captions language to display, if available.";
+"DISABLES_CAPTION_TRACK" = "Automatically disable captions";
+"DISABLES_CAPTION_TRACK_DESC" = "Automatically disable captions if the preferred language is auto-generated or not found.";
+"DEFAULT_SPEED" = "Default playback rate";
+"DEFAULT_SPEED_DESC" = "Overrides the default playback speed.";
+"SPEED_BUTTON" = "Speed controls button";
+"SPEED_BUTTON_DESC" = "Displays a speed controls button on overlay.";
+"MUTE_BUTTON" = "Mute button";
+"MUTE_BUTTON_DESC" = "Displays a button to mute or unmute the volume on the overlay.";
+"SHARE_BUTTON" = "Share button";
+"SHARE_BUTTON_DESC" = "Displays a button to share the video link on the overlay.";
+"LOOP_BUTTON" = "Loop button";
+"LOOP_BUTTON_DESC" = "Displays a button to loop the video on the overlay.";
+"CAPTION_BUTTON" = "Captions button";
+"CAPTION_BUTTON_DESC" = "Displays a button to copy the video captions on the overlay.";
+"HOLD_TO_SPEED" = "Hold to speed";
+"HOLD_TO_SPEED_DESC" = "Uses custom hold to speed playback rate.";
"HIDE_AUTOPLAY" = "Hide autoplay switch";
"HIDE_AUTOPLAY_DESC" = "Hide autoplay switch in the video overlay.";
"HIDE_CAPTIONS_BUTTON" = "Hide captions button";
"HIDE_CAPTIONS_BUTTON_DESC" = "Hide captions button in the video overlay.";
"HIDE_CAST_BUTTON_PLAYER" = "Hide cast button";
"HIDE_CAST_BUTTON_PLAYER_DESC" = "Hide cast button in the video overlay.";
-"HIDE_PREV_BUTTON" = "Hide previous/back button";
-"HIDE_PREV_BUTTON_DESC" = "Hide previous/back button in the video overlay.";
-"HIDE_NEXT_BUTTON" = "Hide next/skip button";
-"HIDE_NEXT_BUTTON_DESC" = "Hide next/skip button in the video overlay.";
+"HIDE_NEXT_AND_PREV_BUTTON" = "Hide previous and next buttons";
+"HIDE_NEXT_AND_PREV_BUTTON_DESC" = "Hide previous and next buttons in the video overlay.";
"REPLACE_PREVNEXT_BUTTONS" = "Replace Previous/Next buttons";
"REPLACE_PREVNEXT_BUTTONS_DESC" = "Replaces the Previous and Next video buttons in the player with Rewind and Fast Forward buttons.";
+"SKIP_BACKWARD" = "Skip backward";
+"SKIP_BACKWARD_DESC" = "Replaces the previous track button in Control Center with a skip backward button by a set number of seconds.";
+"SKIP_FORWARD" = "Skip forward";
+"SKIP_FORWARD_DESC" = "Replaces the next track button in Control Center with a skip forward button by a set number of seconds.";
+"REWIND_SECONDS" = "Rewind seconds";
+"FORWARD_SECONDS" = "Fast-forward seconds";
"REMOVE_DARK_OVERLAY" = "Remove dark overlay";
"REMOVE_DARK_OVERLAY_DESC" = "Remove dark overlay from the video overlay.";
"HIDE_END_SCREEN" = "Hide endscreen cards";
@@ -102,6 +224,12 @@
"HIDE_PAID_OVERLAY_DESC" = "Hide 'Includes paid promotion' in the video overlay.";
"HIDE_WATERMARK" = "Hide watermark";
"HIDE_WATERMARK_DESC" = "Hide channel watermark at the bottom of the video overlay.";
+"PAUSE_ON_OVERLAY" = "Pause on overlay";
+"PAUSE_ON_OVERLAY_DESC" = "Sets playback on pause if overlay appears.";
+"DISABLES_SNAP_TO_CHAPTER" = "Disable snap to chapters";
+"DISABLES_SNAP_TO_CHAPTER_DESC" = "Prevents the scrubbar stick to the chapters.";
+"DISABLES_ENGAGE_PANEL" = "Disables engagement panel in fullscreen";
+"DISABLES_ENGAGE_PANEL_DESC" = "Prevent engagement panel showing in fullscreen.";
"GESTURES" = "Enable gestures controls";
"GESTURES_DESC" = "Swipe up or down on the left/right edges of the video screen to adjust brightness, volume, or playback speed.";
"GESTURE_AREA" = "Gesture activation area";
@@ -136,6 +264,8 @@
"DISABLES_SHOW_REMAINING_DESC" = "Disable the feature that shows how much time is left in a video when tapping the timestamp.";
"ALWAYS_SHOW_REMAINING" = "Always show remaining time";
"ALWAYS_SHOW_REMAINING_DESC" = "Ensure the remaining time is always displayed by default in the player overlay.";
+"COPY_TIMESTAMP_ON_PAUSE" = "Copy timestamped links";
+"COPY_TIMESTAMP_ON_PAUSE_DESC" = "Allows to copy timestamped link to the clipboard by pressing pause button.";
"SHOW_REMAINING_EXTRA" = "Show extra remaining time";
"SHOW_REMAINING_EXTRA_DESC" = "Adds the estimated finish time to the label in the video overlay.";
"HIDE_FULLSCREEN_ACTIONS" = "Hide fullscreen actions";
@@ -148,93 +278,95 @@
"HIDE_CONTENT_WARNING_DESC" = "Automatically skip the sensitive content warning alert in some vidoes.";
"AUTO_FULLSCREEN" = "Auto fullscreen";
"AUTO_FULLSCREEN_DESC" = "Automatically enter fullscreen mode when starting video playback.";
-"AUTO_DISABLES_CAPTION" = "Auto disable captions";
-"AUTO_DISABLES_CAPTION_DESC" = "Automatically turns off closed captions (CC) whenever a new video starts playing.";
"PORTRAIT_FULLSCREEN" = "Portrait fullscren mode";
"PORTRAIT_FULLSCREEN_DESC" = "Enables portrait fullscreen mode support.";
"OLD_QUALITY_PICKER" = "Use old quality picker";
"OLD_QUALITY_PICKER_DESC" = "Reverts the video quality selection menu to the classic version (lists all resolutions immediately).";
"EXTRA_SPEED" = "Extra speed options";
"EXTRA_SPEED_DESC" = "Adds more video playback speed options up to 10x.";
-"DISABLE_HINTS" = "Disable hints";
-"DISABLE_HINTS_DESC" = "Disable the tutorial hints and interactive pop-ups.";
"FORCE_MINIPLAYER" = "Force miniplayer";
"FORCE_MINIPLAYER_DESC" = "Allows all videos, including those made for kids, to play in the miniplayer when dismissing the player.";
"FORCE_SEEKBAR" = "Always show progress bar";
"FORCE_SEEKBAR_DESC" = "Makes the progress bar (seekbar) remains visible at all times while in fullscreen mode.";
-"HIDE_LIKE_BUTTON" = "Hide Like button";
-"HIDE_LIKE_BUTTON_DESC" = "Hide Like button in the action bar under the video overlay.";
-"HIDE_DISLIKE_BUTTON" = "Hide Dislike button";
-"HIDE_DISLIKE_BUTTON_DESC" = "Hide Dislike button in the action bar under the video overlay.";
-"HIDE_SHARE_BUTTON" = "Hide Share button";
-"HIDE_SHARE_BUTTON_DESC" = "Hide Share button in the action bar under the video overlay.";
-"HIDE_DOWNLOAD_BUTTON" = "Hide Download button";
-"HIDE_DOWNLOAD_BUTTON_DESC" = "Hide Download button in the action bar under the video overlay.";
-"HIDE_CLIP_BUTTON" = "Hide Clip button";
-"HIDE_CLIP_BUTTON_DESC" = "Hide Clip button in the action bar under the video overlay.";
-"HIDE_REMIX_BUTTON" = "Hide Remix button";
-"HIDE_REMIX_BUTTON_DESC" = "Hide Remix button in the action bar under the video overlay.";
-"HIDE_SAVE_BUTTON" = "Hide Save button";
-"HIDE_SAVE_BUTTON_DESC" = "Hide Save button in the action bar under the video overlay.";
+"TAP_TO_SEEK" = "Tap to seek";
+"TAP_TO_SEEK_DESC" = "Quickly jump to any point by tapping the progress bar.";
+"DISABLES_ZOOM" = "Disable free zoom gesture";
+"DISABLES_ZOOM_DESC" = "Disables new free zoom gestures.";
+"USES_24_HOURS_TIME" = "24-hour format";
+"USES_24_HOURS_TIME_DESC" = "Shows end time in 24-hour format.";
+"PAUSE_TWO_FINGERS" = "Pause using two fingers";
+"PAUSE_TWO_FINGERS_DESC" = "Pause/Resume playback by tapping on the screen using two fingers.";
+"HIDE_COMMENTS_SECTION" = "Hide comments section";
+"HIDE_COMMENTS_SECTION_DESC" = "Completely removes the entire comments section.";
+"HIDE_COMMENTS_PREVIEW" = "Hide preview comments";
+"HIDE_COMMENTS_PREVIEW_DESC" = "Removes the preview box for the top comment while keeping the full section expandable.";
+"USE_ANOTHER_MINIPLAYER" = "Uses another miniplayer style";
+"USE_ANOTHER_MINIPLAYER_DESC" = "Uses another miniplayer style redesigned from older YT versions.";
"SHORTS" = "Shorts";
-"HIDE_SHORTS_LIKE_BUTTON" = "Hide Like button";
-"HIDE_SHORTS_LIKE_BUTTON_DESC" = "Hide the Like button in the Shorts player.";
-"HIDE_SHORTS_DISLIKE_BUTTON" = "Hide Dislike button";
-"HIDE_SHORTS_DISLIKE_BUTTON_DESC" = "Hide the Dislike button in the Shorts player.";
-"HIDE_SHORTS_COMMENT_BUTTON" = "Hide Comment button";
-"HIDE_SHORTS_COMMENT_BUTTON_DESC" = "Hide the Comment button in the Shorts player.";
-"HIDE_SHORTS_SHARE_BUTTON" = "Hide Share button";
-"HIDE_SHORTS_SHARE_BUTTON_DESC" = "Hide the Share button in the Shorts player.";
-"HIDE_SHORTS_REMIX_BUTTON" = "Hide Remix button";
-"HIDE_SHORTS_REMIX_BUTTON_DESC" = "Hide the Remix button in the Shorts player.";
-"HIDE_METADATA_BUTTON" = "Hide sound metadata button";
-"HIDE_METADATA_BUTTON_DESC" = "Hide the button that shows the sound source or music metadata in the Shorts player.";
+"SHORTS_ONLY" = "Shorts only mode";
+"SHORTS_ONLY_DESC" = "Limits YouTube functionality to viewing Shorts only.";
+"SHORTS_ACTION" = "Shorts action after the video ends";
+"LOOP" = "Loop video";
+"SKIP_TO_NEXT_SHORTS" = "Skip to next video";
+"PAUSE_SHORTS" = "Stop video";
+"REMOVE_LIVE_SHORTS" = "Remove live videos";
+"REMOVE_LIVE_SHORTS_DESC" = "Removes live videos from the feed.";
"HIDE_SHORTS_PRODUCT" = "Hide products";
"HIDE_SHORTS_PRODUCT_DESC" = "Hide tagged products and shopping links in the Shorts overlay.";
"HIDE_SHORTS_RECBAR" = "Hide recommendation bar";
"HIDE_SHORTS_RECBAR_DESC" = "Hide the suggested action bar in the Shorts player (e.g. Try this sound, Shopping, Preview comment).";
-"HIDE_SHORTS_COMMIT" = "Hide commissions bar";
-"HIDE_SHORTS_COMMIT_DESC" = "Hide the bar that shows the video got commissions in the Shorts player.";
-"HIDE_SHORTS_SUBSCRIPT_BUTTON" = "Hide subscriptions button";
-"HIDE_SHORTS_SUBSCRIPT_BUTTON_DESC" = "Hide the Subscriptions button at the top of the Shorts player.";
-"HIDE_SHORTS_LIVE_BUTTON" = "Hide live button";
-"HIDE_SHORTS_LIVE_BUTTON_DESC" = "Hide the Live button at the top of the Shorts player.";
-"HIDE_SHORTS_LENS_BUTTON" = "Hide lens button";
-"HIDE_SHORTS_LENS_BUTTON_DESC" = "Hide the Lens button at the top of the Shorts player.";
-"HIDE_SHORTS_TRENDS_BUTTON" = "Hide trends button";
-"HIDE_SHORTS_TRENDS_BUTTON_DESC" = "Hide the Trends button at the top of the Shorts player.";
-"HIDE_SHORTS_TO_VIDEO" = "Hide link to full video";
-"HIDE_SHORTS_TO_VIDEO_DESC" = "Hide the link that redirects in a Short to a full video in the Shorts player.";
"ENABLES_SHORTS_QUALITY" = "Enable Shorts quality picker";
"ENABLES_SHORTS_QUALITY_DESC" = "Force enable the video quality selection menu in Shorts.";
"SHOW_SHORTS_SEEKBAR" = "Always show seekbar";
"SHOW_SHORTS_SEEKBAR_DESC" = "Ensure the progress/seek bar is always visible at the bottom of the Shorts player.";
+"REMOVE_POSTS_SHORTS" = "Remove posts";
+"REMOVE_POSTS_SHORTS_DESC" = "Remove posts from the feed.";
+"SHORTS_FULLSCREEN" = "Pinch to Fullscreen";
+"SHORTS_FULLSCREEN_DESC" = "Manages visibility of the overlay with pinch in and pinch out gestures, displaying Shorts in fullscreen mode.";
"TABBAR" = "Tab bar";
"DEFAULT_TAB" = "Default startup tab";
-"DEFAULT_TAB_DESC" = "You can select the tab you want to open when opening the app.";
-"HOME_NAME" = "Home";
-"SUBSCRIPT_NAME" = "Subscriptions";
-"LIB_NAME" = "Library (You)";
+"FORSTED_TAB_BAR" = "Translucent tab bar";
+"ENABLED" = "Enabled";
"HIDE_TAB_INDI" = "Hide tab indicators";
"HIDE_TAB_INDI_DESC" = "Hide all tab indicators in the tab bar.";
"HIDE_TAB_LABELS" = "Hide tab labels";
"HIDE_TAB_LABELS_DESC" = "Hide all tab labels in the tab bar.";
-"HIDE_HOME_TAB" = "Hide Home tab";
-"HIDE_HOME_TAB_DESC" = "Hide Home tab in the tab bar.";
-"HIDE_SHORTS_TAB" = "Hide Shorts tab";
-"HIDE_SHORTS_TAB_DESC" = "Hide Shorts tab in the tab bar.";
-"HIDE_CREATE_BUTTON" = "Hide Create button";
-"HIDE_CREATE_BUTTON_DESC" = "Hide Create button in the tab bar.";
-"HIDE_SUBSCRIPT_TAB" = "Hide Subscriptions tab";
-"HIDE_SUBSCRIPT_TAB_DESC" = "Hide Subscriptions tab in the tab bar.";
+"HOME_TAB" = "Home";
+"SHORTS_TAB" = "Shorts";
+"CREATE_TAB" = "Create";
+"SUBSCRIPTIONS_TAB" = "Subscriptions";
+"LIBRARY_TAB" = "Library (You)";
+"HISTORY_TAB" = "History";
+"GAMING_TAB" = "Gaming";
+"SPORTS_TAB" = "Sports";
+"NOTI_TAB" = "Notifications";
+"NEWS_TAB" = "News";
+"MUSIC_TAB" = "Music";
+"WATCH_LATER_TAB" = "Watch later";
+"PLAYLIST_TAB" = "Playlists";
+"LIKE_TAB" = "Liked videos";
+"LIVE_TAB" = "Live";
+"POST_TAB" = "Posts";
+"VIDEO_TAB" = "Your videos";
+"MOVIE_TAB" = "Movies";
+"COURSE_TAB" = "Courses";
+"MINIGAME_TAB" = "Playables";
+"MANAGE_TABS" = "Manage tabs";
+"MANAGE_TABS_DESC" = "Reorder and toggle tabs";
+"TAB_REORDER_HINT" = "Drag to reorder. Toggle to show/hide.\nTab settings are also accessible by long-pressing the first tab in the tab bar.";
+"TAB_LIMIT" = "Tab limit";
+"TAB_LIMIT_DESC" = "Only 6 tabs are allowed.";
+"ZERO_TAB_DESC" = "At least 1 tab has to be enabled.";
"MISCELLANEOUS" = "Miscellaneous";
"BACKGROUND_PLAYBACK" = "Background playback";
"BACKGROUND_PLAYBACK_DESC" = "Enables background playback for videos.";
"DISABLES_SHORTS_PIP" = "Disables Shorts PiP";
"DISABLES_SHORTS_PIP_DESC" = "Try to disables Shorts PiP, this might not 100% disables it.";
+"DISABLE_HINTS" = "Disable hints";
+"DISABLE_HINTS_DESC" = "Disable the tutorial hints and interactive pop-ups.";
"BLOCK_UPGRADE_DIALOGS" = "Block upgrade dialogs";
"BLOCK_UPGRADE_DIALOGS_DESC" = "Block the YouTube dialogs that ask you to upgrade the app.";
"ARE_YOU_THERE_DIALOG" = "Hide 'Are you there?' dialog";
@@ -247,7 +379,109 @@
"DISABLES_SNACK_BAR_DESC" = "Disables snack bar when pressing some buttons or doing something in the app.";
"HIDE_STARTUP_ANIMATIONS" = "Hide YouTube's startup animations";
"HIDE_STARTUP_ANIMATIONS_DESC" = "Hide YouTube's startup animations when opening the app.";
-"HIDE_PLAY_IN_NEXT_QUEUE" = "Hide 'Play next in queue'";
-"HIDE_PLAY_IN_NEXT_QUEUE_DESC" = "Hide the 'Play next in queue' option in the flyout menu.";
"HIDE_LIKE_DISLIKE_VOTES" = "Hide like/dislike votes";
-"HIDE_LIKE_DISLIKE_VOTES_DESC" = "Hide messages that pop up when you like/dislike videos.";
\ No newline at end of file
+"HIDE_LIKE_DISLIKE_VOTES_DESC" = "Hide messages that pop up when you like/dislike videos.";
+"FLYOUT_MENU" = "Flyout menu";
+"REMOVE_PLAY_IN_NEXT_QUEUE_OPTION" = "Removes 'Play next in queue' option";
+"REMOVE_PLAY_IN_NEXT_QUEUE_OPTION_DESC" = "Removes 'Play next in queue' option from the flyout menu.";
+"REMOVE_DOWNLOAD_OPTION" = "Removes 'Download video' option";
+"REMOVE_DOWNLOAD_OPTION_DESC" = "Removes 'Download video' option from the flyout menu.";
+"REMOVE_WATCH_LATER_OPTION" = "Removes 'Save to Watch later' option";
+"REMOVE_WATCH_LATER_OPTION_DESC" = "Removes 'Save to Watch Later' option from the flyout menu.";
+"REMOVE_SAVE_OPTION" = "Removes 'Save to playlist' option";
+"REMOVE_SAVE_OPTION_DESC" = "Removes 'Save to playlist' option from the flyout menu.";
+"REMOVE_REMOVE_FROM_PLAYLIST_OPTION" = "Removes 'Remove from playlist' option";
+"REMOVE_REMOVE_FROM_PLAYLIST_OPTION_DESC" = "Removes 'Remove from playlist' option from the flyout menu.";
+"REMOVE_SHARE_OPTION" = "Removes 'Share' option";
+"REMOVE_SHARE_OPTION_DESC" = "Removes 'Share' option from the flyout menu.";
+"REMOVE_NOT_INTERESTED_OPTION" = "Removes 'Not interested' option";
+"REMOVE_NOT_INTERESTED_OPTION_DESC" = "Removes 'Not interested' option from the flyout menu.";
+"REMOVE_DONT_RECOMMEND_OPTION" = "Removes 'Don't recommend channel' option";
+"REMOVE_DONT_RECOMMEND_OPTION_DESC" = "Removes 'Don't recommend channel' option from the flyout menu.";
+"REMOVE_INFO_OPTION" = "Removes 'Why are you seeing this?' option";
+"REMOVE_INFO_OPTION_DESC" = "Removes 'Why are you seeing this?' option from the flyout menu.";
+"REMOVE_FILTER_OPTION" = "Removes 'Search filters' option";
+"REMOVE_FILTER_OPTION_DESC" = "Removes 'Search filters' option from the flyout menu.";
+"REMOVE_REPORT_OPTION" = "Removes 'Report' option";
+"REMOVE_REPORT_OPTION_DESC" = "Removes 'Report' option from the flyout menu.";
+"REMOVE_YOUTUBE_MUSIC_OPTION" = "Removes 'Listen with YouTube Music' option";
+"REMOVE_YOUTUBE_MUSIC_OPTION_DESC" = "Removes 'Listen with YouTube Music' option from the flyout menu.";
+"REMOVE_FEED_BACK_OPTION" = "Removes 'Send feedback' option";
+"REMOVE_FEED_BACK_OPTION_DESC" = "Removes 'Send feedback' option from the flyout menu.";
+"REMOVE_CAST_OPTION" = "Removes 'Watch on TV' option";
+"REMOVE_CAST_OPTION_DESC" = "Removes 'Watch on TV' option from the flyout menu.";
+"REMOVE_SHUFFLE_OPTION" = "Removes 'Shuffle' option";
+"REMOVE_SHUFFLE_OPTION_DESC" = "Removes 'Shuffle' option from the flyout menu.";
+"REMOVE_UN_SUB_OPTION" = "Removes 'Unsubscribe' option";
+"REMOVE_UN_SUB_OPTION_DESC" = "Removes 'Unsubscribe' option from the flyout menu.";
+"REMOVE_HIDE_FROM_PLAYLIST_OPTION" = "Removes 'Hide video from playlist' option";
+"REMOVE_HIDE_FROM_PLAYLIST_OPTION_DESC" = "Removes 'Hide video from playlist' option from the flyout menu.";
+"REMOVE_HELP_OPTION" = "Removes 'Help & feedback' option";
+"REMOVE_HELP_OPTION_DESC" = "Removes 'Help & feedback' option from the flyout menu.";
+"REMOVE_NOTIFY_OPTION" = "Removes 'Notify me' option";
+"REMOVE_NOTIFY_OPTION_DESC" = "Removes 'Notify me' option from the flyout menu.";
+"REMOVE_CLEARSCREEN_OPTION" = "Removes 'Clear Screen' option";
+"REMOVE_CLEARSCREEN_OPTION_DESC" = "Removes 'Clear Screen' option from the flyout menu.";
+"HIDE_COMMU_GUIDE" = "Hide community guidelines";
+"HIDE_COMMU_GUIDE_DESC" = "Hide community guidelines header.";
+"DISABLES_RTL" = "Disable RTL formatting";
+"DISABLES_RTL_DESC" = "Forcefully displays text in left-to-right (LTR) format for languages that are initially displayed in right-to-left (RTL).";
+"DEVICE_UI" = "Interface style";
+
+"SB_DESCRIPTION" = "Skip sponsored segments automatically using community-submitted data.";
+"SB_ENABLE" = "Enable SponsorBlock";
+"SB_ENABLE_DESC" = "Activate the SponsorBlock extension to skip sponsored segments.";
+"SB_SHOW_BUTTON" = "Show overlay button";
+"SB_SHOW_BUTTON_DESC" = "Display a SponsorBlock toggle button in the player overlay.";
+"SB_SHOW_NOTIFICATIONS" = "Show notifications";
+"SB_SHOW_NOTIFICATIONS_DESC" = "Display notifications when segments are skipped. Notifications for manual-skip categories are always shown.";
+"SB_HAPTIC_FEEDBACK" = "Haptic feedback";
+"SB_HAPTIC_FEEDBACK_DESC" = "Provide haptic feedback when a segment is skipped.";
+"SB_SEGMENTS_IN_PLAYER" = "Segments in player";
+"SB_SEGMENTS_IN_PLAYER_DESC" = "Display colored segments on the progress bar of the main video player.";
+"SB_SEGMENTS_IN_FEED" = "Segments in feed";
+"SB_SEGMENTS_IN_FEED_DESC" = "Display colored segments on the progress bar of players in the feed.";
+"SB_SEGMENTS_IN_MINIPLAYER" = "Segments in mini-player";
+"SB_SEGMENTS_IN_MINIPLAYER_DESC" = "Display colored segments on the progress bar of the mini-player.";
+"SB_SHOW_DURATION" = "Show duration without segments";
+"SB_SHOW_DURATION_DESC" = "Display the total video duration excluding skippable segments.";
+"SB_CATEGORIES_HEADER" = "Segment categories";
+"SB_SEGMENT_COLOR" = "Segment color";
+
+"SB_ACTION_DISABLE" = "Disable";
+"SB_ACTION_AUTO_SKIP" = "Auto-skip";
+"SB_ACTION_ASK" = "Ask before skipping";
+"SB_ACTION_DISPLAY" = "Show on seek bar only";
+"SB_ACTION_SKIP_TO" = "Skip to segment";
+
+"SB_CAT_sponsor" = "Sponsor";
+"SB_CAT_sponsor_DESC" = "Paid promotion for a product or service not directly related to the creator.";
+"SB_CAT_intro" = "Intro";
+"SB_CAT_intro_DESC" = "Intermission or intro animation typically seen at the start of a video.";
+"SB_CAT_outro" = "Endcards";
+"SB_CAT_outro_DESC" = "Endcards, credits, or outros at the end of the video.";
+"SB_CAT_interaction" = "Interaction";
+"SB_CAT_interaction_DESC" = "Reminders to like, subscribe, or follow on social media.";
+"SB_CAT_selfpromo" = "Self-promotion";
+"SB_CAT_selfpromo_DESC" = "Unpaid promotion of the creator's own products, merchandise, or other content.";
+"SB_CAT_music_offtopic" = "Non-music";
+"SB_CAT_music_offtopic_DESC" = "Non-music sections in music videos (e.g. talking, introductions).";
+"SB_CAT_preview" = "Preview";
+"SB_CAT_preview_DESC" = "Recap or preview of content from another video or previous episode.";
+"SB_CAT_hook" = "Hook/Greetings";
+"SB_CAT_hook_DESC" = "Narrated trailers for the upcoming video, greetings and goodbyes.";
+"SB_CAT_poi_highlight" = "Highlight";
+"SB_CAT_poi_highlight_DESC" = "The key moment or highlight point of the video. Use 'Skip to' to jump directly to it.";
+"SB_CAT_filler" = "Filler";
+"SB_CAT_filler_DESC" = "Tangential content, jokes, or filler that is not directly related to the main topic.";
+
+"SB_SKIPPED" = "%@ segment has been skipped";
+"SB_DETECTED" = "%@ segment detected.\nWould you like to skip the segment?";
+"SB_SKIP_NOW" = "Skip";
+"SB_UNSKIP" = "Unskip";
+"SB_JUMPED_TO_HIGHLIGHT" = "Jumped to highlight";
+"SB_JUMP_TO_HIGHLIGHT" = "Highlight available. Jump to the point?";
+"SB_SEGMENT_COLOR_SUFFIX" = "Segment color";
+"SB_SECTION_MAIN" = "Main";
+"SB_SKIP_ALERT_DURATION" = "Skip alert duration";
+"SB_UNSKIP_ALERT_DURATION" = "Unskip alert duration";
\ No newline at end of file
diff --git a/layout/Library/Application Support/YouMod.bundle/da.lproj/Localizable.strings b/layout/Library/Application Support/YouMod.bundle/da.lproj/Localizable.strings
index bccaf550..2c1a8a0f 100644
--- a/layout/Library/Application Support/YouMod.bundle/da.lproj/Localizable.strings
+++ b/layout/Library/Application Support/YouMod.bundle/da.lproj/Localizable.strings
@@ -1,11 +1,18 @@
-"NOTE" = "NOTE: Some hiding features may leave a blank box.";
"SETTINGS" = "Settings";
+"INTERFACE" = "Interface";
+
+"LOOP_DISABLED" = "Loop disabled";
+"LOOP_ENABLED" = "Loop enabled";
"NEW_ISSUES" = "Report issues or request new features";
"NEW_ISSUES_DESC" = "Found bugs or want to request new features? Tap here.";
"SOURCE_CODES" = "Source codes";
"SOURCE_CODES_DESC" = "Take a look at the codes!";
+"EXTRA" = "Extra";
+"FIX_PLAYBACK_ISSUES" = "Fix playback issues (Temporary)";
+"FIX_PLAYBACK_ISSUES_DESC" = "Try to fix video playback issues for some users. Turning this on will make the player automatically reloads itself everytime it errors or fails to play the video.";
+
"PERFER_HEADER" = "YouMod perferences";
"PERFER" = "Perferences";
"IMPORT" = "Import settings";
@@ -24,6 +31,7 @@
"ERROR_NO_KEYS_IMPORT" = "No available keys to import";
"ERROR_NO_KEYS_EXPORT" = "No available keys to export";
"OVERRIDE" = "This will override the current settings. Do you want to continue?";
+"DISABLED" = "Disabled";
"CACHE" = "Cache management";
"CLEARCACHE" = "Clear cache";
@@ -32,12 +40,80 @@
"AUTO_CLEARCACHE_DESC" = "Automatically clears the app cache on startup.";
"DOWNLOADING" = "Downloading";
-"DOWNLOAD_MANAGER" = "Download Manager";
-"DOWNLOAD_MANAGER_DESC" = "Replaces YouTube's video downloader with YouMod one. With ablity to downloading videos, audios, captions, and thumbnails";
+"DOWNLOAD_MANAGER" = "Download manager";
+"DOWNLOAD_MANAGER_DESC" = "Replaces YouTube's video downloader with YouMod one. With ablity to downloading videos, audios, captions, and thumbnails.";
"DOWNLOAD_SAVE_PHOTOS" = "Automatically save to Photos";
"DOWNLOAD_SAVE_PHOTOS_DESC" = "Automatically saves downloaded videos and images directly to your Photos app.";
-"DOWNLOAD_DRC_AUDIO" = "Prefer DRC Audio";
-"DOWNLOAD_DRC_AUDIO_DESC" = "Prioritizes downloading audio tracks with Dynamic Range Compression for more consistent volume levels.";
+"ADD_SHORTS_DOWNLOAD" = "Adds a download button to Shorts";
+"ADD_SHORTS_DOWNLOAD_DESC" = "Adds a download button to Shorts video overlay.";
+"USE_ORIG_AUDIO" = "Automatically uses original audio track";
+"USE_ORIG_AUDIO_DESC" = "Automatically uses original audio track instead of showing a dialog.";
+"DOWNLOAD_SHORTS" = "Download Shorts";
+"DOWNLOAD_SHORTS_DESC" = "Choose Shorts quality";
+"COPY_VID_INFO" = "Copy video informations";
+"COPY_VID_INFO_DESC" = "Choose what to copy";
+"COPY_ALL_VID_INFO" = "Copy all";
+"COPY_ALL_VID_INFO_DESC" = "Copy all video informations";
+"COPY_AUTHOR" = "Copy author";
+"COPY_AUTHOR_DESC" = "Copy the video author";
+"COPY_TITLE" = "Copy title";
+"COPY_TITLE_DESC" = "Copy the video title";
+"COPY_DESCRIPTION" = "Copy description";
+"COPY_DESCRIPTION_DESC" = "Copy the video description";
+"COPY_URL" = "Copy video link";
+"COPY_URL_TIMESTAMP" = "Copy video link with timestamp";
+"URL_COPIED" = "Copied video link";
+"URL_TIMESTAMP_COPIED" = "Copied video link with timestamp";
+"ERROR_VIDEOID" = "Cannot get the video ID";
+"ERROR_ADS" = "Cannot copy video link while playing ads";
+"COPIED_AUTHOR" = "Copied video author";
+"COPIED_TITLE" = "Copied video title";
+"COPIED_DESCRIPTION" = "Copied video description";
+"SAVE_THUMBNAIL" = "Save thumbnail";
+"SAVE_THUMBNAIL_DESC" = "Save to Photos";
+"DOWNLOAD_CAPTIONS" = "Download captions";
+"DOWNLOAD_CAPTIONS_DESC" = "Save subtitles as .VTT file";
+"DOWNLOAD_AUDIO" = "Download audio";
+"DOWNLOAD_AUDIO_DESC" = "Download audio as .m4a file";
+"DOWNLOAD_VIDEO" = "Download video";
+"DOWNLOAD_VIDEO_DESC" = "Choose video quality";
+"OPEN_VID_BEFORE" = "Open a video before using the download manager";
+"NO_CAPTIONS_URL" = "No valid caption URLs found";
+"NO_AUDIO_URL" = "No audio URL found";
+"NO_STREAM_URL" = "No stream URL found";
+"CAPTIONS_FAILED" = "Failed to download captions";
+"NO_CAPTIONS" = "No captions available for this video";
+"NO_AUDIO_STREAM_FOUND" = "No audio streams found";
+"NO_VID_AUDIO_STREAM_FOUND" = "No video/audio streams found";
+"COPIED_VID_INFO" = "Copied all video informations";
+"SAVED_TO_PHOTOS" = "Saved to Photos";
+"SAVE_FAILED" = "Save failed";
+"PHOTO_ACCESS_DENINED" = "Photos access denied";
+"THUMBNAIL_FAILED" = "Thumbnail failed";
+"DOWNLOADING_THUMBNAIL" = "Downloading thumbnail...";
+"DOWNLOADING_AUDIO" = "Downloading audio...";
+"DOWNLOADING_VIDEO" = "Downloading video...";
+"DOWNLOADING_CAPTIONS" = "Downloading captions...";
+"NO_THUMBNAIL_FOUND" = "No thumbnail found";
+"DOWNLOAD_FAILED" = "Download failed";
+"COPY_THUMBNAIL" = "Copy thumbnail";
+"COPY_THUMBNAIL_DESC" = "Copy to clipboard";
+"SHOW_THUMBNAIL" = "Show thumbnail";
+"SHOW_THUMBNAIL_DESC" = "Show in a dialog";
+"THUMBNAIL_OPTIONS" = "Download thumbnail";
+"THUMBNAIL_OPTIONS_DESC" = "Show more options";
+"COPIED_TO_CLIPBOARD" = "Copied to clipboard";
+"DOWNLOAD_COMPLETED" = "Download completed";
+"CANNOT_SAVE_TO_PHOTOS" = "Cannot save to Photos";
+"FINA_VIDEO" = "Finalizing video...";
+"MERGING_VID_FALLBACK" = "Merging video with fallback...";
+"MERGING_VID" = "Merging video...";
+"CONVERTING" = "Converting to";
+"ALREADY_DOWNLOADING" = "Already downloading";
+"DOWNLOAD_CANCELLED" = "Download cancelled";
+"CANCEL" = "Cancel";
+"TRIMMING_AUDIO" = "Trimming audio...";
+"COPY" = "Copy";
"APPEARANCE" = "Appearance";
"OLED_THEME" = "OLED dark mode";
@@ -46,6 +122,8 @@
"OLED_KEYBOARD_DESC" = "Make the keyboard uses a pure black (OLED) color for dark theme.";
"NAVBAR" = "Navigation bar";
+"STICKY_NAVBAR" = "Sticky Navigation bar";
+"STICKY_NAVBAR_DESC" = "Pins the Navigation bar so that it remains visible when scrolling down.";
"HIDE_YT_LOGO" = "Hide YouTube logo";
"HIDE_YT_LOGO_DESC" = "Hide YouTube logo in the navigation bar.";
"PREMIUM_LOGO" = "Use YouTube Premium logo";
@@ -62,34 +140,78 @@
"FEED" = "Feed";
"HIDE_SUBBAR" = "Hide subbar";
"HIDE_SUBBAR_DESC" = "Hide subbar at the top of the feed.";
+"HIDE_HORI_SHELF" = "Remove horizontal feeds";
+"HIDE_HORI_SHELF_DESC" = "Removes all horizontal feeds (News, Continue Watching, Posts collection, etc.) from the feed, except Playables.";
"HIDE_MUSIC_PLAYLISTS" = "Hide music playlist generator";
"HIDE_MUSIC_PLAYLISTS_DESC" = "Hide music playlist generator (Select music tunes in three) in the feed.";
"HIDE_FEED_POST" = "Hide posts";
"HIDE_FEED_POST_DESC" = "Hide posts in feed.";
+"HIDE_SURVEYS" = "Hide surveys";
+"HIDE_SURVEYS_DESC" = "Hide video surveys that YouTube shows sometimes.";
"HIDE_SHORTS_SHELF" = "Hide Shorts shelf";
"HIDE_SHORTS_SHELF_DESC" = "Hide Shorts shelf in the feed, including in the Subscriptions tab.";
+"KEEP_SHORTS_SUBSCRIPT" = "Keep Shorts in Subscriptions";
+"KEEP_SHORTS_SUBSCRIPT_DESC" = "Prevents removing Shorts from Subscriptions tab.";
"HIDE_SEARCH_HISTORY" = "Hide search history";
"HIDE_SEARCH_HISTORY_DESC" = "Hide previous search history and suggestions when using the search bar.\nNOTE: Your search history will still be accessible in your other YouTube clients.";
-"HIDE_SUB_BUTTON" = "Hide Subscribe button";
-"HIDE_SUB_BUTTON_DESC" = "Hides the subscribe button in channels and video descriptions.";
-"HIDE_SHOP_BUTTON" = "Hide Shopping button";
-"HIDE_SHOP_BUTTON_DESC" = "Hides the shopping/store button in channels descriptions.";
-"HIDE_MEMBER_BUTTON" = "Hide Membership button";
-"HIDE_MEMBER_BUTTON_DESC" = "Hides the 'Join' button in channels and video descriptions.";
+"HIDE_PLAYABLES" = "Remove playables";
+"HIDE_PLAYABLES_DESC" = "Removes playables (mini-games) from the feed.";
"PLAYER" = "Player";
+"PLAYBACK_SPEED" = "Playback speed";
+"CONTROL_CENTER" = "Control Center";
+"PLAYER_ACTIONS" = "Player actions";
+"GESTURE_HEADER" = "Gestures";
+"QUALITY_WIFI" = "Playback quality on WiFi";
+"QUALITY_WIFI_DESC" = "Automatically applies the selected quality when streaming over Wi-Fi.";
+"QUALITY_CELLULAR" = "Playback quality on Cellular";
+"QUALITY_CELLULAR_DESC" = "Automatically applies the selected quality when streaming over mobile data.";
+"QUALITY_LOW_POWER" = "Playback quality on Power saver mode";
+"QUALITY_LOW_POWER_DESC" = "Automatically applies the selected quality when when Low power mode is enabled on your device.";
+"DEFAULT" = "Default";
+"BEST" = "Best";
+"AUDIO_TRACK" = "Preferred audio track";
+"ORIGINAL" = "Original";
+"SELECT_MANUALLY" = "Select manually";
+"AUDIO_TRACK_SELECT" = "Selected audio track";
+"AUDIO_TRACK_SELECT_DESC" = "Specifies the preferred audio track language to use for playback, if available.";
+"NO_AUTO_DUBBED" = "Uses original audio track";
+"NO_AUTO_DUBBED_DESC" = "Uses original audio track if the preferred language track is auto-dubbed or not found.";
+"CAPTION_TRACK" = "Preferred captions";
+"CAPTION_TRACK_SELECT" = "Selected captions";
+"CAPTION_TRACK_SELECT_DESC" = "Specifies the preferred captions language to display, if available.";
+"DISABLES_CAPTION_TRACK" = "Automatically disable captions";
+"DISABLES_CAPTION_TRACK_DESC" = "Automatically disable captions if the preferred language is auto-generated or not found.";
+"DEFAULT_SPEED" = "Default playback rate";
+"DEFAULT_SPEED_DESC" = "Overrides the default playback speed.";
+"SPEED_BUTTON" = "Speed controls button";
+"SPEED_BUTTON_DESC" = "Displays a speed controls button on overlay.";
+"MUTE_BUTTON" = "Mute button";
+"MUTE_BUTTON_DESC" = "Displays a button to mute or unmute the volume on the overlay.";
+"SHARE_BUTTON" = "Share button";
+"SHARE_BUTTON_DESC" = "Displays a button to share the video link on the overlay.";
+"LOOP_BUTTON" = "Loop button";
+"LOOP_BUTTON_DESC" = "Displays a button to loop the video on the overlay.";
+"CAPTION_BUTTON" = "Captions button";
+"CAPTION_BUTTON_DESC" = "Displays a button to copy the video captions on the overlay.";
+"HOLD_TO_SPEED" = "Hold to speed";
+"HOLD_TO_SPEED_DESC" = "Uses custom hold to speed playback rate.";
"HIDE_AUTOPLAY" = "Hide autoplay switch";
"HIDE_AUTOPLAY_DESC" = "Hide autoplay switch in the video overlay.";
"HIDE_CAPTIONS_BUTTON" = "Hide captions button";
"HIDE_CAPTIONS_BUTTON_DESC" = "Hide captions button in the video overlay.";
"HIDE_CAST_BUTTON_PLAYER" = "Hide cast button";
"HIDE_CAST_BUTTON_PLAYER_DESC" = "Hide cast button in the video overlay.";
-"HIDE_PREV_BUTTON" = "Hide previous/back button";
-"HIDE_PREV_BUTTON_DESC" = "Hide previous/back button in the video overlay.";
-"HIDE_NEXT_BUTTON" = "Hide next/skip button";
-"HIDE_NEXT_BUTTON_DESC" = "Hide next/skip button in the video overlay.";
+"HIDE_NEXT_AND_PREV_BUTTON" = "Hide previous and next buttons";
+"HIDE_NEXT_AND_PREV_BUTTON_DESC" = "Hide previous and next buttons in the video overlay.";
"REPLACE_PREVNEXT_BUTTONS" = "Replace Previous/Next buttons";
"REPLACE_PREVNEXT_BUTTONS_DESC" = "Replaces the Previous and Next video buttons in the player with Rewind and Fast Forward buttons.";
+"SKIP_BACKWARD" = "Skip backward";
+"SKIP_BACKWARD_DESC" = "Replaces the previous track button in Control Center with a skip backward button by a set number of seconds.";
+"SKIP_FORWARD" = "Skip forward";
+"SKIP_FORWARD_DESC" = "Replaces the next track button in Control Center with a skip forward button by a set number of seconds.";
+"REWIND_SECONDS" = "Rewind seconds";
+"FORWARD_SECONDS" = "Fast-forward seconds";
"REMOVE_DARK_OVERLAY" = "Remove dark overlay";
"REMOVE_DARK_OVERLAY_DESC" = "Remove dark overlay from the video overlay.";
"HIDE_END_SCREEN" = "Hide endscreen cards";
@@ -102,6 +224,12 @@
"HIDE_PAID_OVERLAY_DESC" = "Hide 'Includes paid promotion' in the video overlay.";
"HIDE_WATERMARK" = "Hide watermark";
"HIDE_WATERMARK_DESC" = "Hide channel watermark at the bottom of the video overlay.";
+"PAUSE_ON_OVERLAY" = "Pause on overlay";
+"PAUSE_ON_OVERLAY_DESC" = "Sets playback on pause if overlay appears.";
+"DISABLES_SNAP_TO_CHAPTER" = "Disable snap to chapters";
+"DISABLES_SNAP_TO_CHAPTER_DESC" = "Prevents the scrubbar stick to the chapters.";
+"DISABLES_ENGAGE_PANEL" = "Disables engagement panel in fullscreen";
+"DISABLES_ENGAGE_PANEL_DESC" = "Prevent engagement panel showing in fullscreen.";
"GESTURES" = "Enable gestures controls";
"GESTURES_DESC" = "Swipe up or down on the left/right edges of the video screen to adjust brightness, volume, or playback speed.";
"GESTURE_AREA" = "Gesture activation area";
@@ -136,6 +264,8 @@
"DISABLES_SHOW_REMAINING_DESC" = "Disable the feature that shows how much time is left in a video when tapping the timestamp.";
"ALWAYS_SHOW_REMAINING" = "Always show remaining time";
"ALWAYS_SHOW_REMAINING_DESC" = "Ensure the remaining time is always displayed by default in the player overlay.";
+"COPY_TIMESTAMP_ON_PAUSE" = "Copy timestamped links";
+"COPY_TIMESTAMP_ON_PAUSE_DESC" = "Allows to copy timestamped link to the clipboard by pressing pause button.";
"SHOW_REMAINING_EXTRA" = "Show extra remaining time";
"SHOW_REMAINING_EXTRA_DESC" = "Adds the estimated finish time to the label in the video overlay.";
"HIDE_FULLSCREEN_ACTIONS" = "Hide fullscreen actions";
@@ -148,93 +278,95 @@
"HIDE_CONTENT_WARNING_DESC" = "Automatically skip the sensitive content warning alert in some vidoes.";
"AUTO_FULLSCREEN" = "Auto fullscreen";
"AUTO_FULLSCREEN_DESC" = "Automatically enter fullscreen mode when starting video playback.";
-"AUTO_DISABLES_CAPTION" = "Auto disable captions";
-"AUTO_DISABLES_CAPTION_DESC" = "Automatically turns off closed captions (CC) whenever a new video starts playing.";
"PORTRAIT_FULLSCREEN" = "Portrait fullscren mode";
"PORTRAIT_FULLSCREEN_DESC" = "Enables portrait fullscreen mode support.";
"OLD_QUALITY_PICKER" = "Use old quality picker";
"OLD_QUALITY_PICKER_DESC" = "Reverts the video quality selection menu to the classic version (lists all resolutions immediately).";
"EXTRA_SPEED" = "Extra speed options";
"EXTRA_SPEED_DESC" = "Adds more video playback speed options up to 10x.";
-"DISABLE_HINTS" = "Disable hints";
-"DISABLE_HINTS_DESC" = "Disable the tutorial hints and interactive pop-ups.";
"FORCE_MINIPLAYER" = "Force miniplayer";
"FORCE_MINIPLAYER_DESC" = "Allows all videos, including those made for kids, to play in the miniplayer when dismissing the player.";
"FORCE_SEEKBAR" = "Always show progress bar";
"FORCE_SEEKBAR_DESC" = "Makes the progress bar (seekbar) remains visible at all times while in fullscreen mode.";
-"HIDE_LIKE_BUTTON" = "Hide Like button";
-"HIDE_LIKE_BUTTON_DESC" = "Hide Like button in the action bar under the video overlay.";
-"HIDE_DISLIKE_BUTTON" = "Hide Dislike button";
-"HIDE_DISLIKE_BUTTON_DESC" = "Hide Dislike button in the action bar under the video overlay.";
-"HIDE_SHARE_BUTTON" = "Hide Share button";
-"HIDE_SHARE_BUTTON_DESC" = "Hide Share button in the action bar under the video overlay.";
-"HIDE_DOWNLOAD_BUTTON" = "Hide Download button";
-"HIDE_DOWNLOAD_BUTTON_DESC" = "Hide Download button in the action bar under the video overlay.";
-"HIDE_CLIP_BUTTON" = "Hide Clip button";
-"HIDE_CLIP_BUTTON_DESC" = "Hide Clip button in the action bar under the video overlay.";
-"HIDE_REMIX_BUTTON" = "Hide Remix button";
-"HIDE_REMIX_BUTTON_DESC" = "Hide Remix button in the action bar under the video overlay.";
-"HIDE_SAVE_BUTTON" = "Hide Save button";
-"HIDE_SAVE_BUTTON_DESC" = "Hide Save button in the action bar under the video overlay.";
+"TAP_TO_SEEK" = "Tap to seek";
+"TAP_TO_SEEK_DESC" = "Quickly jump to any point by tapping the progress bar.";
+"DISABLES_ZOOM" = "Disable free zoom gesture";
+"DISABLES_ZOOM_DESC" = "Disables new free zoom gestures.";
+"USES_24_HOURS_TIME" = "24-hour format";
+"USES_24_HOURS_TIME_DESC" = "Shows end time in 24-hour format.";
+"PAUSE_TWO_FINGERS" = "Pause using two fingers";
+"PAUSE_TWO_FINGERS_DESC" = "Pause/Resume playback by tapping on the screen using two fingers.";
+"HIDE_COMMENTS_SECTION" = "Hide comments section";
+"HIDE_COMMENTS_SECTION_DESC" = "Completely removes the entire comments section.";
+"HIDE_COMMENTS_PREVIEW" = "Hide preview comments";
+"HIDE_COMMENTS_PREVIEW_DESC" = "Removes the preview box for the top comment while keeping the full section expandable.";
+"USE_ANOTHER_MINIPLAYER" = "Uses another miniplayer style";
+"USE_ANOTHER_MINIPLAYER_DESC" = "Uses another miniplayer style redesigned from older YT versions.";
"SHORTS" = "Shorts";
-"HIDE_SHORTS_LIKE_BUTTON" = "Hide Like button";
-"HIDE_SHORTS_LIKE_BUTTON_DESC" = "Hide the Like button in the Shorts player.";
-"HIDE_SHORTS_DISLIKE_BUTTON" = "Hide Dislike button";
-"HIDE_SHORTS_DISLIKE_BUTTON_DESC" = "Hide the Dislike button in the Shorts player.";
-"HIDE_SHORTS_COMMENT_BUTTON" = "Hide Comment button";
-"HIDE_SHORTS_COMMENT_BUTTON_DESC" = "Hide the Comment button in the Shorts player.";
-"HIDE_SHORTS_SHARE_BUTTON" = "Hide Share button";
-"HIDE_SHORTS_SHARE_BUTTON_DESC" = "Hide the Share button in the Shorts player.";
-"HIDE_SHORTS_REMIX_BUTTON" = "Hide Remix button";
-"HIDE_SHORTS_REMIX_BUTTON_DESC" = "Hide the Remix button in the Shorts player.";
-"HIDE_METADATA_BUTTON" = "Hide sound metadata button";
-"HIDE_METADATA_BUTTON_DESC" = "Hide the button that shows the sound source or music metadata in the Shorts player.";
+"SHORTS_ONLY" = "Shorts only mode";
+"SHORTS_ONLY_DESC" = "Limits YouTube functionality to viewing Shorts only.";
+"SHORTS_ACTION" = "Shorts action after the video ends";
+"LOOP" = "Loop video";
+"SKIP_TO_NEXT_SHORTS" = "Skip to next video";
+"PAUSE_SHORTS" = "Stop video";
+"REMOVE_LIVE_SHORTS" = "Remove live videos";
+"REMOVE_LIVE_SHORTS_DESC" = "Removes live videos from the feed.";
"HIDE_SHORTS_PRODUCT" = "Hide products";
"HIDE_SHORTS_PRODUCT_DESC" = "Hide tagged products and shopping links in the Shorts overlay.";
"HIDE_SHORTS_RECBAR" = "Hide recommendation bar";
"HIDE_SHORTS_RECBAR_DESC" = "Hide the suggested action bar in the Shorts player (e.g. Try this sound, Shopping, Preview comment).";
-"HIDE_SHORTS_COMMIT" = "Hide commissions bar";
-"HIDE_SHORTS_COMMIT_DESC" = "Hide the bar that shows the video got commissions in the Shorts player.";
-"HIDE_SHORTS_SUBSCRIPT_BUTTON" = "Hide subscriptions button";
-"HIDE_SHORTS_SUBSCRIPT_BUTTON_DESC" = "Hide the Subscriptions button at the top of the Shorts player.";
-"HIDE_SHORTS_LIVE_BUTTON" = "Hide live button";
-"HIDE_SHORTS_LIVE_BUTTON_DESC" = "Hide the Live button at the top of the Shorts player.";
-"HIDE_SHORTS_LENS_BUTTON" = "Hide lens button";
-"HIDE_SHORTS_LENS_BUTTON_DESC" = "Hide the Lens button at the top of the Shorts player.";
-"HIDE_SHORTS_TRENDS_BUTTON" = "Hide trends button";
-"HIDE_SHORTS_TRENDS_BUTTON_DESC" = "Hide the Trends button at the top of the Shorts player.";
-"HIDE_SHORTS_TO_VIDEO" = "Hide link to full video";
-"HIDE_SHORTS_TO_VIDEO_DESC" = "Hide the link that redirects in a Short to a full video in the Shorts player.";
"ENABLES_SHORTS_QUALITY" = "Enable Shorts quality picker";
"ENABLES_SHORTS_QUALITY_DESC" = "Force enable the video quality selection menu in Shorts.";
"SHOW_SHORTS_SEEKBAR" = "Always show seekbar";
"SHOW_SHORTS_SEEKBAR_DESC" = "Ensure the progress/seek bar is always visible at the bottom of the Shorts player.";
+"REMOVE_POSTS_SHORTS" = "Remove posts";
+"REMOVE_POSTS_SHORTS_DESC" = "Remove posts from the feed.";
+"SHORTS_FULLSCREEN" = "Pinch to Fullscreen";
+"SHORTS_FULLSCREEN_DESC" = "Manages visibility of the overlay with pinch in and pinch out gestures, displaying Shorts in fullscreen mode.";
"TABBAR" = "Tab bar";
"DEFAULT_TAB" = "Default startup tab";
-"DEFAULT_TAB_DESC" = "You can select the tab you want to open when opening the app.";
-"HOME_NAME" = "Home";
-"SUBSCRIPT_NAME" = "Subscriptions";
-"LIB_NAME" = "Library (You)";
+"FORSTED_TAB_BAR" = "Translucent tab bar";
+"ENABLED" = "Enabled";
"HIDE_TAB_INDI" = "Hide tab indicators";
"HIDE_TAB_INDI_DESC" = "Hide all tab indicators in the tab bar.";
"HIDE_TAB_LABELS" = "Hide tab labels";
"HIDE_TAB_LABELS_DESC" = "Hide all tab labels in the tab bar.";
-"HIDE_HOME_TAB" = "Hide Home tab";
-"HIDE_HOME_TAB_DESC" = "Hide Home tab in the tab bar.";
-"HIDE_SHORTS_TAB" = "Hide Shorts tab";
-"HIDE_SHORTS_TAB_DESC" = "Hide Shorts tab in the tab bar.";
-"HIDE_CREATE_BUTTON" = "Hide Create button";
-"HIDE_CREATE_BUTTON_DESC" = "Hide Create button in the tab bar.";
-"HIDE_SUBSCRIPT_TAB" = "Hide Subscriptions tab";
-"HIDE_SUBSCRIPT_TAB_DESC" = "Hide Subscriptions tab in the tab bar.";
+"HOME_TAB" = "Home";
+"SHORTS_TAB" = "Shorts";
+"CREATE_TAB" = "Create";
+"SUBSCRIPTIONS_TAB" = "Subscriptions";
+"LIBRARY_TAB" = "Library (You)";
+"HISTORY_TAB" = "History";
+"GAMING_TAB" = "Gaming";
+"SPORTS_TAB" = "Sports";
+"NOTI_TAB" = "Notifications";
+"NEWS_TAB" = "News";
+"MUSIC_TAB" = "Music";
+"WATCH_LATER_TAB" = "Watch later";
+"PLAYLIST_TAB" = "Playlists";
+"LIKE_TAB" = "Liked videos";
+"LIVE_TAB" = "Live";
+"POST_TAB" = "Posts";
+"VIDEO_TAB" = "Your videos";
+"MOVIE_TAB" = "Movies";
+"COURSE_TAB" = "Courses";
+"MINIGAME_TAB" = "Playables";
+"MANAGE_TABS" = "Manage tabs";
+"MANAGE_TABS_DESC" = "Reorder and toggle tabs";
+"TAB_REORDER_HINT" = "Drag to reorder. Toggle to show/hide.\nTab settings are also accessible by long-pressing the first tab in the tab bar.";
+"TAB_LIMIT" = "Tab limit";
+"TAB_LIMIT_DESC" = "Only 6 tabs are allowed.";
+"ZERO_TAB_DESC" = "At least 1 tab has to be enabled.";
"MISCELLANEOUS" = "Miscellaneous";
"BACKGROUND_PLAYBACK" = "Background playback";
"BACKGROUND_PLAYBACK_DESC" = "Enables background playback for videos.";
"DISABLES_SHORTS_PIP" = "Disables Shorts PiP";
"DISABLES_SHORTS_PIP_DESC" = "Try to disables Shorts PiP, this might not 100% disables it.";
+"DISABLE_HINTS" = "Disable hints";
+"DISABLE_HINTS_DESC" = "Disable the tutorial hints and interactive pop-ups.";
"BLOCK_UPGRADE_DIALOGS" = "Block upgrade dialogs";
"BLOCK_UPGRADE_DIALOGS_DESC" = "Block the YouTube dialogs that ask you to upgrade the app.";
"ARE_YOU_THERE_DIALOG" = "Hide 'Are you there?' dialog";
@@ -247,7 +379,109 @@
"DISABLES_SNACK_BAR_DESC" = "Disables snack bar when pressing some buttons or doing something in the app.";
"HIDE_STARTUP_ANIMATIONS" = "Hide YouTube's startup animations";
"HIDE_STARTUP_ANIMATIONS_DESC" = "Hide YouTube's startup animations when opening the app.";
-"HIDE_PLAY_IN_NEXT_QUEUE" = "Hide 'Play next in queue'";
-"HIDE_PLAY_IN_NEXT_QUEUE_DESC" = "Hide the 'Play next in queue' option in the flyout menu.";
"HIDE_LIKE_DISLIKE_VOTES" = "Hide like/dislike votes";
-"HIDE_LIKE_DISLIKE_VOTES_DESC" = "Hide messages that pop up when you like/dislike videos.";
\ No newline at end of file
+"HIDE_LIKE_DISLIKE_VOTES_DESC" = "Hide messages that pop up when you like/dislike videos.";
+"FLYOUT_MENU" = "Flyout menu";
+"REMOVE_PLAY_IN_NEXT_QUEUE_OPTION" = "Removes 'Play next in queue' option";
+"REMOVE_PLAY_IN_NEXT_QUEUE_OPTION_DESC" = "Removes 'Play next in queue' option from the flyout menu.";
+"REMOVE_DOWNLOAD_OPTION" = "Removes 'Download video' option";
+"REMOVE_DOWNLOAD_OPTION_DESC" = "Removes 'Download video' option from the flyout menu.";
+"REMOVE_WATCH_LATER_OPTION" = "Removes 'Save to Watch later' option";
+"REMOVE_WATCH_LATER_OPTION_DESC" = "Removes 'Save to Watch Later' option from the flyout menu.";
+"REMOVE_SAVE_OPTION" = "Removes 'Save to playlist' option";
+"REMOVE_SAVE_OPTION_DESC" = "Removes 'Save to playlist' option from the flyout menu.";
+"REMOVE_REMOVE_FROM_PLAYLIST_OPTION" = "Removes 'Remove from playlist' option";
+"REMOVE_REMOVE_FROM_PLAYLIST_OPTION_DESC" = "Removes 'Remove from playlist' option from the flyout menu.";
+"REMOVE_SHARE_OPTION" = "Removes 'Share' option";
+"REMOVE_SHARE_OPTION_DESC" = "Removes 'Share' option from the flyout menu.";
+"REMOVE_NOT_INTERESTED_OPTION" = "Removes 'Not interested' option";
+"REMOVE_NOT_INTERESTED_OPTION_DESC" = "Removes 'Not interested' option from the flyout menu.";
+"REMOVE_DONT_RECOMMEND_OPTION" = "Removes 'Don't recommend channel' option";
+"REMOVE_DONT_RECOMMEND_OPTION_DESC" = "Removes 'Don't recommend channel' option from the flyout menu.";
+"REMOVE_INFO_OPTION" = "Removes 'Why are you seeing this?' option";
+"REMOVE_INFO_OPTION_DESC" = "Removes 'Why are you seeing this?' option from the flyout menu.";
+"REMOVE_FILTER_OPTION" = "Removes 'Search filters' option";
+"REMOVE_FILTER_OPTION_DESC" = "Removes 'Search filters' option from the flyout menu.";
+"REMOVE_REPORT_OPTION" = "Removes 'Report' option";
+"REMOVE_REPORT_OPTION_DESC" = "Removes 'Report' option from the flyout menu.";
+"REMOVE_YOUTUBE_MUSIC_OPTION" = "Removes 'Listen with YouTube Music' option";
+"REMOVE_YOUTUBE_MUSIC_OPTION_DESC" = "Removes 'Listen with YouTube Music' option from the flyout menu.";
+"REMOVE_FEED_BACK_OPTION" = "Removes 'Send feedback' option";
+"REMOVE_FEED_BACK_OPTION_DESC" = "Removes 'Send feedback' option from the flyout menu.";
+"REMOVE_CAST_OPTION" = "Removes 'Watch on TV' option";
+"REMOVE_CAST_OPTION_DESC" = "Removes 'Watch on TV' option from the flyout menu.";
+"REMOVE_SHUFFLE_OPTION" = "Removes 'Shuffle' option";
+"REMOVE_SHUFFLE_OPTION_DESC" = "Removes 'Shuffle' option from the flyout menu.";
+"REMOVE_UN_SUB_OPTION" = "Removes 'Unsubscribe' option";
+"REMOVE_UN_SUB_OPTION_DESC" = "Removes 'Unsubscribe' option from the flyout menu.";
+"REMOVE_HIDE_FROM_PLAYLIST_OPTION" = "Removes 'Hide video from playlist' option";
+"REMOVE_HIDE_FROM_PLAYLIST_OPTION_DESC" = "Removes 'Hide video from playlist' option from the flyout menu.";
+"REMOVE_HELP_OPTION" = "Removes 'Help & feedback' option";
+"REMOVE_HELP_OPTION_DESC" = "Removes 'Help & feedback' option from the flyout menu.";
+"REMOVE_NOTIFY_OPTION" = "Removes 'Notify me' option";
+"REMOVE_NOTIFY_OPTION_DESC" = "Removes 'Notify me' option from the flyout menu.";
+"REMOVE_CLEARSCREEN_OPTION" = "Removes 'Clear Screen' option";
+"REMOVE_CLEARSCREEN_OPTION_DESC" = "Removes 'Clear Screen' option from the flyout menu.";
+"HIDE_COMMU_GUIDE" = "Hide community guidelines";
+"HIDE_COMMU_GUIDE_DESC" = "Hide community guidelines header.";
+"DISABLES_RTL" = "Disable RTL formatting";
+"DISABLES_RTL_DESC" = "Forcefully displays text in left-to-right (LTR) format for languages that are initially displayed in right-to-left (RTL).";
+"DEVICE_UI" = "Interface style";
+
+"SB_DESCRIPTION" = "Skip sponsored segments automatically using community-submitted data.";
+"SB_ENABLE" = "Enable SponsorBlock";
+"SB_ENABLE_DESC" = "Activate the SponsorBlock extension to skip sponsored segments.";
+"SB_SHOW_BUTTON" = "Show overlay button";
+"SB_SHOW_BUTTON_DESC" = "Display a SponsorBlock toggle button in the player overlay.";
+"SB_SHOW_NOTIFICATIONS" = "Show notifications";
+"SB_SHOW_NOTIFICATIONS_DESC" = "Display notifications when segments are skipped. Notifications for manual-skip categories are always shown.";
+"SB_HAPTIC_FEEDBACK" = "Haptic feedback";
+"SB_HAPTIC_FEEDBACK_DESC" = "Provide haptic feedback when a segment is skipped.";
+"SB_SEGMENTS_IN_PLAYER" = "Segments in player";
+"SB_SEGMENTS_IN_PLAYER_DESC" = "Display colored segments on the progress bar of the main video player.";
+"SB_SEGMENTS_IN_FEED" = "Segments in feed";
+"SB_SEGMENTS_IN_FEED_DESC" = "Display colored segments on the progress bar of players in the feed.";
+"SB_SEGMENTS_IN_MINIPLAYER" = "Segments in mini-player";
+"SB_SEGMENTS_IN_MINIPLAYER_DESC" = "Display colored segments on the progress bar of the mini-player.";
+"SB_SHOW_DURATION" = "Show duration without segments";
+"SB_SHOW_DURATION_DESC" = "Display the total video duration excluding skippable segments.";
+"SB_CATEGORIES_HEADER" = "Segment categories";
+"SB_SEGMENT_COLOR" = "Segment color";
+
+"SB_ACTION_DISABLE" = "Disable";
+"SB_ACTION_AUTO_SKIP" = "Auto-skip";
+"SB_ACTION_ASK" = "Ask before skipping";
+"SB_ACTION_DISPLAY" = "Show on seek bar only";
+"SB_ACTION_SKIP_TO" = "Skip to segment";
+
+"SB_CAT_sponsor" = "Sponsor";
+"SB_CAT_sponsor_DESC" = "Paid promotion for a product or service not directly related to the creator.";
+"SB_CAT_intro" = "Intro";
+"SB_CAT_intro_DESC" = "Intermission or intro animation typically seen at the start of a video.";
+"SB_CAT_outro" = "Endcards";
+"SB_CAT_outro_DESC" = "Endcards, credits, or outros at the end of the video.";
+"SB_CAT_interaction" = "Interaction";
+"SB_CAT_interaction_DESC" = "Reminders to like, subscribe, or follow on social media.";
+"SB_CAT_selfpromo" = "Self-promotion";
+"SB_CAT_selfpromo_DESC" = "Unpaid promotion of the creator's own products, merchandise, or other content.";
+"SB_CAT_music_offtopic" = "Non-music";
+"SB_CAT_music_offtopic_DESC" = "Non-music sections in music videos (e.g. talking, introductions).";
+"SB_CAT_preview" = "Preview";
+"SB_CAT_preview_DESC" = "Recap or preview of content from another video or previous episode.";
+"SB_CAT_hook" = "Hook/Greetings";
+"SB_CAT_hook_DESC" = "Narrated trailers for the upcoming video, greetings and goodbyes.";
+"SB_CAT_poi_highlight" = "Highlight";
+"SB_CAT_poi_highlight_DESC" = "The key moment or highlight point of the video. Use 'Skip to' to jump directly to it.";
+"SB_CAT_filler" = "Filler";
+"SB_CAT_filler_DESC" = "Tangential content, jokes, or filler that is not directly related to the main topic.";
+
+"SB_SKIPPED" = "%@ segment has been skipped";
+"SB_DETECTED" = "%@ segment detected.\nWould you like to skip the segment?";
+"SB_SKIP_NOW" = "Skip";
+"SB_UNSKIP" = "Unskip";
+"SB_JUMPED_TO_HIGHLIGHT" = "Jumped to highlight";
+"SB_JUMP_TO_HIGHLIGHT" = "Highlight available. Jump to the point?";
+"SB_SEGMENT_COLOR_SUFFIX" = "Segment color";
+"SB_SECTION_MAIN" = "Main";
+"SB_SKIP_ALERT_DURATION" = "Skip alert duration";
+"SB_UNSKIP_ALERT_DURATION" = "Unskip alert duration";
\ No newline at end of file
diff --git a/layout/Library/Application Support/YouMod.bundle/de.lproj/Localizable.strings b/layout/Library/Application Support/YouMod.bundle/de.lproj/Localizable.strings
index bccaf550..2c1a8a0f 100644
--- a/layout/Library/Application Support/YouMod.bundle/de.lproj/Localizable.strings
+++ b/layout/Library/Application Support/YouMod.bundle/de.lproj/Localizable.strings
@@ -1,11 +1,18 @@
-"NOTE" = "NOTE: Some hiding features may leave a blank box.";
"SETTINGS" = "Settings";
+"INTERFACE" = "Interface";
+
+"LOOP_DISABLED" = "Loop disabled";
+"LOOP_ENABLED" = "Loop enabled";
"NEW_ISSUES" = "Report issues or request new features";
"NEW_ISSUES_DESC" = "Found bugs or want to request new features? Tap here.";
"SOURCE_CODES" = "Source codes";
"SOURCE_CODES_DESC" = "Take a look at the codes!";
+"EXTRA" = "Extra";
+"FIX_PLAYBACK_ISSUES" = "Fix playback issues (Temporary)";
+"FIX_PLAYBACK_ISSUES_DESC" = "Try to fix video playback issues for some users. Turning this on will make the player automatically reloads itself everytime it errors or fails to play the video.";
+
"PERFER_HEADER" = "YouMod perferences";
"PERFER" = "Perferences";
"IMPORT" = "Import settings";
@@ -24,6 +31,7 @@
"ERROR_NO_KEYS_IMPORT" = "No available keys to import";
"ERROR_NO_KEYS_EXPORT" = "No available keys to export";
"OVERRIDE" = "This will override the current settings. Do you want to continue?";
+"DISABLED" = "Disabled";
"CACHE" = "Cache management";
"CLEARCACHE" = "Clear cache";
@@ -32,12 +40,80 @@
"AUTO_CLEARCACHE_DESC" = "Automatically clears the app cache on startup.";
"DOWNLOADING" = "Downloading";
-"DOWNLOAD_MANAGER" = "Download Manager";
-"DOWNLOAD_MANAGER_DESC" = "Replaces YouTube's video downloader with YouMod one. With ablity to downloading videos, audios, captions, and thumbnails";
+"DOWNLOAD_MANAGER" = "Download manager";
+"DOWNLOAD_MANAGER_DESC" = "Replaces YouTube's video downloader with YouMod one. With ablity to downloading videos, audios, captions, and thumbnails.";
"DOWNLOAD_SAVE_PHOTOS" = "Automatically save to Photos";
"DOWNLOAD_SAVE_PHOTOS_DESC" = "Automatically saves downloaded videos and images directly to your Photos app.";
-"DOWNLOAD_DRC_AUDIO" = "Prefer DRC Audio";
-"DOWNLOAD_DRC_AUDIO_DESC" = "Prioritizes downloading audio tracks with Dynamic Range Compression for more consistent volume levels.";
+"ADD_SHORTS_DOWNLOAD" = "Adds a download button to Shorts";
+"ADD_SHORTS_DOWNLOAD_DESC" = "Adds a download button to Shorts video overlay.";
+"USE_ORIG_AUDIO" = "Automatically uses original audio track";
+"USE_ORIG_AUDIO_DESC" = "Automatically uses original audio track instead of showing a dialog.";
+"DOWNLOAD_SHORTS" = "Download Shorts";
+"DOWNLOAD_SHORTS_DESC" = "Choose Shorts quality";
+"COPY_VID_INFO" = "Copy video informations";
+"COPY_VID_INFO_DESC" = "Choose what to copy";
+"COPY_ALL_VID_INFO" = "Copy all";
+"COPY_ALL_VID_INFO_DESC" = "Copy all video informations";
+"COPY_AUTHOR" = "Copy author";
+"COPY_AUTHOR_DESC" = "Copy the video author";
+"COPY_TITLE" = "Copy title";
+"COPY_TITLE_DESC" = "Copy the video title";
+"COPY_DESCRIPTION" = "Copy description";
+"COPY_DESCRIPTION_DESC" = "Copy the video description";
+"COPY_URL" = "Copy video link";
+"COPY_URL_TIMESTAMP" = "Copy video link with timestamp";
+"URL_COPIED" = "Copied video link";
+"URL_TIMESTAMP_COPIED" = "Copied video link with timestamp";
+"ERROR_VIDEOID" = "Cannot get the video ID";
+"ERROR_ADS" = "Cannot copy video link while playing ads";
+"COPIED_AUTHOR" = "Copied video author";
+"COPIED_TITLE" = "Copied video title";
+"COPIED_DESCRIPTION" = "Copied video description";
+"SAVE_THUMBNAIL" = "Save thumbnail";
+"SAVE_THUMBNAIL_DESC" = "Save to Photos";
+"DOWNLOAD_CAPTIONS" = "Download captions";
+"DOWNLOAD_CAPTIONS_DESC" = "Save subtitles as .VTT file";
+"DOWNLOAD_AUDIO" = "Download audio";
+"DOWNLOAD_AUDIO_DESC" = "Download audio as .m4a file";
+"DOWNLOAD_VIDEO" = "Download video";
+"DOWNLOAD_VIDEO_DESC" = "Choose video quality";
+"OPEN_VID_BEFORE" = "Open a video before using the download manager";
+"NO_CAPTIONS_URL" = "No valid caption URLs found";
+"NO_AUDIO_URL" = "No audio URL found";
+"NO_STREAM_URL" = "No stream URL found";
+"CAPTIONS_FAILED" = "Failed to download captions";
+"NO_CAPTIONS" = "No captions available for this video";
+"NO_AUDIO_STREAM_FOUND" = "No audio streams found";
+"NO_VID_AUDIO_STREAM_FOUND" = "No video/audio streams found";
+"COPIED_VID_INFO" = "Copied all video informations";
+"SAVED_TO_PHOTOS" = "Saved to Photos";
+"SAVE_FAILED" = "Save failed";
+"PHOTO_ACCESS_DENINED" = "Photos access denied";
+"THUMBNAIL_FAILED" = "Thumbnail failed";
+"DOWNLOADING_THUMBNAIL" = "Downloading thumbnail...";
+"DOWNLOADING_AUDIO" = "Downloading audio...";
+"DOWNLOADING_VIDEO" = "Downloading video...";
+"DOWNLOADING_CAPTIONS" = "Downloading captions...";
+"NO_THUMBNAIL_FOUND" = "No thumbnail found";
+"DOWNLOAD_FAILED" = "Download failed";
+"COPY_THUMBNAIL" = "Copy thumbnail";
+"COPY_THUMBNAIL_DESC" = "Copy to clipboard";
+"SHOW_THUMBNAIL" = "Show thumbnail";
+"SHOW_THUMBNAIL_DESC" = "Show in a dialog";
+"THUMBNAIL_OPTIONS" = "Download thumbnail";
+"THUMBNAIL_OPTIONS_DESC" = "Show more options";
+"COPIED_TO_CLIPBOARD" = "Copied to clipboard";
+"DOWNLOAD_COMPLETED" = "Download completed";
+"CANNOT_SAVE_TO_PHOTOS" = "Cannot save to Photos";
+"FINA_VIDEO" = "Finalizing video...";
+"MERGING_VID_FALLBACK" = "Merging video with fallback...";
+"MERGING_VID" = "Merging video...";
+"CONVERTING" = "Converting to";
+"ALREADY_DOWNLOADING" = "Already downloading";
+"DOWNLOAD_CANCELLED" = "Download cancelled";
+"CANCEL" = "Cancel";
+"TRIMMING_AUDIO" = "Trimming audio...";
+"COPY" = "Copy";
"APPEARANCE" = "Appearance";
"OLED_THEME" = "OLED dark mode";
@@ -46,6 +122,8 @@
"OLED_KEYBOARD_DESC" = "Make the keyboard uses a pure black (OLED) color for dark theme.";
"NAVBAR" = "Navigation bar";
+"STICKY_NAVBAR" = "Sticky Navigation bar";
+"STICKY_NAVBAR_DESC" = "Pins the Navigation bar so that it remains visible when scrolling down.";
"HIDE_YT_LOGO" = "Hide YouTube logo";
"HIDE_YT_LOGO_DESC" = "Hide YouTube logo in the navigation bar.";
"PREMIUM_LOGO" = "Use YouTube Premium logo";
@@ -62,34 +140,78 @@
"FEED" = "Feed";
"HIDE_SUBBAR" = "Hide subbar";
"HIDE_SUBBAR_DESC" = "Hide subbar at the top of the feed.";
+"HIDE_HORI_SHELF" = "Remove horizontal feeds";
+"HIDE_HORI_SHELF_DESC" = "Removes all horizontal feeds (News, Continue Watching, Posts collection, etc.) from the feed, except Playables.";
"HIDE_MUSIC_PLAYLISTS" = "Hide music playlist generator";
"HIDE_MUSIC_PLAYLISTS_DESC" = "Hide music playlist generator (Select music tunes in three) in the feed.";
"HIDE_FEED_POST" = "Hide posts";
"HIDE_FEED_POST_DESC" = "Hide posts in feed.";
+"HIDE_SURVEYS" = "Hide surveys";
+"HIDE_SURVEYS_DESC" = "Hide video surveys that YouTube shows sometimes.";
"HIDE_SHORTS_SHELF" = "Hide Shorts shelf";
"HIDE_SHORTS_SHELF_DESC" = "Hide Shorts shelf in the feed, including in the Subscriptions tab.";
+"KEEP_SHORTS_SUBSCRIPT" = "Keep Shorts in Subscriptions";
+"KEEP_SHORTS_SUBSCRIPT_DESC" = "Prevents removing Shorts from Subscriptions tab.";
"HIDE_SEARCH_HISTORY" = "Hide search history";
"HIDE_SEARCH_HISTORY_DESC" = "Hide previous search history and suggestions when using the search bar.\nNOTE: Your search history will still be accessible in your other YouTube clients.";
-"HIDE_SUB_BUTTON" = "Hide Subscribe button";
-"HIDE_SUB_BUTTON_DESC" = "Hides the subscribe button in channels and video descriptions.";
-"HIDE_SHOP_BUTTON" = "Hide Shopping button";
-"HIDE_SHOP_BUTTON_DESC" = "Hides the shopping/store button in channels descriptions.";
-"HIDE_MEMBER_BUTTON" = "Hide Membership button";
-"HIDE_MEMBER_BUTTON_DESC" = "Hides the 'Join' button in channels and video descriptions.";
+"HIDE_PLAYABLES" = "Remove playables";
+"HIDE_PLAYABLES_DESC" = "Removes playables (mini-games) from the feed.";
"PLAYER" = "Player";
+"PLAYBACK_SPEED" = "Playback speed";
+"CONTROL_CENTER" = "Control Center";
+"PLAYER_ACTIONS" = "Player actions";
+"GESTURE_HEADER" = "Gestures";
+"QUALITY_WIFI" = "Playback quality on WiFi";
+"QUALITY_WIFI_DESC" = "Automatically applies the selected quality when streaming over Wi-Fi.";
+"QUALITY_CELLULAR" = "Playback quality on Cellular";
+"QUALITY_CELLULAR_DESC" = "Automatically applies the selected quality when streaming over mobile data.";
+"QUALITY_LOW_POWER" = "Playback quality on Power saver mode";
+"QUALITY_LOW_POWER_DESC" = "Automatically applies the selected quality when when Low power mode is enabled on your device.";
+"DEFAULT" = "Default";
+"BEST" = "Best";
+"AUDIO_TRACK" = "Preferred audio track";
+"ORIGINAL" = "Original";
+"SELECT_MANUALLY" = "Select manually";
+"AUDIO_TRACK_SELECT" = "Selected audio track";
+"AUDIO_TRACK_SELECT_DESC" = "Specifies the preferred audio track language to use for playback, if available.";
+"NO_AUTO_DUBBED" = "Uses original audio track";
+"NO_AUTO_DUBBED_DESC" = "Uses original audio track if the preferred language track is auto-dubbed or not found.";
+"CAPTION_TRACK" = "Preferred captions";
+"CAPTION_TRACK_SELECT" = "Selected captions";
+"CAPTION_TRACK_SELECT_DESC" = "Specifies the preferred captions language to display, if available.";
+"DISABLES_CAPTION_TRACK" = "Automatically disable captions";
+"DISABLES_CAPTION_TRACK_DESC" = "Automatically disable captions if the preferred language is auto-generated or not found.";
+"DEFAULT_SPEED" = "Default playback rate";
+"DEFAULT_SPEED_DESC" = "Overrides the default playback speed.";
+"SPEED_BUTTON" = "Speed controls button";
+"SPEED_BUTTON_DESC" = "Displays a speed controls button on overlay.";
+"MUTE_BUTTON" = "Mute button";
+"MUTE_BUTTON_DESC" = "Displays a button to mute or unmute the volume on the overlay.";
+"SHARE_BUTTON" = "Share button";
+"SHARE_BUTTON_DESC" = "Displays a button to share the video link on the overlay.";
+"LOOP_BUTTON" = "Loop button";
+"LOOP_BUTTON_DESC" = "Displays a button to loop the video on the overlay.";
+"CAPTION_BUTTON" = "Captions button";
+"CAPTION_BUTTON_DESC" = "Displays a button to copy the video captions on the overlay.";
+"HOLD_TO_SPEED" = "Hold to speed";
+"HOLD_TO_SPEED_DESC" = "Uses custom hold to speed playback rate.";
"HIDE_AUTOPLAY" = "Hide autoplay switch";
"HIDE_AUTOPLAY_DESC" = "Hide autoplay switch in the video overlay.";
"HIDE_CAPTIONS_BUTTON" = "Hide captions button";
"HIDE_CAPTIONS_BUTTON_DESC" = "Hide captions button in the video overlay.";
"HIDE_CAST_BUTTON_PLAYER" = "Hide cast button";
"HIDE_CAST_BUTTON_PLAYER_DESC" = "Hide cast button in the video overlay.";
-"HIDE_PREV_BUTTON" = "Hide previous/back button";
-"HIDE_PREV_BUTTON_DESC" = "Hide previous/back button in the video overlay.";
-"HIDE_NEXT_BUTTON" = "Hide next/skip button";
-"HIDE_NEXT_BUTTON_DESC" = "Hide next/skip button in the video overlay.";
+"HIDE_NEXT_AND_PREV_BUTTON" = "Hide previous and next buttons";
+"HIDE_NEXT_AND_PREV_BUTTON_DESC" = "Hide previous and next buttons in the video overlay.";
"REPLACE_PREVNEXT_BUTTONS" = "Replace Previous/Next buttons";
"REPLACE_PREVNEXT_BUTTONS_DESC" = "Replaces the Previous and Next video buttons in the player with Rewind and Fast Forward buttons.";
+"SKIP_BACKWARD" = "Skip backward";
+"SKIP_BACKWARD_DESC" = "Replaces the previous track button in Control Center with a skip backward button by a set number of seconds.";
+"SKIP_FORWARD" = "Skip forward";
+"SKIP_FORWARD_DESC" = "Replaces the next track button in Control Center with a skip forward button by a set number of seconds.";
+"REWIND_SECONDS" = "Rewind seconds";
+"FORWARD_SECONDS" = "Fast-forward seconds";
"REMOVE_DARK_OVERLAY" = "Remove dark overlay";
"REMOVE_DARK_OVERLAY_DESC" = "Remove dark overlay from the video overlay.";
"HIDE_END_SCREEN" = "Hide endscreen cards";
@@ -102,6 +224,12 @@
"HIDE_PAID_OVERLAY_DESC" = "Hide 'Includes paid promotion' in the video overlay.";
"HIDE_WATERMARK" = "Hide watermark";
"HIDE_WATERMARK_DESC" = "Hide channel watermark at the bottom of the video overlay.";
+"PAUSE_ON_OVERLAY" = "Pause on overlay";
+"PAUSE_ON_OVERLAY_DESC" = "Sets playback on pause if overlay appears.";
+"DISABLES_SNAP_TO_CHAPTER" = "Disable snap to chapters";
+"DISABLES_SNAP_TO_CHAPTER_DESC" = "Prevents the scrubbar stick to the chapters.";
+"DISABLES_ENGAGE_PANEL" = "Disables engagement panel in fullscreen";
+"DISABLES_ENGAGE_PANEL_DESC" = "Prevent engagement panel showing in fullscreen.";
"GESTURES" = "Enable gestures controls";
"GESTURES_DESC" = "Swipe up or down on the left/right edges of the video screen to adjust brightness, volume, or playback speed.";
"GESTURE_AREA" = "Gesture activation area";
@@ -136,6 +264,8 @@
"DISABLES_SHOW_REMAINING_DESC" = "Disable the feature that shows how much time is left in a video when tapping the timestamp.";
"ALWAYS_SHOW_REMAINING" = "Always show remaining time";
"ALWAYS_SHOW_REMAINING_DESC" = "Ensure the remaining time is always displayed by default in the player overlay.";
+"COPY_TIMESTAMP_ON_PAUSE" = "Copy timestamped links";
+"COPY_TIMESTAMP_ON_PAUSE_DESC" = "Allows to copy timestamped link to the clipboard by pressing pause button.";
"SHOW_REMAINING_EXTRA" = "Show extra remaining time";
"SHOW_REMAINING_EXTRA_DESC" = "Adds the estimated finish time to the label in the video overlay.";
"HIDE_FULLSCREEN_ACTIONS" = "Hide fullscreen actions";
@@ -148,93 +278,95 @@
"HIDE_CONTENT_WARNING_DESC" = "Automatically skip the sensitive content warning alert in some vidoes.";
"AUTO_FULLSCREEN" = "Auto fullscreen";
"AUTO_FULLSCREEN_DESC" = "Automatically enter fullscreen mode when starting video playback.";
-"AUTO_DISABLES_CAPTION" = "Auto disable captions";
-"AUTO_DISABLES_CAPTION_DESC" = "Automatically turns off closed captions (CC) whenever a new video starts playing.";
"PORTRAIT_FULLSCREEN" = "Portrait fullscren mode";
"PORTRAIT_FULLSCREEN_DESC" = "Enables portrait fullscreen mode support.";
"OLD_QUALITY_PICKER" = "Use old quality picker";
"OLD_QUALITY_PICKER_DESC" = "Reverts the video quality selection menu to the classic version (lists all resolutions immediately).";
"EXTRA_SPEED" = "Extra speed options";
"EXTRA_SPEED_DESC" = "Adds more video playback speed options up to 10x.";
-"DISABLE_HINTS" = "Disable hints";
-"DISABLE_HINTS_DESC" = "Disable the tutorial hints and interactive pop-ups.";
"FORCE_MINIPLAYER" = "Force miniplayer";
"FORCE_MINIPLAYER_DESC" = "Allows all videos, including those made for kids, to play in the miniplayer when dismissing the player.";
"FORCE_SEEKBAR" = "Always show progress bar";
"FORCE_SEEKBAR_DESC" = "Makes the progress bar (seekbar) remains visible at all times while in fullscreen mode.";
-"HIDE_LIKE_BUTTON" = "Hide Like button";
-"HIDE_LIKE_BUTTON_DESC" = "Hide Like button in the action bar under the video overlay.";
-"HIDE_DISLIKE_BUTTON" = "Hide Dislike button";
-"HIDE_DISLIKE_BUTTON_DESC" = "Hide Dislike button in the action bar under the video overlay.";
-"HIDE_SHARE_BUTTON" = "Hide Share button";
-"HIDE_SHARE_BUTTON_DESC" = "Hide Share button in the action bar under the video overlay.";
-"HIDE_DOWNLOAD_BUTTON" = "Hide Download button";
-"HIDE_DOWNLOAD_BUTTON_DESC" = "Hide Download button in the action bar under the video overlay.";
-"HIDE_CLIP_BUTTON" = "Hide Clip button";
-"HIDE_CLIP_BUTTON_DESC" = "Hide Clip button in the action bar under the video overlay.";
-"HIDE_REMIX_BUTTON" = "Hide Remix button";
-"HIDE_REMIX_BUTTON_DESC" = "Hide Remix button in the action bar under the video overlay.";
-"HIDE_SAVE_BUTTON" = "Hide Save button";
-"HIDE_SAVE_BUTTON_DESC" = "Hide Save button in the action bar under the video overlay.";
+"TAP_TO_SEEK" = "Tap to seek";
+"TAP_TO_SEEK_DESC" = "Quickly jump to any point by tapping the progress bar.";
+"DISABLES_ZOOM" = "Disable free zoom gesture";
+"DISABLES_ZOOM_DESC" = "Disables new free zoom gestures.";
+"USES_24_HOURS_TIME" = "24-hour format";
+"USES_24_HOURS_TIME_DESC" = "Shows end time in 24-hour format.";
+"PAUSE_TWO_FINGERS" = "Pause using two fingers";
+"PAUSE_TWO_FINGERS_DESC" = "Pause/Resume playback by tapping on the screen using two fingers.";
+"HIDE_COMMENTS_SECTION" = "Hide comments section";
+"HIDE_COMMENTS_SECTION_DESC" = "Completely removes the entire comments section.";
+"HIDE_COMMENTS_PREVIEW" = "Hide preview comments";
+"HIDE_COMMENTS_PREVIEW_DESC" = "Removes the preview box for the top comment while keeping the full section expandable.";
+"USE_ANOTHER_MINIPLAYER" = "Uses another miniplayer style";
+"USE_ANOTHER_MINIPLAYER_DESC" = "Uses another miniplayer style redesigned from older YT versions.";
"SHORTS" = "Shorts";
-"HIDE_SHORTS_LIKE_BUTTON" = "Hide Like button";
-"HIDE_SHORTS_LIKE_BUTTON_DESC" = "Hide the Like button in the Shorts player.";
-"HIDE_SHORTS_DISLIKE_BUTTON" = "Hide Dislike button";
-"HIDE_SHORTS_DISLIKE_BUTTON_DESC" = "Hide the Dislike button in the Shorts player.";
-"HIDE_SHORTS_COMMENT_BUTTON" = "Hide Comment button";
-"HIDE_SHORTS_COMMENT_BUTTON_DESC" = "Hide the Comment button in the Shorts player.";
-"HIDE_SHORTS_SHARE_BUTTON" = "Hide Share button";
-"HIDE_SHORTS_SHARE_BUTTON_DESC" = "Hide the Share button in the Shorts player.";
-"HIDE_SHORTS_REMIX_BUTTON" = "Hide Remix button";
-"HIDE_SHORTS_REMIX_BUTTON_DESC" = "Hide the Remix button in the Shorts player.";
-"HIDE_METADATA_BUTTON" = "Hide sound metadata button";
-"HIDE_METADATA_BUTTON_DESC" = "Hide the button that shows the sound source or music metadata in the Shorts player.";
+"SHORTS_ONLY" = "Shorts only mode";
+"SHORTS_ONLY_DESC" = "Limits YouTube functionality to viewing Shorts only.";
+"SHORTS_ACTION" = "Shorts action after the video ends";
+"LOOP" = "Loop video";
+"SKIP_TO_NEXT_SHORTS" = "Skip to next video";
+"PAUSE_SHORTS" = "Stop video";
+"REMOVE_LIVE_SHORTS" = "Remove live videos";
+"REMOVE_LIVE_SHORTS_DESC" = "Removes live videos from the feed.";
"HIDE_SHORTS_PRODUCT" = "Hide products";
"HIDE_SHORTS_PRODUCT_DESC" = "Hide tagged products and shopping links in the Shorts overlay.";
"HIDE_SHORTS_RECBAR" = "Hide recommendation bar";
"HIDE_SHORTS_RECBAR_DESC" = "Hide the suggested action bar in the Shorts player (e.g. Try this sound, Shopping, Preview comment).";
-"HIDE_SHORTS_COMMIT" = "Hide commissions bar";
-"HIDE_SHORTS_COMMIT_DESC" = "Hide the bar that shows the video got commissions in the Shorts player.";
-"HIDE_SHORTS_SUBSCRIPT_BUTTON" = "Hide subscriptions button";
-"HIDE_SHORTS_SUBSCRIPT_BUTTON_DESC" = "Hide the Subscriptions button at the top of the Shorts player.";
-"HIDE_SHORTS_LIVE_BUTTON" = "Hide live button";
-"HIDE_SHORTS_LIVE_BUTTON_DESC" = "Hide the Live button at the top of the Shorts player.";
-"HIDE_SHORTS_LENS_BUTTON" = "Hide lens button";
-"HIDE_SHORTS_LENS_BUTTON_DESC" = "Hide the Lens button at the top of the Shorts player.";
-"HIDE_SHORTS_TRENDS_BUTTON" = "Hide trends button";
-"HIDE_SHORTS_TRENDS_BUTTON_DESC" = "Hide the Trends button at the top of the Shorts player.";
-"HIDE_SHORTS_TO_VIDEO" = "Hide link to full video";
-"HIDE_SHORTS_TO_VIDEO_DESC" = "Hide the link that redirects in a Short to a full video in the Shorts player.";
"ENABLES_SHORTS_QUALITY" = "Enable Shorts quality picker";
"ENABLES_SHORTS_QUALITY_DESC" = "Force enable the video quality selection menu in Shorts.";
"SHOW_SHORTS_SEEKBAR" = "Always show seekbar";
"SHOW_SHORTS_SEEKBAR_DESC" = "Ensure the progress/seek bar is always visible at the bottom of the Shorts player.";
+"REMOVE_POSTS_SHORTS" = "Remove posts";
+"REMOVE_POSTS_SHORTS_DESC" = "Remove posts from the feed.";
+"SHORTS_FULLSCREEN" = "Pinch to Fullscreen";
+"SHORTS_FULLSCREEN_DESC" = "Manages visibility of the overlay with pinch in and pinch out gestures, displaying Shorts in fullscreen mode.";
"TABBAR" = "Tab bar";
"DEFAULT_TAB" = "Default startup tab";
-"DEFAULT_TAB_DESC" = "You can select the tab you want to open when opening the app.";
-"HOME_NAME" = "Home";
-"SUBSCRIPT_NAME" = "Subscriptions";
-"LIB_NAME" = "Library (You)";
+"FORSTED_TAB_BAR" = "Translucent tab bar";
+"ENABLED" = "Enabled";
"HIDE_TAB_INDI" = "Hide tab indicators";
"HIDE_TAB_INDI_DESC" = "Hide all tab indicators in the tab bar.";
"HIDE_TAB_LABELS" = "Hide tab labels";
"HIDE_TAB_LABELS_DESC" = "Hide all tab labels in the tab bar.";
-"HIDE_HOME_TAB" = "Hide Home tab";
-"HIDE_HOME_TAB_DESC" = "Hide Home tab in the tab bar.";
-"HIDE_SHORTS_TAB" = "Hide Shorts tab";
-"HIDE_SHORTS_TAB_DESC" = "Hide Shorts tab in the tab bar.";
-"HIDE_CREATE_BUTTON" = "Hide Create button";
-"HIDE_CREATE_BUTTON_DESC" = "Hide Create button in the tab bar.";
-"HIDE_SUBSCRIPT_TAB" = "Hide Subscriptions tab";
-"HIDE_SUBSCRIPT_TAB_DESC" = "Hide Subscriptions tab in the tab bar.";
+"HOME_TAB" = "Home";
+"SHORTS_TAB" = "Shorts";
+"CREATE_TAB" = "Create";
+"SUBSCRIPTIONS_TAB" = "Subscriptions";
+"LIBRARY_TAB" = "Library (You)";
+"HISTORY_TAB" = "History";
+"GAMING_TAB" = "Gaming";
+"SPORTS_TAB" = "Sports";
+"NOTI_TAB" = "Notifications";
+"NEWS_TAB" = "News";
+"MUSIC_TAB" = "Music";
+"WATCH_LATER_TAB" = "Watch later";
+"PLAYLIST_TAB" = "Playlists";
+"LIKE_TAB" = "Liked videos";
+"LIVE_TAB" = "Live";
+"POST_TAB" = "Posts";
+"VIDEO_TAB" = "Your videos";
+"MOVIE_TAB" = "Movies";
+"COURSE_TAB" = "Courses";
+"MINIGAME_TAB" = "Playables";
+"MANAGE_TABS" = "Manage tabs";
+"MANAGE_TABS_DESC" = "Reorder and toggle tabs";
+"TAB_REORDER_HINT" = "Drag to reorder. Toggle to show/hide.\nTab settings are also accessible by long-pressing the first tab in the tab bar.";
+"TAB_LIMIT" = "Tab limit";
+"TAB_LIMIT_DESC" = "Only 6 tabs are allowed.";
+"ZERO_TAB_DESC" = "At least 1 tab has to be enabled.";
"MISCELLANEOUS" = "Miscellaneous";
"BACKGROUND_PLAYBACK" = "Background playback";
"BACKGROUND_PLAYBACK_DESC" = "Enables background playback for videos.";
"DISABLES_SHORTS_PIP" = "Disables Shorts PiP";
"DISABLES_SHORTS_PIP_DESC" = "Try to disables Shorts PiP, this might not 100% disables it.";
+"DISABLE_HINTS" = "Disable hints";
+"DISABLE_HINTS_DESC" = "Disable the tutorial hints and interactive pop-ups.";
"BLOCK_UPGRADE_DIALOGS" = "Block upgrade dialogs";
"BLOCK_UPGRADE_DIALOGS_DESC" = "Block the YouTube dialogs that ask you to upgrade the app.";
"ARE_YOU_THERE_DIALOG" = "Hide 'Are you there?' dialog";
@@ -247,7 +379,109 @@
"DISABLES_SNACK_BAR_DESC" = "Disables snack bar when pressing some buttons or doing something in the app.";
"HIDE_STARTUP_ANIMATIONS" = "Hide YouTube's startup animations";
"HIDE_STARTUP_ANIMATIONS_DESC" = "Hide YouTube's startup animations when opening the app.";
-"HIDE_PLAY_IN_NEXT_QUEUE" = "Hide 'Play next in queue'";
-"HIDE_PLAY_IN_NEXT_QUEUE_DESC" = "Hide the 'Play next in queue' option in the flyout menu.";
"HIDE_LIKE_DISLIKE_VOTES" = "Hide like/dislike votes";
-"HIDE_LIKE_DISLIKE_VOTES_DESC" = "Hide messages that pop up when you like/dislike videos.";
\ No newline at end of file
+"HIDE_LIKE_DISLIKE_VOTES_DESC" = "Hide messages that pop up when you like/dislike videos.";
+"FLYOUT_MENU" = "Flyout menu";
+"REMOVE_PLAY_IN_NEXT_QUEUE_OPTION" = "Removes 'Play next in queue' option";
+"REMOVE_PLAY_IN_NEXT_QUEUE_OPTION_DESC" = "Removes 'Play next in queue' option from the flyout menu.";
+"REMOVE_DOWNLOAD_OPTION" = "Removes 'Download video' option";
+"REMOVE_DOWNLOAD_OPTION_DESC" = "Removes 'Download video' option from the flyout menu.";
+"REMOVE_WATCH_LATER_OPTION" = "Removes 'Save to Watch later' option";
+"REMOVE_WATCH_LATER_OPTION_DESC" = "Removes 'Save to Watch Later' option from the flyout menu.";
+"REMOVE_SAVE_OPTION" = "Removes 'Save to playlist' option";
+"REMOVE_SAVE_OPTION_DESC" = "Removes 'Save to playlist' option from the flyout menu.";
+"REMOVE_REMOVE_FROM_PLAYLIST_OPTION" = "Removes 'Remove from playlist' option";
+"REMOVE_REMOVE_FROM_PLAYLIST_OPTION_DESC" = "Removes 'Remove from playlist' option from the flyout menu.";
+"REMOVE_SHARE_OPTION" = "Removes 'Share' option";
+"REMOVE_SHARE_OPTION_DESC" = "Removes 'Share' option from the flyout menu.";
+"REMOVE_NOT_INTERESTED_OPTION" = "Removes 'Not interested' option";
+"REMOVE_NOT_INTERESTED_OPTION_DESC" = "Removes 'Not interested' option from the flyout menu.";
+"REMOVE_DONT_RECOMMEND_OPTION" = "Removes 'Don't recommend channel' option";
+"REMOVE_DONT_RECOMMEND_OPTION_DESC" = "Removes 'Don't recommend channel' option from the flyout menu.";
+"REMOVE_INFO_OPTION" = "Removes 'Why are you seeing this?' option";
+"REMOVE_INFO_OPTION_DESC" = "Removes 'Why are you seeing this?' option from the flyout menu.";
+"REMOVE_FILTER_OPTION" = "Removes 'Search filters' option";
+"REMOVE_FILTER_OPTION_DESC" = "Removes 'Search filters' option from the flyout menu.";
+"REMOVE_REPORT_OPTION" = "Removes 'Report' option";
+"REMOVE_REPORT_OPTION_DESC" = "Removes 'Report' option from the flyout menu.";
+"REMOVE_YOUTUBE_MUSIC_OPTION" = "Removes 'Listen with YouTube Music' option";
+"REMOVE_YOUTUBE_MUSIC_OPTION_DESC" = "Removes 'Listen with YouTube Music' option from the flyout menu.";
+"REMOVE_FEED_BACK_OPTION" = "Removes 'Send feedback' option";
+"REMOVE_FEED_BACK_OPTION_DESC" = "Removes 'Send feedback' option from the flyout menu.";
+"REMOVE_CAST_OPTION" = "Removes 'Watch on TV' option";
+"REMOVE_CAST_OPTION_DESC" = "Removes 'Watch on TV' option from the flyout menu.";
+"REMOVE_SHUFFLE_OPTION" = "Removes 'Shuffle' option";
+"REMOVE_SHUFFLE_OPTION_DESC" = "Removes 'Shuffle' option from the flyout menu.";
+"REMOVE_UN_SUB_OPTION" = "Removes 'Unsubscribe' option";
+"REMOVE_UN_SUB_OPTION_DESC" = "Removes 'Unsubscribe' option from the flyout menu.";
+"REMOVE_HIDE_FROM_PLAYLIST_OPTION" = "Removes 'Hide video from playlist' option";
+"REMOVE_HIDE_FROM_PLAYLIST_OPTION_DESC" = "Removes 'Hide video from playlist' option from the flyout menu.";
+"REMOVE_HELP_OPTION" = "Removes 'Help & feedback' option";
+"REMOVE_HELP_OPTION_DESC" = "Removes 'Help & feedback' option from the flyout menu.";
+"REMOVE_NOTIFY_OPTION" = "Removes 'Notify me' option";
+"REMOVE_NOTIFY_OPTION_DESC" = "Removes 'Notify me' option from the flyout menu.";
+"REMOVE_CLEARSCREEN_OPTION" = "Removes 'Clear Screen' option";
+"REMOVE_CLEARSCREEN_OPTION_DESC" = "Removes 'Clear Screen' option from the flyout menu.";
+"HIDE_COMMU_GUIDE" = "Hide community guidelines";
+"HIDE_COMMU_GUIDE_DESC" = "Hide community guidelines header.";
+"DISABLES_RTL" = "Disable RTL formatting";
+"DISABLES_RTL_DESC" = "Forcefully displays text in left-to-right (LTR) format for languages that are initially displayed in right-to-left (RTL).";
+"DEVICE_UI" = "Interface style";
+
+"SB_DESCRIPTION" = "Skip sponsored segments automatically using community-submitted data.";
+"SB_ENABLE" = "Enable SponsorBlock";
+"SB_ENABLE_DESC" = "Activate the SponsorBlock extension to skip sponsored segments.";
+"SB_SHOW_BUTTON" = "Show overlay button";
+"SB_SHOW_BUTTON_DESC" = "Display a SponsorBlock toggle button in the player overlay.";
+"SB_SHOW_NOTIFICATIONS" = "Show notifications";
+"SB_SHOW_NOTIFICATIONS_DESC" = "Display notifications when segments are skipped. Notifications for manual-skip categories are always shown.";
+"SB_HAPTIC_FEEDBACK" = "Haptic feedback";
+"SB_HAPTIC_FEEDBACK_DESC" = "Provide haptic feedback when a segment is skipped.";
+"SB_SEGMENTS_IN_PLAYER" = "Segments in player";
+"SB_SEGMENTS_IN_PLAYER_DESC" = "Display colored segments on the progress bar of the main video player.";
+"SB_SEGMENTS_IN_FEED" = "Segments in feed";
+"SB_SEGMENTS_IN_FEED_DESC" = "Display colored segments on the progress bar of players in the feed.";
+"SB_SEGMENTS_IN_MINIPLAYER" = "Segments in mini-player";
+"SB_SEGMENTS_IN_MINIPLAYER_DESC" = "Display colored segments on the progress bar of the mini-player.";
+"SB_SHOW_DURATION" = "Show duration without segments";
+"SB_SHOW_DURATION_DESC" = "Display the total video duration excluding skippable segments.";
+"SB_CATEGORIES_HEADER" = "Segment categories";
+"SB_SEGMENT_COLOR" = "Segment color";
+
+"SB_ACTION_DISABLE" = "Disable";
+"SB_ACTION_AUTO_SKIP" = "Auto-skip";
+"SB_ACTION_ASK" = "Ask before skipping";
+"SB_ACTION_DISPLAY" = "Show on seek bar only";
+"SB_ACTION_SKIP_TO" = "Skip to segment";
+
+"SB_CAT_sponsor" = "Sponsor";
+"SB_CAT_sponsor_DESC" = "Paid promotion for a product or service not directly related to the creator.";
+"SB_CAT_intro" = "Intro";
+"SB_CAT_intro_DESC" = "Intermission or intro animation typically seen at the start of a video.";
+"SB_CAT_outro" = "Endcards";
+"SB_CAT_outro_DESC" = "Endcards, credits, or outros at the end of the video.";
+"SB_CAT_interaction" = "Interaction";
+"SB_CAT_interaction_DESC" = "Reminders to like, subscribe, or follow on social media.";
+"SB_CAT_selfpromo" = "Self-promotion";
+"SB_CAT_selfpromo_DESC" = "Unpaid promotion of the creator's own products, merchandise, or other content.";
+"SB_CAT_music_offtopic" = "Non-music";
+"SB_CAT_music_offtopic_DESC" = "Non-music sections in music videos (e.g. talking, introductions).";
+"SB_CAT_preview" = "Preview";
+"SB_CAT_preview_DESC" = "Recap or preview of content from another video or previous episode.";
+"SB_CAT_hook" = "Hook/Greetings";
+"SB_CAT_hook_DESC" = "Narrated trailers for the upcoming video, greetings and goodbyes.";
+"SB_CAT_poi_highlight" = "Highlight";
+"SB_CAT_poi_highlight_DESC" = "The key moment or highlight point of the video. Use 'Skip to' to jump directly to it.";
+"SB_CAT_filler" = "Filler";
+"SB_CAT_filler_DESC" = "Tangential content, jokes, or filler that is not directly related to the main topic.";
+
+"SB_SKIPPED" = "%@ segment has been skipped";
+"SB_DETECTED" = "%@ segment detected.\nWould you like to skip the segment?";
+"SB_SKIP_NOW" = "Skip";
+"SB_UNSKIP" = "Unskip";
+"SB_JUMPED_TO_HIGHLIGHT" = "Jumped to highlight";
+"SB_JUMP_TO_HIGHLIGHT" = "Highlight available. Jump to the point?";
+"SB_SEGMENT_COLOR_SUFFIX" = "Segment color";
+"SB_SECTION_MAIN" = "Main";
+"SB_SKIP_ALERT_DURATION" = "Skip alert duration";
+"SB_UNSKIP_ALERT_DURATION" = "Unskip alert duration";
\ No newline at end of file
diff --git a/layout/Library/Application Support/YouMod.bundle/el.lproj/Localizable.strings b/layout/Library/Application Support/YouMod.bundle/el.lproj/Localizable.strings
index bccaf550..2c1a8a0f 100644
--- a/layout/Library/Application Support/YouMod.bundle/el.lproj/Localizable.strings
+++ b/layout/Library/Application Support/YouMod.bundle/el.lproj/Localizable.strings
@@ -1,11 +1,18 @@
-"NOTE" = "NOTE: Some hiding features may leave a blank box.";
"SETTINGS" = "Settings";
+"INTERFACE" = "Interface";
+
+"LOOP_DISABLED" = "Loop disabled";
+"LOOP_ENABLED" = "Loop enabled";
"NEW_ISSUES" = "Report issues or request new features";
"NEW_ISSUES_DESC" = "Found bugs or want to request new features? Tap here.";
"SOURCE_CODES" = "Source codes";
"SOURCE_CODES_DESC" = "Take a look at the codes!";
+"EXTRA" = "Extra";
+"FIX_PLAYBACK_ISSUES" = "Fix playback issues (Temporary)";
+"FIX_PLAYBACK_ISSUES_DESC" = "Try to fix video playback issues for some users. Turning this on will make the player automatically reloads itself everytime it errors or fails to play the video.";
+
"PERFER_HEADER" = "YouMod perferences";
"PERFER" = "Perferences";
"IMPORT" = "Import settings";
@@ -24,6 +31,7 @@
"ERROR_NO_KEYS_IMPORT" = "No available keys to import";
"ERROR_NO_KEYS_EXPORT" = "No available keys to export";
"OVERRIDE" = "This will override the current settings. Do you want to continue?";
+"DISABLED" = "Disabled";
"CACHE" = "Cache management";
"CLEARCACHE" = "Clear cache";
@@ -32,12 +40,80 @@
"AUTO_CLEARCACHE_DESC" = "Automatically clears the app cache on startup.";
"DOWNLOADING" = "Downloading";
-"DOWNLOAD_MANAGER" = "Download Manager";
-"DOWNLOAD_MANAGER_DESC" = "Replaces YouTube's video downloader with YouMod one. With ablity to downloading videos, audios, captions, and thumbnails";
+"DOWNLOAD_MANAGER" = "Download manager";
+"DOWNLOAD_MANAGER_DESC" = "Replaces YouTube's video downloader with YouMod one. With ablity to downloading videos, audios, captions, and thumbnails.";
"DOWNLOAD_SAVE_PHOTOS" = "Automatically save to Photos";
"DOWNLOAD_SAVE_PHOTOS_DESC" = "Automatically saves downloaded videos and images directly to your Photos app.";
-"DOWNLOAD_DRC_AUDIO" = "Prefer DRC Audio";
-"DOWNLOAD_DRC_AUDIO_DESC" = "Prioritizes downloading audio tracks with Dynamic Range Compression for more consistent volume levels.";
+"ADD_SHORTS_DOWNLOAD" = "Adds a download button to Shorts";
+"ADD_SHORTS_DOWNLOAD_DESC" = "Adds a download button to Shorts video overlay.";
+"USE_ORIG_AUDIO" = "Automatically uses original audio track";
+"USE_ORIG_AUDIO_DESC" = "Automatically uses original audio track instead of showing a dialog.";
+"DOWNLOAD_SHORTS" = "Download Shorts";
+"DOWNLOAD_SHORTS_DESC" = "Choose Shorts quality";
+"COPY_VID_INFO" = "Copy video informations";
+"COPY_VID_INFO_DESC" = "Choose what to copy";
+"COPY_ALL_VID_INFO" = "Copy all";
+"COPY_ALL_VID_INFO_DESC" = "Copy all video informations";
+"COPY_AUTHOR" = "Copy author";
+"COPY_AUTHOR_DESC" = "Copy the video author";
+"COPY_TITLE" = "Copy title";
+"COPY_TITLE_DESC" = "Copy the video title";
+"COPY_DESCRIPTION" = "Copy description";
+"COPY_DESCRIPTION_DESC" = "Copy the video description";
+"COPY_URL" = "Copy video link";
+"COPY_URL_TIMESTAMP" = "Copy video link with timestamp";
+"URL_COPIED" = "Copied video link";
+"URL_TIMESTAMP_COPIED" = "Copied video link with timestamp";
+"ERROR_VIDEOID" = "Cannot get the video ID";
+"ERROR_ADS" = "Cannot copy video link while playing ads";
+"COPIED_AUTHOR" = "Copied video author";
+"COPIED_TITLE" = "Copied video title";
+"COPIED_DESCRIPTION" = "Copied video description";
+"SAVE_THUMBNAIL" = "Save thumbnail";
+"SAVE_THUMBNAIL_DESC" = "Save to Photos";
+"DOWNLOAD_CAPTIONS" = "Download captions";
+"DOWNLOAD_CAPTIONS_DESC" = "Save subtitles as .VTT file";
+"DOWNLOAD_AUDIO" = "Download audio";
+"DOWNLOAD_AUDIO_DESC" = "Download audio as .m4a file";
+"DOWNLOAD_VIDEO" = "Download video";
+"DOWNLOAD_VIDEO_DESC" = "Choose video quality";
+"OPEN_VID_BEFORE" = "Open a video before using the download manager";
+"NO_CAPTIONS_URL" = "No valid caption URLs found";
+"NO_AUDIO_URL" = "No audio URL found";
+"NO_STREAM_URL" = "No stream URL found";
+"CAPTIONS_FAILED" = "Failed to download captions";
+"NO_CAPTIONS" = "No captions available for this video";
+"NO_AUDIO_STREAM_FOUND" = "No audio streams found";
+"NO_VID_AUDIO_STREAM_FOUND" = "No video/audio streams found";
+"COPIED_VID_INFO" = "Copied all video informations";
+"SAVED_TO_PHOTOS" = "Saved to Photos";
+"SAVE_FAILED" = "Save failed";
+"PHOTO_ACCESS_DENINED" = "Photos access denied";
+"THUMBNAIL_FAILED" = "Thumbnail failed";
+"DOWNLOADING_THUMBNAIL" = "Downloading thumbnail...";
+"DOWNLOADING_AUDIO" = "Downloading audio...";
+"DOWNLOADING_VIDEO" = "Downloading video...";
+"DOWNLOADING_CAPTIONS" = "Downloading captions...";
+"NO_THUMBNAIL_FOUND" = "No thumbnail found";
+"DOWNLOAD_FAILED" = "Download failed";
+"COPY_THUMBNAIL" = "Copy thumbnail";
+"COPY_THUMBNAIL_DESC" = "Copy to clipboard";
+"SHOW_THUMBNAIL" = "Show thumbnail";
+"SHOW_THUMBNAIL_DESC" = "Show in a dialog";
+"THUMBNAIL_OPTIONS" = "Download thumbnail";
+"THUMBNAIL_OPTIONS_DESC" = "Show more options";
+"COPIED_TO_CLIPBOARD" = "Copied to clipboard";
+"DOWNLOAD_COMPLETED" = "Download completed";
+"CANNOT_SAVE_TO_PHOTOS" = "Cannot save to Photos";
+"FINA_VIDEO" = "Finalizing video...";
+"MERGING_VID_FALLBACK" = "Merging video with fallback...";
+"MERGING_VID" = "Merging video...";
+"CONVERTING" = "Converting to";
+"ALREADY_DOWNLOADING" = "Already downloading";
+"DOWNLOAD_CANCELLED" = "Download cancelled";
+"CANCEL" = "Cancel";
+"TRIMMING_AUDIO" = "Trimming audio...";
+"COPY" = "Copy";
"APPEARANCE" = "Appearance";
"OLED_THEME" = "OLED dark mode";
@@ -46,6 +122,8 @@
"OLED_KEYBOARD_DESC" = "Make the keyboard uses a pure black (OLED) color for dark theme.";
"NAVBAR" = "Navigation bar";
+"STICKY_NAVBAR" = "Sticky Navigation bar";
+"STICKY_NAVBAR_DESC" = "Pins the Navigation bar so that it remains visible when scrolling down.";
"HIDE_YT_LOGO" = "Hide YouTube logo";
"HIDE_YT_LOGO_DESC" = "Hide YouTube logo in the navigation bar.";
"PREMIUM_LOGO" = "Use YouTube Premium logo";
@@ -62,34 +140,78 @@
"FEED" = "Feed";
"HIDE_SUBBAR" = "Hide subbar";
"HIDE_SUBBAR_DESC" = "Hide subbar at the top of the feed.";
+"HIDE_HORI_SHELF" = "Remove horizontal feeds";
+"HIDE_HORI_SHELF_DESC" = "Removes all horizontal feeds (News, Continue Watching, Posts collection, etc.) from the feed, except Playables.";
"HIDE_MUSIC_PLAYLISTS" = "Hide music playlist generator";
"HIDE_MUSIC_PLAYLISTS_DESC" = "Hide music playlist generator (Select music tunes in three) in the feed.";
"HIDE_FEED_POST" = "Hide posts";
"HIDE_FEED_POST_DESC" = "Hide posts in feed.";
+"HIDE_SURVEYS" = "Hide surveys";
+"HIDE_SURVEYS_DESC" = "Hide video surveys that YouTube shows sometimes.";
"HIDE_SHORTS_SHELF" = "Hide Shorts shelf";
"HIDE_SHORTS_SHELF_DESC" = "Hide Shorts shelf in the feed, including in the Subscriptions tab.";
+"KEEP_SHORTS_SUBSCRIPT" = "Keep Shorts in Subscriptions";
+"KEEP_SHORTS_SUBSCRIPT_DESC" = "Prevents removing Shorts from Subscriptions tab.";
"HIDE_SEARCH_HISTORY" = "Hide search history";
"HIDE_SEARCH_HISTORY_DESC" = "Hide previous search history and suggestions when using the search bar.\nNOTE: Your search history will still be accessible in your other YouTube clients.";
-"HIDE_SUB_BUTTON" = "Hide Subscribe button";
-"HIDE_SUB_BUTTON_DESC" = "Hides the subscribe button in channels and video descriptions.";
-"HIDE_SHOP_BUTTON" = "Hide Shopping button";
-"HIDE_SHOP_BUTTON_DESC" = "Hides the shopping/store button in channels descriptions.";
-"HIDE_MEMBER_BUTTON" = "Hide Membership button";
-"HIDE_MEMBER_BUTTON_DESC" = "Hides the 'Join' button in channels and video descriptions.";
+"HIDE_PLAYABLES" = "Remove playables";
+"HIDE_PLAYABLES_DESC" = "Removes playables (mini-games) from the feed.";
"PLAYER" = "Player";
+"PLAYBACK_SPEED" = "Playback speed";
+"CONTROL_CENTER" = "Control Center";
+"PLAYER_ACTIONS" = "Player actions";
+"GESTURE_HEADER" = "Gestures";
+"QUALITY_WIFI" = "Playback quality on WiFi";
+"QUALITY_WIFI_DESC" = "Automatically applies the selected quality when streaming over Wi-Fi.";
+"QUALITY_CELLULAR" = "Playback quality on Cellular";
+"QUALITY_CELLULAR_DESC" = "Automatically applies the selected quality when streaming over mobile data.";
+"QUALITY_LOW_POWER" = "Playback quality on Power saver mode";
+"QUALITY_LOW_POWER_DESC" = "Automatically applies the selected quality when when Low power mode is enabled on your device.";
+"DEFAULT" = "Default";
+"BEST" = "Best";
+"AUDIO_TRACK" = "Preferred audio track";
+"ORIGINAL" = "Original";
+"SELECT_MANUALLY" = "Select manually";
+"AUDIO_TRACK_SELECT" = "Selected audio track";
+"AUDIO_TRACK_SELECT_DESC" = "Specifies the preferred audio track language to use for playback, if available.";
+"NO_AUTO_DUBBED" = "Uses original audio track";
+"NO_AUTO_DUBBED_DESC" = "Uses original audio track if the preferred language track is auto-dubbed or not found.";
+"CAPTION_TRACK" = "Preferred captions";
+"CAPTION_TRACK_SELECT" = "Selected captions";
+"CAPTION_TRACK_SELECT_DESC" = "Specifies the preferred captions language to display, if available.";
+"DISABLES_CAPTION_TRACK" = "Automatically disable captions";
+"DISABLES_CAPTION_TRACK_DESC" = "Automatically disable captions if the preferred language is auto-generated or not found.";
+"DEFAULT_SPEED" = "Default playback rate";
+"DEFAULT_SPEED_DESC" = "Overrides the default playback speed.";
+"SPEED_BUTTON" = "Speed controls button";
+"SPEED_BUTTON_DESC" = "Displays a speed controls button on overlay.";
+"MUTE_BUTTON" = "Mute button";
+"MUTE_BUTTON_DESC" = "Displays a button to mute or unmute the volume on the overlay.";
+"SHARE_BUTTON" = "Share button";
+"SHARE_BUTTON_DESC" = "Displays a button to share the video link on the overlay.";
+"LOOP_BUTTON" = "Loop button";
+"LOOP_BUTTON_DESC" = "Displays a button to loop the video on the overlay.";
+"CAPTION_BUTTON" = "Captions button";
+"CAPTION_BUTTON_DESC" = "Displays a button to copy the video captions on the overlay.";
+"HOLD_TO_SPEED" = "Hold to speed";
+"HOLD_TO_SPEED_DESC" = "Uses custom hold to speed playback rate.";
"HIDE_AUTOPLAY" = "Hide autoplay switch";
"HIDE_AUTOPLAY_DESC" = "Hide autoplay switch in the video overlay.";
"HIDE_CAPTIONS_BUTTON" = "Hide captions button";
"HIDE_CAPTIONS_BUTTON_DESC" = "Hide captions button in the video overlay.";
"HIDE_CAST_BUTTON_PLAYER" = "Hide cast button";
"HIDE_CAST_BUTTON_PLAYER_DESC" = "Hide cast button in the video overlay.";
-"HIDE_PREV_BUTTON" = "Hide previous/back button";
-"HIDE_PREV_BUTTON_DESC" = "Hide previous/back button in the video overlay.";
-"HIDE_NEXT_BUTTON" = "Hide next/skip button";
-"HIDE_NEXT_BUTTON_DESC" = "Hide next/skip button in the video overlay.";
+"HIDE_NEXT_AND_PREV_BUTTON" = "Hide previous and next buttons";
+"HIDE_NEXT_AND_PREV_BUTTON_DESC" = "Hide previous and next buttons in the video overlay.";
"REPLACE_PREVNEXT_BUTTONS" = "Replace Previous/Next buttons";
"REPLACE_PREVNEXT_BUTTONS_DESC" = "Replaces the Previous and Next video buttons in the player with Rewind and Fast Forward buttons.";
+"SKIP_BACKWARD" = "Skip backward";
+"SKIP_BACKWARD_DESC" = "Replaces the previous track button in Control Center with a skip backward button by a set number of seconds.";
+"SKIP_FORWARD" = "Skip forward";
+"SKIP_FORWARD_DESC" = "Replaces the next track button in Control Center with a skip forward button by a set number of seconds.";
+"REWIND_SECONDS" = "Rewind seconds";
+"FORWARD_SECONDS" = "Fast-forward seconds";
"REMOVE_DARK_OVERLAY" = "Remove dark overlay";
"REMOVE_DARK_OVERLAY_DESC" = "Remove dark overlay from the video overlay.";
"HIDE_END_SCREEN" = "Hide endscreen cards";
@@ -102,6 +224,12 @@
"HIDE_PAID_OVERLAY_DESC" = "Hide 'Includes paid promotion' in the video overlay.";
"HIDE_WATERMARK" = "Hide watermark";
"HIDE_WATERMARK_DESC" = "Hide channel watermark at the bottom of the video overlay.";
+"PAUSE_ON_OVERLAY" = "Pause on overlay";
+"PAUSE_ON_OVERLAY_DESC" = "Sets playback on pause if overlay appears.";
+"DISABLES_SNAP_TO_CHAPTER" = "Disable snap to chapters";
+"DISABLES_SNAP_TO_CHAPTER_DESC" = "Prevents the scrubbar stick to the chapters.";
+"DISABLES_ENGAGE_PANEL" = "Disables engagement panel in fullscreen";
+"DISABLES_ENGAGE_PANEL_DESC" = "Prevent engagement panel showing in fullscreen.";
"GESTURES" = "Enable gestures controls";
"GESTURES_DESC" = "Swipe up or down on the left/right edges of the video screen to adjust brightness, volume, or playback speed.";
"GESTURE_AREA" = "Gesture activation area";
@@ -136,6 +264,8 @@
"DISABLES_SHOW_REMAINING_DESC" = "Disable the feature that shows how much time is left in a video when tapping the timestamp.";
"ALWAYS_SHOW_REMAINING" = "Always show remaining time";
"ALWAYS_SHOW_REMAINING_DESC" = "Ensure the remaining time is always displayed by default in the player overlay.";
+"COPY_TIMESTAMP_ON_PAUSE" = "Copy timestamped links";
+"COPY_TIMESTAMP_ON_PAUSE_DESC" = "Allows to copy timestamped link to the clipboard by pressing pause button.";
"SHOW_REMAINING_EXTRA" = "Show extra remaining time";
"SHOW_REMAINING_EXTRA_DESC" = "Adds the estimated finish time to the label in the video overlay.";
"HIDE_FULLSCREEN_ACTIONS" = "Hide fullscreen actions";
@@ -148,93 +278,95 @@
"HIDE_CONTENT_WARNING_DESC" = "Automatically skip the sensitive content warning alert in some vidoes.";
"AUTO_FULLSCREEN" = "Auto fullscreen";
"AUTO_FULLSCREEN_DESC" = "Automatically enter fullscreen mode when starting video playback.";
-"AUTO_DISABLES_CAPTION" = "Auto disable captions";
-"AUTO_DISABLES_CAPTION_DESC" = "Automatically turns off closed captions (CC) whenever a new video starts playing.";
"PORTRAIT_FULLSCREEN" = "Portrait fullscren mode";
"PORTRAIT_FULLSCREEN_DESC" = "Enables portrait fullscreen mode support.";
"OLD_QUALITY_PICKER" = "Use old quality picker";
"OLD_QUALITY_PICKER_DESC" = "Reverts the video quality selection menu to the classic version (lists all resolutions immediately).";
"EXTRA_SPEED" = "Extra speed options";
"EXTRA_SPEED_DESC" = "Adds more video playback speed options up to 10x.";
-"DISABLE_HINTS" = "Disable hints";
-"DISABLE_HINTS_DESC" = "Disable the tutorial hints and interactive pop-ups.";
"FORCE_MINIPLAYER" = "Force miniplayer";
"FORCE_MINIPLAYER_DESC" = "Allows all videos, including those made for kids, to play in the miniplayer when dismissing the player.";
"FORCE_SEEKBAR" = "Always show progress bar";
"FORCE_SEEKBAR_DESC" = "Makes the progress bar (seekbar) remains visible at all times while in fullscreen mode.";
-"HIDE_LIKE_BUTTON" = "Hide Like button";
-"HIDE_LIKE_BUTTON_DESC" = "Hide Like button in the action bar under the video overlay.";
-"HIDE_DISLIKE_BUTTON" = "Hide Dislike button";
-"HIDE_DISLIKE_BUTTON_DESC" = "Hide Dislike button in the action bar under the video overlay.";
-"HIDE_SHARE_BUTTON" = "Hide Share button";
-"HIDE_SHARE_BUTTON_DESC" = "Hide Share button in the action bar under the video overlay.";
-"HIDE_DOWNLOAD_BUTTON" = "Hide Download button";
-"HIDE_DOWNLOAD_BUTTON_DESC" = "Hide Download button in the action bar under the video overlay.";
-"HIDE_CLIP_BUTTON" = "Hide Clip button";
-"HIDE_CLIP_BUTTON_DESC" = "Hide Clip button in the action bar under the video overlay.";
-"HIDE_REMIX_BUTTON" = "Hide Remix button";
-"HIDE_REMIX_BUTTON_DESC" = "Hide Remix button in the action bar under the video overlay.";
-"HIDE_SAVE_BUTTON" = "Hide Save button";
-"HIDE_SAVE_BUTTON_DESC" = "Hide Save button in the action bar under the video overlay.";
+"TAP_TO_SEEK" = "Tap to seek";
+"TAP_TO_SEEK_DESC" = "Quickly jump to any point by tapping the progress bar.";
+"DISABLES_ZOOM" = "Disable free zoom gesture";
+"DISABLES_ZOOM_DESC" = "Disables new free zoom gestures.";
+"USES_24_HOURS_TIME" = "24-hour format";
+"USES_24_HOURS_TIME_DESC" = "Shows end time in 24-hour format.";
+"PAUSE_TWO_FINGERS" = "Pause using two fingers";
+"PAUSE_TWO_FINGERS_DESC" = "Pause/Resume playback by tapping on the screen using two fingers.";
+"HIDE_COMMENTS_SECTION" = "Hide comments section";
+"HIDE_COMMENTS_SECTION_DESC" = "Completely removes the entire comments section.";
+"HIDE_COMMENTS_PREVIEW" = "Hide preview comments";
+"HIDE_COMMENTS_PREVIEW_DESC" = "Removes the preview box for the top comment while keeping the full section expandable.";
+"USE_ANOTHER_MINIPLAYER" = "Uses another miniplayer style";
+"USE_ANOTHER_MINIPLAYER_DESC" = "Uses another miniplayer style redesigned from older YT versions.";
"SHORTS" = "Shorts";
-"HIDE_SHORTS_LIKE_BUTTON" = "Hide Like button";
-"HIDE_SHORTS_LIKE_BUTTON_DESC" = "Hide the Like button in the Shorts player.";
-"HIDE_SHORTS_DISLIKE_BUTTON" = "Hide Dislike button";
-"HIDE_SHORTS_DISLIKE_BUTTON_DESC" = "Hide the Dislike button in the Shorts player.";
-"HIDE_SHORTS_COMMENT_BUTTON" = "Hide Comment button";
-"HIDE_SHORTS_COMMENT_BUTTON_DESC" = "Hide the Comment button in the Shorts player.";
-"HIDE_SHORTS_SHARE_BUTTON" = "Hide Share button";
-"HIDE_SHORTS_SHARE_BUTTON_DESC" = "Hide the Share button in the Shorts player.";
-"HIDE_SHORTS_REMIX_BUTTON" = "Hide Remix button";
-"HIDE_SHORTS_REMIX_BUTTON_DESC" = "Hide the Remix button in the Shorts player.";
-"HIDE_METADATA_BUTTON" = "Hide sound metadata button";
-"HIDE_METADATA_BUTTON_DESC" = "Hide the button that shows the sound source or music metadata in the Shorts player.";
+"SHORTS_ONLY" = "Shorts only mode";
+"SHORTS_ONLY_DESC" = "Limits YouTube functionality to viewing Shorts only.";
+"SHORTS_ACTION" = "Shorts action after the video ends";
+"LOOP" = "Loop video";
+"SKIP_TO_NEXT_SHORTS" = "Skip to next video";
+"PAUSE_SHORTS" = "Stop video";
+"REMOVE_LIVE_SHORTS" = "Remove live videos";
+"REMOVE_LIVE_SHORTS_DESC" = "Removes live videos from the feed.";
"HIDE_SHORTS_PRODUCT" = "Hide products";
"HIDE_SHORTS_PRODUCT_DESC" = "Hide tagged products and shopping links in the Shorts overlay.";
"HIDE_SHORTS_RECBAR" = "Hide recommendation bar";
"HIDE_SHORTS_RECBAR_DESC" = "Hide the suggested action bar in the Shorts player (e.g. Try this sound, Shopping, Preview comment).";
-"HIDE_SHORTS_COMMIT" = "Hide commissions bar";
-"HIDE_SHORTS_COMMIT_DESC" = "Hide the bar that shows the video got commissions in the Shorts player.";
-"HIDE_SHORTS_SUBSCRIPT_BUTTON" = "Hide subscriptions button";
-"HIDE_SHORTS_SUBSCRIPT_BUTTON_DESC" = "Hide the Subscriptions button at the top of the Shorts player.";
-"HIDE_SHORTS_LIVE_BUTTON" = "Hide live button";
-"HIDE_SHORTS_LIVE_BUTTON_DESC" = "Hide the Live button at the top of the Shorts player.";
-"HIDE_SHORTS_LENS_BUTTON" = "Hide lens button";
-"HIDE_SHORTS_LENS_BUTTON_DESC" = "Hide the Lens button at the top of the Shorts player.";
-"HIDE_SHORTS_TRENDS_BUTTON" = "Hide trends button";
-"HIDE_SHORTS_TRENDS_BUTTON_DESC" = "Hide the Trends button at the top of the Shorts player.";
-"HIDE_SHORTS_TO_VIDEO" = "Hide link to full video";
-"HIDE_SHORTS_TO_VIDEO_DESC" = "Hide the link that redirects in a Short to a full video in the Shorts player.";
"ENABLES_SHORTS_QUALITY" = "Enable Shorts quality picker";
"ENABLES_SHORTS_QUALITY_DESC" = "Force enable the video quality selection menu in Shorts.";
"SHOW_SHORTS_SEEKBAR" = "Always show seekbar";
"SHOW_SHORTS_SEEKBAR_DESC" = "Ensure the progress/seek bar is always visible at the bottom of the Shorts player.";
+"REMOVE_POSTS_SHORTS" = "Remove posts";
+"REMOVE_POSTS_SHORTS_DESC" = "Remove posts from the feed.";
+"SHORTS_FULLSCREEN" = "Pinch to Fullscreen";
+"SHORTS_FULLSCREEN_DESC" = "Manages visibility of the overlay with pinch in and pinch out gestures, displaying Shorts in fullscreen mode.";
"TABBAR" = "Tab bar";
"DEFAULT_TAB" = "Default startup tab";
-"DEFAULT_TAB_DESC" = "You can select the tab you want to open when opening the app.";
-"HOME_NAME" = "Home";
-"SUBSCRIPT_NAME" = "Subscriptions";
-"LIB_NAME" = "Library (You)";
+"FORSTED_TAB_BAR" = "Translucent tab bar";
+"ENABLED" = "Enabled";
"HIDE_TAB_INDI" = "Hide tab indicators";
"HIDE_TAB_INDI_DESC" = "Hide all tab indicators in the tab bar.";
"HIDE_TAB_LABELS" = "Hide tab labels";
"HIDE_TAB_LABELS_DESC" = "Hide all tab labels in the tab bar.";
-"HIDE_HOME_TAB" = "Hide Home tab";
-"HIDE_HOME_TAB_DESC" = "Hide Home tab in the tab bar.";
-"HIDE_SHORTS_TAB" = "Hide Shorts tab";
-"HIDE_SHORTS_TAB_DESC" = "Hide Shorts tab in the tab bar.";
-"HIDE_CREATE_BUTTON" = "Hide Create button";
-"HIDE_CREATE_BUTTON_DESC" = "Hide Create button in the tab bar.";
-"HIDE_SUBSCRIPT_TAB" = "Hide Subscriptions tab";
-"HIDE_SUBSCRIPT_TAB_DESC" = "Hide Subscriptions tab in the tab bar.";
+"HOME_TAB" = "Home";
+"SHORTS_TAB" = "Shorts";
+"CREATE_TAB" = "Create";
+"SUBSCRIPTIONS_TAB" = "Subscriptions";
+"LIBRARY_TAB" = "Library (You)";
+"HISTORY_TAB" = "History";
+"GAMING_TAB" = "Gaming";
+"SPORTS_TAB" = "Sports";
+"NOTI_TAB" = "Notifications";
+"NEWS_TAB" = "News";
+"MUSIC_TAB" = "Music";
+"WATCH_LATER_TAB" = "Watch later";
+"PLAYLIST_TAB" = "Playlists";
+"LIKE_TAB" = "Liked videos";
+"LIVE_TAB" = "Live";
+"POST_TAB" = "Posts";
+"VIDEO_TAB" = "Your videos";
+"MOVIE_TAB" = "Movies";
+"COURSE_TAB" = "Courses";
+"MINIGAME_TAB" = "Playables";
+"MANAGE_TABS" = "Manage tabs";
+"MANAGE_TABS_DESC" = "Reorder and toggle tabs";
+"TAB_REORDER_HINT" = "Drag to reorder. Toggle to show/hide.\nTab settings are also accessible by long-pressing the first tab in the tab bar.";
+"TAB_LIMIT" = "Tab limit";
+"TAB_LIMIT_DESC" = "Only 6 tabs are allowed.";
+"ZERO_TAB_DESC" = "At least 1 tab has to be enabled.";
"MISCELLANEOUS" = "Miscellaneous";
"BACKGROUND_PLAYBACK" = "Background playback";
"BACKGROUND_PLAYBACK_DESC" = "Enables background playback for videos.";
"DISABLES_SHORTS_PIP" = "Disables Shorts PiP";
"DISABLES_SHORTS_PIP_DESC" = "Try to disables Shorts PiP, this might not 100% disables it.";
+"DISABLE_HINTS" = "Disable hints";
+"DISABLE_HINTS_DESC" = "Disable the tutorial hints and interactive pop-ups.";
"BLOCK_UPGRADE_DIALOGS" = "Block upgrade dialogs";
"BLOCK_UPGRADE_DIALOGS_DESC" = "Block the YouTube dialogs that ask you to upgrade the app.";
"ARE_YOU_THERE_DIALOG" = "Hide 'Are you there?' dialog";
@@ -247,7 +379,109 @@
"DISABLES_SNACK_BAR_DESC" = "Disables snack bar when pressing some buttons or doing something in the app.";
"HIDE_STARTUP_ANIMATIONS" = "Hide YouTube's startup animations";
"HIDE_STARTUP_ANIMATIONS_DESC" = "Hide YouTube's startup animations when opening the app.";
-"HIDE_PLAY_IN_NEXT_QUEUE" = "Hide 'Play next in queue'";
-"HIDE_PLAY_IN_NEXT_QUEUE_DESC" = "Hide the 'Play next in queue' option in the flyout menu.";
"HIDE_LIKE_DISLIKE_VOTES" = "Hide like/dislike votes";
-"HIDE_LIKE_DISLIKE_VOTES_DESC" = "Hide messages that pop up when you like/dislike videos.";
\ No newline at end of file
+"HIDE_LIKE_DISLIKE_VOTES_DESC" = "Hide messages that pop up when you like/dislike videos.";
+"FLYOUT_MENU" = "Flyout menu";
+"REMOVE_PLAY_IN_NEXT_QUEUE_OPTION" = "Removes 'Play next in queue' option";
+"REMOVE_PLAY_IN_NEXT_QUEUE_OPTION_DESC" = "Removes 'Play next in queue' option from the flyout menu.";
+"REMOVE_DOWNLOAD_OPTION" = "Removes 'Download video' option";
+"REMOVE_DOWNLOAD_OPTION_DESC" = "Removes 'Download video' option from the flyout menu.";
+"REMOVE_WATCH_LATER_OPTION" = "Removes 'Save to Watch later' option";
+"REMOVE_WATCH_LATER_OPTION_DESC" = "Removes 'Save to Watch Later' option from the flyout menu.";
+"REMOVE_SAVE_OPTION" = "Removes 'Save to playlist' option";
+"REMOVE_SAVE_OPTION_DESC" = "Removes 'Save to playlist' option from the flyout menu.";
+"REMOVE_REMOVE_FROM_PLAYLIST_OPTION" = "Removes 'Remove from playlist' option";
+"REMOVE_REMOVE_FROM_PLAYLIST_OPTION_DESC" = "Removes 'Remove from playlist' option from the flyout menu.";
+"REMOVE_SHARE_OPTION" = "Removes 'Share' option";
+"REMOVE_SHARE_OPTION_DESC" = "Removes 'Share' option from the flyout menu.";
+"REMOVE_NOT_INTERESTED_OPTION" = "Removes 'Not interested' option";
+"REMOVE_NOT_INTERESTED_OPTION_DESC" = "Removes 'Not interested' option from the flyout menu.";
+"REMOVE_DONT_RECOMMEND_OPTION" = "Removes 'Don't recommend channel' option";
+"REMOVE_DONT_RECOMMEND_OPTION_DESC" = "Removes 'Don't recommend channel' option from the flyout menu.";
+"REMOVE_INFO_OPTION" = "Removes 'Why are you seeing this?' option";
+"REMOVE_INFO_OPTION_DESC" = "Removes 'Why are you seeing this?' option from the flyout menu.";
+"REMOVE_FILTER_OPTION" = "Removes 'Search filters' option";
+"REMOVE_FILTER_OPTION_DESC" = "Removes 'Search filters' option from the flyout menu.";
+"REMOVE_REPORT_OPTION" = "Removes 'Report' option";
+"REMOVE_REPORT_OPTION_DESC" = "Removes 'Report' option from the flyout menu.";
+"REMOVE_YOUTUBE_MUSIC_OPTION" = "Removes 'Listen with YouTube Music' option";
+"REMOVE_YOUTUBE_MUSIC_OPTION_DESC" = "Removes 'Listen with YouTube Music' option from the flyout menu.";
+"REMOVE_FEED_BACK_OPTION" = "Removes 'Send feedback' option";
+"REMOVE_FEED_BACK_OPTION_DESC" = "Removes 'Send feedback' option from the flyout menu.";
+"REMOVE_CAST_OPTION" = "Removes 'Watch on TV' option";
+"REMOVE_CAST_OPTION_DESC" = "Removes 'Watch on TV' option from the flyout menu.";
+"REMOVE_SHUFFLE_OPTION" = "Removes 'Shuffle' option";
+"REMOVE_SHUFFLE_OPTION_DESC" = "Removes 'Shuffle' option from the flyout menu.";
+"REMOVE_UN_SUB_OPTION" = "Removes 'Unsubscribe' option";
+"REMOVE_UN_SUB_OPTION_DESC" = "Removes 'Unsubscribe' option from the flyout menu.";
+"REMOVE_HIDE_FROM_PLAYLIST_OPTION" = "Removes 'Hide video from playlist' option";
+"REMOVE_HIDE_FROM_PLAYLIST_OPTION_DESC" = "Removes 'Hide video from playlist' option from the flyout menu.";
+"REMOVE_HELP_OPTION" = "Removes 'Help & feedback' option";
+"REMOVE_HELP_OPTION_DESC" = "Removes 'Help & feedback' option from the flyout menu.";
+"REMOVE_NOTIFY_OPTION" = "Removes 'Notify me' option";
+"REMOVE_NOTIFY_OPTION_DESC" = "Removes 'Notify me' option from the flyout menu.";
+"REMOVE_CLEARSCREEN_OPTION" = "Removes 'Clear Screen' option";
+"REMOVE_CLEARSCREEN_OPTION_DESC" = "Removes 'Clear Screen' option from the flyout menu.";
+"HIDE_COMMU_GUIDE" = "Hide community guidelines";
+"HIDE_COMMU_GUIDE_DESC" = "Hide community guidelines header.";
+"DISABLES_RTL" = "Disable RTL formatting";
+"DISABLES_RTL_DESC" = "Forcefully displays text in left-to-right (LTR) format for languages that are initially displayed in right-to-left (RTL).";
+"DEVICE_UI" = "Interface style";
+
+"SB_DESCRIPTION" = "Skip sponsored segments automatically using community-submitted data.";
+"SB_ENABLE" = "Enable SponsorBlock";
+"SB_ENABLE_DESC" = "Activate the SponsorBlock extension to skip sponsored segments.";
+"SB_SHOW_BUTTON" = "Show overlay button";
+"SB_SHOW_BUTTON_DESC" = "Display a SponsorBlock toggle button in the player overlay.";
+"SB_SHOW_NOTIFICATIONS" = "Show notifications";
+"SB_SHOW_NOTIFICATIONS_DESC" = "Display notifications when segments are skipped. Notifications for manual-skip categories are always shown.";
+"SB_HAPTIC_FEEDBACK" = "Haptic feedback";
+"SB_HAPTIC_FEEDBACK_DESC" = "Provide haptic feedback when a segment is skipped.";
+"SB_SEGMENTS_IN_PLAYER" = "Segments in player";
+"SB_SEGMENTS_IN_PLAYER_DESC" = "Display colored segments on the progress bar of the main video player.";
+"SB_SEGMENTS_IN_FEED" = "Segments in feed";
+"SB_SEGMENTS_IN_FEED_DESC" = "Display colored segments on the progress bar of players in the feed.";
+"SB_SEGMENTS_IN_MINIPLAYER" = "Segments in mini-player";
+"SB_SEGMENTS_IN_MINIPLAYER_DESC" = "Display colored segments on the progress bar of the mini-player.";
+"SB_SHOW_DURATION" = "Show duration without segments";
+"SB_SHOW_DURATION_DESC" = "Display the total video duration excluding skippable segments.";
+"SB_CATEGORIES_HEADER" = "Segment categories";
+"SB_SEGMENT_COLOR" = "Segment color";
+
+"SB_ACTION_DISABLE" = "Disable";
+"SB_ACTION_AUTO_SKIP" = "Auto-skip";
+"SB_ACTION_ASK" = "Ask before skipping";
+"SB_ACTION_DISPLAY" = "Show on seek bar only";
+"SB_ACTION_SKIP_TO" = "Skip to segment";
+
+"SB_CAT_sponsor" = "Sponsor";
+"SB_CAT_sponsor_DESC" = "Paid promotion for a product or service not directly related to the creator.";
+"SB_CAT_intro" = "Intro";
+"SB_CAT_intro_DESC" = "Intermission or intro animation typically seen at the start of a video.";
+"SB_CAT_outro" = "Endcards";
+"SB_CAT_outro_DESC" = "Endcards, credits, or outros at the end of the video.";
+"SB_CAT_interaction" = "Interaction";
+"SB_CAT_interaction_DESC" = "Reminders to like, subscribe, or follow on social media.";
+"SB_CAT_selfpromo" = "Self-promotion";
+"SB_CAT_selfpromo_DESC" = "Unpaid promotion of the creator's own products, merchandise, or other content.";
+"SB_CAT_music_offtopic" = "Non-music";
+"SB_CAT_music_offtopic_DESC" = "Non-music sections in music videos (e.g. talking, introductions).";
+"SB_CAT_preview" = "Preview";
+"SB_CAT_preview_DESC" = "Recap or preview of content from another video or previous episode.";
+"SB_CAT_hook" = "Hook/Greetings";
+"SB_CAT_hook_DESC" = "Narrated trailers for the upcoming video, greetings and goodbyes.";
+"SB_CAT_poi_highlight" = "Highlight";
+"SB_CAT_poi_highlight_DESC" = "The key moment or highlight point of the video. Use 'Skip to' to jump directly to it.";
+"SB_CAT_filler" = "Filler";
+"SB_CAT_filler_DESC" = "Tangential content, jokes, or filler that is not directly related to the main topic.";
+
+"SB_SKIPPED" = "%@ segment has been skipped";
+"SB_DETECTED" = "%@ segment detected.\nWould you like to skip the segment?";
+"SB_SKIP_NOW" = "Skip";
+"SB_UNSKIP" = "Unskip";
+"SB_JUMPED_TO_HIGHLIGHT" = "Jumped to highlight";
+"SB_JUMP_TO_HIGHLIGHT" = "Highlight available. Jump to the point?";
+"SB_SEGMENT_COLOR_SUFFIX" = "Segment color";
+"SB_SECTION_MAIN" = "Main";
+"SB_SKIP_ALERT_DURATION" = "Skip alert duration";
+"SB_UNSKIP_ALERT_DURATION" = "Unskip alert duration";
\ No newline at end of file
diff --git a/layout/Library/Application Support/YouMod.bundle/en.lproj/Localizable.strings b/layout/Library/Application Support/YouMod.bundle/en.lproj/Localizable.strings
index bccaf550..2c1a8a0f 100644
--- a/layout/Library/Application Support/YouMod.bundle/en.lproj/Localizable.strings
+++ b/layout/Library/Application Support/YouMod.bundle/en.lproj/Localizable.strings
@@ -1,11 +1,18 @@
-"NOTE" = "NOTE: Some hiding features may leave a blank box.";
"SETTINGS" = "Settings";
+"INTERFACE" = "Interface";
+
+"LOOP_DISABLED" = "Loop disabled";
+"LOOP_ENABLED" = "Loop enabled";
"NEW_ISSUES" = "Report issues or request new features";
"NEW_ISSUES_DESC" = "Found bugs or want to request new features? Tap here.";
"SOURCE_CODES" = "Source codes";
"SOURCE_CODES_DESC" = "Take a look at the codes!";
+"EXTRA" = "Extra";
+"FIX_PLAYBACK_ISSUES" = "Fix playback issues (Temporary)";
+"FIX_PLAYBACK_ISSUES_DESC" = "Try to fix video playback issues for some users. Turning this on will make the player automatically reloads itself everytime it errors or fails to play the video.";
+
"PERFER_HEADER" = "YouMod perferences";
"PERFER" = "Perferences";
"IMPORT" = "Import settings";
@@ -24,6 +31,7 @@
"ERROR_NO_KEYS_IMPORT" = "No available keys to import";
"ERROR_NO_KEYS_EXPORT" = "No available keys to export";
"OVERRIDE" = "This will override the current settings. Do you want to continue?";
+"DISABLED" = "Disabled";
"CACHE" = "Cache management";
"CLEARCACHE" = "Clear cache";
@@ -32,12 +40,80 @@
"AUTO_CLEARCACHE_DESC" = "Automatically clears the app cache on startup.";
"DOWNLOADING" = "Downloading";
-"DOWNLOAD_MANAGER" = "Download Manager";
-"DOWNLOAD_MANAGER_DESC" = "Replaces YouTube's video downloader with YouMod one. With ablity to downloading videos, audios, captions, and thumbnails";
+"DOWNLOAD_MANAGER" = "Download manager";
+"DOWNLOAD_MANAGER_DESC" = "Replaces YouTube's video downloader with YouMod one. With ablity to downloading videos, audios, captions, and thumbnails.";
"DOWNLOAD_SAVE_PHOTOS" = "Automatically save to Photos";
"DOWNLOAD_SAVE_PHOTOS_DESC" = "Automatically saves downloaded videos and images directly to your Photos app.";
-"DOWNLOAD_DRC_AUDIO" = "Prefer DRC Audio";
-"DOWNLOAD_DRC_AUDIO_DESC" = "Prioritizes downloading audio tracks with Dynamic Range Compression for more consistent volume levels.";
+"ADD_SHORTS_DOWNLOAD" = "Adds a download button to Shorts";
+"ADD_SHORTS_DOWNLOAD_DESC" = "Adds a download button to Shorts video overlay.";
+"USE_ORIG_AUDIO" = "Automatically uses original audio track";
+"USE_ORIG_AUDIO_DESC" = "Automatically uses original audio track instead of showing a dialog.";
+"DOWNLOAD_SHORTS" = "Download Shorts";
+"DOWNLOAD_SHORTS_DESC" = "Choose Shorts quality";
+"COPY_VID_INFO" = "Copy video informations";
+"COPY_VID_INFO_DESC" = "Choose what to copy";
+"COPY_ALL_VID_INFO" = "Copy all";
+"COPY_ALL_VID_INFO_DESC" = "Copy all video informations";
+"COPY_AUTHOR" = "Copy author";
+"COPY_AUTHOR_DESC" = "Copy the video author";
+"COPY_TITLE" = "Copy title";
+"COPY_TITLE_DESC" = "Copy the video title";
+"COPY_DESCRIPTION" = "Copy description";
+"COPY_DESCRIPTION_DESC" = "Copy the video description";
+"COPY_URL" = "Copy video link";
+"COPY_URL_TIMESTAMP" = "Copy video link with timestamp";
+"URL_COPIED" = "Copied video link";
+"URL_TIMESTAMP_COPIED" = "Copied video link with timestamp";
+"ERROR_VIDEOID" = "Cannot get the video ID";
+"ERROR_ADS" = "Cannot copy video link while playing ads";
+"COPIED_AUTHOR" = "Copied video author";
+"COPIED_TITLE" = "Copied video title";
+"COPIED_DESCRIPTION" = "Copied video description";
+"SAVE_THUMBNAIL" = "Save thumbnail";
+"SAVE_THUMBNAIL_DESC" = "Save to Photos";
+"DOWNLOAD_CAPTIONS" = "Download captions";
+"DOWNLOAD_CAPTIONS_DESC" = "Save subtitles as .VTT file";
+"DOWNLOAD_AUDIO" = "Download audio";
+"DOWNLOAD_AUDIO_DESC" = "Download audio as .m4a file";
+"DOWNLOAD_VIDEO" = "Download video";
+"DOWNLOAD_VIDEO_DESC" = "Choose video quality";
+"OPEN_VID_BEFORE" = "Open a video before using the download manager";
+"NO_CAPTIONS_URL" = "No valid caption URLs found";
+"NO_AUDIO_URL" = "No audio URL found";
+"NO_STREAM_URL" = "No stream URL found";
+"CAPTIONS_FAILED" = "Failed to download captions";
+"NO_CAPTIONS" = "No captions available for this video";
+"NO_AUDIO_STREAM_FOUND" = "No audio streams found";
+"NO_VID_AUDIO_STREAM_FOUND" = "No video/audio streams found";
+"COPIED_VID_INFO" = "Copied all video informations";
+"SAVED_TO_PHOTOS" = "Saved to Photos";
+"SAVE_FAILED" = "Save failed";
+"PHOTO_ACCESS_DENINED" = "Photos access denied";
+"THUMBNAIL_FAILED" = "Thumbnail failed";
+"DOWNLOADING_THUMBNAIL" = "Downloading thumbnail...";
+"DOWNLOADING_AUDIO" = "Downloading audio...";
+"DOWNLOADING_VIDEO" = "Downloading video...";
+"DOWNLOADING_CAPTIONS" = "Downloading captions...";
+"NO_THUMBNAIL_FOUND" = "No thumbnail found";
+"DOWNLOAD_FAILED" = "Download failed";
+"COPY_THUMBNAIL" = "Copy thumbnail";
+"COPY_THUMBNAIL_DESC" = "Copy to clipboard";
+"SHOW_THUMBNAIL" = "Show thumbnail";
+"SHOW_THUMBNAIL_DESC" = "Show in a dialog";
+"THUMBNAIL_OPTIONS" = "Download thumbnail";
+"THUMBNAIL_OPTIONS_DESC" = "Show more options";
+"COPIED_TO_CLIPBOARD" = "Copied to clipboard";
+"DOWNLOAD_COMPLETED" = "Download completed";
+"CANNOT_SAVE_TO_PHOTOS" = "Cannot save to Photos";
+"FINA_VIDEO" = "Finalizing video...";
+"MERGING_VID_FALLBACK" = "Merging video with fallback...";
+"MERGING_VID" = "Merging video...";
+"CONVERTING" = "Converting to";
+"ALREADY_DOWNLOADING" = "Already downloading";
+"DOWNLOAD_CANCELLED" = "Download cancelled";
+"CANCEL" = "Cancel";
+"TRIMMING_AUDIO" = "Trimming audio...";
+"COPY" = "Copy";
"APPEARANCE" = "Appearance";
"OLED_THEME" = "OLED dark mode";
@@ -46,6 +122,8 @@
"OLED_KEYBOARD_DESC" = "Make the keyboard uses a pure black (OLED) color for dark theme.";
"NAVBAR" = "Navigation bar";
+"STICKY_NAVBAR" = "Sticky Navigation bar";
+"STICKY_NAVBAR_DESC" = "Pins the Navigation bar so that it remains visible when scrolling down.";
"HIDE_YT_LOGO" = "Hide YouTube logo";
"HIDE_YT_LOGO_DESC" = "Hide YouTube logo in the navigation bar.";
"PREMIUM_LOGO" = "Use YouTube Premium logo";
@@ -62,34 +140,78 @@
"FEED" = "Feed";
"HIDE_SUBBAR" = "Hide subbar";
"HIDE_SUBBAR_DESC" = "Hide subbar at the top of the feed.";
+"HIDE_HORI_SHELF" = "Remove horizontal feeds";
+"HIDE_HORI_SHELF_DESC" = "Removes all horizontal feeds (News, Continue Watching, Posts collection, etc.) from the feed, except Playables.";
"HIDE_MUSIC_PLAYLISTS" = "Hide music playlist generator";
"HIDE_MUSIC_PLAYLISTS_DESC" = "Hide music playlist generator (Select music tunes in three) in the feed.";
"HIDE_FEED_POST" = "Hide posts";
"HIDE_FEED_POST_DESC" = "Hide posts in feed.";
+"HIDE_SURVEYS" = "Hide surveys";
+"HIDE_SURVEYS_DESC" = "Hide video surveys that YouTube shows sometimes.";
"HIDE_SHORTS_SHELF" = "Hide Shorts shelf";
"HIDE_SHORTS_SHELF_DESC" = "Hide Shorts shelf in the feed, including in the Subscriptions tab.";
+"KEEP_SHORTS_SUBSCRIPT" = "Keep Shorts in Subscriptions";
+"KEEP_SHORTS_SUBSCRIPT_DESC" = "Prevents removing Shorts from Subscriptions tab.";
"HIDE_SEARCH_HISTORY" = "Hide search history";
"HIDE_SEARCH_HISTORY_DESC" = "Hide previous search history and suggestions when using the search bar.\nNOTE: Your search history will still be accessible in your other YouTube clients.";
-"HIDE_SUB_BUTTON" = "Hide Subscribe button";
-"HIDE_SUB_BUTTON_DESC" = "Hides the subscribe button in channels and video descriptions.";
-"HIDE_SHOP_BUTTON" = "Hide Shopping button";
-"HIDE_SHOP_BUTTON_DESC" = "Hides the shopping/store button in channels descriptions.";
-"HIDE_MEMBER_BUTTON" = "Hide Membership button";
-"HIDE_MEMBER_BUTTON_DESC" = "Hides the 'Join' button in channels and video descriptions.";
+"HIDE_PLAYABLES" = "Remove playables";
+"HIDE_PLAYABLES_DESC" = "Removes playables (mini-games) from the feed.";
"PLAYER" = "Player";
+"PLAYBACK_SPEED" = "Playback speed";
+"CONTROL_CENTER" = "Control Center";
+"PLAYER_ACTIONS" = "Player actions";
+"GESTURE_HEADER" = "Gestures";
+"QUALITY_WIFI" = "Playback quality on WiFi";
+"QUALITY_WIFI_DESC" = "Automatically applies the selected quality when streaming over Wi-Fi.";
+"QUALITY_CELLULAR" = "Playback quality on Cellular";
+"QUALITY_CELLULAR_DESC" = "Automatically applies the selected quality when streaming over mobile data.";
+"QUALITY_LOW_POWER" = "Playback quality on Power saver mode";
+"QUALITY_LOW_POWER_DESC" = "Automatically applies the selected quality when when Low power mode is enabled on your device.";
+"DEFAULT" = "Default";
+"BEST" = "Best";
+"AUDIO_TRACK" = "Preferred audio track";
+"ORIGINAL" = "Original";
+"SELECT_MANUALLY" = "Select manually";
+"AUDIO_TRACK_SELECT" = "Selected audio track";
+"AUDIO_TRACK_SELECT_DESC" = "Specifies the preferred audio track language to use for playback, if available.";
+"NO_AUTO_DUBBED" = "Uses original audio track";
+"NO_AUTO_DUBBED_DESC" = "Uses original audio track if the preferred language track is auto-dubbed or not found.";
+"CAPTION_TRACK" = "Preferred captions";
+"CAPTION_TRACK_SELECT" = "Selected captions";
+"CAPTION_TRACK_SELECT_DESC" = "Specifies the preferred captions language to display, if available.";
+"DISABLES_CAPTION_TRACK" = "Automatically disable captions";
+"DISABLES_CAPTION_TRACK_DESC" = "Automatically disable captions if the preferred language is auto-generated or not found.";
+"DEFAULT_SPEED" = "Default playback rate";
+"DEFAULT_SPEED_DESC" = "Overrides the default playback speed.";
+"SPEED_BUTTON" = "Speed controls button";
+"SPEED_BUTTON_DESC" = "Displays a speed controls button on overlay.";
+"MUTE_BUTTON" = "Mute button";
+"MUTE_BUTTON_DESC" = "Displays a button to mute or unmute the volume on the overlay.";
+"SHARE_BUTTON" = "Share button";
+"SHARE_BUTTON_DESC" = "Displays a button to share the video link on the overlay.";
+"LOOP_BUTTON" = "Loop button";
+"LOOP_BUTTON_DESC" = "Displays a button to loop the video on the overlay.";
+"CAPTION_BUTTON" = "Captions button";
+"CAPTION_BUTTON_DESC" = "Displays a button to copy the video captions on the overlay.";
+"HOLD_TO_SPEED" = "Hold to speed";
+"HOLD_TO_SPEED_DESC" = "Uses custom hold to speed playback rate.";
"HIDE_AUTOPLAY" = "Hide autoplay switch";
"HIDE_AUTOPLAY_DESC" = "Hide autoplay switch in the video overlay.";
"HIDE_CAPTIONS_BUTTON" = "Hide captions button";
"HIDE_CAPTIONS_BUTTON_DESC" = "Hide captions button in the video overlay.";
"HIDE_CAST_BUTTON_PLAYER" = "Hide cast button";
"HIDE_CAST_BUTTON_PLAYER_DESC" = "Hide cast button in the video overlay.";
-"HIDE_PREV_BUTTON" = "Hide previous/back button";
-"HIDE_PREV_BUTTON_DESC" = "Hide previous/back button in the video overlay.";
-"HIDE_NEXT_BUTTON" = "Hide next/skip button";
-"HIDE_NEXT_BUTTON_DESC" = "Hide next/skip button in the video overlay.";
+"HIDE_NEXT_AND_PREV_BUTTON" = "Hide previous and next buttons";
+"HIDE_NEXT_AND_PREV_BUTTON_DESC" = "Hide previous and next buttons in the video overlay.";
"REPLACE_PREVNEXT_BUTTONS" = "Replace Previous/Next buttons";
"REPLACE_PREVNEXT_BUTTONS_DESC" = "Replaces the Previous and Next video buttons in the player with Rewind and Fast Forward buttons.";
+"SKIP_BACKWARD" = "Skip backward";
+"SKIP_BACKWARD_DESC" = "Replaces the previous track button in Control Center with a skip backward button by a set number of seconds.";
+"SKIP_FORWARD" = "Skip forward";
+"SKIP_FORWARD_DESC" = "Replaces the next track button in Control Center with a skip forward button by a set number of seconds.";
+"REWIND_SECONDS" = "Rewind seconds";
+"FORWARD_SECONDS" = "Fast-forward seconds";
"REMOVE_DARK_OVERLAY" = "Remove dark overlay";
"REMOVE_DARK_OVERLAY_DESC" = "Remove dark overlay from the video overlay.";
"HIDE_END_SCREEN" = "Hide endscreen cards";
@@ -102,6 +224,12 @@
"HIDE_PAID_OVERLAY_DESC" = "Hide 'Includes paid promotion' in the video overlay.";
"HIDE_WATERMARK" = "Hide watermark";
"HIDE_WATERMARK_DESC" = "Hide channel watermark at the bottom of the video overlay.";
+"PAUSE_ON_OVERLAY" = "Pause on overlay";
+"PAUSE_ON_OVERLAY_DESC" = "Sets playback on pause if overlay appears.";
+"DISABLES_SNAP_TO_CHAPTER" = "Disable snap to chapters";
+"DISABLES_SNAP_TO_CHAPTER_DESC" = "Prevents the scrubbar stick to the chapters.";
+"DISABLES_ENGAGE_PANEL" = "Disables engagement panel in fullscreen";
+"DISABLES_ENGAGE_PANEL_DESC" = "Prevent engagement panel showing in fullscreen.";
"GESTURES" = "Enable gestures controls";
"GESTURES_DESC" = "Swipe up or down on the left/right edges of the video screen to adjust brightness, volume, or playback speed.";
"GESTURE_AREA" = "Gesture activation area";
@@ -136,6 +264,8 @@
"DISABLES_SHOW_REMAINING_DESC" = "Disable the feature that shows how much time is left in a video when tapping the timestamp.";
"ALWAYS_SHOW_REMAINING" = "Always show remaining time";
"ALWAYS_SHOW_REMAINING_DESC" = "Ensure the remaining time is always displayed by default in the player overlay.";
+"COPY_TIMESTAMP_ON_PAUSE" = "Copy timestamped links";
+"COPY_TIMESTAMP_ON_PAUSE_DESC" = "Allows to copy timestamped link to the clipboard by pressing pause button.";
"SHOW_REMAINING_EXTRA" = "Show extra remaining time";
"SHOW_REMAINING_EXTRA_DESC" = "Adds the estimated finish time to the label in the video overlay.";
"HIDE_FULLSCREEN_ACTIONS" = "Hide fullscreen actions";
@@ -148,93 +278,95 @@
"HIDE_CONTENT_WARNING_DESC" = "Automatically skip the sensitive content warning alert in some vidoes.";
"AUTO_FULLSCREEN" = "Auto fullscreen";
"AUTO_FULLSCREEN_DESC" = "Automatically enter fullscreen mode when starting video playback.";
-"AUTO_DISABLES_CAPTION" = "Auto disable captions";
-"AUTO_DISABLES_CAPTION_DESC" = "Automatically turns off closed captions (CC) whenever a new video starts playing.";
"PORTRAIT_FULLSCREEN" = "Portrait fullscren mode";
"PORTRAIT_FULLSCREEN_DESC" = "Enables portrait fullscreen mode support.";
"OLD_QUALITY_PICKER" = "Use old quality picker";
"OLD_QUALITY_PICKER_DESC" = "Reverts the video quality selection menu to the classic version (lists all resolutions immediately).";
"EXTRA_SPEED" = "Extra speed options";
"EXTRA_SPEED_DESC" = "Adds more video playback speed options up to 10x.";
-"DISABLE_HINTS" = "Disable hints";
-"DISABLE_HINTS_DESC" = "Disable the tutorial hints and interactive pop-ups.";
"FORCE_MINIPLAYER" = "Force miniplayer";
"FORCE_MINIPLAYER_DESC" = "Allows all videos, including those made for kids, to play in the miniplayer when dismissing the player.";
"FORCE_SEEKBAR" = "Always show progress bar";
"FORCE_SEEKBAR_DESC" = "Makes the progress bar (seekbar) remains visible at all times while in fullscreen mode.";
-"HIDE_LIKE_BUTTON" = "Hide Like button";
-"HIDE_LIKE_BUTTON_DESC" = "Hide Like button in the action bar under the video overlay.";
-"HIDE_DISLIKE_BUTTON" = "Hide Dislike button";
-"HIDE_DISLIKE_BUTTON_DESC" = "Hide Dislike button in the action bar under the video overlay.";
-"HIDE_SHARE_BUTTON" = "Hide Share button";
-"HIDE_SHARE_BUTTON_DESC" = "Hide Share button in the action bar under the video overlay.";
-"HIDE_DOWNLOAD_BUTTON" = "Hide Download button";
-"HIDE_DOWNLOAD_BUTTON_DESC" = "Hide Download button in the action bar under the video overlay.";
-"HIDE_CLIP_BUTTON" = "Hide Clip button";
-"HIDE_CLIP_BUTTON_DESC" = "Hide Clip button in the action bar under the video overlay.";
-"HIDE_REMIX_BUTTON" = "Hide Remix button";
-"HIDE_REMIX_BUTTON_DESC" = "Hide Remix button in the action bar under the video overlay.";
-"HIDE_SAVE_BUTTON" = "Hide Save button";
-"HIDE_SAVE_BUTTON_DESC" = "Hide Save button in the action bar under the video overlay.";
+"TAP_TO_SEEK" = "Tap to seek";
+"TAP_TO_SEEK_DESC" = "Quickly jump to any point by tapping the progress bar.";
+"DISABLES_ZOOM" = "Disable free zoom gesture";
+"DISABLES_ZOOM_DESC" = "Disables new free zoom gestures.";
+"USES_24_HOURS_TIME" = "24-hour format";
+"USES_24_HOURS_TIME_DESC" = "Shows end time in 24-hour format.";
+"PAUSE_TWO_FINGERS" = "Pause using two fingers";
+"PAUSE_TWO_FINGERS_DESC" = "Pause/Resume playback by tapping on the screen using two fingers.";
+"HIDE_COMMENTS_SECTION" = "Hide comments section";
+"HIDE_COMMENTS_SECTION_DESC" = "Completely removes the entire comments section.";
+"HIDE_COMMENTS_PREVIEW" = "Hide preview comments";
+"HIDE_COMMENTS_PREVIEW_DESC" = "Removes the preview box for the top comment while keeping the full section expandable.";
+"USE_ANOTHER_MINIPLAYER" = "Uses another miniplayer style";
+"USE_ANOTHER_MINIPLAYER_DESC" = "Uses another miniplayer style redesigned from older YT versions.";
"SHORTS" = "Shorts";
-"HIDE_SHORTS_LIKE_BUTTON" = "Hide Like button";
-"HIDE_SHORTS_LIKE_BUTTON_DESC" = "Hide the Like button in the Shorts player.";
-"HIDE_SHORTS_DISLIKE_BUTTON" = "Hide Dislike button";
-"HIDE_SHORTS_DISLIKE_BUTTON_DESC" = "Hide the Dislike button in the Shorts player.";
-"HIDE_SHORTS_COMMENT_BUTTON" = "Hide Comment button";
-"HIDE_SHORTS_COMMENT_BUTTON_DESC" = "Hide the Comment button in the Shorts player.";
-"HIDE_SHORTS_SHARE_BUTTON" = "Hide Share button";
-"HIDE_SHORTS_SHARE_BUTTON_DESC" = "Hide the Share button in the Shorts player.";
-"HIDE_SHORTS_REMIX_BUTTON" = "Hide Remix button";
-"HIDE_SHORTS_REMIX_BUTTON_DESC" = "Hide the Remix button in the Shorts player.";
-"HIDE_METADATA_BUTTON" = "Hide sound metadata button";
-"HIDE_METADATA_BUTTON_DESC" = "Hide the button that shows the sound source or music metadata in the Shorts player.";
+"SHORTS_ONLY" = "Shorts only mode";
+"SHORTS_ONLY_DESC" = "Limits YouTube functionality to viewing Shorts only.";
+"SHORTS_ACTION" = "Shorts action after the video ends";
+"LOOP" = "Loop video";
+"SKIP_TO_NEXT_SHORTS" = "Skip to next video";
+"PAUSE_SHORTS" = "Stop video";
+"REMOVE_LIVE_SHORTS" = "Remove live videos";
+"REMOVE_LIVE_SHORTS_DESC" = "Removes live videos from the feed.";
"HIDE_SHORTS_PRODUCT" = "Hide products";
"HIDE_SHORTS_PRODUCT_DESC" = "Hide tagged products and shopping links in the Shorts overlay.";
"HIDE_SHORTS_RECBAR" = "Hide recommendation bar";
"HIDE_SHORTS_RECBAR_DESC" = "Hide the suggested action bar in the Shorts player (e.g. Try this sound, Shopping, Preview comment).";
-"HIDE_SHORTS_COMMIT" = "Hide commissions bar";
-"HIDE_SHORTS_COMMIT_DESC" = "Hide the bar that shows the video got commissions in the Shorts player.";
-"HIDE_SHORTS_SUBSCRIPT_BUTTON" = "Hide subscriptions button";
-"HIDE_SHORTS_SUBSCRIPT_BUTTON_DESC" = "Hide the Subscriptions button at the top of the Shorts player.";
-"HIDE_SHORTS_LIVE_BUTTON" = "Hide live button";
-"HIDE_SHORTS_LIVE_BUTTON_DESC" = "Hide the Live button at the top of the Shorts player.";
-"HIDE_SHORTS_LENS_BUTTON" = "Hide lens button";
-"HIDE_SHORTS_LENS_BUTTON_DESC" = "Hide the Lens button at the top of the Shorts player.";
-"HIDE_SHORTS_TRENDS_BUTTON" = "Hide trends button";
-"HIDE_SHORTS_TRENDS_BUTTON_DESC" = "Hide the Trends button at the top of the Shorts player.";
-"HIDE_SHORTS_TO_VIDEO" = "Hide link to full video";
-"HIDE_SHORTS_TO_VIDEO_DESC" = "Hide the link that redirects in a Short to a full video in the Shorts player.";
"ENABLES_SHORTS_QUALITY" = "Enable Shorts quality picker";
"ENABLES_SHORTS_QUALITY_DESC" = "Force enable the video quality selection menu in Shorts.";
"SHOW_SHORTS_SEEKBAR" = "Always show seekbar";
"SHOW_SHORTS_SEEKBAR_DESC" = "Ensure the progress/seek bar is always visible at the bottom of the Shorts player.";
+"REMOVE_POSTS_SHORTS" = "Remove posts";
+"REMOVE_POSTS_SHORTS_DESC" = "Remove posts from the feed.";
+"SHORTS_FULLSCREEN" = "Pinch to Fullscreen";
+"SHORTS_FULLSCREEN_DESC" = "Manages visibility of the overlay with pinch in and pinch out gestures, displaying Shorts in fullscreen mode.";
"TABBAR" = "Tab bar";
"DEFAULT_TAB" = "Default startup tab";
-"DEFAULT_TAB_DESC" = "You can select the tab you want to open when opening the app.";
-"HOME_NAME" = "Home";
-"SUBSCRIPT_NAME" = "Subscriptions";
-"LIB_NAME" = "Library (You)";
+"FORSTED_TAB_BAR" = "Translucent tab bar";
+"ENABLED" = "Enabled";
"HIDE_TAB_INDI" = "Hide tab indicators";
"HIDE_TAB_INDI_DESC" = "Hide all tab indicators in the tab bar.";
"HIDE_TAB_LABELS" = "Hide tab labels";
"HIDE_TAB_LABELS_DESC" = "Hide all tab labels in the tab bar.";
-"HIDE_HOME_TAB" = "Hide Home tab";
-"HIDE_HOME_TAB_DESC" = "Hide Home tab in the tab bar.";
-"HIDE_SHORTS_TAB" = "Hide Shorts tab";
-"HIDE_SHORTS_TAB_DESC" = "Hide Shorts tab in the tab bar.";
-"HIDE_CREATE_BUTTON" = "Hide Create button";
-"HIDE_CREATE_BUTTON_DESC" = "Hide Create button in the tab bar.";
-"HIDE_SUBSCRIPT_TAB" = "Hide Subscriptions tab";
-"HIDE_SUBSCRIPT_TAB_DESC" = "Hide Subscriptions tab in the tab bar.";
+"HOME_TAB" = "Home";
+"SHORTS_TAB" = "Shorts";
+"CREATE_TAB" = "Create";
+"SUBSCRIPTIONS_TAB" = "Subscriptions";
+"LIBRARY_TAB" = "Library (You)";
+"HISTORY_TAB" = "History";
+"GAMING_TAB" = "Gaming";
+"SPORTS_TAB" = "Sports";
+"NOTI_TAB" = "Notifications";
+"NEWS_TAB" = "News";
+"MUSIC_TAB" = "Music";
+"WATCH_LATER_TAB" = "Watch later";
+"PLAYLIST_TAB" = "Playlists";
+"LIKE_TAB" = "Liked videos";
+"LIVE_TAB" = "Live";
+"POST_TAB" = "Posts";
+"VIDEO_TAB" = "Your videos";
+"MOVIE_TAB" = "Movies";
+"COURSE_TAB" = "Courses";
+"MINIGAME_TAB" = "Playables";
+"MANAGE_TABS" = "Manage tabs";
+"MANAGE_TABS_DESC" = "Reorder and toggle tabs";
+"TAB_REORDER_HINT" = "Drag to reorder. Toggle to show/hide.\nTab settings are also accessible by long-pressing the first tab in the tab bar.";
+"TAB_LIMIT" = "Tab limit";
+"TAB_LIMIT_DESC" = "Only 6 tabs are allowed.";
+"ZERO_TAB_DESC" = "At least 1 tab has to be enabled.";
"MISCELLANEOUS" = "Miscellaneous";
"BACKGROUND_PLAYBACK" = "Background playback";
"BACKGROUND_PLAYBACK_DESC" = "Enables background playback for videos.";
"DISABLES_SHORTS_PIP" = "Disables Shorts PiP";
"DISABLES_SHORTS_PIP_DESC" = "Try to disables Shorts PiP, this might not 100% disables it.";
+"DISABLE_HINTS" = "Disable hints";
+"DISABLE_HINTS_DESC" = "Disable the tutorial hints and interactive pop-ups.";
"BLOCK_UPGRADE_DIALOGS" = "Block upgrade dialogs";
"BLOCK_UPGRADE_DIALOGS_DESC" = "Block the YouTube dialogs that ask you to upgrade the app.";
"ARE_YOU_THERE_DIALOG" = "Hide 'Are you there?' dialog";
@@ -247,7 +379,109 @@
"DISABLES_SNACK_BAR_DESC" = "Disables snack bar when pressing some buttons or doing something in the app.";
"HIDE_STARTUP_ANIMATIONS" = "Hide YouTube's startup animations";
"HIDE_STARTUP_ANIMATIONS_DESC" = "Hide YouTube's startup animations when opening the app.";
-"HIDE_PLAY_IN_NEXT_QUEUE" = "Hide 'Play next in queue'";
-"HIDE_PLAY_IN_NEXT_QUEUE_DESC" = "Hide the 'Play next in queue' option in the flyout menu.";
"HIDE_LIKE_DISLIKE_VOTES" = "Hide like/dislike votes";
-"HIDE_LIKE_DISLIKE_VOTES_DESC" = "Hide messages that pop up when you like/dislike videos.";
\ No newline at end of file
+"HIDE_LIKE_DISLIKE_VOTES_DESC" = "Hide messages that pop up when you like/dislike videos.";
+"FLYOUT_MENU" = "Flyout menu";
+"REMOVE_PLAY_IN_NEXT_QUEUE_OPTION" = "Removes 'Play next in queue' option";
+"REMOVE_PLAY_IN_NEXT_QUEUE_OPTION_DESC" = "Removes 'Play next in queue' option from the flyout menu.";
+"REMOVE_DOWNLOAD_OPTION" = "Removes 'Download video' option";
+"REMOVE_DOWNLOAD_OPTION_DESC" = "Removes 'Download video' option from the flyout menu.";
+"REMOVE_WATCH_LATER_OPTION" = "Removes 'Save to Watch later' option";
+"REMOVE_WATCH_LATER_OPTION_DESC" = "Removes 'Save to Watch Later' option from the flyout menu.";
+"REMOVE_SAVE_OPTION" = "Removes 'Save to playlist' option";
+"REMOVE_SAVE_OPTION_DESC" = "Removes 'Save to playlist' option from the flyout menu.";
+"REMOVE_REMOVE_FROM_PLAYLIST_OPTION" = "Removes 'Remove from playlist' option";
+"REMOVE_REMOVE_FROM_PLAYLIST_OPTION_DESC" = "Removes 'Remove from playlist' option from the flyout menu.";
+"REMOVE_SHARE_OPTION" = "Removes 'Share' option";
+"REMOVE_SHARE_OPTION_DESC" = "Removes 'Share' option from the flyout menu.";
+"REMOVE_NOT_INTERESTED_OPTION" = "Removes 'Not interested' option";
+"REMOVE_NOT_INTERESTED_OPTION_DESC" = "Removes 'Not interested' option from the flyout menu.";
+"REMOVE_DONT_RECOMMEND_OPTION" = "Removes 'Don't recommend channel' option";
+"REMOVE_DONT_RECOMMEND_OPTION_DESC" = "Removes 'Don't recommend channel' option from the flyout menu.";
+"REMOVE_INFO_OPTION" = "Removes 'Why are you seeing this?' option";
+"REMOVE_INFO_OPTION_DESC" = "Removes 'Why are you seeing this?' option from the flyout menu.";
+"REMOVE_FILTER_OPTION" = "Removes 'Search filters' option";
+"REMOVE_FILTER_OPTION_DESC" = "Removes 'Search filters' option from the flyout menu.";
+"REMOVE_REPORT_OPTION" = "Removes 'Report' option";
+"REMOVE_REPORT_OPTION_DESC" = "Removes 'Report' option from the flyout menu.";
+"REMOVE_YOUTUBE_MUSIC_OPTION" = "Removes 'Listen with YouTube Music' option";
+"REMOVE_YOUTUBE_MUSIC_OPTION_DESC" = "Removes 'Listen with YouTube Music' option from the flyout menu.";
+"REMOVE_FEED_BACK_OPTION" = "Removes 'Send feedback' option";
+"REMOVE_FEED_BACK_OPTION_DESC" = "Removes 'Send feedback' option from the flyout menu.";
+"REMOVE_CAST_OPTION" = "Removes 'Watch on TV' option";
+"REMOVE_CAST_OPTION_DESC" = "Removes 'Watch on TV' option from the flyout menu.";
+"REMOVE_SHUFFLE_OPTION" = "Removes 'Shuffle' option";
+"REMOVE_SHUFFLE_OPTION_DESC" = "Removes 'Shuffle' option from the flyout menu.";
+"REMOVE_UN_SUB_OPTION" = "Removes 'Unsubscribe' option";
+"REMOVE_UN_SUB_OPTION_DESC" = "Removes 'Unsubscribe' option from the flyout menu.";
+"REMOVE_HIDE_FROM_PLAYLIST_OPTION" = "Removes 'Hide video from playlist' option";
+"REMOVE_HIDE_FROM_PLAYLIST_OPTION_DESC" = "Removes 'Hide video from playlist' option from the flyout menu.";
+"REMOVE_HELP_OPTION" = "Removes 'Help & feedback' option";
+"REMOVE_HELP_OPTION_DESC" = "Removes 'Help & feedback' option from the flyout menu.";
+"REMOVE_NOTIFY_OPTION" = "Removes 'Notify me' option";
+"REMOVE_NOTIFY_OPTION_DESC" = "Removes 'Notify me' option from the flyout menu.";
+"REMOVE_CLEARSCREEN_OPTION" = "Removes 'Clear Screen' option";
+"REMOVE_CLEARSCREEN_OPTION_DESC" = "Removes 'Clear Screen' option from the flyout menu.";
+"HIDE_COMMU_GUIDE" = "Hide community guidelines";
+"HIDE_COMMU_GUIDE_DESC" = "Hide community guidelines header.";
+"DISABLES_RTL" = "Disable RTL formatting";
+"DISABLES_RTL_DESC" = "Forcefully displays text in left-to-right (LTR) format for languages that are initially displayed in right-to-left (RTL).";
+"DEVICE_UI" = "Interface style";
+
+"SB_DESCRIPTION" = "Skip sponsored segments automatically using community-submitted data.";
+"SB_ENABLE" = "Enable SponsorBlock";
+"SB_ENABLE_DESC" = "Activate the SponsorBlock extension to skip sponsored segments.";
+"SB_SHOW_BUTTON" = "Show overlay button";
+"SB_SHOW_BUTTON_DESC" = "Display a SponsorBlock toggle button in the player overlay.";
+"SB_SHOW_NOTIFICATIONS" = "Show notifications";
+"SB_SHOW_NOTIFICATIONS_DESC" = "Display notifications when segments are skipped. Notifications for manual-skip categories are always shown.";
+"SB_HAPTIC_FEEDBACK" = "Haptic feedback";
+"SB_HAPTIC_FEEDBACK_DESC" = "Provide haptic feedback when a segment is skipped.";
+"SB_SEGMENTS_IN_PLAYER" = "Segments in player";
+"SB_SEGMENTS_IN_PLAYER_DESC" = "Display colored segments on the progress bar of the main video player.";
+"SB_SEGMENTS_IN_FEED" = "Segments in feed";
+"SB_SEGMENTS_IN_FEED_DESC" = "Display colored segments on the progress bar of players in the feed.";
+"SB_SEGMENTS_IN_MINIPLAYER" = "Segments in mini-player";
+"SB_SEGMENTS_IN_MINIPLAYER_DESC" = "Display colored segments on the progress bar of the mini-player.";
+"SB_SHOW_DURATION" = "Show duration without segments";
+"SB_SHOW_DURATION_DESC" = "Display the total video duration excluding skippable segments.";
+"SB_CATEGORIES_HEADER" = "Segment categories";
+"SB_SEGMENT_COLOR" = "Segment color";
+
+"SB_ACTION_DISABLE" = "Disable";
+"SB_ACTION_AUTO_SKIP" = "Auto-skip";
+"SB_ACTION_ASK" = "Ask before skipping";
+"SB_ACTION_DISPLAY" = "Show on seek bar only";
+"SB_ACTION_SKIP_TO" = "Skip to segment";
+
+"SB_CAT_sponsor" = "Sponsor";
+"SB_CAT_sponsor_DESC" = "Paid promotion for a product or service not directly related to the creator.";
+"SB_CAT_intro" = "Intro";
+"SB_CAT_intro_DESC" = "Intermission or intro animation typically seen at the start of a video.";
+"SB_CAT_outro" = "Endcards";
+"SB_CAT_outro_DESC" = "Endcards, credits, or outros at the end of the video.";
+"SB_CAT_interaction" = "Interaction";
+"SB_CAT_interaction_DESC" = "Reminders to like, subscribe, or follow on social media.";
+"SB_CAT_selfpromo" = "Self-promotion";
+"SB_CAT_selfpromo_DESC" = "Unpaid promotion of the creator's own products, merchandise, or other content.";
+"SB_CAT_music_offtopic" = "Non-music";
+"SB_CAT_music_offtopic_DESC" = "Non-music sections in music videos (e.g. talking, introductions).";
+"SB_CAT_preview" = "Preview";
+"SB_CAT_preview_DESC" = "Recap or preview of content from another video or previous episode.";
+"SB_CAT_hook" = "Hook/Greetings";
+"SB_CAT_hook_DESC" = "Narrated trailers for the upcoming video, greetings and goodbyes.";
+"SB_CAT_poi_highlight" = "Highlight";
+"SB_CAT_poi_highlight_DESC" = "The key moment or highlight point of the video. Use 'Skip to' to jump directly to it.";
+"SB_CAT_filler" = "Filler";
+"SB_CAT_filler_DESC" = "Tangential content, jokes, or filler that is not directly related to the main topic.";
+
+"SB_SKIPPED" = "%@ segment has been skipped";
+"SB_DETECTED" = "%@ segment detected.\nWould you like to skip the segment?";
+"SB_SKIP_NOW" = "Skip";
+"SB_UNSKIP" = "Unskip";
+"SB_JUMPED_TO_HIGHLIGHT" = "Jumped to highlight";
+"SB_JUMP_TO_HIGHLIGHT" = "Highlight available. Jump to the point?";
+"SB_SEGMENT_COLOR_SUFFIX" = "Segment color";
+"SB_SECTION_MAIN" = "Main";
+"SB_SKIP_ALERT_DURATION" = "Skip alert duration";
+"SB_UNSKIP_ALERT_DURATION" = "Unskip alert duration";
\ No newline at end of file
diff --git a/layout/Library/Application Support/YouMod.bundle/es.lproj/Localizable.strings b/layout/Library/Application Support/YouMod.bundle/es.lproj/Localizable.strings
index bccaf550..2c1a8a0f 100644
--- a/layout/Library/Application Support/YouMod.bundle/es.lproj/Localizable.strings
+++ b/layout/Library/Application Support/YouMod.bundle/es.lproj/Localizable.strings
@@ -1,11 +1,18 @@
-"NOTE" = "NOTE: Some hiding features may leave a blank box.";
"SETTINGS" = "Settings";
+"INTERFACE" = "Interface";
+
+"LOOP_DISABLED" = "Loop disabled";
+"LOOP_ENABLED" = "Loop enabled";
"NEW_ISSUES" = "Report issues or request new features";
"NEW_ISSUES_DESC" = "Found bugs or want to request new features? Tap here.";
"SOURCE_CODES" = "Source codes";
"SOURCE_CODES_DESC" = "Take a look at the codes!";
+"EXTRA" = "Extra";
+"FIX_PLAYBACK_ISSUES" = "Fix playback issues (Temporary)";
+"FIX_PLAYBACK_ISSUES_DESC" = "Try to fix video playback issues for some users. Turning this on will make the player automatically reloads itself everytime it errors or fails to play the video.";
+
"PERFER_HEADER" = "YouMod perferences";
"PERFER" = "Perferences";
"IMPORT" = "Import settings";
@@ -24,6 +31,7 @@
"ERROR_NO_KEYS_IMPORT" = "No available keys to import";
"ERROR_NO_KEYS_EXPORT" = "No available keys to export";
"OVERRIDE" = "This will override the current settings. Do you want to continue?";
+"DISABLED" = "Disabled";
"CACHE" = "Cache management";
"CLEARCACHE" = "Clear cache";
@@ -32,12 +40,80 @@
"AUTO_CLEARCACHE_DESC" = "Automatically clears the app cache on startup.";
"DOWNLOADING" = "Downloading";
-"DOWNLOAD_MANAGER" = "Download Manager";
-"DOWNLOAD_MANAGER_DESC" = "Replaces YouTube's video downloader with YouMod one. With ablity to downloading videos, audios, captions, and thumbnails";
+"DOWNLOAD_MANAGER" = "Download manager";
+"DOWNLOAD_MANAGER_DESC" = "Replaces YouTube's video downloader with YouMod one. With ablity to downloading videos, audios, captions, and thumbnails.";
"DOWNLOAD_SAVE_PHOTOS" = "Automatically save to Photos";
"DOWNLOAD_SAVE_PHOTOS_DESC" = "Automatically saves downloaded videos and images directly to your Photos app.";
-"DOWNLOAD_DRC_AUDIO" = "Prefer DRC Audio";
-"DOWNLOAD_DRC_AUDIO_DESC" = "Prioritizes downloading audio tracks with Dynamic Range Compression for more consistent volume levels.";
+"ADD_SHORTS_DOWNLOAD" = "Adds a download button to Shorts";
+"ADD_SHORTS_DOWNLOAD_DESC" = "Adds a download button to Shorts video overlay.";
+"USE_ORIG_AUDIO" = "Automatically uses original audio track";
+"USE_ORIG_AUDIO_DESC" = "Automatically uses original audio track instead of showing a dialog.";
+"DOWNLOAD_SHORTS" = "Download Shorts";
+"DOWNLOAD_SHORTS_DESC" = "Choose Shorts quality";
+"COPY_VID_INFO" = "Copy video informations";
+"COPY_VID_INFO_DESC" = "Choose what to copy";
+"COPY_ALL_VID_INFO" = "Copy all";
+"COPY_ALL_VID_INFO_DESC" = "Copy all video informations";
+"COPY_AUTHOR" = "Copy author";
+"COPY_AUTHOR_DESC" = "Copy the video author";
+"COPY_TITLE" = "Copy title";
+"COPY_TITLE_DESC" = "Copy the video title";
+"COPY_DESCRIPTION" = "Copy description";
+"COPY_DESCRIPTION_DESC" = "Copy the video description";
+"COPY_URL" = "Copy video link";
+"COPY_URL_TIMESTAMP" = "Copy video link with timestamp";
+"URL_COPIED" = "Copied video link";
+"URL_TIMESTAMP_COPIED" = "Copied video link with timestamp";
+"ERROR_VIDEOID" = "Cannot get the video ID";
+"ERROR_ADS" = "Cannot copy video link while playing ads";
+"COPIED_AUTHOR" = "Copied video author";
+"COPIED_TITLE" = "Copied video title";
+"COPIED_DESCRIPTION" = "Copied video description";
+"SAVE_THUMBNAIL" = "Save thumbnail";
+"SAVE_THUMBNAIL_DESC" = "Save to Photos";
+"DOWNLOAD_CAPTIONS" = "Download captions";
+"DOWNLOAD_CAPTIONS_DESC" = "Save subtitles as .VTT file";
+"DOWNLOAD_AUDIO" = "Download audio";
+"DOWNLOAD_AUDIO_DESC" = "Download audio as .m4a file";
+"DOWNLOAD_VIDEO" = "Download video";
+"DOWNLOAD_VIDEO_DESC" = "Choose video quality";
+"OPEN_VID_BEFORE" = "Open a video before using the download manager";
+"NO_CAPTIONS_URL" = "No valid caption URLs found";
+"NO_AUDIO_URL" = "No audio URL found";
+"NO_STREAM_URL" = "No stream URL found";
+"CAPTIONS_FAILED" = "Failed to download captions";
+"NO_CAPTIONS" = "No captions available for this video";
+"NO_AUDIO_STREAM_FOUND" = "No audio streams found";
+"NO_VID_AUDIO_STREAM_FOUND" = "No video/audio streams found";
+"COPIED_VID_INFO" = "Copied all video informations";
+"SAVED_TO_PHOTOS" = "Saved to Photos";
+"SAVE_FAILED" = "Save failed";
+"PHOTO_ACCESS_DENINED" = "Photos access denied";
+"THUMBNAIL_FAILED" = "Thumbnail failed";
+"DOWNLOADING_THUMBNAIL" = "Downloading thumbnail...";
+"DOWNLOADING_AUDIO" = "Downloading audio...";
+"DOWNLOADING_VIDEO" = "Downloading video...";
+"DOWNLOADING_CAPTIONS" = "Downloading captions...";
+"NO_THUMBNAIL_FOUND" = "No thumbnail found";
+"DOWNLOAD_FAILED" = "Download failed";
+"COPY_THUMBNAIL" = "Copy thumbnail";
+"COPY_THUMBNAIL_DESC" = "Copy to clipboard";
+"SHOW_THUMBNAIL" = "Show thumbnail";
+"SHOW_THUMBNAIL_DESC" = "Show in a dialog";
+"THUMBNAIL_OPTIONS" = "Download thumbnail";
+"THUMBNAIL_OPTIONS_DESC" = "Show more options";
+"COPIED_TO_CLIPBOARD" = "Copied to clipboard";
+"DOWNLOAD_COMPLETED" = "Download completed";
+"CANNOT_SAVE_TO_PHOTOS" = "Cannot save to Photos";
+"FINA_VIDEO" = "Finalizing video...";
+"MERGING_VID_FALLBACK" = "Merging video with fallback...";
+"MERGING_VID" = "Merging video...";
+"CONVERTING" = "Converting to";
+"ALREADY_DOWNLOADING" = "Already downloading";
+"DOWNLOAD_CANCELLED" = "Download cancelled";
+"CANCEL" = "Cancel";
+"TRIMMING_AUDIO" = "Trimming audio...";
+"COPY" = "Copy";
"APPEARANCE" = "Appearance";
"OLED_THEME" = "OLED dark mode";
@@ -46,6 +122,8 @@
"OLED_KEYBOARD_DESC" = "Make the keyboard uses a pure black (OLED) color for dark theme.";
"NAVBAR" = "Navigation bar";
+"STICKY_NAVBAR" = "Sticky Navigation bar";
+"STICKY_NAVBAR_DESC" = "Pins the Navigation bar so that it remains visible when scrolling down.";
"HIDE_YT_LOGO" = "Hide YouTube logo";
"HIDE_YT_LOGO_DESC" = "Hide YouTube logo in the navigation bar.";
"PREMIUM_LOGO" = "Use YouTube Premium logo";
@@ -62,34 +140,78 @@
"FEED" = "Feed";
"HIDE_SUBBAR" = "Hide subbar";
"HIDE_SUBBAR_DESC" = "Hide subbar at the top of the feed.";
+"HIDE_HORI_SHELF" = "Remove horizontal feeds";
+"HIDE_HORI_SHELF_DESC" = "Removes all horizontal feeds (News, Continue Watching, Posts collection, etc.) from the feed, except Playables.";
"HIDE_MUSIC_PLAYLISTS" = "Hide music playlist generator";
"HIDE_MUSIC_PLAYLISTS_DESC" = "Hide music playlist generator (Select music tunes in three) in the feed.";
"HIDE_FEED_POST" = "Hide posts";
"HIDE_FEED_POST_DESC" = "Hide posts in feed.";
+"HIDE_SURVEYS" = "Hide surveys";
+"HIDE_SURVEYS_DESC" = "Hide video surveys that YouTube shows sometimes.";
"HIDE_SHORTS_SHELF" = "Hide Shorts shelf";
"HIDE_SHORTS_SHELF_DESC" = "Hide Shorts shelf in the feed, including in the Subscriptions tab.";
+"KEEP_SHORTS_SUBSCRIPT" = "Keep Shorts in Subscriptions";
+"KEEP_SHORTS_SUBSCRIPT_DESC" = "Prevents removing Shorts from Subscriptions tab.";
"HIDE_SEARCH_HISTORY" = "Hide search history";
"HIDE_SEARCH_HISTORY_DESC" = "Hide previous search history and suggestions when using the search bar.\nNOTE: Your search history will still be accessible in your other YouTube clients.";
-"HIDE_SUB_BUTTON" = "Hide Subscribe button";
-"HIDE_SUB_BUTTON_DESC" = "Hides the subscribe button in channels and video descriptions.";
-"HIDE_SHOP_BUTTON" = "Hide Shopping button";
-"HIDE_SHOP_BUTTON_DESC" = "Hides the shopping/store button in channels descriptions.";
-"HIDE_MEMBER_BUTTON" = "Hide Membership button";
-"HIDE_MEMBER_BUTTON_DESC" = "Hides the 'Join' button in channels and video descriptions.";
+"HIDE_PLAYABLES" = "Remove playables";
+"HIDE_PLAYABLES_DESC" = "Removes playables (mini-games) from the feed.";
"PLAYER" = "Player";
+"PLAYBACK_SPEED" = "Playback speed";
+"CONTROL_CENTER" = "Control Center";
+"PLAYER_ACTIONS" = "Player actions";
+"GESTURE_HEADER" = "Gestures";
+"QUALITY_WIFI" = "Playback quality on WiFi";
+"QUALITY_WIFI_DESC" = "Automatically applies the selected quality when streaming over Wi-Fi.";
+"QUALITY_CELLULAR" = "Playback quality on Cellular";
+"QUALITY_CELLULAR_DESC" = "Automatically applies the selected quality when streaming over mobile data.";
+"QUALITY_LOW_POWER" = "Playback quality on Power saver mode";
+"QUALITY_LOW_POWER_DESC" = "Automatically applies the selected quality when when Low power mode is enabled on your device.";
+"DEFAULT" = "Default";
+"BEST" = "Best";
+"AUDIO_TRACK" = "Preferred audio track";
+"ORIGINAL" = "Original";
+"SELECT_MANUALLY" = "Select manually";
+"AUDIO_TRACK_SELECT" = "Selected audio track";
+"AUDIO_TRACK_SELECT_DESC" = "Specifies the preferred audio track language to use for playback, if available.";
+"NO_AUTO_DUBBED" = "Uses original audio track";
+"NO_AUTO_DUBBED_DESC" = "Uses original audio track if the preferred language track is auto-dubbed or not found.";
+"CAPTION_TRACK" = "Preferred captions";
+"CAPTION_TRACK_SELECT" = "Selected captions";
+"CAPTION_TRACK_SELECT_DESC" = "Specifies the preferred captions language to display, if available.";
+"DISABLES_CAPTION_TRACK" = "Automatically disable captions";
+"DISABLES_CAPTION_TRACK_DESC" = "Automatically disable captions if the preferred language is auto-generated or not found.";
+"DEFAULT_SPEED" = "Default playback rate";
+"DEFAULT_SPEED_DESC" = "Overrides the default playback speed.";
+"SPEED_BUTTON" = "Speed controls button";
+"SPEED_BUTTON_DESC" = "Displays a speed controls button on overlay.";
+"MUTE_BUTTON" = "Mute button";
+"MUTE_BUTTON_DESC" = "Displays a button to mute or unmute the volume on the overlay.";
+"SHARE_BUTTON" = "Share button";
+"SHARE_BUTTON_DESC" = "Displays a button to share the video link on the overlay.";
+"LOOP_BUTTON" = "Loop button";
+"LOOP_BUTTON_DESC" = "Displays a button to loop the video on the overlay.";
+"CAPTION_BUTTON" = "Captions button";
+"CAPTION_BUTTON_DESC" = "Displays a button to copy the video captions on the overlay.";
+"HOLD_TO_SPEED" = "Hold to speed";
+"HOLD_TO_SPEED_DESC" = "Uses custom hold to speed playback rate.";
"HIDE_AUTOPLAY" = "Hide autoplay switch";
"HIDE_AUTOPLAY_DESC" = "Hide autoplay switch in the video overlay.";
"HIDE_CAPTIONS_BUTTON" = "Hide captions button";
"HIDE_CAPTIONS_BUTTON_DESC" = "Hide captions button in the video overlay.";
"HIDE_CAST_BUTTON_PLAYER" = "Hide cast button";
"HIDE_CAST_BUTTON_PLAYER_DESC" = "Hide cast button in the video overlay.";
-"HIDE_PREV_BUTTON" = "Hide previous/back button";
-"HIDE_PREV_BUTTON_DESC" = "Hide previous/back button in the video overlay.";
-"HIDE_NEXT_BUTTON" = "Hide next/skip button";
-"HIDE_NEXT_BUTTON_DESC" = "Hide next/skip button in the video overlay.";
+"HIDE_NEXT_AND_PREV_BUTTON" = "Hide previous and next buttons";
+"HIDE_NEXT_AND_PREV_BUTTON_DESC" = "Hide previous and next buttons in the video overlay.";
"REPLACE_PREVNEXT_BUTTONS" = "Replace Previous/Next buttons";
"REPLACE_PREVNEXT_BUTTONS_DESC" = "Replaces the Previous and Next video buttons in the player with Rewind and Fast Forward buttons.";
+"SKIP_BACKWARD" = "Skip backward";
+"SKIP_BACKWARD_DESC" = "Replaces the previous track button in Control Center with a skip backward button by a set number of seconds.";
+"SKIP_FORWARD" = "Skip forward";
+"SKIP_FORWARD_DESC" = "Replaces the next track button in Control Center with a skip forward button by a set number of seconds.";
+"REWIND_SECONDS" = "Rewind seconds";
+"FORWARD_SECONDS" = "Fast-forward seconds";
"REMOVE_DARK_OVERLAY" = "Remove dark overlay";
"REMOVE_DARK_OVERLAY_DESC" = "Remove dark overlay from the video overlay.";
"HIDE_END_SCREEN" = "Hide endscreen cards";
@@ -102,6 +224,12 @@
"HIDE_PAID_OVERLAY_DESC" = "Hide 'Includes paid promotion' in the video overlay.";
"HIDE_WATERMARK" = "Hide watermark";
"HIDE_WATERMARK_DESC" = "Hide channel watermark at the bottom of the video overlay.";
+"PAUSE_ON_OVERLAY" = "Pause on overlay";
+"PAUSE_ON_OVERLAY_DESC" = "Sets playback on pause if overlay appears.";
+"DISABLES_SNAP_TO_CHAPTER" = "Disable snap to chapters";
+"DISABLES_SNAP_TO_CHAPTER_DESC" = "Prevents the scrubbar stick to the chapters.";
+"DISABLES_ENGAGE_PANEL" = "Disables engagement panel in fullscreen";
+"DISABLES_ENGAGE_PANEL_DESC" = "Prevent engagement panel showing in fullscreen.";
"GESTURES" = "Enable gestures controls";
"GESTURES_DESC" = "Swipe up or down on the left/right edges of the video screen to adjust brightness, volume, or playback speed.";
"GESTURE_AREA" = "Gesture activation area";
@@ -136,6 +264,8 @@
"DISABLES_SHOW_REMAINING_DESC" = "Disable the feature that shows how much time is left in a video when tapping the timestamp.";
"ALWAYS_SHOW_REMAINING" = "Always show remaining time";
"ALWAYS_SHOW_REMAINING_DESC" = "Ensure the remaining time is always displayed by default in the player overlay.";
+"COPY_TIMESTAMP_ON_PAUSE" = "Copy timestamped links";
+"COPY_TIMESTAMP_ON_PAUSE_DESC" = "Allows to copy timestamped link to the clipboard by pressing pause button.";
"SHOW_REMAINING_EXTRA" = "Show extra remaining time";
"SHOW_REMAINING_EXTRA_DESC" = "Adds the estimated finish time to the label in the video overlay.";
"HIDE_FULLSCREEN_ACTIONS" = "Hide fullscreen actions";
@@ -148,93 +278,95 @@
"HIDE_CONTENT_WARNING_DESC" = "Automatically skip the sensitive content warning alert in some vidoes.";
"AUTO_FULLSCREEN" = "Auto fullscreen";
"AUTO_FULLSCREEN_DESC" = "Automatically enter fullscreen mode when starting video playback.";
-"AUTO_DISABLES_CAPTION" = "Auto disable captions";
-"AUTO_DISABLES_CAPTION_DESC" = "Automatically turns off closed captions (CC) whenever a new video starts playing.";
"PORTRAIT_FULLSCREEN" = "Portrait fullscren mode";
"PORTRAIT_FULLSCREEN_DESC" = "Enables portrait fullscreen mode support.";
"OLD_QUALITY_PICKER" = "Use old quality picker";
"OLD_QUALITY_PICKER_DESC" = "Reverts the video quality selection menu to the classic version (lists all resolutions immediately).";
"EXTRA_SPEED" = "Extra speed options";
"EXTRA_SPEED_DESC" = "Adds more video playback speed options up to 10x.";
-"DISABLE_HINTS" = "Disable hints";
-"DISABLE_HINTS_DESC" = "Disable the tutorial hints and interactive pop-ups.";
"FORCE_MINIPLAYER" = "Force miniplayer";
"FORCE_MINIPLAYER_DESC" = "Allows all videos, including those made for kids, to play in the miniplayer when dismissing the player.";
"FORCE_SEEKBAR" = "Always show progress bar";
"FORCE_SEEKBAR_DESC" = "Makes the progress bar (seekbar) remains visible at all times while in fullscreen mode.";
-"HIDE_LIKE_BUTTON" = "Hide Like button";
-"HIDE_LIKE_BUTTON_DESC" = "Hide Like button in the action bar under the video overlay.";
-"HIDE_DISLIKE_BUTTON" = "Hide Dislike button";
-"HIDE_DISLIKE_BUTTON_DESC" = "Hide Dislike button in the action bar under the video overlay.";
-"HIDE_SHARE_BUTTON" = "Hide Share button";
-"HIDE_SHARE_BUTTON_DESC" = "Hide Share button in the action bar under the video overlay.";
-"HIDE_DOWNLOAD_BUTTON" = "Hide Download button";
-"HIDE_DOWNLOAD_BUTTON_DESC" = "Hide Download button in the action bar under the video overlay.";
-"HIDE_CLIP_BUTTON" = "Hide Clip button";
-"HIDE_CLIP_BUTTON_DESC" = "Hide Clip button in the action bar under the video overlay.";
-"HIDE_REMIX_BUTTON" = "Hide Remix button";
-"HIDE_REMIX_BUTTON_DESC" = "Hide Remix button in the action bar under the video overlay.";
-"HIDE_SAVE_BUTTON" = "Hide Save button";
-"HIDE_SAVE_BUTTON_DESC" = "Hide Save button in the action bar under the video overlay.";
+"TAP_TO_SEEK" = "Tap to seek";
+"TAP_TO_SEEK_DESC" = "Quickly jump to any point by tapping the progress bar.";
+"DISABLES_ZOOM" = "Disable free zoom gesture";
+"DISABLES_ZOOM_DESC" = "Disables new free zoom gestures.";
+"USES_24_HOURS_TIME" = "24-hour format";
+"USES_24_HOURS_TIME_DESC" = "Shows end time in 24-hour format.";
+"PAUSE_TWO_FINGERS" = "Pause using two fingers";
+"PAUSE_TWO_FINGERS_DESC" = "Pause/Resume playback by tapping on the screen using two fingers.";
+"HIDE_COMMENTS_SECTION" = "Hide comments section";
+"HIDE_COMMENTS_SECTION_DESC" = "Completely removes the entire comments section.";
+"HIDE_COMMENTS_PREVIEW" = "Hide preview comments";
+"HIDE_COMMENTS_PREVIEW_DESC" = "Removes the preview box for the top comment while keeping the full section expandable.";
+"USE_ANOTHER_MINIPLAYER" = "Uses another miniplayer style";
+"USE_ANOTHER_MINIPLAYER_DESC" = "Uses another miniplayer style redesigned from older YT versions.";
"SHORTS" = "Shorts";
-"HIDE_SHORTS_LIKE_BUTTON" = "Hide Like button";
-"HIDE_SHORTS_LIKE_BUTTON_DESC" = "Hide the Like button in the Shorts player.";
-"HIDE_SHORTS_DISLIKE_BUTTON" = "Hide Dislike button";
-"HIDE_SHORTS_DISLIKE_BUTTON_DESC" = "Hide the Dislike button in the Shorts player.";
-"HIDE_SHORTS_COMMENT_BUTTON" = "Hide Comment button";
-"HIDE_SHORTS_COMMENT_BUTTON_DESC" = "Hide the Comment button in the Shorts player.";
-"HIDE_SHORTS_SHARE_BUTTON" = "Hide Share button";
-"HIDE_SHORTS_SHARE_BUTTON_DESC" = "Hide the Share button in the Shorts player.";
-"HIDE_SHORTS_REMIX_BUTTON" = "Hide Remix button";
-"HIDE_SHORTS_REMIX_BUTTON_DESC" = "Hide the Remix button in the Shorts player.";
-"HIDE_METADATA_BUTTON" = "Hide sound metadata button";
-"HIDE_METADATA_BUTTON_DESC" = "Hide the button that shows the sound source or music metadata in the Shorts player.";
+"SHORTS_ONLY" = "Shorts only mode";
+"SHORTS_ONLY_DESC" = "Limits YouTube functionality to viewing Shorts only.";
+"SHORTS_ACTION" = "Shorts action after the video ends";
+"LOOP" = "Loop video";
+"SKIP_TO_NEXT_SHORTS" = "Skip to next video";
+"PAUSE_SHORTS" = "Stop video";
+"REMOVE_LIVE_SHORTS" = "Remove live videos";
+"REMOVE_LIVE_SHORTS_DESC" = "Removes live videos from the feed.";
"HIDE_SHORTS_PRODUCT" = "Hide products";
"HIDE_SHORTS_PRODUCT_DESC" = "Hide tagged products and shopping links in the Shorts overlay.";
"HIDE_SHORTS_RECBAR" = "Hide recommendation bar";
"HIDE_SHORTS_RECBAR_DESC" = "Hide the suggested action bar in the Shorts player (e.g. Try this sound, Shopping, Preview comment).";
-"HIDE_SHORTS_COMMIT" = "Hide commissions bar";
-"HIDE_SHORTS_COMMIT_DESC" = "Hide the bar that shows the video got commissions in the Shorts player.";
-"HIDE_SHORTS_SUBSCRIPT_BUTTON" = "Hide subscriptions button";
-"HIDE_SHORTS_SUBSCRIPT_BUTTON_DESC" = "Hide the Subscriptions button at the top of the Shorts player.";
-"HIDE_SHORTS_LIVE_BUTTON" = "Hide live button";
-"HIDE_SHORTS_LIVE_BUTTON_DESC" = "Hide the Live button at the top of the Shorts player.";
-"HIDE_SHORTS_LENS_BUTTON" = "Hide lens button";
-"HIDE_SHORTS_LENS_BUTTON_DESC" = "Hide the Lens button at the top of the Shorts player.";
-"HIDE_SHORTS_TRENDS_BUTTON" = "Hide trends button";
-"HIDE_SHORTS_TRENDS_BUTTON_DESC" = "Hide the Trends button at the top of the Shorts player.";
-"HIDE_SHORTS_TO_VIDEO" = "Hide link to full video";
-"HIDE_SHORTS_TO_VIDEO_DESC" = "Hide the link that redirects in a Short to a full video in the Shorts player.";
"ENABLES_SHORTS_QUALITY" = "Enable Shorts quality picker";
"ENABLES_SHORTS_QUALITY_DESC" = "Force enable the video quality selection menu in Shorts.";
"SHOW_SHORTS_SEEKBAR" = "Always show seekbar";
"SHOW_SHORTS_SEEKBAR_DESC" = "Ensure the progress/seek bar is always visible at the bottom of the Shorts player.";
+"REMOVE_POSTS_SHORTS" = "Remove posts";
+"REMOVE_POSTS_SHORTS_DESC" = "Remove posts from the feed.";
+"SHORTS_FULLSCREEN" = "Pinch to Fullscreen";
+"SHORTS_FULLSCREEN_DESC" = "Manages visibility of the overlay with pinch in and pinch out gestures, displaying Shorts in fullscreen mode.";
"TABBAR" = "Tab bar";
"DEFAULT_TAB" = "Default startup tab";
-"DEFAULT_TAB_DESC" = "You can select the tab you want to open when opening the app.";
-"HOME_NAME" = "Home";
-"SUBSCRIPT_NAME" = "Subscriptions";
-"LIB_NAME" = "Library (You)";
+"FORSTED_TAB_BAR" = "Translucent tab bar";
+"ENABLED" = "Enabled";
"HIDE_TAB_INDI" = "Hide tab indicators";
"HIDE_TAB_INDI_DESC" = "Hide all tab indicators in the tab bar.";
"HIDE_TAB_LABELS" = "Hide tab labels";
"HIDE_TAB_LABELS_DESC" = "Hide all tab labels in the tab bar.";
-"HIDE_HOME_TAB" = "Hide Home tab";
-"HIDE_HOME_TAB_DESC" = "Hide Home tab in the tab bar.";
-"HIDE_SHORTS_TAB" = "Hide Shorts tab";
-"HIDE_SHORTS_TAB_DESC" = "Hide Shorts tab in the tab bar.";
-"HIDE_CREATE_BUTTON" = "Hide Create button";
-"HIDE_CREATE_BUTTON_DESC" = "Hide Create button in the tab bar.";
-"HIDE_SUBSCRIPT_TAB" = "Hide Subscriptions tab";
-"HIDE_SUBSCRIPT_TAB_DESC" = "Hide Subscriptions tab in the tab bar.";
+"HOME_TAB" = "Home";
+"SHORTS_TAB" = "Shorts";
+"CREATE_TAB" = "Create";
+"SUBSCRIPTIONS_TAB" = "Subscriptions";
+"LIBRARY_TAB" = "Library (You)";
+"HISTORY_TAB" = "History";
+"GAMING_TAB" = "Gaming";
+"SPORTS_TAB" = "Sports";
+"NOTI_TAB" = "Notifications";
+"NEWS_TAB" = "News";
+"MUSIC_TAB" = "Music";
+"WATCH_LATER_TAB" = "Watch later";
+"PLAYLIST_TAB" = "Playlists";
+"LIKE_TAB" = "Liked videos";
+"LIVE_TAB" = "Live";
+"POST_TAB" = "Posts";
+"VIDEO_TAB" = "Your videos";
+"MOVIE_TAB" = "Movies";
+"COURSE_TAB" = "Courses";
+"MINIGAME_TAB" = "Playables";
+"MANAGE_TABS" = "Manage tabs";
+"MANAGE_TABS_DESC" = "Reorder and toggle tabs";
+"TAB_REORDER_HINT" = "Drag to reorder. Toggle to show/hide.\nTab settings are also accessible by long-pressing the first tab in the tab bar.";
+"TAB_LIMIT" = "Tab limit";
+"TAB_LIMIT_DESC" = "Only 6 tabs are allowed.";
+"ZERO_TAB_DESC" = "At least 1 tab has to be enabled.";
"MISCELLANEOUS" = "Miscellaneous";
"BACKGROUND_PLAYBACK" = "Background playback";
"BACKGROUND_PLAYBACK_DESC" = "Enables background playback for videos.";
"DISABLES_SHORTS_PIP" = "Disables Shorts PiP";
"DISABLES_SHORTS_PIP_DESC" = "Try to disables Shorts PiP, this might not 100% disables it.";
+"DISABLE_HINTS" = "Disable hints";
+"DISABLE_HINTS_DESC" = "Disable the tutorial hints and interactive pop-ups.";
"BLOCK_UPGRADE_DIALOGS" = "Block upgrade dialogs";
"BLOCK_UPGRADE_DIALOGS_DESC" = "Block the YouTube dialogs that ask you to upgrade the app.";
"ARE_YOU_THERE_DIALOG" = "Hide 'Are you there?' dialog";
@@ -247,7 +379,109 @@
"DISABLES_SNACK_BAR_DESC" = "Disables snack bar when pressing some buttons or doing something in the app.";
"HIDE_STARTUP_ANIMATIONS" = "Hide YouTube's startup animations";
"HIDE_STARTUP_ANIMATIONS_DESC" = "Hide YouTube's startup animations when opening the app.";
-"HIDE_PLAY_IN_NEXT_QUEUE" = "Hide 'Play next in queue'";
-"HIDE_PLAY_IN_NEXT_QUEUE_DESC" = "Hide the 'Play next in queue' option in the flyout menu.";
"HIDE_LIKE_DISLIKE_VOTES" = "Hide like/dislike votes";
-"HIDE_LIKE_DISLIKE_VOTES_DESC" = "Hide messages that pop up when you like/dislike videos.";
\ No newline at end of file
+"HIDE_LIKE_DISLIKE_VOTES_DESC" = "Hide messages that pop up when you like/dislike videos.";
+"FLYOUT_MENU" = "Flyout menu";
+"REMOVE_PLAY_IN_NEXT_QUEUE_OPTION" = "Removes 'Play next in queue' option";
+"REMOVE_PLAY_IN_NEXT_QUEUE_OPTION_DESC" = "Removes 'Play next in queue' option from the flyout menu.";
+"REMOVE_DOWNLOAD_OPTION" = "Removes 'Download video' option";
+"REMOVE_DOWNLOAD_OPTION_DESC" = "Removes 'Download video' option from the flyout menu.";
+"REMOVE_WATCH_LATER_OPTION" = "Removes 'Save to Watch later' option";
+"REMOVE_WATCH_LATER_OPTION_DESC" = "Removes 'Save to Watch Later' option from the flyout menu.";
+"REMOVE_SAVE_OPTION" = "Removes 'Save to playlist' option";
+"REMOVE_SAVE_OPTION_DESC" = "Removes 'Save to playlist' option from the flyout menu.";
+"REMOVE_REMOVE_FROM_PLAYLIST_OPTION" = "Removes 'Remove from playlist' option";
+"REMOVE_REMOVE_FROM_PLAYLIST_OPTION_DESC" = "Removes 'Remove from playlist' option from the flyout menu.";
+"REMOVE_SHARE_OPTION" = "Removes 'Share' option";
+"REMOVE_SHARE_OPTION_DESC" = "Removes 'Share' option from the flyout menu.";
+"REMOVE_NOT_INTERESTED_OPTION" = "Removes 'Not interested' option";
+"REMOVE_NOT_INTERESTED_OPTION_DESC" = "Removes 'Not interested' option from the flyout menu.";
+"REMOVE_DONT_RECOMMEND_OPTION" = "Removes 'Don't recommend channel' option";
+"REMOVE_DONT_RECOMMEND_OPTION_DESC" = "Removes 'Don't recommend channel' option from the flyout menu.";
+"REMOVE_INFO_OPTION" = "Removes 'Why are you seeing this?' option";
+"REMOVE_INFO_OPTION_DESC" = "Removes 'Why are you seeing this?' option from the flyout menu.";
+"REMOVE_FILTER_OPTION" = "Removes 'Search filters' option";
+"REMOVE_FILTER_OPTION_DESC" = "Removes 'Search filters' option from the flyout menu.";
+"REMOVE_REPORT_OPTION" = "Removes 'Report' option";
+"REMOVE_REPORT_OPTION_DESC" = "Removes 'Report' option from the flyout menu.";
+"REMOVE_YOUTUBE_MUSIC_OPTION" = "Removes 'Listen with YouTube Music' option";
+"REMOVE_YOUTUBE_MUSIC_OPTION_DESC" = "Removes 'Listen with YouTube Music' option from the flyout menu.";
+"REMOVE_FEED_BACK_OPTION" = "Removes 'Send feedback' option";
+"REMOVE_FEED_BACK_OPTION_DESC" = "Removes 'Send feedback' option from the flyout menu.";
+"REMOVE_CAST_OPTION" = "Removes 'Watch on TV' option";
+"REMOVE_CAST_OPTION_DESC" = "Removes 'Watch on TV' option from the flyout menu.";
+"REMOVE_SHUFFLE_OPTION" = "Removes 'Shuffle' option";
+"REMOVE_SHUFFLE_OPTION_DESC" = "Removes 'Shuffle' option from the flyout menu.";
+"REMOVE_UN_SUB_OPTION" = "Removes 'Unsubscribe' option";
+"REMOVE_UN_SUB_OPTION_DESC" = "Removes 'Unsubscribe' option from the flyout menu.";
+"REMOVE_HIDE_FROM_PLAYLIST_OPTION" = "Removes 'Hide video from playlist' option";
+"REMOVE_HIDE_FROM_PLAYLIST_OPTION_DESC" = "Removes 'Hide video from playlist' option from the flyout menu.";
+"REMOVE_HELP_OPTION" = "Removes 'Help & feedback' option";
+"REMOVE_HELP_OPTION_DESC" = "Removes 'Help & feedback' option from the flyout menu.";
+"REMOVE_NOTIFY_OPTION" = "Removes 'Notify me' option";
+"REMOVE_NOTIFY_OPTION_DESC" = "Removes 'Notify me' option from the flyout menu.";
+"REMOVE_CLEARSCREEN_OPTION" = "Removes 'Clear Screen' option";
+"REMOVE_CLEARSCREEN_OPTION_DESC" = "Removes 'Clear Screen' option from the flyout menu.";
+"HIDE_COMMU_GUIDE" = "Hide community guidelines";
+"HIDE_COMMU_GUIDE_DESC" = "Hide community guidelines header.";
+"DISABLES_RTL" = "Disable RTL formatting";
+"DISABLES_RTL_DESC" = "Forcefully displays text in left-to-right (LTR) format for languages that are initially displayed in right-to-left (RTL).";
+"DEVICE_UI" = "Interface style";
+
+"SB_DESCRIPTION" = "Skip sponsored segments automatically using community-submitted data.";
+"SB_ENABLE" = "Enable SponsorBlock";
+"SB_ENABLE_DESC" = "Activate the SponsorBlock extension to skip sponsored segments.";
+"SB_SHOW_BUTTON" = "Show overlay button";
+"SB_SHOW_BUTTON_DESC" = "Display a SponsorBlock toggle button in the player overlay.";
+"SB_SHOW_NOTIFICATIONS" = "Show notifications";
+"SB_SHOW_NOTIFICATIONS_DESC" = "Display notifications when segments are skipped. Notifications for manual-skip categories are always shown.";
+"SB_HAPTIC_FEEDBACK" = "Haptic feedback";
+"SB_HAPTIC_FEEDBACK_DESC" = "Provide haptic feedback when a segment is skipped.";
+"SB_SEGMENTS_IN_PLAYER" = "Segments in player";
+"SB_SEGMENTS_IN_PLAYER_DESC" = "Display colored segments on the progress bar of the main video player.";
+"SB_SEGMENTS_IN_FEED" = "Segments in feed";
+"SB_SEGMENTS_IN_FEED_DESC" = "Display colored segments on the progress bar of players in the feed.";
+"SB_SEGMENTS_IN_MINIPLAYER" = "Segments in mini-player";
+"SB_SEGMENTS_IN_MINIPLAYER_DESC" = "Display colored segments on the progress bar of the mini-player.";
+"SB_SHOW_DURATION" = "Show duration without segments";
+"SB_SHOW_DURATION_DESC" = "Display the total video duration excluding skippable segments.";
+"SB_CATEGORIES_HEADER" = "Segment categories";
+"SB_SEGMENT_COLOR" = "Segment color";
+
+"SB_ACTION_DISABLE" = "Disable";
+"SB_ACTION_AUTO_SKIP" = "Auto-skip";
+"SB_ACTION_ASK" = "Ask before skipping";
+"SB_ACTION_DISPLAY" = "Show on seek bar only";
+"SB_ACTION_SKIP_TO" = "Skip to segment";
+
+"SB_CAT_sponsor" = "Sponsor";
+"SB_CAT_sponsor_DESC" = "Paid promotion for a product or service not directly related to the creator.";
+"SB_CAT_intro" = "Intro";
+"SB_CAT_intro_DESC" = "Intermission or intro animation typically seen at the start of a video.";
+"SB_CAT_outro" = "Endcards";
+"SB_CAT_outro_DESC" = "Endcards, credits, or outros at the end of the video.";
+"SB_CAT_interaction" = "Interaction";
+"SB_CAT_interaction_DESC" = "Reminders to like, subscribe, or follow on social media.";
+"SB_CAT_selfpromo" = "Self-promotion";
+"SB_CAT_selfpromo_DESC" = "Unpaid promotion of the creator's own products, merchandise, or other content.";
+"SB_CAT_music_offtopic" = "Non-music";
+"SB_CAT_music_offtopic_DESC" = "Non-music sections in music videos (e.g. talking, introductions).";
+"SB_CAT_preview" = "Preview";
+"SB_CAT_preview_DESC" = "Recap or preview of content from another video or previous episode.";
+"SB_CAT_hook" = "Hook/Greetings";
+"SB_CAT_hook_DESC" = "Narrated trailers for the upcoming video, greetings and goodbyes.";
+"SB_CAT_poi_highlight" = "Highlight";
+"SB_CAT_poi_highlight_DESC" = "The key moment or highlight point of the video. Use 'Skip to' to jump directly to it.";
+"SB_CAT_filler" = "Filler";
+"SB_CAT_filler_DESC" = "Tangential content, jokes, or filler that is not directly related to the main topic.";
+
+"SB_SKIPPED" = "%@ segment has been skipped";
+"SB_DETECTED" = "%@ segment detected.\nWould you like to skip the segment?";
+"SB_SKIP_NOW" = "Skip";
+"SB_UNSKIP" = "Unskip";
+"SB_JUMPED_TO_HIGHLIGHT" = "Jumped to highlight";
+"SB_JUMP_TO_HIGHLIGHT" = "Highlight available. Jump to the point?";
+"SB_SEGMENT_COLOR_SUFFIX" = "Segment color";
+"SB_SECTION_MAIN" = "Main";
+"SB_SKIP_ALERT_DURATION" = "Skip alert duration";
+"SB_UNSKIP_ALERT_DURATION" = "Unskip alert duration";
\ No newline at end of file
diff --git a/layout/Library/Application Support/YouMod.bundle/fi.lproj/Localizable.strings b/layout/Library/Application Support/YouMod.bundle/fi.lproj/Localizable.strings
index bccaf550..2c1a8a0f 100644
--- a/layout/Library/Application Support/YouMod.bundle/fi.lproj/Localizable.strings
+++ b/layout/Library/Application Support/YouMod.bundle/fi.lproj/Localizable.strings
@@ -1,11 +1,18 @@
-"NOTE" = "NOTE: Some hiding features may leave a blank box.";
"SETTINGS" = "Settings";
+"INTERFACE" = "Interface";
+
+"LOOP_DISABLED" = "Loop disabled";
+"LOOP_ENABLED" = "Loop enabled";
"NEW_ISSUES" = "Report issues or request new features";
"NEW_ISSUES_DESC" = "Found bugs or want to request new features? Tap here.";
"SOURCE_CODES" = "Source codes";
"SOURCE_CODES_DESC" = "Take a look at the codes!";
+"EXTRA" = "Extra";
+"FIX_PLAYBACK_ISSUES" = "Fix playback issues (Temporary)";
+"FIX_PLAYBACK_ISSUES_DESC" = "Try to fix video playback issues for some users. Turning this on will make the player automatically reloads itself everytime it errors or fails to play the video.";
+
"PERFER_HEADER" = "YouMod perferences";
"PERFER" = "Perferences";
"IMPORT" = "Import settings";
@@ -24,6 +31,7 @@
"ERROR_NO_KEYS_IMPORT" = "No available keys to import";
"ERROR_NO_KEYS_EXPORT" = "No available keys to export";
"OVERRIDE" = "This will override the current settings. Do you want to continue?";
+"DISABLED" = "Disabled";
"CACHE" = "Cache management";
"CLEARCACHE" = "Clear cache";
@@ -32,12 +40,80 @@
"AUTO_CLEARCACHE_DESC" = "Automatically clears the app cache on startup.";
"DOWNLOADING" = "Downloading";
-"DOWNLOAD_MANAGER" = "Download Manager";
-"DOWNLOAD_MANAGER_DESC" = "Replaces YouTube's video downloader with YouMod one. With ablity to downloading videos, audios, captions, and thumbnails";
+"DOWNLOAD_MANAGER" = "Download manager";
+"DOWNLOAD_MANAGER_DESC" = "Replaces YouTube's video downloader with YouMod one. With ablity to downloading videos, audios, captions, and thumbnails.";
"DOWNLOAD_SAVE_PHOTOS" = "Automatically save to Photos";
"DOWNLOAD_SAVE_PHOTOS_DESC" = "Automatically saves downloaded videos and images directly to your Photos app.";
-"DOWNLOAD_DRC_AUDIO" = "Prefer DRC Audio";
-"DOWNLOAD_DRC_AUDIO_DESC" = "Prioritizes downloading audio tracks with Dynamic Range Compression for more consistent volume levels.";
+"ADD_SHORTS_DOWNLOAD" = "Adds a download button to Shorts";
+"ADD_SHORTS_DOWNLOAD_DESC" = "Adds a download button to Shorts video overlay.";
+"USE_ORIG_AUDIO" = "Automatically uses original audio track";
+"USE_ORIG_AUDIO_DESC" = "Automatically uses original audio track instead of showing a dialog.";
+"DOWNLOAD_SHORTS" = "Download Shorts";
+"DOWNLOAD_SHORTS_DESC" = "Choose Shorts quality";
+"COPY_VID_INFO" = "Copy video informations";
+"COPY_VID_INFO_DESC" = "Choose what to copy";
+"COPY_ALL_VID_INFO" = "Copy all";
+"COPY_ALL_VID_INFO_DESC" = "Copy all video informations";
+"COPY_AUTHOR" = "Copy author";
+"COPY_AUTHOR_DESC" = "Copy the video author";
+"COPY_TITLE" = "Copy title";
+"COPY_TITLE_DESC" = "Copy the video title";
+"COPY_DESCRIPTION" = "Copy description";
+"COPY_DESCRIPTION_DESC" = "Copy the video description";
+"COPY_URL" = "Copy video link";
+"COPY_URL_TIMESTAMP" = "Copy video link with timestamp";
+"URL_COPIED" = "Copied video link";
+"URL_TIMESTAMP_COPIED" = "Copied video link with timestamp";
+"ERROR_VIDEOID" = "Cannot get the video ID";
+"ERROR_ADS" = "Cannot copy video link while playing ads";
+"COPIED_AUTHOR" = "Copied video author";
+"COPIED_TITLE" = "Copied video title";
+"COPIED_DESCRIPTION" = "Copied video description";
+"SAVE_THUMBNAIL" = "Save thumbnail";
+"SAVE_THUMBNAIL_DESC" = "Save to Photos";
+"DOWNLOAD_CAPTIONS" = "Download captions";
+"DOWNLOAD_CAPTIONS_DESC" = "Save subtitles as .VTT file";
+"DOWNLOAD_AUDIO" = "Download audio";
+"DOWNLOAD_AUDIO_DESC" = "Download audio as .m4a file";
+"DOWNLOAD_VIDEO" = "Download video";
+"DOWNLOAD_VIDEO_DESC" = "Choose video quality";
+"OPEN_VID_BEFORE" = "Open a video before using the download manager";
+"NO_CAPTIONS_URL" = "No valid caption URLs found";
+"NO_AUDIO_URL" = "No audio URL found";
+"NO_STREAM_URL" = "No stream URL found";
+"CAPTIONS_FAILED" = "Failed to download captions";
+"NO_CAPTIONS" = "No captions available for this video";
+"NO_AUDIO_STREAM_FOUND" = "No audio streams found";
+"NO_VID_AUDIO_STREAM_FOUND" = "No video/audio streams found";
+"COPIED_VID_INFO" = "Copied all video informations";
+"SAVED_TO_PHOTOS" = "Saved to Photos";
+"SAVE_FAILED" = "Save failed";
+"PHOTO_ACCESS_DENINED" = "Photos access denied";
+"THUMBNAIL_FAILED" = "Thumbnail failed";
+"DOWNLOADING_THUMBNAIL" = "Downloading thumbnail...";
+"DOWNLOADING_AUDIO" = "Downloading audio...";
+"DOWNLOADING_VIDEO" = "Downloading video...";
+"DOWNLOADING_CAPTIONS" = "Downloading captions...";
+"NO_THUMBNAIL_FOUND" = "No thumbnail found";
+"DOWNLOAD_FAILED" = "Download failed";
+"COPY_THUMBNAIL" = "Copy thumbnail";
+"COPY_THUMBNAIL_DESC" = "Copy to clipboard";
+"SHOW_THUMBNAIL" = "Show thumbnail";
+"SHOW_THUMBNAIL_DESC" = "Show in a dialog";
+"THUMBNAIL_OPTIONS" = "Download thumbnail";
+"THUMBNAIL_OPTIONS_DESC" = "Show more options";
+"COPIED_TO_CLIPBOARD" = "Copied to clipboard";
+"DOWNLOAD_COMPLETED" = "Download completed";
+"CANNOT_SAVE_TO_PHOTOS" = "Cannot save to Photos";
+"FINA_VIDEO" = "Finalizing video...";
+"MERGING_VID_FALLBACK" = "Merging video with fallback...";
+"MERGING_VID" = "Merging video...";
+"CONVERTING" = "Converting to";
+"ALREADY_DOWNLOADING" = "Already downloading";
+"DOWNLOAD_CANCELLED" = "Download cancelled";
+"CANCEL" = "Cancel";
+"TRIMMING_AUDIO" = "Trimming audio...";
+"COPY" = "Copy";
"APPEARANCE" = "Appearance";
"OLED_THEME" = "OLED dark mode";
@@ -46,6 +122,8 @@
"OLED_KEYBOARD_DESC" = "Make the keyboard uses a pure black (OLED) color for dark theme.";
"NAVBAR" = "Navigation bar";
+"STICKY_NAVBAR" = "Sticky Navigation bar";
+"STICKY_NAVBAR_DESC" = "Pins the Navigation bar so that it remains visible when scrolling down.";
"HIDE_YT_LOGO" = "Hide YouTube logo";
"HIDE_YT_LOGO_DESC" = "Hide YouTube logo in the navigation bar.";
"PREMIUM_LOGO" = "Use YouTube Premium logo";
@@ -62,34 +140,78 @@
"FEED" = "Feed";
"HIDE_SUBBAR" = "Hide subbar";
"HIDE_SUBBAR_DESC" = "Hide subbar at the top of the feed.";
+"HIDE_HORI_SHELF" = "Remove horizontal feeds";
+"HIDE_HORI_SHELF_DESC" = "Removes all horizontal feeds (News, Continue Watching, Posts collection, etc.) from the feed, except Playables.";
"HIDE_MUSIC_PLAYLISTS" = "Hide music playlist generator";
"HIDE_MUSIC_PLAYLISTS_DESC" = "Hide music playlist generator (Select music tunes in three) in the feed.";
"HIDE_FEED_POST" = "Hide posts";
"HIDE_FEED_POST_DESC" = "Hide posts in feed.";
+"HIDE_SURVEYS" = "Hide surveys";
+"HIDE_SURVEYS_DESC" = "Hide video surveys that YouTube shows sometimes.";
"HIDE_SHORTS_SHELF" = "Hide Shorts shelf";
"HIDE_SHORTS_SHELF_DESC" = "Hide Shorts shelf in the feed, including in the Subscriptions tab.";
+"KEEP_SHORTS_SUBSCRIPT" = "Keep Shorts in Subscriptions";
+"KEEP_SHORTS_SUBSCRIPT_DESC" = "Prevents removing Shorts from Subscriptions tab.";
"HIDE_SEARCH_HISTORY" = "Hide search history";
"HIDE_SEARCH_HISTORY_DESC" = "Hide previous search history and suggestions when using the search bar.\nNOTE: Your search history will still be accessible in your other YouTube clients.";
-"HIDE_SUB_BUTTON" = "Hide Subscribe button";
-"HIDE_SUB_BUTTON_DESC" = "Hides the subscribe button in channels and video descriptions.";
-"HIDE_SHOP_BUTTON" = "Hide Shopping button";
-"HIDE_SHOP_BUTTON_DESC" = "Hides the shopping/store button in channels descriptions.";
-"HIDE_MEMBER_BUTTON" = "Hide Membership button";
-"HIDE_MEMBER_BUTTON_DESC" = "Hides the 'Join' button in channels and video descriptions.";
+"HIDE_PLAYABLES" = "Remove playables";
+"HIDE_PLAYABLES_DESC" = "Removes playables (mini-games) from the feed.";
"PLAYER" = "Player";
+"PLAYBACK_SPEED" = "Playback speed";
+"CONTROL_CENTER" = "Control Center";
+"PLAYER_ACTIONS" = "Player actions";
+"GESTURE_HEADER" = "Gestures";
+"QUALITY_WIFI" = "Playback quality on WiFi";
+"QUALITY_WIFI_DESC" = "Automatically applies the selected quality when streaming over Wi-Fi.";
+"QUALITY_CELLULAR" = "Playback quality on Cellular";
+"QUALITY_CELLULAR_DESC" = "Automatically applies the selected quality when streaming over mobile data.";
+"QUALITY_LOW_POWER" = "Playback quality on Power saver mode";
+"QUALITY_LOW_POWER_DESC" = "Automatically applies the selected quality when when Low power mode is enabled on your device.";
+"DEFAULT" = "Default";
+"BEST" = "Best";
+"AUDIO_TRACK" = "Preferred audio track";
+"ORIGINAL" = "Original";
+"SELECT_MANUALLY" = "Select manually";
+"AUDIO_TRACK_SELECT" = "Selected audio track";
+"AUDIO_TRACK_SELECT_DESC" = "Specifies the preferred audio track language to use for playback, if available.";
+"NO_AUTO_DUBBED" = "Uses original audio track";
+"NO_AUTO_DUBBED_DESC" = "Uses original audio track if the preferred language track is auto-dubbed or not found.";
+"CAPTION_TRACK" = "Preferred captions";
+"CAPTION_TRACK_SELECT" = "Selected captions";
+"CAPTION_TRACK_SELECT_DESC" = "Specifies the preferred captions language to display, if available.";
+"DISABLES_CAPTION_TRACK" = "Automatically disable captions";
+"DISABLES_CAPTION_TRACK_DESC" = "Automatically disable captions if the preferred language is auto-generated or not found.";
+"DEFAULT_SPEED" = "Default playback rate";
+"DEFAULT_SPEED_DESC" = "Overrides the default playback speed.";
+"SPEED_BUTTON" = "Speed controls button";
+"SPEED_BUTTON_DESC" = "Displays a speed controls button on overlay.";
+"MUTE_BUTTON" = "Mute button";
+"MUTE_BUTTON_DESC" = "Displays a button to mute or unmute the volume on the overlay.";
+"SHARE_BUTTON" = "Share button";
+"SHARE_BUTTON_DESC" = "Displays a button to share the video link on the overlay.";
+"LOOP_BUTTON" = "Loop button";
+"LOOP_BUTTON_DESC" = "Displays a button to loop the video on the overlay.";
+"CAPTION_BUTTON" = "Captions button";
+"CAPTION_BUTTON_DESC" = "Displays a button to copy the video captions on the overlay.";
+"HOLD_TO_SPEED" = "Hold to speed";
+"HOLD_TO_SPEED_DESC" = "Uses custom hold to speed playback rate.";
"HIDE_AUTOPLAY" = "Hide autoplay switch";
"HIDE_AUTOPLAY_DESC" = "Hide autoplay switch in the video overlay.";
"HIDE_CAPTIONS_BUTTON" = "Hide captions button";
"HIDE_CAPTIONS_BUTTON_DESC" = "Hide captions button in the video overlay.";
"HIDE_CAST_BUTTON_PLAYER" = "Hide cast button";
"HIDE_CAST_BUTTON_PLAYER_DESC" = "Hide cast button in the video overlay.";
-"HIDE_PREV_BUTTON" = "Hide previous/back button";
-"HIDE_PREV_BUTTON_DESC" = "Hide previous/back button in the video overlay.";
-"HIDE_NEXT_BUTTON" = "Hide next/skip button";
-"HIDE_NEXT_BUTTON_DESC" = "Hide next/skip button in the video overlay.";
+"HIDE_NEXT_AND_PREV_BUTTON" = "Hide previous and next buttons";
+"HIDE_NEXT_AND_PREV_BUTTON_DESC" = "Hide previous and next buttons in the video overlay.";
"REPLACE_PREVNEXT_BUTTONS" = "Replace Previous/Next buttons";
"REPLACE_PREVNEXT_BUTTONS_DESC" = "Replaces the Previous and Next video buttons in the player with Rewind and Fast Forward buttons.";
+"SKIP_BACKWARD" = "Skip backward";
+"SKIP_BACKWARD_DESC" = "Replaces the previous track button in Control Center with a skip backward button by a set number of seconds.";
+"SKIP_FORWARD" = "Skip forward";
+"SKIP_FORWARD_DESC" = "Replaces the next track button in Control Center with a skip forward button by a set number of seconds.";
+"REWIND_SECONDS" = "Rewind seconds";
+"FORWARD_SECONDS" = "Fast-forward seconds";
"REMOVE_DARK_OVERLAY" = "Remove dark overlay";
"REMOVE_DARK_OVERLAY_DESC" = "Remove dark overlay from the video overlay.";
"HIDE_END_SCREEN" = "Hide endscreen cards";
@@ -102,6 +224,12 @@
"HIDE_PAID_OVERLAY_DESC" = "Hide 'Includes paid promotion' in the video overlay.";
"HIDE_WATERMARK" = "Hide watermark";
"HIDE_WATERMARK_DESC" = "Hide channel watermark at the bottom of the video overlay.";
+"PAUSE_ON_OVERLAY" = "Pause on overlay";
+"PAUSE_ON_OVERLAY_DESC" = "Sets playback on pause if overlay appears.";
+"DISABLES_SNAP_TO_CHAPTER" = "Disable snap to chapters";
+"DISABLES_SNAP_TO_CHAPTER_DESC" = "Prevents the scrubbar stick to the chapters.";
+"DISABLES_ENGAGE_PANEL" = "Disables engagement panel in fullscreen";
+"DISABLES_ENGAGE_PANEL_DESC" = "Prevent engagement panel showing in fullscreen.";
"GESTURES" = "Enable gestures controls";
"GESTURES_DESC" = "Swipe up or down on the left/right edges of the video screen to adjust brightness, volume, or playback speed.";
"GESTURE_AREA" = "Gesture activation area";
@@ -136,6 +264,8 @@
"DISABLES_SHOW_REMAINING_DESC" = "Disable the feature that shows how much time is left in a video when tapping the timestamp.";
"ALWAYS_SHOW_REMAINING" = "Always show remaining time";
"ALWAYS_SHOW_REMAINING_DESC" = "Ensure the remaining time is always displayed by default in the player overlay.";
+"COPY_TIMESTAMP_ON_PAUSE" = "Copy timestamped links";
+"COPY_TIMESTAMP_ON_PAUSE_DESC" = "Allows to copy timestamped link to the clipboard by pressing pause button.";
"SHOW_REMAINING_EXTRA" = "Show extra remaining time";
"SHOW_REMAINING_EXTRA_DESC" = "Adds the estimated finish time to the label in the video overlay.";
"HIDE_FULLSCREEN_ACTIONS" = "Hide fullscreen actions";
@@ -148,93 +278,95 @@
"HIDE_CONTENT_WARNING_DESC" = "Automatically skip the sensitive content warning alert in some vidoes.";
"AUTO_FULLSCREEN" = "Auto fullscreen";
"AUTO_FULLSCREEN_DESC" = "Automatically enter fullscreen mode when starting video playback.";
-"AUTO_DISABLES_CAPTION" = "Auto disable captions";
-"AUTO_DISABLES_CAPTION_DESC" = "Automatically turns off closed captions (CC) whenever a new video starts playing.";
"PORTRAIT_FULLSCREEN" = "Portrait fullscren mode";
"PORTRAIT_FULLSCREEN_DESC" = "Enables portrait fullscreen mode support.";
"OLD_QUALITY_PICKER" = "Use old quality picker";
"OLD_QUALITY_PICKER_DESC" = "Reverts the video quality selection menu to the classic version (lists all resolutions immediately).";
"EXTRA_SPEED" = "Extra speed options";
"EXTRA_SPEED_DESC" = "Adds more video playback speed options up to 10x.";
-"DISABLE_HINTS" = "Disable hints";
-"DISABLE_HINTS_DESC" = "Disable the tutorial hints and interactive pop-ups.";
"FORCE_MINIPLAYER" = "Force miniplayer";
"FORCE_MINIPLAYER_DESC" = "Allows all videos, including those made for kids, to play in the miniplayer when dismissing the player.";
"FORCE_SEEKBAR" = "Always show progress bar";
"FORCE_SEEKBAR_DESC" = "Makes the progress bar (seekbar) remains visible at all times while in fullscreen mode.";
-"HIDE_LIKE_BUTTON" = "Hide Like button";
-"HIDE_LIKE_BUTTON_DESC" = "Hide Like button in the action bar under the video overlay.";
-"HIDE_DISLIKE_BUTTON" = "Hide Dislike button";
-"HIDE_DISLIKE_BUTTON_DESC" = "Hide Dislike button in the action bar under the video overlay.";
-"HIDE_SHARE_BUTTON" = "Hide Share button";
-"HIDE_SHARE_BUTTON_DESC" = "Hide Share button in the action bar under the video overlay.";
-"HIDE_DOWNLOAD_BUTTON" = "Hide Download button";
-"HIDE_DOWNLOAD_BUTTON_DESC" = "Hide Download button in the action bar under the video overlay.";
-"HIDE_CLIP_BUTTON" = "Hide Clip button";
-"HIDE_CLIP_BUTTON_DESC" = "Hide Clip button in the action bar under the video overlay.";
-"HIDE_REMIX_BUTTON" = "Hide Remix button";
-"HIDE_REMIX_BUTTON_DESC" = "Hide Remix button in the action bar under the video overlay.";
-"HIDE_SAVE_BUTTON" = "Hide Save button";
-"HIDE_SAVE_BUTTON_DESC" = "Hide Save button in the action bar under the video overlay.";
+"TAP_TO_SEEK" = "Tap to seek";
+"TAP_TO_SEEK_DESC" = "Quickly jump to any point by tapping the progress bar.";
+"DISABLES_ZOOM" = "Disable free zoom gesture";
+"DISABLES_ZOOM_DESC" = "Disables new free zoom gestures.";
+"USES_24_HOURS_TIME" = "24-hour format";
+"USES_24_HOURS_TIME_DESC" = "Shows end time in 24-hour format.";
+"PAUSE_TWO_FINGERS" = "Pause using two fingers";
+"PAUSE_TWO_FINGERS_DESC" = "Pause/Resume playback by tapping on the screen using two fingers.";
+"HIDE_COMMENTS_SECTION" = "Hide comments section";
+"HIDE_COMMENTS_SECTION_DESC" = "Completely removes the entire comments section.";
+"HIDE_COMMENTS_PREVIEW" = "Hide preview comments";
+"HIDE_COMMENTS_PREVIEW_DESC" = "Removes the preview box for the top comment while keeping the full section expandable.";
+"USE_ANOTHER_MINIPLAYER" = "Uses another miniplayer style";
+"USE_ANOTHER_MINIPLAYER_DESC" = "Uses another miniplayer style redesigned from older YT versions.";
"SHORTS" = "Shorts";
-"HIDE_SHORTS_LIKE_BUTTON" = "Hide Like button";
-"HIDE_SHORTS_LIKE_BUTTON_DESC" = "Hide the Like button in the Shorts player.";
-"HIDE_SHORTS_DISLIKE_BUTTON" = "Hide Dislike button";
-"HIDE_SHORTS_DISLIKE_BUTTON_DESC" = "Hide the Dislike button in the Shorts player.";
-"HIDE_SHORTS_COMMENT_BUTTON" = "Hide Comment button";
-"HIDE_SHORTS_COMMENT_BUTTON_DESC" = "Hide the Comment button in the Shorts player.";
-"HIDE_SHORTS_SHARE_BUTTON" = "Hide Share button";
-"HIDE_SHORTS_SHARE_BUTTON_DESC" = "Hide the Share button in the Shorts player.";
-"HIDE_SHORTS_REMIX_BUTTON" = "Hide Remix button";
-"HIDE_SHORTS_REMIX_BUTTON_DESC" = "Hide the Remix button in the Shorts player.";
-"HIDE_METADATA_BUTTON" = "Hide sound metadata button";
-"HIDE_METADATA_BUTTON_DESC" = "Hide the button that shows the sound source or music metadata in the Shorts player.";
+"SHORTS_ONLY" = "Shorts only mode";
+"SHORTS_ONLY_DESC" = "Limits YouTube functionality to viewing Shorts only.";
+"SHORTS_ACTION" = "Shorts action after the video ends";
+"LOOP" = "Loop video";
+"SKIP_TO_NEXT_SHORTS" = "Skip to next video";
+"PAUSE_SHORTS" = "Stop video";
+"REMOVE_LIVE_SHORTS" = "Remove live videos";
+"REMOVE_LIVE_SHORTS_DESC" = "Removes live videos from the feed.";
"HIDE_SHORTS_PRODUCT" = "Hide products";
"HIDE_SHORTS_PRODUCT_DESC" = "Hide tagged products and shopping links in the Shorts overlay.";
"HIDE_SHORTS_RECBAR" = "Hide recommendation bar";
"HIDE_SHORTS_RECBAR_DESC" = "Hide the suggested action bar in the Shorts player (e.g. Try this sound, Shopping, Preview comment).";
-"HIDE_SHORTS_COMMIT" = "Hide commissions bar";
-"HIDE_SHORTS_COMMIT_DESC" = "Hide the bar that shows the video got commissions in the Shorts player.";
-"HIDE_SHORTS_SUBSCRIPT_BUTTON" = "Hide subscriptions button";
-"HIDE_SHORTS_SUBSCRIPT_BUTTON_DESC" = "Hide the Subscriptions button at the top of the Shorts player.";
-"HIDE_SHORTS_LIVE_BUTTON" = "Hide live button";
-"HIDE_SHORTS_LIVE_BUTTON_DESC" = "Hide the Live button at the top of the Shorts player.";
-"HIDE_SHORTS_LENS_BUTTON" = "Hide lens button";
-"HIDE_SHORTS_LENS_BUTTON_DESC" = "Hide the Lens button at the top of the Shorts player.";
-"HIDE_SHORTS_TRENDS_BUTTON" = "Hide trends button";
-"HIDE_SHORTS_TRENDS_BUTTON_DESC" = "Hide the Trends button at the top of the Shorts player.";
-"HIDE_SHORTS_TO_VIDEO" = "Hide link to full video";
-"HIDE_SHORTS_TO_VIDEO_DESC" = "Hide the link that redirects in a Short to a full video in the Shorts player.";
"ENABLES_SHORTS_QUALITY" = "Enable Shorts quality picker";
"ENABLES_SHORTS_QUALITY_DESC" = "Force enable the video quality selection menu in Shorts.";
"SHOW_SHORTS_SEEKBAR" = "Always show seekbar";
"SHOW_SHORTS_SEEKBAR_DESC" = "Ensure the progress/seek bar is always visible at the bottom of the Shorts player.";
+"REMOVE_POSTS_SHORTS" = "Remove posts";
+"REMOVE_POSTS_SHORTS_DESC" = "Remove posts from the feed.";
+"SHORTS_FULLSCREEN" = "Pinch to Fullscreen";
+"SHORTS_FULLSCREEN_DESC" = "Manages visibility of the overlay with pinch in and pinch out gestures, displaying Shorts in fullscreen mode.";
"TABBAR" = "Tab bar";
"DEFAULT_TAB" = "Default startup tab";
-"DEFAULT_TAB_DESC" = "You can select the tab you want to open when opening the app.";
-"HOME_NAME" = "Home";
-"SUBSCRIPT_NAME" = "Subscriptions";
-"LIB_NAME" = "Library (You)";
+"FORSTED_TAB_BAR" = "Translucent tab bar";
+"ENABLED" = "Enabled";
"HIDE_TAB_INDI" = "Hide tab indicators";
"HIDE_TAB_INDI_DESC" = "Hide all tab indicators in the tab bar.";
"HIDE_TAB_LABELS" = "Hide tab labels";
"HIDE_TAB_LABELS_DESC" = "Hide all tab labels in the tab bar.";
-"HIDE_HOME_TAB" = "Hide Home tab";
-"HIDE_HOME_TAB_DESC" = "Hide Home tab in the tab bar.";
-"HIDE_SHORTS_TAB" = "Hide Shorts tab";
-"HIDE_SHORTS_TAB_DESC" = "Hide Shorts tab in the tab bar.";
-"HIDE_CREATE_BUTTON" = "Hide Create button";
-"HIDE_CREATE_BUTTON_DESC" = "Hide Create button in the tab bar.";
-"HIDE_SUBSCRIPT_TAB" = "Hide Subscriptions tab";
-"HIDE_SUBSCRIPT_TAB_DESC" = "Hide Subscriptions tab in the tab bar.";
+"HOME_TAB" = "Home";
+"SHORTS_TAB" = "Shorts";
+"CREATE_TAB" = "Create";
+"SUBSCRIPTIONS_TAB" = "Subscriptions";
+"LIBRARY_TAB" = "Library (You)";
+"HISTORY_TAB" = "History";
+"GAMING_TAB" = "Gaming";
+"SPORTS_TAB" = "Sports";
+"NOTI_TAB" = "Notifications";
+"NEWS_TAB" = "News";
+"MUSIC_TAB" = "Music";
+"WATCH_LATER_TAB" = "Watch later";
+"PLAYLIST_TAB" = "Playlists";
+"LIKE_TAB" = "Liked videos";
+"LIVE_TAB" = "Live";
+"POST_TAB" = "Posts";
+"VIDEO_TAB" = "Your videos";
+"MOVIE_TAB" = "Movies";
+"COURSE_TAB" = "Courses";
+"MINIGAME_TAB" = "Playables";
+"MANAGE_TABS" = "Manage tabs";
+"MANAGE_TABS_DESC" = "Reorder and toggle tabs";
+"TAB_REORDER_HINT" = "Drag to reorder. Toggle to show/hide.\nTab settings are also accessible by long-pressing the first tab in the tab bar.";
+"TAB_LIMIT" = "Tab limit";
+"TAB_LIMIT_DESC" = "Only 6 tabs are allowed.";
+"ZERO_TAB_DESC" = "At least 1 tab has to be enabled.";
"MISCELLANEOUS" = "Miscellaneous";
"BACKGROUND_PLAYBACK" = "Background playback";
"BACKGROUND_PLAYBACK_DESC" = "Enables background playback for videos.";
"DISABLES_SHORTS_PIP" = "Disables Shorts PiP";
"DISABLES_SHORTS_PIP_DESC" = "Try to disables Shorts PiP, this might not 100% disables it.";
+"DISABLE_HINTS" = "Disable hints";
+"DISABLE_HINTS_DESC" = "Disable the tutorial hints and interactive pop-ups.";
"BLOCK_UPGRADE_DIALOGS" = "Block upgrade dialogs";
"BLOCK_UPGRADE_DIALOGS_DESC" = "Block the YouTube dialogs that ask you to upgrade the app.";
"ARE_YOU_THERE_DIALOG" = "Hide 'Are you there?' dialog";
@@ -247,7 +379,109 @@
"DISABLES_SNACK_BAR_DESC" = "Disables snack bar when pressing some buttons or doing something in the app.";
"HIDE_STARTUP_ANIMATIONS" = "Hide YouTube's startup animations";
"HIDE_STARTUP_ANIMATIONS_DESC" = "Hide YouTube's startup animations when opening the app.";
-"HIDE_PLAY_IN_NEXT_QUEUE" = "Hide 'Play next in queue'";
-"HIDE_PLAY_IN_NEXT_QUEUE_DESC" = "Hide the 'Play next in queue' option in the flyout menu.";
"HIDE_LIKE_DISLIKE_VOTES" = "Hide like/dislike votes";
-"HIDE_LIKE_DISLIKE_VOTES_DESC" = "Hide messages that pop up when you like/dislike videos.";
\ No newline at end of file
+"HIDE_LIKE_DISLIKE_VOTES_DESC" = "Hide messages that pop up when you like/dislike videos.";
+"FLYOUT_MENU" = "Flyout menu";
+"REMOVE_PLAY_IN_NEXT_QUEUE_OPTION" = "Removes 'Play next in queue' option";
+"REMOVE_PLAY_IN_NEXT_QUEUE_OPTION_DESC" = "Removes 'Play next in queue' option from the flyout menu.";
+"REMOVE_DOWNLOAD_OPTION" = "Removes 'Download video' option";
+"REMOVE_DOWNLOAD_OPTION_DESC" = "Removes 'Download video' option from the flyout menu.";
+"REMOVE_WATCH_LATER_OPTION" = "Removes 'Save to Watch later' option";
+"REMOVE_WATCH_LATER_OPTION_DESC" = "Removes 'Save to Watch Later' option from the flyout menu.";
+"REMOVE_SAVE_OPTION" = "Removes 'Save to playlist' option";
+"REMOVE_SAVE_OPTION_DESC" = "Removes 'Save to playlist' option from the flyout menu.";
+"REMOVE_REMOVE_FROM_PLAYLIST_OPTION" = "Removes 'Remove from playlist' option";
+"REMOVE_REMOVE_FROM_PLAYLIST_OPTION_DESC" = "Removes 'Remove from playlist' option from the flyout menu.";
+"REMOVE_SHARE_OPTION" = "Removes 'Share' option";
+"REMOVE_SHARE_OPTION_DESC" = "Removes 'Share' option from the flyout menu.";
+"REMOVE_NOT_INTERESTED_OPTION" = "Removes 'Not interested' option";
+"REMOVE_NOT_INTERESTED_OPTION_DESC" = "Removes 'Not interested' option from the flyout menu.";
+"REMOVE_DONT_RECOMMEND_OPTION" = "Removes 'Don't recommend channel' option";
+"REMOVE_DONT_RECOMMEND_OPTION_DESC" = "Removes 'Don't recommend channel' option from the flyout menu.";
+"REMOVE_INFO_OPTION" = "Removes 'Why are you seeing this?' option";
+"REMOVE_INFO_OPTION_DESC" = "Removes 'Why are you seeing this?' option from the flyout menu.";
+"REMOVE_FILTER_OPTION" = "Removes 'Search filters' option";
+"REMOVE_FILTER_OPTION_DESC" = "Removes 'Search filters' option from the flyout menu.";
+"REMOVE_REPORT_OPTION" = "Removes 'Report' option";
+"REMOVE_REPORT_OPTION_DESC" = "Removes 'Report' option from the flyout menu.";
+"REMOVE_YOUTUBE_MUSIC_OPTION" = "Removes 'Listen with YouTube Music' option";
+"REMOVE_YOUTUBE_MUSIC_OPTION_DESC" = "Removes 'Listen with YouTube Music' option from the flyout menu.";
+"REMOVE_FEED_BACK_OPTION" = "Removes 'Send feedback' option";
+"REMOVE_FEED_BACK_OPTION_DESC" = "Removes 'Send feedback' option from the flyout menu.";
+"REMOVE_CAST_OPTION" = "Removes 'Watch on TV' option";
+"REMOVE_CAST_OPTION_DESC" = "Removes 'Watch on TV' option from the flyout menu.";
+"REMOVE_SHUFFLE_OPTION" = "Removes 'Shuffle' option";
+"REMOVE_SHUFFLE_OPTION_DESC" = "Removes 'Shuffle' option from the flyout menu.";
+"REMOVE_UN_SUB_OPTION" = "Removes 'Unsubscribe' option";
+"REMOVE_UN_SUB_OPTION_DESC" = "Removes 'Unsubscribe' option from the flyout menu.";
+"REMOVE_HIDE_FROM_PLAYLIST_OPTION" = "Removes 'Hide video from playlist' option";
+"REMOVE_HIDE_FROM_PLAYLIST_OPTION_DESC" = "Removes 'Hide video from playlist' option from the flyout menu.";
+"REMOVE_HELP_OPTION" = "Removes 'Help & feedback' option";
+"REMOVE_HELP_OPTION_DESC" = "Removes 'Help & feedback' option from the flyout menu.";
+"REMOVE_NOTIFY_OPTION" = "Removes 'Notify me' option";
+"REMOVE_NOTIFY_OPTION_DESC" = "Removes 'Notify me' option from the flyout menu.";
+"REMOVE_CLEARSCREEN_OPTION" = "Removes 'Clear Screen' option";
+"REMOVE_CLEARSCREEN_OPTION_DESC" = "Removes 'Clear Screen' option from the flyout menu.";
+"HIDE_COMMU_GUIDE" = "Hide community guidelines";
+"HIDE_COMMU_GUIDE_DESC" = "Hide community guidelines header.";
+"DISABLES_RTL" = "Disable RTL formatting";
+"DISABLES_RTL_DESC" = "Forcefully displays text in left-to-right (LTR) format for languages that are initially displayed in right-to-left (RTL).";
+"DEVICE_UI" = "Interface style";
+
+"SB_DESCRIPTION" = "Skip sponsored segments automatically using community-submitted data.";
+"SB_ENABLE" = "Enable SponsorBlock";
+"SB_ENABLE_DESC" = "Activate the SponsorBlock extension to skip sponsored segments.";
+"SB_SHOW_BUTTON" = "Show overlay button";
+"SB_SHOW_BUTTON_DESC" = "Display a SponsorBlock toggle button in the player overlay.";
+"SB_SHOW_NOTIFICATIONS" = "Show notifications";
+"SB_SHOW_NOTIFICATIONS_DESC" = "Display notifications when segments are skipped. Notifications for manual-skip categories are always shown.";
+"SB_HAPTIC_FEEDBACK" = "Haptic feedback";
+"SB_HAPTIC_FEEDBACK_DESC" = "Provide haptic feedback when a segment is skipped.";
+"SB_SEGMENTS_IN_PLAYER" = "Segments in player";
+"SB_SEGMENTS_IN_PLAYER_DESC" = "Display colored segments on the progress bar of the main video player.";
+"SB_SEGMENTS_IN_FEED" = "Segments in feed";
+"SB_SEGMENTS_IN_FEED_DESC" = "Display colored segments on the progress bar of players in the feed.";
+"SB_SEGMENTS_IN_MINIPLAYER" = "Segments in mini-player";
+"SB_SEGMENTS_IN_MINIPLAYER_DESC" = "Display colored segments on the progress bar of the mini-player.";
+"SB_SHOW_DURATION" = "Show duration without segments";
+"SB_SHOW_DURATION_DESC" = "Display the total video duration excluding skippable segments.";
+"SB_CATEGORIES_HEADER" = "Segment categories";
+"SB_SEGMENT_COLOR" = "Segment color";
+
+"SB_ACTION_DISABLE" = "Disable";
+"SB_ACTION_AUTO_SKIP" = "Auto-skip";
+"SB_ACTION_ASK" = "Ask before skipping";
+"SB_ACTION_DISPLAY" = "Show on seek bar only";
+"SB_ACTION_SKIP_TO" = "Skip to segment";
+
+"SB_CAT_sponsor" = "Sponsor";
+"SB_CAT_sponsor_DESC" = "Paid promotion for a product or service not directly related to the creator.";
+"SB_CAT_intro" = "Intro";
+"SB_CAT_intro_DESC" = "Intermission or intro animation typically seen at the start of a video.";
+"SB_CAT_outro" = "Endcards";
+"SB_CAT_outro_DESC" = "Endcards, credits, or outros at the end of the video.";
+"SB_CAT_interaction" = "Interaction";
+"SB_CAT_interaction_DESC" = "Reminders to like, subscribe, or follow on social media.";
+"SB_CAT_selfpromo" = "Self-promotion";
+"SB_CAT_selfpromo_DESC" = "Unpaid promotion of the creator's own products, merchandise, or other content.";
+"SB_CAT_music_offtopic" = "Non-music";
+"SB_CAT_music_offtopic_DESC" = "Non-music sections in music videos (e.g. talking, introductions).";
+"SB_CAT_preview" = "Preview";
+"SB_CAT_preview_DESC" = "Recap or preview of content from another video or previous episode.";
+"SB_CAT_hook" = "Hook/Greetings";
+"SB_CAT_hook_DESC" = "Narrated trailers for the upcoming video, greetings and goodbyes.";
+"SB_CAT_poi_highlight" = "Highlight";
+"SB_CAT_poi_highlight_DESC" = "The key moment or highlight point of the video. Use 'Skip to' to jump directly to it.";
+"SB_CAT_filler" = "Filler";
+"SB_CAT_filler_DESC" = "Tangential content, jokes, or filler that is not directly related to the main topic.";
+
+"SB_SKIPPED" = "%@ segment has been skipped";
+"SB_DETECTED" = "%@ segment detected.\nWould you like to skip the segment?";
+"SB_SKIP_NOW" = "Skip";
+"SB_UNSKIP" = "Unskip";
+"SB_JUMPED_TO_HIGHLIGHT" = "Jumped to highlight";
+"SB_JUMP_TO_HIGHLIGHT" = "Highlight available. Jump to the point?";
+"SB_SEGMENT_COLOR_SUFFIX" = "Segment color";
+"SB_SECTION_MAIN" = "Main";
+"SB_SKIP_ALERT_DURATION" = "Skip alert duration";
+"SB_UNSKIP_ALERT_DURATION" = "Unskip alert duration";
\ No newline at end of file
diff --git a/layout/Library/Application Support/YouMod.bundle/fr.lproj/Localizable.strings b/layout/Library/Application Support/YouMod.bundle/fr.lproj/Localizable.strings
index 3e68e72a..7c6b2240 100644
--- a/layout/Library/Application Support/YouMod.bundle/fr.lproj/Localizable.strings
+++ b/layout/Library/Application Support/YouMod.bundle/fr.lproj/Localizable.strings
@@ -1,11 +1,18 @@
-"NOTE" = "REMARQUE : Certaines fonctionnalités de masquage peuvent laisser une case vide.";
"SETTINGS" = "Paramètres";
+"INTERFACE" = "Interface";
+
+"LOOP_DISABLED" = "Loop disabled";
+"LOOP_ENABLED" = "Loop enabled";
"NEW_ISSUES" = "Signaler des problèmes ou demander de nouvelles fonctionnalités";
"NEW_ISSUES_DESC" = "Des bugs ou des demandes de nouvelles fonctionnalités ? Appuie ici.";
"SOURCE_CODES" = "Code source";
"SOURCE_CODES_DESC" = "Jette un œil au code !";
+"EXTRA" = "Extra";
+"FIX_PLAYBACK_ISSUES" = "Fix playback issues (Temporary)";
+"FIX_PLAYBACK_ISSUES_DESC" = "Try to fix video playback issues for some users. Turning this on will make the player automatically reloads itself everytime it errors or fails to play the video.";
+
"PERFER_HEADER" = "Réglages YouMod";
"PERFER" = "Réglages";
"IMPORT" = "Importer les réglages";
@@ -24,6 +31,7 @@
"ERROR_NO_KEYS_IMPORT" = "Aucun réglages à importer";
"ERROR_NO_KEYS_EXPORT" = "Aucun réglages à exporter";
"OVERRIDE" = "Cela va remplacer les réglages actuels. Souhaite-tu continuer?";
+"DISABLED" = "Disabled";
"CACHE" = "Cache";
"CLEARCACHE" = "Vider le cache";
@@ -36,8 +44,77 @@
"DOWNLOAD_MANAGER_DESC" = "Remplace le téléchargeur vidéo de YouTube par celui de YouMod. Avec la possibilité de télécharger des vidéos, des audios, des sous-titres et des vignettes";
"DOWNLOAD_SAVE_PHOTOS" = "Enregistrer automatiquement dans Photos";
"DOWNLOAD_SAVE_PHOTOS_DESC" = "Enregistre automatiquement les vidéos et images téléchargées directement dans votre application Photos.";
-"DOWNLOAD_DRC_AUDIO" = "Préférer l'audio DRC";
-"DOWNLOAD_DRC_AUDIO_DESC" = "Préfère les flux audio avec Dynamic Range Compression (DRC) pour un niveau de volume plus consistant.";
+"ADD_SHORTS_DOWNLOAD" = "Adds a download button to Shorts";
+"ADD_SHORTS_DOWNLOAD_DESC" = "Adds a download button to Shorts video overlay.";
+"USE_ORIG_AUDIO" = "Automatically uses original audio track";
+"USE_ORIG_AUDIO_DESC" = "Automatically uses original audio track instead of showing a dialog.";
+"DOWNLOAD_SHORTS" = "Download Shorts";
+"DOWNLOAD_SHORTS_DESC" = "Choose Shorts quality";
+"COPY_VID_INFO" = "Copy video informations";
+"COPY_VID_INFO_DESC" = "Choose what to copy";
+"COPY_ALL_VID_INFO" = "Copy all";
+"COPY_ALL_VID_INFO_DESC" = "Copy all video informations";
+"COPY_AUTHOR" = "Copy author";
+"COPY_AUTHOR_DESC" = "Copy the video author";
+"COPY_TITLE" = "Copy title";
+"COPY_TITLE_DESC" = "Copy the video title";
+"COPY_DESCRIPTION" = "Copy description";
+"COPY_DESCRIPTION_DESC" = "Copy the video description";
+"COPY_URL" = "Copy video link";
+"COPY_URL_TIMESTAMP" = "Copy video link with timestamp";
+"URL_COPIED" = "Copied video link";
+"URL_TIMESTAMP_COPIED" = "Copied video link with timestamp";
+"ERROR_VIDEOID" = "Cannot get the video ID";
+"ERROR_ADS" = "Cannot copy video link while playing ads";
+"COPIED_AUTHOR" = "Copied video author";
+"COPIED_TITLE" = "Copied video title";
+"COPIED_DESCRIPTION" = "Copied video description";
+"SAVE_THUMBNAIL" = "Save thumbnail";
+"SAVE_THUMBNAIL_DESC" = "Save to Photos";
+"DOWNLOAD_CAPTIONS" = "Download captions";
+"DOWNLOAD_CAPTIONS_DESC" = "Save subtitles as .VTT file";
+"DOWNLOAD_AUDIO" = "Download audio";
+"DOWNLOAD_AUDIO_DESC" = "Download audio as .m4a file";
+"DOWNLOAD_VIDEO" = "Download video";
+"DOWNLOAD_VIDEO_DESC" = "Choose video quality";
+"OPEN_VID_BEFORE" = "Open a video before using the download manager";
+"NO_CAPTIONS_URL" = "No valid caption URLs found";
+"NO_AUDIO_URL" = "No audio URL found";
+"NO_STREAM_URL" = "No stream URL found";
+"CAPTIONS_FAILED" = "Failed to download captions";
+"NO_CAPTIONS" = "No captions available for this video";
+"NO_AUDIO_STREAM_FOUND" = "No audio streams found";
+"NO_VID_AUDIO_STREAM_FOUND" = "No video/audio streams found";
+"COPIED_VID_INFO" = "Copied all video informations";
+"SAVED_TO_PHOTOS" = "Saved to Photos";
+"SAVE_FAILED" = "Save failed";
+"PHOTO_ACCESS_DENINED" = "Photos access denied";
+"THUMBNAIL_FAILED" = "Thumbnail failed";
+"DOWNLOADING_THUMBNAIL" = "Downloading thumbnail...";
+"DOWNLOADING_AUDIO" = "Downloading audio...";
+"DOWNLOADING_VIDEO" = "Downloading video...";
+"DOWNLOADING_CAPTIONS" = "Downloading captions...";
+"NO_THUMBNAIL_FOUND" = "No thumbnail found";
+"DOWNLOAD_FAILED" = "Download failed";
+"AUDIO_SAVED" = "Audio saved";
+"DOWNLOAD_COMPLETED" = "Download completed";
+"CANNOT_SAVE_TO_PHOTOS" = "Cannot save to Photos";
+"FINA_VIDEO" = "Finalizing video...";
+"MERGING_VID_FALLBACK" = "Merging video with fallback...";
+"MERGING_VID" = "Merging video...";
+"CONVERTING" = "Converting to";
+"ALREADY_DOWNLOADING" = "Already downloading";
+"DOWNLOAD_CANCELLED" = "Download cancelled";
+"CANCEL" = "Cancel";
+"TRIMMING_AUDIO" = "Trimming audio...";
+"COPY_THUMBNAIL" = "Copy thumbnail";
+"COPY_THUMBNAIL_DESC" = "Copy to clipboard";
+"SHOW_THUMBNAIL" = "Show thumbnail";
+"SHOW_THUMBNAIL_DESC" = "Show in a dialog";
+"THUMBNAIL_OPTIONS" = "Download thumbnail";
+"THUMBNAIL_OPTIONS_DESC" = "Show more options";
+"COPIED_TO_CLIPBOARD" = "Copied to clipboard";
+"COPY" = "Copy";
"APPEARANCE" = "Apparence";
"OLED_THEME" = "Thème OLED";
@@ -46,6 +123,8 @@
"OLED_KEYBOARD_DESC" = "Applique un effet noir (OLED) au clavier pour le thème sombre.";
"NAVBAR" = "Bar de navigation";
+"STICKY_NAVBAR" = "Sticky Navigation bar";
+"STICKY_NAVBAR_DESC" = "Pins the Navigation bar so that it remains visible when scrolling down.";
"HIDE_YT_LOGO" = "Cacher le logo YouTube";
"HIDE_YT_LOGO_DESC" = "Cache le logo YouTube dans la bar de navigation.";
"PREMIUM_LOGO" = "Utiliser le logo YouTube Premium";
@@ -62,32 +141,76 @@
"FEED" = "Fil YouTube";
"HIDE_SUBBAR" = "Cacher la barre d'abonnements";
"HIDE_SUBBAR_DESC" = "Cache la barre d'abonnements en haut du fil YouTube.";
+"HIDE_HORI_SHELF" = "Remove horizontal feeds";
+"HIDE_HORI_SHELF_DESC" = "Removes all horizontal feeds (News, Continue Watching, Posts collection, etc.) from the feed, except Playables.";
"HIDE_MUSIC_PLAYLISTS" = "Cacher le générateur de playlists musicales";
"HIDE_MUSIC_PLAYLISTS_DESC" = "Cache le générateur de playlists musicales (Sélection musicale en trois titres) dans le fil YouTube.";
"HIDE_FEED_POST" = "Cacher les posts";
"HIDE_FEED_POST_DESC" = "Cacher les posts dans le fil YouTube.";
+"HIDE_SURVEYS" = "Hide surveys";
+"HIDE_SURVEYS_DESC" = "Hide video surveys that YouTube shows sometimes.";
"HIDE_SHORTS_SHELF" = "Cacher les Shorts";
"HIDE_SHORTS_SHELF_DESC" = "Cache les Shorts dans le fil YouTube, y compris dans l'onglet Abonnements.";
+"KEEP_SHORTS_SUBSCRIPT" = "Keep Shorts in Subscriptions";
+"KEEP_SHORTS_SUBSCRIPT_DESC" = "Prevents removing Shorts from Subscriptions tab.";
"HIDE_SEARCH_HISTORY" = "Cacher l'historique de recherche";
"HIDE_SEARCH_HISTORY_DESC" = "Cache l'historique de recherche et les suggestions lors de l'utilisation de la barre de recherche.\nNOTE: Ton historique de recherche restera accessible sur tes autres clients YouTube.";
-"HIDE_SUB_BUTTON" = "Cacher le bouton S'abonner";
-"HIDE_SUB_BUTTON_DESC" = "Cache le bouton s'abonner dans les chaînes et les descriptions de vidéos.";
-"HIDE_SHOP_BUTTON" = "Cacher le bouton boutique";
-"HIDE_SHOP_BUTTON_DESC" = "Cache le bouton boutique/magasin dans les descriptions de chaînes.";
-"HIDE_MEMBER_BUTTON" = "Cacher le bouton rejoindre";
-"HIDE_MEMBER_BUTTON_DESC" = "Cache le bouton 'Rejoindre' dans les chaînes et les descriptions de vidéos.";
+"HIDE_PLAYABLES" = "Remove playables";
+"HIDE_PLAYABLES_DESC" = "Removes playables (mini-games) from the feed.";
"PLAYER" = "Lecteur";
+"PLAYBACK_SPEED" = "Playback speed";
+"CONTROL_CENTER" = "Control Center";
+"PLAYER_ACTIONS" = "Player actions";
+"GESTURE_HEADER" = "Gestures";
+"QUALITY_WIFI" = "Playback quality on WiFi";
+"QUALITY_WIFI_DESC" = "Automatically applies the selected quality when streaming over Wi-Fi.";
+"QUALITY_CELLULAR" = "Playback quality on Cellular";
+"QUALITY_CELLULAR_DESC" = "Automatically applies the selected quality when streaming over mobile data.";
+"QUALITY_LOW_POWER" = "Playback quality on Power saver mode";
+"QUALITY_LOW_POWER_DESC" = "Automatically applies the selected quality when when Low power mode is enabled on your device.";
+"DEFAULT" = "Default";
+"BEST" = "Best";
+"AUDIO_TRACK" = "Preferred audio track";
+"ORIGINAL" = "Original";
+"SELECT_MANUALLY" = "Select manually";
+"AUDIO_TRACK_SELECT" = "Selected audio track";
+"AUDIO_TRACK_SELECT_DESC" = "Specifies the preferred audio track language to use for playback, if available.";
+"NO_AUTO_DUBBED" = "Uses original audio track";
+"NO_AUTO_DUBBED_DESC" = "Uses original audio track if the preferred language track is auto-dubbed or not found.";
+"CAPTION_TRACK" = "Preferred captions";
+"CAPTION_TRACK_SELECT" = "Selected captions";
+"CAPTION_TRACK_SELECT_DESC" = "Specifies the preferred captions language to display, if available.";
+"DISABLES_CAPTION_TRACK" = "Automatically disable captions";
+"DISABLES_CAPTION_TRACK_DESC" = "Automatically disable captions if the preferred language is auto-generated or not found.";
+"DEFAULT_SPEED" = "Default playback rate";
+"DEFAULT_SPEED_DESC" = "Overrides the default playback speed.";
+"SPEED_BUTTON" = "Speed controls button";
+"SPEED_BUTTON_DESC" = "Displays a speed controls button on overlay.";
+"MUTE_BUTTON" = "Mute button";
+"MUTE_BUTTON_DESC" = "Displays a button to mute or unmute the volume on the overlay.";
+"SHARE_BUTTON" = "Share button";
+"SHARE_BUTTON_DESC" = "Displays a button to share the video link on the overlay.";
+"LOOP_BUTTON" = "Loop button";
+"LOOP_BUTTON_DESC" = "Displays a button to loop the video on the overlay.";
+"CAPTION_BUTTON" = "Captions button";
+"CAPTION_BUTTON_DESC" = "Displays a button to copy the video captions on the overlay.";
+"HOLD_TO_SPEED" = "Hold to speed";
+"HOLD_TO_SPEED_DESC" = "Uses custom hold to speed playback rate.";
+"SKIP_BACKWARD" = "Skip backward";
+"SKIP_BACKWARD_DESC" = "Replaces the previous track button in Control Center with a skip backward button by a set number of seconds.";
+"SKIP_FORWARD" = "Skip forward";
+"SKIP_FORWARD_DESC" = "Replaces the next track button in Control Center with a skip forward button by a set number of seconds.";
+"REWIND_SECONDS" = "Rewind seconds";
+"FORWARD_SECONDS" = "Fast-forward seconds";
"HIDE_AUTOPLAY" = "Cacher le bouton lecture automatique";
"HIDE_AUTOPLAY_DESC" = "Cache le bouton de lecture automatique dans l'interface vidéo.";
"HIDE_CAPTIONS_BUTTON" = "Cacher le bouton des sous-titres";
"HIDE_CAPTIONS_BUTTON_DESC" = "Cache le bouton des sous-titres dans l'interface vidéo.";
"HIDE_CAST_BUTTON_PLAYER" = "Cacher le bouton de diffusion";
"HIDE_CAST_BUTTON_PLAYER_DESC" = "Cache le bouton de diffusion dans l'interface vidéo.";
-"HIDE_PREV_BUTTON" = "Cacher le bouton précédent/retour";
-"HIDE_PREV_BUTTON_DESC" = "Cache le bouton précédent/retour dans l'interface vidéo.";
-"HIDE_NEXT_BUTTON" = "Cacher le bouton suivant/passer";
-"HIDE_NEXT_BUTTON_DESC" = "Cache le bouton suivant/passer dans l'interface vidéo.";
+"HIDE_NEXT_AND_PREV_BUTTON" = "Hide previous and next buttons";
+"HIDE_NEXT_AND_PREV_BUTTON_DESC" = "Hide previous and next buttons in the video overlay.";
"REPLACE_PREVNEXT_BUTTONS" = "Remplacer les boutons Précédent/Suivant";
"REPLACE_PREVNEXT_BUTTONS_DESC" = "Remplace les boutons Précédent et Suivant du lecteur par des boutons Reculer et Avance rapide.";
"REMOVE_DARK_OVERLAY" = "Supprimer l'overlay sombre";
@@ -102,6 +225,12 @@
"HIDE_PAID_OVERLAY_DESC" = "Cacher 'Inclus du contenu sponsorisé' dans l'interface vidéo.";
"HIDE_WATERMARK" = "Cacher le filigrane";
"HIDE_WATERMARK_DESC" = "Cache le filigrane de la chaîne en bas de l'interface vidéo.";
+"PAUSE_ON_OVERLAY" = "Pause on overlay";
+"PAUSE_ON_OVERLAY_DESC" = "Sets playback on pause if overlay appears.";
+"DISABLES_SNAP_TO_CHAPTER" = "Disable snap to chapters";
+"DISABLES_SNAP_TO_CHAPTER_DESC" = "Prevents the scrubbar stick to the chapters.";
+"DISABLES_ENGAGE_PANEL" = "Disables engagement panel in fullscreen";
+"DISABLES_ENGAGE_PANEL_DESC" = "Prevent engagement panel showing in fullscreen.";
"GESTURES" = "Activer le contrôle par gestes";
"GESTURES_DESC" = "Faites glisser vers le haut ou le bas sur les bords gauche/droit de l'écran vidéo pour régler la luminosité, le volume ou la vitesse de lecture.";
"GESTURE_AREA" = "Zone d'activation des gestes";
@@ -136,6 +265,8 @@
"DISABLES_SHOW_REMAINING_DESC" = "Désactive la fonctionnalité qui affiche le temps restant dans une vidéo en appuyant sur l'horodatage.";
"ALWAYS_SHOW_REMAINING" = "Toujours afficher le temps restant";
"ALWAYS_SHOW_REMAINING_DESC" = "Affiche en permanence le temps restant par défaut dans l'interface du lecteur.";
+"COPY_TIMESTAMP_ON_PAUSE" = "Copy timestamped links";
+"COPY_TIMESTAMP_ON_PAUSE_DESC" = "Allows to copy timestamped link to the clipboard by pressing pause button.";
"SHOW_REMAINING_EXTRA" = "Afficher le temps restant supplémentaire";
"SHOW_REMAINING_EXTRA_DESC" = "Ajoute l'heure de fin estimée au libellé dans l'interface du lecteur.";
"HIDE_FULLSCREEN_ACTIONS" = "Cacher les actions en plein écran";
@@ -148,93 +279,98 @@
"HIDE_CONTENT_WARNING_DESC" = "Ignore automatiquement l'alerte d'avertissement pour contenu sensible dans certaines vidéos.";
"AUTO_FULLSCREEN" = "Plein écran automatique";
"AUTO_FULLSCREEN_DESC" = "Passe automatiquement en mode plein écran au démarrage de la lecture.";
-"AUTO_DISABLES_CAPTION" = "Désactivation automatique des sous-titres";
-"AUTO_DISABLES_CAPTION_DESC" = "Désactive automatiquement les sous-titres (CC) à chaque nouvelle lecture de vidéo.";
"PORTRAIT_FULLSCREEN" = "Plein écran en mode portrait";
"PORTRAIT_FULLSCREEN_DESC" = "Active le support du mode plein écran en portrait.";
"OLD_QUALITY_PICKER" = "Utiliser l'ancien sélecteur de qualité vidéo";
"OLD_QUALITY_PICKER_DESC" = "Rétablit le menu de sélection de qualité vidéo en version classique (affiche immédiatement toutes les résolutions).";
"EXTRA_SPEED" = "Options de vitesse supplémentaires";
"EXTRA_SPEED_DESC" = "Ajoute plus d'options de vitesse de lecture jusqu'à 10x.";
-"DISABLE_HINTS" = "Désactiver les conseils";
-"DISABLE_HINTS_DESC" = "Désactive les conseils et les pop-ups interactifs.";
"FORCE_MINIPLAYER" = "Forcer le minilecteur";
"FORCE_MINIPLAYER_DESC" = "Permet à toutes les vidéos, y compris celles destinées aux enfants, de se lire en minilecteur lorsqu'on ferme le lecteur.";
"FORCE_SEEKBAR" = "Toujours afficher la barre de progression";
"FORCE_SEEKBAR_DESC" = "Assure que la barre de progression reste visible en permanence en mode plein écran.";
-"HIDE_LIKE_BUTTON" = "Cacher le bouton J'aime";
-"HIDE_LIKE_BUTTON_DESC" = "Cache le bouton J'aime dans la barre d'actions sous l'interface vidéo.";
-"HIDE_DISLIKE_BUTTON" = "Cacher le bouton Je n'aime pas";
-"HIDE_DISLIKE_BUTTON_DESC" = "Cache le bouton Je n'aime pas dans la barre d'actions sous l'interface vidéo.";
-"HIDE_SHARE_BUTTON" = "Cacher le bouton Partager";
-"HIDE_SHARE_BUTTON_DESC" = "Cache le bouton Partager dans la barre d'actions sous l'interface vidéo.";
-"HIDE_DOWNLOAD_BUTTON" = "Cacher le bouton Télécharger";
-"HIDE_DOWNLOAD_BUTTON_DESC" = "Cache le bouton Télécharger dans la barre d'actions sous l'interface vidéo.";
-"HIDE_CLIP_BUTTON" = "Cacher le bouton Clip";
-"HIDE_CLIP_BUTTON_DESC" = "Cache le bouton Clip dans la barre d'actions sous l'interface vidéo.";
-"HIDE_REMIX_BUTTON" = "Cacher le bouton Remix";
-"HIDE_REMIX_BUTTON_DESC" = "Cache le bouton Remix dans la barre d'actions sous l'interface vidéo.";
-"HIDE_SAVE_BUTTON" = "Cacher le bouton Enregistrer";
-"HIDE_SAVE_BUTTON_DESC" = "Cache le bouton Enregistrer dans la barre d'actions sous l'interface vidéo.";
+"TAP_TO_SEEK" = "Tap to seek";
+"TAP_TO_SEEK_DESC" = "Quickly jump to any point by tapping the progress bar.";
+"DISABLES_ZOOM" = "Disable free zoom gesture";
+"DISABLES_ZOOM_DESC" = "Disables new free zoom gestures.";
+"USES_24_HOURS_TIME" = "24-hour format";
+"USES_24_HOURS_TIME_DESC" = "Shows end time in 24-hour format.";
+"PAUSE_TWO_FINGERS" = "Pause using two fingers";
+"PAUSE_TWO_FINGERS_DESC" = "Pause/Resume playback by tapping on the screen using two fingers.";
+"HIDE_ACTION_BAR" = "Hide actions bar";
+"HIDE_ACTION_BAR_DESC" = "Removes the bar containing the like, dislike, share buttons etc. below the video player.";
+"HIDE_COMMENTS_SECTION" = "Hide comments section";
+"HIDE_COMMENTS_SECTION_DESC" = "Completely removes the entire comments section.";
+"HIDE_COMMENTS_PREVIEW" = "Hide preview comments";
+"HIDE_COMMENTS_PREVIEW_DESC" = "Removes the preview box for the top comment while keeping the full section expandable.";
+"USE_ANOTHER_MINIPLAYER" = "Uses another miniplayer style";
+"USE_ANOTHER_MINIPLAYER_DESC" = "Uses another miniplayer style redesigned from older YT versions.";
"SHORTS" = "Shorts";
-"HIDE_SHORTS_LIKE_BUTTON" = "Cacher le bouton J'aime";
-"HIDE_SHORTS_LIKE_BUTTON_DESC" = "Cache le bouton J'aime dans le lecteur Shorts.";
-"HIDE_SHORTS_DISLIKE_BUTTON" = "Cacher le bouton Je n'aime pas";
-"HIDE_SHORTS_DISLIKE_BUTTON_DESC" = "Cache le bouton Je n'aime pas dans le lecteur Shorts.";
-"HIDE_SHORTS_COMMENT_BUTTON" = "Cacher le bouton Commentaires";
-"HIDE_SHORTS_COMMENT_BUTTON_DESC" = "Cache le bouton Commentaires dans le lecteur Shorts.";
-"HIDE_SHORTS_SHARE_BUTTON" = "Cacher le bouton Partager";
-"HIDE_SHORTS_SHARE_BUTTON_DESC" = "Cache le bouton Partager dans le lecteur Shorts.";
-"HIDE_SHORTS_REMIX_BUTTON" = "Cacher le bouton Remix";
-"HIDE_SHORTS_REMIX_BUTTON_DESC" = "Cache le bouton Remix dans le lecteur Shorts.";
-"HIDE_METADATA_BUTTON" = "Cacher le bouton de métadonnées audio";
-"HIDE_METADATA_BUTTON_DESC" = "Cache le bouton affichant la source audio ou les métadonnées musicales dans le lecteur Shorts.";
+"SHORTS_ONLY" = "Shorts only mode";
+"SHORTS_ONLY_DESC" = "Limits YouTube functionality to viewing Shorts only.";
+"SHORTS_ACTION" = "Shorts action after the video ends";
+"LOOP" = "Loop video";
+"SKIP_TO_NEXT_SHORTS" = "Skip to next video";
+"PAUSE_SHORTS" = "Stop video";
+"REMOVE_LIVE_SHORTS" = "Remove live videos";
+"REMOVE_LIVE_SHORTS_DESC" = "Removes live videos from the feed.";
"HIDE_SHORTS_PRODUCT" = "Cacher les produits";
"HIDE_SHORTS_PRODUCT_DESC" = "Cache les produits tagués et les liens de shopping dans l'interface Shorts.";
"HIDE_SHORTS_RECBAR" = "Cacher la barre de recommandations";
"HIDE_SHORTS_RECBAR_DESC" = "Cache la barre d'actions suggérées dans le lecteur Shorts (ex. Essayer ce son, Shopping).";
-"HIDE_SHORTS_COMMIT" = "Cacher la barre de commissions";
-"HIDE_SHORTS_COMMIT_DESC" = "Cache la barre indiquant que la vidéo est sponsorisée dans le lecteur Shorts.";
-"HIDE_SHORTS_SUBSCRIPT_BUTTON" = "Cacher le bouton Abonnements";
-"HIDE_SHORTS_SUBSCRIPT_BUTTON_DESC" = "Cache le bouton Abonnements en haut du lecteur Shorts.";
-"HIDE_SHORTS_LIVE_BUTTON" = "Cacher le bouton En direct";
-"HIDE_SHORTS_LIVE_BUTTON_DESC" = "Cache le bouton En direct en haut du lecteur Shorts.";
-"HIDE_SHORTS_LENS_BUTTON" = "Cacher le bouton Lens";
-"HIDE_SHORTS_LENS_BUTTON_DESC" = "Cache le bouton Lens en haut du lecteur Shorts.";
-"HIDE_SHORTS_TRENDS_BUTTON" = "Cacher le bouton Tendances";
-"HIDE_SHORTS_TRENDS_BUTTON_DESC" = "Cache le bouton Tendances en haut du lecteur Shorts.";
-"HIDE_SHORTS_TO_VIDEO" = "Cacher le lien vers la vidéo complète";
-"HIDE_SHORTS_TO_VIDEO_DESC" = "Cache le lien qui redirige depuis un Short vers la vidéo complète dans le lecteur Shorts.";
"ENABLES_SHORTS_QUALITY" = "Activer le sélecteur de qualité vidéo des Shorts";
"ENABLES_SHORTS_QUALITY_DESC" = "Force l'activation du menu de sélection de qualité vidéo dans les Shorts.";
"SHOW_SHORTS_SEEKBAR" = "Toujours afficher la barre de progression";
"SHOW_SHORTS_SEEKBAR_DESC" = "Affiche en permanence la barre de progression en bas du lecteur Shorts.";
+"REMOVE_POSTS_SHORTS" = "Remove posts";
+"REMOVE_POSTS_SHORTS_DESC" = "Remove posts from the feed.";
+
+"SHORTS_FULLSCREEN" = "Pinch to Fullscreen";
+"SHORTS_FULLSCREEN_DESC" = "Manages visibility of the overlay with pinch in and pinch out gestures, displaying Shorts in fullscreen mode.";
"TABBAR" = "Barre d'onglets";
"DEFAULT_TAB" = "Onglet de démarrage par défaut";
-"DEFAULT_TAB_DESC" = "Tu peux sélectionner l'onglet à ouvrir au lancement de l'application.";
-"HOME_NAME" = "Accueil";
-"SUBSCRIPT_NAME" = "Abonnements";
-"LIB_NAME" = "Bibliothèque (Vous)";
+"FORSTED_TAB_BAR" = "Translucent tab bar";
+"ENABLED" = "Enabled";
"HIDE_TAB_INDI" = "Cacher les indicateurs d'onglets";
"HIDE_TAB_INDI_DESC" = "Cache tous les indicateurs d'onglets dans la barre d'onglets.";
"HIDE_TAB_LABELS" = "Cacher les libellés d'onglets";
"HIDE_TAB_LABELS_DESC" = "Cache tous les libellés d'onglets dans la barre d'onglets.";
-"HIDE_HOME_TAB" = "Cacher l'onglet Accueil";
-"HIDE_HOME_TAB_DESC" = "Cache l'onglet Accueil dans la barre d'onglets.";
-"HIDE_SHORTS_TAB" = "Cacher l'onglet Shorts";
-"HIDE_SHORTS_TAB_DESC" = "Cache l'onglet Shorts dans la barre d'onglets.";
-"HIDE_CREATE_BUTTON" = "Cacher le bouton Créer";
-"HIDE_CREATE_BUTTON_DESC" = "Cache le bouton Créer dans la barre d'onglets.";
-"HIDE_SUBSCRIPT_TAB" = "Cacher l'onglet Abonnements";
-"HIDE_SUBSCRIPT_TAB_DESC" = "Cache l'onglet Abonnements dans la barre d'onglets.";
+"HOME_TAB" = "Accueil";
+"SHORTS_TAB" = "Shorts";
+"CREATE_TAB" = "Create";
+"SUBSCRIPTIONS_TAB" = "Abonnements";
+"LIBRARY_TAB" = "Bibliothèque (Vous)";
+"HISTORY_TAB" = "History";
+"GAMING_TAB" = "Gaming";
+"SPORTS_TAB" = "Sports";
+"NOTI_TAB" = "Notifications";
+"NEWS_TAB" = "News";
+"MUSIC_TAB" = "Music";
+"WATCH_LATER_TAB" = "Watch later";
+"PLAYLIST_TAB" = "Playlists";
+"LIKE_TAB" = "Liked videos";
+"LIVE_TAB" = "Live";
+"POST_TAB" = "Posts";
+"VIDEO_TAB" = "Your videos";
+"MOVIE_TAB" = "Movies";
+"COURSE_TAB" = "Courses";
+"MINIGAME_TAB" = "Playables";
+"MANAGE_TABS" = "Manage tabs";
+"MANAGE_TABS_DESC" = "Reorder and toggle tabs";
+"TAB_REORDER_HINT" = "Drag to reorder. Toggle to show/hide.\nTab settings are also accessible by long-pressing the first tab in the tab bar.";
+"TAB_LIMIT" = "Tab limit";
+"TAB_LIMIT_DESC" = "Only 6 tabs are allowed.";
+"ZERO_TAB_DESC" = "At least 1 tab has to be enabled.";
"MISCELLANEOUS" = "Autres";
"BACKGROUND_PLAYBACK" = "Lecture en arrière-plan";
"BACKGROUND_PLAYBACK_DESC" = "Active la lecture en arrière-plan pour les vidéos.";
"DISABLES_SHORTS_PIP" = "Désactiver le PiP des Shorts";
"DISABLES_SHORTS_PIP_DESC" = "Tente de désactiver le PiP des Shorts, cela peut ne pas fonctionner à 100%.";
+"DISABLE_HINTS" = "Désactiver les conseils";
+"DISABLE_HINTS_DESC" = "Désactive les conseils et les pop-ups interactifs.";
"BLOCK_UPGRADE_DIALOGS" = "Bloquer les dialogues de mise à jour";
"BLOCK_UPGRADE_DIALOGS_DESC" = "Bloque les dialogues YouTube qui te demandent de mettre à jour l'application.";
"ARE_YOU_THERE_DIALOG" = "Cacher la boîte de dialogue 'Êtes-vous là ?'";
@@ -247,7 +383,109 @@
"DISABLES_SNACK_BAR_DESC" = "Désactive la barre de notification qui apparaît en appuyant sur certains boutons ou en effectuant certaines actions dans l'application.";
"HIDE_STARTUP_ANIMATIONS" = "Cacher les animations de démarrage de YouTube";
"HIDE_STARTUP_ANIMATIONS_DESC" = "Cache les animations de démarrage de YouTube à l'ouverture de l'application.";
-"HIDE_PLAY_IN_NEXT_QUEUE" = "Cacher 'Lire ensuite dans la file'";
-"HIDE_PLAY_IN_NEXT_QUEUE_DESC" = "Cache l'option 'Lire ensuite dans la file' dans le menu contextuel.";
"HIDE_LIKE_DISLIKE_VOTES" = "Cacher les votes J'aime/Je n'aime pas";
"HIDE_LIKE_DISLIKE_VOTES_DESC" = "Cache les messages qui apparaissent lorsque tu aimes ou n'aimes pas des vidéos.";
+"FLYOUT_MENU" = "Flyout menu";
+"REMOVE_PLAY_IN_NEXT_QUEUE_OPTION" = "Removes 'Play next in queue' option";
+"REMOVE_PLAY_IN_NEXT_QUEUE_OPTION_DESC" = "Removes 'Play next in queue' option from the flyout menu.";
+"REMOVE_DOWNLOAD_OPTION" = "Removes 'Download video' option";
+"REMOVE_DOWNLOAD_OPTION_DESC" = "Removes 'Download video' option from the flyout menu.";
+"REMOVE_WATCH_LATER_OPTION" = "Removes 'Save to Watch later' option";
+"REMOVE_WATCH_LATER_OPTION_DESC" = "Removes 'Save to Watch Later' option from the flyout menu.";
+"REMOVE_SAVE_OPTION" = "Removes 'Save to playlist' option";
+"REMOVE_SAVE_OPTION_DESC" = "Removes 'Save to playlist' option from the flyout menu.";
+"REMOVE_REMOVE_FROM_PLAYLIST_OPTION" = "Removes 'Remove from playlist' option";
+"REMOVE_REMOVE_FROM_PLAYLIST_OPTION_DESC" = "Removes 'Remove from playlist' option from the flyout menu.";
+"REMOVE_SHARE_OPTION" = "Removes 'Share' option";
+"REMOVE_SHARE_OPTION_DESC" = "Removes 'Share' option from the flyout menu.";
+"REMOVE_NOT_INTERESTED_OPTION" = "Removes 'Not interested' option";
+"REMOVE_NOT_INTERESTED_OPTION_DESC" = "Removes 'Not interested' option from the flyout menu.";
+"REMOVE_DONT_RECOMMEND_OPTION" = "Removes 'Don't recommend channel' option";
+"REMOVE_DONT_RECOMMEND_OPTION_DESC" = "Removes 'Don't recommend channel' option from the flyout menu.";
+"REMOVE_INFO_OPTION" = "Removes 'Why are you seeing this?' option";
+"REMOVE_INFO_OPTION_DESC" = "Removes 'Why are you seeing this?' option from the flyout menu.";
+"REMOVE_FILTER_OPTION" = "Removes 'Search filters' option";
+"REMOVE_FILTER_OPTION_DESC" = "Removes 'Search filters' option from the flyout menu.";
+"REMOVE_REPORT_OPTION" = "Removes 'Report' option";
+"REMOVE_REPORT_OPTION_DESC" = "Removes 'Report' option from the flyout menu.";
+"REMOVE_YOUTUBE_MUSIC_OPTION" = "Removes 'Listen with YouTube Music' option";
+"REMOVE_YOUTUBE_MUSIC_OPTION_DESC" = "Removes 'Listen with YouTube Music' option from the flyout menu.";
+"REMOVE_FEED_BACK_OPTION" = "Removes 'Send feedback' option";
+"REMOVE_FEED_BACK_OPTION_DESC" = "Removes 'Send feedback' option from the flyout menu.";
+"REMOVE_CAST_OPTION" = "Removes 'Watch on TV' option";
+"REMOVE_CAST_OPTION_DESC" = "Removes 'Watch on TV' option from the flyout menu.";
+"REMOVE_SHUFFLE_OPTION" = "Removes 'Shuffle' option";
+"REMOVE_SHUFFLE_OPTION_DESC" = "Removes 'Shuffle' option from the flyout menu.";
+"REMOVE_UN_SUB_OPTION" = "Removes 'Unsubscribe' option";
+"REMOVE_UN_SUB_OPTION_DESC" = "Removes 'Unsubscribe' option from the flyout menu.";
+"REMOVE_HIDE_FROM_PLAYLIST_OPTION" = "Removes 'Hide video from playlist' option";
+"REMOVE_HIDE_FROM_PLAYLIST_OPTION_DESC" = "Removes 'Hide video from playlist' option from the flyout menu.";
+"REMOVE_HELP_OPTION" = "Removes 'Help & feedback' option";
+"REMOVE_HELP_OPTION_DESC" = "Removes 'Help & feedback' option from the flyout menu.";
+"REMOVE_NOTIFY_OPTION" = "Removes 'Notify me' option";
+"REMOVE_NOTIFY_OPTION_DESC" = "Removes 'Notify me' option from the flyout menu.";
+"REMOVE_CLEARSCREEN_OPTION" = "Removes 'Clear Screen' option";
+"REMOVE_CLEARSCREEN_OPTION_DESC" = "Removes 'Clear Screen' option from the flyout menu.";
+"HIDE_COMMU_GUIDE" = "Remove guidelines";
+"HIDE_COMMU_GUIDE_DESC" = "Removes pinned guidelines from the comments section.";
+"DISABLES_RTL" = "Disable RTL formatting";
+"DISABLES_RTL_DESC" = "Forcefully displays text in left-to-right (LTR) format for languages that are initially displayed in right-to-left (RTL).";
+"DEVICE_UI" = "Interface style";
+
+"SB_DESCRIPTION" = "Skip sponsored segments automatically using community-submitted data.";
+"SB_ENABLE" = "Enable SponsorBlock";
+"SB_ENABLE_DESC" = "Activate the SponsorBlock extension to skip sponsored segments.";
+"SB_SHOW_BUTTON" = "Show overlay button";
+"SB_SHOW_BUTTON_DESC" = "Display a SponsorBlock toggle button in the player overlay.";
+"SB_SHOW_NOTIFICATIONS" = "Show notifications";
+"SB_SHOW_NOTIFICATIONS_DESC" = "Display notifications when segments are skipped. Notifications for manual-skip categories are always shown.";
+"SB_HAPTIC_FEEDBACK" = "Haptic feedback";
+"SB_HAPTIC_FEEDBACK_DESC" = "Provide haptic feedback when a segment is skipped.";
+"SB_SEGMENTS_IN_PLAYER" = "Segments in player";
+"SB_SEGMENTS_IN_PLAYER_DESC" = "Display colored segments on the progress bar of the main video player.";
+"SB_SEGMENTS_IN_FEED" = "Segments in feed";
+"SB_SEGMENTS_IN_FEED_DESC" = "Display colored segments on the progress bar of players in the feed.";
+"SB_SEGMENTS_IN_MINIPLAYER" = "Segments in mini-player";
+"SB_SEGMENTS_IN_MINIPLAYER_DESC" = "Display colored segments on the progress bar of the mini-player.";
+"SB_SHOW_DURATION" = "Show duration without segments";
+"SB_SHOW_DURATION_DESC" = "Display the total video duration excluding skippable segments.";
+"SB_CATEGORIES_HEADER" = "Segment categories";
+"SB_SEGMENT_COLOR" = "Segment color";
+
+"SB_ACTION_DISABLE" = "Disable";
+"SB_ACTION_AUTO_SKIP" = "Auto-skip";
+"SB_ACTION_ASK" = "Ask before skipping";
+"SB_ACTION_DISPLAY" = "Show on seek bar only";
+"SB_ACTION_SKIP_TO" = "Skip to segment";
+
+"SB_CAT_sponsor" = "Sponsor";
+"SB_CAT_sponsor_DESC" = "Paid promotion for a product or service not directly related to the creator.";
+"SB_CAT_intro" = "Intro";
+"SB_CAT_intro_DESC" = "Intermission or intro animation typically seen at the start of a video.";
+"SB_CAT_outro" = "Endcards";
+"SB_CAT_outro_DESC" = "Endcards, credits, or outros at the end of the video.";
+"SB_CAT_interaction" = "Interaction";
+"SB_CAT_interaction_DESC" = "Reminders to like, subscribe, or follow on social media.";
+"SB_CAT_selfpromo" = "Self-promotion";
+"SB_CAT_selfpromo_DESC" = "Unpaid promotion of the creator's own products, merchandise, or other content.";
+"SB_CAT_music_offtopic" = "Non-music";
+"SB_CAT_music_offtopic_DESC" = "Non-music sections in music videos (e.g. talking, introductions).";
+"SB_CAT_preview" = "Preview";
+"SB_CAT_preview_DESC" = "Recap or preview of content from another video or previous episode.";
+"SB_CAT_hook" = "Hook/Greetings";
+"SB_CAT_hook_DESC" = "Narrated trailers for the upcoming video, greetings and goodbyes.";
+"SB_CAT_poi_highlight" = "Highlight";
+"SB_CAT_poi_highlight_DESC" = "The key moment or highlight point of the video. Use 'Skip to' to jump directly to it.";
+"SB_CAT_filler" = "Filler";
+"SB_CAT_filler_DESC" = "Tangential content, jokes, or filler that is not directly related to the main topic.";
+
+"SB_SKIPPED" = "%@ segment has been skipped";
+"SB_DETECTED" = "%@ segment detected.\nWould you like to skip the segment?";
+"SB_SKIP_NOW" = "Skip";
+"SB_UNSKIP" = "Unskip";
+"SB_JUMPED_TO_HIGHLIGHT" = "Jumped to highlight";
+"SB_JUMP_TO_HIGHLIGHT" = "Highlight available. Jump to the point?";
+"SB_SEGMENT_COLOR_SUFFIX" = "Segment color";
+"SB_SECTION_MAIN" = "Main";
+"SB_SKIP_ALERT_DURATION" = "Skip alert duration";
+"SB_UNSKIP_ALERT_DURATION" = "Unskip alert duration";
\ No newline at end of file
diff --git a/layout/Library/Application Support/YouMod.bundle/he.lproj/Localizable.strings b/layout/Library/Application Support/YouMod.bundle/he.lproj/Localizable.strings
index bccaf550..2c1a8a0f 100644
--- a/layout/Library/Application Support/YouMod.bundle/he.lproj/Localizable.strings
+++ b/layout/Library/Application Support/YouMod.bundle/he.lproj/Localizable.strings
@@ -1,11 +1,18 @@
-"NOTE" = "NOTE: Some hiding features may leave a blank box.";
"SETTINGS" = "Settings";
+"INTERFACE" = "Interface";
+
+"LOOP_DISABLED" = "Loop disabled";
+"LOOP_ENABLED" = "Loop enabled";
"NEW_ISSUES" = "Report issues or request new features";
"NEW_ISSUES_DESC" = "Found bugs or want to request new features? Tap here.";
"SOURCE_CODES" = "Source codes";
"SOURCE_CODES_DESC" = "Take a look at the codes!";
+"EXTRA" = "Extra";
+"FIX_PLAYBACK_ISSUES" = "Fix playback issues (Temporary)";
+"FIX_PLAYBACK_ISSUES_DESC" = "Try to fix video playback issues for some users. Turning this on will make the player automatically reloads itself everytime it errors or fails to play the video.";
+
"PERFER_HEADER" = "YouMod perferences";
"PERFER" = "Perferences";
"IMPORT" = "Import settings";
@@ -24,6 +31,7 @@
"ERROR_NO_KEYS_IMPORT" = "No available keys to import";
"ERROR_NO_KEYS_EXPORT" = "No available keys to export";
"OVERRIDE" = "This will override the current settings. Do you want to continue?";
+"DISABLED" = "Disabled";
"CACHE" = "Cache management";
"CLEARCACHE" = "Clear cache";
@@ -32,12 +40,80 @@
"AUTO_CLEARCACHE_DESC" = "Automatically clears the app cache on startup.";
"DOWNLOADING" = "Downloading";
-"DOWNLOAD_MANAGER" = "Download Manager";
-"DOWNLOAD_MANAGER_DESC" = "Replaces YouTube's video downloader with YouMod one. With ablity to downloading videos, audios, captions, and thumbnails";
+"DOWNLOAD_MANAGER" = "Download manager";
+"DOWNLOAD_MANAGER_DESC" = "Replaces YouTube's video downloader with YouMod one. With ablity to downloading videos, audios, captions, and thumbnails.";
"DOWNLOAD_SAVE_PHOTOS" = "Automatically save to Photos";
"DOWNLOAD_SAVE_PHOTOS_DESC" = "Automatically saves downloaded videos and images directly to your Photos app.";
-"DOWNLOAD_DRC_AUDIO" = "Prefer DRC Audio";
-"DOWNLOAD_DRC_AUDIO_DESC" = "Prioritizes downloading audio tracks with Dynamic Range Compression for more consistent volume levels.";
+"ADD_SHORTS_DOWNLOAD" = "Adds a download button to Shorts";
+"ADD_SHORTS_DOWNLOAD_DESC" = "Adds a download button to Shorts video overlay.";
+"USE_ORIG_AUDIO" = "Automatically uses original audio track";
+"USE_ORIG_AUDIO_DESC" = "Automatically uses original audio track instead of showing a dialog.";
+"DOWNLOAD_SHORTS" = "Download Shorts";
+"DOWNLOAD_SHORTS_DESC" = "Choose Shorts quality";
+"COPY_VID_INFO" = "Copy video informations";
+"COPY_VID_INFO_DESC" = "Choose what to copy";
+"COPY_ALL_VID_INFO" = "Copy all";
+"COPY_ALL_VID_INFO_DESC" = "Copy all video informations";
+"COPY_AUTHOR" = "Copy author";
+"COPY_AUTHOR_DESC" = "Copy the video author";
+"COPY_TITLE" = "Copy title";
+"COPY_TITLE_DESC" = "Copy the video title";
+"COPY_DESCRIPTION" = "Copy description";
+"COPY_DESCRIPTION_DESC" = "Copy the video description";
+"COPY_URL" = "Copy video link";
+"COPY_URL_TIMESTAMP" = "Copy video link with timestamp";
+"URL_COPIED" = "Copied video link";
+"URL_TIMESTAMP_COPIED" = "Copied video link with timestamp";
+"ERROR_VIDEOID" = "Cannot get the video ID";
+"ERROR_ADS" = "Cannot copy video link while playing ads";
+"COPIED_AUTHOR" = "Copied video author";
+"COPIED_TITLE" = "Copied video title";
+"COPIED_DESCRIPTION" = "Copied video description";
+"SAVE_THUMBNAIL" = "Save thumbnail";
+"SAVE_THUMBNAIL_DESC" = "Save to Photos";
+"DOWNLOAD_CAPTIONS" = "Download captions";
+"DOWNLOAD_CAPTIONS_DESC" = "Save subtitles as .VTT file";
+"DOWNLOAD_AUDIO" = "Download audio";
+"DOWNLOAD_AUDIO_DESC" = "Download audio as .m4a file";
+"DOWNLOAD_VIDEO" = "Download video";
+"DOWNLOAD_VIDEO_DESC" = "Choose video quality";
+"OPEN_VID_BEFORE" = "Open a video before using the download manager";
+"NO_CAPTIONS_URL" = "No valid caption URLs found";
+"NO_AUDIO_URL" = "No audio URL found";
+"NO_STREAM_URL" = "No stream URL found";
+"CAPTIONS_FAILED" = "Failed to download captions";
+"NO_CAPTIONS" = "No captions available for this video";
+"NO_AUDIO_STREAM_FOUND" = "No audio streams found";
+"NO_VID_AUDIO_STREAM_FOUND" = "No video/audio streams found";
+"COPIED_VID_INFO" = "Copied all video informations";
+"SAVED_TO_PHOTOS" = "Saved to Photos";
+"SAVE_FAILED" = "Save failed";
+"PHOTO_ACCESS_DENINED" = "Photos access denied";
+"THUMBNAIL_FAILED" = "Thumbnail failed";
+"DOWNLOADING_THUMBNAIL" = "Downloading thumbnail...";
+"DOWNLOADING_AUDIO" = "Downloading audio...";
+"DOWNLOADING_VIDEO" = "Downloading video...";
+"DOWNLOADING_CAPTIONS" = "Downloading captions...";
+"NO_THUMBNAIL_FOUND" = "No thumbnail found";
+"DOWNLOAD_FAILED" = "Download failed";
+"COPY_THUMBNAIL" = "Copy thumbnail";
+"COPY_THUMBNAIL_DESC" = "Copy to clipboard";
+"SHOW_THUMBNAIL" = "Show thumbnail";
+"SHOW_THUMBNAIL_DESC" = "Show in a dialog";
+"THUMBNAIL_OPTIONS" = "Download thumbnail";
+"THUMBNAIL_OPTIONS_DESC" = "Show more options";
+"COPIED_TO_CLIPBOARD" = "Copied to clipboard";
+"DOWNLOAD_COMPLETED" = "Download completed";
+"CANNOT_SAVE_TO_PHOTOS" = "Cannot save to Photos";
+"FINA_VIDEO" = "Finalizing video...";
+"MERGING_VID_FALLBACK" = "Merging video with fallback...";
+"MERGING_VID" = "Merging video...";
+"CONVERTING" = "Converting to";
+"ALREADY_DOWNLOADING" = "Already downloading";
+"DOWNLOAD_CANCELLED" = "Download cancelled";
+"CANCEL" = "Cancel";
+"TRIMMING_AUDIO" = "Trimming audio...";
+"COPY" = "Copy";
"APPEARANCE" = "Appearance";
"OLED_THEME" = "OLED dark mode";
@@ -46,6 +122,8 @@
"OLED_KEYBOARD_DESC" = "Make the keyboard uses a pure black (OLED) color for dark theme.";
"NAVBAR" = "Navigation bar";
+"STICKY_NAVBAR" = "Sticky Navigation bar";
+"STICKY_NAVBAR_DESC" = "Pins the Navigation bar so that it remains visible when scrolling down.";
"HIDE_YT_LOGO" = "Hide YouTube logo";
"HIDE_YT_LOGO_DESC" = "Hide YouTube logo in the navigation bar.";
"PREMIUM_LOGO" = "Use YouTube Premium logo";
@@ -62,34 +140,78 @@
"FEED" = "Feed";
"HIDE_SUBBAR" = "Hide subbar";
"HIDE_SUBBAR_DESC" = "Hide subbar at the top of the feed.";
+"HIDE_HORI_SHELF" = "Remove horizontal feeds";
+"HIDE_HORI_SHELF_DESC" = "Removes all horizontal feeds (News, Continue Watching, Posts collection, etc.) from the feed, except Playables.";
"HIDE_MUSIC_PLAYLISTS" = "Hide music playlist generator";
"HIDE_MUSIC_PLAYLISTS_DESC" = "Hide music playlist generator (Select music tunes in three) in the feed.";
"HIDE_FEED_POST" = "Hide posts";
"HIDE_FEED_POST_DESC" = "Hide posts in feed.";
+"HIDE_SURVEYS" = "Hide surveys";
+"HIDE_SURVEYS_DESC" = "Hide video surveys that YouTube shows sometimes.";
"HIDE_SHORTS_SHELF" = "Hide Shorts shelf";
"HIDE_SHORTS_SHELF_DESC" = "Hide Shorts shelf in the feed, including in the Subscriptions tab.";
+"KEEP_SHORTS_SUBSCRIPT" = "Keep Shorts in Subscriptions";
+"KEEP_SHORTS_SUBSCRIPT_DESC" = "Prevents removing Shorts from Subscriptions tab.";
"HIDE_SEARCH_HISTORY" = "Hide search history";
"HIDE_SEARCH_HISTORY_DESC" = "Hide previous search history and suggestions when using the search bar.\nNOTE: Your search history will still be accessible in your other YouTube clients.";
-"HIDE_SUB_BUTTON" = "Hide Subscribe button";
-"HIDE_SUB_BUTTON_DESC" = "Hides the subscribe button in channels and video descriptions.";
-"HIDE_SHOP_BUTTON" = "Hide Shopping button";
-"HIDE_SHOP_BUTTON_DESC" = "Hides the shopping/store button in channels descriptions.";
-"HIDE_MEMBER_BUTTON" = "Hide Membership button";
-"HIDE_MEMBER_BUTTON_DESC" = "Hides the 'Join' button in channels and video descriptions.";
+"HIDE_PLAYABLES" = "Remove playables";
+"HIDE_PLAYABLES_DESC" = "Removes playables (mini-games) from the feed.";
"PLAYER" = "Player";
+"PLAYBACK_SPEED" = "Playback speed";
+"CONTROL_CENTER" = "Control Center";
+"PLAYER_ACTIONS" = "Player actions";
+"GESTURE_HEADER" = "Gestures";
+"QUALITY_WIFI" = "Playback quality on WiFi";
+"QUALITY_WIFI_DESC" = "Automatically applies the selected quality when streaming over Wi-Fi.";
+"QUALITY_CELLULAR" = "Playback quality on Cellular";
+"QUALITY_CELLULAR_DESC" = "Automatically applies the selected quality when streaming over mobile data.";
+"QUALITY_LOW_POWER" = "Playback quality on Power saver mode";
+"QUALITY_LOW_POWER_DESC" = "Automatically applies the selected quality when when Low power mode is enabled on your device.";
+"DEFAULT" = "Default";
+"BEST" = "Best";
+"AUDIO_TRACK" = "Preferred audio track";
+"ORIGINAL" = "Original";
+"SELECT_MANUALLY" = "Select manually";
+"AUDIO_TRACK_SELECT" = "Selected audio track";
+"AUDIO_TRACK_SELECT_DESC" = "Specifies the preferred audio track language to use for playback, if available.";
+"NO_AUTO_DUBBED" = "Uses original audio track";
+"NO_AUTO_DUBBED_DESC" = "Uses original audio track if the preferred language track is auto-dubbed or not found.";
+"CAPTION_TRACK" = "Preferred captions";
+"CAPTION_TRACK_SELECT" = "Selected captions";
+"CAPTION_TRACK_SELECT_DESC" = "Specifies the preferred captions language to display, if available.";
+"DISABLES_CAPTION_TRACK" = "Automatically disable captions";
+"DISABLES_CAPTION_TRACK_DESC" = "Automatically disable captions if the preferred language is auto-generated or not found.";
+"DEFAULT_SPEED" = "Default playback rate";
+"DEFAULT_SPEED_DESC" = "Overrides the default playback speed.";
+"SPEED_BUTTON" = "Speed controls button";
+"SPEED_BUTTON_DESC" = "Displays a speed controls button on overlay.";
+"MUTE_BUTTON" = "Mute button";
+"MUTE_BUTTON_DESC" = "Displays a button to mute or unmute the volume on the overlay.";
+"SHARE_BUTTON" = "Share button";
+"SHARE_BUTTON_DESC" = "Displays a button to share the video link on the overlay.";
+"LOOP_BUTTON" = "Loop button";
+"LOOP_BUTTON_DESC" = "Displays a button to loop the video on the overlay.";
+"CAPTION_BUTTON" = "Captions button";
+"CAPTION_BUTTON_DESC" = "Displays a button to copy the video captions on the overlay.";
+"HOLD_TO_SPEED" = "Hold to speed";
+"HOLD_TO_SPEED_DESC" = "Uses custom hold to speed playback rate.";
"HIDE_AUTOPLAY" = "Hide autoplay switch";
"HIDE_AUTOPLAY_DESC" = "Hide autoplay switch in the video overlay.";
"HIDE_CAPTIONS_BUTTON" = "Hide captions button";
"HIDE_CAPTIONS_BUTTON_DESC" = "Hide captions button in the video overlay.";
"HIDE_CAST_BUTTON_PLAYER" = "Hide cast button";
"HIDE_CAST_BUTTON_PLAYER_DESC" = "Hide cast button in the video overlay.";
-"HIDE_PREV_BUTTON" = "Hide previous/back button";
-"HIDE_PREV_BUTTON_DESC" = "Hide previous/back button in the video overlay.";
-"HIDE_NEXT_BUTTON" = "Hide next/skip button";
-"HIDE_NEXT_BUTTON_DESC" = "Hide next/skip button in the video overlay.";
+"HIDE_NEXT_AND_PREV_BUTTON" = "Hide previous and next buttons";
+"HIDE_NEXT_AND_PREV_BUTTON_DESC" = "Hide previous and next buttons in the video overlay.";
"REPLACE_PREVNEXT_BUTTONS" = "Replace Previous/Next buttons";
"REPLACE_PREVNEXT_BUTTONS_DESC" = "Replaces the Previous and Next video buttons in the player with Rewind and Fast Forward buttons.";
+"SKIP_BACKWARD" = "Skip backward";
+"SKIP_BACKWARD_DESC" = "Replaces the previous track button in Control Center with a skip backward button by a set number of seconds.";
+"SKIP_FORWARD" = "Skip forward";
+"SKIP_FORWARD_DESC" = "Replaces the next track button in Control Center with a skip forward button by a set number of seconds.";
+"REWIND_SECONDS" = "Rewind seconds";
+"FORWARD_SECONDS" = "Fast-forward seconds";
"REMOVE_DARK_OVERLAY" = "Remove dark overlay";
"REMOVE_DARK_OVERLAY_DESC" = "Remove dark overlay from the video overlay.";
"HIDE_END_SCREEN" = "Hide endscreen cards";
@@ -102,6 +224,12 @@
"HIDE_PAID_OVERLAY_DESC" = "Hide 'Includes paid promotion' in the video overlay.";
"HIDE_WATERMARK" = "Hide watermark";
"HIDE_WATERMARK_DESC" = "Hide channel watermark at the bottom of the video overlay.";
+"PAUSE_ON_OVERLAY" = "Pause on overlay";
+"PAUSE_ON_OVERLAY_DESC" = "Sets playback on pause if overlay appears.";
+"DISABLES_SNAP_TO_CHAPTER" = "Disable snap to chapters";
+"DISABLES_SNAP_TO_CHAPTER_DESC" = "Prevents the scrubbar stick to the chapters.";
+"DISABLES_ENGAGE_PANEL" = "Disables engagement panel in fullscreen";
+"DISABLES_ENGAGE_PANEL_DESC" = "Prevent engagement panel showing in fullscreen.";
"GESTURES" = "Enable gestures controls";
"GESTURES_DESC" = "Swipe up or down on the left/right edges of the video screen to adjust brightness, volume, or playback speed.";
"GESTURE_AREA" = "Gesture activation area";
@@ -136,6 +264,8 @@
"DISABLES_SHOW_REMAINING_DESC" = "Disable the feature that shows how much time is left in a video when tapping the timestamp.";
"ALWAYS_SHOW_REMAINING" = "Always show remaining time";
"ALWAYS_SHOW_REMAINING_DESC" = "Ensure the remaining time is always displayed by default in the player overlay.";
+"COPY_TIMESTAMP_ON_PAUSE" = "Copy timestamped links";
+"COPY_TIMESTAMP_ON_PAUSE_DESC" = "Allows to copy timestamped link to the clipboard by pressing pause button.";
"SHOW_REMAINING_EXTRA" = "Show extra remaining time";
"SHOW_REMAINING_EXTRA_DESC" = "Adds the estimated finish time to the label in the video overlay.";
"HIDE_FULLSCREEN_ACTIONS" = "Hide fullscreen actions";
@@ -148,93 +278,95 @@
"HIDE_CONTENT_WARNING_DESC" = "Automatically skip the sensitive content warning alert in some vidoes.";
"AUTO_FULLSCREEN" = "Auto fullscreen";
"AUTO_FULLSCREEN_DESC" = "Automatically enter fullscreen mode when starting video playback.";
-"AUTO_DISABLES_CAPTION" = "Auto disable captions";
-"AUTO_DISABLES_CAPTION_DESC" = "Automatically turns off closed captions (CC) whenever a new video starts playing.";
"PORTRAIT_FULLSCREEN" = "Portrait fullscren mode";
"PORTRAIT_FULLSCREEN_DESC" = "Enables portrait fullscreen mode support.";
"OLD_QUALITY_PICKER" = "Use old quality picker";
"OLD_QUALITY_PICKER_DESC" = "Reverts the video quality selection menu to the classic version (lists all resolutions immediately).";
"EXTRA_SPEED" = "Extra speed options";
"EXTRA_SPEED_DESC" = "Adds more video playback speed options up to 10x.";
-"DISABLE_HINTS" = "Disable hints";
-"DISABLE_HINTS_DESC" = "Disable the tutorial hints and interactive pop-ups.";
"FORCE_MINIPLAYER" = "Force miniplayer";
"FORCE_MINIPLAYER_DESC" = "Allows all videos, including those made for kids, to play in the miniplayer when dismissing the player.";
"FORCE_SEEKBAR" = "Always show progress bar";
"FORCE_SEEKBAR_DESC" = "Makes the progress bar (seekbar) remains visible at all times while in fullscreen mode.";
-"HIDE_LIKE_BUTTON" = "Hide Like button";
-"HIDE_LIKE_BUTTON_DESC" = "Hide Like button in the action bar under the video overlay.";
-"HIDE_DISLIKE_BUTTON" = "Hide Dislike button";
-"HIDE_DISLIKE_BUTTON_DESC" = "Hide Dislike button in the action bar under the video overlay.";
-"HIDE_SHARE_BUTTON" = "Hide Share button";
-"HIDE_SHARE_BUTTON_DESC" = "Hide Share button in the action bar under the video overlay.";
-"HIDE_DOWNLOAD_BUTTON" = "Hide Download button";
-"HIDE_DOWNLOAD_BUTTON_DESC" = "Hide Download button in the action bar under the video overlay.";
-"HIDE_CLIP_BUTTON" = "Hide Clip button";
-"HIDE_CLIP_BUTTON_DESC" = "Hide Clip button in the action bar under the video overlay.";
-"HIDE_REMIX_BUTTON" = "Hide Remix button";
-"HIDE_REMIX_BUTTON_DESC" = "Hide Remix button in the action bar under the video overlay.";
-"HIDE_SAVE_BUTTON" = "Hide Save button";
-"HIDE_SAVE_BUTTON_DESC" = "Hide Save button in the action bar under the video overlay.";
+"TAP_TO_SEEK" = "Tap to seek";
+"TAP_TO_SEEK_DESC" = "Quickly jump to any point by tapping the progress bar.";
+"DISABLES_ZOOM" = "Disable free zoom gesture";
+"DISABLES_ZOOM_DESC" = "Disables new free zoom gestures.";
+"USES_24_HOURS_TIME" = "24-hour format";
+"USES_24_HOURS_TIME_DESC" = "Shows end time in 24-hour format.";
+"PAUSE_TWO_FINGERS" = "Pause using two fingers";
+"PAUSE_TWO_FINGERS_DESC" = "Pause/Resume playback by tapping on the screen using two fingers.";
+"HIDE_COMMENTS_SECTION" = "Hide comments section";
+"HIDE_COMMENTS_SECTION_DESC" = "Completely removes the entire comments section.";
+"HIDE_COMMENTS_PREVIEW" = "Hide preview comments";
+"HIDE_COMMENTS_PREVIEW_DESC" = "Removes the preview box for the top comment while keeping the full section expandable.";
+"USE_ANOTHER_MINIPLAYER" = "Uses another miniplayer style";
+"USE_ANOTHER_MINIPLAYER_DESC" = "Uses another miniplayer style redesigned from older YT versions.";
"SHORTS" = "Shorts";
-"HIDE_SHORTS_LIKE_BUTTON" = "Hide Like button";
-"HIDE_SHORTS_LIKE_BUTTON_DESC" = "Hide the Like button in the Shorts player.";
-"HIDE_SHORTS_DISLIKE_BUTTON" = "Hide Dislike button";
-"HIDE_SHORTS_DISLIKE_BUTTON_DESC" = "Hide the Dislike button in the Shorts player.";
-"HIDE_SHORTS_COMMENT_BUTTON" = "Hide Comment button";
-"HIDE_SHORTS_COMMENT_BUTTON_DESC" = "Hide the Comment button in the Shorts player.";
-"HIDE_SHORTS_SHARE_BUTTON" = "Hide Share button";
-"HIDE_SHORTS_SHARE_BUTTON_DESC" = "Hide the Share button in the Shorts player.";
-"HIDE_SHORTS_REMIX_BUTTON" = "Hide Remix button";
-"HIDE_SHORTS_REMIX_BUTTON_DESC" = "Hide the Remix button in the Shorts player.";
-"HIDE_METADATA_BUTTON" = "Hide sound metadata button";
-"HIDE_METADATA_BUTTON_DESC" = "Hide the button that shows the sound source or music metadata in the Shorts player.";
+"SHORTS_ONLY" = "Shorts only mode";
+"SHORTS_ONLY_DESC" = "Limits YouTube functionality to viewing Shorts only.";
+"SHORTS_ACTION" = "Shorts action after the video ends";
+"LOOP" = "Loop video";
+"SKIP_TO_NEXT_SHORTS" = "Skip to next video";
+"PAUSE_SHORTS" = "Stop video";
+"REMOVE_LIVE_SHORTS" = "Remove live videos";
+"REMOVE_LIVE_SHORTS_DESC" = "Removes live videos from the feed.";
"HIDE_SHORTS_PRODUCT" = "Hide products";
"HIDE_SHORTS_PRODUCT_DESC" = "Hide tagged products and shopping links in the Shorts overlay.";
"HIDE_SHORTS_RECBAR" = "Hide recommendation bar";
"HIDE_SHORTS_RECBAR_DESC" = "Hide the suggested action bar in the Shorts player (e.g. Try this sound, Shopping, Preview comment).";
-"HIDE_SHORTS_COMMIT" = "Hide commissions bar";
-"HIDE_SHORTS_COMMIT_DESC" = "Hide the bar that shows the video got commissions in the Shorts player.";
-"HIDE_SHORTS_SUBSCRIPT_BUTTON" = "Hide subscriptions button";
-"HIDE_SHORTS_SUBSCRIPT_BUTTON_DESC" = "Hide the Subscriptions button at the top of the Shorts player.";
-"HIDE_SHORTS_LIVE_BUTTON" = "Hide live button";
-"HIDE_SHORTS_LIVE_BUTTON_DESC" = "Hide the Live button at the top of the Shorts player.";
-"HIDE_SHORTS_LENS_BUTTON" = "Hide lens button";
-"HIDE_SHORTS_LENS_BUTTON_DESC" = "Hide the Lens button at the top of the Shorts player.";
-"HIDE_SHORTS_TRENDS_BUTTON" = "Hide trends button";
-"HIDE_SHORTS_TRENDS_BUTTON_DESC" = "Hide the Trends button at the top of the Shorts player.";
-"HIDE_SHORTS_TO_VIDEO" = "Hide link to full video";
-"HIDE_SHORTS_TO_VIDEO_DESC" = "Hide the link that redirects in a Short to a full video in the Shorts player.";
"ENABLES_SHORTS_QUALITY" = "Enable Shorts quality picker";
"ENABLES_SHORTS_QUALITY_DESC" = "Force enable the video quality selection menu in Shorts.";
"SHOW_SHORTS_SEEKBAR" = "Always show seekbar";
"SHOW_SHORTS_SEEKBAR_DESC" = "Ensure the progress/seek bar is always visible at the bottom of the Shorts player.";
+"REMOVE_POSTS_SHORTS" = "Remove posts";
+"REMOVE_POSTS_SHORTS_DESC" = "Remove posts from the feed.";
+"SHORTS_FULLSCREEN" = "Pinch to Fullscreen";
+"SHORTS_FULLSCREEN_DESC" = "Manages visibility of the overlay with pinch in and pinch out gestures, displaying Shorts in fullscreen mode.";
"TABBAR" = "Tab bar";
"DEFAULT_TAB" = "Default startup tab";
-"DEFAULT_TAB_DESC" = "You can select the tab you want to open when opening the app.";
-"HOME_NAME" = "Home";
-"SUBSCRIPT_NAME" = "Subscriptions";
-"LIB_NAME" = "Library (You)";
+"FORSTED_TAB_BAR" = "Translucent tab bar";
+"ENABLED" = "Enabled";
"HIDE_TAB_INDI" = "Hide tab indicators";
"HIDE_TAB_INDI_DESC" = "Hide all tab indicators in the tab bar.";
"HIDE_TAB_LABELS" = "Hide tab labels";
"HIDE_TAB_LABELS_DESC" = "Hide all tab labels in the tab bar.";
-"HIDE_HOME_TAB" = "Hide Home tab";
-"HIDE_HOME_TAB_DESC" = "Hide Home tab in the tab bar.";
-"HIDE_SHORTS_TAB" = "Hide Shorts tab";
-"HIDE_SHORTS_TAB_DESC" = "Hide Shorts tab in the tab bar.";
-"HIDE_CREATE_BUTTON" = "Hide Create button";
-"HIDE_CREATE_BUTTON_DESC" = "Hide Create button in the tab bar.";
-"HIDE_SUBSCRIPT_TAB" = "Hide Subscriptions tab";
-"HIDE_SUBSCRIPT_TAB_DESC" = "Hide Subscriptions tab in the tab bar.";
+"HOME_TAB" = "Home";
+"SHORTS_TAB" = "Shorts";
+"CREATE_TAB" = "Create";
+"SUBSCRIPTIONS_TAB" = "Subscriptions";
+"LIBRARY_TAB" = "Library (You)";
+"HISTORY_TAB" = "History";
+"GAMING_TAB" = "Gaming";
+"SPORTS_TAB" = "Sports";
+"NOTI_TAB" = "Notifications";
+"NEWS_TAB" = "News";
+"MUSIC_TAB" = "Music";
+"WATCH_LATER_TAB" = "Watch later";
+"PLAYLIST_TAB" = "Playlists";
+"LIKE_TAB" = "Liked videos";
+"LIVE_TAB" = "Live";
+"POST_TAB" = "Posts";
+"VIDEO_TAB" = "Your videos";
+"MOVIE_TAB" = "Movies";
+"COURSE_TAB" = "Courses";
+"MINIGAME_TAB" = "Playables";
+"MANAGE_TABS" = "Manage tabs";
+"MANAGE_TABS_DESC" = "Reorder and toggle tabs";
+"TAB_REORDER_HINT" = "Drag to reorder. Toggle to show/hide.\nTab settings are also accessible by long-pressing the first tab in the tab bar.";
+"TAB_LIMIT" = "Tab limit";
+"TAB_LIMIT_DESC" = "Only 6 tabs are allowed.";
+"ZERO_TAB_DESC" = "At least 1 tab has to be enabled.";
"MISCELLANEOUS" = "Miscellaneous";
"BACKGROUND_PLAYBACK" = "Background playback";
"BACKGROUND_PLAYBACK_DESC" = "Enables background playback for videos.";
"DISABLES_SHORTS_PIP" = "Disables Shorts PiP";
"DISABLES_SHORTS_PIP_DESC" = "Try to disables Shorts PiP, this might not 100% disables it.";
+"DISABLE_HINTS" = "Disable hints";
+"DISABLE_HINTS_DESC" = "Disable the tutorial hints and interactive pop-ups.";
"BLOCK_UPGRADE_DIALOGS" = "Block upgrade dialogs";
"BLOCK_UPGRADE_DIALOGS_DESC" = "Block the YouTube dialogs that ask you to upgrade the app.";
"ARE_YOU_THERE_DIALOG" = "Hide 'Are you there?' dialog";
@@ -247,7 +379,109 @@
"DISABLES_SNACK_BAR_DESC" = "Disables snack bar when pressing some buttons or doing something in the app.";
"HIDE_STARTUP_ANIMATIONS" = "Hide YouTube's startup animations";
"HIDE_STARTUP_ANIMATIONS_DESC" = "Hide YouTube's startup animations when opening the app.";
-"HIDE_PLAY_IN_NEXT_QUEUE" = "Hide 'Play next in queue'";
-"HIDE_PLAY_IN_NEXT_QUEUE_DESC" = "Hide the 'Play next in queue' option in the flyout menu.";
"HIDE_LIKE_DISLIKE_VOTES" = "Hide like/dislike votes";
-"HIDE_LIKE_DISLIKE_VOTES_DESC" = "Hide messages that pop up when you like/dislike videos.";
\ No newline at end of file
+"HIDE_LIKE_DISLIKE_VOTES_DESC" = "Hide messages that pop up when you like/dislike videos.";
+"FLYOUT_MENU" = "Flyout menu";
+"REMOVE_PLAY_IN_NEXT_QUEUE_OPTION" = "Removes 'Play next in queue' option";
+"REMOVE_PLAY_IN_NEXT_QUEUE_OPTION_DESC" = "Removes 'Play next in queue' option from the flyout menu.";
+"REMOVE_DOWNLOAD_OPTION" = "Removes 'Download video' option";
+"REMOVE_DOWNLOAD_OPTION_DESC" = "Removes 'Download video' option from the flyout menu.";
+"REMOVE_WATCH_LATER_OPTION" = "Removes 'Save to Watch later' option";
+"REMOVE_WATCH_LATER_OPTION_DESC" = "Removes 'Save to Watch Later' option from the flyout menu.";
+"REMOVE_SAVE_OPTION" = "Removes 'Save to playlist' option";
+"REMOVE_SAVE_OPTION_DESC" = "Removes 'Save to playlist' option from the flyout menu.";
+"REMOVE_REMOVE_FROM_PLAYLIST_OPTION" = "Removes 'Remove from playlist' option";
+"REMOVE_REMOVE_FROM_PLAYLIST_OPTION_DESC" = "Removes 'Remove from playlist' option from the flyout menu.";
+"REMOVE_SHARE_OPTION" = "Removes 'Share' option";
+"REMOVE_SHARE_OPTION_DESC" = "Removes 'Share' option from the flyout menu.";
+"REMOVE_NOT_INTERESTED_OPTION" = "Removes 'Not interested' option";
+"REMOVE_NOT_INTERESTED_OPTION_DESC" = "Removes 'Not interested' option from the flyout menu.";
+"REMOVE_DONT_RECOMMEND_OPTION" = "Removes 'Don't recommend channel' option";
+"REMOVE_DONT_RECOMMEND_OPTION_DESC" = "Removes 'Don't recommend channel' option from the flyout menu.";
+"REMOVE_INFO_OPTION" = "Removes 'Why are you seeing this?' option";
+"REMOVE_INFO_OPTION_DESC" = "Removes 'Why are you seeing this?' option from the flyout menu.";
+"REMOVE_FILTER_OPTION" = "Removes 'Search filters' option";
+"REMOVE_FILTER_OPTION_DESC" = "Removes 'Search filters' option from the flyout menu.";
+"REMOVE_REPORT_OPTION" = "Removes 'Report' option";
+"REMOVE_REPORT_OPTION_DESC" = "Removes 'Report' option from the flyout menu.";
+"REMOVE_YOUTUBE_MUSIC_OPTION" = "Removes 'Listen with YouTube Music' option";
+"REMOVE_YOUTUBE_MUSIC_OPTION_DESC" = "Removes 'Listen with YouTube Music' option from the flyout menu.";
+"REMOVE_FEED_BACK_OPTION" = "Removes 'Send feedback' option";
+"REMOVE_FEED_BACK_OPTION_DESC" = "Removes 'Send feedback' option from the flyout menu.";
+"REMOVE_CAST_OPTION" = "Removes 'Watch on TV' option";
+"REMOVE_CAST_OPTION_DESC" = "Removes 'Watch on TV' option from the flyout menu.";
+"REMOVE_SHUFFLE_OPTION" = "Removes 'Shuffle' option";
+"REMOVE_SHUFFLE_OPTION_DESC" = "Removes 'Shuffle' option from the flyout menu.";
+"REMOVE_UN_SUB_OPTION" = "Removes 'Unsubscribe' option";
+"REMOVE_UN_SUB_OPTION_DESC" = "Removes 'Unsubscribe' option from the flyout menu.";
+"REMOVE_HIDE_FROM_PLAYLIST_OPTION" = "Removes 'Hide video from playlist' option";
+"REMOVE_HIDE_FROM_PLAYLIST_OPTION_DESC" = "Removes 'Hide video from playlist' option from the flyout menu.";
+"REMOVE_HELP_OPTION" = "Removes 'Help & feedback' option";
+"REMOVE_HELP_OPTION_DESC" = "Removes 'Help & feedback' option from the flyout menu.";
+"REMOVE_NOTIFY_OPTION" = "Removes 'Notify me' option";
+"REMOVE_NOTIFY_OPTION_DESC" = "Removes 'Notify me' option from the flyout menu.";
+"REMOVE_CLEARSCREEN_OPTION" = "Removes 'Clear Screen' option";
+"REMOVE_CLEARSCREEN_OPTION_DESC" = "Removes 'Clear Screen' option from the flyout menu.";
+"HIDE_COMMU_GUIDE" = "Hide community guidelines";
+"HIDE_COMMU_GUIDE_DESC" = "Hide community guidelines header.";
+"DISABLES_RTL" = "Disable RTL formatting";
+"DISABLES_RTL_DESC" = "Forcefully displays text in left-to-right (LTR) format for languages that are initially displayed in right-to-left (RTL).";
+"DEVICE_UI" = "Interface style";
+
+"SB_DESCRIPTION" = "Skip sponsored segments automatically using community-submitted data.";
+"SB_ENABLE" = "Enable SponsorBlock";
+"SB_ENABLE_DESC" = "Activate the SponsorBlock extension to skip sponsored segments.";
+"SB_SHOW_BUTTON" = "Show overlay button";
+"SB_SHOW_BUTTON_DESC" = "Display a SponsorBlock toggle button in the player overlay.";
+"SB_SHOW_NOTIFICATIONS" = "Show notifications";
+"SB_SHOW_NOTIFICATIONS_DESC" = "Display notifications when segments are skipped. Notifications for manual-skip categories are always shown.";
+"SB_HAPTIC_FEEDBACK" = "Haptic feedback";
+"SB_HAPTIC_FEEDBACK_DESC" = "Provide haptic feedback when a segment is skipped.";
+"SB_SEGMENTS_IN_PLAYER" = "Segments in player";
+"SB_SEGMENTS_IN_PLAYER_DESC" = "Display colored segments on the progress bar of the main video player.";
+"SB_SEGMENTS_IN_FEED" = "Segments in feed";
+"SB_SEGMENTS_IN_FEED_DESC" = "Display colored segments on the progress bar of players in the feed.";
+"SB_SEGMENTS_IN_MINIPLAYER" = "Segments in mini-player";
+"SB_SEGMENTS_IN_MINIPLAYER_DESC" = "Display colored segments on the progress bar of the mini-player.";
+"SB_SHOW_DURATION" = "Show duration without segments";
+"SB_SHOW_DURATION_DESC" = "Display the total video duration excluding skippable segments.";
+"SB_CATEGORIES_HEADER" = "Segment categories";
+"SB_SEGMENT_COLOR" = "Segment color";
+
+"SB_ACTION_DISABLE" = "Disable";
+"SB_ACTION_AUTO_SKIP" = "Auto-skip";
+"SB_ACTION_ASK" = "Ask before skipping";
+"SB_ACTION_DISPLAY" = "Show on seek bar only";
+"SB_ACTION_SKIP_TO" = "Skip to segment";
+
+"SB_CAT_sponsor" = "Sponsor";
+"SB_CAT_sponsor_DESC" = "Paid promotion for a product or service not directly related to the creator.";
+"SB_CAT_intro" = "Intro";
+"SB_CAT_intro_DESC" = "Intermission or intro animation typically seen at the start of a video.";
+"SB_CAT_outro" = "Endcards";
+"SB_CAT_outro_DESC" = "Endcards, credits, or outros at the end of the video.";
+"SB_CAT_interaction" = "Interaction";
+"SB_CAT_interaction_DESC" = "Reminders to like, subscribe, or follow on social media.";
+"SB_CAT_selfpromo" = "Self-promotion";
+"SB_CAT_selfpromo_DESC" = "Unpaid promotion of the creator's own products, merchandise, or other content.";
+"SB_CAT_music_offtopic" = "Non-music";
+"SB_CAT_music_offtopic_DESC" = "Non-music sections in music videos (e.g. talking, introductions).";
+"SB_CAT_preview" = "Preview";
+"SB_CAT_preview_DESC" = "Recap or preview of content from another video or previous episode.";
+"SB_CAT_hook" = "Hook/Greetings";
+"SB_CAT_hook_DESC" = "Narrated trailers for the upcoming video, greetings and goodbyes.";
+"SB_CAT_poi_highlight" = "Highlight";
+"SB_CAT_poi_highlight_DESC" = "The key moment or highlight point of the video. Use 'Skip to' to jump directly to it.";
+"SB_CAT_filler" = "Filler";
+"SB_CAT_filler_DESC" = "Tangential content, jokes, or filler that is not directly related to the main topic.";
+
+"SB_SKIPPED" = "%@ segment has been skipped";
+"SB_DETECTED" = "%@ segment detected.\nWould you like to skip the segment?";
+"SB_SKIP_NOW" = "Skip";
+"SB_UNSKIP" = "Unskip";
+"SB_JUMPED_TO_HIGHLIGHT" = "Jumped to highlight";
+"SB_JUMP_TO_HIGHLIGHT" = "Highlight available. Jump to the point?";
+"SB_SEGMENT_COLOR_SUFFIX" = "Segment color";
+"SB_SECTION_MAIN" = "Main";
+"SB_SKIP_ALERT_DURATION" = "Skip alert duration";
+"SB_UNSKIP_ALERT_DURATION" = "Unskip alert duration";
\ No newline at end of file
diff --git a/layout/Library/Application Support/YouMod.bundle/hi.lproj/Localizable.strings b/layout/Library/Application Support/YouMod.bundle/hi.lproj/Localizable.strings
index bccaf550..2c1a8a0f 100644
--- a/layout/Library/Application Support/YouMod.bundle/hi.lproj/Localizable.strings
+++ b/layout/Library/Application Support/YouMod.bundle/hi.lproj/Localizable.strings
@@ -1,11 +1,18 @@
-"NOTE" = "NOTE: Some hiding features may leave a blank box.";
"SETTINGS" = "Settings";
+"INTERFACE" = "Interface";
+
+"LOOP_DISABLED" = "Loop disabled";
+"LOOP_ENABLED" = "Loop enabled";
"NEW_ISSUES" = "Report issues or request new features";
"NEW_ISSUES_DESC" = "Found bugs or want to request new features? Tap here.";
"SOURCE_CODES" = "Source codes";
"SOURCE_CODES_DESC" = "Take a look at the codes!";
+"EXTRA" = "Extra";
+"FIX_PLAYBACK_ISSUES" = "Fix playback issues (Temporary)";
+"FIX_PLAYBACK_ISSUES_DESC" = "Try to fix video playback issues for some users. Turning this on will make the player automatically reloads itself everytime it errors or fails to play the video.";
+
"PERFER_HEADER" = "YouMod perferences";
"PERFER" = "Perferences";
"IMPORT" = "Import settings";
@@ -24,6 +31,7 @@
"ERROR_NO_KEYS_IMPORT" = "No available keys to import";
"ERROR_NO_KEYS_EXPORT" = "No available keys to export";
"OVERRIDE" = "This will override the current settings. Do you want to continue?";
+"DISABLED" = "Disabled";
"CACHE" = "Cache management";
"CLEARCACHE" = "Clear cache";
@@ -32,12 +40,80 @@
"AUTO_CLEARCACHE_DESC" = "Automatically clears the app cache on startup.";
"DOWNLOADING" = "Downloading";
-"DOWNLOAD_MANAGER" = "Download Manager";
-"DOWNLOAD_MANAGER_DESC" = "Replaces YouTube's video downloader with YouMod one. With ablity to downloading videos, audios, captions, and thumbnails";
+"DOWNLOAD_MANAGER" = "Download manager";
+"DOWNLOAD_MANAGER_DESC" = "Replaces YouTube's video downloader with YouMod one. With ablity to downloading videos, audios, captions, and thumbnails.";
"DOWNLOAD_SAVE_PHOTOS" = "Automatically save to Photos";
"DOWNLOAD_SAVE_PHOTOS_DESC" = "Automatically saves downloaded videos and images directly to your Photos app.";
-"DOWNLOAD_DRC_AUDIO" = "Prefer DRC Audio";
-"DOWNLOAD_DRC_AUDIO_DESC" = "Prioritizes downloading audio tracks with Dynamic Range Compression for more consistent volume levels.";
+"ADD_SHORTS_DOWNLOAD" = "Adds a download button to Shorts";
+"ADD_SHORTS_DOWNLOAD_DESC" = "Adds a download button to Shorts video overlay.";
+"USE_ORIG_AUDIO" = "Automatically uses original audio track";
+"USE_ORIG_AUDIO_DESC" = "Automatically uses original audio track instead of showing a dialog.";
+"DOWNLOAD_SHORTS" = "Download Shorts";
+"DOWNLOAD_SHORTS_DESC" = "Choose Shorts quality";
+"COPY_VID_INFO" = "Copy video informations";
+"COPY_VID_INFO_DESC" = "Choose what to copy";
+"COPY_ALL_VID_INFO" = "Copy all";
+"COPY_ALL_VID_INFO_DESC" = "Copy all video informations";
+"COPY_AUTHOR" = "Copy author";
+"COPY_AUTHOR_DESC" = "Copy the video author";
+"COPY_TITLE" = "Copy title";
+"COPY_TITLE_DESC" = "Copy the video title";
+"COPY_DESCRIPTION" = "Copy description";
+"COPY_DESCRIPTION_DESC" = "Copy the video description";
+"COPY_URL" = "Copy video link";
+"COPY_URL_TIMESTAMP" = "Copy video link with timestamp";
+"URL_COPIED" = "Copied video link";
+"URL_TIMESTAMP_COPIED" = "Copied video link with timestamp";
+"ERROR_VIDEOID" = "Cannot get the video ID";
+"ERROR_ADS" = "Cannot copy video link while playing ads";
+"COPIED_AUTHOR" = "Copied video author";
+"COPIED_TITLE" = "Copied video title";
+"COPIED_DESCRIPTION" = "Copied video description";
+"SAVE_THUMBNAIL" = "Save thumbnail";
+"SAVE_THUMBNAIL_DESC" = "Save to Photos";
+"DOWNLOAD_CAPTIONS" = "Download captions";
+"DOWNLOAD_CAPTIONS_DESC" = "Save subtitles as .VTT file";
+"DOWNLOAD_AUDIO" = "Download audio";
+"DOWNLOAD_AUDIO_DESC" = "Download audio as .m4a file";
+"DOWNLOAD_VIDEO" = "Download video";
+"DOWNLOAD_VIDEO_DESC" = "Choose video quality";
+"OPEN_VID_BEFORE" = "Open a video before using the download manager";
+"NO_CAPTIONS_URL" = "No valid caption URLs found";
+"NO_AUDIO_URL" = "No audio URL found";
+"NO_STREAM_URL" = "No stream URL found";
+"CAPTIONS_FAILED" = "Failed to download captions";
+"NO_CAPTIONS" = "No captions available for this video";
+"NO_AUDIO_STREAM_FOUND" = "No audio streams found";
+"NO_VID_AUDIO_STREAM_FOUND" = "No video/audio streams found";
+"COPIED_VID_INFO" = "Copied all video informations";
+"SAVED_TO_PHOTOS" = "Saved to Photos";
+"SAVE_FAILED" = "Save failed";
+"PHOTO_ACCESS_DENINED" = "Photos access denied";
+"THUMBNAIL_FAILED" = "Thumbnail failed";
+"DOWNLOADING_THUMBNAIL" = "Downloading thumbnail...";
+"DOWNLOADING_AUDIO" = "Downloading audio...";
+"DOWNLOADING_VIDEO" = "Downloading video...";
+"DOWNLOADING_CAPTIONS" = "Downloading captions...";
+"NO_THUMBNAIL_FOUND" = "No thumbnail found";
+"DOWNLOAD_FAILED" = "Download failed";
+"COPY_THUMBNAIL" = "Copy thumbnail";
+"COPY_THUMBNAIL_DESC" = "Copy to clipboard";
+"SHOW_THUMBNAIL" = "Show thumbnail";
+"SHOW_THUMBNAIL_DESC" = "Show in a dialog";
+"THUMBNAIL_OPTIONS" = "Download thumbnail";
+"THUMBNAIL_OPTIONS_DESC" = "Show more options";
+"COPIED_TO_CLIPBOARD" = "Copied to clipboard";
+"DOWNLOAD_COMPLETED" = "Download completed";
+"CANNOT_SAVE_TO_PHOTOS" = "Cannot save to Photos";
+"FINA_VIDEO" = "Finalizing video...";
+"MERGING_VID_FALLBACK" = "Merging video with fallback...";
+"MERGING_VID" = "Merging video...";
+"CONVERTING" = "Converting to";
+"ALREADY_DOWNLOADING" = "Already downloading";
+"DOWNLOAD_CANCELLED" = "Download cancelled";
+"CANCEL" = "Cancel";
+"TRIMMING_AUDIO" = "Trimming audio...";
+"COPY" = "Copy";
"APPEARANCE" = "Appearance";
"OLED_THEME" = "OLED dark mode";
@@ -46,6 +122,8 @@
"OLED_KEYBOARD_DESC" = "Make the keyboard uses a pure black (OLED) color for dark theme.";
"NAVBAR" = "Navigation bar";
+"STICKY_NAVBAR" = "Sticky Navigation bar";
+"STICKY_NAVBAR_DESC" = "Pins the Navigation bar so that it remains visible when scrolling down.";
"HIDE_YT_LOGO" = "Hide YouTube logo";
"HIDE_YT_LOGO_DESC" = "Hide YouTube logo in the navigation bar.";
"PREMIUM_LOGO" = "Use YouTube Premium logo";
@@ -62,34 +140,78 @@
"FEED" = "Feed";
"HIDE_SUBBAR" = "Hide subbar";
"HIDE_SUBBAR_DESC" = "Hide subbar at the top of the feed.";
+"HIDE_HORI_SHELF" = "Remove horizontal feeds";
+"HIDE_HORI_SHELF_DESC" = "Removes all horizontal feeds (News, Continue Watching, Posts collection, etc.) from the feed, except Playables.";
"HIDE_MUSIC_PLAYLISTS" = "Hide music playlist generator";
"HIDE_MUSIC_PLAYLISTS_DESC" = "Hide music playlist generator (Select music tunes in three) in the feed.";
"HIDE_FEED_POST" = "Hide posts";
"HIDE_FEED_POST_DESC" = "Hide posts in feed.";
+"HIDE_SURVEYS" = "Hide surveys";
+"HIDE_SURVEYS_DESC" = "Hide video surveys that YouTube shows sometimes.";
"HIDE_SHORTS_SHELF" = "Hide Shorts shelf";
"HIDE_SHORTS_SHELF_DESC" = "Hide Shorts shelf in the feed, including in the Subscriptions tab.";
+"KEEP_SHORTS_SUBSCRIPT" = "Keep Shorts in Subscriptions";
+"KEEP_SHORTS_SUBSCRIPT_DESC" = "Prevents removing Shorts from Subscriptions tab.";
"HIDE_SEARCH_HISTORY" = "Hide search history";
"HIDE_SEARCH_HISTORY_DESC" = "Hide previous search history and suggestions when using the search bar.\nNOTE: Your search history will still be accessible in your other YouTube clients.";
-"HIDE_SUB_BUTTON" = "Hide Subscribe button";
-"HIDE_SUB_BUTTON_DESC" = "Hides the subscribe button in channels and video descriptions.";
-"HIDE_SHOP_BUTTON" = "Hide Shopping button";
-"HIDE_SHOP_BUTTON_DESC" = "Hides the shopping/store button in channels descriptions.";
-"HIDE_MEMBER_BUTTON" = "Hide Membership button";
-"HIDE_MEMBER_BUTTON_DESC" = "Hides the 'Join' button in channels and video descriptions.";
+"HIDE_PLAYABLES" = "Remove playables";
+"HIDE_PLAYABLES_DESC" = "Removes playables (mini-games) from the feed.";
"PLAYER" = "Player";
+"PLAYBACK_SPEED" = "Playback speed";
+"CONTROL_CENTER" = "Control Center";
+"PLAYER_ACTIONS" = "Player actions";
+"GESTURE_HEADER" = "Gestures";
+"QUALITY_WIFI" = "Playback quality on WiFi";
+"QUALITY_WIFI_DESC" = "Automatically applies the selected quality when streaming over Wi-Fi.";
+"QUALITY_CELLULAR" = "Playback quality on Cellular";
+"QUALITY_CELLULAR_DESC" = "Automatically applies the selected quality when streaming over mobile data.";
+"QUALITY_LOW_POWER" = "Playback quality on Power saver mode";
+"QUALITY_LOW_POWER_DESC" = "Automatically applies the selected quality when when Low power mode is enabled on your device.";
+"DEFAULT" = "Default";
+"BEST" = "Best";
+"AUDIO_TRACK" = "Preferred audio track";
+"ORIGINAL" = "Original";
+"SELECT_MANUALLY" = "Select manually";
+"AUDIO_TRACK_SELECT" = "Selected audio track";
+"AUDIO_TRACK_SELECT_DESC" = "Specifies the preferred audio track language to use for playback, if available.";
+"NO_AUTO_DUBBED" = "Uses original audio track";
+"NO_AUTO_DUBBED_DESC" = "Uses original audio track if the preferred language track is auto-dubbed or not found.";
+"CAPTION_TRACK" = "Preferred captions";
+"CAPTION_TRACK_SELECT" = "Selected captions";
+"CAPTION_TRACK_SELECT_DESC" = "Specifies the preferred captions language to display, if available.";
+"DISABLES_CAPTION_TRACK" = "Automatically disable captions";
+"DISABLES_CAPTION_TRACK_DESC" = "Automatically disable captions if the preferred language is auto-generated or not found.";
+"DEFAULT_SPEED" = "Default playback rate";
+"DEFAULT_SPEED_DESC" = "Overrides the default playback speed.";
+"SPEED_BUTTON" = "Speed controls button";
+"SPEED_BUTTON_DESC" = "Displays a speed controls button on overlay.";
+"MUTE_BUTTON" = "Mute button";
+"MUTE_BUTTON_DESC" = "Displays a button to mute or unmute the volume on the overlay.";
+"SHARE_BUTTON" = "Share button";
+"SHARE_BUTTON_DESC" = "Displays a button to share the video link on the overlay.";
+"LOOP_BUTTON" = "Loop button";
+"LOOP_BUTTON_DESC" = "Displays a button to loop the video on the overlay.";
+"CAPTION_BUTTON" = "Captions button";
+"CAPTION_BUTTON_DESC" = "Displays a button to copy the video captions on the overlay.";
+"HOLD_TO_SPEED" = "Hold to speed";
+"HOLD_TO_SPEED_DESC" = "Uses custom hold to speed playback rate.";
"HIDE_AUTOPLAY" = "Hide autoplay switch";
"HIDE_AUTOPLAY_DESC" = "Hide autoplay switch in the video overlay.";
"HIDE_CAPTIONS_BUTTON" = "Hide captions button";
"HIDE_CAPTIONS_BUTTON_DESC" = "Hide captions button in the video overlay.";
"HIDE_CAST_BUTTON_PLAYER" = "Hide cast button";
"HIDE_CAST_BUTTON_PLAYER_DESC" = "Hide cast button in the video overlay.";
-"HIDE_PREV_BUTTON" = "Hide previous/back button";
-"HIDE_PREV_BUTTON_DESC" = "Hide previous/back button in the video overlay.";
-"HIDE_NEXT_BUTTON" = "Hide next/skip button";
-"HIDE_NEXT_BUTTON_DESC" = "Hide next/skip button in the video overlay.";
+"HIDE_NEXT_AND_PREV_BUTTON" = "Hide previous and next buttons";
+"HIDE_NEXT_AND_PREV_BUTTON_DESC" = "Hide previous and next buttons in the video overlay.";
"REPLACE_PREVNEXT_BUTTONS" = "Replace Previous/Next buttons";
"REPLACE_PREVNEXT_BUTTONS_DESC" = "Replaces the Previous and Next video buttons in the player with Rewind and Fast Forward buttons.";
+"SKIP_BACKWARD" = "Skip backward";
+"SKIP_BACKWARD_DESC" = "Replaces the previous track button in Control Center with a skip backward button by a set number of seconds.";
+"SKIP_FORWARD" = "Skip forward";
+"SKIP_FORWARD_DESC" = "Replaces the next track button in Control Center with a skip forward button by a set number of seconds.";
+"REWIND_SECONDS" = "Rewind seconds";
+"FORWARD_SECONDS" = "Fast-forward seconds";
"REMOVE_DARK_OVERLAY" = "Remove dark overlay";
"REMOVE_DARK_OVERLAY_DESC" = "Remove dark overlay from the video overlay.";
"HIDE_END_SCREEN" = "Hide endscreen cards";
@@ -102,6 +224,12 @@
"HIDE_PAID_OVERLAY_DESC" = "Hide 'Includes paid promotion' in the video overlay.";
"HIDE_WATERMARK" = "Hide watermark";
"HIDE_WATERMARK_DESC" = "Hide channel watermark at the bottom of the video overlay.";
+"PAUSE_ON_OVERLAY" = "Pause on overlay";
+"PAUSE_ON_OVERLAY_DESC" = "Sets playback on pause if overlay appears.";
+"DISABLES_SNAP_TO_CHAPTER" = "Disable snap to chapters";
+"DISABLES_SNAP_TO_CHAPTER_DESC" = "Prevents the scrubbar stick to the chapters.";
+"DISABLES_ENGAGE_PANEL" = "Disables engagement panel in fullscreen";
+"DISABLES_ENGAGE_PANEL_DESC" = "Prevent engagement panel showing in fullscreen.";
"GESTURES" = "Enable gestures controls";
"GESTURES_DESC" = "Swipe up or down on the left/right edges of the video screen to adjust brightness, volume, or playback speed.";
"GESTURE_AREA" = "Gesture activation area";
@@ -136,6 +264,8 @@
"DISABLES_SHOW_REMAINING_DESC" = "Disable the feature that shows how much time is left in a video when tapping the timestamp.";
"ALWAYS_SHOW_REMAINING" = "Always show remaining time";
"ALWAYS_SHOW_REMAINING_DESC" = "Ensure the remaining time is always displayed by default in the player overlay.";
+"COPY_TIMESTAMP_ON_PAUSE" = "Copy timestamped links";
+"COPY_TIMESTAMP_ON_PAUSE_DESC" = "Allows to copy timestamped link to the clipboard by pressing pause button.";
"SHOW_REMAINING_EXTRA" = "Show extra remaining time";
"SHOW_REMAINING_EXTRA_DESC" = "Adds the estimated finish time to the label in the video overlay.";
"HIDE_FULLSCREEN_ACTIONS" = "Hide fullscreen actions";
@@ -148,93 +278,95 @@
"HIDE_CONTENT_WARNING_DESC" = "Automatically skip the sensitive content warning alert in some vidoes.";
"AUTO_FULLSCREEN" = "Auto fullscreen";
"AUTO_FULLSCREEN_DESC" = "Automatically enter fullscreen mode when starting video playback.";
-"AUTO_DISABLES_CAPTION" = "Auto disable captions";
-"AUTO_DISABLES_CAPTION_DESC" = "Automatically turns off closed captions (CC) whenever a new video starts playing.";
"PORTRAIT_FULLSCREEN" = "Portrait fullscren mode";
"PORTRAIT_FULLSCREEN_DESC" = "Enables portrait fullscreen mode support.";
"OLD_QUALITY_PICKER" = "Use old quality picker";
"OLD_QUALITY_PICKER_DESC" = "Reverts the video quality selection menu to the classic version (lists all resolutions immediately).";
"EXTRA_SPEED" = "Extra speed options";
"EXTRA_SPEED_DESC" = "Adds more video playback speed options up to 10x.";
-"DISABLE_HINTS" = "Disable hints";
-"DISABLE_HINTS_DESC" = "Disable the tutorial hints and interactive pop-ups.";
"FORCE_MINIPLAYER" = "Force miniplayer";
"FORCE_MINIPLAYER_DESC" = "Allows all videos, including those made for kids, to play in the miniplayer when dismissing the player.";
"FORCE_SEEKBAR" = "Always show progress bar";
"FORCE_SEEKBAR_DESC" = "Makes the progress bar (seekbar) remains visible at all times while in fullscreen mode.";
-"HIDE_LIKE_BUTTON" = "Hide Like button";
-"HIDE_LIKE_BUTTON_DESC" = "Hide Like button in the action bar under the video overlay.";
-"HIDE_DISLIKE_BUTTON" = "Hide Dislike button";
-"HIDE_DISLIKE_BUTTON_DESC" = "Hide Dislike button in the action bar under the video overlay.";
-"HIDE_SHARE_BUTTON" = "Hide Share button";
-"HIDE_SHARE_BUTTON_DESC" = "Hide Share button in the action bar under the video overlay.";
-"HIDE_DOWNLOAD_BUTTON" = "Hide Download button";
-"HIDE_DOWNLOAD_BUTTON_DESC" = "Hide Download button in the action bar under the video overlay.";
-"HIDE_CLIP_BUTTON" = "Hide Clip button";
-"HIDE_CLIP_BUTTON_DESC" = "Hide Clip button in the action bar under the video overlay.";
-"HIDE_REMIX_BUTTON" = "Hide Remix button";
-"HIDE_REMIX_BUTTON_DESC" = "Hide Remix button in the action bar under the video overlay.";
-"HIDE_SAVE_BUTTON" = "Hide Save button";
-"HIDE_SAVE_BUTTON_DESC" = "Hide Save button in the action bar under the video overlay.";
+"TAP_TO_SEEK" = "Tap to seek";
+"TAP_TO_SEEK_DESC" = "Quickly jump to any point by tapping the progress bar.";
+"DISABLES_ZOOM" = "Disable free zoom gesture";
+"DISABLES_ZOOM_DESC" = "Disables new free zoom gestures.";
+"USES_24_HOURS_TIME" = "24-hour format";
+"USES_24_HOURS_TIME_DESC" = "Shows end time in 24-hour format.";
+"PAUSE_TWO_FINGERS" = "Pause using two fingers";
+"PAUSE_TWO_FINGERS_DESC" = "Pause/Resume playback by tapping on the screen using two fingers.";
+"HIDE_COMMENTS_SECTION" = "Hide comments section";
+"HIDE_COMMENTS_SECTION_DESC" = "Completely removes the entire comments section.";
+"HIDE_COMMENTS_PREVIEW" = "Hide preview comments";
+"HIDE_COMMENTS_PREVIEW_DESC" = "Removes the preview box for the top comment while keeping the full section expandable.";
+"USE_ANOTHER_MINIPLAYER" = "Uses another miniplayer style";
+"USE_ANOTHER_MINIPLAYER_DESC" = "Uses another miniplayer style redesigned from older YT versions.";
"SHORTS" = "Shorts";
-"HIDE_SHORTS_LIKE_BUTTON" = "Hide Like button";
-"HIDE_SHORTS_LIKE_BUTTON_DESC" = "Hide the Like button in the Shorts player.";
-"HIDE_SHORTS_DISLIKE_BUTTON" = "Hide Dislike button";
-"HIDE_SHORTS_DISLIKE_BUTTON_DESC" = "Hide the Dislike button in the Shorts player.";
-"HIDE_SHORTS_COMMENT_BUTTON" = "Hide Comment button";
-"HIDE_SHORTS_COMMENT_BUTTON_DESC" = "Hide the Comment button in the Shorts player.";
-"HIDE_SHORTS_SHARE_BUTTON" = "Hide Share button";
-"HIDE_SHORTS_SHARE_BUTTON_DESC" = "Hide the Share button in the Shorts player.";
-"HIDE_SHORTS_REMIX_BUTTON" = "Hide Remix button";
-"HIDE_SHORTS_REMIX_BUTTON_DESC" = "Hide the Remix button in the Shorts player.";
-"HIDE_METADATA_BUTTON" = "Hide sound metadata button";
-"HIDE_METADATA_BUTTON_DESC" = "Hide the button that shows the sound source or music metadata in the Shorts player.";
+"SHORTS_ONLY" = "Shorts only mode";
+"SHORTS_ONLY_DESC" = "Limits YouTube functionality to viewing Shorts only.";
+"SHORTS_ACTION" = "Shorts action after the video ends";
+"LOOP" = "Loop video";
+"SKIP_TO_NEXT_SHORTS" = "Skip to next video";
+"PAUSE_SHORTS" = "Stop video";
+"REMOVE_LIVE_SHORTS" = "Remove live videos";
+"REMOVE_LIVE_SHORTS_DESC" = "Removes live videos from the feed.";
"HIDE_SHORTS_PRODUCT" = "Hide products";
"HIDE_SHORTS_PRODUCT_DESC" = "Hide tagged products and shopping links in the Shorts overlay.";
"HIDE_SHORTS_RECBAR" = "Hide recommendation bar";
"HIDE_SHORTS_RECBAR_DESC" = "Hide the suggested action bar in the Shorts player (e.g. Try this sound, Shopping, Preview comment).";
-"HIDE_SHORTS_COMMIT" = "Hide commissions bar";
-"HIDE_SHORTS_COMMIT_DESC" = "Hide the bar that shows the video got commissions in the Shorts player.";
-"HIDE_SHORTS_SUBSCRIPT_BUTTON" = "Hide subscriptions button";
-"HIDE_SHORTS_SUBSCRIPT_BUTTON_DESC" = "Hide the Subscriptions button at the top of the Shorts player.";
-"HIDE_SHORTS_LIVE_BUTTON" = "Hide live button";
-"HIDE_SHORTS_LIVE_BUTTON_DESC" = "Hide the Live button at the top of the Shorts player.";
-"HIDE_SHORTS_LENS_BUTTON" = "Hide lens button";
-"HIDE_SHORTS_LENS_BUTTON_DESC" = "Hide the Lens button at the top of the Shorts player.";
-"HIDE_SHORTS_TRENDS_BUTTON" = "Hide trends button";
-"HIDE_SHORTS_TRENDS_BUTTON_DESC" = "Hide the Trends button at the top of the Shorts player.";
-"HIDE_SHORTS_TO_VIDEO" = "Hide link to full video";
-"HIDE_SHORTS_TO_VIDEO_DESC" = "Hide the link that redirects in a Short to a full video in the Shorts player.";
"ENABLES_SHORTS_QUALITY" = "Enable Shorts quality picker";
"ENABLES_SHORTS_QUALITY_DESC" = "Force enable the video quality selection menu in Shorts.";
"SHOW_SHORTS_SEEKBAR" = "Always show seekbar";
"SHOW_SHORTS_SEEKBAR_DESC" = "Ensure the progress/seek bar is always visible at the bottom of the Shorts player.";
+"REMOVE_POSTS_SHORTS" = "Remove posts";
+"REMOVE_POSTS_SHORTS_DESC" = "Remove posts from the feed.";
+"SHORTS_FULLSCREEN" = "Pinch to Fullscreen";
+"SHORTS_FULLSCREEN_DESC" = "Manages visibility of the overlay with pinch in and pinch out gestures, displaying Shorts in fullscreen mode.";
"TABBAR" = "Tab bar";
"DEFAULT_TAB" = "Default startup tab";
-"DEFAULT_TAB_DESC" = "You can select the tab you want to open when opening the app.";
-"HOME_NAME" = "Home";
-"SUBSCRIPT_NAME" = "Subscriptions";
-"LIB_NAME" = "Library (You)";
+"FORSTED_TAB_BAR" = "Translucent tab bar";
+"ENABLED" = "Enabled";
"HIDE_TAB_INDI" = "Hide tab indicators";
"HIDE_TAB_INDI_DESC" = "Hide all tab indicators in the tab bar.";
"HIDE_TAB_LABELS" = "Hide tab labels";
"HIDE_TAB_LABELS_DESC" = "Hide all tab labels in the tab bar.";
-"HIDE_HOME_TAB" = "Hide Home tab";
-"HIDE_HOME_TAB_DESC" = "Hide Home tab in the tab bar.";
-"HIDE_SHORTS_TAB" = "Hide Shorts tab";
-"HIDE_SHORTS_TAB_DESC" = "Hide Shorts tab in the tab bar.";
-"HIDE_CREATE_BUTTON" = "Hide Create button";
-"HIDE_CREATE_BUTTON_DESC" = "Hide Create button in the tab bar.";
-"HIDE_SUBSCRIPT_TAB" = "Hide Subscriptions tab";
-"HIDE_SUBSCRIPT_TAB_DESC" = "Hide Subscriptions tab in the tab bar.";
+"HOME_TAB" = "Home";
+"SHORTS_TAB" = "Shorts";
+"CREATE_TAB" = "Create";
+"SUBSCRIPTIONS_TAB" = "Subscriptions";
+"LIBRARY_TAB" = "Library (You)";
+"HISTORY_TAB" = "History";
+"GAMING_TAB" = "Gaming";
+"SPORTS_TAB" = "Sports";
+"NOTI_TAB" = "Notifications";
+"NEWS_TAB" = "News";
+"MUSIC_TAB" = "Music";
+"WATCH_LATER_TAB" = "Watch later";
+"PLAYLIST_TAB" = "Playlists";
+"LIKE_TAB" = "Liked videos";
+"LIVE_TAB" = "Live";
+"POST_TAB" = "Posts";
+"VIDEO_TAB" = "Your videos";
+"MOVIE_TAB" = "Movies";
+"COURSE_TAB" = "Courses";
+"MINIGAME_TAB" = "Playables";
+"MANAGE_TABS" = "Manage tabs";
+"MANAGE_TABS_DESC" = "Reorder and toggle tabs";
+"TAB_REORDER_HINT" = "Drag to reorder. Toggle to show/hide.\nTab settings are also accessible by long-pressing the first tab in the tab bar.";
+"TAB_LIMIT" = "Tab limit";
+"TAB_LIMIT_DESC" = "Only 6 tabs are allowed.";
+"ZERO_TAB_DESC" = "At least 1 tab has to be enabled.";
"MISCELLANEOUS" = "Miscellaneous";
"BACKGROUND_PLAYBACK" = "Background playback";
"BACKGROUND_PLAYBACK_DESC" = "Enables background playback for videos.";
"DISABLES_SHORTS_PIP" = "Disables Shorts PiP";
"DISABLES_SHORTS_PIP_DESC" = "Try to disables Shorts PiP, this might not 100% disables it.";
+"DISABLE_HINTS" = "Disable hints";
+"DISABLE_HINTS_DESC" = "Disable the tutorial hints and interactive pop-ups.";
"BLOCK_UPGRADE_DIALOGS" = "Block upgrade dialogs";
"BLOCK_UPGRADE_DIALOGS_DESC" = "Block the YouTube dialogs that ask you to upgrade the app.";
"ARE_YOU_THERE_DIALOG" = "Hide 'Are you there?' dialog";
@@ -247,7 +379,109 @@
"DISABLES_SNACK_BAR_DESC" = "Disables snack bar when pressing some buttons or doing something in the app.";
"HIDE_STARTUP_ANIMATIONS" = "Hide YouTube's startup animations";
"HIDE_STARTUP_ANIMATIONS_DESC" = "Hide YouTube's startup animations when opening the app.";
-"HIDE_PLAY_IN_NEXT_QUEUE" = "Hide 'Play next in queue'";
-"HIDE_PLAY_IN_NEXT_QUEUE_DESC" = "Hide the 'Play next in queue' option in the flyout menu.";
"HIDE_LIKE_DISLIKE_VOTES" = "Hide like/dislike votes";
-"HIDE_LIKE_DISLIKE_VOTES_DESC" = "Hide messages that pop up when you like/dislike videos.";
\ No newline at end of file
+"HIDE_LIKE_DISLIKE_VOTES_DESC" = "Hide messages that pop up when you like/dislike videos.";
+"FLYOUT_MENU" = "Flyout menu";
+"REMOVE_PLAY_IN_NEXT_QUEUE_OPTION" = "Removes 'Play next in queue' option";
+"REMOVE_PLAY_IN_NEXT_QUEUE_OPTION_DESC" = "Removes 'Play next in queue' option from the flyout menu.";
+"REMOVE_DOWNLOAD_OPTION" = "Removes 'Download video' option";
+"REMOVE_DOWNLOAD_OPTION_DESC" = "Removes 'Download video' option from the flyout menu.";
+"REMOVE_WATCH_LATER_OPTION" = "Removes 'Save to Watch later' option";
+"REMOVE_WATCH_LATER_OPTION_DESC" = "Removes 'Save to Watch Later' option from the flyout menu.";
+"REMOVE_SAVE_OPTION" = "Removes 'Save to playlist' option";
+"REMOVE_SAVE_OPTION_DESC" = "Removes 'Save to playlist' option from the flyout menu.";
+"REMOVE_REMOVE_FROM_PLAYLIST_OPTION" = "Removes 'Remove from playlist' option";
+"REMOVE_REMOVE_FROM_PLAYLIST_OPTION_DESC" = "Removes 'Remove from playlist' option from the flyout menu.";
+"REMOVE_SHARE_OPTION" = "Removes 'Share' option";
+"REMOVE_SHARE_OPTION_DESC" = "Removes 'Share' option from the flyout menu.";
+"REMOVE_NOT_INTERESTED_OPTION" = "Removes 'Not interested' option";
+"REMOVE_NOT_INTERESTED_OPTION_DESC" = "Removes 'Not interested' option from the flyout menu.";
+"REMOVE_DONT_RECOMMEND_OPTION" = "Removes 'Don't recommend channel' option";
+"REMOVE_DONT_RECOMMEND_OPTION_DESC" = "Removes 'Don't recommend channel' option from the flyout menu.";
+"REMOVE_INFO_OPTION" = "Removes 'Why are you seeing this?' option";
+"REMOVE_INFO_OPTION_DESC" = "Removes 'Why are you seeing this?' option from the flyout menu.";
+"REMOVE_FILTER_OPTION" = "Removes 'Search filters' option";
+"REMOVE_FILTER_OPTION_DESC" = "Removes 'Search filters' option from the flyout menu.";
+"REMOVE_REPORT_OPTION" = "Removes 'Report' option";
+"REMOVE_REPORT_OPTION_DESC" = "Removes 'Report' option from the flyout menu.";
+"REMOVE_YOUTUBE_MUSIC_OPTION" = "Removes 'Listen with YouTube Music' option";
+"REMOVE_YOUTUBE_MUSIC_OPTION_DESC" = "Removes 'Listen with YouTube Music' option from the flyout menu.";
+"REMOVE_FEED_BACK_OPTION" = "Removes 'Send feedback' option";
+"REMOVE_FEED_BACK_OPTION_DESC" = "Removes 'Send feedback' option from the flyout menu.";
+"REMOVE_CAST_OPTION" = "Removes 'Watch on TV' option";
+"REMOVE_CAST_OPTION_DESC" = "Removes 'Watch on TV' option from the flyout menu.";
+"REMOVE_SHUFFLE_OPTION" = "Removes 'Shuffle' option";
+"REMOVE_SHUFFLE_OPTION_DESC" = "Removes 'Shuffle' option from the flyout menu.";
+"REMOVE_UN_SUB_OPTION" = "Removes 'Unsubscribe' option";
+"REMOVE_UN_SUB_OPTION_DESC" = "Removes 'Unsubscribe' option from the flyout menu.";
+"REMOVE_HIDE_FROM_PLAYLIST_OPTION" = "Removes 'Hide video from playlist' option";
+"REMOVE_HIDE_FROM_PLAYLIST_OPTION_DESC" = "Removes 'Hide video from playlist' option from the flyout menu.";
+"REMOVE_HELP_OPTION" = "Removes 'Help & feedback' option";
+"REMOVE_HELP_OPTION_DESC" = "Removes 'Help & feedback' option from the flyout menu.";
+"REMOVE_NOTIFY_OPTION" = "Removes 'Notify me' option";
+"REMOVE_NOTIFY_OPTION_DESC" = "Removes 'Notify me' option from the flyout menu.";
+"REMOVE_CLEARSCREEN_OPTION" = "Removes 'Clear Screen' option";
+"REMOVE_CLEARSCREEN_OPTION_DESC" = "Removes 'Clear Screen' option from the flyout menu.";
+"HIDE_COMMU_GUIDE" = "Hide community guidelines";
+"HIDE_COMMU_GUIDE_DESC" = "Hide community guidelines header.";
+"DISABLES_RTL" = "Disable RTL formatting";
+"DISABLES_RTL_DESC" = "Forcefully displays text in left-to-right (LTR) format for languages that are initially displayed in right-to-left (RTL).";
+"DEVICE_UI" = "Interface style";
+
+"SB_DESCRIPTION" = "Skip sponsored segments automatically using community-submitted data.";
+"SB_ENABLE" = "Enable SponsorBlock";
+"SB_ENABLE_DESC" = "Activate the SponsorBlock extension to skip sponsored segments.";
+"SB_SHOW_BUTTON" = "Show overlay button";
+"SB_SHOW_BUTTON_DESC" = "Display a SponsorBlock toggle button in the player overlay.";
+"SB_SHOW_NOTIFICATIONS" = "Show notifications";
+"SB_SHOW_NOTIFICATIONS_DESC" = "Display notifications when segments are skipped. Notifications for manual-skip categories are always shown.";
+"SB_HAPTIC_FEEDBACK" = "Haptic feedback";
+"SB_HAPTIC_FEEDBACK_DESC" = "Provide haptic feedback when a segment is skipped.";
+"SB_SEGMENTS_IN_PLAYER" = "Segments in player";
+"SB_SEGMENTS_IN_PLAYER_DESC" = "Display colored segments on the progress bar of the main video player.";
+"SB_SEGMENTS_IN_FEED" = "Segments in feed";
+"SB_SEGMENTS_IN_FEED_DESC" = "Display colored segments on the progress bar of players in the feed.";
+"SB_SEGMENTS_IN_MINIPLAYER" = "Segments in mini-player";
+"SB_SEGMENTS_IN_MINIPLAYER_DESC" = "Display colored segments on the progress bar of the mini-player.";
+"SB_SHOW_DURATION" = "Show duration without segments";
+"SB_SHOW_DURATION_DESC" = "Display the total video duration excluding skippable segments.";
+"SB_CATEGORIES_HEADER" = "Segment categories";
+"SB_SEGMENT_COLOR" = "Segment color";
+
+"SB_ACTION_DISABLE" = "Disable";
+"SB_ACTION_AUTO_SKIP" = "Auto-skip";
+"SB_ACTION_ASK" = "Ask before skipping";
+"SB_ACTION_DISPLAY" = "Show on seek bar only";
+"SB_ACTION_SKIP_TO" = "Skip to segment";
+
+"SB_CAT_sponsor" = "Sponsor";
+"SB_CAT_sponsor_DESC" = "Paid promotion for a product or service not directly related to the creator.";
+"SB_CAT_intro" = "Intro";
+"SB_CAT_intro_DESC" = "Intermission or intro animation typically seen at the start of a video.";
+"SB_CAT_outro" = "Endcards";
+"SB_CAT_outro_DESC" = "Endcards, credits, or outros at the end of the video.";
+"SB_CAT_interaction" = "Interaction";
+"SB_CAT_interaction_DESC" = "Reminders to like, subscribe, or follow on social media.";
+"SB_CAT_selfpromo" = "Self-promotion";
+"SB_CAT_selfpromo_DESC" = "Unpaid promotion of the creator's own products, merchandise, or other content.";
+"SB_CAT_music_offtopic" = "Non-music";
+"SB_CAT_music_offtopic_DESC" = "Non-music sections in music videos (e.g. talking, introductions).";
+"SB_CAT_preview" = "Preview";
+"SB_CAT_preview_DESC" = "Recap or preview of content from another video or previous episode.";
+"SB_CAT_hook" = "Hook/Greetings";
+"SB_CAT_hook_DESC" = "Narrated trailers for the upcoming video, greetings and goodbyes.";
+"SB_CAT_poi_highlight" = "Highlight";
+"SB_CAT_poi_highlight_DESC" = "The key moment or highlight point of the video. Use 'Skip to' to jump directly to it.";
+"SB_CAT_filler" = "Filler";
+"SB_CAT_filler_DESC" = "Tangential content, jokes, or filler that is not directly related to the main topic.";
+
+"SB_SKIPPED" = "%@ segment has been skipped";
+"SB_DETECTED" = "%@ segment detected.\nWould you like to skip the segment?";
+"SB_SKIP_NOW" = "Skip";
+"SB_UNSKIP" = "Unskip";
+"SB_JUMPED_TO_HIGHLIGHT" = "Jumped to highlight";
+"SB_JUMP_TO_HIGHLIGHT" = "Highlight available. Jump to the point?";
+"SB_SEGMENT_COLOR_SUFFIX" = "Segment color";
+"SB_SECTION_MAIN" = "Main";
+"SB_SKIP_ALERT_DURATION" = "Skip alert duration";
+"SB_UNSKIP_ALERT_DURATION" = "Unskip alert duration";
\ No newline at end of file
diff --git a/layout/Library/Application Support/YouMod.bundle/hu.lproj/Localizable.strings b/layout/Library/Application Support/YouMod.bundle/hu.lproj/Localizable.strings
index bccaf550..2c1a8a0f 100644
--- a/layout/Library/Application Support/YouMod.bundle/hu.lproj/Localizable.strings
+++ b/layout/Library/Application Support/YouMod.bundle/hu.lproj/Localizable.strings
@@ -1,11 +1,18 @@
-"NOTE" = "NOTE: Some hiding features may leave a blank box.";
"SETTINGS" = "Settings";
+"INTERFACE" = "Interface";
+
+"LOOP_DISABLED" = "Loop disabled";
+"LOOP_ENABLED" = "Loop enabled";
"NEW_ISSUES" = "Report issues or request new features";
"NEW_ISSUES_DESC" = "Found bugs or want to request new features? Tap here.";
"SOURCE_CODES" = "Source codes";
"SOURCE_CODES_DESC" = "Take a look at the codes!";
+"EXTRA" = "Extra";
+"FIX_PLAYBACK_ISSUES" = "Fix playback issues (Temporary)";
+"FIX_PLAYBACK_ISSUES_DESC" = "Try to fix video playback issues for some users. Turning this on will make the player automatically reloads itself everytime it errors or fails to play the video.";
+
"PERFER_HEADER" = "YouMod perferences";
"PERFER" = "Perferences";
"IMPORT" = "Import settings";
@@ -24,6 +31,7 @@
"ERROR_NO_KEYS_IMPORT" = "No available keys to import";
"ERROR_NO_KEYS_EXPORT" = "No available keys to export";
"OVERRIDE" = "This will override the current settings. Do you want to continue?";
+"DISABLED" = "Disabled";
"CACHE" = "Cache management";
"CLEARCACHE" = "Clear cache";
@@ -32,12 +40,80 @@
"AUTO_CLEARCACHE_DESC" = "Automatically clears the app cache on startup.";
"DOWNLOADING" = "Downloading";
-"DOWNLOAD_MANAGER" = "Download Manager";
-"DOWNLOAD_MANAGER_DESC" = "Replaces YouTube's video downloader with YouMod one. With ablity to downloading videos, audios, captions, and thumbnails";
+"DOWNLOAD_MANAGER" = "Download manager";
+"DOWNLOAD_MANAGER_DESC" = "Replaces YouTube's video downloader with YouMod one. With ablity to downloading videos, audios, captions, and thumbnails.";
"DOWNLOAD_SAVE_PHOTOS" = "Automatically save to Photos";
"DOWNLOAD_SAVE_PHOTOS_DESC" = "Automatically saves downloaded videos and images directly to your Photos app.";
-"DOWNLOAD_DRC_AUDIO" = "Prefer DRC Audio";
-"DOWNLOAD_DRC_AUDIO_DESC" = "Prioritizes downloading audio tracks with Dynamic Range Compression for more consistent volume levels.";
+"ADD_SHORTS_DOWNLOAD" = "Adds a download button to Shorts";
+"ADD_SHORTS_DOWNLOAD_DESC" = "Adds a download button to Shorts video overlay.";
+"USE_ORIG_AUDIO" = "Automatically uses original audio track";
+"USE_ORIG_AUDIO_DESC" = "Automatically uses original audio track instead of showing a dialog.";
+"DOWNLOAD_SHORTS" = "Download Shorts";
+"DOWNLOAD_SHORTS_DESC" = "Choose Shorts quality";
+"COPY_VID_INFO" = "Copy video informations";
+"COPY_VID_INFO_DESC" = "Choose what to copy";
+"COPY_ALL_VID_INFO" = "Copy all";
+"COPY_ALL_VID_INFO_DESC" = "Copy all video informations";
+"COPY_AUTHOR" = "Copy author";
+"COPY_AUTHOR_DESC" = "Copy the video author";
+"COPY_TITLE" = "Copy title";
+"COPY_TITLE_DESC" = "Copy the video title";
+"COPY_DESCRIPTION" = "Copy description";
+"COPY_DESCRIPTION_DESC" = "Copy the video description";
+"COPY_URL" = "Copy video link";
+"COPY_URL_TIMESTAMP" = "Copy video link with timestamp";
+"URL_COPIED" = "Copied video link";
+"URL_TIMESTAMP_COPIED" = "Copied video link with timestamp";
+"ERROR_VIDEOID" = "Cannot get the video ID";
+"ERROR_ADS" = "Cannot copy video link while playing ads";
+"COPIED_AUTHOR" = "Copied video author";
+"COPIED_TITLE" = "Copied video title";
+"COPIED_DESCRIPTION" = "Copied video description";
+"SAVE_THUMBNAIL" = "Save thumbnail";
+"SAVE_THUMBNAIL_DESC" = "Save to Photos";
+"DOWNLOAD_CAPTIONS" = "Download captions";
+"DOWNLOAD_CAPTIONS_DESC" = "Save subtitles as .VTT file";
+"DOWNLOAD_AUDIO" = "Download audio";
+"DOWNLOAD_AUDIO_DESC" = "Download audio as .m4a file";
+"DOWNLOAD_VIDEO" = "Download video";
+"DOWNLOAD_VIDEO_DESC" = "Choose video quality";
+"OPEN_VID_BEFORE" = "Open a video before using the download manager";
+"NO_CAPTIONS_URL" = "No valid caption URLs found";
+"NO_AUDIO_URL" = "No audio URL found";
+"NO_STREAM_URL" = "No stream URL found";
+"CAPTIONS_FAILED" = "Failed to download captions";
+"NO_CAPTIONS" = "No captions available for this video";
+"NO_AUDIO_STREAM_FOUND" = "No audio streams found";
+"NO_VID_AUDIO_STREAM_FOUND" = "No video/audio streams found";
+"COPIED_VID_INFO" = "Copied all video informations";
+"SAVED_TO_PHOTOS" = "Saved to Photos";
+"SAVE_FAILED" = "Save failed";
+"PHOTO_ACCESS_DENINED" = "Photos access denied";
+"THUMBNAIL_FAILED" = "Thumbnail failed";
+"DOWNLOADING_THUMBNAIL" = "Downloading thumbnail...";
+"DOWNLOADING_AUDIO" = "Downloading audio...";
+"DOWNLOADING_VIDEO" = "Downloading video...";
+"DOWNLOADING_CAPTIONS" = "Downloading captions...";
+"NO_THUMBNAIL_FOUND" = "No thumbnail found";
+"DOWNLOAD_FAILED" = "Download failed";
+"COPY_THUMBNAIL" = "Copy thumbnail";
+"COPY_THUMBNAIL_DESC" = "Copy to clipboard";
+"SHOW_THUMBNAIL" = "Show thumbnail";
+"SHOW_THUMBNAIL_DESC" = "Show in a dialog";
+"THUMBNAIL_OPTIONS" = "Download thumbnail";
+"THUMBNAIL_OPTIONS_DESC" = "Show more options";
+"COPIED_TO_CLIPBOARD" = "Copied to clipboard";
+"DOWNLOAD_COMPLETED" = "Download completed";
+"CANNOT_SAVE_TO_PHOTOS" = "Cannot save to Photos";
+"FINA_VIDEO" = "Finalizing video...";
+"MERGING_VID_FALLBACK" = "Merging video with fallback...";
+"MERGING_VID" = "Merging video...";
+"CONVERTING" = "Converting to";
+"ALREADY_DOWNLOADING" = "Already downloading";
+"DOWNLOAD_CANCELLED" = "Download cancelled";
+"CANCEL" = "Cancel";
+"TRIMMING_AUDIO" = "Trimming audio...";
+"COPY" = "Copy";
"APPEARANCE" = "Appearance";
"OLED_THEME" = "OLED dark mode";
@@ -46,6 +122,8 @@
"OLED_KEYBOARD_DESC" = "Make the keyboard uses a pure black (OLED) color for dark theme.";
"NAVBAR" = "Navigation bar";
+"STICKY_NAVBAR" = "Sticky Navigation bar";
+"STICKY_NAVBAR_DESC" = "Pins the Navigation bar so that it remains visible when scrolling down.";
"HIDE_YT_LOGO" = "Hide YouTube logo";
"HIDE_YT_LOGO_DESC" = "Hide YouTube logo in the navigation bar.";
"PREMIUM_LOGO" = "Use YouTube Premium logo";
@@ -62,34 +140,78 @@
"FEED" = "Feed";
"HIDE_SUBBAR" = "Hide subbar";
"HIDE_SUBBAR_DESC" = "Hide subbar at the top of the feed.";
+"HIDE_HORI_SHELF" = "Remove horizontal feeds";
+"HIDE_HORI_SHELF_DESC" = "Removes all horizontal feeds (News, Continue Watching, Posts collection, etc.) from the feed, except Playables.";
"HIDE_MUSIC_PLAYLISTS" = "Hide music playlist generator";
"HIDE_MUSIC_PLAYLISTS_DESC" = "Hide music playlist generator (Select music tunes in three) in the feed.";
"HIDE_FEED_POST" = "Hide posts";
"HIDE_FEED_POST_DESC" = "Hide posts in feed.";
+"HIDE_SURVEYS" = "Hide surveys";
+"HIDE_SURVEYS_DESC" = "Hide video surveys that YouTube shows sometimes.";
"HIDE_SHORTS_SHELF" = "Hide Shorts shelf";
"HIDE_SHORTS_SHELF_DESC" = "Hide Shorts shelf in the feed, including in the Subscriptions tab.";
+"KEEP_SHORTS_SUBSCRIPT" = "Keep Shorts in Subscriptions";
+"KEEP_SHORTS_SUBSCRIPT_DESC" = "Prevents removing Shorts from Subscriptions tab.";
"HIDE_SEARCH_HISTORY" = "Hide search history";
"HIDE_SEARCH_HISTORY_DESC" = "Hide previous search history and suggestions when using the search bar.\nNOTE: Your search history will still be accessible in your other YouTube clients.";
-"HIDE_SUB_BUTTON" = "Hide Subscribe button";
-"HIDE_SUB_BUTTON_DESC" = "Hides the subscribe button in channels and video descriptions.";
-"HIDE_SHOP_BUTTON" = "Hide Shopping button";
-"HIDE_SHOP_BUTTON_DESC" = "Hides the shopping/store button in channels descriptions.";
-"HIDE_MEMBER_BUTTON" = "Hide Membership button";
-"HIDE_MEMBER_BUTTON_DESC" = "Hides the 'Join' button in channels and video descriptions.";
+"HIDE_PLAYABLES" = "Remove playables";
+"HIDE_PLAYABLES_DESC" = "Removes playables (mini-games) from the feed.";
"PLAYER" = "Player";
+"PLAYBACK_SPEED" = "Playback speed";
+"CONTROL_CENTER" = "Control Center";
+"PLAYER_ACTIONS" = "Player actions";
+"GESTURE_HEADER" = "Gestures";
+"QUALITY_WIFI" = "Playback quality on WiFi";
+"QUALITY_WIFI_DESC" = "Automatically applies the selected quality when streaming over Wi-Fi.";
+"QUALITY_CELLULAR" = "Playback quality on Cellular";
+"QUALITY_CELLULAR_DESC" = "Automatically applies the selected quality when streaming over mobile data.";
+"QUALITY_LOW_POWER" = "Playback quality on Power saver mode";
+"QUALITY_LOW_POWER_DESC" = "Automatically applies the selected quality when when Low power mode is enabled on your device.";
+"DEFAULT" = "Default";
+"BEST" = "Best";
+"AUDIO_TRACK" = "Preferred audio track";
+"ORIGINAL" = "Original";
+"SELECT_MANUALLY" = "Select manually";
+"AUDIO_TRACK_SELECT" = "Selected audio track";
+"AUDIO_TRACK_SELECT_DESC" = "Specifies the preferred audio track language to use for playback, if available.";
+"NO_AUTO_DUBBED" = "Uses original audio track";
+"NO_AUTO_DUBBED_DESC" = "Uses original audio track if the preferred language track is auto-dubbed or not found.";
+"CAPTION_TRACK" = "Preferred captions";
+"CAPTION_TRACK_SELECT" = "Selected captions";
+"CAPTION_TRACK_SELECT_DESC" = "Specifies the preferred captions language to display, if available.";
+"DISABLES_CAPTION_TRACK" = "Automatically disable captions";
+"DISABLES_CAPTION_TRACK_DESC" = "Automatically disable captions if the preferred language is auto-generated or not found.";
+"DEFAULT_SPEED" = "Default playback rate";
+"DEFAULT_SPEED_DESC" = "Overrides the default playback speed.";
+"SPEED_BUTTON" = "Speed controls button";
+"SPEED_BUTTON_DESC" = "Displays a speed controls button on overlay.";
+"MUTE_BUTTON" = "Mute button";
+"MUTE_BUTTON_DESC" = "Displays a button to mute or unmute the volume on the overlay.";
+"SHARE_BUTTON" = "Share button";
+"SHARE_BUTTON_DESC" = "Displays a button to share the video link on the overlay.";
+"LOOP_BUTTON" = "Loop button";
+"LOOP_BUTTON_DESC" = "Displays a button to loop the video on the overlay.";
+"CAPTION_BUTTON" = "Captions button";
+"CAPTION_BUTTON_DESC" = "Displays a button to copy the video captions on the overlay.";
+"HOLD_TO_SPEED" = "Hold to speed";
+"HOLD_TO_SPEED_DESC" = "Uses custom hold to speed playback rate.";
"HIDE_AUTOPLAY" = "Hide autoplay switch";
"HIDE_AUTOPLAY_DESC" = "Hide autoplay switch in the video overlay.";
"HIDE_CAPTIONS_BUTTON" = "Hide captions button";
"HIDE_CAPTIONS_BUTTON_DESC" = "Hide captions button in the video overlay.";
"HIDE_CAST_BUTTON_PLAYER" = "Hide cast button";
"HIDE_CAST_BUTTON_PLAYER_DESC" = "Hide cast button in the video overlay.";
-"HIDE_PREV_BUTTON" = "Hide previous/back button";
-"HIDE_PREV_BUTTON_DESC" = "Hide previous/back button in the video overlay.";
-"HIDE_NEXT_BUTTON" = "Hide next/skip button";
-"HIDE_NEXT_BUTTON_DESC" = "Hide next/skip button in the video overlay.";
+"HIDE_NEXT_AND_PREV_BUTTON" = "Hide previous and next buttons";
+"HIDE_NEXT_AND_PREV_BUTTON_DESC" = "Hide previous and next buttons in the video overlay.";
"REPLACE_PREVNEXT_BUTTONS" = "Replace Previous/Next buttons";
"REPLACE_PREVNEXT_BUTTONS_DESC" = "Replaces the Previous and Next video buttons in the player with Rewind and Fast Forward buttons.";
+"SKIP_BACKWARD" = "Skip backward";
+"SKIP_BACKWARD_DESC" = "Replaces the previous track button in Control Center with a skip backward button by a set number of seconds.";
+"SKIP_FORWARD" = "Skip forward";
+"SKIP_FORWARD_DESC" = "Replaces the next track button in Control Center with a skip forward button by a set number of seconds.";
+"REWIND_SECONDS" = "Rewind seconds";
+"FORWARD_SECONDS" = "Fast-forward seconds";
"REMOVE_DARK_OVERLAY" = "Remove dark overlay";
"REMOVE_DARK_OVERLAY_DESC" = "Remove dark overlay from the video overlay.";
"HIDE_END_SCREEN" = "Hide endscreen cards";
@@ -102,6 +224,12 @@
"HIDE_PAID_OVERLAY_DESC" = "Hide 'Includes paid promotion' in the video overlay.";
"HIDE_WATERMARK" = "Hide watermark";
"HIDE_WATERMARK_DESC" = "Hide channel watermark at the bottom of the video overlay.";
+"PAUSE_ON_OVERLAY" = "Pause on overlay";
+"PAUSE_ON_OVERLAY_DESC" = "Sets playback on pause if overlay appears.";
+"DISABLES_SNAP_TO_CHAPTER" = "Disable snap to chapters";
+"DISABLES_SNAP_TO_CHAPTER_DESC" = "Prevents the scrubbar stick to the chapters.";
+"DISABLES_ENGAGE_PANEL" = "Disables engagement panel in fullscreen";
+"DISABLES_ENGAGE_PANEL_DESC" = "Prevent engagement panel showing in fullscreen.";
"GESTURES" = "Enable gestures controls";
"GESTURES_DESC" = "Swipe up or down on the left/right edges of the video screen to adjust brightness, volume, or playback speed.";
"GESTURE_AREA" = "Gesture activation area";
@@ -136,6 +264,8 @@
"DISABLES_SHOW_REMAINING_DESC" = "Disable the feature that shows how much time is left in a video when tapping the timestamp.";
"ALWAYS_SHOW_REMAINING" = "Always show remaining time";
"ALWAYS_SHOW_REMAINING_DESC" = "Ensure the remaining time is always displayed by default in the player overlay.";
+"COPY_TIMESTAMP_ON_PAUSE" = "Copy timestamped links";
+"COPY_TIMESTAMP_ON_PAUSE_DESC" = "Allows to copy timestamped link to the clipboard by pressing pause button.";
"SHOW_REMAINING_EXTRA" = "Show extra remaining time";
"SHOW_REMAINING_EXTRA_DESC" = "Adds the estimated finish time to the label in the video overlay.";
"HIDE_FULLSCREEN_ACTIONS" = "Hide fullscreen actions";
@@ -148,93 +278,95 @@
"HIDE_CONTENT_WARNING_DESC" = "Automatically skip the sensitive content warning alert in some vidoes.";
"AUTO_FULLSCREEN" = "Auto fullscreen";
"AUTO_FULLSCREEN_DESC" = "Automatically enter fullscreen mode when starting video playback.";
-"AUTO_DISABLES_CAPTION" = "Auto disable captions";
-"AUTO_DISABLES_CAPTION_DESC" = "Automatically turns off closed captions (CC) whenever a new video starts playing.";
"PORTRAIT_FULLSCREEN" = "Portrait fullscren mode";
"PORTRAIT_FULLSCREEN_DESC" = "Enables portrait fullscreen mode support.";
"OLD_QUALITY_PICKER" = "Use old quality picker";
"OLD_QUALITY_PICKER_DESC" = "Reverts the video quality selection menu to the classic version (lists all resolutions immediately).";
"EXTRA_SPEED" = "Extra speed options";
"EXTRA_SPEED_DESC" = "Adds more video playback speed options up to 10x.";
-"DISABLE_HINTS" = "Disable hints";
-"DISABLE_HINTS_DESC" = "Disable the tutorial hints and interactive pop-ups.";
"FORCE_MINIPLAYER" = "Force miniplayer";
"FORCE_MINIPLAYER_DESC" = "Allows all videos, including those made for kids, to play in the miniplayer when dismissing the player.";
"FORCE_SEEKBAR" = "Always show progress bar";
"FORCE_SEEKBAR_DESC" = "Makes the progress bar (seekbar) remains visible at all times while in fullscreen mode.";
-"HIDE_LIKE_BUTTON" = "Hide Like button";
-"HIDE_LIKE_BUTTON_DESC" = "Hide Like button in the action bar under the video overlay.";
-"HIDE_DISLIKE_BUTTON" = "Hide Dislike button";
-"HIDE_DISLIKE_BUTTON_DESC" = "Hide Dislike button in the action bar under the video overlay.";
-"HIDE_SHARE_BUTTON" = "Hide Share button";
-"HIDE_SHARE_BUTTON_DESC" = "Hide Share button in the action bar under the video overlay.";
-"HIDE_DOWNLOAD_BUTTON" = "Hide Download button";
-"HIDE_DOWNLOAD_BUTTON_DESC" = "Hide Download button in the action bar under the video overlay.";
-"HIDE_CLIP_BUTTON" = "Hide Clip button";
-"HIDE_CLIP_BUTTON_DESC" = "Hide Clip button in the action bar under the video overlay.";
-"HIDE_REMIX_BUTTON" = "Hide Remix button";
-"HIDE_REMIX_BUTTON_DESC" = "Hide Remix button in the action bar under the video overlay.";
-"HIDE_SAVE_BUTTON" = "Hide Save button";
-"HIDE_SAVE_BUTTON_DESC" = "Hide Save button in the action bar under the video overlay.";
+"TAP_TO_SEEK" = "Tap to seek";
+"TAP_TO_SEEK_DESC" = "Quickly jump to any point by tapping the progress bar.";
+"DISABLES_ZOOM" = "Disable free zoom gesture";
+"DISABLES_ZOOM_DESC" = "Disables new free zoom gestures.";
+"USES_24_HOURS_TIME" = "24-hour format";
+"USES_24_HOURS_TIME_DESC" = "Shows end time in 24-hour format.";
+"PAUSE_TWO_FINGERS" = "Pause using two fingers";
+"PAUSE_TWO_FINGERS_DESC" = "Pause/Resume playback by tapping on the screen using two fingers.";
+"HIDE_COMMENTS_SECTION" = "Hide comments section";
+"HIDE_COMMENTS_SECTION_DESC" = "Completely removes the entire comments section.";
+"HIDE_COMMENTS_PREVIEW" = "Hide preview comments";
+"HIDE_COMMENTS_PREVIEW_DESC" = "Removes the preview box for the top comment while keeping the full section expandable.";
+"USE_ANOTHER_MINIPLAYER" = "Uses another miniplayer style";
+"USE_ANOTHER_MINIPLAYER_DESC" = "Uses another miniplayer style redesigned from older YT versions.";
"SHORTS" = "Shorts";
-"HIDE_SHORTS_LIKE_BUTTON" = "Hide Like button";
-"HIDE_SHORTS_LIKE_BUTTON_DESC" = "Hide the Like button in the Shorts player.";
-"HIDE_SHORTS_DISLIKE_BUTTON" = "Hide Dislike button";
-"HIDE_SHORTS_DISLIKE_BUTTON_DESC" = "Hide the Dislike button in the Shorts player.";
-"HIDE_SHORTS_COMMENT_BUTTON" = "Hide Comment button";
-"HIDE_SHORTS_COMMENT_BUTTON_DESC" = "Hide the Comment button in the Shorts player.";
-"HIDE_SHORTS_SHARE_BUTTON" = "Hide Share button";
-"HIDE_SHORTS_SHARE_BUTTON_DESC" = "Hide the Share button in the Shorts player.";
-"HIDE_SHORTS_REMIX_BUTTON" = "Hide Remix button";
-"HIDE_SHORTS_REMIX_BUTTON_DESC" = "Hide the Remix button in the Shorts player.";
-"HIDE_METADATA_BUTTON" = "Hide sound metadata button";
-"HIDE_METADATA_BUTTON_DESC" = "Hide the button that shows the sound source or music metadata in the Shorts player.";
+"SHORTS_ONLY" = "Shorts only mode";
+"SHORTS_ONLY_DESC" = "Limits YouTube functionality to viewing Shorts only.";
+"SHORTS_ACTION" = "Shorts action after the video ends";
+"LOOP" = "Loop video";
+"SKIP_TO_NEXT_SHORTS" = "Skip to next video";
+"PAUSE_SHORTS" = "Stop video";
+"REMOVE_LIVE_SHORTS" = "Remove live videos";
+"REMOVE_LIVE_SHORTS_DESC" = "Removes live videos from the feed.";
"HIDE_SHORTS_PRODUCT" = "Hide products";
"HIDE_SHORTS_PRODUCT_DESC" = "Hide tagged products and shopping links in the Shorts overlay.";
"HIDE_SHORTS_RECBAR" = "Hide recommendation bar";
"HIDE_SHORTS_RECBAR_DESC" = "Hide the suggested action bar in the Shorts player (e.g. Try this sound, Shopping, Preview comment).";
-"HIDE_SHORTS_COMMIT" = "Hide commissions bar";
-"HIDE_SHORTS_COMMIT_DESC" = "Hide the bar that shows the video got commissions in the Shorts player.";
-"HIDE_SHORTS_SUBSCRIPT_BUTTON" = "Hide subscriptions button";
-"HIDE_SHORTS_SUBSCRIPT_BUTTON_DESC" = "Hide the Subscriptions button at the top of the Shorts player.";
-"HIDE_SHORTS_LIVE_BUTTON" = "Hide live button";
-"HIDE_SHORTS_LIVE_BUTTON_DESC" = "Hide the Live button at the top of the Shorts player.";
-"HIDE_SHORTS_LENS_BUTTON" = "Hide lens button";
-"HIDE_SHORTS_LENS_BUTTON_DESC" = "Hide the Lens button at the top of the Shorts player.";
-"HIDE_SHORTS_TRENDS_BUTTON" = "Hide trends button";
-"HIDE_SHORTS_TRENDS_BUTTON_DESC" = "Hide the Trends button at the top of the Shorts player.";
-"HIDE_SHORTS_TO_VIDEO" = "Hide link to full video";
-"HIDE_SHORTS_TO_VIDEO_DESC" = "Hide the link that redirects in a Short to a full video in the Shorts player.";
"ENABLES_SHORTS_QUALITY" = "Enable Shorts quality picker";
"ENABLES_SHORTS_QUALITY_DESC" = "Force enable the video quality selection menu in Shorts.";
"SHOW_SHORTS_SEEKBAR" = "Always show seekbar";
"SHOW_SHORTS_SEEKBAR_DESC" = "Ensure the progress/seek bar is always visible at the bottom of the Shorts player.";
+"REMOVE_POSTS_SHORTS" = "Remove posts";
+"REMOVE_POSTS_SHORTS_DESC" = "Remove posts from the feed.";
+"SHORTS_FULLSCREEN" = "Pinch to Fullscreen";
+"SHORTS_FULLSCREEN_DESC" = "Manages visibility of the overlay with pinch in and pinch out gestures, displaying Shorts in fullscreen mode.";
"TABBAR" = "Tab bar";
"DEFAULT_TAB" = "Default startup tab";
-"DEFAULT_TAB_DESC" = "You can select the tab you want to open when opening the app.";
-"HOME_NAME" = "Home";
-"SUBSCRIPT_NAME" = "Subscriptions";
-"LIB_NAME" = "Library (You)";
+"FORSTED_TAB_BAR" = "Translucent tab bar";
+"ENABLED" = "Enabled";
"HIDE_TAB_INDI" = "Hide tab indicators";
"HIDE_TAB_INDI_DESC" = "Hide all tab indicators in the tab bar.";
"HIDE_TAB_LABELS" = "Hide tab labels";
"HIDE_TAB_LABELS_DESC" = "Hide all tab labels in the tab bar.";
-"HIDE_HOME_TAB" = "Hide Home tab";
-"HIDE_HOME_TAB_DESC" = "Hide Home tab in the tab bar.";
-"HIDE_SHORTS_TAB" = "Hide Shorts tab";
-"HIDE_SHORTS_TAB_DESC" = "Hide Shorts tab in the tab bar.";
-"HIDE_CREATE_BUTTON" = "Hide Create button";
-"HIDE_CREATE_BUTTON_DESC" = "Hide Create button in the tab bar.";
-"HIDE_SUBSCRIPT_TAB" = "Hide Subscriptions tab";
-"HIDE_SUBSCRIPT_TAB_DESC" = "Hide Subscriptions tab in the tab bar.";
+"HOME_TAB" = "Home";
+"SHORTS_TAB" = "Shorts";
+"CREATE_TAB" = "Create";
+"SUBSCRIPTIONS_TAB" = "Subscriptions";
+"LIBRARY_TAB" = "Library (You)";
+"HISTORY_TAB" = "History";
+"GAMING_TAB" = "Gaming";
+"SPORTS_TAB" = "Sports";
+"NOTI_TAB" = "Notifications";
+"NEWS_TAB" = "News";
+"MUSIC_TAB" = "Music";
+"WATCH_LATER_TAB" = "Watch later";
+"PLAYLIST_TAB" = "Playlists";
+"LIKE_TAB" = "Liked videos";
+"LIVE_TAB" = "Live";
+"POST_TAB" = "Posts";
+"VIDEO_TAB" = "Your videos";
+"MOVIE_TAB" = "Movies";
+"COURSE_TAB" = "Courses";
+"MINIGAME_TAB" = "Playables";
+"MANAGE_TABS" = "Manage tabs";
+"MANAGE_TABS_DESC" = "Reorder and toggle tabs";
+"TAB_REORDER_HINT" = "Drag to reorder. Toggle to show/hide.\nTab settings are also accessible by long-pressing the first tab in the tab bar.";
+"TAB_LIMIT" = "Tab limit";
+"TAB_LIMIT_DESC" = "Only 6 tabs are allowed.";
+"ZERO_TAB_DESC" = "At least 1 tab has to be enabled.";
"MISCELLANEOUS" = "Miscellaneous";
"BACKGROUND_PLAYBACK" = "Background playback";
"BACKGROUND_PLAYBACK_DESC" = "Enables background playback for videos.";
"DISABLES_SHORTS_PIP" = "Disables Shorts PiP";
"DISABLES_SHORTS_PIP_DESC" = "Try to disables Shorts PiP, this might not 100% disables it.";
+"DISABLE_HINTS" = "Disable hints";
+"DISABLE_HINTS_DESC" = "Disable the tutorial hints and interactive pop-ups.";
"BLOCK_UPGRADE_DIALOGS" = "Block upgrade dialogs";
"BLOCK_UPGRADE_DIALOGS_DESC" = "Block the YouTube dialogs that ask you to upgrade the app.";
"ARE_YOU_THERE_DIALOG" = "Hide 'Are you there?' dialog";
@@ -247,7 +379,109 @@
"DISABLES_SNACK_BAR_DESC" = "Disables snack bar when pressing some buttons or doing something in the app.";
"HIDE_STARTUP_ANIMATIONS" = "Hide YouTube's startup animations";
"HIDE_STARTUP_ANIMATIONS_DESC" = "Hide YouTube's startup animations when opening the app.";
-"HIDE_PLAY_IN_NEXT_QUEUE" = "Hide 'Play next in queue'";
-"HIDE_PLAY_IN_NEXT_QUEUE_DESC" = "Hide the 'Play next in queue' option in the flyout menu.";
"HIDE_LIKE_DISLIKE_VOTES" = "Hide like/dislike votes";
-"HIDE_LIKE_DISLIKE_VOTES_DESC" = "Hide messages that pop up when you like/dislike videos.";
\ No newline at end of file
+"HIDE_LIKE_DISLIKE_VOTES_DESC" = "Hide messages that pop up when you like/dislike videos.";
+"FLYOUT_MENU" = "Flyout menu";
+"REMOVE_PLAY_IN_NEXT_QUEUE_OPTION" = "Removes 'Play next in queue' option";
+"REMOVE_PLAY_IN_NEXT_QUEUE_OPTION_DESC" = "Removes 'Play next in queue' option from the flyout menu.";
+"REMOVE_DOWNLOAD_OPTION" = "Removes 'Download video' option";
+"REMOVE_DOWNLOAD_OPTION_DESC" = "Removes 'Download video' option from the flyout menu.";
+"REMOVE_WATCH_LATER_OPTION" = "Removes 'Save to Watch later' option";
+"REMOVE_WATCH_LATER_OPTION_DESC" = "Removes 'Save to Watch Later' option from the flyout menu.";
+"REMOVE_SAVE_OPTION" = "Removes 'Save to playlist' option";
+"REMOVE_SAVE_OPTION_DESC" = "Removes 'Save to playlist' option from the flyout menu.";
+"REMOVE_REMOVE_FROM_PLAYLIST_OPTION" = "Removes 'Remove from playlist' option";
+"REMOVE_REMOVE_FROM_PLAYLIST_OPTION_DESC" = "Removes 'Remove from playlist' option from the flyout menu.";
+"REMOVE_SHARE_OPTION" = "Removes 'Share' option";
+"REMOVE_SHARE_OPTION_DESC" = "Removes 'Share' option from the flyout menu.";
+"REMOVE_NOT_INTERESTED_OPTION" = "Removes 'Not interested' option";
+"REMOVE_NOT_INTERESTED_OPTION_DESC" = "Removes 'Not interested' option from the flyout menu.";
+"REMOVE_DONT_RECOMMEND_OPTION" = "Removes 'Don't recommend channel' option";
+"REMOVE_DONT_RECOMMEND_OPTION_DESC" = "Removes 'Don't recommend channel' option from the flyout menu.";
+"REMOVE_INFO_OPTION" = "Removes 'Why are you seeing this?' option";
+"REMOVE_INFO_OPTION_DESC" = "Removes 'Why are you seeing this?' option from the flyout menu.";
+"REMOVE_FILTER_OPTION" = "Removes 'Search filters' option";
+"REMOVE_FILTER_OPTION_DESC" = "Removes 'Search filters' option from the flyout menu.";
+"REMOVE_REPORT_OPTION" = "Removes 'Report' option";
+"REMOVE_REPORT_OPTION_DESC" = "Removes 'Report' option from the flyout menu.";
+"REMOVE_YOUTUBE_MUSIC_OPTION" = "Removes 'Listen with YouTube Music' option";
+"REMOVE_YOUTUBE_MUSIC_OPTION_DESC" = "Removes 'Listen with YouTube Music' option from the flyout menu.";
+"REMOVE_FEED_BACK_OPTION" = "Removes 'Send feedback' option";
+"REMOVE_FEED_BACK_OPTION_DESC" = "Removes 'Send feedback' option from the flyout menu.";
+"REMOVE_CAST_OPTION" = "Removes 'Watch on TV' option";
+"REMOVE_CAST_OPTION_DESC" = "Removes 'Watch on TV' option from the flyout menu.";
+"REMOVE_SHUFFLE_OPTION" = "Removes 'Shuffle' option";
+"REMOVE_SHUFFLE_OPTION_DESC" = "Removes 'Shuffle' option from the flyout menu.";
+"REMOVE_UN_SUB_OPTION" = "Removes 'Unsubscribe' option";
+"REMOVE_UN_SUB_OPTION_DESC" = "Removes 'Unsubscribe' option from the flyout menu.";
+"REMOVE_HIDE_FROM_PLAYLIST_OPTION" = "Removes 'Hide video from playlist' option";
+"REMOVE_HIDE_FROM_PLAYLIST_OPTION_DESC" = "Removes 'Hide video from playlist' option from the flyout menu.";
+"REMOVE_HELP_OPTION" = "Removes 'Help & feedback' option";
+"REMOVE_HELP_OPTION_DESC" = "Removes 'Help & feedback' option from the flyout menu.";
+"REMOVE_NOTIFY_OPTION" = "Removes 'Notify me' option";
+"REMOVE_NOTIFY_OPTION_DESC" = "Removes 'Notify me' option from the flyout menu.";
+"REMOVE_CLEARSCREEN_OPTION" = "Removes 'Clear Screen' option";
+"REMOVE_CLEARSCREEN_OPTION_DESC" = "Removes 'Clear Screen' option from the flyout menu.";
+"HIDE_COMMU_GUIDE" = "Hide community guidelines";
+"HIDE_COMMU_GUIDE_DESC" = "Hide community guidelines header.";
+"DISABLES_RTL" = "Disable RTL formatting";
+"DISABLES_RTL_DESC" = "Forcefully displays text in left-to-right (LTR) format for languages that are initially displayed in right-to-left (RTL).";
+"DEVICE_UI" = "Interface style";
+
+"SB_DESCRIPTION" = "Skip sponsored segments automatically using community-submitted data.";
+"SB_ENABLE" = "Enable SponsorBlock";
+"SB_ENABLE_DESC" = "Activate the SponsorBlock extension to skip sponsored segments.";
+"SB_SHOW_BUTTON" = "Show overlay button";
+"SB_SHOW_BUTTON_DESC" = "Display a SponsorBlock toggle button in the player overlay.";
+"SB_SHOW_NOTIFICATIONS" = "Show notifications";
+"SB_SHOW_NOTIFICATIONS_DESC" = "Display notifications when segments are skipped. Notifications for manual-skip categories are always shown.";
+"SB_HAPTIC_FEEDBACK" = "Haptic feedback";
+"SB_HAPTIC_FEEDBACK_DESC" = "Provide haptic feedback when a segment is skipped.";
+"SB_SEGMENTS_IN_PLAYER" = "Segments in player";
+"SB_SEGMENTS_IN_PLAYER_DESC" = "Display colored segments on the progress bar of the main video player.";
+"SB_SEGMENTS_IN_FEED" = "Segments in feed";
+"SB_SEGMENTS_IN_FEED_DESC" = "Display colored segments on the progress bar of players in the feed.";
+"SB_SEGMENTS_IN_MINIPLAYER" = "Segments in mini-player";
+"SB_SEGMENTS_IN_MINIPLAYER_DESC" = "Display colored segments on the progress bar of the mini-player.";
+"SB_SHOW_DURATION" = "Show duration without segments";
+"SB_SHOW_DURATION_DESC" = "Display the total video duration excluding skippable segments.";
+"SB_CATEGORIES_HEADER" = "Segment categories";
+"SB_SEGMENT_COLOR" = "Segment color";
+
+"SB_ACTION_DISABLE" = "Disable";
+"SB_ACTION_AUTO_SKIP" = "Auto-skip";
+"SB_ACTION_ASK" = "Ask before skipping";
+"SB_ACTION_DISPLAY" = "Show on seek bar only";
+"SB_ACTION_SKIP_TO" = "Skip to segment";
+
+"SB_CAT_sponsor" = "Sponsor";
+"SB_CAT_sponsor_DESC" = "Paid promotion for a product or service not directly related to the creator.";
+"SB_CAT_intro" = "Intro";
+"SB_CAT_intro_DESC" = "Intermission or intro animation typically seen at the start of a video.";
+"SB_CAT_outro" = "Endcards";
+"SB_CAT_outro_DESC" = "Endcards, credits, or outros at the end of the video.";
+"SB_CAT_interaction" = "Interaction";
+"SB_CAT_interaction_DESC" = "Reminders to like, subscribe, or follow on social media.";
+"SB_CAT_selfpromo" = "Self-promotion";
+"SB_CAT_selfpromo_DESC" = "Unpaid promotion of the creator's own products, merchandise, or other content.";
+"SB_CAT_music_offtopic" = "Non-music";
+"SB_CAT_music_offtopic_DESC" = "Non-music sections in music videos (e.g. talking, introductions).";
+"SB_CAT_preview" = "Preview";
+"SB_CAT_preview_DESC" = "Recap or preview of content from another video or previous episode.";
+"SB_CAT_hook" = "Hook/Greetings";
+"SB_CAT_hook_DESC" = "Narrated trailers for the upcoming video, greetings and goodbyes.";
+"SB_CAT_poi_highlight" = "Highlight";
+"SB_CAT_poi_highlight_DESC" = "The key moment or highlight point of the video. Use 'Skip to' to jump directly to it.";
+"SB_CAT_filler" = "Filler";
+"SB_CAT_filler_DESC" = "Tangential content, jokes, or filler that is not directly related to the main topic.";
+
+"SB_SKIPPED" = "%@ segment has been skipped";
+"SB_DETECTED" = "%@ segment detected.\nWould you like to skip the segment?";
+"SB_SKIP_NOW" = "Skip";
+"SB_UNSKIP" = "Unskip";
+"SB_JUMPED_TO_HIGHLIGHT" = "Jumped to highlight";
+"SB_JUMP_TO_HIGHLIGHT" = "Highlight available. Jump to the point?";
+"SB_SEGMENT_COLOR_SUFFIX" = "Segment color";
+"SB_SECTION_MAIN" = "Main";
+"SB_SKIP_ALERT_DURATION" = "Skip alert duration";
+"SB_UNSKIP_ALERT_DURATION" = "Unskip alert duration";
\ No newline at end of file
diff --git a/layout/Library/Application Support/YouMod.bundle/icons/course@3x.png b/layout/Library/Application Support/YouMod.bundle/icons/course@3x.png
new file mode 100644
index 00000000..959cd054
Binary files /dev/null and b/layout/Library/Application Support/YouMod.bundle/icons/course@3x.png differ
diff --git a/layout/Library/Application Support/YouMod.bundle/icons/course_selected@3x.png b/layout/Library/Application Support/YouMod.bundle/icons/course_selected@3x.png
new file mode 100644
index 00000000..21b2a15a
Binary files /dev/null and b/layout/Library/Application Support/YouMod.bundle/icons/course_selected@3x.png differ
diff --git a/layout/Library/Application Support/YouMod.bundle/icons/gaming@3x.png b/layout/Library/Application Support/YouMod.bundle/icons/gaming@3x.png
new file mode 100644
index 00000000..02d77fd8
Binary files /dev/null and b/layout/Library/Application Support/YouMod.bundle/icons/gaming@3x.png differ
diff --git a/layout/Library/Application Support/YouMod.bundle/icons/gaming_selected@3x.png b/layout/Library/Application Support/YouMod.bundle/icons/gaming_selected@3x.png
new file mode 100644
index 00000000..e1850716
Binary files /dev/null and b/layout/Library/Application Support/YouMod.bundle/icons/gaming_selected@3x.png differ
diff --git a/layout/Library/Application Support/YouMod.bundle/icons/history@3x.png b/layout/Library/Application Support/YouMod.bundle/icons/history@3x.png
new file mode 100644
index 00000000..784c003f
Binary files /dev/null and b/layout/Library/Application Support/YouMod.bundle/icons/history@3x.png differ
diff --git a/layout/Library/Application Support/YouMod.bundle/icons/history_selected@3x.png b/layout/Library/Application Support/YouMod.bundle/icons/history_selected@3x.png
new file mode 100644
index 00000000..89cf4162
Binary files /dev/null and b/layout/Library/Application Support/YouMod.bundle/icons/history_selected@3x.png differ
diff --git a/layout/Library/Application Support/YouMod.bundle/icons/like@3x.png b/layout/Library/Application Support/YouMod.bundle/icons/like@3x.png
new file mode 100644
index 00000000..c11c1aed
Binary files /dev/null and b/layout/Library/Application Support/YouMod.bundle/icons/like@3x.png differ
diff --git a/layout/Library/Application Support/YouMod.bundle/icons/like_selected@3x.png b/layout/Library/Application Support/YouMod.bundle/icons/like_selected@3x.png
new file mode 100644
index 00000000..ee22dc57
Binary files /dev/null and b/layout/Library/Application Support/YouMod.bundle/icons/like_selected@3x.png differ
diff --git a/layout/Library/Application Support/YouMod.bundle/icons/live@3x.png b/layout/Library/Application Support/YouMod.bundle/icons/live@3x.png
new file mode 100644
index 00000000..3843e196
Binary files /dev/null and b/layout/Library/Application Support/YouMod.bundle/icons/live@3x.png differ
diff --git a/layout/Library/Application Support/YouMod.bundle/icons/live_selected@3x.png b/layout/Library/Application Support/YouMod.bundle/icons/live_selected@3x.png
new file mode 100644
index 00000000..621cc75c
Binary files /dev/null and b/layout/Library/Application Support/YouMod.bundle/icons/live_selected@3x.png differ
diff --git a/layout/Library/Application Support/YouMod.bundle/icons/minigame@3x.png b/layout/Library/Application Support/YouMod.bundle/icons/minigame@3x.png
new file mode 100644
index 00000000..db197904
Binary files /dev/null and b/layout/Library/Application Support/YouMod.bundle/icons/minigame@3x.png differ
diff --git a/layout/Library/Application Support/YouMod.bundle/icons/minigame_selected@3x.png b/layout/Library/Application Support/YouMod.bundle/icons/minigame_selected@3x.png
new file mode 100644
index 00000000..9b728c72
Binary files /dev/null and b/layout/Library/Application Support/YouMod.bundle/icons/minigame_selected@3x.png differ
diff --git a/layout/Library/Application Support/YouMod.bundle/icons/movie@3x.png b/layout/Library/Application Support/YouMod.bundle/icons/movie@3x.png
new file mode 100644
index 00000000..dc9c7dbf
Binary files /dev/null and b/layout/Library/Application Support/YouMod.bundle/icons/movie@3x.png differ
diff --git a/layout/Library/Application Support/YouMod.bundle/icons/movie_selected@3x.png b/layout/Library/Application Support/YouMod.bundle/icons/movie_selected@3x.png
new file mode 100644
index 00000000..5141b4d7
Binary files /dev/null and b/layout/Library/Application Support/YouMod.bundle/icons/movie_selected@3x.png differ
diff --git a/layout/Library/Application Support/YouMod.bundle/icons/music@3x.png b/layout/Library/Application Support/YouMod.bundle/icons/music@3x.png
new file mode 100644
index 00000000..8de5a904
Binary files /dev/null and b/layout/Library/Application Support/YouMod.bundle/icons/music@3x.png differ
diff --git a/layout/Library/Application Support/YouMod.bundle/icons/music_selected@3x.png b/layout/Library/Application Support/YouMod.bundle/icons/music_selected@3x.png
new file mode 100644
index 00000000..16209212
Binary files /dev/null and b/layout/Library/Application Support/YouMod.bundle/icons/music_selected@3x.png differ
diff --git a/layout/Library/Application Support/YouMod.bundle/icons/news@3x.png b/layout/Library/Application Support/YouMod.bundle/icons/news@3x.png
new file mode 100644
index 00000000..fd63821e
Binary files /dev/null and b/layout/Library/Application Support/YouMod.bundle/icons/news@3x.png differ
diff --git a/layout/Library/Application Support/YouMod.bundle/icons/news_selected@3x.png b/layout/Library/Application Support/YouMod.bundle/icons/news_selected@3x.png
new file mode 100644
index 00000000..75cb8584
Binary files /dev/null and b/layout/Library/Application Support/YouMod.bundle/icons/news_selected@3x.png differ
diff --git a/layout/Library/Application Support/YouMod.bundle/icons/noti@3x.png b/layout/Library/Application Support/YouMod.bundle/icons/noti@3x.png
new file mode 100644
index 00000000..272fe733
Binary files /dev/null and b/layout/Library/Application Support/YouMod.bundle/icons/noti@3x.png differ
diff --git a/layout/Library/Application Support/YouMod.bundle/icons/noti_selected@3x.png b/layout/Library/Application Support/YouMod.bundle/icons/noti_selected@3x.png
new file mode 100644
index 00000000..d184fd9c
Binary files /dev/null and b/layout/Library/Application Support/YouMod.bundle/icons/noti_selected@3x.png differ
diff --git a/layout/Library/Application Support/YouMod.bundle/icons/playlist@3x.png b/layout/Library/Application Support/YouMod.bundle/icons/playlist@3x.png
new file mode 100644
index 00000000..a48a2374
Binary files /dev/null and b/layout/Library/Application Support/YouMod.bundle/icons/playlist@3x.png differ
diff --git a/layout/Library/Application Support/YouMod.bundle/icons/playlist_selected@3x.png b/layout/Library/Application Support/YouMod.bundle/icons/playlist_selected@3x.png
new file mode 100644
index 00000000..7afd5edc
Binary files /dev/null and b/layout/Library/Application Support/YouMod.bundle/icons/playlist_selected@3x.png differ
diff --git a/layout/Library/Application Support/YouMod.bundle/icons/post@3x.png b/layout/Library/Application Support/YouMod.bundle/icons/post@3x.png
new file mode 100644
index 00000000..b63ea72b
Binary files /dev/null and b/layout/Library/Application Support/YouMod.bundle/icons/post@3x.png differ
diff --git a/layout/Library/Application Support/YouMod.bundle/icons/post_selected@3x.png b/layout/Library/Application Support/YouMod.bundle/icons/post_selected@3x.png
new file mode 100644
index 00000000..f1952f9e
Binary files /dev/null and b/layout/Library/Application Support/YouMod.bundle/icons/post_selected@3x.png differ
diff --git a/layout/Library/Application Support/YouMod.bundle/icons/sports@3x.png b/layout/Library/Application Support/YouMod.bundle/icons/sports@3x.png
new file mode 100644
index 00000000..3cc508fa
Binary files /dev/null and b/layout/Library/Application Support/YouMod.bundle/icons/sports@3x.png differ
diff --git a/layout/Library/Application Support/YouMod.bundle/icons/sports_selected@3x.png b/layout/Library/Application Support/YouMod.bundle/icons/sports_selected@3x.png
new file mode 100644
index 00000000..d3384c66
Binary files /dev/null and b/layout/Library/Application Support/YouMod.bundle/icons/sports_selected@3x.png differ
diff --git a/layout/Library/Application Support/YouMod.bundle/icons/video@3x.png b/layout/Library/Application Support/YouMod.bundle/icons/video@3x.png
new file mode 100644
index 00000000..9d516472
Binary files /dev/null and b/layout/Library/Application Support/YouMod.bundle/icons/video@3x.png differ
diff --git a/layout/Library/Application Support/YouMod.bundle/icons/video_selected@3x.png b/layout/Library/Application Support/YouMod.bundle/icons/video_selected@3x.png
new file mode 100644
index 00000000..9f19fa27
Binary files /dev/null and b/layout/Library/Application Support/YouMod.bundle/icons/video_selected@3x.png differ
diff --git a/layout/Library/Application Support/YouMod.bundle/icons/watchlater@3x.png b/layout/Library/Application Support/YouMod.bundle/icons/watchlater@3x.png
new file mode 100644
index 00000000..fc35003c
Binary files /dev/null and b/layout/Library/Application Support/YouMod.bundle/icons/watchlater@3x.png differ
diff --git a/layout/Library/Application Support/YouMod.bundle/icons/watchlater_selected@3x.png b/layout/Library/Application Support/YouMod.bundle/icons/watchlater_selected@3x.png
new file mode 100644
index 00000000..82b0f6f4
Binary files /dev/null and b/layout/Library/Application Support/YouMod.bundle/icons/watchlater_selected@3x.png differ
diff --git a/layout/Library/Application Support/YouMod.bundle/id.lproj/Localizable.strings b/layout/Library/Application Support/YouMod.bundle/id.lproj/Localizable.strings
index bccaf550..2c1a8a0f 100644
--- a/layout/Library/Application Support/YouMod.bundle/id.lproj/Localizable.strings
+++ b/layout/Library/Application Support/YouMod.bundle/id.lproj/Localizable.strings
@@ -1,11 +1,18 @@
-"NOTE" = "NOTE: Some hiding features may leave a blank box.";
"SETTINGS" = "Settings";
+"INTERFACE" = "Interface";
+
+"LOOP_DISABLED" = "Loop disabled";
+"LOOP_ENABLED" = "Loop enabled";
"NEW_ISSUES" = "Report issues or request new features";
"NEW_ISSUES_DESC" = "Found bugs or want to request new features? Tap here.";
"SOURCE_CODES" = "Source codes";
"SOURCE_CODES_DESC" = "Take a look at the codes!";
+"EXTRA" = "Extra";
+"FIX_PLAYBACK_ISSUES" = "Fix playback issues (Temporary)";
+"FIX_PLAYBACK_ISSUES_DESC" = "Try to fix video playback issues for some users. Turning this on will make the player automatically reloads itself everytime it errors or fails to play the video.";
+
"PERFER_HEADER" = "YouMod perferences";
"PERFER" = "Perferences";
"IMPORT" = "Import settings";
@@ -24,6 +31,7 @@
"ERROR_NO_KEYS_IMPORT" = "No available keys to import";
"ERROR_NO_KEYS_EXPORT" = "No available keys to export";
"OVERRIDE" = "This will override the current settings. Do you want to continue?";
+"DISABLED" = "Disabled";
"CACHE" = "Cache management";
"CLEARCACHE" = "Clear cache";
@@ -32,12 +40,80 @@
"AUTO_CLEARCACHE_DESC" = "Automatically clears the app cache on startup.";
"DOWNLOADING" = "Downloading";
-"DOWNLOAD_MANAGER" = "Download Manager";
-"DOWNLOAD_MANAGER_DESC" = "Replaces YouTube's video downloader with YouMod one. With ablity to downloading videos, audios, captions, and thumbnails";
+"DOWNLOAD_MANAGER" = "Download manager";
+"DOWNLOAD_MANAGER_DESC" = "Replaces YouTube's video downloader with YouMod one. With ablity to downloading videos, audios, captions, and thumbnails.";
"DOWNLOAD_SAVE_PHOTOS" = "Automatically save to Photos";
"DOWNLOAD_SAVE_PHOTOS_DESC" = "Automatically saves downloaded videos and images directly to your Photos app.";
-"DOWNLOAD_DRC_AUDIO" = "Prefer DRC Audio";
-"DOWNLOAD_DRC_AUDIO_DESC" = "Prioritizes downloading audio tracks with Dynamic Range Compression for more consistent volume levels.";
+"ADD_SHORTS_DOWNLOAD" = "Adds a download button to Shorts";
+"ADD_SHORTS_DOWNLOAD_DESC" = "Adds a download button to Shorts video overlay.";
+"USE_ORIG_AUDIO" = "Automatically uses original audio track";
+"USE_ORIG_AUDIO_DESC" = "Automatically uses original audio track instead of showing a dialog.";
+"DOWNLOAD_SHORTS" = "Download Shorts";
+"DOWNLOAD_SHORTS_DESC" = "Choose Shorts quality";
+"COPY_VID_INFO" = "Copy video informations";
+"COPY_VID_INFO_DESC" = "Choose what to copy";
+"COPY_ALL_VID_INFO" = "Copy all";
+"COPY_ALL_VID_INFO_DESC" = "Copy all video informations";
+"COPY_AUTHOR" = "Copy author";
+"COPY_AUTHOR_DESC" = "Copy the video author";
+"COPY_TITLE" = "Copy title";
+"COPY_TITLE_DESC" = "Copy the video title";
+"COPY_DESCRIPTION" = "Copy description";
+"COPY_DESCRIPTION_DESC" = "Copy the video description";
+"COPY_URL" = "Copy video link";
+"COPY_URL_TIMESTAMP" = "Copy video link with timestamp";
+"URL_COPIED" = "Copied video link";
+"URL_TIMESTAMP_COPIED" = "Copied video link with timestamp";
+"ERROR_VIDEOID" = "Cannot get the video ID";
+"ERROR_ADS" = "Cannot copy video link while playing ads";
+"COPIED_AUTHOR" = "Copied video author";
+"COPIED_TITLE" = "Copied video title";
+"COPIED_DESCRIPTION" = "Copied video description";
+"SAVE_THUMBNAIL" = "Save thumbnail";
+"SAVE_THUMBNAIL_DESC" = "Save to Photos";
+"DOWNLOAD_CAPTIONS" = "Download captions";
+"DOWNLOAD_CAPTIONS_DESC" = "Save subtitles as .VTT file";
+"DOWNLOAD_AUDIO" = "Download audio";
+"DOWNLOAD_AUDIO_DESC" = "Download audio as .m4a file";
+"DOWNLOAD_VIDEO" = "Download video";
+"DOWNLOAD_VIDEO_DESC" = "Choose video quality";
+"OPEN_VID_BEFORE" = "Open a video before using the download manager";
+"NO_CAPTIONS_URL" = "No valid caption URLs found";
+"NO_AUDIO_URL" = "No audio URL found";
+"NO_STREAM_URL" = "No stream URL found";
+"CAPTIONS_FAILED" = "Failed to download captions";
+"NO_CAPTIONS" = "No captions available for this video";
+"NO_AUDIO_STREAM_FOUND" = "No audio streams found";
+"NO_VID_AUDIO_STREAM_FOUND" = "No video/audio streams found";
+"COPIED_VID_INFO" = "Copied all video informations";
+"SAVED_TO_PHOTOS" = "Saved to Photos";
+"SAVE_FAILED" = "Save failed";
+"PHOTO_ACCESS_DENINED" = "Photos access denied";
+"THUMBNAIL_FAILED" = "Thumbnail failed";
+"DOWNLOADING_THUMBNAIL" = "Downloading thumbnail...";
+"DOWNLOADING_AUDIO" = "Downloading audio...";
+"DOWNLOADING_VIDEO" = "Downloading video...";
+"DOWNLOADING_CAPTIONS" = "Downloading captions...";
+"NO_THUMBNAIL_FOUND" = "No thumbnail found";
+"DOWNLOAD_FAILED" = "Download failed";
+"COPY_THUMBNAIL" = "Copy thumbnail";
+"COPY_THUMBNAIL_DESC" = "Copy to clipboard";
+"SHOW_THUMBNAIL" = "Show thumbnail";
+"SHOW_THUMBNAIL_DESC" = "Show in a dialog";
+"THUMBNAIL_OPTIONS" = "Download thumbnail";
+"THUMBNAIL_OPTIONS_DESC" = "Show more options";
+"COPIED_TO_CLIPBOARD" = "Copied to clipboard";
+"DOWNLOAD_COMPLETED" = "Download completed";
+"CANNOT_SAVE_TO_PHOTOS" = "Cannot save to Photos";
+"FINA_VIDEO" = "Finalizing video...";
+"MERGING_VID_FALLBACK" = "Merging video with fallback...";
+"MERGING_VID" = "Merging video...";
+"CONVERTING" = "Converting to";
+"ALREADY_DOWNLOADING" = "Already downloading";
+"DOWNLOAD_CANCELLED" = "Download cancelled";
+"CANCEL" = "Cancel";
+"TRIMMING_AUDIO" = "Trimming audio...";
+"COPY" = "Copy";
"APPEARANCE" = "Appearance";
"OLED_THEME" = "OLED dark mode";
@@ -46,6 +122,8 @@
"OLED_KEYBOARD_DESC" = "Make the keyboard uses a pure black (OLED) color for dark theme.";
"NAVBAR" = "Navigation bar";
+"STICKY_NAVBAR" = "Sticky Navigation bar";
+"STICKY_NAVBAR_DESC" = "Pins the Navigation bar so that it remains visible when scrolling down.";
"HIDE_YT_LOGO" = "Hide YouTube logo";
"HIDE_YT_LOGO_DESC" = "Hide YouTube logo in the navigation bar.";
"PREMIUM_LOGO" = "Use YouTube Premium logo";
@@ -62,34 +140,78 @@
"FEED" = "Feed";
"HIDE_SUBBAR" = "Hide subbar";
"HIDE_SUBBAR_DESC" = "Hide subbar at the top of the feed.";
+"HIDE_HORI_SHELF" = "Remove horizontal feeds";
+"HIDE_HORI_SHELF_DESC" = "Removes all horizontal feeds (News, Continue Watching, Posts collection, etc.) from the feed, except Playables.";
"HIDE_MUSIC_PLAYLISTS" = "Hide music playlist generator";
"HIDE_MUSIC_PLAYLISTS_DESC" = "Hide music playlist generator (Select music tunes in three) in the feed.";
"HIDE_FEED_POST" = "Hide posts";
"HIDE_FEED_POST_DESC" = "Hide posts in feed.";
+"HIDE_SURVEYS" = "Hide surveys";
+"HIDE_SURVEYS_DESC" = "Hide video surveys that YouTube shows sometimes.";
"HIDE_SHORTS_SHELF" = "Hide Shorts shelf";
"HIDE_SHORTS_SHELF_DESC" = "Hide Shorts shelf in the feed, including in the Subscriptions tab.";
+"KEEP_SHORTS_SUBSCRIPT" = "Keep Shorts in Subscriptions";
+"KEEP_SHORTS_SUBSCRIPT_DESC" = "Prevents removing Shorts from Subscriptions tab.";
"HIDE_SEARCH_HISTORY" = "Hide search history";
"HIDE_SEARCH_HISTORY_DESC" = "Hide previous search history and suggestions when using the search bar.\nNOTE: Your search history will still be accessible in your other YouTube clients.";
-"HIDE_SUB_BUTTON" = "Hide Subscribe button";
-"HIDE_SUB_BUTTON_DESC" = "Hides the subscribe button in channels and video descriptions.";
-"HIDE_SHOP_BUTTON" = "Hide Shopping button";
-"HIDE_SHOP_BUTTON_DESC" = "Hides the shopping/store button in channels descriptions.";
-"HIDE_MEMBER_BUTTON" = "Hide Membership button";
-"HIDE_MEMBER_BUTTON_DESC" = "Hides the 'Join' button in channels and video descriptions.";
+"HIDE_PLAYABLES" = "Remove playables";
+"HIDE_PLAYABLES_DESC" = "Removes playables (mini-games) from the feed.";
"PLAYER" = "Player";
+"PLAYBACK_SPEED" = "Playback speed";
+"CONTROL_CENTER" = "Control Center";
+"PLAYER_ACTIONS" = "Player actions";
+"GESTURE_HEADER" = "Gestures";
+"QUALITY_WIFI" = "Playback quality on WiFi";
+"QUALITY_WIFI_DESC" = "Automatically applies the selected quality when streaming over Wi-Fi.";
+"QUALITY_CELLULAR" = "Playback quality on Cellular";
+"QUALITY_CELLULAR_DESC" = "Automatically applies the selected quality when streaming over mobile data.";
+"QUALITY_LOW_POWER" = "Playback quality on Power saver mode";
+"QUALITY_LOW_POWER_DESC" = "Automatically applies the selected quality when when Low power mode is enabled on your device.";
+"DEFAULT" = "Default";
+"BEST" = "Best";
+"AUDIO_TRACK" = "Preferred audio track";
+"ORIGINAL" = "Original";
+"SELECT_MANUALLY" = "Select manually";
+"AUDIO_TRACK_SELECT" = "Selected audio track";
+"AUDIO_TRACK_SELECT_DESC" = "Specifies the preferred audio track language to use for playback, if available.";
+"NO_AUTO_DUBBED" = "Uses original audio track";
+"NO_AUTO_DUBBED_DESC" = "Uses original audio track if the preferred language track is auto-dubbed or not found.";
+"CAPTION_TRACK" = "Preferred captions";
+"CAPTION_TRACK_SELECT" = "Selected captions";
+"CAPTION_TRACK_SELECT_DESC" = "Specifies the preferred captions language to display, if available.";
+"DISABLES_CAPTION_TRACK" = "Automatically disable captions";
+"DISABLES_CAPTION_TRACK_DESC" = "Automatically disable captions if the preferred language is auto-generated or not found.";
+"DEFAULT_SPEED" = "Default playback rate";
+"DEFAULT_SPEED_DESC" = "Overrides the default playback speed.";
+"SPEED_BUTTON" = "Speed controls button";
+"SPEED_BUTTON_DESC" = "Displays a speed controls button on overlay.";
+"MUTE_BUTTON" = "Mute button";
+"MUTE_BUTTON_DESC" = "Displays a button to mute or unmute the volume on the overlay.";
+"SHARE_BUTTON" = "Share button";
+"SHARE_BUTTON_DESC" = "Displays a button to share the video link on the overlay.";
+"LOOP_BUTTON" = "Loop button";
+"LOOP_BUTTON_DESC" = "Displays a button to loop the video on the overlay.";
+"CAPTION_BUTTON" = "Captions button";
+"CAPTION_BUTTON_DESC" = "Displays a button to copy the video captions on the overlay.";
+"HOLD_TO_SPEED" = "Hold to speed";
+"HOLD_TO_SPEED_DESC" = "Uses custom hold to speed playback rate.";
"HIDE_AUTOPLAY" = "Hide autoplay switch";
"HIDE_AUTOPLAY_DESC" = "Hide autoplay switch in the video overlay.";
"HIDE_CAPTIONS_BUTTON" = "Hide captions button";
"HIDE_CAPTIONS_BUTTON_DESC" = "Hide captions button in the video overlay.";
"HIDE_CAST_BUTTON_PLAYER" = "Hide cast button";
"HIDE_CAST_BUTTON_PLAYER_DESC" = "Hide cast button in the video overlay.";
-"HIDE_PREV_BUTTON" = "Hide previous/back button";
-"HIDE_PREV_BUTTON_DESC" = "Hide previous/back button in the video overlay.";
-"HIDE_NEXT_BUTTON" = "Hide next/skip button";
-"HIDE_NEXT_BUTTON_DESC" = "Hide next/skip button in the video overlay.";
+"HIDE_NEXT_AND_PREV_BUTTON" = "Hide previous and next buttons";
+"HIDE_NEXT_AND_PREV_BUTTON_DESC" = "Hide previous and next buttons in the video overlay.";
"REPLACE_PREVNEXT_BUTTONS" = "Replace Previous/Next buttons";
"REPLACE_PREVNEXT_BUTTONS_DESC" = "Replaces the Previous and Next video buttons in the player with Rewind and Fast Forward buttons.";
+"SKIP_BACKWARD" = "Skip backward";
+"SKIP_BACKWARD_DESC" = "Replaces the previous track button in Control Center with a skip backward button by a set number of seconds.";
+"SKIP_FORWARD" = "Skip forward";
+"SKIP_FORWARD_DESC" = "Replaces the next track button in Control Center with a skip forward button by a set number of seconds.";
+"REWIND_SECONDS" = "Rewind seconds";
+"FORWARD_SECONDS" = "Fast-forward seconds";
"REMOVE_DARK_OVERLAY" = "Remove dark overlay";
"REMOVE_DARK_OVERLAY_DESC" = "Remove dark overlay from the video overlay.";
"HIDE_END_SCREEN" = "Hide endscreen cards";
@@ -102,6 +224,12 @@
"HIDE_PAID_OVERLAY_DESC" = "Hide 'Includes paid promotion' in the video overlay.";
"HIDE_WATERMARK" = "Hide watermark";
"HIDE_WATERMARK_DESC" = "Hide channel watermark at the bottom of the video overlay.";
+"PAUSE_ON_OVERLAY" = "Pause on overlay";
+"PAUSE_ON_OVERLAY_DESC" = "Sets playback on pause if overlay appears.";
+"DISABLES_SNAP_TO_CHAPTER" = "Disable snap to chapters";
+"DISABLES_SNAP_TO_CHAPTER_DESC" = "Prevents the scrubbar stick to the chapters.";
+"DISABLES_ENGAGE_PANEL" = "Disables engagement panel in fullscreen";
+"DISABLES_ENGAGE_PANEL_DESC" = "Prevent engagement panel showing in fullscreen.";
"GESTURES" = "Enable gestures controls";
"GESTURES_DESC" = "Swipe up or down on the left/right edges of the video screen to adjust brightness, volume, or playback speed.";
"GESTURE_AREA" = "Gesture activation area";
@@ -136,6 +264,8 @@
"DISABLES_SHOW_REMAINING_DESC" = "Disable the feature that shows how much time is left in a video when tapping the timestamp.";
"ALWAYS_SHOW_REMAINING" = "Always show remaining time";
"ALWAYS_SHOW_REMAINING_DESC" = "Ensure the remaining time is always displayed by default in the player overlay.";
+"COPY_TIMESTAMP_ON_PAUSE" = "Copy timestamped links";
+"COPY_TIMESTAMP_ON_PAUSE_DESC" = "Allows to copy timestamped link to the clipboard by pressing pause button.";
"SHOW_REMAINING_EXTRA" = "Show extra remaining time";
"SHOW_REMAINING_EXTRA_DESC" = "Adds the estimated finish time to the label in the video overlay.";
"HIDE_FULLSCREEN_ACTIONS" = "Hide fullscreen actions";
@@ -148,93 +278,95 @@
"HIDE_CONTENT_WARNING_DESC" = "Automatically skip the sensitive content warning alert in some vidoes.";
"AUTO_FULLSCREEN" = "Auto fullscreen";
"AUTO_FULLSCREEN_DESC" = "Automatically enter fullscreen mode when starting video playback.";
-"AUTO_DISABLES_CAPTION" = "Auto disable captions";
-"AUTO_DISABLES_CAPTION_DESC" = "Automatically turns off closed captions (CC) whenever a new video starts playing.";
"PORTRAIT_FULLSCREEN" = "Portrait fullscren mode";
"PORTRAIT_FULLSCREEN_DESC" = "Enables portrait fullscreen mode support.";
"OLD_QUALITY_PICKER" = "Use old quality picker";
"OLD_QUALITY_PICKER_DESC" = "Reverts the video quality selection menu to the classic version (lists all resolutions immediately).";
"EXTRA_SPEED" = "Extra speed options";
"EXTRA_SPEED_DESC" = "Adds more video playback speed options up to 10x.";
-"DISABLE_HINTS" = "Disable hints";
-"DISABLE_HINTS_DESC" = "Disable the tutorial hints and interactive pop-ups.";
"FORCE_MINIPLAYER" = "Force miniplayer";
"FORCE_MINIPLAYER_DESC" = "Allows all videos, including those made for kids, to play in the miniplayer when dismissing the player.";
"FORCE_SEEKBAR" = "Always show progress bar";
"FORCE_SEEKBAR_DESC" = "Makes the progress bar (seekbar) remains visible at all times while in fullscreen mode.";
-"HIDE_LIKE_BUTTON" = "Hide Like button";
-"HIDE_LIKE_BUTTON_DESC" = "Hide Like button in the action bar under the video overlay.";
-"HIDE_DISLIKE_BUTTON" = "Hide Dislike button";
-"HIDE_DISLIKE_BUTTON_DESC" = "Hide Dislike button in the action bar under the video overlay.";
-"HIDE_SHARE_BUTTON" = "Hide Share button";
-"HIDE_SHARE_BUTTON_DESC" = "Hide Share button in the action bar under the video overlay.";
-"HIDE_DOWNLOAD_BUTTON" = "Hide Download button";
-"HIDE_DOWNLOAD_BUTTON_DESC" = "Hide Download button in the action bar under the video overlay.";
-"HIDE_CLIP_BUTTON" = "Hide Clip button";
-"HIDE_CLIP_BUTTON_DESC" = "Hide Clip button in the action bar under the video overlay.";
-"HIDE_REMIX_BUTTON" = "Hide Remix button";
-"HIDE_REMIX_BUTTON_DESC" = "Hide Remix button in the action bar under the video overlay.";
-"HIDE_SAVE_BUTTON" = "Hide Save button";
-"HIDE_SAVE_BUTTON_DESC" = "Hide Save button in the action bar under the video overlay.";
+"TAP_TO_SEEK" = "Tap to seek";
+"TAP_TO_SEEK_DESC" = "Quickly jump to any point by tapping the progress bar.";
+"DISABLES_ZOOM" = "Disable free zoom gesture";
+"DISABLES_ZOOM_DESC" = "Disables new free zoom gestures.";
+"USES_24_HOURS_TIME" = "24-hour format";
+"USES_24_HOURS_TIME_DESC" = "Shows end time in 24-hour format.";
+"PAUSE_TWO_FINGERS" = "Pause using two fingers";
+"PAUSE_TWO_FINGERS_DESC" = "Pause/Resume playback by tapping on the screen using two fingers.";
+"HIDE_COMMENTS_SECTION" = "Hide comments section";
+"HIDE_COMMENTS_SECTION_DESC" = "Completely removes the entire comments section.";
+"HIDE_COMMENTS_PREVIEW" = "Hide preview comments";
+"HIDE_COMMENTS_PREVIEW_DESC" = "Removes the preview box for the top comment while keeping the full section expandable.";
+"USE_ANOTHER_MINIPLAYER" = "Uses another miniplayer style";
+"USE_ANOTHER_MINIPLAYER_DESC" = "Uses another miniplayer style redesigned from older YT versions.";
"SHORTS" = "Shorts";
-"HIDE_SHORTS_LIKE_BUTTON" = "Hide Like button";
-"HIDE_SHORTS_LIKE_BUTTON_DESC" = "Hide the Like button in the Shorts player.";
-"HIDE_SHORTS_DISLIKE_BUTTON" = "Hide Dislike button";
-"HIDE_SHORTS_DISLIKE_BUTTON_DESC" = "Hide the Dislike button in the Shorts player.";
-"HIDE_SHORTS_COMMENT_BUTTON" = "Hide Comment button";
-"HIDE_SHORTS_COMMENT_BUTTON_DESC" = "Hide the Comment button in the Shorts player.";
-"HIDE_SHORTS_SHARE_BUTTON" = "Hide Share button";
-"HIDE_SHORTS_SHARE_BUTTON_DESC" = "Hide the Share button in the Shorts player.";
-"HIDE_SHORTS_REMIX_BUTTON" = "Hide Remix button";
-"HIDE_SHORTS_REMIX_BUTTON_DESC" = "Hide the Remix button in the Shorts player.";
-"HIDE_METADATA_BUTTON" = "Hide sound metadata button";
-"HIDE_METADATA_BUTTON_DESC" = "Hide the button that shows the sound source or music metadata in the Shorts player.";
+"SHORTS_ONLY" = "Shorts only mode";
+"SHORTS_ONLY_DESC" = "Limits YouTube functionality to viewing Shorts only.";
+"SHORTS_ACTION" = "Shorts action after the video ends";
+"LOOP" = "Loop video";
+"SKIP_TO_NEXT_SHORTS" = "Skip to next video";
+"PAUSE_SHORTS" = "Stop video";
+"REMOVE_LIVE_SHORTS" = "Remove live videos";
+"REMOVE_LIVE_SHORTS_DESC" = "Removes live videos from the feed.";
"HIDE_SHORTS_PRODUCT" = "Hide products";
"HIDE_SHORTS_PRODUCT_DESC" = "Hide tagged products and shopping links in the Shorts overlay.";
"HIDE_SHORTS_RECBAR" = "Hide recommendation bar";
"HIDE_SHORTS_RECBAR_DESC" = "Hide the suggested action bar in the Shorts player (e.g. Try this sound, Shopping, Preview comment).";
-"HIDE_SHORTS_COMMIT" = "Hide commissions bar";
-"HIDE_SHORTS_COMMIT_DESC" = "Hide the bar that shows the video got commissions in the Shorts player.";
-"HIDE_SHORTS_SUBSCRIPT_BUTTON" = "Hide subscriptions button";
-"HIDE_SHORTS_SUBSCRIPT_BUTTON_DESC" = "Hide the Subscriptions button at the top of the Shorts player.";
-"HIDE_SHORTS_LIVE_BUTTON" = "Hide live button";
-"HIDE_SHORTS_LIVE_BUTTON_DESC" = "Hide the Live button at the top of the Shorts player.";
-"HIDE_SHORTS_LENS_BUTTON" = "Hide lens button";
-"HIDE_SHORTS_LENS_BUTTON_DESC" = "Hide the Lens button at the top of the Shorts player.";
-"HIDE_SHORTS_TRENDS_BUTTON" = "Hide trends button";
-"HIDE_SHORTS_TRENDS_BUTTON_DESC" = "Hide the Trends button at the top of the Shorts player.";
-"HIDE_SHORTS_TO_VIDEO" = "Hide link to full video";
-"HIDE_SHORTS_TO_VIDEO_DESC" = "Hide the link that redirects in a Short to a full video in the Shorts player.";
"ENABLES_SHORTS_QUALITY" = "Enable Shorts quality picker";
"ENABLES_SHORTS_QUALITY_DESC" = "Force enable the video quality selection menu in Shorts.";
"SHOW_SHORTS_SEEKBAR" = "Always show seekbar";
"SHOW_SHORTS_SEEKBAR_DESC" = "Ensure the progress/seek bar is always visible at the bottom of the Shorts player.";
+"REMOVE_POSTS_SHORTS" = "Remove posts";
+"REMOVE_POSTS_SHORTS_DESC" = "Remove posts from the feed.";
+"SHORTS_FULLSCREEN" = "Pinch to Fullscreen";
+"SHORTS_FULLSCREEN_DESC" = "Manages visibility of the overlay with pinch in and pinch out gestures, displaying Shorts in fullscreen mode.";
"TABBAR" = "Tab bar";
"DEFAULT_TAB" = "Default startup tab";
-"DEFAULT_TAB_DESC" = "You can select the tab you want to open when opening the app.";
-"HOME_NAME" = "Home";
-"SUBSCRIPT_NAME" = "Subscriptions";
-"LIB_NAME" = "Library (You)";
+"FORSTED_TAB_BAR" = "Translucent tab bar";
+"ENABLED" = "Enabled";
"HIDE_TAB_INDI" = "Hide tab indicators";
"HIDE_TAB_INDI_DESC" = "Hide all tab indicators in the tab bar.";
"HIDE_TAB_LABELS" = "Hide tab labels";
"HIDE_TAB_LABELS_DESC" = "Hide all tab labels in the tab bar.";
-"HIDE_HOME_TAB" = "Hide Home tab";
-"HIDE_HOME_TAB_DESC" = "Hide Home tab in the tab bar.";
-"HIDE_SHORTS_TAB" = "Hide Shorts tab";
-"HIDE_SHORTS_TAB_DESC" = "Hide Shorts tab in the tab bar.";
-"HIDE_CREATE_BUTTON" = "Hide Create button";
-"HIDE_CREATE_BUTTON_DESC" = "Hide Create button in the tab bar.";
-"HIDE_SUBSCRIPT_TAB" = "Hide Subscriptions tab";
-"HIDE_SUBSCRIPT_TAB_DESC" = "Hide Subscriptions tab in the tab bar.";
+"HOME_TAB" = "Home";
+"SHORTS_TAB" = "Shorts";
+"CREATE_TAB" = "Create";
+"SUBSCRIPTIONS_TAB" = "Subscriptions";
+"LIBRARY_TAB" = "Library (You)";
+"HISTORY_TAB" = "History";
+"GAMING_TAB" = "Gaming";
+"SPORTS_TAB" = "Sports";
+"NOTI_TAB" = "Notifications";
+"NEWS_TAB" = "News";
+"MUSIC_TAB" = "Music";
+"WATCH_LATER_TAB" = "Watch later";
+"PLAYLIST_TAB" = "Playlists";
+"LIKE_TAB" = "Liked videos";
+"LIVE_TAB" = "Live";
+"POST_TAB" = "Posts";
+"VIDEO_TAB" = "Your videos";
+"MOVIE_TAB" = "Movies";
+"COURSE_TAB" = "Courses";
+"MINIGAME_TAB" = "Playables";
+"MANAGE_TABS" = "Manage tabs";
+"MANAGE_TABS_DESC" = "Reorder and toggle tabs";
+"TAB_REORDER_HINT" = "Drag to reorder. Toggle to show/hide.\nTab settings are also accessible by long-pressing the first tab in the tab bar.";
+"TAB_LIMIT" = "Tab limit";
+"TAB_LIMIT_DESC" = "Only 6 tabs are allowed.";
+"ZERO_TAB_DESC" = "At least 1 tab has to be enabled.";
"MISCELLANEOUS" = "Miscellaneous";
"BACKGROUND_PLAYBACK" = "Background playback";
"BACKGROUND_PLAYBACK_DESC" = "Enables background playback for videos.";
"DISABLES_SHORTS_PIP" = "Disables Shorts PiP";
"DISABLES_SHORTS_PIP_DESC" = "Try to disables Shorts PiP, this might not 100% disables it.";
+"DISABLE_HINTS" = "Disable hints";
+"DISABLE_HINTS_DESC" = "Disable the tutorial hints and interactive pop-ups.";
"BLOCK_UPGRADE_DIALOGS" = "Block upgrade dialogs";
"BLOCK_UPGRADE_DIALOGS_DESC" = "Block the YouTube dialogs that ask you to upgrade the app.";
"ARE_YOU_THERE_DIALOG" = "Hide 'Are you there?' dialog";
@@ -247,7 +379,109 @@
"DISABLES_SNACK_BAR_DESC" = "Disables snack bar when pressing some buttons or doing something in the app.";
"HIDE_STARTUP_ANIMATIONS" = "Hide YouTube's startup animations";
"HIDE_STARTUP_ANIMATIONS_DESC" = "Hide YouTube's startup animations when opening the app.";
-"HIDE_PLAY_IN_NEXT_QUEUE" = "Hide 'Play next in queue'";
-"HIDE_PLAY_IN_NEXT_QUEUE_DESC" = "Hide the 'Play next in queue' option in the flyout menu.";
"HIDE_LIKE_DISLIKE_VOTES" = "Hide like/dislike votes";
-"HIDE_LIKE_DISLIKE_VOTES_DESC" = "Hide messages that pop up when you like/dislike videos.";
\ No newline at end of file
+"HIDE_LIKE_DISLIKE_VOTES_DESC" = "Hide messages that pop up when you like/dislike videos.";
+"FLYOUT_MENU" = "Flyout menu";
+"REMOVE_PLAY_IN_NEXT_QUEUE_OPTION" = "Removes 'Play next in queue' option";
+"REMOVE_PLAY_IN_NEXT_QUEUE_OPTION_DESC" = "Removes 'Play next in queue' option from the flyout menu.";
+"REMOVE_DOWNLOAD_OPTION" = "Removes 'Download video' option";
+"REMOVE_DOWNLOAD_OPTION_DESC" = "Removes 'Download video' option from the flyout menu.";
+"REMOVE_WATCH_LATER_OPTION" = "Removes 'Save to Watch later' option";
+"REMOVE_WATCH_LATER_OPTION_DESC" = "Removes 'Save to Watch Later' option from the flyout menu.";
+"REMOVE_SAVE_OPTION" = "Removes 'Save to playlist' option";
+"REMOVE_SAVE_OPTION_DESC" = "Removes 'Save to playlist' option from the flyout menu.";
+"REMOVE_REMOVE_FROM_PLAYLIST_OPTION" = "Removes 'Remove from playlist' option";
+"REMOVE_REMOVE_FROM_PLAYLIST_OPTION_DESC" = "Removes 'Remove from playlist' option from the flyout menu.";
+"REMOVE_SHARE_OPTION" = "Removes 'Share' option";
+"REMOVE_SHARE_OPTION_DESC" = "Removes 'Share' option from the flyout menu.";
+"REMOVE_NOT_INTERESTED_OPTION" = "Removes 'Not interested' option";
+"REMOVE_NOT_INTERESTED_OPTION_DESC" = "Removes 'Not interested' option from the flyout menu.";
+"REMOVE_DONT_RECOMMEND_OPTION" = "Removes 'Don't recommend channel' option";
+"REMOVE_DONT_RECOMMEND_OPTION_DESC" = "Removes 'Don't recommend channel' option from the flyout menu.";
+"REMOVE_INFO_OPTION" = "Removes 'Why are you seeing this?' option";
+"REMOVE_INFO_OPTION_DESC" = "Removes 'Why are you seeing this?' option from the flyout menu.";
+"REMOVE_FILTER_OPTION" = "Removes 'Search filters' option";
+"REMOVE_FILTER_OPTION_DESC" = "Removes 'Search filters' option from the flyout menu.";
+"REMOVE_REPORT_OPTION" = "Removes 'Report' option";
+"REMOVE_REPORT_OPTION_DESC" = "Removes 'Report' option from the flyout menu.";
+"REMOVE_YOUTUBE_MUSIC_OPTION" = "Removes 'Listen with YouTube Music' option";
+"REMOVE_YOUTUBE_MUSIC_OPTION_DESC" = "Removes 'Listen with YouTube Music' option from the flyout menu.";
+"REMOVE_FEED_BACK_OPTION" = "Removes 'Send feedback' option";
+"REMOVE_FEED_BACK_OPTION_DESC" = "Removes 'Send feedback' option from the flyout menu.";
+"REMOVE_CAST_OPTION" = "Removes 'Watch on TV' option";
+"REMOVE_CAST_OPTION_DESC" = "Removes 'Watch on TV' option from the flyout menu.";
+"REMOVE_SHUFFLE_OPTION" = "Removes 'Shuffle' option";
+"REMOVE_SHUFFLE_OPTION_DESC" = "Removes 'Shuffle' option from the flyout menu.";
+"REMOVE_UN_SUB_OPTION" = "Removes 'Unsubscribe' option";
+"REMOVE_UN_SUB_OPTION_DESC" = "Removes 'Unsubscribe' option from the flyout menu.";
+"REMOVE_HIDE_FROM_PLAYLIST_OPTION" = "Removes 'Hide video from playlist' option";
+"REMOVE_HIDE_FROM_PLAYLIST_OPTION_DESC" = "Removes 'Hide video from playlist' option from the flyout menu.";
+"REMOVE_HELP_OPTION" = "Removes 'Help & feedback' option";
+"REMOVE_HELP_OPTION_DESC" = "Removes 'Help & feedback' option from the flyout menu.";
+"REMOVE_NOTIFY_OPTION" = "Removes 'Notify me' option";
+"REMOVE_NOTIFY_OPTION_DESC" = "Removes 'Notify me' option from the flyout menu.";
+"REMOVE_CLEARSCREEN_OPTION" = "Removes 'Clear Screen' option";
+"REMOVE_CLEARSCREEN_OPTION_DESC" = "Removes 'Clear Screen' option from the flyout menu.";
+"HIDE_COMMU_GUIDE" = "Hide community guidelines";
+"HIDE_COMMU_GUIDE_DESC" = "Hide community guidelines header.";
+"DISABLES_RTL" = "Disable RTL formatting";
+"DISABLES_RTL_DESC" = "Forcefully displays text in left-to-right (LTR) format for languages that are initially displayed in right-to-left (RTL).";
+"DEVICE_UI" = "Interface style";
+
+"SB_DESCRIPTION" = "Skip sponsored segments automatically using community-submitted data.";
+"SB_ENABLE" = "Enable SponsorBlock";
+"SB_ENABLE_DESC" = "Activate the SponsorBlock extension to skip sponsored segments.";
+"SB_SHOW_BUTTON" = "Show overlay button";
+"SB_SHOW_BUTTON_DESC" = "Display a SponsorBlock toggle button in the player overlay.";
+"SB_SHOW_NOTIFICATIONS" = "Show notifications";
+"SB_SHOW_NOTIFICATIONS_DESC" = "Display notifications when segments are skipped. Notifications for manual-skip categories are always shown.";
+"SB_HAPTIC_FEEDBACK" = "Haptic feedback";
+"SB_HAPTIC_FEEDBACK_DESC" = "Provide haptic feedback when a segment is skipped.";
+"SB_SEGMENTS_IN_PLAYER" = "Segments in player";
+"SB_SEGMENTS_IN_PLAYER_DESC" = "Display colored segments on the progress bar of the main video player.";
+"SB_SEGMENTS_IN_FEED" = "Segments in feed";
+"SB_SEGMENTS_IN_FEED_DESC" = "Display colored segments on the progress bar of players in the feed.";
+"SB_SEGMENTS_IN_MINIPLAYER" = "Segments in mini-player";
+"SB_SEGMENTS_IN_MINIPLAYER_DESC" = "Display colored segments on the progress bar of the mini-player.";
+"SB_SHOW_DURATION" = "Show duration without segments";
+"SB_SHOW_DURATION_DESC" = "Display the total video duration excluding skippable segments.";
+"SB_CATEGORIES_HEADER" = "Segment categories";
+"SB_SEGMENT_COLOR" = "Segment color";
+
+"SB_ACTION_DISABLE" = "Disable";
+"SB_ACTION_AUTO_SKIP" = "Auto-skip";
+"SB_ACTION_ASK" = "Ask before skipping";
+"SB_ACTION_DISPLAY" = "Show on seek bar only";
+"SB_ACTION_SKIP_TO" = "Skip to segment";
+
+"SB_CAT_sponsor" = "Sponsor";
+"SB_CAT_sponsor_DESC" = "Paid promotion for a product or service not directly related to the creator.";
+"SB_CAT_intro" = "Intro";
+"SB_CAT_intro_DESC" = "Intermission or intro animation typically seen at the start of a video.";
+"SB_CAT_outro" = "Endcards";
+"SB_CAT_outro_DESC" = "Endcards, credits, or outros at the end of the video.";
+"SB_CAT_interaction" = "Interaction";
+"SB_CAT_interaction_DESC" = "Reminders to like, subscribe, or follow on social media.";
+"SB_CAT_selfpromo" = "Self-promotion";
+"SB_CAT_selfpromo_DESC" = "Unpaid promotion of the creator's own products, merchandise, or other content.";
+"SB_CAT_music_offtopic" = "Non-music";
+"SB_CAT_music_offtopic_DESC" = "Non-music sections in music videos (e.g. talking, introductions).";
+"SB_CAT_preview" = "Preview";
+"SB_CAT_preview_DESC" = "Recap or preview of content from another video or previous episode.";
+"SB_CAT_hook" = "Hook/Greetings";
+"SB_CAT_hook_DESC" = "Narrated trailers for the upcoming video, greetings and goodbyes.";
+"SB_CAT_poi_highlight" = "Highlight";
+"SB_CAT_poi_highlight_DESC" = "The key moment or highlight point of the video. Use 'Skip to' to jump directly to it.";
+"SB_CAT_filler" = "Filler";
+"SB_CAT_filler_DESC" = "Tangential content, jokes, or filler that is not directly related to the main topic.";
+
+"SB_SKIPPED" = "%@ segment has been skipped";
+"SB_DETECTED" = "%@ segment detected.\nWould you like to skip the segment?";
+"SB_SKIP_NOW" = "Skip";
+"SB_UNSKIP" = "Unskip";
+"SB_JUMPED_TO_HIGHLIGHT" = "Jumped to highlight";
+"SB_JUMP_TO_HIGHLIGHT" = "Highlight available. Jump to the point?";
+"SB_SEGMENT_COLOR_SUFFIX" = "Segment color";
+"SB_SECTION_MAIN" = "Main";
+"SB_SKIP_ALERT_DURATION" = "Skip alert duration";
+"SB_UNSKIP_ALERT_DURATION" = "Unskip alert duration";
\ No newline at end of file
diff --git a/layout/Library/Application Support/YouMod.bundle/it.lproj/Localizable.strings b/layout/Library/Application Support/YouMod.bundle/it.lproj/Localizable.strings
index 7bf6685a..caf077f7 100644
--- a/layout/Library/Application Support/YouMod.bundle/it.lproj/Localizable.strings
+++ b/layout/Library/Application Support/YouMod.bundle/it.lproj/Localizable.strings
@@ -1,11 +1,18 @@
-"NOTE" = "NOTA: Alcune funzioni potrebbero lasciare un riquadro vuoto.";
"SETTINGS" = "Impostazioni";
+"INTERFACE" = "Interface";
+
+"LOOP_DISABLED" = "Loop disabled";
+"LOOP_ENABLED" = "Loop enabled";
"NEW_ISSUES" = "Segnala un problema o richiedi una funzione";
"NEW_ISSUES_DESC" = "Hai trovato un bug o vuoi una nuova funzione? Clicca qui!";
"SOURCE_CODES" = "Codice sorgente";
"SOURCE_CODES_DESC" = "Dai un'occhiata al codice!";
+"EXTRA" = "Extra";
+"FIX_PLAYBACK_ISSUES" = "Fix playback issues (Temporary)";
+"FIX_PLAYBACK_ISSUES_DESC" = "Try to fix video playback issues for some users. Turning this on will make the player automatically reloads itself everytime it errors or fails to play the video.";
+
"PERFER_HEADER" = "Opzioni YouMod";
"PERFER" = "Opzioni";
"IMPORT" = "Importa impostazioni";
@@ -24,6 +31,7 @@
"ERROR_NO_KEYS_IMPORT" = "Non ci sono chiavi valide da importare in questo file";
"ERROR_NO_KEYS_EXPORT" = "Non ci sono chiavi valide da esportare in questo file";
"OVERRIDE" = "Questo sovrascriverà le tue impostazioni attuali. Vuoi continuare?";
+"DISABLED" = "Disabled";
"CACHE" = "Gestione cache";
"CLEARCACHE" = "Pulisci cache";
@@ -32,12 +40,81 @@
"AUTO_CLEARCACHE_DESC" = "Pulisce automaticamente la cache all'avvio.";
"DOWNLOADING" = "Downloading";
-"DOWNLOAD_MANAGER" = "Download Manager";
-"DOWNLOAD_MANAGER_DESC" = "Replaces YouTube's video downloader with YouMod one. With ablity to downloading videos, audios, captions, and thumbnails";
+"DOWNLOAD_MANAGER" = "Download manager";
+"DOWNLOAD_MANAGER_DESC" = "Replaces YouTube's video downloader with YouMod one. With ablity to downloading videos, audios, captions, and thumbnails.";
"DOWNLOAD_SAVE_PHOTOS" = "Automatically save to Photos";
"DOWNLOAD_SAVE_PHOTOS_DESC" = "Automatically saves downloaded videos and images directly to your Photos app.";
-"DOWNLOAD_DRC_AUDIO" = "Prefer DRC Audio";
-"DOWNLOAD_DRC_AUDIO_DESC" = "Prioritizes downloading audio tracks with Dynamic Range Compression for more consistent volume levels.";
+"ADD_SHORTS_DOWNLOAD" = "Adds a download button to Shorts";
+"ADD_SHORTS_DOWNLOAD_DESC" = "Adds a download button to Shorts video overlay.";
+"USE_ORIG_AUDIO" = "Automatically uses original audio track";
+"USE_ORIG_AUDIO_DESC" = "Automatically uses original audio track instead of showing a dialog.";
+"DOWNLOAD_SHORTS" = "Download Shorts";
+"DOWNLOAD_SHORTS_DESC" = "Choose Shorts quality";
+"COPY_VID_INFO" = "Copy video informations";
+"COPY_VID_INFO_DESC" = "Choose what to copy";
+"COPY_ALL_VID_INFO" = "Copy all";
+"COPY_ALL_VID_INFO_DESC" = "Copy all video informations";
+"COPY_AUTHOR" = "Copy author";
+"COPY_AUTHOR_DESC" = "Copy the video author";
+"COPY_TITLE" = "Copy title";
+"COPY_TITLE_DESC" = "Copy the video title";
+"COPY_DESCRIPTION" = "Copy description";
+"COPY_DESCRIPTION_DESC" = "Copy the video description";
+"COPY_URL" = "Copy video link";
+"COPY_URL_TIMESTAMP" = "Copy video link with timestamp";
+"URL_COPIED" = "Copied video link";
+"URL_TIMESTAMP_COPIED" = "Copied video link with timestamp";
+"ERROR_VIDEOID" = "Cannot get the video ID";
+"ERROR_ADS" = "Cannot copy video link while playing ads";
+"COPIED_AUTHOR" = "Copied video author";
+"COPIED_TITLE" = "Copied video title";
+"COPIED_DESCRIPTION" = "Copied video description";
+"SAVE_THUMBNAIL" = "Save thumbnail";
+"SAVE_THUMBNAIL_DESC" = "Save to Photos";
+"DOWNLOAD_CAPTIONS" = "Download captions";
+"DOWNLOAD_CAPTIONS_DESC" = "Save subtitles as .VTT file";
+"DOWNLOAD_AUDIO" = "Download audio";
+"DOWNLOAD_AUDIO_DESC" = "Download audio as .m4a file";
+"DOWNLOAD_VIDEO" = "Download video";
+"DOWNLOAD_VIDEO_DESC" = "Choose video quality";
+"OPEN_VID_BEFORE" = "Open a video before using the download manager";
+"NO_CAPTIONS_URL" = "No valid caption URLs found";
+"NO_AUDIO_URL" = "No audio URL found";
+"NO_STREAM_URL" = "No stream URL found";
+"CAPTIONS_FAILED" = "Failed to download captions";
+"NO_CAPTIONS" = "No captions available for this video";
+"NO_AUDIO_STREAM_FOUND" = "No audio streams found";
+"NO_VID_AUDIO_STREAM_FOUND" = "No video/audio streams found";
+"COPIED_VID_INFO" = "Copied all video informations";
+"SAVED_TO_PHOTOS" = "Saved to Photos";
+"SAVE_FAILED" = "Save failed";
+"PHOTO_ACCESS_DENINED" = "Photos access denied";
+"THUMBNAIL_FAILED" = "Thumbnail failed";
+"DOWNLOADING_THUMBNAIL" = "Downloading thumbnail...";
+"DOWNLOADING_AUDIO" = "Downloading audio...";
+"DOWNLOADING_VIDEO" = "Downloading video...";
+"DOWNLOADING_CAPTIONS" = "Downloading captions...";
+"NO_THUMBNAIL_FOUND" = "No thumbnail found";
+"DOWNLOAD_FAILED" = "Download failed";
+"AUDIO_SAVED" = "Audio saved";
+"DOWNLOAD_COMPLETED" = "Download completed";
+"CANNOT_SAVE_TO_PHOTOS" = "Cannot save to Photos";
+"FINA_VIDEO" = "Finalizing video...";
+"MERGING_VID_FALLBACK" = "Merging video with fallback...";
+"MERGING_VID" = "Merging video...";
+"CONVERTING" = "Converting to";
+"ALREADY_DOWNLOADING" = "Already downloading";
+"DOWNLOAD_CANCELLED" = "Download cancelled";
+"CANCEL" = "Cancel";
+"TRIMMING_AUDIO" = "Trimming audio...";
+"COPY_THUMBNAIL" = "Copy thumbnail";
+"COPY_THUMBNAIL_DESC" = "Copy to clipboard";
+"SHOW_THUMBNAIL" = "Show thumbnail";
+"SHOW_THUMBNAIL_DESC" = "Show in a dialog";
+"THUMBNAIL_OPTIONS" = "Download thumbnail";
+"THUMBNAIL_OPTIONS_DESC" = "Show more options";
+"COPIED_TO_CLIPBOARD" = "Copied to clipboard";
+"COPY" = "Copy";
"APPEARANCE" = "Aspetto";
"OLED_THEME" = "OLED dark mode";
@@ -46,6 +123,8 @@
"OLED_KEYBOARD_DESC" = "La tastiera usa un nero puro (OLED) per la modalità scura.";
"NAVBAR" = "Barra di navigazione";
+"STICKY_NAVBAR" = "Sticky Navigation bar";
+"STICKY_NAVBAR_DESC" = "Pins the Navigation bar so that it remains visible when scrolling down.";
"HIDE_YT_LOGO" = "Nascondi logo YouTube";
"HIDE_YT_LOGO_DESC" = "Nasconde il logo YouTube nella barra di navigazione.";
"PREMIUM_LOGO" = "Usa il logo YouTube Premium";
@@ -58,36 +137,80 @@
"HIDE_VOICE_SEARCH_BUTTON_DESC" = "Nasconde il pulsante ricerca vocale nella barra di navigazione.";
"HIDE_CAST_BUTTON_NAVBAR" = "Nascondi pulsante cast";
"HIDE_CAST_BUTTON_NAVBAR_DESC" = "Nasconde il pulsante cast nella barra di navigazione.";
+"USE_ANOTHER_MINIPLAYER" = "Uses another miniplayer style";
+"USE_ANOTHER_MINIPLAYER_DESC" = "Uses another miniplayer style redesigned from older YT versions.";
"FEED" = "Feed";
"HIDE_SUBBAR" = "Nascondi barra superiore";
"HIDE_SUBBAR_DESC" = "Nasconde la barra in alto al feed.";
+"HIDE_HORI_SHELF" = "Remove horizontal feeds";
+"HIDE_HORI_SHELF_DESC" = "Removes all horizontal feeds (News, Continue Watching, Posts collection, etc.) from the feed, except Playables.";
"HIDE_MUSIC_PLAYLISTS" = "Nascondi il generatore di playlist musicali";
"HIDE_MUSIC_PLAYLISTS_DESC" = "Nasconde il generatore di playlist musicali (Scegli brani musicali tra tre) nel feed.";
"HIDE_FEED_POST" = "Nascondi post";
"HIDE_FEED_POST_DESC" = "Nasconde i post nel feed.";
+"HIDE_SURVEYS" = "Hide surveys";
+"HIDE_SURVEYS_DESC" = "Hide video surveys that YouTube shows sometimes.";
"HIDE_SHORTS_SHELF" = "Nascondi il riquadro degli Shorts";
"HIDE_SHORTS_SHELF_DESC" = "Nasconde il riquadro degli Shorts nel feed, anche nella scheda Iscrizioni.";
+"KEEP_SHORTS_SUBSCRIPT" = "Keep Shorts in Subscriptions";
+"KEEP_SHORTS_SUBSCRIPT_DESC" = "Prevents removing Shorts from Subscriptions tab.";
"HIDE_SEARCH_HISTORY" = "Nascondi cronologia ricerche";
"HIDE_SEARCH_HISTORY_DESC" = "Nasconde la cronologia delle ricerche precedenti e i suggerimenti quando si usa la barra di ricerca.\nNOTA: La tua cronologia delle ricerche sarà ancora accessibile negli altri client YouTube.";
-"HIDE_SUB_BUTTON" = "Nascondi pulsante Iscriviti";
-"HIDE_SUB_BUTTON_DESC" = "Nasconde il pulsante iscriviti nei canali e nelle descrizioni dei video.";
-"HIDE_SHOP_BUTTON" = "Nascondi pulsante Shopping";
-"HIDE_SHOP_BUTTON_DESC" = "Nasconde il pulsante shopping/negozio nelle descrizioni dei canali.";
-"HIDE_MEMBER_BUTTON" = "Nascondi pulsante Abbonati";
-"HIDE_MEMBER_BUTTON_DESC" = "Nasconde il pulsante 'Abbonati' nei canali e nelle descrizioni dei video.";
+"HIDE_PLAYABLES" = "Remove playables";
+"HIDE_PLAYABLES_DESC" = "Removes playables (mini-games) from the feed.";
"PLAYER" = "Player";
+"PLAYBACK_SPEED" = "Playback speed";
+"CONTROL_CENTER" = "Control Center";
+"QUALITY_WIFI" = "Playback quality on WiFi";
+"QUALITY_WIFI_DESC" = "Automatically applies the selected quality when streaming over Wi-Fi.";
+"QUALITY_CELLULAR" = "Playback quality on Cellular";
+"QUALITY_CELLULAR_DESC" = "Automatically applies the selected quality when streaming over mobile data.";
+"QUALITY_LOW_POWER" = "Playback quality on Power saver mode";
+"QUALITY_LOW_POWER_DESC" = "Automatically applies the selected quality when when Low power mode is enabled on your device.";
+"DEFAULT" = "Default";
+"BEST" = "Best";
+"AUDIO_TRACK" = "Preferred audio track";
+"ORIGINAL" = "Original";
+"SELECT_MANUALLY" = "Select manually";
+"AUDIO_TRACK_SELECT" = "Selected audio track";
+"AUDIO_TRACK_SELECT_DESC" = "Specifies the preferred audio track language to use for playback, if available.";
+"NO_AUTO_DUBBED" = "Uses original audio track";
+"NO_AUTO_DUBBED_DESC" = "Uses original audio track if the preferred language track is auto-dubbed or not found.";
+"CAPTION_TRACK" = "Preferred captions";
+"CAPTION_TRACK_SELECT" = "Selected captions";
+"CAPTION_TRACK_SELECT_DESC" = "Specifies the preferred captions language to display, if available.";
+"DISABLES_CAPTION_TRACK" = "Automatically disable captions";
+"DISABLES_CAPTION_TRACK_DESC" = "Automatically Disable captions if the preferred language is auto-generated or not found.";
+"DEFAULT_SPEED" = "Default playback rate";
+"DEFAULT_SPEED_DESC" = "Overrides the default playback speed.";
+"SPEED_BUTTON" = "Speed controls button";
+"SPEED_BUTTON_DESC" = "Displays a speed controls button on overlay.";
+"MUTE_BUTTON" = "Mute button";
+"MUTE_BUTTON_DESC" = "Displays a button to mute or unmute the volume on the overlay.";
+"SHARE_BUTTON" = "Share button";
+"SHARE_BUTTON_DESC" = "Displays a button to share the video link on the overlay.";
+"LOOP_BUTTON" = "Loop button";
+"LOOP_BUTTON_DESC" = "Displays a button to loop the video on the overlay.";
+"CAPTION_BUTTON" = "Captions button";
+"CAPTION_BUTTON_DESC" = "Displays a button to copy the video captions on the overlay.";
+"SKIP_BACKWARD" = "Skip backward";
+"SKIP_BACKWARD_DESC" = "Replaces the previous track button in Control Center with a skip backward button by a set number of seconds.";
+"SKIP_FORWARD" = "Skip forward";
+"SKIP_FORWARD_DESC" = "Replaces the next track button in Control Center with a skip forward button by a set number of seconds.";
+"REWIND_SECONDS" = "Rewind seconds";
+"FORWARD_SECONDS" = "Fast-forward seconds";
+"HOLD_TO_SPEED" = "Hold to speed";
+"HOLD_TO_SPEED_DESC" = "Uses custom hold to speed playback rate.";
"HIDE_AUTOPLAY" = "Nascondi interruttore riproduzione automatica";
"HIDE_AUTOPLAY_DESC" = "Nasconde l'interruttore della riproduzione automatica nel player.";
"HIDE_CAPTIONS_BUTTON" = "Nascondi pulsante sottotitoli";
"HIDE_CAPTIONS_BUTTON_DESC" = "Nasconde il pulsante dei sottotitoli nel player.";
"HIDE_CAST_BUTTON_PLAYER" = "Nascondi pulsante cast";
"HIDE_CAST_BUTTON_PLAYER_DESC" = "Nasconde il pulsante cast nel player.";
-"HIDE_PREV_BUTTON" = "Nascondi pulsante precedente/indietro";
-"HIDE_PREV_BUTTON_DESC" = "Nasconde il pulsante precedente/indietro nel player.";
-"HIDE_NEXT_BUTTON" = "Nascondi pulsante successivo/avanti";
-"HIDE_NEXT_BUTTON_DESC" = "Nasconde il pulsante successivo/avanti nel player.";
+"HIDE_NEXT_AND_PREV_BUTTON" = "Hide previous and next buttons";
+"HIDE_NEXT_AND_PREV_BUTTON_DESC" = "Hide previous and next buttons in the video overlay.";
"REPLACE_PREVNEXT_BUTTONS" = "Replace Previous/Next buttons";
"REPLACE_PREVNEXT_BUTTONS_DESC" = "Replaces the Previous and Next video buttons in the player with Rewind and Fast Forward buttons.";
"REMOVE_DARK_OVERLAY" = "Rimuovi overlay scuro";
@@ -102,6 +225,12 @@
"HIDE_PAID_OVERLAY_DESC" = "Nasconde l'avviso 'Include promozioni a pagamento' nel player.";
"HIDE_WATERMARK" = "Nascondi filigrana";
"HIDE_WATERMARK_DESC" = "Nasconde la filigrana del canale in basso nel player.";
+"PAUSE_ON_OVERLAY" = "Pause on overlay";
+"PAUSE_ON_OVERLAY_DESC" = "Sets playback on pause if overlay appears.";
+"DISABLES_SNAP_TO_CHAPTER" = "Disable snap to chapters";
+"DISABLES_SNAP_TO_CHAPTER_DESC" = "Prevents the scrubbar stick to the chapters.";
+"DISABLES_ENGAGE_PANEL" = "Disables engagement panel in fullscreen";
+"DISABLES_ENGAGE_PANEL_DESC" = "Prevent engagement panel showing in fullscreen.";
"GESTURES" = "Abilita gestures";
"GESTURES_DESC" = "Swipe up or down on the left/right edges of the video screen to adjust brightness, volume, or playback speed.";
"GESTURE_AREA" = "Gesture activation area";
@@ -136,6 +265,8 @@
"DISABLES_SHOW_REMAINING_DESC" = "Disabilita la funzione che mostra quanto tempo manca alla fine del video toccando il timestamp.";
"ALWAYS_SHOW_REMAINING" = "Mostra sempre il tempo rimanente";
"ALWAYS_SHOW_REMAINING_DESC" = "Assicura che il tempo rimanente sia sempre visualizzato per impostazione predefinita nel player.";
+"COPY_TIMESTAMP_ON_PAUSE" = "Copy timestamped links";
+"COPY_TIMESTAMP_ON_PAUSE_DESC" = "Allows to copy timestamped link to the clipboard by pressing pause button.";
"SHOW_REMAINING_EXTRA" = "Show extra remaining time";
"SHOW_REMAINING_EXTRA_DESC" = "Adds the estimated finish time to the label in the video overlay.";
"HIDE_FULLSCREEN_ACTIONS" = "Nascondi azioni a schermo intero";
@@ -148,93 +279,95 @@
"HIDE_CONTENT_WARNING_DESC" = "Salta automaticamente l'avviso per contenuti sensibili in alcuni video.";
"AUTO_FULLSCREEN" = "Schermo intero automatico";
"AUTO_FULLSCREEN_DESC" = "Entra automaticamente in modalità schermo intero all'avvio della riproduzione.";
-"AUTO_DISABLES_CAPTION" = "Auto disable captions";
-"AUTO_DISABLES_CAPTION_DESC" = "Automatically turns off closed captions (CC) whenever a new video starts playing.";
"PORTRAIT_FULLSCREEN" = "Modalità schermo intero verticale";
"PORTRAIT_FULLSCREEN_DESC" = "Abilita il supporto alla modalità schermo intero verticale.";
"OLD_QUALITY_PICKER" = "Usa vecchio selettore qualità";
"OLD_QUALITY_PICKER_DESC" = "Ripristina il menu di selezione della qualità video alla versione classica (elenca subito tutte le risoluzioni).";
"EXTRA_SPEED" = "Opzioni di velocità extra";
"EXTRA_SPEED_DESC" = "Aggiunge più opzioni di velocità di riproduzione video fino a 10x.";
-"DISABLE_HINTS" = "Disable hints";
-"DISABLE_HINTS_DESC" = "Disable the tutorial hints and interactive pop-ups.";
"FORCE_MINIPLAYER" = "Force miniplayer";
"FORCE_MINIPLAYER_DESC" = "Allows all videos, including those made for kids, to play in the miniplayer when dismissing the player.";
"FORCE_SEEKBAR" = "Always show progress bar";
"FORCE_SEEKBAR_DESC" = "Ensures the progress bar (seekbar) remains visible at all times while in fullscreen mode.";
-"HIDE_LIKE_BUTTON" = "Nascondi pulsante Mi piace";
-"HIDE_LIKE_BUTTON_DESC" = "Nasconde il pulsante Mi piace nella barra delle azioni sotto il video.";
-"HIDE_DISLIKE_BUTTON" = "Nascondi pulsante Non mi piace";
-"HIDE_DISLIKE_BUTTON_DESC" = "Nasconde il pulsante Non mi piace nella barra delle azioni sotto il video.";
-"HIDE_SHARE_BUTTON" = "Nascondi pulsante Condividi";
-"HIDE_SHARE_BUTTON_DESC" = "Nasconde il pulsante Condividi nella barra delle azioni sotto il video.";
-"HIDE_DOWNLOAD_BUTTON" = "Nascondi pulsante Scarica";
-"HIDE_DOWNLOAD_BUTTON_DESC" = "Nasconde il pulsante Scarica nella barra delle azioni sotto il video.";
-"HIDE_CLIP_BUTTON" = "Nascondi pulsante Clip";
-"HIDE_CLIP_BUTTON_DESC" = "Nasconde il pulsante Clip nella barra delle azioni sotto il video.";
-"HIDE_REMIX_BUTTON" = "Nascondi pulsante Remix";
-"HIDE_REMIX_BUTTON_DESC" = "Nasconde il pulsante Remix nella barra delle azioni sotto il video.";
-"HIDE_SAVE_BUTTON" = "Nascondi pulsante Salva";
-"HIDE_SAVE_BUTTON_DESC" = "Nasconde il pulsante Salva nella barra delle azioni sotto il video.";
+"TAP_TO_SEEK" = "Tap to seek";
+"TAP_TO_SEEK_DESC" = "Quickly jump to any point by tapping the progress bar.";
+"DISABLES_ZOOM" = "Disable free zoom gesture";
+"DISABLES_ZOOM_DESC" = "Disables new free zoom gestures.";
+"USES_24_HOURS_TIME" = "24-hour format";
+"USES_24_HOURS_TIME_DESC" = "Shows end time in 24-hour format.";
+"PAUSE_TWO_FINGERS" = "Pause using two fingers";
+"PAUSE_TWO_FINGERS_DESC" = "Pause/Resume playback by tapping on the screen using two fingers.";
+"HIDE_ACTION_BAR" = "Hide actions bar";
+"HIDE_ACTION_BAR_DESC" = "Removes the bar containing the like, dislike, share buttons etc. below the video player.";
+"HIDE_COMMENTS_SECTION" = "Hide comments section";
+"HIDE_COMMENTS_SECTION_DESC" = "Completely removes the entire comments section.";
+"HIDE_COMMENTS_PREVIEW" = "Hide preview comments";
+"HIDE_COMMENTS_PREVIEW_DESC" = "Removes the preview box for the top comment while keeping the full section expandable.";
"SHORTS" = "Shorts";
-"HIDE_SHORTS_LIKE_BUTTON" = "Nascondi pulsante Mi piace";
-"HIDE_SHORTS_LIKE_BUTTON_DESC" = "Nasconde il pulsante Mi piace nel player degli Shorts.";
-"HIDE_SHORTS_DISLIKE_BUTTON" = "Nascondi pulsante Non mi piace";
-"HIDE_SHORTS_DISLIKE_BUTTON_DESC" = "Nasconde il pulsante Non mi piace nel player degli Shorts.";
-"HIDE_SHORTS_COMMENT_BUTTON" = "Nascondi pulsante Commenti";
-"HIDE_SHORTS_COMMENT_BUTTON_DESC" = "Nasconde il pulsante dei commenti nel player degli Shorts.";
-"HIDE_SHORTS_SHARE_BUTTON" = "Nascondi pulsante Condividi";
-"HIDE_SHORTS_SHARE_BUTTON_DESC" = "Nasconde il pulsante Condividi nel player degli Shorts.";
-"HIDE_SHORTS_REMIX_BUTTON" = "Nascondi pulsante Remix";
-"HIDE_SHORTS_REMIX_BUTTON_DESC" = "Nasconde il pulsante Remix nel player degli Shorts.";
-"HIDE_METADATA_BUTTON" = "Nascondi pulsante metadati audio";
-"HIDE_METADATA_BUTTON_DESC" = "Nasconde il pulsante che mostra la sorgente audio o i metadati musicali nel player degli Shorts.";
+"SHORTS_ONLY" = "Shorts only mode";
+"SHORTS_ONLY_DESC" = "Limits YouTube functionality to viewing Shorts only.";
+"SHORTS_ACTION" = "Shorts action after the video ends";
+"LOOP" = "Loop video";
+"SKIP_TO_NEXT_SHORTS" = "Skip to next video";
+"PAUSE_SHORTS" = "Stop video";
+"REMOVE_LIVE_SHORTS" = "Remove live videos";
+"REMOVE_LIVE_SHORTS_DESC" = "Removes live videos from the feed.";
"HIDE_SHORTS_PRODUCT" = "Nascondi prodotti";
"HIDE_SHORTS_PRODUCT_DESC" = "Nasconde i prodotti taggati e i link per lo shopping negli Shorts.";
"HIDE_SHORTS_RECBAR" = "Nascondi barra dei suggerimenti";
"HIDE_SHORTS_RECBAR_DESC" = "Nasconde la barra delle azioni suggerite nel player degli Shorts (es. Prova questo audio, Shopping, Anteprima commento).";
-"HIDE_SHORTS_COMMIT" = "Nascondi barra commissioni";
-"HIDE_SHORTS_COMMIT_DESC" = "Nasconde la barra che indica che il video ha commissioni nel player degli Shorts.";
-"HIDE_SHORTS_SUBSCRIPT_BUTTON" = "Nascondi pulsante iscrizioni";
-"HIDE_SHORTS_SUBSCRIPT_BUTTON_DESC" = "Nasconde il pulsante Iscrizioni in alto nel player degli Shorts.";
-"HIDE_SHORTS_LIVE_BUTTON" = "Nascondi pulsante live";
-"HIDE_SHORTS_LIVE_BUTTON_DESC" = "Nasconde il pulsante Live in alto nel player degli Shorts.";
-"HIDE_SHORTS_LENS_BUTTON" = "Nascondi pulsante lens";
-"HIDE_SHORTS_LENS_BUTTON_DESC" = "Nasconde il pulsante Lens in alto nel player degli Shorts.";
-"HIDE_SHORTS_TRENDS_BUTTON" = "Nascondi pulsante tendenze";
-"HIDE_SHORTS_TRENDS_BUTTON_DESC" = "Nasconde il pulsante Tendenze in alto nel player degli Shorts.";
-"HIDE_SHORTS_TO_VIDEO" = "Nascondi link al video completo";
-"HIDE_SHORTS_TO_VIDEO_DESC" = "Nasconde il link che reindirizza da uno Short al video completo.";
"ENABLES_SHORTS_QUALITY" = "Abilita selettore qualità negli Shorts";
"ENABLES_SHORTS_QUALITY_DESC" = "Forza l'abilitazione del menu di selezione della qualità video negli Shorts.";
"SHOW_SHORTS_SEEKBAR" = "Mostra sempre barra di scorrimento";
"SHOW_SHORTS_SEEKBAR_DESC" = "Assicura che la barra di avanzamento sia sempre visibile in basso nel player degli Shorts.";
+"REMOVE_POSTS_SHORTS" = "Remove posts";
+"REMOVE_POSTS_SHORTS_DESC" = "Remove posts from the feed.";
+"SHORTS_FULLSCREEN" = "Pinch to Fullscreen";
+"SHORTS_FULLSCREEN_DESC" = "Manages visibility of the overlay with pinch in and pinch out gestures, displaying Shorts in fullscreen mode.";
"TABBAR" = "Barra delle schede";
"DEFAULT_TAB" = "Scheda di avvio predefinita";
-"DEFAULT_TAB_DESC" = "Puoi selezionare la scheda che desideri visualizzare all'apertura dell'app.";
-"HOME_NAME" = "Home";
-"SUBSCRIPT_NAME" = "Iscrizioni";
-"LIB_NAME" = "Raccolta (Tu)";
+"FORSTED_TAB_BAR" = "Translucent tab bar";
+"ENABLED" = "Enabled";
"HIDE_TAB_INDI" = "Nascondi indicatori schede";
"HIDE_TAB_INDI_DESC" = "Nasconde tutti gli indicatori delle schede nella barra delle schede.";
"HIDE_TAB_LABELS" = "Nascondi etichette schede";
"HIDE_TAB_LABELS_DESC" = "Nasconde tutte le etichette delle schede nella barra delle schede.";
-"HIDE_HOME_TAB" = "Nascondi scheda Home";
-"HIDE_HOME_TAB_DESC" = "Nasconde la scheda Home nella barra delle schede.";
-"HIDE_SHORTS_TAB" = "Nascondi scheda Shorts";
-"HIDE_SHORTS_TAB_DESC" = "Nasconde la scheda Shorts nella barra delle schede.";
-"HIDE_CREATE_BUTTON" = "Nascondi pulsante Crea";
-"HIDE_CREATE_BUTTON_DESC" = "Nasconde il pulsante Crea nella barra delle schede.";
-"HIDE_SUBSCRIPT_TAB" = "Nascondi scheda Iscrizioni";
-"HIDE_SUBSCRIPT_TAB_DESC" = "Nasconde la scheda Iscrizioni nella barra delle schede.";
+"HOME_TAB" = "Home";
+"SHORTS_TAB" = "Shorts";
+"CREATE_TAB" = "Create";
+"SUBSCRIPTIONS_TAB" = "Iscrizioni";
+"LIBRARY_TAB" = "Raccolta (Tu)";
+"HISTORY_TAB" = "History";
+"GAMING_TAB" = "Gaming";
+"SPORTS_TAB" = "Sports";
+"NOTI_TAB" = "Notifications";
+"NEWS_TAB" = "News";
+"MUSIC_TAB" = "Music";
+"WATCH_LATER_TAB" = "Watch later";
+"PLAYLIST_TAB" = "Playlists";
+"LIKE_TAB" = "Liked videos";
+"LIVE_TAB" = "Live";
+"POST_TAB" = "Posts";
+"VIDEO_TAB" = "Your videos";
+"MOVIE_TAB" = "Movies";
+"COURSE_TAB" = "Courses";
+"MINIGAME_TAB" = "Playables";
+"MANAGE_TABS" = "Manage tabs";
+"MANAGE_TABS_DESC" = "Reorder and toggle tabs";
+"TAB_REORDER_HINT" = "Drag to reorder. Toggle to show/hide.\nTab settings are also accessible by long-pressing the first tab in the tab bar.";
+"TAB_LIMIT" = "Tab limit";
+"TAB_LIMIT_DESC" = "Only 6 tabs are allowed.";
+"ZERO_TAB_DESC" = "At least 1 tab has to be enabled.";
"MISCELLANEOUS" = "Varie";
"BACKGROUND_PLAYBACK" = "Riproduzione in background";
"BACKGROUND_PLAYBACK_DESC" = "Abilita la riproduzione in background per i video.";
"DISABLES_SHORTS_PIP" = "Disabilita PiP negli Shorts";
"DISABLES_SHORTS_PIP_DESC" = "Tenta di disabilitare il PiP negli Shorts, potrebbe non funzionare al 100%.";
+"DISABLE_HINTS" = "Disable hints";
+"DISABLE_HINTS_DESC" = "Disable the tutorial hints and interactive pop-ups.";
"BLOCK_UPGRADE_DIALOGS" = "Blocca avvisi di aggiornamento";
"BLOCK_UPGRADE_DIALOGS_DESC" = "Blocca gli avvisi di YouTube che chiedono di aggiornare l'app.";
"ARE_YOU_THERE_DIALOG" = "Nascondi avviso 'Ci sei ancora?'";
@@ -247,7 +380,109 @@
"DISABLES_SNACK_BAR_DESC" = "Disabilita la barra di notifica (snack bar) che appare premendo alcuni pulsanti o eseguendo azioni nell'app.";
"HIDE_STARTUP_ANIMATIONS" = "Nascondi animazioni di avvio di YouTube";
"HIDE_STARTUP_ANIMATIONS_DESC" = "Nasconde le animazioni di avvio di YouTube all'apertura dell'app.";
-"HIDE_PLAY_IN_NEXT_QUEUE" = "Nascondi 'Riproduci come successivo nella coda'";
-"HIDE_PLAY_IN_NEXT_QUEUE_DESC" = "Nasconde l'opzione 'Riproduci come successivo nella coda' nel menu a comparsa.";
"HIDE_LIKE_DISLIKE_VOTES" = "Nascondi voti mi piace/non mi piace";
"HIDE_LIKE_DISLIKE_VOTES_DESC" = "Nasconde i messaggi che appaiono quando metti mi piace/non mi piace ai video.";
+"FLYOUT_MENU" = "Flyout menu";
+"REMOVE_PLAY_IN_NEXT_QUEUE_OPTION" = "Removes 'Play next in queue' option";
+"REMOVE_PLAY_IN_NEXT_QUEUE_OPTION_DESC" = "Removes 'Play next in queue' option from the flyout menu.";
+"REMOVE_DOWNLOAD_OPTION" = "Removes 'Download video' option";
+"REMOVE_DOWNLOAD_OPTION_DESC" = "Removes 'Download video' option from the flyout menu.";
+"REMOVE_WATCH_LATER_OPTION" = "Removes 'Save to Watch later' option";
+"REMOVE_WATCH_LATER_OPTION_DESC" = "Removes 'Save to Watch Later' option from the flyout menu.";
+"REMOVE_SAVE_OPTION" = "Removes 'Save to playlist' option";
+"REMOVE_SAVE_OPTION_DESC" = "Removes 'Save to playlist' option from the flyout menu.";
+"REMOVE_REMOVE_FROM_PLAYLIST_OPTION" = "Removes 'Remove from playlist' option";
+"REMOVE_REMOVE_FROM_PLAYLIST_OPTION_DESC" = "Removes 'Remove from playlist' option from the flyout menu.";
+"REMOVE_SHARE_OPTION" = "Removes 'Share' option";
+"REMOVE_SHARE_OPTION_DESC" = "Removes 'Share' option from the flyout menu.";
+"REMOVE_NOT_INTERESTED_OPTION" = "Removes 'Not interested' option";
+"REMOVE_NOT_INTERESTED_OPTION_DESC" = "Removes 'Not interested' option from the flyout menu.";
+"REMOVE_DONT_RECOMMEND_OPTION" = "Removes 'Don't recommend channel' option";
+"REMOVE_DONT_RECOMMEND_OPTION_DESC" = "Removes 'Don't recommend channel' option from the flyout menu.";
+"REMOVE_INFO_OPTION" = "Removes 'Why are you seeing this?' option";
+"REMOVE_INFO_OPTION_DESC" = "Removes 'Why are you seeing this?' option from the flyout menu.";
+"REMOVE_FILTER_OPTION" = "Removes 'Search filters' option";
+"REMOVE_FILTER_OPTION_DESC" = "Removes 'Search filters' option from the flyout menu.";
+"REMOVE_REPORT_OPTION" = "Removes 'Report' option";
+"REMOVE_REPORT_OPTION_DESC" = "Removes 'Report' option from the flyout menu.";
+"REMOVE_YOUTUBE_MUSIC_OPTION" = "Removes 'Listen with YouTube Music' option";
+"REMOVE_YOUTUBE_MUSIC_OPTION_DESC" = "Removes 'Listen with YouTube Music' option from the flyout menu.";
+"REMOVE_FEED_BACK_OPTION" = "Removes 'Send feedback' option";
+"REMOVE_FEED_BACK_OPTION_DESC" = "Removes 'Send feedback' option from the flyout menu.";
+"REMOVE_CAST_OPTION" = "Removes 'Watch on TV' option";
+"REMOVE_CAST_OPTION_DESC" = "Removes 'Watch on TV' option from the flyout menu.";
+"REMOVE_SHUFFLE_OPTION" = "Removes 'Shuffle' option";
+"REMOVE_SHUFFLE_OPTION_DESC" = "Removes 'Shuffle' option from the flyout menu.";
+"REMOVE_UN_SUB_OPTION" = "Removes 'Unsubscribe' option";
+"REMOVE_UN_SUB_OPTION_DESC" = "Removes 'Unsubscribe' option from the flyout menu.";
+"REMOVE_HIDE_FROM_PLAYLIST_OPTION" = "Removes 'Hide video from playlist' option";
+"REMOVE_HIDE_FROM_PLAYLIST_OPTION_DESC" = "Removes 'Hide video from playlist' option from the flyout menu.";
+"REMOVE_HELP_OPTION" = "Removes 'Help & feedback' option";
+"REMOVE_HELP_OPTION_DESC" = "Removes 'Help & feedback' option from the flyout menu.";
+"REMOVE_NOTIFY_OPTION" = "Removes 'Notify me' option";
+"REMOVE_NOTIFY_OPTION_DESC" = "Removes 'Notify me' option from the flyout menu.";
+"REMOVE_CLEARSCREEN_OPTION" = "Removes 'Clear Screen' option";
+"REMOVE_CLEARSCREEN_OPTION_DESC" = "Removes 'Clear Screen' option from the flyout menu.";
+"DISABLES_RTL" = "Disable RTL formatting";
+"DISABLES_RTL_DESC" = "Forcefully displays text in left-to-right (LTR) format for languages that are initially displayed in right-to-left (RTL).";
+"HIDE_COMMU_GUIDE" = "Remove guidelines";
+"HIDE_COMMU_GUIDE_DESC" = "Removes pinned guidelines from the comments section.";
+"DEVICE_UI" = "Interface style";
+
+"SB_DESCRIPTION" = "Skip sponsored segments automatically using community-submitted data.";
+"SB_ENABLE" = "Enable SponsorBlock";
+"SB_ENABLE_DESC" = "Activate the SponsorBlock extension to skip sponsored segments.";
+"SB_SHOW_BUTTON" = "Show overlay button";
+"SB_SHOW_BUTTON_DESC" = "Display a SponsorBlock toggle button in the player overlay.";
+"SB_SHOW_NOTIFICATIONS" = "Show notifications";
+"SB_SHOW_NOTIFICATIONS_DESC" = "Display notifications when segments are skipped. Notifications for manual-skip categories are always shown.";
+"SB_HAPTIC_FEEDBACK" = "Haptic feedback";
+"SB_HAPTIC_FEEDBACK_DESC" = "Provide haptic feedback when a segment is skipped.";
+"SB_SEGMENTS_IN_PLAYER" = "Segments in player";
+"SB_SEGMENTS_IN_PLAYER_DESC" = "Display colored segments on the progress bar of the main video player.";
+"SB_SEGMENTS_IN_FEED" = "Segments in feed";
+"SB_SEGMENTS_IN_FEED_DESC" = "Display colored segments on the progress bar of players in the feed.";
+"SB_SEGMENTS_IN_MINIPLAYER" = "Segments in mini-player";
+"SB_SEGMENTS_IN_MINIPLAYER_DESC" = "Display colored segments on the progress bar of the mini-player.";
+"SB_SHOW_DURATION" = "Show duration without segments";
+"SB_SHOW_DURATION_DESC" = "Display the total video duration excluding skippable segments.";
+"SB_CATEGORIES_HEADER" = "Segment categories";
+"SB_SEGMENT_COLOR" = "Segment color";
+
+"SB_ACTION_DISABLE" = "Disable";
+"SB_ACTION_AUTO_SKIP" = "Auto-skip";
+"SB_ACTION_ASK" = "Ask before skipping";
+"SB_ACTION_DISPLAY" = "Show on seek bar only";
+"SB_ACTION_SKIP_TO" = "Skip to segment";
+
+"SB_CAT_sponsor" = "Sponsor";
+"SB_CAT_sponsor_DESC" = "Paid promotion for a product or service not directly related to the creator.";
+"SB_CAT_intro" = "Intro";
+"SB_CAT_intro_DESC" = "Intermission or intro animation typically seen at the start of a video.";
+"SB_CAT_outro" = "Endcards";
+"SB_CAT_outro_DESC" = "Endcards, credits, or outros at the end of the video.";
+"SB_CAT_interaction" = "Interaction";
+"SB_CAT_interaction_DESC" = "Reminders to like, subscribe, or follow on social media.";
+"SB_CAT_selfpromo" = "Self-promotion";
+"SB_CAT_selfpromo_DESC" = "Unpaid promotion of the creator's own products, merchandise, or other content.";
+"SB_CAT_music_offtopic" = "Non-music";
+"SB_CAT_music_offtopic_DESC" = "Non-music sections in music videos (e.g. talking, introductions).";
+"SB_CAT_preview" = "Preview";
+"SB_CAT_preview_DESC" = "Recap or preview of content from another video or previous episode.";
+"SB_CAT_hook" = "Hook/Greetings";
+"SB_CAT_hook_DESC" = "Narrated trailers for the upcoming video, greetings and goodbyes.";
+"SB_CAT_poi_highlight" = "Highlight";
+"SB_CAT_poi_highlight_DESC" = "The key moment or highlight point of the video. Use 'Skip to' to jump directly to it.";
+"SB_CAT_filler" = "Filler";
+"SB_CAT_filler_DESC" = "Tangential content, jokes, or filler that is not directly related to the main topic.";
+
+"SB_SKIPPED" = "%@ segment has been skipped";
+"SB_DETECTED" = "%@ segment detected.\nWould you like to skip the segment?";
+"SB_SKIP_NOW" = "Skip";
+"SB_UNSKIP" = "Unskip";
+"SB_JUMPED_TO_HIGHLIGHT" = "Jumped to highlight";
+"SB_JUMP_TO_HIGHLIGHT" = "Highlight available. Jump to the point?";
+"SB_SEGMENT_COLOR_SUFFIX" = "Segment color";
+"SB_SECTION_MAIN" = "Main";
+"SB_SKIP_ALERT_DURATION" = "Skip alert duration";
+"SB_UNSKIP_ALERT_DURATION" = "Unskip alert duration";
\ No newline at end of file
diff --git a/layout/Library/Application Support/YouMod.bundle/ja.lproj/Localizable.strings b/layout/Library/Application Support/YouMod.bundle/ja.lproj/Localizable.strings
index bccaf550..2c1a8a0f 100644
--- a/layout/Library/Application Support/YouMod.bundle/ja.lproj/Localizable.strings
+++ b/layout/Library/Application Support/YouMod.bundle/ja.lproj/Localizable.strings
@@ -1,11 +1,18 @@
-"NOTE" = "NOTE: Some hiding features may leave a blank box.";
"SETTINGS" = "Settings";
+"INTERFACE" = "Interface";
+
+"LOOP_DISABLED" = "Loop disabled";
+"LOOP_ENABLED" = "Loop enabled";
"NEW_ISSUES" = "Report issues or request new features";
"NEW_ISSUES_DESC" = "Found bugs or want to request new features? Tap here.";
"SOURCE_CODES" = "Source codes";
"SOURCE_CODES_DESC" = "Take a look at the codes!";
+"EXTRA" = "Extra";
+"FIX_PLAYBACK_ISSUES" = "Fix playback issues (Temporary)";
+"FIX_PLAYBACK_ISSUES_DESC" = "Try to fix video playback issues for some users. Turning this on will make the player automatically reloads itself everytime it errors or fails to play the video.";
+
"PERFER_HEADER" = "YouMod perferences";
"PERFER" = "Perferences";
"IMPORT" = "Import settings";
@@ -24,6 +31,7 @@
"ERROR_NO_KEYS_IMPORT" = "No available keys to import";
"ERROR_NO_KEYS_EXPORT" = "No available keys to export";
"OVERRIDE" = "This will override the current settings. Do you want to continue?";
+"DISABLED" = "Disabled";
"CACHE" = "Cache management";
"CLEARCACHE" = "Clear cache";
@@ -32,12 +40,80 @@
"AUTO_CLEARCACHE_DESC" = "Automatically clears the app cache on startup.";
"DOWNLOADING" = "Downloading";
-"DOWNLOAD_MANAGER" = "Download Manager";
-"DOWNLOAD_MANAGER_DESC" = "Replaces YouTube's video downloader with YouMod one. With ablity to downloading videos, audios, captions, and thumbnails";
+"DOWNLOAD_MANAGER" = "Download manager";
+"DOWNLOAD_MANAGER_DESC" = "Replaces YouTube's video downloader with YouMod one. With ablity to downloading videos, audios, captions, and thumbnails.";
"DOWNLOAD_SAVE_PHOTOS" = "Automatically save to Photos";
"DOWNLOAD_SAVE_PHOTOS_DESC" = "Automatically saves downloaded videos and images directly to your Photos app.";
-"DOWNLOAD_DRC_AUDIO" = "Prefer DRC Audio";
-"DOWNLOAD_DRC_AUDIO_DESC" = "Prioritizes downloading audio tracks with Dynamic Range Compression for more consistent volume levels.";
+"ADD_SHORTS_DOWNLOAD" = "Adds a download button to Shorts";
+"ADD_SHORTS_DOWNLOAD_DESC" = "Adds a download button to Shorts video overlay.";
+"USE_ORIG_AUDIO" = "Automatically uses original audio track";
+"USE_ORIG_AUDIO_DESC" = "Automatically uses original audio track instead of showing a dialog.";
+"DOWNLOAD_SHORTS" = "Download Shorts";
+"DOWNLOAD_SHORTS_DESC" = "Choose Shorts quality";
+"COPY_VID_INFO" = "Copy video informations";
+"COPY_VID_INFO_DESC" = "Choose what to copy";
+"COPY_ALL_VID_INFO" = "Copy all";
+"COPY_ALL_VID_INFO_DESC" = "Copy all video informations";
+"COPY_AUTHOR" = "Copy author";
+"COPY_AUTHOR_DESC" = "Copy the video author";
+"COPY_TITLE" = "Copy title";
+"COPY_TITLE_DESC" = "Copy the video title";
+"COPY_DESCRIPTION" = "Copy description";
+"COPY_DESCRIPTION_DESC" = "Copy the video description";
+"COPY_URL" = "Copy video link";
+"COPY_URL_TIMESTAMP" = "Copy video link with timestamp";
+"URL_COPIED" = "Copied video link";
+"URL_TIMESTAMP_COPIED" = "Copied video link with timestamp";
+"ERROR_VIDEOID" = "Cannot get the video ID";
+"ERROR_ADS" = "Cannot copy video link while playing ads";
+"COPIED_AUTHOR" = "Copied video author";
+"COPIED_TITLE" = "Copied video title";
+"COPIED_DESCRIPTION" = "Copied video description";
+"SAVE_THUMBNAIL" = "Save thumbnail";
+"SAVE_THUMBNAIL_DESC" = "Save to Photos";
+"DOWNLOAD_CAPTIONS" = "Download captions";
+"DOWNLOAD_CAPTIONS_DESC" = "Save subtitles as .VTT file";
+"DOWNLOAD_AUDIO" = "Download audio";
+"DOWNLOAD_AUDIO_DESC" = "Download audio as .m4a file";
+"DOWNLOAD_VIDEO" = "Download video";
+"DOWNLOAD_VIDEO_DESC" = "Choose video quality";
+"OPEN_VID_BEFORE" = "Open a video before using the download manager";
+"NO_CAPTIONS_URL" = "No valid caption URLs found";
+"NO_AUDIO_URL" = "No audio URL found";
+"NO_STREAM_URL" = "No stream URL found";
+"CAPTIONS_FAILED" = "Failed to download captions";
+"NO_CAPTIONS" = "No captions available for this video";
+"NO_AUDIO_STREAM_FOUND" = "No audio streams found";
+"NO_VID_AUDIO_STREAM_FOUND" = "No video/audio streams found";
+"COPIED_VID_INFO" = "Copied all video informations";
+"SAVED_TO_PHOTOS" = "Saved to Photos";
+"SAVE_FAILED" = "Save failed";
+"PHOTO_ACCESS_DENINED" = "Photos access denied";
+"THUMBNAIL_FAILED" = "Thumbnail failed";
+"DOWNLOADING_THUMBNAIL" = "Downloading thumbnail...";
+"DOWNLOADING_AUDIO" = "Downloading audio...";
+"DOWNLOADING_VIDEO" = "Downloading video...";
+"DOWNLOADING_CAPTIONS" = "Downloading captions...";
+"NO_THUMBNAIL_FOUND" = "No thumbnail found";
+"DOWNLOAD_FAILED" = "Download failed";
+"COPY_THUMBNAIL" = "Copy thumbnail";
+"COPY_THUMBNAIL_DESC" = "Copy to clipboard";
+"SHOW_THUMBNAIL" = "Show thumbnail";
+"SHOW_THUMBNAIL_DESC" = "Show in a dialog";
+"THUMBNAIL_OPTIONS" = "Download thumbnail";
+"THUMBNAIL_OPTIONS_DESC" = "Show more options";
+"COPIED_TO_CLIPBOARD" = "Copied to clipboard";
+"DOWNLOAD_COMPLETED" = "Download completed";
+"CANNOT_SAVE_TO_PHOTOS" = "Cannot save to Photos";
+"FINA_VIDEO" = "Finalizing video...";
+"MERGING_VID_FALLBACK" = "Merging video with fallback...";
+"MERGING_VID" = "Merging video...";
+"CONVERTING" = "Converting to";
+"ALREADY_DOWNLOADING" = "Already downloading";
+"DOWNLOAD_CANCELLED" = "Download cancelled";
+"CANCEL" = "Cancel";
+"TRIMMING_AUDIO" = "Trimming audio...";
+"COPY" = "Copy";
"APPEARANCE" = "Appearance";
"OLED_THEME" = "OLED dark mode";
@@ -46,6 +122,8 @@
"OLED_KEYBOARD_DESC" = "Make the keyboard uses a pure black (OLED) color for dark theme.";
"NAVBAR" = "Navigation bar";
+"STICKY_NAVBAR" = "Sticky Navigation bar";
+"STICKY_NAVBAR_DESC" = "Pins the Navigation bar so that it remains visible when scrolling down.";
"HIDE_YT_LOGO" = "Hide YouTube logo";
"HIDE_YT_LOGO_DESC" = "Hide YouTube logo in the navigation bar.";
"PREMIUM_LOGO" = "Use YouTube Premium logo";
@@ -62,34 +140,78 @@
"FEED" = "Feed";
"HIDE_SUBBAR" = "Hide subbar";
"HIDE_SUBBAR_DESC" = "Hide subbar at the top of the feed.";
+"HIDE_HORI_SHELF" = "Remove horizontal feeds";
+"HIDE_HORI_SHELF_DESC" = "Removes all horizontal feeds (News, Continue Watching, Posts collection, etc.) from the feed, except Playables.";
"HIDE_MUSIC_PLAYLISTS" = "Hide music playlist generator";
"HIDE_MUSIC_PLAYLISTS_DESC" = "Hide music playlist generator (Select music tunes in three) in the feed.";
"HIDE_FEED_POST" = "Hide posts";
"HIDE_FEED_POST_DESC" = "Hide posts in feed.";
+"HIDE_SURVEYS" = "Hide surveys";
+"HIDE_SURVEYS_DESC" = "Hide video surveys that YouTube shows sometimes.";
"HIDE_SHORTS_SHELF" = "Hide Shorts shelf";
"HIDE_SHORTS_SHELF_DESC" = "Hide Shorts shelf in the feed, including in the Subscriptions tab.";
+"KEEP_SHORTS_SUBSCRIPT" = "Keep Shorts in Subscriptions";
+"KEEP_SHORTS_SUBSCRIPT_DESC" = "Prevents removing Shorts from Subscriptions tab.";
"HIDE_SEARCH_HISTORY" = "Hide search history";
"HIDE_SEARCH_HISTORY_DESC" = "Hide previous search history and suggestions when using the search bar.\nNOTE: Your search history will still be accessible in your other YouTube clients.";
-"HIDE_SUB_BUTTON" = "Hide Subscribe button";
-"HIDE_SUB_BUTTON_DESC" = "Hides the subscribe button in channels and video descriptions.";
-"HIDE_SHOP_BUTTON" = "Hide Shopping button";
-"HIDE_SHOP_BUTTON_DESC" = "Hides the shopping/store button in channels descriptions.";
-"HIDE_MEMBER_BUTTON" = "Hide Membership button";
-"HIDE_MEMBER_BUTTON_DESC" = "Hides the 'Join' button in channels and video descriptions.";
+"HIDE_PLAYABLES" = "Remove playables";
+"HIDE_PLAYABLES_DESC" = "Removes playables (mini-games) from the feed.";
"PLAYER" = "Player";
+"PLAYBACK_SPEED" = "Playback speed";
+"CONTROL_CENTER" = "Control Center";
+"PLAYER_ACTIONS" = "Player actions";
+"GESTURE_HEADER" = "Gestures";
+"QUALITY_WIFI" = "Playback quality on WiFi";
+"QUALITY_WIFI_DESC" = "Automatically applies the selected quality when streaming over Wi-Fi.";
+"QUALITY_CELLULAR" = "Playback quality on Cellular";
+"QUALITY_CELLULAR_DESC" = "Automatically applies the selected quality when streaming over mobile data.";
+"QUALITY_LOW_POWER" = "Playback quality on Power saver mode";
+"QUALITY_LOW_POWER_DESC" = "Automatically applies the selected quality when when Low power mode is enabled on your device.";
+"DEFAULT" = "Default";
+"BEST" = "Best";
+"AUDIO_TRACK" = "Preferred audio track";
+"ORIGINAL" = "Original";
+"SELECT_MANUALLY" = "Select manually";
+"AUDIO_TRACK_SELECT" = "Selected audio track";
+"AUDIO_TRACK_SELECT_DESC" = "Specifies the preferred audio track language to use for playback, if available.";
+"NO_AUTO_DUBBED" = "Uses original audio track";
+"NO_AUTO_DUBBED_DESC" = "Uses original audio track if the preferred language track is auto-dubbed or not found.";
+"CAPTION_TRACK" = "Preferred captions";
+"CAPTION_TRACK_SELECT" = "Selected captions";
+"CAPTION_TRACK_SELECT_DESC" = "Specifies the preferred captions language to display, if available.";
+"DISABLES_CAPTION_TRACK" = "Automatically disable captions";
+"DISABLES_CAPTION_TRACK_DESC" = "Automatically disable captions if the preferred language is auto-generated or not found.";
+"DEFAULT_SPEED" = "Default playback rate";
+"DEFAULT_SPEED_DESC" = "Overrides the default playback speed.";
+"SPEED_BUTTON" = "Speed controls button";
+"SPEED_BUTTON_DESC" = "Displays a speed controls button on overlay.";
+"MUTE_BUTTON" = "Mute button";
+"MUTE_BUTTON_DESC" = "Displays a button to mute or unmute the volume on the overlay.";
+"SHARE_BUTTON" = "Share button";
+"SHARE_BUTTON_DESC" = "Displays a button to share the video link on the overlay.";
+"LOOP_BUTTON" = "Loop button";
+"LOOP_BUTTON_DESC" = "Displays a button to loop the video on the overlay.";
+"CAPTION_BUTTON" = "Captions button";
+"CAPTION_BUTTON_DESC" = "Displays a button to copy the video captions on the overlay.";
+"HOLD_TO_SPEED" = "Hold to speed";
+"HOLD_TO_SPEED_DESC" = "Uses custom hold to speed playback rate.";
"HIDE_AUTOPLAY" = "Hide autoplay switch";
"HIDE_AUTOPLAY_DESC" = "Hide autoplay switch in the video overlay.";
"HIDE_CAPTIONS_BUTTON" = "Hide captions button";
"HIDE_CAPTIONS_BUTTON_DESC" = "Hide captions button in the video overlay.";
"HIDE_CAST_BUTTON_PLAYER" = "Hide cast button";
"HIDE_CAST_BUTTON_PLAYER_DESC" = "Hide cast button in the video overlay.";
-"HIDE_PREV_BUTTON" = "Hide previous/back button";
-"HIDE_PREV_BUTTON_DESC" = "Hide previous/back button in the video overlay.";
-"HIDE_NEXT_BUTTON" = "Hide next/skip button";
-"HIDE_NEXT_BUTTON_DESC" = "Hide next/skip button in the video overlay.";
+"HIDE_NEXT_AND_PREV_BUTTON" = "Hide previous and next buttons";
+"HIDE_NEXT_AND_PREV_BUTTON_DESC" = "Hide previous and next buttons in the video overlay.";
"REPLACE_PREVNEXT_BUTTONS" = "Replace Previous/Next buttons";
"REPLACE_PREVNEXT_BUTTONS_DESC" = "Replaces the Previous and Next video buttons in the player with Rewind and Fast Forward buttons.";
+"SKIP_BACKWARD" = "Skip backward";
+"SKIP_BACKWARD_DESC" = "Replaces the previous track button in Control Center with a skip backward button by a set number of seconds.";
+"SKIP_FORWARD" = "Skip forward";
+"SKIP_FORWARD_DESC" = "Replaces the next track button in Control Center with a skip forward button by a set number of seconds.";
+"REWIND_SECONDS" = "Rewind seconds";
+"FORWARD_SECONDS" = "Fast-forward seconds";
"REMOVE_DARK_OVERLAY" = "Remove dark overlay";
"REMOVE_DARK_OVERLAY_DESC" = "Remove dark overlay from the video overlay.";
"HIDE_END_SCREEN" = "Hide endscreen cards";
@@ -102,6 +224,12 @@
"HIDE_PAID_OVERLAY_DESC" = "Hide 'Includes paid promotion' in the video overlay.";
"HIDE_WATERMARK" = "Hide watermark";
"HIDE_WATERMARK_DESC" = "Hide channel watermark at the bottom of the video overlay.";
+"PAUSE_ON_OVERLAY" = "Pause on overlay";
+"PAUSE_ON_OVERLAY_DESC" = "Sets playback on pause if overlay appears.";
+"DISABLES_SNAP_TO_CHAPTER" = "Disable snap to chapters";
+"DISABLES_SNAP_TO_CHAPTER_DESC" = "Prevents the scrubbar stick to the chapters.";
+"DISABLES_ENGAGE_PANEL" = "Disables engagement panel in fullscreen";
+"DISABLES_ENGAGE_PANEL_DESC" = "Prevent engagement panel showing in fullscreen.";
"GESTURES" = "Enable gestures controls";
"GESTURES_DESC" = "Swipe up or down on the left/right edges of the video screen to adjust brightness, volume, or playback speed.";
"GESTURE_AREA" = "Gesture activation area";
@@ -136,6 +264,8 @@
"DISABLES_SHOW_REMAINING_DESC" = "Disable the feature that shows how much time is left in a video when tapping the timestamp.";
"ALWAYS_SHOW_REMAINING" = "Always show remaining time";
"ALWAYS_SHOW_REMAINING_DESC" = "Ensure the remaining time is always displayed by default in the player overlay.";
+"COPY_TIMESTAMP_ON_PAUSE" = "Copy timestamped links";
+"COPY_TIMESTAMP_ON_PAUSE_DESC" = "Allows to copy timestamped link to the clipboard by pressing pause button.";
"SHOW_REMAINING_EXTRA" = "Show extra remaining time";
"SHOW_REMAINING_EXTRA_DESC" = "Adds the estimated finish time to the label in the video overlay.";
"HIDE_FULLSCREEN_ACTIONS" = "Hide fullscreen actions";
@@ -148,93 +278,95 @@
"HIDE_CONTENT_WARNING_DESC" = "Automatically skip the sensitive content warning alert in some vidoes.";
"AUTO_FULLSCREEN" = "Auto fullscreen";
"AUTO_FULLSCREEN_DESC" = "Automatically enter fullscreen mode when starting video playback.";
-"AUTO_DISABLES_CAPTION" = "Auto disable captions";
-"AUTO_DISABLES_CAPTION_DESC" = "Automatically turns off closed captions (CC) whenever a new video starts playing.";
"PORTRAIT_FULLSCREEN" = "Portrait fullscren mode";
"PORTRAIT_FULLSCREEN_DESC" = "Enables portrait fullscreen mode support.";
"OLD_QUALITY_PICKER" = "Use old quality picker";
"OLD_QUALITY_PICKER_DESC" = "Reverts the video quality selection menu to the classic version (lists all resolutions immediately).";
"EXTRA_SPEED" = "Extra speed options";
"EXTRA_SPEED_DESC" = "Adds more video playback speed options up to 10x.";
-"DISABLE_HINTS" = "Disable hints";
-"DISABLE_HINTS_DESC" = "Disable the tutorial hints and interactive pop-ups.";
"FORCE_MINIPLAYER" = "Force miniplayer";
"FORCE_MINIPLAYER_DESC" = "Allows all videos, including those made for kids, to play in the miniplayer when dismissing the player.";
"FORCE_SEEKBAR" = "Always show progress bar";
"FORCE_SEEKBAR_DESC" = "Makes the progress bar (seekbar) remains visible at all times while in fullscreen mode.";
-"HIDE_LIKE_BUTTON" = "Hide Like button";
-"HIDE_LIKE_BUTTON_DESC" = "Hide Like button in the action bar under the video overlay.";
-"HIDE_DISLIKE_BUTTON" = "Hide Dislike button";
-"HIDE_DISLIKE_BUTTON_DESC" = "Hide Dislike button in the action bar under the video overlay.";
-"HIDE_SHARE_BUTTON" = "Hide Share button";
-"HIDE_SHARE_BUTTON_DESC" = "Hide Share button in the action bar under the video overlay.";
-"HIDE_DOWNLOAD_BUTTON" = "Hide Download button";
-"HIDE_DOWNLOAD_BUTTON_DESC" = "Hide Download button in the action bar under the video overlay.";
-"HIDE_CLIP_BUTTON" = "Hide Clip button";
-"HIDE_CLIP_BUTTON_DESC" = "Hide Clip button in the action bar under the video overlay.";
-"HIDE_REMIX_BUTTON" = "Hide Remix button";
-"HIDE_REMIX_BUTTON_DESC" = "Hide Remix button in the action bar under the video overlay.";
-"HIDE_SAVE_BUTTON" = "Hide Save button";
-"HIDE_SAVE_BUTTON_DESC" = "Hide Save button in the action bar under the video overlay.";
+"TAP_TO_SEEK" = "Tap to seek";
+"TAP_TO_SEEK_DESC" = "Quickly jump to any point by tapping the progress bar.";
+"DISABLES_ZOOM" = "Disable free zoom gesture";
+"DISABLES_ZOOM_DESC" = "Disables new free zoom gestures.";
+"USES_24_HOURS_TIME" = "24-hour format";
+"USES_24_HOURS_TIME_DESC" = "Shows end time in 24-hour format.";
+"PAUSE_TWO_FINGERS" = "Pause using two fingers";
+"PAUSE_TWO_FINGERS_DESC" = "Pause/Resume playback by tapping on the screen using two fingers.";
+"HIDE_COMMENTS_SECTION" = "Hide comments section";
+"HIDE_COMMENTS_SECTION_DESC" = "Completely removes the entire comments section.";
+"HIDE_COMMENTS_PREVIEW" = "Hide preview comments";
+"HIDE_COMMENTS_PREVIEW_DESC" = "Removes the preview box for the top comment while keeping the full section expandable.";
+"USE_ANOTHER_MINIPLAYER" = "Uses another miniplayer style";
+"USE_ANOTHER_MINIPLAYER_DESC" = "Uses another miniplayer style redesigned from older YT versions.";
"SHORTS" = "Shorts";
-"HIDE_SHORTS_LIKE_BUTTON" = "Hide Like button";
-"HIDE_SHORTS_LIKE_BUTTON_DESC" = "Hide the Like button in the Shorts player.";
-"HIDE_SHORTS_DISLIKE_BUTTON" = "Hide Dislike button";
-"HIDE_SHORTS_DISLIKE_BUTTON_DESC" = "Hide the Dislike button in the Shorts player.";
-"HIDE_SHORTS_COMMENT_BUTTON" = "Hide Comment button";
-"HIDE_SHORTS_COMMENT_BUTTON_DESC" = "Hide the Comment button in the Shorts player.";
-"HIDE_SHORTS_SHARE_BUTTON" = "Hide Share button";
-"HIDE_SHORTS_SHARE_BUTTON_DESC" = "Hide the Share button in the Shorts player.";
-"HIDE_SHORTS_REMIX_BUTTON" = "Hide Remix button";
-"HIDE_SHORTS_REMIX_BUTTON_DESC" = "Hide the Remix button in the Shorts player.";
-"HIDE_METADATA_BUTTON" = "Hide sound metadata button";
-"HIDE_METADATA_BUTTON_DESC" = "Hide the button that shows the sound source or music metadata in the Shorts player.";
+"SHORTS_ONLY" = "Shorts only mode";
+"SHORTS_ONLY_DESC" = "Limits YouTube functionality to viewing Shorts only.";
+"SHORTS_ACTION" = "Shorts action after the video ends";
+"LOOP" = "Loop video";
+"SKIP_TO_NEXT_SHORTS" = "Skip to next video";
+"PAUSE_SHORTS" = "Stop video";
+"REMOVE_LIVE_SHORTS" = "Remove live videos";
+"REMOVE_LIVE_SHORTS_DESC" = "Removes live videos from the feed.";
"HIDE_SHORTS_PRODUCT" = "Hide products";
"HIDE_SHORTS_PRODUCT_DESC" = "Hide tagged products and shopping links in the Shorts overlay.";
"HIDE_SHORTS_RECBAR" = "Hide recommendation bar";
"HIDE_SHORTS_RECBAR_DESC" = "Hide the suggested action bar in the Shorts player (e.g. Try this sound, Shopping, Preview comment).";
-"HIDE_SHORTS_COMMIT" = "Hide commissions bar";
-"HIDE_SHORTS_COMMIT_DESC" = "Hide the bar that shows the video got commissions in the Shorts player.";
-"HIDE_SHORTS_SUBSCRIPT_BUTTON" = "Hide subscriptions button";
-"HIDE_SHORTS_SUBSCRIPT_BUTTON_DESC" = "Hide the Subscriptions button at the top of the Shorts player.";
-"HIDE_SHORTS_LIVE_BUTTON" = "Hide live button";
-"HIDE_SHORTS_LIVE_BUTTON_DESC" = "Hide the Live button at the top of the Shorts player.";
-"HIDE_SHORTS_LENS_BUTTON" = "Hide lens button";
-"HIDE_SHORTS_LENS_BUTTON_DESC" = "Hide the Lens button at the top of the Shorts player.";
-"HIDE_SHORTS_TRENDS_BUTTON" = "Hide trends button";
-"HIDE_SHORTS_TRENDS_BUTTON_DESC" = "Hide the Trends button at the top of the Shorts player.";
-"HIDE_SHORTS_TO_VIDEO" = "Hide link to full video";
-"HIDE_SHORTS_TO_VIDEO_DESC" = "Hide the link that redirects in a Short to a full video in the Shorts player.";
"ENABLES_SHORTS_QUALITY" = "Enable Shorts quality picker";
"ENABLES_SHORTS_QUALITY_DESC" = "Force enable the video quality selection menu in Shorts.";
"SHOW_SHORTS_SEEKBAR" = "Always show seekbar";
"SHOW_SHORTS_SEEKBAR_DESC" = "Ensure the progress/seek bar is always visible at the bottom of the Shorts player.";
+"REMOVE_POSTS_SHORTS" = "Remove posts";
+"REMOVE_POSTS_SHORTS_DESC" = "Remove posts from the feed.";
+"SHORTS_FULLSCREEN" = "Pinch to Fullscreen";
+"SHORTS_FULLSCREEN_DESC" = "Manages visibility of the overlay with pinch in and pinch out gestures, displaying Shorts in fullscreen mode.";
"TABBAR" = "Tab bar";
"DEFAULT_TAB" = "Default startup tab";
-"DEFAULT_TAB_DESC" = "You can select the tab you want to open when opening the app.";
-"HOME_NAME" = "Home";
-"SUBSCRIPT_NAME" = "Subscriptions";
-"LIB_NAME" = "Library (You)";
+"FORSTED_TAB_BAR" = "Translucent tab bar";
+"ENABLED" = "Enabled";
"HIDE_TAB_INDI" = "Hide tab indicators";
"HIDE_TAB_INDI_DESC" = "Hide all tab indicators in the tab bar.";
"HIDE_TAB_LABELS" = "Hide tab labels";
"HIDE_TAB_LABELS_DESC" = "Hide all tab labels in the tab bar.";
-"HIDE_HOME_TAB" = "Hide Home tab";
-"HIDE_HOME_TAB_DESC" = "Hide Home tab in the tab bar.";
-"HIDE_SHORTS_TAB" = "Hide Shorts tab";
-"HIDE_SHORTS_TAB_DESC" = "Hide Shorts tab in the tab bar.";
-"HIDE_CREATE_BUTTON" = "Hide Create button";
-"HIDE_CREATE_BUTTON_DESC" = "Hide Create button in the tab bar.";
-"HIDE_SUBSCRIPT_TAB" = "Hide Subscriptions tab";
-"HIDE_SUBSCRIPT_TAB_DESC" = "Hide Subscriptions tab in the tab bar.";
+"HOME_TAB" = "Home";
+"SHORTS_TAB" = "Shorts";
+"CREATE_TAB" = "Create";
+"SUBSCRIPTIONS_TAB" = "Subscriptions";
+"LIBRARY_TAB" = "Library (You)";
+"HISTORY_TAB" = "History";
+"GAMING_TAB" = "Gaming";
+"SPORTS_TAB" = "Sports";
+"NOTI_TAB" = "Notifications";
+"NEWS_TAB" = "News";
+"MUSIC_TAB" = "Music";
+"WATCH_LATER_TAB" = "Watch later";
+"PLAYLIST_TAB" = "Playlists";
+"LIKE_TAB" = "Liked videos";
+"LIVE_TAB" = "Live";
+"POST_TAB" = "Posts";
+"VIDEO_TAB" = "Your videos";
+"MOVIE_TAB" = "Movies";
+"COURSE_TAB" = "Courses";
+"MINIGAME_TAB" = "Playables";
+"MANAGE_TABS" = "Manage tabs";
+"MANAGE_TABS_DESC" = "Reorder and toggle tabs";
+"TAB_REORDER_HINT" = "Drag to reorder. Toggle to show/hide.\nTab settings are also accessible by long-pressing the first tab in the tab bar.";
+"TAB_LIMIT" = "Tab limit";
+"TAB_LIMIT_DESC" = "Only 6 tabs are allowed.";
+"ZERO_TAB_DESC" = "At least 1 tab has to be enabled.";
"MISCELLANEOUS" = "Miscellaneous";
"BACKGROUND_PLAYBACK" = "Background playback";
"BACKGROUND_PLAYBACK_DESC" = "Enables background playback for videos.";
"DISABLES_SHORTS_PIP" = "Disables Shorts PiP";
"DISABLES_SHORTS_PIP_DESC" = "Try to disables Shorts PiP, this might not 100% disables it.";
+"DISABLE_HINTS" = "Disable hints";
+"DISABLE_HINTS_DESC" = "Disable the tutorial hints and interactive pop-ups.";
"BLOCK_UPGRADE_DIALOGS" = "Block upgrade dialogs";
"BLOCK_UPGRADE_DIALOGS_DESC" = "Block the YouTube dialogs that ask you to upgrade the app.";
"ARE_YOU_THERE_DIALOG" = "Hide 'Are you there?' dialog";
@@ -247,7 +379,109 @@
"DISABLES_SNACK_BAR_DESC" = "Disables snack bar when pressing some buttons or doing something in the app.";
"HIDE_STARTUP_ANIMATIONS" = "Hide YouTube's startup animations";
"HIDE_STARTUP_ANIMATIONS_DESC" = "Hide YouTube's startup animations when opening the app.";
-"HIDE_PLAY_IN_NEXT_QUEUE" = "Hide 'Play next in queue'";
-"HIDE_PLAY_IN_NEXT_QUEUE_DESC" = "Hide the 'Play next in queue' option in the flyout menu.";
"HIDE_LIKE_DISLIKE_VOTES" = "Hide like/dislike votes";
-"HIDE_LIKE_DISLIKE_VOTES_DESC" = "Hide messages that pop up when you like/dislike videos.";
\ No newline at end of file
+"HIDE_LIKE_DISLIKE_VOTES_DESC" = "Hide messages that pop up when you like/dislike videos.";
+"FLYOUT_MENU" = "Flyout menu";
+"REMOVE_PLAY_IN_NEXT_QUEUE_OPTION" = "Removes 'Play next in queue' option";
+"REMOVE_PLAY_IN_NEXT_QUEUE_OPTION_DESC" = "Removes 'Play next in queue' option from the flyout menu.";
+"REMOVE_DOWNLOAD_OPTION" = "Removes 'Download video' option";
+"REMOVE_DOWNLOAD_OPTION_DESC" = "Removes 'Download video' option from the flyout menu.";
+"REMOVE_WATCH_LATER_OPTION" = "Removes 'Save to Watch later' option";
+"REMOVE_WATCH_LATER_OPTION_DESC" = "Removes 'Save to Watch Later' option from the flyout menu.";
+"REMOVE_SAVE_OPTION" = "Removes 'Save to playlist' option";
+"REMOVE_SAVE_OPTION_DESC" = "Removes 'Save to playlist' option from the flyout menu.";
+"REMOVE_REMOVE_FROM_PLAYLIST_OPTION" = "Removes 'Remove from playlist' option";
+"REMOVE_REMOVE_FROM_PLAYLIST_OPTION_DESC" = "Removes 'Remove from playlist' option from the flyout menu.";
+"REMOVE_SHARE_OPTION" = "Removes 'Share' option";
+"REMOVE_SHARE_OPTION_DESC" = "Removes 'Share' option from the flyout menu.";
+"REMOVE_NOT_INTERESTED_OPTION" = "Removes 'Not interested' option";
+"REMOVE_NOT_INTERESTED_OPTION_DESC" = "Removes 'Not interested' option from the flyout menu.";
+"REMOVE_DONT_RECOMMEND_OPTION" = "Removes 'Don't recommend channel' option";
+"REMOVE_DONT_RECOMMEND_OPTION_DESC" = "Removes 'Don't recommend channel' option from the flyout menu.";
+"REMOVE_INFO_OPTION" = "Removes 'Why are you seeing this?' option";
+"REMOVE_INFO_OPTION_DESC" = "Removes 'Why are you seeing this?' option from the flyout menu.";
+"REMOVE_FILTER_OPTION" = "Removes 'Search filters' option";
+"REMOVE_FILTER_OPTION_DESC" = "Removes 'Search filters' option from the flyout menu.";
+"REMOVE_REPORT_OPTION" = "Removes 'Report' option";
+"REMOVE_REPORT_OPTION_DESC" = "Removes 'Report' option from the flyout menu.";
+"REMOVE_YOUTUBE_MUSIC_OPTION" = "Removes 'Listen with YouTube Music' option";
+"REMOVE_YOUTUBE_MUSIC_OPTION_DESC" = "Removes 'Listen with YouTube Music' option from the flyout menu.";
+"REMOVE_FEED_BACK_OPTION" = "Removes 'Send feedback' option";
+"REMOVE_FEED_BACK_OPTION_DESC" = "Removes 'Send feedback' option from the flyout menu.";
+"REMOVE_CAST_OPTION" = "Removes 'Watch on TV' option";
+"REMOVE_CAST_OPTION_DESC" = "Removes 'Watch on TV' option from the flyout menu.";
+"REMOVE_SHUFFLE_OPTION" = "Removes 'Shuffle' option";
+"REMOVE_SHUFFLE_OPTION_DESC" = "Removes 'Shuffle' option from the flyout menu.";
+"REMOVE_UN_SUB_OPTION" = "Removes 'Unsubscribe' option";
+"REMOVE_UN_SUB_OPTION_DESC" = "Removes 'Unsubscribe' option from the flyout menu.";
+"REMOVE_HIDE_FROM_PLAYLIST_OPTION" = "Removes 'Hide video from playlist' option";
+"REMOVE_HIDE_FROM_PLAYLIST_OPTION_DESC" = "Removes 'Hide video from playlist' option from the flyout menu.";
+"REMOVE_HELP_OPTION" = "Removes 'Help & feedback' option";
+"REMOVE_HELP_OPTION_DESC" = "Removes 'Help & feedback' option from the flyout menu.";
+"REMOVE_NOTIFY_OPTION" = "Removes 'Notify me' option";
+"REMOVE_NOTIFY_OPTION_DESC" = "Removes 'Notify me' option from the flyout menu.";
+"REMOVE_CLEARSCREEN_OPTION" = "Removes 'Clear Screen' option";
+"REMOVE_CLEARSCREEN_OPTION_DESC" = "Removes 'Clear Screen' option from the flyout menu.";
+"HIDE_COMMU_GUIDE" = "Hide community guidelines";
+"HIDE_COMMU_GUIDE_DESC" = "Hide community guidelines header.";
+"DISABLES_RTL" = "Disable RTL formatting";
+"DISABLES_RTL_DESC" = "Forcefully displays text in left-to-right (LTR) format for languages that are initially displayed in right-to-left (RTL).";
+"DEVICE_UI" = "Interface style";
+
+"SB_DESCRIPTION" = "Skip sponsored segments automatically using community-submitted data.";
+"SB_ENABLE" = "Enable SponsorBlock";
+"SB_ENABLE_DESC" = "Activate the SponsorBlock extension to skip sponsored segments.";
+"SB_SHOW_BUTTON" = "Show overlay button";
+"SB_SHOW_BUTTON_DESC" = "Display a SponsorBlock toggle button in the player overlay.";
+"SB_SHOW_NOTIFICATIONS" = "Show notifications";
+"SB_SHOW_NOTIFICATIONS_DESC" = "Display notifications when segments are skipped. Notifications for manual-skip categories are always shown.";
+"SB_HAPTIC_FEEDBACK" = "Haptic feedback";
+"SB_HAPTIC_FEEDBACK_DESC" = "Provide haptic feedback when a segment is skipped.";
+"SB_SEGMENTS_IN_PLAYER" = "Segments in player";
+"SB_SEGMENTS_IN_PLAYER_DESC" = "Display colored segments on the progress bar of the main video player.";
+"SB_SEGMENTS_IN_FEED" = "Segments in feed";
+"SB_SEGMENTS_IN_FEED_DESC" = "Display colored segments on the progress bar of players in the feed.";
+"SB_SEGMENTS_IN_MINIPLAYER" = "Segments in mini-player";
+"SB_SEGMENTS_IN_MINIPLAYER_DESC" = "Display colored segments on the progress bar of the mini-player.";
+"SB_SHOW_DURATION" = "Show duration without segments";
+"SB_SHOW_DURATION_DESC" = "Display the total video duration excluding skippable segments.";
+"SB_CATEGORIES_HEADER" = "Segment categories";
+"SB_SEGMENT_COLOR" = "Segment color";
+
+"SB_ACTION_DISABLE" = "Disable";
+"SB_ACTION_AUTO_SKIP" = "Auto-skip";
+"SB_ACTION_ASK" = "Ask before skipping";
+"SB_ACTION_DISPLAY" = "Show on seek bar only";
+"SB_ACTION_SKIP_TO" = "Skip to segment";
+
+"SB_CAT_sponsor" = "Sponsor";
+"SB_CAT_sponsor_DESC" = "Paid promotion for a product or service not directly related to the creator.";
+"SB_CAT_intro" = "Intro";
+"SB_CAT_intro_DESC" = "Intermission or intro animation typically seen at the start of a video.";
+"SB_CAT_outro" = "Endcards";
+"SB_CAT_outro_DESC" = "Endcards, credits, or outros at the end of the video.";
+"SB_CAT_interaction" = "Interaction";
+"SB_CAT_interaction_DESC" = "Reminders to like, subscribe, or follow on social media.";
+"SB_CAT_selfpromo" = "Self-promotion";
+"SB_CAT_selfpromo_DESC" = "Unpaid promotion of the creator's own products, merchandise, or other content.";
+"SB_CAT_music_offtopic" = "Non-music";
+"SB_CAT_music_offtopic_DESC" = "Non-music sections in music videos (e.g. talking, introductions).";
+"SB_CAT_preview" = "Preview";
+"SB_CAT_preview_DESC" = "Recap or preview of content from another video or previous episode.";
+"SB_CAT_hook" = "Hook/Greetings";
+"SB_CAT_hook_DESC" = "Narrated trailers for the upcoming video, greetings and goodbyes.";
+"SB_CAT_poi_highlight" = "Highlight";
+"SB_CAT_poi_highlight_DESC" = "The key moment or highlight point of the video. Use 'Skip to' to jump directly to it.";
+"SB_CAT_filler" = "Filler";
+"SB_CAT_filler_DESC" = "Tangential content, jokes, or filler that is not directly related to the main topic.";
+
+"SB_SKIPPED" = "%@ segment has been skipped";
+"SB_DETECTED" = "%@ segment detected.\nWould you like to skip the segment?";
+"SB_SKIP_NOW" = "Skip";
+"SB_UNSKIP" = "Unskip";
+"SB_JUMPED_TO_HIGHLIGHT" = "Jumped to highlight";
+"SB_JUMP_TO_HIGHLIGHT" = "Highlight available. Jump to the point?";
+"SB_SEGMENT_COLOR_SUFFIX" = "Segment color";
+"SB_SECTION_MAIN" = "Main";
+"SB_SKIP_ALERT_DURATION" = "Skip alert duration";
+"SB_UNSKIP_ALERT_DURATION" = "Unskip alert duration";
\ No newline at end of file
diff --git a/layout/Library/Application Support/YouMod.bundle/ko.lproj/Localizable.strings b/layout/Library/Application Support/YouMod.bundle/ko.lproj/Localizable.strings
index 422c60fc..8243a24f 100644
--- a/layout/Library/Application Support/YouMod.bundle/ko.lproj/Localizable.strings
+++ b/layout/Library/Application Support/YouMod.bundle/ko.lproj/Localizable.strings
@@ -1,11 +1,18 @@
-"NOTE" = "참고: 일부 숨기기 기능을 사용하면 화면에 빈 공간이 남을 수 있습니다.";
"SETTINGS" = "설정";
+"INTERFACE" = "Interface";
+
+"LOOP_DISABLED" = "Loop disabled";
+"LOOP_ENABLED" = "Loop enabled";
"NEW_ISSUES" = "버그 신고 및 새 기능 요청";
"NEW_ISSUES_DESC" = "버그를 발견했거나 새로운 기능을 원하시나요? 여기를 눌러주세요.";
"SOURCE_CODES" = "소스 코드";
"SOURCE_CODES_DESC" = "앱의 소스 코드를 확인해 보세요!";
+"EXTRA" = "Extra";
+"FIX_PLAYBACK_ISSUES" = "Fix playback issues (Temporary)";
+"FIX_PLAYBACK_ISSUES_DESC" = "Try to fix video playback issues for some users. Turning this on will make the player automatically reloads itself everytime it errors or fails to play the video.";
+
"PERFER_HEADER" = "YouMod 환경설정";
"PERFER" = "환경설정";
"IMPORT" = "설정 불러오기";
@@ -24,6 +31,7 @@
"ERROR_NO_KEYS_IMPORT" = "불러올 수 있는 설정값이 없습니다.";
"ERROR_NO_KEYS_EXPORT" = "내보낼 수 있는 설정값이 없습니다.";
"OVERRIDE" = "기존 설정에 덮어쓰게 됩니다. 계속하시겠습니까?";
+"DISABLED" = "Disabled";
"CACHE" = "캐시 (임시 데이터)";
"CLEARCACHE" = "캐시 지우기";
@@ -36,8 +44,77 @@
"DOWNLOAD_MANAGER_DESC" = "유튜브 기본 다운로더 대신 YouMod 전용 다운로더를 사용합니다. 동영상, 오디오, 자막, 썸네일을 다운로드할 수 있습니다.";
"DOWNLOAD_SAVE_PHOTOS" = "사진 앱에 자동 저장";
"DOWNLOAD_SAVE_PHOTOS_DESC" = "다운로드한 동영상과 이미지를 기기의 사진(갤러리) 앱에 바로 저장합니다.";
-"DOWNLOAD_DRC_AUDIO" = "DRC 오디오 우선 적용";
-"DOWNLOAD_DRC_AUDIO_DESC" = "볼륨을 일정하게 유지해 주는 DRC(동적 범위 압축) 오디오 트랙을 우선적으로 다운로드합니다.";
+"ADD_SHORTS_DOWNLOAD" = "Adds a download button to Shorts";
+"ADD_SHORTS_DOWNLOAD_DESC" = "Adds a download button to Shorts video overlay.";
+"USE_ORIG_AUDIO" = "Automatically uses original audio track";
+"USE_ORIG_AUDIO_DESC" = "Automatically uses original audio track instead of showing a dialog.";
+"DOWNLOAD_SHORTS" = "Download Shorts";
+"DOWNLOAD_SHORTS_DESC" = "Choose Shorts quality";
+"COPY_VID_INFO" = "Copy video informations";
+"COPY_VID_INFO_DESC" = "Choose what to copy";
+"COPY_ALL_VID_INFO" = "Copy all";
+"COPY_ALL_VID_INFO_DESC" = "Copy all video informations";
+"COPY_AUTHOR" = "Copy author";
+"COPY_AUTHOR_DESC" = "Copy the video author";
+"COPY_TITLE" = "Copy title";
+"COPY_TITLE_DESC" = "Copy the video title";
+"COPY_DESCRIPTION" = "Copy description";
+"COPY_DESCRIPTION_DESC" = "Copy the video description";
+"COPY_URL" = "Copy video link";
+"COPY_URL_TIMESTAMP" = "Copy video link with timestamp";
+"URL_COPIED" = "Copied video link";
+"URL_TIMESTAMP_COPIED" = "Copied video link with timestamp";
+"ERROR_VIDEOID" = "Cannot get the video ID";
+"ERROR_ADS" = "Cannot copy video link while playing ads";
+"COPIED_AUTHOR" = "Copied video author";
+"COPIED_TITLE" = "Copied video title";
+"COPIED_DESCRIPTION" = "Copied video description";
+"SAVE_THUMBNAIL" = "Save thumbnail";
+"SAVE_THUMBNAIL_DESC" = "Save to Photos";
+"DOWNLOAD_CAPTIONS" = "Download captions";
+"DOWNLOAD_CAPTIONS_DESC" = "Save subtitles as .VTT file";
+"DOWNLOAD_AUDIO" = "Download audio";
+"DOWNLOAD_AUDIO_DESC" = "Download audio as .m4a file";
+"DOWNLOAD_VIDEO" = "Download video";
+"DOWNLOAD_VIDEO_DESC" = "Choose video quality";
+"OPEN_VID_BEFORE" = "Open a video before using the download manager";
+"NO_CAPTIONS_URL" = "No valid caption URLs found";
+"NO_AUDIO_URL" = "No audio URL found";
+"NO_STREAM_URL" = "No stream URL found";
+"CAPTIONS_FAILED" = "Failed to download captions";
+"NO_CAPTIONS" = "No captions available for this video";
+"NO_AUDIO_STREAM_FOUND" = "No audio streams found";
+"NO_VID_AUDIO_STREAM_FOUND" = "No video/audio streams found";
+"COPIED_VID_INFO" = "Copied all video informations";
+"SAVED_TO_PHOTOS" = "Saved to Photos";
+"SAVE_FAILED" = "Save failed";
+"PHOTO_ACCESS_DENINED" = "Photos access denied";
+"THUMBNAIL_FAILED" = "Thumbnail failed";
+"DOWNLOADING_THUMBNAIL" = "Downloading thumbnail...";
+"DOWNLOADING_AUDIO" = "Downloading audio...";
+"DOWNLOADING_VIDEO" = "Downloading video...";
+"DOWNLOADING_CAPTIONS" = "Downloading captions...";
+"NO_THUMBNAIL_FOUND" = "No thumbnail found";
+"DOWNLOAD_FAILED" = "Download failed";
+"AUDIO_SAVED" = "Audio saved";
+"DOWNLOAD_COMPLETED" = "Download completed";
+"CANNOT_SAVE_TO_PHOTOS" = "Cannot save to Photos";
+"FINA_VIDEO" = "Finalizing video...";
+"MERGING_VID_FALLBACK" = "Merging video with fallback...";
+"MERGING_VID" = "Merging video...";
+"CONVERTING" = "Converting to";
+"ALREADY_DOWNLOADING" = "Already downloading";
+"DOWNLOAD_CANCELLED" = "Download cancelled";
+"CANCEL" = "Cancel";
+"TRIMMING_AUDIO" = "Trimming audio...";
+"COPY_THUMBNAIL" = "Copy thumbnail";
+"COPY_THUMBNAIL_DESC" = "Copy to clipboard";
+"SHOW_THUMBNAIL" = "Show thumbnail";
+"SHOW_THUMBNAIL_DESC" = "Show in a dialog";
+"THUMBNAIL_OPTIONS" = "Download thumbnail";
+"THUMBNAIL_OPTIONS_DESC" = "Show more options";
+"COPIED_TO_CLIPBOARD" = "Copied to clipboard";
+"COPY" = "Copy";
"APPEARANCE" = "화면 및 테마";
"OLED_THEME" = "OLED 다크 모드";
@@ -46,6 +123,8 @@
"OLED_KEYBOARD_DESC" = "다크 모드 사용 시 키보드 배경을 완전한 검은색(OLED 블랙)으로 바꿉니다.";
"NAVBAR" = "상단 메뉴 바";
+"STICKY_NAVBAR" = "Sticky Navigation bar";
+"STICKY_NAVBAR_DESC" = "Pins the Navigation bar so that it remains visible when scrolling down.";
"HIDE_YT_LOGO" = "유튜브 로고 숨기기";
"HIDE_YT_LOGO_DESC" = "화면 맨 위 상단 바에서 유튜브 로고를 보이지 않게 숨깁니다.";
"PREMIUM_LOGO" = "유튜브 프리미엄 로고 사용";
@@ -62,32 +141,76 @@
"FEED" = "피드 (홈 화면)";
"HIDE_SUBBAR" = "서브 바(카테고리) 숨기기";
"HIDE_SUBBAR_DESC" = "홈 화면 맨 위에 있는 주제별 필터(게임, 음악 등)를 숨깁니다.";
+"HIDE_HORI_SHELF" = "Remove horizontal feeds";
+"HIDE_HORI_SHELF_DESC" = "Removes all horizontal feeds (News, Continue Watching, Posts collection, etc.) from the feed, except Playables.";
"HIDE_MUSIC_PLAYLISTS" = "음악 재생목록 자동생성 숨기기";
"HIDE_MUSIC_PLAYLISTS_DESC" = "피드 중간에 뜨는 유튜브 자동 생성 음악 믹스 추천을 숨깁니다.";
"HIDE_FEED_POST" = "게시물 숨기기";
"HIDE_FEED_POST_DESC" = "피드에서 텍스트나 이미지로 된 커뮤니티 게시물을 숨깁니다.";
+"HIDE_SURVEYS" = "Hide surveys";
+"HIDE_SURVEYS_DESC" = "Hide video surveys that YouTube shows sometimes.";
"HIDE_SHORTS_SHELF" = "쇼츠(Shorts) 선반 숨기기";
"HIDE_SHORTS_SHELF_DESC" = "홈 화면과 구독 탭에서 여러 개가 가로로 나열된 쇼츠 영상 모음을 안 보이게 합니다.";
+"KEEP_SHORTS_SUBSCRIPT" = "Keep Shorts in Subscriptions";
+"KEEP_SHORTS_SUBSCRIPT_DESC" = "Prevents removing Shorts from Subscriptions tab.";
"HIDE_SEARCH_HISTORY" = "검색 기록 숨기기";
"HIDE_SEARCH_HISTORY_DESC" = "검색창을 누를 때 이전 검색 기록이나 추천 검색어가 안 보이게 숨깁니다.\n참고: 이 앱에서만 안 보일 뿐, 다른 기기의 유튜브에는 기록이 남아있습니다.";
-"HIDE_SUB_BUTTON" = "구독 버튼 숨기기";
-"HIDE_SUB_BUTTON_DESC" = "채널 홈이나 동영상 설명창에 있는 구독 버튼을 숨깁니다.";
-"HIDE_SHOP_BUTTON" = "쇼핑 버튼 숨기기";
-"HIDE_SHOP_BUTTON_DESC" = "채널 설명창에 있는 스토어(쇼핑) 버튼을 숨깁니다.";
-"HIDE_MEMBER_BUTTON" = "멤버십 가입 버튼 숨기기";
-"HIDE_MEMBER_BUTTON_DESC" = "채널 홈이나 영상 설명창에서 멤버십 '가입' 버튼을 숨깁니다.";
+"HIDE_PLAYABLES" = "Remove playables";
+"HIDE_PLAYABLES_DESC" = "Removes playables (mini-games) from the feed.";
"PLAYER" = "동영상 플레이어";
+"PLAYBACK_SPEED" = "Playback speed";
+"CONTROL_CENTER" = "Control Center";
+"PLAYER_ACTIONS" = "Player actions";
+"GESTURE_HEADER" = "Gestures";
+"QUALITY_WIFI" = "Playback quality on WiFi";
+"QUALITY_WIFI_DESC" = "Automatically applies the selected quality when streaming over Wi-Fi.";
+"QUALITY_CELLULAR" = "Playback quality on Cellular";
+"QUALITY_CELLULAR_DESC" = "Automatically applies the selected quality when streaming over mobile data.";
+"QUALITY_LOW_POWER" = "Playback quality on Power saver mode";
+"QUALITY_LOW_POWER_DESC" = "Automatically applies the selected quality when when Low power mode is enabled on your device.";
+"DEFAULT" = "Default";
+"BEST" = "Best";
+"AUDIO_TRACK" = "Preferred audio track";
+"ORIGINAL" = "Original";
+"SELECT_MANUALLY" = "Select manually";
+"AUDIO_TRACK_SELECT" = "Selected audio track";
+"AUDIO_TRACK_SELECT_DESC" = "Specifies the preferred audio track language to use for playback, if available.";
+"NO_AUTO_DUBBED" = "Uses original audio track";
+"NO_AUTO_DUBBED_DESC" = "Uses original audio track if the preferred language track is auto-dubbed or not found.";
+"CAPTION_TRACK" = "Preferred captions";
+"CAPTION_TRACK_SELECT" = "Selected captions";
+"CAPTION_TRACK_SELECT_DESC" = "Specifies the preferred captions language to display, if available.";
+"DISABLES_CAPTION_TRACK" = "Automatically disable captions";
+"DISABLES_CAPTION_TRACK_DESC" = "Automatically disable captions if the preferred language is auto-generated or not found.";
+"DEFAULT_SPEED" = "Default playback rate";
+"DEFAULT_SPEED_DESC" = "Overrides the default playback speed.";
+"SPEED_BUTTON" = "Speed controls button";
+"SPEED_BUTTON_DESC" = "Displays a speed controls button on overlay.";
+"MUTE_BUTTON" = "Mute button";
+"MUTE_BUTTON_DESC" = "Displays a button to mute or unmute the volume on the overlay.";
+"SHARE_BUTTON" = "Share button";
+"SHARE_BUTTON_DESC" = "Displays a button to share the video link on the overlay.";
+"LOOP_BUTTON" = "Loop button";
+"LOOP_BUTTON_DESC" = "Displays a button to loop the video on the overlay.";
+"CAPTION_BUTTON" = "Captions button";
+"CAPTION_BUTTON_DESC" = "Displays a button to copy the video captions on the overlay.";
+"HOLD_TO_SPEED" = "Hold to speed";
+"HOLD_TO_SPEED_DESC" = "Uses custom hold to speed playback rate.";
+"SKIP_BACKWARD" = "Skip backward";
+"SKIP_BACKWARD_DESC" = "Replaces the previous track button in Control Center with a skip backward button by a set number of seconds.";
+"SKIP_FORWARD" = "Skip forward";
+"SKIP_FORWARD_DESC" = "Replaces the next track button in Control Center with a skip forward button by a set number of seconds.";
+"REWIND_SECONDS" = "Rewind seconds";
+"FORWARD_SECONDS" = "Fast-forward seconds";
"HIDE_AUTOPLAY" = "자동재생 스위치 숨기기";
"HIDE_AUTOPLAY_DESC" = "영상 화면 위쪽에 있는 자동재생 켜기/끄기 스위치를 숨깁니다.";
"HIDE_CAPTIONS_BUTTON" = "자막 버튼 숨기기";
"HIDE_CAPTIONS_BUTTON_DESC" = "영상 화면 위쪽에 있는 CC(자막) 버튼을 숨깁니다.";
"HIDE_CAST_BUTTON_PLAYER" = "화면 전송(캐스트) 버튼 숨기기";
"HIDE_CAST_BUTTON_PLAYER_DESC" = "영상 화면에 있는 TV 모양 캐스트 버튼을 숨깁니다.";
-"HIDE_PREV_BUTTON" = "이전 영상 버튼 숨기기";
-"HIDE_PREV_BUTTON_DESC" = "재생바 옆에 있는 이전 영상으로 가기 버튼을 숨깁니다.";
-"HIDE_NEXT_BUTTON" = "다음 영상 버튼 숨기기";
-"HIDE_NEXT_BUTTON_DESC" = "재생바 옆에 있는 다음 영상으로 가기 버튼을 숨깁니다.";
+"HIDE_NEXT_AND_PREV_BUTTON" = "Hide previous and next buttons";
+"HIDE_NEXT_AND_PREV_BUTTON_DESC" = "Hide previous and next buttons in the video overlay.";
"REPLACE_PREVNEXT_BUTTONS" = "이전/다음 버튼 교체하기";
"REPLACE_PREVNEXT_BUTTONS_DESC" = "플레이어의 이전 영상, 다음 영상 버튼을 뒤로 감기, 빨리 감기 버튼으로 바꿉니다.";
"REMOVE_DARK_OVERLAY" = "어두운 음영 효과 제거";
@@ -102,6 +225,12 @@
"HIDE_PAID_OVERLAY_DESC" = "영상 화면에 뜨는 '유료 광고 포함' 안내 배너를 숨깁니다.";
"HIDE_WATERMARK" = "채널 워터마크 숨기기";
"HIDE_WATERMARK_DESC" = "영상 오른쪽 아래 구석에 작게 떠 있는 채널 로고(워터마크)를 숨깁니다.";
+"PAUSE_ON_OVERLAY" = "Pause on overlay";
+"PAUSE_ON_OVERLAY_DESC" = "Sets playback on pause if overlay appears.";
+"DISABLES_SNAP_TO_CHAPTER" = "Disable snap to chapters";
+"DISABLES_SNAP_TO_CHAPTER_DESC" = "Prevents the scrubbar stick to the chapters.";
+"DISABLES_ENGAGE_PANEL" = "Disables engagement panel in fullscreen";
+"DISABLES_ENGAGE_PANEL_DESC" = "Prevent engagement panel showing in fullscreen.";
"GESTURES" = "제스처 제어 켜기";
"GESTURES_DESC" = "화면을 스와이프하여 조작하는 제스처 기능을 켭니다.\n상단: 밝기 조절, 하단: 볼륨 조절";
"GESTURE_AREA" = "제스처 작동 영역 범위";
@@ -135,6 +264,8 @@
"DISABLES_SHOW_REMAINING_DESC" = "시간 표시를 눌렀을 때 영상의 '남은 시간'으로 바뀌어 보여주는 기능을 끕니다.";
"ALWAYS_SHOW_REMAINING" = "항상 남은 시간으로 표시";
"ALWAYS_SHOW_REMAINING_DESC" = "영상의 현재 재생 시간 대신, 끝날 때까지 남은 시간을 기본으로 띄워둡니다.";
+"COPY_TIMESTAMP_ON_PAUSE" = "Copy timestamped links";
+"COPY_TIMESTAMP_ON_PAUSE_DESC" = "Allows to copy timestamped link to the clipboard by pressing pause button.";
"SHOW_REMAINING_EXTRA" = "예상 종료 시간 표시";
"SHOW_REMAINING_EXTRA_DESC" = "영상 남은 시간에 이 영상이 언제 끝나는지 예상 종료 시간도 함께 보여줍니다.";
"HIDE_FULLSCREEN_ACTIONS" = "전체화면 빠른 동작 버튼 숨기기";
@@ -147,8 +278,6 @@
"HIDE_CONTENT_WARNING_DESC" = "연령 제한이나 민감한 콘텐츠를 볼 때 뜨는 경고 안내창을 묻지도 따지지도 않고 건너뜁니다.";
"AUTO_FULLSCREEN" = "자동 전체화면";
"AUTO_FULLSCREEN_DESC" = "영상을 재생하자마자 자동으로 화면을 꽉 채우는 전체화면 모드로 시작합니다.";
-"AUTO_DISABLES_CAPTION" = "자막 자동 끄기";
-"AUTO_DISABLES_CAPTION_DESC" = "새로운 영상을 재생할 때마다 켜져 있는 자막(CC)을 자동으로 끕니다.";
"PORTRAIT_FULLSCREEN" = "세로 전체화면 모드 지원";
"PORTRAIT_FULLSCREEN_DESC" = "스마트폰을 세로로 든 상태에서도 전체화면이 꽉 차게 보이도록 지원합니다.";
"OLD_QUALITY_PICKER" = "구형 화질 선택기 사용";
@@ -161,79 +290,87 @@
"FORCE_MINIPLAYER_DESC" = "아동용 동영상을 포함한 모든 영상을 아래로 내렸을 때 미니 플레이어에서 재생되도록 강제합니다.";
"FORCE_SEEKBAR" = "진행 바 항상 표시";
"FORCE_SEEKBAR_DESC" = "전체화면 모드에서 영상을 볼 때 하단에 재생 진행 바를 항상 띄워둡니다.";
-"HIDE_LIKE_BUTTON" = "좋아요 버튼 숨기기";
-"HIDE_LIKE_BUTTON_DESC" = "영상 아래쪽 메뉴에서 좋아요 버튼을 숨깁니다.";
-"HIDE_DISLIKE_BUTTON" = "싫어요 버튼 숨기기";
-"HIDE_DISLIKE_BUTTON_DESC" = "영상 아래쪽 메뉴에서 싫어요 버튼을 숨깁니다.";
-"HIDE_SHARE_BUTTON" = "공유 버튼 숨기기";
-"HIDE_SHARE_BUTTON_DESC" = "영상 아래쪽 메뉴에서 공유 버튼을 숨깁니다.";
-"HIDE_DOWNLOAD_BUTTON" = "오프라인 저장 버튼 숨기기";
-"HIDE_DOWNLOAD_BUTTON_DESC" = "영상 아래쪽 메뉴에서 다운로드(오프라인 저장) 버튼을 숨깁니다.";
-"HIDE_CLIP_BUTTON" = "클립 버튼 숨기기";
-"HIDE_CLIP_BUTTON_DESC" = "영상 아래쪽 메뉴에서 짧은 구간을 잘라내는 클립(Clip) 버튼을 숨깁니다.";
-"HIDE_REMIX_BUTTON" = "리믹스 버튼 숨기기";
-"HIDE_REMIX_BUTTON_DESC" = "영상 아래쪽 메뉴에서 쇼츠로 만들기(리믹스) 버튼을 숨깁니다.";
-"HIDE_SAVE_BUTTON" = "저장 버튼 숨기기";
-"HIDE_SAVE_BUTTON_DESC" = "영상 아래쪽 메뉴에서 재생목록에 추가하는 저장 버튼을 숨깁니다.";
+"TAP_TO_SEEK" = "Tap to seek";
+"TAP_TO_SEEK_DESC" = "Quickly jump to any point by tapping the progress bar.";
+"DISABLES_ZOOM" = "Disable free zoom gesture";
+"DISABLES_ZOOM_DESC" = "Disables new free zoom gestures.";
+"USES_24_HOURS_TIME" = "24-hour format";
+"USES_24_HOURS_TIME_DESC" = "Shows end time in 24-hour format.";
+"PAUSE_TWO_FINGERS" = "Pause using two fingers";
+"PAUSE_TWO_FINGERS_DESC" = "Pause/Resume playback by tapping on the screen using two fingers.";
+"HIDE_ACTION_BAR" = "Hide actions bar";
+"HIDE_ACTION_BAR_DESC" = "Removes the bar containing the like, dislike, share buttons etc. below the video player.";
+"HIDE_COMMENTS_SECTION" = "Hide comments section";
+"HIDE_COMMENTS_SECTION_DESC" = "Completely removes the entire comments section.";
+"HIDE_COMMENTS_PREVIEW" = "Hide preview comments";
+"HIDE_COMMENTS_PREVIEW_DESC" = "Removes the preview box for the top comment while keeping the full section expandable.";
+"USE_ANOTHER_MINIPLAYER" = "Uses another miniplayer style";
+"USE_ANOTHER_MINIPLAYER_DESC" = "Uses another miniplayer style redesigned from older YT versions.";
"SHORTS" = "쇼츠 (Shorts)";
-"HIDE_SHORTS_LIKE_BUTTON" = "좋아요 버튼 숨기기";
-"HIDE_SHORTS_LIKE_BUTTON_DESC" = "쇼츠 화면에서 엄지척(좋아요) 버튼을 숨깁니다.";
-"HIDE_SHORTS_DISLIKE_BUTTON" = "싫어요 버튼 숨기기";
-"HIDE_SHORTS_DISLIKE_BUTTON_DESC" = "쇼츠 화면에서 싫어요 버튼을 숨깁니다.";
-"HIDE_SHORTS_COMMENT_BUTTON" = "댓글 버튼 숨기기";
-"HIDE_SHORTS_COMMENT_BUTTON_DESC" = "쇼츠 화면에서 말풍선(댓글) 버튼을 숨깁니다.";
-"HIDE_SHORTS_SHARE_BUTTON" = "공유 버튼 숨기기";
-"HIDE_SHORTS_SHARE_BUTTON_DESC" = "쇼츠 화면에서 화살표(공유) 버튼을 숨깁니다.";
-"HIDE_SHORTS_REMIX_BUTTON" = "리믹스 버튼 숨기기";
-"HIDE_SHORTS_REMIX_BUTTON_DESC" = "쇼츠 화면에서 다른 쇼츠를 만드는 리믹스 버튼을 숨깁니다.";
-"HIDE_METADATA_BUTTON" = "음원 정보 버튼 숨기기";
-"HIDE_METADATA_BUTTON_DESC" = "쇼츠 우측 하단에 있는 배경음악/사운드 출처 버튼을 숨깁니다.";
+"SHORTS_ONLY" = "Shorts only mode";
+"SHORTS_ONLY_DESC" = "Limits YouTube functionality to viewing Shorts only.";
+"SHORTS_ACTION" = "Shorts action after the video ends";
+"LOOP" = "Loop video";
+"SKIP_TO_NEXT_SHORTS" = "Skip to next video";
+"PAUSE_SHORTS" = "Stop video";
+"REMOVE_LIVE_SHORTS" = "Remove live videos";
+"REMOVE_LIVE_SHORTS_DESC" = "Removes live videos from the feed.";
"HIDE_SHORTS_PRODUCT" = "상품 숨기기";
"HIDE_SHORTS_PRODUCT_DESC" = "쇼츠 화면에 뜨는 태그된 상품이나 쇼핑몰 링크들을 안 보이게 합니다.";
"HIDE_SHORTS_RECBAR" = "추천 동작 바 숨기기";
"HIDE_SHORTS_RECBAR_DESC" = "쇼츠 하단에 뜨는 '이 사운드 사용해보기', '쇼핑하기' 같은 추천 행동 유도 문구를 숨깁니다.";
-"HIDE_SHORTS_COMMIT" = "유료 광고 표시 숨기기";
-"HIDE_SHORTS_COMMIT_DESC" = "쇼츠에 뜨는 '유료 광고 포함(협찬/수수료)' 배너를 숨깁니다.";
-"HIDE_SHORTS_SUBSCRIPT_BUTTON" = "구독 버튼 숨기기";
-"HIDE_SHORTS_SUBSCRIPT_BUTTON_DESC" = "쇼츠 화면 상단에 둥둥 떠 있는 구독 버튼을 숨깁니다.";
-"HIDE_SHORTS_LIVE_BUTTON" = "라이브 버튼 숨기기";
-"HIDE_SHORTS_LIVE_BUTTON_DESC" = "쇼츠 화면 상단에 뜨는 실시간 방송(라이브) 버튼을 숨깁니다.";
-"HIDE_SHORTS_LENS_BUTTON" = "렌즈 버튼 숨기기";
-"HIDE_SHORTS_LENS_BUTTON_DESC" = "쇼츠 화면 상단에 있는 렌즈(검색) 버튼을 숨깁니다.";
-"HIDE_SHORTS_TRENDS_BUTTON" = "트렌드 버튼 숨기기";
-"HIDE_SHORTS_TRENDS_BUTTON_DESC" = "쇼츠 화면 상단에 있는 트렌드(인기) 버튼을 숨깁니다.";
-"HIDE_SHORTS_TO_VIDEO" = "원본 영상 링크 숨기기";
-"HIDE_SHORTS_TO_VIDEO_DESC" = "쇼츠 영상에 걸려있는 전체 길이의 원본 동영상으로 넘어가는 링크를 숨깁니다.";
"ENABLES_SHORTS_QUALITY" = "쇼츠 화질 선택 기능 켜기";
"ENABLES_SHORTS_QUALITY_DESC" = "쇼츠에서도 사용자가 직접 1080p 등 원하는 화질을 선택할 수 있도록 메뉴를 활성화합니다.";
"SHOW_SHORTS_SEEKBAR" = "진행 바 항상 표시";
"SHOW_SHORTS_SEEKBAR_DESC" = "쇼츠 맨 밑에 영상이 얼마나 재생됐는지 붉은 선으로 보여주는 진행 바를 항상 띄워둡니다.";
+"REMOVE_POSTS_SHORTS" = "Remove posts";
+"REMOVE_POSTS_SHORTS_DESC" = "Remove posts from the feed.";
+"SHORTS_FULLSCREEN" = "Pinch to Fullscreen";
+"SHORTS_FULLSCREEN_DESC" = "Manages visibility of the overlay with pinch in and pinch out gestures, displaying Shorts in fullscreen mode.";
"TABBAR" = "하단 탭 바";
"DEFAULT_TAB" = "기본 시작 탭 설정";
-"DEFAULT_TAB_DESC" = "유튜브 앱을 켤 때 '홈', '구독' 등 제일 처음으로 보여줄 화면을 고를 수 있습니다.";
-"HOME_NAME" = "홈";
-"SUBSCRIPT_NAME" = "구독";
-"LIB_NAME" = "보관함 (나)";
+"FORSTED_TAB_BAR" = "Translucent tab bar";
+"ENABLED" = "Enabled";
"HIDE_TAB_INDI" = "선택 탭 표시기 숨기기";
"HIDE_TAB_INDI_DESC" = "하단 탭에서 현재 내가 있는 탭을 강조해서 보여주는 효과를 없앱니다.";
"HIDE_TAB_LABELS" = "탭 글자 숨기기";
"HIDE_TAB_LABELS_DESC" = "하단 탭 메뉴들에 적힌 글자(홈, Shorts 등)를 다 지우고 깔끔하게 아이콘만 남깁니다.";
-"HIDE_HOME_TAB" = "홈 탭 숨기기";
-"HIDE_HOME_TAB_DESC" = "하단 탭에서 '홈' 탭을 아예 지워버립니다.";
-"HIDE_SHORTS_TAB" = "쇼츠 탭 숨기기";
-"HIDE_SHORTS_TAB_DESC" = "하단 탭에서 'Shorts' 탭을 아예 지워버립니다.";
-"HIDE_CREATE_BUTTON" = "만들기 버튼 숨기기";
-"HIDE_CREATE_BUTTON_DESC" = "하단 탭 한가운데에 있는 영상 업로드용 십자가(+) 버튼을 지웁니다.";
-"HIDE_SUBSCRIPT_TAB" = "구독 탭 숨기기";
-"HIDE_SUBSCRIPT_TAB_DESC" = "하단 탭에서 '구독' 탭을 아예 지워버립니다.";
+"HOME_TAB" = "홈";
+"SHORTS_TAB" = "Shorts";
+"CREATE_TAB" = "Create";
+"SUBSCRIPTIONS_TAB" = "구독";
+"LIBRARY_TAB" = "보관함 (나)";
+"HISTORY_TAB" = "History";
+"GAMING_TAB" = "Gaming";
+"SPORTS_TAB" = "Sports";
+"NOTI_TAB" = "Notifications";
+"NEWS_TAB" = "News";
+"MUSIC_TAB" = "Music";
+"WATCH_LATER_TAB" = "Watch later";
+"PLAYLIST_TAB" = "Playlists";
+"LIKE_TAB" = "Liked videos";
+"LIVE_TAB" = "Live";
+"POST_TAB" = "Posts";
+"VIDEO_TAB" = "Your videos";
+"MOVIE_TAB" = "Movies";
+"COURSE_TAB" = "Courses";
+"MINIGAME_TAB" = "Playables";
+"MANAGE_TABS" = "Manage tabs";
+"MANAGE_TABS_DESC" = "Reorder and toggle tabs";
+"TAB_REORDER_HINT" = "Drag to reorder. Toggle to show/hide.\nTab settings are also accessible by long-pressing the first tab in the tab bar.";
+"TAB_LIMIT" = "Tab limit";
+"TAB_LIMIT_DESC" = "Only 6 tabs are allowed.";
+"ZERO_TAB_DESC" = "At least 1 tab has to be enabled.";
"MISCELLANEOUS" = "기타 설정";
"BACKGROUND_PLAYBACK" = "백그라운드 재생";
"BACKGROUND_PLAYBACK_DESC" = "유튜브 앱을 닫거나 스마트폰 화면을 꺼도 소리가 계속 끊기지 않고 나오게 해줍니다.";
"DISABLES_SHORTS_PIP" = "Shorts PIP 모드 끄기";
"DISABLES_SHORTS_PIP_DESC" = "Shorts PIP 화면을 작은 팝업창으로 띄우는 기능을 끕니다. (완벽하게 꺼지지 않을 수도 있습니다.)";
+"DISABLE_HINTS" = "도움말 힌트 끄기";
+"DISABLE_HINTS_DESC" = "앱 사용법을 알려주는 튜토리얼 힌트나 팝업창을 안 보이게 막습니다.";
"BLOCK_UPGRADE_DIALOGS" = "업데이트 강요 알림 차단";
"BLOCK_UPGRADE_DIALOGS_DESC" = "유튜브 앱을 새 버전으로 업데이트하라고 귀찮게 뜨는 팝업창을 막아줍니다.";
"ARE_YOU_THERE_DIALOG" = "'계속 시청하시겠어요?' 팝업 숨기기";
@@ -246,7 +383,109 @@
"DISABLES_SNACK_BAR_DESC" = "재생목록 추가 등 앱에서 특정 행동을 할 때 화면 아래쪽에 잠깐 나타났다 사라지는 검은색 알림창을 끕니다.";
"HIDE_STARTUP_ANIMATIONS" = "유튜브 로고 애니메이션 숨기기";
"HIDE_STARTUP_ANIMATIONS_DESC" = "앱을 켤 때 나오는 유튜브 로고 오프닝 애니메이션을 없애서 앱이 더 빨리 켜지게 합니다.";
-"HIDE_PLAY_IN_NEXT_QUEUE" = "'대기열 다음으로 재생' 숨기기";
-"HIDE_PLAY_IN_NEXT_QUEUE_DESC" = "더보기 메뉴에서 '대기열 다음으로 재생(예약)' 옵션을 안 보이게 지웁니다.";
"HIDE_LIKE_DISLIKE_VOTES" = "좋아요/싫어요 이펙트 끄기";
"HIDE_LIKE_DISLIKE_VOTES_DESC" = "좋아요나 싫어요 버튼을 눌렀을 때 뿅 하고 나타나는 메시지나 화려한 애니메이션 효과를 숨깁니다.";
+"FLYOUT_MENU" = "Flyout menu";
+"REMOVE_PLAY_IN_NEXT_QUEUE_OPTION" = "Removes 'Play next in queue' option";
+"REMOVE_PLAY_IN_NEXT_QUEUE_OPTION_DESC" = "Removes 'Play next in queue' option from the flyout menu.";
+"REMOVE_DOWNLOAD_OPTION" = "Removes 'Download video' option";
+"REMOVE_DOWNLOAD_OPTION_DESC" = "Removes 'Download video' option from the flyout menu.";
+"REMOVE_WATCH_LATER_OPTION" = "Removes 'Save to Watch later' option";
+"REMOVE_WATCH_LATER_OPTION_DESC" = "Removes 'Save to Watch Later' option from the flyout menu.";
+"REMOVE_SAVE_OPTION" = "Removes 'Save to playlist' option";
+"REMOVE_SAVE_OPTION_DESC" = "Removes 'Save to playlist' option from the flyout menu.";
+"REMOVE_REMOVE_FROM_PLAYLIST_OPTION" = "Removes 'Remove from playlist' option";
+"REMOVE_REMOVE_FROM_PLAYLIST_OPTION_DESC" = "Removes 'Remove from playlist' option from the flyout menu.";
+"REMOVE_SHARE_OPTION" = "Removes 'Share' option";
+"REMOVE_SHARE_OPTION_DESC" = "Removes 'Share' option from the flyout menu.";
+"REMOVE_NOT_INTERESTED_OPTION" = "Removes 'Not interested' option";
+"REMOVE_NOT_INTERESTED_OPTION_DESC" = "Removes 'Not interested' option from the flyout menu.";
+"REMOVE_DONT_RECOMMEND_OPTION" = "Removes 'Don't recommend channel' option";
+"REMOVE_DONT_RECOMMEND_OPTION_DESC" = "Removes 'Don't recommend channel' option from the flyout menu.";
+"REMOVE_INFO_OPTION" = "Removes 'Why are you seeing this?' option";
+"REMOVE_INFO_OPTION_DESC" = "Removes 'Why are you seeing this?' option from the flyout menu.";
+"REMOVE_FILTER_OPTION" = "Removes 'Search filters' option";
+"REMOVE_FILTER_OPTION_DESC" = "Removes 'Search filters' option from the flyout menu.";
+"REMOVE_REPORT_OPTION" = "Removes 'Report' option";
+"REMOVE_REPORT_OPTION_DESC" = "Removes 'Report' option from the flyout menu.";
+"REMOVE_YOUTUBE_MUSIC_OPTION" = "Removes 'Listen with YouTube Music' option";
+"REMOVE_YOUTUBE_MUSIC_OPTION_DESC" = "Removes 'Listen with YouTube Music' option from the flyout menu.";
+"REMOVE_FEED_BACK_OPTION" = "Removes 'Send feedback' option";
+"REMOVE_FEED_BACK_OPTION_DESC" = "Removes 'Send feedback' option from the flyout menu.";
+"REMOVE_CAST_OPTION" = "Removes 'Watch on TV' option";
+"REMOVE_CAST_OPTION_DESC" = "Removes 'Watch on TV' option from the flyout menu.";
+"REMOVE_SHUFFLE_OPTION" = "Removes 'Shuffle' option";
+"REMOVE_SHUFFLE_OPTION_DESC" = "Removes 'Shuffle' option from the flyout menu.";
+"REMOVE_UN_SUB_OPTION" = "Removes 'Unsubscribe' option";
+"REMOVE_UN_SUB_OPTION_DESC" = "Removes 'Unsubscribe' option from the flyout menu.";
+"REMOVE_HIDE_FROM_PLAYLIST_OPTION" = "Removes 'Hide video from playlist' option";
+"REMOVE_HIDE_FROM_PLAYLIST_OPTION_DESC" = "Removes 'Hide video from playlist' option from the flyout menu.";
+"REMOVE_HELP_OPTION" = "Removes 'Help & feedback' option";
+"REMOVE_HELP_OPTION_DESC" = "Removes 'Help & feedback' option from the flyout menu.";
+"REMOVE_NOTIFY_OPTION" = "Removes 'Notify me' option";
+"REMOVE_NOTIFY_OPTION_DESC" = "Removes 'Notify me' option from the flyout menu.";
+"REMOVE_CLEARSCREEN_OPTION" = "Removes 'Clear Screen' option";
+"REMOVE_CLEARSCREEN_OPTION_DESC" = "Removes 'Clear Screen' option from the flyout menu.";
+"HIDE_COMMU_GUIDE" = "Remove guidelines";
+"HIDE_COMMU_GUIDE_DESC" = "Removes pinned guidelines from the comments section.";
+"DISABLES_RTL" = "Disable RTL formatting";
+"DISABLES_RTL_DESC" = "Forcefully displays text in left-to-right (LTR) format for languages that are initially displayed in right-to-left (RTL).";
+"DEVICE_UI" = "Interface style";
+
+"SB_DESCRIPTION" = "Skip sponsored segments automatically using community-submitted data.";
+"SB_ENABLE" = "Enable SponsorBlock";
+"SB_ENABLE_DESC" = "Activate the SponsorBlock extension to skip sponsored segments.";
+"SB_SHOW_BUTTON" = "Show overlay button";
+"SB_SHOW_BUTTON_DESC" = "Display a SponsorBlock toggle button in the player overlay.";
+"SB_SHOW_NOTIFICATIONS" = "Show notifications";
+"SB_SHOW_NOTIFICATIONS_DESC" = "Display notifications when segments are skipped. Notifications for manual-skip categories are always shown.";
+"SB_HAPTIC_FEEDBACK" = "Haptic feedback";
+"SB_HAPTIC_FEEDBACK_DESC" = "Provide haptic feedback when a segment is skipped.";
+"SB_SEGMENTS_IN_PLAYER" = "Segments in player";
+"SB_SEGMENTS_IN_PLAYER_DESC" = "Display colored segments on the progress bar of the main video player.";
+"SB_SEGMENTS_IN_FEED" = "Segments in feed";
+"SB_SEGMENTS_IN_FEED_DESC" = "Display colored segments on the progress bar of players in the feed.";
+"SB_SEGMENTS_IN_MINIPLAYER" = "Segments in mini-player";
+"SB_SEGMENTS_IN_MINIPLAYER_DESC" = "Display colored segments on the progress bar of the mini-player.";
+"SB_SHOW_DURATION" = "Show duration without segments";
+"SB_SHOW_DURATION_DESC" = "Display the total video duration excluding skippable segments.";
+"SB_CATEGORIES_HEADER" = "Segment categories";
+"SB_SEGMENT_COLOR" = "Segment color";
+
+"SB_ACTION_DISABLE" = "Disable";
+"SB_ACTION_AUTO_SKIP" = "Auto-skip";
+"SB_ACTION_ASK" = "Ask before skipping";
+"SB_ACTION_DISPLAY" = "Show on seek bar only";
+"SB_ACTION_SKIP_TO" = "Skip to segment";
+
+"SB_CAT_sponsor" = "Sponsor";
+"SB_CAT_sponsor_DESC" = "Paid promotion for a product or service not directly related to the creator.";
+"SB_CAT_intro" = "Intro";
+"SB_CAT_intro_DESC" = "Intermission or intro animation typically seen at the start of a video.";
+"SB_CAT_outro" = "Endcards";
+"SB_CAT_outro_DESC" = "Endcards, credits, or outros at the end of the video.";
+"SB_CAT_interaction" = "Interaction";
+"SB_CAT_interaction_DESC" = "Reminders to like, subscribe, or follow on social media.";
+"SB_CAT_selfpromo" = "Self-promotion";
+"SB_CAT_selfpromo_DESC" = "Unpaid promotion of the creator's own products, merchandise, or other content.";
+"SB_CAT_music_offtopic" = "Non-music";
+"SB_CAT_music_offtopic_DESC" = "Non-music sections in music videos (e.g. talking, introductions).";
+"SB_CAT_preview" = "Preview";
+"SB_CAT_preview_DESC" = "Recap or preview of content from another video or previous episode.";
+"SB_CAT_hook" = "Hook/Greetings";
+"SB_CAT_hook_DESC" = "Narrated trailers for the upcoming video, greetings and goodbyes.";
+"SB_CAT_poi_highlight" = "Highlight";
+"SB_CAT_poi_highlight_DESC" = "The key moment or highlight point of the video. Use 'Skip to' to jump directly to it.";
+"SB_CAT_filler" = "Filler";
+"SB_CAT_filler_DESC" = "Tangential content, jokes, or filler that is not directly related to the main topic.";
+
+"SB_SKIPPED" = "%@ segment has been skipped";
+"SB_DETECTED" = "%@ segment detected.\nWould you like to skip the segment?";
+"SB_SKIP_NOW" = "Skip";
+"SB_UNSKIP" = "Unskip";
+"SB_JUMPED_TO_HIGHLIGHT" = "Jumped to highlight";
+"SB_JUMP_TO_HIGHLIGHT" = "Highlight available. Jump to the point?";
+"SB_SEGMENT_COLOR_SUFFIX" = "Segment color";
+"SB_SECTION_MAIN" = "Main";
+"SB_SKIP_ALERT_DURATION" = "Skip alert duration";
+"SB_UNSKIP_ALERT_DURATION" = "Unskip alert duration";
\ No newline at end of file
diff --git a/layout/Library/Application Support/YouMod.bundle/ms.lproj/Localizable.strings b/layout/Library/Application Support/YouMod.bundle/ms.lproj/Localizable.strings
index bccaf550..2c1a8a0f 100644
--- a/layout/Library/Application Support/YouMod.bundle/ms.lproj/Localizable.strings
+++ b/layout/Library/Application Support/YouMod.bundle/ms.lproj/Localizable.strings
@@ -1,11 +1,18 @@
-"NOTE" = "NOTE: Some hiding features may leave a blank box.";
"SETTINGS" = "Settings";
+"INTERFACE" = "Interface";
+
+"LOOP_DISABLED" = "Loop disabled";
+"LOOP_ENABLED" = "Loop enabled";
"NEW_ISSUES" = "Report issues or request new features";
"NEW_ISSUES_DESC" = "Found bugs or want to request new features? Tap here.";
"SOURCE_CODES" = "Source codes";
"SOURCE_CODES_DESC" = "Take a look at the codes!";
+"EXTRA" = "Extra";
+"FIX_PLAYBACK_ISSUES" = "Fix playback issues (Temporary)";
+"FIX_PLAYBACK_ISSUES_DESC" = "Try to fix video playback issues for some users. Turning this on will make the player automatically reloads itself everytime it errors or fails to play the video.";
+
"PERFER_HEADER" = "YouMod perferences";
"PERFER" = "Perferences";
"IMPORT" = "Import settings";
@@ -24,6 +31,7 @@
"ERROR_NO_KEYS_IMPORT" = "No available keys to import";
"ERROR_NO_KEYS_EXPORT" = "No available keys to export";
"OVERRIDE" = "This will override the current settings. Do you want to continue?";
+"DISABLED" = "Disabled";
"CACHE" = "Cache management";
"CLEARCACHE" = "Clear cache";
@@ -32,12 +40,80 @@
"AUTO_CLEARCACHE_DESC" = "Automatically clears the app cache on startup.";
"DOWNLOADING" = "Downloading";
-"DOWNLOAD_MANAGER" = "Download Manager";
-"DOWNLOAD_MANAGER_DESC" = "Replaces YouTube's video downloader with YouMod one. With ablity to downloading videos, audios, captions, and thumbnails";
+"DOWNLOAD_MANAGER" = "Download manager";
+"DOWNLOAD_MANAGER_DESC" = "Replaces YouTube's video downloader with YouMod one. With ablity to downloading videos, audios, captions, and thumbnails.";
"DOWNLOAD_SAVE_PHOTOS" = "Automatically save to Photos";
"DOWNLOAD_SAVE_PHOTOS_DESC" = "Automatically saves downloaded videos and images directly to your Photos app.";
-"DOWNLOAD_DRC_AUDIO" = "Prefer DRC Audio";
-"DOWNLOAD_DRC_AUDIO_DESC" = "Prioritizes downloading audio tracks with Dynamic Range Compression for more consistent volume levels.";
+"ADD_SHORTS_DOWNLOAD" = "Adds a download button to Shorts";
+"ADD_SHORTS_DOWNLOAD_DESC" = "Adds a download button to Shorts video overlay.";
+"USE_ORIG_AUDIO" = "Automatically uses original audio track";
+"USE_ORIG_AUDIO_DESC" = "Automatically uses original audio track instead of showing a dialog.";
+"DOWNLOAD_SHORTS" = "Download Shorts";
+"DOWNLOAD_SHORTS_DESC" = "Choose Shorts quality";
+"COPY_VID_INFO" = "Copy video informations";
+"COPY_VID_INFO_DESC" = "Choose what to copy";
+"COPY_ALL_VID_INFO" = "Copy all";
+"COPY_ALL_VID_INFO_DESC" = "Copy all video informations";
+"COPY_AUTHOR" = "Copy author";
+"COPY_AUTHOR_DESC" = "Copy the video author";
+"COPY_TITLE" = "Copy title";
+"COPY_TITLE_DESC" = "Copy the video title";
+"COPY_DESCRIPTION" = "Copy description";
+"COPY_DESCRIPTION_DESC" = "Copy the video description";
+"COPY_URL" = "Copy video link";
+"COPY_URL_TIMESTAMP" = "Copy video link with timestamp";
+"URL_COPIED" = "Copied video link";
+"URL_TIMESTAMP_COPIED" = "Copied video link with timestamp";
+"ERROR_VIDEOID" = "Cannot get the video ID";
+"ERROR_ADS" = "Cannot copy video link while playing ads";
+"COPIED_AUTHOR" = "Copied video author";
+"COPIED_TITLE" = "Copied video title";
+"COPIED_DESCRIPTION" = "Copied video description";
+"SAVE_THUMBNAIL" = "Save thumbnail";
+"SAVE_THUMBNAIL_DESC" = "Save to Photos";
+"DOWNLOAD_CAPTIONS" = "Download captions";
+"DOWNLOAD_CAPTIONS_DESC" = "Save subtitles as .VTT file";
+"DOWNLOAD_AUDIO" = "Download audio";
+"DOWNLOAD_AUDIO_DESC" = "Download audio as .m4a file";
+"DOWNLOAD_VIDEO" = "Download video";
+"DOWNLOAD_VIDEO_DESC" = "Choose video quality";
+"OPEN_VID_BEFORE" = "Open a video before using the download manager";
+"NO_CAPTIONS_URL" = "No valid caption URLs found";
+"NO_AUDIO_URL" = "No audio URL found";
+"NO_STREAM_URL" = "No stream URL found";
+"CAPTIONS_FAILED" = "Failed to download captions";
+"NO_CAPTIONS" = "No captions available for this video";
+"NO_AUDIO_STREAM_FOUND" = "No audio streams found";
+"NO_VID_AUDIO_STREAM_FOUND" = "No video/audio streams found";
+"COPIED_VID_INFO" = "Copied all video informations";
+"SAVED_TO_PHOTOS" = "Saved to Photos";
+"SAVE_FAILED" = "Save failed";
+"PHOTO_ACCESS_DENINED" = "Photos access denied";
+"THUMBNAIL_FAILED" = "Thumbnail failed";
+"DOWNLOADING_THUMBNAIL" = "Downloading thumbnail...";
+"DOWNLOADING_AUDIO" = "Downloading audio...";
+"DOWNLOADING_VIDEO" = "Downloading video...";
+"DOWNLOADING_CAPTIONS" = "Downloading captions...";
+"NO_THUMBNAIL_FOUND" = "No thumbnail found";
+"DOWNLOAD_FAILED" = "Download failed";
+"COPY_THUMBNAIL" = "Copy thumbnail";
+"COPY_THUMBNAIL_DESC" = "Copy to clipboard";
+"SHOW_THUMBNAIL" = "Show thumbnail";
+"SHOW_THUMBNAIL_DESC" = "Show in a dialog";
+"THUMBNAIL_OPTIONS" = "Download thumbnail";
+"THUMBNAIL_OPTIONS_DESC" = "Show more options";
+"COPIED_TO_CLIPBOARD" = "Copied to clipboard";
+"DOWNLOAD_COMPLETED" = "Download completed";
+"CANNOT_SAVE_TO_PHOTOS" = "Cannot save to Photos";
+"FINA_VIDEO" = "Finalizing video...";
+"MERGING_VID_FALLBACK" = "Merging video with fallback...";
+"MERGING_VID" = "Merging video...";
+"CONVERTING" = "Converting to";
+"ALREADY_DOWNLOADING" = "Already downloading";
+"DOWNLOAD_CANCELLED" = "Download cancelled";
+"CANCEL" = "Cancel";
+"TRIMMING_AUDIO" = "Trimming audio...";
+"COPY" = "Copy";
"APPEARANCE" = "Appearance";
"OLED_THEME" = "OLED dark mode";
@@ -46,6 +122,8 @@
"OLED_KEYBOARD_DESC" = "Make the keyboard uses a pure black (OLED) color for dark theme.";
"NAVBAR" = "Navigation bar";
+"STICKY_NAVBAR" = "Sticky Navigation bar";
+"STICKY_NAVBAR_DESC" = "Pins the Navigation bar so that it remains visible when scrolling down.";
"HIDE_YT_LOGO" = "Hide YouTube logo";
"HIDE_YT_LOGO_DESC" = "Hide YouTube logo in the navigation bar.";
"PREMIUM_LOGO" = "Use YouTube Premium logo";
@@ -62,34 +140,78 @@
"FEED" = "Feed";
"HIDE_SUBBAR" = "Hide subbar";
"HIDE_SUBBAR_DESC" = "Hide subbar at the top of the feed.";
+"HIDE_HORI_SHELF" = "Remove horizontal feeds";
+"HIDE_HORI_SHELF_DESC" = "Removes all horizontal feeds (News, Continue Watching, Posts collection, etc.) from the feed, except Playables.";
"HIDE_MUSIC_PLAYLISTS" = "Hide music playlist generator";
"HIDE_MUSIC_PLAYLISTS_DESC" = "Hide music playlist generator (Select music tunes in three) in the feed.";
"HIDE_FEED_POST" = "Hide posts";
"HIDE_FEED_POST_DESC" = "Hide posts in feed.";
+"HIDE_SURVEYS" = "Hide surveys";
+"HIDE_SURVEYS_DESC" = "Hide video surveys that YouTube shows sometimes.";
"HIDE_SHORTS_SHELF" = "Hide Shorts shelf";
"HIDE_SHORTS_SHELF_DESC" = "Hide Shorts shelf in the feed, including in the Subscriptions tab.";
+"KEEP_SHORTS_SUBSCRIPT" = "Keep Shorts in Subscriptions";
+"KEEP_SHORTS_SUBSCRIPT_DESC" = "Prevents removing Shorts from Subscriptions tab.";
"HIDE_SEARCH_HISTORY" = "Hide search history";
"HIDE_SEARCH_HISTORY_DESC" = "Hide previous search history and suggestions when using the search bar.\nNOTE: Your search history will still be accessible in your other YouTube clients.";
-"HIDE_SUB_BUTTON" = "Hide Subscribe button";
-"HIDE_SUB_BUTTON_DESC" = "Hides the subscribe button in channels and video descriptions.";
-"HIDE_SHOP_BUTTON" = "Hide Shopping button";
-"HIDE_SHOP_BUTTON_DESC" = "Hides the shopping/store button in channels descriptions.";
-"HIDE_MEMBER_BUTTON" = "Hide Membership button";
-"HIDE_MEMBER_BUTTON_DESC" = "Hides the 'Join' button in channels and video descriptions.";
+"HIDE_PLAYABLES" = "Remove playables";
+"HIDE_PLAYABLES_DESC" = "Removes playables (mini-games) from the feed.";
"PLAYER" = "Player";
+"PLAYBACK_SPEED" = "Playback speed";
+"CONTROL_CENTER" = "Control Center";
+"PLAYER_ACTIONS" = "Player actions";
+"GESTURE_HEADER" = "Gestures";
+"QUALITY_WIFI" = "Playback quality on WiFi";
+"QUALITY_WIFI_DESC" = "Automatically applies the selected quality when streaming over Wi-Fi.";
+"QUALITY_CELLULAR" = "Playback quality on Cellular";
+"QUALITY_CELLULAR_DESC" = "Automatically applies the selected quality when streaming over mobile data.";
+"QUALITY_LOW_POWER" = "Playback quality on Power saver mode";
+"QUALITY_LOW_POWER_DESC" = "Automatically applies the selected quality when when Low power mode is enabled on your device.";
+"DEFAULT" = "Default";
+"BEST" = "Best";
+"AUDIO_TRACK" = "Preferred audio track";
+"ORIGINAL" = "Original";
+"SELECT_MANUALLY" = "Select manually";
+"AUDIO_TRACK_SELECT" = "Selected audio track";
+"AUDIO_TRACK_SELECT_DESC" = "Specifies the preferred audio track language to use for playback, if available.";
+"NO_AUTO_DUBBED" = "Uses original audio track";
+"NO_AUTO_DUBBED_DESC" = "Uses original audio track if the preferred language track is auto-dubbed or not found.";
+"CAPTION_TRACK" = "Preferred captions";
+"CAPTION_TRACK_SELECT" = "Selected captions";
+"CAPTION_TRACK_SELECT_DESC" = "Specifies the preferred captions language to display, if available.";
+"DISABLES_CAPTION_TRACK" = "Automatically disable captions";
+"DISABLES_CAPTION_TRACK_DESC" = "Automatically disable captions if the preferred language is auto-generated or not found.";
+"DEFAULT_SPEED" = "Default playback rate";
+"DEFAULT_SPEED_DESC" = "Overrides the default playback speed.";
+"SPEED_BUTTON" = "Speed controls button";
+"SPEED_BUTTON_DESC" = "Displays a speed controls button on overlay.";
+"MUTE_BUTTON" = "Mute button";
+"MUTE_BUTTON_DESC" = "Displays a button to mute or unmute the volume on the overlay.";
+"SHARE_BUTTON" = "Share button";
+"SHARE_BUTTON_DESC" = "Displays a button to share the video link on the overlay.";
+"LOOP_BUTTON" = "Loop button";
+"LOOP_BUTTON_DESC" = "Displays a button to loop the video on the overlay.";
+"CAPTION_BUTTON" = "Captions button";
+"CAPTION_BUTTON_DESC" = "Displays a button to copy the video captions on the overlay.";
+"HOLD_TO_SPEED" = "Hold to speed";
+"HOLD_TO_SPEED_DESC" = "Uses custom hold to speed playback rate.";
"HIDE_AUTOPLAY" = "Hide autoplay switch";
"HIDE_AUTOPLAY_DESC" = "Hide autoplay switch in the video overlay.";
"HIDE_CAPTIONS_BUTTON" = "Hide captions button";
"HIDE_CAPTIONS_BUTTON_DESC" = "Hide captions button in the video overlay.";
"HIDE_CAST_BUTTON_PLAYER" = "Hide cast button";
"HIDE_CAST_BUTTON_PLAYER_DESC" = "Hide cast button in the video overlay.";
-"HIDE_PREV_BUTTON" = "Hide previous/back button";
-"HIDE_PREV_BUTTON_DESC" = "Hide previous/back button in the video overlay.";
-"HIDE_NEXT_BUTTON" = "Hide next/skip button";
-"HIDE_NEXT_BUTTON_DESC" = "Hide next/skip button in the video overlay.";
+"HIDE_NEXT_AND_PREV_BUTTON" = "Hide previous and next buttons";
+"HIDE_NEXT_AND_PREV_BUTTON_DESC" = "Hide previous and next buttons in the video overlay.";
"REPLACE_PREVNEXT_BUTTONS" = "Replace Previous/Next buttons";
"REPLACE_PREVNEXT_BUTTONS_DESC" = "Replaces the Previous and Next video buttons in the player with Rewind and Fast Forward buttons.";
+"SKIP_BACKWARD" = "Skip backward";
+"SKIP_BACKWARD_DESC" = "Replaces the previous track button in Control Center with a skip backward button by a set number of seconds.";
+"SKIP_FORWARD" = "Skip forward";
+"SKIP_FORWARD_DESC" = "Replaces the next track button in Control Center with a skip forward button by a set number of seconds.";
+"REWIND_SECONDS" = "Rewind seconds";
+"FORWARD_SECONDS" = "Fast-forward seconds";
"REMOVE_DARK_OVERLAY" = "Remove dark overlay";
"REMOVE_DARK_OVERLAY_DESC" = "Remove dark overlay from the video overlay.";
"HIDE_END_SCREEN" = "Hide endscreen cards";
@@ -102,6 +224,12 @@
"HIDE_PAID_OVERLAY_DESC" = "Hide 'Includes paid promotion' in the video overlay.";
"HIDE_WATERMARK" = "Hide watermark";
"HIDE_WATERMARK_DESC" = "Hide channel watermark at the bottom of the video overlay.";
+"PAUSE_ON_OVERLAY" = "Pause on overlay";
+"PAUSE_ON_OVERLAY_DESC" = "Sets playback on pause if overlay appears.";
+"DISABLES_SNAP_TO_CHAPTER" = "Disable snap to chapters";
+"DISABLES_SNAP_TO_CHAPTER_DESC" = "Prevents the scrubbar stick to the chapters.";
+"DISABLES_ENGAGE_PANEL" = "Disables engagement panel in fullscreen";
+"DISABLES_ENGAGE_PANEL_DESC" = "Prevent engagement panel showing in fullscreen.";
"GESTURES" = "Enable gestures controls";
"GESTURES_DESC" = "Swipe up or down on the left/right edges of the video screen to adjust brightness, volume, or playback speed.";
"GESTURE_AREA" = "Gesture activation area";
@@ -136,6 +264,8 @@
"DISABLES_SHOW_REMAINING_DESC" = "Disable the feature that shows how much time is left in a video when tapping the timestamp.";
"ALWAYS_SHOW_REMAINING" = "Always show remaining time";
"ALWAYS_SHOW_REMAINING_DESC" = "Ensure the remaining time is always displayed by default in the player overlay.";
+"COPY_TIMESTAMP_ON_PAUSE" = "Copy timestamped links";
+"COPY_TIMESTAMP_ON_PAUSE_DESC" = "Allows to copy timestamped link to the clipboard by pressing pause button.";
"SHOW_REMAINING_EXTRA" = "Show extra remaining time";
"SHOW_REMAINING_EXTRA_DESC" = "Adds the estimated finish time to the label in the video overlay.";
"HIDE_FULLSCREEN_ACTIONS" = "Hide fullscreen actions";
@@ -148,93 +278,95 @@
"HIDE_CONTENT_WARNING_DESC" = "Automatically skip the sensitive content warning alert in some vidoes.";
"AUTO_FULLSCREEN" = "Auto fullscreen";
"AUTO_FULLSCREEN_DESC" = "Automatically enter fullscreen mode when starting video playback.";
-"AUTO_DISABLES_CAPTION" = "Auto disable captions";
-"AUTO_DISABLES_CAPTION_DESC" = "Automatically turns off closed captions (CC) whenever a new video starts playing.";
"PORTRAIT_FULLSCREEN" = "Portrait fullscren mode";
"PORTRAIT_FULLSCREEN_DESC" = "Enables portrait fullscreen mode support.";
"OLD_QUALITY_PICKER" = "Use old quality picker";
"OLD_QUALITY_PICKER_DESC" = "Reverts the video quality selection menu to the classic version (lists all resolutions immediately).";
"EXTRA_SPEED" = "Extra speed options";
"EXTRA_SPEED_DESC" = "Adds more video playback speed options up to 10x.";
-"DISABLE_HINTS" = "Disable hints";
-"DISABLE_HINTS_DESC" = "Disable the tutorial hints and interactive pop-ups.";
"FORCE_MINIPLAYER" = "Force miniplayer";
"FORCE_MINIPLAYER_DESC" = "Allows all videos, including those made for kids, to play in the miniplayer when dismissing the player.";
"FORCE_SEEKBAR" = "Always show progress bar";
"FORCE_SEEKBAR_DESC" = "Makes the progress bar (seekbar) remains visible at all times while in fullscreen mode.";
-"HIDE_LIKE_BUTTON" = "Hide Like button";
-"HIDE_LIKE_BUTTON_DESC" = "Hide Like button in the action bar under the video overlay.";
-"HIDE_DISLIKE_BUTTON" = "Hide Dislike button";
-"HIDE_DISLIKE_BUTTON_DESC" = "Hide Dislike button in the action bar under the video overlay.";
-"HIDE_SHARE_BUTTON" = "Hide Share button";
-"HIDE_SHARE_BUTTON_DESC" = "Hide Share button in the action bar under the video overlay.";
-"HIDE_DOWNLOAD_BUTTON" = "Hide Download button";
-"HIDE_DOWNLOAD_BUTTON_DESC" = "Hide Download button in the action bar under the video overlay.";
-"HIDE_CLIP_BUTTON" = "Hide Clip button";
-"HIDE_CLIP_BUTTON_DESC" = "Hide Clip button in the action bar under the video overlay.";
-"HIDE_REMIX_BUTTON" = "Hide Remix button";
-"HIDE_REMIX_BUTTON_DESC" = "Hide Remix button in the action bar under the video overlay.";
-"HIDE_SAVE_BUTTON" = "Hide Save button";
-"HIDE_SAVE_BUTTON_DESC" = "Hide Save button in the action bar under the video overlay.";
+"TAP_TO_SEEK" = "Tap to seek";
+"TAP_TO_SEEK_DESC" = "Quickly jump to any point by tapping the progress bar.";
+"DISABLES_ZOOM" = "Disable free zoom gesture";
+"DISABLES_ZOOM_DESC" = "Disables new free zoom gestures.";
+"USES_24_HOURS_TIME" = "24-hour format";
+"USES_24_HOURS_TIME_DESC" = "Shows end time in 24-hour format.";
+"PAUSE_TWO_FINGERS" = "Pause using two fingers";
+"PAUSE_TWO_FINGERS_DESC" = "Pause/Resume playback by tapping on the screen using two fingers.";
+"HIDE_COMMENTS_SECTION" = "Hide comments section";
+"HIDE_COMMENTS_SECTION_DESC" = "Completely removes the entire comments section.";
+"HIDE_COMMENTS_PREVIEW" = "Hide preview comments";
+"HIDE_COMMENTS_PREVIEW_DESC" = "Removes the preview box for the top comment while keeping the full section expandable.";
+"USE_ANOTHER_MINIPLAYER" = "Uses another miniplayer style";
+"USE_ANOTHER_MINIPLAYER_DESC" = "Uses another miniplayer style redesigned from older YT versions.";
"SHORTS" = "Shorts";
-"HIDE_SHORTS_LIKE_BUTTON" = "Hide Like button";
-"HIDE_SHORTS_LIKE_BUTTON_DESC" = "Hide the Like button in the Shorts player.";
-"HIDE_SHORTS_DISLIKE_BUTTON" = "Hide Dislike button";
-"HIDE_SHORTS_DISLIKE_BUTTON_DESC" = "Hide the Dislike button in the Shorts player.";
-"HIDE_SHORTS_COMMENT_BUTTON" = "Hide Comment button";
-"HIDE_SHORTS_COMMENT_BUTTON_DESC" = "Hide the Comment button in the Shorts player.";
-"HIDE_SHORTS_SHARE_BUTTON" = "Hide Share button";
-"HIDE_SHORTS_SHARE_BUTTON_DESC" = "Hide the Share button in the Shorts player.";
-"HIDE_SHORTS_REMIX_BUTTON" = "Hide Remix button";
-"HIDE_SHORTS_REMIX_BUTTON_DESC" = "Hide the Remix button in the Shorts player.";
-"HIDE_METADATA_BUTTON" = "Hide sound metadata button";
-"HIDE_METADATA_BUTTON_DESC" = "Hide the button that shows the sound source or music metadata in the Shorts player.";
+"SHORTS_ONLY" = "Shorts only mode";
+"SHORTS_ONLY_DESC" = "Limits YouTube functionality to viewing Shorts only.";
+"SHORTS_ACTION" = "Shorts action after the video ends";
+"LOOP" = "Loop video";
+"SKIP_TO_NEXT_SHORTS" = "Skip to next video";
+"PAUSE_SHORTS" = "Stop video";
+"REMOVE_LIVE_SHORTS" = "Remove live videos";
+"REMOVE_LIVE_SHORTS_DESC" = "Removes live videos from the feed.";
"HIDE_SHORTS_PRODUCT" = "Hide products";
"HIDE_SHORTS_PRODUCT_DESC" = "Hide tagged products and shopping links in the Shorts overlay.";
"HIDE_SHORTS_RECBAR" = "Hide recommendation bar";
"HIDE_SHORTS_RECBAR_DESC" = "Hide the suggested action bar in the Shorts player (e.g. Try this sound, Shopping, Preview comment).";
-"HIDE_SHORTS_COMMIT" = "Hide commissions bar";
-"HIDE_SHORTS_COMMIT_DESC" = "Hide the bar that shows the video got commissions in the Shorts player.";
-"HIDE_SHORTS_SUBSCRIPT_BUTTON" = "Hide subscriptions button";
-"HIDE_SHORTS_SUBSCRIPT_BUTTON_DESC" = "Hide the Subscriptions button at the top of the Shorts player.";
-"HIDE_SHORTS_LIVE_BUTTON" = "Hide live button";
-"HIDE_SHORTS_LIVE_BUTTON_DESC" = "Hide the Live button at the top of the Shorts player.";
-"HIDE_SHORTS_LENS_BUTTON" = "Hide lens button";
-"HIDE_SHORTS_LENS_BUTTON_DESC" = "Hide the Lens button at the top of the Shorts player.";
-"HIDE_SHORTS_TRENDS_BUTTON" = "Hide trends button";
-"HIDE_SHORTS_TRENDS_BUTTON_DESC" = "Hide the Trends button at the top of the Shorts player.";
-"HIDE_SHORTS_TO_VIDEO" = "Hide link to full video";
-"HIDE_SHORTS_TO_VIDEO_DESC" = "Hide the link that redirects in a Short to a full video in the Shorts player.";
"ENABLES_SHORTS_QUALITY" = "Enable Shorts quality picker";
"ENABLES_SHORTS_QUALITY_DESC" = "Force enable the video quality selection menu in Shorts.";
"SHOW_SHORTS_SEEKBAR" = "Always show seekbar";
"SHOW_SHORTS_SEEKBAR_DESC" = "Ensure the progress/seek bar is always visible at the bottom of the Shorts player.";
+"REMOVE_POSTS_SHORTS" = "Remove posts";
+"REMOVE_POSTS_SHORTS_DESC" = "Remove posts from the feed.";
+"SHORTS_FULLSCREEN" = "Pinch to Fullscreen";
+"SHORTS_FULLSCREEN_DESC" = "Manages visibility of the overlay with pinch in and pinch out gestures, displaying Shorts in fullscreen mode.";
"TABBAR" = "Tab bar";
"DEFAULT_TAB" = "Default startup tab";
-"DEFAULT_TAB_DESC" = "You can select the tab you want to open when opening the app.";
-"HOME_NAME" = "Home";
-"SUBSCRIPT_NAME" = "Subscriptions";
-"LIB_NAME" = "Library (You)";
+"FORSTED_TAB_BAR" = "Translucent tab bar";
+"ENABLED" = "Enabled";
"HIDE_TAB_INDI" = "Hide tab indicators";
"HIDE_TAB_INDI_DESC" = "Hide all tab indicators in the tab bar.";
"HIDE_TAB_LABELS" = "Hide tab labels";
"HIDE_TAB_LABELS_DESC" = "Hide all tab labels in the tab bar.";
-"HIDE_HOME_TAB" = "Hide Home tab";
-"HIDE_HOME_TAB_DESC" = "Hide Home tab in the tab bar.";
-"HIDE_SHORTS_TAB" = "Hide Shorts tab";
-"HIDE_SHORTS_TAB_DESC" = "Hide Shorts tab in the tab bar.";
-"HIDE_CREATE_BUTTON" = "Hide Create button";
-"HIDE_CREATE_BUTTON_DESC" = "Hide Create button in the tab bar.";
-"HIDE_SUBSCRIPT_TAB" = "Hide Subscriptions tab";
-"HIDE_SUBSCRIPT_TAB_DESC" = "Hide Subscriptions tab in the tab bar.";
+"HOME_TAB" = "Home";
+"SHORTS_TAB" = "Shorts";
+"CREATE_TAB" = "Create";
+"SUBSCRIPTIONS_TAB" = "Subscriptions";
+"LIBRARY_TAB" = "Library (You)";
+"HISTORY_TAB" = "History";
+"GAMING_TAB" = "Gaming";
+"SPORTS_TAB" = "Sports";
+"NOTI_TAB" = "Notifications";
+"NEWS_TAB" = "News";
+"MUSIC_TAB" = "Music";
+"WATCH_LATER_TAB" = "Watch later";
+"PLAYLIST_TAB" = "Playlists";
+"LIKE_TAB" = "Liked videos";
+"LIVE_TAB" = "Live";
+"POST_TAB" = "Posts";
+"VIDEO_TAB" = "Your videos";
+"MOVIE_TAB" = "Movies";
+"COURSE_TAB" = "Courses";
+"MINIGAME_TAB" = "Playables";
+"MANAGE_TABS" = "Manage tabs";
+"MANAGE_TABS_DESC" = "Reorder and toggle tabs";
+"TAB_REORDER_HINT" = "Drag to reorder. Toggle to show/hide.\nTab settings are also accessible by long-pressing the first tab in the tab bar.";
+"TAB_LIMIT" = "Tab limit";
+"TAB_LIMIT_DESC" = "Only 6 tabs are allowed.";
+"ZERO_TAB_DESC" = "At least 1 tab has to be enabled.";
"MISCELLANEOUS" = "Miscellaneous";
"BACKGROUND_PLAYBACK" = "Background playback";
"BACKGROUND_PLAYBACK_DESC" = "Enables background playback for videos.";
"DISABLES_SHORTS_PIP" = "Disables Shorts PiP";
"DISABLES_SHORTS_PIP_DESC" = "Try to disables Shorts PiP, this might not 100% disables it.";
+"DISABLE_HINTS" = "Disable hints";
+"DISABLE_HINTS_DESC" = "Disable the tutorial hints and interactive pop-ups.";
"BLOCK_UPGRADE_DIALOGS" = "Block upgrade dialogs";
"BLOCK_UPGRADE_DIALOGS_DESC" = "Block the YouTube dialogs that ask you to upgrade the app.";
"ARE_YOU_THERE_DIALOG" = "Hide 'Are you there?' dialog";
@@ -247,7 +379,109 @@
"DISABLES_SNACK_BAR_DESC" = "Disables snack bar when pressing some buttons or doing something in the app.";
"HIDE_STARTUP_ANIMATIONS" = "Hide YouTube's startup animations";
"HIDE_STARTUP_ANIMATIONS_DESC" = "Hide YouTube's startup animations when opening the app.";
-"HIDE_PLAY_IN_NEXT_QUEUE" = "Hide 'Play next in queue'";
-"HIDE_PLAY_IN_NEXT_QUEUE_DESC" = "Hide the 'Play next in queue' option in the flyout menu.";
"HIDE_LIKE_DISLIKE_VOTES" = "Hide like/dislike votes";
-"HIDE_LIKE_DISLIKE_VOTES_DESC" = "Hide messages that pop up when you like/dislike videos.";
\ No newline at end of file
+"HIDE_LIKE_DISLIKE_VOTES_DESC" = "Hide messages that pop up when you like/dislike videos.";
+"FLYOUT_MENU" = "Flyout menu";
+"REMOVE_PLAY_IN_NEXT_QUEUE_OPTION" = "Removes 'Play next in queue' option";
+"REMOVE_PLAY_IN_NEXT_QUEUE_OPTION_DESC" = "Removes 'Play next in queue' option from the flyout menu.";
+"REMOVE_DOWNLOAD_OPTION" = "Removes 'Download video' option";
+"REMOVE_DOWNLOAD_OPTION_DESC" = "Removes 'Download video' option from the flyout menu.";
+"REMOVE_WATCH_LATER_OPTION" = "Removes 'Save to Watch later' option";
+"REMOVE_WATCH_LATER_OPTION_DESC" = "Removes 'Save to Watch Later' option from the flyout menu.";
+"REMOVE_SAVE_OPTION" = "Removes 'Save to playlist' option";
+"REMOVE_SAVE_OPTION_DESC" = "Removes 'Save to playlist' option from the flyout menu.";
+"REMOVE_REMOVE_FROM_PLAYLIST_OPTION" = "Removes 'Remove from playlist' option";
+"REMOVE_REMOVE_FROM_PLAYLIST_OPTION_DESC" = "Removes 'Remove from playlist' option from the flyout menu.";
+"REMOVE_SHARE_OPTION" = "Removes 'Share' option";
+"REMOVE_SHARE_OPTION_DESC" = "Removes 'Share' option from the flyout menu.";
+"REMOVE_NOT_INTERESTED_OPTION" = "Removes 'Not interested' option";
+"REMOVE_NOT_INTERESTED_OPTION_DESC" = "Removes 'Not interested' option from the flyout menu.";
+"REMOVE_DONT_RECOMMEND_OPTION" = "Removes 'Don't recommend channel' option";
+"REMOVE_DONT_RECOMMEND_OPTION_DESC" = "Removes 'Don't recommend channel' option from the flyout menu.";
+"REMOVE_INFO_OPTION" = "Removes 'Why are you seeing this?' option";
+"REMOVE_INFO_OPTION_DESC" = "Removes 'Why are you seeing this?' option from the flyout menu.";
+"REMOVE_FILTER_OPTION" = "Removes 'Search filters' option";
+"REMOVE_FILTER_OPTION_DESC" = "Removes 'Search filters' option from the flyout menu.";
+"REMOVE_REPORT_OPTION" = "Removes 'Report' option";
+"REMOVE_REPORT_OPTION_DESC" = "Removes 'Report' option from the flyout menu.";
+"REMOVE_YOUTUBE_MUSIC_OPTION" = "Removes 'Listen with YouTube Music' option";
+"REMOVE_YOUTUBE_MUSIC_OPTION_DESC" = "Removes 'Listen with YouTube Music' option from the flyout menu.";
+"REMOVE_FEED_BACK_OPTION" = "Removes 'Send feedback' option";
+"REMOVE_FEED_BACK_OPTION_DESC" = "Removes 'Send feedback' option from the flyout menu.";
+"REMOVE_CAST_OPTION" = "Removes 'Watch on TV' option";
+"REMOVE_CAST_OPTION_DESC" = "Removes 'Watch on TV' option from the flyout menu.";
+"REMOVE_SHUFFLE_OPTION" = "Removes 'Shuffle' option";
+"REMOVE_SHUFFLE_OPTION_DESC" = "Removes 'Shuffle' option from the flyout menu.";
+"REMOVE_UN_SUB_OPTION" = "Removes 'Unsubscribe' option";
+"REMOVE_UN_SUB_OPTION_DESC" = "Removes 'Unsubscribe' option from the flyout menu.";
+"REMOVE_HIDE_FROM_PLAYLIST_OPTION" = "Removes 'Hide video from playlist' option";
+"REMOVE_HIDE_FROM_PLAYLIST_OPTION_DESC" = "Removes 'Hide video from playlist' option from the flyout menu.";
+"REMOVE_HELP_OPTION" = "Removes 'Help & feedback' option";
+"REMOVE_HELP_OPTION_DESC" = "Removes 'Help & feedback' option from the flyout menu.";
+"REMOVE_NOTIFY_OPTION" = "Removes 'Notify me' option";
+"REMOVE_NOTIFY_OPTION_DESC" = "Removes 'Notify me' option from the flyout menu.";
+"REMOVE_CLEARSCREEN_OPTION" = "Removes 'Clear Screen' option";
+"REMOVE_CLEARSCREEN_OPTION_DESC" = "Removes 'Clear Screen' option from the flyout menu.";
+"HIDE_COMMU_GUIDE" = "Hide community guidelines";
+"HIDE_COMMU_GUIDE_DESC" = "Hide community guidelines header.";
+"DISABLES_RTL" = "Disable RTL formatting";
+"DISABLES_RTL_DESC" = "Forcefully displays text in left-to-right (LTR) format for languages that are initially displayed in right-to-left (RTL).";
+"DEVICE_UI" = "Interface style";
+
+"SB_DESCRIPTION" = "Skip sponsored segments automatically using community-submitted data.";
+"SB_ENABLE" = "Enable SponsorBlock";
+"SB_ENABLE_DESC" = "Activate the SponsorBlock extension to skip sponsored segments.";
+"SB_SHOW_BUTTON" = "Show overlay button";
+"SB_SHOW_BUTTON_DESC" = "Display a SponsorBlock toggle button in the player overlay.";
+"SB_SHOW_NOTIFICATIONS" = "Show notifications";
+"SB_SHOW_NOTIFICATIONS_DESC" = "Display notifications when segments are skipped. Notifications for manual-skip categories are always shown.";
+"SB_HAPTIC_FEEDBACK" = "Haptic feedback";
+"SB_HAPTIC_FEEDBACK_DESC" = "Provide haptic feedback when a segment is skipped.";
+"SB_SEGMENTS_IN_PLAYER" = "Segments in player";
+"SB_SEGMENTS_IN_PLAYER_DESC" = "Display colored segments on the progress bar of the main video player.";
+"SB_SEGMENTS_IN_FEED" = "Segments in feed";
+"SB_SEGMENTS_IN_FEED_DESC" = "Display colored segments on the progress bar of players in the feed.";
+"SB_SEGMENTS_IN_MINIPLAYER" = "Segments in mini-player";
+"SB_SEGMENTS_IN_MINIPLAYER_DESC" = "Display colored segments on the progress bar of the mini-player.";
+"SB_SHOW_DURATION" = "Show duration without segments";
+"SB_SHOW_DURATION_DESC" = "Display the total video duration excluding skippable segments.";
+"SB_CATEGORIES_HEADER" = "Segment categories";
+"SB_SEGMENT_COLOR" = "Segment color";
+
+"SB_ACTION_DISABLE" = "Disable";
+"SB_ACTION_AUTO_SKIP" = "Auto-skip";
+"SB_ACTION_ASK" = "Ask before skipping";
+"SB_ACTION_DISPLAY" = "Show on seek bar only";
+"SB_ACTION_SKIP_TO" = "Skip to segment";
+
+"SB_CAT_sponsor" = "Sponsor";
+"SB_CAT_sponsor_DESC" = "Paid promotion for a product or service not directly related to the creator.";
+"SB_CAT_intro" = "Intro";
+"SB_CAT_intro_DESC" = "Intermission or intro animation typically seen at the start of a video.";
+"SB_CAT_outro" = "Endcards";
+"SB_CAT_outro_DESC" = "Endcards, credits, or outros at the end of the video.";
+"SB_CAT_interaction" = "Interaction";
+"SB_CAT_interaction_DESC" = "Reminders to like, subscribe, or follow on social media.";
+"SB_CAT_selfpromo" = "Self-promotion";
+"SB_CAT_selfpromo_DESC" = "Unpaid promotion of the creator's own products, merchandise, or other content.";
+"SB_CAT_music_offtopic" = "Non-music";
+"SB_CAT_music_offtopic_DESC" = "Non-music sections in music videos (e.g. talking, introductions).";
+"SB_CAT_preview" = "Preview";
+"SB_CAT_preview_DESC" = "Recap or preview of content from another video or previous episode.";
+"SB_CAT_hook" = "Hook/Greetings";
+"SB_CAT_hook_DESC" = "Narrated trailers for the upcoming video, greetings and goodbyes.";
+"SB_CAT_poi_highlight" = "Highlight";
+"SB_CAT_poi_highlight_DESC" = "The key moment or highlight point of the video. Use 'Skip to' to jump directly to it.";
+"SB_CAT_filler" = "Filler";
+"SB_CAT_filler_DESC" = "Tangential content, jokes, or filler that is not directly related to the main topic.";
+
+"SB_SKIPPED" = "%@ segment has been skipped";
+"SB_DETECTED" = "%@ segment detected.\nWould you like to skip the segment?";
+"SB_SKIP_NOW" = "Skip";
+"SB_UNSKIP" = "Unskip";
+"SB_JUMPED_TO_HIGHLIGHT" = "Jumped to highlight";
+"SB_JUMP_TO_HIGHLIGHT" = "Highlight available. Jump to the point?";
+"SB_SEGMENT_COLOR_SUFFIX" = "Segment color";
+"SB_SECTION_MAIN" = "Main";
+"SB_SKIP_ALERT_DURATION" = "Skip alert duration";
+"SB_UNSKIP_ALERT_DURATION" = "Unskip alert duration";
\ No newline at end of file
diff --git a/layout/Library/Application Support/YouMod.bundle/nl.lproj/Localizable.strings b/layout/Library/Application Support/YouMod.bundle/nl.lproj/Localizable.strings
index bccaf550..2c1a8a0f 100644
--- a/layout/Library/Application Support/YouMod.bundle/nl.lproj/Localizable.strings
+++ b/layout/Library/Application Support/YouMod.bundle/nl.lproj/Localizable.strings
@@ -1,11 +1,18 @@
-"NOTE" = "NOTE: Some hiding features may leave a blank box.";
"SETTINGS" = "Settings";
+"INTERFACE" = "Interface";
+
+"LOOP_DISABLED" = "Loop disabled";
+"LOOP_ENABLED" = "Loop enabled";
"NEW_ISSUES" = "Report issues or request new features";
"NEW_ISSUES_DESC" = "Found bugs or want to request new features? Tap here.";
"SOURCE_CODES" = "Source codes";
"SOURCE_CODES_DESC" = "Take a look at the codes!";
+"EXTRA" = "Extra";
+"FIX_PLAYBACK_ISSUES" = "Fix playback issues (Temporary)";
+"FIX_PLAYBACK_ISSUES_DESC" = "Try to fix video playback issues for some users. Turning this on will make the player automatically reloads itself everytime it errors or fails to play the video.";
+
"PERFER_HEADER" = "YouMod perferences";
"PERFER" = "Perferences";
"IMPORT" = "Import settings";
@@ -24,6 +31,7 @@
"ERROR_NO_KEYS_IMPORT" = "No available keys to import";
"ERROR_NO_KEYS_EXPORT" = "No available keys to export";
"OVERRIDE" = "This will override the current settings. Do you want to continue?";
+"DISABLED" = "Disabled";
"CACHE" = "Cache management";
"CLEARCACHE" = "Clear cache";
@@ -32,12 +40,80 @@
"AUTO_CLEARCACHE_DESC" = "Automatically clears the app cache on startup.";
"DOWNLOADING" = "Downloading";
-"DOWNLOAD_MANAGER" = "Download Manager";
-"DOWNLOAD_MANAGER_DESC" = "Replaces YouTube's video downloader with YouMod one. With ablity to downloading videos, audios, captions, and thumbnails";
+"DOWNLOAD_MANAGER" = "Download manager";
+"DOWNLOAD_MANAGER_DESC" = "Replaces YouTube's video downloader with YouMod one. With ablity to downloading videos, audios, captions, and thumbnails.";
"DOWNLOAD_SAVE_PHOTOS" = "Automatically save to Photos";
"DOWNLOAD_SAVE_PHOTOS_DESC" = "Automatically saves downloaded videos and images directly to your Photos app.";
-"DOWNLOAD_DRC_AUDIO" = "Prefer DRC Audio";
-"DOWNLOAD_DRC_AUDIO_DESC" = "Prioritizes downloading audio tracks with Dynamic Range Compression for more consistent volume levels.";
+"ADD_SHORTS_DOWNLOAD" = "Adds a download button to Shorts";
+"ADD_SHORTS_DOWNLOAD_DESC" = "Adds a download button to Shorts video overlay.";
+"USE_ORIG_AUDIO" = "Automatically uses original audio track";
+"USE_ORIG_AUDIO_DESC" = "Automatically uses original audio track instead of showing a dialog.";
+"DOWNLOAD_SHORTS" = "Download Shorts";
+"DOWNLOAD_SHORTS_DESC" = "Choose Shorts quality";
+"COPY_VID_INFO" = "Copy video informations";
+"COPY_VID_INFO_DESC" = "Choose what to copy";
+"COPY_ALL_VID_INFO" = "Copy all";
+"COPY_ALL_VID_INFO_DESC" = "Copy all video informations";
+"COPY_AUTHOR" = "Copy author";
+"COPY_AUTHOR_DESC" = "Copy the video author";
+"COPY_TITLE" = "Copy title";
+"COPY_TITLE_DESC" = "Copy the video title";
+"COPY_DESCRIPTION" = "Copy description";
+"COPY_DESCRIPTION_DESC" = "Copy the video description";
+"COPY_URL" = "Copy video link";
+"COPY_URL_TIMESTAMP" = "Copy video link with timestamp";
+"URL_COPIED" = "Copied video link";
+"URL_TIMESTAMP_COPIED" = "Copied video link with timestamp";
+"ERROR_VIDEOID" = "Cannot get the video ID";
+"ERROR_ADS" = "Cannot copy video link while playing ads";
+"COPIED_AUTHOR" = "Copied video author";
+"COPIED_TITLE" = "Copied video title";
+"COPIED_DESCRIPTION" = "Copied video description";
+"SAVE_THUMBNAIL" = "Save thumbnail";
+"SAVE_THUMBNAIL_DESC" = "Save to Photos";
+"DOWNLOAD_CAPTIONS" = "Download captions";
+"DOWNLOAD_CAPTIONS_DESC" = "Save subtitles as .VTT file";
+"DOWNLOAD_AUDIO" = "Download audio";
+"DOWNLOAD_AUDIO_DESC" = "Download audio as .m4a file";
+"DOWNLOAD_VIDEO" = "Download video";
+"DOWNLOAD_VIDEO_DESC" = "Choose video quality";
+"OPEN_VID_BEFORE" = "Open a video before using the download manager";
+"NO_CAPTIONS_URL" = "No valid caption URLs found";
+"NO_AUDIO_URL" = "No audio URL found";
+"NO_STREAM_URL" = "No stream URL found";
+"CAPTIONS_FAILED" = "Failed to download captions";
+"NO_CAPTIONS" = "No captions available for this video";
+"NO_AUDIO_STREAM_FOUND" = "No audio streams found";
+"NO_VID_AUDIO_STREAM_FOUND" = "No video/audio streams found";
+"COPIED_VID_INFO" = "Copied all video informations";
+"SAVED_TO_PHOTOS" = "Saved to Photos";
+"SAVE_FAILED" = "Save failed";
+"PHOTO_ACCESS_DENINED" = "Photos access denied";
+"THUMBNAIL_FAILED" = "Thumbnail failed";
+"DOWNLOADING_THUMBNAIL" = "Downloading thumbnail...";
+"DOWNLOADING_AUDIO" = "Downloading audio...";
+"DOWNLOADING_VIDEO" = "Downloading video...";
+"DOWNLOADING_CAPTIONS" = "Downloading captions...";
+"NO_THUMBNAIL_FOUND" = "No thumbnail found";
+"DOWNLOAD_FAILED" = "Download failed";
+"COPY_THUMBNAIL" = "Copy thumbnail";
+"COPY_THUMBNAIL_DESC" = "Copy to clipboard";
+"SHOW_THUMBNAIL" = "Show thumbnail";
+"SHOW_THUMBNAIL_DESC" = "Show in a dialog";
+"THUMBNAIL_OPTIONS" = "Download thumbnail";
+"THUMBNAIL_OPTIONS_DESC" = "Show more options";
+"COPIED_TO_CLIPBOARD" = "Copied to clipboard";
+"DOWNLOAD_COMPLETED" = "Download completed";
+"CANNOT_SAVE_TO_PHOTOS" = "Cannot save to Photos";
+"FINA_VIDEO" = "Finalizing video...";
+"MERGING_VID_FALLBACK" = "Merging video with fallback...";
+"MERGING_VID" = "Merging video...";
+"CONVERTING" = "Converting to";
+"ALREADY_DOWNLOADING" = "Already downloading";
+"DOWNLOAD_CANCELLED" = "Download cancelled";
+"CANCEL" = "Cancel";
+"TRIMMING_AUDIO" = "Trimming audio...";
+"COPY" = "Copy";
"APPEARANCE" = "Appearance";
"OLED_THEME" = "OLED dark mode";
@@ -46,6 +122,8 @@
"OLED_KEYBOARD_DESC" = "Make the keyboard uses a pure black (OLED) color for dark theme.";
"NAVBAR" = "Navigation bar";
+"STICKY_NAVBAR" = "Sticky Navigation bar";
+"STICKY_NAVBAR_DESC" = "Pins the Navigation bar so that it remains visible when scrolling down.";
"HIDE_YT_LOGO" = "Hide YouTube logo";
"HIDE_YT_LOGO_DESC" = "Hide YouTube logo in the navigation bar.";
"PREMIUM_LOGO" = "Use YouTube Premium logo";
@@ -62,34 +140,78 @@
"FEED" = "Feed";
"HIDE_SUBBAR" = "Hide subbar";
"HIDE_SUBBAR_DESC" = "Hide subbar at the top of the feed.";
+"HIDE_HORI_SHELF" = "Remove horizontal feeds";
+"HIDE_HORI_SHELF_DESC" = "Removes all horizontal feeds (News, Continue Watching, Posts collection, etc.) from the feed, except Playables.";
"HIDE_MUSIC_PLAYLISTS" = "Hide music playlist generator";
"HIDE_MUSIC_PLAYLISTS_DESC" = "Hide music playlist generator (Select music tunes in three) in the feed.";
"HIDE_FEED_POST" = "Hide posts";
"HIDE_FEED_POST_DESC" = "Hide posts in feed.";
+"HIDE_SURVEYS" = "Hide surveys";
+"HIDE_SURVEYS_DESC" = "Hide video surveys that YouTube shows sometimes.";
"HIDE_SHORTS_SHELF" = "Hide Shorts shelf";
"HIDE_SHORTS_SHELF_DESC" = "Hide Shorts shelf in the feed, including in the Subscriptions tab.";
+"KEEP_SHORTS_SUBSCRIPT" = "Keep Shorts in Subscriptions";
+"KEEP_SHORTS_SUBSCRIPT_DESC" = "Prevents removing Shorts from Subscriptions tab.";
"HIDE_SEARCH_HISTORY" = "Hide search history";
"HIDE_SEARCH_HISTORY_DESC" = "Hide previous search history and suggestions when using the search bar.\nNOTE: Your search history will still be accessible in your other YouTube clients.";
-"HIDE_SUB_BUTTON" = "Hide Subscribe button";
-"HIDE_SUB_BUTTON_DESC" = "Hides the subscribe button in channels and video descriptions.";
-"HIDE_SHOP_BUTTON" = "Hide Shopping button";
-"HIDE_SHOP_BUTTON_DESC" = "Hides the shopping/store button in channels descriptions.";
-"HIDE_MEMBER_BUTTON" = "Hide Membership button";
-"HIDE_MEMBER_BUTTON_DESC" = "Hides the 'Join' button in channels and video descriptions.";
+"HIDE_PLAYABLES" = "Remove playables";
+"HIDE_PLAYABLES_DESC" = "Removes playables (mini-games) from the feed.";
"PLAYER" = "Player";
+"PLAYBACK_SPEED" = "Playback speed";
+"CONTROL_CENTER" = "Control Center";
+"PLAYER_ACTIONS" = "Player actions";
+"GESTURE_HEADER" = "Gestures";
+"QUALITY_WIFI" = "Playback quality on WiFi";
+"QUALITY_WIFI_DESC" = "Automatically applies the selected quality when streaming over Wi-Fi.";
+"QUALITY_CELLULAR" = "Playback quality on Cellular";
+"QUALITY_CELLULAR_DESC" = "Automatically applies the selected quality when streaming over mobile data.";
+"QUALITY_LOW_POWER" = "Playback quality on Power saver mode";
+"QUALITY_LOW_POWER_DESC" = "Automatically applies the selected quality when when Low power mode is enabled on your device.";
+"DEFAULT" = "Default";
+"BEST" = "Best";
+"AUDIO_TRACK" = "Preferred audio track";
+"ORIGINAL" = "Original";
+"SELECT_MANUALLY" = "Select manually";
+"AUDIO_TRACK_SELECT" = "Selected audio track";
+"AUDIO_TRACK_SELECT_DESC" = "Specifies the preferred audio track language to use for playback, if available.";
+"NO_AUTO_DUBBED" = "Uses original audio track";
+"NO_AUTO_DUBBED_DESC" = "Uses original audio track if the preferred language track is auto-dubbed or not found.";
+"CAPTION_TRACK" = "Preferred captions";
+"CAPTION_TRACK_SELECT" = "Selected captions";
+"CAPTION_TRACK_SELECT_DESC" = "Specifies the preferred captions language to display, if available.";
+"DISABLES_CAPTION_TRACK" = "Automatically disable captions";
+"DISABLES_CAPTION_TRACK_DESC" = "Automatically disable captions if the preferred language is auto-generated or not found.";
+"DEFAULT_SPEED" = "Default playback rate";
+"DEFAULT_SPEED_DESC" = "Overrides the default playback speed.";
+"SPEED_BUTTON" = "Speed controls button";
+"SPEED_BUTTON_DESC" = "Displays a speed controls button on overlay.";
+"MUTE_BUTTON" = "Mute button";
+"MUTE_BUTTON_DESC" = "Displays a button to mute or unmute the volume on the overlay.";
+"SHARE_BUTTON" = "Share button";
+"SHARE_BUTTON_DESC" = "Displays a button to share the video link on the overlay.";
+"LOOP_BUTTON" = "Loop button";
+"LOOP_BUTTON_DESC" = "Displays a button to loop the video on the overlay.";
+"CAPTION_BUTTON" = "Captions button";
+"CAPTION_BUTTON_DESC" = "Displays a button to copy the video captions on the overlay.";
+"HOLD_TO_SPEED" = "Hold to speed";
+"HOLD_TO_SPEED_DESC" = "Uses custom hold to speed playback rate.";
"HIDE_AUTOPLAY" = "Hide autoplay switch";
"HIDE_AUTOPLAY_DESC" = "Hide autoplay switch in the video overlay.";
"HIDE_CAPTIONS_BUTTON" = "Hide captions button";
"HIDE_CAPTIONS_BUTTON_DESC" = "Hide captions button in the video overlay.";
"HIDE_CAST_BUTTON_PLAYER" = "Hide cast button";
"HIDE_CAST_BUTTON_PLAYER_DESC" = "Hide cast button in the video overlay.";
-"HIDE_PREV_BUTTON" = "Hide previous/back button";
-"HIDE_PREV_BUTTON_DESC" = "Hide previous/back button in the video overlay.";
-"HIDE_NEXT_BUTTON" = "Hide next/skip button";
-"HIDE_NEXT_BUTTON_DESC" = "Hide next/skip button in the video overlay.";
+"HIDE_NEXT_AND_PREV_BUTTON" = "Hide previous and next buttons";
+"HIDE_NEXT_AND_PREV_BUTTON_DESC" = "Hide previous and next buttons in the video overlay.";
"REPLACE_PREVNEXT_BUTTONS" = "Replace Previous/Next buttons";
"REPLACE_PREVNEXT_BUTTONS_DESC" = "Replaces the Previous and Next video buttons in the player with Rewind and Fast Forward buttons.";
+"SKIP_BACKWARD" = "Skip backward";
+"SKIP_BACKWARD_DESC" = "Replaces the previous track button in Control Center with a skip backward button by a set number of seconds.";
+"SKIP_FORWARD" = "Skip forward";
+"SKIP_FORWARD_DESC" = "Replaces the next track button in Control Center with a skip forward button by a set number of seconds.";
+"REWIND_SECONDS" = "Rewind seconds";
+"FORWARD_SECONDS" = "Fast-forward seconds";
"REMOVE_DARK_OVERLAY" = "Remove dark overlay";
"REMOVE_DARK_OVERLAY_DESC" = "Remove dark overlay from the video overlay.";
"HIDE_END_SCREEN" = "Hide endscreen cards";
@@ -102,6 +224,12 @@
"HIDE_PAID_OVERLAY_DESC" = "Hide 'Includes paid promotion' in the video overlay.";
"HIDE_WATERMARK" = "Hide watermark";
"HIDE_WATERMARK_DESC" = "Hide channel watermark at the bottom of the video overlay.";
+"PAUSE_ON_OVERLAY" = "Pause on overlay";
+"PAUSE_ON_OVERLAY_DESC" = "Sets playback on pause if overlay appears.";
+"DISABLES_SNAP_TO_CHAPTER" = "Disable snap to chapters";
+"DISABLES_SNAP_TO_CHAPTER_DESC" = "Prevents the scrubbar stick to the chapters.";
+"DISABLES_ENGAGE_PANEL" = "Disables engagement panel in fullscreen";
+"DISABLES_ENGAGE_PANEL_DESC" = "Prevent engagement panel showing in fullscreen.";
"GESTURES" = "Enable gestures controls";
"GESTURES_DESC" = "Swipe up or down on the left/right edges of the video screen to adjust brightness, volume, or playback speed.";
"GESTURE_AREA" = "Gesture activation area";
@@ -136,6 +264,8 @@
"DISABLES_SHOW_REMAINING_DESC" = "Disable the feature that shows how much time is left in a video when tapping the timestamp.";
"ALWAYS_SHOW_REMAINING" = "Always show remaining time";
"ALWAYS_SHOW_REMAINING_DESC" = "Ensure the remaining time is always displayed by default in the player overlay.";
+"COPY_TIMESTAMP_ON_PAUSE" = "Copy timestamped links";
+"COPY_TIMESTAMP_ON_PAUSE_DESC" = "Allows to copy timestamped link to the clipboard by pressing pause button.";
"SHOW_REMAINING_EXTRA" = "Show extra remaining time";
"SHOW_REMAINING_EXTRA_DESC" = "Adds the estimated finish time to the label in the video overlay.";
"HIDE_FULLSCREEN_ACTIONS" = "Hide fullscreen actions";
@@ -148,93 +278,95 @@
"HIDE_CONTENT_WARNING_DESC" = "Automatically skip the sensitive content warning alert in some vidoes.";
"AUTO_FULLSCREEN" = "Auto fullscreen";
"AUTO_FULLSCREEN_DESC" = "Automatically enter fullscreen mode when starting video playback.";
-"AUTO_DISABLES_CAPTION" = "Auto disable captions";
-"AUTO_DISABLES_CAPTION_DESC" = "Automatically turns off closed captions (CC) whenever a new video starts playing.";
"PORTRAIT_FULLSCREEN" = "Portrait fullscren mode";
"PORTRAIT_FULLSCREEN_DESC" = "Enables portrait fullscreen mode support.";
"OLD_QUALITY_PICKER" = "Use old quality picker";
"OLD_QUALITY_PICKER_DESC" = "Reverts the video quality selection menu to the classic version (lists all resolutions immediately).";
"EXTRA_SPEED" = "Extra speed options";
"EXTRA_SPEED_DESC" = "Adds more video playback speed options up to 10x.";
-"DISABLE_HINTS" = "Disable hints";
-"DISABLE_HINTS_DESC" = "Disable the tutorial hints and interactive pop-ups.";
"FORCE_MINIPLAYER" = "Force miniplayer";
"FORCE_MINIPLAYER_DESC" = "Allows all videos, including those made for kids, to play in the miniplayer when dismissing the player.";
"FORCE_SEEKBAR" = "Always show progress bar";
"FORCE_SEEKBAR_DESC" = "Makes the progress bar (seekbar) remains visible at all times while in fullscreen mode.";
-"HIDE_LIKE_BUTTON" = "Hide Like button";
-"HIDE_LIKE_BUTTON_DESC" = "Hide Like button in the action bar under the video overlay.";
-"HIDE_DISLIKE_BUTTON" = "Hide Dislike button";
-"HIDE_DISLIKE_BUTTON_DESC" = "Hide Dislike button in the action bar under the video overlay.";
-"HIDE_SHARE_BUTTON" = "Hide Share button";
-"HIDE_SHARE_BUTTON_DESC" = "Hide Share button in the action bar under the video overlay.";
-"HIDE_DOWNLOAD_BUTTON" = "Hide Download button";
-"HIDE_DOWNLOAD_BUTTON_DESC" = "Hide Download button in the action bar under the video overlay.";
-"HIDE_CLIP_BUTTON" = "Hide Clip button";
-"HIDE_CLIP_BUTTON_DESC" = "Hide Clip button in the action bar under the video overlay.";
-"HIDE_REMIX_BUTTON" = "Hide Remix button";
-"HIDE_REMIX_BUTTON_DESC" = "Hide Remix button in the action bar under the video overlay.";
-"HIDE_SAVE_BUTTON" = "Hide Save button";
-"HIDE_SAVE_BUTTON_DESC" = "Hide Save button in the action bar under the video overlay.";
+"TAP_TO_SEEK" = "Tap to seek";
+"TAP_TO_SEEK_DESC" = "Quickly jump to any point by tapping the progress bar.";
+"DISABLES_ZOOM" = "Disable free zoom gesture";
+"DISABLES_ZOOM_DESC" = "Disables new free zoom gestures.";
+"USES_24_HOURS_TIME" = "24-hour format";
+"USES_24_HOURS_TIME_DESC" = "Shows end time in 24-hour format.";
+"PAUSE_TWO_FINGERS" = "Pause using two fingers";
+"PAUSE_TWO_FINGERS_DESC" = "Pause/Resume playback by tapping on the screen using two fingers.";
+"HIDE_COMMENTS_SECTION" = "Hide comments section";
+"HIDE_COMMENTS_SECTION_DESC" = "Completely removes the entire comments section.";
+"HIDE_COMMENTS_PREVIEW" = "Hide preview comments";
+"HIDE_COMMENTS_PREVIEW_DESC" = "Removes the preview box for the top comment while keeping the full section expandable.";
+"USE_ANOTHER_MINIPLAYER" = "Uses another miniplayer style";
+"USE_ANOTHER_MINIPLAYER_DESC" = "Uses another miniplayer style redesigned from older YT versions.";
"SHORTS" = "Shorts";
-"HIDE_SHORTS_LIKE_BUTTON" = "Hide Like button";
-"HIDE_SHORTS_LIKE_BUTTON_DESC" = "Hide the Like button in the Shorts player.";
-"HIDE_SHORTS_DISLIKE_BUTTON" = "Hide Dislike button";
-"HIDE_SHORTS_DISLIKE_BUTTON_DESC" = "Hide the Dislike button in the Shorts player.";
-"HIDE_SHORTS_COMMENT_BUTTON" = "Hide Comment button";
-"HIDE_SHORTS_COMMENT_BUTTON_DESC" = "Hide the Comment button in the Shorts player.";
-"HIDE_SHORTS_SHARE_BUTTON" = "Hide Share button";
-"HIDE_SHORTS_SHARE_BUTTON_DESC" = "Hide the Share button in the Shorts player.";
-"HIDE_SHORTS_REMIX_BUTTON" = "Hide Remix button";
-"HIDE_SHORTS_REMIX_BUTTON_DESC" = "Hide the Remix button in the Shorts player.";
-"HIDE_METADATA_BUTTON" = "Hide sound metadata button";
-"HIDE_METADATA_BUTTON_DESC" = "Hide the button that shows the sound source or music metadata in the Shorts player.";
+"SHORTS_ONLY" = "Shorts only mode";
+"SHORTS_ONLY_DESC" = "Limits YouTube functionality to viewing Shorts only.";
+"SHORTS_ACTION" = "Shorts action after the video ends";
+"LOOP" = "Loop video";
+"SKIP_TO_NEXT_SHORTS" = "Skip to next video";
+"PAUSE_SHORTS" = "Stop video";
+"REMOVE_LIVE_SHORTS" = "Remove live videos";
+"REMOVE_LIVE_SHORTS_DESC" = "Removes live videos from the feed.";
"HIDE_SHORTS_PRODUCT" = "Hide products";
"HIDE_SHORTS_PRODUCT_DESC" = "Hide tagged products and shopping links in the Shorts overlay.";
"HIDE_SHORTS_RECBAR" = "Hide recommendation bar";
"HIDE_SHORTS_RECBAR_DESC" = "Hide the suggested action bar in the Shorts player (e.g. Try this sound, Shopping, Preview comment).";
-"HIDE_SHORTS_COMMIT" = "Hide commissions bar";
-"HIDE_SHORTS_COMMIT_DESC" = "Hide the bar that shows the video got commissions in the Shorts player.";
-"HIDE_SHORTS_SUBSCRIPT_BUTTON" = "Hide subscriptions button";
-"HIDE_SHORTS_SUBSCRIPT_BUTTON_DESC" = "Hide the Subscriptions button at the top of the Shorts player.";
-"HIDE_SHORTS_LIVE_BUTTON" = "Hide live button";
-"HIDE_SHORTS_LIVE_BUTTON_DESC" = "Hide the Live button at the top of the Shorts player.";
-"HIDE_SHORTS_LENS_BUTTON" = "Hide lens button";
-"HIDE_SHORTS_LENS_BUTTON_DESC" = "Hide the Lens button at the top of the Shorts player.";
-"HIDE_SHORTS_TRENDS_BUTTON" = "Hide trends button";
-"HIDE_SHORTS_TRENDS_BUTTON_DESC" = "Hide the Trends button at the top of the Shorts player.";
-"HIDE_SHORTS_TO_VIDEO" = "Hide link to full video";
-"HIDE_SHORTS_TO_VIDEO_DESC" = "Hide the link that redirects in a Short to a full video in the Shorts player.";
"ENABLES_SHORTS_QUALITY" = "Enable Shorts quality picker";
"ENABLES_SHORTS_QUALITY_DESC" = "Force enable the video quality selection menu in Shorts.";
"SHOW_SHORTS_SEEKBAR" = "Always show seekbar";
"SHOW_SHORTS_SEEKBAR_DESC" = "Ensure the progress/seek bar is always visible at the bottom of the Shorts player.";
+"REMOVE_POSTS_SHORTS" = "Remove posts";
+"REMOVE_POSTS_SHORTS_DESC" = "Remove posts from the feed.";
+"SHORTS_FULLSCREEN" = "Pinch to Fullscreen";
+"SHORTS_FULLSCREEN_DESC" = "Manages visibility of the overlay with pinch in and pinch out gestures, displaying Shorts in fullscreen mode.";
"TABBAR" = "Tab bar";
"DEFAULT_TAB" = "Default startup tab";
-"DEFAULT_TAB_DESC" = "You can select the tab you want to open when opening the app.";
-"HOME_NAME" = "Home";
-"SUBSCRIPT_NAME" = "Subscriptions";
-"LIB_NAME" = "Library (You)";
+"FORSTED_TAB_BAR" = "Translucent tab bar";
+"ENABLED" = "Enabled";
"HIDE_TAB_INDI" = "Hide tab indicators";
"HIDE_TAB_INDI_DESC" = "Hide all tab indicators in the tab bar.";
"HIDE_TAB_LABELS" = "Hide tab labels";
"HIDE_TAB_LABELS_DESC" = "Hide all tab labels in the tab bar.";
-"HIDE_HOME_TAB" = "Hide Home tab";
-"HIDE_HOME_TAB_DESC" = "Hide Home tab in the tab bar.";
-"HIDE_SHORTS_TAB" = "Hide Shorts tab";
-"HIDE_SHORTS_TAB_DESC" = "Hide Shorts tab in the tab bar.";
-"HIDE_CREATE_BUTTON" = "Hide Create button";
-"HIDE_CREATE_BUTTON_DESC" = "Hide Create button in the tab bar.";
-"HIDE_SUBSCRIPT_TAB" = "Hide Subscriptions tab";
-"HIDE_SUBSCRIPT_TAB_DESC" = "Hide Subscriptions tab in the tab bar.";
+"HOME_TAB" = "Home";
+"SHORTS_TAB" = "Shorts";
+"CREATE_TAB" = "Create";
+"SUBSCRIPTIONS_TAB" = "Subscriptions";
+"LIBRARY_TAB" = "Library (You)";
+"HISTORY_TAB" = "History";
+"GAMING_TAB" = "Gaming";
+"SPORTS_TAB" = "Sports";
+"NOTI_TAB" = "Notifications";
+"NEWS_TAB" = "News";
+"MUSIC_TAB" = "Music";
+"WATCH_LATER_TAB" = "Watch later";
+"PLAYLIST_TAB" = "Playlists";
+"LIKE_TAB" = "Liked videos";
+"LIVE_TAB" = "Live";
+"POST_TAB" = "Posts";
+"VIDEO_TAB" = "Your videos";
+"MOVIE_TAB" = "Movies";
+"COURSE_TAB" = "Courses";
+"MINIGAME_TAB" = "Playables";
+"MANAGE_TABS" = "Manage tabs";
+"MANAGE_TABS_DESC" = "Reorder and toggle tabs";
+"TAB_REORDER_HINT" = "Drag to reorder. Toggle to show/hide.\nTab settings are also accessible by long-pressing the first tab in the tab bar.";
+"TAB_LIMIT" = "Tab limit";
+"TAB_LIMIT_DESC" = "Only 6 tabs are allowed.";
+"ZERO_TAB_DESC" = "At least 1 tab has to be enabled.";
"MISCELLANEOUS" = "Miscellaneous";
"BACKGROUND_PLAYBACK" = "Background playback";
"BACKGROUND_PLAYBACK_DESC" = "Enables background playback for videos.";
"DISABLES_SHORTS_PIP" = "Disables Shorts PiP";
"DISABLES_SHORTS_PIP_DESC" = "Try to disables Shorts PiP, this might not 100% disables it.";
+"DISABLE_HINTS" = "Disable hints";
+"DISABLE_HINTS_DESC" = "Disable the tutorial hints and interactive pop-ups.";
"BLOCK_UPGRADE_DIALOGS" = "Block upgrade dialogs";
"BLOCK_UPGRADE_DIALOGS_DESC" = "Block the YouTube dialogs that ask you to upgrade the app.";
"ARE_YOU_THERE_DIALOG" = "Hide 'Are you there?' dialog";
@@ -247,7 +379,109 @@
"DISABLES_SNACK_BAR_DESC" = "Disables snack bar when pressing some buttons or doing something in the app.";
"HIDE_STARTUP_ANIMATIONS" = "Hide YouTube's startup animations";
"HIDE_STARTUP_ANIMATIONS_DESC" = "Hide YouTube's startup animations when opening the app.";
-"HIDE_PLAY_IN_NEXT_QUEUE" = "Hide 'Play next in queue'";
-"HIDE_PLAY_IN_NEXT_QUEUE_DESC" = "Hide the 'Play next in queue' option in the flyout menu.";
"HIDE_LIKE_DISLIKE_VOTES" = "Hide like/dislike votes";
-"HIDE_LIKE_DISLIKE_VOTES_DESC" = "Hide messages that pop up when you like/dislike videos.";
\ No newline at end of file
+"HIDE_LIKE_DISLIKE_VOTES_DESC" = "Hide messages that pop up when you like/dislike videos.";
+"FLYOUT_MENU" = "Flyout menu";
+"REMOVE_PLAY_IN_NEXT_QUEUE_OPTION" = "Removes 'Play next in queue' option";
+"REMOVE_PLAY_IN_NEXT_QUEUE_OPTION_DESC" = "Removes 'Play next in queue' option from the flyout menu.";
+"REMOVE_DOWNLOAD_OPTION" = "Removes 'Download video' option";
+"REMOVE_DOWNLOAD_OPTION_DESC" = "Removes 'Download video' option from the flyout menu.";
+"REMOVE_WATCH_LATER_OPTION" = "Removes 'Save to Watch later' option";
+"REMOVE_WATCH_LATER_OPTION_DESC" = "Removes 'Save to Watch Later' option from the flyout menu.";
+"REMOVE_SAVE_OPTION" = "Removes 'Save to playlist' option";
+"REMOVE_SAVE_OPTION_DESC" = "Removes 'Save to playlist' option from the flyout menu.";
+"REMOVE_REMOVE_FROM_PLAYLIST_OPTION" = "Removes 'Remove from playlist' option";
+"REMOVE_REMOVE_FROM_PLAYLIST_OPTION_DESC" = "Removes 'Remove from playlist' option from the flyout menu.";
+"REMOVE_SHARE_OPTION" = "Removes 'Share' option";
+"REMOVE_SHARE_OPTION_DESC" = "Removes 'Share' option from the flyout menu.";
+"REMOVE_NOT_INTERESTED_OPTION" = "Removes 'Not interested' option";
+"REMOVE_NOT_INTERESTED_OPTION_DESC" = "Removes 'Not interested' option from the flyout menu.";
+"REMOVE_DONT_RECOMMEND_OPTION" = "Removes 'Don't recommend channel' option";
+"REMOVE_DONT_RECOMMEND_OPTION_DESC" = "Removes 'Don't recommend channel' option from the flyout menu.";
+"REMOVE_INFO_OPTION" = "Removes 'Why are you seeing this?' option";
+"REMOVE_INFO_OPTION_DESC" = "Removes 'Why are you seeing this?' option from the flyout menu.";
+"REMOVE_FILTER_OPTION" = "Removes 'Search filters' option";
+"REMOVE_FILTER_OPTION_DESC" = "Removes 'Search filters' option from the flyout menu.";
+"REMOVE_REPORT_OPTION" = "Removes 'Report' option";
+"REMOVE_REPORT_OPTION_DESC" = "Removes 'Report' option from the flyout menu.";
+"REMOVE_YOUTUBE_MUSIC_OPTION" = "Removes 'Listen with YouTube Music' option";
+"REMOVE_YOUTUBE_MUSIC_OPTION_DESC" = "Removes 'Listen with YouTube Music' option from the flyout menu.";
+"REMOVE_FEED_BACK_OPTION" = "Removes 'Send feedback' option";
+"REMOVE_FEED_BACK_OPTION_DESC" = "Removes 'Send feedback' option from the flyout menu.";
+"REMOVE_CAST_OPTION" = "Removes 'Watch on TV' option";
+"REMOVE_CAST_OPTION_DESC" = "Removes 'Watch on TV' option from the flyout menu.";
+"REMOVE_SHUFFLE_OPTION" = "Removes 'Shuffle' option";
+"REMOVE_SHUFFLE_OPTION_DESC" = "Removes 'Shuffle' option from the flyout menu.";
+"REMOVE_UN_SUB_OPTION" = "Removes 'Unsubscribe' option";
+"REMOVE_UN_SUB_OPTION_DESC" = "Removes 'Unsubscribe' option from the flyout menu.";
+"REMOVE_HIDE_FROM_PLAYLIST_OPTION" = "Removes 'Hide video from playlist' option";
+"REMOVE_HIDE_FROM_PLAYLIST_OPTION_DESC" = "Removes 'Hide video from playlist' option from the flyout menu.";
+"REMOVE_HELP_OPTION" = "Removes 'Help & feedback' option";
+"REMOVE_HELP_OPTION_DESC" = "Removes 'Help & feedback' option from the flyout menu.";
+"REMOVE_NOTIFY_OPTION" = "Removes 'Notify me' option";
+"REMOVE_NOTIFY_OPTION_DESC" = "Removes 'Notify me' option from the flyout menu.";
+"REMOVE_CLEARSCREEN_OPTION" = "Removes 'Clear Screen' option";
+"REMOVE_CLEARSCREEN_OPTION_DESC" = "Removes 'Clear Screen' option from the flyout menu.";
+"HIDE_COMMU_GUIDE" = "Hide community guidelines";
+"HIDE_COMMU_GUIDE_DESC" = "Hide community guidelines header.";
+"DISABLES_RTL" = "Disable RTL formatting";
+"DISABLES_RTL_DESC" = "Forcefully displays text in left-to-right (LTR) format for languages that are initially displayed in right-to-left (RTL).";
+"DEVICE_UI" = "Interface style";
+
+"SB_DESCRIPTION" = "Skip sponsored segments automatically using community-submitted data.";
+"SB_ENABLE" = "Enable SponsorBlock";
+"SB_ENABLE_DESC" = "Activate the SponsorBlock extension to skip sponsored segments.";
+"SB_SHOW_BUTTON" = "Show overlay button";
+"SB_SHOW_BUTTON_DESC" = "Display a SponsorBlock toggle button in the player overlay.";
+"SB_SHOW_NOTIFICATIONS" = "Show notifications";
+"SB_SHOW_NOTIFICATIONS_DESC" = "Display notifications when segments are skipped. Notifications for manual-skip categories are always shown.";
+"SB_HAPTIC_FEEDBACK" = "Haptic feedback";
+"SB_HAPTIC_FEEDBACK_DESC" = "Provide haptic feedback when a segment is skipped.";
+"SB_SEGMENTS_IN_PLAYER" = "Segments in player";
+"SB_SEGMENTS_IN_PLAYER_DESC" = "Display colored segments on the progress bar of the main video player.";
+"SB_SEGMENTS_IN_FEED" = "Segments in feed";
+"SB_SEGMENTS_IN_FEED_DESC" = "Display colored segments on the progress bar of players in the feed.";
+"SB_SEGMENTS_IN_MINIPLAYER" = "Segments in mini-player";
+"SB_SEGMENTS_IN_MINIPLAYER_DESC" = "Display colored segments on the progress bar of the mini-player.";
+"SB_SHOW_DURATION" = "Show duration without segments";
+"SB_SHOW_DURATION_DESC" = "Display the total video duration excluding skippable segments.";
+"SB_CATEGORIES_HEADER" = "Segment categories";
+"SB_SEGMENT_COLOR" = "Segment color";
+
+"SB_ACTION_DISABLE" = "Disable";
+"SB_ACTION_AUTO_SKIP" = "Auto-skip";
+"SB_ACTION_ASK" = "Ask before skipping";
+"SB_ACTION_DISPLAY" = "Show on seek bar only";
+"SB_ACTION_SKIP_TO" = "Skip to segment";
+
+"SB_CAT_sponsor" = "Sponsor";
+"SB_CAT_sponsor_DESC" = "Paid promotion for a product or service not directly related to the creator.";
+"SB_CAT_intro" = "Intro";
+"SB_CAT_intro_DESC" = "Intermission or intro animation typically seen at the start of a video.";
+"SB_CAT_outro" = "Endcards";
+"SB_CAT_outro_DESC" = "Endcards, credits, or outros at the end of the video.";
+"SB_CAT_interaction" = "Interaction";
+"SB_CAT_interaction_DESC" = "Reminders to like, subscribe, or follow on social media.";
+"SB_CAT_selfpromo" = "Self-promotion";
+"SB_CAT_selfpromo_DESC" = "Unpaid promotion of the creator's own products, merchandise, or other content.";
+"SB_CAT_music_offtopic" = "Non-music";
+"SB_CAT_music_offtopic_DESC" = "Non-music sections in music videos (e.g. talking, introductions).";
+"SB_CAT_preview" = "Preview";
+"SB_CAT_preview_DESC" = "Recap or preview of content from another video or previous episode.";
+"SB_CAT_hook" = "Hook/Greetings";
+"SB_CAT_hook_DESC" = "Narrated trailers for the upcoming video, greetings and goodbyes.";
+"SB_CAT_poi_highlight" = "Highlight";
+"SB_CAT_poi_highlight_DESC" = "The key moment or highlight point of the video. Use 'Skip to' to jump directly to it.";
+"SB_CAT_filler" = "Filler";
+"SB_CAT_filler_DESC" = "Tangential content, jokes, or filler that is not directly related to the main topic.";
+
+"SB_SKIPPED" = "%@ segment has been skipped";
+"SB_DETECTED" = "%@ segment detected.\nWould you like to skip the segment?";
+"SB_SKIP_NOW" = "Skip";
+"SB_UNSKIP" = "Unskip";
+"SB_JUMPED_TO_HIGHLIGHT" = "Jumped to highlight";
+"SB_JUMP_TO_HIGHLIGHT" = "Highlight available. Jump to the point?";
+"SB_SEGMENT_COLOR_SUFFIX" = "Segment color";
+"SB_SECTION_MAIN" = "Main";
+"SB_SKIP_ALERT_DURATION" = "Skip alert duration";
+"SB_UNSKIP_ALERT_DURATION" = "Unskip alert duration";
\ No newline at end of file
diff --git a/layout/Library/Application Support/YouMod.bundle/no.lproj/Localizable.strings b/layout/Library/Application Support/YouMod.bundle/no.lproj/Localizable.strings
index bccaf550..2c1a8a0f 100644
--- a/layout/Library/Application Support/YouMod.bundle/no.lproj/Localizable.strings
+++ b/layout/Library/Application Support/YouMod.bundle/no.lproj/Localizable.strings
@@ -1,11 +1,18 @@
-"NOTE" = "NOTE: Some hiding features may leave a blank box.";
"SETTINGS" = "Settings";
+"INTERFACE" = "Interface";
+
+"LOOP_DISABLED" = "Loop disabled";
+"LOOP_ENABLED" = "Loop enabled";
"NEW_ISSUES" = "Report issues or request new features";
"NEW_ISSUES_DESC" = "Found bugs or want to request new features? Tap here.";
"SOURCE_CODES" = "Source codes";
"SOURCE_CODES_DESC" = "Take a look at the codes!";
+"EXTRA" = "Extra";
+"FIX_PLAYBACK_ISSUES" = "Fix playback issues (Temporary)";
+"FIX_PLAYBACK_ISSUES_DESC" = "Try to fix video playback issues for some users. Turning this on will make the player automatically reloads itself everytime it errors or fails to play the video.";
+
"PERFER_HEADER" = "YouMod perferences";
"PERFER" = "Perferences";
"IMPORT" = "Import settings";
@@ -24,6 +31,7 @@
"ERROR_NO_KEYS_IMPORT" = "No available keys to import";
"ERROR_NO_KEYS_EXPORT" = "No available keys to export";
"OVERRIDE" = "This will override the current settings. Do you want to continue?";
+"DISABLED" = "Disabled";
"CACHE" = "Cache management";
"CLEARCACHE" = "Clear cache";
@@ -32,12 +40,80 @@
"AUTO_CLEARCACHE_DESC" = "Automatically clears the app cache on startup.";
"DOWNLOADING" = "Downloading";
-"DOWNLOAD_MANAGER" = "Download Manager";
-"DOWNLOAD_MANAGER_DESC" = "Replaces YouTube's video downloader with YouMod one. With ablity to downloading videos, audios, captions, and thumbnails";
+"DOWNLOAD_MANAGER" = "Download manager";
+"DOWNLOAD_MANAGER_DESC" = "Replaces YouTube's video downloader with YouMod one. With ablity to downloading videos, audios, captions, and thumbnails.";
"DOWNLOAD_SAVE_PHOTOS" = "Automatically save to Photos";
"DOWNLOAD_SAVE_PHOTOS_DESC" = "Automatically saves downloaded videos and images directly to your Photos app.";
-"DOWNLOAD_DRC_AUDIO" = "Prefer DRC Audio";
-"DOWNLOAD_DRC_AUDIO_DESC" = "Prioritizes downloading audio tracks with Dynamic Range Compression for more consistent volume levels.";
+"ADD_SHORTS_DOWNLOAD" = "Adds a download button to Shorts";
+"ADD_SHORTS_DOWNLOAD_DESC" = "Adds a download button to Shorts video overlay.";
+"USE_ORIG_AUDIO" = "Automatically uses original audio track";
+"USE_ORIG_AUDIO_DESC" = "Automatically uses original audio track instead of showing a dialog.";
+"DOWNLOAD_SHORTS" = "Download Shorts";
+"DOWNLOAD_SHORTS_DESC" = "Choose Shorts quality";
+"COPY_VID_INFO" = "Copy video informations";
+"COPY_VID_INFO_DESC" = "Choose what to copy";
+"COPY_ALL_VID_INFO" = "Copy all";
+"COPY_ALL_VID_INFO_DESC" = "Copy all video informations";
+"COPY_AUTHOR" = "Copy author";
+"COPY_AUTHOR_DESC" = "Copy the video author";
+"COPY_TITLE" = "Copy title";
+"COPY_TITLE_DESC" = "Copy the video title";
+"COPY_DESCRIPTION" = "Copy description";
+"COPY_DESCRIPTION_DESC" = "Copy the video description";
+"COPY_URL" = "Copy video link";
+"COPY_URL_TIMESTAMP" = "Copy video link with timestamp";
+"URL_COPIED" = "Copied video link";
+"URL_TIMESTAMP_COPIED" = "Copied video link with timestamp";
+"ERROR_VIDEOID" = "Cannot get the video ID";
+"ERROR_ADS" = "Cannot copy video link while playing ads";
+"COPIED_AUTHOR" = "Copied video author";
+"COPIED_TITLE" = "Copied video title";
+"COPIED_DESCRIPTION" = "Copied video description";
+"SAVE_THUMBNAIL" = "Save thumbnail";
+"SAVE_THUMBNAIL_DESC" = "Save to Photos";
+"DOWNLOAD_CAPTIONS" = "Download captions";
+"DOWNLOAD_CAPTIONS_DESC" = "Save subtitles as .VTT file";
+"DOWNLOAD_AUDIO" = "Download audio";
+"DOWNLOAD_AUDIO_DESC" = "Download audio as .m4a file";
+"DOWNLOAD_VIDEO" = "Download video";
+"DOWNLOAD_VIDEO_DESC" = "Choose video quality";
+"OPEN_VID_BEFORE" = "Open a video before using the download manager";
+"NO_CAPTIONS_URL" = "No valid caption URLs found";
+"NO_AUDIO_URL" = "No audio URL found";
+"NO_STREAM_URL" = "No stream URL found";
+"CAPTIONS_FAILED" = "Failed to download captions";
+"NO_CAPTIONS" = "No captions available for this video";
+"NO_AUDIO_STREAM_FOUND" = "No audio streams found";
+"NO_VID_AUDIO_STREAM_FOUND" = "No video/audio streams found";
+"COPIED_VID_INFO" = "Copied all video informations";
+"SAVED_TO_PHOTOS" = "Saved to Photos";
+"SAVE_FAILED" = "Save failed";
+"PHOTO_ACCESS_DENINED" = "Photos access denied";
+"THUMBNAIL_FAILED" = "Thumbnail failed";
+"DOWNLOADING_THUMBNAIL" = "Downloading thumbnail...";
+"DOWNLOADING_AUDIO" = "Downloading audio...";
+"DOWNLOADING_VIDEO" = "Downloading video...";
+"DOWNLOADING_CAPTIONS" = "Downloading captions...";
+"NO_THUMBNAIL_FOUND" = "No thumbnail found";
+"DOWNLOAD_FAILED" = "Download failed";
+"COPY_THUMBNAIL" = "Copy thumbnail";
+"COPY_THUMBNAIL_DESC" = "Copy to clipboard";
+"SHOW_THUMBNAIL" = "Show thumbnail";
+"SHOW_THUMBNAIL_DESC" = "Show in a dialog";
+"THUMBNAIL_OPTIONS" = "Download thumbnail";
+"THUMBNAIL_OPTIONS_DESC" = "Show more options";
+"COPIED_TO_CLIPBOARD" = "Copied to clipboard";
+"DOWNLOAD_COMPLETED" = "Download completed";
+"CANNOT_SAVE_TO_PHOTOS" = "Cannot save to Photos";
+"FINA_VIDEO" = "Finalizing video...";
+"MERGING_VID_FALLBACK" = "Merging video with fallback...";
+"MERGING_VID" = "Merging video...";
+"CONVERTING" = "Converting to";
+"ALREADY_DOWNLOADING" = "Already downloading";
+"DOWNLOAD_CANCELLED" = "Download cancelled";
+"CANCEL" = "Cancel";
+"TRIMMING_AUDIO" = "Trimming audio...";
+"COPY" = "Copy";
"APPEARANCE" = "Appearance";
"OLED_THEME" = "OLED dark mode";
@@ -46,6 +122,8 @@
"OLED_KEYBOARD_DESC" = "Make the keyboard uses a pure black (OLED) color for dark theme.";
"NAVBAR" = "Navigation bar";
+"STICKY_NAVBAR" = "Sticky Navigation bar";
+"STICKY_NAVBAR_DESC" = "Pins the Navigation bar so that it remains visible when scrolling down.";
"HIDE_YT_LOGO" = "Hide YouTube logo";
"HIDE_YT_LOGO_DESC" = "Hide YouTube logo in the navigation bar.";
"PREMIUM_LOGO" = "Use YouTube Premium logo";
@@ -62,34 +140,78 @@
"FEED" = "Feed";
"HIDE_SUBBAR" = "Hide subbar";
"HIDE_SUBBAR_DESC" = "Hide subbar at the top of the feed.";
+"HIDE_HORI_SHELF" = "Remove horizontal feeds";
+"HIDE_HORI_SHELF_DESC" = "Removes all horizontal feeds (News, Continue Watching, Posts collection, etc.) from the feed, except Playables.";
"HIDE_MUSIC_PLAYLISTS" = "Hide music playlist generator";
"HIDE_MUSIC_PLAYLISTS_DESC" = "Hide music playlist generator (Select music tunes in three) in the feed.";
"HIDE_FEED_POST" = "Hide posts";
"HIDE_FEED_POST_DESC" = "Hide posts in feed.";
+"HIDE_SURVEYS" = "Hide surveys";
+"HIDE_SURVEYS_DESC" = "Hide video surveys that YouTube shows sometimes.";
"HIDE_SHORTS_SHELF" = "Hide Shorts shelf";
"HIDE_SHORTS_SHELF_DESC" = "Hide Shorts shelf in the feed, including in the Subscriptions tab.";
+"KEEP_SHORTS_SUBSCRIPT" = "Keep Shorts in Subscriptions";
+"KEEP_SHORTS_SUBSCRIPT_DESC" = "Prevents removing Shorts from Subscriptions tab.";
"HIDE_SEARCH_HISTORY" = "Hide search history";
"HIDE_SEARCH_HISTORY_DESC" = "Hide previous search history and suggestions when using the search bar.\nNOTE: Your search history will still be accessible in your other YouTube clients.";
-"HIDE_SUB_BUTTON" = "Hide Subscribe button";
-"HIDE_SUB_BUTTON_DESC" = "Hides the subscribe button in channels and video descriptions.";
-"HIDE_SHOP_BUTTON" = "Hide Shopping button";
-"HIDE_SHOP_BUTTON_DESC" = "Hides the shopping/store button in channels descriptions.";
-"HIDE_MEMBER_BUTTON" = "Hide Membership button";
-"HIDE_MEMBER_BUTTON_DESC" = "Hides the 'Join' button in channels and video descriptions.";
+"HIDE_PLAYABLES" = "Remove playables";
+"HIDE_PLAYABLES_DESC" = "Removes playables (mini-games) from the feed.";
"PLAYER" = "Player";
+"PLAYBACK_SPEED" = "Playback speed";
+"CONTROL_CENTER" = "Control Center";
+"PLAYER_ACTIONS" = "Player actions";
+"GESTURE_HEADER" = "Gestures";
+"QUALITY_WIFI" = "Playback quality on WiFi";
+"QUALITY_WIFI_DESC" = "Automatically applies the selected quality when streaming over Wi-Fi.";
+"QUALITY_CELLULAR" = "Playback quality on Cellular";
+"QUALITY_CELLULAR_DESC" = "Automatically applies the selected quality when streaming over mobile data.";
+"QUALITY_LOW_POWER" = "Playback quality on Power saver mode";
+"QUALITY_LOW_POWER_DESC" = "Automatically applies the selected quality when when Low power mode is enabled on your device.";
+"DEFAULT" = "Default";
+"BEST" = "Best";
+"AUDIO_TRACK" = "Preferred audio track";
+"ORIGINAL" = "Original";
+"SELECT_MANUALLY" = "Select manually";
+"AUDIO_TRACK_SELECT" = "Selected audio track";
+"AUDIO_TRACK_SELECT_DESC" = "Specifies the preferred audio track language to use for playback, if available.";
+"NO_AUTO_DUBBED" = "Uses original audio track";
+"NO_AUTO_DUBBED_DESC" = "Uses original audio track if the preferred language track is auto-dubbed or not found.";
+"CAPTION_TRACK" = "Preferred captions";
+"CAPTION_TRACK_SELECT" = "Selected captions";
+"CAPTION_TRACK_SELECT_DESC" = "Specifies the preferred captions language to display, if available.";
+"DISABLES_CAPTION_TRACK" = "Automatically disable captions";
+"DISABLES_CAPTION_TRACK_DESC" = "Automatically disable captions if the preferred language is auto-generated or not found.";
+"DEFAULT_SPEED" = "Default playback rate";
+"DEFAULT_SPEED_DESC" = "Overrides the default playback speed.";
+"SPEED_BUTTON" = "Speed controls button";
+"SPEED_BUTTON_DESC" = "Displays a speed controls button on overlay.";
+"MUTE_BUTTON" = "Mute button";
+"MUTE_BUTTON_DESC" = "Displays a button to mute or unmute the volume on the overlay.";
+"SHARE_BUTTON" = "Share button";
+"SHARE_BUTTON_DESC" = "Displays a button to share the video link on the overlay.";
+"LOOP_BUTTON" = "Loop button";
+"LOOP_BUTTON_DESC" = "Displays a button to loop the video on the overlay.";
+"CAPTION_BUTTON" = "Captions button";
+"CAPTION_BUTTON_DESC" = "Displays a button to copy the video captions on the overlay.";
+"HOLD_TO_SPEED" = "Hold to speed";
+"HOLD_TO_SPEED_DESC" = "Uses custom hold to speed playback rate.";
"HIDE_AUTOPLAY" = "Hide autoplay switch";
"HIDE_AUTOPLAY_DESC" = "Hide autoplay switch in the video overlay.";
"HIDE_CAPTIONS_BUTTON" = "Hide captions button";
"HIDE_CAPTIONS_BUTTON_DESC" = "Hide captions button in the video overlay.";
"HIDE_CAST_BUTTON_PLAYER" = "Hide cast button";
"HIDE_CAST_BUTTON_PLAYER_DESC" = "Hide cast button in the video overlay.";
-"HIDE_PREV_BUTTON" = "Hide previous/back button";
-"HIDE_PREV_BUTTON_DESC" = "Hide previous/back button in the video overlay.";
-"HIDE_NEXT_BUTTON" = "Hide next/skip button";
-"HIDE_NEXT_BUTTON_DESC" = "Hide next/skip button in the video overlay.";
+"HIDE_NEXT_AND_PREV_BUTTON" = "Hide previous and next buttons";
+"HIDE_NEXT_AND_PREV_BUTTON_DESC" = "Hide previous and next buttons in the video overlay.";
"REPLACE_PREVNEXT_BUTTONS" = "Replace Previous/Next buttons";
"REPLACE_PREVNEXT_BUTTONS_DESC" = "Replaces the Previous and Next video buttons in the player with Rewind and Fast Forward buttons.";
+"SKIP_BACKWARD" = "Skip backward";
+"SKIP_BACKWARD_DESC" = "Replaces the previous track button in Control Center with a skip backward button by a set number of seconds.";
+"SKIP_FORWARD" = "Skip forward";
+"SKIP_FORWARD_DESC" = "Replaces the next track button in Control Center with a skip forward button by a set number of seconds.";
+"REWIND_SECONDS" = "Rewind seconds";
+"FORWARD_SECONDS" = "Fast-forward seconds";
"REMOVE_DARK_OVERLAY" = "Remove dark overlay";
"REMOVE_DARK_OVERLAY_DESC" = "Remove dark overlay from the video overlay.";
"HIDE_END_SCREEN" = "Hide endscreen cards";
@@ -102,6 +224,12 @@
"HIDE_PAID_OVERLAY_DESC" = "Hide 'Includes paid promotion' in the video overlay.";
"HIDE_WATERMARK" = "Hide watermark";
"HIDE_WATERMARK_DESC" = "Hide channel watermark at the bottom of the video overlay.";
+"PAUSE_ON_OVERLAY" = "Pause on overlay";
+"PAUSE_ON_OVERLAY_DESC" = "Sets playback on pause if overlay appears.";
+"DISABLES_SNAP_TO_CHAPTER" = "Disable snap to chapters";
+"DISABLES_SNAP_TO_CHAPTER_DESC" = "Prevents the scrubbar stick to the chapters.";
+"DISABLES_ENGAGE_PANEL" = "Disables engagement panel in fullscreen";
+"DISABLES_ENGAGE_PANEL_DESC" = "Prevent engagement panel showing in fullscreen.";
"GESTURES" = "Enable gestures controls";
"GESTURES_DESC" = "Swipe up or down on the left/right edges of the video screen to adjust brightness, volume, or playback speed.";
"GESTURE_AREA" = "Gesture activation area";
@@ -136,6 +264,8 @@
"DISABLES_SHOW_REMAINING_DESC" = "Disable the feature that shows how much time is left in a video when tapping the timestamp.";
"ALWAYS_SHOW_REMAINING" = "Always show remaining time";
"ALWAYS_SHOW_REMAINING_DESC" = "Ensure the remaining time is always displayed by default in the player overlay.";
+"COPY_TIMESTAMP_ON_PAUSE" = "Copy timestamped links";
+"COPY_TIMESTAMP_ON_PAUSE_DESC" = "Allows to copy timestamped link to the clipboard by pressing pause button.";
"SHOW_REMAINING_EXTRA" = "Show extra remaining time";
"SHOW_REMAINING_EXTRA_DESC" = "Adds the estimated finish time to the label in the video overlay.";
"HIDE_FULLSCREEN_ACTIONS" = "Hide fullscreen actions";
@@ -148,93 +278,95 @@
"HIDE_CONTENT_WARNING_DESC" = "Automatically skip the sensitive content warning alert in some vidoes.";
"AUTO_FULLSCREEN" = "Auto fullscreen";
"AUTO_FULLSCREEN_DESC" = "Automatically enter fullscreen mode when starting video playback.";
-"AUTO_DISABLES_CAPTION" = "Auto disable captions";
-"AUTO_DISABLES_CAPTION_DESC" = "Automatically turns off closed captions (CC) whenever a new video starts playing.";
"PORTRAIT_FULLSCREEN" = "Portrait fullscren mode";
"PORTRAIT_FULLSCREEN_DESC" = "Enables portrait fullscreen mode support.";
"OLD_QUALITY_PICKER" = "Use old quality picker";
"OLD_QUALITY_PICKER_DESC" = "Reverts the video quality selection menu to the classic version (lists all resolutions immediately).";
"EXTRA_SPEED" = "Extra speed options";
"EXTRA_SPEED_DESC" = "Adds more video playback speed options up to 10x.";
-"DISABLE_HINTS" = "Disable hints";
-"DISABLE_HINTS_DESC" = "Disable the tutorial hints and interactive pop-ups.";
"FORCE_MINIPLAYER" = "Force miniplayer";
"FORCE_MINIPLAYER_DESC" = "Allows all videos, including those made for kids, to play in the miniplayer when dismissing the player.";
"FORCE_SEEKBAR" = "Always show progress bar";
"FORCE_SEEKBAR_DESC" = "Makes the progress bar (seekbar) remains visible at all times while in fullscreen mode.";
-"HIDE_LIKE_BUTTON" = "Hide Like button";
-"HIDE_LIKE_BUTTON_DESC" = "Hide Like button in the action bar under the video overlay.";
-"HIDE_DISLIKE_BUTTON" = "Hide Dislike button";
-"HIDE_DISLIKE_BUTTON_DESC" = "Hide Dislike button in the action bar under the video overlay.";
-"HIDE_SHARE_BUTTON" = "Hide Share button";
-"HIDE_SHARE_BUTTON_DESC" = "Hide Share button in the action bar under the video overlay.";
-"HIDE_DOWNLOAD_BUTTON" = "Hide Download button";
-"HIDE_DOWNLOAD_BUTTON_DESC" = "Hide Download button in the action bar under the video overlay.";
-"HIDE_CLIP_BUTTON" = "Hide Clip button";
-"HIDE_CLIP_BUTTON_DESC" = "Hide Clip button in the action bar under the video overlay.";
-"HIDE_REMIX_BUTTON" = "Hide Remix button";
-"HIDE_REMIX_BUTTON_DESC" = "Hide Remix button in the action bar under the video overlay.";
-"HIDE_SAVE_BUTTON" = "Hide Save button";
-"HIDE_SAVE_BUTTON_DESC" = "Hide Save button in the action bar under the video overlay.";
+"TAP_TO_SEEK" = "Tap to seek";
+"TAP_TO_SEEK_DESC" = "Quickly jump to any point by tapping the progress bar.";
+"DISABLES_ZOOM" = "Disable free zoom gesture";
+"DISABLES_ZOOM_DESC" = "Disables new free zoom gestures.";
+"USES_24_HOURS_TIME" = "24-hour format";
+"USES_24_HOURS_TIME_DESC" = "Shows end time in 24-hour format.";
+"PAUSE_TWO_FINGERS" = "Pause using two fingers";
+"PAUSE_TWO_FINGERS_DESC" = "Pause/Resume playback by tapping on the screen using two fingers.";
+"HIDE_COMMENTS_SECTION" = "Hide comments section";
+"HIDE_COMMENTS_SECTION_DESC" = "Completely removes the entire comments section.";
+"HIDE_COMMENTS_PREVIEW" = "Hide preview comments";
+"HIDE_COMMENTS_PREVIEW_DESC" = "Removes the preview box for the top comment while keeping the full section expandable.";
+"USE_ANOTHER_MINIPLAYER" = "Uses another miniplayer style";
+"USE_ANOTHER_MINIPLAYER_DESC" = "Uses another miniplayer style redesigned from older YT versions.";
"SHORTS" = "Shorts";
-"HIDE_SHORTS_LIKE_BUTTON" = "Hide Like button";
-"HIDE_SHORTS_LIKE_BUTTON_DESC" = "Hide the Like button in the Shorts player.";
-"HIDE_SHORTS_DISLIKE_BUTTON" = "Hide Dislike button";
-"HIDE_SHORTS_DISLIKE_BUTTON_DESC" = "Hide the Dislike button in the Shorts player.";
-"HIDE_SHORTS_COMMENT_BUTTON" = "Hide Comment button";
-"HIDE_SHORTS_COMMENT_BUTTON_DESC" = "Hide the Comment button in the Shorts player.";
-"HIDE_SHORTS_SHARE_BUTTON" = "Hide Share button";
-"HIDE_SHORTS_SHARE_BUTTON_DESC" = "Hide the Share button in the Shorts player.";
-"HIDE_SHORTS_REMIX_BUTTON" = "Hide Remix button";
-"HIDE_SHORTS_REMIX_BUTTON_DESC" = "Hide the Remix button in the Shorts player.";
-"HIDE_METADATA_BUTTON" = "Hide sound metadata button";
-"HIDE_METADATA_BUTTON_DESC" = "Hide the button that shows the sound source or music metadata in the Shorts player.";
+"SHORTS_ONLY" = "Shorts only mode";
+"SHORTS_ONLY_DESC" = "Limits YouTube functionality to viewing Shorts only.";
+"SHORTS_ACTION" = "Shorts action after the video ends";
+"LOOP" = "Loop video";
+"SKIP_TO_NEXT_SHORTS" = "Skip to next video";
+"PAUSE_SHORTS" = "Stop video";
+"REMOVE_LIVE_SHORTS" = "Remove live videos";
+"REMOVE_LIVE_SHORTS_DESC" = "Removes live videos from the feed.";
"HIDE_SHORTS_PRODUCT" = "Hide products";
"HIDE_SHORTS_PRODUCT_DESC" = "Hide tagged products and shopping links in the Shorts overlay.";
"HIDE_SHORTS_RECBAR" = "Hide recommendation bar";
"HIDE_SHORTS_RECBAR_DESC" = "Hide the suggested action bar in the Shorts player (e.g. Try this sound, Shopping, Preview comment).";
-"HIDE_SHORTS_COMMIT" = "Hide commissions bar";
-"HIDE_SHORTS_COMMIT_DESC" = "Hide the bar that shows the video got commissions in the Shorts player.";
-"HIDE_SHORTS_SUBSCRIPT_BUTTON" = "Hide subscriptions button";
-"HIDE_SHORTS_SUBSCRIPT_BUTTON_DESC" = "Hide the Subscriptions button at the top of the Shorts player.";
-"HIDE_SHORTS_LIVE_BUTTON" = "Hide live button";
-"HIDE_SHORTS_LIVE_BUTTON_DESC" = "Hide the Live button at the top of the Shorts player.";
-"HIDE_SHORTS_LENS_BUTTON" = "Hide lens button";
-"HIDE_SHORTS_LENS_BUTTON_DESC" = "Hide the Lens button at the top of the Shorts player.";
-"HIDE_SHORTS_TRENDS_BUTTON" = "Hide trends button";
-"HIDE_SHORTS_TRENDS_BUTTON_DESC" = "Hide the Trends button at the top of the Shorts player.";
-"HIDE_SHORTS_TO_VIDEO" = "Hide link to full video";
-"HIDE_SHORTS_TO_VIDEO_DESC" = "Hide the link that redirects in a Short to a full video in the Shorts player.";
"ENABLES_SHORTS_QUALITY" = "Enable Shorts quality picker";
"ENABLES_SHORTS_QUALITY_DESC" = "Force enable the video quality selection menu in Shorts.";
"SHOW_SHORTS_SEEKBAR" = "Always show seekbar";
"SHOW_SHORTS_SEEKBAR_DESC" = "Ensure the progress/seek bar is always visible at the bottom of the Shorts player.";
+"REMOVE_POSTS_SHORTS" = "Remove posts";
+"REMOVE_POSTS_SHORTS_DESC" = "Remove posts from the feed.";
+"SHORTS_FULLSCREEN" = "Pinch to Fullscreen";
+"SHORTS_FULLSCREEN_DESC" = "Manages visibility of the overlay with pinch in and pinch out gestures, displaying Shorts in fullscreen mode.";
"TABBAR" = "Tab bar";
"DEFAULT_TAB" = "Default startup tab";
-"DEFAULT_TAB_DESC" = "You can select the tab you want to open when opening the app.";
-"HOME_NAME" = "Home";
-"SUBSCRIPT_NAME" = "Subscriptions";
-"LIB_NAME" = "Library (You)";
+"FORSTED_TAB_BAR" = "Translucent tab bar";
+"ENABLED" = "Enabled";
"HIDE_TAB_INDI" = "Hide tab indicators";
"HIDE_TAB_INDI_DESC" = "Hide all tab indicators in the tab bar.";
"HIDE_TAB_LABELS" = "Hide tab labels";
"HIDE_TAB_LABELS_DESC" = "Hide all tab labels in the tab bar.";
-"HIDE_HOME_TAB" = "Hide Home tab";
-"HIDE_HOME_TAB_DESC" = "Hide Home tab in the tab bar.";
-"HIDE_SHORTS_TAB" = "Hide Shorts tab";
-"HIDE_SHORTS_TAB_DESC" = "Hide Shorts tab in the tab bar.";
-"HIDE_CREATE_BUTTON" = "Hide Create button";
-"HIDE_CREATE_BUTTON_DESC" = "Hide Create button in the tab bar.";
-"HIDE_SUBSCRIPT_TAB" = "Hide Subscriptions tab";
-"HIDE_SUBSCRIPT_TAB_DESC" = "Hide Subscriptions tab in the tab bar.";
+"HOME_TAB" = "Home";
+"SHORTS_TAB" = "Shorts";
+"CREATE_TAB" = "Create";
+"SUBSCRIPTIONS_TAB" = "Subscriptions";
+"LIBRARY_TAB" = "Library (You)";
+"HISTORY_TAB" = "History";
+"GAMING_TAB" = "Gaming";
+"SPORTS_TAB" = "Sports";
+"NOTI_TAB" = "Notifications";
+"NEWS_TAB" = "News";
+"MUSIC_TAB" = "Music";
+"WATCH_LATER_TAB" = "Watch later";
+"PLAYLIST_TAB" = "Playlists";
+"LIKE_TAB" = "Liked videos";
+"LIVE_TAB" = "Live";
+"POST_TAB" = "Posts";
+"VIDEO_TAB" = "Your videos";
+"MOVIE_TAB" = "Movies";
+"COURSE_TAB" = "Courses";
+"MINIGAME_TAB" = "Playables";
+"MANAGE_TABS" = "Manage tabs";
+"MANAGE_TABS_DESC" = "Reorder and toggle tabs";
+"TAB_REORDER_HINT" = "Drag to reorder. Toggle to show/hide.\nTab settings are also accessible by long-pressing the first tab in the tab bar.";
+"TAB_LIMIT" = "Tab limit";
+"TAB_LIMIT_DESC" = "Only 6 tabs are allowed.";
+"ZERO_TAB_DESC" = "At least 1 tab has to be enabled.";
"MISCELLANEOUS" = "Miscellaneous";
"BACKGROUND_PLAYBACK" = "Background playback";
"BACKGROUND_PLAYBACK_DESC" = "Enables background playback for videos.";
"DISABLES_SHORTS_PIP" = "Disables Shorts PiP";
"DISABLES_SHORTS_PIP_DESC" = "Try to disables Shorts PiP, this might not 100% disables it.";
+"DISABLE_HINTS" = "Disable hints";
+"DISABLE_HINTS_DESC" = "Disable the tutorial hints and interactive pop-ups.";
"BLOCK_UPGRADE_DIALOGS" = "Block upgrade dialogs";
"BLOCK_UPGRADE_DIALOGS_DESC" = "Block the YouTube dialogs that ask you to upgrade the app.";
"ARE_YOU_THERE_DIALOG" = "Hide 'Are you there?' dialog";
@@ -247,7 +379,109 @@
"DISABLES_SNACK_BAR_DESC" = "Disables snack bar when pressing some buttons or doing something in the app.";
"HIDE_STARTUP_ANIMATIONS" = "Hide YouTube's startup animations";
"HIDE_STARTUP_ANIMATIONS_DESC" = "Hide YouTube's startup animations when opening the app.";
-"HIDE_PLAY_IN_NEXT_QUEUE" = "Hide 'Play next in queue'";
-"HIDE_PLAY_IN_NEXT_QUEUE_DESC" = "Hide the 'Play next in queue' option in the flyout menu.";
"HIDE_LIKE_DISLIKE_VOTES" = "Hide like/dislike votes";
-"HIDE_LIKE_DISLIKE_VOTES_DESC" = "Hide messages that pop up when you like/dislike videos.";
\ No newline at end of file
+"HIDE_LIKE_DISLIKE_VOTES_DESC" = "Hide messages that pop up when you like/dislike videos.";
+"FLYOUT_MENU" = "Flyout menu";
+"REMOVE_PLAY_IN_NEXT_QUEUE_OPTION" = "Removes 'Play next in queue' option";
+"REMOVE_PLAY_IN_NEXT_QUEUE_OPTION_DESC" = "Removes 'Play next in queue' option from the flyout menu.";
+"REMOVE_DOWNLOAD_OPTION" = "Removes 'Download video' option";
+"REMOVE_DOWNLOAD_OPTION_DESC" = "Removes 'Download video' option from the flyout menu.";
+"REMOVE_WATCH_LATER_OPTION" = "Removes 'Save to Watch later' option";
+"REMOVE_WATCH_LATER_OPTION_DESC" = "Removes 'Save to Watch Later' option from the flyout menu.";
+"REMOVE_SAVE_OPTION" = "Removes 'Save to playlist' option";
+"REMOVE_SAVE_OPTION_DESC" = "Removes 'Save to playlist' option from the flyout menu.";
+"REMOVE_REMOVE_FROM_PLAYLIST_OPTION" = "Removes 'Remove from playlist' option";
+"REMOVE_REMOVE_FROM_PLAYLIST_OPTION_DESC" = "Removes 'Remove from playlist' option from the flyout menu.";
+"REMOVE_SHARE_OPTION" = "Removes 'Share' option";
+"REMOVE_SHARE_OPTION_DESC" = "Removes 'Share' option from the flyout menu.";
+"REMOVE_NOT_INTERESTED_OPTION" = "Removes 'Not interested' option";
+"REMOVE_NOT_INTERESTED_OPTION_DESC" = "Removes 'Not interested' option from the flyout menu.";
+"REMOVE_DONT_RECOMMEND_OPTION" = "Removes 'Don't recommend channel' option";
+"REMOVE_DONT_RECOMMEND_OPTION_DESC" = "Removes 'Don't recommend channel' option from the flyout menu.";
+"REMOVE_INFO_OPTION" = "Removes 'Why are you seeing this?' option";
+"REMOVE_INFO_OPTION_DESC" = "Removes 'Why are you seeing this?' option from the flyout menu.";
+"REMOVE_FILTER_OPTION" = "Removes 'Search filters' option";
+"REMOVE_FILTER_OPTION_DESC" = "Removes 'Search filters' option from the flyout menu.";
+"REMOVE_REPORT_OPTION" = "Removes 'Report' option";
+"REMOVE_REPORT_OPTION_DESC" = "Removes 'Report' option from the flyout menu.";
+"REMOVE_YOUTUBE_MUSIC_OPTION" = "Removes 'Listen with YouTube Music' option";
+"REMOVE_YOUTUBE_MUSIC_OPTION_DESC" = "Removes 'Listen with YouTube Music' option from the flyout menu.";
+"REMOVE_FEED_BACK_OPTION" = "Removes 'Send feedback' option";
+"REMOVE_FEED_BACK_OPTION_DESC" = "Removes 'Send feedback' option from the flyout menu.";
+"REMOVE_CAST_OPTION" = "Removes 'Watch on TV' option";
+"REMOVE_CAST_OPTION_DESC" = "Removes 'Watch on TV' option from the flyout menu.";
+"REMOVE_SHUFFLE_OPTION" = "Removes 'Shuffle' option";
+"REMOVE_SHUFFLE_OPTION_DESC" = "Removes 'Shuffle' option from the flyout menu.";
+"REMOVE_UN_SUB_OPTION" = "Removes 'Unsubscribe' option";
+"REMOVE_UN_SUB_OPTION_DESC" = "Removes 'Unsubscribe' option from the flyout menu.";
+"REMOVE_HIDE_FROM_PLAYLIST_OPTION" = "Removes 'Hide video from playlist' option";
+"REMOVE_HIDE_FROM_PLAYLIST_OPTION_DESC" = "Removes 'Hide video from playlist' option from the flyout menu.";
+"REMOVE_HELP_OPTION" = "Removes 'Help & feedback' option";
+"REMOVE_HELP_OPTION_DESC" = "Removes 'Help & feedback' option from the flyout menu.";
+"REMOVE_NOTIFY_OPTION" = "Removes 'Notify me' option";
+"REMOVE_NOTIFY_OPTION_DESC" = "Removes 'Notify me' option from the flyout menu.";
+"REMOVE_CLEARSCREEN_OPTION" = "Removes 'Clear Screen' option";
+"REMOVE_CLEARSCREEN_OPTION_DESC" = "Removes 'Clear Screen' option from the flyout menu.";
+"HIDE_COMMU_GUIDE" = "Hide community guidelines";
+"HIDE_COMMU_GUIDE_DESC" = "Hide community guidelines header.";
+"DISABLES_RTL" = "Disable RTL formatting";
+"DISABLES_RTL_DESC" = "Forcefully displays text in left-to-right (LTR) format for languages that are initially displayed in right-to-left (RTL).";
+"DEVICE_UI" = "Interface style";
+
+"SB_DESCRIPTION" = "Skip sponsored segments automatically using community-submitted data.";
+"SB_ENABLE" = "Enable SponsorBlock";
+"SB_ENABLE_DESC" = "Activate the SponsorBlock extension to skip sponsored segments.";
+"SB_SHOW_BUTTON" = "Show overlay button";
+"SB_SHOW_BUTTON_DESC" = "Display a SponsorBlock toggle button in the player overlay.";
+"SB_SHOW_NOTIFICATIONS" = "Show notifications";
+"SB_SHOW_NOTIFICATIONS_DESC" = "Display notifications when segments are skipped. Notifications for manual-skip categories are always shown.";
+"SB_HAPTIC_FEEDBACK" = "Haptic feedback";
+"SB_HAPTIC_FEEDBACK_DESC" = "Provide haptic feedback when a segment is skipped.";
+"SB_SEGMENTS_IN_PLAYER" = "Segments in player";
+"SB_SEGMENTS_IN_PLAYER_DESC" = "Display colored segments on the progress bar of the main video player.";
+"SB_SEGMENTS_IN_FEED" = "Segments in feed";
+"SB_SEGMENTS_IN_FEED_DESC" = "Display colored segments on the progress bar of players in the feed.";
+"SB_SEGMENTS_IN_MINIPLAYER" = "Segments in mini-player";
+"SB_SEGMENTS_IN_MINIPLAYER_DESC" = "Display colored segments on the progress bar of the mini-player.";
+"SB_SHOW_DURATION" = "Show duration without segments";
+"SB_SHOW_DURATION_DESC" = "Display the total video duration excluding skippable segments.";
+"SB_CATEGORIES_HEADER" = "Segment categories";
+"SB_SEGMENT_COLOR" = "Segment color";
+
+"SB_ACTION_DISABLE" = "Disable";
+"SB_ACTION_AUTO_SKIP" = "Auto-skip";
+"SB_ACTION_ASK" = "Ask before skipping";
+"SB_ACTION_DISPLAY" = "Show on seek bar only";
+"SB_ACTION_SKIP_TO" = "Skip to segment";
+
+"SB_CAT_sponsor" = "Sponsor";
+"SB_CAT_sponsor_DESC" = "Paid promotion for a product or service not directly related to the creator.";
+"SB_CAT_intro" = "Intro";
+"SB_CAT_intro_DESC" = "Intermission or intro animation typically seen at the start of a video.";
+"SB_CAT_outro" = "Endcards";
+"SB_CAT_outro_DESC" = "Endcards, credits, or outros at the end of the video.";
+"SB_CAT_interaction" = "Interaction";
+"SB_CAT_interaction_DESC" = "Reminders to like, subscribe, or follow on social media.";
+"SB_CAT_selfpromo" = "Self-promotion";
+"SB_CAT_selfpromo_DESC" = "Unpaid promotion of the creator's own products, merchandise, or other content.";
+"SB_CAT_music_offtopic" = "Non-music";
+"SB_CAT_music_offtopic_DESC" = "Non-music sections in music videos (e.g. talking, introductions).";
+"SB_CAT_preview" = "Preview";
+"SB_CAT_preview_DESC" = "Recap or preview of content from another video or previous episode.";
+"SB_CAT_hook" = "Hook/Greetings";
+"SB_CAT_hook_DESC" = "Narrated trailers for the upcoming video, greetings and goodbyes.";
+"SB_CAT_poi_highlight" = "Highlight";
+"SB_CAT_poi_highlight_DESC" = "The key moment or highlight point of the video. Use 'Skip to' to jump directly to it.";
+"SB_CAT_filler" = "Filler";
+"SB_CAT_filler_DESC" = "Tangential content, jokes, or filler that is not directly related to the main topic.";
+
+"SB_SKIPPED" = "%@ segment has been skipped";
+"SB_DETECTED" = "%@ segment detected.\nWould you like to skip the segment?";
+"SB_SKIP_NOW" = "Skip";
+"SB_UNSKIP" = "Unskip";
+"SB_JUMPED_TO_HIGHLIGHT" = "Jumped to highlight";
+"SB_JUMP_TO_HIGHLIGHT" = "Highlight available. Jump to the point?";
+"SB_SEGMENT_COLOR_SUFFIX" = "Segment color";
+"SB_SECTION_MAIN" = "Main";
+"SB_SKIP_ALERT_DURATION" = "Skip alert duration";
+"SB_UNSKIP_ALERT_DURATION" = "Unskip alert duration";
\ No newline at end of file
diff --git a/layout/Library/Application Support/YouMod.bundle/pl.lproj/Localizable.strings b/layout/Library/Application Support/YouMod.bundle/pl.lproj/Localizable.strings
index bccaf550..2c1a8a0f 100644
--- a/layout/Library/Application Support/YouMod.bundle/pl.lproj/Localizable.strings
+++ b/layout/Library/Application Support/YouMod.bundle/pl.lproj/Localizable.strings
@@ -1,11 +1,18 @@
-"NOTE" = "NOTE: Some hiding features may leave a blank box.";
"SETTINGS" = "Settings";
+"INTERFACE" = "Interface";
+
+"LOOP_DISABLED" = "Loop disabled";
+"LOOP_ENABLED" = "Loop enabled";
"NEW_ISSUES" = "Report issues or request new features";
"NEW_ISSUES_DESC" = "Found bugs or want to request new features? Tap here.";
"SOURCE_CODES" = "Source codes";
"SOURCE_CODES_DESC" = "Take a look at the codes!";
+"EXTRA" = "Extra";
+"FIX_PLAYBACK_ISSUES" = "Fix playback issues (Temporary)";
+"FIX_PLAYBACK_ISSUES_DESC" = "Try to fix video playback issues for some users. Turning this on will make the player automatically reloads itself everytime it errors or fails to play the video.";
+
"PERFER_HEADER" = "YouMod perferences";
"PERFER" = "Perferences";
"IMPORT" = "Import settings";
@@ -24,6 +31,7 @@
"ERROR_NO_KEYS_IMPORT" = "No available keys to import";
"ERROR_NO_KEYS_EXPORT" = "No available keys to export";
"OVERRIDE" = "This will override the current settings. Do you want to continue?";
+"DISABLED" = "Disabled";
"CACHE" = "Cache management";
"CLEARCACHE" = "Clear cache";
@@ -32,12 +40,80 @@
"AUTO_CLEARCACHE_DESC" = "Automatically clears the app cache on startup.";
"DOWNLOADING" = "Downloading";
-"DOWNLOAD_MANAGER" = "Download Manager";
-"DOWNLOAD_MANAGER_DESC" = "Replaces YouTube's video downloader with YouMod one. With ablity to downloading videos, audios, captions, and thumbnails";
+"DOWNLOAD_MANAGER" = "Download manager";
+"DOWNLOAD_MANAGER_DESC" = "Replaces YouTube's video downloader with YouMod one. With ablity to downloading videos, audios, captions, and thumbnails.";
"DOWNLOAD_SAVE_PHOTOS" = "Automatically save to Photos";
"DOWNLOAD_SAVE_PHOTOS_DESC" = "Automatically saves downloaded videos and images directly to your Photos app.";
-"DOWNLOAD_DRC_AUDIO" = "Prefer DRC Audio";
-"DOWNLOAD_DRC_AUDIO_DESC" = "Prioritizes downloading audio tracks with Dynamic Range Compression for more consistent volume levels.";
+"ADD_SHORTS_DOWNLOAD" = "Adds a download button to Shorts";
+"ADD_SHORTS_DOWNLOAD_DESC" = "Adds a download button to Shorts video overlay.";
+"USE_ORIG_AUDIO" = "Automatically uses original audio track";
+"USE_ORIG_AUDIO_DESC" = "Automatically uses original audio track instead of showing a dialog.";
+"DOWNLOAD_SHORTS" = "Download Shorts";
+"DOWNLOAD_SHORTS_DESC" = "Choose Shorts quality";
+"COPY_VID_INFO" = "Copy video informations";
+"COPY_VID_INFO_DESC" = "Choose what to copy";
+"COPY_ALL_VID_INFO" = "Copy all";
+"COPY_ALL_VID_INFO_DESC" = "Copy all video informations";
+"COPY_AUTHOR" = "Copy author";
+"COPY_AUTHOR_DESC" = "Copy the video author";
+"COPY_TITLE" = "Copy title";
+"COPY_TITLE_DESC" = "Copy the video title";
+"COPY_DESCRIPTION" = "Copy description";
+"COPY_DESCRIPTION_DESC" = "Copy the video description";
+"COPY_URL" = "Copy video link";
+"COPY_URL_TIMESTAMP" = "Copy video link with timestamp";
+"URL_COPIED" = "Copied video link";
+"URL_TIMESTAMP_COPIED" = "Copied video link with timestamp";
+"ERROR_VIDEOID" = "Cannot get the video ID";
+"ERROR_ADS" = "Cannot copy video link while playing ads";
+"COPIED_AUTHOR" = "Copied video author";
+"COPIED_TITLE" = "Copied video title";
+"COPIED_DESCRIPTION" = "Copied video description";
+"SAVE_THUMBNAIL" = "Save thumbnail";
+"SAVE_THUMBNAIL_DESC" = "Save to Photos";
+"DOWNLOAD_CAPTIONS" = "Download captions";
+"DOWNLOAD_CAPTIONS_DESC" = "Save subtitles as .VTT file";
+"DOWNLOAD_AUDIO" = "Download audio";
+"DOWNLOAD_AUDIO_DESC" = "Download audio as .m4a file";
+"DOWNLOAD_VIDEO" = "Download video";
+"DOWNLOAD_VIDEO_DESC" = "Choose video quality";
+"OPEN_VID_BEFORE" = "Open a video before using the download manager";
+"NO_CAPTIONS_URL" = "No valid caption URLs found";
+"NO_AUDIO_URL" = "No audio URL found";
+"NO_STREAM_URL" = "No stream URL found";
+"CAPTIONS_FAILED" = "Failed to download captions";
+"NO_CAPTIONS" = "No captions available for this video";
+"NO_AUDIO_STREAM_FOUND" = "No audio streams found";
+"NO_VID_AUDIO_STREAM_FOUND" = "No video/audio streams found";
+"COPIED_VID_INFO" = "Copied all video informations";
+"SAVED_TO_PHOTOS" = "Saved to Photos";
+"SAVE_FAILED" = "Save failed";
+"PHOTO_ACCESS_DENINED" = "Photos access denied";
+"THUMBNAIL_FAILED" = "Thumbnail failed";
+"DOWNLOADING_THUMBNAIL" = "Downloading thumbnail...";
+"DOWNLOADING_AUDIO" = "Downloading audio...";
+"DOWNLOADING_VIDEO" = "Downloading video...";
+"DOWNLOADING_CAPTIONS" = "Downloading captions...";
+"NO_THUMBNAIL_FOUND" = "No thumbnail found";
+"DOWNLOAD_FAILED" = "Download failed";
+"COPY_THUMBNAIL" = "Copy thumbnail";
+"COPY_THUMBNAIL_DESC" = "Copy to clipboard";
+"SHOW_THUMBNAIL" = "Show thumbnail";
+"SHOW_THUMBNAIL_DESC" = "Show in a dialog";
+"THUMBNAIL_OPTIONS" = "Download thumbnail";
+"THUMBNAIL_OPTIONS_DESC" = "Show more options";
+"COPIED_TO_CLIPBOARD" = "Copied to clipboard";
+"DOWNLOAD_COMPLETED" = "Download completed";
+"CANNOT_SAVE_TO_PHOTOS" = "Cannot save to Photos";
+"FINA_VIDEO" = "Finalizing video...";
+"MERGING_VID_FALLBACK" = "Merging video with fallback...";
+"MERGING_VID" = "Merging video...";
+"CONVERTING" = "Converting to";
+"ALREADY_DOWNLOADING" = "Already downloading";
+"DOWNLOAD_CANCELLED" = "Download cancelled";
+"CANCEL" = "Cancel";
+"TRIMMING_AUDIO" = "Trimming audio...";
+"COPY" = "Copy";
"APPEARANCE" = "Appearance";
"OLED_THEME" = "OLED dark mode";
@@ -46,6 +122,8 @@
"OLED_KEYBOARD_DESC" = "Make the keyboard uses a pure black (OLED) color for dark theme.";
"NAVBAR" = "Navigation bar";
+"STICKY_NAVBAR" = "Sticky Navigation bar";
+"STICKY_NAVBAR_DESC" = "Pins the Navigation bar so that it remains visible when scrolling down.";
"HIDE_YT_LOGO" = "Hide YouTube logo";
"HIDE_YT_LOGO_DESC" = "Hide YouTube logo in the navigation bar.";
"PREMIUM_LOGO" = "Use YouTube Premium logo";
@@ -62,34 +140,78 @@
"FEED" = "Feed";
"HIDE_SUBBAR" = "Hide subbar";
"HIDE_SUBBAR_DESC" = "Hide subbar at the top of the feed.";
+"HIDE_HORI_SHELF" = "Remove horizontal feeds";
+"HIDE_HORI_SHELF_DESC" = "Removes all horizontal feeds (News, Continue Watching, Posts collection, etc.) from the feed, except Playables.";
"HIDE_MUSIC_PLAYLISTS" = "Hide music playlist generator";
"HIDE_MUSIC_PLAYLISTS_DESC" = "Hide music playlist generator (Select music tunes in three) in the feed.";
"HIDE_FEED_POST" = "Hide posts";
"HIDE_FEED_POST_DESC" = "Hide posts in feed.";
+"HIDE_SURVEYS" = "Hide surveys";
+"HIDE_SURVEYS_DESC" = "Hide video surveys that YouTube shows sometimes.";
"HIDE_SHORTS_SHELF" = "Hide Shorts shelf";
"HIDE_SHORTS_SHELF_DESC" = "Hide Shorts shelf in the feed, including in the Subscriptions tab.";
+"KEEP_SHORTS_SUBSCRIPT" = "Keep Shorts in Subscriptions";
+"KEEP_SHORTS_SUBSCRIPT_DESC" = "Prevents removing Shorts from Subscriptions tab.";
"HIDE_SEARCH_HISTORY" = "Hide search history";
"HIDE_SEARCH_HISTORY_DESC" = "Hide previous search history and suggestions when using the search bar.\nNOTE: Your search history will still be accessible in your other YouTube clients.";
-"HIDE_SUB_BUTTON" = "Hide Subscribe button";
-"HIDE_SUB_BUTTON_DESC" = "Hides the subscribe button in channels and video descriptions.";
-"HIDE_SHOP_BUTTON" = "Hide Shopping button";
-"HIDE_SHOP_BUTTON_DESC" = "Hides the shopping/store button in channels descriptions.";
-"HIDE_MEMBER_BUTTON" = "Hide Membership button";
-"HIDE_MEMBER_BUTTON_DESC" = "Hides the 'Join' button in channels and video descriptions.";
+"HIDE_PLAYABLES" = "Remove playables";
+"HIDE_PLAYABLES_DESC" = "Removes playables (mini-games) from the feed.";
"PLAYER" = "Player";
+"PLAYBACK_SPEED" = "Playback speed";
+"CONTROL_CENTER" = "Control Center";
+"PLAYER_ACTIONS" = "Player actions";
+"GESTURE_HEADER" = "Gestures";
+"QUALITY_WIFI" = "Playback quality on WiFi";
+"QUALITY_WIFI_DESC" = "Automatically applies the selected quality when streaming over Wi-Fi.";
+"QUALITY_CELLULAR" = "Playback quality on Cellular";
+"QUALITY_CELLULAR_DESC" = "Automatically applies the selected quality when streaming over mobile data.";
+"QUALITY_LOW_POWER" = "Playback quality on Power saver mode";
+"QUALITY_LOW_POWER_DESC" = "Automatically applies the selected quality when when Low power mode is enabled on your device.";
+"DEFAULT" = "Default";
+"BEST" = "Best";
+"AUDIO_TRACK" = "Preferred audio track";
+"ORIGINAL" = "Original";
+"SELECT_MANUALLY" = "Select manually";
+"AUDIO_TRACK_SELECT" = "Selected audio track";
+"AUDIO_TRACK_SELECT_DESC" = "Specifies the preferred audio track language to use for playback, if available.";
+"NO_AUTO_DUBBED" = "Uses original audio track";
+"NO_AUTO_DUBBED_DESC" = "Uses original audio track if the preferred language track is auto-dubbed or not found.";
+"CAPTION_TRACK" = "Preferred captions";
+"CAPTION_TRACK_SELECT" = "Selected captions";
+"CAPTION_TRACK_SELECT_DESC" = "Specifies the preferred captions language to display, if available.";
+"DISABLES_CAPTION_TRACK" = "Automatically disable captions";
+"DISABLES_CAPTION_TRACK_DESC" = "Automatically disable captions if the preferred language is auto-generated or not found.";
+"DEFAULT_SPEED" = "Default playback rate";
+"DEFAULT_SPEED_DESC" = "Overrides the default playback speed.";
+"SPEED_BUTTON" = "Speed controls button";
+"SPEED_BUTTON_DESC" = "Displays a speed controls button on overlay.";
+"MUTE_BUTTON" = "Mute button";
+"MUTE_BUTTON_DESC" = "Displays a button to mute or unmute the volume on the overlay.";
+"SHARE_BUTTON" = "Share button";
+"SHARE_BUTTON_DESC" = "Displays a button to share the video link on the overlay.";
+"LOOP_BUTTON" = "Loop button";
+"LOOP_BUTTON_DESC" = "Displays a button to loop the video on the overlay.";
+"CAPTION_BUTTON" = "Captions button";
+"CAPTION_BUTTON_DESC" = "Displays a button to copy the video captions on the overlay.";
+"HOLD_TO_SPEED" = "Hold to speed";
+"HOLD_TO_SPEED_DESC" = "Uses custom hold to speed playback rate.";
"HIDE_AUTOPLAY" = "Hide autoplay switch";
"HIDE_AUTOPLAY_DESC" = "Hide autoplay switch in the video overlay.";
"HIDE_CAPTIONS_BUTTON" = "Hide captions button";
"HIDE_CAPTIONS_BUTTON_DESC" = "Hide captions button in the video overlay.";
"HIDE_CAST_BUTTON_PLAYER" = "Hide cast button";
"HIDE_CAST_BUTTON_PLAYER_DESC" = "Hide cast button in the video overlay.";
-"HIDE_PREV_BUTTON" = "Hide previous/back button";
-"HIDE_PREV_BUTTON_DESC" = "Hide previous/back button in the video overlay.";
-"HIDE_NEXT_BUTTON" = "Hide next/skip button";
-"HIDE_NEXT_BUTTON_DESC" = "Hide next/skip button in the video overlay.";
+"HIDE_NEXT_AND_PREV_BUTTON" = "Hide previous and next buttons";
+"HIDE_NEXT_AND_PREV_BUTTON_DESC" = "Hide previous and next buttons in the video overlay.";
"REPLACE_PREVNEXT_BUTTONS" = "Replace Previous/Next buttons";
"REPLACE_PREVNEXT_BUTTONS_DESC" = "Replaces the Previous and Next video buttons in the player with Rewind and Fast Forward buttons.";
+"SKIP_BACKWARD" = "Skip backward";
+"SKIP_BACKWARD_DESC" = "Replaces the previous track button in Control Center with a skip backward button by a set number of seconds.";
+"SKIP_FORWARD" = "Skip forward";
+"SKIP_FORWARD_DESC" = "Replaces the next track button in Control Center with a skip forward button by a set number of seconds.";
+"REWIND_SECONDS" = "Rewind seconds";
+"FORWARD_SECONDS" = "Fast-forward seconds";
"REMOVE_DARK_OVERLAY" = "Remove dark overlay";
"REMOVE_DARK_OVERLAY_DESC" = "Remove dark overlay from the video overlay.";
"HIDE_END_SCREEN" = "Hide endscreen cards";
@@ -102,6 +224,12 @@
"HIDE_PAID_OVERLAY_DESC" = "Hide 'Includes paid promotion' in the video overlay.";
"HIDE_WATERMARK" = "Hide watermark";
"HIDE_WATERMARK_DESC" = "Hide channel watermark at the bottom of the video overlay.";
+"PAUSE_ON_OVERLAY" = "Pause on overlay";
+"PAUSE_ON_OVERLAY_DESC" = "Sets playback on pause if overlay appears.";
+"DISABLES_SNAP_TO_CHAPTER" = "Disable snap to chapters";
+"DISABLES_SNAP_TO_CHAPTER_DESC" = "Prevents the scrubbar stick to the chapters.";
+"DISABLES_ENGAGE_PANEL" = "Disables engagement panel in fullscreen";
+"DISABLES_ENGAGE_PANEL_DESC" = "Prevent engagement panel showing in fullscreen.";
"GESTURES" = "Enable gestures controls";
"GESTURES_DESC" = "Swipe up or down on the left/right edges of the video screen to adjust brightness, volume, or playback speed.";
"GESTURE_AREA" = "Gesture activation area";
@@ -136,6 +264,8 @@
"DISABLES_SHOW_REMAINING_DESC" = "Disable the feature that shows how much time is left in a video when tapping the timestamp.";
"ALWAYS_SHOW_REMAINING" = "Always show remaining time";
"ALWAYS_SHOW_REMAINING_DESC" = "Ensure the remaining time is always displayed by default in the player overlay.";
+"COPY_TIMESTAMP_ON_PAUSE" = "Copy timestamped links";
+"COPY_TIMESTAMP_ON_PAUSE_DESC" = "Allows to copy timestamped link to the clipboard by pressing pause button.";
"SHOW_REMAINING_EXTRA" = "Show extra remaining time";
"SHOW_REMAINING_EXTRA_DESC" = "Adds the estimated finish time to the label in the video overlay.";
"HIDE_FULLSCREEN_ACTIONS" = "Hide fullscreen actions";
@@ -148,93 +278,95 @@
"HIDE_CONTENT_WARNING_DESC" = "Automatically skip the sensitive content warning alert in some vidoes.";
"AUTO_FULLSCREEN" = "Auto fullscreen";
"AUTO_FULLSCREEN_DESC" = "Automatically enter fullscreen mode when starting video playback.";
-"AUTO_DISABLES_CAPTION" = "Auto disable captions";
-"AUTO_DISABLES_CAPTION_DESC" = "Automatically turns off closed captions (CC) whenever a new video starts playing.";
"PORTRAIT_FULLSCREEN" = "Portrait fullscren mode";
"PORTRAIT_FULLSCREEN_DESC" = "Enables portrait fullscreen mode support.";
"OLD_QUALITY_PICKER" = "Use old quality picker";
"OLD_QUALITY_PICKER_DESC" = "Reverts the video quality selection menu to the classic version (lists all resolutions immediately).";
"EXTRA_SPEED" = "Extra speed options";
"EXTRA_SPEED_DESC" = "Adds more video playback speed options up to 10x.";
-"DISABLE_HINTS" = "Disable hints";
-"DISABLE_HINTS_DESC" = "Disable the tutorial hints and interactive pop-ups.";
"FORCE_MINIPLAYER" = "Force miniplayer";
"FORCE_MINIPLAYER_DESC" = "Allows all videos, including those made for kids, to play in the miniplayer when dismissing the player.";
"FORCE_SEEKBAR" = "Always show progress bar";
"FORCE_SEEKBAR_DESC" = "Makes the progress bar (seekbar) remains visible at all times while in fullscreen mode.";
-"HIDE_LIKE_BUTTON" = "Hide Like button";
-"HIDE_LIKE_BUTTON_DESC" = "Hide Like button in the action bar under the video overlay.";
-"HIDE_DISLIKE_BUTTON" = "Hide Dislike button";
-"HIDE_DISLIKE_BUTTON_DESC" = "Hide Dislike button in the action bar under the video overlay.";
-"HIDE_SHARE_BUTTON" = "Hide Share button";
-"HIDE_SHARE_BUTTON_DESC" = "Hide Share button in the action bar under the video overlay.";
-"HIDE_DOWNLOAD_BUTTON" = "Hide Download button";
-"HIDE_DOWNLOAD_BUTTON_DESC" = "Hide Download button in the action bar under the video overlay.";
-"HIDE_CLIP_BUTTON" = "Hide Clip button";
-"HIDE_CLIP_BUTTON_DESC" = "Hide Clip button in the action bar under the video overlay.";
-"HIDE_REMIX_BUTTON" = "Hide Remix button";
-"HIDE_REMIX_BUTTON_DESC" = "Hide Remix button in the action bar under the video overlay.";
-"HIDE_SAVE_BUTTON" = "Hide Save button";
-"HIDE_SAVE_BUTTON_DESC" = "Hide Save button in the action bar under the video overlay.";
+"TAP_TO_SEEK" = "Tap to seek";
+"TAP_TO_SEEK_DESC" = "Quickly jump to any point by tapping the progress bar.";
+"DISABLES_ZOOM" = "Disable free zoom gesture";
+"DISABLES_ZOOM_DESC" = "Disables new free zoom gestures.";
+"USES_24_HOURS_TIME" = "24-hour format";
+"USES_24_HOURS_TIME_DESC" = "Shows end time in 24-hour format.";
+"PAUSE_TWO_FINGERS" = "Pause using two fingers";
+"PAUSE_TWO_FINGERS_DESC" = "Pause/Resume playback by tapping on the screen using two fingers.";
+"HIDE_COMMENTS_SECTION" = "Hide comments section";
+"HIDE_COMMENTS_SECTION_DESC" = "Completely removes the entire comments section.";
+"HIDE_COMMENTS_PREVIEW" = "Hide preview comments";
+"HIDE_COMMENTS_PREVIEW_DESC" = "Removes the preview box for the top comment while keeping the full section expandable.";
+"USE_ANOTHER_MINIPLAYER" = "Uses another miniplayer style";
+"USE_ANOTHER_MINIPLAYER_DESC" = "Uses another miniplayer style redesigned from older YT versions.";
"SHORTS" = "Shorts";
-"HIDE_SHORTS_LIKE_BUTTON" = "Hide Like button";
-"HIDE_SHORTS_LIKE_BUTTON_DESC" = "Hide the Like button in the Shorts player.";
-"HIDE_SHORTS_DISLIKE_BUTTON" = "Hide Dislike button";
-"HIDE_SHORTS_DISLIKE_BUTTON_DESC" = "Hide the Dislike button in the Shorts player.";
-"HIDE_SHORTS_COMMENT_BUTTON" = "Hide Comment button";
-"HIDE_SHORTS_COMMENT_BUTTON_DESC" = "Hide the Comment button in the Shorts player.";
-"HIDE_SHORTS_SHARE_BUTTON" = "Hide Share button";
-"HIDE_SHORTS_SHARE_BUTTON_DESC" = "Hide the Share button in the Shorts player.";
-"HIDE_SHORTS_REMIX_BUTTON" = "Hide Remix button";
-"HIDE_SHORTS_REMIX_BUTTON_DESC" = "Hide the Remix button in the Shorts player.";
-"HIDE_METADATA_BUTTON" = "Hide sound metadata button";
-"HIDE_METADATA_BUTTON_DESC" = "Hide the button that shows the sound source or music metadata in the Shorts player.";
+"SHORTS_ONLY" = "Shorts only mode";
+"SHORTS_ONLY_DESC" = "Limits YouTube functionality to viewing Shorts only.";
+"SHORTS_ACTION" = "Shorts action after the video ends";
+"LOOP" = "Loop video";
+"SKIP_TO_NEXT_SHORTS" = "Skip to next video";
+"PAUSE_SHORTS" = "Stop video";
+"REMOVE_LIVE_SHORTS" = "Remove live videos";
+"REMOVE_LIVE_SHORTS_DESC" = "Removes live videos from the feed.";
"HIDE_SHORTS_PRODUCT" = "Hide products";
"HIDE_SHORTS_PRODUCT_DESC" = "Hide tagged products and shopping links in the Shorts overlay.";
"HIDE_SHORTS_RECBAR" = "Hide recommendation bar";
"HIDE_SHORTS_RECBAR_DESC" = "Hide the suggested action bar in the Shorts player (e.g. Try this sound, Shopping, Preview comment).";
-"HIDE_SHORTS_COMMIT" = "Hide commissions bar";
-"HIDE_SHORTS_COMMIT_DESC" = "Hide the bar that shows the video got commissions in the Shorts player.";
-"HIDE_SHORTS_SUBSCRIPT_BUTTON" = "Hide subscriptions button";
-"HIDE_SHORTS_SUBSCRIPT_BUTTON_DESC" = "Hide the Subscriptions button at the top of the Shorts player.";
-"HIDE_SHORTS_LIVE_BUTTON" = "Hide live button";
-"HIDE_SHORTS_LIVE_BUTTON_DESC" = "Hide the Live button at the top of the Shorts player.";
-"HIDE_SHORTS_LENS_BUTTON" = "Hide lens button";
-"HIDE_SHORTS_LENS_BUTTON_DESC" = "Hide the Lens button at the top of the Shorts player.";
-"HIDE_SHORTS_TRENDS_BUTTON" = "Hide trends button";
-"HIDE_SHORTS_TRENDS_BUTTON_DESC" = "Hide the Trends button at the top of the Shorts player.";
-"HIDE_SHORTS_TO_VIDEO" = "Hide link to full video";
-"HIDE_SHORTS_TO_VIDEO_DESC" = "Hide the link that redirects in a Short to a full video in the Shorts player.";
"ENABLES_SHORTS_QUALITY" = "Enable Shorts quality picker";
"ENABLES_SHORTS_QUALITY_DESC" = "Force enable the video quality selection menu in Shorts.";
"SHOW_SHORTS_SEEKBAR" = "Always show seekbar";
"SHOW_SHORTS_SEEKBAR_DESC" = "Ensure the progress/seek bar is always visible at the bottom of the Shorts player.";
+"REMOVE_POSTS_SHORTS" = "Remove posts";
+"REMOVE_POSTS_SHORTS_DESC" = "Remove posts from the feed.";
+"SHORTS_FULLSCREEN" = "Pinch to Fullscreen";
+"SHORTS_FULLSCREEN_DESC" = "Manages visibility of the overlay with pinch in and pinch out gestures, displaying Shorts in fullscreen mode.";
"TABBAR" = "Tab bar";
"DEFAULT_TAB" = "Default startup tab";
-"DEFAULT_TAB_DESC" = "You can select the tab you want to open when opening the app.";
-"HOME_NAME" = "Home";
-"SUBSCRIPT_NAME" = "Subscriptions";
-"LIB_NAME" = "Library (You)";
+"FORSTED_TAB_BAR" = "Translucent tab bar";
+"ENABLED" = "Enabled";
"HIDE_TAB_INDI" = "Hide tab indicators";
"HIDE_TAB_INDI_DESC" = "Hide all tab indicators in the tab bar.";
"HIDE_TAB_LABELS" = "Hide tab labels";
"HIDE_TAB_LABELS_DESC" = "Hide all tab labels in the tab bar.";
-"HIDE_HOME_TAB" = "Hide Home tab";
-"HIDE_HOME_TAB_DESC" = "Hide Home tab in the tab bar.";
-"HIDE_SHORTS_TAB" = "Hide Shorts tab";
-"HIDE_SHORTS_TAB_DESC" = "Hide Shorts tab in the tab bar.";
-"HIDE_CREATE_BUTTON" = "Hide Create button";
-"HIDE_CREATE_BUTTON_DESC" = "Hide Create button in the tab bar.";
-"HIDE_SUBSCRIPT_TAB" = "Hide Subscriptions tab";
-"HIDE_SUBSCRIPT_TAB_DESC" = "Hide Subscriptions tab in the tab bar.";
+"HOME_TAB" = "Home";
+"SHORTS_TAB" = "Shorts";
+"CREATE_TAB" = "Create";
+"SUBSCRIPTIONS_TAB" = "Subscriptions";
+"LIBRARY_TAB" = "Library (You)";
+"HISTORY_TAB" = "History";
+"GAMING_TAB" = "Gaming";
+"SPORTS_TAB" = "Sports";
+"NOTI_TAB" = "Notifications";
+"NEWS_TAB" = "News";
+"MUSIC_TAB" = "Music";
+"WATCH_LATER_TAB" = "Watch later";
+"PLAYLIST_TAB" = "Playlists";
+"LIKE_TAB" = "Liked videos";
+"LIVE_TAB" = "Live";
+"POST_TAB" = "Posts";
+"VIDEO_TAB" = "Your videos";
+"MOVIE_TAB" = "Movies";
+"COURSE_TAB" = "Courses";
+"MINIGAME_TAB" = "Playables";
+"MANAGE_TABS" = "Manage tabs";
+"MANAGE_TABS_DESC" = "Reorder and toggle tabs";
+"TAB_REORDER_HINT" = "Drag to reorder. Toggle to show/hide.\nTab settings are also accessible by long-pressing the first tab in the tab bar.";
+"TAB_LIMIT" = "Tab limit";
+"TAB_LIMIT_DESC" = "Only 6 tabs are allowed.";
+"ZERO_TAB_DESC" = "At least 1 tab has to be enabled.";
"MISCELLANEOUS" = "Miscellaneous";
"BACKGROUND_PLAYBACK" = "Background playback";
"BACKGROUND_PLAYBACK_DESC" = "Enables background playback for videos.";
"DISABLES_SHORTS_PIP" = "Disables Shorts PiP";
"DISABLES_SHORTS_PIP_DESC" = "Try to disables Shorts PiP, this might not 100% disables it.";
+"DISABLE_HINTS" = "Disable hints";
+"DISABLE_HINTS_DESC" = "Disable the tutorial hints and interactive pop-ups.";
"BLOCK_UPGRADE_DIALOGS" = "Block upgrade dialogs";
"BLOCK_UPGRADE_DIALOGS_DESC" = "Block the YouTube dialogs that ask you to upgrade the app.";
"ARE_YOU_THERE_DIALOG" = "Hide 'Are you there?' dialog";
@@ -247,7 +379,109 @@
"DISABLES_SNACK_BAR_DESC" = "Disables snack bar when pressing some buttons or doing something in the app.";
"HIDE_STARTUP_ANIMATIONS" = "Hide YouTube's startup animations";
"HIDE_STARTUP_ANIMATIONS_DESC" = "Hide YouTube's startup animations when opening the app.";
-"HIDE_PLAY_IN_NEXT_QUEUE" = "Hide 'Play next in queue'";
-"HIDE_PLAY_IN_NEXT_QUEUE_DESC" = "Hide the 'Play next in queue' option in the flyout menu.";
"HIDE_LIKE_DISLIKE_VOTES" = "Hide like/dislike votes";
-"HIDE_LIKE_DISLIKE_VOTES_DESC" = "Hide messages that pop up when you like/dislike videos.";
\ No newline at end of file
+"HIDE_LIKE_DISLIKE_VOTES_DESC" = "Hide messages that pop up when you like/dislike videos.";
+"FLYOUT_MENU" = "Flyout menu";
+"REMOVE_PLAY_IN_NEXT_QUEUE_OPTION" = "Removes 'Play next in queue' option";
+"REMOVE_PLAY_IN_NEXT_QUEUE_OPTION_DESC" = "Removes 'Play next in queue' option from the flyout menu.";
+"REMOVE_DOWNLOAD_OPTION" = "Removes 'Download video' option";
+"REMOVE_DOWNLOAD_OPTION_DESC" = "Removes 'Download video' option from the flyout menu.";
+"REMOVE_WATCH_LATER_OPTION" = "Removes 'Save to Watch later' option";
+"REMOVE_WATCH_LATER_OPTION_DESC" = "Removes 'Save to Watch Later' option from the flyout menu.";
+"REMOVE_SAVE_OPTION" = "Removes 'Save to playlist' option";
+"REMOVE_SAVE_OPTION_DESC" = "Removes 'Save to playlist' option from the flyout menu.";
+"REMOVE_REMOVE_FROM_PLAYLIST_OPTION" = "Removes 'Remove from playlist' option";
+"REMOVE_REMOVE_FROM_PLAYLIST_OPTION_DESC" = "Removes 'Remove from playlist' option from the flyout menu.";
+"REMOVE_SHARE_OPTION" = "Removes 'Share' option";
+"REMOVE_SHARE_OPTION_DESC" = "Removes 'Share' option from the flyout menu.";
+"REMOVE_NOT_INTERESTED_OPTION" = "Removes 'Not interested' option";
+"REMOVE_NOT_INTERESTED_OPTION_DESC" = "Removes 'Not interested' option from the flyout menu.";
+"REMOVE_DONT_RECOMMEND_OPTION" = "Removes 'Don't recommend channel' option";
+"REMOVE_DONT_RECOMMEND_OPTION_DESC" = "Removes 'Don't recommend channel' option from the flyout menu.";
+"REMOVE_INFO_OPTION" = "Removes 'Why are you seeing this?' option";
+"REMOVE_INFO_OPTION_DESC" = "Removes 'Why are you seeing this?' option from the flyout menu.";
+"REMOVE_FILTER_OPTION" = "Removes 'Search filters' option";
+"REMOVE_FILTER_OPTION_DESC" = "Removes 'Search filters' option from the flyout menu.";
+"REMOVE_REPORT_OPTION" = "Removes 'Report' option";
+"REMOVE_REPORT_OPTION_DESC" = "Removes 'Report' option from the flyout menu.";
+"REMOVE_YOUTUBE_MUSIC_OPTION" = "Removes 'Listen with YouTube Music' option";
+"REMOVE_YOUTUBE_MUSIC_OPTION_DESC" = "Removes 'Listen with YouTube Music' option from the flyout menu.";
+"REMOVE_FEED_BACK_OPTION" = "Removes 'Send feedback' option";
+"REMOVE_FEED_BACK_OPTION_DESC" = "Removes 'Send feedback' option from the flyout menu.";
+"REMOVE_CAST_OPTION" = "Removes 'Watch on TV' option";
+"REMOVE_CAST_OPTION_DESC" = "Removes 'Watch on TV' option from the flyout menu.";
+"REMOVE_SHUFFLE_OPTION" = "Removes 'Shuffle' option";
+"REMOVE_SHUFFLE_OPTION_DESC" = "Removes 'Shuffle' option from the flyout menu.";
+"REMOVE_UN_SUB_OPTION" = "Removes 'Unsubscribe' option";
+"REMOVE_UN_SUB_OPTION_DESC" = "Removes 'Unsubscribe' option from the flyout menu.";
+"REMOVE_HIDE_FROM_PLAYLIST_OPTION" = "Removes 'Hide video from playlist' option";
+"REMOVE_HIDE_FROM_PLAYLIST_OPTION_DESC" = "Removes 'Hide video from playlist' option from the flyout menu.";
+"REMOVE_HELP_OPTION" = "Removes 'Help & feedback' option";
+"REMOVE_HELP_OPTION_DESC" = "Removes 'Help & feedback' option from the flyout menu.";
+"REMOVE_NOTIFY_OPTION" = "Removes 'Notify me' option";
+"REMOVE_NOTIFY_OPTION_DESC" = "Removes 'Notify me' option from the flyout menu.";
+"REMOVE_CLEARSCREEN_OPTION" = "Removes 'Clear Screen' option";
+"REMOVE_CLEARSCREEN_OPTION_DESC" = "Removes 'Clear Screen' option from the flyout menu.";
+"HIDE_COMMU_GUIDE" = "Hide community guidelines";
+"HIDE_COMMU_GUIDE_DESC" = "Hide community guidelines header.";
+"DISABLES_RTL" = "Disable RTL formatting";
+"DISABLES_RTL_DESC" = "Forcefully displays text in left-to-right (LTR) format for languages that are initially displayed in right-to-left (RTL).";
+"DEVICE_UI" = "Interface style";
+
+"SB_DESCRIPTION" = "Skip sponsored segments automatically using community-submitted data.";
+"SB_ENABLE" = "Enable SponsorBlock";
+"SB_ENABLE_DESC" = "Activate the SponsorBlock extension to skip sponsored segments.";
+"SB_SHOW_BUTTON" = "Show overlay button";
+"SB_SHOW_BUTTON_DESC" = "Display a SponsorBlock toggle button in the player overlay.";
+"SB_SHOW_NOTIFICATIONS" = "Show notifications";
+"SB_SHOW_NOTIFICATIONS_DESC" = "Display notifications when segments are skipped. Notifications for manual-skip categories are always shown.";
+"SB_HAPTIC_FEEDBACK" = "Haptic feedback";
+"SB_HAPTIC_FEEDBACK_DESC" = "Provide haptic feedback when a segment is skipped.";
+"SB_SEGMENTS_IN_PLAYER" = "Segments in player";
+"SB_SEGMENTS_IN_PLAYER_DESC" = "Display colored segments on the progress bar of the main video player.";
+"SB_SEGMENTS_IN_FEED" = "Segments in feed";
+"SB_SEGMENTS_IN_FEED_DESC" = "Display colored segments on the progress bar of players in the feed.";
+"SB_SEGMENTS_IN_MINIPLAYER" = "Segments in mini-player";
+"SB_SEGMENTS_IN_MINIPLAYER_DESC" = "Display colored segments on the progress bar of the mini-player.";
+"SB_SHOW_DURATION" = "Show duration without segments";
+"SB_SHOW_DURATION_DESC" = "Display the total video duration excluding skippable segments.";
+"SB_CATEGORIES_HEADER" = "Segment categories";
+"SB_SEGMENT_COLOR" = "Segment color";
+
+"SB_ACTION_DISABLE" = "Disable";
+"SB_ACTION_AUTO_SKIP" = "Auto-skip";
+"SB_ACTION_ASK" = "Ask before skipping";
+"SB_ACTION_DISPLAY" = "Show on seek bar only";
+"SB_ACTION_SKIP_TO" = "Skip to segment";
+
+"SB_CAT_sponsor" = "Sponsor";
+"SB_CAT_sponsor_DESC" = "Paid promotion for a product or service not directly related to the creator.";
+"SB_CAT_intro" = "Intro";
+"SB_CAT_intro_DESC" = "Intermission or intro animation typically seen at the start of a video.";
+"SB_CAT_outro" = "Endcards";
+"SB_CAT_outro_DESC" = "Endcards, credits, or outros at the end of the video.";
+"SB_CAT_interaction" = "Interaction";
+"SB_CAT_interaction_DESC" = "Reminders to like, subscribe, or follow on social media.";
+"SB_CAT_selfpromo" = "Self-promotion";
+"SB_CAT_selfpromo_DESC" = "Unpaid promotion of the creator's own products, merchandise, or other content.";
+"SB_CAT_music_offtopic" = "Non-music";
+"SB_CAT_music_offtopic_DESC" = "Non-music sections in music videos (e.g. talking, introductions).";
+"SB_CAT_preview" = "Preview";
+"SB_CAT_preview_DESC" = "Recap or preview of content from another video or previous episode.";
+"SB_CAT_hook" = "Hook/Greetings";
+"SB_CAT_hook_DESC" = "Narrated trailers for the upcoming video, greetings and goodbyes.";
+"SB_CAT_poi_highlight" = "Highlight";
+"SB_CAT_poi_highlight_DESC" = "The key moment or highlight point of the video. Use 'Skip to' to jump directly to it.";
+"SB_CAT_filler" = "Filler";
+"SB_CAT_filler_DESC" = "Tangential content, jokes, or filler that is not directly related to the main topic.";
+
+"SB_SKIPPED" = "%@ segment has been skipped";
+"SB_DETECTED" = "%@ segment detected.\nWould you like to skip the segment?";
+"SB_SKIP_NOW" = "Skip";
+"SB_UNSKIP" = "Unskip";
+"SB_JUMPED_TO_HIGHLIGHT" = "Jumped to highlight";
+"SB_JUMP_TO_HIGHLIGHT" = "Highlight available. Jump to the point?";
+"SB_SEGMENT_COLOR_SUFFIX" = "Segment color";
+"SB_SECTION_MAIN" = "Main";
+"SB_SKIP_ALERT_DURATION" = "Skip alert duration";
+"SB_UNSKIP_ALERT_DURATION" = "Unskip alert duration";
\ No newline at end of file
diff --git a/layout/Library/Application Support/YouMod.bundle/pt-BR.lproj/Localizable.strings b/layout/Library/Application Support/YouMod.bundle/pt-BR.lproj/Localizable.strings
index bccaf550..2c1a8a0f 100644
--- a/layout/Library/Application Support/YouMod.bundle/pt-BR.lproj/Localizable.strings
+++ b/layout/Library/Application Support/YouMod.bundle/pt-BR.lproj/Localizable.strings
@@ -1,11 +1,18 @@
-"NOTE" = "NOTE: Some hiding features may leave a blank box.";
"SETTINGS" = "Settings";
+"INTERFACE" = "Interface";
+
+"LOOP_DISABLED" = "Loop disabled";
+"LOOP_ENABLED" = "Loop enabled";
"NEW_ISSUES" = "Report issues or request new features";
"NEW_ISSUES_DESC" = "Found bugs or want to request new features? Tap here.";
"SOURCE_CODES" = "Source codes";
"SOURCE_CODES_DESC" = "Take a look at the codes!";
+"EXTRA" = "Extra";
+"FIX_PLAYBACK_ISSUES" = "Fix playback issues (Temporary)";
+"FIX_PLAYBACK_ISSUES_DESC" = "Try to fix video playback issues for some users. Turning this on will make the player automatically reloads itself everytime it errors or fails to play the video.";
+
"PERFER_HEADER" = "YouMod perferences";
"PERFER" = "Perferences";
"IMPORT" = "Import settings";
@@ -24,6 +31,7 @@
"ERROR_NO_KEYS_IMPORT" = "No available keys to import";
"ERROR_NO_KEYS_EXPORT" = "No available keys to export";
"OVERRIDE" = "This will override the current settings. Do you want to continue?";
+"DISABLED" = "Disabled";
"CACHE" = "Cache management";
"CLEARCACHE" = "Clear cache";
@@ -32,12 +40,80 @@
"AUTO_CLEARCACHE_DESC" = "Automatically clears the app cache on startup.";
"DOWNLOADING" = "Downloading";
-"DOWNLOAD_MANAGER" = "Download Manager";
-"DOWNLOAD_MANAGER_DESC" = "Replaces YouTube's video downloader with YouMod one. With ablity to downloading videos, audios, captions, and thumbnails";
+"DOWNLOAD_MANAGER" = "Download manager";
+"DOWNLOAD_MANAGER_DESC" = "Replaces YouTube's video downloader with YouMod one. With ablity to downloading videos, audios, captions, and thumbnails.";
"DOWNLOAD_SAVE_PHOTOS" = "Automatically save to Photos";
"DOWNLOAD_SAVE_PHOTOS_DESC" = "Automatically saves downloaded videos and images directly to your Photos app.";
-"DOWNLOAD_DRC_AUDIO" = "Prefer DRC Audio";
-"DOWNLOAD_DRC_AUDIO_DESC" = "Prioritizes downloading audio tracks with Dynamic Range Compression for more consistent volume levels.";
+"ADD_SHORTS_DOWNLOAD" = "Adds a download button to Shorts";
+"ADD_SHORTS_DOWNLOAD_DESC" = "Adds a download button to Shorts video overlay.";
+"USE_ORIG_AUDIO" = "Automatically uses original audio track";
+"USE_ORIG_AUDIO_DESC" = "Automatically uses original audio track instead of showing a dialog.";
+"DOWNLOAD_SHORTS" = "Download Shorts";
+"DOWNLOAD_SHORTS_DESC" = "Choose Shorts quality";
+"COPY_VID_INFO" = "Copy video informations";
+"COPY_VID_INFO_DESC" = "Choose what to copy";
+"COPY_ALL_VID_INFO" = "Copy all";
+"COPY_ALL_VID_INFO_DESC" = "Copy all video informations";
+"COPY_AUTHOR" = "Copy author";
+"COPY_AUTHOR_DESC" = "Copy the video author";
+"COPY_TITLE" = "Copy title";
+"COPY_TITLE_DESC" = "Copy the video title";
+"COPY_DESCRIPTION" = "Copy description";
+"COPY_DESCRIPTION_DESC" = "Copy the video description";
+"COPY_URL" = "Copy video link";
+"COPY_URL_TIMESTAMP" = "Copy video link with timestamp";
+"URL_COPIED" = "Copied video link";
+"URL_TIMESTAMP_COPIED" = "Copied video link with timestamp";
+"ERROR_VIDEOID" = "Cannot get the video ID";
+"ERROR_ADS" = "Cannot copy video link while playing ads";
+"COPIED_AUTHOR" = "Copied video author";
+"COPIED_TITLE" = "Copied video title";
+"COPIED_DESCRIPTION" = "Copied video description";
+"SAVE_THUMBNAIL" = "Save thumbnail";
+"SAVE_THUMBNAIL_DESC" = "Save to Photos";
+"DOWNLOAD_CAPTIONS" = "Download captions";
+"DOWNLOAD_CAPTIONS_DESC" = "Save subtitles as .VTT file";
+"DOWNLOAD_AUDIO" = "Download audio";
+"DOWNLOAD_AUDIO_DESC" = "Download audio as .m4a file";
+"DOWNLOAD_VIDEO" = "Download video";
+"DOWNLOAD_VIDEO_DESC" = "Choose video quality";
+"OPEN_VID_BEFORE" = "Open a video before using the download manager";
+"NO_CAPTIONS_URL" = "No valid caption URLs found";
+"NO_AUDIO_URL" = "No audio URL found";
+"NO_STREAM_URL" = "No stream URL found";
+"CAPTIONS_FAILED" = "Failed to download captions";
+"NO_CAPTIONS" = "No captions available for this video";
+"NO_AUDIO_STREAM_FOUND" = "No audio streams found";
+"NO_VID_AUDIO_STREAM_FOUND" = "No video/audio streams found";
+"COPIED_VID_INFO" = "Copied all video informations";
+"SAVED_TO_PHOTOS" = "Saved to Photos";
+"SAVE_FAILED" = "Save failed";
+"PHOTO_ACCESS_DENINED" = "Photos access denied";
+"THUMBNAIL_FAILED" = "Thumbnail failed";
+"DOWNLOADING_THUMBNAIL" = "Downloading thumbnail...";
+"DOWNLOADING_AUDIO" = "Downloading audio...";
+"DOWNLOADING_VIDEO" = "Downloading video...";
+"DOWNLOADING_CAPTIONS" = "Downloading captions...";
+"NO_THUMBNAIL_FOUND" = "No thumbnail found";
+"DOWNLOAD_FAILED" = "Download failed";
+"COPY_THUMBNAIL" = "Copy thumbnail";
+"COPY_THUMBNAIL_DESC" = "Copy to clipboard";
+"SHOW_THUMBNAIL" = "Show thumbnail";
+"SHOW_THUMBNAIL_DESC" = "Show in a dialog";
+"THUMBNAIL_OPTIONS" = "Download thumbnail";
+"THUMBNAIL_OPTIONS_DESC" = "Show more options";
+"COPIED_TO_CLIPBOARD" = "Copied to clipboard";
+"DOWNLOAD_COMPLETED" = "Download completed";
+"CANNOT_SAVE_TO_PHOTOS" = "Cannot save to Photos";
+"FINA_VIDEO" = "Finalizing video...";
+"MERGING_VID_FALLBACK" = "Merging video with fallback...";
+"MERGING_VID" = "Merging video...";
+"CONVERTING" = "Converting to";
+"ALREADY_DOWNLOADING" = "Already downloading";
+"DOWNLOAD_CANCELLED" = "Download cancelled";
+"CANCEL" = "Cancel";
+"TRIMMING_AUDIO" = "Trimming audio...";
+"COPY" = "Copy";
"APPEARANCE" = "Appearance";
"OLED_THEME" = "OLED dark mode";
@@ -46,6 +122,8 @@
"OLED_KEYBOARD_DESC" = "Make the keyboard uses a pure black (OLED) color for dark theme.";
"NAVBAR" = "Navigation bar";
+"STICKY_NAVBAR" = "Sticky Navigation bar";
+"STICKY_NAVBAR_DESC" = "Pins the Navigation bar so that it remains visible when scrolling down.";
"HIDE_YT_LOGO" = "Hide YouTube logo";
"HIDE_YT_LOGO_DESC" = "Hide YouTube logo in the navigation bar.";
"PREMIUM_LOGO" = "Use YouTube Premium logo";
@@ -62,34 +140,78 @@
"FEED" = "Feed";
"HIDE_SUBBAR" = "Hide subbar";
"HIDE_SUBBAR_DESC" = "Hide subbar at the top of the feed.";
+"HIDE_HORI_SHELF" = "Remove horizontal feeds";
+"HIDE_HORI_SHELF_DESC" = "Removes all horizontal feeds (News, Continue Watching, Posts collection, etc.) from the feed, except Playables.";
"HIDE_MUSIC_PLAYLISTS" = "Hide music playlist generator";
"HIDE_MUSIC_PLAYLISTS_DESC" = "Hide music playlist generator (Select music tunes in three) in the feed.";
"HIDE_FEED_POST" = "Hide posts";
"HIDE_FEED_POST_DESC" = "Hide posts in feed.";
+"HIDE_SURVEYS" = "Hide surveys";
+"HIDE_SURVEYS_DESC" = "Hide video surveys that YouTube shows sometimes.";
"HIDE_SHORTS_SHELF" = "Hide Shorts shelf";
"HIDE_SHORTS_SHELF_DESC" = "Hide Shorts shelf in the feed, including in the Subscriptions tab.";
+"KEEP_SHORTS_SUBSCRIPT" = "Keep Shorts in Subscriptions";
+"KEEP_SHORTS_SUBSCRIPT_DESC" = "Prevents removing Shorts from Subscriptions tab.";
"HIDE_SEARCH_HISTORY" = "Hide search history";
"HIDE_SEARCH_HISTORY_DESC" = "Hide previous search history and suggestions when using the search bar.\nNOTE: Your search history will still be accessible in your other YouTube clients.";
-"HIDE_SUB_BUTTON" = "Hide Subscribe button";
-"HIDE_SUB_BUTTON_DESC" = "Hides the subscribe button in channels and video descriptions.";
-"HIDE_SHOP_BUTTON" = "Hide Shopping button";
-"HIDE_SHOP_BUTTON_DESC" = "Hides the shopping/store button in channels descriptions.";
-"HIDE_MEMBER_BUTTON" = "Hide Membership button";
-"HIDE_MEMBER_BUTTON_DESC" = "Hides the 'Join' button in channels and video descriptions.";
+"HIDE_PLAYABLES" = "Remove playables";
+"HIDE_PLAYABLES_DESC" = "Removes playables (mini-games) from the feed.";
"PLAYER" = "Player";
+"PLAYBACK_SPEED" = "Playback speed";
+"CONTROL_CENTER" = "Control Center";
+"PLAYER_ACTIONS" = "Player actions";
+"GESTURE_HEADER" = "Gestures";
+"QUALITY_WIFI" = "Playback quality on WiFi";
+"QUALITY_WIFI_DESC" = "Automatically applies the selected quality when streaming over Wi-Fi.";
+"QUALITY_CELLULAR" = "Playback quality on Cellular";
+"QUALITY_CELLULAR_DESC" = "Automatically applies the selected quality when streaming over mobile data.";
+"QUALITY_LOW_POWER" = "Playback quality on Power saver mode";
+"QUALITY_LOW_POWER_DESC" = "Automatically applies the selected quality when when Low power mode is enabled on your device.";
+"DEFAULT" = "Default";
+"BEST" = "Best";
+"AUDIO_TRACK" = "Preferred audio track";
+"ORIGINAL" = "Original";
+"SELECT_MANUALLY" = "Select manually";
+"AUDIO_TRACK_SELECT" = "Selected audio track";
+"AUDIO_TRACK_SELECT_DESC" = "Specifies the preferred audio track language to use for playback, if available.";
+"NO_AUTO_DUBBED" = "Uses original audio track";
+"NO_AUTO_DUBBED_DESC" = "Uses original audio track if the preferred language track is auto-dubbed or not found.";
+"CAPTION_TRACK" = "Preferred captions";
+"CAPTION_TRACK_SELECT" = "Selected captions";
+"CAPTION_TRACK_SELECT_DESC" = "Specifies the preferred captions language to display, if available.";
+"DISABLES_CAPTION_TRACK" = "Automatically disable captions";
+"DISABLES_CAPTION_TRACK_DESC" = "Automatically disable captions if the preferred language is auto-generated or not found.";
+"DEFAULT_SPEED" = "Default playback rate";
+"DEFAULT_SPEED_DESC" = "Overrides the default playback speed.";
+"SPEED_BUTTON" = "Speed controls button";
+"SPEED_BUTTON_DESC" = "Displays a speed controls button on overlay.";
+"MUTE_BUTTON" = "Mute button";
+"MUTE_BUTTON_DESC" = "Displays a button to mute or unmute the volume on the overlay.";
+"SHARE_BUTTON" = "Share button";
+"SHARE_BUTTON_DESC" = "Displays a button to share the video link on the overlay.";
+"LOOP_BUTTON" = "Loop button";
+"LOOP_BUTTON_DESC" = "Displays a button to loop the video on the overlay.";
+"CAPTION_BUTTON" = "Captions button";
+"CAPTION_BUTTON_DESC" = "Displays a button to copy the video captions on the overlay.";
+"HOLD_TO_SPEED" = "Hold to speed";
+"HOLD_TO_SPEED_DESC" = "Uses custom hold to speed playback rate.";
"HIDE_AUTOPLAY" = "Hide autoplay switch";
"HIDE_AUTOPLAY_DESC" = "Hide autoplay switch in the video overlay.";
"HIDE_CAPTIONS_BUTTON" = "Hide captions button";
"HIDE_CAPTIONS_BUTTON_DESC" = "Hide captions button in the video overlay.";
"HIDE_CAST_BUTTON_PLAYER" = "Hide cast button";
"HIDE_CAST_BUTTON_PLAYER_DESC" = "Hide cast button in the video overlay.";
-"HIDE_PREV_BUTTON" = "Hide previous/back button";
-"HIDE_PREV_BUTTON_DESC" = "Hide previous/back button in the video overlay.";
-"HIDE_NEXT_BUTTON" = "Hide next/skip button";
-"HIDE_NEXT_BUTTON_DESC" = "Hide next/skip button in the video overlay.";
+"HIDE_NEXT_AND_PREV_BUTTON" = "Hide previous and next buttons";
+"HIDE_NEXT_AND_PREV_BUTTON_DESC" = "Hide previous and next buttons in the video overlay.";
"REPLACE_PREVNEXT_BUTTONS" = "Replace Previous/Next buttons";
"REPLACE_PREVNEXT_BUTTONS_DESC" = "Replaces the Previous and Next video buttons in the player with Rewind and Fast Forward buttons.";
+"SKIP_BACKWARD" = "Skip backward";
+"SKIP_BACKWARD_DESC" = "Replaces the previous track button in Control Center with a skip backward button by a set number of seconds.";
+"SKIP_FORWARD" = "Skip forward";
+"SKIP_FORWARD_DESC" = "Replaces the next track button in Control Center with a skip forward button by a set number of seconds.";
+"REWIND_SECONDS" = "Rewind seconds";
+"FORWARD_SECONDS" = "Fast-forward seconds";
"REMOVE_DARK_OVERLAY" = "Remove dark overlay";
"REMOVE_DARK_OVERLAY_DESC" = "Remove dark overlay from the video overlay.";
"HIDE_END_SCREEN" = "Hide endscreen cards";
@@ -102,6 +224,12 @@
"HIDE_PAID_OVERLAY_DESC" = "Hide 'Includes paid promotion' in the video overlay.";
"HIDE_WATERMARK" = "Hide watermark";
"HIDE_WATERMARK_DESC" = "Hide channel watermark at the bottom of the video overlay.";
+"PAUSE_ON_OVERLAY" = "Pause on overlay";
+"PAUSE_ON_OVERLAY_DESC" = "Sets playback on pause if overlay appears.";
+"DISABLES_SNAP_TO_CHAPTER" = "Disable snap to chapters";
+"DISABLES_SNAP_TO_CHAPTER_DESC" = "Prevents the scrubbar stick to the chapters.";
+"DISABLES_ENGAGE_PANEL" = "Disables engagement panel in fullscreen";
+"DISABLES_ENGAGE_PANEL_DESC" = "Prevent engagement panel showing in fullscreen.";
"GESTURES" = "Enable gestures controls";
"GESTURES_DESC" = "Swipe up or down on the left/right edges of the video screen to adjust brightness, volume, or playback speed.";
"GESTURE_AREA" = "Gesture activation area";
@@ -136,6 +264,8 @@
"DISABLES_SHOW_REMAINING_DESC" = "Disable the feature that shows how much time is left in a video when tapping the timestamp.";
"ALWAYS_SHOW_REMAINING" = "Always show remaining time";
"ALWAYS_SHOW_REMAINING_DESC" = "Ensure the remaining time is always displayed by default in the player overlay.";
+"COPY_TIMESTAMP_ON_PAUSE" = "Copy timestamped links";
+"COPY_TIMESTAMP_ON_PAUSE_DESC" = "Allows to copy timestamped link to the clipboard by pressing pause button.";
"SHOW_REMAINING_EXTRA" = "Show extra remaining time";
"SHOW_REMAINING_EXTRA_DESC" = "Adds the estimated finish time to the label in the video overlay.";
"HIDE_FULLSCREEN_ACTIONS" = "Hide fullscreen actions";
@@ -148,93 +278,95 @@
"HIDE_CONTENT_WARNING_DESC" = "Automatically skip the sensitive content warning alert in some vidoes.";
"AUTO_FULLSCREEN" = "Auto fullscreen";
"AUTO_FULLSCREEN_DESC" = "Automatically enter fullscreen mode when starting video playback.";
-"AUTO_DISABLES_CAPTION" = "Auto disable captions";
-"AUTO_DISABLES_CAPTION_DESC" = "Automatically turns off closed captions (CC) whenever a new video starts playing.";
"PORTRAIT_FULLSCREEN" = "Portrait fullscren mode";
"PORTRAIT_FULLSCREEN_DESC" = "Enables portrait fullscreen mode support.";
"OLD_QUALITY_PICKER" = "Use old quality picker";
"OLD_QUALITY_PICKER_DESC" = "Reverts the video quality selection menu to the classic version (lists all resolutions immediately).";
"EXTRA_SPEED" = "Extra speed options";
"EXTRA_SPEED_DESC" = "Adds more video playback speed options up to 10x.";
-"DISABLE_HINTS" = "Disable hints";
-"DISABLE_HINTS_DESC" = "Disable the tutorial hints and interactive pop-ups.";
"FORCE_MINIPLAYER" = "Force miniplayer";
"FORCE_MINIPLAYER_DESC" = "Allows all videos, including those made for kids, to play in the miniplayer when dismissing the player.";
"FORCE_SEEKBAR" = "Always show progress bar";
"FORCE_SEEKBAR_DESC" = "Makes the progress bar (seekbar) remains visible at all times while in fullscreen mode.";
-"HIDE_LIKE_BUTTON" = "Hide Like button";
-"HIDE_LIKE_BUTTON_DESC" = "Hide Like button in the action bar under the video overlay.";
-"HIDE_DISLIKE_BUTTON" = "Hide Dislike button";
-"HIDE_DISLIKE_BUTTON_DESC" = "Hide Dislike button in the action bar under the video overlay.";
-"HIDE_SHARE_BUTTON" = "Hide Share button";
-"HIDE_SHARE_BUTTON_DESC" = "Hide Share button in the action bar under the video overlay.";
-"HIDE_DOWNLOAD_BUTTON" = "Hide Download button";
-"HIDE_DOWNLOAD_BUTTON_DESC" = "Hide Download button in the action bar under the video overlay.";
-"HIDE_CLIP_BUTTON" = "Hide Clip button";
-"HIDE_CLIP_BUTTON_DESC" = "Hide Clip button in the action bar under the video overlay.";
-"HIDE_REMIX_BUTTON" = "Hide Remix button";
-"HIDE_REMIX_BUTTON_DESC" = "Hide Remix button in the action bar under the video overlay.";
-"HIDE_SAVE_BUTTON" = "Hide Save button";
-"HIDE_SAVE_BUTTON_DESC" = "Hide Save button in the action bar under the video overlay.";
+"TAP_TO_SEEK" = "Tap to seek";
+"TAP_TO_SEEK_DESC" = "Quickly jump to any point by tapping the progress bar.";
+"DISABLES_ZOOM" = "Disable free zoom gesture";
+"DISABLES_ZOOM_DESC" = "Disables new free zoom gestures.";
+"USES_24_HOURS_TIME" = "24-hour format";
+"USES_24_HOURS_TIME_DESC" = "Shows end time in 24-hour format.";
+"PAUSE_TWO_FINGERS" = "Pause using two fingers";
+"PAUSE_TWO_FINGERS_DESC" = "Pause/Resume playback by tapping on the screen using two fingers.";
+"HIDE_COMMENTS_SECTION" = "Hide comments section";
+"HIDE_COMMENTS_SECTION_DESC" = "Completely removes the entire comments section.";
+"HIDE_COMMENTS_PREVIEW" = "Hide preview comments";
+"HIDE_COMMENTS_PREVIEW_DESC" = "Removes the preview box for the top comment while keeping the full section expandable.";
+"USE_ANOTHER_MINIPLAYER" = "Uses another miniplayer style";
+"USE_ANOTHER_MINIPLAYER_DESC" = "Uses another miniplayer style redesigned from older YT versions.";
"SHORTS" = "Shorts";
-"HIDE_SHORTS_LIKE_BUTTON" = "Hide Like button";
-"HIDE_SHORTS_LIKE_BUTTON_DESC" = "Hide the Like button in the Shorts player.";
-"HIDE_SHORTS_DISLIKE_BUTTON" = "Hide Dislike button";
-"HIDE_SHORTS_DISLIKE_BUTTON_DESC" = "Hide the Dislike button in the Shorts player.";
-"HIDE_SHORTS_COMMENT_BUTTON" = "Hide Comment button";
-"HIDE_SHORTS_COMMENT_BUTTON_DESC" = "Hide the Comment button in the Shorts player.";
-"HIDE_SHORTS_SHARE_BUTTON" = "Hide Share button";
-"HIDE_SHORTS_SHARE_BUTTON_DESC" = "Hide the Share button in the Shorts player.";
-"HIDE_SHORTS_REMIX_BUTTON" = "Hide Remix button";
-"HIDE_SHORTS_REMIX_BUTTON_DESC" = "Hide the Remix button in the Shorts player.";
-"HIDE_METADATA_BUTTON" = "Hide sound metadata button";
-"HIDE_METADATA_BUTTON_DESC" = "Hide the button that shows the sound source or music metadata in the Shorts player.";
+"SHORTS_ONLY" = "Shorts only mode";
+"SHORTS_ONLY_DESC" = "Limits YouTube functionality to viewing Shorts only.";
+"SHORTS_ACTION" = "Shorts action after the video ends";
+"LOOP" = "Loop video";
+"SKIP_TO_NEXT_SHORTS" = "Skip to next video";
+"PAUSE_SHORTS" = "Stop video";
+"REMOVE_LIVE_SHORTS" = "Remove live videos";
+"REMOVE_LIVE_SHORTS_DESC" = "Removes live videos from the feed.";
"HIDE_SHORTS_PRODUCT" = "Hide products";
"HIDE_SHORTS_PRODUCT_DESC" = "Hide tagged products and shopping links in the Shorts overlay.";
"HIDE_SHORTS_RECBAR" = "Hide recommendation bar";
"HIDE_SHORTS_RECBAR_DESC" = "Hide the suggested action bar in the Shorts player (e.g. Try this sound, Shopping, Preview comment).";
-"HIDE_SHORTS_COMMIT" = "Hide commissions bar";
-"HIDE_SHORTS_COMMIT_DESC" = "Hide the bar that shows the video got commissions in the Shorts player.";
-"HIDE_SHORTS_SUBSCRIPT_BUTTON" = "Hide subscriptions button";
-"HIDE_SHORTS_SUBSCRIPT_BUTTON_DESC" = "Hide the Subscriptions button at the top of the Shorts player.";
-"HIDE_SHORTS_LIVE_BUTTON" = "Hide live button";
-"HIDE_SHORTS_LIVE_BUTTON_DESC" = "Hide the Live button at the top of the Shorts player.";
-"HIDE_SHORTS_LENS_BUTTON" = "Hide lens button";
-"HIDE_SHORTS_LENS_BUTTON_DESC" = "Hide the Lens button at the top of the Shorts player.";
-"HIDE_SHORTS_TRENDS_BUTTON" = "Hide trends button";
-"HIDE_SHORTS_TRENDS_BUTTON_DESC" = "Hide the Trends button at the top of the Shorts player.";
-"HIDE_SHORTS_TO_VIDEO" = "Hide link to full video";
-"HIDE_SHORTS_TO_VIDEO_DESC" = "Hide the link that redirects in a Short to a full video in the Shorts player.";
"ENABLES_SHORTS_QUALITY" = "Enable Shorts quality picker";
"ENABLES_SHORTS_QUALITY_DESC" = "Force enable the video quality selection menu in Shorts.";
"SHOW_SHORTS_SEEKBAR" = "Always show seekbar";
"SHOW_SHORTS_SEEKBAR_DESC" = "Ensure the progress/seek bar is always visible at the bottom of the Shorts player.";
+"REMOVE_POSTS_SHORTS" = "Remove posts";
+"REMOVE_POSTS_SHORTS_DESC" = "Remove posts from the feed.";
+"SHORTS_FULLSCREEN" = "Pinch to Fullscreen";
+"SHORTS_FULLSCREEN_DESC" = "Manages visibility of the overlay with pinch in and pinch out gestures, displaying Shorts in fullscreen mode.";
"TABBAR" = "Tab bar";
"DEFAULT_TAB" = "Default startup tab";
-"DEFAULT_TAB_DESC" = "You can select the tab you want to open when opening the app.";
-"HOME_NAME" = "Home";
-"SUBSCRIPT_NAME" = "Subscriptions";
-"LIB_NAME" = "Library (You)";
+"FORSTED_TAB_BAR" = "Translucent tab bar";
+"ENABLED" = "Enabled";
"HIDE_TAB_INDI" = "Hide tab indicators";
"HIDE_TAB_INDI_DESC" = "Hide all tab indicators in the tab bar.";
"HIDE_TAB_LABELS" = "Hide tab labels";
"HIDE_TAB_LABELS_DESC" = "Hide all tab labels in the tab bar.";
-"HIDE_HOME_TAB" = "Hide Home tab";
-"HIDE_HOME_TAB_DESC" = "Hide Home tab in the tab bar.";
-"HIDE_SHORTS_TAB" = "Hide Shorts tab";
-"HIDE_SHORTS_TAB_DESC" = "Hide Shorts tab in the tab bar.";
-"HIDE_CREATE_BUTTON" = "Hide Create button";
-"HIDE_CREATE_BUTTON_DESC" = "Hide Create button in the tab bar.";
-"HIDE_SUBSCRIPT_TAB" = "Hide Subscriptions tab";
-"HIDE_SUBSCRIPT_TAB_DESC" = "Hide Subscriptions tab in the tab bar.";
+"HOME_TAB" = "Home";
+"SHORTS_TAB" = "Shorts";
+"CREATE_TAB" = "Create";
+"SUBSCRIPTIONS_TAB" = "Subscriptions";
+"LIBRARY_TAB" = "Library (You)";
+"HISTORY_TAB" = "History";
+"GAMING_TAB" = "Gaming";
+"SPORTS_TAB" = "Sports";
+"NOTI_TAB" = "Notifications";
+"NEWS_TAB" = "News";
+"MUSIC_TAB" = "Music";
+"WATCH_LATER_TAB" = "Watch later";
+"PLAYLIST_TAB" = "Playlists";
+"LIKE_TAB" = "Liked videos";
+"LIVE_TAB" = "Live";
+"POST_TAB" = "Posts";
+"VIDEO_TAB" = "Your videos";
+"MOVIE_TAB" = "Movies";
+"COURSE_TAB" = "Courses";
+"MINIGAME_TAB" = "Playables";
+"MANAGE_TABS" = "Manage tabs";
+"MANAGE_TABS_DESC" = "Reorder and toggle tabs";
+"TAB_REORDER_HINT" = "Drag to reorder. Toggle to show/hide.\nTab settings are also accessible by long-pressing the first tab in the tab bar.";
+"TAB_LIMIT" = "Tab limit";
+"TAB_LIMIT_DESC" = "Only 6 tabs are allowed.";
+"ZERO_TAB_DESC" = "At least 1 tab has to be enabled.";
"MISCELLANEOUS" = "Miscellaneous";
"BACKGROUND_PLAYBACK" = "Background playback";
"BACKGROUND_PLAYBACK_DESC" = "Enables background playback for videos.";
"DISABLES_SHORTS_PIP" = "Disables Shorts PiP";
"DISABLES_SHORTS_PIP_DESC" = "Try to disables Shorts PiP, this might not 100% disables it.";
+"DISABLE_HINTS" = "Disable hints";
+"DISABLE_HINTS_DESC" = "Disable the tutorial hints and interactive pop-ups.";
"BLOCK_UPGRADE_DIALOGS" = "Block upgrade dialogs";
"BLOCK_UPGRADE_DIALOGS_DESC" = "Block the YouTube dialogs that ask you to upgrade the app.";
"ARE_YOU_THERE_DIALOG" = "Hide 'Are you there?' dialog";
@@ -247,7 +379,109 @@
"DISABLES_SNACK_BAR_DESC" = "Disables snack bar when pressing some buttons or doing something in the app.";
"HIDE_STARTUP_ANIMATIONS" = "Hide YouTube's startup animations";
"HIDE_STARTUP_ANIMATIONS_DESC" = "Hide YouTube's startup animations when opening the app.";
-"HIDE_PLAY_IN_NEXT_QUEUE" = "Hide 'Play next in queue'";
-"HIDE_PLAY_IN_NEXT_QUEUE_DESC" = "Hide the 'Play next in queue' option in the flyout menu.";
"HIDE_LIKE_DISLIKE_VOTES" = "Hide like/dislike votes";
-"HIDE_LIKE_DISLIKE_VOTES_DESC" = "Hide messages that pop up when you like/dislike videos.";
\ No newline at end of file
+"HIDE_LIKE_DISLIKE_VOTES_DESC" = "Hide messages that pop up when you like/dislike videos.";
+"FLYOUT_MENU" = "Flyout menu";
+"REMOVE_PLAY_IN_NEXT_QUEUE_OPTION" = "Removes 'Play next in queue' option";
+"REMOVE_PLAY_IN_NEXT_QUEUE_OPTION_DESC" = "Removes 'Play next in queue' option from the flyout menu.";
+"REMOVE_DOWNLOAD_OPTION" = "Removes 'Download video' option";
+"REMOVE_DOWNLOAD_OPTION_DESC" = "Removes 'Download video' option from the flyout menu.";
+"REMOVE_WATCH_LATER_OPTION" = "Removes 'Save to Watch later' option";
+"REMOVE_WATCH_LATER_OPTION_DESC" = "Removes 'Save to Watch Later' option from the flyout menu.";
+"REMOVE_SAVE_OPTION" = "Removes 'Save to playlist' option";
+"REMOVE_SAVE_OPTION_DESC" = "Removes 'Save to playlist' option from the flyout menu.";
+"REMOVE_REMOVE_FROM_PLAYLIST_OPTION" = "Removes 'Remove from playlist' option";
+"REMOVE_REMOVE_FROM_PLAYLIST_OPTION_DESC" = "Removes 'Remove from playlist' option from the flyout menu.";
+"REMOVE_SHARE_OPTION" = "Removes 'Share' option";
+"REMOVE_SHARE_OPTION_DESC" = "Removes 'Share' option from the flyout menu.";
+"REMOVE_NOT_INTERESTED_OPTION" = "Removes 'Not interested' option";
+"REMOVE_NOT_INTERESTED_OPTION_DESC" = "Removes 'Not interested' option from the flyout menu.";
+"REMOVE_DONT_RECOMMEND_OPTION" = "Removes 'Don't recommend channel' option";
+"REMOVE_DONT_RECOMMEND_OPTION_DESC" = "Removes 'Don't recommend channel' option from the flyout menu.";
+"REMOVE_INFO_OPTION" = "Removes 'Why are you seeing this?' option";
+"REMOVE_INFO_OPTION_DESC" = "Removes 'Why are you seeing this?' option from the flyout menu.";
+"REMOVE_FILTER_OPTION" = "Removes 'Search filters' option";
+"REMOVE_FILTER_OPTION_DESC" = "Removes 'Search filters' option from the flyout menu.";
+"REMOVE_REPORT_OPTION" = "Removes 'Report' option";
+"REMOVE_REPORT_OPTION_DESC" = "Removes 'Report' option from the flyout menu.";
+"REMOVE_YOUTUBE_MUSIC_OPTION" = "Removes 'Listen with YouTube Music' option";
+"REMOVE_YOUTUBE_MUSIC_OPTION_DESC" = "Removes 'Listen with YouTube Music' option from the flyout menu.";
+"REMOVE_FEED_BACK_OPTION" = "Removes 'Send feedback' option";
+"REMOVE_FEED_BACK_OPTION_DESC" = "Removes 'Send feedback' option from the flyout menu.";
+"REMOVE_CAST_OPTION" = "Removes 'Watch on TV' option";
+"REMOVE_CAST_OPTION_DESC" = "Removes 'Watch on TV' option from the flyout menu.";
+"REMOVE_SHUFFLE_OPTION" = "Removes 'Shuffle' option";
+"REMOVE_SHUFFLE_OPTION_DESC" = "Removes 'Shuffle' option from the flyout menu.";
+"REMOVE_UN_SUB_OPTION" = "Removes 'Unsubscribe' option";
+"REMOVE_UN_SUB_OPTION_DESC" = "Removes 'Unsubscribe' option from the flyout menu.";
+"REMOVE_HIDE_FROM_PLAYLIST_OPTION" = "Removes 'Hide video from playlist' option";
+"REMOVE_HIDE_FROM_PLAYLIST_OPTION_DESC" = "Removes 'Hide video from playlist' option from the flyout menu.";
+"REMOVE_HELP_OPTION" = "Removes 'Help & feedback' option";
+"REMOVE_HELP_OPTION_DESC" = "Removes 'Help & feedback' option from the flyout menu.";
+"REMOVE_NOTIFY_OPTION" = "Removes 'Notify me' option";
+"REMOVE_NOTIFY_OPTION_DESC" = "Removes 'Notify me' option from the flyout menu.";
+"REMOVE_CLEARSCREEN_OPTION" = "Removes 'Clear Screen' option";
+"REMOVE_CLEARSCREEN_OPTION_DESC" = "Removes 'Clear Screen' option from the flyout menu.";
+"HIDE_COMMU_GUIDE" = "Hide community guidelines";
+"HIDE_COMMU_GUIDE_DESC" = "Hide community guidelines header.";
+"DISABLES_RTL" = "Disable RTL formatting";
+"DISABLES_RTL_DESC" = "Forcefully displays text in left-to-right (LTR) format for languages that are initially displayed in right-to-left (RTL).";
+"DEVICE_UI" = "Interface style";
+
+"SB_DESCRIPTION" = "Skip sponsored segments automatically using community-submitted data.";
+"SB_ENABLE" = "Enable SponsorBlock";
+"SB_ENABLE_DESC" = "Activate the SponsorBlock extension to skip sponsored segments.";
+"SB_SHOW_BUTTON" = "Show overlay button";
+"SB_SHOW_BUTTON_DESC" = "Display a SponsorBlock toggle button in the player overlay.";
+"SB_SHOW_NOTIFICATIONS" = "Show notifications";
+"SB_SHOW_NOTIFICATIONS_DESC" = "Display notifications when segments are skipped. Notifications for manual-skip categories are always shown.";
+"SB_HAPTIC_FEEDBACK" = "Haptic feedback";
+"SB_HAPTIC_FEEDBACK_DESC" = "Provide haptic feedback when a segment is skipped.";
+"SB_SEGMENTS_IN_PLAYER" = "Segments in player";
+"SB_SEGMENTS_IN_PLAYER_DESC" = "Display colored segments on the progress bar of the main video player.";
+"SB_SEGMENTS_IN_FEED" = "Segments in feed";
+"SB_SEGMENTS_IN_FEED_DESC" = "Display colored segments on the progress bar of players in the feed.";
+"SB_SEGMENTS_IN_MINIPLAYER" = "Segments in mini-player";
+"SB_SEGMENTS_IN_MINIPLAYER_DESC" = "Display colored segments on the progress bar of the mini-player.";
+"SB_SHOW_DURATION" = "Show duration without segments";
+"SB_SHOW_DURATION_DESC" = "Display the total video duration excluding skippable segments.";
+"SB_CATEGORIES_HEADER" = "Segment categories";
+"SB_SEGMENT_COLOR" = "Segment color";
+
+"SB_ACTION_DISABLE" = "Disable";
+"SB_ACTION_AUTO_SKIP" = "Auto-skip";
+"SB_ACTION_ASK" = "Ask before skipping";
+"SB_ACTION_DISPLAY" = "Show on seek bar only";
+"SB_ACTION_SKIP_TO" = "Skip to segment";
+
+"SB_CAT_sponsor" = "Sponsor";
+"SB_CAT_sponsor_DESC" = "Paid promotion for a product or service not directly related to the creator.";
+"SB_CAT_intro" = "Intro";
+"SB_CAT_intro_DESC" = "Intermission or intro animation typically seen at the start of a video.";
+"SB_CAT_outro" = "Endcards";
+"SB_CAT_outro_DESC" = "Endcards, credits, or outros at the end of the video.";
+"SB_CAT_interaction" = "Interaction";
+"SB_CAT_interaction_DESC" = "Reminders to like, subscribe, or follow on social media.";
+"SB_CAT_selfpromo" = "Self-promotion";
+"SB_CAT_selfpromo_DESC" = "Unpaid promotion of the creator's own products, merchandise, or other content.";
+"SB_CAT_music_offtopic" = "Non-music";
+"SB_CAT_music_offtopic_DESC" = "Non-music sections in music videos (e.g. talking, introductions).";
+"SB_CAT_preview" = "Preview";
+"SB_CAT_preview_DESC" = "Recap or preview of content from another video or previous episode.";
+"SB_CAT_hook" = "Hook/Greetings";
+"SB_CAT_hook_DESC" = "Narrated trailers for the upcoming video, greetings and goodbyes.";
+"SB_CAT_poi_highlight" = "Highlight";
+"SB_CAT_poi_highlight_DESC" = "The key moment or highlight point of the video. Use 'Skip to' to jump directly to it.";
+"SB_CAT_filler" = "Filler";
+"SB_CAT_filler_DESC" = "Tangential content, jokes, or filler that is not directly related to the main topic.";
+
+"SB_SKIPPED" = "%@ segment has been skipped";
+"SB_DETECTED" = "%@ segment detected.\nWould you like to skip the segment?";
+"SB_SKIP_NOW" = "Skip";
+"SB_UNSKIP" = "Unskip";
+"SB_JUMPED_TO_HIGHLIGHT" = "Jumped to highlight";
+"SB_JUMP_TO_HIGHLIGHT" = "Highlight available. Jump to the point?";
+"SB_SEGMENT_COLOR_SUFFIX" = "Segment color";
+"SB_SECTION_MAIN" = "Main";
+"SB_SKIP_ALERT_DURATION" = "Skip alert duration";
+"SB_UNSKIP_ALERT_DURATION" = "Unskip alert duration";
\ No newline at end of file
diff --git a/layout/Library/Application Support/YouMod.bundle/pt.lproj/Localizable.strings b/layout/Library/Application Support/YouMod.bundle/pt.lproj/Localizable.strings
index bccaf550..2c1a8a0f 100644
--- a/layout/Library/Application Support/YouMod.bundle/pt.lproj/Localizable.strings
+++ b/layout/Library/Application Support/YouMod.bundle/pt.lproj/Localizable.strings
@@ -1,11 +1,18 @@
-"NOTE" = "NOTE: Some hiding features may leave a blank box.";
"SETTINGS" = "Settings";
+"INTERFACE" = "Interface";
+
+"LOOP_DISABLED" = "Loop disabled";
+"LOOP_ENABLED" = "Loop enabled";
"NEW_ISSUES" = "Report issues or request new features";
"NEW_ISSUES_DESC" = "Found bugs or want to request new features? Tap here.";
"SOURCE_CODES" = "Source codes";
"SOURCE_CODES_DESC" = "Take a look at the codes!";
+"EXTRA" = "Extra";
+"FIX_PLAYBACK_ISSUES" = "Fix playback issues (Temporary)";
+"FIX_PLAYBACK_ISSUES_DESC" = "Try to fix video playback issues for some users. Turning this on will make the player automatically reloads itself everytime it errors or fails to play the video.";
+
"PERFER_HEADER" = "YouMod perferences";
"PERFER" = "Perferences";
"IMPORT" = "Import settings";
@@ -24,6 +31,7 @@
"ERROR_NO_KEYS_IMPORT" = "No available keys to import";
"ERROR_NO_KEYS_EXPORT" = "No available keys to export";
"OVERRIDE" = "This will override the current settings. Do you want to continue?";
+"DISABLED" = "Disabled";
"CACHE" = "Cache management";
"CLEARCACHE" = "Clear cache";
@@ -32,12 +40,80 @@
"AUTO_CLEARCACHE_DESC" = "Automatically clears the app cache on startup.";
"DOWNLOADING" = "Downloading";
-"DOWNLOAD_MANAGER" = "Download Manager";
-"DOWNLOAD_MANAGER_DESC" = "Replaces YouTube's video downloader with YouMod one. With ablity to downloading videos, audios, captions, and thumbnails";
+"DOWNLOAD_MANAGER" = "Download manager";
+"DOWNLOAD_MANAGER_DESC" = "Replaces YouTube's video downloader with YouMod one. With ablity to downloading videos, audios, captions, and thumbnails.";
"DOWNLOAD_SAVE_PHOTOS" = "Automatically save to Photos";
"DOWNLOAD_SAVE_PHOTOS_DESC" = "Automatically saves downloaded videos and images directly to your Photos app.";
-"DOWNLOAD_DRC_AUDIO" = "Prefer DRC Audio";
-"DOWNLOAD_DRC_AUDIO_DESC" = "Prioritizes downloading audio tracks with Dynamic Range Compression for more consistent volume levels.";
+"ADD_SHORTS_DOWNLOAD" = "Adds a download button to Shorts";
+"ADD_SHORTS_DOWNLOAD_DESC" = "Adds a download button to Shorts video overlay.";
+"USE_ORIG_AUDIO" = "Automatically uses original audio track";
+"USE_ORIG_AUDIO_DESC" = "Automatically uses original audio track instead of showing a dialog.";
+"DOWNLOAD_SHORTS" = "Download Shorts";
+"DOWNLOAD_SHORTS_DESC" = "Choose Shorts quality";
+"COPY_VID_INFO" = "Copy video informations";
+"COPY_VID_INFO_DESC" = "Choose what to copy";
+"COPY_ALL_VID_INFO" = "Copy all";
+"COPY_ALL_VID_INFO_DESC" = "Copy all video informations";
+"COPY_AUTHOR" = "Copy author";
+"COPY_AUTHOR_DESC" = "Copy the video author";
+"COPY_TITLE" = "Copy title";
+"COPY_TITLE_DESC" = "Copy the video title";
+"COPY_DESCRIPTION" = "Copy description";
+"COPY_DESCRIPTION_DESC" = "Copy the video description";
+"COPY_URL" = "Copy video link";
+"COPY_URL_TIMESTAMP" = "Copy video link with timestamp";
+"URL_COPIED" = "Copied video link";
+"URL_TIMESTAMP_COPIED" = "Copied video link with timestamp";
+"ERROR_VIDEOID" = "Cannot get the video ID";
+"ERROR_ADS" = "Cannot copy video link while playing ads";
+"COPIED_AUTHOR" = "Copied video author";
+"COPIED_TITLE" = "Copied video title";
+"COPIED_DESCRIPTION" = "Copied video description";
+"SAVE_THUMBNAIL" = "Save thumbnail";
+"SAVE_THUMBNAIL_DESC" = "Save to Photos";
+"DOWNLOAD_CAPTIONS" = "Download captions";
+"DOWNLOAD_CAPTIONS_DESC" = "Save subtitles as .VTT file";
+"DOWNLOAD_AUDIO" = "Download audio";
+"DOWNLOAD_AUDIO_DESC" = "Download audio as .m4a file";
+"DOWNLOAD_VIDEO" = "Download video";
+"DOWNLOAD_VIDEO_DESC" = "Choose video quality";
+"OPEN_VID_BEFORE" = "Open a video before using the download manager";
+"NO_CAPTIONS_URL" = "No valid caption URLs found";
+"NO_AUDIO_URL" = "No audio URL found";
+"NO_STREAM_URL" = "No stream URL found";
+"CAPTIONS_FAILED" = "Failed to download captions";
+"NO_CAPTIONS" = "No captions available for this video";
+"NO_AUDIO_STREAM_FOUND" = "No audio streams found";
+"NO_VID_AUDIO_STREAM_FOUND" = "No video/audio streams found";
+"COPIED_VID_INFO" = "Copied all video informations";
+"SAVED_TO_PHOTOS" = "Saved to Photos";
+"SAVE_FAILED" = "Save failed";
+"PHOTO_ACCESS_DENINED" = "Photos access denied";
+"THUMBNAIL_FAILED" = "Thumbnail failed";
+"DOWNLOADING_THUMBNAIL" = "Downloading thumbnail...";
+"DOWNLOADING_AUDIO" = "Downloading audio...";
+"DOWNLOADING_VIDEO" = "Downloading video...";
+"DOWNLOADING_CAPTIONS" = "Downloading captions...";
+"NO_THUMBNAIL_FOUND" = "No thumbnail found";
+"DOWNLOAD_FAILED" = "Download failed";
+"COPY_THUMBNAIL" = "Copy thumbnail";
+"COPY_THUMBNAIL_DESC" = "Copy to clipboard";
+"SHOW_THUMBNAIL" = "Show thumbnail";
+"SHOW_THUMBNAIL_DESC" = "Show in a dialog";
+"THUMBNAIL_OPTIONS" = "Download thumbnail";
+"THUMBNAIL_OPTIONS_DESC" = "Show more options";
+"COPIED_TO_CLIPBOARD" = "Copied to clipboard";
+"DOWNLOAD_COMPLETED" = "Download completed";
+"CANNOT_SAVE_TO_PHOTOS" = "Cannot save to Photos";
+"FINA_VIDEO" = "Finalizing video...";
+"MERGING_VID_FALLBACK" = "Merging video with fallback...";
+"MERGING_VID" = "Merging video...";
+"CONVERTING" = "Converting to";
+"ALREADY_DOWNLOADING" = "Already downloading";
+"DOWNLOAD_CANCELLED" = "Download cancelled";
+"CANCEL" = "Cancel";
+"TRIMMING_AUDIO" = "Trimming audio...";
+"COPY" = "Copy";
"APPEARANCE" = "Appearance";
"OLED_THEME" = "OLED dark mode";
@@ -46,6 +122,8 @@
"OLED_KEYBOARD_DESC" = "Make the keyboard uses a pure black (OLED) color for dark theme.";
"NAVBAR" = "Navigation bar";
+"STICKY_NAVBAR" = "Sticky Navigation bar";
+"STICKY_NAVBAR_DESC" = "Pins the Navigation bar so that it remains visible when scrolling down.";
"HIDE_YT_LOGO" = "Hide YouTube logo";
"HIDE_YT_LOGO_DESC" = "Hide YouTube logo in the navigation bar.";
"PREMIUM_LOGO" = "Use YouTube Premium logo";
@@ -62,34 +140,78 @@
"FEED" = "Feed";
"HIDE_SUBBAR" = "Hide subbar";
"HIDE_SUBBAR_DESC" = "Hide subbar at the top of the feed.";
+"HIDE_HORI_SHELF" = "Remove horizontal feeds";
+"HIDE_HORI_SHELF_DESC" = "Removes all horizontal feeds (News, Continue Watching, Posts collection, etc.) from the feed, except Playables.";
"HIDE_MUSIC_PLAYLISTS" = "Hide music playlist generator";
"HIDE_MUSIC_PLAYLISTS_DESC" = "Hide music playlist generator (Select music tunes in three) in the feed.";
"HIDE_FEED_POST" = "Hide posts";
"HIDE_FEED_POST_DESC" = "Hide posts in feed.";
+"HIDE_SURVEYS" = "Hide surveys";
+"HIDE_SURVEYS_DESC" = "Hide video surveys that YouTube shows sometimes.";
"HIDE_SHORTS_SHELF" = "Hide Shorts shelf";
"HIDE_SHORTS_SHELF_DESC" = "Hide Shorts shelf in the feed, including in the Subscriptions tab.";
+"KEEP_SHORTS_SUBSCRIPT" = "Keep Shorts in Subscriptions";
+"KEEP_SHORTS_SUBSCRIPT_DESC" = "Prevents removing Shorts from Subscriptions tab.";
"HIDE_SEARCH_HISTORY" = "Hide search history";
"HIDE_SEARCH_HISTORY_DESC" = "Hide previous search history and suggestions when using the search bar.\nNOTE: Your search history will still be accessible in your other YouTube clients.";
-"HIDE_SUB_BUTTON" = "Hide Subscribe button";
-"HIDE_SUB_BUTTON_DESC" = "Hides the subscribe button in channels and video descriptions.";
-"HIDE_SHOP_BUTTON" = "Hide Shopping button";
-"HIDE_SHOP_BUTTON_DESC" = "Hides the shopping/store button in channels descriptions.";
-"HIDE_MEMBER_BUTTON" = "Hide Membership button";
-"HIDE_MEMBER_BUTTON_DESC" = "Hides the 'Join' button in channels and video descriptions.";
+"HIDE_PLAYABLES" = "Remove playables";
+"HIDE_PLAYABLES_DESC" = "Removes playables (mini-games) from the feed.";
"PLAYER" = "Player";
+"PLAYBACK_SPEED" = "Playback speed";
+"CONTROL_CENTER" = "Control Center";
+"PLAYER_ACTIONS" = "Player actions";
+"GESTURE_HEADER" = "Gestures";
+"QUALITY_WIFI" = "Playback quality on WiFi";
+"QUALITY_WIFI_DESC" = "Automatically applies the selected quality when streaming over Wi-Fi.";
+"QUALITY_CELLULAR" = "Playback quality on Cellular";
+"QUALITY_CELLULAR_DESC" = "Automatically applies the selected quality when streaming over mobile data.";
+"QUALITY_LOW_POWER" = "Playback quality on Power saver mode";
+"QUALITY_LOW_POWER_DESC" = "Automatically applies the selected quality when when Low power mode is enabled on your device.";
+"DEFAULT" = "Default";
+"BEST" = "Best";
+"AUDIO_TRACK" = "Preferred audio track";
+"ORIGINAL" = "Original";
+"SELECT_MANUALLY" = "Select manually";
+"AUDIO_TRACK_SELECT" = "Selected audio track";
+"AUDIO_TRACK_SELECT_DESC" = "Specifies the preferred audio track language to use for playback, if available.";
+"NO_AUTO_DUBBED" = "Uses original audio track";
+"NO_AUTO_DUBBED_DESC" = "Uses original audio track if the preferred language track is auto-dubbed or not found.";
+"CAPTION_TRACK" = "Preferred captions";
+"CAPTION_TRACK_SELECT" = "Selected captions";
+"CAPTION_TRACK_SELECT_DESC" = "Specifies the preferred captions language to display, if available.";
+"DISABLES_CAPTION_TRACK" = "Automatically disable captions";
+"DISABLES_CAPTION_TRACK_DESC" = "Automatically disable captions if the preferred language is auto-generated or not found.";
+"DEFAULT_SPEED" = "Default playback rate";
+"DEFAULT_SPEED_DESC" = "Overrides the default playback speed.";
+"SPEED_BUTTON" = "Speed controls button";
+"SPEED_BUTTON_DESC" = "Displays a speed controls button on overlay.";
+"MUTE_BUTTON" = "Mute button";
+"MUTE_BUTTON_DESC" = "Displays a button to mute or unmute the volume on the overlay.";
+"SHARE_BUTTON" = "Share button";
+"SHARE_BUTTON_DESC" = "Displays a button to share the video link on the overlay.";
+"LOOP_BUTTON" = "Loop button";
+"LOOP_BUTTON_DESC" = "Displays a button to loop the video on the overlay.";
+"CAPTION_BUTTON" = "Captions button";
+"CAPTION_BUTTON_DESC" = "Displays a button to copy the video captions on the overlay.";
+"HOLD_TO_SPEED" = "Hold to speed";
+"HOLD_TO_SPEED_DESC" = "Uses custom hold to speed playback rate.";
"HIDE_AUTOPLAY" = "Hide autoplay switch";
"HIDE_AUTOPLAY_DESC" = "Hide autoplay switch in the video overlay.";
"HIDE_CAPTIONS_BUTTON" = "Hide captions button";
"HIDE_CAPTIONS_BUTTON_DESC" = "Hide captions button in the video overlay.";
"HIDE_CAST_BUTTON_PLAYER" = "Hide cast button";
"HIDE_CAST_BUTTON_PLAYER_DESC" = "Hide cast button in the video overlay.";
-"HIDE_PREV_BUTTON" = "Hide previous/back button";
-"HIDE_PREV_BUTTON_DESC" = "Hide previous/back button in the video overlay.";
-"HIDE_NEXT_BUTTON" = "Hide next/skip button";
-"HIDE_NEXT_BUTTON_DESC" = "Hide next/skip button in the video overlay.";
+"HIDE_NEXT_AND_PREV_BUTTON" = "Hide previous and next buttons";
+"HIDE_NEXT_AND_PREV_BUTTON_DESC" = "Hide previous and next buttons in the video overlay.";
"REPLACE_PREVNEXT_BUTTONS" = "Replace Previous/Next buttons";
"REPLACE_PREVNEXT_BUTTONS_DESC" = "Replaces the Previous and Next video buttons in the player with Rewind and Fast Forward buttons.";
+"SKIP_BACKWARD" = "Skip backward";
+"SKIP_BACKWARD_DESC" = "Replaces the previous track button in Control Center with a skip backward button by a set number of seconds.";
+"SKIP_FORWARD" = "Skip forward";
+"SKIP_FORWARD_DESC" = "Replaces the next track button in Control Center with a skip forward button by a set number of seconds.";
+"REWIND_SECONDS" = "Rewind seconds";
+"FORWARD_SECONDS" = "Fast-forward seconds";
"REMOVE_DARK_OVERLAY" = "Remove dark overlay";
"REMOVE_DARK_OVERLAY_DESC" = "Remove dark overlay from the video overlay.";
"HIDE_END_SCREEN" = "Hide endscreen cards";
@@ -102,6 +224,12 @@
"HIDE_PAID_OVERLAY_DESC" = "Hide 'Includes paid promotion' in the video overlay.";
"HIDE_WATERMARK" = "Hide watermark";
"HIDE_WATERMARK_DESC" = "Hide channel watermark at the bottom of the video overlay.";
+"PAUSE_ON_OVERLAY" = "Pause on overlay";
+"PAUSE_ON_OVERLAY_DESC" = "Sets playback on pause if overlay appears.";
+"DISABLES_SNAP_TO_CHAPTER" = "Disable snap to chapters";
+"DISABLES_SNAP_TO_CHAPTER_DESC" = "Prevents the scrubbar stick to the chapters.";
+"DISABLES_ENGAGE_PANEL" = "Disables engagement panel in fullscreen";
+"DISABLES_ENGAGE_PANEL_DESC" = "Prevent engagement panel showing in fullscreen.";
"GESTURES" = "Enable gestures controls";
"GESTURES_DESC" = "Swipe up or down on the left/right edges of the video screen to adjust brightness, volume, or playback speed.";
"GESTURE_AREA" = "Gesture activation area";
@@ -136,6 +264,8 @@
"DISABLES_SHOW_REMAINING_DESC" = "Disable the feature that shows how much time is left in a video when tapping the timestamp.";
"ALWAYS_SHOW_REMAINING" = "Always show remaining time";
"ALWAYS_SHOW_REMAINING_DESC" = "Ensure the remaining time is always displayed by default in the player overlay.";
+"COPY_TIMESTAMP_ON_PAUSE" = "Copy timestamped links";
+"COPY_TIMESTAMP_ON_PAUSE_DESC" = "Allows to copy timestamped link to the clipboard by pressing pause button.";
"SHOW_REMAINING_EXTRA" = "Show extra remaining time";
"SHOW_REMAINING_EXTRA_DESC" = "Adds the estimated finish time to the label in the video overlay.";
"HIDE_FULLSCREEN_ACTIONS" = "Hide fullscreen actions";
@@ -148,93 +278,95 @@
"HIDE_CONTENT_WARNING_DESC" = "Automatically skip the sensitive content warning alert in some vidoes.";
"AUTO_FULLSCREEN" = "Auto fullscreen";
"AUTO_FULLSCREEN_DESC" = "Automatically enter fullscreen mode when starting video playback.";
-"AUTO_DISABLES_CAPTION" = "Auto disable captions";
-"AUTO_DISABLES_CAPTION_DESC" = "Automatically turns off closed captions (CC) whenever a new video starts playing.";
"PORTRAIT_FULLSCREEN" = "Portrait fullscren mode";
"PORTRAIT_FULLSCREEN_DESC" = "Enables portrait fullscreen mode support.";
"OLD_QUALITY_PICKER" = "Use old quality picker";
"OLD_QUALITY_PICKER_DESC" = "Reverts the video quality selection menu to the classic version (lists all resolutions immediately).";
"EXTRA_SPEED" = "Extra speed options";
"EXTRA_SPEED_DESC" = "Adds more video playback speed options up to 10x.";
-"DISABLE_HINTS" = "Disable hints";
-"DISABLE_HINTS_DESC" = "Disable the tutorial hints and interactive pop-ups.";
"FORCE_MINIPLAYER" = "Force miniplayer";
"FORCE_MINIPLAYER_DESC" = "Allows all videos, including those made for kids, to play in the miniplayer when dismissing the player.";
"FORCE_SEEKBAR" = "Always show progress bar";
"FORCE_SEEKBAR_DESC" = "Makes the progress bar (seekbar) remains visible at all times while in fullscreen mode.";
-"HIDE_LIKE_BUTTON" = "Hide Like button";
-"HIDE_LIKE_BUTTON_DESC" = "Hide Like button in the action bar under the video overlay.";
-"HIDE_DISLIKE_BUTTON" = "Hide Dislike button";
-"HIDE_DISLIKE_BUTTON_DESC" = "Hide Dislike button in the action bar under the video overlay.";
-"HIDE_SHARE_BUTTON" = "Hide Share button";
-"HIDE_SHARE_BUTTON_DESC" = "Hide Share button in the action bar under the video overlay.";
-"HIDE_DOWNLOAD_BUTTON" = "Hide Download button";
-"HIDE_DOWNLOAD_BUTTON_DESC" = "Hide Download button in the action bar under the video overlay.";
-"HIDE_CLIP_BUTTON" = "Hide Clip button";
-"HIDE_CLIP_BUTTON_DESC" = "Hide Clip button in the action bar under the video overlay.";
-"HIDE_REMIX_BUTTON" = "Hide Remix button";
-"HIDE_REMIX_BUTTON_DESC" = "Hide Remix button in the action bar under the video overlay.";
-"HIDE_SAVE_BUTTON" = "Hide Save button";
-"HIDE_SAVE_BUTTON_DESC" = "Hide Save button in the action bar under the video overlay.";
+"TAP_TO_SEEK" = "Tap to seek";
+"TAP_TO_SEEK_DESC" = "Quickly jump to any point by tapping the progress bar.";
+"DISABLES_ZOOM" = "Disable free zoom gesture";
+"DISABLES_ZOOM_DESC" = "Disables new free zoom gestures.";
+"USES_24_HOURS_TIME" = "24-hour format";
+"USES_24_HOURS_TIME_DESC" = "Shows end time in 24-hour format.";
+"PAUSE_TWO_FINGERS" = "Pause using two fingers";
+"PAUSE_TWO_FINGERS_DESC" = "Pause/Resume playback by tapping on the screen using two fingers.";
+"HIDE_COMMENTS_SECTION" = "Hide comments section";
+"HIDE_COMMENTS_SECTION_DESC" = "Completely removes the entire comments section.";
+"HIDE_COMMENTS_PREVIEW" = "Hide preview comments";
+"HIDE_COMMENTS_PREVIEW_DESC" = "Removes the preview box for the top comment while keeping the full section expandable.";
+"USE_ANOTHER_MINIPLAYER" = "Uses another miniplayer style";
+"USE_ANOTHER_MINIPLAYER_DESC" = "Uses another miniplayer style redesigned from older YT versions.";
"SHORTS" = "Shorts";
-"HIDE_SHORTS_LIKE_BUTTON" = "Hide Like button";
-"HIDE_SHORTS_LIKE_BUTTON_DESC" = "Hide the Like button in the Shorts player.";
-"HIDE_SHORTS_DISLIKE_BUTTON" = "Hide Dislike button";
-"HIDE_SHORTS_DISLIKE_BUTTON_DESC" = "Hide the Dislike button in the Shorts player.";
-"HIDE_SHORTS_COMMENT_BUTTON" = "Hide Comment button";
-"HIDE_SHORTS_COMMENT_BUTTON_DESC" = "Hide the Comment button in the Shorts player.";
-"HIDE_SHORTS_SHARE_BUTTON" = "Hide Share button";
-"HIDE_SHORTS_SHARE_BUTTON_DESC" = "Hide the Share button in the Shorts player.";
-"HIDE_SHORTS_REMIX_BUTTON" = "Hide Remix button";
-"HIDE_SHORTS_REMIX_BUTTON_DESC" = "Hide the Remix button in the Shorts player.";
-"HIDE_METADATA_BUTTON" = "Hide sound metadata button";
-"HIDE_METADATA_BUTTON_DESC" = "Hide the button that shows the sound source or music metadata in the Shorts player.";
+"SHORTS_ONLY" = "Shorts only mode";
+"SHORTS_ONLY_DESC" = "Limits YouTube functionality to viewing Shorts only.";
+"SHORTS_ACTION" = "Shorts action after the video ends";
+"LOOP" = "Loop video";
+"SKIP_TO_NEXT_SHORTS" = "Skip to next video";
+"PAUSE_SHORTS" = "Stop video";
+"REMOVE_LIVE_SHORTS" = "Remove live videos";
+"REMOVE_LIVE_SHORTS_DESC" = "Removes live videos from the feed.";
"HIDE_SHORTS_PRODUCT" = "Hide products";
"HIDE_SHORTS_PRODUCT_DESC" = "Hide tagged products and shopping links in the Shorts overlay.";
"HIDE_SHORTS_RECBAR" = "Hide recommendation bar";
"HIDE_SHORTS_RECBAR_DESC" = "Hide the suggested action bar in the Shorts player (e.g. Try this sound, Shopping, Preview comment).";
-"HIDE_SHORTS_COMMIT" = "Hide commissions bar";
-"HIDE_SHORTS_COMMIT_DESC" = "Hide the bar that shows the video got commissions in the Shorts player.";
-"HIDE_SHORTS_SUBSCRIPT_BUTTON" = "Hide subscriptions button";
-"HIDE_SHORTS_SUBSCRIPT_BUTTON_DESC" = "Hide the Subscriptions button at the top of the Shorts player.";
-"HIDE_SHORTS_LIVE_BUTTON" = "Hide live button";
-"HIDE_SHORTS_LIVE_BUTTON_DESC" = "Hide the Live button at the top of the Shorts player.";
-"HIDE_SHORTS_LENS_BUTTON" = "Hide lens button";
-"HIDE_SHORTS_LENS_BUTTON_DESC" = "Hide the Lens button at the top of the Shorts player.";
-"HIDE_SHORTS_TRENDS_BUTTON" = "Hide trends button";
-"HIDE_SHORTS_TRENDS_BUTTON_DESC" = "Hide the Trends button at the top of the Shorts player.";
-"HIDE_SHORTS_TO_VIDEO" = "Hide link to full video";
-"HIDE_SHORTS_TO_VIDEO_DESC" = "Hide the link that redirects in a Short to a full video in the Shorts player.";
"ENABLES_SHORTS_QUALITY" = "Enable Shorts quality picker";
"ENABLES_SHORTS_QUALITY_DESC" = "Force enable the video quality selection menu in Shorts.";
"SHOW_SHORTS_SEEKBAR" = "Always show seekbar";
"SHOW_SHORTS_SEEKBAR_DESC" = "Ensure the progress/seek bar is always visible at the bottom of the Shorts player.";
+"REMOVE_POSTS_SHORTS" = "Remove posts";
+"REMOVE_POSTS_SHORTS_DESC" = "Remove posts from the feed.";
+"SHORTS_FULLSCREEN" = "Pinch to Fullscreen";
+"SHORTS_FULLSCREEN_DESC" = "Manages visibility of the overlay with pinch in and pinch out gestures, displaying Shorts in fullscreen mode.";
"TABBAR" = "Tab bar";
"DEFAULT_TAB" = "Default startup tab";
-"DEFAULT_TAB_DESC" = "You can select the tab you want to open when opening the app.";
-"HOME_NAME" = "Home";
-"SUBSCRIPT_NAME" = "Subscriptions";
-"LIB_NAME" = "Library (You)";
+"FORSTED_TAB_BAR" = "Translucent tab bar";
+"ENABLED" = "Enabled";
"HIDE_TAB_INDI" = "Hide tab indicators";
"HIDE_TAB_INDI_DESC" = "Hide all tab indicators in the tab bar.";
"HIDE_TAB_LABELS" = "Hide tab labels";
"HIDE_TAB_LABELS_DESC" = "Hide all tab labels in the tab bar.";
-"HIDE_HOME_TAB" = "Hide Home tab";
-"HIDE_HOME_TAB_DESC" = "Hide Home tab in the tab bar.";
-"HIDE_SHORTS_TAB" = "Hide Shorts tab";
-"HIDE_SHORTS_TAB_DESC" = "Hide Shorts tab in the tab bar.";
-"HIDE_CREATE_BUTTON" = "Hide Create button";
-"HIDE_CREATE_BUTTON_DESC" = "Hide Create button in the tab bar.";
-"HIDE_SUBSCRIPT_TAB" = "Hide Subscriptions tab";
-"HIDE_SUBSCRIPT_TAB_DESC" = "Hide Subscriptions tab in the tab bar.";
+"HOME_TAB" = "Home";
+"SHORTS_TAB" = "Shorts";
+"CREATE_TAB" = "Create";
+"SUBSCRIPTIONS_TAB" = "Subscriptions";
+"LIBRARY_TAB" = "Library (You)";
+"HISTORY_TAB" = "History";
+"GAMING_TAB" = "Gaming";
+"SPORTS_TAB" = "Sports";
+"NOTI_TAB" = "Notifications";
+"NEWS_TAB" = "News";
+"MUSIC_TAB" = "Music";
+"WATCH_LATER_TAB" = "Watch later";
+"PLAYLIST_TAB" = "Playlists";
+"LIKE_TAB" = "Liked videos";
+"LIVE_TAB" = "Live";
+"POST_TAB" = "Posts";
+"VIDEO_TAB" = "Your videos";
+"MOVIE_TAB" = "Movies";
+"COURSE_TAB" = "Courses";
+"MINIGAME_TAB" = "Playables";
+"MANAGE_TABS" = "Manage tabs";
+"MANAGE_TABS_DESC" = "Reorder and toggle tabs";
+"TAB_REORDER_HINT" = "Drag to reorder. Toggle to show/hide.\nTab settings are also accessible by long-pressing the first tab in the tab bar.";
+"TAB_LIMIT" = "Tab limit";
+"TAB_LIMIT_DESC" = "Only 6 tabs are allowed.";
+"ZERO_TAB_DESC" = "At least 1 tab has to be enabled.";
"MISCELLANEOUS" = "Miscellaneous";
"BACKGROUND_PLAYBACK" = "Background playback";
"BACKGROUND_PLAYBACK_DESC" = "Enables background playback for videos.";
"DISABLES_SHORTS_PIP" = "Disables Shorts PiP";
"DISABLES_SHORTS_PIP_DESC" = "Try to disables Shorts PiP, this might not 100% disables it.";
+"DISABLE_HINTS" = "Disable hints";
+"DISABLE_HINTS_DESC" = "Disable the tutorial hints and interactive pop-ups.";
"BLOCK_UPGRADE_DIALOGS" = "Block upgrade dialogs";
"BLOCK_UPGRADE_DIALOGS_DESC" = "Block the YouTube dialogs that ask you to upgrade the app.";
"ARE_YOU_THERE_DIALOG" = "Hide 'Are you there?' dialog";
@@ -247,7 +379,109 @@
"DISABLES_SNACK_BAR_DESC" = "Disables snack bar when pressing some buttons or doing something in the app.";
"HIDE_STARTUP_ANIMATIONS" = "Hide YouTube's startup animations";
"HIDE_STARTUP_ANIMATIONS_DESC" = "Hide YouTube's startup animations when opening the app.";
-"HIDE_PLAY_IN_NEXT_QUEUE" = "Hide 'Play next in queue'";
-"HIDE_PLAY_IN_NEXT_QUEUE_DESC" = "Hide the 'Play next in queue' option in the flyout menu.";
"HIDE_LIKE_DISLIKE_VOTES" = "Hide like/dislike votes";
-"HIDE_LIKE_DISLIKE_VOTES_DESC" = "Hide messages that pop up when you like/dislike videos.";
\ No newline at end of file
+"HIDE_LIKE_DISLIKE_VOTES_DESC" = "Hide messages that pop up when you like/dislike videos.";
+"FLYOUT_MENU" = "Flyout menu";
+"REMOVE_PLAY_IN_NEXT_QUEUE_OPTION" = "Removes 'Play next in queue' option";
+"REMOVE_PLAY_IN_NEXT_QUEUE_OPTION_DESC" = "Removes 'Play next in queue' option from the flyout menu.";
+"REMOVE_DOWNLOAD_OPTION" = "Removes 'Download video' option";
+"REMOVE_DOWNLOAD_OPTION_DESC" = "Removes 'Download video' option from the flyout menu.";
+"REMOVE_WATCH_LATER_OPTION" = "Removes 'Save to Watch later' option";
+"REMOVE_WATCH_LATER_OPTION_DESC" = "Removes 'Save to Watch Later' option from the flyout menu.";
+"REMOVE_SAVE_OPTION" = "Removes 'Save to playlist' option";
+"REMOVE_SAVE_OPTION_DESC" = "Removes 'Save to playlist' option from the flyout menu.";
+"REMOVE_REMOVE_FROM_PLAYLIST_OPTION" = "Removes 'Remove from playlist' option";
+"REMOVE_REMOVE_FROM_PLAYLIST_OPTION_DESC" = "Removes 'Remove from playlist' option from the flyout menu.";
+"REMOVE_SHARE_OPTION" = "Removes 'Share' option";
+"REMOVE_SHARE_OPTION_DESC" = "Removes 'Share' option from the flyout menu.";
+"REMOVE_NOT_INTERESTED_OPTION" = "Removes 'Not interested' option";
+"REMOVE_NOT_INTERESTED_OPTION_DESC" = "Removes 'Not interested' option from the flyout menu.";
+"REMOVE_DONT_RECOMMEND_OPTION" = "Removes 'Don't recommend channel' option";
+"REMOVE_DONT_RECOMMEND_OPTION_DESC" = "Removes 'Don't recommend channel' option from the flyout menu.";
+"REMOVE_INFO_OPTION" = "Removes 'Why are you seeing this?' option";
+"REMOVE_INFO_OPTION_DESC" = "Removes 'Why are you seeing this?' option from the flyout menu.";
+"REMOVE_FILTER_OPTION" = "Removes 'Search filters' option";
+"REMOVE_FILTER_OPTION_DESC" = "Removes 'Search filters' option from the flyout menu.";
+"REMOVE_REPORT_OPTION" = "Removes 'Report' option";
+"REMOVE_REPORT_OPTION_DESC" = "Removes 'Report' option from the flyout menu.";
+"REMOVE_YOUTUBE_MUSIC_OPTION" = "Removes 'Listen with YouTube Music' option";
+"REMOVE_YOUTUBE_MUSIC_OPTION_DESC" = "Removes 'Listen with YouTube Music' option from the flyout menu.";
+"REMOVE_FEED_BACK_OPTION" = "Removes 'Send feedback' option";
+"REMOVE_FEED_BACK_OPTION_DESC" = "Removes 'Send feedback' option from the flyout menu.";
+"REMOVE_CAST_OPTION" = "Removes 'Watch on TV' option";
+"REMOVE_CAST_OPTION_DESC" = "Removes 'Watch on TV' option from the flyout menu.";
+"REMOVE_SHUFFLE_OPTION" = "Removes 'Shuffle' option";
+"REMOVE_SHUFFLE_OPTION_DESC" = "Removes 'Shuffle' option from the flyout menu.";
+"REMOVE_UN_SUB_OPTION" = "Removes 'Unsubscribe' option";
+"REMOVE_UN_SUB_OPTION_DESC" = "Removes 'Unsubscribe' option from the flyout menu.";
+"REMOVE_HIDE_FROM_PLAYLIST_OPTION" = "Removes 'Hide video from playlist' option";
+"REMOVE_HIDE_FROM_PLAYLIST_OPTION_DESC" = "Removes 'Hide video from playlist' option from the flyout menu.";
+"REMOVE_HELP_OPTION" = "Removes 'Help & feedback' option";
+"REMOVE_HELP_OPTION_DESC" = "Removes 'Help & feedback' option from the flyout menu.";
+"REMOVE_NOTIFY_OPTION" = "Removes 'Notify me' option";
+"REMOVE_NOTIFY_OPTION_DESC" = "Removes 'Notify me' option from the flyout menu.";
+"REMOVE_CLEARSCREEN_OPTION" = "Removes 'Clear Screen' option";
+"REMOVE_CLEARSCREEN_OPTION_DESC" = "Removes 'Clear Screen' option from the flyout menu.";
+"HIDE_COMMU_GUIDE" = "Hide community guidelines";
+"HIDE_COMMU_GUIDE_DESC" = "Hide community guidelines header.";
+"DISABLES_RTL" = "Disable RTL formatting";
+"DISABLES_RTL_DESC" = "Forcefully displays text in left-to-right (LTR) format for languages that are initially displayed in right-to-left (RTL).";
+"DEVICE_UI" = "Interface style";
+
+"SB_DESCRIPTION" = "Skip sponsored segments automatically using community-submitted data.";
+"SB_ENABLE" = "Enable SponsorBlock";
+"SB_ENABLE_DESC" = "Activate the SponsorBlock extension to skip sponsored segments.";
+"SB_SHOW_BUTTON" = "Show overlay button";
+"SB_SHOW_BUTTON_DESC" = "Display a SponsorBlock toggle button in the player overlay.";
+"SB_SHOW_NOTIFICATIONS" = "Show notifications";
+"SB_SHOW_NOTIFICATIONS_DESC" = "Display notifications when segments are skipped. Notifications for manual-skip categories are always shown.";
+"SB_HAPTIC_FEEDBACK" = "Haptic feedback";
+"SB_HAPTIC_FEEDBACK_DESC" = "Provide haptic feedback when a segment is skipped.";
+"SB_SEGMENTS_IN_PLAYER" = "Segments in player";
+"SB_SEGMENTS_IN_PLAYER_DESC" = "Display colored segments on the progress bar of the main video player.";
+"SB_SEGMENTS_IN_FEED" = "Segments in feed";
+"SB_SEGMENTS_IN_FEED_DESC" = "Display colored segments on the progress bar of players in the feed.";
+"SB_SEGMENTS_IN_MINIPLAYER" = "Segments in mini-player";
+"SB_SEGMENTS_IN_MINIPLAYER_DESC" = "Display colored segments on the progress bar of the mini-player.";
+"SB_SHOW_DURATION" = "Show duration without segments";
+"SB_SHOW_DURATION_DESC" = "Display the total video duration excluding skippable segments.";
+"SB_CATEGORIES_HEADER" = "Segment categories";
+"SB_SEGMENT_COLOR" = "Segment color";
+
+"SB_ACTION_DISABLE" = "Disable";
+"SB_ACTION_AUTO_SKIP" = "Auto-skip";
+"SB_ACTION_ASK" = "Ask before skipping";
+"SB_ACTION_DISPLAY" = "Show on seek bar only";
+"SB_ACTION_SKIP_TO" = "Skip to segment";
+
+"SB_CAT_sponsor" = "Sponsor";
+"SB_CAT_sponsor_DESC" = "Paid promotion for a product or service not directly related to the creator.";
+"SB_CAT_intro" = "Intro";
+"SB_CAT_intro_DESC" = "Intermission or intro animation typically seen at the start of a video.";
+"SB_CAT_outro" = "Endcards";
+"SB_CAT_outro_DESC" = "Endcards, credits, or outros at the end of the video.";
+"SB_CAT_interaction" = "Interaction";
+"SB_CAT_interaction_DESC" = "Reminders to like, subscribe, or follow on social media.";
+"SB_CAT_selfpromo" = "Self-promotion";
+"SB_CAT_selfpromo_DESC" = "Unpaid promotion of the creator's own products, merchandise, or other content.";
+"SB_CAT_music_offtopic" = "Non-music";
+"SB_CAT_music_offtopic_DESC" = "Non-music sections in music videos (e.g. talking, introductions).";
+"SB_CAT_preview" = "Preview";
+"SB_CAT_preview_DESC" = "Recap or preview of content from another video or previous episode.";
+"SB_CAT_hook" = "Hook/Greetings";
+"SB_CAT_hook_DESC" = "Narrated trailers for the upcoming video, greetings and goodbyes.";
+"SB_CAT_poi_highlight" = "Highlight";
+"SB_CAT_poi_highlight_DESC" = "The key moment or highlight point of the video. Use 'Skip to' to jump directly to it.";
+"SB_CAT_filler" = "Filler";
+"SB_CAT_filler_DESC" = "Tangential content, jokes, or filler that is not directly related to the main topic.";
+
+"SB_SKIPPED" = "%@ segment has been skipped";
+"SB_DETECTED" = "%@ segment detected.\nWould you like to skip the segment?";
+"SB_SKIP_NOW" = "Skip";
+"SB_UNSKIP" = "Unskip";
+"SB_JUMPED_TO_HIGHLIGHT" = "Jumped to highlight";
+"SB_JUMP_TO_HIGHLIGHT" = "Highlight available. Jump to the point?";
+"SB_SEGMENT_COLOR_SUFFIX" = "Segment color";
+"SB_SECTION_MAIN" = "Main";
+"SB_SKIP_ALERT_DURATION" = "Skip alert duration";
+"SB_UNSKIP_ALERT_DURATION" = "Unskip alert duration";
\ No newline at end of file
diff --git a/layout/Library/Application Support/YouMod.bundle/ro.lproj/Localizable.strings b/layout/Library/Application Support/YouMod.bundle/ro.lproj/Localizable.strings
index bccaf550..2c1a8a0f 100644
--- a/layout/Library/Application Support/YouMod.bundle/ro.lproj/Localizable.strings
+++ b/layout/Library/Application Support/YouMod.bundle/ro.lproj/Localizable.strings
@@ -1,11 +1,18 @@
-"NOTE" = "NOTE: Some hiding features may leave a blank box.";
"SETTINGS" = "Settings";
+"INTERFACE" = "Interface";
+
+"LOOP_DISABLED" = "Loop disabled";
+"LOOP_ENABLED" = "Loop enabled";
"NEW_ISSUES" = "Report issues or request new features";
"NEW_ISSUES_DESC" = "Found bugs or want to request new features? Tap here.";
"SOURCE_CODES" = "Source codes";
"SOURCE_CODES_DESC" = "Take a look at the codes!";
+"EXTRA" = "Extra";
+"FIX_PLAYBACK_ISSUES" = "Fix playback issues (Temporary)";
+"FIX_PLAYBACK_ISSUES_DESC" = "Try to fix video playback issues for some users. Turning this on will make the player automatically reloads itself everytime it errors or fails to play the video.";
+
"PERFER_HEADER" = "YouMod perferences";
"PERFER" = "Perferences";
"IMPORT" = "Import settings";
@@ -24,6 +31,7 @@
"ERROR_NO_KEYS_IMPORT" = "No available keys to import";
"ERROR_NO_KEYS_EXPORT" = "No available keys to export";
"OVERRIDE" = "This will override the current settings. Do you want to continue?";
+"DISABLED" = "Disabled";
"CACHE" = "Cache management";
"CLEARCACHE" = "Clear cache";
@@ -32,12 +40,80 @@
"AUTO_CLEARCACHE_DESC" = "Automatically clears the app cache on startup.";
"DOWNLOADING" = "Downloading";
-"DOWNLOAD_MANAGER" = "Download Manager";
-"DOWNLOAD_MANAGER_DESC" = "Replaces YouTube's video downloader with YouMod one. With ablity to downloading videos, audios, captions, and thumbnails";
+"DOWNLOAD_MANAGER" = "Download manager";
+"DOWNLOAD_MANAGER_DESC" = "Replaces YouTube's video downloader with YouMod one. With ablity to downloading videos, audios, captions, and thumbnails.";
"DOWNLOAD_SAVE_PHOTOS" = "Automatically save to Photos";
"DOWNLOAD_SAVE_PHOTOS_DESC" = "Automatically saves downloaded videos and images directly to your Photos app.";
-"DOWNLOAD_DRC_AUDIO" = "Prefer DRC Audio";
-"DOWNLOAD_DRC_AUDIO_DESC" = "Prioritizes downloading audio tracks with Dynamic Range Compression for more consistent volume levels.";
+"ADD_SHORTS_DOWNLOAD" = "Adds a download button to Shorts";
+"ADD_SHORTS_DOWNLOAD_DESC" = "Adds a download button to Shorts video overlay.";
+"USE_ORIG_AUDIO" = "Automatically uses original audio track";
+"USE_ORIG_AUDIO_DESC" = "Automatically uses original audio track instead of showing a dialog.";
+"DOWNLOAD_SHORTS" = "Download Shorts";
+"DOWNLOAD_SHORTS_DESC" = "Choose Shorts quality";
+"COPY_VID_INFO" = "Copy video informations";
+"COPY_VID_INFO_DESC" = "Choose what to copy";
+"COPY_ALL_VID_INFO" = "Copy all";
+"COPY_ALL_VID_INFO_DESC" = "Copy all video informations";
+"COPY_AUTHOR" = "Copy author";
+"COPY_AUTHOR_DESC" = "Copy the video author";
+"COPY_TITLE" = "Copy title";
+"COPY_TITLE_DESC" = "Copy the video title";
+"COPY_DESCRIPTION" = "Copy description";
+"COPY_DESCRIPTION_DESC" = "Copy the video description";
+"COPY_URL" = "Copy video link";
+"COPY_URL_TIMESTAMP" = "Copy video link with timestamp";
+"URL_COPIED" = "Copied video link";
+"URL_TIMESTAMP_COPIED" = "Copied video link with timestamp";
+"ERROR_VIDEOID" = "Cannot get the video ID";
+"ERROR_ADS" = "Cannot copy video link while playing ads";
+"COPIED_AUTHOR" = "Copied video author";
+"COPIED_TITLE" = "Copied video title";
+"COPIED_DESCRIPTION" = "Copied video description";
+"SAVE_THUMBNAIL" = "Save thumbnail";
+"SAVE_THUMBNAIL_DESC" = "Save to Photos";
+"DOWNLOAD_CAPTIONS" = "Download captions";
+"DOWNLOAD_CAPTIONS_DESC" = "Save subtitles as .VTT file";
+"DOWNLOAD_AUDIO" = "Download audio";
+"DOWNLOAD_AUDIO_DESC" = "Download audio as .m4a file";
+"DOWNLOAD_VIDEO" = "Download video";
+"DOWNLOAD_VIDEO_DESC" = "Choose video quality";
+"OPEN_VID_BEFORE" = "Open a video before using the download manager";
+"NO_CAPTIONS_URL" = "No valid caption URLs found";
+"NO_AUDIO_URL" = "No audio URL found";
+"NO_STREAM_URL" = "No stream URL found";
+"CAPTIONS_FAILED" = "Failed to download captions";
+"NO_CAPTIONS" = "No captions available for this video";
+"NO_AUDIO_STREAM_FOUND" = "No audio streams found";
+"NO_VID_AUDIO_STREAM_FOUND" = "No video/audio streams found";
+"COPIED_VID_INFO" = "Copied all video informations";
+"SAVED_TO_PHOTOS" = "Saved to Photos";
+"SAVE_FAILED" = "Save failed";
+"PHOTO_ACCESS_DENINED" = "Photos access denied";
+"THUMBNAIL_FAILED" = "Thumbnail failed";
+"DOWNLOADING_THUMBNAIL" = "Downloading thumbnail...";
+"DOWNLOADING_AUDIO" = "Downloading audio...";
+"DOWNLOADING_VIDEO" = "Downloading video...";
+"DOWNLOADING_CAPTIONS" = "Downloading captions...";
+"NO_THUMBNAIL_FOUND" = "No thumbnail found";
+"DOWNLOAD_FAILED" = "Download failed";
+"COPY_THUMBNAIL" = "Copy thumbnail";
+"COPY_THUMBNAIL_DESC" = "Copy to clipboard";
+"SHOW_THUMBNAIL" = "Show thumbnail";
+"SHOW_THUMBNAIL_DESC" = "Show in a dialog";
+"THUMBNAIL_OPTIONS" = "Download thumbnail";
+"THUMBNAIL_OPTIONS_DESC" = "Show more options";
+"COPIED_TO_CLIPBOARD" = "Copied to clipboard";
+"DOWNLOAD_COMPLETED" = "Download completed";
+"CANNOT_SAVE_TO_PHOTOS" = "Cannot save to Photos";
+"FINA_VIDEO" = "Finalizing video...";
+"MERGING_VID_FALLBACK" = "Merging video with fallback...";
+"MERGING_VID" = "Merging video...";
+"CONVERTING" = "Converting to";
+"ALREADY_DOWNLOADING" = "Already downloading";
+"DOWNLOAD_CANCELLED" = "Download cancelled";
+"CANCEL" = "Cancel";
+"TRIMMING_AUDIO" = "Trimming audio...";
+"COPY" = "Copy";
"APPEARANCE" = "Appearance";
"OLED_THEME" = "OLED dark mode";
@@ -46,6 +122,8 @@
"OLED_KEYBOARD_DESC" = "Make the keyboard uses a pure black (OLED) color for dark theme.";
"NAVBAR" = "Navigation bar";
+"STICKY_NAVBAR" = "Sticky Navigation bar";
+"STICKY_NAVBAR_DESC" = "Pins the Navigation bar so that it remains visible when scrolling down.";
"HIDE_YT_LOGO" = "Hide YouTube logo";
"HIDE_YT_LOGO_DESC" = "Hide YouTube logo in the navigation bar.";
"PREMIUM_LOGO" = "Use YouTube Premium logo";
@@ -62,34 +140,78 @@
"FEED" = "Feed";
"HIDE_SUBBAR" = "Hide subbar";
"HIDE_SUBBAR_DESC" = "Hide subbar at the top of the feed.";
+"HIDE_HORI_SHELF" = "Remove horizontal feeds";
+"HIDE_HORI_SHELF_DESC" = "Removes all horizontal feeds (News, Continue Watching, Posts collection, etc.) from the feed, except Playables.";
"HIDE_MUSIC_PLAYLISTS" = "Hide music playlist generator";
"HIDE_MUSIC_PLAYLISTS_DESC" = "Hide music playlist generator (Select music tunes in three) in the feed.";
"HIDE_FEED_POST" = "Hide posts";
"HIDE_FEED_POST_DESC" = "Hide posts in feed.";
+"HIDE_SURVEYS" = "Hide surveys";
+"HIDE_SURVEYS_DESC" = "Hide video surveys that YouTube shows sometimes.";
"HIDE_SHORTS_SHELF" = "Hide Shorts shelf";
"HIDE_SHORTS_SHELF_DESC" = "Hide Shorts shelf in the feed, including in the Subscriptions tab.";
+"KEEP_SHORTS_SUBSCRIPT" = "Keep Shorts in Subscriptions";
+"KEEP_SHORTS_SUBSCRIPT_DESC" = "Prevents removing Shorts from Subscriptions tab.";
"HIDE_SEARCH_HISTORY" = "Hide search history";
"HIDE_SEARCH_HISTORY_DESC" = "Hide previous search history and suggestions when using the search bar.\nNOTE: Your search history will still be accessible in your other YouTube clients.";
-"HIDE_SUB_BUTTON" = "Hide Subscribe button";
-"HIDE_SUB_BUTTON_DESC" = "Hides the subscribe button in channels and video descriptions.";
-"HIDE_SHOP_BUTTON" = "Hide Shopping button";
-"HIDE_SHOP_BUTTON_DESC" = "Hides the shopping/store button in channels descriptions.";
-"HIDE_MEMBER_BUTTON" = "Hide Membership button";
-"HIDE_MEMBER_BUTTON_DESC" = "Hides the 'Join' button in channels and video descriptions.";
+"HIDE_PLAYABLES" = "Remove playables";
+"HIDE_PLAYABLES_DESC" = "Removes playables (mini-games) from the feed.";
"PLAYER" = "Player";
+"PLAYBACK_SPEED" = "Playback speed";
+"CONTROL_CENTER" = "Control Center";
+"PLAYER_ACTIONS" = "Player actions";
+"GESTURE_HEADER" = "Gestures";
+"QUALITY_WIFI" = "Playback quality on WiFi";
+"QUALITY_WIFI_DESC" = "Automatically applies the selected quality when streaming over Wi-Fi.";
+"QUALITY_CELLULAR" = "Playback quality on Cellular";
+"QUALITY_CELLULAR_DESC" = "Automatically applies the selected quality when streaming over mobile data.";
+"QUALITY_LOW_POWER" = "Playback quality on Power saver mode";
+"QUALITY_LOW_POWER_DESC" = "Automatically applies the selected quality when when Low power mode is enabled on your device.";
+"DEFAULT" = "Default";
+"BEST" = "Best";
+"AUDIO_TRACK" = "Preferred audio track";
+"ORIGINAL" = "Original";
+"SELECT_MANUALLY" = "Select manually";
+"AUDIO_TRACK_SELECT" = "Selected audio track";
+"AUDIO_TRACK_SELECT_DESC" = "Specifies the preferred audio track language to use for playback, if available.";
+"NO_AUTO_DUBBED" = "Uses original audio track";
+"NO_AUTO_DUBBED_DESC" = "Uses original audio track if the preferred language track is auto-dubbed or not found.";
+"CAPTION_TRACK" = "Preferred captions";
+"CAPTION_TRACK_SELECT" = "Selected captions";
+"CAPTION_TRACK_SELECT_DESC" = "Specifies the preferred captions language to display, if available.";
+"DISABLES_CAPTION_TRACK" = "Automatically disable captions";
+"DISABLES_CAPTION_TRACK_DESC" = "Automatically disable captions if the preferred language is auto-generated or not found.";
+"DEFAULT_SPEED" = "Default playback rate";
+"DEFAULT_SPEED_DESC" = "Overrides the default playback speed.";
+"SPEED_BUTTON" = "Speed controls button";
+"SPEED_BUTTON_DESC" = "Displays a speed controls button on overlay.";
+"MUTE_BUTTON" = "Mute button";
+"MUTE_BUTTON_DESC" = "Displays a button to mute or unmute the volume on the overlay.";
+"SHARE_BUTTON" = "Share button";
+"SHARE_BUTTON_DESC" = "Displays a button to share the video link on the overlay.";
+"LOOP_BUTTON" = "Loop button";
+"LOOP_BUTTON_DESC" = "Displays a button to loop the video on the overlay.";
+"CAPTION_BUTTON" = "Captions button";
+"CAPTION_BUTTON_DESC" = "Displays a button to copy the video captions on the overlay.";
+"HOLD_TO_SPEED" = "Hold to speed";
+"HOLD_TO_SPEED_DESC" = "Uses custom hold to speed playback rate.";
"HIDE_AUTOPLAY" = "Hide autoplay switch";
"HIDE_AUTOPLAY_DESC" = "Hide autoplay switch in the video overlay.";
"HIDE_CAPTIONS_BUTTON" = "Hide captions button";
"HIDE_CAPTIONS_BUTTON_DESC" = "Hide captions button in the video overlay.";
"HIDE_CAST_BUTTON_PLAYER" = "Hide cast button";
"HIDE_CAST_BUTTON_PLAYER_DESC" = "Hide cast button in the video overlay.";
-"HIDE_PREV_BUTTON" = "Hide previous/back button";
-"HIDE_PREV_BUTTON_DESC" = "Hide previous/back button in the video overlay.";
-"HIDE_NEXT_BUTTON" = "Hide next/skip button";
-"HIDE_NEXT_BUTTON_DESC" = "Hide next/skip button in the video overlay.";
+"HIDE_NEXT_AND_PREV_BUTTON" = "Hide previous and next buttons";
+"HIDE_NEXT_AND_PREV_BUTTON_DESC" = "Hide previous and next buttons in the video overlay.";
"REPLACE_PREVNEXT_BUTTONS" = "Replace Previous/Next buttons";
"REPLACE_PREVNEXT_BUTTONS_DESC" = "Replaces the Previous and Next video buttons in the player with Rewind and Fast Forward buttons.";
+"SKIP_BACKWARD" = "Skip backward";
+"SKIP_BACKWARD_DESC" = "Replaces the previous track button in Control Center with a skip backward button by a set number of seconds.";
+"SKIP_FORWARD" = "Skip forward";
+"SKIP_FORWARD_DESC" = "Replaces the next track button in Control Center with a skip forward button by a set number of seconds.";
+"REWIND_SECONDS" = "Rewind seconds";
+"FORWARD_SECONDS" = "Fast-forward seconds";
"REMOVE_DARK_OVERLAY" = "Remove dark overlay";
"REMOVE_DARK_OVERLAY_DESC" = "Remove dark overlay from the video overlay.";
"HIDE_END_SCREEN" = "Hide endscreen cards";
@@ -102,6 +224,12 @@
"HIDE_PAID_OVERLAY_DESC" = "Hide 'Includes paid promotion' in the video overlay.";
"HIDE_WATERMARK" = "Hide watermark";
"HIDE_WATERMARK_DESC" = "Hide channel watermark at the bottom of the video overlay.";
+"PAUSE_ON_OVERLAY" = "Pause on overlay";
+"PAUSE_ON_OVERLAY_DESC" = "Sets playback on pause if overlay appears.";
+"DISABLES_SNAP_TO_CHAPTER" = "Disable snap to chapters";
+"DISABLES_SNAP_TO_CHAPTER_DESC" = "Prevents the scrubbar stick to the chapters.";
+"DISABLES_ENGAGE_PANEL" = "Disables engagement panel in fullscreen";
+"DISABLES_ENGAGE_PANEL_DESC" = "Prevent engagement panel showing in fullscreen.";
"GESTURES" = "Enable gestures controls";
"GESTURES_DESC" = "Swipe up or down on the left/right edges of the video screen to adjust brightness, volume, or playback speed.";
"GESTURE_AREA" = "Gesture activation area";
@@ -136,6 +264,8 @@
"DISABLES_SHOW_REMAINING_DESC" = "Disable the feature that shows how much time is left in a video when tapping the timestamp.";
"ALWAYS_SHOW_REMAINING" = "Always show remaining time";
"ALWAYS_SHOW_REMAINING_DESC" = "Ensure the remaining time is always displayed by default in the player overlay.";
+"COPY_TIMESTAMP_ON_PAUSE" = "Copy timestamped links";
+"COPY_TIMESTAMP_ON_PAUSE_DESC" = "Allows to copy timestamped link to the clipboard by pressing pause button.";
"SHOW_REMAINING_EXTRA" = "Show extra remaining time";
"SHOW_REMAINING_EXTRA_DESC" = "Adds the estimated finish time to the label in the video overlay.";
"HIDE_FULLSCREEN_ACTIONS" = "Hide fullscreen actions";
@@ -148,93 +278,95 @@
"HIDE_CONTENT_WARNING_DESC" = "Automatically skip the sensitive content warning alert in some vidoes.";
"AUTO_FULLSCREEN" = "Auto fullscreen";
"AUTO_FULLSCREEN_DESC" = "Automatically enter fullscreen mode when starting video playback.";
-"AUTO_DISABLES_CAPTION" = "Auto disable captions";
-"AUTO_DISABLES_CAPTION_DESC" = "Automatically turns off closed captions (CC) whenever a new video starts playing.";
"PORTRAIT_FULLSCREEN" = "Portrait fullscren mode";
"PORTRAIT_FULLSCREEN_DESC" = "Enables portrait fullscreen mode support.";
"OLD_QUALITY_PICKER" = "Use old quality picker";
"OLD_QUALITY_PICKER_DESC" = "Reverts the video quality selection menu to the classic version (lists all resolutions immediately).";
"EXTRA_SPEED" = "Extra speed options";
"EXTRA_SPEED_DESC" = "Adds more video playback speed options up to 10x.";
-"DISABLE_HINTS" = "Disable hints";
-"DISABLE_HINTS_DESC" = "Disable the tutorial hints and interactive pop-ups.";
"FORCE_MINIPLAYER" = "Force miniplayer";
"FORCE_MINIPLAYER_DESC" = "Allows all videos, including those made for kids, to play in the miniplayer when dismissing the player.";
"FORCE_SEEKBAR" = "Always show progress bar";
"FORCE_SEEKBAR_DESC" = "Makes the progress bar (seekbar) remains visible at all times while in fullscreen mode.";
-"HIDE_LIKE_BUTTON" = "Hide Like button";
-"HIDE_LIKE_BUTTON_DESC" = "Hide Like button in the action bar under the video overlay.";
-"HIDE_DISLIKE_BUTTON" = "Hide Dislike button";
-"HIDE_DISLIKE_BUTTON_DESC" = "Hide Dislike button in the action bar under the video overlay.";
-"HIDE_SHARE_BUTTON" = "Hide Share button";
-"HIDE_SHARE_BUTTON_DESC" = "Hide Share button in the action bar under the video overlay.";
-"HIDE_DOWNLOAD_BUTTON" = "Hide Download button";
-"HIDE_DOWNLOAD_BUTTON_DESC" = "Hide Download button in the action bar under the video overlay.";
-"HIDE_CLIP_BUTTON" = "Hide Clip button";
-"HIDE_CLIP_BUTTON_DESC" = "Hide Clip button in the action bar under the video overlay.";
-"HIDE_REMIX_BUTTON" = "Hide Remix button";
-"HIDE_REMIX_BUTTON_DESC" = "Hide Remix button in the action bar under the video overlay.";
-"HIDE_SAVE_BUTTON" = "Hide Save button";
-"HIDE_SAVE_BUTTON_DESC" = "Hide Save button in the action bar under the video overlay.";
+"TAP_TO_SEEK" = "Tap to seek";
+"TAP_TO_SEEK_DESC" = "Quickly jump to any point by tapping the progress bar.";
+"DISABLES_ZOOM" = "Disable free zoom gesture";
+"DISABLES_ZOOM_DESC" = "Disables new free zoom gestures.";
+"USES_24_HOURS_TIME" = "24-hour format";
+"USES_24_HOURS_TIME_DESC" = "Shows end time in 24-hour format.";
+"PAUSE_TWO_FINGERS" = "Pause using two fingers";
+"PAUSE_TWO_FINGERS_DESC" = "Pause/Resume playback by tapping on the screen using two fingers.";
+"HIDE_COMMENTS_SECTION" = "Hide comments section";
+"HIDE_COMMENTS_SECTION_DESC" = "Completely removes the entire comments section.";
+"HIDE_COMMENTS_PREVIEW" = "Hide preview comments";
+"HIDE_COMMENTS_PREVIEW_DESC" = "Removes the preview box for the top comment while keeping the full section expandable.";
+"USE_ANOTHER_MINIPLAYER" = "Uses another miniplayer style";
+"USE_ANOTHER_MINIPLAYER_DESC" = "Uses another miniplayer style redesigned from older YT versions.";
"SHORTS" = "Shorts";
-"HIDE_SHORTS_LIKE_BUTTON" = "Hide Like button";
-"HIDE_SHORTS_LIKE_BUTTON_DESC" = "Hide the Like button in the Shorts player.";
-"HIDE_SHORTS_DISLIKE_BUTTON" = "Hide Dislike button";
-"HIDE_SHORTS_DISLIKE_BUTTON_DESC" = "Hide the Dislike button in the Shorts player.";
-"HIDE_SHORTS_COMMENT_BUTTON" = "Hide Comment button";
-"HIDE_SHORTS_COMMENT_BUTTON_DESC" = "Hide the Comment button in the Shorts player.";
-"HIDE_SHORTS_SHARE_BUTTON" = "Hide Share button";
-"HIDE_SHORTS_SHARE_BUTTON_DESC" = "Hide the Share button in the Shorts player.";
-"HIDE_SHORTS_REMIX_BUTTON" = "Hide Remix button";
-"HIDE_SHORTS_REMIX_BUTTON_DESC" = "Hide the Remix button in the Shorts player.";
-"HIDE_METADATA_BUTTON" = "Hide sound metadata button";
-"HIDE_METADATA_BUTTON_DESC" = "Hide the button that shows the sound source or music metadata in the Shorts player.";
+"SHORTS_ONLY" = "Shorts only mode";
+"SHORTS_ONLY_DESC" = "Limits YouTube functionality to viewing Shorts only.";
+"SHORTS_ACTION" = "Shorts action after the video ends";
+"LOOP" = "Loop video";
+"SKIP_TO_NEXT_SHORTS" = "Skip to next video";
+"PAUSE_SHORTS" = "Stop video";
+"REMOVE_LIVE_SHORTS" = "Remove live videos";
+"REMOVE_LIVE_SHORTS_DESC" = "Removes live videos from the feed.";
"HIDE_SHORTS_PRODUCT" = "Hide products";
"HIDE_SHORTS_PRODUCT_DESC" = "Hide tagged products and shopping links in the Shorts overlay.";
"HIDE_SHORTS_RECBAR" = "Hide recommendation bar";
"HIDE_SHORTS_RECBAR_DESC" = "Hide the suggested action bar in the Shorts player (e.g. Try this sound, Shopping, Preview comment).";
-"HIDE_SHORTS_COMMIT" = "Hide commissions bar";
-"HIDE_SHORTS_COMMIT_DESC" = "Hide the bar that shows the video got commissions in the Shorts player.";
-"HIDE_SHORTS_SUBSCRIPT_BUTTON" = "Hide subscriptions button";
-"HIDE_SHORTS_SUBSCRIPT_BUTTON_DESC" = "Hide the Subscriptions button at the top of the Shorts player.";
-"HIDE_SHORTS_LIVE_BUTTON" = "Hide live button";
-"HIDE_SHORTS_LIVE_BUTTON_DESC" = "Hide the Live button at the top of the Shorts player.";
-"HIDE_SHORTS_LENS_BUTTON" = "Hide lens button";
-"HIDE_SHORTS_LENS_BUTTON_DESC" = "Hide the Lens button at the top of the Shorts player.";
-"HIDE_SHORTS_TRENDS_BUTTON" = "Hide trends button";
-"HIDE_SHORTS_TRENDS_BUTTON_DESC" = "Hide the Trends button at the top of the Shorts player.";
-"HIDE_SHORTS_TO_VIDEO" = "Hide link to full video";
-"HIDE_SHORTS_TO_VIDEO_DESC" = "Hide the link that redirects in a Short to a full video in the Shorts player.";
"ENABLES_SHORTS_QUALITY" = "Enable Shorts quality picker";
"ENABLES_SHORTS_QUALITY_DESC" = "Force enable the video quality selection menu in Shorts.";
"SHOW_SHORTS_SEEKBAR" = "Always show seekbar";
"SHOW_SHORTS_SEEKBAR_DESC" = "Ensure the progress/seek bar is always visible at the bottom of the Shorts player.";
+"REMOVE_POSTS_SHORTS" = "Remove posts";
+"REMOVE_POSTS_SHORTS_DESC" = "Remove posts from the feed.";
+"SHORTS_FULLSCREEN" = "Pinch to Fullscreen";
+"SHORTS_FULLSCREEN_DESC" = "Manages visibility of the overlay with pinch in and pinch out gestures, displaying Shorts in fullscreen mode.";
"TABBAR" = "Tab bar";
"DEFAULT_TAB" = "Default startup tab";
-"DEFAULT_TAB_DESC" = "You can select the tab you want to open when opening the app.";
-"HOME_NAME" = "Home";
-"SUBSCRIPT_NAME" = "Subscriptions";
-"LIB_NAME" = "Library (You)";
+"FORSTED_TAB_BAR" = "Translucent tab bar";
+"ENABLED" = "Enabled";
"HIDE_TAB_INDI" = "Hide tab indicators";
"HIDE_TAB_INDI_DESC" = "Hide all tab indicators in the tab bar.";
"HIDE_TAB_LABELS" = "Hide tab labels";
"HIDE_TAB_LABELS_DESC" = "Hide all tab labels in the tab bar.";
-"HIDE_HOME_TAB" = "Hide Home tab";
-"HIDE_HOME_TAB_DESC" = "Hide Home tab in the tab bar.";
-"HIDE_SHORTS_TAB" = "Hide Shorts tab";
-"HIDE_SHORTS_TAB_DESC" = "Hide Shorts tab in the tab bar.";
-"HIDE_CREATE_BUTTON" = "Hide Create button";
-"HIDE_CREATE_BUTTON_DESC" = "Hide Create button in the tab bar.";
-"HIDE_SUBSCRIPT_TAB" = "Hide Subscriptions tab";
-"HIDE_SUBSCRIPT_TAB_DESC" = "Hide Subscriptions tab in the tab bar.";
+"HOME_TAB" = "Home";
+"SHORTS_TAB" = "Shorts";
+"CREATE_TAB" = "Create";
+"SUBSCRIPTIONS_TAB" = "Subscriptions";
+"LIBRARY_TAB" = "Library (You)";
+"HISTORY_TAB" = "History";
+"GAMING_TAB" = "Gaming";
+"SPORTS_TAB" = "Sports";
+"NOTI_TAB" = "Notifications";
+"NEWS_TAB" = "News";
+"MUSIC_TAB" = "Music";
+"WATCH_LATER_TAB" = "Watch later";
+"PLAYLIST_TAB" = "Playlists";
+"LIKE_TAB" = "Liked videos";
+"LIVE_TAB" = "Live";
+"POST_TAB" = "Posts";
+"VIDEO_TAB" = "Your videos";
+"MOVIE_TAB" = "Movies";
+"COURSE_TAB" = "Courses";
+"MINIGAME_TAB" = "Playables";
+"MANAGE_TABS" = "Manage tabs";
+"MANAGE_TABS_DESC" = "Reorder and toggle tabs";
+"TAB_REORDER_HINT" = "Drag to reorder. Toggle to show/hide.\nTab settings are also accessible by long-pressing the first tab in the tab bar.";
+"TAB_LIMIT" = "Tab limit";
+"TAB_LIMIT_DESC" = "Only 6 tabs are allowed.";
+"ZERO_TAB_DESC" = "At least 1 tab has to be enabled.";
"MISCELLANEOUS" = "Miscellaneous";
"BACKGROUND_PLAYBACK" = "Background playback";
"BACKGROUND_PLAYBACK_DESC" = "Enables background playback for videos.";
"DISABLES_SHORTS_PIP" = "Disables Shorts PiP";
"DISABLES_SHORTS_PIP_DESC" = "Try to disables Shorts PiP, this might not 100% disables it.";
+"DISABLE_HINTS" = "Disable hints";
+"DISABLE_HINTS_DESC" = "Disable the tutorial hints and interactive pop-ups.";
"BLOCK_UPGRADE_DIALOGS" = "Block upgrade dialogs";
"BLOCK_UPGRADE_DIALOGS_DESC" = "Block the YouTube dialogs that ask you to upgrade the app.";
"ARE_YOU_THERE_DIALOG" = "Hide 'Are you there?' dialog";
@@ -247,7 +379,109 @@
"DISABLES_SNACK_BAR_DESC" = "Disables snack bar when pressing some buttons or doing something in the app.";
"HIDE_STARTUP_ANIMATIONS" = "Hide YouTube's startup animations";
"HIDE_STARTUP_ANIMATIONS_DESC" = "Hide YouTube's startup animations when opening the app.";
-"HIDE_PLAY_IN_NEXT_QUEUE" = "Hide 'Play next in queue'";
-"HIDE_PLAY_IN_NEXT_QUEUE_DESC" = "Hide the 'Play next in queue' option in the flyout menu.";
"HIDE_LIKE_DISLIKE_VOTES" = "Hide like/dislike votes";
-"HIDE_LIKE_DISLIKE_VOTES_DESC" = "Hide messages that pop up when you like/dislike videos.";
\ No newline at end of file
+"HIDE_LIKE_DISLIKE_VOTES_DESC" = "Hide messages that pop up when you like/dislike videos.";
+"FLYOUT_MENU" = "Flyout menu";
+"REMOVE_PLAY_IN_NEXT_QUEUE_OPTION" = "Removes 'Play next in queue' option";
+"REMOVE_PLAY_IN_NEXT_QUEUE_OPTION_DESC" = "Removes 'Play next in queue' option from the flyout menu.";
+"REMOVE_DOWNLOAD_OPTION" = "Removes 'Download video' option";
+"REMOVE_DOWNLOAD_OPTION_DESC" = "Removes 'Download video' option from the flyout menu.";
+"REMOVE_WATCH_LATER_OPTION" = "Removes 'Save to Watch later' option";
+"REMOVE_WATCH_LATER_OPTION_DESC" = "Removes 'Save to Watch Later' option from the flyout menu.";
+"REMOVE_SAVE_OPTION" = "Removes 'Save to playlist' option";
+"REMOVE_SAVE_OPTION_DESC" = "Removes 'Save to playlist' option from the flyout menu.";
+"REMOVE_REMOVE_FROM_PLAYLIST_OPTION" = "Removes 'Remove from playlist' option";
+"REMOVE_REMOVE_FROM_PLAYLIST_OPTION_DESC" = "Removes 'Remove from playlist' option from the flyout menu.";
+"REMOVE_SHARE_OPTION" = "Removes 'Share' option";
+"REMOVE_SHARE_OPTION_DESC" = "Removes 'Share' option from the flyout menu.";
+"REMOVE_NOT_INTERESTED_OPTION" = "Removes 'Not interested' option";
+"REMOVE_NOT_INTERESTED_OPTION_DESC" = "Removes 'Not interested' option from the flyout menu.";
+"REMOVE_DONT_RECOMMEND_OPTION" = "Removes 'Don't recommend channel' option";
+"REMOVE_DONT_RECOMMEND_OPTION_DESC" = "Removes 'Don't recommend channel' option from the flyout menu.";
+"REMOVE_INFO_OPTION" = "Removes 'Why are you seeing this?' option";
+"REMOVE_INFO_OPTION_DESC" = "Removes 'Why are you seeing this?' option from the flyout menu.";
+"REMOVE_FILTER_OPTION" = "Removes 'Search filters' option";
+"REMOVE_FILTER_OPTION_DESC" = "Removes 'Search filters' option from the flyout menu.";
+"REMOVE_REPORT_OPTION" = "Removes 'Report' option";
+"REMOVE_REPORT_OPTION_DESC" = "Removes 'Report' option from the flyout menu.";
+"REMOVE_YOUTUBE_MUSIC_OPTION" = "Removes 'Listen with YouTube Music' option";
+"REMOVE_YOUTUBE_MUSIC_OPTION_DESC" = "Removes 'Listen with YouTube Music' option from the flyout menu.";
+"REMOVE_FEED_BACK_OPTION" = "Removes 'Send feedback' option";
+"REMOVE_FEED_BACK_OPTION_DESC" = "Removes 'Send feedback' option from the flyout menu.";
+"REMOVE_CAST_OPTION" = "Removes 'Watch on TV' option";
+"REMOVE_CAST_OPTION_DESC" = "Removes 'Watch on TV' option from the flyout menu.";
+"REMOVE_SHUFFLE_OPTION" = "Removes 'Shuffle' option";
+"REMOVE_SHUFFLE_OPTION_DESC" = "Removes 'Shuffle' option from the flyout menu.";
+"REMOVE_UN_SUB_OPTION" = "Removes 'Unsubscribe' option";
+"REMOVE_UN_SUB_OPTION_DESC" = "Removes 'Unsubscribe' option from the flyout menu.";
+"REMOVE_HIDE_FROM_PLAYLIST_OPTION" = "Removes 'Hide video from playlist' option";
+"REMOVE_HIDE_FROM_PLAYLIST_OPTION_DESC" = "Removes 'Hide video from playlist' option from the flyout menu.";
+"REMOVE_HELP_OPTION" = "Removes 'Help & feedback' option";
+"REMOVE_HELP_OPTION_DESC" = "Removes 'Help & feedback' option from the flyout menu.";
+"REMOVE_NOTIFY_OPTION" = "Removes 'Notify me' option";
+"REMOVE_NOTIFY_OPTION_DESC" = "Removes 'Notify me' option from the flyout menu.";
+"REMOVE_CLEARSCREEN_OPTION" = "Removes 'Clear Screen' option";
+"REMOVE_CLEARSCREEN_OPTION_DESC" = "Removes 'Clear Screen' option from the flyout menu.";
+"HIDE_COMMU_GUIDE" = "Hide community guidelines";
+"HIDE_COMMU_GUIDE_DESC" = "Hide community guidelines header.";
+"DISABLES_RTL" = "Disable RTL formatting";
+"DISABLES_RTL_DESC" = "Forcefully displays text in left-to-right (LTR) format for languages that are initially displayed in right-to-left (RTL).";
+"DEVICE_UI" = "Interface style";
+
+"SB_DESCRIPTION" = "Skip sponsored segments automatically using community-submitted data.";
+"SB_ENABLE" = "Enable SponsorBlock";
+"SB_ENABLE_DESC" = "Activate the SponsorBlock extension to skip sponsored segments.";
+"SB_SHOW_BUTTON" = "Show overlay button";
+"SB_SHOW_BUTTON_DESC" = "Display a SponsorBlock toggle button in the player overlay.";
+"SB_SHOW_NOTIFICATIONS" = "Show notifications";
+"SB_SHOW_NOTIFICATIONS_DESC" = "Display notifications when segments are skipped. Notifications for manual-skip categories are always shown.";
+"SB_HAPTIC_FEEDBACK" = "Haptic feedback";
+"SB_HAPTIC_FEEDBACK_DESC" = "Provide haptic feedback when a segment is skipped.";
+"SB_SEGMENTS_IN_PLAYER" = "Segments in player";
+"SB_SEGMENTS_IN_PLAYER_DESC" = "Display colored segments on the progress bar of the main video player.";
+"SB_SEGMENTS_IN_FEED" = "Segments in feed";
+"SB_SEGMENTS_IN_FEED_DESC" = "Display colored segments on the progress bar of players in the feed.";
+"SB_SEGMENTS_IN_MINIPLAYER" = "Segments in mini-player";
+"SB_SEGMENTS_IN_MINIPLAYER_DESC" = "Display colored segments on the progress bar of the mini-player.";
+"SB_SHOW_DURATION" = "Show duration without segments";
+"SB_SHOW_DURATION_DESC" = "Display the total video duration excluding skippable segments.";
+"SB_CATEGORIES_HEADER" = "Segment categories";
+"SB_SEGMENT_COLOR" = "Segment color";
+
+"SB_ACTION_DISABLE" = "Disable";
+"SB_ACTION_AUTO_SKIP" = "Auto-skip";
+"SB_ACTION_ASK" = "Ask before skipping";
+"SB_ACTION_DISPLAY" = "Show on seek bar only";
+"SB_ACTION_SKIP_TO" = "Skip to segment";
+
+"SB_CAT_sponsor" = "Sponsor";
+"SB_CAT_sponsor_DESC" = "Paid promotion for a product or service not directly related to the creator.";
+"SB_CAT_intro" = "Intro";
+"SB_CAT_intro_DESC" = "Intermission or intro animation typically seen at the start of a video.";
+"SB_CAT_outro" = "Endcards";
+"SB_CAT_outro_DESC" = "Endcards, credits, or outros at the end of the video.";
+"SB_CAT_interaction" = "Interaction";
+"SB_CAT_interaction_DESC" = "Reminders to like, subscribe, or follow on social media.";
+"SB_CAT_selfpromo" = "Self-promotion";
+"SB_CAT_selfpromo_DESC" = "Unpaid promotion of the creator's own products, merchandise, or other content.";
+"SB_CAT_music_offtopic" = "Non-music";
+"SB_CAT_music_offtopic_DESC" = "Non-music sections in music videos (e.g. talking, introductions).";
+"SB_CAT_preview" = "Preview";
+"SB_CAT_preview_DESC" = "Recap or preview of content from another video or previous episode.";
+"SB_CAT_hook" = "Hook/Greetings";
+"SB_CAT_hook_DESC" = "Narrated trailers for the upcoming video, greetings and goodbyes.";
+"SB_CAT_poi_highlight" = "Highlight";
+"SB_CAT_poi_highlight_DESC" = "The key moment or highlight point of the video. Use 'Skip to' to jump directly to it.";
+"SB_CAT_filler" = "Filler";
+"SB_CAT_filler_DESC" = "Tangential content, jokes, or filler that is not directly related to the main topic.";
+
+"SB_SKIPPED" = "%@ segment has been skipped";
+"SB_DETECTED" = "%@ segment detected.\nWould you like to skip the segment?";
+"SB_SKIP_NOW" = "Skip";
+"SB_UNSKIP" = "Unskip";
+"SB_JUMPED_TO_HIGHLIGHT" = "Jumped to highlight";
+"SB_JUMP_TO_HIGHLIGHT" = "Highlight available. Jump to the point?";
+"SB_SEGMENT_COLOR_SUFFIX" = "Segment color";
+"SB_SECTION_MAIN" = "Main";
+"SB_SKIP_ALERT_DURATION" = "Skip alert duration";
+"SB_UNSKIP_ALERT_DURATION" = "Unskip alert duration";
\ No newline at end of file
diff --git a/layout/Library/Application Support/YouMod.bundle/ru.lproj/Localizable.strings b/layout/Library/Application Support/YouMod.bundle/ru.lproj/Localizable.strings
index 73913f4c..60e8ad8d 100644
--- a/layout/Library/Application Support/YouMod.bundle/ru.lproj/Localizable.strings
+++ b/layout/Library/Application Support/YouMod.bundle/ru.lproj/Localizable.strings
@@ -1,11 +1,18 @@
-"NOTE" = "ПРИМЕЧАНИЕ: Некоторые функции скрытия могут оставлять пустую область.";
"SETTINGS" = "Settings";
+"INTERFACE" = "Interface";
+
+"LOOP_DISABLED" = "Loop disabled";
+"LOOP_ENABLED" = "Loop enabled";
"NEW_ISSUES" = "Сообщить о проблеме или предложить новую функцию";
"NEW_ISSUES_DESC" = "Нашли ошибку или хотите предложить новую функцию? Нажмите здесь.";
"SOURCE_CODES" = "Исходный код";
"SOURCE_CODES_DESC" = "Посмотреть исходный код!";
+"EXTRA" = "Extra";
+"FIX_PLAYBACK_ISSUES" = "Fix playback issues (Temporary)";
+"FIX_PLAYBACK_ISSUES_DESC" = "Try to fix video playback issues for some users. Turning this on will make the player automatically reloads itself everytime it errors or fails to play the video.";
+
"PERFER_HEADER" = "YouMod perferences";
"PERFER" = "Настройки";
"IMPORT" = "Импорт настроек";
@@ -24,6 +31,7 @@
"ERROR_NO_KEYS_IMPORT" = "Нет доступных ключей для импорта";
"ERROR_NO_KEYS_EXPORT" = "Нет доступных ключей для экспорта";
"OVERRIDE" = "Это перезапишет текущие настройки. Продолжить?";
+"DISABLED" = "Disabled";
"CACHE" = "Кэш";
"CLEARCACHE" = "Очистить кэш";
@@ -32,12 +40,81 @@
"AUTO_CLEARCACHE_DESC" = "Автоматически очищать кэш приложения при запуске.";
"DOWNLOADING" = "Downloading";
-"DOWNLOAD_MANAGER" = "Download Manager";
-"DOWNLOAD_MANAGER_DESC" = "Replaces YouTube's video downloader with YouMod one. With ablity to downloading videos, audios, captions, and thumbnails";
+"DOWNLOAD_MANAGER" = "Download manager";
+"DOWNLOAD_MANAGER_DESC" = "Replaces YouTube's video downloader with YouMod one. With ablity to downloading videos, audios, captions, and thumbnails.";
"DOWNLOAD_SAVE_PHOTOS" = "Automatically save to Photos";
"DOWNLOAD_SAVE_PHOTOS_DESC" = "Automatically saves downloaded videos and images directly to your Photos app.";
-"DOWNLOAD_DRC_AUDIO" = "Prefer DRC Audio";
-"DOWNLOAD_DRC_AUDIO_DESC" = "Prioritizes downloading audio tracks with Dynamic Range Compression for more consistent volume levels.";
+"ADD_SHORTS_DOWNLOAD" = "Adds a download button to Shorts";
+"ADD_SHORTS_DOWNLOAD_DESC" = "Adds a download button to Shorts video overlay.";
+"USE_ORIG_AUDIO" = "Automatically uses original audio track";
+"USE_ORIG_AUDIO_DESC" = "Automatically uses original audio track instead of showing a dialog.";
+"DOWNLOAD_SHORTS" = "Download Shorts";
+"DOWNLOAD_SHORTS_DESC" = "Choose Shorts quality";
+"COPY_VID_INFO" = "Copy video informations";
+"COPY_VID_INFO_DESC" = "Choose what to copy";
+"COPY_ALL_VID_INFO" = "Copy all";
+"COPY_ALL_VID_INFO_DESC" = "Copy all video informations";
+"COPY_AUTHOR" = "Copy author";
+"COPY_AUTHOR_DESC" = "Copy the video author";
+"COPY_TITLE" = "Copy title";
+"COPY_TITLE_DESC" = "Copy the video title";
+"COPY_DESCRIPTION" = "Copy description";
+"COPY_DESCRIPTION_DESC" = "Copy the video description";
+"COPY_URL" = "Copy video link";
+"COPY_URL_TIMESTAMP" = "Copy video link with timestamp";
+"URL_COPIED" = "Copied video link";
+"URL_TIMESTAMP_COPIED" = "Copied video link with timestamp";
+"ERROR_VIDEOID" = "Cannot get the video ID";
+"ERROR_ADS" = "Cannot copy video link while playing ads";
+"COPIED_AUTHOR" = "Copied video author";
+"COPIED_TITLE" = "Copied video title";
+"COPIED_DESCRIPTION" = "Copied video description";
+"SAVE_THUMBNAIL" = "Save thumbnail";
+"SAVE_THUMBNAIL_DESC" = "Save to Photos";
+"DOWNLOAD_CAPTIONS" = "Download captions";
+"DOWNLOAD_CAPTIONS_DESC" = "Save subtitles as .VTT file";
+"DOWNLOAD_AUDIO" = "Download audio";
+"DOWNLOAD_AUDIO_DESC" = "Download audio as .m4a file";
+"DOWNLOAD_VIDEO" = "Download video";
+"DOWNLOAD_VIDEO_DESC" = "Choose video quality";
+"OPEN_VID_BEFORE" = "Open a video before using the download manager";
+"NO_CAPTIONS_URL" = "No valid caption URLs found";
+"NO_AUDIO_URL" = "No audio URL found";
+"NO_STREAM_URL" = "No stream URL found";
+"CAPTIONS_FAILED" = "Failed to download captions";
+"NO_CAPTIONS" = "No captions available for this video";
+"NO_AUDIO_STREAM_FOUND" = "No audio streams found";
+"NO_VID_AUDIO_STREAM_FOUND" = "No video/audio streams found";
+"COPIED_VID_INFO" = "Copied all video informations";
+"SAVED_TO_PHOTOS" = "Saved to Photos";
+"SAVE_FAILED" = "Save failed";
+"PHOTO_ACCESS_DENINED" = "Photos access denied";
+"THUMBNAIL_FAILED" = "Thumbnail failed";
+"DOWNLOADING_THUMBNAIL" = "Downloading thumbnail...";
+"DOWNLOADING_AUDIO" = "Downloading audio...";
+"DOWNLOADING_VIDEO" = "Downloading video...";
+"DOWNLOADING_CAPTIONS" = "Downloading captions...";
+"NO_THUMBNAIL_FOUND" = "No thumbnail found";
+"DOWNLOAD_FAILED" = "Download failed";
+"AUDIO_SAVED" = "Audio saved";
+"DOWNLOAD_COMPLETED" = "Download completed";
+"CANNOT_SAVE_TO_PHOTOS" = "Cannot save to Photos";
+"FINA_VIDEO" = "Finalizing video...";
+"MERGING_VID_FALLBACK" = "Merging video with fallback...";
+"MERGING_VID" = "Merging video...";
+"CONVERTING" = "Converting to";
+"ALREADY_DOWNLOADING" = "Already downloading";
+"DOWNLOAD_CANCELLED" = "Download cancelled";
+"CANCEL" = "Cancel";
+"TRIMMING_AUDIO" = "Trimming audio...";
+"COPY_THUMBNAIL" = "Copy thumbnail";
+"COPY_THUMBNAIL_DESC" = "Copy to clipboard";
+"SHOW_THUMBNAIL" = "Show thumbnail";
+"SHOW_THUMBNAIL_DESC" = "Show in a dialog";
+"THUMBNAIL_OPTIONS" = "Download thumbnail";
+"THUMBNAIL_OPTIONS_DESC" = "Show more options";
+"COPIED_TO_CLIPBOARD" = "Copied to clipboard";
+"COPY" = "Copy";
"APPEARANCE" = "Внешний вид";
"OLED_THEME" = "OLED dark mode";
@@ -46,6 +123,8 @@
"OLED_KEYBOARD_DESC" = "Использовать для клавиатуры чисто чёрный цвет в тёмной теме.";
"NAVBAR" = "Панель навигации";
+"STICKY_NAVBAR" = "Sticky Navigation bar";
+"STICKY_NAVBAR_DESC" = "Pins the Navigation bar so that it remains visible when scrolling down.";
"HIDE_YT_LOGO" = "Скрыть логотип YouTube";
"HIDE_YT_LOGO_DESC" = "Скрыть логотип YouTube на панели навигации.";
"PREMIUM_LOGO" = "Использовать логотип YouTube Premium";
@@ -62,32 +141,76 @@
"FEED" = "Лента";
"HIDE_SUBBAR" = "Скрыть верхнюю панель";
"HIDE_SUBBAR_DESC" = "Скрыть верхнюю панель в ленте.";
+"HIDE_HORI_SHELF" = "Remove horizontal feeds";
+"HIDE_HORI_SHELF_DESC" = "Removes all horizontal feeds (News, Continue Watching, Posts collection, etc.) from the feed, except Playables.";
"HIDE_MUSIC_PLAYLISTS" = "Скрыть генератор музыкальных плейлистов";
"HIDE_MUSIC_PLAYLISTS_DESC" = "Скрыть генератор музыкальных плейлистов в ленте.";
"HIDE_FEED_POST" = "Hide posts";
"HIDE_FEED_POST_DESC" = "Hide posts in feed.";
+"HIDE_SURVEYS" = "Hide surveys";
+"HIDE_SURVEYS_DESC" = "Hide video surveys that YouTube shows sometimes.";
"HIDE_SHORTS_SHELF" = "Скрыть блок Shorts";
"HIDE_SHORTS_SHELF_DESC" = "Скрыть блок Shorts в ленте, включая вкладку «Подписки».";
+"KEEP_SHORTS_SUBSCRIPT" = "Keep Shorts in Subscriptions";
+"KEEP_SHORTS_SUBSCRIPT_DESC" = "Prevents removing Shorts from Subscriptions tab.";
"HIDE_SEARCH_HISTORY" = "Скрыть историю поиска";
"HIDE_SEARCH_HISTORY_DESC" = "Скрыть предыдущие поисковые запросы и подсказки при использовании строки поиска.\nПРИМЕЧАНИЕ: История поиска по-прежнему будет доступна в других клиентах YouTube.";
-"HIDE_SUB_BUTTON" = "Скрыть кнопку подписки";
-"HIDE_SUB_BUTTON_DESC" = "Скрыть кнопку подписки на каналах и в описаниях видео.";
-"HIDE_SHOP_BUTTON" = "Скрыть кнопку покупок";
-"HIDE_SHOP_BUTTON_DESC" = "Скрыть кнопку покупок/магазина в описаниях каналов.";
-"HIDE_MEMBER_BUTTON" = "Скрыть кнопку спонсорства";
-"HIDE_MEMBER_BUTTON_DESC" = "Скрыть кнопку «Стать спонсором» на каналах и в описаниях видео.";
+"HIDE_PLAYABLES" = "Remove playables";
+"HIDE_PLAYABLES_DESC" = "Removes playables (mini-games) from the feed.";
"PLAYER" = "Плеер";
+"PLAYBACK_SPEED" = "Playback speed";
+"CONTROL_CENTER" = "Control Center";
+"PLAYER_ACTIONS" = "Player actions";
+"GESTURE_HEADER" = "Gestures";
+"QUALITY_WIFI" = "Playback quality on WiFi";
+"QUALITY_WIFI_DESC" = "Automatically applies the selected quality when streaming over Wi-Fi.";
+"QUALITY_CELLULAR" = "Playback quality on Cellular";
+"QUALITY_CELLULAR_DESC" = "Automatically applies the selected quality when streaming over mobile data.";
+"QUALITY_LOW_POWER" = "Playback quality on Power saver mode";
+"QUALITY_LOW_POWER_DESC" = "Automatically applies the selected quality when when Low power mode is enabled on your device.";
+"DEFAULT" = "Default";
+"BEST" = "Best";
+"AUDIO_TRACK" = "Preferred audio track";
+"ORIGINAL" = "Original";
+"SELECT_MANUALLY" = "Select manually";
+"AUDIO_TRACK_SELECT" = "Selected audio track";
+"AUDIO_TRACK_SELECT_DESC" = "Specifies the preferred audio track language to use for playback, if available.";
+"NO_AUTO_DUBBED" = "Uses original audio track";
+"NO_AUTO_DUBBED_DESC" = "Uses original audio track if the preferred language track is auto-dubbed or not found.";
+"CAPTION_TRACK" = "Preferred captions";
+"CAPTION_TRACK_SELECT" = "Selected captions";
+"CAPTION_TRACK_SELECT_DESC" = "Specifies the preferred captions language to display, if available.";
+"DISABLES_CAPTION_TRACK" = "Automatically disable captions";
+"DISABLES_CAPTION_TRACK_DESC" = "Automatically disable captions if the preferred language is auto-generated or not found.";
+"DEFAULT_SPEED" = "Default playback rate";
+"DEFAULT_SPEED_DESC" = "Overrides the default playback speed.";
+"SPEED_BUTTON" = "Speed controls button";
+"SPEED_BUTTON_DESC" = "Displays a speed controls button on overlay.";
+"SKIP_BACKWARD" = "Skip backward";
+"SKIP_BACKWARD_DESC" = "Replaces the previous track button in Control Center with a skip backward button by a set number of seconds.";
+"SKIP_FORWARD" = "Skip forward";
+"SKIP_FORWARD_DESC" = "Replaces the next track button in Control Center with a skip forward button by a set number of seconds.";
+"REWIND_SECONDS" = "Rewind seconds";
+"FORWARD_SECONDS" = "Fast-forward seconds";
+"MUTE_BUTTON" = "Mute button";
+"MUTE_BUTTON_DESC" = "Displays a button to mute or unmute the volume on the overlay.";
+"SHARE_BUTTON" = "Share button";
+"SHARE_BUTTON_DESC" = "Displays a button to share the video link on the overlay.";
+"LOOP_BUTTON" = "Loop button";
+"LOOP_BUTTON_DESC" = "Displays a button to loop the video on the overlay.";
+"CAPTION_BUTTON" = "Captions button";
+"CAPTION_BUTTON_DESC" = "Displays a button to copy the video captions on the overlay.";
+"HOLD_TO_SPEED" = "Hold to speed";
+"HOLD_TO_SPEED_DESC" = "Uses custom hold to speed playback rate.";
"HIDE_AUTOPLAY" = "Скрыть переключатель автовоспроизведения";
"HIDE_AUTOPLAY_DESC" = "Скрыть переключатель автовоспроизведения в интерфейсе видео.";
"HIDE_CAPTIONS_BUTTON" = "Скрыть кнопку субтитров";
"HIDE_CAPTIONS_BUTTON_DESC" = "Скрыть кнопку субтитров в интерфейсе видео.";
"HIDE_CAST_BUTTON_PLAYER" = "Скрыть кнопку трансляции";
"HIDE_CAST_BUTTON_PLAYER_DESC" = "Скрыть кнопку трансляции в интерфейсе видео.";
-"HIDE_PREV_BUTTON" = "Скрыть кнопку «Назад/Предыдущее»";
-"HIDE_PREV_BUTTON_DESC" = "Скрыть кнопку «Назад/Предыдущее» в интерфейсе видео.";
-"HIDE_NEXT_BUTTON" = "Скрыть кнопку «Далее/Пропустить»";
-"HIDE_NEXT_BUTTON_DESC" = "Скрыть кнопку «Далее/Пропустить» в интерфейсе видео.";
+"HIDE_NEXT_AND_PREV_BUTTON" = "Hide previous and next buttons";
+"HIDE_NEXT_AND_PREV_BUTTON_DESC" = "Hide previous and next buttons in the video overlay.";
"REPLACE_PREVNEXT_BUTTONS" = "Replace Previous/Next buttons";
"REPLACE_PREVNEXT_BUTTONS_DESC" = "Replaces the Previous and Next video buttons in the player with Rewind and Fast Forward buttons.";
"REMOVE_DARK_OVERLAY" = "Убрать тёмное затемнение";
@@ -102,6 +225,12 @@
"HIDE_PAID_OVERLAY_DESC" = "Hide 'Includes paid promotion' in the video overlay.";
"HIDE_WATERMARK" = "Скрыть водяной знак";
"HIDE_WATERMARK_DESC" = "Скрыть водяной знак канала внизу интерфейса видео.";
+"PAUSE_ON_OVERLAY" = "Pause on overlay";
+"PAUSE_ON_OVERLAY_DESC" = "Sets playback on pause if overlay appears.";
+"DISABLES_SNAP_TO_CHAPTER" = "Disable snap to chapters";
+"DISABLES_SNAP_TO_CHAPTER_DESC" = "Prevents the scrubbar stick to the chapters.";
+"DISABLES_ENGAGE_PANEL" = "Disables engagement panel in fullscreen";
+"DISABLES_ENGAGE_PANEL_DESC" = "Prevent engagement panel showing in fullscreen.";
"GESTURES" = "Enable gestures controls";
"GESTURES_DESC" = "Swipe up or down on the left/right edges of the video screen to adjust brightness, volume, or playback speed.";
"GESTURE_AREA" = "Gesture activation area";
@@ -136,6 +265,8 @@
"DISABLES_SHOW_REMAINING_DESC" = "Отключить функцию показа оставшегося времени при нажатии на тайм-код.";
"ALWAYS_SHOW_REMAINING" = "Всегда показывать оставшееся время";
"ALWAYS_SHOW_REMAINING_DESC" = "По умолчанию всегда отображать оставшееся время в интерфейсе плеера.";
+"COPY_TIMESTAMP_ON_PAUSE" = "Copy timestamped links";
+"COPY_TIMESTAMP_ON_PAUSE_DESC" = "Allows to copy timestamped link to the clipboard by pressing pause button.";
"SHOW_REMAINING_EXTRA" = "Show extra remaining time";
"SHOW_REMAINING_EXTRA_DESC" = "Adds the estimated finish time to the label in the video overlay.";
"HIDE_FULLSCREEN_ACTIONS" = "Скрыть действия в полноэкранном режиме";
@@ -148,93 +279,97 @@
"HIDE_CONTENT_WARNING_DESC" = "Автоматически пропускать предупреждение о деликатном содержимом в некоторых видео.";
"AUTO_FULLSCREEN" = "Автополноэкранный режим";
"AUTO_FULLSCREEN_DESC" = "Автоматически переходить в полноэкранный режим при запуске воспроизведения видео.";
-"AUTO_DISABLES_CAPTION" = "Auto disable captions";
-"AUTO_DISABLES_CAPTION_DESC" = "Automatically turns off closed captions (CC) whenever a new video starts playing.";
"PORTRAIT_FULLSCREEN" = "Portrait fullscreen mode";
"PORTRAIT_FULLSCREEN_DESC" = "Enables portrait fullscreen mode support.";
"OLD_QUALITY_PICKER" = "Использовать старый выбор качества";
"OLD_QUALITY_PICKER_DESC" = "Вернуть классическое меню выбора качества видео, в котором сразу отображаются все разрешения.";
"EXTRA_SPEED" = "Extra speed options";
"EXTRA_SPEED_DESC" = "Adds more video playback speed options up to 10x.";
-"DISABLE_HINTS" = "Disable hints";
-"DISABLE_HINTS_DESC" = "Disable the tutorial hints and interactive pop-ups.";
"FORCE_MINIPLAYER" = "Force miniplayer";
"FORCE_MINIPLAYER_DESC" = "Allows all videos, including those made for kids, to play in the miniplayer when dismissing the player.";
"FORCE_SEEKBAR" = "Always show progress bar";
"FORCE_SEEKBAR_DESC" = "Ensures the progress bar (seekbar) remains visible at all times while in fullscreen mode.";
-"HIDE_LIKE_BUTTON" = "Скрыть кнопку «Нравится»";
-"HIDE_LIKE_BUTTON_DESC" = "Скрыть кнопку «Нравится» на панели действий под видео.";
-"HIDE_DISLIKE_BUTTON" = "Скрыть кнопку «Не нравится»";
-"HIDE_DISLIKE_BUTTON_DESC" = "Скрыть кнопку «Не нравится» на панели действий под видео.";
-"HIDE_SHARE_BUTTON" = "Скрыть кнопку «Поделиться»";
-"HIDE_SHARE_BUTTON_DESC" = "Скрыть кнопку «Поделиться» на панели действий под видео.";
-"HIDE_DOWNLOAD_BUTTON" = "Скрыть кнопку «Скачать»";
-"HIDE_DOWNLOAD_BUTTON_DESC" = "Скрыть кнопку «Скачать» на панели действий под видео.";
-"HIDE_CLIP_BUTTON" = "Скрыть кнопку «Клип»";
-"HIDE_CLIP_BUTTON_DESC" = "Скрыть кнопку «Клип» на панели действий под видео.";
-"HIDE_REMIX_BUTTON" = "Скрыть кнопку Remix";
-"HIDE_REMIX_BUTTON_DESC" = "Скрыть кнопку Remix на панели действий под видео.";
-"HIDE_SAVE_BUTTON" = "Скрыть кнопку «Сохранить»";
-"HIDE_SAVE_BUTTON_DESC" = "Скрыть кнопку «Сохранить» на панели действий под видео.";
+"TAP_TO_SEEK" = "Tap to seek";
+"TAP_TO_SEEK_DESC" = "Quickly jump to any point by tapping the progress bar.";
+"DISABLES_ZOOM" = "Disable free zoom gesture";
+"DISABLES_ZOOM_DESC" = "Disables new free zoom gestures.";
+"USES_24_HOURS_TIME" = "24-hour format";
+"USES_24_HOURS_TIME_DESC" = "Shows end time in 24-hour format.";
+"PAUSE_TWO_FINGERS" = "Pause using two fingers";
+"PAUSE_TWO_FINGERS_DESC" = "Pause/Resume playback by tapping on the screen using two fingers.";
+"HIDE_ACTION_BAR" = "Hide actions bar";
+"HIDE_ACTION_BAR_DESC" = "Removes the bar containing the like, dislike, share buttons etc. below the video player.";
+"HIDE_COMMENTS_SECTION" = "Hide comments section";
+"HIDE_COMMENTS_SECTION_DESC" = "Completely removes the entire comments section.";
+"HIDE_COMMENTS_PREVIEW" = "Hide preview comments";
+"HIDE_COMMENTS_PREVIEW_DESC" = "Removes the preview box for the top comment while keeping the full section expandable.";
+"USE_ANOTHER_MINIPLAYER" = "Uses another miniplayer style";
+"USE_ANOTHER_MINIPLAYER_DESC" = "Uses another miniplayer style redesigned from older YT versions.";
"SHORTS" = "Shorts";
-"HIDE_SHORTS_LIKE_BUTTON" = "Скрыть кнопку «Нравится»";
-"HIDE_SHORTS_LIKE_BUTTON_DESC" = "Скрыть кнопку «Нравится» в плеере Shorts.";
-"HIDE_SHORTS_DISLIKE_BUTTON" = "Скрыть кнопку «Не нравится»";
-"HIDE_SHORTS_DISLIKE_BUTTON_DESC" = "Скрыть кнопку «Не нравится» в плеере Shorts.";
-"HIDE_SHORTS_COMMENT_BUTTON" = "Скрыть кнопку комментариев";
-"HIDE_SHORTS_COMMENT_BUTTON_DESC" = "Скрыть кнопку комментариев в плеере Shorts.";
-"HIDE_SHORTS_SHARE_BUTTON" = "Скрыть кнопку «Поделиться»";
-"HIDE_SHORTS_SHARE_BUTTON_DESC" = "Скрыть кнопку «Поделиться» в плеере Shorts.";
-"HIDE_SHORTS_REMIX_BUTTON" = "Скрыть кнопку Remix";
-"HIDE_SHORTS_REMIX_BUTTON_DESC" = "Скрыть кнопку Remix в плеере Shorts.";
-"HIDE_METADATA_BUTTON" = "Скрыть кнопку метаданных звука";
-"HIDE_METADATA_BUTTON_DESC" = "Скрыть кнопку, которая показывает источник звука или музыкальные метаданные в плеере Shorts.";
+"SHORTS_ONLY" = "Shorts only mode";
+"SHORTS_ONLY_DESC" = "Limits YouTube functionality to viewing Shorts only.";
+"SHORTS_ACTION" = "Shorts action after the video ends";
+"LOOP" = "Loop video";
+"SKIP_TO_NEXT_SHORTS" = "Skip to next video";
+"PAUSE_SHORTS" = "Stop video";
+"REMOVE_LIVE_SHORTS" = "Remove live videos";
+"REMOVE_LIVE_SHORTS_DESC" = "Removes live videos from the feed.";
"HIDE_SHORTS_PRODUCT" = "Скрыть товары";
"HIDE_SHORTS_PRODUCT_DESC" = "Скрыть отмеченные товары и ссылки на покупки в интерфейсе Shorts.";
"HIDE_SHORTS_RECBAR" = "Скрыть панель рекомендаций";
"HIDE_SHORTS_RECBAR_DESC" = "Скрыть панель предлагаемых действий в плеере Shorts (например, «Попробовать этот звук», «Покупки»).";
-"HIDE_SHORTS_COMMIT" = "Скрыть панель комиссий";
-"HIDE_SHORTS_COMMIT_DESC" = "Скрыть панель, которая показывает, что видео приносит комиссию, в плеере Shorts.";
-"HIDE_SHORTS_SUBSCRIPT_BUTTON" = "Скрыть кнопку подписок";
-"HIDE_SHORTS_SUBSCRIPT_BUTTON_DESC" = "Скрыть кнопку «Подписки» в верхней части плеера Shorts.";
-"HIDE_SHORTS_LIVE_BUTTON" = "Скрыть кнопку прямого эфира";
-"HIDE_SHORTS_LIVE_BUTTON_DESC" = "Скрыть кнопку прямого эфира в верхней части плеера Shorts.";
-"HIDE_SHORTS_LENS_BUTTON" = "Hide lens button";
-"HIDE_SHORTS_LENS_BUTTON_DESC" = "Hide the Lens button at the top of the Shorts player.";
-"HIDE_SHORTS_TRENDS_BUTTON" = "Hide trends button";
-"HIDE_SHORTS_TRENDS_BUTTON_DESC" = "Hide the Trends button at the top of the Shorts player.";
-"HIDE_SHORTS_TO_VIDEO" = "Скрыть ссылку на полное видео";
-"HIDE_SHORTS_TO_VIDEO_DESC" = "Скрыть ссылку, которая ведёт из Shorts к полному видео.";
"ENABLES_SHORTS_QUALITY" = "Включить выбор качества в Shorts";
"ENABLES_SHORTS_QUALITY_DESC" = "Принудительно включить меню выбора качества видео в Shorts.";
"SHOW_SHORTS_SEEKBAR" = "Всегда показывать полосу перемотки";
"SHOW_SHORTS_SEEKBAR_DESC" = "Сделать полосу прогресса/перемотки внизу плеера Shorts всегда видимой.";
+"REMOVE_POSTS_SHORTS" = "Remove posts";
+"REMOVE_POSTS_SHORTS_DESC" = "Remove posts from the feed.";
+"SHORTS_FULLSCREEN" = "Pinch to Fullscreen";
+"SHORTS_FULLSCREEN_DESC" = "Manages visibility of the overlay with pinch in and pinch out gestures, displaying Shorts in fullscreen mode.";
"TABBAR" = "Панель вкладок";
"DEFAULT_TAB" = "Вкладка по умолчанию при запуске";
-"DEFAULT_TAB_DESC" = "Вы можете выбрать вкладку, которая будет открываться при запуске приложения.";
-"HOME_NAME" = "Главная";
-"SUBSCRIPT_NAME" = "Подписки";
-"LIB_NAME" = "Библиотека (Вы)";
+"FORSTED_TAB_BAR" = "Translucent tab bar";
+"ENABLED" = "Enabled";
"HIDE_TAB_INDI" = "Скрыть индикаторы вкладок";
"HIDE_TAB_INDI_DESC" = "Скрыть все индикаторы вкладок на панели вкладок.";
"HIDE_TAB_LABELS" = "Скрыть подписи вкладок";
"HIDE_TAB_LABELS_DESC" = "Скрыть все подписи вкладок на панели вкладок.";
-"HIDE_HOME_TAB" = "Скрыть вкладку «Главная»";
-"HIDE_HOME_TAB_DESC" = "Скрыть вкладку «Главная» на панели вкладок.";
-"HIDE_SHORTS_TAB" = "Скрыть вкладку Shorts";
-"HIDE_SHORTS_TAB_DESC" = "Скрыть вкладку Shorts на панели вкладок.";
-"HIDE_CREATE_BUTTON" = "Скрыть кнопку «Создать»";
-"HIDE_CREATE_BUTTON_DESC" = "Скрыть кнопку «Создать» на панели вкладок.";
-"HIDE_SUBSCRIPT_TAB" = "Скрыть вкладку «Подписки»";
-"HIDE_SUBSCRIPT_TAB_DESC" = "Скрыть вкладку «Подписки» на панели вкладок.";
+"HOME_TAB" = "Главная";
+"SHORTS_TAB" = "Shorts";
+"CREATE_TAB" = "Create";
+"SUBSCRIPTIONS_TAB" = "Подписки";
+"LIBRARY_TAB" = "Библиотека (Вы)";
+"HISTORY_TAB" = "History";
+"GAMING_TAB" = "Gaming";
+"SPORTS_TAB" = "Sports";
+"NOTI_TAB" = "Notifications";
+"NEWS_TAB" = "News";
+"MUSIC_TAB" = "Music";
+"WATCH_LATER_TAB" = "Watch later";
+"PLAYLIST_TAB" = "Playlists";
+"LIKE_TAB" = "Liked videos";
+"LIVE_TAB" = "Live";
+"POST_TAB" = "Posts";
+"VIDEO_TAB" = "Your videos";
+"MOVIE_TAB" = "Movies";
+"COURSE_TAB" = "Courses";
+"MINIGAME_TAB" = "Playables";
+"MANAGE_TABS" = "Manage tabs";
+"MANAGE_TABS_DESC" = "Reorder and toggle tabs";
+"TAB_REORDER_HINT" = "Drag to reorder. Toggle to show/hide.\nTab settings are also accessible by long-pressing the first tab in the tab bar.";
+"TAB_LIMIT" = "Tab limit";
+"TAB_LIMIT_DESC" = "Only 6 tabs are allowed.";
+"ZERO_TAB_DESC" = "At least 1 tab has to be enabled.";
"MISCELLANEOUS" = "Разное";
"BACKGROUND_PLAYBACK" = "Background playback";
"BACKGROUND_PLAYBACK_DESC" = "Enables background playback for videos.";
"DISABLES_SHORTS_PIP" = "Отключить PiP для Shorts";
"DISABLES_SHORTS_PIP_DESC" = "Попробовать отключить режим «картинка в картинке» для Shorts; это может сработать не во всех случаях.";
+"DISABLE_HINTS" = "Disable hints";
+"DISABLE_HINTS_DESC" = "Disable the tutorial hints and interactive pop-ups.";
"BLOCK_UPGRADE_DIALOGS" = "Блокировать диалоги обновления";
"BLOCK_UPGRADE_DIALOGS_DESC" = "Блокировать диалоги YouTube с предложением обновить приложение.";
"ARE_YOU_THERE_DIALOG" = "Скрыть диалог «Вы ещё здесь?»";
@@ -247,7 +382,109 @@
"DISABLES_SNACK_BAR_DESC" = "Отключить всплывающие уведомления при нажатии некоторых кнопок или выполнении действий в приложении.";
"HIDE_STARTUP_ANIMATIONS" = "Скрыть анимации запуска YouTube";
"HIDE_STARTUP_ANIMATIONS_DESC" = "Скрыть анимации запуска YouTube при открытии приложения.";
-"HIDE_PLAY_IN_NEXT_QUEUE" = "Скрыть «Воспроизвести следующим в очереди»";
-"HIDE_PLAY_IN_NEXT_QUEUE_DESC" = "Скрыть пункт «Воспроизвести следующим в очереди» во всплывающем меню.";
"HIDE_LIKE_DISLIKE_VOTES" = "Скрыть уведомления о лайках/дизлайках";
"HIDE_LIKE_DISLIKE_VOTES_DESC" = "Скрыть сообщения, которые появляются, когда вы ставите лайк или дизлайк видео.";
+"FLYOUT_MENU" = "Flyout menu";
+"REMOVE_PLAY_IN_NEXT_QUEUE_OPTION" = "Removes 'Play next in queue' option";
+"REMOVE_PLAY_IN_NEXT_QUEUE_OPTION_DESC" = "Removes 'Play next in queue' option from the flyout menu.";
+"REMOVE_DOWNLOAD_OPTION" = "Removes 'Download video' option";
+"REMOVE_DOWNLOAD_OPTION_DESC" = "Removes 'Download video' option from the flyout menu.";
+"REMOVE_WATCH_LATER_OPTION" = "Removes 'Save to Watch later' option";
+"REMOVE_WATCH_LATER_OPTION_DESC" = "Removes 'Save to Watch Later' option from the flyout menu.";
+"REMOVE_SAVE_OPTION" = "Removes 'Save to playlist' option";
+"REMOVE_SAVE_OPTION_DESC" = "Removes 'Save to playlist' option from the flyout menu.";
+"REMOVE_REMOVE_FROM_PLAYLIST_OPTION" = "Removes 'Remove from playlist' option";
+"REMOVE_REMOVE_FROM_PLAYLIST_OPTION_DESC" = "Removes 'Remove from playlist' option from the flyout menu.";
+"REMOVE_SHARE_OPTION" = "Removes 'Share' option";
+"REMOVE_SHARE_OPTION_DESC" = "Removes 'Share' option from the flyout menu.";
+"REMOVE_NOT_INTERESTED_OPTION" = "Removes 'Not interested' option";
+"REMOVE_NOT_INTERESTED_OPTION_DESC" = "Removes 'Not interested' option from the flyout menu.";
+"REMOVE_DONT_RECOMMEND_OPTION" = "Removes 'Don't recommend channel' option";
+"REMOVE_DONT_RECOMMEND_OPTION_DESC" = "Removes 'Don't recommend channel' option from the flyout menu.";
+"REMOVE_INFO_OPTION" = "Removes 'Why are you seeing this?' option";
+"REMOVE_INFO_OPTION_DESC" = "Removes 'Why are you seeing this?' option from the flyout menu.";
+"REMOVE_FILTER_OPTION" = "Removes 'Search filters' option";
+"REMOVE_FILTER_OPTION_DESC" = "Removes 'Search filters' option from the flyout menu.";
+"REMOVE_REPORT_OPTION" = "Removes 'Report' option";
+"REMOVE_REPORT_OPTION_DESC" = "Removes 'Report' option from the flyout menu.";
+"REMOVE_YOUTUBE_MUSIC_OPTION" = "Removes 'Listen with YouTube Music' option";
+"REMOVE_YOUTUBE_MUSIC_OPTION_DESC" = "Removes 'Listen with YouTube Music' option from the flyout menu.";
+"REMOVE_FEED_BACK_OPTION" = "Removes 'Send feedback' option";
+"REMOVE_FEED_BACK_OPTION_DESC" = "Removes 'Send feedback' option from the flyout menu.";
+"REMOVE_CAST_OPTION" = "Removes 'Watch on TV' option";
+"REMOVE_CAST_OPTION_DESC" = "Removes 'Watch on TV' option from the flyout menu.";
+"REMOVE_SHUFFLE_OPTION" = "Removes 'Shuffle' option";
+"REMOVE_SHUFFLE_OPTION_DESC" = "Removes 'Shuffle' option from the flyout menu.";
+"REMOVE_UN_SUB_OPTION" = "Removes 'Unsubscribe' option";
+"REMOVE_UN_SUB_OPTION_DESC" = "Removes 'Unsubscribe' option from the flyout menu.";
+"REMOVE_HIDE_FROM_PLAYLIST_OPTION" = "Removes 'Hide video from playlist' option";
+"REMOVE_HIDE_FROM_PLAYLIST_OPTION_DESC" = "Removes 'Hide video from playlist' option from the flyout menu.";
+"REMOVE_HELP_OPTION" = "Removes 'Help & feedback' option";
+"REMOVE_HELP_OPTION_DESC" = "Removes 'Help & feedback' option from the flyout menu.";
+"REMOVE_NOTIFY_OPTION" = "Removes 'Notify me' option";
+"REMOVE_NOTIFY_OPTION_DESC" = "Removes 'Notify me' option from the flyout menu.";
+"REMOVE_CLEARSCREEN_OPTION" = "Removes 'Clear Screen' option";
+"REMOVE_CLEARSCREEN_OPTION_DESC" = "Removes 'Clear Screen' option from the flyout menu.";
+"HIDE_COMMU_GUIDE" = "Remove guidelines";
+"HIDE_COMMU_GUIDE_DESC" = "Removes pinned guidelines from the comments section.";
+"DISABLES_RTL" = "Disable RTL formatting";
+"DISABLES_RTL_DESC" = "Forcefully displays text in left-to-right (LTR) format for languages that are initially displayed in right-to-left (RTL).";
+"DEVICE_UI" = "Interface style";
+
+"SB_DESCRIPTION" = "Skip sponsored segments automatically using community-submitted data.";
+"SB_ENABLE" = "Enable SponsorBlock";
+"SB_ENABLE_DESC" = "Activate the SponsorBlock extension to skip sponsored segments.";
+"SB_SHOW_BUTTON" = "Show overlay button";
+"SB_SHOW_BUTTON_DESC" = "Display a SponsorBlock toggle button in the player overlay.";
+"SB_SHOW_NOTIFICATIONS" = "Show notifications";
+"SB_SHOW_NOTIFICATIONS_DESC" = "Display notifications when segments are skipped. Notifications for manual-skip categories are always shown.";
+"SB_HAPTIC_FEEDBACK" = "Haptic feedback";
+"SB_HAPTIC_FEEDBACK_DESC" = "Provide haptic feedback when a segment is skipped.";
+"SB_SEGMENTS_IN_PLAYER" = "Segments in player";
+"SB_SEGMENTS_IN_PLAYER_DESC" = "Display colored segments on the progress bar of the main video player.";
+"SB_SEGMENTS_IN_FEED" = "Segments in feed";
+"SB_SEGMENTS_IN_FEED_DESC" = "Display colored segments on the progress bar of players in the feed.";
+"SB_SEGMENTS_IN_MINIPLAYER" = "Segments in mini-player";
+"SB_SEGMENTS_IN_MINIPLAYER_DESC" = "Display colored segments on the progress bar of the mini-player.";
+"SB_SHOW_DURATION" = "Show duration without segments";
+"SB_SHOW_DURATION_DESC" = "Display the total video duration excluding skippable segments.";
+"SB_CATEGORIES_HEADER" = "Segment categories";
+"SB_SEGMENT_COLOR" = "Segment color";
+
+"SB_ACTION_DISABLE" = "Disable";
+"SB_ACTION_AUTO_SKIP" = "Auto-skip";
+"SB_ACTION_ASK" = "Ask before skipping";
+"SB_ACTION_DISPLAY" = "Show on seek bar only";
+"SB_ACTION_SKIP_TO" = "Skip to segment";
+
+"SB_CAT_sponsor" = "Sponsor";
+"SB_CAT_sponsor_DESC" = "Paid promotion for a product or service not directly related to the creator.";
+"SB_CAT_intro" = "Intro";
+"SB_CAT_intro_DESC" = "Intermission or intro animation typically seen at the start of a video.";
+"SB_CAT_outro" = "Endcards";
+"SB_CAT_outro_DESC" = "Endcards, credits, or outros at the end of the video.";
+"SB_CAT_interaction" = "Interaction";
+"SB_CAT_interaction_DESC" = "Reminders to like, subscribe, or follow on social media.";
+"SB_CAT_selfpromo" = "Self-promotion";
+"SB_CAT_selfpromo_DESC" = "Unpaid promotion of the creator's own products, merchandise, or other content.";
+"SB_CAT_music_offtopic" = "Non-music";
+"SB_CAT_music_offtopic_DESC" = "Non-music sections in music videos (e.g. talking, introductions).";
+"SB_CAT_preview" = "Preview";
+"SB_CAT_preview_DESC" = "Recap or preview of content from another video or previous episode.";
+"SB_CAT_hook" = "Hook/Greetings";
+"SB_CAT_hook_DESC" = "Narrated trailers for the upcoming video, greetings and goodbyes.";
+"SB_CAT_poi_highlight" = "Highlight";
+"SB_CAT_poi_highlight_DESC" = "The key moment or highlight point of the video. Use 'Skip to' to jump directly to it.";
+"SB_CAT_filler" = "Filler";
+"SB_CAT_filler_DESC" = "Tangential content, jokes, or filler that is not directly related to the main topic.";
+
+"SB_SKIPPED" = "%@ segment has been skipped";
+"SB_DETECTED" = "%@ segment detected.\nWould you like to skip the segment?";
+"SB_SKIP_NOW" = "Skip";
+"SB_UNSKIP" = "Unskip";
+"SB_JUMPED_TO_HIGHLIGHT" = "Jumped to highlight";
+"SB_JUMP_TO_HIGHLIGHT" = "Highlight available. Jump to the point?";
+"SB_SEGMENT_COLOR_SUFFIX" = "Segment color";
+"SB_SECTION_MAIN" = "Main";
+"SB_SKIP_ALERT_DURATION" = "Skip alert duration";
+"SB_UNSKIP_ALERT_DURATION" = "Unskip alert duration";
\ No newline at end of file
diff --git a/layout/Library/Application Support/YouMod.bundle/sk.lproj/Localizable.strings b/layout/Library/Application Support/YouMod.bundle/sk.lproj/Localizable.strings
index bccaf550..2c1a8a0f 100644
--- a/layout/Library/Application Support/YouMod.bundle/sk.lproj/Localizable.strings
+++ b/layout/Library/Application Support/YouMod.bundle/sk.lproj/Localizable.strings
@@ -1,11 +1,18 @@
-"NOTE" = "NOTE: Some hiding features may leave a blank box.";
"SETTINGS" = "Settings";
+"INTERFACE" = "Interface";
+
+"LOOP_DISABLED" = "Loop disabled";
+"LOOP_ENABLED" = "Loop enabled";
"NEW_ISSUES" = "Report issues or request new features";
"NEW_ISSUES_DESC" = "Found bugs or want to request new features? Tap here.";
"SOURCE_CODES" = "Source codes";
"SOURCE_CODES_DESC" = "Take a look at the codes!";
+"EXTRA" = "Extra";
+"FIX_PLAYBACK_ISSUES" = "Fix playback issues (Temporary)";
+"FIX_PLAYBACK_ISSUES_DESC" = "Try to fix video playback issues for some users. Turning this on will make the player automatically reloads itself everytime it errors or fails to play the video.";
+
"PERFER_HEADER" = "YouMod perferences";
"PERFER" = "Perferences";
"IMPORT" = "Import settings";
@@ -24,6 +31,7 @@
"ERROR_NO_KEYS_IMPORT" = "No available keys to import";
"ERROR_NO_KEYS_EXPORT" = "No available keys to export";
"OVERRIDE" = "This will override the current settings. Do you want to continue?";
+"DISABLED" = "Disabled";
"CACHE" = "Cache management";
"CLEARCACHE" = "Clear cache";
@@ -32,12 +40,80 @@
"AUTO_CLEARCACHE_DESC" = "Automatically clears the app cache on startup.";
"DOWNLOADING" = "Downloading";
-"DOWNLOAD_MANAGER" = "Download Manager";
-"DOWNLOAD_MANAGER_DESC" = "Replaces YouTube's video downloader with YouMod one. With ablity to downloading videos, audios, captions, and thumbnails";
+"DOWNLOAD_MANAGER" = "Download manager";
+"DOWNLOAD_MANAGER_DESC" = "Replaces YouTube's video downloader with YouMod one. With ablity to downloading videos, audios, captions, and thumbnails.";
"DOWNLOAD_SAVE_PHOTOS" = "Automatically save to Photos";
"DOWNLOAD_SAVE_PHOTOS_DESC" = "Automatically saves downloaded videos and images directly to your Photos app.";
-"DOWNLOAD_DRC_AUDIO" = "Prefer DRC Audio";
-"DOWNLOAD_DRC_AUDIO_DESC" = "Prioritizes downloading audio tracks with Dynamic Range Compression for more consistent volume levels.";
+"ADD_SHORTS_DOWNLOAD" = "Adds a download button to Shorts";
+"ADD_SHORTS_DOWNLOAD_DESC" = "Adds a download button to Shorts video overlay.";
+"USE_ORIG_AUDIO" = "Automatically uses original audio track";
+"USE_ORIG_AUDIO_DESC" = "Automatically uses original audio track instead of showing a dialog.";
+"DOWNLOAD_SHORTS" = "Download Shorts";
+"DOWNLOAD_SHORTS_DESC" = "Choose Shorts quality";
+"COPY_VID_INFO" = "Copy video informations";
+"COPY_VID_INFO_DESC" = "Choose what to copy";
+"COPY_ALL_VID_INFO" = "Copy all";
+"COPY_ALL_VID_INFO_DESC" = "Copy all video informations";
+"COPY_AUTHOR" = "Copy author";
+"COPY_AUTHOR_DESC" = "Copy the video author";
+"COPY_TITLE" = "Copy title";
+"COPY_TITLE_DESC" = "Copy the video title";
+"COPY_DESCRIPTION" = "Copy description";
+"COPY_DESCRIPTION_DESC" = "Copy the video description";
+"COPY_URL" = "Copy video link";
+"COPY_URL_TIMESTAMP" = "Copy video link with timestamp";
+"URL_COPIED" = "Copied video link";
+"URL_TIMESTAMP_COPIED" = "Copied video link with timestamp";
+"ERROR_VIDEOID" = "Cannot get the video ID";
+"ERROR_ADS" = "Cannot copy video link while playing ads";
+"COPIED_AUTHOR" = "Copied video author";
+"COPIED_TITLE" = "Copied video title";
+"COPIED_DESCRIPTION" = "Copied video description";
+"SAVE_THUMBNAIL" = "Save thumbnail";
+"SAVE_THUMBNAIL_DESC" = "Save to Photos";
+"DOWNLOAD_CAPTIONS" = "Download captions";
+"DOWNLOAD_CAPTIONS_DESC" = "Save subtitles as .VTT file";
+"DOWNLOAD_AUDIO" = "Download audio";
+"DOWNLOAD_AUDIO_DESC" = "Download audio as .m4a file";
+"DOWNLOAD_VIDEO" = "Download video";
+"DOWNLOAD_VIDEO_DESC" = "Choose video quality";
+"OPEN_VID_BEFORE" = "Open a video before using the download manager";
+"NO_CAPTIONS_URL" = "No valid caption URLs found";
+"NO_AUDIO_URL" = "No audio URL found";
+"NO_STREAM_URL" = "No stream URL found";
+"CAPTIONS_FAILED" = "Failed to download captions";
+"NO_CAPTIONS" = "No captions available for this video";
+"NO_AUDIO_STREAM_FOUND" = "No audio streams found";
+"NO_VID_AUDIO_STREAM_FOUND" = "No video/audio streams found";
+"COPIED_VID_INFO" = "Copied all video informations";
+"SAVED_TO_PHOTOS" = "Saved to Photos";
+"SAVE_FAILED" = "Save failed";
+"PHOTO_ACCESS_DENINED" = "Photos access denied";
+"THUMBNAIL_FAILED" = "Thumbnail failed";
+"DOWNLOADING_THUMBNAIL" = "Downloading thumbnail...";
+"DOWNLOADING_AUDIO" = "Downloading audio...";
+"DOWNLOADING_VIDEO" = "Downloading video...";
+"DOWNLOADING_CAPTIONS" = "Downloading captions...";
+"NO_THUMBNAIL_FOUND" = "No thumbnail found";
+"DOWNLOAD_FAILED" = "Download failed";
+"COPY_THUMBNAIL" = "Copy thumbnail";
+"COPY_THUMBNAIL_DESC" = "Copy to clipboard";
+"SHOW_THUMBNAIL" = "Show thumbnail";
+"SHOW_THUMBNAIL_DESC" = "Show in a dialog";
+"THUMBNAIL_OPTIONS" = "Download thumbnail";
+"THUMBNAIL_OPTIONS_DESC" = "Show more options";
+"COPIED_TO_CLIPBOARD" = "Copied to clipboard";
+"DOWNLOAD_COMPLETED" = "Download completed";
+"CANNOT_SAVE_TO_PHOTOS" = "Cannot save to Photos";
+"FINA_VIDEO" = "Finalizing video...";
+"MERGING_VID_FALLBACK" = "Merging video with fallback...";
+"MERGING_VID" = "Merging video...";
+"CONVERTING" = "Converting to";
+"ALREADY_DOWNLOADING" = "Already downloading";
+"DOWNLOAD_CANCELLED" = "Download cancelled";
+"CANCEL" = "Cancel";
+"TRIMMING_AUDIO" = "Trimming audio...";
+"COPY" = "Copy";
"APPEARANCE" = "Appearance";
"OLED_THEME" = "OLED dark mode";
@@ -46,6 +122,8 @@
"OLED_KEYBOARD_DESC" = "Make the keyboard uses a pure black (OLED) color for dark theme.";
"NAVBAR" = "Navigation bar";
+"STICKY_NAVBAR" = "Sticky Navigation bar";
+"STICKY_NAVBAR_DESC" = "Pins the Navigation bar so that it remains visible when scrolling down.";
"HIDE_YT_LOGO" = "Hide YouTube logo";
"HIDE_YT_LOGO_DESC" = "Hide YouTube logo in the navigation bar.";
"PREMIUM_LOGO" = "Use YouTube Premium logo";
@@ -62,34 +140,78 @@
"FEED" = "Feed";
"HIDE_SUBBAR" = "Hide subbar";
"HIDE_SUBBAR_DESC" = "Hide subbar at the top of the feed.";
+"HIDE_HORI_SHELF" = "Remove horizontal feeds";
+"HIDE_HORI_SHELF_DESC" = "Removes all horizontal feeds (News, Continue Watching, Posts collection, etc.) from the feed, except Playables.";
"HIDE_MUSIC_PLAYLISTS" = "Hide music playlist generator";
"HIDE_MUSIC_PLAYLISTS_DESC" = "Hide music playlist generator (Select music tunes in three) in the feed.";
"HIDE_FEED_POST" = "Hide posts";
"HIDE_FEED_POST_DESC" = "Hide posts in feed.";
+"HIDE_SURVEYS" = "Hide surveys";
+"HIDE_SURVEYS_DESC" = "Hide video surveys that YouTube shows sometimes.";
"HIDE_SHORTS_SHELF" = "Hide Shorts shelf";
"HIDE_SHORTS_SHELF_DESC" = "Hide Shorts shelf in the feed, including in the Subscriptions tab.";
+"KEEP_SHORTS_SUBSCRIPT" = "Keep Shorts in Subscriptions";
+"KEEP_SHORTS_SUBSCRIPT_DESC" = "Prevents removing Shorts from Subscriptions tab.";
"HIDE_SEARCH_HISTORY" = "Hide search history";
"HIDE_SEARCH_HISTORY_DESC" = "Hide previous search history and suggestions when using the search bar.\nNOTE: Your search history will still be accessible in your other YouTube clients.";
-"HIDE_SUB_BUTTON" = "Hide Subscribe button";
-"HIDE_SUB_BUTTON_DESC" = "Hides the subscribe button in channels and video descriptions.";
-"HIDE_SHOP_BUTTON" = "Hide Shopping button";
-"HIDE_SHOP_BUTTON_DESC" = "Hides the shopping/store button in channels descriptions.";
-"HIDE_MEMBER_BUTTON" = "Hide Membership button";
-"HIDE_MEMBER_BUTTON_DESC" = "Hides the 'Join' button in channels and video descriptions.";
+"HIDE_PLAYABLES" = "Remove playables";
+"HIDE_PLAYABLES_DESC" = "Removes playables (mini-games) from the feed.";
"PLAYER" = "Player";
+"PLAYBACK_SPEED" = "Playback speed";
+"CONTROL_CENTER" = "Control Center";
+"PLAYER_ACTIONS" = "Player actions";
+"GESTURE_HEADER" = "Gestures";
+"QUALITY_WIFI" = "Playback quality on WiFi";
+"QUALITY_WIFI_DESC" = "Automatically applies the selected quality when streaming over Wi-Fi.";
+"QUALITY_CELLULAR" = "Playback quality on Cellular";
+"QUALITY_CELLULAR_DESC" = "Automatically applies the selected quality when streaming over mobile data.";
+"QUALITY_LOW_POWER" = "Playback quality on Power saver mode";
+"QUALITY_LOW_POWER_DESC" = "Automatically applies the selected quality when when Low power mode is enabled on your device.";
+"DEFAULT" = "Default";
+"BEST" = "Best";
+"AUDIO_TRACK" = "Preferred audio track";
+"ORIGINAL" = "Original";
+"SELECT_MANUALLY" = "Select manually";
+"AUDIO_TRACK_SELECT" = "Selected audio track";
+"AUDIO_TRACK_SELECT_DESC" = "Specifies the preferred audio track language to use for playback, if available.";
+"NO_AUTO_DUBBED" = "Uses original audio track";
+"NO_AUTO_DUBBED_DESC" = "Uses original audio track if the preferred language track is auto-dubbed or not found.";
+"CAPTION_TRACK" = "Preferred captions";
+"CAPTION_TRACK_SELECT" = "Selected captions";
+"CAPTION_TRACK_SELECT_DESC" = "Specifies the preferred captions language to display, if available.";
+"DISABLES_CAPTION_TRACK" = "Automatically disable captions";
+"DISABLES_CAPTION_TRACK_DESC" = "Automatically disable captions if the preferred language is auto-generated or not found.";
+"DEFAULT_SPEED" = "Default playback rate";
+"DEFAULT_SPEED_DESC" = "Overrides the default playback speed.";
+"SPEED_BUTTON" = "Speed controls button";
+"SPEED_BUTTON_DESC" = "Displays a speed controls button on overlay.";
+"MUTE_BUTTON" = "Mute button";
+"MUTE_BUTTON_DESC" = "Displays a button to mute or unmute the volume on the overlay.";
+"SHARE_BUTTON" = "Share button";
+"SHARE_BUTTON_DESC" = "Displays a button to share the video link on the overlay.";
+"LOOP_BUTTON" = "Loop button";
+"LOOP_BUTTON_DESC" = "Displays a button to loop the video on the overlay.";
+"CAPTION_BUTTON" = "Captions button";
+"CAPTION_BUTTON_DESC" = "Displays a button to copy the video captions on the overlay.";
+"HOLD_TO_SPEED" = "Hold to speed";
+"HOLD_TO_SPEED_DESC" = "Uses custom hold to speed playback rate.";
"HIDE_AUTOPLAY" = "Hide autoplay switch";
"HIDE_AUTOPLAY_DESC" = "Hide autoplay switch in the video overlay.";
"HIDE_CAPTIONS_BUTTON" = "Hide captions button";
"HIDE_CAPTIONS_BUTTON_DESC" = "Hide captions button in the video overlay.";
"HIDE_CAST_BUTTON_PLAYER" = "Hide cast button";
"HIDE_CAST_BUTTON_PLAYER_DESC" = "Hide cast button in the video overlay.";
-"HIDE_PREV_BUTTON" = "Hide previous/back button";
-"HIDE_PREV_BUTTON_DESC" = "Hide previous/back button in the video overlay.";
-"HIDE_NEXT_BUTTON" = "Hide next/skip button";
-"HIDE_NEXT_BUTTON_DESC" = "Hide next/skip button in the video overlay.";
+"HIDE_NEXT_AND_PREV_BUTTON" = "Hide previous and next buttons";
+"HIDE_NEXT_AND_PREV_BUTTON_DESC" = "Hide previous and next buttons in the video overlay.";
"REPLACE_PREVNEXT_BUTTONS" = "Replace Previous/Next buttons";
"REPLACE_PREVNEXT_BUTTONS_DESC" = "Replaces the Previous and Next video buttons in the player with Rewind and Fast Forward buttons.";
+"SKIP_BACKWARD" = "Skip backward";
+"SKIP_BACKWARD_DESC" = "Replaces the previous track button in Control Center with a skip backward button by a set number of seconds.";
+"SKIP_FORWARD" = "Skip forward";
+"SKIP_FORWARD_DESC" = "Replaces the next track button in Control Center with a skip forward button by a set number of seconds.";
+"REWIND_SECONDS" = "Rewind seconds";
+"FORWARD_SECONDS" = "Fast-forward seconds";
"REMOVE_DARK_OVERLAY" = "Remove dark overlay";
"REMOVE_DARK_OVERLAY_DESC" = "Remove dark overlay from the video overlay.";
"HIDE_END_SCREEN" = "Hide endscreen cards";
@@ -102,6 +224,12 @@
"HIDE_PAID_OVERLAY_DESC" = "Hide 'Includes paid promotion' in the video overlay.";
"HIDE_WATERMARK" = "Hide watermark";
"HIDE_WATERMARK_DESC" = "Hide channel watermark at the bottom of the video overlay.";
+"PAUSE_ON_OVERLAY" = "Pause on overlay";
+"PAUSE_ON_OVERLAY_DESC" = "Sets playback on pause if overlay appears.";
+"DISABLES_SNAP_TO_CHAPTER" = "Disable snap to chapters";
+"DISABLES_SNAP_TO_CHAPTER_DESC" = "Prevents the scrubbar stick to the chapters.";
+"DISABLES_ENGAGE_PANEL" = "Disables engagement panel in fullscreen";
+"DISABLES_ENGAGE_PANEL_DESC" = "Prevent engagement panel showing in fullscreen.";
"GESTURES" = "Enable gestures controls";
"GESTURES_DESC" = "Swipe up or down on the left/right edges of the video screen to adjust brightness, volume, or playback speed.";
"GESTURE_AREA" = "Gesture activation area";
@@ -136,6 +264,8 @@
"DISABLES_SHOW_REMAINING_DESC" = "Disable the feature that shows how much time is left in a video when tapping the timestamp.";
"ALWAYS_SHOW_REMAINING" = "Always show remaining time";
"ALWAYS_SHOW_REMAINING_DESC" = "Ensure the remaining time is always displayed by default in the player overlay.";
+"COPY_TIMESTAMP_ON_PAUSE" = "Copy timestamped links";
+"COPY_TIMESTAMP_ON_PAUSE_DESC" = "Allows to copy timestamped link to the clipboard by pressing pause button.";
"SHOW_REMAINING_EXTRA" = "Show extra remaining time";
"SHOW_REMAINING_EXTRA_DESC" = "Adds the estimated finish time to the label in the video overlay.";
"HIDE_FULLSCREEN_ACTIONS" = "Hide fullscreen actions";
@@ -148,93 +278,95 @@
"HIDE_CONTENT_WARNING_DESC" = "Automatically skip the sensitive content warning alert in some vidoes.";
"AUTO_FULLSCREEN" = "Auto fullscreen";
"AUTO_FULLSCREEN_DESC" = "Automatically enter fullscreen mode when starting video playback.";
-"AUTO_DISABLES_CAPTION" = "Auto disable captions";
-"AUTO_DISABLES_CAPTION_DESC" = "Automatically turns off closed captions (CC) whenever a new video starts playing.";
"PORTRAIT_FULLSCREEN" = "Portrait fullscren mode";
"PORTRAIT_FULLSCREEN_DESC" = "Enables portrait fullscreen mode support.";
"OLD_QUALITY_PICKER" = "Use old quality picker";
"OLD_QUALITY_PICKER_DESC" = "Reverts the video quality selection menu to the classic version (lists all resolutions immediately).";
"EXTRA_SPEED" = "Extra speed options";
"EXTRA_SPEED_DESC" = "Adds more video playback speed options up to 10x.";
-"DISABLE_HINTS" = "Disable hints";
-"DISABLE_HINTS_DESC" = "Disable the tutorial hints and interactive pop-ups.";
"FORCE_MINIPLAYER" = "Force miniplayer";
"FORCE_MINIPLAYER_DESC" = "Allows all videos, including those made for kids, to play in the miniplayer when dismissing the player.";
"FORCE_SEEKBAR" = "Always show progress bar";
"FORCE_SEEKBAR_DESC" = "Makes the progress bar (seekbar) remains visible at all times while in fullscreen mode.";
-"HIDE_LIKE_BUTTON" = "Hide Like button";
-"HIDE_LIKE_BUTTON_DESC" = "Hide Like button in the action bar under the video overlay.";
-"HIDE_DISLIKE_BUTTON" = "Hide Dislike button";
-"HIDE_DISLIKE_BUTTON_DESC" = "Hide Dislike button in the action bar under the video overlay.";
-"HIDE_SHARE_BUTTON" = "Hide Share button";
-"HIDE_SHARE_BUTTON_DESC" = "Hide Share button in the action bar under the video overlay.";
-"HIDE_DOWNLOAD_BUTTON" = "Hide Download button";
-"HIDE_DOWNLOAD_BUTTON_DESC" = "Hide Download button in the action bar under the video overlay.";
-"HIDE_CLIP_BUTTON" = "Hide Clip button";
-"HIDE_CLIP_BUTTON_DESC" = "Hide Clip button in the action bar under the video overlay.";
-"HIDE_REMIX_BUTTON" = "Hide Remix button";
-"HIDE_REMIX_BUTTON_DESC" = "Hide Remix button in the action bar under the video overlay.";
-"HIDE_SAVE_BUTTON" = "Hide Save button";
-"HIDE_SAVE_BUTTON_DESC" = "Hide Save button in the action bar under the video overlay.";
+"TAP_TO_SEEK" = "Tap to seek";
+"TAP_TO_SEEK_DESC" = "Quickly jump to any point by tapping the progress bar.";
+"DISABLES_ZOOM" = "Disable free zoom gesture";
+"DISABLES_ZOOM_DESC" = "Disables new free zoom gestures.";
+"USES_24_HOURS_TIME" = "24-hour format";
+"USES_24_HOURS_TIME_DESC" = "Shows end time in 24-hour format.";
+"PAUSE_TWO_FINGERS" = "Pause using two fingers";
+"PAUSE_TWO_FINGERS_DESC" = "Pause/Resume playback by tapping on the screen using two fingers.";
+"HIDE_COMMENTS_SECTION" = "Hide comments section";
+"HIDE_COMMENTS_SECTION_DESC" = "Completely removes the entire comments section.";
+"HIDE_COMMENTS_PREVIEW" = "Hide preview comments";
+"HIDE_COMMENTS_PREVIEW_DESC" = "Removes the preview box for the top comment while keeping the full section expandable.";
+"USE_ANOTHER_MINIPLAYER" = "Uses another miniplayer style";
+"USE_ANOTHER_MINIPLAYER_DESC" = "Uses another miniplayer style redesigned from older YT versions.";
"SHORTS" = "Shorts";
-"HIDE_SHORTS_LIKE_BUTTON" = "Hide Like button";
-"HIDE_SHORTS_LIKE_BUTTON_DESC" = "Hide the Like button in the Shorts player.";
-"HIDE_SHORTS_DISLIKE_BUTTON" = "Hide Dislike button";
-"HIDE_SHORTS_DISLIKE_BUTTON_DESC" = "Hide the Dislike button in the Shorts player.";
-"HIDE_SHORTS_COMMENT_BUTTON" = "Hide Comment button";
-"HIDE_SHORTS_COMMENT_BUTTON_DESC" = "Hide the Comment button in the Shorts player.";
-"HIDE_SHORTS_SHARE_BUTTON" = "Hide Share button";
-"HIDE_SHORTS_SHARE_BUTTON_DESC" = "Hide the Share button in the Shorts player.";
-"HIDE_SHORTS_REMIX_BUTTON" = "Hide Remix button";
-"HIDE_SHORTS_REMIX_BUTTON_DESC" = "Hide the Remix button in the Shorts player.";
-"HIDE_METADATA_BUTTON" = "Hide sound metadata button";
-"HIDE_METADATA_BUTTON_DESC" = "Hide the button that shows the sound source or music metadata in the Shorts player.";
+"SHORTS_ONLY" = "Shorts only mode";
+"SHORTS_ONLY_DESC" = "Limits YouTube functionality to viewing Shorts only.";
+"SHORTS_ACTION" = "Shorts action after the video ends";
+"LOOP" = "Loop video";
+"SKIP_TO_NEXT_SHORTS" = "Skip to next video";
+"PAUSE_SHORTS" = "Stop video";
+"REMOVE_LIVE_SHORTS" = "Remove live videos";
+"REMOVE_LIVE_SHORTS_DESC" = "Removes live videos from the feed.";
"HIDE_SHORTS_PRODUCT" = "Hide products";
"HIDE_SHORTS_PRODUCT_DESC" = "Hide tagged products and shopping links in the Shorts overlay.";
"HIDE_SHORTS_RECBAR" = "Hide recommendation bar";
"HIDE_SHORTS_RECBAR_DESC" = "Hide the suggested action bar in the Shorts player (e.g. Try this sound, Shopping, Preview comment).";
-"HIDE_SHORTS_COMMIT" = "Hide commissions bar";
-"HIDE_SHORTS_COMMIT_DESC" = "Hide the bar that shows the video got commissions in the Shorts player.";
-"HIDE_SHORTS_SUBSCRIPT_BUTTON" = "Hide subscriptions button";
-"HIDE_SHORTS_SUBSCRIPT_BUTTON_DESC" = "Hide the Subscriptions button at the top of the Shorts player.";
-"HIDE_SHORTS_LIVE_BUTTON" = "Hide live button";
-"HIDE_SHORTS_LIVE_BUTTON_DESC" = "Hide the Live button at the top of the Shorts player.";
-"HIDE_SHORTS_LENS_BUTTON" = "Hide lens button";
-"HIDE_SHORTS_LENS_BUTTON_DESC" = "Hide the Lens button at the top of the Shorts player.";
-"HIDE_SHORTS_TRENDS_BUTTON" = "Hide trends button";
-"HIDE_SHORTS_TRENDS_BUTTON_DESC" = "Hide the Trends button at the top of the Shorts player.";
-"HIDE_SHORTS_TO_VIDEO" = "Hide link to full video";
-"HIDE_SHORTS_TO_VIDEO_DESC" = "Hide the link that redirects in a Short to a full video in the Shorts player.";
"ENABLES_SHORTS_QUALITY" = "Enable Shorts quality picker";
"ENABLES_SHORTS_QUALITY_DESC" = "Force enable the video quality selection menu in Shorts.";
"SHOW_SHORTS_SEEKBAR" = "Always show seekbar";
"SHOW_SHORTS_SEEKBAR_DESC" = "Ensure the progress/seek bar is always visible at the bottom of the Shorts player.";
+"REMOVE_POSTS_SHORTS" = "Remove posts";
+"REMOVE_POSTS_SHORTS_DESC" = "Remove posts from the feed.";
+"SHORTS_FULLSCREEN" = "Pinch to Fullscreen";
+"SHORTS_FULLSCREEN_DESC" = "Manages visibility of the overlay with pinch in and pinch out gestures, displaying Shorts in fullscreen mode.";
"TABBAR" = "Tab bar";
"DEFAULT_TAB" = "Default startup tab";
-"DEFAULT_TAB_DESC" = "You can select the tab you want to open when opening the app.";
-"HOME_NAME" = "Home";
-"SUBSCRIPT_NAME" = "Subscriptions";
-"LIB_NAME" = "Library (You)";
+"FORSTED_TAB_BAR" = "Translucent tab bar";
+"ENABLED" = "Enabled";
"HIDE_TAB_INDI" = "Hide tab indicators";
"HIDE_TAB_INDI_DESC" = "Hide all tab indicators in the tab bar.";
"HIDE_TAB_LABELS" = "Hide tab labels";
"HIDE_TAB_LABELS_DESC" = "Hide all tab labels in the tab bar.";
-"HIDE_HOME_TAB" = "Hide Home tab";
-"HIDE_HOME_TAB_DESC" = "Hide Home tab in the tab bar.";
-"HIDE_SHORTS_TAB" = "Hide Shorts tab";
-"HIDE_SHORTS_TAB_DESC" = "Hide Shorts tab in the tab bar.";
-"HIDE_CREATE_BUTTON" = "Hide Create button";
-"HIDE_CREATE_BUTTON_DESC" = "Hide Create button in the tab bar.";
-"HIDE_SUBSCRIPT_TAB" = "Hide Subscriptions tab";
-"HIDE_SUBSCRIPT_TAB_DESC" = "Hide Subscriptions tab in the tab bar.";
+"HOME_TAB" = "Home";
+"SHORTS_TAB" = "Shorts";
+"CREATE_TAB" = "Create";
+"SUBSCRIPTIONS_TAB" = "Subscriptions";
+"LIBRARY_TAB" = "Library (You)";
+"HISTORY_TAB" = "History";
+"GAMING_TAB" = "Gaming";
+"SPORTS_TAB" = "Sports";
+"NOTI_TAB" = "Notifications";
+"NEWS_TAB" = "News";
+"MUSIC_TAB" = "Music";
+"WATCH_LATER_TAB" = "Watch later";
+"PLAYLIST_TAB" = "Playlists";
+"LIKE_TAB" = "Liked videos";
+"LIVE_TAB" = "Live";
+"POST_TAB" = "Posts";
+"VIDEO_TAB" = "Your videos";
+"MOVIE_TAB" = "Movies";
+"COURSE_TAB" = "Courses";
+"MINIGAME_TAB" = "Playables";
+"MANAGE_TABS" = "Manage tabs";
+"MANAGE_TABS_DESC" = "Reorder and toggle tabs";
+"TAB_REORDER_HINT" = "Drag to reorder. Toggle to show/hide.\nTab settings are also accessible by long-pressing the first tab in the tab bar.";
+"TAB_LIMIT" = "Tab limit";
+"TAB_LIMIT_DESC" = "Only 6 tabs are allowed.";
+"ZERO_TAB_DESC" = "At least 1 tab has to be enabled.";
"MISCELLANEOUS" = "Miscellaneous";
"BACKGROUND_PLAYBACK" = "Background playback";
"BACKGROUND_PLAYBACK_DESC" = "Enables background playback for videos.";
"DISABLES_SHORTS_PIP" = "Disables Shorts PiP";
"DISABLES_SHORTS_PIP_DESC" = "Try to disables Shorts PiP, this might not 100% disables it.";
+"DISABLE_HINTS" = "Disable hints";
+"DISABLE_HINTS_DESC" = "Disable the tutorial hints and interactive pop-ups.";
"BLOCK_UPGRADE_DIALOGS" = "Block upgrade dialogs";
"BLOCK_UPGRADE_DIALOGS_DESC" = "Block the YouTube dialogs that ask you to upgrade the app.";
"ARE_YOU_THERE_DIALOG" = "Hide 'Are you there?' dialog";
@@ -247,7 +379,109 @@
"DISABLES_SNACK_BAR_DESC" = "Disables snack bar when pressing some buttons or doing something in the app.";
"HIDE_STARTUP_ANIMATIONS" = "Hide YouTube's startup animations";
"HIDE_STARTUP_ANIMATIONS_DESC" = "Hide YouTube's startup animations when opening the app.";
-"HIDE_PLAY_IN_NEXT_QUEUE" = "Hide 'Play next in queue'";
-"HIDE_PLAY_IN_NEXT_QUEUE_DESC" = "Hide the 'Play next in queue' option in the flyout menu.";
"HIDE_LIKE_DISLIKE_VOTES" = "Hide like/dislike votes";
-"HIDE_LIKE_DISLIKE_VOTES_DESC" = "Hide messages that pop up when you like/dislike videos.";
\ No newline at end of file
+"HIDE_LIKE_DISLIKE_VOTES_DESC" = "Hide messages that pop up when you like/dislike videos.";
+"FLYOUT_MENU" = "Flyout menu";
+"REMOVE_PLAY_IN_NEXT_QUEUE_OPTION" = "Removes 'Play next in queue' option";
+"REMOVE_PLAY_IN_NEXT_QUEUE_OPTION_DESC" = "Removes 'Play next in queue' option from the flyout menu.";
+"REMOVE_DOWNLOAD_OPTION" = "Removes 'Download video' option";
+"REMOVE_DOWNLOAD_OPTION_DESC" = "Removes 'Download video' option from the flyout menu.";
+"REMOVE_WATCH_LATER_OPTION" = "Removes 'Save to Watch later' option";
+"REMOVE_WATCH_LATER_OPTION_DESC" = "Removes 'Save to Watch Later' option from the flyout menu.";
+"REMOVE_SAVE_OPTION" = "Removes 'Save to playlist' option";
+"REMOVE_SAVE_OPTION_DESC" = "Removes 'Save to playlist' option from the flyout menu.";
+"REMOVE_REMOVE_FROM_PLAYLIST_OPTION" = "Removes 'Remove from playlist' option";
+"REMOVE_REMOVE_FROM_PLAYLIST_OPTION_DESC" = "Removes 'Remove from playlist' option from the flyout menu.";
+"REMOVE_SHARE_OPTION" = "Removes 'Share' option";
+"REMOVE_SHARE_OPTION_DESC" = "Removes 'Share' option from the flyout menu.";
+"REMOVE_NOT_INTERESTED_OPTION" = "Removes 'Not interested' option";
+"REMOVE_NOT_INTERESTED_OPTION_DESC" = "Removes 'Not interested' option from the flyout menu.";
+"REMOVE_DONT_RECOMMEND_OPTION" = "Removes 'Don't recommend channel' option";
+"REMOVE_DONT_RECOMMEND_OPTION_DESC" = "Removes 'Don't recommend channel' option from the flyout menu.";
+"REMOVE_INFO_OPTION" = "Removes 'Why are you seeing this?' option";
+"REMOVE_INFO_OPTION_DESC" = "Removes 'Why are you seeing this?' option from the flyout menu.";
+"REMOVE_FILTER_OPTION" = "Removes 'Search filters' option";
+"REMOVE_FILTER_OPTION_DESC" = "Removes 'Search filters' option from the flyout menu.";
+"REMOVE_REPORT_OPTION" = "Removes 'Report' option";
+"REMOVE_REPORT_OPTION_DESC" = "Removes 'Report' option from the flyout menu.";
+"REMOVE_YOUTUBE_MUSIC_OPTION" = "Removes 'Listen with YouTube Music' option";
+"REMOVE_YOUTUBE_MUSIC_OPTION_DESC" = "Removes 'Listen with YouTube Music' option from the flyout menu.";
+"REMOVE_FEED_BACK_OPTION" = "Removes 'Send feedback' option";
+"REMOVE_FEED_BACK_OPTION_DESC" = "Removes 'Send feedback' option from the flyout menu.";
+"REMOVE_CAST_OPTION" = "Removes 'Watch on TV' option";
+"REMOVE_CAST_OPTION_DESC" = "Removes 'Watch on TV' option from the flyout menu.";
+"REMOVE_SHUFFLE_OPTION" = "Removes 'Shuffle' option";
+"REMOVE_SHUFFLE_OPTION_DESC" = "Removes 'Shuffle' option from the flyout menu.";
+"REMOVE_UN_SUB_OPTION" = "Removes 'Unsubscribe' option";
+"REMOVE_UN_SUB_OPTION_DESC" = "Removes 'Unsubscribe' option from the flyout menu.";
+"REMOVE_HIDE_FROM_PLAYLIST_OPTION" = "Removes 'Hide video from playlist' option";
+"REMOVE_HIDE_FROM_PLAYLIST_OPTION_DESC" = "Removes 'Hide video from playlist' option from the flyout menu.";
+"REMOVE_HELP_OPTION" = "Removes 'Help & feedback' option";
+"REMOVE_HELP_OPTION_DESC" = "Removes 'Help & feedback' option from the flyout menu.";
+"REMOVE_NOTIFY_OPTION" = "Removes 'Notify me' option";
+"REMOVE_NOTIFY_OPTION_DESC" = "Removes 'Notify me' option from the flyout menu.";
+"REMOVE_CLEARSCREEN_OPTION" = "Removes 'Clear Screen' option";
+"REMOVE_CLEARSCREEN_OPTION_DESC" = "Removes 'Clear Screen' option from the flyout menu.";
+"HIDE_COMMU_GUIDE" = "Hide community guidelines";
+"HIDE_COMMU_GUIDE_DESC" = "Hide community guidelines header.";
+"DISABLES_RTL" = "Disable RTL formatting";
+"DISABLES_RTL_DESC" = "Forcefully displays text in left-to-right (LTR) format for languages that are initially displayed in right-to-left (RTL).";
+"DEVICE_UI" = "Interface style";
+
+"SB_DESCRIPTION" = "Skip sponsored segments automatically using community-submitted data.";
+"SB_ENABLE" = "Enable SponsorBlock";
+"SB_ENABLE_DESC" = "Activate the SponsorBlock extension to skip sponsored segments.";
+"SB_SHOW_BUTTON" = "Show overlay button";
+"SB_SHOW_BUTTON_DESC" = "Display a SponsorBlock toggle button in the player overlay.";
+"SB_SHOW_NOTIFICATIONS" = "Show notifications";
+"SB_SHOW_NOTIFICATIONS_DESC" = "Display notifications when segments are skipped. Notifications for manual-skip categories are always shown.";
+"SB_HAPTIC_FEEDBACK" = "Haptic feedback";
+"SB_HAPTIC_FEEDBACK_DESC" = "Provide haptic feedback when a segment is skipped.";
+"SB_SEGMENTS_IN_PLAYER" = "Segments in player";
+"SB_SEGMENTS_IN_PLAYER_DESC" = "Display colored segments on the progress bar of the main video player.";
+"SB_SEGMENTS_IN_FEED" = "Segments in feed";
+"SB_SEGMENTS_IN_FEED_DESC" = "Display colored segments on the progress bar of players in the feed.";
+"SB_SEGMENTS_IN_MINIPLAYER" = "Segments in mini-player";
+"SB_SEGMENTS_IN_MINIPLAYER_DESC" = "Display colored segments on the progress bar of the mini-player.";
+"SB_SHOW_DURATION" = "Show duration without segments";
+"SB_SHOW_DURATION_DESC" = "Display the total video duration excluding skippable segments.";
+"SB_CATEGORIES_HEADER" = "Segment categories";
+"SB_SEGMENT_COLOR" = "Segment color";
+
+"SB_ACTION_DISABLE" = "Disable";
+"SB_ACTION_AUTO_SKIP" = "Auto-skip";
+"SB_ACTION_ASK" = "Ask before skipping";
+"SB_ACTION_DISPLAY" = "Show on seek bar only";
+"SB_ACTION_SKIP_TO" = "Skip to segment";
+
+"SB_CAT_sponsor" = "Sponsor";
+"SB_CAT_sponsor_DESC" = "Paid promotion for a product or service not directly related to the creator.";
+"SB_CAT_intro" = "Intro";
+"SB_CAT_intro_DESC" = "Intermission or intro animation typically seen at the start of a video.";
+"SB_CAT_outro" = "Endcards";
+"SB_CAT_outro_DESC" = "Endcards, credits, or outros at the end of the video.";
+"SB_CAT_interaction" = "Interaction";
+"SB_CAT_interaction_DESC" = "Reminders to like, subscribe, or follow on social media.";
+"SB_CAT_selfpromo" = "Self-promotion";
+"SB_CAT_selfpromo_DESC" = "Unpaid promotion of the creator's own products, merchandise, or other content.";
+"SB_CAT_music_offtopic" = "Non-music";
+"SB_CAT_music_offtopic_DESC" = "Non-music sections in music videos (e.g. talking, introductions).";
+"SB_CAT_preview" = "Preview";
+"SB_CAT_preview_DESC" = "Recap or preview of content from another video or previous episode.";
+"SB_CAT_hook" = "Hook/Greetings";
+"SB_CAT_hook_DESC" = "Narrated trailers for the upcoming video, greetings and goodbyes.";
+"SB_CAT_poi_highlight" = "Highlight";
+"SB_CAT_poi_highlight_DESC" = "The key moment or highlight point of the video. Use 'Skip to' to jump directly to it.";
+"SB_CAT_filler" = "Filler";
+"SB_CAT_filler_DESC" = "Tangential content, jokes, or filler that is not directly related to the main topic.";
+
+"SB_SKIPPED" = "%@ segment has been skipped";
+"SB_DETECTED" = "%@ segment detected.\nWould you like to skip the segment?";
+"SB_SKIP_NOW" = "Skip";
+"SB_UNSKIP" = "Unskip";
+"SB_JUMPED_TO_HIGHLIGHT" = "Jumped to highlight";
+"SB_JUMP_TO_HIGHLIGHT" = "Highlight available. Jump to the point?";
+"SB_SEGMENT_COLOR_SUFFIX" = "Segment color";
+"SB_SECTION_MAIN" = "Main";
+"SB_SKIP_ALERT_DURATION" = "Skip alert duration";
+"SB_UNSKIP_ALERT_DURATION" = "Unskip alert duration";
\ No newline at end of file
diff --git a/layout/Library/Application Support/YouMod.bundle/sv.lproj/Localizable.strings b/layout/Library/Application Support/YouMod.bundle/sv.lproj/Localizable.strings
index bccaf550..2c1a8a0f 100644
--- a/layout/Library/Application Support/YouMod.bundle/sv.lproj/Localizable.strings
+++ b/layout/Library/Application Support/YouMod.bundle/sv.lproj/Localizable.strings
@@ -1,11 +1,18 @@
-"NOTE" = "NOTE: Some hiding features may leave a blank box.";
"SETTINGS" = "Settings";
+"INTERFACE" = "Interface";
+
+"LOOP_DISABLED" = "Loop disabled";
+"LOOP_ENABLED" = "Loop enabled";
"NEW_ISSUES" = "Report issues or request new features";
"NEW_ISSUES_DESC" = "Found bugs or want to request new features? Tap here.";
"SOURCE_CODES" = "Source codes";
"SOURCE_CODES_DESC" = "Take a look at the codes!";
+"EXTRA" = "Extra";
+"FIX_PLAYBACK_ISSUES" = "Fix playback issues (Temporary)";
+"FIX_PLAYBACK_ISSUES_DESC" = "Try to fix video playback issues for some users. Turning this on will make the player automatically reloads itself everytime it errors or fails to play the video.";
+
"PERFER_HEADER" = "YouMod perferences";
"PERFER" = "Perferences";
"IMPORT" = "Import settings";
@@ -24,6 +31,7 @@
"ERROR_NO_KEYS_IMPORT" = "No available keys to import";
"ERROR_NO_KEYS_EXPORT" = "No available keys to export";
"OVERRIDE" = "This will override the current settings. Do you want to continue?";
+"DISABLED" = "Disabled";
"CACHE" = "Cache management";
"CLEARCACHE" = "Clear cache";
@@ -32,12 +40,80 @@
"AUTO_CLEARCACHE_DESC" = "Automatically clears the app cache on startup.";
"DOWNLOADING" = "Downloading";
-"DOWNLOAD_MANAGER" = "Download Manager";
-"DOWNLOAD_MANAGER_DESC" = "Replaces YouTube's video downloader with YouMod one. With ablity to downloading videos, audios, captions, and thumbnails";
+"DOWNLOAD_MANAGER" = "Download manager";
+"DOWNLOAD_MANAGER_DESC" = "Replaces YouTube's video downloader with YouMod one. With ablity to downloading videos, audios, captions, and thumbnails.";
"DOWNLOAD_SAVE_PHOTOS" = "Automatically save to Photos";
"DOWNLOAD_SAVE_PHOTOS_DESC" = "Automatically saves downloaded videos and images directly to your Photos app.";
-"DOWNLOAD_DRC_AUDIO" = "Prefer DRC Audio";
-"DOWNLOAD_DRC_AUDIO_DESC" = "Prioritizes downloading audio tracks with Dynamic Range Compression for more consistent volume levels.";
+"ADD_SHORTS_DOWNLOAD" = "Adds a download button to Shorts";
+"ADD_SHORTS_DOWNLOAD_DESC" = "Adds a download button to Shorts video overlay.";
+"USE_ORIG_AUDIO" = "Automatically uses original audio track";
+"USE_ORIG_AUDIO_DESC" = "Automatically uses original audio track instead of showing a dialog.";
+"DOWNLOAD_SHORTS" = "Download Shorts";
+"DOWNLOAD_SHORTS_DESC" = "Choose Shorts quality";
+"COPY_VID_INFO" = "Copy video informations";
+"COPY_VID_INFO_DESC" = "Choose what to copy";
+"COPY_ALL_VID_INFO" = "Copy all";
+"COPY_ALL_VID_INFO_DESC" = "Copy all video informations";
+"COPY_AUTHOR" = "Copy author";
+"COPY_AUTHOR_DESC" = "Copy the video author";
+"COPY_TITLE" = "Copy title";
+"COPY_TITLE_DESC" = "Copy the video title";
+"COPY_DESCRIPTION" = "Copy description";
+"COPY_DESCRIPTION_DESC" = "Copy the video description";
+"COPY_URL" = "Copy video link";
+"COPY_URL_TIMESTAMP" = "Copy video link with timestamp";
+"URL_COPIED" = "Copied video link";
+"URL_TIMESTAMP_COPIED" = "Copied video link with timestamp";
+"ERROR_VIDEOID" = "Cannot get the video ID";
+"ERROR_ADS" = "Cannot copy video link while playing ads";
+"COPIED_AUTHOR" = "Copied video author";
+"COPIED_TITLE" = "Copied video title";
+"COPIED_DESCRIPTION" = "Copied video description";
+"SAVE_THUMBNAIL" = "Save thumbnail";
+"SAVE_THUMBNAIL_DESC" = "Save to Photos";
+"DOWNLOAD_CAPTIONS" = "Download captions";
+"DOWNLOAD_CAPTIONS_DESC" = "Save subtitles as .VTT file";
+"DOWNLOAD_AUDIO" = "Download audio";
+"DOWNLOAD_AUDIO_DESC" = "Download audio as .m4a file";
+"DOWNLOAD_VIDEO" = "Download video";
+"DOWNLOAD_VIDEO_DESC" = "Choose video quality";
+"OPEN_VID_BEFORE" = "Open a video before using the download manager";
+"NO_CAPTIONS_URL" = "No valid caption URLs found";
+"NO_AUDIO_URL" = "No audio URL found";
+"NO_STREAM_URL" = "No stream URL found";
+"CAPTIONS_FAILED" = "Failed to download captions";
+"NO_CAPTIONS" = "No captions available for this video";
+"NO_AUDIO_STREAM_FOUND" = "No audio streams found";
+"NO_VID_AUDIO_STREAM_FOUND" = "No video/audio streams found";
+"COPIED_VID_INFO" = "Copied all video informations";
+"SAVED_TO_PHOTOS" = "Saved to Photos";
+"SAVE_FAILED" = "Save failed";
+"PHOTO_ACCESS_DENINED" = "Photos access denied";
+"THUMBNAIL_FAILED" = "Thumbnail failed";
+"DOWNLOADING_THUMBNAIL" = "Downloading thumbnail...";
+"DOWNLOADING_AUDIO" = "Downloading audio...";
+"DOWNLOADING_VIDEO" = "Downloading video...";
+"DOWNLOADING_CAPTIONS" = "Downloading captions...";
+"NO_THUMBNAIL_FOUND" = "No thumbnail found";
+"DOWNLOAD_FAILED" = "Download failed";
+"COPY_THUMBNAIL" = "Copy thumbnail";
+"COPY_THUMBNAIL_DESC" = "Copy to clipboard";
+"SHOW_THUMBNAIL" = "Show thumbnail";
+"SHOW_THUMBNAIL_DESC" = "Show in a dialog";
+"THUMBNAIL_OPTIONS" = "Download thumbnail";
+"THUMBNAIL_OPTIONS_DESC" = "Show more options";
+"COPIED_TO_CLIPBOARD" = "Copied to clipboard";
+"DOWNLOAD_COMPLETED" = "Download completed";
+"CANNOT_SAVE_TO_PHOTOS" = "Cannot save to Photos";
+"FINA_VIDEO" = "Finalizing video...";
+"MERGING_VID_FALLBACK" = "Merging video with fallback...";
+"MERGING_VID" = "Merging video...";
+"CONVERTING" = "Converting to";
+"ALREADY_DOWNLOADING" = "Already downloading";
+"DOWNLOAD_CANCELLED" = "Download cancelled";
+"CANCEL" = "Cancel";
+"TRIMMING_AUDIO" = "Trimming audio...";
+"COPY" = "Copy";
"APPEARANCE" = "Appearance";
"OLED_THEME" = "OLED dark mode";
@@ -46,6 +122,8 @@
"OLED_KEYBOARD_DESC" = "Make the keyboard uses a pure black (OLED) color for dark theme.";
"NAVBAR" = "Navigation bar";
+"STICKY_NAVBAR" = "Sticky Navigation bar";
+"STICKY_NAVBAR_DESC" = "Pins the Navigation bar so that it remains visible when scrolling down.";
"HIDE_YT_LOGO" = "Hide YouTube logo";
"HIDE_YT_LOGO_DESC" = "Hide YouTube logo in the navigation bar.";
"PREMIUM_LOGO" = "Use YouTube Premium logo";
@@ -62,34 +140,78 @@
"FEED" = "Feed";
"HIDE_SUBBAR" = "Hide subbar";
"HIDE_SUBBAR_DESC" = "Hide subbar at the top of the feed.";
+"HIDE_HORI_SHELF" = "Remove horizontal feeds";
+"HIDE_HORI_SHELF_DESC" = "Removes all horizontal feeds (News, Continue Watching, Posts collection, etc.) from the feed, except Playables.";
"HIDE_MUSIC_PLAYLISTS" = "Hide music playlist generator";
"HIDE_MUSIC_PLAYLISTS_DESC" = "Hide music playlist generator (Select music tunes in three) in the feed.";
"HIDE_FEED_POST" = "Hide posts";
"HIDE_FEED_POST_DESC" = "Hide posts in feed.";
+"HIDE_SURVEYS" = "Hide surveys";
+"HIDE_SURVEYS_DESC" = "Hide video surveys that YouTube shows sometimes.";
"HIDE_SHORTS_SHELF" = "Hide Shorts shelf";
"HIDE_SHORTS_SHELF_DESC" = "Hide Shorts shelf in the feed, including in the Subscriptions tab.";
+"KEEP_SHORTS_SUBSCRIPT" = "Keep Shorts in Subscriptions";
+"KEEP_SHORTS_SUBSCRIPT_DESC" = "Prevents removing Shorts from Subscriptions tab.";
"HIDE_SEARCH_HISTORY" = "Hide search history";
"HIDE_SEARCH_HISTORY_DESC" = "Hide previous search history and suggestions when using the search bar.\nNOTE: Your search history will still be accessible in your other YouTube clients.";
-"HIDE_SUB_BUTTON" = "Hide Subscribe button";
-"HIDE_SUB_BUTTON_DESC" = "Hides the subscribe button in channels and video descriptions.";
-"HIDE_SHOP_BUTTON" = "Hide Shopping button";
-"HIDE_SHOP_BUTTON_DESC" = "Hides the shopping/store button in channels descriptions.";
-"HIDE_MEMBER_BUTTON" = "Hide Membership button";
-"HIDE_MEMBER_BUTTON_DESC" = "Hides the 'Join' button in channels and video descriptions.";
+"HIDE_PLAYABLES" = "Remove playables";
+"HIDE_PLAYABLES_DESC" = "Removes playables (mini-games) from the feed.";
"PLAYER" = "Player";
+"PLAYBACK_SPEED" = "Playback speed";
+"CONTROL_CENTER" = "Control Center";
+"PLAYER_ACTIONS" = "Player actions";
+"GESTURE_HEADER" = "Gestures";
+"QUALITY_WIFI" = "Playback quality on WiFi";
+"QUALITY_WIFI_DESC" = "Automatically applies the selected quality when streaming over Wi-Fi.";
+"QUALITY_CELLULAR" = "Playback quality on Cellular";
+"QUALITY_CELLULAR_DESC" = "Automatically applies the selected quality when streaming over mobile data.";
+"QUALITY_LOW_POWER" = "Playback quality on Power saver mode";
+"QUALITY_LOW_POWER_DESC" = "Automatically applies the selected quality when when Low power mode is enabled on your device.";
+"DEFAULT" = "Default";
+"BEST" = "Best";
+"AUDIO_TRACK" = "Preferred audio track";
+"ORIGINAL" = "Original";
+"SELECT_MANUALLY" = "Select manually";
+"AUDIO_TRACK_SELECT" = "Selected audio track";
+"AUDIO_TRACK_SELECT_DESC" = "Specifies the preferred audio track language to use for playback, if available.";
+"NO_AUTO_DUBBED" = "Uses original audio track";
+"NO_AUTO_DUBBED_DESC" = "Uses original audio track if the preferred language track is auto-dubbed or not found.";
+"CAPTION_TRACK" = "Preferred captions";
+"CAPTION_TRACK_SELECT" = "Selected captions";
+"CAPTION_TRACK_SELECT_DESC" = "Specifies the preferred captions language to display, if available.";
+"DISABLES_CAPTION_TRACK" = "Automatically disable captions";
+"DISABLES_CAPTION_TRACK_DESC" = "Automatically disable captions if the preferred language is auto-generated or not found.";
+"DEFAULT_SPEED" = "Default playback rate";
+"DEFAULT_SPEED_DESC" = "Overrides the default playback speed.";
+"SPEED_BUTTON" = "Speed controls button";
+"SPEED_BUTTON_DESC" = "Displays a speed controls button on overlay.";
+"MUTE_BUTTON" = "Mute button";
+"MUTE_BUTTON_DESC" = "Displays a button to mute or unmute the volume on the overlay.";
+"SHARE_BUTTON" = "Share button";
+"SHARE_BUTTON_DESC" = "Displays a button to share the video link on the overlay.";
+"LOOP_BUTTON" = "Loop button";
+"LOOP_BUTTON_DESC" = "Displays a button to loop the video on the overlay.";
+"CAPTION_BUTTON" = "Captions button";
+"CAPTION_BUTTON_DESC" = "Displays a button to copy the video captions on the overlay.";
+"HOLD_TO_SPEED" = "Hold to speed";
+"HOLD_TO_SPEED_DESC" = "Uses custom hold to speed playback rate.";
"HIDE_AUTOPLAY" = "Hide autoplay switch";
"HIDE_AUTOPLAY_DESC" = "Hide autoplay switch in the video overlay.";
"HIDE_CAPTIONS_BUTTON" = "Hide captions button";
"HIDE_CAPTIONS_BUTTON_DESC" = "Hide captions button in the video overlay.";
"HIDE_CAST_BUTTON_PLAYER" = "Hide cast button";
"HIDE_CAST_BUTTON_PLAYER_DESC" = "Hide cast button in the video overlay.";
-"HIDE_PREV_BUTTON" = "Hide previous/back button";
-"HIDE_PREV_BUTTON_DESC" = "Hide previous/back button in the video overlay.";
-"HIDE_NEXT_BUTTON" = "Hide next/skip button";
-"HIDE_NEXT_BUTTON_DESC" = "Hide next/skip button in the video overlay.";
+"HIDE_NEXT_AND_PREV_BUTTON" = "Hide previous and next buttons";
+"HIDE_NEXT_AND_PREV_BUTTON_DESC" = "Hide previous and next buttons in the video overlay.";
"REPLACE_PREVNEXT_BUTTONS" = "Replace Previous/Next buttons";
"REPLACE_PREVNEXT_BUTTONS_DESC" = "Replaces the Previous and Next video buttons in the player with Rewind and Fast Forward buttons.";
+"SKIP_BACKWARD" = "Skip backward";
+"SKIP_BACKWARD_DESC" = "Replaces the previous track button in Control Center with a skip backward button by a set number of seconds.";
+"SKIP_FORWARD" = "Skip forward";
+"SKIP_FORWARD_DESC" = "Replaces the next track button in Control Center with a skip forward button by a set number of seconds.";
+"REWIND_SECONDS" = "Rewind seconds";
+"FORWARD_SECONDS" = "Fast-forward seconds";
"REMOVE_DARK_OVERLAY" = "Remove dark overlay";
"REMOVE_DARK_OVERLAY_DESC" = "Remove dark overlay from the video overlay.";
"HIDE_END_SCREEN" = "Hide endscreen cards";
@@ -102,6 +224,12 @@
"HIDE_PAID_OVERLAY_DESC" = "Hide 'Includes paid promotion' in the video overlay.";
"HIDE_WATERMARK" = "Hide watermark";
"HIDE_WATERMARK_DESC" = "Hide channel watermark at the bottom of the video overlay.";
+"PAUSE_ON_OVERLAY" = "Pause on overlay";
+"PAUSE_ON_OVERLAY_DESC" = "Sets playback on pause if overlay appears.";
+"DISABLES_SNAP_TO_CHAPTER" = "Disable snap to chapters";
+"DISABLES_SNAP_TO_CHAPTER_DESC" = "Prevents the scrubbar stick to the chapters.";
+"DISABLES_ENGAGE_PANEL" = "Disables engagement panel in fullscreen";
+"DISABLES_ENGAGE_PANEL_DESC" = "Prevent engagement panel showing in fullscreen.";
"GESTURES" = "Enable gestures controls";
"GESTURES_DESC" = "Swipe up or down on the left/right edges of the video screen to adjust brightness, volume, or playback speed.";
"GESTURE_AREA" = "Gesture activation area";
@@ -136,6 +264,8 @@
"DISABLES_SHOW_REMAINING_DESC" = "Disable the feature that shows how much time is left in a video when tapping the timestamp.";
"ALWAYS_SHOW_REMAINING" = "Always show remaining time";
"ALWAYS_SHOW_REMAINING_DESC" = "Ensure the remaining time is always displayed by default in the player overlay.";
+"COPY_TIMESTAMP_ON_PAUSE" = "Copy timestamped links";
+"COPY_TIMESTAMP_ON_PAUSE_DESC" = "Allows to copy timestamped link to the clipboard by pressing pause button.";
"SHOW_REMAINING_EXTRA" = "Show extra remaining time";
"SHOW_REMAINING_EXTRA_DESC" = "Adds the estimated finish time to the label in the video overlay.";
"HIDE_FULLSCREEN_ACTIONS" = "Hide fullscreen actions";
@@ -148,93 +278,95 @@
"HIDE_CONTENT_WARNING_DESC" = "Automatically skip the sensitive content warning alert in some vidoes.";
"AUTO_FULLSCREEN" = "Auto fullscreen";
"AUTO_FULLSCREEN_DESC" = "Automatically enter fullscreen mode when starting video playback.";
-"AUTO_DISABLES_CAPTION" = "Auto disable captions";
-"AUTO_DISABLES_CAPTION_DESC" = "Automatically turns off closed captions (CC) whenever a new video starts playing.";
"PORTRAIT_FULLSCREEN" = "Portrait fullscren mode";
"PORTRAIT_FULLSCREEN_DESC" = "Enables portrait fullscreen mode support.";
"OLD_QUALITY_PICKER" = "Use old quality picker";
"OLD_QUALITY_PICKER_DESC" = "Reverts the video quality selection menu to the classic version (lists all resolutions immediately).";
"EXTRA_SPEED" = "Extra speed options";
"EXTRA_SPEED_DESC" = "Adds more video playback speed options up to 10x.";
-"DISABLE_HINTS" = "Disable hints";
-"DISABLE_HINTS_DESC" = "Disable the tutorial hints and interactive pop-ups.";
"FORCE_MINIPLAYER" = "Force miniplayer";
"FORCE_MINIPLAYER_DESC" = "Allows all videos, including those made for kids, to play in the miniplayer when dismissing the player.";
"FORCE_SEEKBAR" = "Always show progress bar";
"FORCE_SEEKBAR_DESC" = "Makes the progress bar (seekbar) remains visible at all times while in fullscreen mode.";
-"HIDE_LIKE_BUTTON" = "Hide Like button";
-"HIDE_LIKE_BUTTON_DESC" = "Hide Like button in the action bar under the video overlay.";
-"HIDE_DISLIKE_BUTTON" = "Hide Dislike button";
-"HIDE_DISLIKE_BUTTON_DESC" = "Hide Dislike button in the action bar under the video overlay.";
-"HIDE_SHARE_BUTTON" = "Hide Share button";
-"HIDE_SHARE_BUTTON_DESC" = "Hide Share button in the action bar under the video overlay.";
-"HIDE_DOWNLOAD_BUTTON" = "Hide Download button";
-"HIDE_DOWNLOAD_BUTTON_DESC" = "Hide Download button in the action bar under the video overlay.";
-"HIDE_CLIP_BUTTON" = "Hide Clip button";
-"HIDE_CLIP_BUTTON_DESC" = "Hide Clip button in the action bar under the video overlay.";
-"HIDE_REMIX_BUTTON" = "Hide Remix button";
-"HIDE_REMIX_BUTTON_DESC" = "Hide Remix button in the action bar under the video overlay.";
-"HIDE_SAVE_BUTTON" = "Hide Save button";
-"HIDE_SAVE_BUTTON_DESC" = "Hide Save button in the action bar under the video overlay.";
+"TAP_TO_SEEK" = "Tap to seek";
+"TAP_TO_SEEK_DESC" = "Quickly jump to any point by tapping the progress bar.";
+"DISABLES_ZOOM" = "Disable free zoom gesture";
+"DISABLES_ZOOM_DESC" = "Disables new free zoom gestures.";
+"USES_24_HOURS_TIME" = "24-hour format";
+"USES_24_HOURS_TIME_DESC" = "Shows end time in 24-hour format.";
+"PAUSE_TWO_FINGERS" = "Pause using two fingers";
+"PAUSE_TWO_FINGERS_DESC" = "Pause/Resume playback by tapping on the screen using two fingers.";
+"HIDE_COMMENTS_SECTION" = "Hide comments section";
+"HIDE_COMMENTS_SECTION_DESC" = "Completely removes the entire comments section.";
+"HIDE_COMMENTS_PREVIEW" = "Hide preview comments";
+"HIDE_COMMENTS_PREVIEW_DESC" = "Removes the preview box for the top comment while keeping the full section expandable.";
+"USE_ANOTHER_MINIPLAYER" = "Uses another miniplayer style";
+"USE_ANOTHER_MINIPLAYER_DESC" = "Uses another miniplayer style redesigned from older YT versions.";
"SHORTS" = "Shorts";
-"HIDE_SHORTS_LIKE_BUTTON" = "Hide Like button";
-"HIDE_SHORTS_LIKE_BUTTON_DESC" = "Hide the Like button in the Shorts player.";
-"HIDE_SHORTS_DISLIKE_BUTTON" = "Hide Dislike button";
-"HIDE_SHORTS_DISLIKE_BUTTON_DESC" = "Hide the Dislike button in the Shorts player.";
-"HIDE_SHORTS_COMMENT_BUTTON" = "Hide Comment button";
-"HIDE_SHORTS_COMMENT_BUTTON_DESC" = "Hide the Comment button in the Shorts player.";
-"HIDE_SHORTS_SHARE_BUTTON" = "Hide Share button";
-"HIDE_SHORTS_SHARE_BUTTON_DESC" = "Hide the Share button in the Shorts player.";
-"HIDE_SHORTS_REMIX_BUTTON" = "Hide Remix button";
-"HIDE_SHORTS_REMIX_BUTTON_DESC" = "Hide the Remix button in the Shorts player.";
-"HIDE_METADATA_BUTTON" = "Hide sound metadata button";
-"HIDE_METADATA_BUTTON_DESC" = "Hide the button that shows the sound source or music metadata in the Shorts player.";
+"SHORTS_ONLY" = "Shorts only mode";
+"SHORTS_ONLY_DESC" = "Limits YouTube functionality to viewing Shorts only.";
+"SHORTS_ACTION" = "Shorts action after the video ends";
+"LOOP" = "Loop video";
+"SKIP_TO_NEXT_SHORTS" = "Skip to next video";
+"PAUSE_SHORTS" = "Stop video";
+"REMOVE_LIVE_SHORTS" = "Remove live videos";
+"REMOVE_LIVE_SHORTS_DESC" = "Removes live videos from the feed.";
"HIDE_SHORTS_PRODUCT" = "Hide products";
"HIDE_SHORTS_PRODUCT_DESC" = "Hide tagged products and shopping links in the Shorts overlay.";
"HIDE_SHORTS_RECBAR" = "Hide recommendation bar";
"HIDE_SHORTS_RECBAR_DESC" = "Hide the suggested action bar in the Shorts player (e.g. Try this sound, Shopping, Preview comment).";
-"HIDE_SHORTS_COMMIT" = "Hide commissions bar";
-"HIDE_SHORTS_COMMIT_DESC" = "Hide the bar that shows the video got commissions in the Shorts player.";
-"HIDE_SHORTS_SUBSCRIPT_BUTTON" = "Hide subscriptions button";
-"HIDE_SHORTS_SUBSCRIPT_BUTTON_DESC" = "Hide the Subscriptions button at the top of the Shorts player.";
-"HIDE_SHORTS_LIVE_BUTTON" = "Hide live button";
-"HIDE_SHORTS_LIVE_BUTTON_DESC" = "Hide the Live button at the top of the Shorts player.";
-"HIDE_SHORTS_LENS_BUTTON" = "Hide lens button";
-"HIDE_SHORTS_LENS_BUTTON_DESC" = "Hide the Lens button at the top of the Shorts player.";
-"HIDE_SHORTS_TRENDS_BUTTON" = "Hide trends button";
-"HIDE_SHORTS_TRENDS_BUTTON_DESC" = "Hide the Trends button at the top of the Shorts player.";
-"HIDE_SHORTS_TO_VIDEO" = "Hide link to full video";
-"HIDE_SHORTS_TO_VIDEO_DESC" = "Hide the link that redirects in a Short to a full video in the Shorts player.";
"ENABLES_SHORTS_QUALITY" = "Enable Shorts quality picker";
"ENABLES_SHORTS_QUALITY_DESC" = "Force enable the video quality selection menu in Shorts.";
"SHOW_SHORTS_SEEKBAR" = "Always show seekbar";
"SHOW_SHORTS_SEEKBAR_DESC" = "Ensure the progress/seek bar is always visible at the bottom of the Shorts player.";
+"REMOVE_POSTS_SHORTS" = "Remove posts";
+"REMOVE_POSTS_SHORTS_DESC" = "Remove posts from the feed.";
+"SHORTS_FULLSCREEN" = "Pinch to Fullscreen";
+"SHORTS_FULLSCREEN_DESC" = "Manages visibility of the overlay with pinch in and pinch out gestures, displaying Shorts in fullscreen mode.";
"TABBAR" = "Tab bar";
"DEFAULT_TAB" = "Default startup tab";
-"DEFAULT_TAB_DESC" = "You can select the tab you want to open when opening the app.";
-"HOME_NAME" = "Home";
-"SUBSCRIPT_NAME" = "Subscriptions";
-"LIB_NAME" = "Library (You)";
+"FORSTED_TAB_BAR" = "Translucent tab bar";
+"ENABLED" = "Enabled";
"HIDE_TAB_INDI" = "Hide tab indicators";
"HIDE_TAB_INDI_DESC" = "Hide all tab indicators in the tab bar.";
"HIDE_TAB_LABELS" = "Hide tab labels";
"HIDE_TAB_LABELS_DESC" = "Hide all tab labels in the tab bar.";
-"HIDE_HOME_TAB" = "Hide Home tab";
-"HIDE_HOME_TAB_DESC" = "Hide Home tab in the tab bar.";
-"HIDE_SHORTS_TAB" = "Hide Shorts tab";
-"HIDE_SHORTS_TAB_DESC" = "Hide Shorts tab in the tab bar.";
-"HIDE_CREATE_BUTTON" = "Hide Create button";
-"HIDE_CREATE_BUTTON_DESC" = "Hide Create button in the tab bar.";
-"HIDE_SUBSCRIPT_TAB" = "Hide Subscriptions tab";
-"HIDE_SUBSCRIPT_TAB_DESC" = "Hide Subscriptions tab in the tab bar.";
+"HOME_TAB" = "Home";
+"SHORTS_TAB" = "Shorts";
+"CREATE_TAB" = "Create";
+"SUBSCRIPTIONS_TAB" = "Subscriptions";
+"LIBRARY_TAB" = "Library (You)";
+"HISTORY_TAB" = "History";
+"GAMING_TAB" = "Gaming";
+"SPORTS_TAB" = "Sports";
+"NOTI_TAB" = "Notifications";
+"NEWS_TAB" = "News";
+"MUSIC_TAB" = "Music";
+"WATCH_LATER_TAB" = "Watch later";
+"PLAYLIST_TAB" = "Playlists";
+"LIKE_TAB" = "Liked videos";
+"LIVE_TAB" = "Live";
+"POST_TAB" = "Posts";
+"VIDEO_TAB" = "Your videos";
+"MOVIE_TAB" = "Movies";
+"COURSE_TAB" = "Courses";
+"MINIGAME_TAB" = "Playables";
+"MANAGE_TABS" = "Manage tabs";
+"MANAGE_TABS_DESC" = "Reorder and toggle tabs";
+"TAB_REORDER_HINT" = "Drag to reorder. Toggle to show/hide.\nTab settings are also accessible by long-pressing the first tab in the tab bar.";
+"TAB_LIMIT" = "Tab limit";
+"TAB_LIMIT_DESC" = "Only 6 tabs are allowed.";
+"ZERO_TAB_DESC" = "At least 1 tab has to be enabled.";
"MISCELLANEOUS" = "Miscellaneous";
"BACKGROUND_PLAYBACK" = "Background playback";
"BACKGROUND_PLAYBACK_DESC" = "Enables background playback for videos.";
"DISABLES_SHORTS_PIP" = "Disables Shorts PiP";
"DISABLES_SHORTS_PIP_DESC" = "Try to disables Shorts PiP, this might not 100% disables it.";
+"DISABLE_HINTS" = "Disable hints";
+"DISABLE_HINTS_DESC" = "Disable the tutorial hints and interactive pop-ups.";
"BLOCK_UPGRADE_DIALOGS" = "Block upgrade dialogs";
"BLOCK_UPGRADE_DIALOGS_DESC" = "Block the YouTube dialogs that ask you to upgrade the app.";
"ARE_YOU_THERE_DIALOG" = "Hide 'Are you there?' dialog";
@@ -247,7 +379,109 @@
"DISABLES_SNACK_BAR_DESC" = "Disables snack bar when pressing some buttons or doing something in the app.";
"HIDE_STARTUP_ANIMATIONS" = "Hide YouTube's startup animations";
"HIDE_STARTUP_ANIMATIONS_DESC" = "Hide YouTube's startup animations when opening the app.";
-"HIDE_PLAY_IN_NEXT_QUEUE" = "Hide 'Play next in queue'";
-"HIDE_PLAY_IN_NEXT_QUEUE_DESC" = "Hide the 'Play next in queue' option in the flyout menu.";
"HIDE_LIKE_DISLIKE_VOTES" = "Hide like/dislike votes";
-"HIDE_LIKE_DISLIKE_VOTES_DESC" = "Hide messages that pop up when you like/dislike videos.";
\ No newline at end of file
+"HIDE_LIKE_DISLIKE_VOTES_DESC" = "Hide messages that pop up when you like/dislike videos.";
+"FLYOUT_MENU" = "Flyout menu";
+"REMOVE_PLAY_IN_NEXT_QUEUE_OPTION" = "Removes 'Play next in queue' option";
+"REMOVE_PLAY_IN_NEXT_QUEUE_OPTION_DESC" = "Removes 'Play next in queue' option from the flyout menu.";
+"REMOVE_DOWNLOAD_OPTION" = "Removes 'Download video' option";
+"REMOVE_DOWNLOAD_OPTION_DESC" = "Removes 'Download video' option from the flyout menu.";
+"REMOVE_WATCH_LATER_OPTION" = "Removes 'Save to Watch later' option";
+"REMOVE_WATCH_LATER_OPTION_DESC" = "Removes 'Save to Watch Later' option from the flyout menu.";
+"REMOVE_SAVE_OPTION" = "Removes 'Save to playlist' option";
+"REMOVE_SAVE_OPTION_DESC" = "Removes 'Save to playlist' option from the flyout menu.";
+"REMOVE_REMOVE_FROM_PLAYLIST_OPTION" = "Removes 'Remove from playlist' option";
+"REMOVE_REMOVE_FROM_PLAYLIST_OPTION_DESC" = "Removes 'Remove from playlist' option from the flyout menu.";
+"REMOVE_SHARE_OPTION" = "Removes 'Share' option";
+"REMOVE_SHARE_OPTION_DESC" = "Removes 'Share' option from the flyout menu.";
+"REMOVE_NOT_INTERESTED_OPTION" = "Removes 'Not interested' option";
+"REMOVE_NOT_INTERESTED_OPTION_DESC" = "Removes 'Not interested' option from the flyout menu.";
+"REMOVE_DONT_RECOMMEND_OPTION" = "Removes 'Don't recommend channel' option";
+"REMOVE_DONT_RECOMMEND_OPTION_DESC" = "Removes 'Don't recommend channel' option from the flyout menu.";
+"REMOVE_INFO_OPTION" = "Removes 'Why are you seeing this?' option";
+"REMOVE_INFO_OPTION_DESC" = "Removes 'Why are you seeing this?' option from the flyout menu.";
+"REMOVE_FILTER_OPTION" = "Removes 'Search filters' option";
+"REMOVE_FILTER_OPTION_DESC" = "Removes 'Search filters' option from the flyout menu.";
+"REMOVE_REPORT_OPTION" = "Removes 'Report' option";
+"REMOVE_REPORT_OPTION_DESC" = "Removes 'Report' option from the flyout menu.";
+"REMOVE_YOUTUBE_MUSIC_OPTION" = "Removes 'Listen with YouTube Music' option";
+"REMOVE_YOUTUBE_MUSIC_OPTION_DESC" = "Removes 'Listen with YouTube Music' option from the flyout menu.";
+"REMOVE_FEED_BACK_OPTION" = "Removes 'Send feedback' option";
+"REMOVE_FEED_BACK_OPTION_DESC" = "Removes 'Send feedback' option from the flyout menu.";
+"REMOVE_CAST_OPTION" = "Removes 'Watch on TV' option";
+"REMOVE_CAST_OPTION_DESC" = "Removes 'Watch on TV' option from the flyout menu.";
+"REMOVE_SHUFFLE_OPTION" = "Removes 'Shuffle' option";
+"REMOVE_SHUFFLE_OPTION_DESC" = "Removes 'Shuffle' option from the flyout menu.";
+"REMOVE_UN_SUB_OPTION" = "Removes 'Unsubscribe' option";
+"REMOVE_UN_SUB_OPTION_DESC" = "Removes 'Unsubscribe' option from the flyout menu.";
+"REMOVE_HIDE_FROM_PLAYLIST_OPTION" = "Removes 'Hide video from playlist' option";
+"REMOVE_HIDE_FROM_PLAYLIST_OPTION_DESC" = "Removes 'Hide video from playlist' option from the flyout menu.";
+"REMOVE_HELP_OPTION" = "Removes 'Help & feedback' option";
+"REMOVE_HELP_OPTION_DESC" = "Removes 'Help & feedback' option from the flyout menu.";
+"REMOVE_NOTIFY_OPTION" = "Removes 'Notify me' option";
+"REMOVE_NOTIFY_OPTION_DESC" = "Removes 'Notify me' option from the flyout menu.";
+"REMOVE_CLEARSCREEN_OPTION" = "Removes 'Clear Screen' option";
+"REMOVE_CLEARSCREEN_OPTION_DESC" = "Removes 'Clear Screen' option from the flyout menu.";
+"HIDE_COMMU_GUIDE" = "Hide community guidelines";
+"HIDE_COMMU_GUIDE_DESC" = "Hide community guidelines header.";
+"DISABLES_RTL" = "Disable RTL formatting";
+"DISABLES_RTL_DESC" = "Forcefully displays text in left-to-right (LTR) format for languages that are initially displayed in right-to-left (RTL).";
+"DEVICE_UI" = "Interface style";
+
+"SB_DESCRIPTION" = "Skip sponsored segments automatically using community-submitted data.";
+"SB_ENABLE" = "Enable SponsorBlock";
+"SB_ENABLE_DESC" = "Activate the SponsorBlock extension to skip sponsored segments.";
+"SB_SHOW_BUTTON" = "Show overlay button";
+"SB_SHOW_BUTTON_DESC" = "Display a SponsorBlock toggle button in the player overlay.";
+"SB_SHOW_NOTIFICATIONS" = "Show notifications";
+"SB_SHOW_NOTIFICATIONS_DESC" = "Display notifications when segments are skipped. Notifications for manual-skip categories are always shown.";
+"SB_HAPTIC_FEEDBACK" = "Haptic feedback";
+"SB_HAPTIC_FEEDBACK_DESC" = "Provide haptic feedback when a segment is skipped.";
+"SB_SEGMENTS_IN_PLAYER" = "Segments in player";
+"SB_SEGMENTS_IN_PLAYER_DESC" = "Display colored segments on the progress bar of the main video player.";
+"SB_SEGMENTS_IN_FEED" = "Segments in feed";
+"SB_SEGMENTS_IN_FEED_DESC" = "Display colored segments on the progress bar of players in the feed.";
+"SB_SEGMENTS_IN_MINIPLAYER" = "Segments in mini-player";
+"SB_SEGMENTS_IN_MINIPLAYER_DESC" = "Display colored segments on the progress bar of the mini-player.";
+"SB_SHOW_DURATION" = "Show duration without segments";
+"SB_SHOW_DURATION_DESC" = "Display the total video duration excluding skippable segments.";
+"SB_CATEGORIES_HEADER" = "Segment categories";
+"SB_SEGMENT_COLOR" = "Segment color";
+
+"SB_ACTION_DISABLE" = "Disable";
+"SB_ACTION_AUTO_SKIP" = "Auto-skip";
+"SB_ACTION_ASK" = "Ask before skipping";
+"SB_ACTION_DISPLAY" = "Show on seek bar only";
+"SB_ACTION_SKIP_TO" = "Skip to segment";
+
+"SB_CAT_sponsor" = "Sponsor";
+"SB_CAT_sponsor_DESC" = "Paid promotion for a product or service not directly related to the creator.";
+"SB_CAT_intro" = "Intro";
+"SB_CAT_intro_DESC" = "Intermission or intro animation typically seen at the start of a video.";
+"SB_CAT_outro" = "Endcards";
+"SB_CAT_outro_DESC" = "Endcards, credits, or outros at the end of the video.";
+"SB_CAT_interaction" = "Interaction";
+"SB_CAT_interaction_DESC" = "Reminders to like, subscribe, or follow on social media.";
+"SB_CAT_selfpromo" = "Self-promotion";
+"SB_CAT_selfpromo_DESC" = "Unpaid promotion of the creator's own products, merchandise, or other content.";
+"SB_CAT_music_offtopic" = "Non-music";
+"SB_CAT_music_offtopic_DESC" = "Non-music sections in music videos (e.g. talking, introductions).";
+"SB_CAT_preview" = "Preview";
+"SB_CAT_preview_DESC" = "Recap or preview of content from another video or previous episode.";
+"SB_CAT_hook" = "Hook/Greetings";
+"SB_CAT_hook_DESC" = "Narrated trailers for the upcoming video, greetings and goodbyes.";
+"SB_CAT_poi_highlight" = "Highlight";
+"SB_CAT_poi_highlight_DESC" = "The key moment or highlight point of the video. Use 'Skip to' to jump directly to it.";
+"SB_CAT_filler" = "Filler";
+"SB_CAT_filler_DESC" = "Tangential content, jokes, or filler that is not directly related to the main topic.";
+
+"SB_SKIPPED" = "%@ segment has been skipped";
+"SB_DETECTED" = "%@ segment detected.\nWould you like to skip the segment?";
+"SB_SKIP_NOW" = "Skip";
+"SB_UNSKIP" = "Unskip";
+"SB_JUMPED_TO_HIGHLIGHT" = "Jumped to highlight";
+"SB_JUMP_TO_HIGHLIGHT" = "Highlight available. Jump to the point?";
+"SB_SEGMENT_COLOR_SUFFIX" = "Segment color";
+"SB_SECTION_MAIN" = "Main";
+"SB_SKIP_ALERT_DURATION" = "Skip alert duration";
+"SB_UNSKIP_ALERT_DURATION" = "Unskip alert duration";
\ No newline at end of file
diff --git a/layout/Library/Application Support/YouMod.bundle/th.lproj/Localizable.strings b/layout/Library/Application Support/YouMod.bundle/th.lproj/Localizable.strings
index 37eebe70..ce15b440 100644
--- a/layout/Library/Application Support/YouMod.bundle/th.lproj/Localizable.strings
+++ b/layout/Library/Application Support/YouMod.bundle/th.lproj/Localizable.strings
@@ -1,11 +1,27 @@
-"NOTE" = "หมายเหตุ: ฟีเจอร์การซ่อนบางอย่างอาจทำให้เกิดช่องว่างเปล่า";
"SETTINGS" = "การตั้งค่า";
+"INTERFACE" = "ลักษณะ";
+
+"URL_COPIED" = "คัดลอกลิงก์วิดีโอแล้ว";
+"COPY_URL" = "คัดลอกลิงก์วิดีโอ";
+"COPY_URL_TIMESTAMP" = "คัดลอกลิงก์วิดีโอพร้อมเวลา";
+"URL_TIMESTAMP_COPIED" = "คัดลอกลิงก์วิดีโอพร้อมเวลาแล้ว";
+"CANCEL" = "ยกเลิก";
+"ERROR" = "เกิดข้อผิดผลาด";
+"ERROR_VIDEOID" = "ไม่พบ ID วิดีโอ";
+"ERROR_ADS" = "ไม่สามารถคัดลอกลิงก์วิดีโอขณะกำลังเล่นโฆษณาอยู่ได้";
+
+"LOOP_DISABLED" = "ปิดใช้งานการวนซ้ำแล้ว";
+"LOOP_ENABLED" = "เปิดใช้งานการวนซ้ำแล้ว";
"NEW_ISSUES" = "รายงานปัญหาหรือขอฟีเจอร์ใหม่";
"NEW_ISSUES_DESC" = "พบบัคหรืออยากได้ฟีเจอร์ใหม่? แตะที่นี่";
"SOURCE_CODES" = "ซอร์สโค้ด";
"SOURCE_CODES_DESC" = "ลองอ่านโค้ดดูสิ!";
+"EXTRA" = "เพิ่มเติม";
+"FIX_PLAYBACK_ISSUES" = "แก้ไขปัญหาการเล่นวิดีโอ (ชั่วคราว)";
+"FIX_PLAYBACK_ISSUES_DESC" = "แก้ไขปัญหาการเล่นวิดีโอสำหรับผู้ใช้บางราย เมื่อเปิดใช้งานตัวเลือกนี้เครื่องเล่นจะรีโหลดอัตโนมัติเมื่อเกิดข้อผิดพลาดในการเล่นวิดีโอ";
+
"PERFER_HEADER" = "การตั้งค่า YouMod";
"PERFER" = "การตั้งค่า";
"IMPORT" = "นำเข้าการตั้งค่า";
@@ -24,6 +40,7 @@
"ERROR_NO_KEYS_IMPORT" = "ไม่มีคีย์สำหรับนำเข้า";
"ERROR_NO_KEYS_EXPORT" = "ไม่มีคีย์สำหรับส่งออก";
"OVERRIDE" = "การกระทำนี้จะเขียนทับการตั้งค่าปัจจุบัน ดำเนินการต่อหรือไม่?";
+"DISABLED" = "ปิดใช้งาน";
"CACHE" = "การจัดการแคช";
"CLEARCACHE" = "ล้างแคช";
@@ -36,8 +53,71 @@
"DOWNLOAD_MANAGER_DESC" = "แทนที่ตัวดาวน์โหลดวิดีโอของ YouTube ด้วยของ YouMod รองรับการดาวน์โหลดวิดีโอ เสียง คำบรรยาย และปกคลิป";
"DOWNLOAD_SAVE_PHOTOS" = "บันทึกลงรูปภาพอัตโนมัติ";
"DOWNLOAD_SAVE_PHOTOS_DESC" = "บันทึกวิดีโอและรูปภาพที่ดาวน์โหลดไปยังแอปรูปภาพของคุณโดยอัตโนมัติ";
-"DOWNLOAD_DRC_AUDIO" = "เลือกเสียงแบบ DRC";
-"DOWNLOAD_DRC_AUDIO_DESC" = "ให้ความสำคัญกับการดาวน์โหลดแทร็กเสียงที่มีการบีบอัดช่วงไดนามิก เพื่อให้ระดับเสียงสม่ำเสมอมากขึ้น";
+"ADD_SHORTS_DOWNLOAD" = "เพิ่มปุ่มดาวน์โหลดไปยัง Shorts";
+"ADD_SHORTS_DOWNLOAD_DESC" = "เพิ่มปุ่มดาวน์โหลดไปยังโอเวอร์เลย์วิดีโอ Shorts";
+"USE_ORIG_AUDIO" = "ใช้แทร็กเสียงต้นฉบับโดยอัตโนมัติ";
+"USE_ORIG_AUDIO_DESC" = "ใช้แทร็กเสียงต้นฉบับโดยอัตโนมัติแทนแสดงกล่องตัวเลือก";
+"DOWNLOAD_SHORTS" = "ดาวน์โหลด Shorts";
+"DOWNLOAD_SHORTS_DESC" = "เลือกคุณภาพ Shorts";
+"COPY_VID_INFO" = "คัดลอกข้อมูลวิดีโอ";
+"COPY_VID_INFO_DESC" = "เลือกสิ่งที่ต้องการคัดลอก";
+"COPY_TITLE" = "คัดลอกชื่อ";
+"COPY_TITLE_DESC" = "คัดลอกชื่อวิดีโอ";
+"COPY_DESCRIPTION" = "คัดลอกคำอธิบาย";
+"COPY_DESCRIPTION_DESC" = "คัดลอกคำอธิบายวิดีโอ";
+"COPY_ALL_VID_INFO" = "คัดลอกทั้งหมด";
+"COPY_ALL_VID_INFO_DESC" = "คัดลอกข้อมูลวิดีโอทั้งหมด";
+"COPIED_TITLE" = "คัดลอกชื่อวิดีโอแล้ว";
+"COPIED_DESCRIPTION" = "คัดลอกคำอธิบายวิดีโอแล้ว";
+"COPY_AUTHOR" = "คัดลอกชื่อช่อง";
+"COPY_AUTHOR_DESC" = "คัดลอกชื่อช่องวิดีโอ";
+"COPIED_AUTHOR" = "คัดลอกชื่อช่องวิดีโอแล้ว";
+"SAVE_THUMBNAIL" = "บันทึกปกคลิป";
+"SAVE_THUMBNAIL_DESC" = "บันทึกลงในแอปรูปภาพ";
+"DOWNLOAD_CAPTIONS" = "ดาวน์โหลดคำบรรยาย";
+"DOWNLOAD_CAPTIONS_DESC" = "บันทึกซับไตเติ้ลเป็นไฟล์ .VTT";
+"DOWNLOAD_AUDIO" = "ดาวน์โหลดเสียง";
+"DOWNLOAD_AUDIO_DESC" = "บันทึกเสียงเป็นไฟล์ .m4a";
+"DOWNLOAD_VIDEO" = "ดาวน์โหลดวิดีโอ";
+"DOWNLOAD_VIDEO_DESC" = "เลือกคุณภาพวิดีโอ";
+"OPEN_VID_BEFORE" = "กรุณาเปิดวิดีโอก่อนใช้ตัวจัดการดาวน์โหลด";
+"NO_CAPTIONS_URL" = "ไม่พบ URL ของคำบรรยายที่ถูกต้อง";
+"NO_AUDIO_URL" = "ไม่พบ URL ของเสียง";
+"NO_STREAM_URL" = "ไม่พบ URL ของสตรีม";
+"CAPTIONS_FAILED" = "ดาวน์โหลดคำบรรยายไม่สำเร็จ";
+"NO_CAPTIONS" = "ไม่มีคำบรรยายสำหรับวิดีโอนี้";
+"NO_AUDIO_STREAM_FOUND" = "ไม่พบสตรีมเสียง";
+"NO_VID_AUDIO_STREAM_FOUND" = "ไม่พบสตรีมวิดีโอ/เสียง";
+"COPIED_VID_INFO" = "คัดลอกข้อมูลวิดีโอทั้งหมดแล้ว";
+"SAVED_TO_PHOTOS" = "บันทึกลงในแอปรูปภาพแล้ว";
+"SAVE_FAILED" = "บันทึกไม่สำเร็จ";
+"PHOTO_ACCESS_DENINED" = "การเข้าถึงแอปรูปภาพถูกปฏิเสธ";
+"THUMBNAIL_FAILED" = "บันทึกปกคลิปไม่สำเร็จ";
+"DOWNLOADING_THUMBNAIL" = "กำลังดาวน์โหลดปกคลิป...";
+"DOWNLOADING_AUDIO" = "กำลังดาวน์โหลดเสียง...";
+"DOWNLOADING_VIDEO" = "กำลังดาวน์โหลดวิดีโอ...";
+"DOWNLOADING_CAPTIONS" = "กำลังดาวน์โหลดคำบรรยาย...";
+"NO_THUMBNAIL_FOUND" = "ไม่พบปกคลิป";
+"DOWNLOAD_FAILED" = "ดาวน์โหลดไม่สำเร็จ";
+"AUDIO_SAVED" = "บันทึกเสียงเรียบร้อยแล้ว";
+"DOWNLOAD_COMPLETED" = "ดาวน์โหลดเสร็จสมบูรณ์";
+"CANNOT_SAVE_TO_PHOTOS" = "ไม่สามารถบันทึกลงในแอปรูปภาพได้";
+"FINA_VIDEO" = "กำลังจัดเตรียมวิดีโอ...";
+"MERGING_VID_FALLBACK" = "กำลังรวมไฟล์วิดีโอ (โหมดสำรอง)...";
+"MERGING_VID" = "กำลังรวมไฟล์วิดีโอ...";
+"CONVERTING" = "กำลังแปลงเป็น";
+"ALREADY_DOWNLOADING" = "กำลังดาวน์โหลดอยู่แล้ว";
+"DOWNLOAD_CANCELLED" = "ยกเลิกการดาวน์โหลดแล้ว";
+"CANCEL" = "ยกเลิก";
+"TRIMMING_AUDIO" = "กำลังตัดต่อเสียง...";
+"SHOW_THUMBNAIL" = "ดูปกคลิป";
+"SHOW_THUMBNAIL_DESC" = "ดูปกคลิปวิดีโอ";
+"COPY_THUMBNAIL" = "คัดลอกปกคลิป";
+"COPY_THUMBNAIL_DESC" = "คัดลอกปกคลิปไปยังคลิปบอร์ด";
+"THUMBNAIL_OPTIONS" = "ดาวน์โหลดปกคลิป";
+"THUMBNAIL_OPTIONS_DESC" = "แสดงตัวเลือกเพิ่มเติม";
+"COPY" = "คัดลอก";
+"COPIED_TO_CLIPBOARD" = "คัดลอกไปยังคลิปบอร์ดแล้ว";
"APPEARANCE" = "ลักษณะ";
"OLED_THEME" = "ธีม OLED";
@@ -46,6 +126,8 @@
"OLED_KEYBOARD_DESC" = "ใช้สีดำสนิท (OLED) กับคีย์บอร์ดในโหมดมืด";
"NAVBAR" = "แถบนำทาง";
+"STICKY_NAVBAR" = "ยึดแถบนำทาง";
+"STICKY_NAVBAR_DESC" = "ล็อกแถบนำทางให้คงเห็นอยู่เมื่อเลื่อนลง";
"HIDE_YT_LOGO" = "ซ่อนโลโก้ YouTube";
"HIDE_YT_LOGO_DESC" = "ซ่อนโลโก้ YouTube ในแถบนำทาง";
"PREMIUM_LOGO" = "ใช้โลโก้ YouTube Premium";
@@ -62,32 +144,70 @@
"FEED" = "ฟีด";
"HIDE_SUBBAR" = "ซ่อนแถบหมวดหมู่";
"HIDE_SUBBAR_DESC" = "ซ่อนแถบหมวดหมู่ในฟีด";
+"HIDE_HORI_SHELF" = "ซ่อนฟีดแนวนอน";
+"HIDE_HORI_SHELF_DESC" = "ลบฟีดแนวนอนทั้งหมด (เช่น ข่าวสาร, รับชมต่อ, โพสต์ ฯลฯ) ออกจากหน้าฟีด ยกเว้นเกม";
"HIDE_MUSIC_PLAYLISTS" = "ซ่อนเพลย์ลิสต์เพลง";
"HIDE_MUSIC_PLAYLISTS_DESC" = "ซ่อนตัวสร้างเพลย์ลิสต์ในฟีด";
"HIDE_FEED_POST" = "ซ่อนโพส";
"HIDE_FEED_POST_DESC" = "ซ่อนโพสในฟีด";
+"HIDE_SURVEYS" = "ซ่อนแบบสำรวจ";
+"HIDE_SURVEYS_DESC" = "ซ่อนแบบสำรวจวิดีโอที่ YouTube แสดงในบางครั้ง";
"HIDE_SHORTS_SHELF" = "ซ่อน Shorts";
"HIDE_SHORTS_SHELF_DESC" = "ซ่อน Shorts ในฟีด";
+"KEEP_SHORTS_SUBSCRIPT" = "แสดง Shorts ในหน้าการติดตาม";
+"KEEP_SHORTS_SUBSCRIPT_DESC" = "ป้องกันไม่ให้วิดีโอ Shorts ถูกลบออกจากแท็บการติดตาม";
"HIDE_SEARCH_HISTORY" = "ซ่อนประวัติการค้นหา";
"HIDE_SEARCH_HISTORY_DESC" = "ซ่อนประวัติและคำแนะนำการค้นหา\nหมายเหตุ: ประวัติการค้นหายังสามารถเข้าถึงได้ในอุปกรณ์อื่น";
-"HIDE_SUB_BUTTON" = "ซ่อนปุ่มติดตาม";
-"HIDE_SUB_BUTTON_DESC" = "ซ่อนปุ่มติดตามในช่องและคำอธิบายวิดีโอ";
-"HIDE_SHOP_BUTTON" = "ซ่อนปุ่มร้านค้า";
-"HIDE_SHOP_BUTTON_DESC" = "ซ่อนปุ่มร้านค้าในช่อง";
-"HIDE_MEMBER_BUTTON" = "ซ่อนปุ่ม 'สมัคร'";
-"HIDE_MEMBER_BUTTON_DESC" = "ซ่อนปุ่ม 'สมัคร' ในช่องและคำอธิบายวิดีโอ";
+"HIDE_PLAYABLES" = "ซ่อนมินิเกม";
+"HIDE_PLAYABLES_DESC" = "ซ่อนมินิเกมออกจากหน้าฟีด";
"PLAYER" = "เครื่องเล่น";
+"PLAYBACK_SPEED" = "ความเร็ววิดีโอ";
+"CONTROL_CENTER" = "ศูนย์ควบคุม";
+"PLAYER_ACTIONS" = "การกระทำเครื่องเล่น";
+"GESTURE_HEADER" = "ท่าทาง";
+"QUALITY_WIFI" = "คุณภาพวิดีโอเมื่อเล่นบน WiFi";
+"QUALITY_WIFI_DESC" = "เลือกคุณภาพวิดีโออัตโนมัติจากตัวเลือกเมื่อเล่นบน Wi-Fi";
+"QUALITY_CELLULAR" = "คุณภาพวิดีโอเมื่อเล่นบนเซลลูลาร์";
+"QUALITY_CELLULAR_DESC" = "เลือกคุณภาพวิดีโออัตโนมัติจากตัวเลือกเมื่อเล่นบนเซลลูลาร์";
+"QUALITY_LOW_POWER" = "คุณภาพวิดีโอในโหมดประหยังพลังงาน";
+"QUALITY_LOW_POWER_DESC" = "เลือกคุณภาพวิดีโออัตโนมัติจากตัวเลือกเมื่อเปิดโหลดประหยัดพลังงานในอุปกรณ์";
+"DEFAULT" = "ค่าเริ่มต้น";
+"BEST" = "ดีที่สุด";
+"AUDIO_TRACK" = "แทร็กเสียงที่ต้องการ";
+"ORIGINAL" = "ต้นฉบับ";
+"SELECT_MANUALLY" = "เลือกเอง";
+"AUDIO_TRACK_SELECT" = "แทร็กเสียงที่เลือก";
+"AUDIO_TRACK_SELECT_DESC" = "เลือกแทร็กเสียงที่ต้องการโดยอัตโนมัติ ถ้ามี";
+"NO_AUTO_DUBBED" = "ใช้เสียงต้นฉบับ";
+"NO_AUTO_DUBBED_DESC" = "ใช้เสียงต้นฉบับถ้าไม่พบภาษาที่ต้องการหรือเป็นเสียงแบบพากย์อัตโนมัติ";
+"CAPTION_TRACK" = "คำบรรยายที่ต้องการ";
+"CAPTION_TRACK_SELECT" = "คำบรรยายที่เลือก";
+"CAPTION_TRACK_SELECT_DESC" = "เลือกภาษาของคำบรรยายที่ต้องการให้แสดง ถ้ามี";
+"DISABLES_CAPTION_TRACK" = "ปิดใช้งานคำบรรยายอัตโนมัติ";
+"DISABLES_CAPTION_TRACK_DESC" = "ปิดใช้งานคำบรรยายอัตโนมัติถ้าภาษาที่เลือกเป็นแบบสร้างโดยอัตโนมัติหรือไม่พบ";
+"DEFAULT_SPEED" = "ความเร็วเริ่มต้น";
+"DEFAULT_SPEED_DESC" = "ตั้งค่าความเร็วเริ่มต้นเมื่อเปิดวิดีโอใหม่";
+"SPEED_BUTTON" = "ปุ่มควบคุมความเร็ววิดีโอ";
+"SPEED_BUTTON_DESC" = "แสดงปุ่มควบคุมความเร็ววิดีโอบนโอเวอร์เลย์";
+"MUTE_BUTTON" = "ปุ่มปิดเสียง";
+"MUTE_BUTTON_DESC" = "แสดงปุ่มปิด/เปิดเสียงบนโอเวอร์เลย์";
+"SHARE_BUTTON" = "ปุ่มแชร์";
+"SHARE_BUTTON_DESC" = "แสดงปุ่มแชร์ลิงก์วิดีโอบนโอเวอร์เลย์";
+"LOOP_BUTTON" = "ปุ่มวนซ้ำวิดีโอ";
+"LOOP_BUTTON_DESC" = "แสดงปุ่มวนซ้ำวิดีโอบนโอเวอร์เลย์";
+"CAPTION_BUTTON" = "ปุ่มคำบรรยาย";
+"CAPTION_BUTTON_DESC" = "แสดงปุ่มคัดลอกคำบรรยายวิดีโอบนโอเวอร์เลย์";
+"HOLD_TO_SPEED" = "ความเร็วเมื่อกดค้าง";
+"HOLD_TO_SPEED_DESC" = "ใช้ความเร็วในการเล่นวิดีโอแบบกำหนดเองเมื่อกดค้างที่หน้าจอ";
"HIDE_AUTOPLAY" = "ซ่อนสวิตซ์เล่นอัตโนมัติ";
"HIDE_AUTOPLAY_DESC" = "ซ่อนสวิตซ์เล่นอัตโนมัติในโอเวอร์เลย์วิดีโอ";
"HIDE_CAPTIONS_BUTTON" = "ซ่อนปุ่มคำบรรยาย";
"HIDE_CAPTIONS_BUTTON_DESC" = "ซ่อนปุ่มคำบรรยายในโอเวอร์เลย์วิดีโอ";
"HIDE_CAST_BUTTON_PLAYER" = "ซ่อนปุ่มแคสต์";
"HIDE_CAST_BUTTON_PLAYER_DESC" = "ซ่อนปุ่มแคสต์ในโอเวอร์เลย์วิดีโอ";
-"HIDE_PREV_BUTTON" = "ซ่อนปุ่มก่อนหน้า";
-"HIDE_PREV_BUTTON_DESC" = "ซ่อนปุ่มก่อนหน้าในโอเวอร์เลย์วิดีโอ";
-"HIDE_NEXT_BUTTON" = "ซ่อนปุ่มถัดไป";
-"HIDE_NEXT_BUTTON_DESC" = "ซ่อนปุ่มถัดไปในโอเวอร์เลย์วิดีโอ";
+"HIDE_NEXT_AND_PREV_BUTTON" = "ซ่อนปุ่มก่อนหน้าและปุ่มถัดไป";
+"HIDE_NEXT_AND_PREV_BUTTON_DESC" = "ซ่อนปุ่มก่อนหน้าและปุ่มถัดไปในโอเวอร์เลย์วิดีโอ";
"REPLACE_PREVNEXT_BUTTONS" = "เปลี่ยนปุ่มก่อนหน้า/ถัดไป";
"REPLACE_PREVNEXT_BUTTONS_DESC" = "เปลี่ยนปุ่มเล่นวิดีโอก่อนหน้าและถัดไปในเครื่องเล่น เป็นปุ่มกรอกลับและปุ่มข้ามไปข้างหน้าแทน";
"REMOVE_DARK_OVERLAY" = "ลบโอเวอร์เลย์มืด";
@@ -102,6 +222,12 @@
"HIDE_PAID_OVERLAY_DESC" = "ซ่อนกล่องข้อความ 'มีโฆษณาจากสปอนเซอร์' ในโอเวอร์เลย์วิดีโอ";
"HIDE_WATERMARK" = "ซ่อนลายน้ำ";
"HIDE_WATERMARK_DESC" = "ซ่อนลายน้ำช่องในโอเวอร์เลย์วิดีโอ";
+"PAUSE_ON_OVERLAY" = "หยุดวิดีโอบนโอเวอร์เลย์";
+"PAUSE_ON_OVERLAY_DESC" = "หยุดวิดีโอเมื่อเห็นโอเวอร์เลย์";
+"DISABLES_SNAP_TO_CHAPTER" = "ปิดใช้งานการเลื่อนไปยังตอนถัดไป";
+"DISABLES_SNAP_TO_CHAPTER_DESC" = "ปิดใช้งานการแตะสองครั้งเพื่อข้ามไปยังตอนถัดไป";
+"DISABLES_ENGAGE_PANEL" = "ปิดใช้งานแผงควบคุมในโหมดเต็มจอ";
+"DISABLES_ENGAGE_PANEL_DESC" = "ป้องกันไม่ให้แผงควบคุมแสดงขึ้นมาในโหมดเต็มหน้าจอ";
"GESTURES" = "เปิดใช้งานการควบคุมด้วยท่าทาง";
"GESTURES_DESC" = "ปัดขึ้นหรือลงบริเวณขอบซ้ายหรือขวาของหน้าจอวิดีโอ เพื่อปรับความสว่าง ระดับเสียง หรือความเร็ววิดีโอ";
"GESTURE_AREA" = "พื้นที่เปิดใช้งานท่าทาง";
@@ -132,12 +258,12 @@
"DISABLES_LONG_HOLD_DESC" = "ปิดการเร่งความเร็วโดยการกดค้างในโอเวอร์เลย์วิดีโอ";
"AUTO_EXIT_FULLSCREEN" = "ออกจากเต็มจออัตโนมัติ";
"AUTO_EXIT_FULLSCREEN_DESC" = "ออกจากเต็มจออัตโนมัติเมื่อวิดีโอจบ";
-"AUTO_DISABLES_CAPTION" = "ปิดคำบรรยายอัตโนมัติ";
-"AUTO_DISABLES_CAPTION_DESC" = "ปิดคำบรรยาย (CC) โดยอัตโนมัติทุกครั้งเมื่อเริ่มเล่นวิดีโอใหม่";
"DISABLES_SHOW_REMAINING" = "ซ่อนเวลาที่เหลือ";
"DISABLES_SHOW_REMAINING_DESC" = "ซ่อนเวลาที่เหลือในโอเวอร์เลย์วิดีโอ";
"ALWAYS_SHOW_REMAINING" = "แสดงเวลาที่เหลือเสมอ";
"ALWAYS_SHOW_REMAINING_DESC" = "แสดงเวลาที่เหลือเสมอในโอเวอร์เลย์วิดีโอ";
+"COPY_TIMESTAMP_ON_PAUSE" = "คัดลอกลิงก์วิดีโอพร้อมเวลา";
+"COPY_TIMESTAMP_ON_PAUSE_DESC" = "อนุญาตให้คัดลอกลิงก์วิดีโอพร้อมเวลาปัจจุบันไปยังคลิปบอร์ดเมื่อกดปุ่มหยุดเล่น";
"SHOW_REMAINING_EXTRA" = "แสดงเวลาที่เหลือ";
"SHOW_REMAINING_EXTRA_DESC" = "เพิ่มเวลาที่จะเล่นจบจริงในเวลาของวิดีโอ";
"HIDE_FULLSCREEN_ACTIONS" = "ซ่อนปุ่มในโหมดเต็มหน้าจอ";
@@ -156,85 +282,98 @@
"OLD_QUALITY_PICKER_DESC" = "ใช้ตัวเลือกคุณภาพแบบเก่าเพื่อลิสต์คุณภาพวิดีโอได้เร็วขึ้น";
"EXTRA_SPEED" = "เพิ่มตัวเลือกความเร็ววิดีโอ";
"EXTRA_SPEED_DESC" = "เพิ่มตัวเลือกความเร็ววิดีโอถึง 10x";
-"DISABLE_HINTS" = "ปิดคำแนะนำ";
-"DISABLE_HINTS_DESC" = "ปิดคำแนะนำการใช้งานต่างๆ";
-"FORCE_MINIPLAYER" = "บังคับใช้ตัวเล่นย่อส่วน";
-"FORCE_MINIPLAYER_DESC" = "อนุญาตให้เล่นวิดีโอทุกประเภทในมินิเพลเยอร์ รวมถึงวิดีโอสำหรับเด็ก";
+"FORCE_MINIPLAYER" = "บังคับใช้เครื่องเล่นขนาดเล็ก";
+"FORCE_MINIPLAYER_DESC" = "อนุญาตให้เล่นวิดีโอทุกประเภทในเครื่องเล่นขนาดเล็ก รวมถึงวิดีโอสำหรับเด็ก";
"FORCE_SEEKBAR" = "แสดงแถบสถานะการเล่นตลอดเวลา";
"FORCE_SEEKBAR_DESC" = "ทำให้แถบความคืบหน้าแสดงขึ้นตลอดเวลาในขณะที่เล่นวิดีโอแบบเต็มหน้าจอ";
-"HIDE_LIKE_BUTTON" = "ซ่อนปุ่มถูกใจ";
-"HIDE_LIKE_BUTTON_DESC" = "ซ่อนปุ่มถูกใจใต้โอเวอร์เลย์วิดีโอ";
-"HIDE_DISLIKE_BUTTON" = "ซ่อนปุ่มไม่ถูกใจ";
-"HIDE_DISLIKE_BUTTON_DESC" = "ซ่อนปุ่มไม่ถูกใจใต้โอเวอร์เลย์วิดีโอ";
-"HIDE_SHARE_BUTTON" = "ซ่อนปุ่มแชร์";
-"HIDE_SHARE_BUTTON_DESC" = "ซ่อนปุ่มแชร์ใต้โอเวอร์เลย์วิดีโอ";
-"HIDE_DOWNLOAD_BUTTON" = "ซ่อนปุ่มดาวน์โหลด";
-"HIDE_DOWNLOAD_BUTTON_DESC" = "ซ่อนปุ่มดาวน์โหลดใต้โอเวอร์เลย์วิดีโอ";
-"HIDE_CLIP_BUTTON" = "ซ่อนปุ่มคลิป";
-"HIDE_CLIP_BUTTON_DESC" = "ซ่อนปุ่มคลิปใต้โอเวอร์เลย์วิดีโอ";
-"HIDE_REMIX_BUTTON" = "ซ่อนปุ่มรีมิกซ์";
-"HIDE_REMIX_BUTTON_DESC" = "ซ่อนปุ่มรีมิกซ์ใต้โอเวอร์เลย์วิดีโอ";
-"HIDE_SAVE_BUTTON" = "ซ่อนปุ่มบันทึก";
-"HIDE_SAVE_BUTTON_DESC" = "ซ่อนปุ่มบันทึกใต้โอเวอร์เลย์วิดีโอ";
+"TAP_TO_SEEK" = "แตะเพื่อข้าม";
+"TAP_TO_SEEK_DESC" = "ข้ามไปยังส่วนต่างๆของวิดีโออย่างรวดเร็วโดยแตะที่แถบคืบหน้า";
+"DISABLES_ZOOM" = "ปิดใช้งานการซูมวิดีโอ";
+"DISABLES_ZOOM_DESC" = "ปิดใช้งานการซูมวิดีโอแบบอิสระ";
+"USES_24_HOURS_TIME" = "รูปแบบเวลา 24 ชั่วโมง";
+"USES_24_HOURS_TIME_DESC" = "แสดงเวลาจบวิดีโอแบบ 24 ชั่วโมง";
+"PAUSE_TWO_FINGERS" = "หยุดวิดีโอโดยใช้ 2 นิ้ว";
+"PAUSE_TWO_FINGERS_DESC" = "หยุด/เล่น วิดีโอโดยใช้ 2 นิ้วแตะหน้าจอ";
+"SKIP_BACKWARD" = "ย้อนกลับ";
+"SKIP_BACKWARD_DESC" = "เปลี่ยนปุ่มเล่นเพลงก่อนหน้าในศูนย์ควบคุมให้เป็นปุ่มข้ามย้อนกลับตามจำนวนวินาทีที่กำหนด";
+"SKIP_FORWARD" = "ไปข้างหน้า";
+"SKIP_FORWARD_DESC" = "เปลี่ยนปุ่มเล่นเพลงถัดไปในศูนย์ควบคุมให้เป็นปุ่มข้ามไปข้างหน้าตามจำนวนวินาทีที่กำหนด";
+"REWIND_SECONDS" = "จำนวนวินาทีที่ย้อนกลับ";
+"FORWARD_SECONDS" = "จำนวนวินาทีที่ไปข้างหน้า";
+"HIDE_ACTION_BAR" = "ซ่อนแถบปุ่ม";
+"HIDE_ACTION_BAR_DESC" = "ซ่อนแถบที่มีปุ่มไลก์, ไม่ชอบ, แชร์, และอื่นๆ ที่อยู่ใต้เครื่องเล่นวิดีโอ";
+"HIDE_COMMENTS_SECTION" = "ซ่อนกล่องความคิดเห็น";
+"HIDE_COMMENTS_SECTION_DESC" = "ซ่อนกล่องความคิดเห็นทั้งหมด";
+"HIDE_COMMENTS_PREVIEW" = "ซ่อนตัวอย่างความคิดเห็น";
+"HIDE_COMMENTS_PREVIEW_DESC" = "ซ่อนตัวอย่างความคิดเห็นยอดนิยม แต่ยังคงสามารถกดเปิดดูความคิดเห็นทั้งหมดได้";
+"USE_ANOTHER_MINIPLAYER" = "ใช้เครื่องเล่นขนาดเล็กอีกรูปแบบหนึ่ง";
+"USE_ANOTHER_MINIPLAYER_DESC" = "ใช้เครื่องเล่นขนาดเล็กรูปแบบที่ได้รับการออกแบบใหม่จาก YouTube เวอร์ชันเก่า";
"SHORTS" = "Shorts";
-"HIDE_SHORTS_LIKE_BUTTON" = "ซ่อนปุ่มถูกใจ";
-"HIDE_SHORTS_LIKE_BUTTON_DESC" = "ซ่อนปุ่มถูกใจใน Shorts";
-"HIDE_SHORTS_DISLIKE_BUTTON" = "ซ่อนปุ่มไม่ถูกใจ";
-"HIDE_SHORTS_DISLIKE_BUTTON_DESC" = "ซ่อนปุ่มไม่ถูกใจใน Shorts";
-"HIDE_SHORTS_COMMENT_BUTTON" = "ซ่อนปุ่มคอมเมนต์";
-"HIDE_SHORTS_COMMENT_BUTTON_DESC" = "ซ่อนปุ่มคอมเมนต์ใน Shorts";
-"HIDE_SHORTS_SHARE_BUTTON" = "ซ่อนปุ่มแชร์";
-"HIDE_SHORTS_SHARE_BUTTON_DESC" = "ซ่อนปุ่มแชร์ใน Shorts";
-"HIDE_SHORTS_REMIX_BUTTON" = "ซ่อนปุ่มรีมิกซ์";
-"HIDE_SHORTS_REMIX_BUTTON_DESC" = "ซ่อนปุ่มรีมิกซ์ใน Shorts";
-"HIDE_METADATA_BUTTON" = "ซ่อนปุ่มข้อมูลเพลง";
-"HIDE_METADATA_BUTTON_DESC" = "ซ่อนปุ่มข้อมูลเพลงใน Shorts";
+"SHORTS_ONLY" = "โหมด Shorts เท่านั้น";
+"SHORTS_ONLY_DESC" = "จำกัดหน้าฟีดให้แสดงเฉพาะ Shorts เท่านั้น";
+"SHORTS_ACTION" = "การกระทำของ Shorts เมื่อจบวิดีโอ";
+"LOOP" = "ลูปวิดีโอ";
+"SKIP_TO_NEXT_SHORTS" = "ข้ามไปยังวิดีโอถัดไป";
+"PAUSE_SHORTS" = "หยุดวิดีโอ";
+"REMOVE_LIVE_SHORTS" = "ลบวิดีโอสด";
+"REMOVE_LIVE_SHORTS_DESC" = "นำวิดีโอสดออกจากฟีด";
"HIDE_SHORTS_PRODUCT" = "ซ่อนสินค้า";
"HIDE_SHORTS_PRODUCT_DESC" = "ซ่อนสินค้าและลิงค์สินค้าในโอเวอร์เลย์ Shorts";
"HIDE_SHORTS_RECBAR" = "ซ่อนแถบแนะนำ";
"HIDE_SHORTS_RECBAR_DESC" = "ซ่อนแถบแนะนำในโอเวอร์เลย์ Shorts";
-"HIDE_SHORTS_COMMIT" = "ซ่อนแถบค่าคอมมิชชั่น";
-"HIDE_SHORTS_COMMIT_DESC" = "ซ่อนแถบค่าคอมมิชชั่นในบางวิดีโอ";
-"HIDE_SHORTS_SUBSCRIPT_BUTTON" = "ซ่อนปุ่มการติดตาม";
-"HIDE_SHORTS_SUBSCRIPT_BUTTON_DESC" = "ซ่อนปุ่มการติดตามด้านบนตัววิดีโอ";
-"HIDE_SHORTS_LIVE_BUTTON" = "ซ่อนปุ่มสด";
-"HIDE_SHORTS_LIVE_BUTTON_DESC" = "ซ่อนปุ่มสดด้านบนตัววิดีโอ";
-"HIDE_SHORTS_LENS_BUTTON" = "ซ่อนปุ่ม Lens";
-"HIDE_SHORTS_LENS_BUTTON_DESC" = "ซ่อนปุ่ม Lens ด้านบนตัววิดีโอ";
-"HIDE_SHORTS_TRENDS_BUTTON" = "ซ่อนปุ่มเทรนด์";
-"HIDE_SHORTS_TRENDS_BUTTON_DESC" = "ซ่อนปุ่มเทรนด์ด้านบนตัววิดีโอ";
-"HIDE_SHORTS_TO_VIDEO" = "ซ่อนลิงก์ไปวิดีโอเต็ม";
-"HIDE_SHORTS_TO_VIDEO_DESC" = "ซ่อนลิงก์ไปวิดีโอเต็มใน Shorts";
"ENABLES_SHORTS_QUALITY" = "เปิดใช้งานตัวเลือกคุณภาพ Shorts";
"ENABLES_SHORTS_QUALITY_DESC" = "สามารถเลือกคุณภาพ Shorts ได้";
"SHOW_SHORTS_SEEKBAR" = "แสดงแถบเลื่อนเสมอ";
"SHOW_SHORTS_SEEKBAR_DESC" = "แสดงแถบความคืบหน้าเสมอ";
+"REMOVE_POSTS_SHORTS" = "ลบโพสต์";
+"REMOVE_POSTS_SHORTS_DESC" = "นำโพสต์ออกจากฟีด";
+"SHORTS_FULLSCREEN" = "ซูมเพื่อดูแบบเต็มหน้าจอ";
+"SHORTS_FULLSCREEN_DESC" = "ซูมเข้า-ออกเพื่อรับชม Shorts แบบเต็มหน้าจอ";
"TABBAR" = "แท็บนำทาง";
"DEFAULT_TAB" = "แท็บเริ่มต้น";
-"DEFAULT_TAB_DESC" = "คุณสามารถเลือกแท็บที่จะเปิดเมื่อเริ่มแอปได้";
-"HOME_NAME" = "หน้าแรก";
-"SUBSCRIPT_NAME" = "การติดตาม";
-"LIB_NAME" = "คลัง (คุณ)";
+"FORSTED_TAB_BAR" = "แท็บโปร่งใส";
+"ENABLED" = "เปิดใช้งาน";
+"HOME_TAB" = "หน้าแรก";
+"SHORTS_TAB" = "Shorts";
+"CREATE_TAB" = "สร้าง";
+"SUBSCRIPTIONS_TAB" = "การติดตาม";
+"LIBRARY_TAB" = "คลัง (คุณ)";
"HIDE_TAB_INDI" = "ซ่อนตัวบอกสถานะ";
"HIDE_TAB_INDI_DESC" = "ซ่อนตัวบอกสถานะในแท็บต่างๆ";
"HIDE_TAB_LABELS" = "ซ่อนชื่อแท็บ";
"HIDE_TAB_LABELS_DESC" = "ซ่อนชื่อแท็บในแท็บต่างๆ";
-"HIDE_HOME_TAB" = "ซ่อนแท็บหน้าแรก";
-"HIDE_HOME_TAB_DESC" = "ซ่อนแท็บหน้าแรกในแท็บนำทาง";
-"HIDE_SHORTS_TAB" = "ซ่อนแท็บ Shorts";
-"HIDE_SHORTS_TAB_DESC" = "ซ่อนแท็บ Shorts ในแท็บนำทาง";
-"HIDE_CREATE_BUTTON" = "ซ่อนปุ่มสร้าง";
-"HIDE_CREATE_BUTTON_DESC" = "ซ่อนปุ่มสร้างในแท็บนำทาง";
-"HIDE_SUBSCRIPT_TAB" = "ซ่อนแท็บการติดตาม";
-"HIDE_SUBSCRIPT_TAB_DESC" = "ซ่อนแท็บการติดตามในแท็บนำทาง";
+"HISTORY_TAB" = "ประวัติการเข้าชม";
+"GAMING_TAB" = "เกม";
+"SPORTS_TAB" = "กีฬา";
+"NOTI_TAB" = "การแจ้งเตือน";
+"NEWS_TAB" = "ข่าวสาร";
+"MUSIC_TAB" = "เพลง";
+"WATCH_LATER_TAB" = "ดูภายหลัง";
+"PLAYLIST_TAB" = "เพลย์ลิสต์";
+"LIKE_TAB" = "วิดีโอที่ชอบ";
+"LIVE_TAB" = "สด";
+"POST_TAB" = "โพสต์";
+"VIDEO_TAB" = "วิดีโอของคุณ";
+"MOVIE_TAB" = "ภาพยนตร์";
+"COURSE_TAB" = "หลักสูตร";
+"MINIGAME_TAB" = "เล่นเกม";
+"FASHION_TAB" = "แฟชั่น";
+"MANAGE_TABS" = "จัดการแท็บ";
+"MANAGE_TABS_DESC" = "จัดเรียงและเปิด/ปิดใช้งานแท็บ";
+"TAB_REORDER_HINT" = "ลากเพื่อจัดเรียงใหม่ เปิด/ปิดเพื่อแสดง/ซ่อน\nนอกจากนี้ การตั้งค่าแท็บยังสามารถเข้าถึงได้โดยการแตะแท็บแรกค้างไว้";
+"TAB_LIMIT" = "จำกัดแท็บ";
+"TAB_LIMIT_DESC" = "สามารถมีแท็บได้สูงสุด 6 แท็บ";
+"ZERO_TAB_DESC" = "ต้องเปิดใช้งานอย่างน้อย 1 แท็บ";
"MISCELLANEOUS" = "อื่นๆ";
"BACKGROUND_PLAYBACK" = "เล่นภายในเบื้องหลัง";
"BACKGROUND_PLAYBACK_DESC" = "เปิดใช้งานการเล่นวิดีโอภายในเบื้องหลัง";
"DISABLES_SHORTS_PIP" = "ปิด PiP ของ Shorts";
"DISABLES_SHORTS_PIP_DESC" = "พยายามปิด PiP ของ Shorts อาจไม่สามารถปิดได้ 100%";
+"DISABLE_HINTS" = "ปิดคำแนะนำ";
+"DISABLE_HINTS_DESC" = "ปิดคำแนะนำการใช้งานต่างๆ";
"BLOCK_UPGRADE_DIALOGS" = "บล็อกกล่องแจ้งเตือนอัปเดต";
"BLOCK_UPGRADE_DIALOGS_DESC" = "บล็อกกล่องข้อความแจ้งเตือนให้อัปเดตแอป";
"ARE_YOU_THERE_DIALOG" = "ซ่อนกล่องแจ้งเตือน 'คุณยังอยู่ไหม'";
@@ -247,7 +386,109 @@
"DISABLES_SNACK_BAR_DESC" = "ปิดใข้งาน Snack bar เมื่อแตะปุ่มหรือทำอะไรในแอป";
"HIDE_STARTUP_ANIMATIONS" = "ซ่อนแอนิเมชัน";
"HIDE_STARTUP_ANIMATIONS_DESC" = "ซ่อนแอนิเมชันตอนเปิดแอป";
-"HIDE_PLAY_IN_NEXT_QUEUE" = "ซ่อน 'เล่นถัดไป'";
-"HIDE_PLAY_IN_NEXT_QUEUE_DESC" = "ซ่อน 'เล่นถัดไป' ในเมนู 3 จุด";
"HIDE_LIKE_DISLIKE_VOTES" = "ซ่อนโหวดชอบ/ไม่ชอบ";
-"HIDE_LIKE_DISLIKE_VOTES_DESC" = "ซ่อนกล่องโหวดชอบ/ไม่ชอบ";
\ No newline at end of file
+"HIDE_LIKE_DISLIKE_VOTES_DESC" = "ซ่อนกล่องโหวดชอบ/ไม่ชอบ";
+"FLYOUT_MENU" = "เมนูป๊อปอัป";
+"REMOVE_PLAY_IN_NEXT_QUEUE_OPTION" = "ลบตัวเลือก 'เล่นต่อจากวิดีโอนี้'";
+"REMOVE_PLAY_IN_NEXT_QUEUE_OPTION_DESC" = "ลบตัวเลือก 'เล่นต่อจากวิดีโอนี้' ออกจากเมนูป๊อปอัป";
+"REMOVE_DOWNLOAD_OPTION" = "ลบตัวเลือก 'ดาวน์โหลดวิดีโอ'";
+"REMOVE_DOWNLOAD_OPTION_DESC" = "ลบตัวเลือก 'ดาวน์โหลดวิดีโอ' ออกจากเมนูป๊อปอัป";
+"REMOVE_WATCH_LATER_OPTION" = "ลบตัวเลือก 'บันทึกไปยังดูภายหลัง'";
+"REMOVE_WATCH_LATER_OPTION_DESC" = "ลบตัวเลือก 'บันทึกไปยังดูภายหลัง' ออกจากเมนูป๊อปอัป";
+"REMOVE_SAVE_OPTION" = "ลบตัวเลือก 'บันทึกไปยังเพลย์ลิสต์'";
+"REMOVE_SAVE_OPTION_DESC" = "ลบตัวเลือก 'บันทึกไปยังเพลย์ลิสต์' ออกจากเมนูป๊อปอัป";
+"REMOVE_REMOVE_FROM_PLAYLIST_OPTION" = "ลบตัวเลือก 'ลบจากเพลย์ลิสต์'";
+"REMOVE_REMOVE_FROM_PLAYLIST_OPTION_DESC" = "ลบตัวเลือก 'ลบจากเพลย์ลิสต์' ออกจากเมนูป๊อปอัป";
+"REMOVE_SHARE_OPTION" = "ลบตัวเลือก 'แชร์'";
+"REMOVE_SHARE_OPTION_DESC" = "ลบตัวเลือก 'แชร์' ออกจากเมนูป๊อปอัป";
+"REMOVE_NOT_INTERESTED_OPTION" = "ลบตัวเลือก 'ไม่สนใจ'";
+"REMOVE_NOT_INTERESTED_OPTION_DESC" = "ลบตัวเลือก 'ไม่สนใจ' ออกจากเมนูป๊อปอัป";
+"REMOVE_DONT_RECOMMEND_OPTION" = "ลบตัวเลือก 'ไม่ต้องแนะนำช่อง'";
+"REMOVE_DONT_RECOMMEND_OPTION_DESC" = "ลบตัวเลือก 'ไม่ต้องแนะนำช่อง' ออกจากเมนูป๊อปอัป";
+"REMOVE_INFO_OPTION" = "ลบตัวเลือก 'เพราะเหตุใดฉันจึงเห็นฟีเจอร์นี้'";
+"REMOVE_INFO_OPTION_DESC" = "ลบตัวเลือก 'เพราะเหตุใดฉันจึงเห็นฟีเจอร์นี้' ออกจากเมนูป๊อปอัป";
+"REMOVE_FILTER_OPTION" = "ลบตัวเลือก 'ตัวกรองการค้นหา'";
+"REMOVE_FILTER_OPTION_DESC" = "ลบตัวเลือก 'ตัวกรองการค้นหา' ออกจากเมนูป๊อปอัป";
+"REMOVE_REPORT_OPTION" = "ลบตัวเลือก 'รายงาน'";
+"REMOVE_REPORT_OPTION_DESC" = "ลบตัวเลือก 'รายงาน' ออกจากเมนูป๊อปอัป";
+"REMOVE_YOUTUBE_MUSIC_OPTION" = "ลบตัวเลือก 'ฟังเพลงด้วย YouTube Music'";
+"REMOVE_YOUTUBE_MUSIC_OPTION_DESC" = "ลบตัวเลือก 'ฟังเพลงด้วย YouTube Music' ออกจากเมนูป๊อปอัป";
+"REMOVE_FEED_BACK_OPTION" = "ลบตัวเลือก 'ส่งความคิดเห็น'";
+"REMOVE_FEED_BACK_OPTION_DESC" = "ลบตัวเลือก 'ส่งความคิดเห็น' ออกจากเมนูป๊อปอัป";
+"REMOVE_CAST_OPTION" = "ลบตัวเลือก 'รับชมบนทีวี'";
+"REMOVE_CAST_OPTION_DESC" = "ลบตัวเลือก 'แคสต์' ออกจากเมนูป๊อปอัป";
+"REMOVE_SHUFFLE_OPTION" = "ลบตัวเลือก 'สุ่ม'";
+"REMOVE_SHUFFLE_OPTION_DESC" = "ลบตัวเลือก 'สุ่ม' ออกจากเมนูป๊อปอัป";
+"REMOVE_UN_SUB_OPTION" = "ลบตัวเลือก 'ยกเลิกการสมัคร'";
+"REMOVE_UN_SUB_OPTION_DESC" = "ลบตัวเลือก 'ยกเลิกการสมัคร' ออกจากเมนูป๊อปอัป";
+"REMOVE_HIDE_FROM_PLAYLIST_OPTION" = "ลบตัวเลือก 'ซ่อนวิดีโอจากเพลย์ลิสต์'";
+"REMOVE_HIDE_FROM_PLAYLIST_OPTION_DESC" = "ลบตัวเลือก 'ซ่อนวิดีโอจากเพลย์ลิสต์' ออกจากเมนูป๊อปอัป";
+"REMOVE_HELP_OPTION" = "ลบตัวเลือก 'ความช่วยเหลือและความคิดเห็น'";
+"REMOVE_HELP_OPTION_DESC" = "ลบตัวเลือก 'ความช่วยเหลือและความคิดเห็น' ออกจากเมนูป๊อปอัป";
+"REMOVE_NOTIFY_OPTION" = "ลบตัวเลือก 'แจ้งเตือนให้ฉันทราบ'";
+"REMOVE_NOTIFY_OPTION_DESC" = "ลบตัวเลือก 'แจ้งเตือนให้ฉันทราบ' ออกจากเมนูป๊อปอัป";
+"REMOVE_CLEARSCREEN_OPTION" = "ลบตัวเลือก 'ล้างหน้าจอ'";
+"REMOVE_CLEARSCREEN_OPTION_DESC" = "ลบตัวเลือก 'ล้างหน้าจอ' ออกจากเมนูป๊อปอัป";
+"HIDE_COMMU_GUIDE" = "นำหลักเกณฑ์ชุมชนออก";
+"HIDE_COMMU_GUIDE_DESC" = "นำหลักเกณฑ์ชุมชนที่ปักหมุดไว้ออกจากคอมเมนต์";
+"DISABLES_RTL" = "ปิดใช้งานเลย์เอาต์ RTL";
+"DISABLES_RTL_DESC" = "บังคับให้แสดงผลจากซ้ายไปขวา (LTR) และปิดการจัดหน้าแบบขวาไปซ้าย (RTL)";
+"DEVICE_UI" = "รูปแบบอุปกรณ์";
+
+"SB_DESCRIPTION" = "ข้ามช่วงสปอนเซอร์โดยอัตโนมัติด้วยข้อมูลจากชุมชน";
+"SB_ENABLE" = "เปิดใช้งาน SponsorBlock";
+"SB_ENABLE_DESC" = "เปิดส่วนขยาย SponsorBlock เพื่อข้ามส่วนที่เป็นโฆษณาหรือสปอนเซอร์";
+"SB_SHOW_BUTTON" = "แสดงปุ่มบนหน้าจอ";
+"SB_SHOW_BUTTON_DESC" = "แสดงปุ่มเปิด-ปิด SponsorBlock บนหน้าเครื่องเล่นวิดีโอ";
+"SB_SHOW_NOTIFICATIONS" = "แสดงการแจ้งเตือน";
+"SB_SHOW_NOTIFICATIONS_DESC" = "แสดงการแจ้งเตือนเมื่อมีการข้ามช่วงวิดีโอ (การแจ้งเตือนสำหรับหมวดหมู่ที่ตั้งค่าแบบกดข้ามเองจะแสดงเสมอ)";
+"SB_HAPTIC_FEEDBACK" = "ตอบสนองด้วยการสั่น";
+"SB_HAPTIC_FEEDBACK_DESC" = "สั่นเมื่อมีการข้ามช่วงวิดีโอ";
+"SB_SEGMENTS_IN_PLAYER" = "แสดงช่วงเวลาบนเครื่องเล่นวิดีโอ";
+"SB_SEGMENTS_IN_PLAYER_DESC" = "แสดงช่วงเวลาเป็นแถบสีต่างๆ บนแถบความคืบหน้าของเครื่องเล่นวิดีโอหลัก";
+"SB_SEGMENTS_IN_FEED" = "แสดงช่วงวิดีโอในฟีด";
+"SB_SEGMENTS_IN_FEED_DESC" = "แสดงแถบสีระบุช่วงต่างๆ บนแถบความคืบหน้าของวิดีโอในหน้าฟีด";
+"SB_SEGMENTS_IN_MINIPLAYER" = "แสดงช่วงวิดีโอในเครื่องเล่นขนาดเล็ก";
+"SB_SEGMENTS_IN_MINIPLAYER_DESC" = "แสดงแถบสีระบุช่วงต่างๆ บนแถบความคืบหน้าของเครื่องเล่นขนาดเล็ก";
+"SB_SHOW_DURATION" = "แสดงเวลาวิดีโอแบบไม่รวมช่วงสปอนเซอร์";
+"SB_SHOW_DURATION_DESC" = "แสดงเวลาความยาวทั้งหมดของวิดีโอโดยหักช่วงที่ข้ามได้ออก";
+"SB_CATEGORIES_HEADER" = "หมวดหมู่ช่วงวิดีโอ";
+"SB_SEGMENT_COLOR" = "สีประจำช่วง";
+
+"SB_ACTION_DISABLE" = "ปิดการใช้งาน";
+"SB_ACTION_AUTO_SKIP" = "ข้ามอัตโนมัติ";
+"SB_ACTION_ASK" = "ถามก่อนข้าม";
+"SB_ACTION_DISPLAY" = "แสดงบนแถบเวลาเท่านั้น";
+"SB_ACTION_SKIP_TO" = "ข้ามไปยังช่วงนี้";
+
+"SB_CAT_sponsor" = "สปอนเซอร์";
+"SB_CAT_sponsor_DESC" = "การโฆษณาสินค้าหรือบริการที่มีค่าตอบแทน ซึ่งไม่เกี่ยวข้องกับตัวผู้สร้างโดยตรง";
+"SB_CAT_intro" = "อินโทร/บทนำ";
+"SB_CAT_intro_DESC" = "ช่วงพักหรือแอนิเมชันเปิดตัวที่มักพบบ่อยในช่วงต้นวิดีโอ";
+"SB_CAT_outro" = "ท้ายคลิป/เครดิต";
+"SB_CAT_outro_DESC" = "เครดิต หรือส่วนปิดท้ายที่อยู่ช่วงท้ายของวิดีโอ";
+"SB_CAT_interaction" = "การปฏิสัมพันธ์";
+"SB_CAT_interaction_DESC" = "การเตือนให้กดไลก์ กดติดตาม หรือติดตามบนโซเชียลมีเดียต่างๆ";
+"SB_CAT_selfpromo" = "โปรโมทตัวเอง";
+"SB_CAT_selfpromo_DESC" = "การโปรโมทสินค้า สินค้าที่ระลึก หรือเนื้อหาอื่นๆ ของผู้สร้างเองโดยไม่มีค่าจ้าง";
+"SB_CAT_music_offtopic" = "ส่วนที่ไม่ใช่เพลง";
+"SB_CAT_music_offtopic_DESC" = "ส่วนที่ไม่มีเพลงในมิวสิกวิดีโอ (เช่น การพูดคุย หรือบทสนทนาเปิดเรื่อง)";
+"SB_CAT_preview" = "ตัวอย่างตอนต่อไป/สรุป";
+"SB_CAT_preview_DESC" = "การสรุปเนื้อหาเดิมหรือตัวอย่างจากวิดีโออื่นหรือตอนก่อนหน้า";
+"SB_CAT_hook" = "ฮุก/คำทักทาย";
+"SB_CAT_hook_DESC" = "คลิปเกริ่นนำนำร่องสำหรับวิดีโอถัดไป คำทักทาย และคำกล่าวลา";
+"SB_CAT_poi_highlight" = "ไฮไลท์";
+"SB_CAT_poi_highlight_DESC" = "ช่วงสำคัญหรือจุดที่เป็นไฮไลท์ของวิดีโอ ใช้คำสั่ง 'ข้ามไปยัง' เพื่อดูส่วนนี้ทันที";
+"SB_CAT_filler" = "เนื้อหาน้ำ/ส่วนเติม";
+"SB_CAT_filler_DESC" = "เนื้อหาที่ออกนอกเรื่อง มุกตลก หรือส่วนเติมที่ไม่ได้เกี่ยวข้องกับหัวข้อหลัก";
+
+"SB_SKIPPED" = "ข้ามช่วง %@ แล้ว";
+"SB_DETECTED" = "ตรวจพบช่วง %@\nคุณต้องการข้ามช่วงนี้หรือไม่?";
+"SB_SKIP_NOW" = "ข้าม";
+"SB_UNSKIP" = "ยกเลิกการข้าม";
+"SB_JUMPED_TO_HIGHLIGHT" = "ข้ามไปยังไฮไลท์แล้ว";
+"SB_JUMP_TO_HIGHLIGHT" = "พบช่วงไฮไลท์ ต้องการข้ามไปดูเลยหรือไม่?";
+"SB_SEGMENT_COLOR_SUFFIX" = "สีประจำช่วง";
+"SB_SECTION_MAIN" = "หลัก";
+"SB_SKIP_ALERT_DURATION" = "ระยะเวลาแสดงการแจ้งเตือนเมื่อข้าม";
+"SB_UNSKIP_ALERT_DURATION" = "ระยะเวลาแสดงการแจ้งเตือนเมื่อยกเลิกการข้าม";
\ No newline at end of file
diff --git a/layout/Library/Application Support/YouMod.bundle/tr.lproj/Localizable.strings b/layout/Library/Application Support/YouMod.bundle/tr.lproj/Localizable.strings
index bccaf550..2c1a8a0f 100644
--- a/layout/Library/Application Support/YouMod.bundle/tr.lproj/Localizable.strings
+++ b/layout/Library/Application Support/YouMod.bundle/tr.lproj/Localizable.strings
@@ -1,11 +1,18 @@
-"NOTE" = "NOTE: Some hiding features may leave a blank box.";
"SETTINGS" = "Settings";
+"INTERFACE" = "Interface";
+
+"LOOP_DISABLED" = "Loop disabled";
+"LOOP_ENABLED" = "Loop enabled";
"NEW_ISSUES" = "Report issues or request new features";
"NEW_ISSUES_DESC" = "Found bugs or want to request new features? Tap here.";
"SOURCE_CODES" = "Source codes";
"SOURCE_CODES_DESC" = "Take a look at the codes!";
+"EXTRA" = "Extra";
+"FIX_PLAYBACK_ISSUES" = "Fix playback issues (Temporary)";
+"FIX_PLAYBACK_ISSUES_DESC" = "Try to fix video playback issues for some users. Turning this on will make the player automatically reloads itself everytime it errors or fails to play the video.";
+
"PERFER_HEADER" = "YouMod perferences";
"PERFER" = "Perferences";
"IMPORT" = "Import settings";
@@ -24,6 +31,7 @@
"ERROR_NO_KEYS_IMPORT" = "No available keys to import";
"ERROR_NO_KEYS_EXPORT" = "No available keys to export";
"OVERRIDE" = "This will override the current settings. Do you want to continue?";
+"DISABLED" = "Disabled";
"CACHE" = "Cache management";
"CLEARCACHE" = "Clear cache";
@@ -32,12 +40,80 @@
"AUTO_CLEARCACHE_DESC" = "Automatically clears the app cache on startup.";
"DOWNLOADING" = "Downloading";
-"DOWNLOAD_MANAGER" = "Download Manager";
-"DOWNLOAD_MANAGER_DESC" = "Replaces YouTube's video downloader with YouMod one. With ablity to downloading videos, audios, captions, and thumbnails";
+"DOWNLOAD_MANAGER" = "Download manager";
+"DOWNLOAD_MANAGER_DESC" = "Replaces YouTube's video downloader with YouMod one. With ablity to downloading videos, audios, captions, and thumbnails.";
"DOWNLOAD_SAVE_PHOTOS" = "Automatically save to Photos";
"DOWNLOAD_SAVE_PHOTOS_DESC" = "Automatically saves downloaded videos and images directly to your Photos app.";
-"DOWNLOAD_DRC_AUDIO" = "Prefer DRC Audio";
-"DOWNLOAD_DRC_AUDIO_DESC" = "Prioritizes downloading audio tracks with Dynamic Range Compression for more consistent volume levels.";
+"ADD_SHORTS_DOWNLOAD" = "Adds a download button to Shorts";
+"ADD_SHORTS_DOWNLOAD_DESC" = "Adds a download button to Shorts video overlay.";
+"USE_ORIG_AUDIO" = "Automatically uses original audio track";
+"USE_ORIG_AUDIO_DESC" = "Automatically uses original audio track instead of showing a dialog.";
+"DOWNLOAD_SHORTS" = "Download Shorts";
+"DOWNLOAD_SHORTS_DESC" = "Choose Shorts quality";
+"COPY_VID_INFO" = "Copy video informations";
+"COPY_VID_INFO_DESC" = "Choose what to copy";
+"COPY_ALL_VID_INFO" = "Copy all";
+"COPY_ALL_VID_INFO_DESC" = "Copy all video informations";
+"COPY_AUTHOR" = "Copy author";
+"COPY_AUTHOR_DESC" = "Copy the video author";
+"COPY_TITLE" = "Copy title";
+"COPY_TITLE_DESC" = "Copy the video title";
+"COPY_DESCRIPTION" = "Copy description";
+"COPY_DESCRIPTION_DESC" = "Copy the video description";
+"COPY_URL" = "Copy video link";
+"COPY_URL_TIMESTAMP" = "Copy video link with timestamp";
+"URL_COPIED" = "Copied video link";
+"URL_TIMESTAMP_COPIED" = "Copied video link with timestamp";
+"ERROR_VIDEOID" = "Cannot get the video ID";
+"ERROR_ADS" = "Cannot copy video link while playing ads";
+"COPIED_AUTHOR" = "Copied video author";
+"COPIED_TITLE" = "Copied video title";
+"COPIED_DESCRIPTION" = "Copied video description";
+"SAVE_THUMBNAIL" = "Save thumbnail";
+"SAVE_THUMBNAIL_DESC" = "Save to Photos";
+"DOWNLOAD_CAPTIONS" = "Download captions";
+"DOWNLOAD_CAPTIONS_DESC" = "Save subtitles as .VTT file";
+"DOWNLOAD_AUDIO" = "Download audio";
+"DOWNLOAD_AUDIO_DESC" = "Download audio as .m4a file";
+"DOWNLOAD_VIDEO" = "Download video";
+"DOWNLOAD_VIDEO_DESC" = "Choose video quality";
+"OPEN_VID_BEFORE" = "Open a video before using the download manager";
+"NO_CAPTIONS_URL" = "No valid caption URLs found";
+"NO_AUDIO_URL" = "No audio URL found";
+"NO_STREAM_URL" = "No stream URL found";
+"CAPTIONS_FAILED" = "Failed to download captions";
+"NO_CAPTIONS" = "No captions available for this video";
+"NO_AUDIO_STREAM_FOUND" = "No audio streams found";
+"NO_VID_AUDIO_STREAM_FOUND" = "No video/audio streams found";
+"COPIED_VID_INFO" = "Copied all video informations";
+"SAVED_TO_PHOTOS" = "Saved to Photos";
+"SAVE_FAILED" = "Save failed";
+"PHOTO_ACCESS_DENINED" = "Photos access denied";
+"THUMBNAIL_FAILED" = "Thumbnail failed";
+"DOWNLOADING_THUMBNAIL" = "Downloading thumbnail...";
+"DOWNLOADING_AUDIO" = "Downloading audio...";
+"DOWNLOADING_VIDEO" = "Downloading video...";
+"DOWNLOADING_CAPTIONS" = "Downloading captions...";
+"NO_THUMBNAIL_FOUND" = "No thumbnail found";
+"DOWNLOAD_FAILED" = "Download failed";
+"COPY_THUMBNAIL" = "Copy thumbnail";
+"COPY_THUMBNAIL_DESC" = "Copy to clipboard";
+"SHOW_THUMBNAIL" = "Show thumbnail";
+"SHOW_THUMBNAIL_DESC" = "Show in a dialog";
+"THUMBNAIL_OPTIONS" = "Download thumbnail";
+"THUMBNAIL_OPTIONS_DESC" = "Show more options";
+"COPIED_TO_CLIPBOARD" = "Copied to clipboard";
+"DOWNLOAD_COMPLETED" = "Download completed";
+"CANNOT_SAVE_TO_PHOTOS" = "Cannot save to Photos";
+"FINA_VIDEO" = "Finalizing video...";
+"MERGING_VID_FALLBACK" = "Merging video with fallback...";
+"MERGING_VID" = "Merging video...";
+"CONVERTING" = "Converting to";
+"ALREADY_DOWNLOADING" = "Already downloading";
+"DOWNLOAD_CANCELLED" = "Download cancelled";
+"CANCEL" = "Cancel";
+"TRIMMING_AUDIO" = "Trimming audio...";
+"COPY" = "Copy";
"APPEARANCE" = "Appearance";
"OLED_THEME" = "OLED dark mode";
@@ -46,6 +122,8 @@
"OLED_KEYBOARD_DESC" = "Make the keyboard uses a pure black (OLED) color for dark theme.";
"NAVBAR" = "Navigation bar";
+"STICKY_NAVBAR" = "Sticky Navigation bar";
+"STICKY_NAVBAR_DESC" = "Pins the Navigation bar so that it remains visible when scrolling down.";
"HIDE_YT_LOGO" = "Hide YouTube logo";
"HIDE_YT_LOGO_DESC" = "Hide YouTube logo in the navigation bar.";
"PREMIUM_LOGO" = "Use YouTube Premium logo";
@@ -62,34 +140,78 @@
"FEED" = "Feed";
"HIDE_SUBBAR" = "Hide subbar";
"HIDE_SUBBAR_DESC" = "Hide subbar at the top of the feed.";
+"HIDE_HORI_SHELF" = "Remove horizontal feeds";
+"HIDE_HORI_SHELF_DESC" = "Removes all horizontal feeds (News, Continue Watching, Posts collection, etc.) from the feed, except Playables.";
"HIDE_MUSIC_PLAYLISTS" = "Hide music playlist generator";
"HIDE_MUSIC_PLAYLISTS_DESC" = "Hide music playlist generator (Select music tunes in three) in the feed.";
"HIDE_FEED_POST" = "Hide posts";
"HIDE_FEED_POST_DESC" = "Hide posts in feed.";
+"HIDE_SURVEYS" = "Hide surveys";
+"HIDE_SURVEYS_DESC" = "Hide video surveys that YouTube shows sometimes.";
"HIDE_SHORTS_SHELF" = "Hide Shorts shelf";
"HIDE_SHORTS_SHELF_DESC" = "Hide Shorts shelf in the feed, including in the Subscriptions tab.";
+"KEEP_SHORTS_SUBSCRIPT" = "Keep Shorts in Subscriptions";
+"KEEP_SHORTS_SUBSCRIPT_DESC" = "Prevents removing Shorts from Subscriptions tab.";
"HIDE_SEARCH_HISTORY" = "Hide search history";
"HIDE_SEARCH_HISTORY_DESC" = "Hide previous search history and suggestions when using the search bar.\nNOTE: Your search history will still be accessible in your other YouTube clients.";
-"HIDE_SUB_BUTTON" = "Hide Subscribe button";
-"HIDE_SUB_BUTTON_DESC" = "Hides the subscribe button in channels and video descriptions.";
-"HIDE_SHOP_BUTTON" = "Hide Shopping button";
-"HIDE_SHOP_BUTTON_DESC" = "Hides the shopping/store button in channels descriptions.";
-"HIDE_MEMBER_BUTTON" = "Hide Membership button";
-"HIDE_MEMBER_BUTTON_DESC" = "Hides the 'Join' button in channels and video descriptions.";
+"HIDE_PLAYABLES" = "Remove playables";
+"HIDE_PLAYABLES_DESC" = "Removes playables (mini-games) from the feed.";
"PLAYER" = "Player";
+"PLAYBACK_SPEED" = "Playback speed";
+"CONTROL_CENTER" = "Control Center";
+"PLAYER_ACTIONS" = "Player actions";
+"GESTURE_HEADER" = "Gestures";
+"QUALITY_WIFI" = "Playback quality on WiFi";
+"QUALITY_WIFI_DESC" = "Automatically applies the selected quality when streaming over Wi-Fi.";
+"QUALITY_CELLULAR" = "Playback quality on Cellular";
+"QUALITY_CELLULAR_DESC" = "Automatically applies the selected quality when streaming over mobile data.";
+"QUALITY_LOW_POWER" = "Playback quality on Power saver mode";
+"QUALITY_LOW_POWER_DESC" = "Automatically applies the selected quality when when Low power mode is enabled on your device.";
+"DEFAULT" = "Default";
+"BEST" = "Best";
+"AUDIO_TRACK" = "Preferred audio track";
+"ORIGINAL" = "Original";
+"SELECT_MANUALLY" = "Select manually";
+"AUDIO_TRACK_SELECT" = "Selected audio track";
+"AUDIO_TRACK_SELECT_DESC" = "Specifies the preferred audio track language to use for playback, if available.";
+"NO_AUTO_DUBBED" = "Uses original audio track";
+"NO_AUTO_DUBBED_DESC" = "Uses original audio track if the preferred language track is auto-dubbed or not found.";
+"CAPTION_TRACK" = "Preferred captions";
+"CAPTION_TRACK_SELECT" = "Selected captions";
+"CAPTION_TRACK_SELECT_DESC" = "Specifies the preferred captions language to display, if available.";
+"DISABLES_CAPTION_TRACK" = "Automatically disable captions";
+"DISABLES_CAPTION_TRACK_DESC" = "Automatically disable captions if the preferred language is auto-generated or not found.";
+"DEFAULT_SPEED" = "Default playback rate";
+"DEFAULT_SPEED_DESC" = "Overrides the default playback speed.";
+"SPEED_BUTTON" = "Speed controls button";
+"SPEED_BUTTON_DESC" = "Displays a speed controls button on overlay.";
+"MUTE_BUTTON" = "Mute button";
+"MUTE_BUTTON_DESC" = "Displays a button to mute or unmute the volume on the overlay.";
+"SHARE_BUTTON" = "Share button";
+"SHARE_BUTTON_DESC" = "Displays a button to share the video link on the overlay.";
+"LOOP_BUTTON" = "Loop button";
+"LOOP_BUTTON_DESC" = "Displays a button to loop the video on the overlay.";
+"CAPTION_BUTTON" = "Captions button";
+"CAPTION_BUTTON_DESC" = "Displays a button to copy the video captions on the overlay.";
+"HOLD_TO_SPEED" = "Hold to speed";
+"HOLD_TO_SPEED_DESC" = "Uses custom hold to speed playback rate.";
"HIDE_AUTOPLAY" = "Hide autoplay switch";
"HIDE_AUTOPLAY_DESC" = "Hide autoplay switch in the video overlay.";
"HIDE_CAPTIONS_BUTTON" = "Hide captions button";
"HIDE_CAPTIONS_BUTTON_DESC" = "Hide captions button in the video overlay.";
"HIDE_CAST_BUTTON_PLAYER" = "Hide cast button";
"HIDE_CAST_BUTTON_PLAYER_DESC" = "Hide cast button in the video overlay.";
-"HIDE_PREV_BUTTON" = "Hide previous/back button";
-"HIDE_PREV_BUTTON_DESC" = "Hide previous/back button in the video overlay.";
-"HIDE_NEXT_BUTTON" = "Hide next/skip button";
-"HIDE_NEXT_BUTTON_DESC" = "Hide next/skip button in the video overlay.";
+"HIDE_NEXT_AND_PREV_BUTTON" = "Hide previous and next buttons";
+"HIDE_NEXT_AND_PREV_BUTTON_DESC" = "Hide previous and next buttons in the video overlay.";
"REPLACE_PREVNEXT_BUTTONS" = "Replace Previous/Next buttons";
"REPLACE_PREVNEXT_BUTTONS_DESC" = "Replaces the Previous and Next video buttons in the player with Rewind and Fast Forward buttons.";
+"SKIP_BACKWARD" = "Skip backward";
+"SKIP_BACKWARD_DESC" = "Replaces the previous track button in Control Center with a skip backward button by a set number of seconds.";
+"SKIP_FORWARD" = "Skip forward";
+"SKIP_FORWARD_DESC" = "Replaces the next track button in Control Center with a skip forward button by a set number of seconds.";
+"REWIND_SECONDS" = "Rewind seconds";
+"FORWARD_SECONDS" = "Fast-forward seconds";
"REMOVE_DARK_OVERLAY" = "Remove dark overlay";
"REMOVE_DARK_OVERLAY_DESC" = "Remove dark overlay from the video overlay.";
"HIDE_END_SCREEN" = "Hide endscreen cards";
@@ -102,6 +224,12 @@
"HIDE_PAID_OVERLAY_DESC" = "Hide 'Includes paid promotion' in the video overlay.";
"HIDE_WATERMARK" = "Hide watermark";
"HIDE_WATERMARK_DESC" = "Hide channel watermark at the bottom of the video overlay.";
+"PAUSE_ON_OVERLAY" = "Pause on overlay";
+"PAUSE_ON_OVERLAY_DESC" = "Sets playback on pause if overlay appears.";
+"DISABLES_SNAP_TO_CHAPTER" = "Disable snap to chapters";
+"DISABLES_SNAP_TO_CHAPTER_DESC" = "Prevents the scrubbar stick to the chapters.";
+"DISABLES_ENGAGE_PANEL" = "Disables engagement panel in fullscreen";
+"DISABLES_ENGAGE_PANEL_DESC" = "Prevent engagement panel showing in fullscreen.";
"GESTURES" = "Enable gestures controls";
"GESTURES_DESC" = "Swipe up or down on the left/right edges of the video screen to adjust brightness, volume, or playback speed.";
"GESTURE_AREA" = "Gesture activation area";
@@ -136,6 +264,8 @@
"DISABLES_SHOW_REMAINING_DESC" = "Disable the feature that shows how much time is left in a video when tapping the timestamp.";
"ALWAYS_SHOW_REMAINING" = "Always show remaining time";
"ALWAYS_SHOW_REMAINING_DESC" = "Ensure the remaining time is always displayed by default in the player overlay.";
+"COPY_TIMESTAMP_ON_PAUSE" = "Copy timestamped links";
+"COPY_TIMESTAMP_ON_PAUSE_DESC" = "Allows to copy timestamped link to the clipboard by pressing pause button.";
"SHOW_REMAINING_EXTRA" = "Show extra remaining time";
"SHOW_REMAINING_EXTRA_DESC" = "Adds the estimated finish time to the label in the video overlay.";
"HIDE_FULLSCREEN_ACTIONS" = "Hide fullscreen actions";
@@ -148,93 +278,95 @@
"HIDE_CONTENT_WARNING_DESC" = "Automatically skip the sensitive content warning alert in some vidoes.";
"AUTO_FULLSCREEN" = "Auto fullscreen";
"AUTO_FULLSCREEN_DESC" = "Automatically enter fullscreen mode when starting video playback.";
-"AUTO_DISABLES_CAPTION" = "Auto disable captions";
-"AUTO_DISABLES_CAPTION_DESC" = "Automatically turns off closed captions (CC) whenever a new video starts playing.";
"PORTRAIT_FULLSCREEN" = "Portrait fullscren mode";
"PORTRAIT_FULLSCREEN_DESC" = "Enables portrait fullscreen mode support.";
"OLD_QUALITY_PICKER" = "Use old quality picker";
"OLD_QUALITY_PICKER_DESC" = "Reverts the video quality selection menu to the classic version (lists all resolutions immediately).";
"EXTRA_SPEED" = "Extra speed options";
"EXTRA_SPEED_DESC" = "Adds more video playback speed options up to 10x.";
-"DISABLE_HINTS" = "Disable hints";
-"DISABLE_HINTS_DESC" = "Disable the tutorial hints and interactive pop-ups.";
"FORCE_MINIPLAYER" = "Force miniplayer";
"FORCE_MINIPLAYER_DESC" = "Allows all videos, including those made for kids, to play in the miniplayer when dismissing the player.";
"FORCE_SEEKBAR" = "Always show progress bar";
"FORCE_SEEKBAR_DESC" = "Makes the progress bar (seekbar) remains visible at all times while in fullscreen mode.";
-"HIDE_LIKE_BUTTON" = "Hide Like button";
-"HIDE_LIKE_BUTTON_DESC" = "Hide Like button in the action bar under the video overlay.";
-"HIDE_DISLIKE_BUTTON" = "Hide Dislike button";
-"HIDE_DISLIKE_BUTTON_DESC" = "Hide Dislike button in the action bar under the video overlay.";
-"HIDE_SHARE_BUTTON" = "Hide Share button";
-"HIDE_SHARE_BUTTON_DESC" = "Hide Share button in the action bar under the video overlay.";
-"HIDE_DOWNLOAD_BUTTON" = "Hide Download button";
-"HIDE_DOWNLOAD_BUTTON_DESC" = "Hide Download button in the action bar under the video overlay.";
-"HIDE_CLIP_BUTTON" = "Hide Clip button";
-"HIDE_CLIP_BUTTON_DESC" = "Hide Clip button in the action bar under the video overlay.";
-"HIDE_REMIX_BUTTON" = "Hide Remix button";
-"HIDE_REMIX_BUTTON_DESC" = "Hide Remix button in the action bar under the video overlay.";
-"HIDE_SAVE_BUTTON" = "Hide Save button";
-"HIDE_SAVE_BUTTON_DESC" = "Hide Save button in the action bar under the video overlay.";
+"TAP_TO_SEEK" = "Tap to seek";
+"TAP_TO_SEEK_DESC" = "Quickly jump to any point by tapping the progress bar.";
+"DISABLES_ZOOM" = "Disable free zoom gesture";
+"DISABLES_ZOOM_DESC" = "Disables new free zoom gestures.";
+"USES_24_HOURS_TIME" = "24-hour format";
+"USES_24_HOURS_TIME_DESC" = "Shows end time in 24-hour format.";
+"PAUSE_TWO_FINGERS" = "Pause using two fingers";
+"PAUSE_TWO_FINGERS_DESC" = "Pause/Resume playback by tapping on the screen using two fingers.";
+"HIDE_COMMENTS_SECTION" = "Hide comments section";
+"HIDE_COMMENTS_SECTION_DESC" = "Completely removes the entire comments section.";
+"HIDE_COMMENTS_PREVIEW" = "Hide preview comments";
+"HIDE_COMMENTS_PREVIEW_DESC" = "Removes the preview box for the top comment while keeping the full section expandable.";
+"USE_ANOTHER_MINIPLAYER" = "Uses another miniplayer style";
+"USE_ANOTHER_MINIPLAYER_DESC" = "Uses another miniplayer style redesigned from older YT versions.";
"SHORTS" = "Shorts";
-"HIDE_SHORTS_LIKE_BUTTON" = "Hide Like button";
-"HIDE_SHORTS_LIKE_BUTTON_DESC" = "Hide the Like button in the Shorts player.";
-"HIDE_SHORTS_DISLIKE_BUTTON" = "Hide Dislike button";
-"HIDE_SHORTS_DISLIKE_BUTTON_DESC" = "Hide the Dislike button in the Shorts player.";
-"HIDE_SHORTS_COMMENT_BUTTON" = "Hide Comment button";
-"HIDE_SHORTS_COMMENT_BUTTON_DESC" = "Hide the Comment button in the Shorts player.";
-"HIDE_SHORTS_SHARE_BUTTON" = "Hide Share button";
-"HIDE_SHORTS_SHARE_BUTTON_DESC" = "Hide the Share button in the Shorts player.";
-"HIDE_SHORTS_REMIX_BUTTON" = "Hide Remix button";
-"HIDE_SHORTS_REMIX_BUTTON_DESC" = "Hide the Remix button in the Shorts player.";
-"HIDE_METADATA_BUTTON" = "Hide sound metadata button";
-"HIDE_METADATA_BUTTON_DESC" = "Hide the button that shows the sound source or music metadata in the Shorts player.";
+"SHORTS_ONLY" = "Shorts only mode";
+"SHORTS_ONLY_DESC" = "Limits YouTube functionality to viewing Shorts only.";
+"SHORTS_ACTION" = "Shorts action after the video ends";
+"LOOP" = "Loop video";
+"SKIP_TO_NEXT_SHORTS" = "Skip to next video";
+"PAUSE_SHORTS" = "Stop video";
+"REMOVE_LIVE_SHORTS" = "Remove live videos";
+"REMOVE_LIVE_SHORTS_DESC" = "Removes live videos from the feed.";
"HIDE_SHORTS_PRODUCT" = "Hide products";
"HIDE_SHORTS_PRODUCT_DESC" = "Hide tagged products and shopping links in the Shorts overlay.";
"HIDE_SHORTS_RECBAR" = "Hide recommendation bar";
"HIDE_SHORTS_RECBAR_DESC" = "Hide the suggested action bar in the Shorts player (e.g. Try this sound, Shopping, Preview comment).";
-"HIDE_SHORTS_COMMIT" = "Hide commissions bar";
-"HIDE_SHORTS_COMMIT_DESC" = "Hide the bar that shows the video got commissions in the Shorts player.";
-"HIDE_SHORTS_SUBSCRIPT_BUTTON" = "Hide subscriptions button";
-"HIDE_SHORTS_SUBSCRIPT_BUTTON_DESC" = "Hide the Subscriptions button at the top of the Shorts player.";
-"HIDE_SHORTS_LIVE_BUTTON" = "Hide live button";
-"HIDE_SHORTS_LIVE_BUTTON_DESC" = "Hide the Live button at the top of the Shorts player.";
-"HIDE_SHORTS_LENS_BUTTON" = "Hide lens button";
-"HIDE_SHORTS_LENS_BUTTON_DESC" = "Hide the Lens button at the top of the Shorts player.";
-"HIDE_SHORTS_TRENDS_BUTTON" = "Hide trends button";
-"HIDE_SHORTS_TRENDS_BUTTON_DESC" = "Hide the Trends button at the top of the Shorts player.";
-"HIDE_SHORTS_TO_VIDEO" = "Hide link to full video";
-"HIDE_SHORTS_TO_VIDEO_DESC" = "Hide the link that redirects in a Short to a full video in the Shorts player.";
"ENABLES_SHORTS_QUALITY" = "Enable Shorts quality picker";
"ENABLES_SHORTS_QUALITY_DESC" = "Force enable the video quality selection menu in Shorts.";
"SHOW_SHORTS_SEEKBAR" = "Always show seekbar";
"SHOW_SHORTS_SEEKBAR_DESC" = "Ensure the progress/seek bar is always visible at the bottom of the Shorts player.";
+"REMOVE_POSTS_SHORTS" = "Remove posts";
+"REMOVE_POSTS_SHORTS_DESC" = "Remove posts from the feed.";
+"SHORTS_FULLSCREEN" = "Pinch to Fullscreen";
+"SHORTS_FULLSCREEN_DESC" = "Manages visibility of the overlay with pinch in and pinch out gestures, displaying Shorts in fullscreen mode.";
"TABBAR" = "Tab bar";
"DEFAULT_TAB" = "Default startup tab";
-"DEFAULT_TAB_DESC" = "You can select the tab you want to open when opening the app.";
-"HOME_NAME" = "Home";
-"SUBSCRIPT_NAME" = "Subscriptions";
-"LIB_NAME" = "Library (You)";
+"FORSTED_TAB_BAR" = "Translucent tab bar";
+"ENABLED" = "Enabled";
"HIDE_TAB_INDI" = "Hide tab indicators";
"HIDE_TAB_INDI_DESC" = "Hide all tab indicators in the tab bar.";
"HIDE_TAB_LABELS" = "Hide tab labels";
"HIDE_TAB_LABELS_DESC" = "Hide all tab labels in the tab bar.";
-"HIDE_HOME_TAB" = "Hide Home tab";
-"HIDE_HOME_TAB_DESC" = "Hide Home tab in the tab bar.";
-"HIDE_SHORTS_TAB" = "Hide Shorts tab";
-"HIDE_SHORTS_TAB_DESC" = "Hide Shorts tab in the tab bar.";
-"HIDE_CREATE_BUTTON" = "Hide Create button";
-"HIDE_CREATE_BUTTON_DESC" = "Hide Create button in the tab bar.";
-"HIDE_SUBSCRIPT_TAB" = "Hide Subscriptions tab";
-"HIDE_SUBSCRIPT_TAB_DESC" = "Hide Subscriptions tab in the tab bar.";
+"HOME_TAB" = "Home";
+"SHORTS_TAB" = "Shorts";
+"CREATE_TAB" = "Create";
+"SUBSCRIPTIONS_TAB" = "Subscriptions";
+"LIBRARY_TAB" = "Library (You)";
+"HISTORY_TAB" = "History";
+"GAMING_TAB" = "Gaming";
+"SPORTS_TAB" = "Sports";
+"NOTI_TAB" = "Notifications";
+"NEWS_TAB" = "News";
+"MUSIC_TAB" = "Music";
+"WATCH_LATER_TAB" = "Watch later";
+"PLAYLIST_TAB" = "Playlists";
+"LIKE_TAB" = "Liked videos";
+"LIVE_TAB" = "Live";
+"POST_TAB" = "Posts";
+"VIDEO_TAB" = "Your videos";
+"MOVIE_TAB" = "Movies";
+"COURSE_TAB" = "Courses";
+"MINIGAME_TAB" = "Playables";
+"MANAGE_TABS" = "Manage tabs";
+"MANAGE_TABS_DESC" = "Reorder and toggle tabs";
+"TAB_REORDER_HINT" = "Drag to reorder. Toggle to show/hide.\nTab settings are also accessible by long-pressing the first tab in the tab bar.";
+"TAB_LIMIT" = "Tab limit";
+"TAB_LIMIT_DESC" = "Only 6 tabs are allowed.";
+"ZERO_TAB_DESC" = "At least 1 tab has to be enabled.";
"MISCELLANEOUS" = "Miscellaneous";
"BACKGROUND_PLAYBACK" = "Background playback";
"BACKGROUND_PLAYBACK_DESC" = "Enables background playback for videos.";
"DISABLES_SHORTS_PIP" = "Disables Shorts PiP";
"DISABLES_SHORTS_PIP_DESC" = "Try to disables Shorts PiP, this might not 100% disables it.";
+"DISABLE_HINTS" = "Disable hints";
+"DISABLE_HINTS_DESC" = "Disable the tutorial hints and interactive pop-ups.";
"BLOCK_UPGRADE_DIALOGS" = "Block upgrade dialogs";
"BLOCK_UPGRADE_DIALOGS_DESC" = "Block the YouTube dialogs that ask you to upgrade the app.";
"ARE_YOU_THERE_DIALOG" = "Hide 'Are you there?' dialog";
@@ -247,7 +379,109 @@
"DISABLES_SNACK_BAR_DESC" = "Disables snack bar when pressing some buttons or doing something in the app.";
"HIDE_STARTUP_ANIMATIONS" = "Hide YouTube's startup animations";
"HIDE_STARTUP_ANIMATIONS_DESC" = "Hide YouTube's startup animations when opening the app.";
-"HIDE_PLAY_IN_NEXT_QUEUE" = "Hide 'Play next in queue'";
-"HIDE_PLAY_IN_NEXT_QUEUE_DESC" = "Hide the 'Play next in queue' option in the flyout menu.";
"HIDE_LIKE_DISLIKE_VOTES" = "Hide like/dislike votes";
-"HIDE_LIKE_DISLIKE_VOTES_DESC" = "Hide messages that pop up when you like/dislike videos.";
\ No newline at end of file
+"HIDE_LIKE_DISLIKE_VOTES_DESC" = "Hide messages that pop up when you like/dislike videos.";
+"FLYOUT_MENU" = "Flyout menu";
+"REMOVE_PLAY_IN_NEXT_QUEUE_OPTION" = "Removes 'Play next in queue' option";
+"REMOVE_PLAY_IN_NEXT_QUEUE_OPTION_DESC" = "Removes 'Play next in queue' option from the flyout menu.";
+"REMOVE_DOWNLOAD_OPTION" = "Removes 'Download video' option";
+"REMOVE_DOWNLOAD_OPTION_DESC" = "Removes 'Download video' option from the flyout menu.";
+"REMOVE_WATCH_LATER_OPTION" = "Removes 'Save to Watch later' option";
+"REMOVE_WATCH_LATER_OPTION_DESC" = "Removes 'Save to Watch Later' option from the flyout menu.";
+"REMOVE_SAVE_OPTION" = "Removes 'Save to playlist' option";
+"REMOVE_SAVE_OPTION_DESC" = "Removes 'Save to playlist' option from the flyout menu.";
+"REMOVE_REMOVE_FROM_PLAYLIST_OPTION" = "Removes 'Remove from playlist' option";
+"REMOVE_REMOVE_FROM_PLAYLIST_OPTION_DESC" = "Removes 'Remove from playlist' option from the flyout menu.";
+"REMOVE_SHARE_OPTION" = "Removes 'Share' option";
+"REMOVE_SHARE_OPTION_DESC" = "Removes 'Share' option from the flyout menu.";
+"REMOVE_NOT_INTERESTED_OPTION" = "Removes 'Not interested' option";
+"REMOVE_NOT_INTERESTED_OPTION_DESC" = "Removes 'Not interested' option from the flyout menu.";
+"REMOVE_DONT_RECOMMEND_OPTION" = "Removes 'Don't recommend channel' option";
+"REMOVE_DONT_RECOMMEND_OPTION_DESC" = "Removes 'Don't recommend channel' option from the flyout menu.";
+"REMOVE_INFO_OPTION" = "Removes 'Why are you seeing this?' option";
+"REMOVE_INFO_OPTION_DESC" = "Removes 'Why are you seeing this?' option from the flyout menu.";
+"REMOVE_FILTER_OPTION" = "Removes 'Search filters' option";
+"REMOVE_FILTER_OPTION_DESC" = "Removes 'Search filters' option from the flyout menu.";
+"REMOVE_REPORT_OPTION" = "Removes 'Report' option";
+"REMOVE_REPORT_OPTION_DESC" = "Removes 'Report' option from the flyout menu.";
+"REMOVE_YOUTUBE_MUSIC_OPTION" = "Removes 'Listen with YouTube Music' option";
+"REMOVE_YOUTUBE_MUSIC_OPTION_DESC" = "Removes 'Listen with YouTube Music' option from the flyout menu.";
+"REMOVE_FEED_BACK_OPTION" = "Removes 'Send feedback' option";
+"REMOVE_FEED_BACK_OPTION_DESC" = "Removes 'Send feedback' option from the flyout menu.";
+"REMOVE_CAST_OPTION" = "Removes 'Watch on TV' option";
+"REMOVE_CAST_OPTION_DESC" = "Removes 'Watch on TV' option from the flyout menu.";
+"REMOVE_SHUFFLE_OPTION" = "Removes 'Shuffle' option";
+"REMOVE_SHUFFLE_OPTION_DESC" = "Removes 'Shuffle' option from the flyout menu.";
+"REMOVE_UN_SUB_OPTION" = "Removes 'Unsubscribe' option";
+"REMOVE_UN_SUB_OPTION_DESC" = "Removes 'Unsubscribe' option from the flyout menu.";
+"REMOVE_HIDE_FROM_PLAYLIST_OPTION" = "Removes 'Hide video from playlist' option";
+"REMOVE_HIDE_FROM_PLAYLIST_OPTION_DESC" = "Removes 'Hide video from playlist' option from the flyout menu.";
+"REMOVE_HELP_OPTION" = "Removes 'Help & feedback' option";
+"REMOVE_HELP_OPTION_DESC" = "Removes 'Help & feedback' option from the flyout menu.";
+"REMOVE_NOTIFY_OPTION" = "Removes 'Notify me' option";
+"REMOVE_NOTIFY_OPTION_DESC" = "Removes 'Notify me' option from the flyout menu.";
+"REMOVE_CLEARSCREEN_OPTION" = "Removes 'Clear Screen' option";
+"REMOVE_CLEARSCREEN_OPTION_DESC" = "Removes 'Clear Screen' option from the flyout menu.";
+"HIDE_COMMU_GUIDE" = "Hide community guidelines";
+"HIDE_COMMU_GUIDE_DESC" = "Hide community guidelines header.";
+"DISABLES_RTL" = "Disable RTL formatting";
+"DISABLES_RTL_DESC" = "Forcefully displays text in left-to-right (LTR) format for languages that are initially displayed in right-to-left (RTL).";
+"DEVICE_UI" = "Interface style";
+
+"SB_DESCRIPTION" = "Skip sponsored segments automatically using community-submitted data.";
+"SB_ENABLE" = "Enable SponsorBlock";
+"SB_ENABLE_DESC" = "Activate the SponsorBlock extension to skip sponsored segments.";
+"SB_SHOW_BUTTON" = "Show overlay button";
+"SB_SHOW_BUTTON_DESC" = "Display a SponsorBlock toggle button in the player overlay.";
+"SB_SHOW_NOTIFICATIONS" = "Show notifications";
+"SB_SHOW_NOTIFICATIONS_DESC" = "Display notifications when segments are skipped. Notifications for manual-skip categories are always shown.";
+"SB_HAPTIC_FEEDBACK" = "Haptic feedback";
+"SB_HAPTIC_FEEDBACK_DESC" = "Provide haptic feedback when a segment is skipped.";
+"SB_SEGMENTS_IN_PLAYER" = "Segments in player";
+"SB_SEGMENTS_IN_PLAYER_DESC" = "Display colored segments on the progress bar of the main video player.";
+"SB_SEGMENTS_IN_FEED" = "Segments in feed";
+"SB_SEGMENTS_IN_FEED_DESC" = "Display colored segments on the progress bar of players in the feed.";
+"SB_SEGMENTS_IN_MINIPLAYER" = "Segments in mini-player";
+"SB_SEGMENTS_IN_MINIPLAYER_DESC" = "Display colored segments on the progress bar of the mini-player.";
+"SB_SHOW_DURATION" = "Show duration without segments";
+"SB_SHOW_DURATION_DESC" = "Display the total video duration excluding skippable segments.";
+"SB_CATEGORIES_HEADER" = "Segment categories";
+"SB_SEGMENT_COLOR" = "Segment color";
+
+"SB_ACTION_DISABLE" = "Disable";
+"SB_ACTION_AUTO_SKIP" = "Auto-skip";
+"SB_ACTION_ASK" = "Ask before skipping";
+"SB_ACTION_DISPLAY" = "Show on seek bar only";
+"SB_ACTION_SKIP_TO" = "Skip to segment";
+
+"SB_CAT_sponsor" = "Sponsor";
+"SB_CAT_sponsor_DESC" = "Paid promotion for a product or service not directly related to the creator.";
+"SB_CAT_intro" = "Intro";
+"SB_CAT_intro_DESC" = "Intermission or intro animation typically seen at the start of a video.";
+"SB_CAT_outro" = "Endcards";
+"SB_CAT_outro_DESC" = "Endcards, credits, or outros at the end of the video.";
+"SB_CAT_interaction" = "Interaction";
+"SB_CAT_interaction_DESC" = "Reminders to like, subscribe, or follow on social media.";
+"SB_CAT_selfpromo" = "Self-promotion";
+"SB_CAT_selfpromo_DESC" = "Unpaid promotion of the creator's own products, merchandise, or other content.";
+"SB_CAT_music_offtopic" = "Non-music";
+"SB_CAT_music_offtopic_DESC" = "Non-music sections in music videos (e.g. talking, introductions).";
+"SB_CAT_preview" = "Preview";
+"SB_CAT_preview_DESC" = "Recap or preview of content from another video or previous episode.";
+"SB_CAT_hook" = "Hook/Greetings";
+"SB_CAT_hook_DESC" = "Narrated trailers for the upcoming video, greetings and goodbyes.";
+"SB_CAT_poi_highlight" = "Highlight";
+"SB_CAT_poi_highlight_DESC" = "The key moment or highlight point of the video. Use 'Skip to' to jump directly to it.";
+"SB_CAT_filler" = "Filler";
+"SB_CAT_filler_DESC" = "Tangential content, jokes, or filler that is not directly related to the main topic.";
+
+"SB_SKIPPED" = "%@ segment has been skipped";
+"SB_DETECTED" = "%@ segment detected.\nWould you like to skip the segment?";
+"SB_SKIP_NOW" = "Skip";
+"SB_UNSKIP" = "Unskip";
+"SB_JUMPED_TO_HIGHLIGHT" = "Jumped to highlight";
+"SB_JUMP_TO_HIGHLIGHT" = "Highlight available. Jump to the point?";
+"SB_SEGMENT_COLOR_SUFFIX" = "Segment color";
+"SB_SECTION_MAIN" = "Main";
+"SB_SKIP_ALERT_DURATION" = "Skip alert duration";
+"SB_UNSKIP_ALERT_DURATION" = "Unskip alert duration";
\ No newline at end of file
diff --git a/layout/Library/Application Support/YouMod.bundle/uk.lproj/Localizable.strings b/layout/Library/Application Support/YouMod.bundle/uk.lproj/Localizable.strings
index bccaf550..2c1a8a0f 100644
--- a/layout/Library/Application Support/YouMod.bundle/uk.lproj/Localizable.strings
+++ b/layout/Library/Application Support/YouMod.bundle/uk.lproj/Localizable.strings
@@ -1,11 +1,18 @@
-"NOTE" = "NOTE: Some hiding features may leave a blank box.";
"SETTINGS" = "Settings";
+"INTERFACE" = "Interface";
+
+"LOOP_DISABLED" = "Loop disabled";
+"LOOP_ENABLED" = "Loop enabled";
"NEW_ISSUES" = "Report issues or request new features";
"NEW_ISSUES_DESC" = "Found bugs or want to request new features? Tap here.";
"SOURCE_CODES" = "Source codes";
"SOURCE_CODES_DESC" = "Take a look at the codes!";
+"EXTRA" = "Extra";
+"FIX_PLAYBACK_ISSUES" = "Fix playback issues (Temporary)";
+"FIX_PLAYBACK_ISSUES_DESC" = "Try to fix video playback issues for some users. Turning this on will make the player automatically reloads itself everytime it errors or fails to play the video.";
+
"PERFER_HEADER" = "YouMod perferences";
"PERFER" = "Perferences";
"IMPORT" = "Import settings";
@@ -24,6 +31,7 @@
"ERROR_NO_KEYS_IMPORT" = "No available keys to import";
"ERROR_NO_KEYS_EXPORT" = "No available keys to export";
"OVERRIDE" = "This will override the current settings. Do you want to continue?";
+"DISABLED" = "Disabled";
"CACHE" = "Cache management";
"CLEARCACHE" = "Clear cache";
@@ -32,12 +40,80 @@
"AUTO_CLEARCACHE_DESC" = "Automatically clears the app cache on startup.";
"DOWNLOADING" = "Downloading";
-"DOWNLOAD_MANAGER" = "Download Manager";
-"DOWNLOAD_MANAGER_DESC" = "Replaces YouTube's video downloader with YouMod one. With ablity to downloading videos, audios, captions, and thumbnails";
+"DOWNLOAD_MANAGER" = "Download manager";
+"DOWNLOAD_MANAGER_DESC" = "Replaces YouTube's video downloader with YouMod one. With ablity to downloading videos, audios, captions, and thumbnails.";
"DOWNLOAD_SAVE_PHOTOS" = "Automatically save to Photos";
"DOWNLOAD_SAVE_PHOTOS_DESC" = "Automatically saves downloaded videos and images directly to your Photos app.";
-"DOWNLOAD_DRC_AUDIO" = "Prefer DRC Audio";
-"DOWNLOAD_DRC_AUDIO_DESC" = "Prioritizes downloading audio tracks with Dynamic Range Compression for more consistent volume levels.";
+"ADD_SHORTS_DOWNLOAD" = "Adds a download button to Shorts";
+"ADD_SHORTS_DOWNLOAD_DESC" = "Adds a download button to Shorts video overlay.";
+"USE_ORIG_AUDIO" = "Automatically uses original audio track";
+"USE_ORIG_AUDIO_DESC" = "Automatically uses original audio track instead of showing a dialog.";
+"DOWNLOAD_SHORTS" = "Download Shorts";
+"DOWNLOAD_SHORTS_DESC" = "Choose Shorts quality";
+"COPY_VID_INFO" = "Copy video informations";
+"COPY_VID_INFO_DESC" = "Choose what to copy";
+"COPY_ALL_VID_INFO" = "Copy all";
+"COPY_ALL_VID_INFO_DESC" = "Copy all video informations";
+"COPY_AUTHOR" = "Copy author";
+"COPY_AUTHOR_DESC" = "Copy the video author";
+"COPY_TITLE" = "Copy title";
+"COPY_TITLE_DESC" = "Copy the video title";
+"COPY_DESCRIPTION" = "Copy description";
+"COPY_DESCRIPTION_DESC" = "Copy the video description";
+"COPY_URL" = "Copy video link";
+"COPY_URL_TIMESTAMP" = "Copy video link with timestamp";
+"URL_COPIED" = "Copied video link";
+"URL_TIMESTAMP_COPIED" = "Copied video link with timestamp";
+"ERROR_VIDEOID" = "Cannot get the video ID";
+"ERROR_ADS" = "Cannot copy video link while playing ads";
+"COPIED_AUTHOR" = "Copied video author";
+"COPIED_TITLE" = "Copied video title";
+"COPIED_DESCRIPTION" = "Copied video description";
+"SAVE_THUMBNAIL" = "Save thumbnail";
+"SAVE_THUMBNAIL_DESC" = "Save to Photos";
+"DOWNLOAD_CAPTIONS" = "Download captions";
+"DOWNLOAD_CAPTIONS_DESC" = "Save subtitles as .VTT file";
+"DOWNLOAD_AUDIO" = "Download audio";
+"DOWNLOAD_AUDIO_DESC" = "Download audio as .m4a file";
+"DOWNLOAD_VIDEO" = "Download video";
+"DOWNLOAD_VIDEO_DESC" = "Choose video quality";
+"OPEN_VID_BEFORE" = "Open a video before using the download manager";
+"NO_CAPTIONS_URL" = "No valid caption URLs found";
+"NO_AUDIO_URL" = "No audio URL found";
+"NO_STREAM_URL" = "No stream URL found";
+"CAPTIONS_FAILED" = "Failed to download captions";
+"NO_CAPTIONS" = "No captions available for this video";
+"NO_AUDIO_STREAM_FOUND" = "No audio streams found";
+"NO_VID_AUDIO_STREAM_FOUND" = "No video/audio streams found";
+"COPIED_VID_INFO" = "Copied all video informations";
+"SAVED_TO_PHOTOS" = "Saved to Photos";
+"SAVE_FAILED" = "Save failed";
+"PHOTO_ACCESS_DENINED" = "Photos access denied";
+"THUMBNAIL_FAILED" = "Thumbnail failed";
+"DOWNLOADING_THUMBNAIL" = "Downloading thumbnail...";
+"DOWNLOADING_AUDIO" = "Downloading audio...";
+"DOWNLOADING_VIDEO" = "Downloading video...";
+"DOWNLOADING_CAPTIONS" = "Downloading captions...";
+"NO_THUMBNAIL_FOUND" = "No thumbnail found";
+"DOWNLOAD_FAILED" = "Download failed";
+"COPY_THUMBNAIL" = "Copy thumbnail";
+"COPY_THUMBNAIL_DESC" = "Copy to clipboard";
+"SHOW_THUMBNAIL" = "Show thumbnail";
+"SHOW_THUMBNAIL_DESC" = "Show in a dialog";
+"THUMBNAIL_OPTIONS" = "Download thumbnail";
+"THUMBNAIL_OPTIONS_DESC" = "Show more options";
+"COPIED_TO_CLIPBOARD" = "Copied to clipboard";
+"DOWNLOAD_COMPLETED" = "Download completed";
+"CANNOT_SAVE_TO_PHOTOS" = "Cannot save to Photos";
+"FINA_VIDEO" = "Finalizing video...";
+"MERGING_VID_FALLBACK" = "Merging video with fallback...";
+"MERGING_VID" = "Merging video...";
+"CONVERTING" = "Converting to";
+"ALREADY_DOWNLOADING" = "Already downloading";
+"DOWNLOAD_CANCELLED" = "Download cancelled";
+"CANCEL" = "Cancel";
+"TRIMMING_AUDIO" = "Trimming audio...";
+"COPY" = "Copy";
"APPEARANCE" = "Appearance";
"OLED_THEME" = "OLED dark mode";
@@ -46,6 +122,8 @@
"OLED_KEYBOARD_DESC" = "Make the keyboard uses a pure black (OLED) color for dark theme.";
"NAVBAR" = "Navigation bar";
+"STICKY_NAVBAR" = "Sticky Navigation bar";
+"STICKY_NAVBAR_DESC" = "Pins the Navigation bar so that it remains visible when scrolling down.";
"HIDE_YT_LOGO" = "Hide YouTube logo";
"HIDE_YT_LOGO_DESC" = "Hide YouTube logo in the navigation bar.";
"PREMIUM_LOGO" = "Use YouTube Premium logo";
@@ -62,34 +140,78 @@
"FEED" = "Feed";
"HIDE_SUBBAR" = "Hide subbar";
"HIDE_SUBBAR_DESC" = "Hide subbar at the top of the feed.";
+"HIDE_HORI_SHELF" = "Remove horizontal feeds";
+"HIDE_HORI_SHELF_DESC" = "Removes all horizontal feeds (News, Continue Watching, Posts collection, etc.) from the feed, except Playables.";
"HIDE_MUSIC_PLAYLISTS" = "Hide music playlist generator";
"HIDE_MUSIC_PLAYLISTS_DESC" = "Hide music playlist generator (Select music tunes in three) in the feed.";
"HIDE_FEED_POST" = "Hide posts";
"HIDE_FEED_POST_DESC" = "Hide posts in feed.";
+"HIDE_SURVEYS" = "Hide surveys";
+"HIDE_SURVEYS_DESC" = "Hide video surveys that YouTube shows sometimes.";
"HIDE_SHORTS_SHELF" = "Hide Shorts shelf";
"HIDE_SHORTS_SHELF_DESC" = "Hide Shorts shelf in the feed, including in the Subscriptions tab.";
+"KEEP_SHORTS_SUBSCRIPT" = "Keep Shorts in Subscriptions";
+"KEEP_SHORTS_SUBSCRIPT_DESC" = "Prevents removing Shorts from Subscriptions tab.";
"HIDE_SEARCH_HISTORY" = "Hide search history";
"HIDE_SEARCH_HISTORY_DESC" = "Hide previous search history and suggestions when using the search bar.\nNOTE: Your search history will still be accessible in your other YouTube clients.";
-"HIDE_SUB_BUTTON" = "Hide Subscribe button";
-"HIDE_SUB_BUTTON_DESC" = "Hides the subscribe button in channels and video descriptions.";
-"HIDE_SHOP_BUTTON" = "Hide Shopping button";
-"HIDE_SHOP_BUTTON_DESC" = "Hides the shopping/store button in channels descriptions.";
-"HIDE_MEMBER_BUTTON" = "Hide Membership button";
-"HIDE_MEMBER_BUTTON_DESC" = "Hides the 'Join' button in channels and video descriptions.";
+"HIDE_PLAYABLES" = "Remove playables";
+"HIDE_PLAYABLES_DESC" = "Removes playables (mini-games) from the feed.";
"PLAYER" = "Player";
+"PLAYBACK_SPEED" = "Playback speed";
+"CONTROL_CENTER" = "Control Center";
+"PLAYER_ACTIONS" = "Player actions";
+"GESTURE_HEADER" = "Gestures";
+"QUALITY_WIFI" = "Playback quality on WiFi";
+"QUALITY_WIFI_DESC" = "Automatically applies the selected quality when streaming over Wi-Fi.";
+"QUALITY_CELLULAR" = "Playback quality on Cellular";
+"QUALITY_CELLULAR_DESC" = "Automatically applies the selected quality when streaming over mobile data.";
+"QUALITY_LOW_POWER" = "Playback quality on Power saver mode";
+"QUALITY_LOW_POWER_DESC" = "Automatically applies the selected quality when when Low power mode is enabled on your device.";
+"DEFAULT" = "Default";
+"BEST" = "Best";
+"AUDIO_TRACK" = "Preferred audio track";
+"ORIGINAL" = "Original";
+"SELECT_MANUALLY" = "Select manually";
+"AUDIO_TRACK_SELECT" = "Selected audio track";
+"AUDIO_TRACK_SELECT_DESC" = "Specifies the preferred audio track language to use for playback, if available.";
+"NO_AUTO_DUBBED" = "Uses original audio track";
+"NO_AUTO_DUBBED_DESC" = "Uses original audio track if the preferred language track is auto-dubbed or not found.";
+"CAPTION_TRACK" = "Preferred captions";
+"CAPTION_TRACK_SELECT" = "Selected captions";
+"CAPTION_TRACK_SELECT_DESC" = "Specifies the preferred captions language to display, if available.";
+"DISABLES_CAPTION_TRACK" = "Automatically disable captions";
+"DISABLES_CAPTION_TRACK_DESC" = "Automatically disable captions if the preferred language is auto-generated or not found.";
+"DEFAULT_SPEED" = "Default playback rate";
+"DEFAULT_SPEED_DESC" = "Overrides the default playback speed.";
+"SPEED_BUTTON" = "Speed controls button";
+"SPEED_BUTTON_DESC" = "Displays a speed controls button on overlay.";
+"MUTE_BUTTON" = "Mute button";
+"MUTE_BUTTON_DESC" = "Displays a button to mute or unmute the volume on the overlay.";
+"SHARE_BUTTON" = "Share button";
+"SHARE_BUTTON_DESC" = "Displays a button to share the video link on the overlay.";
+"LOOP_BUTTON" = "Loop button";
+"LOOP_BUTTON_DESC" = "Displays a button to loop the video on the overlay.";
+"CAPTION_BUTTON" = "Captions button";
+"CAPTION_BUTTON_DESC" = "Displays a button to copy the video captions on the overlay.";
+"HOLD_TO_SPEED" = "Hold to speed";
+"HOLD_TO_SPEED_DESC" = "Uses custom hold to speed playback rate.";
"HIDE_AUTOPLAY" = "Hide autoplay switch";
"HIDE_AUTOPLAY_DESC" = "Hide autoplay switch in the video overlay.";
"HIDE_CAPTIONS_BUTTON" = "Hide captions button";
"HIDE_CAPTIONS_BUTTON_DESC" = "Hide captions button in the video overlay.";
"HIDE_CAST_BUTTON_PLAYER" = "Hide cast button";
"HIDE_CAST_BUTTON_PLAYER_DESC" = "Hide cast button in the video overlay.";
-"HIDE_PREV_BUTTON" = "Hide previous/back button";
-"HIDE_PREV_BUTTON_DESC" = "Hide previous/back button in the video overlay.";
-"HIDE_NEXT_BUTTON" = "Hide next/skip button";
-"HIDE_NEXT_BUTTON_DESC" = "Hide next/skip button in the video overlay.";
+"HIDE_NEXT_AND_PREV_BUTTON" = "Hide previous and next buttons";
+"HIDE_NEXT_AND_PREV_BUTTON_DESC" = "Hide previous and next buttons in the video overlay.";
"REPLACE_PREVNEXT_BUTTONS" = "Replace Previous/Next buttons";
"REPLACE_PREVNEXT_BUTTONS_DESC" = "Replaces the Previous and Next video buttons in the player with Rewind and Fast Forward buttons.";
+"SKIP_BACKWARD" = "Skip backward";
+"SKIP_BACKWARD_DESC" = "Replaces the previous track button in Control Center with a skip backward button by a set number of seconds.";
+"SKIP_FORWARD" = "Skip forward";
+"SKIP_FORWARD_DESC" = "Replaces the next track button in Control Center with a skip forward button by a set number of seconds.";
+"REWIND_SECONDS" = "Rewind seconds";
+"FORWARD_SECONDS" = "Fast-forward seconds";
"REMOVE_DARK_OVERLAY" = "Remove dark overlay";
"REMOVE_DARK_OVERLAY_DESC" = "Remove dark overlay from the video overlay.";
"HIDE_END_SCREEN" = "Hide endscreen cards";
@@ -102,6 +224,12 @@
"HIDE_PAID_OVERLAY_DESC" = "Hide 'Includes paid promotion' in the video overlay.";
"HIDE_WATERMARK" = "Hide watermark";
"HIDE_WATERMARK_DESC" = "Hide channel watermark at the bottom of the video overlay.";
+"PAUSE_ON_OVERLAY" = "Pause on overlay";
+"PAUSE_ON_OVERLAY_DESC" = "Sets playback on pause if overlay appears.";
+"DISABLES_SNAP_TO_CHAPTER" = "Disable snap to chapters";
+"DISABLES_SNAP_TO_CHAPTER_DESC" = "Prevents the scrubbar stick to the chapters.";
+"DISABLES_ENGAGE_PANEL" = "Disables engagement panel in fullscreen";
+"DISABLES_ENGAGE_PANEL_DESC" = "Prevent engagement panel showing in fullscreen.";
"GESTURES" = "Enable gestures controls";
"GESTURES_DESC" = "Swipe up or down on the left/right edges of the video screen to adjust brightness, volume, or playback speed.";
"GESTURE_AREA" = "Gesture activation area";
@@ -136,6 +264,8 @@
"DISABLES_SHOW_REMAINING_DESC" = "Disable the feature that shows how much time is left in a video when tapping the timestamp.";
"ALWAYS_SHOW_REMAINING" = "Always show remaining time";
"ALWAYS_SHOW_REMAINING_DESC" = "Ensure the remaining time is always displayed by default in the player overlay.";
+"COPY_TIMESTAMP_ON_PAUSE" = "Copy timestamped links";
+"COPY_TIMESTAMP_ON_PAUSE_DESC" = "Allows to copy timestamped link to the clipboard by pressing pause button.";
"SHOW_REMAINING_EXTRA" = "Show extra remaining time";
"SHOW_REMAINING_EXTRA_DESC" = "Adds the estimated finish time to the label in the video overlay.";
"HIDE_FULLSCREEN_ACTIONS" = "Hide fullscreen actions";
@@ -148,93 +278,95 @@
"HIDE_CONTENT_WARNING_DESC" = "Automatically skip the sensitive content warning alert in some vidoes.";
"AUTO_FULLSCREEN" = "Auto fullscreen";
"AUTO_FULLSCREEN_DESC" = "Automatically enter fullscreen mode when starting video playback.";
-"AUTO_DISABLES_CAPTION" = "Auto disable captions";
-"AUTO_DISABLES_CAPTION_DESC" = "Automatically turns off closed captions (CC) whenever a new video starts playing.";
"PORTRAIT_FULLSCREEN" = "Portrait fullscren mode";
"PORTRAIT_FULLSCREEN_DESC" = "Enables portrait fullscreen mode support.";
"OLD_QUALITY_PICKER" = "Use old quality picker";
"OLD_QUALITY_PICKER_DESC" = "Reverts the video quality selection menu to the classic version (lists all resolutions immediately).";
"EXTRA_SPEED" = "Extra speed options";
"EXTRA_SPEED_DESC" = "Adds more video playback speed options up to 10x.";
-"DISABLE_HINTS" = "Disable hints";
-"DISABLE_HINTS_DESC" = "Disable the tutorial hints and interactive pop-ups.";
"FORCE_MINIPLAYER" = "Force miniplayer";
"FORCE_MINIPLAYER_DESC" = "Allows all videos, including those made for kids, to play in the miniplayer when dismissing the player.";
"FORCE_SEEKBAR" = "Always show progress bar";
"FORCE_SEEKBAR_DESC" = "Makes the progress bar (seekbar) remains visible at all times while in fullscreen mode.";
-"HIDE_LIKE_BUTTON" = "Hide Like button";
-"HIDE_LIKE_BUTTON_DESC" = "Hide Like button in the action bar under the video overlay.";
-"HIDE_DISLIKE_BUTTON" = "Hide Dislike button";
-"HIDE_DISLIKE_BUTTON_DESC" = "Hide Dislike button in the action bar under the video overlay.";
-"HIDE_SHARE_BUTTON" = "Hide Share button";
-"HIDE_SHARE_BUTTON_DESC" = "Hide Share button in the action bar under the video overlay.";
-"HIDE_DOWNLOAD_BUTTON" = "Hide Download button";
-"HIDE_DOWNLOAD_BUTTON_DESC" = "Hide Download button in the action bar under the video overlay.";
-"HIDE_CLIP_BUTTON" = "Hide Clip button";
-"HIDE_CLIP_BUTTON_DESC" = "Hide Clip button in the action bar under the video overlay.";
-"HIDE_REMIX_BUTTON" = "Hide Remix button";
-"HIDE_REMIX_BUTTON_DESC" = "Hide Remix button in the action bar under the video overlay.";
-"HIDE_SAVE_BUTTON" = "Hide Save button";
-"HIDE_SAVE_BUTTON_DESC" = "Hide Save button in the action bar under the video overlay.";
+"TAP_TO_SEEK" = "Tap to seek";
+"TAP_TO_SEEK_DESC" = "Quickly jump to any point by tapping the progress bar.";
+"DISABLES_ZOOM" = "Disable free zoom gesture";
+"DISABLES_ZOOM_DESC" = "Disables new free zoom gestures.";
+"USES_24_HOURS_TIME" = "24-hour format";
+"USES_24_HOURS_TIME_DESC" = "Shows end time in 24-hour format.";
+"PAUSE_TWO_FINGERS" = "Pause using two fingers";
+"PAUSE_TWO_FINGERS_DESC" = "Pause/Resume playback by tapping on the screen using two fingers.";
+"HIDE_COMMENTS_SECTION" = "Hide comments section";
+"HIDE_COMMENTS_SECTION_DESC" = "Completely removes the entire comments section.";
+"HIDE_COMMENTS_PREVIEW" = "Hide preview comments";
+"HIDE_COMMENTS_PREVIEW_DESC" = "Removes the preview box for the top comment while keeping the full section expandable.";
+"USE_ANOTHER_MINIPLAYER" = "Uses another miniplayer style";
+"USE_ANOTHER_MINIPLAYER_DESC" = "Uses another miniplayer style redesigned from older YT versions.";
"SHORTS" = "Shorts";
-"HIDE_SHORTS_LIKE_BUTTON" = "Hide Like button";
-"HIDE_SHORTS_LIKE_BUTTON_DESC" = "Hide the Like button in the Shorts player.";
-"HIDE_SHORTS_DISLIKE_BUTTON" = "Hide Dislike button";
-"HIDE_SHORTS_DISLIKE_BUTTON_DESC" = "Hide the Dislike button in the Shorts player.";
-"HIDE_SHORTS_COMMENT_BUTTON" = "Hide Comment button";
-"HIDE_SHORTS_COMMENT_BUTTON_DESC" = "Hide the Comment button in the Shorts player.";
-"HIDE_SHORTS_SHARE_BUTTON" = "Hide Share button";
-"HIDE_SHORTS_SHARE_BUTTON_DESC" = "Hide the Share button in the Shorts player.";
-"HIDE_SHORTS_REMIX_BUTTON" = "Hide Remix button";
-"HIDE_SHORTS_REMIX_BUTTON_DESC" = "Hide the Remix button in the Shorts player.";
-"HIDE_METADATA_BUTTON" = "Hide sound metadata button";
-"HIDE_METADATA_BUTTON_DESC" = "Hide the button that shows the sound source or music metadata in the Shorts player.";
+"SHORTS_ONLY" = "Shorts only mode";
+"SHORTS_ONLY_DESC" = "Limits YouTube functionality to viewing Shorts only.";
+"SHORTS_ACTION" = "Shorts action after the video ends";
+"LOOP" = "Loop video";
+"SKIP_TO_NEXT_SHORTS" = "Skip to next video";
+"PAUSE_SHORTS" = "Stop video";
+"REMOVE_LIVE_SHORTS" = "Remove live videos";
+"REMOVE_LIVE_SHORTS_DESC" = "Removes live videos from the feed.";
"HIDE_SHORTS_PRODUCT" = "Hide products";
"HIDE_SHORTS_PRODUCT_DESC" = "Hide tagged products and shopping links in the Shorts overlay.";
"HIDE_SHORTS_RECBAR" = "Hide recommendation bar";
"HIDE_SHORTS_RECBAR_DESC" = "Hide the suggested action bar in the Shorts player (e.g. Try this sound, Shopping, Preview comment).";
-"HIDE_SHORTS_COMMIT" = "Hide commissions bar";
-"HIDE_SHORTS_COMMIT_DESC" = "Hide the bar that shows the video got commissions in the Shorts player.";
-"HIDE_SHORTS_SUBSCRIPT_BUTTON" = "Hide subscriptions button";
-"HIDE_SHORTS_SUBSCRIPT_BUTTON_DESC" = "Hide the Subscriptions button at the top of the Shorts player.";
-"HIDE_SHORTS_LIVE_BUTTON" = "Hide live button";
-"HIDE_SHORTS_LIVE_BUTTON_DESC" = "Hide the Live button at the top of the Shorts player.";
-"HIDE_SHORTS_LENS_BUTTON" = "Hide lens button";
-"HIDE_SHORTS_LENS_BUTTON_DESC" = "Hide the Lens button at the top of the Shorts player.";
-"HIDE_SHORTS_TRENDS_BUTTON" = "Hide trends button";
-"HIDE_SHORTS_TRENDS_BUTTON_DESC" = "Hide the Trends button at the top of the Shorts player.";
-"HIDE_SHORTS_TO_VIDEO" = "Hide link to full video";
-"HIDE_SHORTS_TO_VIDEO_DESC" = "Hide the link that redirects in a Short to a full video in the Shorts player.";
"ENABLES_SHORTS_QUALITY" = "Enable Shorts quality picker";
"ENABLES_SHORTS_QUALITY_DESC" = "Force enable the video quality selection menu in Shorts.";
"SHOW_SHORTS_SEEKBAR" = "Always show seekbar";
"SHOW_SHORTS_SEEKBAR_DESC" = "Ensure the progress/seek bar is always visible at the bottom of the Shorts player.";
+"REMOVE_POSTS_SHORTS" = "Remove posts";
+"REMOVE_POSTS_SHORTS_DESC" = "Remove posts from the feed.";
+"SHORTS_FULLSCREEN" = "Pinch to Fullscreen";
+"SHORTS_FULLSCREEN_DESC" = "Manages visibility of the overlay with pinch in and pinch out gestures, displaying Shorts in fullscreen mode.";
"TABBAR" = "Tab bar";
"DEFAULT_TAB" = "Default startup tab";
-"DEFAULT_TAB_DESC" = "You can select the tab you want to open when opening the app.";
-"HOME_NAME" = "Home";
-"SUBSCRIPT_NAME" = "Subscriptions";
-"LIB_NAME" = "Library (You)";
+"FORSTED_TAB_BAR" = "Translucent tab bar";
+"ENABLED" = "Enabled";
"HIDE_TAB_INDI" = "Hide tab indicators";
"HIDE_TAB_INDI_DESC" = "Hide all tab indicators in the tab bar.";
"HIDE_TAB_LABELS" = "Hide tab labels";
"HIDE_TAB_LABELS_DESC" = "Hide all tab labels in the tab bar.";
-"HIDE_HOME_TAB" = "Hide Home tab";
-"HIDE_HOME_TAB_DESC" = "Hide Home tab in the tab bar.";
-"HIDE_SHORTS_TAB" = "Hide Shorts tab";
-"HIDE_SHORTS_TAB_DESC" = "Hide Shorts tab in the tab bar.";
-"HIDE_CREATE_BUTTON" = "Hide Create button";
-"HIDE_CREATE_BUTTON_DESC" = "Hide Create button in the tab bar.";
-"HIDE_SUBSCRIPT_TAB" = "Hide Subscriptions tab";
-"HIDE_SUBSCRIPT_TAB_DESC" = "Hide Subscriptions tab in the tab bar.";
+"HOME_TAB" = "Home";
+"SHORTS_TAB" = "Shorts";
+"CREATE_TAB" = "Create";
+"SUBSCRIPTIONS_TAB" = "Subscriptions";
+"LIBRARY_TAB" = "Library (You)";
+"HISTORY_TAB" = "History";
+"GAMING_TAB" = "Gaming";
+"SPORTS_TAB" = "Sports";
+"NOTI_TAB" = "Notifications";
+"NEWS_TAB" = "News";
+"MUSIC_TAB" = "Music";
+"WATCH_LATER_TAB" = "Watch later";
+"PLAYLIST_TAB" = "Playlists";
+"LIKE_TAB" = "Liked videos";
+"LIVE_TAB" = "Live";
+"POST_TAB" = "Posts";
+"VIDEO_TAB" = "Your videos";
+"MOVIE_TAB" = "Movies";
+"COURSE_TAB" = "Courses";
+"MINIGAME_TAB" = "Playables";
+"MANAGE_TABS" = "Manage tabs";
+"MANAGE_TABS_DESC" = "Reorder and toggle tabs";
+"TAB_REORDER_HINT" = "Drag to reorder. Toggle to show/hide.\nTab settings are also accessible by long-pressing the first tab in the tab bar.";
+"TAB_LIMIT" = "Tab limit";
+"TAB_LIMIT_DESC" = "Only 6 tabs are allowed.";
+"ZERO_TAB_DESC" = "At least 1 tab has to be enabled.";
"MISCELLANEOUS" = "Miscellaneous";
"BACKGROUND_PLAYBACK" = "Background playback";
"BACKGROUND_PLAYBACK_DESC" = "Enables background playback for videos.";
"DISABLES_SHORTS_PIP" = "Disables Shorts PiP";
"DISABLES_SHORTS_PIP_DESC" = "Try to disables Shorts PiP, this might not 100% disables it.";
+"DISABLE_HINTS" = "Disable hints";
+"DISABLE_HINTS_DESC" = "Disable the tutorial hints and interactive pop-ups.";
"BLOCK_UPGRADE_DIALOGS" = "Block upgrade dialogs";
"BLOCK_UPGRADE_DIALOGS_DESC" = "Block the YouTube dialogs that ask you to upgrade the app.";
"ARE_YOU_THERE_DIALOG" = "Hide 'Are you there?' dialog";
@@ -247,7 +379,109 @@
"DISABLES_SNACK_BAR_DESC" = "Disables snack bar when pressing some buttons or doing something in the app.";
"HIDE_STARTUP_ANIMATIONS" = "Hide YouTube's startup animations";
"HIDE_STARTUP_ANIMATIONS_DESC" = "Hide YouTube's startup animations when opening the app.";
-"HIDE_PLAY_IN_NEXT_QUEUE" = "Hide 'Play next in queue'";
-"HIDE_PLAY_IN_NEXT_QUEUE_DESC" = "Hide the 'Play next in queue' option in the flyout menu.";
"HIDE_LIKE_DISLIKE_VOTES" = "Hide like/dislike votes";
-"HIDE_LIKE_DISLIKE_VOTES_DESC" = "Hide messages that pop up when you like/dislike videos.";
\ No newline at end of file
+"HIDE_LIKE_DISLIKE_VOTES_DESC" = "Hide messages that pop up when you like/dislike videos.";
+"FLYOUT_MENU" = "Flyout menu";
+"REMOVE_PLAY_IN_NEXT_QUEUE_OPTION" = "Removes 'Play next in queue' option";
+"REMOVE_PLAY_IN_NEXT_QUEUE_OPTION_DESC" = "Removes 'Play next in queue' option from the flyout menu.";
+"REMOVE_DOWNLOAD_OPTION" = "Removes 'Download video' option";
+"REMOVE_DOWNLOAD_OPTION_DESC" = "Removes 'Download video' option from the flyout menu.";
+"REMOVE_WATCH_LATER_OPTION" = "Removes 'Save to Watch later' option";
+"REMOVE_WATCH_LATER_OPTION_DESC" = "Removes 'Save to Watch Later' option from the flyout menu.";
+"REMOVE_SAVE_OPTION" = "Removes 'Save to playlist' option";
+"REMOVE_SAVE_OPTION_DESC" = "Removes 'Save to playlist' option from the flyout menu.";
+"REMOVE_REMOVE_FROM_PLAYLIST_OPTION" = "Removes 'Remove from playlist' option";
+"REMOVE_REMOVE_FROM_PLAYLIST_OPTION_DESC" = "Removes 'Remove from playlist' option from the flyout menu.";
+"REMOVE_SHARE_OPTION" = "Removes 'Share' option";
+"REMOVE_SHARE_OPTION_DESC" = "Removes 'Share' option from the flyout menu.";
+"REMOVE_NOT_INTERESTED_OPTION" = "Removes 'Not interested' option";
+"REMOVE_NOT_INTERESTED_OPTION_DESC" = "Removes 'Not interested' option from the flyout menu.";
+"REMOVE_DONT_RECOMMEND_OPTION" = "Removes 'Don't recommend channel' option";
+"REMOVE_DONT_RECOMMEND_OPTION_DESC" = "Removes 'Don't recommend channel' option from the flyout menu.";
+"REMOVE_INFO_OPTION" = "Removes 'Why are you seeing this?' option";
+"REMOVE_INFO_OPTION_DESC" = "Removes 'Why are you seeing this?' option from the flyout menu.";
+"REMOVE_FILTER_OPTION" = "Removes 'Search filters' option";
+"REMOVE_FILTER_OPTION_DESC" = "Removes 'Search filters' option from the flyout menu.";
+"REMOVE_REPORT_OPTION" = "Removes 'Report' option";
+"REMOVE_REPORT_OPTION_DESC" = "Removes 'Report' option from the flyout menu.";
+"REMOVE_YOUTUBE_MUSIC_OPTION" = "Removes 'Listen with YouTube Music' option";
+"REMOVE_YOUTUBE_MUSIC_OPTION_DESC" = "Removes 'Listen with YouTube Music' option from the flyout menu.";
+"REMOVE_FEED_BACK_OPTION" = "Removes 'Send feedback' option";
+"REMOVE_FEED_BACK_OPTION_DESC" = "Removes 'Send feedback' option from the flyout menu.";
+"REMOVE_CAST_OPTION" = "Removes 'Watch on TV' option";
+"REMOVE_CAST_OPTION_DESC" = "Removes 'Watch on TV' option from the flyout menu.";
+"REMOVE_SHUFFLE_OPTION" = "Removes 'Shuffle' option";
+"REMOVE_SHUFFLE_OPTION_DESC" = "Removes 'Shuffle' option from the flyout menu.";
+"REMOVE_UN_SUB_OPTION" = "Removes 'Unsubscribe' option";
+"REMOVE_UN_SUB_OPTION_DESC" = "Removes 'Unsubscribe' option from the flyout menu.";
+"REMOVE_HIDE_FROM_PLAYLIST_OPTION" = "Removes 'Hide video from playlist' option";
+"REMOVE_HIDE_FROM_PLAYLIST_OPTION_DESC" = "Removes 'Hide video from playlist' option from the flyout menu.";
+"REMOVE_HELP_OPTION" = "Removes 'Help & feedback' option";
+"REMOVE_HELP_OPTION_DESC" = "Removes 'Help & feedback' option from the flyout menu.";
+"REMOVE_NOTIFY_OPTION" = "Removes 'Notify me' option";
+"REMOVE_NOTIFY_OPTION_DESC" = "Removes 'Notify me' option from the flyout menu.";
+"REMOVE_CLEARSCREEN_OPTION" = "Removes 'Clear Screen' option";
+"REMOVE_CLEARSCREEN_OPTION_DESC" = "Removes 'Clear Screen' option from the flyout menu.";
+"HIDE_COMMU_GUIDE" = "Hide community guidelines";
+"HIDE_COMMU_GUIDE_DESC" = "Hide community guidelines header.";
+"DISABLES_RTL" = "Disable RTL formatting";
+"DISABLES_RTL_DESC" = "Forcefully displays text in left-to-right (LTR) format for languages that are initially displayed in right-to-left (RTL).";
+"DEVICE_UI" = "Interface style";
+
+"SB_DESCRIPTION" = "Skip sponsored segments automatically using community-submitted data.";
+"SB_ENABLE" = "Enable SponsorBlock";
+"SB_ENABLE_DESC" = "Activate the SponsorBlock extension to skip sponsored segments.";
+"SB_SHOW_BUTTON" = "Show overlay button";
+"SB_SHOW_BUTTON_DESC" = "Display a SponsorBlock toggle button in the player overlay.";
+"SB_SHOW_NOTIFICATIONS" = "Show notifications";
+"SB_SHOW_NOTIFICATIONS_DESC" = "Display notifications when segments are skipped. Notifications for manual-skip categories are always shown.";
+"SB_HAPTIC_FEEDBACK" = "Haptic feedback";
+"SB_HAPTIC_FEEDBACK_DESC" = "Provide haptic feedback when a segment is skipped.";
+"SB_SEGMENTS_IN_PLAYER" = "Segments in player";
+"SB_SEGMENTS_IN_PLAYER_DESC" = "Display colored segments on the progress bar of the main video player.";
+"SB_SEGMENTS_IN_FEED" = "Segments in feed";
+"SB_SEGMENTS_IN_FEED_DESC" = "Display colored segments on the progress bar of players in the feed.";
+"SB_SEGMENTS_IN_MINIPLAYER" = "Segments in mini-player";
+"SB_SEGMENTS_IN_MINIPLAYER_DESC" = "Display colored segments on the progress bar of the mini-player.";
+"SB_SHOW_DURATION" = "Show duration without segments";
+"SB_SHOW_DURATION_DESC" = "Display the total video duration excluding skippable segments.";
+"SB_CATEGORIES_HEADER" = "Segment categories";
+"SB_SEGMENT_COLOR" = "Segment color";
+
+"SB_ACTION_DISABLE" = "Disable";
+"SB_ACTION_AUTO_SKIP" = "Auto-skip";
+"SB_ACTION_ASK" = "Ask before skipping";
+"SB_ACTION_DISPLAY" = "Show on seek bar only";
+"SB_ACTION_SKIP_TO" = "Skip to segment";
+
+"SB_CAT_sponsor" = "Sponsor";
+"SB_CAT_sponsor_DESC" = "Paid promotion for a product or service not directly related to the creator.";
+"SB_CAT_intro" = "Intro";
+"SB_CAT_intro_DESC" = "Intermission or intro animation typically seen at the start of a video.";
+"SB_CAT_outro" = "Endcards";
+"SB_CAT_outro_DESC" = "Endcards, credits, or outros at the end of the video.";
+"SB_CAT_interaction" = "Interaction";
+"SB_CAT_interaction_DESC" = "Reminders to like, subscribe, or follow on social media.";
+"SB_CAT_selfpromo" = "Self-promotion";
+"SB_CAT_selfpromo_DESC" = "Unpaid promotion of the creator's own products, merchandise, or other content.";
+"SB_CAT_music_offtopic" = "Non-music";
+"SB_CAT_music_offtopic_DESC" = "Non-music sections in music videos (e.g. talking, introductions).";
+"SB_CAT_preview" = "Preview";
+"SB_CAT_preview_DESC" = "Recap or preview of content from another video or previous episode.";
+"SB_CAT_hook" = "Hook/Greetings";
+"SB_CAT_hook_DESC" = "Narrated trailers for the upcoming video, greetings and goodbyes.";
+"SB_CAT_poi_highlight" = "Highlight";
+"SB_CAT_poi_highlight_DESC" = "The key moment or highlight point of the video. Use 'Skip to' to jump directly to it.";
+"SB_CAT_filler" = "Filler";
+"SB_CAT_filler_DESC" = "Tangential content, jokes, or filler that is not directly related to the main topic.";
+
+"SB_SKIPPED" = "%@ segment has been skipped";
+"SB_DETECTED" = "%@ segment detected.\nWould you like to skip the segment?";
+"SB_SKIP_NOW" = "Skip";
+"SB_UNSKIP" = "Unskip";
+"SB_JUMPED_TO_HIGHLIGHT" = "Jumped to highlight";
+"SB_JUMP_TO_HIGHLIGHT" = "Highlight available. Jump to the point?";
+"SB_SEGMENT_COLOR_SUFFIX" = "Segment color";
+"SB_SECTION_MAIN" = "Main";
+"SB_SKIP_ALERT_DURATION" = "Skip alert duration";
+"SB_UNSKIP_ALERT_DURATION" = "Unskip alert duration";
\ No newline at end of file
diff --git a/layout/Library/Application Support/YouMod.bundle/vi.lproj/Localizable.strings b/layout/Library/Application Support/YouMod.bundle/vi.lproj/Localizable.strings
index b6e1e6c0..319e862a 100644
--- a/layout/Library/Application Support/YouMod.bundle/vi.lproj/Localizable.strings
+++ b/layout/Library/Application Support/YouMod.bundle/vi.lproj/Localizable.strings
@@ -1,11 +1,18 @@
-"NOTE" = "LƯU Ý : Một số tính năng ẩn sẽ để lại khoảng trống.";
"SETTINGS" = "Cài đặt";
+"INTERFACE" = "Interface";
+
+"LOOP_DISABLED" = "Loop disabled";
+"LOOP_ENABLED" = "Loop enabled";
"NEW_ISSUES" = "Báo lỗi hoặc yêu cầu tính năng mới.";
"NEW_ISSUES_DESC" = "Bạn tìm thấy lỗi hoặc muốn yêu cầu thêm tính năng mới? Nhấn vào đây.";
"SOURCE_CODES" = "Mã nguồn";
"SOURCE_CODES_DESC" = "Xem mã nguồn dự án!";
+"EXTRA" = "Extra";
+"FIX_PLAYBACK_ISSUES" = "Fix playback issues (Temporary)";
+"FIX_PLAYBACK_ISSUES_DESC" = "Try to fix video playback issues for some users. Turning this on will make the player automatically reloads itself everytime it errors or fails to play the video.";
+
"PERFER_HEADER" = "Tuỳ chọn YouMod";
"PERFER" = "Tùy chọn";
"IMPORT" = "Nhập cài đặt";
@@ -24,6 +31,7 @@
"ERROR_NO_KEYS_IMPORT" = "Không có từ khóa nào khả dụng để nhập";
"ERROR_NO_KEYS_EXPORT" = "Không có từ khóa nào khả dụng để xuất";
"OVERRIDE" = "Thao tác này sẽ ghi đè lên các cài đặt hiện tại của bạn. Bạn có muốn tiếp tục không?";
+"DISABLED" = "Disabled";
"CACHE" = "Quản lý bộ nhớ tạm";
"CLEARCACHE" = "Xóa bộ nhớ tạm";
@@ -36,8 +44,77 @@
"DOWNLOAD_MANAGER_DESC" = "Thay thế trình tải xuống của YouTube bằng YouMod. Với khả năng tải video, âm thanh, phụ đề, và ảnh thu nhỏ";
"DOWNLOAD_SAVE_PHOTOS" = "Tự động lưu vào Thư viện ảnh";
"DOWNLOAD_SAVE_PHOTOS_DESC" = "Tự động lưu video và ảnh trực tiếp vào ứng dụng Ảnh.";
-"DOWNLOAD_DRC_AUDIO" = "Ưu tiên DRC";
-"DOWNLOAD_DRC_AUDIO_DESC" = "Ưu tiên tải các bản âm thanh có nén dải động (Dynamic Range Compression) để có mức âm lượng ổn định hơn.";
+"ADD_SHORTS_DOWNLOAD" = "Adds a download button to Shorts";
+"ADD_SHORTS_DOWNLOAD_DESC" = "Adds a download button to Shorts video overlay.";
+"USE_ORIG_AUDIO" = "Automatically uses original audio track";
+"USE_ORIG_AUDIO_DESC" = "Automatically uses original audio track instead of showing a dialog.";
+"DOWNLOAD_SHORTS" = "Download Shorts";
+"DOWNLOAD_SHORTS_DESC" = "Choose Shorts quality";
+"COPY_VID_INFO" = "Copy video informations";
+"COPY_VID_INFO_DESC" = "Choose what to copy";
+"COPY_ALL_VID_INFO" = "Copy all";
+"COPY_ALL_VID_INFO_DESC" = "Copy all video informations";
+"COPY_AUTHOR" = "Copy author";
+"COPY_AUTHOR_DESC" = "Copy the video author";
+"COPY_TITLE" = "Copy title";
+"COPY_TITLE_DESC" = "Copy the video title";
+"COPY_DESCRIPTION" = "Copy description";
+"COPY_DESCRIPTION_DESC" = "Copy the video description";
+"COPY_URL" = "Copy video link";
+"COPY_URL_TIMESTAMP" = "Copy video link with timestamp";
+"URL_COPIED" = "Copied video link";
+"URL_TIMESTAMP_COPIED" = "Copied video link with timestamp";
+"ERROR_VIDEOID" = "Cannot get the video ID";
+"ERROR_ADS" = "Cannot copy video link while playing ads";
+"COPIED_AUTHOR" = "Copied video author";
+"COPIED_TITLE" = "Copied video title";
+"COPIED_DESCRIPTION" = "Copied video description";
+"SAVE_THUMBNAIL" = "Save thumbnail";
+"SAVE_THUMBNAIL_DESC" = "Save to Photos";
+"DOWNLOAD_CAPTIONS" = "Download captions";
+"DOWNLOAD_CAPTIONS_DESC" = "Save subtitles as .VTT file";
+"DOWNLOAD_AUDIO" = "Download audio";
+"DOWNLOAD_AUDIO_DESC" = "Download audio as .m4a file";
+"DOWNLOAD_VIDEO" = "Download video";
+"DOWNLOAD_VIDEO_DESC" = "Choose video quality";
+"OPEN_VID_BEFORE" = "Open a video before using the download manager";
+"NO_CAPTIONS_URL" = "No valid caption URLs found";
+"NO_AUDIO_URL" = "No audio URL found";
+"NO_STREAM_URL" = "No stream URL found";
+"CAPTIONS_FAILED" = "Failed to download captions";
+"NO_CAPTIONS" = "No captions available for this video";
+"NO_AUDIO_STREAM_FOUND" = "No audio streams found";
+"NO_VID_AUDIO_STREAM_FOUND" = "No video/audio streams found";
+"COPIED_VID_INFO" = "Copied all video informations";
+"SAVED_TO_PHOTOS" = "Saved to Photos";
+"SAVE_FAILED" = "Save failed";
+"PHOTO_ACCESS_DENINED" = "Photos access denied";
+"THUMBNAIL_FAILED" = "Thumbnail failed";
+"DOWNLOADING_THUMBNAIL" = "Downloading thumbnail...";
+"DOWNLOADING_AUDIO" = "Downloading audio...";
+"DOWNLOADING_VIDEO" = "Downloading video...";
+"DOWNLOADING_CAPTIONS" = "Downloading captions...";
+"NO_THUMBNAIL_FOUND" = "No thumbnail found";
+"DOWNLOAD_FAILED" = "Download failed";
+"AUDIO_SAVED" = "Audio saved";
+"DOWNLOAD_COMPLETED" = "Download completed";
+"CANNOT_SAVE_TO_PHOTOS" = "Cannot save to Photos";
+"FINA_VIDEO" = "Finalizing video...";
+"MERGING_VID_FALLBACK" = "Merging video with fallback...";
+"MERGING_VID" = "Merging video...";
+"CONVERTING" = "Converting to";
+"ALREADY_DOWNLOADING" = "Already downloading";
+"DOWNLOAD_CANCELLED" = "Download cancelled";
+"CANCEL" = "Cancel";
+"TRIMMING_AUDIO" = "Trimming audio...";
+"COPY_THUMBNAIL" = "Copy thumbnail";
+"COPY_THUMBNAIL_DESC" = "Copy to clipboard";
+"SHOW_THUMBNAIL" = "Show thumbnail";
+"SHOW_THUMBNAIL_DESC" = "Show in a dialog";
+"THUMBNAIL_OPTIONS" = "Download thumbnail";
+"THUMBNAIL_OPTIONS_DESC" = "Show more options";
+"COPIED_TO_CLIPBOARD" = "Copied to clipboard";
+"COPY" = "Copy";
"APPEARANCE" = "Giao diện";
"OLED_THEME" = "Chế độ OLED";
@@ -46,6 +123,8 @@
"OLED_KEYBOARD_DESC" = "Làm cho bàn phím sử dụng giao diện OLED.";
"NAVBAR" = "Thanh điều hướng";
+"STICKY_NAVBAR" = "Sticky Navigation bar";
+"STICKY_NAVBAR_DESC" = "Pins the Navigation bar so that it remains visible when scrolling down.";
"HIDE_YT_LOGO" = "Ẩn logo YouTube";
"HIDE_YT_LOGO_DESC" = "Ẩn logo của YouTube khỏi thanh điều hướng.";
"PREMIUM_LOGO" = "Sử dụng logo Youtube Premium";
@@ -62,32 +141,76 @@
"FEED" = "Bảng tin";
"HIDE_SUBBAR" = "Ẩn thanh phụ";
"HIDE_SUBBAR_DESC" = "Ẩn thanh phụ ở đầu bảng tin.";
+"HIDE_HORI_SHELF" = "Remove horizontal feeds";
+"HIDE_HORI_SHELF_DESC" = "Removes all horizontal feeds (News, Continue Watching, Posts collection, etc.) from the feed, except Playables.";
"HIDE_MUSIC_PLAYLISTS" = "Ẩn trình tạo danh sách phát";
"HIDE_MUSIC_PLAYLISTS_DESC" = "Ẩn trình tạo danh sách phát nhạc (Chọn ba giai điệu) trong bảng tin.";
"HIDE_FEED_POST" = "Ẩn các bài đăng";
"HIDE_FEED_POST_DESC" = "Ẩn bài đăng trên bảng tin.";
+"HIDE_SURVEYS" = "Hide surveys";
+"HIDE_SURVEYS_DESC" = "Hide video surveys that YouTube shows sometimes.";
"HIDE_SHORTS_SHELF" = "Ẩn Shorts trong bảng tin";
"HIDE_SHORTS_SHELF_DESC" = "Ẩn các Shorts trên bảng tin, kể cả trong tab Kênh đăng kí.";
+"KEEP_SHORTS_SUBSCRIPT" = "Keep Shorts in Subscriptions";
+"KEEP_SHORTS_SUBSCRIPT_DESC" = "Prevents removing Shorts from Subscriptions tab.";
"HIDE_SEARCH_HISTORY" = "Ẩn lịch sử tìm kiếm";
"HIDE_SEARCH_HISTORY_DESC" = "Ẩn lịch sử tìm kiếm và các đề xuất trước đó khi sử dụng thanh tìm kiếm.\nGHI CHÚ: Lịch sử tìm kiếm của bạn vẫn khả dụng trên các ứng dụng YouTube khác.";
-"HIDE_SUB_BUTTON" = "Ẩn nút Đăng kí";
-"HIDE_SUB_BUTTON_DESC" = "Ẩn nút Đăng kí trong các kênh và phần mô tả.";
-"HIDE_SHOP_BUTTON" = "Ẩn nút Mua sắm";
-"HIDE_SHOP_BUTTON_DESC" = "Ẩn nút Mua sắm/Cửa hàng trong phần mô tả video.";
-"HIDE_MEMBER_BUTTON" = "Ẩn nút Hội viên";
-"HIDE_MEMBER_BUTTON_DESC" = "Ẩn nút 'Tham gia' hội viên trong kênh và phần mô tả video.";
+"HIDE_PLAYABLES" = "Remove playables";
+"HIDE_PLAYABLES_DESC" = "Removes playables (mini-games) from the feed.";
"PLAYER" = "Trình phát video";
+"PLAYBACK_SPEED" = "Playback speed";
+"CONTROL_CENTER" = "Control Center";
+"PLAYER_ACTIONS" = "Player actions";
+"GESTURE_HEADER" = "Gestures";
+"QUALITY_WIFI" = "Playback quality on WiFi";
+"QUALITY_WIFI_DESC" = "Automatically applies the selected quality when streaming over Wi-Fi.";
+"QUALITY_CELLULAR" = "Playback quality on Cellular";
+"QUALITY_CELLULAR_DESC" = "Automatically applies the selected quality when streaming over mobile data.";
+"QUALITY_LOW_POWER" = "Playback quality on Power saver mode";
+"QUALITY_LOW_POWER_DESC" = "Automatically applies the selected quality when when Low power mode is enabled on your device.";
+"DEFAULT" = "Default";
+"BEST" = "Best";
+"AUDIO_TRACK" = "Preferred audio track";
+"ORIGINAL" = "Original";
+"SELECT_MANUALLY" = "Select manually";
+"AUDIO_TRACK_SELECT" = "Selected audio track";
+"AUDIO_TRACK_SELECT_DESC" = "Specifies the preferred audio track language to use for playback, if available.";
+"NO_AUTO_DUBBED" = "Uses original audio track";
+"NO_AUTO_DUBBED_DESC" = "Uses original audio track if the preferred language track is auto-dubbed or not found.";
+"CAPTION_TRACK" = "Preferred captions";
+"CAPTION_TRACK_SELECT" = "Selected captions";
+"CAPTION_TRACK_SELECT_DESC" = "Specifies the preferred captions language to display, if available.";
+"DISABLES_CAPTION_TRACK" = "Automatically disable captions";
+"DISABLES_CAPTION_TRACK_DESC" = "Automatically disable captions if the preferred language is auto-generated or not found.";
+"DEFAULT_SPEED" = "Default playback rate";
+"DEFAULT_SPEED_DESC" = "Overrides the default playback speed.";
+"SPEED_BUTTON" = "Speed controls button";
+"SPEED_BUTTON_DESC" = "Displays a speed controls button on overlay.";
+"MUTE_BUTTON" = "Mute button";
+"MUTE_BUTTON_DESC" = "Displays a button to mute or unmute the volume on the overlay.";
+"SHARE_BUTTON" = "Share button";
+"SHARE_BUTTON_DESC" = "Displays a button to share the video link on the overlay.";
+"LOOP_BUTTON" = "Loop button";
+"LOOP_BUTTON_DESC" = "Displays a button to loop the video on the overlay.";
+"CAPTION_BUTTON" = "Captions button";
+"CAPTION_BUTTON_DESC" = "Displays a button to copy the video captions on the overlay.";
+"HOLD_TO_SPEED" = "Hold to speed";
+"HOLD_TO_SPEED_DESC" = "Uses custom hold to speed playback rate.";
+"SKIP_BACKWARD" = "Skip backward";
+"SKIP_BACKWARD_DESC" = "Replaces the previous track button in Control Center with a skip backward button by a set number of seconds.";
+"SKIP_FORWARD" = "Skip forward";
+"SKIP_FORWARD_DESC" = "Replaces the next track button in Control Center with a skip forward button by a set number of seconds.";
+"REWIND_SECONDS" = "Rewind seconds";
+"FORWARD_SECONDS" = "Fast-forward seconds";
"HIDE_AUTOPLAY" = "Ẩn công tắc Tự động phát";
"HIDE_AUTOPLAY_DESC" = "Ẩn công tắc tự động phát trong trình phát.";
"HIDE_CAPTIONS_BUTTON" = "Ẩn nút Phụ đề (CC)";
"HIDE_CAPTIONS_BUTTON_DESC" = "Ẩn nút Phụ đề trong trình phát.";
"HIDE_CAST_BUTTON_PLAYER" = "Ẩn nút Trình chiếu (Cast)";
"HIDE_CAST_BUTTON_PLAYER_DESC" = "Ẩn nút Trình chiếu trong trình phát.";
-"HIDE_PREV_BUTTON" = "Ẩn nút Trước/Quay lại";
-"HIDE_PREV_BUTTON_DESC" = "Ẩn nút Trước/Quay lại trong trình phát.";
-"HIDE_NEXT_BUTTON" = "Ẩn nút Tiếp theo/Bỏ qua";
-"HIDE_NEXT_BUTTON_DESC" = "Ẩn nút Tiếp theo/Bỏ qua trong trình phát.";
+"HIDE_NEXT_AND_PREV_BUTTON" = "Hide previous and next buttons";
+"HIDE_NEXT_AND_PREV_BUTTON_DESC" = "Hide previous and next buttons in the video overlay.";
"REPLACE_PREVNEXT_BUTTONS" = "Thay thế nút Trước/Tiếp theo";
"REPLACE_PREVNEXT_BUTTONS_DESC" = "Thay thế các nút video Trước và Tiếp theo trong trình phát bằng nút Tua lại và Tua nhanh.";
"REMOVE_DARK_OVERLAY" = "Xóa lớp phủ tối";
@@ -102,6 +225,12 @@
"HIDE_PAID_OVERLAY_DESC" = "Ẩn 'Chứa nội dung quảng cáo trả phí' trong trình phát video.";
"HIDE_WATERMARK" = "Ẩn hình mờ (Watermark)";
"HIDE_WATERMARK_DESC" = "Ẩn hình mờ của các kênh trong trình phát video.";
+"PAUSE_ON_OVERLAY" = "Pause on overlay";
+"PAUSE_ON_OVERLAY_DESC" = "Sets playback on pause if overlay appears.";
+"DISABLES_SNAP_TO_CHAPTER" = "Disable snap to chapters";
+"DISABLES_SNAP_TO_CHAPTER_DESC" = "Prevents the scrubbar stick to the chapters.";
+"DISABLES_ENGAGE_PANEL" = "Disables engagement panel in fullscreen";
+"DISABLES_ENGAGE_PANEL_DESC" = "Prevent engagement panel showing in fullscreen.";
"GESTURES" = "Bật điều khiển bằng cử chỉ";
"GESTURES_DESC" = "Vuốt lên hoặc xuống ở cạnh trái/phải của màn hình video để điều chỉnh độ sáng, âm lượng hoặc tốc độ phát.";
"GESTURE_AREA" = "Khu vực cử chỉ";
@@ -136,6 +265,8 @@
"DISABLES_SHOW_REMAINING_DESC" = "Tắt tính năng hiển thị thời lượng còn lại của video khi nhấn vào mốc thời gian.";
"ALWAYS_SHOW_REMAINING" = "Luôn hiển thị thời gian còn lại";
"ALWAYS_SHOW_REMAINING_DESC" = "Đảm bảo rằng thời gian còn lại luôn được hiển thị theo mặc định trong trình phát.";
+"COPY_TIMESTAMP_ON_PAUSE" = "Copy timestamped links";
+"COPY_TIMESTAMP_ON_PAUSE_DESC" = "Allows to copy timestamped link to the clipboard by pressing pause button.";
"SHOW_REMAINING_EXTRA" = "Hiển thị thời gian dự kiến";
"SHOW_REMAINING_EXTRA_DESC" = "Thêm thời gian kết thúc dự kiến vào nhãn trên trình phát video.";
"HIDE_FULLSCREEN_ACTIONS" = "Ẩn các hành động Toàn màn hình";
@@ -148,93 +279,97 @@
"HIDE_CONTENT_WARNING_DESC" = "Tự động bỏ qua thông báo cảnh báo nội dung nhạy cảm trong một số video.";
"AUTO_FULLSCREEN" = "Tự động Toàn màn hình";
"AUTO_FULLSCREEN_DESC" = "Tự động chuyển sang chế độ Toàn màn hình khi bắt đầu phát video.";
-"AUTO_DISABLES_CAPTION" = "Tự tắt Phụ đề (CC)";
-"AUTO_DISABLES_CAPTION_DESC" = "Tự động tắt phụ đề (CC) mỗi khi một video mới bắt đầu phát.";
"PORTRAIT_FULLSCREEN" = "Toàn màn hình dọc";
"PORTRAIT_FULLSCREEN_DESC" = "Bật hỗ trợ chế độ toàn màn hình theo chiều dọc.";
"OLD_QUALITY_PICKER" = "Sử dụng menu chất lượng video kiểu cũ";
"OLD_QUALITY_PICKER_DESC" = "Đưa menu chọn chất lượng video về phiên bản cũ (liệt kê tất cả độ phân giải).";
"EXTRA_SPEED" = "Tùy chọn tốc độ bổ sung";
"EXTRA_SPEED_DESC" = "Thêm các tùy chọn tốc độ phát video lên tới 10x.";
-"DISABLE_HINTS" = "Tắt gợi ý";
-"DISABLE_HINTS_DESC" = "Tắt các gợi ý hướng dẫn và cửa sổ tương tác bật lên";
"FORCE_MINIPLAYER" = "Ép dùng trình phát thu nhỏ (miniplayer)";
"FORCE_MINIPLAYER_DESC" = "Cho phép tất cả các video, bao gồm cả video dành cho trẻ em, phát trong trình phát thu nhỏ khi vuốt đóng video.";
"FORCE_SEEKBAR" = "Luôn hiện thanh tiến trình";
"FORCE_SEEKBAR_DESC" = "Làm cho thanh tiến trình (thanh tua) luôn hiển thị ở mọi lúc khi ở chế độ toàn màn hình.";
-"HIDE_LIKE_BUTTON" = "Ẩn nút Thích";
-"HIDE_LIKE_BUTTON_DESC" = "Ẩn nút Thích trên thanh hành động bên dưới trình phát.";
-"HIDE_DISLIKE_BUTTON" = "Ẩn nút Không thích";
-"HIDE_DISLIKE_BUTTON_DESC" = "Ẩn nút Không thích trên thanh hành động bên dưới trình phát.";
-"HIDE_SHARE_BUTTON" = "Ẩn nút Chia sẻ";
-"HIDE_SHARE_BUTTON_DESC" = "Ẩn nút Chia sẻ trên thanh hành động bên dưới trình phát.";
-"HIDE_DOWNLOAD_BUTTON" = "Ẩn nút Tải xuống";
-"HIDE_DOWNLOAD_BUTTON_DESC" = "Ẩn nút Tải xuống trên thanh hành động bên dưới trình phát.";
-"HIDE_CLIP_BUTTON" = "Ẩn nút Clip";
-"HIDE_CLIP_BUTTON_DESC" = "Ẩn nút Tạo đoạn trích (Clip) trên thanh hành động bên dưới lớp phủ video.";
-"HIDE_REMIX_BUTTON" = "Ẩn nút 'Phối lại'";
-"HIDE_REMIX_BUTTON_DESC" = "Ẩn nút 'Phối lại' trên thanh hành động bên dưới trình phát.";
-"HIDE_SAVE_BUTTON" = "Ẩn nút Lưu";
-"HIDE_SAVE_BUTTON_DESC" = "Ẩn nút Lưu trên thanh hành động bên dưới trình phát.";
+"TAP_TO_SEEK" = "Tap to seek";
+"TAP_TO_SEEK_DESC" = "Quickly jump to any point by tapping the progress bar.";
+"DISABLES_ZOOM" = "Disable free zoom gesture";
+"DISABLES_ZOOM_DESC" = "Disables new free zoom gestures.";
+"USES_24_HOURS_TIME" = "24-hour format";
+"USES_24_HOURS_TIME_DESC" = "Shows end time in 24-hour format.";
+"PAUSE_TWO_FINGERS" = "Pause using two fingers";
+"PAUSE_TWO_FINGERS_DESC" = "Pause/Resume playback by tapping on the screen using two fingers.";
+"HIDE_ACTION_BAR" = "Hide actions bar";
+"HIDE_ACTION_BAR_DESC" = "Removes the bar containing the like, dislike, share buttons etc. below the video player.";
+"HIDE_COMMENTS_SECTION" = "Hide comments section";
+"HIDE_COMMENTS_SECTION_DESC" = "Completely removes the entire comments section.";
+"HIDE_COMMENTS_PREVIEW" = "Hide preview comments";
+"HIDE_COMMENTS_PREVIEW_DESC" = "Removes the preview box for the top comment while keeping the full section expandable.";
+"USE_ANOTHER_MINIPLAYER" = "Uses another miniplayer style";
+"USE_ANOTHER_MINIPLAYER_DESC" = "Uses another miniplayer style redesigned from older YT versions.";
"SHORTS" = "Shorts";
-"HIDE_SHORTS_LIKE_BUTTON" = "Ẩn nút Thích";
-"HIDE_SHORTS_LIKE_BUTTON_DESC" = "Ẩn nút Thích trong trình phát Shorts.";
-"HIDE_SHORTS_DISLIKE_BUTTON" = "Ẩn nút Không thích";
-"HIDE_SHORTS_DISLIKE_BUTTON_DESC" = "Ẩn nút Không thích trong trình phát Shorts.";
-"HIDE_SHORTS_COMMENT_BUTTON" = "Ẩn nút Bình luận";
-"HIDE_SHORTS_COMMENT_BUTTON_DESC" = "Ẩn nút Bình luận trong trình phát Shorts.";
-"HIDE_SHORTS_SHARE_BUTTON" = "Ẩn nút Chia sẻ";
-"HIDE_SHORTS_SHARE_BUTTON_DESC" = "Ẩn nút Chia sẻ trong trình phát Shorts.";
-"HIDE_SHORTS_REMIX_BUTTON" = "Ẩn nút 'Phối lại'";
-"HIDE_SHORTS_REMIX_BUTTON_DESC" = "Ẩn nút 'Phối lại' trong trình phát Shorts.";
-"HIDE_METADATA_BUTTON" = "Ẩn nút nguồn âm thanh";
-"HIDE_METADATA_BUTTON_DESC" = "Ẩn nút hiển thị nguồn âm thanh hoặc thông tin bài hát trong trình phát Shorts.";
+"SHORTS_ONLY" = "Shorts only mode";
+"SHORTS_ONLY_DESC" = "Limits YouTube functionality to viewing Shorts only.";
+"SHORTS_ACTION" = "Shorts action after the video ends";
+"LOOP" = "Loop video";
+"SKIP_TO_NEXT_SHORTS" = "Skip to next video";
+"PAUSE_SHORTS" = "Stop video";
+"REMOVE_LIVE_SHORTS" = "Remove live videos";
+"REMOVE_LIVE_SHORTS_DESC" = "Removes live videos from the feed.";
"HIDE_SHORTS_PRODUCT" = "Ẩn các sản phẩm";
"HIDE_SHORTS_PRODUCT_DESC" = "Ẩn các sản phẩm được gắn thẻ và liên kết mua sắm trong trình phát Shorts.";
"HIDE_SHORTS_RECBAR" = "Ẩn thanh đề xuất";
"HIDE_SHORTS_RECBAR_DESC" = "Ẩn thanh hành động đề xuất trong trình phát Shorts (ví dụ: Thử âm thanh, Mua sắm, Xem trước bình luận).";
-"HIDE_SHORTS_COMMIT" = "Ẩn thanh hoa hồng";
-"HIDE_SHORTS_COMMIT_DESC" = "Ẩn thanh thông báo video có nhận hoa hồng trong trình phát Shorts.";
-"HIDE_SHORTS_SUBSCRIPT_BUTTON" = "Ẩn nút Kênh đăng ký";
-"HIDE_SHORTS_SUBSCRIPT_BUTTON_DESC" = "Ẩn nút Kênh đăng ký ở phía trên trình phát Shorts.";
-"HIDE_SHORTS_LIVE_BUTTON" = "Ẩn nút Phát trực tiếp";
-"HIDE_SHORTS_LIVE_BUTTON_DESC" = "Ẩn nút Phát trực tiếp ở phía trên trình phát Shorts.";
-"HIDE_SHORTS_LENS_BUTTON" = "Ẩn nút Ống kính";
-"HIDE_SHORTS_LENS_BUTTON_DESC" = "Ẩn nút Ống kính ở phía trên trình phát Shorts";
-"HIDE_SHORTS_TRENDS_BUTTON" = "Ẩn nút Xu hướng";
-"HIDE_SHORTS_TRENDS_BUTTON_DESC" = "Ẩn nút Xu hướng ở phía trên trình phát Shorts.";
-"HIDE_SHORTS_TO_VIDEO" = "Ẩn liên kết tới video đầy đủ";
-"HIDE_SHORTS_TO_VIDEO_DESC" = "Ẩn liên kết chuyển hướng từ một video ngắn sang một video đầy đủ trong trình phát Shorts.";
"ENABLES_SHORTS_QUALITY" = "Bật bộ chọn chất lượng Shorts";
"ENABLES_SHORTS_QUALITY_DESC" = "Ép bật menu chọn chất lượng video trong Shorts.";
"SHOW_SHORTS_SEEKBAR" = "Luôn hiện thanh tiến trình";
"SHOW_SHORTS_SEEKBAR_DESC" = "Đảm bảo thanh tiến trình/thanh tua luôn hiển thị ở dưới cùng của trình phát Shorts.";
+"REMOVE_POSTS_SHORTS" = "Remove posts";
+"REMOVE_POSTS_SHORTS_DESC" = "Remove posts from the feed.";
+"SHORTS_FULLSCREEN" = "Pinch to Fullscreen";
+"SHORTS_FULLSCREEN_DESC" = "Manages visibility of the overlay with pinch in and pinch out gestures, displaying Shorts in fullscreen mode.";
"TABBAR" = "Thanh Tab";
"DEFAULT_TAB" = "Tab khởi động mặc định";
-"DEFAULT_TAB_DESC" = "Bạn có thể chọn tab muốn mở khi khởi động ứng dụng.";
-"HOME_NAME" = "Trang chủ";
-"SUBSCRIPT_NAME" = "Kênh đăng ký";
-"LIB_NAME" = "Thư viện (Bạn)";
+"FORSTED_TAB_BAR" = "Translucent tab bar";
+"ENABLED" = "Enabled";
"HIDE_TAB_INDI" = "Ẩn chỉ báo tab";
"HIDE_TAB_INDI_DESC" = "Ẩn tất cả các chỉ báo tab trên thanh tab.";
"HIDE_TAB_LABELS" = "Ẩn nhãn tab";
"HIDE_TAB_LABELS_DESC" = "Ẩn tất cả nhãn tab trên thanh tab.";
-"HIDE_HOME_TAB" = "Ẩn tab Trang chủ";
-"HIDE_HOME_TAB_DESC" = "Ẩn tab Trang chủ trên thanh tab.";
-"HIDE_SHORTS_TAB" = "Ẩn tab Shorts";
-"HIDE_SHORTS_TAB_DESC" = "Ẩn tab Shorts trên thanh tab.";
-"HIDE_CREATE_BUTTON" = "Ẩn nút Tạo";
-"HIDE_CREATE_BUTTON_DESC" = "Ẩn nút Tạo trên thanh tab.";
-"HIDE_SUBSCRIPT_TAB" = "Ẩn tab Kênh đăng ký";
-"HIDE_SUBSCRIPT_TAB_DESC" = "Ẩn tab Kênh đăng ký trên thanh tab.";
+"HOME_TAB" = "Trang chủ";
+"SHORTS_TAB" = "Shorts";
+"CREATE_TAB" = "Create";
+"SUBSCRIPTIONS_TAB" = "Kênh đăng ký";
+"LIBRARY_TAB" = "Thư viện (Bạn)";
+"HISTORY_TAB" = "History";
+"GAMING_TAB" = "Gaming";
+"SPORTS_TAB" = "Sports";
+"NOTI_TAB" = "Notifications";
+"NEWS_TAB" = "News";
+"MUSIC_TAB" = "Music";
+"WATCH_LATER_TAB" = "Watch later";
+"PLAYLIST_TAB" = "Playlists";
+"LIKE_TAB" = "Liked videos";
+"LIVE_TAB" = "Live";
+"POST_TAB" = "Posts";
+"VIDEO_TAB" = "Your videos";
+"MOVIE_TAB" = "Movies";
+"COURSE_TAB" = "Courses";
+"MINIGAME_TAB" = "Playables";
+"MANAGE_TABS" = "Manage tabs";
+"MANAGE_TABS_DESC" = "Reorder and toggle tabs";
+"TAB_REORDER_HINT" = "Drag to reorder. Toggle to show/hide.\nTab settings are also accessible by long-pressing the first tab in the tab bar.";
+"TAB_LIMIT" = "Tab limit";
+"TAB_LIMIT_DESC" = "Only 6 tabs are allowed.";
+"ZERO_TAB_DESC" = "At least 1 tab has to be enabled.";
"MISCELLANEOUS" = "Khác";
"BACKGROUND_PLAYBACK" = "Phát trong nền";
"BACKGROUND_PLAYBACK_DESC" = "Cho phép video phát dưới nền";
"DISABLES_SHORTS_PIP" = "Tắt PiP của Shorts";
"DISABLES_SHORTS_PIP_DESC" = "Thử tắt tính năng Hình-trong-hình (PiP) của Shorts, có thể không hoạt động 100%.";
+"DISABLE_HINTS" = "Tắt gợi ý";
+"DISABLE_HINTS_DESC" = "Tắt các gợi ý hướng dẫn và cửa sổ tương tác bật lên";
"BLOCK_UPGRADE_DIALOGS" = "Chặn hộp thoại nâng cấp";
"BLOCK_UPGRADE_DIALOGS_DESC" = "Chặn các hộp thoại của YouTube yêu cầu bạn nâng cấp ứng dụng.";
"ARE_YOU_THERE_DIALOG" = "Ẩn hộp thoại 'Bạn có ở đó không?'";
@@ -247,7 +382,109 @@
"DISABLES_SNACK_BAR_DESC" = "Tắt thanh thông báo (snack bar) khi nhấn một số nút hoặc thực hiện thao tác trong ứng dụng.";
"HIDE_STARTUP_ANIMATIONS" = "Ẩn hoạt ảnh khởi động của Youtube";
"HIDE_STARTUP_ANIMATIONS_DESC" = "Ẩn các hoạt ảnh khởi động của YouTube khi mở ứng dụng.";
-"HIDE_PLAY_IN_NEXT_QUEUE" = "Ẩn 'Phát tiếp theo trong danh sách chờ'";
-"HIDE_PLAY_IN_NEXT_QUEUE_DESC" = "Ẩn tùy chọn 'Phát tiếp theo trong danh sách chờ' trong menu nổi.";
"HIDE_LIKE_DISLIKE_VOTES" = "Ẩn thông báo Thích/Không thích";
"HIDE_LIKE_DISLIKE_VOTES_DESC" = "Ẩn các thông báo bật lên khi bạn thích/không thích video.";
+"FLYOUT_MENU" = "Flyout menu";
+"REMOVE_PLAY_IN_NEXT_QUEUE_OPTION" = "Removes 'Play next in queue' option";
+"REMOVE_PLAY_IN_NEXT_QUEUE_OPTION_DESC" = "Removes 'Play next in queue' option from the flyout menu.";
+"REMOVE_DOWNLOAD_OPTION" = "Removes 'Download video' option";
+"REMOVE_DOWNLOAD_OPTION_DESC" = "Removes 'Download video' option from the flyout menu.";
+"REMOVE_WATCH_LATER_OPTION" = "Removes 'Save to Watch later' option";
+"REMOVE_WATCH_LATER_OPTION_DESC" = "Removes 'Save to Watch Later' option from the flyout menu.";
+"REMOVE_SAVE_OPTION" = "Removes 'Save to playlist' option";
+"REMOVE_SAVE_OPTION_DESC" = "Removes 'Save to playlist' option from the flyout menu.";
+"REMOVE_REMOVE_FROM_PLAYLIST_OPTION" = "Removes 'Remove from playlist' option";
+"REMOVE_REMOVE_FROM_PLAYLIST_OPTION_DESC" = "Removes 'Remove from playlist' option from the flyout menu.";
+"REMOVE_SHARE_OPTION" = "Removes 'Share' option";
+"REMOVE_SHARE_OPTION_DESC" = "Removes 'Share' option from the flyout menu.";
+"REMOVE_NOT_INTERESTED_OPTION" = "Removes 'Not interested' option";
+"REMOVE_NOT_INTERESTED_OPTION_DESC" = "Removes 'Not interested' option from the flyout menu.";
+"REMOVE_DONT_RECOMMEND_OPTION" = "Removes 'Don't recommend channel' option";
+"REMOVE_DONT_RECOMMEND_OPTION_DESC" = "Removes 'Don't recommend channel' option from the flyout menu.";
+"REMOVE_INFO_OPTION" = "Removes 'Why are you seeing this?' option";
+"REMOVE_INFO_OPTION_DESC" = "Removes 'Why are you seeing this?' option from the flyout menu.";
+"REMOVE_FILTER_OPTION" = "Removes 'Search filters' option";
+"REMOVE_FILTER_OPTION_DESC" = "Removes 'Search filters' option from the flyout menu.";
+"REMOVE_REPORT_OPTION" = "Removes 'Report' option";
+"REMOVE_REPORT_OPTION_DESC" = "Removes 'Report' option from the flyout menu.";
+"REMOVE_YOUTUBE_MUSIC_OPTION" = "Removes 'Listen with YouTube Music' option";
+"REMOVE_YOUTUBE_MUSIC_OPTION_DESC" = "Removes 'Listen with YouTube Music' option from the flyout menu.";
+"REMOVE_FEED_BACK_OPTION" = "Removes 'Send feedback' option";
+"REMOVE_FEED_BACK_OPTION_DESC" = "Removes 'Send feedback' option from the flyout menu.";
+"REMOVE_CAST_OPTION" = "Removes 'Watch on TV' option";
+"REMOVE_CAST_OPTION_DESC" = "Removes 'Watch on TV' option from the flyout menu.";
+"REMOVE_SHUFFLE_OPTION" = "Removes 'Shuffle' option";
+"REMOVE_SHUFFLE_OPTION_DESC" = "Removes 'Shuffle' option from the flyout menu.";
+"REMOVE_UN_SUB_OPTION" = "Removes 'Unsubscribe' option";
+"REMOVE_UN_SUB_OPTION_DESC" = "Removes 'Unsubscribe' option from the flyout menu.";
+"REMOVE_HIDE_FROM_PLAYLIST_OPTION" = "Removes 'Hide video from playlist' option";
+"REMOVE_HIDE_FROM_PLAYLIST_OPTION_DESC" = "Removes 'Hide video from playlist' option from the flyout menu.";
+"REMOVE_HELP_OPTION" = "Removes 'Help & feedback' option";
+"REMOVE_HELP_OPTION_DESC" = "Removes 'Help & feedback' option from the flyout menu.";
+"REMOVE_NOTIFY_OPTION" = "Removes 'Notify me' option";
+"REMOVE_NOTIFY_OPTION_DESC" = "Removes 'Notify me' option from the flyout menu.";
+"REMOVE_CLEARSCREEN_OPTION" = "Removes 'Clear Screen' option";
+"REMOVE_CLEARSCREEN_OPTION_DESC" = "Removes 'Clear Screen' option from the flyout menu.";
+"HIDE_COMMU_GUIDE" = "Remove guidelines";
+"HIDE_COMMU_GUIDE_DESC" = "Removes pinned guidelines from the comments section.";
+"DISABLES_RTL" = "Disable RTL formatting";
+"DISABLES_RTL_DESC" = "Forcefully displays text in left-to-right (LTR) format for languages that are initially displayed in right-to-left (RTL).";
+"DEVICE_UI" = "Interface style";
+
+"SB_DESCRIPTION" = "Skip sponsored segments automatically using community-submitted data.";
+"SB_ENABLE" = "Enable SponsorBlock";
+"SB_ENABLE_DESC" = "Activate the SponsorBlock extension to skip sponsored segments.";
+"SB_SHOW_BUTTON" = "Show overlay button";
+"SB_SHOW_BUTTON_DESC" = "Display a SponsorBlock toggle button in the player overlay.";
+"SB_SHOW_NOTIFICATIONS" = "Show notifications";
+"SB_SHOW_NOTIFICATIONS_DESC" = "Display notifications when segments are skipped. Notifications for manual-skip categories are always shown.";
+"SB_HAPTIC_FEEDBACK" = "Haptic feedback";
+"SB_HAPTIC_FEEDBACK_DESC" = "Provide haptic feedback when a segment is skipped.";
+"SB_SEGMENTS_IN_PLAYER" = "Segments in player";
+"SB_SEGMENTS_IN_PLAYER_DESC" = "Display colored segments on the progress bar of the main video player.";
+"SB_SEGMENTS_IN_FEED" = "Segments in feed";
+"SB_SEGMENTS_IN_FEED_DESC" = "Display colored segments on the progress bar of players in the feed.";
+"SB_SEGMENTS_IN_MINIPLAYER" = "Segments in mini-player";
+"SB_SEGMENTS_IN_MINIPLAYER_DESC" = "Display colored segments on the progress bar of the mini-player.";
+"SB_SHOW_DURATION" = "Show duration without segments";
+"SB_SHOW_DURATION_DESC" = "Display the total video duration excluding skippable segments.";
+"SB_CATEGORIES_HEADER" = "Segment categories";
+"SB_SEGMENT_COLOR" = "Segment color";
+
+"SB_ACTION_DISABLE" = "Disable";
+"SB_ACTION_AUTO_SKIP" = "Auto-skip";
+"SB_ACTION_ASK" = "Ask before skipping";
+"SB_ACTION_DISPLAY" = "Show on seek bar only";
+"SB_ACTION_SKIP_TO" = "Skip to segment";
+
+"SB_CAT_sponsor" = "Sponsor";
+"SB_CAT_sponsor_DESC" = "Paid promotion for a product or service not directly related to the creator.";
+"SB_CAT_intro" = "Intro";
+"SB_CAT_intro_DESC" = "Intermission or intro animation typically seen at the start of a video.";
+"SB_CAT_outro" = "Endcards";
+"SB_CAT_outro_DESC" = "Endcards, credits, or outros at the end of the video.";
+"SB_CAT_interaction" = "Interaction";
+"SB_CAT_interaction_DESC" = "Reminders to like, subscribe, or follow on social media.";
+"SB_CAT_selfpromo" = "Self-promotion";
+"SB_CAT_selfpromo_DESC" = "Unpaid promotion of the creator's own products, merchandise, or other content.";
+"SB_CAT_music_offtopic" = "Non-music";
+"SB_CAT_music_offtopic_DESC" = "Non-music sections in music videos (e.g. talking, introductions).";
+"SB_CAT_preview" = "Preview";
+"SB_CAT_preview_DESC" = "Recap or preview of content from another video or previous episode.";
+"SB_CAT_hook" = "Hook/Greetings";
+"SB_CAT_hook_DESC" = "Narrated trailers for the upcoming video, greetings and goodbyes.";
+"SB_CAT_poi_highlight" = "Highlight";
+"SB_CAT_poi_highlight_DESC" = "The key moment or highlight point of the video. Use 'Skip to' to jump directly to it.";
+"SB_CAT_filler" = "Filler";
+"SB_CAT_filler_DESC" = "Tangential content, jokes, or filler that is not directly related to the main topic.";
+
+"SB_SKIPPED" = "%@ segment has been skipped";
+"SB_DETECTED" = "%@ segment detected.\nWould you like to skip the segment?";
+"SB_SKIP_NOW" = "Skip";
+"SB_UNSKIP" = "Unskip";
+"SB_JUMPED_TO_HIGHLIGHT" = "Jumped to highlight";
+"SB_JUMP_TO_HIGHLIGHT" = "Highlight available. Jump to the point?";
+"SB_SEGMENT_COLOR_SUFFIX" = "Segment color";
+"SB_SECTION_MAIN" = "Main";
+"SB_SKIP_ALERT_DURATION" = "Skip alert duration";
+"SB_UNSKIP_ALERT_DURATION" = "Unskip alert duration";
\ No newline at end of file
diff --git a/layout/Library/Application Support/YouMod.bundle/zh-CN.lproj/Localizable.strings b/layout/Library/Application Support/YouMod.bundle/zh-CN.lproj/Localizable.strings
index bccaf550..ad7bec72 100644
--- a/layout/Library/Application Support/YouMod.bundle/zh-CN.lproj/Localizable.strings
+++ b/layout/Library/Application Support/YouMod.bundle/zh-CN.lproj/Localizable.strings
@@ -1,11 +1,18 @@
-"NOTE" = "NOTE: Some hiding features may leave a blank box.";
"SETTINGS" = "Settings";
+"INTERFACE" = "Interface";
+
+"LOOP_DISABLED" = "Loop disabled";
+"LOOP_ENABLED" = "Loop enabled";
"NEW_ISSUES" = "Report issues or request new features";
"NEW_ISSUES_DESC" = "Found bugs or want to request new features? Tap here.";
"SOURCE_CODES" = "Source codes";
"SOURCE_CODES_DESC" = "Take a look at the codes!";
+"EXTRA" = "Extra";
+"FIX_PLAYBACK_ISSUES" = "Fix playback issues (Temporary)";
+"FIX_PLAYBACK_ISSUES_DESC" = "Try to fix video playback issues for some users. Turning this on will make the player automatically reloads itself everytime it errors or fails to play the video.";
+
"PERFER_HEADER" = "YouMod perferences";
"PERFER" = "Perferences";
"IMPORT" = "Import settings";
@@ -24,6 +31,7 @@
"ERROR_NO_KEYS_IMPORT" = "No available keys to import";
"ERROR_NO_KEYS_EXPORT" = "No available keys to export";
"OVERRIDE" = "This will override the current settings. Do you want to continue?";
+"DISABLED" = "Disabled";
"CACHE" = "Cache management";
"CLEARCACHE" = "Clear cache";
@@ -32,12 +40,73 @@
"AUTO_CLEARCACHE_DESC" = "Automatically clears the app cache on startup.";
"DOWNLOADING" = "Downloading";
-"DOWNLOAD_MANAGER" = "Download Manager";
-"DOWNLOAD_MANAGER_DESC" = "Replaces YouTube's video downloader with YouMod one. With ablity to downloading videos, audios, captions, and thumbnails";
+"DOWNLOAD_MANAGER" = "Download manager";
+"DOWNLOAD_MANAGER_DESC" = "Replaces YouTube's video downloader with YouMod one. With ablity to downloading videos, audios, captions, and thumbnails.";
"DOWNLOAD_SAVE_PHOTOS" = "Automatically save to Photos";
"DOWNLOAD_SAVE_PHOTOS_DESC" = "Automatically saves downloaded videos and images directly to your Photos app.";
-"DOWNLOAD_DRC_AUDIO" = "Prefer DRC Audio";
-"DOWNLOAD_DRC_AUDIO_DESC" = "Prioritizes downloading audio tracks with Dynamic Range Compression for more consistent volume levels.";
+"ADD_SHORTS_DOWNLOAD" = "Adds a download button to Shorts";
+"ADD_SHORTS_DOWNLOAD_DESC" = "Adds a download button to Shorts video overlay.";
+"USE_ORIG_AUDIO" = "Automatically uses original audio track";
+"USE_ORIG_AUDIO_DESC" = "Automatically uses original audio track instead of showing a dialog.";
+"DOWNLOAD_SHORTS" = "Download Shorts";
+"DOWNLOAD_SHORTS_DESC" = "Choose Shorts quality";
+"COPY_VID_INFO" = "Copy video informations";
+"COPY_VID_INFO_DESC" = "Choose what to copy";
+"COPY_ALL_VID_INFO" = "Copy all";
+"COPY_ALL_VID_INFO_DESC" = "Copy all video informations";
+"COPY_AUTHOR" = "Copy author";
+"COPY_AUTHOR_DESC" = "Copy the video author";
+"COPY_TITLE" = "Copy title";
+"COPY_TITLE_DESC" = "Copy the video title";
+"COPY_DESCRIPTION" = "Copy description";
+"COPY_DESCRIPTION_DESC" = "Copy the video description";
+"COPY_URL" = "Copy video link";
+"COPY_URL_TIMESTAMP" = "Copy video link with timestamp";
+"URL_COPIED" = "Copied video link";
+"URL_TIMESTAMP_COPIED" = "Copied video link with timestamp";
+"ERROR_VIDEOID" = "Cannot get the video ID";
+"ERROR_ADS" = "Cannot copy video link while playing ads";
+"COPIED_AUTHOR" = "Copied video author";
+"COPIED_TITLE" = "Copied video title";
+"COPIED_DESCRIPTION" = "Copied video description";
+"SAVE_THUMBNAIL" = "Save thumbnail";
+"SAVE_THUMBNAIL_DESC" = "Save to Photos";
+"DOWNLOAD_CAPTIONS" = "Download captions";
+"DOWNLOAD_CAPTIONS_DESC" = "Save subtitles as .VTT file";
+"DOWNLOAD_AUDIO" = "Download audio";
+"DOWNLOAD_AUDIO_DESC" = "Download audio as .m4a file";
+"DOWNLOAD_VIDEO" = "Download video";
+"DOWNLOAD_VIDEO_DESC" = "Choose video quality";
+"OPEN_VID_BEFORE" = "Open a video before using the download manager";
+"NO_CAPTIONS_URL" = "No valid caption URLs found";
+"NO_AUDIO_URL" = "No audio URL found";
+"NO_STREAM_URL" = "No stream URL found";
+"CAPTIONS_FAILED" = "Failed to download captions";
+"NO_CAPTIONS" = "No captions available for this video";
+"NO_AUDIO_STREAM_FOUND" = "No audio streams found";
+"NO_VID_AUDIO_STREAM_FOUND" = "No video/audio streams found";
+"COPIED_VID_INFO" = "Copied all video informations";
+"SAVED_TO_PHOTOS" = "Saved to Photos";
+"SAVE_FAILED" = "Save failed";
+"PHOTO_ACCESS_DENINED" = "Photos access denied";
+"THUMBNAIL_FAILED" = "Thumbnail failed";
+"DOWNLOADING_THUMBNAIL" = "Downloading thumbnail...";
+"DOWNLOADING_AUDIO" = "Downloading audio...";
+"DOWNLOADING_VIDEO" = "Downloading video...";
+"DOWNLOADING_CAPTIONS" = "Downloading captions...";
+"NO_THUMBNAIL_FOUND" = "No thumbnail found";
+"DOWNLOAD_FAILED" = "Download failed";
+"AUDIO_SAVED" = "Audio saved";
+"DOWNLOAD_COMPLETED" = "Download completed";
+"CANNOT_SAVE_TO_PHOTOS" = "Cannot save to Photos";
+"FINA_VIDEO" = "Finalizing video...";
+"MERGING_VID_FALLBACK" = "Merging video with fallback...";
+"MERGING_VID" = "Merging video...";
+"CONVERTING" = "Converting to";
+"ALREADY_DOWNLOADING" = "Already downloading";
+"DOWNLOAD_CANCELLED" = "Download cancelled";
+"CANCEL" = "Cancel";
+"TRIMMING_AUDIO" = "Trimming audio...";
"APPEARANCE" = "Appearance";
"OLED_THEME" = "OLED dark mode";
@@ -46,6 +115,8 @@
"OLED_KEYBOARD_DESC" = "Make the keyboard uses a pure black (OLED) color for dark theme.";
"NAVBAR" = "Navigation bar";
+"STICKY_NAVBAR" = "Sticky Navigation bar";
+"STICKY_NAVBAR_DESC" = "Pins the Navigation bar so that it remains visible when scrolling down.";
"HIDE_YT_LOGO" = "Hide YouTube logo";
"HIDE_YT_LOGO_DESC" = "Hide YouTube logo in the navigation bar.";
"PREMIUM_LOGO" = "Use YouTube Premium logo";
@@ -62,34 +133,76 @@
"FEED" = "Feed";
"HIDE_SUBBAR" = "Hide subbar";
"HIDE_SUBBAR_DESC" = "Hide subbar at the top of the feed.";
+"HIDE_HORI_SHELF" = "Remove horizontal feeds";
+"HIDE_HORI_SHELF_DESC" = "Removes all horizontal feeds (News, Continue Watching, Posts collection, etc.) from the feed, except Playables.";
"HIDE_MUSIC_PLAYLISTS" = "Hide music playlist generator";
"HIDE_MUSIC_PLAYLISTS_DESC" = "Hide music playlist generator (Select music tunes in three) in the feed.";
"HIDE_FEED_POST" = "Hide posts";
"HIDE_FEED_POST_DESC" = "Hide posts in feed.";
+"HIDE_SURVEYS" = "Hide surveys";
+"HIDE_SURVEYS_DESC" = "Hide video surveys that YouTube shows sometimes.";
"HIDE_SHORTS_SHELF" = "Hide Shorts shelf";
"HIDE_SHORTS_SHELF_DESC" = "Hide Shorts shelf in the feed, including in the Subscriptions tab.";
+"KEEP_SHORTS_SUBSCRIPT" = "Keep Shorts in Subscriptions";
+"KEEP_SHORTS_SUBSCRIPT_DESC" = "Prevents removing Shorts from Subscriptions tab.";
"HIDE_SEARCH_HISTORY" = "Hide search history";
"HIDE_SEARCH_HISTORY_DESC" = "Hide previous search history and suggestions when using the search bar.\nNOTE: Your search history will still be accessible in your other YouTube clients.";
-"HIDE_SUB_BUTTON" = "Hide Subscribe button";
-"HIDE_SUB_BUTTON_DESC" = "Hides the subscribe button in channels and video descriptions.";
-"HIDE_SHOP_BUTTON" = "Hide Shopping button";
-"HIDE_SHOP_BUTTON_DESC" = "Hides the shopping/store button in channels descriptions.";
-"HIDE_MEMBER_BUTTON" = "Hide Membership button";
-"HIDE_MEMBER_BUTTON_DESC" = "Hides the 'Join' button in channels and video descriptions.";
+"HIDE_PLAYABLES" = "Remove playables";
+"HIDE_PLAYABLES_DESC" = "Removes playables (mini-games) from the feed.";
"PLAYER" = "Player";
+"PLAYBACK_SPEED" = "Playback speed";
+"CONTROL_CENTER" = "Control Center";
+"PLAYER_ACTIONS" = "Player actions";
+"GESTURE_HEADER" = "Gestures";
+"QUALITY_WIFI" = "Playback quality on WiFi";
+"QUALITY_WIFI_DESC" = "Automatically applies the selected quality when streaming over Wi-Fi.";
+"QUALITY_CELLULAR" = "Playback quality on Cellular";
+"QUALITY_CELLULAR_DESC" = "Automatically applies the selected quality when streaming over mobile data.";
+"QUALITY_LOW_POWER" = "Playback quality on Power saver mode";
+"QUALITY_LOW_POWER_DESC" = "Automatically applies the selected quality when when Low power mode is enabled on your device.";
+"DEFAULT" = "Default";
+"BEST" = "Best";
+"AUDIO_TRACK" = "Preferred audio track";
+"ORIGINAL" = "Original";
+"SELECT_MANUALLY" = "Select manually";
+"AUDIO_TRACK_SELECT" = "Selected audio track";
+"AUDIO_TRACK_SELECT_DESC" = "Specifies the preferred audio track language to use for playback, if available.";
+"NO_AUTO_DUBBED" = "Uses original audio track";
+"NO_AUTO_DUBBED_DESC" = "Uses original audio track if the preferred language track is auto-dubbed or not found.";
+"CAPTION_TRACK" = "Preferred captions";
+"CAPTION_TRACK_SELECT" = "Selected captions";
+"CAPTION_TRACK_SELECT_DESC" = "Specifies the preferred captions language to display, if available.";
+"DISABLES_CAPTION_TRACK" = "Automatically disable captions";
+"DISABLES_CAPTION_TRACK_DESC" = "Automatically disable captions if the preferred language is auto-generated or not found.";
+"DEFAULT_SPEED" = "Default playback rate";
+"DEFAULT_SPEED_DESC" = "Overrides the default playback speed.";
+"SPEED_BUTTON" = "Speed controls button";
+"SPEED_BUTTON_DESC" = "Displays a speed controls button on overlay.";
+"MUTE_BUTTON" = "Mute button";
+"MUTE_BUTTON_DESC" = "Displays a button to mute or unmute the volume on the overlay.";
+"SHARE_BUTTON" = "Share button";
+"SHARE_BUTTON_DESC" = "Displays a button to share the video link on the overlay.";
+"LOOP_BUTTON" = "Loop button";
+"LOOP_BUTTON_DESC" = "Displays a button to loop the video on the overlay.";
+"HOLD_TO_SPEED" = "Hold to speed";
+"HOLD_TO_SPEED_DESC" = "Uses custom hold to speed playback rate.";
"HIDE_AUTOPLAY" = "Hide autoplay switch";
"HIDE_AUTOPLAY_DESC" = "Hide autoplay switch in the video overlay.";
"HIDE_CAPTIONS_BUTTON" = "Hide captions button";
"HIDE_CAPTIONS_BUTTON_DESC" = "Hide captions button in the video overlay.";
"HIDE_CAST_BUTTON_PLAYER" = "Hide cast button";
"HIDE_CAST_BUTTON_PLAYER_DESC" = "Hide cast button in the video overlay.";
-"HIDE_PREV_BUTTON" = "Hide previous/back button";
-"HIDE_PREV_BUTTON_DESC" = "Hide previous/back button in the video overlay.";
-"HIDE_NEXT_BUTTON" = "Hide next/skip button";
-"HIDE_NEXT_BUTTON_DESC" = "Hide next/skip button in the video overlay.";
+"HIDE_NEXT_AND_PREV_BUTTON" = "Hide previous and next buttons";
+"HIDE_NEXT_AND_PREV_BUTTON_DESC" = "Hide previous and next buttons in the video overlay.";
"REPLACE_PREVNEXT_BUTTONS" = "Replace Previous/Next buttons";
"REPLACE_PREVNEXT_BUTTONS_DESC" = "Replaces the Previous and Next video buttons in the player with Rewind and Fast Forward buttons.";
+"SKIP_BACKWARD" = "Skip backward";
+"SKIP_BACKWARD_DESC" = "Replaces the previous track button in Control Center with a skip backward button by a set number of seconds.";
+"SKIP_FORWARD" = "Skip forward";
+"SKIP_FORWARD_DESC" = "Replaces the next track button in Control Center with a skip forward button by a set number of seconds.";
+"REWIND_SECONDS" = "Rewind seconds";
+"FORWARD_SECONDS" = "Fast-forward seconds";
"REMOVE_DARK_OVERLAY" = "Remove dark overlay";
"REMOVE_DARK_OVERLAY_DESC" = "Remove dark overlay from the video overlay.";
"HIDE_END_SCREEN" = "Hide endscreen cards";
@@ -102,6 +215,12 @@
"HIDE_PAID_OVERLAY_DESC" = "Hide 'Includes paid promotion' in the video overlay.";
"HIDE_WATERMARK" = "Hide watermark";
"HIDE_WATERMARK_DESC" = "Hide channel watermark at the bottom of the video overlay.";
+"PAUSE_ON_OVERLAY" = "Pause on overlay";
+"PAUSE_ON_OVERLAY_DESC" = "Sets playback on pause if overlay appears.";
+"DISABLES_SNAP_TO_CHAPTER" = "Disable snap to chapters";
+"DISABLES_SNAP_TO_CHAPTER_DESC" = "Prevents the scrubbar stick to the chapters.";
+"DISABLES_ENGAGE_PANEL" = "Disables engagement panel in fullscreen";
+"DISABLES_ENGAGE_PANEL_DESC" = "Prevent engagement panel showing in fullscreen.";
"GESTURES" = "Enable gestures controls";
"GESTURES_DESC" = "Swipe up or down on the left/right edges of the video screen to adjust brightness, volume, or playback speed.";
"GESTURE_AREA" = "Gesture activation area";
@@ -136,6 +255,8 @@
"DISABLES_SHOW_REMAINING_DESC" = "Disable the feature that shows how much time is left in a video when tapping the timestamp.";
"ALWAYS_SHOW_REMAINING" = "Always show remaining time";
"ALWAYS_SHOW_REMAINING_DESC" = "Ensure the remaining time is always displayed by default in the player overlay.";
+"COPY_TIMESTAMP_ON_PAUSE" = "Copy timestamped links";
+"COPY_TIMESTAMP_ON_PAUSE_DESC" = "Allows to copy timestamped link to the clipboard by pressing pause button.";
"SHOW_REMAINING_EXTRA" = "Show extra remaining time";
"SHOW_REMAINING_EXTRA_DESC" = "Adds the estimated finish time to the label in the video overlay.";
"HIDE_FULLSCREEN_ACTIONS" = "Hide fullscreen actions";
@@ -148,93 +269,95 @@
"HIDE_CONTENT_WARNING_DESC" = "Automatically skip the sensitive content warning alert in some vidoes.";
"AUTO_FULLSCREEN" = "Auto fullscreen";
"AUTO_FULLSCREEN_DESC" = "Automatically enter fullscreen mode when starting video playback.";
-"AUTO_DISABLES_CAPTION" = "Auto disable captions";
-"AUTO_DISABLES_CAPTION_DESC" = "Automatically turns off closed captions (CC) whenever a new video starts playing.";
"PORTRAIT_FULLSCREEN" = "Portrait fullscren mode";
"PORTRAIT_FULLSCREEN_DESC" = "Enables portrait fullscreen mode support.";
"OLD_QUALITY_PICKER" = "Use old quality picker";
"OLD_QUALITY_PICKER_DESC" = "Reverts the video quality selection menu to the classic version (lists all resolutions immediately).";
"EXTRA_SPEED" = "Extra speed options";
"EXTRA_SPEED_DESC" = "Adds more video playback speed options up to 10x.";
-"DISABLE_HINTS" = "Disable hints";
-"DISABLE_HINTS_DESC" = "Disable the tutorial hints and interactive pop-ups.";
"FORCE_MINIPLAYER" = "Force miniplayer";
"FORCE_MINIPLAYER_DESC" = "Allows all videos, including those made for kids, to play in the miniplayer when dismissing the player.";
"FORCE_SEEKBAR" = "Always show progress bar";
"FORCE_SEEKBAR_DESC" = "Makes the progress bar (seekbar) remains visible at all times while in fullscreen mode.";
-"HIDE_LIKE_BUTTON" = "Hide Like button";
-"HIDE_LIKE_BUTTON_DESC" = "Hide Like button in the action bar under the video overlay.";
-"HIDE_DISLIKE_BUTTON" = "Hide Dislike button";
-"HIDE_DISLIKE_BUTTON_DESC" = "Hide Dislike button in the action bar under the video overlay.";
-"HIDE_SHARE_BUTTON" = "Hide Share button";
-"HIDE_SHARE_BUTTON_DESC" = "Hide Share button in the action bar under the video overlay.";
-"HIDE_DOWNLOAD_BUTTON" = "Hide Download button";
-"HIDE_DOWNLOAD_BUTTON_DESC" = "Hide Download button in the action bar under the video overlay.";
-"HIDE_CLIP_BUTTON" = "Hide Clip button";
-"HIDE_CLIP_BUTTON_DESC" = "Hide Clip button in the action bar under the video overlay.";
-"HIDE_REMIX_BUTTON" = "Hide Remix button";
-"HIDE_REMIX_BUTTON_DESC" = "Hide Remix button in the action bar under the video overlay.";
-"HIDE_SAVE_BUTTON" = "Hide Save button";
-"HIDE_SAVE_BUTTON_DESC" = "Hide Save button in the action bar under the video overlay.";
+"TAP_TO_SEEK" = "Tap to seek";
+"TAP_TO_SEEK_DESC" = "Quickly jump to any point by tapping the progress bar.";
+"DISABLES_ZOOM" = "Disable free zoom gesture";
+"DISABLES_ZOOM_DESC" = "Disables new free zoom gestures.";
+"USES_24_HOURS_TIME" = "24-hour format";
+"USES_24_HOURS_TIME_DESC" = "Shows end time in 24-hour format.";
+"PAUSE_TWO_FINGERS" = "Pause using two fingers";
+"PAUSE_TWO_FINGERS_DESC" = "Pause/Resume playback by tapping on the screen using two fingers.";
+"HIDE_ACTION_BAR" = "Hide actions bar";
+"HIDE_ACTION_BAR_DESC" = "Removes the bar containing the like, dislike, share buttons etc. below the video player.";
+"HIDE_COMMENTS_SECTION" = "Hide comments section";
+"HIDE_COMMENTS_SECTION_DESC" = "Completely removes the entire comments section.";
+"HIDE_COMMENTS_PREVIEW" = "Hide preview comments";
+"HIDE_COMMENTS_PREVIEW_DESC" = "Removes the preview box for the top comment while keeping the full section expandable.";
+"USE_ANOTHER_MINIPLAYER" = "Uses another miniplayer style";
+"USE_ANOTHER_MINIPLAYER_DESC" = "Uses another miniplayer style redesigned from older YT versions.";
"SHORTS" = "Shorts";
-"HIDE_SHORTS_LIKE_BUTTON" = "Hide Like button";
-"HIDE_SHORTS_LIKE_BUTTON_DESC" = "Hide the Like button in the Shorts player.";
-"HIDE_SHORTS_DISLIKE_BUTTON" = "Hide Dislike button";
-"HIDE_SHORTS_DISLIKE_BUTTON_DESC" = "Hide the Dislike button in the Shorts player.";
-"HIDE_SHORTS_COMMENT_BUTTON" = "Hide Comment button";
-"HIDE_SHORTS_COMMENT_BUTTON_DESC" = "Hide the Comment button in the Shorts player.";
-"HIDE_SHORTS_SHARE_BUTTON" = "Hide Share button";
-"HIDE_SHORTS_SHARE_BUTTON_DESC" = "Hide the Share button in the Shorts player.";
-"HIDE_SHORTS_REMIX_BUTTON" = "Hide Remix button";
-"HIDE_SHORTS_REMIX_BUTTON_DESC" = "Hide the Remix button in the Shorts player.";
-"HIDE_METADATA_BUTTON" = "Hide sound metadata button";
-"HIDE_METADATA_BUTTON_DESC" = "Hide the button that shows the sound source or music metadata in the Shorts player.";
+"SHORTS_ACTION" = "Shorts action after the video ends";
+"LOOP" = "Loop video";
+"SKIP_TO_NEXT_SHORTS" = "Skip to next video";
+"PAUSE_SHORTS" = "Stop video";
+"REMOVE_LIVE_SHORTS" = "Remove live videos";
+"REMOVE_LIVE_SHORTS_DESC" = "Removes live videos from the feed.";
"HIDE_SHORTS_PRODUCT" = "Hide products";
"HIDE_SHORTS_PRODUCT_DESC" = "Hide tagged products and shopping links in the Shorts overlay.";
"HIDE_SHORTS_RECBAR" = "Hide recommendation bar";
"HIDE_SHORTS_RECBAR_DESC" = "Hide the suggested action bar in the Shorts player (e.g. Try this sound, Shopping, Preview comment).";
-"HIDE_SHORTS_COMMIT" = "Hide commissions bar";
-"HIDE_SHORTS_COMMIT_DESC" = "Hide the bar that shows the video got commissions in the Shorts player.";
-"HIDE_SHORTS_SUBSCRIPT_BUTTON" = "Hide subscriptions button";
-"HIDE_SHORTS_SUBSCRIPT_BUTTON_DESC" = "Hide the Subscriptions button at the top of the Shorts player.";
-"HIDE_SHORTS_LIVE_BUTTON" = "Hide live button";
-"HIDE_SHORTS_LIVE_BUTTON_DESC" = "Hide the Live button at the top of the Shorts player.";
-"HIDE_SHORTS_LENS_BUTTON" = "Hide lens button";
-"HIDE_SHORTS_LENS_BUTTON_DESC" = "Hide the Lens button at the top of the Shorts player.";
-"HIDE_SHORTS_TRENDS_BUTTON" = "Hide trends button";
-"HIDE_SHORTS_TRENDS_BUTTON_DESC" = "Hide the Trends button at the top of the Shorts player.";
-"HIDE_SHORTS_TO_VIDEO" = "Hide link to full video";
-"HIDE_SHORTS_TO_VIDEO_DESC" = "Hide the link that redirects in a Short to a full video in the Shorts player.";
"ENABLES_SHORTS_QUALITY" = "Enable Shorts quality picker";
"ENABLES_SHORTS_QUALITY_DESC" = "Force enable the video quality selection menu in Shorts.";
"SHOW_SHORTS_SEEKBAR" = "Always show seekbar";
"SHOW_SHORTS_SEEKBAR_DESC" = "Ensure the progress/seek bar is always visible at the bottom of the Shorts player.";
+"REMOVE_POSTS_SHORTS" = "Remove posts";
+"REMOVE_POSTS_SHORTS_DESC" = "Remove posts from the feed.";
+"SHORTS_FULLSCREEN" = "Pinch to Fullscreen";
+"SHORTS_FULLSCREEN_DESC" = "Manages visibility of the overlay with pinch in and pinch out gestures, displaying Shorts in fullscreen mode.";
"TABBAR" = "Tab bar";
"DEFAULT_TAB" = "Default startup tab";
-"DEFAULT_TAB_DESC" = "You can select the tab you want to open when opening the app.";
-"HOME_NAME" = "Home";
-"SUBSCRIPT_NAME" = "Subscriptions";
-"LIB_NAME" = "Library (You)";
+"FORSTED_TAB_BAR" = "Translucent tab bar";
+"ENABLED" = "Enabled";
"HIDE_TAB_INDI" = "Hide tab indicators";
"HIDE_TAB_INDI_DESC" = "Hide all tab indicators in the tab bar.";
"HIDE_TAB_LABELS" = "Hide tab labels";
"HIDE_TAB_LABELS_DESC" = "Hide all tab labels in the tab bar.";
-"HIDE_HOME_TAB" = "Hide Home tab";
-"HIDE_HOME_TAB_DESC" = "Hide Home tab in the tab bar.";
-"HIDE_SHORTS_TAB" = "Hide Shorts tab";
-"HIDE_SHORTS_TAB_DESC" = "Hide Shorts tab in the tab bar.";
-"HIDE_CREATE_BUTTON" = "Hide Create button";
-"HIDE_CREATE_BUTTON_DESC" = "Hide Create button in the tab bar.";
-"HIDE_SUBSCRIPT_TAB" = "Hide Subscriptions tab";
-"HIDE_SUBSCRIPT_TAB_DESC" = "Hide Subscriptions tab in the tab bar.";
+"HOME_TAB" = "Home";
+"SHORTS_TAB" = "Shorts";
+"CREATE_TAB" = "Create";
+"SUBSCRIPTIONS_TAB" = "Subscriptions";
+"LIBRARY_TAB" = "Library (You)";
+"HISTORY_TAB" = "History";
+"GAMING_TAB" = "Gaming";
+"SPORTS_TAB" = "Sports";
+"NOTI_TAB" = "Notifications";
+"NEWS_TAB" = "News";
+"MUSIC_TAB" = "Music";
+"WATCH_LATER_TAB" = "Watch later";
+"PLAYLIST_TAB" = "Playlists";
+"LIKE_TAB" = "Liked videos";
+"LIVE_TAB" = "Live";
+"POST_TAB" = "Posts";
+"VIDEO_TAB" = "Your videos";
+"MOVIE_TAB" = "Movies";
+"COURSE_TAB" = "Courses";
+"MINIGAME_TAB" = "Playables";
+"MANAGE_TABS" = "Manage tabs";
+"MANAGE_TABS_DESC" = "Reorder and toggle tabs";
+"TAB_REORDER_HINT" = "Drag to reorder. Toggle to show/hide.\nTab settings are also accessible by long-pressing the first tab in the tab bar.";
+"TAB_LIMIT" = "Tab limit";
+"TAB_LIMIT_DESC" = "Only 6 tabs are allowed.";
+"ZERO_TAB_DESC" = "At least 1 tab has to be enabled.";
"MISCELLANEOUS" = "Miscellaneous";
"BACKGROUND_PLAYBACK" = "Background playback";
"BACKGROUND_PLAYBACK_DESC" = "Enables background playback for videos.";
"DISABLES_SHORTS_PIP" = "Disables Shorts PiP";
"DISABLES_SHORTS_PIP_DESC" = "Try to disables Shorts PiP, this might not 100% disables it.";
+"DISABLE_HINTS" = "Disable hints";
+"DISABLE_HINTS_DESC" = "Disable the tutorial hints and interactive pop-ups.";
"BLOCK_UPGRADE_DIALOGS" = "Block upgrade dialogs";
"BLOCK_UPGRADE_DIALOGS_DESC" = "Block the YouTube dialogs that ask you to upgrade the app.";
"ARE_YOU_THERE_DIALOG" = "Hide 'Are you there?' dialog";
@@ -247,7 +370,104 @@
"DISABLES_SNACK_BAR_DESC" = "Disables snack bar when pressing some buttons or doing something in the app.";
"HIDE_STARTUP_ANIMATIONS" = "Hide YouTube's startup animations";
"HIDE_STARTUP_ANIMATIONS_DESC" = "Hide YouTube's startup animations when opening the app.";
-"HIDE_PLAY_IN_NEXT_QUEUE" = "Hide 'Play next in queue'";
-"HIDE_PLAY_IN_NEXT_QUEUE_DESC" = "Hide the 'Play next in queue' option in the flyout menu.";
"HIDE_LIKE_DISLIKE_VOTES" = "Hide like/dislike votes";
-"HIDE_LIKE_DISLIKE_VOTES_DESC" = "Hide messages that pop up when you like/dislike videos.";
\ No newline at end of file
+"HIDE_LIKE_DISLIKE_VOTES_DESC" = "Hide messages that pop up when you like/dislike videos.";
+"FLYOUT_MENU" = "Flyout menu";
+"REMOVE_PLAY_IN_NEXT_QUEUE_OPTION" = "Remove 'Play next in queue' option";
+"REMOVE_PLAY_IN_NEXT_QUEUE_OPTION_DESC" = "Remove 'Play next in queue' option from the flyout menu.";
+"REMOVE_DOWNLOAD_OPTION" = "Remove 'Download video' option";
+"REMOVE_DOWNLOAD_OPTION_DESC" = "Remove 'Download video' option from the flyout menu.";
+"REMOVE_WATCH_LATER_OPTION" = "Remove 'Save to Watch later' option";
+"REMOVE_WATCH_LATER_OPTION_DESC" = "Remove 'Save to Watch Later' option from the flyout menu.";
+"REMOVE_SAVE_OPTION" = "Remove 'Save to playlist' option";
+"REMOVE_SAVE_OPTION_DESC" = "Remove 'Save to playlist' option from the flyout menu.";
+"REMOVE_REMOVE_FROM_PLAYLIST_OPTION" = "Remove 'Remove from playlist' option";
+"REMOVE_REMOVE_FROM_PLAYLIST_OPTION_DESC" = "Remove 'Remove from playlist' option from the flyout menu.";
+"REMOVE_SHARE_OPTION" = "Remove 'Share' option";
+"REMOVE_SHARE_OPTION_DESC" = "Remove 'Share' option from the flyout menu.";
+"REMOVE_NOT_INTERESTED_OPTION" = "Remove 'Not interested' option";
+"REMOVE_NOT_INTERESTED_OPTION_DESC" = "Remove 'Not interested' option from the flyout menu.";
+"REMOVE_DONT_RECOMMEND_OPTION" = "Remove 'Don't recommend channel' option";
+"REMOVE_DONT_RECOMMEND_OPTION_DESC" = "Remove 'Don't recommend channel' option from the flyout menu.";
+"REMOVE_INFO_OPTION" = "Remove 'Why are you seeing this?' option";
+"REMOVE_INFO_OPTION_DESC" = "Remove 'Why are you seeing this?' option from the flyout menu.";
+"REMOVE_FILTER_OPTION" = "Remove 'Search filters' option";
+"REMOVE_FILTER_OPTION_DESC" = "Remove 'Search filters' option from the flyout menu.";
+"REMOVE_REPORT_OPTION" = "Remove 'Report' option";
+"REMOVE_REPORT_OPTION_DESC" = "Remove 'Report' option from the flyout menu.";
+"REMOVE_YOUTUBE_MUSIC_OPTION" = "Remove 'Listen with YouTube Music' option";
+"REMOVE_YOUTUBE_MUSIC_OPTION_DESC" = "Remove 'Listen with YouTube Music' option from the flyout menu.";
+"REMOVE_FEED_BACK_OPTION" = "Remove 'Send feedback' option";
+"REMOVE_FEED_BACK_OPTION_DESC" = "Remove 'Send feedback' option from the flyout menu.";
+"REMOVE_CAST_OPTION" = "Remove 'Watch on TV' option";
+"REMOVE_CAST_OPTION_DESC" = "Remove 'Watch on TV' option from the flyout menu.";
+"REMOVE_SHUFFLE_OPTION" = "Remove 'Shuffle' option";
+"REMOVE_SHUFFLE_OPTION_DESC" = "Remove 'Shuffle' option from the flyout menu.";
+"REMOVE_UN_SUB_OPTION" = "Remove 'Unsubscribe' option";
+"REMOVE_UN_SUB_OPTION_DESC" = "Remove 'Unsubscribe' option from the flyout menu.";
+"REMOVE_HIDE_FROM_PLAYLIST_OPTION" = "Remove 'Hide video from playlist' option";
+"REMOVE_HIDE_FROM_PLAYLIST_OPTION_DESC" = "Remove 'Hide video from playlist' option from the flyout menu.";
+"REMOVE_HELP_OPTION" = "Remove 'Help & feedback' option";
+"REMOVE_HELP_OPTION_DESC" = "Remove 'Help & feedback' option from the flyout menu.";
+"REMOVE_NOTIFY_OPTION" = "Remove 'Notify me' option";
+"REMOVE_NOTIFY_OPTION_DESC" = "Remove 'Notify me' option from the flyout menu.";
+"REMOVE_CLEARSCREEN_OPTION" = "Remove 'Clear Screen' option";
+"REMOVE_CLEARSCREEN_OPTION_DESC" = "Remove 'Clear Screen' option from the flyout menu.";
+
+"SB_DESCRIPTION" = "Skip sponsored segments automatically using community-submitted data.";
+"SB_ENABLE" = "Enable SponsorBlock";
+"SB_ENABLE_DESC" = "Activate the SponsorBlock extension to skip sponsored segments.";
+"SB_SHOW_BUTTON" = "Show overlay button";
+"SB_SHOW_BUTTON_DESC" = "Display a SponsorBlock toggle button in the player overlay.";
+"SB_SHOW_NOTIFICATIONS" = "Show notifications";
+"SB_SHOW_NOTIFICATIONS_DESC" = "Display notifications when segments are skipped. Notifications for manual-skip categories are always shown.";
+"SB_HAPTIC_FEEDBACK" = "Haptic feedback";
+"SB_HAPTIC_FEEDBACK_DESC" = "Provide haptic feedback when a segment is skipped.";
+"SB_SEGMENTS_IN_PLAYER" = "Segments in player";
+"SB_SEGMENTS_IN_PLAYER_DESC" = "Display colored segments on the progress bar of the main video player.";
+"SB_SEGMENTS_IN_FEED" = "Segments in feed";
+"SB_SEGMENTS_IN_FEED_DESC" = "Display colored segments on the progress bar of players in the feed.";
+"SB_SEGMENTS_IN_MINIPLAYER" = "Segments in mini-player";
+"SB_SEGMENTS_IN_MINIPLAYER_DESC" = "Display colored segments on the progress bar of the mini-player.";
+"SB_SHOW_DURATION" = "Show duration without segments";
+"SB_SHOW_DURATION_DESC" = "Display the total video duration excluding skippable segments.";
+"SB_CATEGORIES_HEADER" = "Segment categories";
+"SB_SEGMENT_COLOR" = "Segment color";
+
+"SB_ACTION_DISABLE" = "Disable";
+"SB_ACTION_AUTO_SKIP" = "Auto-skip";
+"SB_ACTION_ASK" = "Ask before skipping";
+"SB_ACTION_DISPLAY" = "Show on seek bar only";
+"SB_ACTION_SKIP_TO" = "Skip to segment";
+
+"SB_CAT_sponsor" = "Sponsor";
+"SB_CAT_sponsor_DESC" = "Paid promotion for a product or service not directly related to the creator.";
+"SB_CAT_intro" = "Intro";
+"SB_CAT_intro_DESC" = "Intermission or intro animation typically seen at the start of a video.";
+"SB_CAT_outro" = "Endcards";
+"SB_CAT_outro_DESC" = "Endcards, credits, or outros at the end of the video.";
+"SB_CAT_interaction" = "Interaction";
+"SB_CAT_interaction_DESC" = "Reminders to like, subscribe, or follow on social media.";
+"SB_CAT_selfpromo" = "Self-promotion";
+"SB_CAT_selfpromo_DESC" = "Unpaid promotion of the creator's own products, merchandise, or other content.";
+"SB_CAT_music_offtopic" = "Non-music";
+"SB_CAT_music_offtopic_DESC" = "Non-music sections in music videos (e.g. talking, introductions).";
+"SB_CAT_preview" = "Preview";
+"SB_CAT_preview_DESC" = "Recap or preview of content from another video or previous episode.";
+"SB_CAT_hook" = "Hook/Greetings";
+"SB_CAT_hook_DESC" = "Narrated trailers for the upcoming video, greetings and goodbyes.";
+"SB_CAT_poi_highlight" = "Highlight";
+"SB_CAT_poi_highlight_DESC" = "The key moment or highlight point of the video. Use 'Skip to' to jump directly to it.";
+"SB_CAT_filler" = "Filler";
+"SB_CAT_filler_DESC" = "Tangential content, jokes, or filler that is not directly related to the main topic.";
+
+"SB_SKIPPED" = "%@ segment has been skipped";
+"SB_DETECTED" = "%@ segment detected.\nWould you like to skip the segment?";
+"SB_SKIP_NOW" = "Skip";
+"SB_UNSKIP" = "Unskip";
+"SB_JUMPED_TO_HIGHLIGHT" = "Jumped to highlight";
+"SB_JUMP_TO_HIGHLIGHT" = "Highlight available. Jump to the point?";
+"SB_SEGMENT_COLOR_SUFFIX" = "Segment color";
+"SB_SECTION_MAIN" = "Main";
+"SB_SKIP_ALERT_DURATION" = "Skip alert duration";
+"SB_UNSKIP_ALERT_DURATION" = "Unskip alert duration";
\ No newline at end of file
diff --git a/layout/Library/Application Support/YouMod.bundle/zh-TW.lproj/Localizable.strings b/layout/Library/Application Support/YouMod.bundle/zh-TW.lproj/Localizable.strings
index 8ec0526c..b10e780e 100644
--- a/layout/Library/Application Support/YouMod.bundle/zh-TW.lproj/Localizable.strings
+++ b/layout/Library/Application Support/YouMod.bundle/zh-TW.lproj/Localizable.strings
@@ -1,11 +1,18 @@
-"NOTE" = "NOTE: Some hiding features may leave a blank box.";
"SETTINGS" = "Settings";
+"INTERFACE" = "Interface";
+
+"LOOP_DISABLED" = "Loop disabled";
+"LOOP_ENABLED" = "Loop enabled";
"NEW_ISSUES" = "Report issues or request new features";
"NEW_ISSUES_DESC" = "Found bugs or want to request new features? Tap here.";
"SOURCE_CODES" = "Source codes";
"SOURCE_CODES_DESC" = "Take a look at the codes!";
+"EXTRA" = "Extra";
+"FIX_PLAYBACK_ISSUES" = "Fix playback issues (Temporary)";
+"FIX_PLAYBACK_ISSUES_DESC" = "Try to fix video playback issues for some users. Turning this on will make the player automatically reloads itself everytime it errors or fails to play the video.";
+
"PERFER_HEADER" = "YouMod perferences";
"PERFER" = "Perferences";
"IMPORT" = "Import settings";
@@ -24,6 +31,7 @@
"ERROR_NO_KEYS_IMPORT" = "No available keys to import";
"ERROR_NO_KEYS_EXPORT" = "No available keys to export";
"OVERRIDE" = "This will override the current settings. Do you want to continue?";
+"DISABLED" = "Disabled";
"CACHE" = "Cache management";
"CLEARCACHE" = "Clear cache";
@@ -32,12 +40,80 @@
"AUTO_CLEARCACHE_DESC" = "Automatically clears the app cache on startup.";
"DOWNLOADING" = "Downloading";
-"DOWNLOAD_MANAGER" = "Download Manager";
-"DOWNLOAD_MANAGER_DESC" = "Replaces YouTube's video downloader with YouMod one. With ablity to downloading videos, audios, captions, and thumbnails";
+"DOWNLOAD_MANAGER" = "Download manager";
+"DOWNLOAD_MANAGER_DESC" = "Replaces YouTube's video downloader with YouMod one. With ablity to downloading videos, audios, captions, and thumbnails.";
"DOWNLOAD_SAVE_PHOTOS" = "Automatically save to Photos";
"DOWNLOAD_SAVE_PHOTOS_DESC" = "Automatically saves downloaded videos and images directly to your Photos app.";
-"DOWNLOAD_DRC_AUDIO" = "Prefer DRC Audio";
-"DOWNLOAD_DRC_AUDIO_DESC" = "Prioritizes downloading audio tracks with Dynamic Range Compression for more consistent volume levels.";
+"ADD_SHORTS_DOWNLOAD" = "Adds a download button to Shorts";
+"ADD_SHORTS_DOWNLOAD_DESC" = "Adds a download button to Shorts video overlay.";
+"USE_ORIG_AUDIO" = "Automatically uses original audio track";
+"USE_ORIG_AUDIO_DESC" = "Automatically uses original audio track instead of showing a dialog.";
+"DOWNLOAD_SHORTS" = "Download Shorts";
+"DOWNLOAD_SHORTS_DESC" = "Choose Shorts quality";
+"COPY_VID_INFO" = "Copy video informations";
+"COPY_VID_INFO_DESC" = "Choose what to copy";
+"COPY_ALL_VID_INFO" = "Copy all";
+"COPY_ALL_VID_INFO_DESC" = "Copy all video informations";
+"COPY_AUTHOR" = "Copy author";
+"COPY_AUTHOR_DESC" = "Copy the video author";
+"COPY_TITLE" = "Copy title";
+"COPY_TITLE_DESC" = "Copy the video title";
+"COPY_DESCRIPTION" = "Copy description";
+"COPY_DESCRIPTION_DESC" = "Copy the video description";
+"COPY_URL" = "Copy video link";
+"COPY_URL_TIMESTAMP" = "Copy video link with timestamp";
+"URL_COPIED" = "Copied video link";
+"URL_TIMESTAMP_COPIED" = "Copied video link with timestamp";
+"ERROR_VIDEOID" = "Cannot get the video ID";
+"ERROR_ADS" = "Cannot copy video link while playing ads";
+"COPIED_AUTHOR" = "Copied video author";
+"COPIED_TITLE" = "Copied video title";
+"COPIED_DESCRIPTION" = "Copied video description";
+"SAVE_THUMBNAIL" = "Save thumbnail";
+"SAVE_THUMBNAIL_DESC" = "Save to Photos";
+"DOWNLOAD_CAPTIONS" = "Download captions";
+"DOWNLOAD_CAPTIONS_DESC" = "Save subtitles as .VTT file";
+"DOWNLOAD_AUDIO" = "Download audio";
+"DOWNLOAD_AUDIO_DESC" = "Download audio as .m4a file";
+"DOWNLOAD_VIDEO" = "Download video";
+"DOWNLOAD_VIDEO_DESC" = "Choose video quality";
+"OPEN_VID_BEFORE" = "Open a video before using the download manager";
+"NO_CAPTIONS_URL" = "No valid caption URLs found";
+"NO_AUDIO_URL" = "No audio URL found";
+"NO_STREAM_URL" = "No stream URL found";
+"CAPTIONS_FAILED" = "Failed to download captions";
+"NO_CAPTIONS" = "No captions available for this video";
+"NO_AUDIO_STREAM_FOUND" = "No audio streams found";
+"NO_VID_AUDIO_STREAM_FOUND" = "No video/audio streams found";
+"COPIED_VID_INFO" = "Copied all video informations";
+"SAVED_TO_PHOTOS" = "Saved to Photos";
+"SAVE_FAILED" = "Save failed";
+"PHOTO_ACCESS_DENINED" = "Photos access denied";
+"THUMBNAIL_FAILED" = "Thumbnail failed";
+"DOWNLOADING_THUMBNAIL" = "Downloading thumbnail...";
+"DOWNLOADING_AUDIO" = "Downloading audio...";
+"DOWNLOADING_VIDEO" = "Downloading video...";
+"DOWNLOADING_CAPTIONS" = "Downloading captions...";
+"NO_THUMBNAIL_FOUND" = "No thumbnail found";
+"DOWNLOAD_FAILED" = "Download failed";
+"COPY_THUMBNAIL" = "Copy thumbnail";
+"COPY_THUMBNAIL_DESC" = "Copy to clipboard";
+"SHOW_THUMBNAIL" = "Show thumbnail";
+"SHOW_THUMBNAIL_DESC" = "Show in a dialog";
+"THUMBNAIL_OPTIONS" = "Download thumbnail";
+"THUMBNAIL_OPTIONS_DESC" = "Show more options";
+"COPIED_TO_CLIPBOARD" = "Copied to clipboard";
+"DOWNLOAD_COMPLETED" = "Download completed";
+"CANNOT_SAVE_TO_PHOTOS" = "Cannot save to Photos";
+"FINA_VIDEO" = "Finalizing video...";
+"MERGING_VID_FALLBACK" = "Merging video with fallback...";
+"MERGING_VID" = "Merging video...";
+"CONVERTING" = "Converting to";
+"ALREADY_DOWNLOADING" = "Already downloading";
+"DOWNLOAD_CANCELLED" = "Download cancelled";
+"CANCEL" = "Cancel";
+"TRIMMING_AUDIO" = "Trimming audio...";
+"COPY" = "Copy";
"APPEARANCE" = "Appearance";
"OLED_THEME" = "OLED dark mode";
@@ -46,6 +122,8 @@
"OLED_KEYBOARD_DESC" = "Make the keyboard uses a pure black (OLED) color for dark theme.";
"NAVBAR" = "Navigation bar";
+"STICKY_NAVBAR" = "Sticky Navigation bar";
+"STICKY_NAVBAR_DESC" = "Pins the Navigation bar so that it remains visible when scrolling down.";
"HIDE_YT_LOGO" = "Hide YouTube logo";
"HIDE_YT_LOGO_DESC" = "Hide YouTube logo in the navigation bar.";
"PREMIUM_LOGO" = "Use YouTube Premium logo";
@@ -62,34 +140,78 @@
"FEED" = "Feed";
"HIDE_SUBBAR" = "Hide subbar";
"HIDE_SUBBAR_DESC" = "Hide subbar at the top of the feed.";
+"HIDE_HORI_SHELF" = "Remove horizontal feeds";
+"HIDE_HORI_SHELF_DESC" = "Removes all horizontal feeds (News, Continue Watching, Posts collection, etc.) from the feed, except Playables.";
"HIDE_MUSIC_PLAYLISTS" = "Hide music playlist generator";
"HIDE_MUSIC_PLAYLISTS_DESC" = "Hide music playlist generator (Select music tunes in three) in the feed.";
"HIDE_FEED_POST" = "Hide posts";
"HIDE_FEED_POST_DESC" = "Hide posts in feed.";
+"HIDE_SURVEYS" = "Hide surveys";
+"HIDE_SURVEYS_DESC" = "Hide video surveys that YouTube shows sometimes.";
"HIDE_SHORTS_SHELF" = "Hide Shorts shelf";
"HIDE_SHORTS_SHELF_DESC" = "Hide Shorts shelf in the feed, including in the Subscriptions tab.";
+"KEEP_SHORTS_SUBSCRIPT" = "Keep Shorts in Subscriptions";
+"KEEP_SHORTS_SUBSCRIPT_DESC" = "Prevents removing Shorts from Subscriptions tab.";
"HIDE_SEARCH_HISTORY" = "Hide search history";
"HIDE_SEARCH_HISTORY_DESC" = "Hide previous search history and suggestions when using the search bar.\nNOTE: Your search history will still be accessible in your other YouTube clients.";
-"HIDE_SUB_BUTTON" = "Hide Subscribe button";
-"HIDE_SUB_BUTTON_DESC" = "Hides the subscribe button in channels and video descriptions.";
-"HIDE_SHOP_BUTTON" = "Hide Shopping button";
-"HIDE_SHOP_BUTTON_DESC" = "Hides the shopping/store button in channels descriptions.";
-"HIDE_MEMBER_BUTTON" = "Hide Membership button";
-"HIDE_MEMBER_BUTTON_DESC" = "Hides the 'Join' button in channels and video descriptions.";
+"HIDE_PLAYABLES" = "Remove playables";
+"HIDE_PLAYABLES_DESC" = "Removes playables (mini-games) from the feed.";
"PLAYER" = "Player";
+"PLAYBACK_SPEED" = "Playback speed";
+"CONTROL_CENTER" = "Control Center";
+"PLAYER_ACTIONS" = "Player actions";
+"GESTURE_HEADER" = "Gestures";
+"QUALITY_WIFI" = "Playback quality on WiFi";
+"QUALITY_WIFI_DESC" = "Automatically applies the selected quality when streaming over Wi-Fi.";
+"QUALITY_CELLULAR" = "Playback quality on Cellular";
+"QUALITY_CELLULAR_DESC" = "Automatically applies the selected quality when streaming over mobile data.";
+"QUALITY_LOW_POWER" = "Playback quality on Power saver mode";
+"QUALITY_LOW_POWER_DESC" = "Automatically applies the selected quality when when Low power mode is enabled on your device.";
+"DEFAULT" = "Default";
+"BEST" = "Best";
+"AUDIO_TRACK" = "Preferred audio track";
+"ORIGINAL" = "Original";
+"SELECT_MANUALLY" = "Select manually";
+"AUDIO_TRACK_SELECT" = "Selected audio track";
+"AUDIO_TRACK_SELECT_DESC" = "Specifies the preferred audio track language to use for playback, if available.";
+"NO_AUTO_DUBBED" = "Uses original audio track";
+"NO_AUTO_DUBBED_DESC" = "Uses original audio track if the preferred language track is auto-dubbed or not found.";
+"CAPTION_TRACK" = "Preferred captions";
+"CAPTION_TRACK_SELECT" = "Selected captions";
+"CAPTION_TRACK_SELECT_DESC" = "Specifies the preferred captions language to display, if available.";
+"DISABLES_CAPTION_TRACK" = "Automatically disable captions";
+"DISABLES_CAPTION_TRACK_DESC" = "Automatically disable captions if the preferred language is auto-generated or not found.";
+"DEFAULT_SPEED" = "Default playback rate";
+"DEFAULT_SPEED_DESC" = "Overrides the default playback speed.";
+"SPEED_BUTTON" = "Speed controls button";
+"SPEED_BUTTON_DESC" = "Displays a speed controls button on overlay.";
+"MUTE_BUTTON" = "Mute button";
+"MUTE_BUTTON_DESC" = "Displays a button to mute or unmute the volume on the overlay.";
+"SHARE_BUTTON" = "Share button";
+"SHARE_BUTTON_DESC" = "Displays a button to share the video link on the overlay.";
+"LOOP_BUTTON" = "Loop button";
+"LOOP_BUTTON_DESC" = "Displays a button to loop the video on the overlay.";
+"CAPTION_BUTTON" = "Captions button";
+"CAPTION_BUTTON_DESC" = "Displays a button to copy the video captions on the overlay.";
+"HOLD_TO_SPEED" = "Hold to speed";
+"HOLD_TO_SPEED_DESC" = "Uses custom hold to speed playback rate.";
"HIDE_AUTOPLAY" = "Hide autoplay switch";
"HIDE_AUTOPLAY_DESC" = "Hide autoplay switch in the video overlay.";
"HIDE_CAPTIONS_BUTTON" = "Hide captions button";
"HIDE_CAPTIONS_BUTTON_DESC" = "Hide captions button in the video overlay.";
"HIDE_CAST_BUTTON_PLAYER" = "Hide cast button";
"HIDE_CAST_BUTTON_PLAYER_DESC" = "Hide cast button in the video overlay.";
-"HIDE_PREV_BUTTON" = "Hide previous/back button";
-"HIDE_PREV_BUTTON_DESC" = "Hide previous/back button in the video overlay.";
-"HIDE_NEXT_BUTTON" = "Hide next/skip button";
-"HIDE_NEXT_BUTTON_DESC" = "Hide next/skip button in the video overlay.";
+"HIDE_NEXT_AND_PREV_BUTTON" = "Hide previous and next buttons";
+"HIDE_NEXT_AND_PREV_BUTTON_DESC" = "Hide previous and next buttons in the video overlay.";
"REPLACE_PREVNEXT_BUTTONS" = "Replace Previous/Next buttons";
"REPLACE_PREVNEXT_BUTTONS_DESC" = "Replaces the Previous and Next video buttons in the player with Rewind and Fast Forward buttons.";
+"SKIP_BACKWARD" = "Skip backward";
+"SKIP_BACKWARD_DESC" = "Replaces the previous track button in Control Center with a skip backward button by a set number of seconds.";
+"SKIP_FORWARD" = "Skip forward";
+"SKIP_FORWARD_DESC" = "Replaces the next track button in Control Center with a skip forward button by a set number of seconds.";
+"REWIND_SECONDS" = "Rewind seconds";
+"FORWARD_SECONDS" = "Fast-forward seconds";
"REMOVE_DARK_OVERLAY" = "Remove dark overlay";
"REMOVE_DARK_OVERLAY_DESC" = "Remove dark overlay from the video overlay.";
"HIDE_END_SCREEN" = "Hide endscreen cards";
@@ -102,6 +224,12 @@
"HIDE_PAID_OVERLAY_DESC" = "Hide 'Includes paid promotion' in the video overlay.";
"HIDE_WATERMARK" = "Hide watermark";
"HIDE_WATERMARK_DESC" = "Hide channel watermark at the bottom of the video overlay.";
+"PAUSE_ON_OVERLAY" = "Pause on overlay";
+"PAUSE_ON_OVERLAY_DESC" = "Sets playback on pause if overlay appears.";
+"DISABLES_SNAP_TO_CHAPTER" = "Disable snap to chapters";
+"DISABLES_SNAP_TO_CHAPTER_DESC" = "Prevents the scrubbar stick to the chapters.";
+"DISABLES_ENGAGE_PANEL" = "Disables engagement panel in fullscreen";
+"DISABLES_ENGAGE_PANEL_DESC" = "Prevent engagement panel showing in fullscreen.";
"GESTURES" = "Enable gestures controls";
"GESTURES_DESC" = "Swipe up or down on the left/right edges of the video screen to adjust brightness, volume, or playback speed.";
"GESTURE_AREA" = "Gesture activation area";
@@ -136,6 +264,8 @@
"DISABLES_SHOW_REMAINING_DESC" = "Disable the feature that shows how much time is left in a video when tapping the timestamp.";
"ALWAYS_SHOW_REMAINING" = "Always show remaining time";
"ALWAYS_SHOW_REMAINING_DESC" = "Ensure the remaining time is always displayed by default in the player overlay.";
+"COPY_TIMESTAMP_ON_PAUSE" = "Copy timestamped links";
+"COPY_TIMESTAMP_ON_PAUSE_DESC" = "Allows to copy timestamped link to the clipboard by pressing pause button.";
"SHOW_REMAINING_EXTRA" = "Show extra remaining time";
"SHOW_REMAINING_EXTRA_DESC" = "Adds the estimated finish time to the label in the video overlay.";
"HIDE_FULLSCREEN_ACTIONS" = "Hide fullscreen actions";
@@ -148,93 +278,95 @@
"HIDE_CONTENT_WARNING_DESC" = "Automatically skip the sensitive content warning alert in some vidoes.";
"AUTO_FULLSCREEN" = "Auto fullscreen";
"AUTO_FULLSCREEN_DESC" = "Automatically enter fullscreen mode when starting video playback.";
-"AUTO_DISABLES_CAPTION" = "Auto disable captions";
-"AUTO_DISABLES_CAPTION_DESC" = "Automatically turns off closed captions (CC) whenever a new video starts playing.";
"PORTRAIT_FULLSCREEN" = "Portrait fullscren mode";
"PORTRAIT_FULLSCREEN_DESC" = "Enables portrait fullscreen mode support.";
"OLD_QUALITY_PICKER" = "Use old quality picker";
"OLD_QUALITY_PICKER_DESC" = "Reverts the video quality selection menu to the classic version (lists all resolutions immediately).";
"EXTRA_SPEED" = "Extra speed options";
"EXTRA_SPEED_DESC" = "Adds more video playback speed options up to 10x.";
-"DISABLE_HINTS" = "Disable hints";
-"DISABLE_HINTS_DESC" = "Disable the tutorial hints and interactive pop-ups.";
"FORCE_MINIPLAYER" = "Force miniplayer";
"FORCE_MINIPLAYER_DESC" = "Allows all videos, including those made for kids, to play in the miniplayer when dismissing the player.";
"FORCE_SEEKBAR" = "Always show progress bar";
"FORCE_SEEKBAR_DESC" = "Makes the progress bar (seekbar) remains visible at all times while in fullscreen mode.";
-"HIDE_LIKE_BUTTON" = "Hide Like button";
-"HIDE_LIKE_BUTTON_DESC" = "Hide Like button in the action bar under the video overlay.";
-"HIDE_DISLIKE_BUTTON" = "Hide Dislike button";
-"HIDE_DISLIKE_BUTTON_DESC" = "Hide Dislike button in the action bar under the video overlay.";
-"HIDE_SHARE_BUTTON" = "Hide Share button";
-"HIDE_SHARE_BUTTON_DESC" = "Hide Share button in the action bar under the video overlay.";
-"HIDE_DOWNLOAD_BUTTON" = "Hide Download button";
-"HIDE_DOWNLOAD_BUTTON_DESC" = "Hide Download button in the action bar under the video overlay.";
-"HIDE_CLIP_BUTTON" = "Hide Clip button";
-"HIDE_CLIP_BUTTON_DESC" = "Hide Clip button in the action bar under the video overlay.";
-"HIDE_REMIX_BUTTON" = "Hide Remix button";
-"HIDE_REMIX_BUTTON_DESC" = "Hide Remix button in the action bar under the video overlay.";
-"HIDE_SAVE_BUTTON" = "Hide Save button";
-"HIDE_SAVE_BUTTON_DESC" = "Hide Save button in the action bar under the video overlay.";
+"TAP_TO_SEEK" = "Tap to seek";
+"TAP_TO_SEEK_DESC" = "Quickly jump to any point by tapping the progress bar.";
+"DISABLES_ZOOM" = "Disable free zoom gesture";
+"DISABLES_ZOOM_DESC" = "Disables new free zoom gestures.";
+"USES_24_HOURS_TIME" = "24-hour format";
+"USES_24_HOURS_TIME_DESC" = "Shows end time in 24-hour format.";
+"PAUSE_TWO_FINGERS" = "Pause using two fingers";
+"PAUSE_TWO_FINGERS_DESC" = "Pause/Resume playback by tapping on the screen using two fingers.";
+"HIDE_COMMENTS_SECTION" = "Hide comments section";
+"HIDE_COMMENTS_SECTION_DESC" = "Completely removes the entire comments section.";
+"HIDE_COMMENTS_PREVIEW" = "Hide preview comments";
+"HIDE_COMMENTS_PREVIEW_DESC" = "Removes the preview box for the top comment while keeping the full section expandable.";
+"USE_ANOTHER_MINIPLAYER" = "Uses another miniplayer style";
+"USE_ANOTHER_MINIPLAYER_DESC" = "Uses another miniplayer style redesigned from older YT versions.";
"SHORTS" = "Shorts";
-"HIDE_SHORTS_LIKE_BUTTON" = "Hide Like button";
-"HIDE_SHORTS_LIKE_BUTTON_DESC" = "Hide the Like button in the Shorts player.";
-"HIDE_SHORTS_DISLIKE_BUTTON" = "Hide Dislike button";
-"HIDE_SHORTS_DISLIKE_BUTTON_DESC" = "Hide the Dislike button in the Shorts player.";
-"HIDE_SHORTS_COMMENT_BUTTON" = "Hide Comment button";
-"HIDE_SHORTS_COMMENT_BUTTON_DESC" = "Hide the Comment button in the Shorts player.";
-"HIDE_SHORTS_SHARE_BUTTON" = "Hide Share button";
-"HIDE_SHORTS_SHARE_BUTTON_DESC" = "Hide the Share button in the Shorts player.";
-"HIDE_SHORTS_REMIX_BUTTON" = "Hide Remix button";
-"HIDE_SHORTS_REMIX_BUTTON_DESC" = "Hide the Remix button in the Shorts player.";
-"HIDE_METADATA_BUTTON" = "Hide sound metadata button";
-"HIDE_METADATA_BUTTON_DESC" = "Hide the button that shows the sound source or music metadata in the Shorts player.";
+"SHORTS_ONLY" = "Shorts only mode";
+"SHORTS_ONLY_DESC" = "Limits YouTube functionality to viewing Shorts only.";
+"SHORTS_ACTION" = "Shorts action after the video ends";
+"LOOP" = "Loop video";
+"SKIP_TO_NEXT_SHORTS" = "Skip to next video";
+"PAUSE_SHORTS" = "Stop video";
+"REMOVE_LIVE_SHORTS" = "Remove live videos";
+"REMOVE_LIVE_SHORTS_DESC" = "Removes live videos from the feed.";
"HIDE_SHORTS_PRODUCT" = "Hide products";
"HIDE_SHORTS_PRODUCT_DESC" = "Hide tagged products and shopping links in the Shorts overlay.";
"HIDE_SHORTS_RECBAR" = "Hide recommendation bar";
"HIDE_SHORTS_RECBAR_DESC" = "Hide the suggested action bar in the Shorts player (e.g. Try this sound, Shopping, Preview comment).";
-"HIDE_SHORTS_COMMIT" = "Hide commissions bar";
-"HIDE_SHORTS_COMMIT_DESC" = "Hide the bar that shows the video got commissions in the Shorts player.";
-"HIDE_SHORTS_SUBSCRIPT_BUTTON" = "Hide subscriptions button";
-"HIDE_SHORTS_SUBSCRIPT_BUTTON_DESC" = "Hide the Subscriptions button at the top of the Shorts player.";
-"HIDE_SHORTS_LIVE_BUTTON" = "Hide live button";
-"HIDE_SHORTS_LIVE_BUTTON_DESC" = "Hide the Live button at the top of the Shorts player.";
-"HIDE_SHORTS_LENS_BUTTON" = "Hide lens button";
-"HIDE_SHORTS_LENS_BUTTON_DESC" = "Hide the Lens button at the top of the Shorts player.";
-"HIDE_SHORTS_TRENDS_BUTTON" = "Hide trends button";
-"HIDE_SHORTS_TRENDS_BUTTON_DESC" = "Hide the Trends button at the top of the Shorts player.";
-"HIDE_SHORTS_TO_VIDEO" = "Hide link to full video";
-"HIDE_SHORTS_TO_VIDEO_DESC" = "Hide the link that redirects in a Short to a full video in the Shorts player.";
"ENABLES_SHORTS_QUALITY" = "Enable Shorts quality picker";
"ENABLES_SHORTS_QUALITY_DESC" = "Force enable the video quality selection menu in Shorts.";
"SHOW_SHORTS_SEEKBAR" = "Always show seekbar";
"SHOW_SHORTS_SEEKBAR_DESC" = "Ensure the progress/seek bar is always visible at the bottom of the Shorts player.";
+"REMOVE_POSTS_SHORTS" = "Remove posts";
+"REMOVE_POSTS_SHORTS_DESC" = "Remove posts from the feed.";
+"SHORTS_FULLSCREEN" = "Pinch to Fullscreen";
+"SHORTS_FULLSCREEN_DESC" = "Manages visibility of the overlay with pinch in and pinch out gestures, displaying Shorts in fullscreen mode.";
"TABBAR" = "Tab bar";
"DEFAULT_TAB" = "Default startup tab";
-"DEFAULT_TAB_DESC" = "You can select the tab you want to open when opening the app.";
-"HOME_NAME" = "Home";
-"SUBSCRIPT_NAME" = "Subscriptions";
-"LIB_NAME" = "Library (You)";
+"FORSTED_TAB_BAR" = "Translucent tab bar";
+"ENABLED" = "Enabled";
"HIDE_TAB_INDI" = "Hide tab indicators";
"HIDE_TAB_INDI_DESC" = "Hide all tab indicators in the tab bar.";
"HIDE_TAB_LABELS" = "Hide tab labels";
"HIDE_TAB_LABELS_DESC" = "Hide all tab labels in the tab bar.";
-"HIDE_HOME_TAB" = "Hide Home tab";
-"HIDE_HOME_TAB_DESC" = "Hide Home tab in the tab bar.";
-"HIDE_SHORTS_TAB" = "Hide Shorts tab";
-"HIDE_SHORTS_TAB_DESC" = "Hide Shorts tab in the tab bar.";
-"HIDE_CREATE_BUTTON" = "Hide Create button";
-"HIDE_CREATE_BUTTON_DESC" = "Hide Create button in the tab bar.";
-"HIDE_SUBSCRIPT_TAB" = "Hide Subscriptions tab";
-"HIDE_SUBSCRIPT_TAB_DESC" = "Hide Subscriptions tab in the tab bar.";
+"HOME_TAB" = "Home";
+"SHORTS_TAB" = "Shorts";
+"CREATE_TAB" = "Create";
+"SUBSCRIPTIONS_TAB" = "Subscriptions";
+"LIBRARY_TAB" = "Library (You)";
+"HISTORY_TAB" = "History";
+"GAMING_TAB" = "Gaming";
+"SPORTS_TAB" = "Sports";
+"NOTI_TAB" = "Notifications";
+"NEWS_TAB" = "News";
+"MUSIC_TAB" = "Music";
+"WATCH_LATER_TAB" = "Watch later";
+"PLAYLIST_TAB" = "Playlists";
+"LIKE_TAB" = "Liked videos";
+"LIVE_TAB" = "Live";
+"POST_TAB" = "Posts";
+"VIDEO_TAB" = "Your videos";
+"MOVIE_TAB" = "Movies";
+"COURSE_TAB" = "Courses";
+"MINIGAME_TAB" = "Playables";
+"MANAGE_TABS" = "Manage tabs";
+"MANAGE_TABS_DESC" = "Reorder and toggle tabs";
+"TAB_REORDER_HINT" = "Drag to reorder. Toggle to show/hide.\nTab settings are also accessible by long-pressing the first tab in the tab bar.";
+"TAB_LIMIT" = "Tab limit";
+"TAB_LIMIT_DESC" = "Only 6 tabs are allowed.";
+"ZERO_TAB_DESC" = "At least 1 tab has to be enabled.";
"MISCELLANEOUS" = "Miscellaneous";
"BACKGROUND_PLAYBACK" = "Background playback";
"BACKGROUND_PLAYBACK_DESC" = "Enables background playback for videos.";
"DISABLES_SHORTS_PIP" = "Disables Shorts PiP";
"DISABLES_SHORTS_PIP_DESC" = "Try to disables Shorts PiP, this might not 100% disables it.";
+"DISABLE_HINTS" = "Disable hints";
+"DISABLE_HINTS_DESC" = "Disable the tutorial hints and interactive pop-ups.";
"BLOCK_UPGRADE_DIALOGS" = "Block upgrade dialogs";
"BLOCK_UPGRADE_DIALOGS_DESC" = "Block the YouTube dialogs that ask you to upgrade the app.";
"ARE_YOU_THERE_DIALOG" = "Hide 'Are you there?' dialog";
@@ -247,7 +379,109 @@
"DISABLES_SNACK_BAR_DESC" = "Disables snack bar when pressing some buttons or doing something in the app.";
"HIDE_STARTUP_ANIMATIONS" = "Hide YouTube's startup animations";
"HIDE_STARTUP_ANIMATIONS_DESC" = "Hide YouTube's startup animations when opening the app.";
-"HIDE_PLAY_IN_NEXT_QUEUE" = "Hide 'Play next in queue'";
-"HIDE_PLAY_IN_NEXT_QUEUE_DESC" = "Hide the 'Play next in queue' option in the flyout menu.";
"HIDE_LIKE_DISLIKE_VOTES" = "Hide like/dislike votes";
-"HIDE_LIKE_DISLIKE_VOTES_DESC" = "Hide messages that pop up when you like/dislike videos.";
\ No newline at end of file
+"HIDE_LIKE_DISLIKE_VOTES_DESC" = "Hide messages that pop up when you like/dislike videos.";
+"FLYOUT_MENU" = "Flyout menu";
+"REMOVE_PLAY_IN_NEXT_QUEUE_OPTION" = "Removes 'Play next in queue' option";
+"REMOVE_PLAY_IN_NEXT_QUEUE_OPTION_DESC" = "Removes 'Play next in queue' option from the flyout menu.";
+"REMOVE_DOWNLOAD_OPTION" = "Removes 'Download video' option";
+"REMOVE_DOWNLOAD_OPTION_DESC" = "Removes 'Download video' option from the flyout menu.";
+"REMOVE_WATCH_LATER_OPTION" = "Removes 'Save to Watch later' option";
+"REMOVE_WATCH_LATER_OPTION_DESC" = "Removes 'Save to Watch Later' option from the flyout menu.";
+"REMOVE_SAVE_OPTION" = "Removes 'Save to playlist' option";
+"REMOVE_SAVE_OPTION_DESC" = "Removes 'Save to playlist' option from the flyout menu.";
+"REMOVE_REMOVE_FROM_PLAYLIST_OPTION" = "Removes 'Remove from playlist' option";
+"REMOVE_REMOVE_FROM_PLAYLIST_OPTION_DESC" = "Removes 'Remove from playlist' option from the flyout menu.";
+"REMOVE_SHARE_OPTION" = "Removes 'Share' option";
+"REMOVE_SHARE_OPTION_DESC" = "Removes 'Share' option from the flyout menu.";
+"REMOVE_NOT_INTERESTED_OPTION" = "Removes 'Not interested' option";
+"REMOVE_NOT_INTERESTED_OPTION_DESC" = "Removes 'Not interested' option from the flyout menu.";
+"REMOVE_DONT_RECOMMEND_OPTION" = "Removes 'Don't recommend channel' option";
+"REMOVE_DONT_RECOMMEND_OPTION_DESC" = "Removes 'Don't recommend channel' option from the flyout menu.";
+"REMOVE_INFO_OPTION" = "Removes 'Why are you seeing this?' option";
+"REMOVE_INFO_OPTION_DESC" = "Removes 'Why are you seeing this?' option from the flyout menu.";
+"REMOVE_FILTER_OPTION" = "Removes 'Search filters' option";
+"REMOVE_FILTER_OPTION_DESC" = "Removes 'Search filters' option from the flyout menu.";
+"REMOVE_REPORT_OPTION" = "Removes 'Report' option";
+"REMOVE_REPORT_OPTION_DESC" = "Removes 'Report' option from the flyout menu.";
+"REMOVE_YOUTUBE_MUSIC_OPTION" = "Removes 'Listen with YouTube Music' option";
+"REMOVE_YOUTUBE_MUSIC_OPTION_DESC" = "Removes 'Listen with YouTube Music' option from the flyout menu.";
+"REMOVE_FEED_BACK_OPTION" = "Removes 'Send feedback' option";
+"REMOVE_FEED_BACK_OPTION_DESC" = "Removes 'Send feedback' option from the flyout menu.";
+"REMOVE_CAST_OPTION" = "Removes 'Watch on TV' option";
+"REMOVE_CAST_OPTION_DESC" = "Removes 'Watch on TV' option from the flyout menu.";
+"REMOVE_SHUFFLE_OPTION" = "Removes 'Shuffle' option";
+"REMOVE_SHUFFLE_OPTION_DESC" = "Removes 'Shuffle' option from the flyout menu.";
+"REMOVE_UN_SUB_OPTION" = "Removes 'Unsubscribe' option";
+"REMOVE_UN_SUB_OPTION_DESC" = "Removes 'Unsubscribe' option from the flyout menu.";
+"REMOVE_HIDE_FROM_PLAYLIST_OPTION" = "Removes 'Hide video from playlist' option";
+"REMOVE_HIDE_FROM_PLAYLIST_OPTION_DESC" = "Removes 'Hide video from playlist' option from the flyout menu.";
+"REMOVE_HELP_OPTION" = "Removes 'Help & feedback' option";
+"REMOVE_HELP_OPTION_DESC" = "Removes 'Help & feedback' option from the flyout menu.";
+"REMOVE_NOTIFY_OPTION" = "Removes 'Notify me' option";
+"REMOVE_NOTIFY_OPTION_DESC" = "Removes 'Notify me' option from the flyout menu.";
+"REMOVE_CLEARSCREEN_OPTION" = "Removes 'Clear Screen' option";
+"REMOVE_CLEARSCREEN_OPTION_DESC" = "Removes 'Clear Screen' option from the flyout menu.";
+"HIDE_COMMU_GUIDE" = "Hide community guidelines";
+"HIDE_COMMU_GUIDE_DESC" = "Hide community guidelines header.";
+"DISABLES_RTL" = "Disable RTL formatting";
+"DISABLES_RTL_DESC" = "Forcefully displays text in left-to-right (LTR) format for languages that are initially displayed in right-to-left (RTL).";
+"DEVICE_UI" = "Interface style";
+
+"SB_DESCRIPTION" = "Skip sponsored segments automatically using community-submitted data.";
+"SB_ENABLE" = "Enable SponsorBlock";
+"SB_ENABLE_DESC" = "Activate the SponsorBlock extension to skip sponsored segments.";
+"SB_SHOW_BUTTON" = "Show overlay button";
+"SB_SHOW_BUTTON_DESC" = "Display a SponsorBlock toggle button in the player overlay.";
+"SB_SHOW_NOTIFICATIONS" = "Show notifications";
+"SB_SHOW_NOTIFICATIONS_DESC" = "Display notifications when segments are skipped. Notifications for manual-skip categories are always shown.";
+"SB_HAPTIC_FEEDBACK" = "Haptic feedback";
+"SB_HAPTIC_FEEDBACK_DESC" = "Provide haptic feedback when a segment is skipped.";
+"SB_SEGMENTS_IN_PLAYER" = "Segments in player";
+"SB_SEGMENTS_IN_PLAYER_DESC" = "Display colored segments on the progress bar of the main video player.";
+"SB_SEGMENTS_IN_FEED" = "Segments in feed";
+"SB_SEGMENTS_IN_FEED_DESC" = "Display colored segments on the progress bar of players in the feed.";
+"SB_SEGMENTS_IN_MINIPLAYER" = "Segments in mini-player";
+"SB_SEGMENTS_IN_MINIPLAYER_DESC" = "Display colored segments on the progress bar of the mini-player.";
+"SB_SHOW_DURATION" = "Show duration without segments";
+"SB_SHOW_DURATION_DESC" = "Display the total video duration excluding skippable segments.";
+"SB_CATEGORIES_HEADER" = "Segment categories";
+"SB_SEGMENT_COLOR" = "Segment color";
+
+"SB_ACTION_DISABLE" = "Disable";
+"SB_ACTION_AUTO_SKIP" = "Auto-skip";
+"SB_ACTION_ASK" = "Ask before skipping";
+"SB_ACTION_DISPLAY" = "Show on seek bar only";
+"SB_ACTION_SKIP_TO" = "Skip to segment";
+
+"SB_CAT_sponsor" = "Sponsor";
+"SB_CAT_sponsor_DESC" = "Paid promotion for a product or service not directly related to the creator.";
+"SB_CAT_intro" = "Intro";
+"SB_CAT_intro_DESC" = "Intermission or intro animation typically seen at the start of a video.";
+"SB_CAT_outro" = "Endcards";
+"SB_CAT_outro_DESC" = "Endcards, credits, or outros at the end of the video.";
+"SB_CAT_interaction" = "Interaction";
+"SB_CAT_interaction_DESC" = "Reminders to like, subscribe, or follow on social media.";
+"SB_CAT_selfpromo" = "Self-promotion";
+"SB_CAT_selfpromo_DESC" = "Unpaid promotion of the creator's own products, merchandise, or other content.";
+"SB_CAT_music_offtopic" = "Non-music";
+"SB_CAT_music_offtopic_DESC" = "Non-music sections in music videos (e.g. talking, introductions).";
+"SB_CAT_preview" = "Preview";
+"SB_CAT_preview_DESC" = "Recap or preview of content from another video or previous episode.";
+"SB_CAT_hook" = "Hook/Greetings";
+"SB_CAT_hook_DESC" = "Narrated trailers for the upcoming video, greetings and goodbyes.";
+"SB_CAT_poi_highlight" = "Highlight";
+"SB_CAT_poi_highlight_DESC" = "The key moment or highlight point of the video. Use 'Skip to' to jump directly to it.";
+"SB_CAT_filler" = "Filler";
+"SB_CAT_filler_DESC" = "Tangential content, jokes, or filler that is not directly related to the main topic.";
+
+"SB_SKIPPED" = "%@ segment has been skipped";
+"SB_DETECTED" = "%@ segment detected.\nWould you like to skip the segment?";
+"SB_SKIP_NOW" = "Skip";
+"SB_UNSKIP" = "Unskip";
+"SB_JUMPED_TO_HIGHLIGHT" = "Jumped to highlight";
+"SB_JUMP_TO_HIGHLIGHT" = "Highlight available. Jump to the point?";
+"SB_SEGMENT_COLOR_SUFFIX" = "Segment color";
+"SB_SECTION_MAIN" = "Main";
+"SB_SKIP_ALERT_DURATION" = "Skip alert duration";
+"SB_UNSKIP_ALERT_DURATION" = "Unskip alert duration";
\ No newline at end of file