From 0d72c03167ae605aaa30905c120f0855e65ac8de Mon Sep 17 00:00:00 2001 From: Bryce Hackel <34104885+bhackel@users.noreply.github.com> Date: Wed, 21 Aug 2024 22:23:02 -0700 Subject: [PATCH 01/47] Add keys to Copy Settings --- Source/Settings.xm | 18 +++----- Source/SettingsKeys.h | 42 +++++++++++++++++++ Source/get_keys.py | 96 +++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 143 insertions(+), 13 deletions(-) create mode 100644 Source/SettingsKeys.h create mode 100644 Source/get_keys.py diff --git a/Source/Settings.xm b/Source/Settings.xm index c25f262..00e49e7 100644 --- a/Source/Settings.xm +++ b/Source/Settings.xm @@ -5,6 +5,7 @@ #import "../Tweaks/YouTubeHeader/YTSettingsSectionItemManager.h" #import "../Tweaks/YouTubeHeader/YTUIUtils.h" #import "../Tweaks/YouTubeHeader/YTSettingsPickerViewController.h" +#import "SettingsKeys.h" // #import "AppIconOptionsController.h" // Basic switch item @@ -47,17 +48,6 @@ static int appVersionSpoofer() { extern NSBundle *YTLitePlusBundle(); -// Keys for "Copy Settings" button (for: YTLitePlus) -NSArray *copyKeys = @[ -/* MAIN Controls Keys 1/2 */ @"enableShareButton_enabled", @"enableSaveToButton_enabled", @"hideVideoPlayerShadowOverlayButtons_enabled", @"hideRightPanel_enabled", @"hideHeatwaves_enabled", @"disableAmbientModePortrait_enabled", -/* MAIN Controls Keys 2/2 */ @"disableAmbientModeFullscreen_enabled", @"fullscreenToTheRight_enabled", @"seekAnywhere_enabled", @"YTTapToSeek_enabled", @"disablePullToFull_enabled", @"alwaysShowRemainingTime_enabled", @"disableRemainingTime_enabled", @"disableEngagementOverlay_enabled", -/* MAIN App Overlay Keys 1/2 */ @"disableAccountSection_enabled", @"disableAutoplaySection_enabled", @"disableTryNewFeaturesSection_enabled", @"disableVideoQualityPreferencesSection_enabled", @"disableNotificationsSection_enabled", -/* MAIN App Overlay Keys 2/2 */ @"disableManageAllHistorySection_enabled", @"disableYourDataInYouTubeSection_enabled", @"disablePrivacySection_enabled", @"disableLiveChatSection_enabled", -/* MAIN Playback Keys */ @"inline_muted_playback_enabled", -/* MAIN Misc Keys */ @"newSettingsUI_enabled", @"ytStartupAnimation_enabled", @"ytNoModernUI_enabled", @"iPadLayout_enabled", @"iPhoneLayout_enabled", @"castConfirm_enabled", @"bigYTMiniPlayer_enabled", @"hideCastButton_enabled", @"hideSponsorBlockButton_enabled", @"hideHomeTab_enabled", @"fixCasting_enabled", @"flex_enabled", @"enableVersionSpoofer_enabled", -/* TWEAK YTUHD Keys */ @"EnableVP9", @"AllVP9" -]; - // Add both YTLite and YTLitePlus to YouGroupSettings static const NSInteger YTLitePlusSection = 788; static const NSInteger YTLiteSection = 789; @@ -115,6 +105,7 @@ static const NSInteger YTLiteSection = 789; }]; [sectionItems addObject:main]; +# pragma mark - Copy and Paste Settings YTSettingsSectionItem *copySettings = [%c(YTSettingsSectionItem) itemWithTitle:LOC(@"COPY_SETTINGS") titleDescription:IS_ENABLED(@"switchCopyandPasteFunctionality_enabled") ? LOC(@"COPY_SETTINGS_DESC_2") : LOC(@"COPY_SETTINGS_DESC") @@ -125,7 +116,7 @@ static const NSInteger YTLiteSection = 789; // Export Settings functionality NSURL *tempFileURL = [NSURL fileURLWithPath:[NSTemporaryDirectory() stringByAppendingPathComponent:@"exported_settings.txt"]]; NSMutableString *settingsString = [NSMutableString string]; - for (NSString *key in copyKeys) { + for (NSString *key in NSUserDefaultsCopyKeys) { id value = [[NSUserDefaults standardUserDefaults] objectForKey:key]; if (value) { [settingsString appendFormat:@"%@: %@\n", key, value]; @@ -140,7 +131,7 @@ static const NSInteger YTLiteSection = 789; // Copy Settings functionality (DEFAULT - Copies to Clipboard) NSUserDefaults *userDefaults = [NSUserDefaults standardUserDefaults]; NSMutableString *settingsString = [NSMutableString string]; - for (NSString *key in copyKeys) { + for (NSString *key in NSUserDefaultsCopyKeys) { if ([userDefaults objectForKey:key]) { NSString *value = [userDefaults objectForKey:key]; [settingsString appendFormat:@"%@: %@\n", key, value]; @@ -194,6 +185,7 @@ static const NSInteger YTLiteSection = 789; ]; [sectionItems addObject:pasteSettings]; +# pragma mark - Video Player YTSettingsSectionItem *videoPlayer = [%c(YTSettingsSectionItem) itemWithTitle:LOC(@"VIDEO_PLAYER") titleDescription:LOC(@"VIDEO_PLAYER_DESC") diff --git a/Source/SettingsKeys.h b/Source/SettingsKeys.h new file mode 100644 index 0000000..96a5bfb --- /dev/null +++ b/Source/SettingsKeys.h @@ -0,0 +1,42 @@ +#import "../YTLitePlus.h" + +// Keys for "Copy Settings" button (for: YTLitePlus) +// In alphabetical order for tweaks after YTLitePlus +NSArray *NSUserDefaultsCopyKeys = @[ + // YTLitePlus - gathered using get_keys.py + @"YTTapToSeek_enabled", @"alwaysShowRemainingTime_enabled", @"bigYTMiniPlayer_enabled", @"castConfirm_enabled", + @"disableAccountSection_enabled", @"disableAmbientModeFullscreen_enabled", + @"disableAmbientModePortrait_enabled", @"disableAutoplaySection_enabled", @"disableCollapseButton_enabled", + @"disableEngagementOverlay_enabled", @"disableLiveChatSection_enabled", + @"disableManageAllHistorySection_enabled", @"disableNotificationsSection_enabled", + @"disablePrivacySection_enabled", @"disablePullToFull_enabled", @"disableRemainingTime_enabled", + @"disableTryNewFeaturesSection_enabled", @"disableVideoQualityPreferencesSection_enabled", + @"disableYourDataInYouTubeSection_enabled", @"enableSaveToButton_enabled", @"enableShareButton_enabled", + @"enableVersionSpoofer_enabled", @"fixCasting_enabled", @"flex_enabled", @"fullscreenToTheRight_enabled", + @"hideAutoplayMiniPreview_enabled", @"hideCastButton_enabled", @"hideHUD_enabled", @"hideHeatwaves_enabled", + @"hideHomeTab_enabled", @"hidePreviewCommentSection_enabled", @"hideRightPanel_enabled", + @"hideSpeedToast_enabled", @"hideSponsorBlockButton_enabled", @"hideVideoPlayerShadowOverlayButtons_enabled", + @"iPadLayout_enabled", @"iPhoneLayout_enabled", @"inline_muted_playback_enabled", @"lowContrastMode_enabled", + @"newSettingsUI_enabled", @"oledKeyBoard_enabled", @"playerGestures_enabled", @"seekAnywhere_enabled", + @"switchCopyandPasteFunctionality_enabled", @"ytNoModernUI_enabled", @"ytStartupAnimation_enabled", + // DEMC - https://github.com/therealFoxster/DontEatMyContent/blob/master/Tweak.h + @"DEMC_enabled", @"DEMC_colorViewsEnabled", @"DEMC_safeAreaConstant", @"DEMC_disableAmbientMode", + @"DEMC_limitZoomToFill", @"DEMC_enableForAllVideos", + // iSponsorBlock cannot be exported using this method - it is also being removed in v5 + // Return-YouTube-Dislike - https://github.com/PoomSmart/Return-YouTube-Dislikes/blob/main/TweakSettings.h + @"RYD-ENABLED", @"RYD-VOTE-SUBMISSION", @"RYD-EXACT-LIKE-NUMBER", @"RYD-EXACT-NUMBER", + // All YTVideoOverlay Tweaks - https://github.com/PoomSmart/YTVideoOverlay/blob/0fc6d29d1aa9e75f8c13d675daec9365f753d45e/Tweak.x#L28C1-L41C84 + @"YTVideoOverlay-YouLoop-Enabled", @"YTVideoOverlay-YouTimeStamp-Enabled", @"YTVideoOverlay-YouMute-Enabled", + @"YTVideoOverlay-YouQuality-Enabled", @"YTVideoOverlay-YouLoop-Position", @"YTVideoOverlay-YouTimeStamp-Position", + @"YTVideoOverlay-YouMute-Position", @"YTVideoOverlay-YouQuality-Position", + // YouPiP - https://github.com/PoomSmart/YouPiP/blob/main/Header.h + @"YouPiPPosition", @"CompatibilityModeKey", @"PiPActivationMethodKey", @"PiPActivationMethod2Key", + @"NoMiniPlayerPiPKey", @"NonBackgroundableKey", + // YTABConfig cannot be reasonably exported using this method + // YTHoldForSpeed will be removed in v5 + // YouTube Plus / YTLite cannot be exported using this method + // YTUHD - https://github.com/PoomSmart/YTUHD/blob/master/Header.h + @"EnableVP9", @"AllVP9", + // Useful YouTube Keys + @"inline_muted_playback_enabled", +]; diff --git a/Source/get_keys.py b/Source/get_keys.py new file mode 100644 index 0000000..e763e3a --- /dev/null +++ b/Source/get_keys.py @@ -0,0 +1,96 @@ +import re +import os + +def extract_values_from_file(file_path): + """ + Extracts keys that match the pattern @\"_enabled\" from the given file. + + Args: + file_path (str): The path to the file to be searched. + + Returns: + list: A list of matching keys found in the file. + """ + # Define the regex pattern to match the strings that resemble the given examples + pattern = r'@\"[a-zA-Z0-9_]+_enabled\"' + matches = [] + + try: + # Read the content of the file + with open(file_path, 'r') as file: + file_content = file.read() + + # Find all matches + matches = re.findall(pattern, file_content) + except Exception as e: + print(f"Error reading {file_path}: {e}") + + return matches + +def format_output(keys): + """ + Formats the keys with indentation and line breaks if the segment exceeds 120 characters (116 excluding indentation). + + Args: + keys (list): The list of keys to be formatted. + + Returns: + str: A formatted string with the keys. + """ + indent = " " * 4 + line_length_limit = 116 # Limit excluding indentation + current_line = indent + formatted_output = "" + + for key in keys: + # Check if adding the next key would exceed the line length limit + if len(current_line) + len(key) + 2 > line_length_limit: # +2 accounts for the comma and space + # Add the current line to the formatted output and start a new line + formatted_output += current_line.rstrip(", ") + ",\n" + current_line = indent # Start a new indented line + + # Add the key to the current line + current_line += key + ", " + + # Add the last line to the output + formatted_output += current_line.rstrip(", ") # Remove trailing comma and space from the final line + return formatted_output + +def find_and_extract_keys(): + """ + Recursively searches for .xm and .h files in the parent directory and extracts keys + that match the pattern @\"_enabled\". The matching keys are then printed + with indentation and line breaks if the line exceeds 120 characters. + + Usage: + 1. Place this script in the desired directory. + 2. Run the script with the command: python extract_keys.py + 3. The script will search for all .xm and .h files in the parent directory and + print any matching keys it finds. + + Note: + - The script searches the directory where it is located (the parent directory). + - It only looks for files with extensions .xm and .h. + """ + # Get the parent directory + parent_directory = os.path.dirname(os.path.abspath(__file__)) + + # Store the found keys + found_keys = set() # Use a set to automatically remove duplicates + + # Walk through the parent directory and find all .xm and .h files + for root, dirs, files in os.walk(parent_directory): + for file in files: + if file.endswith(('.xm', '.h')): + file_path = os.path.join(root, file) + found_keys.update(extract_values_from_file(file_path)) + + # Print the found keys with formatting + if found_keys: + sorted_keys = sorted(found_keys) + print(format_output(sorted_keys)) + else: + print("No keys found.") + +if __name__ == "__main__": + find_and_extract_keys() From beeb9eb19fb6b185b6d369484eecda3c4cc77d5c Mon Sep 17 00:00:00 2001 From: Bryce Hackel <34104885+bhackel@users.noreply.github.com> Date: Wed, 21 Aug 2024 22:46:32 -0700 Subject: [PATCH 02/47] Add default value ignore --- Source/Settings.xm | 10 ++++++++-- Source/SettingsKeys.h | 12 ++++++++++++ 2 files changed, 20 insertions(+), 2 deletions(-) diff --git a/Source/Settings.xm b/Source/Settings.xm index 00e49e7..eff47c3 100644 --- a/Source/Settings.xm +++ b/Source/Settings.xm @@ -132,13 +132,18 @@ static const NSInteger YTLiteSection = 789; NSUserDefaults *userDefaults = [NSUserDefaults standardUserDefaults]; NSMutableString *settingsString = [NSMutableString string]; for (NSString *key in NSUserDefaultsCopyKeys) { - if ([userDefaults objectForKey:key]) { - NSString *value = [userDefaults objectForKey:key]; + id value = [userDefaults objectForKey:key]; + id defaultValue = NSUserDefaultsCopyKeysDefaults[key]; + + // Only include the setting if it is different from the default value + // If no default value is found, include it by default + if (value && (!defaultValue || ![value isEqual:defaultValue])) { [settingsString appendFormat:@"%@: %@\n", key, value]; } } [[UIPasteboard generalPasteboard] setString:settingsString]; // Show a confirmation message or perform some other action here + [[%c(GOOHUDManagerInternal) sharedInstance] showMessageMainThread:[%c(YTHUDMessage) messageWithText:@"Settings copied"]]; } return YES; } @@ -176,6 +181,7 @@ static const NSInteger YTLiteSection = 789; } [settingsViewController reloadData]; // Show a confirmation message or perform some other action here + [[%c(GOOHUDManagerInternal) sharedInstance] showMessageMainThread:[%c(YTHUDMessage) messageWithText:@"Settings applied"]]; } }]]; [settingsViewController presentViewController:confirmPasteAlert animated:YES completion:nil]; diff --git a/Source/SettingsKeys.h b/Source/SettingsKeys.h index 96a5bfb..384f08b 100644 --- a/Source/SettingsKeys.h +++ b/Source/SettingsKeys.h @@ -40,3 +40,15 @@ NSArray *NSUserDefaultsCopyKeys = @[ // Useful YouTube Keys @"inline_muted_playback_enabled", ]; + + +// Some default values to ignore when exporting settings +NSDictionary *NSUserDefaultsCopyKeysDefaults = @{ + @"fixCasting_enabled": @1, + @"inline_muted_playback_enabled": @5, + @"newSettingsUI_enabled": @1, + @"DEMC_safeAreaConstant": @21.5, + @"RYD-ENABLED": @1, + @"RYD-VOTE-SUBMISSION": @1, + // Duplicate keys are not allowed in NSDictionary. If present, only the last one will be kept. +}; \ No newline at end of file From 6e9ec38c4a235ef0ee03d065b0e5247271baacf7 Mon Sep 17 00:00:00 2001 From: Bryce Hackel <34104885+bhackel@users.noreply.github.com> Date: Sat, 24 Aug 2024 16:30:30 -0700 Subject: [PATCH 03/47] Add reminder for YouTube Plus --- Source/Settings.xm | 15 +++++++++++++++ YTLitePlus.h | 1 + 2 files changed, 16 insertions(+) diff --git a/Source/Settings.xm b/Source/Settings.xm index eff47c3..94bccaf 100644 --- a/Source/Settings.xm +++ b/Source/Settings.xm @@ -145,6 +145,14 @@ static const NSInteger YTLiteSection = 789; // Show a confirmation message or perform some other action here [[%c(GOOHUDManagerInternal) sharedInstance] showMessageMainThread:[%c(YTHUDMessage) messageWithText:@"Settings copied"]]; } + // Prompt to export YouTube Plus settings + UIAlertController *exportAlert = [UIAlertController alertControllerWithTitle:@"Export Settings" message:@"Note: This cannot save iSponsorBlock and most YouTube settings.\nWould you like to also export your YouTube Plus Settings?" preferredStyle:UIAlertControllerStyleAlert]; + [exportAlert addAction:[UIAlertAction actionWithTitle:@"Cancel" style:UIAlertActionStyleCancel handler:nil]]; + [exportAlert addAction:[UIAlertAction actionWithTitle:@"Export" style:UIAlertActionStyleDefault handler:^(UIAlertAction * _Nonnull action) { + // Export YouTube Plus Settings functionality + [%c(YTLUserDefaults) exportYtlSettings]; + }]]; + [settingsViewController presentViewController:exportAlert animated:YES completion:nil]; return YES; } ]; @@ -186,6 +194,13 @@ static const NSInteger YTLiteSection = 789; }]]; [settingsViewController presentViewController:confirmPasteAlert animated:YES completion:nil]; } + // Reminder to import YouTube Plus settings + UIAlertController *reminderAlert = [UIAlertController alertControllerWithTitle:@"Reminder" + message:@"Remember to import your YouTube Plus settings as well." + preferredStyle:UIAlertControllerStyleAlert]; + [reminderAlert addAction:[UIAlertAction actionWithTitle:@"OK" style:UIAlertActionStyleDefault handler:nil]]; + [settingsViewController presentViewController:reminderAlert animated:YES completion:nil]; + return YES; } ]; diff --git a/YTLitePlus.h b/YTLitePlus.h index 8a41583..062a504 100644 --- a/YTLitePlus.h +++ b/YTLitePlus.h @@ -133,6 +133,7 @@ typedef NS_ENUM(NSUInteger, GestureSection) { // OLED Live Chat - @bhackel @interface YTLUserDefaults : NSUserDefaults ++ (void)exportYtlSettings; @end // Hide Home Tab - @bhackel From 2097308a8321e27d68c4bfe774fa3abd77000a15 Mon Sep 17 00:00:00 2001 From: Bryce Hackel <34104885+bhackel@users.noreply.github.com> Date: Sat, 24 Aug 2024 16:57:38 -0700 Subject: [PATCH 04/47] Improve localizations, fix bugs --- Source/Settings.xm | 57 +++++++++++++------ .../ar.lproj/Localizable.strings | 1 + .../bg.lproj/Localizable.strings | 1 + .../de.lproj/Localizable.strings | 1 + .../en.lproj/Localizable.strings | 1 + .../es.lproj/Localizable.strings | 1 + .../fr.lproj/Localizable.strings | 1 + .../ja.lproj/Localizable.strings | 1 + .../pt.lproj/Localizable.strings | 1 + .../ro.lproj/Localizable.strings | 1 + .../ru.lproj/Localizable.strings | 1 + .../template.lproj/Localizable.strings | 2 + .../tr.lproj/Localizable.strings | 1 + .../vi.lproj/Localizable.strings | 1 + .../zh_TW.lproj/Localizable.strings | 1 + 15 files changed, 55 insertions(+), 17 deletions(-) diff --git a/Source/Settings.xm b/Source/Settings.xm index 94bccaf..7861547 100644 --- a/Source/Settings.xm +++ b/Source/Settings.xm @@ -108,13 +108,13 @@ static const NSInteger YTLiteSection = 789; # pragma mark - Copy and Paste Settings YTSettingsSectionItem *copySettings = [%c(YTSettingsSectionItem) itemWithTitle:LOC(@"COPY_SETTINGS") - titleDescription:IS_ENABLED(@"switchCopyandPasteFunctionality_enabled") ? LOC(@"COPY_SETTINGS_DESC_2") : LOC(@"COPY_SETTINGS_DESC") + titleDescription:IS_ENABLED(@"switchCopyandPasteFunctionality_enabled") ? LOC(@"EXPORT_SETTINGS_DESC") : LOC(@"COPY_SETTINGS_DESC") accessibilityIdentifier:nil detailTextBlock:nil selectBlock:^BOOL (YTSettingsCell *cell, NSUInteger arg1) { if (IS_ENABLED(@"switchCopyandPasteFunctionality_enabled")) { // Export Settings functionality - NSURL *tempFileURL = [NSURL fileURLWithPath:[NSTemporaryDirectory() stringByAppendingPathComponent:@"exported_settings.txt"]]; + NSURL *tempFileURL = [NSURL fileURLWithPath:[NSTemporaryDirectory() stringByAppendingPathComponent:@"YTLitePlusSettings.txt"]]; NSMutableString *settingsString = [NSMutableString string]; for (NSString *key in NSUserDefaultsCopyKeys) { id value = [[NSUserDefaults standardUserDefaults] objectForKey:key]; @@ -146,7 +146,7 @@ static const NSInteger YTLiteSection = 789; [[%c(GOOHUDManagerInternal) sharedInstance] showMessageMainThread:[%c(YTHUDMessage) messageWithText:@"Settings copied"]]; } // Prompt to export YouTube Plus settings - UIAlertController *exportAlert = [UIAlertController alertControllerWithTitle:@"Export Settings" message:@"Note: This cannot save iSponsorBlock and most YouTube settings.\nWould you like to also export your YouTube Plus Settings?" preferredStyle:UIAlertControllerStyleAlert]; + UIAlertController *exportAlert = [UIAlertController alertControllerWithTitle:@"Export Settings" message:@"Note: This feature cannot save iSponsorBlock and most YouTube settings.\n\nWould you like to also export your YouTube Plus Settings?" preferredStyle:UIAlertControllerStyleAlert]; [exportAlert addAction:[UIAlertAction actionWithTitle:@"Cancel" style:UIAlertActionStyleCancel handler:nil]]; [exportAlert addAction:[UIAlertAction actionWithTitle:@"Export" style:UIAlertActionStyleDefault handler:^(UIAlertAction * _Nonnull action) { // Export YouTube Plus Settings functionality @@ -160,7 +160,7 @@ static const NSInteger YTLiteSection = 789; YTSettingsSectionItem *pasteSettings = [%c(YTSettingsSectionItem) itemWithTitle:LOC(@"PASTE_SETTINGS") - titleDescription:IS_ENABLED(@"switchCopyandPasteFunctionality_enabled") ? LOC(@"PASTE_SETTINGS_DESC_2") : LOC(@"PASTE_SETTINGS_DESC") + titleDescription:IS_ENABLED(@"switchCopyandPasteFunctionality_enabled") ? LOC(@"IMPORT_SETTINGS_DESC") : LOC(@"PASTE_SETTINGS_DESC") accessibilityIdentifier:nil detailTextBlock:nil selectBlock:^BOOL (YTSettingsCell *cell, NSUInteger arg1) { @@ -170,7 +170,6 @@ static const NSInteger YTLiteSection = 789; documentPicker.delegate = (id)self; documentPicker.allowsMultipleSelection = NO; [settingsViewController presentViewController:documentPicker animated:YES completion:nil]; - return YES; } else { // Paste Settings functionality (DEFAULT - Pastes from Clipboard) UIAlertController *confirmPasteAlert = [UIAlertController alertControllerWithTitle:LOC(@"PASTE_SETTINGS_ALERT") message:nil preferredStyle:UIAlertControllerStyleAlert]; @@ -194,6 +193,8 @@ static const NSInteger YTLiteSection = 789; }]]; [settingsViewController presentViewController:confirmPasteAlert animated:YES completion:nil]; } + // Show a toast message to confirm the action + [[%c(GOOHUDManagerInternal) sharedInstance] showMessageMainThread:[%c(YTHUDMessage) messageWithText:@"Settings pasted"]]; // Reminder to import YouTube Plus settings UIAlertController *reminderAlert = [UIAlertController alertControllerWithTitle:@"Reminder" message:@"Remember to import your YouTube Plus settings as well." @@ -689,23 +690,45 @@ static const NSInteger YTLiteSection = 789; // Implement the delegate method for document picker %new - (void)documentPicker:(UIDocumentPickerViewController *)controller didPickDocumentsAtURLs:(NSArray *)urls { - NSURL *pickedURL = [urls firstObject]; - - if (pickedURL) { - // Use AVPlayerViewController to play the video - AVPlayer *player = [AVPlayer playerWithURL:pickedURL]; - AVPlayerViewController *playerViewController = [[AVPlayerViewController alloc] init]; - playerViewController.player = player; - + if (urls.count > 0) { + NSURL *pickedURL = [urls firstObject]; + NSError *error; + NSString *fileType = [pickedURL resourceValuesForKeys:@[NSURLTypeIdentifierKey] error:&error][NSURLTypeIdentifierKey]; + UIViewController *settingsViewController = [self valueForKey:@"_settingsViewControllerDelegate"]; - if (settingsViewController) { - [settingsViewController presentViewController:playerViewController animated:YES completion:^{ - [player play]; - }]; + + if (UTTypeConformsTo((__bridge CFStringRef)fileType, kUTTypePlainText)) { + // This block handles the import of settings from a text file. + NSString *fileContents = [NSString stringWithContentsOfURL:pickedURL encoding:NSUTF8StringEncoding error:nil]; + NSArray *lines = [fileContents componentsSeparatedByString:@"\n"]; + for (NSString *line in lines) { + NSArray *components = [line componentsSeparatedByString:@": "]; + if (components.count == 2) { + NSString *key = components[0]; + NSString *value = components[1]; + [[NSUserDefaults standardUserDefaults] setObject:value forKey:key]; + } + } + if ([settingsViewController respondsToSelector:@selector(reloadData)]) { + // Call a custom reloadData method if it exists + [settingsViewController performSelector:@selector(reloadData)]; + } + } else if (UTTypeConformsTo((__bridge CFStringRef)fileType, kUTTypeMovie)) { + // This block handles video playback using AVPlayer and AVPlayerViewController. + AVPlayer *player = [AVPlayer playerWithURL:pickedURL]; + AVPlayerViewController *playerViewController = [[AVPlayerViewController alloc] init]; + playerViewController.player = player; + + if (settingsViewController) { + [settingsViewController presentViewController:playerViewController animated:YES completion:^{ + [player play]; + }]; + } } } } + %new - (void)documentPickerWasCancelled:(UIDocumentPickerViewController *)controller { // Handle cancellation if needed diff --git a/lang/YTLitePlus.bundle/ar.lproj/Localizable.strings b/lang/YTLitePlus.bundle/ar.lproj/Localizable.strings index a577d98..bbca947 100644 --- a/lang/YTLitePlus.bundle/ar.lproj/Localizable.strings +++ b/lang/YTLitePlus.bundle/ar.lproj/Localizable.strings @@ -6,6 +6,7 @@ "COPY_SETTINGS_DESC" = "Copy all current settings to the clipboard"; "PASTE_SETTINGS" = "Paste Settings"; "PASTE_SETTINGS_DESC" = "Paste settings from clipboard and apply"; +"PASTE_SETTINGS_ALERT" = "Apply settings from clipboard?"; "EXPORT_SETTINGS" = "Export Settings"; "EXPORT_SETTINGS_DESC" = "Exports all current settings into a .txt file"; "IMPORT_SETTINGS" = "Import Settings"; diff --git a/lang/YTLitePlus.bundle/bg.lproj/Localizable.strings b/lang/YTLitePlus.bundle/bg.lproj/Localizable.strings index 4689437..1bf547f 100644 --- a/lang/YTLitePlus.bundle/bg.lproj/Localizable.strings +++ b/lang/YTLitePlus.bundle/bg.lproj/Localizable.strings @@ -6,6 +6,7 @@ "COPY_SETTINGS_DESC" = "Копиране на всички текущи настройки в клипборда"; "PASTE_SETTINGS" = "Поставяне на настройки"; "PASTE_SETTINGS_DESC" = "Поставяне на настройки от клипборда и прилагане"; +"PASTE_SETTINGS_ALERT" = "Apply settings from clipboard?"; "EXPORT_SETTINGS" = "Експортиране на настройки"; "EXPORT_SETTINGS_DESC" = "Експортиране на всички текущи настройки в .txt файл"; "IMPORT_SETTINGS" = "Импортиране на настройки"; diff --git a/lang/YTLitePlus.bundle/de.lproj/Localizable.strings b/lang/YTLitePlus.bundle/de.lproj/Localizable.strings index 76a63fe..18e8820 100644 --- a/lang/YTLitePlus.bundle/de.lproj/Localizable.strings +++ b/lang/YTLitePlus.bundle/de.lproj/Localizable.strings @@ -6,6 +6,7 @@ "COPY_SETTINGS_DESC" = "Copy all current settings to the clipboard"; "PASTE_SETTINGS" = "Paste Settings"; "PASTE_SETTINGS_DESC" = "Paste settings from clipboard and apply"; +"PASTE_SETTINGS_ALERT" = "Apply settings from clipboard?"; "EXPORT_SETTINGS" = "Export Settings"; "EXPORT_SETTINGS_DESC" = "Exports all current settings into a .txt file"; "IMPORT_SETTINGS" = "Import Settings"; diff --git a/lang/YTLitePlus.bundle/en.lproj/Localizable.strings b/lang/YTLitePlus.bundle/en.lproj/Localizable.strings index 9801761..a892a2f 100644 --- a/lang/YTLitePlus.bundle/en.lproj/Localizable.strings +++ b/lang/YTLitePlus.bundle/en.lproj/Localizable.strings @@ -6,6 +6,7 @@ "COPY_SETTINGS_DESC" = "Copy all current settings to the clipboard"; "PASTE_SETTINGS" = "Paste Settings"; "PASTE_SETTINGS_DESC" = "Paste settings from clipboard and apply"; +"PASTE_SETTINGS_ALERT" = "Apply settings from clipboard?"; "EXPORT_SETTINGS" = "Export Settings"; "EXPORT_SETTINGS_DESC" = "Exports all current settings into a .txt file"; "IMPORT_SETTINGS" = "Import Settings"; diff --git a/lang/YTLitePlus.bundle/es.lproj/Localizable.strings b/lang/YTLitePlus.bundle/es.lproj/Localizable.strings index 1cf7433..739f5dd 100644 --- a/lang/YTLitePlus.bundle/es.lproj/Localizable.strings +++ b/lang/YTLitePlus.bundle/es.lproj/Localizable.strings @@ -6,6 +6,7 @@ "COPY_SETTINGS_DESC" = "Copy all current settings to the clipboard"; "PASTE_SETTINGS" = "Paste Settings"; "PASTE_SETTINGS_DESC" = "Paste settings from clipboard and apply"; +"PASTE_SETTINGS_ALERT" = "Apply settings from clipboard?"; "EXPORT_SETTINGS" = "Export Settings"; "EXPORT_SETTINGS_DESC" = "Exports all current settings into a .txt file"; "IMPORT_SETTINGS" = "Import Settings"; diff --git a/lang/YTLitePlus.bundle/fr.lproj/Localizable.strings b/lang/YTLitePlus.bundle/fr.lproj/Localizable.strings index e788b97..b493803 100644 --- a/lang/YTLitePlus.bundle/fr.lproj/Localizable.strings +++ b/lang/YTLitePlus.bundle/fr.lproj/Localizable.strings @@ -6,6 +6,7 @@ "COPY_SETTINGS_DESC" = "Copy all current settings to the clipboard"; "PASTE_SETTINGS" = "Paste Settings"; "PASTE_SETTINGS_DESC" = "Paste settings from clipboard and apply"; +"PASTE_SETTINGS_ALERT" = "Apply settings from clipboard?"; "EXPORT_SETTINGS" = "Export Settings"; "EXPORT_SETTINGS_DESC" = "Exports all current settings into a .txt file"; "IMPORT_SETTINGS" = "Import Settings"; diff --git a/lang/YTLitePlus.bundle/ja.lproj/Localizable.strings b/lang/YTLitePlus.bundle/ja.lproj/Localizable.strings index 2749ff2..ea2c349 100644 --- a/lang/YTLitePlus.bundle/ja.lproj/Localizable.strings +++ b/lang/YTLitePlus.bundle/ja.lproj/Localizable.strings @@ -6,6 +6,7 @@ "COPY_SETTINGS_DESC" = "現在のすべての設定をクリップボードにコピーします"; "PASTE_SETTINGS" = "設定を貼り付け"; "PASTE_SETTINGS_DESC" = "クリップボードから設定を貼り付けて適用します"; +"PASTE_SETTINGS_ALERT" = "Apply settings from clipboard?"; "EXPORT_SETTINGS" = "Export Settings"; "EXPORT_SETTINGS_DESC" = "Exports all current settings into a .txt file"; "IMPORT_SETTINGS" = "Import Settings"; diff --git a/lang/YTLitePlus.bundle/pt.lproj/Localizable.strings b/lang/YTLitePlus.bundle/pt.lproj/Localizable.strings index df987ed..db9ffcb 100644 --- a/lang/YTLitePlus.bundle/pt.lproj/Localizable.strings +++ b/lang/YTLitePlus.bundle/pt.lproj/Localizable.strings @@ -6,6 +6,7 @@ "COPY_SETTINGS_DESC" = "Copia todas as configurações atuais para a área de transferência"; "PASTE_SETTINGS" = "Colar Configurações"; "PASTE_SETTINGS_DESC" = "Cola as configurações da área de transferência e aplica"; +"PASTE_SETTINGS_ALERT" = "Apply settings from clipboard?"; "EXPORT_SETTINGS" = "Exportar Configurações"; "EXPORT_SETTINGS_DESC" = "Exporta todas as configurações atuais para um arquivo .txt"; "IMPORT_SETTINGS" = "Importar Configurações"; diff --git a/lang/YTLitePlus.bundle/ro.lproj/Localizable.strings b/lang/YTLitePlus.bundle/ro.lproj/Localizable.strings index 343c226..86905e3 100644 --- a/lang/YTLitePlus.bundle/ro.lproj/Localizable.strings +++ b/lang/YTLitePlus.bundle/ro.lproj/Localizable.strings @@ -6,6 +6,7 @@ "COPY_SETTINGS_DESC" = "Copy all current settings to the clipboard"; "PASTE_SETTINGS" = "Paste Settings"; "PASTE_SETTINGS_DESC" = "Paste settings from clipboard and apply"; +"PASTE_SETTINGS_ALERT" = "Apply settings from clipboard?"; "EXPORT_SETTINGS" = "Export Settings"; "EXPORT_SETTINGS_DESC" = "Exports all current settings into a .txt file"; "IMPORT_SETTINGS" = "Import Settings"; diff --git a/lang/YTLitePlus.bundle/ru.lproj/Localizable.strings b/lang/YTLitePlus.bundle/ru.lproj/Localizable.strings index 5e6bb59..b09cc70 100644 --- a/lang/YTLitePlus.bundle/ru.lproj/Localizable.strings +++ b/lang/YTLitePlus.bundle/ru.lproj/Localizable.strings @@ -6,6 +6,7 @@ "COPY_SETTINGS_DESC" = "Copy all current settings to the clipboard"; "PASTE_SETTINGS" = "Paste Settings"; "PASTE_SETTINGS_DESC" = "Paste settings from clipboard and apply"; +"PASTE_SETTINGS_ALERT" = "Apply settings from clipboard?"; "EXPORT_SETTINGS" = "Export Settings"; "EXPORT_SETTINGS_DESC" = "Exports all current settings into a .txt file"; "IMPORT_SETTINGS" = "Import Settings"; diff --git a/lang/YTLitePlus.bundle/template.lproj/Localizable.strings b/lang/YTLitePlus.bundle/template.lproj/Localizable.strings index 413a20a..308b84c 100644 --- a/lang/YTLitePlus.bundle/template.lproj/Localizable.strings +++ b/lang/YTLitePlus.bundle/template.lproj/Localizable.strings @@ -21,6 +21,8 @@ https://github.com/PoomSmart/Return-YouTube-Dislikes/tree/main/layout/Library/Ap "COPY_SETTINGS_DESC" = "Copy all current settings to the clipboard"; "PASTE_SETTINGS" = "Paste Settings"; "PASTE_SETTINGS_DESC" = "Paste settings from clipboard and apply"; +"PASTE_SETTINGS_ALERT" = "Apply settings from clipboard?"; + "EXPORT_SETTINGS" = "Export Settings"; "EXPORT_SETTINGS_DESC" = "Exports all current settings into a .txt file"; "IMPORT_SETTINGS" = "Import Settings"; diff --git a/lang/YTLitePlus.bundle/tr.lproj/Localizable.strings b/lang/YTLitePlus.bundle/tr.lproj/Localizable.strings index a9b0ea3..a845fe7 100644 --- a/lang/YTLitePlus.bundle/tr.lproj/Localizable.strings +++ b/lang/YTLitePlus.bundle/tr.lproj/Localizable.strings @@ -6,6 +6,7 @@ "COPY_SETTINGS_DESC" = "Tüm mevcut ayarları panoya kopyala"; "PASTE_SETTINGS" = "Ayarları Yapıştır"; "PASTE_SETTINGS_DESC" = "Panodaki ayarları yapıştır ve uygula"; +"PASTE_SETTINGS_ALERT" = "Apply settings from clipboard?"; "EXPORT_SETTINGS" = "Ayarları Dışa Aktar"; "EXPORT_SETTINGS_DESC" = "Tüm mevcut ayarları bir .txt dosyasına dışa aktarır"; "IMPORT_SETTINGS" = "Ayarları İçe Aktar"; diff --git a/lang/YTLitePlus.bundle/vi.lproj/Localizable.strings b/lang/YTLitePlus.bundle/vi.lproj/Localizable.strings index dd3fdb0..d2c81b3 100644 --- a/lang/YTLitePlus.bundle/vi.lproj/Localizable.strings +++ b/lang/YTLitePlus.bundle/vi.lproj/Localizable.strings @@ -6,6 +6,7 @@ "COPY_SETTINGS_DESC" = "Copy all current settings to the clipboard"; "PASTE_SETTINGS" = "Paste Settings"; "PASTE_SETTINGS_DESC" = "Paste settings from clipboard and apply"; +"PASTE_SETTINGS_ALERT" = "Apply settings from clipboard?"; "EXPORT_SETTINGS" = "Export Settings"; "EXPORT_SETTINGS_DESC" = "Exports all current settings into a .txt file"; "IMPORT_SETTINGS" = "Import Settings"; diff --git a/lang/YTLitePlus.bundle/zh_TW.lproj/Localizable.strings b/lang/YTLitePlus.bundle/zh_TW.lproj/Localizable.strings index aef5cf3..e48afb9 100644 --- a/lang/YTLitePlus.bundle/zh_TW.lproj/Localizable.strings +++ b/lang/YTLitePlus.bundle/zh_TW.lproj/Localizable.strings @@ -7,6 +7,7 @@ "COPY_SETTINGS_DESC" = "Copy all current settings to the clipboard"; "PASTE_SETTINGS" = "Paste Settings"; "PASTE_SETTINGS_DESC" = "Paste settings from clipboard and apply"; +"PASTE_SETTINGS_ALERT" = "Apply settings from clipboard?"; "EXPORT_SETTINGS" = "Export Settings"; "EXPORT_SETTINGS_DESC" = "Exports all current settings into a .txt file"; "IMPORT_SETTINGS" = "Import Settings"; From aa5300f54f2b6eada861b82ff9ebad0c2da93139 Mon Sep 17 00:00:00 2001 From: Bryce Hackel <34104885+bhackel@users.noreply.github.com> Date: Sun, 25 Aug 2024 15:01:03 -0700 Subject: [PATCH 05/47] Refactor, update seek implementation, improve speed --- YTLitePlus.h | 10 ++ YTLitePlus.xm | 247 ++++++++++++++++++++++++++++++++++++-------------- 2 files changed, 188 insertions(+), 69 deletions(-) diff --git a/YTLitePlus.h b/YTLitePlus.h index 34c0deb..5bb9961 100644 --- a/YTLitePlus.h +++ b/YTLitePlus.h @@ -162,6 +162,16 @@ typedef NS_ENUM(NSUInteger, GestureSection) { @interface MPVolumeController : NSObject @property (nonatomic, assign, readwrite) float volumeValue; @end +@interface YTPlayerBarController (YTLitePlus) +- (void)inlinePlayerBarContainerViewDidStartFineScrub:(YTInlinePlayerBarContainerView *)playerBar; +- (void)inlinePlayerBarContainerView:(YTInlinePlayerBarContainerView *)playerBar didFineScrubToTime:(CGFloat)time; +- (void)inlinePlayerBarContainerViewDidEndFineScrub:(YTInlinePlayerBarContainerView *)playerBar seekSource:(int)source; +- (void)didScrub:(UIPanGestureRecognizer *)gestureRecognizer; +- (void)seekAnywhereDidScrubWithRecognizer:(UIPanGestureRecognizer *)recognizer; +@end +@interface YTMainAppVideoPlayerOverlayViewController (YTLitePlus) +@property (nonatomic, strong, readwrite) YTPlayerBarController *playerBarController; +@end // Hide Collapse Button - @arichornlover @interface YTMainAppControlsOverlayView (YTLitePlus) diff --git a/YTLitePlus.xm b/YTLitePlus.xm index eac6062..ba0da3e 100644 --- a/YTLitePlus.xm +++ b/YTLitePlus.xm @@ -641,7 +641,7 @@ BOOL isTabSelected = NO; %end // Gestures - @bhackel -%group playerGestures +%group gPlayerGestures %hook YTWatchLayerViewController // invoked when the player view controller is either created or destroyed - (void)watchController:(YTWatchController *)watchController didSetPlayerViewController:(YTPlayerViewController *)playerViewController { @@ -662,10 +662,17 @@ BOOL isTabSelected = NO; %hook YTPlayerViewController // the pan gesture that will be created and added to the player view %property (nonatomic, retain) UIPanGestureRecognizer *YTLitePlusPanGesture; +/** + * This method is called when the pan gesture is started, changed, or ended. It handles + * 12 different possible cases depending on the configuration: 3 zones with 4 choices + * for each zone. The zones are horizontal sections that divide the player into + * 3 equal parts. The choices are volume, brightness, seek, and disabled. + * There is also a deadzone that can be configured in the settings. + */ %new - (void)YTLitePlusHandlePanGesture:(UIPanGestureRecognizer *)panGestureRecognizer { // Haptic feedback generator - static UIImpactFeedbackGenerator *feedbackGenerator = [[UIImpactFeedbackGenerator alloc] initWithStyle:UIImpactFeedbackStyleMedium]; + static UIImpactFeedbackGenerator *feedbackGenerator; // Variables for storing initial values to be adjusted static float initialVolume; static float initialBrightness; @@ -678,62 +685,116 @@ BOOL isTabSelected = NO; static CGPoint startLocation; // Variable to track the X translation when exiting the deadzone static CGFloat deadzoneStartingXTranslation; + // Variable to track the X translation of the pan gesture after exiting the deadzone + static CGFloat adjustedTranslationX; // Constant for the deadzone radius that can be changed in the settings static CGFloat deadzoneRadius = (CGFloat)GetFloat(@"playerGesturesDeadzone"); // Constant for the sensitivity factor that can be changed in the settings static CGFloat sensitivityFactor = (CGFloat)GetFloat(@"playerGesturesSensitivity"); - -/***** Helper functions *****/ - // Helper function to adjust brightness - void (^adjustBrightness)(CGFloat, CGFloat) = ^(CGFloat translationX, CGFloat initialBrightness) { - float newBrightness = initialBrightness + ((translationX / 1000.0) * sensitivityFactor); - newBrightness = fmaxf(fminf(newBrightness, 1.0), 0.0); - [[UIScreen mainScreen] setBrightness:newBrightness]; - }; - // Helper function to adjust volume - void (^adjustVolume)(CGFloat, CGFloat) = ^(CGFloat translationX, CGFloat initialVolume) { - float newVolume = initialVolume + ((translationX / 1000.0) * sensitivityFactor); - newVolume = fmaxf(fminf(newVolume, 1.0), 0.0); - // https://stackoverflow.com/questions/50737943/how-to-change-volume-programmatically-on-ios-11-4 - MPVolumeView *volumeView = [[MPVolumeView alloc] init]; - UISlider *volumeViewSlider = nil; + // Objects for modifying the system volume + static MPVolumeView *volumeView; + static UISlider *volumeViewSlider; + // Get objects that should only be initialized once + static dispatch_once_t onceToken; + dispatch_once(&onceToken, ^{ + volumeView = [[MPVolumeView alloc] init]; for (UIView *view in volumeView.subviews) { if ([view isKindOfClass:[UISlider class]]) { volumeViewSlider = (UISlider *)view; break; } } + feedbackGenerator = [[UIImpactFeedbackGenerator alloc] initWithStyle:UIImpactFeedbackStyleMedium]; + }); + // Get objects used to seek nicely in the video player + static YTMainAppVideoPlayerOverlayViewController *mainVideoPlayerController = (YTMainAppVideoPlayerOverlayViewController *)self.childViewControllers.firstObject; + static YTPlayerBarController *playerBarController = mainVideoPlayerController.playerBarController; + // static YTInlinePlayerBarContainerView *playerBar = playerBarController.playerBar; + +/***** Helper functions for adjusting player state *****/ + // Helper function to adjust brightness + void (^adjustBrightness)(CGFloat, CGFloat) = ^(CGFloat translationX, CGFloat initialBrightness) { + float brightnessSensitivityFactor = 2.0; + float newBrightness = initialBrightness + ((translationX / 1000.0) * sensitivityFactor * brightnessSensitivityFactor); + newBrightness = fmaxf(fminf(newBrightness, 1.0), 0.0); + [[UIScreen mainScreen] setBrightness:newBrightness]; + }; + + // Helper function to adjust volume + void (^adjustVolume)(CGFloat, CGFloat) = ^(CGFloat translationX, CGFloat initialVolume) { + float volumeSensitivityFactor = 2.0; + float newVolume = initialVolume + ((translationX / 1000.0) * sensitivityFactor * volumeSensitivityFactor); + newVolume = fmaxf(fminf(newVolume, 1.0), 0.0); + // https://stackoverflow.com/questions/50737943/how-to-change-volume-programmatically-on-ios-11-4 + dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0.01 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{ volumeViewSlider.value = newVolume; }); }; + // Helper function to adjust seek time - void (^adjustSeek)(CGFloat, CGFloat) = ^(CGFloat translationX, CGFloat currentTime) { - // Calculate a seek fraction based on the horizontal translation - CGFloat totalDuration = self.currentVideoTotalMediaTime; - CGFloat viewWidth = self.view.bounds.size.width; - CGFloat seekFraction = (translationX / viewWidth); - // Seek to the new time based on the calculated offset - CGFloat sensitivityFactor = 1; // Adjust this value to make seeking less sensitive - seekFraction = sensitivityFactor * seekFraction; - CGFloat seekTime = currentTime + totalDuration * seekFraction; - [self seekToTime:seekTime]; + // void (^adjustSeek)(CGFloat, CGFloat) = ^(CGFloat translationX, CGFloat currentTime) { + // // Calculate a seek fraction based on the horizontal translation + // CGFloat totalDuration = self.currentVideoTotalMediaTime; + // CGFloat viewWidth = self.view.bounds.size.width; + // CGFloat seekFraction = (translationX / viewWidth); + // // Calculate the new seek time based on the calculated offset + // CGFloat sensitivityFactor = 1; // Adjust this value to make seeking more/less sensitive + // seekFraction = sensitivityFactor * seekFraction; + // CGFloat seekTime = currentTime + totalDuration * seekFraction; + // // Seek to the new time + // [playerBarController inlinePlayerBarContainerView:playerBar didFineScrubToTime:seekTime]; + // }; + +/***** Helper functions for running the selected gesture *****/ + // Helper function to run any setup for the selected gesture mode + void (^runSelectedGestureSetup)(NSString*) = ^(NSString *sectionKey) { + // Determine the selected gesture mode using the section key + GestureMode selectedGestureMode = (GestureMode)GetInteger(sectionKey); + // Handle the setup based on the selected mode + switch (selectedGestureMode) { + case GestureModeVolume: + // Store initial volume value + initialVolume = [[AVAudioSession sharedInstance] outputVolume]; + break; + case GestureModeBrightness: + // Store the initial brightness value + initialBrightness = [UIScreen mainScreen].brightness; + break; + case GestureModeSeek: + // Store the current time value + currentTime = self.currentVideoMediaTime; + // Start a seek action + [playerBarController seekAnywhereDidScrubWithRecognizer:panGestureRecognizer]; + break; + case GestureModeDisabled: + // Do nothing if the gesture is disabled + break; + default: + // Show an alert if the gesture mode is invalid + UIAlertController *alertController = [UIAlertController alertControllerWithTitle:@"Invalid Gesture Mode" message:@"Please report this bug." preferredStyle:UIAlertControllerStyleAlert]; + UIAlertAction *okAction = [UIAlertAction actionWithTitle:@"OK" style:UIAlertActionStyleDefault handler:nil]; + [alertController addAction:okAction]; + [self presentViewController:alertController animated:YES completion:nil]; + break; + } }; - // Helper function to run the selected gesture action - void (^runSelectedGesture)(NSString*, CGFloat, CGFloat, CGFloat, CGFloat) - = ^(NSString *sectionKey, CGFloat translationX, CGFloat initialBrightness, CGFloat initialVolume, CGFloat currentTime) { + + // Helper function to run the selected gesture action when the gesture changes + void (^runSelectedGestureChanged)(NSString*) = ^(NSString *sectionKey) { // Determine the selected gesture mode using the section key GestureMode selectedGestureMode = (GestureMode)GetInteger(sectionKey); // Handle the gesture action based on the selected mode switch (selectedGestureMode) { case GestureModeVolume: - adjustVolume(translationX, initialVolume); + adjustVolume(adjustedTranslationX, initialVolume); break; case GestureModeBrightness: - adjustBrightness(translationX, initialBrightness); + adjustBrightness(adjustedTranslationX, initialBrightness); break; case GestureModeSeek: - adjustSeek(translationX, currentTime); + // adjustSeek(adjustedTranslationX, currentTime); + [playerBarController seekAnywhereDidScrubWithRecognizer:panGestureRecognizer]; break; case GestureModeDisabled: // Do nothing if the gesture is disabled @@ -747,6 +808,31 @@ BOOL isTabSelected = NO; break; } }; + + // Helper function to run the selected gesture action when the gesture ends + void (^runSelectedGestureEnded)(NSString*) = ^(NSString *sectionKey) { + // Determine the selected gesture mode using the section key + GestureMode selectedGestureMode = (GestureMode)GetInteger(sectionKey); + // Handle the gesture action based on the selected mode + switch (selectedGestureMode) { + case GestureModeVolume: + break; + case GestureModeBrightness: + break; + case GestureModeSeek: + [playerBarController seekAnywhereDidScrubWithRecognizer:panGestureRecognizer]; + break; + case GestureModeDisabled: + break; + default: + // Show an alert if the gesture mode is invalid + UIAlertController *alertController = [UIAlertController alertControllerWithTitle:@"Invalid Gesture Mode" message:@"Please report this bug." preferredStyle:UIAlertControllerStyleAlert]; + UIAlertAction *okAction = [UIAlertAction actionWithTitle:@"OK" style:UIAlertActionStyleDefault handler:nil]; + [alertController addAction:okAction]; + [self presentViewController:alertController animated:YES completion:nil]; + break; + } + }; /***** End of Helper functions *****/ // Handle gesture based on current gesture state @@ -754,37 +840,29 @@ BOOL isTabSelected = NO; // Get the gesture's start position startLocation = [panGestureRecognizer locationInView:self.view]; CGFloat viewHeight = self.view.bounds.size.height; - - // Determine the section based on the start position - // by dividing the view into thirds + // Determine the section based on the start position by dividing the view into thirds if (startLocation.y <= viewHeight / 3.0) { gestureSection = GestureSectionTop; - // Cancel the gesture if the mode is disabled - if (GetInteger(@"playerGestureTopSelection") == GestureModeDisabled) { - panGestureRecognizer.state = UIGestureRecognizerStateCancelled; - return; - } } else if (startLocation.y <= 2 * viewHeight / 3.0) { gestureSection = GestureSectionMiddle; - // Cancel the gesture if the mode is disabled - if (GetInteger(@"playerGestureMiddleSelection") == GestureModeDisabled) { - panGestureRecognizer.state = UIGestureRecognizerStateCancelled; - return; - } } else if (startLocation.y <= viewHeight) { gestureSection = GestureSectionBottom; - // Cancel the gesture if the mode is disabled - if (GetInteger(@"playerGestureBottomSelection") == GestureModeDisabled) { - panGestureRecognizer.state = UIGestureRecognizerStateCancelled; - return; - } } else { gestureSection = GestureSectionInvalid; } + // Cancel the gesture if the chosen mode for this section is disabled + if ( ((gestureSection == GestureSectionTop) && (GetInteger(@"playerGestureTopSelection") == GestureModeDisabled)) + || ((gestureSection == GestureSectionMiddle) && (GetInteger(@"playerGestureMiddleSelection") == GestureModeDisabled)) + || ((gestureSection == GestureSectionBottom) && (GetInteger(@"playerGestureBottomSelection") == GestureModeDisabled))) { + panGestureRecognizer.state = UIGestureRecognizerStateCancelled; + return; + } // Deactive the activity flag isValidHorizontalPan = NO; } + // Handle changed gesture state by activating the gesture once it has exited the deadzone, + // and then adjusting the player based on the selected gesture mode if (panGestureRecognizer.state == UIGestureRecognizerStateChanged) { // Determine if the gesture is predominantly horizontal CGPoint translation = [panGestureRecognizer translationInView:self.view]; @@ -799,9 +877,22 @@ BOOL isTabSelected = NO; // If outside the deadzone, activate the pan gesture and store the initial values isValidHorizontalPan = YES; deadzoneStartingXTranslation = translation.x; - initialBrightness = [UIScreen mainScreen].brightness; - initialVolume = [[AVAudioSession sharedInstance] outputVolume]; - currentTime = self.currentVideoMediaTime; + // Run the setup for the selected gesture mode + switch (gestureSection) { + case GestureSectionTop: + runSelectedGestureSetup(@"playerGestureTopSelection"); + break; + case GestureSectionMiddle: + runSelectedGestureSetup(@"playerGestureMiddleSelection"); + break; + case GestureSectionBottom: + runSelectedGestureSetup(@"playerGestureBottomSelection"); + break; + default: + // If the section is invalid, cancel the gesture + panGestureRecognizer.state = UIGestureRecognizerStateCancelled; + break; + } // Provide haptic feedback to indicate a gesture start [feedbackGenerator prepare]; [feedbackGenerator impactOccurred]; @@ -814,27 +905,45 @@ BOOL isTabSelected = NO; // Handle the gesture based on the identified section if (isValidHorizontalPan) { - // Adjust the X translation based on the value hit after - // exiting the deadzone - CGFloat adjustedTranslationX = translation.x - deadzoneStartingXTranslation; + // Adjust the X translation based on the value hit after exiting the deadzone + adjustedTranslationX = translation.x - deadzoneStartingXTranslation; // Pass the adjusted translation to the selected gesture - if (gestureSection == GestureSectionTop) { - runSelectedGesture(@"playerGestureTopSelection", adjustedTranslationX, initialBrightness, initialVolume, currentTime); - } else if (gestureSection == GestureSectionMiddle) { - runSelectedGesture(@"playerGestureMiddleSelection", adjustedTranslationX, initialBrightness, initialVolume, currentTime); - } else if (gestureSection == GestureSectionBottom) { - runSelectedGesture(@"playerGestureBottomSelection", adjustedTranslationX, initialBrightness, initialVolume, currentTime); - } else { - // If the section is invalid, cancel the gesture - panGestureRecognizer.state = UIGestureRecognizerStateCancelled; + switch (gestureSection) { + case GestureSectionTop: + runSelectedGestureChanged(@"playerGestureTopSelection"); + break; + case GestureSectionMiddle: + runSelectedGestureChanged(@"playerGestureMiddleSelection"); + break; + case GestureSectionBottom: + runSelectedGestureChanged(@"playerGestureBottomSelection"); + break; + default: + // If the section is invalid, cancel the gesture + panGestureRecognizer.state = UIGestureRecognizerStateCancelled; + break; } } } if (panGestureRecognizer.state == UIGestureRecognizerStateEnded) { if (isValidHorizontalPan) { + // Handle the gesture based on the identified section + switch (gestureSection) { + case GestureSectionTop: + runSelectedGestureEnded(@"playerGestureTopSelection"); + break; + case GestureSectionMiddle: + runSelectedGestureEnded(@"playerGestureMiddleSelection"); + break; + case GestureSectionBottom: + runSelectedGestureEnded(@"playerGestureBottomSelection"); + break; + default: + break; + } // Provide haptic feedback upon successful gesture recognition - [feedbackGenerator prepare]; - [feedbackGenerator impactOccurred]; + // [feedbackGenerator prepare]; + // [feedbackGenerator impactOccurred]; } } @@ -1062,7 +1171,7 @@ BOOL isTabSelected = NO; %init(gDisableEngagementOverlay); } if (IsEnabled(@"playerGestures_enabled")) { - %init(playerGestures); + %init(gPlayerGestures); } // Change the default value of some options From fa922a1acd48d1d48b67b838ee454534495db465 Mon Sep 17 00:00:00 2001 From: Bryce Hackel <34104885+bhackel@users.noreply.github.com> Date: Sun, 25 Aug 2024 14:04:32 -0700 Subject: [PATCH 06/47] Add player button in navbar --- Source/Settings.xm | 53 +--------- YTLitePlus.h | 5 +- YTLitePlus.xm | 100 ++++++++++++++++++ .../YTLitePlusColored-1024.png | Bin 0 -> 10981 bytes .../YTLitePlusDarkMode-1024.png | Bin 0 -> 11289 bytes .../YTLitePlusLightMode-1024.png | Bin 0 -> 14797 bytes .../ar.lproj/Localizable.strings | 3 + .../bg.lproj/Localizable.strings | 3 + .../de.lproj/Localizable.strings | 3 + .../en.lproj/Localizable.strings | 3 + .../es.lproj/Localizable.strings | 3 + .../fr.lproj/Localizable.strings | 3 + .../ja.lproj/Localizable.strings | 3 + .../pt.lproj/Localizable.strings | 3 + .../ro.lproj/Localizable.strings | 3 + .../ru.lproj/Localizable.strings | 3 + .../template.lproj/Localizable.strings | 3 + .../tr.lproj/Localizable.strings | 3 + .../vi.lproj/Localizable.strings | 3 + .../zh_TW.lproj/Localizable.strings | 3 + 20 files changed, 148 insertions(+), 52 deletions(-) create mode 100644 lang/YTLitePlus.bundle/YTLitePlusColored-1024.png create mode 100644 lang/YTLitePlus.bundle/YTLitePlusDarkMode-1024.png create mode 100644 lang/YTLitePlus.bundle/YTLitePlusLightMode-1024.png diff --git a/Source/Settings.xm b/Source/Settings.xm index 5bfd862..c182f43 100644 --- a/Source/Settings.xm +++ b/Source/Settings.xm @@ -41,8 +41,6 @@ static int appVersionSpoofer() { @interface YTSettingsSectionItemManager (YTLitePlus) - (void)updateYTLitePlusSectionWithEntry:(id)entry; -- (void)documentPicker:(UIDocumentPickerViewController *)controller didPickDocumentsAtURLs:(NSArray *)urls; -- (void)documentPickerWasCancelled:(UIDocumentPickerViewController *)controller; @end extern NSBundle *YTLitePlusBundle(); @@ -194,29 +192,6 @@ static const NSInteger YTLiteSection = 789; ]; [sectionItems addObject:pasteSettings]; - YTSettingsSectionItem *videoPlayer = [%c(YTSettingsSectionItem) - itemWithTitle:LOC(@"VIDEO_PLAYER") - titleDescription:LOC(@"VIDEO_PLAYER_DESC") - accessibilityIdentifier:nil - detailTextBlock:nil - selectBlock:^BOOL (YTSettingsCell *cell, NSUInteger arg1) { - // Access the current view controller - UIViewController *settingsViewController = [self valueForKey:@"_settingsViewControllerDelegate"]; - if (settingsViewController) { - // Present the video picker - UIDocumentPickerViewController *documentPicker = [[UIDocumentPickerViewController alloc] initWithDocumentTypes:@[(NSString *)kUTTypeMovie, (NSString *)kUTTypeVideo] inMode:UIDocumentPickerModeImport]; - documentPicker.delegate = (id)self; - documentPicker.allowsMultipleSelection = NO; - [settingsViewController presentViewController:documentPicker animated:YES completion:nil]; - } else { - NSLog(@"settingsViewController is nil"); - } - - return YES; // Return YES to indicate that the action was handled - } - ]; - [sectionItems addObject:videoPlayer]; - /* YTSettingsSectionItem *appIcon = [%c(YTSettingsSectionItem) itemWithTitle:LOC(@"CHANGE_APP_ICON") @@ -652,6 +627,7 @@ static const NSInteger YTLiteSection = 789; BASIC_SWITCH(LOC(@"CAST_CONFIRM"), LOC(@"CAST_CONFIRM_DESC"), @"castConfirm_enabled"), BASIC_SWITCH(LOC(@"NEW_MINIPLAYER_STYLE"), LOC(@"NEW_MINIPLAYER_STYLE_DESC"), @"bigYTMiniPlayer_enabled"), BASIC_SWITCH(LOC(@"HIDE_CAST_BUTTON"), LOC(@"HIDE_CAST_BUTTON_DESC"), @"hideCastButton_enabled"), + BASIC_SWITCH(LOC(@"VIDEO_PLAYER_BUTTON"), LOC(@"VIDEO_PLAYER_BUTTON_DESC"), @"videoPlayerButton_enabled"), BASIC_SWITCH(LOC(@"HIDE_SPONSORBLOCK_BUTTON"), LOC(@"HIDE_SPONSORBLOCK_BUTTON_DESC"), @"hideSponsorBlockButton_enabled"), BASIC_SWITCH(LOC(@"HIDE_HOME_TAB"), LOC(@"HIDE_HOME_TAB_DESC"), @"hideHomeTab_enabled"), BASIC_SWITCH(LOC(@"FIX_CASTING"), LOC(@"FIX_CASTING_DESC"), @"fixCasting_enabled"), @@ -679,30 +655,5 @@ static const NSInteger YTLiteSection = 789; %orig; } -// Implement the delegate method for document picker -%new -- (void)documentPicker:(UIDocumentPickerViewController *)controller didPickDocumentsAtURLs:(NSArray *)urls { - NSURL *pickedURL = [urls firstObject]; - - if (pickedURL) { - // Use AVPlayerViewController to play the video - AVPlayer *player = [AVPlayer playerWithURL:pickedURL]; - AVPlayerViewController *playerViewController = [[AVPlayerViewController alloc] init]; - playerViewController.player = player; - - UIViewController *settingsViewController = [self valueForKey:@"_settingsViewControllerDelegate"]; - if (settingsViewController) { - [settingsViewController presentViewController:playerViewController animated:YES completion:^{ - [player play]; - }]; - } - } -} - -%new -- (void)documentPickerWasCancelled:(UIDocumentPickerViewController *)controller { - // Handle cancellation if needed - NSLog(@"Document picker was cancelled"); -} - %end + diff --git a/YTLitePlus.h b/YTLitePlus.h index 34c0deb..57184f1 100644 --- a/YTLitePlus.h +++ b/YTLitePlus.h @@ -46,6 +46,8 @@ #import "Tweaks/YouTubeHeader/YTMainAppControlsOverlayView.h" #import "Tweaks/YouTubeHeader/YTMultiSizeViewController.h" #import "Tweaks/YouTubeHeader/YTWatchLayerViewController.h" +#import "Tweaks/YouTubeHeader/YTPageStyleController.h" +#import "Tweaks/YouTubeHeader/YTRightNavigationButtons.h" #define LOC(x) [tweakBundle localizedStringForKey:x value:nil table:nil] #define YT_BUNDLE_ID @"com.google.ios.youtube" @@ -172,9 +174,10 @@ typedef NS_ENUM(NSUInteger, GestureSection) { @interface MDCButton : UIButton @end -@interface YTRightNavigationButtons : UIView +@interface YTRightNavigationButtons (YTLitePlus) @property YTQTMButton *notificationButton; @property YTQTMButton *sponsorBlockButton; +@property YTQTMButton *videoPlayerButton; @end // BigYTMiniPlayer diff --git a/YTLitePlus.xm b/YTLitePlus.xm index eac6062..9214f64 100644 --- a/YTLitePlus.xm +++ b/YTLitePlus.xm @@ -872,6 +872,100 @@ BOOL isTabSelected = NO; %end %end +// Video player button in the navigation bar - @bhackel +// This code is based on the iSponsorBlock button code +%group gVideoPlayerButton +NSInteger pageStyle = 0; +%hook YTRightNavigationButtons +%property (retain, nonatomic) YTQTMButton *videoPlayerButton; +- (NSMutableArray *)buttons { + NSMutableArray *retVal = %orig.mutableCopy; + [self.videoPlayerButton removeFromSuperview]; + [self addSubview:self.videoPlayerButton]; + if (!self.videoPlayerButton || pageStyle != [%c(YTPageStyleController) pageStyle]) { + self.videoPlayerButton = [%c(YTQTMButton) iconButton]; + [self.videoPlayerButton enableNewTouchFeedback]; + self.videoPlayerButton.frame = CGRectMake(0, 0, 40, 40); + + if ([%c(YTPageStyleController) pageStyle]) { //dark mode + [self.videoPlayerButton setImage:[UIImage imageWithContentsOfFile:[tweakBundle pathForResource:@"YTLitePlusColored-1024" ofType:@"png"]] forState:UIControlStateNormal]; + } + else { // light mode + UIImage *image = [UIImage imageWithContentsOfFile:[tweakBundle pathForResource:@"YTLitePlusColored-1024" ofType:@"png"]]; + image = [image imageWithTintColor:UIColor.blackColor renderingMode:UIImageRenderingModeAlwaysTemplate]; + [self.videoPlayerButton setImage:image forState:UIControlStateNormal]; + [self.videoPlayerButton setTintColor:UIColor.blackColor]; + } + pageStyle = [%c(YTPageStyleController) pageStyle]; + + [self.videoPlayerButton addTarget:self action:@selector(videoPlayerButtonPressed:) forControlEvents:UIControlEventTouchUpInside]; + [retVal insertObject:self.videoPlayerButton atIndex:0]; + } + return retVal; +} +- (NSMutableArray *)visibleButtons { + NSMutableArray *retVal = %orig.mutableCopy; + + // fixes button overlapping yt logo on smaller devices + [self setLeadingPadding:-10]; + if (self.videoPlayerButton) { + [self.videoPlayerButton removeFromSuperview]; + [self addSubview:self.videoPlayerButton]; + [retVal insertObject:self.videoPlayerButton atIndex:0]; + } + return retVal; +} +// Method to handle the video player button press by showing a document picker +%new +- (void)videoPlayerButtonPressed:(UIButton *)sender { + // Traversing the responder chain to find the nearest UIViewController + UIResponder *responder = sender; + UIViewController *settingsViewController = nil; + while (responder) { + if ([responder isKindOfClass:[UIViewController class]]) { + settingsViewController = (UIViewController *)responder; + break; + } + responder = responder.nextResponder; + } + + if (settingsViewController) { + // Present the video picker + UIDocumentPickerViewController *documentPicker = [[UIDocumentPickerViewController alloc] initWithDocumentTypes:@[(NSString *)kUTTypeMovie, (NSString *)kUTTypeVideo] inMode:UIDocumentPickerModeImport]; + documentPicker.delegate = (id)self; + documentPicker.allowsMultipleSelection = NO; + [settingsViewController presentViewController:documentPicker animated:YES completion:nil]; + } else { + NSLog(@"No view controller found for the sender button."); + } +} +// Delegate method to handle the picked video by showing the apple player +%new +- (void)documentPicker:(UIDocumentPickerViewController *)controller didPickDocumentsAtURLs:(NSArray *)urls { + NSURL *pickedURL = [urls firstObject]; + + if (pickedURL) { + // Use AVPlayerViewController to play the video + AVPlayer *player = [AVPlayer playerWithURL:pickedURL]; + AVPlayerViewController *playerViewController = [[AVPlayerViewController alloc] init]; + playerViewController.player = player; + + // Get the root view controller + UIViewController *presentingViewController = [UIApplication sharedApplication].keyWindow.rootViewController; + // Present the Video Player + if (presentingViewController) { + [presentingViewController presentViewController:playerViewController animated:YES completion:^{ + [player play]; + }]; + } else { + // Handle case where no view controller was found + NSLog(@"Error: No view controller found to present AVPlayerViewController."); + } + } +} +%end +%end + // App Settings Overlay Options %group gDisableAccountSection %hook YTSettingsSectionItemManager @@ -1064,6 +1158,9 @@ BOOL isTabSelected = NO; if (IsEnabled(@"playerGestures_enabled")) { %init(playerGestures); } + if (IsEnabled(@"videoPlayerButton_enabled")) { + %init(gVideoPlayerButton); + } // Change the default value of some options NSArray *allKeys = [[[NSUserDefaults standardUserDefaults] dictionaryRepresentation] allKeys]; @@ -1079,6 +1176,9 @@ BOOL isTabSelected = NO; if (![allKeys containsObject:@"fixCasting_enabled"]) { [[NSUserDefaults standardUserDefaults] setBool:YES forKey:@"fixCasting_enabled"]; } + if (![allKeys containsObject:@"videoPlayerButton_enabled"]) { + [[NSUserDefaults standardUserDefaults] setBool:YES forKey:@"videoPlayerButton_enabled"]; + } // Default gestures as volume, brightness, seek if (![allKeys containsObject:@"playerGestureTopSelection"]) { [[NSUserDefaults standardUserDefaults] setInteger:GestureModeVolume forKey:@"playerGestureTopSelection"]; diff --git a/lang/YTLitePlus.bundle/YTLitePlusColored-1024.png b/lang/YTLitePlus.bundle/YTLitePlusColored-1024.png new file mode 100644 index 0000000000000000000000000000000000000000..879a0b1ba3605bdc16226f7d7a53fee9c0dfabe7 GIT binary patch literal 10981 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 WtMh+$eCSK6_uPzhk3AN%b`t0WSdE) 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 S2X;}@Ueo7HJLfLi{(k`6T*EH_ literal 0 HcmV?d00001 diff --git a/lang/YTLitePlus.bundle/YTLitePlusLightMode-1024.png b/lang/YTLitePlus.bundle/YTLitePlusLightMode-1024.png new file mode 100644 index 0000000000000000000000000000000000000000..3c5093849840d6110723f4d1a8fc1c0d9ba8020c GIT binary patch literal 14797 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 QffPb}Oe~CFY Date: Sun, 25 Aug 2024 20:38:59 -0700 Subject: [PATCH 07/47] Add smoothing, improve seek, adjust sensitivity --- YTLitePlus.xm | 93 ++++++++++++++++++++++++++++----------------------- 1 file changed, 51 insertions(+), 42 deletions(-) diff --git a/YTLitePlus.xm b/YTLitePlus.xm index ba0da3e..742b7bf 100644 --- a/YTLitePlus.xm +++ b/YTLitePlus.xm @@ -668,6 +668,7 @@ BOOL isTabSelected = NO; * for each zone. The zones are horizontal sections that divide the player into * 3 equal parts. The choices are volume, brightness, seek, and disabled. * There is also a deadzone that can be configured in the settings. + * There are 4 logical states: initial, changed in deadzone, changed, end. */ %new - (void)YTLitePlusHandlePanGesture:(UIPanGestureRecognizer *)panGestureRecognizer { @@ -676,7 +677,7 @@ BOOL isTabSelected = NO; // Variables for storing initial values to be adjusted static float initialVolume; static float initialBrightness; - static CGFloat currentTime; + static CGFloat initialTime; // Flag to determine if the pan gesture is valid static BOOL isValidHorizontalPan = NO; // Variable to store the section of the screen the gesture is in @@ -687,6 +688,10 @@ BOOL isTabSelected = NO; static CGFloat deadzoneStartingXTranslation; // Variable to track the X translation of the pan gesture after exiting the deadzone static CGFloat adjustedTranslationX; + // Variable used to smooth out the X translation + static CGFloat smoothedTranslationX = 0; + // Constant for the filter constant to change responsiveness + static const CGFloat filterConstant = 0.1; // Constant for the deadzone radius that can be changed in the settings static CGFloat deadzoneRadius = (CGFloat)GetFloat(@"playerGesturesDeadzone"); // Constant for the sensitivity factor that can be changed in the settings @@ -714,7 +719,7 @@ BOOL isTabSelected = NO; /***** Helper functions for adjusting player state *****/ // Helper function to adjust brightness void (^adjustBrightness)(CGFloat, CGFloat) = ^(CGFloat translationX, CGFloat initialBrightness) { - float brightnessSensitivityFactor = 2.0; + float brightnessSensitivityFactor = 3; float newBrightness = initialBrightness + ((translationX / 1000.0) * sensitivityFactor * brightnessSensitivityFactor); newBrightness = fmaxf(fminf(newBrightness, 1.0), 0.0); [[UIScreen mainScreen] setBrightness:newBrightness]; @@ -722,7 +727,7 @@ BOOL isTabSelected = NO; // Helper function to adjust volume void (^adjustVolume)(CGFloat, CGFloat) = ^(CGFloat translationX, CGFloat initialVolume) { - float volumeSensitivityFactor = 2.0; + float volumeSensitivityFactor = 3.0; float newVolume = initialVolume + ((translationX / 1000.0) * sensitivityFactor * volumeSensitivityFactor); newVolume = fmaxf(fminf(newVolume, 1.0), 0.0); // https://stackoverflow.com/questions/50737943/how-to-change-volume-programmatically-on-ios-11-4 @@ -733,18 +738,25 @@ BOOL isTabSelected = NO; }; // Helper function to adjust seek time - // void (^adjustSeek)(CGFloat, CGFloat) = ^(CGFloat translationX, CGFloat currentTime) { - // // Calculate a seek fraction based on the horizontal translation - // CGFloat totalDuration = self.currentVideoTotalMediaTime; - // CGFloat viewWidth = self.view.bounds.size.width; - // CGFloat seekFraction = (translationX / viewWidth); - // // Calculate the new seek time based on the calculated offset - // CGFloat sensitivityFactor = 1; // Adjust this value to make seeking more/less sensitive - // seekFraction = sensitivityFactor * seekFraction; - // CGFloat seekTime = currentTime + totalDuration * seekFraction; - // // Seek to the new time - // [playerBarController inlinePlayerBarContainerView:playerBar didFineScrubToTime:seekTime]; - // }; + void (^adjustSeek)(CGFloat, CGFloat) = ^(CGFloat translationX, CGFloat initialTime) { + // Get the location in view for the current video time + CGFloat totalTime = self.currentVideoTotalMediaTime; + CGFloat videoFraction = initialTime / totalTime; + CGFloat initialTimeXPosition = scrubXForScrubRange(videoFraction); + // Calculate the new seek X position + CGFloat sensitivityFactor = 1; // Adjust this value to make seeking more/less sensitive + CGFloat newSeekXPosition = initialTimeXPosition + translationX * sensitivityFactor; + // Create a CGPoint using this new X position + CGPoint newSeekPoint = CGPointMake(newSeekXPosition, 0); + // Send this to a seek method in the player bar controller + [playerBarController didScrbToPoint:newSeekPoint]; + }; + + // Helper function to smooth out the X translation + CGFloat (^applyLowPassFilter)(CGFloat) = ^(CGFloat newTranslation) { + smoothedTranslationX = filterConstant * newTranslation + (1 - filterConstant) * smoothedTranslationX; + return smoothedTranslationX; + }; /***** Helper functions for running the selected gesture *****/ // Helper function to run any setup for the selected gesture mode @@ -754,18 +766,15 @@ BOOL isTabSelected = NO; // Handle the setup based on the selected mode switch (selectedGestureMode) { case GestureModeVolume: - // Store initial volume value initialVolume = [[AVAudioSession sharedInstance] outputVolume]; break; case GestureModeBrightness: - // Store the initial brightness value initialBrightness = [UIScreen mainScreen].brightness; break; case GestureModeSeek: - // Store the current time value - currentTime = self.currentVideoMediaTime; + initialTime = self.currentVideoMediaTime; // Start a seek action - [playerBarController seekAnywhereDidScrubWithRecognizer:panGestureRecognizer]; + [playerBarController startScrubbing]; break; case GestureModeDisabled: // Do nothing if the gesture is disabled @@ -793,8 +802,7 @@ BOOL isTabSelected = NO; adjustBrightness(adjustedTranslationX, initialBrightness); break; case GestureModeSeek: - // adjustSeek(adjustedTranslationX, currentTime); - [playerBarController seekAnywhereDidScrubWithRecognizer:panGestureRecognizer]; + adjustSeek(adjustedTranslationX, initialTime); break; case GestureModeDisabled: // Do nothing if the gesture is disabled @@ -820,7 +828,7 @@ BOOL isTabSelected = NO; case GestureModeBrightness: break; case GestureModeSeek: - [playerBarController seekAnywhereDidScrubWithRecognizer:panGestureRecognizer]; + [playerBarController endScrubbingForSeekSource:0]; break; case GestureModeDisabled: break; @@ -907,6 +915,8 @@ BOOL isTabSelected = NO; if (isValidHorizontalPan) { // Adjust the X translation based on the value hit after exiting the deadzone adjustedTranslationX = translation.x - deadzoneStartingXTranslation; + // Smooth the translation value + adjustedTranslationX = applyLowPassFilter(adjustedTranslationX); // Pass the adjusted translation to the selected gesture switch (gestureSection) { case GestureSectionTop: @@ -925,26 +935,25 @@ BOOL isTabSelected = NO; } } } - if (panGestureRecognizer.state == UIGestureRecognizerStateEnded) { - if (isValidHorizontalPan) { - // Handle the gesture based on the identified section - switch (gestureSection) { - case GestureSectionTop: - runSelectedGestureEnded(@"playerGestureTopSelection"); - break; - case GestureSectionMiddle: - runSelectedGestureEnded(@"playerGestureMiddleSelection"); - break; - case GestureSectionBottom: - runSelectedGestureEnded(@"playerGestureBottomSelection"); - break; - default: - break; - } - // Provide haptic feedback upon successful gesture recognition - // [feedbackGenerator prepare]; - // [feedbackGenerator impactOccurred]; + + // Handle the gesture end state by running the selected gesture mode's end action + if (panGestureRecognizer.state == UIGestureRecognizerStateEnded && isValidHorizontalPan) { + switch (gestureSection) { + case GestureSectionTop: + runSelectedGestureEnded(@"playerGestureTopSelection"); + break; + case GestureSectionMiddle: + runSelectedGestureEnded(@"playerGestureMiddleSelection"); + break; + case GestureSectionBottom: + runSelectedGestureEnded(@"playerGestureBottomSelection"); + break; + default: + break; } + // Provide haptic feedback upon successful gesture recognition + // [feedbackGenerator prepare]; + // [feedbackGenerator impactOccurred]; } } From 235e2966bce945af169851d58fcffde8fc38c709 Mon Sep 17 00:00:00 2001 From: Bryce Hackel <34104885+bhackel@users.noreply.github.com> Date: Sun, 25 Aug 2024 22:10:27 -0700 Subject: [PATCH 08/47] Fix bugs, only allow one seek gesture --- YTLitePlus.h | 13 +++++++++---- YTLitePlus.xm | 17 ++++++++++++++--- 2 files changed, 23 insertions(+), 7 deletions(-) diff --git a/YTLitePlus.h b/YTLitePlus.h index 5bb9961..41c1562 100644 --- a/YTLitePlus.h +++ b/YTLitePlus.h @@ -46,6 +46,7 @@ #import "Tweaks/YouTubeHeader/YTMainAppControlsOverlayView.h" #import "Tweaks/YouTubeHeader/YTMultiSizeViewController.h" #import "Tweaks/YouTubeHeader/YTWatchLayerViewController.h" +#import "Tweaks/YouTubeHeader/YTInlinePlayerBarView.h" #define LOC(x) [tweakBundle localizedStringForKey:x value:nil table:nil] #define YT_BUNDLE_ID @"com.google.ios.youtube" @@ -163,15 +164,19 @@ typedef NS_ENUM(NSUInteger, GestureSection) { @property (nonatomic, assign, readwrite) float volumeValue; @end @interface YTPlayerBarController (YTLitePlus) -- (void)inlinePlayerBarContainerViewDidStartFineScrub:(YTInlinePlayerBarContainerView *)playerBar; -- (void)inlinePlayerBarContainerView:(YTInlinePlayerBarContainerView *)playerBar didFineScrubToTime:(CGFloat)time; -- (void)inlinePlayerBarContainerViewDidEndFineScrub:(YTInlinePlayerBarContainerView *)playerBar seekSource:(int)source; - (void)didScrub:(UIPanGestureRecognizer *)gestureRecognizer; -- (void)seekAnywhereDidScrubWithRecognizer:(UIPanGestureRecognizer *)recognizer; +- (void)startScrubbing; +- (void)didScrubToPoint:(CGPoint)point; +- (void)endScrubbingForSeekSource:(int)seekSource; @end @interface YTMainAppVideoPlayerOverlayViewController (YTLitePlus) @property (nonatomic, strong, readwrite) YTPlayerBarController *playerBarController; @end +@interface YTInlinePlayerBarContainerView (YTLitePlus) +@property UIPanGestureRecognizer *scrubGestureRecognizer; +- (CGFloat)scrubXForScrubRange:(CGFloat)scrubRange; +@end + // Hide Collapse Button - @arichornlover @interface YTMainAppControlsOverlayView (YTLitePlus) diff --git a/YTLitePlus.xm b/YTLitePlus.xm index 742b7bf..8cf80d6 100644 --- a/YTLitePlus.xm +++ b/YTLitePlus.xm @@ -714,7 +714,7 @@ BOOL isTabSelected = NO; // Get objects used to seek nicely in the video player static YTMainAppVideoPlayerOverlayViewController *mainVideoPlayerController = (YTMainAppVideoPlayerOverlayViewController *)self.childViewControllers.firstObject; static YTPlayerBarController *playerBarController = mainVideoPlayerController.playerBarController; - // static YTInlinePlayerBarContainerView *playerBar = playerBarController.playerBar; + static YTInlinePlayerBarContainerView *playerBar = playerBarController.playerBar; /***** Helper functions for adjusting player state *****/ // Helper function to adjust brightness @@ -742,14 +742,14 @@ BOOL isTabSelected = NO; // Get the location in view for the current video time CGFloat totalTime = self.currentVideoTotalMediaTime; CGFloat videoFraction = initialTime / totalTime; - CGFloat initialTimeXPosition = scrubXForScrubRange(videoFraction); + CGFloat initialTimeXPosition = [playerBar scrubXForScrubRange:videoFraction]; // Calculate the new seek X position CGFloat sensitivityFactor = 1; // Adjust this value to make seeking more/less sensitive CGFloat newSeekXPosition = initialTimeXPosition + translationX * sensitivityFactor; // Create a CGPoint using this new X position CGPoint newSeekPoint = CGPointMake(newSeekXPosition, 0); // Send this to a seek method in the player bar controller - [playerBarController didScrbToPoint:newSeekPoint]; + [playerBarController didScrubToPoint:newSeekPoint]; }; // Helper function to smooth out the X translation @@ -885,6 +885,8 @@ BOOL isTabSelected = NO; // If outside the deadzone, activate the pan gesture and store the initial values isValidHorizontalPan = YES; deadzoneStartingXTranslation = translation.x; + adjustedTranslationX = 0; + smoothedTranslationX = 0; // Run the setup for the selected gesture mode switch (gestureSection) { case GestureSectionTop: @@ -960,6 +962,15 @@ BOOL isTabSelected = NO; // allow the pan gesture to be recognized simultaneously with other gestures %new - (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldRecognizeSimultaneouslyWithGestureRecognizer:(UIGestureRecognizer *)otherGestureRecognizer { + if ([gestureRecognizer isKindOfClass:[UIPanGestureRecognizer class]] && [otherGestureRecognizer isKindOfClass:[UIPanGestureRecognizer class]]) { + // Do not allow this gesture to activate with the normal seek bar gesture + YTMainAppVideoPlayerOverlayViewController *mainVideoPlayerController = (YTMainAppVideoPlayerOverlayViewController *)self.childViewControllers.firstObject; + YTPlayerBarController *playerBarController = mainVideoPlayerController.playerBarController; + YTInlinePlayerBarContainerView *playerBar = playerBarController.playerBar; + if (otherGestureRecognizer == playerBar.scrubGestureRecognizer) { + return NO; + } + } return YES; } %end From 268568b106fb55d96218cf2c9ef16646f4ec8d76 Mon Sep 17 00:00:00 2001 From: Bryce Hackel <34104885+bhackel@users.noreply.github.com> Date: Sun, 25 Aug 2024 23:46:29 -0700 Subject: [PATCH 09/47] Improve audio smoothness visuals --- YTLitePlus.xm | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/YTLitePlus.xm b/YTLitePlus.xm index 8cf80d6..125d30a 100644 --- a/YTLitePlus.xm +++ b/YTLitePlus.xm @@ -730,6 +730,11 @@ BOOL isTabSelected = NO; float volumeSensitivityFactor = 3.0; float newVolume = initialVolume + ((translationX / 1000.0) * sensitivityFactor * volumeSensitivityFactor); newVolume = fmaxf(fminf(newVolume, 1.0), 0.0); + // Improve smoothness - ignore if the volume is within 0.01 of the current volume + CGFloat currentVolume = [[AVAudioSession sharedInstance] outputVolume]; + if (fabs(newVolume - currentVolume) < 0.01 && currentVolume > 0.01 && currentVolume < 0.99) { + return; + } // https://stackoverflow.com/questions/50737943/how-to-change-volume-programmatically-on-ios-11-4 dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0.01 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{ From 573c2d2f433067a350d31a878d89bfdfb44785cf Mon Sep 17 00:00:00 2001 From: Bryce Hackel <34104885+bhackel@users.noreply.github.com> Date: Mon, 26 Aug 2024 00:06:39 -0700 Subject: [PATCH 10/47] Add setting for haptic feedback --- Source/Settings.xm | 4 +++- YTLitePlus.xm | 9 +++++++-- lang/YTLitePlus.bundle/ar.lproj/Localizable.strings | 1 + lang/YTLitePlus.bundle/bg.lproj/Localizable.strings | 1 + lang/YTLitePlus.bundle/de.lproj/Localizable.strings | 1 + lang/YTLitePlus.bundle/en.lproj/Localizable.strings | 1 + lang/YTLitePlus.bundle/es.lproj/Localizable.strings | 1 + lang/YTLitePlus.bundle/fr.lproj/Localizable.strings | 1 + lang/YTLitePlus.bundle/ja.lproj/Localizable.strings | 1 + lang/YTLitePlus.bundle/pt.lproj/Localizable.strings | 1 + lang/YTLitePlus.bundle/ro.lproj/Localizable.strings | 1 + lang/YTLitePlus.bundle/ru.lproj/Localizable.strings | 1 + .../YTLitePlus.bundle/template.lproj/Localizable.strings | 1 + lang/YTLitePlus.bundle/tr.lproj/Localizable.strings | 1 + lang/YTLitePlus.bundle/vi.lproj/Localizable.strings | 1 + lang/YTLitePlus.bundle/zh_TW.lproj/Localizable.strings | 1 + 16 files changed, 24 insertions(+), 3 deletions(-) diff --git a/Source/Settings.xm b/Source/Settings.xm index 5bfd862..21d6374 100644 --- a/Source/Settings.xm +++ b/Source/Settings.xm @@ -397,7 +397,9 @@ static const NSInteger YTLiteSection = 789; createSectionGestureSelector(@"BOTTOM_SECTION", @"playerGestureBottomSelection"), // Pickers for configuration settings deadzonePicker, - sensitivityPicker + sensitivityPicker, + // Toggle for haptic feedback + BASIC_SWITCH(LOC(@"PLAYER_GESTURES_HAPTIC_FEEDBACK"), nil, @"playerGesturesHapticFeedback_enabled"), ]; YTSettingsPickerViewController *picker = [[%c(YTSettingsPickerViewController) alloc] initWithNavTitle:LOC(@"Player Gestures (Beta)") pickerSectionTitle:nil rows:rows selectedItemIndex:NSNotFound parentResponder:[self parentResponder]]; [settingsViewController pushViewController:picker]; diff --git a/YTLitePlus.xm b/YTLitePlus.xm index 125d30a..bb6c372 100644 --- a/YTLitePlus.xm +++ b/YTLitePlus.xm @@ -909,8 +909,10 @@ BOOL isTabSelected = NO; break; } // Provide haptic feedback to indicate a gesture start - [feedbackGenerator prepare]; - [feedbackGenerator impactOccurred]; + if (IS_ENABLED(@"playerGesturesHapticFeedback_enabled")) { + [feedbackGenerator prepare]; + [feedbackGenerator impactOccurred]; + } } else { // Cancel the gesture if the translation is not horizontal panGestureRecognizer.state = UIGestureRecognizerStateCancelled; @@ -1230,4 +1232,7 @@ BOOL isTabSelected = NO; if (![allKeys containsObject:@"playerGesturesSensitivity"]) { [[NSUserDefaults standardUserDefaults] setFloat:1.0 forKey:@"playerGesturesSensitivity"]; } + if (![allKeys containsObject:@"playerGesturesHapticFeedback_enabled"]) { + [[NSUserDefaults standardUserDefaults] setBool:YES forKey:@"playerGesturesHapticFeedback_enabled"]; + } } diff --git a/lang/YTLitePlus.bundle/ar.lproj/Localizable.strings b/lang/YTLitePlus.bundle/ar.lproj/Localizable.strings index a577d98..8890c45 100644 --- a/lang/YTLitePlus.bundle/ar.lproj/Localizable.strings +++ b/lang/YTLitePlus.bundle/ar.lproj/Localizable.strings @@ -31,6 +31,7 @@ "TOP_SECTION" = "Top Section"; "MIDDLE_SECTION" = "Middle Section"; "BOTTOM_SECTION" = "Bottom Section"; +"PLAYER_GESTURES_HAPTIC_FEEDBACK" = "Enable Haptic Feedback"; // Video controls overlay options "VIDEO_CONTROLS_OVERLAY_OPTIONS" = "خيارات تراكب ضوابط الفيديو"; diff --git a/lang/YTLitePlus.bundle/bg.lproj/Localizable.strings b/lang/YTLitePlus.bundle/bg.lproj/Localizable.strings index 4689437..214f171 100644 --- a/lang/YTLitePlus.bundle/bg.lproj/Localizable.strings +++ b/lang/YTLitePlus.bundle/bg.lproj/Localizable.strings @@ -31,6 +31,7 @@ "TOP_SECTION" = "Top Section"; "MIDDLE_SECTION" = "Middle Section"; "BOTTOM_SECTION" = "Bottom Section"; +"PLAYER_GESTURES_HAPTIC_FEEDBACK" = "Enable Haptic Feedback"; // Video controls overlay options "VIDEO_CONTROLS_OVERLAY_OPTIONS" = "Опции за контрол на видеото"; diff --git a/lang/YTLitePlus.bundle/de.lproj/Localizable.strings b/lang/YTLitePlus.bundle/de.lproj/Localizable.strings index 76a63fe..755b54d 100644 --- a/lang/YTLitePlus.bundle/de.lproj/Localizable.strings +++ b/lang/YTLitePlus.bundle/de.lproj/Localizable.strings @@ -31,6 +31,7 @@ "TOP_SECTION" = "Top Section"; "MIDDLE_SECTION" = "Middle Section"; "BOTTOM_SECTION" = "Bottom Section"; +"PLAYER_GESTURES_HAPTIC_FEEDBACK" = "Enable Haptic Feedback"; // Video controls overlay options "VIDEO_CONTROLS_OVERLAY_OPTIONS" = "Overlay-Optionen für Videosteuerungen"; diff --git a/lang/YTLitePlus.bundle/en.lproj/Localizable.strings b/lang/YTLitePlus.bundle/en.lproj/Localizable.strings index 57fd3dc..37f9e2e 100644 --- a/lang/YTLitePlus.bundle/en.lproj/Localizable.strings +++ b/lang/YTLitePlus.bundle/en.lproj/Localizable.strings @@ -31,6 +31,7 @@ "TOP_SECTION" = "Top Section"; "MIDDLE_SECTION" = "Middle Section"; "BOTTOM_SECTION" = "Bottom Section"; +"PLAYER_GESTURES_HAPTIC_FEEDBACK" = "Enable Haptic Feedback"; // Video controls overlay options "VIDEO_CONTROLS_OVERLAY_OPTIONS" = "Video Controls Overlay Options"; diff --git a/lang/YTLitePlus.bundle/es.lproj/Localizable.strings b/lang/YTLitePlus.bundle/es.lproj/Localizable.strings index 1cf7433..a6ea96d 100644 --- a/lang/YTLitePlus.bundle/es.lproj/Localizable.strings +++ b/lang/YTLitePlus.bundle/es.lproj/Localizable.strings @@ -31,6 +31,7 @@ "TOP_SECTION" = "Top Section"; "MIDDLE_SECTION" = "Middle Section"; "BOTTOM_SECTION" = "Bottom Section"; +"PLAYER_GESTURES_HAPTIC_FEEDBACK" = "Enable Haptic Feedback"; // Video controls overlay options "VIDEO_CONTROLS_OVERLAY_OPTIONS" = "Opciones de superposición de controles de vídeo"; diff --git a/lang/YTLitePlus.bundle/fr.lproj/Localizable.strings b/lang/YTLitePlus.bundle/fr.lproj/Localizable.strings index e788b97..7e2102f 100644 --- a/lang/YTLitePlus.bundle/fr.lproj/Localizable.strings +++ b/lang/YTLitePlus.bundle/fr.lproj/Localizable.strings @@ -31,6 +31,7 @@ "TOP_SECTION" = "Top Section"; "MIDDLE_SECTION" = "Middle Section"; "BOTTOM_SECTION" = "Bottom Section"; +"PLAYER_GESTURES_HAPTIC_FEEDBACK" = "Enable Haptic Feedback"; // Video controls overlay options "VIDEO_CONTROLS_OVERLAY_OPTIONS" = "Options de l'overlay des contrôles vidéo"; diff --git a/lang/YTLitePlus.bundle/ja.lproj/Localizable.strings b/lang/YTLitePlus.bundle/ja.lproj/Localizable.strings index 2749ff2..7c7fc64 100644 --- a/lang/YTLitePlus.bundle/ja.lproj/Localizable.strings +++ b/lang/YTLitePlus.bundle/ja.lproj/Localizable.strings @@ -31,6 +31,7 @@ "TOP_SECTION" = "Top Section"; "MIDDLE_SECTION" = "Middle Section"; "BOTTOM_SECTION" = "Bottom Section"; +"PLAYER_GESTURES_HAPTIC_FEEDBACK" = "Enable Haptic Feedback"; // Video controls overlay options "VIDEO_CONTROLS_OVERLAY_OPTIONS" = "動画コントロールオーバーレイの設定"; diff --git a/lang/YTLitePlus.bundle/pt.lproj/Localizable.strings b/lang/YTLitePlus.bundle/pt.lproj/Localizable.strings index df987ed..02de1f8 100644 --- a/lang/YTLitePlus.bundle/pt.lproj/Localizable.strings +++ b/lang/YTLitePlus.bundle/pt.lproj/Localizable.strings @@ -31,6 +31,7 @@ "TOP_SECTION" = "Top Section"; "MIDDLE_SECTION" = "Middle Section"; "BOTTOM_SECTION" = "Bottom Section"; +"PLAYER_GESTURES_HAPTIC_FEEDBACK" = "Enable Haptic Feedback"; // Video controls overlay options "VIDEO_CONTROLS_OVERLAY_OPTIONS" = "Opções de Sobreposição de Controles de Vídeo"; diff --git a/lang/YTLitePlus.bundle/ro.lproj/Localizable.strings b/lang/YTLitePlus.bundle/ro.lproj/Localizable.strings index 343c226..f12659a 100644 --- a/lang/YTLitePlus.bundle/ro.lproj/Localizable.strings +++ b/lang/YTLitePlus.bundle/ro.lproj/Localizable.strings @@ -31,6 +31,7 @@ "TOP_SECTION" = "Top Section"; "MIDDLE_SECTION" = "Middle Section"; "BOTTOM_SECTION" = "Bottom Section"; +"PLAYER_GESTURES_HAPTIC_FEEDBACK" = "Enable Haptic Feedback"; // Video controls overlay options "VIDEO_CONTROLS_OVERLAY_OPTIONS" = "Opțiuni Overlay Controale Video"; diff --git a/lang/YTLitePlus.bundle/ru.lproj/Localizable.strings b/lang/YTLitePlus.bundle/ru.lproj/Localizable.strings index 5e6bb59..bfe751a 100644 --- a/lang/YTLitePlus.bundle/ru.lproj/Localizable.strings +++ b/lang/YTLitePlus.bundle/ru.lproj/Localizable.strings @@ -31,6 +31,7 @@ "TOP_SECTION" = "Top Section"; "MIDDLE_SECTION" = "Middle Section"; "BOTTOM_SECTION" = "Bottom Section"; +"PLAYER_GESTURES_HAPTIC_FEEDBACK" = "Enable Haptic Feedback"; // Video controls overlay options "VIDEO_CONTROLS_OVERLAY_OPTIONS" = "Video Controls Overlay Options"; diff --git a/lang/YTLitePlus.bundle/template.lproj/Localizable.strings b/lang/YTLitePlus.bundle/template.lproj/Localizable.strings index 982d0e2..4bf0f2a 100644 --- a/lang/YTLitePlus.bundle/template.lproj/Localizable.strings +++ b/lang/YTLitePlus.bundle/template.lproj/Localizable.strings @@ -46,6 +46,7 @@ https://github.com/PoomSmart/Return-YouTube-Dislikes/tree/main/layout/Library/Ap "TOP_SECTION" = "Top Section"; "MIDDLE_SECTION" = "Middle Section"; "BOTTOM_SECTION" = "Bottom Section"; +"PLAYER_GESTURES_HAPTIC_FEEDBACK" = "Enable Haptic Feedback"; // Video controls overlay options "VIDEO_CONTROLS_OVERLAY_OPTIONS" = "Video Controls Overlay Options"; diff --git a/lang/YTLitePlus.bundle/tr.lproj/Localizable.strings b/lang/YTLitePlus.bundle/tr.lproj/Localizable.strings index a9b0ea3..2dad47f 100644 --- a/lang/YTLitePlus.bundle/tr.lproj/Localizable.strings +++ b/lang/YTLitePlus.bundle/tr.lproj/Localizable.strings @@ -31,6 +31,7 @@ "TOP_SECTION" = "Top Section"; "MIDDLE_SECTION" = "Middle Section"; "BOTTOM_SECTION" = "Bottom Section"; +"PLAYER_GESTURES_HAPTIC_FEEDBACK" = "Enable Haptic Feedback"; // Video controls overlay options "VIDEO_CONTROLS_OVERLAY_OPTIONS" = "Video Kontrol Seç."; diff --git a/lang/YTLitePlus.bundle/vi.lproj/Localizable.strings b/lang/YTLitePlus.bundle/vi.lproj/Localizable.strings index dd3fdb0..6724afc 100644 --- a/lang/YTLitePlus.bundle/vi.lproj/Localizable.strings +++ b/lang/YTLitePlus.bundle/vi.lproj/Localizable.strings @@ -31,6 +31,7 @@ "TOP_SECTION" = "Top Section"; "MIDDLE_SECTION" = "Middle Section"; "BOTTOM_SECTION" = "Bottom Section"; +"PLAYER_GESTURES_HAPTIC_FEEDBACK" = "Enable Haptic Feedback"; // Video player options "VIDEO_PLAYER_OPTIONS" = "Tùy chọn trình phát video"; diff --git a/lang/YTLitePlus.bundle/zh_TW.lproj/Localizable.strings b/lang/YTLitePlus.bundle/zh_TW.lproj/Localizable.strings index aef5cf3..8470f3f 100644 --- a/lang/YTLitePlus.bundle/zh_TW.lproj/Localizable.strings +++ b/lang/YTLitePlus.bundle/zh_TW.lproj/Localizable.strings @@ -32,6 +32,7 @@ "TOP_SECTION" = "Top Section"; "MIDDLE_SECTION" = "Middle Section"; "BOTTOM_SECTION" = "Bottom Section"; +"PLAYER_GESTURES_HAPTIC_FEEDBACK" = "Enable Haptic Feedback"; // Video controls overlay options "VIDEO_CONTROLS_OVERLAY_OPTIONS" = "影片區覆蓋按鈕設定"; From 963b9d5fd32195143addfff2fee9926760dec892 Mon Sep 17 00:00:00 2001 From: Bryce Hackel <34104885+bhackel@users.noreply.github.com> Date: Mon, 26 Aug 2024 00:41:55 -0700 Subject: [PATCH 11/47] Ignore fine scrubber gesture overlap --- YTLitePlus.h | 6 +++++- YTLitePlus.xm | 10 ++++++++++ 2 files changed, 15 insertions(+), 1 deletion(-) diff --git a/YTLitePlus.h b/YTLitePlus.h index 41c1562..cc7bb43 100644 --- a/YTLitePlus.h +++ b/YTLitePlus.h @@ -154,6 +154,10 @@ typedef NS_ENUM(NSUInteger, GestureSection) { @end // Player Gestures - @bhackel +@interface YTFineScrubberFilmstripView : UIView +@end +@interface YTFineScrubberFilmstripCollectionView : UICollectionView +@end @interface YTPlayerViewController (YTLitePlus) @property (nonatomic, retain) UIPanGestureRecognizer *YTLitePlusPanGesture; - (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldRecognizeSimultaneouslyWithGestureRecognizer:(UIGestureRecognizer *)otherGestureRecognizer; @@ -174,10 +178,10 @@ typedef NS_ENUM(NSUInteger, GestureSection) { @end @interface YTInlinePlayerBarContainerView (YTLitePlus) @property UIPanGestureRecognizer *scrubGestureRecognizer; +@property (nonatomic, strong, readwrite) YTFineScrubberFilmstripView *fineScrubberFilmstrip; - (CGFloat)scrubXForScrubRange:(CGFloat)scrubRange; @end - // Hide Collapse Button - @arichornlover @interface YTMainAppControlsOverlayView (YTLitePlus) @property (nonatomic, assign, readwrite) YTQTMButton *watchCollapseButton; diff --git a/YTLitePlus.xm b/YTLitePlus.xm index bb6c372..57ff99b 100644 --- a/YTLitePlus.xm +++ b/YTLitePlus.xm @@ -977,6 +977,16 @@ BOOL isTabSelected = NO; if (otherGestureRecognizer == playerBar.scrubGestureRecognizer) { return NO; } + // Do not allow this gesture to activate with the fine scrubber gesture + YTFineScrubberFilmstripView *fineScrubberFilmstrip = playerBar.fineScrubberFilmstrip; + if (!fineScrubberFilmstrip) { + return YES; + } + YTFineScrubberFilmstripCollectionView *filmstripCollectionView = [fineScrubberFilmstrip valueForKey:@"_filmstripCollectionView"]; + if (filmstripCollectionView && otherGestureRecognizer == filmstripCollectionView.panGestureRecognizer) { + return NO; + } + } return YES; } From 6996c0ed66543c17df8bb012e0c8c8321cb3360b Mon Sep 17 00:00:00 2001 From: Bryce Hackel <34104885+bhackel@users.noreply.github.com> Date: Mon, 26 Aug 2024 01:46:52 -0700 Subject: [PATCH 12/47] Add keys, fix toasts --- Source/Settings.xm | 13 +++++-------- Source/SettingsKeys.h | 5 +++-- Source/get_keys.py | 4 ++++ 3 files changed, 12 insertions(+), 10 deletions(-) diff --git a/Source/Settings.xm b/Source/Settings.xm index 456959f..00e78c0 100644 --- a/Source/Settings.xm +++ b/Source/Settings.xm @@ -191,11 +191,9 @@ static const NSInteger YTLiteSection = 789; }]]; [settingsViewController presentViewController:confirmPasteAlert animated:YES completion:nil]; } - // Show a toast message to confirm the action - [[%c(GOOHUDManagerInternal) sharedInstance] showMessageMainThread:[%c(YTHUDMessage) messageWithText:@"Settings pasted"]]; // Reminder to import YouTube Plus settings UIAlertController *reminderAlert = [UIAlertController alertControllerWithTitle:@"Reminder" - message:@"Remember to import your YouTube Plus settings as well." + message:@"Remember to import your YouTube Plus settings as well" preferredStyle:UIAlertControllerStyleAlert]; [reminderAlert addAction:[UIAlertAction actionWithTitle:@"OK" style:UIAlertActionStyleDefault handler:nil]]; [settingsViewController presentViewController:reminderAlert animated:YES completion:nil]; @@ -678,7 +676,7 @@ static const NSInteger YTLiteSection = 789; NSError *error; NSString *fileType = [pickedURL resourceValuesForKeys:@[NSURLTypeIdentifierKey] error:&error][NSURLTypeIdentifierKey]; - UIViewController *settingsViewController = [self valueForKey:@"_settingsViewControllerDelegate"]; + YTSettingsViewController *settingsViewController = [self valueForKey:@"_settingsViewControllerDelegate"]; if (UTTypeConformsTo((__bridge CFStringRef)fileType, kUTTypePlainText)) { // This block handles the import of settings from a text file. @@ -692,10 +690,9 @@ static const NSInteger YTLiteSection = 789; [[NSUserDefaults standardUserDefaults] setObject:value forKey:key]; } } - if ([settingsViewController respondsToSelector:@selector(reloadData)]) { - // Call a custom reloadData method if it exists - [settingsViewController performSelector:@selector(reloadData)]; - } + [settingsViewController reloadData]; + // Show a toast message to confirm the action + [[%c(GOOHUDManagerInternal) sharedInstance] showMessageMainThread:[%c(YTHUDMessage) messageWithText:@"Settings applied"]]; } } } diff --git a/Source/SettingsKeys.h b/Source/SettingsKeys.h index 384f08b..d18b4e8 100644 --- a/Source/SettingsKeys.h +++ b/Source/SettingsKeys.h @@ -17,8 +17,9 @@ NSArray *NSUserDefaultsCopyKeys = @[ @"hideHomeTab_enabled", @"hidePreviewCommentSection_enabled", @"hideRightPanel_enabled", @"hideSpeedToast_enabled", @"hideSponsorBlockButton_enabled", @"hideVideoPlayerShadowOverlayButtons_enabled", @"iPadLayout_enabled", @"iPhoneLayout_enabled", @"inline_muted_playback_enabled", @"lowContrastMode_enabled", - @"newSettingsUI_enabled", @"oledKeyBoard_enabled", @"playerGestures_enabled", @"seekAnywhere_enabled", - @"switchCopyandPasteFunctionality_enabled", @"ytNoModernUI_enabled", @"ytStartupAnimation_enabled", + @"newSettingsUI_enabled", @"oledKeyBoard_enabled", @"playerGesturesHapticFeedback_enabled", + @"playerGestures_enabled", @"seekAnywhere_enabled", @"switchCopyandPasteFunctionality_enabled", + @"videoPlayerButton_enabled", @"ytNoModernUI_enabled", @"ytStartupAnimation_enabled", // DEMC - https://github.com/therealFoxster/DontEatMyContent/blob/master/Tweak.h @"DEMC_enabled", @"DEMC_colorViewsEnabled", @"DEMC_safeAreaConstant", @"DEMC_disableAmbientMode", @"DEMC_limitZoomToFill", @"DEMC_enableForAllVideos", diff --git a/Source/get_keys.py b/Source/get_keys.py index e763e3a..dd6f221 100644 --- a/Source/get_keys.py +++ b/Source/get_keys.py @@ -61,6 +61,7 @@ def find_and_extract_keys(): Recursively searches for .xm and .h files in the parent directory and extracts keys that match the pattern @\"_enabled\". The matching keys are then printed with indentation and line breaks if the line exceeds 120 characters. + Ignores SettingsKeys.h Usage: 1. Place this script in the desired directory. @@ -82,6 +83,9 @@ def find_and_extract_keys(): for root, dirs, files in os.walk(parent_directory): for file in files: if file.endswith(('.xm', '.h')): + # Skip SettingsKeys.h + if file == "SettingsKeys.h": + continue file_path = os.path.join(root, file) found_keys.update(extract_values_from_file(file_path)) From 969efaaf4dcbb5fe59495ebebe6bdf8082a3b88e Mon Sep 17 00:00:00 2001 From: Bryce Hackel <34104885+bhackel@users.noreply.github.com> Date: Mon, 26 Aug 2024 12:36:26 -0700 Subject: [PATCH 13/47] Revert default Player Button, reduce image size --- YTLitePlus.xm | 7 ++----- lang/YTLitePlus.bundle/YTLitePlusColored-128.png | Bin 0 -> 1241 bytes 2 files changed, 2 insertions(+), 5 deletions(-) create mode 100644 lang/YTLitePlus.bundle/YTLitePlusColored-128.png diff --git a/YTLitePlus.xm b/YTLitePlus.xm index 3bb632e..a857011 100644 --- a/YTLitePlus.xm +++ b/YTLitePlus.xm @@ -1034,10 +1034,10 @@ NSInteger pageStyle = 0; self.videoPlayerButton.frame = CGRectMake(0, 0, 40, 40); if ([%c(YTPageStyleController) pageStyle]) { //dark mode - [self.videoPlayerButton setImage:[UIImage imageWithContentsOfFile:[tweakBundle pathForResource:@"YTLitePlusColored-1024" ofType:@"png"]] forState:UIControlStateNormal]; + [self.videoPlayerButton setImage:[UIImage imageWithContentsOfFile:[tweakBundle pathForResource:@"YTLitePlusColored-128" ofType:@"png"]] forState:UIControlStateNormal]; } else { // light mode - UIImage *image = [UIImage imageWithContentsOfFile:[tweakBundle pathForResource:@"YTLitePlusColored-1024" ofType:@"png"]]; + UIImage *image = [UIImage imageWithContentsOfFile:[tweakBundle pathForResource:@"YTLitePlusColored-128" ofType:@"png"]]; image = [image imageWithTintColor:UIColor.blackColor renderingMode:UIImageRenderingModeAlwaysTemplate]; [self.videoPlayerButton setImage:image forState:UIControlStateNormal]; [self.videoPlayerButton setTintColor:UIColor.blackColor]; @@ -1322,9 +1322,6 @@ NSInteger pageStyle = 0; if (![allKeys containsObject:@"fixCasting_enabled"]) { [[NSUserDefaults standardUserDefaults] setBool:YES forKey:@"fixCasting_enabled"]; } - if (![allKeys containsObject:@"videoPlayerButton_enabled"]) { - [[NSUserDefaults standardUserDefaults] setBool:YES forKey:@"videoPlayerButton_enabled"]; - } // Default gestures as volume, brightness, seek if (![allKeys containsObject:@"playerGestureTopSelection"]) { [[NSUserDefaults standardUserDefaults] setInteger:GestureModeVolume forKey:@"playerGestureTopSelection"]; diff --git a/lang/YTLitePlus.bundle/YTLitePlusColored-128.png b/lang/YTLitePlus.bundle/YTLitePlusColored-128.png new file mode 100644 index 0000000000000000000000000000000000000000..de19892cc98e78c7125dd2e11349cdb0852b5877 GIT binary patch literal 1241 zcmV;~1Sb25P)r@oS@m~=GX0J#fAtsm=yh<#9cEq$3#-szrv@2*UF<|(*f@TBgbpt42j?^sRNp&R# z43HWm21xud_dGkVY3f97?PL2o-;Z?F)^^e)_xRjD{u2oR00000000000000000000 z000000000$f9)s&nY63Cm}F^Hva)GX^6(c+kA13nD1WS3K41DfEm=3;dALn4bshkr za`%)MtE88@uk1t*D?V0>fJlqK?dNSMvts!yhCpdlsPw~7CZXJgl2P7%79$N^c$52~ z+(c3-U6h-_210ma-$hgrU6)f03633cse=J* zhJe-_ST;C4jU^D`34o}aoyF2Wh$;Z$ng(9(AMEvZ0+Rr6XMu2iJ%nLnWi$b0miq?- zM*wVM{R0bxe)JFM1a@N4Kd`2u+h&0P=+tngl^-Ago@!v7!}o=1W;5yJM4C=jn`M*T zm5e^0=l^2}viu^@?k}$zjk4PA?y~IoxY^e=nLPeKwrum`j^%(1u*tdwyI}Iq+-(QuXP~pg7w3P3QwW z+tpRx&fDG}ufvtBdey}J1&5shtir9GZ(~?zXBt;Mo2cw~f-SR2r}Zx1{bm6@o27ET ze^4zUTF0GF1-Dto`+aG_cxNV|52-ZE%uPe*`3l;x`^mWPRZ`1(q0Qgy35CESPrLK>P zvf?K>HmBYvg-(UKuH?uy0E&CuPKD=6!)G{_qc%881vg33OK$%uA4YQOM%IC@hVY_Au ztGOz&WotS)A*5*A>~OKo5-z?tGEsha(II_lp6FV;L=m7q#Dth29?op0EFzEbQ1US4ktfB0Mas^qy9mjC#v^u1ct`v4fAMC*g_ zcRijB1^@s60000000000000000000000000z&86Iv3mFYN+*+_00000NkvXXu0mjf DV!tr# literal 0 HcmV?d00001 From 5741d7c900d12b5d706534cc9597b9acc35a3940 Mon Sep 17 00:00:00 2001 From: Bryce Hackel <34104885+bhackel@users.noreply.github.com> Date: Mon, 26 Aug 2024 12:40:39 -0700 Subject: [PATCH 14/47] Fix black tinting in light mode --- YTLitePlus.xm | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/YTLitePlus.xm b/YTLitePlus.xm index a857011..093b70d 100644 --- a/YTLitePlus.xm +++ b/YTLitePlus.xm @@ -1037,10 +1037,11 @@ NSInteger pageStyle = 0; [self.videoPlayerButton setImage:[UIImage imageWithContentsOfFile:[tweakBundle pathForResource:@"YTLitePlusColored-128" ofType:@"png"]] forState:UIControlStateNormal]; } else { // light mode - UIImage *image = [UIImage imageWithContentsOfFile:[tweakBundle pathForResource:@"YTLitePlusColored-128" ofType:@"png"]]; - image = [image imageWithTintColor:UIColor.blackColor renderingMode:UIImageRenderingModeAlwaysTemplate]; - [self.videoPlayerButton setImage:image forState:UIControlStateNormal]; - [self.videoPlayerButton setTintColor:UIColor.blackColor]; + [self.videoPlayerButton setImage:[UIImage imageWithContentsOfFile:[tweakBundle pathForResource:@"YTLitePlusColored-128" ofType:@"png"]] forState:UIControlStateNormal]; + // UIImage *image = [UIImage imageWithContentsOfFile:[tweakBundle pathForResource:@"YTLitePlusColored-128" ofType:@"png"]]; + // image = [image imageWithTintColor:UIColor.blackColor renderingMode:UIImageRenderingModeAlwaysTemplate]; + // [self.videoPlayerButton setImage:image forState:UIControlStateNormal]; + // [self.videoPlayerButton setTintColor:UIColor.blackColor]; } pageStyle = [%c(YTPageStyleController) pageStyle]; From a46b3b4e201c1736a412d72f04491f744b25559d Mon Sep 17 00:00:00 2001 From: GitHub Actions Date: Tue, 27 Aug 2024 00:46:19 +0000 Subject: [PATCH 15/47] updated submodules --- Tweaks/protobuf | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Tweaks/protobuf b/Tweaks/protobuf index 86dab71..6e2fe5b 160000 --- a/Tweaks/protobuf +++ b/Tweaks/protobuf @@ -1 +1 @@ -Subproject commit 86dab71245ade3f2c8e46ac642489fd70f6a81b3 +Subproject commit 6e2fe5b8c96ff6fb38ca887055453d09b30b6689 From 09721d4b3a37792f4d25cc3c44ca3a610f83f07a Mon Sep 17 00:00:00 2001 From: Bryce Hackel <34104885+bhackel@users.noreply.github.com> Date: Mon, 26 Aug 2024 15:31:00 -0700 Subject: [PATCH 16/47] Fix reminder popups --- Source/Settings.xm | 107 ++++++++++++++++++++++++++++++--------------- 1 file changed, 72 insertions(+), 35 deletions(-) diff --git a/Source/Settings.xm b/Source/Settings.xm index 00e78c0..d6212fb 100644 --- a/Source/Settings.xm +++ b/Source/Settings.xm @@ -105,7 +105,7 @@ static const NSInteger YTLiteSection = 789; # pragma mark - Copy and Paste Settings YTSettingsSectionItem *copySettings = [%c(YTSettingsSectionItem) - itemWithTitle:LOC(@"COPY_SETTINGS") + itemWithTitle:IS_ENABLED(@"switchCopyandPasteFunctionality_enabled") ? LOC(@"EXPORT_SETTINGS") : LOC(@"COPY_SETTINGS") titleDescription:IS_ENABLED(@"switchCopyandPasteFunctionality_enabled") ? LOC(@"EXPORT_SETTINGS_DESC") : LOC(@"COPY_SETTINGS_DESC") accessibilityIdentifier:nil detailTextBlock:nil @@ -116,7 +116,11 @@ static const NSInteger YTLiteSection = 789; NSMutableString *settingsString = [NSMutableString string]; for (NSString *key in NSUserDefaultsCopyKeys) { id value = [[NSUserDefaults standardUserDefaults] objectForKey:key]; - if (value) { + id defaultValue = NSUserDefaultsCopyKeysDefaults[key]; + + // Only include the setting if it is different from the default value + // If no default value is found, include it by default + if (value && (!defaultValue || ![value isEqual:defaultValue])) { [settingsString appendFormat:@"%@: %@\n", key, value]; } } @@ -142,22 +146,31 @@ static const NSInteger YTLiteSection = 789; [[UIPasteboard generalPasteboard] setString:settingsString]; // Show a confirmation message or perform some other action here [[%c(GOOHUDManagerInternal) sharedInstance] showMessageMainThread:[%c(YTHUDMessage) messageWithText:@"Settings copied"]]; + + // Show an option to export YouTube Plus settings + UIAlertController *exportAlert = [UIAlertController alertControllerWithTitle:@"Export Settings" + message:@"Note: This feature cannot save iSponsorBlock and most YouTube settings.\n\nWould you like to also export your YouTube Plus Settings?" + preferredStyle:UIAlertControllerStyleAlert]; + [exportAlert addAction:[UIAlertAction actionWithTitle:@"Cancel" style:UIAlertActionStyleCancel handler:nil]]; + [exportAlert addAction:[UIAlertAction actionWithTitle:@"Export" style:UIAlertActionStyleDefault handler:^(UIAlertAction * _Nonnull action) { + // Export YouTube Plus Settings functionality + [%c(YTLUserDefaults) exportYtlSettings]; + }]]; + + // Get the root view controller + UIViewController *rootViewController = [UIApplication sharedApplication].keyWindow.rootViewController; + + // Present the alert from the root view controller + [rootViewController presentViewController:exportAlert animated:YES completion:nil]; } - // Prompt to export YouTube Plus settings - UIAlertController *exportAlert = [UIAlertController alertControllerWithTitle:@"Export Settings" message:@"Note: This feature cannot save iSponsorBlock and most YouTube settings.\n\nWould you like to also export your YouTube Plus Settings?" preferredStyle:UIAlertControllerStyleAlert]; - [exportAlert addAction:[UIAlertAction actionWithTitle:@"Cancel" style:UIAlertActionStyleCancel handler:nil]]; - [exportAlert addAction:[UIAlertAction actionWithTitle:@"Export" style:UIAlertActionStyleDefault handler:^(UIAlertAction * _Nonnull action) { - // Export YouTube Plus Settings functionality - [%c(YTLUserDefaults) exportYtlSettings]; - }]]; - [settingsViewController presentViewController:exportAlert animated:YES completion:nil]; + return YES; } ]; [sectionItems addObject:copySettings]; YTSettingsSectionItem *pasteSettings = [%c(YTSettingsSectionItem) - itemWithTitle:LOC(@"PASTE_SETTINGS") + itemWithTitle:IS_ENABLED(@"switchCopyandPasteFunctionality_enabled") ? LOC(@"IMPORT_SETTINGS") : LOC(@"PASTE_SETTINGS") titleDescription:IS_ENABLED(@"switchCopyandPasteFunctionality_enabled") ? LOC(@"IMPORT_SETTINGS_DESC") : LOC(@"PASTE_SETTINGS_DESC") accessibilityIdentifier:nil detailTextBlock:nil @@ -183,20 +196,20 @@ static const NSInteger YTLiteSection = 789; NSString *value = components[1]; [[NSUserDefaults standardUserDefaults] setObject:value forKey:key]; } - } + } [settingsViewController reloadData]; - // Show a confirmation message or perform some other action here + // Show a confirmation toast [[%c(GOOHUDManagerInternal) sharedInstance] showMessageMainThread:[%c(YTHUDMessage) messageWithText:@"Settings applied"]]; + // Show a reminder to import YouTube Plus settings as well + UIAlertController *reminderAlert = [UIAlertController alertControllerWithTitle:@"Reminder" + message:@"Remember to import your YouTube Plus settings as well" + preferredStyle:UIAlertControllerStyleAlert]; + [reminderAlert addAction:[UIAlertAction actionWithTitle:@"OK" style:UIAlertActionStyleDefault handler:nil]]; + [settingsViewController presentViewController:reminderAlert animated:YES completion:nil]; } }]]; [settingsViewController presentViewController:confirmPasteAlert animated:YES completion:nil]; } - // Reminder to import YouTube Plus settings - UIAlertController *reminderAlert = [UIAlertController alertControllerWithTitle:@"Reminder" - message:@"Remember to import your YouTube Plus settings as well" - preferredStyle:UIAlertControllerStyleAlert]; - [reminderAlert addAction:[UIAlertAction actionWithTitle:@"OK" style:UIAlertActionStyleDefault handler:nil]]; - [settingsViewController presentViewController:reminderAlert animated:YES completion:nil]; return YES; } @@ -674,25 +687,49 @@ static const NSInteger YTLiteSection = 789; if (urls.count > 0) { NSURL *pickedURL = [urls firstObject]; NSError *error; - NSString *fileType = [pickedURL resourceValuesForKeys:@[NSURLTypeIdentifierKey] error:&error][NSURLTypeIdentifierKey]; + // Check which mode the document picker is in + if (controller.documentPickerMode == UIDocumentPickerModeImport) { + // Import mode: Handle the import of settings from a text file + NSString *fileType = [pickedURL resourceValuesForKeys:@[NSURLTypeIdentifierKey] error:&error][NSURLTypeIdentifierKey]; - YTSettingsViewController *settingsViewController = [self valueForKey:@"_settingsViewControllerDelegate"]; - - if (UTTypeConformsTo((__bridge CFStringRef)fileType, kUTTypePlainText)) { - // This block handles the import of settings from a text file. - NSString *fileContents = [NSString stringWithContentsOfURL:pickedURL encoding:NSUTF8StringEncoding error:nil]; - NSArray *lines = [fileContents componentsSeparatedByString:@"\n"]; - for (NSString *line in lines) { - NSArray *components = [line componentsSeparatedByString:@": "]; - if (components.count == 2) { - NSString *key = components[0]; - NSString *value = components[1]; - [[NSUserDefaults standardUserDefaults] setObject:value forKey:key]; + if (UTTypeConformsTo((__bridge CFStringRef)fileType, kUTTypePlainText)) { + NSString *fileContents = [NSString stringWithContentsOfURL:pickedURL encoding:NSUTF8StringEncoding error:nil]; + NSArray *lines = [fileContents componentsSeparatedByString:@"\n"]; + for (NSString *line in lines) { + NSArray *components = [line componentsSeparatedByString:@": "]; + if (components.count == 2) { + NSString *key = components[0]; + NSString *value = components[1]; + [[NSUserDefaults standardUserDefaults] setObject:value forKey:key]; + } } + // Reload settings view after importing + YTSettingsViewController *settingsViewController = [self valueForKey:@"_settingsViewControllerDelegate"]; + [settingsViewController reloadData]; + // Show a confirmation message or perform some other action here + [[%c(GOOHUDManagerInternal) sharedInstance] showMessageMainThread:[%c(YTHUDMessage) messageWithText:@"Settings applied"]]; + // Show a reminder to import YouTube Plus settings as well + UIAlertController *reminderAlert = [UIAlertController alertControllerWithTitle:@"Reminder" + message:@"Remember to import your YouTube Plus settings as well" + preferredStyle:UIAlertControllerStyleAlert]; + [reminderAlert addAction:[UIAlertAction actionWithTitle:@"OK" style:UIAlertActionStyleDefault handler:nil]]; + [settingsViewController presentViewController:reminderAlert animated:YES completion:nil]; } - [settingsViewController reloadData]; - // Show a toast message to confirm the action - [[%c(GOOHUDManagerInternal) sharedInstance] showMessageMainThread:[%c(YTHUDMessage) messageWithText:@"Settings applied"]]; + + } else if (controller.documentPickerMode == UIDocumentPickerModeExportToService || controller.documentPickerMode == UIDocumentPickerModeMoveToService) { + [[%c(GOOHUDManagerInternal) sharedInstance] showMessageMainThread:[%c(YTHUDMessage) messageWithText:@"Settings saved"]]; + // Export mode: Display a reminder to save YouTube Plus settings + UIAlertController *exportAlert = [UIAlertController alertControllerWithTitle:@"Export Settings" + message:@"Note: This feature cannot save iSponsorBlock and most YouTube settings.\n\nWould you like to also export your YouTube Plus Settings?" + preferredStyle:UIAlertControllerStyleAlert]; + [exportAlert addAction:[UIAlertAction actionWithTitle:@"Cancel" style:UIAlertActionStyleCancel handler:nil]]; + [exportAlert addAction:[UIAlertAction actionWithTitle:@"Export" style:UIAlertActionStyleDefault handler:^(UIAlertAction * _Nonnull action) { + // Export YouTube Plus Settings functionality + [%c(YTLUserDefaults) exportYtlSettings]; + }]]; + YTSettingsViewController *settingsViewController = [self valueForKey:@"_settingsViewControllerDelegate"]; + // Present the alert from the root view controller + [settingsViewController presentViewController:exportAlert animated:YES completion:nil]; } } } From 5fb849297adcb1e60f9f078e00b5b7cbf5f8531d Mon Sep 17 00:00:00 2001 From: Bryce Hackel <34104885+bhackel@users.noreply.github.com> Date: Mon, 26 Aug 2024 23:12:24 -0700 Subject: [PATCH 17/47] Fix bug with reminder again --- Source/Settings.xm | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/Source/Settings.xm b/Source/Settings.xm index d6212fb..418d6f4 100644 --- a/Source/Settings.xm +++ b/Source/Settings.xm @@ -156,12 +156,8 @@ static const NSInteger YTLiteSection = 789; // Export YouTube Plus Settings functionality [%c(YTLUserDefaults) exportYtlSettings]; }]]; - - // Get the root view controller - UIViewController *rootViewController = [UIApplication sharedApplication].keyWindow.rootViewController; - // Present the alert from the root view controller - [rootViewController presentViewController:exportAlert animated:YES completion:nil]; + [settingsViewController presentViewController:exportAlert animated:YES completion:nil]; } return YES; From f17bb793d4a5559afa038dad204004228df3c833 Mon Sep 17 00:00:00 2001 From: GitHub Actions Date: Wed, 28 Aug 2024 00:46:43 +0000 Subject: [PATCH 18/47] updated submodules --- Tweaks/protobuf | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Tweaks/protobuf b/Tweaks/protobuf index 6e2fe5b..807ecfd 160000 --- a/Tweaks/protobuf +++ b/Tweaks/protobuf @@ -1 +1 @@ -Subproject commit 6e2fe5b8c96ff6fb38ca887055453d09b30b6689 +Subproject commit 807ecfd0faf20b8bb5246675d15ac952901cd5d1 From 4a9237893961b244ce0e97d9e69fc39d3b4ea24f Mon Sep 17 00:00:00 2001 From: GitHub Actions Date: Thu, 29 Aug 2024 00:47:16 +0000 Subject: [PATCH 19/47] updated submodules --- Tweaks/protobuf | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Tweaks/protobuf b/Tweaks/protobuf index 807ecfd..1b44ce0 160000 --- a/Tweaks/protobuf +++ b/Tweaks/protobuf @@ -1 +1 @@ -Subproject commit 807ecfd0faf20b8bb5246675d15ac952901cd5d1 +Subproject commit 1b44ce0feef45a78ba99d09bd2e5ff5052a6541b From b5a45c15ac48e5931025271b86fa117e8f4b731c Mon Sep 17 00:00:00 2001 From: GitHub Actions Date: Fri, 30 Aug 2024 00:47:31 +0000 Subject: [PATCH 20/47] updated submodules --- Tweaks/protobuf | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Tweaks/protobuf b/Tweaks/protobuf index 1b44ce0..890dd25 160000 --- a/Tweaks/protobuf +++ b/Tweaks/protobuf @@ -1 +1 @@ -Subproject commit 1b44ce0feef45a78ba99d09bd2e5ff5052a6541b +Subproject commit 890dd25c7f6fbe9680b5467497235796bd4dcc9e From 197f31edf47f96ef0dda5fc7cdee7beeee64678d Mon Sep 17 00:00:00 2001 From: Victor Alves Date: Fri, 30 Aug 2024 15:44:40 -0300 Subject: [PATCH 21/47] Update Portuguese Localization (#389) * Update Portuguese Localization * Update Portuguese Localization * Update Portuguese localization * Update Portuguese Localization --- .../pt.lproj/Localizable.strings | 382 +++++++++--------- 1 file changed, 191 insertions(+), 191 deletions(-) diff --git a/lang/YTLitePlus.bundle/pt.lproj/Localizable.strings b/lang/YTLitePlus.bundle/pt.lproj/Localizable.strings index 26835d4..eb1e6b4 100644 --- a/lang/YTLitePlus.bundle/pt.lproj/Localizable.strings +++ b/lang/YTLitePlus.bundle/pt.lproj/Localizable.strings @@ -1,191 +1,191 @@ -// Settings -"VERSION" = "Versão do YTLitePlus: %@"; -"VERSION_CHECK" = "Toque para verificar se há atualização!"; - -"COPY_SETTINGS" = "Copiar Configurações"; -"COPY_SETTINGS_DESC" = "Copia todas as configurações atuais para a área de transferência"; -"PASTE_SETTINGS" = "Colar Configurações"; -"PASTE_SETTINGS_DESC" = "Cola as configurações da área de transferência e aplica"; -"PASTE_SETTINGS_ALERT" = "Apply settings from clipboard?"; -"EXPORT_SETTINGS" = "Exportar Configurações"; -"EXPORT_SETTINGS_DESC" = "Exporta todas as configurações atuais para um arquivo .txt"; -"IMPORT_SETTINGS" = "Importar Configurações"; -"IMPORT_SETTINGS_DESC" = "Pressione para importar as configurações (.txt)"; -"REPLACE_COPY_AND_PASTE_BUTTONS" = "Substituir os Botões 'Copiar Configurações' e 'Colar Configurações'"; -"REPLACE_COPY_AND_PASTE_BUTTONS_DESC" = "Substitui os Botões 'Exportar Configurações' e 'Importar Configurações'"; - -"VIDEO_PLAYER" = "Video Player (Beta)"; -"VIDEO_PLAYER_DESC" = "Open a downloaded video in the Apple player"; - -// Player Gestures -"PLAYER_GESTURES_TOGGLE" = "Enable Player Gestures"; -"VOLUME" = "Volume"; -"BRIGHTNESS" = "Brightness"; -"SEEK" = "Seek"; -"DISABLED" = "Disabled"; -"DEADZONE" = "Deadzone"; -"DEADZONE_DESC" = "Minimum distance to move before a gesture is recognized"; -"SENSITIVITY" = "Sensitivity"; -"SENSITIVITY_DESC" = "Multiplier on volume and brightness gestures"; -"PLAYER_GESTURES_TITLE" = "Player Gestures"; -"PLAYER_GESTURES_DESC" = "Configure horizontal pan gestures for the player"; -"TOP_SECTION" = "Top Section"; -"MIDDLE_SECTION" = "Middle Section"; -"BOTTOM_SECTION" = "Bottom Section"; -"PLAYER_GESTURES_HAPTIC_FEEDBACK" = "Enable Haptic Feedback"; - -// Video controls overlay options -"VIDEO_CONTROLS_OVERLAY_OPTIONS" = "Opções de Sobreposição de Controles de Vídeo"; - -"ENABLE_SHARE_BUTTON" = "Ativar o botão 'Compartilhar'"; -"ENABLE_SHARE_BUTTON_DESC" = "Ativa o botão Compartilhar na sobreposição de controles de vídeo."; - -"ENABLE_SAVE_TO_PLAYLIST_BUTTON" = "Ativar o botão 'Salvar'"; -"ENABLE_SAVE_TO_PLAYLIST_BUTTON_DESC" = "Ativa o botão 'Salvar' na sobreposição de controles de vídeo."; - -"HIDE_SHADOW_OVERLAY_BUTTONS" = "Ocultar Sombras nos Botões de Sobreposição"; -"HIDE_SHADOW_OVERLAY_BUTTONS_DESC" = "Oculta as sombras nos botões de sobreposição Reproduzir/Pausar, Anterior, Próximo, Avançar e Retroceder."; - -"HIDE_RIGHT_PANEL" = "Ocultar o painel direito no modo de tela cheia"; -"HIDE_RIGHT_PANEL_DESC" = "A reinicialização do app é necessária."; - -"HIDE_HEATWAVES" = "Ocultar Ondas de calor"; -"HIDE_HEATWAVES_DESC" = "Oculta as Ondas de calor no player de vídeo. A reinicialização do app é necessária."; - -"DISABLE_AMBIENT_PORTRAIT" = "Desativar Iluminação cinematográfica (Retrato)"; -"DISABLE_AMBIENT_PORTRAIT_DESC" = "Desativa a iluminação ao redor do título do vídeo"; - -"DISABLE_AMBIENT_FULLSCREEN" = "Desativar Iluminação cinematográfica (Tela cheia)"; -"DISABLE_AMBIENT_FULLSCREEN_DESC" = "Desativa a iluminação ao redor do player de vídeo"; - -"FULLSCREEN_TO_THE_RIGHT" = "Tela cheia para a direita"; -"FULLSCREEN_TO_THE_RIGHT_DESC" = "Sempre entre em tela cheia com o botão home no lado direito."; - -"SEEK_ANYWHERE" = "Gesto de Busca em qualquer lugar"; -"SEEK_ANYWHERE_DESC" = "Segure e arraste o player de vídeo para buscar. Você deve desativar o YTLite - Segurar para velocidade (Hold for speed)"; - -"ENABLE_TAP_TO_SEEK" = "Ativar Toque para Buscar"; -"ENABLE_TAP_TO_SEEK_DESC" = "Vá para qualquer lugar em um vídeo tocando uma vez na barra de busca"; - -"DISABLE_PULL_TO_FULLSCREEN_GESTURE" = "Desativar gesto de puxar para tela cheia"; -"DISABLE_PULL_TO_FULLSCREEN_GESTURE_DESC" = "Desativa o gesto de arrastar para entrar em tela cheia vertical. Aplica-se apenas a vídeos em paisagem."; - -"ALWAYS_USE_REMAINING_TIME" = "Sempre usar o tempo restante"; -"ALWAYS_USE_REMAINING_TIME_DESC" = "Altera o padrão para mostrar o tempo restante na barra do player."; - -"DISABLE_TOGGLE_TIME_REMAINING" = "Desativar alternar tempo restante"; -"DISABLE_TOGGLE_TIME_REMAINING_DESC" = "Desativa a alteração do tempo decorrido para o tempo restante. Use com outra configuração para mostrar sempre o tempo restante."; - -"DISABLE_ENGAGEMENT_OVERLAY" = "Desativar sobreposição de engajamento em tela cheia"; -"DISABLE_ENGAGEMENT_OVERLAY_DESC" = "Desativa o gesto de deslizar para cima e a lista de vídeos sugeridos em tela cheia"; - -"HIDE_COMMENT_PREVIEWS_UNDER_PLAYER" = "Ocultar Visualizações de comentários sob o player"; -"HIDE_COMMENT_PREVIEWS_UNDER_PLAYER_DESC" = "Oculta a Prévia de comentário no botão de comentários"; - -"HIDE_AUTOPLAY_MINI_PREVIEW" = "Ocultar mini visualização de reprodução automática"; -"HIDE_AUTOPLAY_MINI_PREVIEW_DESC" = "Oculta a pequena caixa de vídeo sugerida perto do título em tela cheia"; - -"HIDE_HUD_MESSAGES" = "Ocultar Mensagens do HUD"; -"HIDE_HUD_MESSAGES_DESC" = "Exemplo: Legendas/CC ativadas/desativadas, A repetição do vídeo está ativada,..."; - -"HIDE_COLLAPSE_BUTTON" = "Ocultar Botão de Recolhimento"; -"HIDE_COLLAPSE_BUTTON_DESC" = "Oculta o botão de seta para recolher que era exibido no canto superior esquerdo do player de vídeo."; - -"HIDE_SPEED_TOAST" = "Ocultar Toast de Velocidade"; -"HIDE_SPEED_TOAST_DESC" = "Ocultar o popup de Velocidade 2X ao segurar o player"; - - -// App settings overlay options -"APP_SETTINGS_OVERLAY_OPTIONS" = "Configurações do Aplicativo"; - -"HIDE_ACCOUNT_SECTION" = "Ocultar a sessão \"Conta\""; -"HIDE_AUTOPLAY_SECTION" = "Ocultar a sessão \"Reprodução automática\""; -"HIDE_TRYNEWFEATURES_SECTION" = "Ocultar a sessão \"Experimente novos recursos\""; -"HIDE_VIDEOQUALITYPREFERENCES_SECTION" = "Ocultar a sessão \"Preferências de qualidade de vídeo\""; -"HIDE_NOTIFICATIONS_SECTION" = "Ocultar a sessão \"Notificações\""; -"HIDE_MANAGEALLHISTORY_SECTION" = "Ocultar a sessão \"Gerencie todo o histórico\""; -"HIDE_YOURDATAINYOUTUBE_SECTION" = "Ocultar a sessão \"Seus dados no YouTube\""; -"HIDE_PRIVACY_SECTION" = "Ocultar a sessão \"Privacidade\""; -"HIDE_LIVECHAT_SECTION" = "Ocultar a sessão \"Chat ao vivo\""; - -// Theme -"THEME_OPTIONS" = "Opções de Temas"; - -"OLED_DARK_THEME" = "Modo escuro OLED"; -"OLED_DARK_THEME_2" = "Modo escuro OLED"; -"OLED_DARK_THEME_DESC" = "Verdadeiro tema escuro. Pode não funcionar corretamente em alguns casos. App restart is required after you enable/disable this option."; - -"OLD_DARK_THEME" = "Antigo tema escuro"; -"OLD_DARK_THEME_DESC" = "Tema escuro do YouTube antigo (tema cinza). A reinicialização do app é necessária."; - -"DEFAULT_THEME" = "Padrão"; -"DEFAULT_THEME_DESC" = "Tema escuro padrão do YouTube. A reinicialização do app é necessária."; - -"OLED_KEYBOARD" = "Teclado OLED"; -"OLED_KEYBOARD_DESC" = "Pode não funcionar corretamente em alguns casos. A reinicialização do app é necessária."; - -"LOW_CONTRAST_MODE" = "Modo de Baixo Contraste"; -"LOW_CONTRAST_MODE_DESC" = "Esta opção terá baixo contraste dos textos e botões, assim como era a antiga interface do YouTube. A reinicialização do app é necessária."; -"LCM_SELECTOR" = "Seleção do modo de baixo contraste"; -"DEFAULT_LOWCONTRASTMODE" = "(Padrão) LowContrastMode"; -"CUSTOM_LOWCONTRASTMODE" = "(Cor Personalizada) LowContrastMode"; - -// Miscellaneous -"MISCELLANEOUS" = "Diversos"; - -"PLAYBACK_IN_FEEDS" = "Reprodução nos feeds"; -"PLAYBACK_IN_FEEDS_ALWAYS_ON" = "Sempre ativada"; -"PLAYBACK_IN_FEEDS_WIFI_ONLY" = "Somente Wi-Fi"; -"PLAYBACK_IN_FEEDS_OFF" = "Desativada"; - -"NEW_SETTINGS_UI" = "Nova Interface de Configurações"; -"NEW_SETTINGS_UI_DESC" = "Usa a nova Interface de configurações agrupadas. Pode ocultar algumas configurações"; - -"ENABLE_YT_STARTUP_ANIMATION" = "Ative a animação de inicialização do YouTube"; -"ENABLE_YT_STARTUP_ANIMATION_DESC" = ""; - -"HIDE_MODERN_INTERFACE" = "Ocultar Interface Moderna (YTNoModernUI)"; -"HIDE_MODERN_INTERFACE_DESC" = "Ative esta opção para ocultar qualquer elemento moderno adicionado pelo YouTube. Remove a iluminação cinematogrática, design arredondado e muito mais. A reinicialização do app é necessária."; - -"IPAD_LAYOUT" = "Layout do iPad"; -"IPAD_LAYOUT_DESC" = "Use isso apenas se quiser ter o layout do iPad no seu iPhone/iPod atual. A reinicialização do app é necessária."; - -"IPHONE_LAYOUT" = "Layout do iPhone"; -"IPHONE_LAYOUT_DESC" = "Use isso apenas se quiser ter o layout do iPhone no seu iPad atual. A reinicialização do app é necessária."; - -"CAST_CONFIRM" = "Alerta de confirmação antes de transmitir (YTCastConfirm)"; -"CAST_CONFIRM_DESC" = "Mostra um alerta de confirmação antes da transmissão para evitar o sequestro acidental da TV."; -"CASTING" = "Transmissão"; -"MSG_ARE_YOU_SURE" = "Tem certeza de que deseja começar a transmitir?"; -"MSG_YES" = "Sim"; -"MSG_CANCEL" = "Cancelar"; - -"NEW_MINIPLAYER_STYLE" = "Novo estilo de barra de miniplayer (BigYTMiniPlayer)"; -"NEW_MINIPLAYER_STYLE_DESC" = "A reinicialização do app é necessária."; - -"HIDE_CAST_BUTTON" = "Ocultar o botão Transmitir"; -"HIDE_CAST_BUTTON_DESC" = "A reinicialização do app é necessária."; - -"VIDEO_PLAYER_BUTTON" = "Video Player Button"; -"VIDEO_PLAYER_BUTTON_DESC" = "Show a button in the navigation bar to open downloaded videos in the Apple player"; - -"HIDE_SPONSORBLOCK_BUTTON" = "Ocultar o botão iSponsorBlock na barra de navegação"; -"HIDE_SPONSORBLOCK_BUTTON_DESC" = ""; - -"HIDE_HOME_TAB" = "Ocultar guia Início"; -"HIDE_HOME_TAB_DESC" = "Tenha cuidado ao ocultar todas as guias"; - -"FIX_CASTING" = "Corrigir Transmissão"; -"FIX_CASTING_DESC" = "Altera alguns sinalizadores AB para corrigir a transmissão"; - -"ENABLE_FLEX" = "Ativar FLEX"; -"ENABLE_FLEX_DESC" = "Ativa o FLEX para depuração (não recomendado). Deixe isso desligado, a menos que você saiba o que está fazendo."; - -// Version Spoofer -"APP_VERSION_SPOOFER_LITE" = "Ativar Falsificação da Versão do App (Lite)"; -"APP_VERSION_SPOOFER_LITE_DESC" = "Ative isto para usar a Falsificação de Versão (Lite) e selecione sua versão preferida abaixo. A reinicialização do app é necessária."; -"VERSION_SPOOFER_TITLE" = "Selecionar Versão Falsa"; - -// Other Localization -"APP_RESTART_DESC" = "A reinicialização do app é necessária."; -"CHANGE_APP_ICON" = "Mudar o Ícone do Aplicativo"; +// Settings +"VERSION" = "Versão do YTLitePlus: %@"; +"VERSION_CHECK" = "Toque para verificar se há atualização!"; + +"COPY_SETTINGS" = "Copiar Configurações"; +"COPY_SETTINGS_DESC" = "Copia todas as configurações atuais para a área de transferência"; +"PASTE_SETTINGS" = "Colar Configurações"; +"PASTE_SETTINGS_DESC" = "Cola as configurações da área de transferência e aplica"; +"PASTE_SETTINGS_ALERT" = "Aplicar configurações da área de transferência?"; +"EXPORT_SETTINGS" = "Exportar Configurações"; +"EXPORT_SETTINGS_DESC" = "Exporta todas as configurações atuais para um arquivo .txt"; +"IMPORT_SETTINGS" = "Importar Configurações"; +"IMPORT_SETTINGS_DESC" = "Pressione para importar as configurações (.txt)"; +"REPLACE_COPY_AND_PASTE_BUTTONS" = "Substituir os Botões 'Copiar Configurações' e 'Colar Configurações'"; +"REPLACE_COPY_AND_PASTE_BUTTONS_DESC" = "Substitui os Botões 'Exportar Configurações' e 'Importar Configurações'"; + +"VIDEO_PLAYER" = "Reprodutor de Vídeo (Beta)"; +"VIDEO_PLAYER_DESC" = "Abra um vídeo baixado no Reprodutor de video da Apple"; + +// Player Gestures +"PLAYER_GESTURES_TOGGLE" = "Habilitar Gestos no Reprodutor de video"; +"VOLUME" = "Volume"; +"BRIGHTNESS" = "Brilho"; +"SEEK" = "Busca"; +"DISABLED" = "Desabilitado"; +"DEADZONE" = "Zona morta"; +"DEADZONE_DESC" = "Distância mínima a percorrer antes de um gesto ser reconhecido"; +"SENSITIVITY" = "Sensibilidade"; +"SENSITIVITY_DESC" = "Multiplicador em gestos de volume e brilho"; +"PLAYER_GESTURES_TITLE" = "Gestos do Reprodutor de video"; +"PLAYER_GESTURES_DESC" = "Configurar gestos panorâmicos horizontal para o player"; +"TOP_SECTION" = "Seção Superior"; +"MIDDLE_SECTION" = "Seção do Meio"; +"BOTTOM_SECTION" = "Seção Inferior"; +"PLAYER_GESTURES_HAPTIC_FEEDBACK" = "Habilitar Feedback Tátil"; + +// Video controls overlay options +"VIDEO_CONTROLS_OVERLAY_OPTIONS" = "Opções de Sobreposição de Controles de Vídeo"; + +"ENABLE_SHARE_BUTTON" = "Ativar o botão 'Compartilhar'"; +"ENABLE_SHARE_BUTTON_DESC" = "Ativa o botão Compartilhar na sobreposição de controles de vídeo."; + +"ENABLE_SAVE_TO_PLAYLIST_BUTTON" = "Ativar o botão 'Salvar'"; +"ENABLE_SAVE_TO_PLAYLIST_BUTTON_DESC" = "Ativa o botão 'Salvar' na sobreposição de controles de vídeo."; + +"HIDE_SHADOW_OVERLAY_BUTTONS" = "Ocultar Sombras nos Botões de Sobreposição"; +"HIDE_SHADOW_OVERLAY_BUTTONS_DESC" = "Oculta as sombras nos botões de sobreposição Reproduzir/Pausar, Anterior, Próximo, Avançar e Retroceder."; + +"HIDE_RIGHT_PANEL" = "Ocultar o painel direito no modo de tela cheia"; +"HIDE_RIGHT_PANEL_DESC" = "A reinicialização do app é necessária."; + +"HIDE_HEATWAVES" = "Ocultar Ondas de calor"; +"HIDE_HEATWAVES_DESC" = "Oculta as Ondas de calor no player de vídeo. A reinicialização do app é necessária."; + +"DISABLE_AMBIENT_PORTRAIT" = "Desativar Iluminação cinematográfica (Retrato)"; +"DISABLE_AMBIENT_PORTRAIT_DESC" = "Desativa a iluminação ao redor do título do vídeo"; + +"DISABLE_AMBIENT_FULLSCREEN" = "Desativar Iluminação cinematográfica (Tela cheia)"; +"DISABLE_AMBIENT_FULLSCREEN_DESC" = "Desativa a iluminação ao redor do player de vídeo"; + +"FULLSCREEN_TO_THE_RIGHT" = "Tela cheia para a direita"; +"FULLSCREEN_TO_THE_RIGHT_DESC" = "Sempre entre em tela cheia com o botão home no lado direito."; + +"SEEK_ANYWHERE" = "Gesto de Busca em qualquer lugar"; +"SEEK_ANYWHERE_DESC" = "Segure e arraste o player de vídeo para buscar. Você deve desativar o YTLite - Segurar para velocidade (Hold for speed)"; + +"ENABLE_TAP_TO_SEEK" = "Ativar Toque para Buscar"; +"ENABLE_TAP_TO_SEEK_DESC" = "Vá para qualquer lugar em um vídeo tocando uma vez na barra de busca"; + +"DISABLE_PULL_TO_FULLSCREEN_GESTURE" = "Desativar gesto de puxar para tela cheia"; +"DISABLE_PULL_TO_FULLSCREEN_GESTURE_DESC" = "Desativa o gesto de arrastar para entrar em tela cheia vertical. Aplica-se apenas a vídeos em paisagem."; + +"ALWAYS_USE_REMAINING_TIME" = "Sempre usar o tempo restante"; +"ALWAYS_USE_REMAINING_TIME_DESC" = "Altera o padrão para mostrar o tempo restante na barra do player."; + +"DISABLE_TOGGLE_TIME_REMAINING" = "Desativar alternar tempo restante"; +"DISABLE_TOGGLE_TIME_REMAINING_DESC" = "Desativa a alteração do tempo decorrido para o tempo restante. Use com outra configuração para mostrar sempre o tempo restante."; + +"DISABLE_ENGAGEMENT_OVERLAY" = "Desativar sobreposição de engajamento em tela cheia"; +"DISABLE_ENGAGEMENT_OVERLAY_DESC" = "Desativa o gesto de deslizar para cima e a lista de vídeos sugeridos em tela cheia"; + +"HIDE_COMMENT_PREVIEWS_UNDER_PLAYER" = "Ocultar Visualizações de comentários sob o player"; +"HIDE_COMMENT_PREVIEWS_UNDER_PLAYER_DESC" = "Oculta a Prévia de comentário no botão de comentários"; + +"HIDE_AUTOPLAY_MINI_PREVIEW" = "Ocultar mini visualização de reprodução automática"; +"HIDE_AUTOPLAY_MINI_PREVIEW_DESC" = "Oculta a pequena caixa de vídeo sugerida perto do título em tela cheia"; + +"HIDE_HUD_MESSAGES" = "Ocultar Mensagens do HUD"; +"HIDE_HUD_MESSAGES_DESC" = "Exemplo: Legendas/CC ativadas/desativadas, A repetição do vídeo está ativada,..."; + +"HIDE_COLLAPSE_BUTTON" = "Ocultar Botão de Recolhimento"; +"HIDE_COLLAPSE_BUTTON_DESC" = "Oculta o botão de seta para recolher que era exibido no canto superior esquerdo do player de vídeo."; + +"HIDE_SPEED_TOAST" = "Ocultar Toast de Velocidade"; +"HIDE_SPEED_TOAST_DESC" = "Ocultar o popup de Velocidade 2X ao segurar o player"; + + +// App settings overlay options +"APP_SETTINGS_OVERLAY_OPTIONS" = "Configurações do Aplicativo"; + +"HIDE_ACCOUNT_SECTION" = "Ocultar a sessão \"Conta\""; +"HIDE_AUTOPLAY_SECTION" = "Ocultar a sessão \"Reprodução automática\""; +"HIDE_TRYNEWFEATURES_SECTION" = "Ocultar a sessão \"Experimente novos recursos\""; +"HIDE_VIDEOQUALITYPREFERENCES_SECTION" = "Ocultar a sessão \"Preferências de qualidade de vídeo\""; +"HIDE_NOTIFICATIONS_SECTION" = "Ocultar a sessão \"Notificações\""; +"HIDE_MANAGEALLHISTORY_SECTION" = "Ocultar a sessão \"Gerencie todo o histórico\""; +"HIDE_YOURDATAINYOUTUBE_SECTION" = "Ocultar a sessão \"Seus dados no YouTube\""; +"HIDE_PRIVACY_SECTION" = "Ocultar a sessão \"Privacidade\""; +"HIDE_LIVECHAT_SECTION" = "Ocultar a sessão \"Chat ao vivo\""; + +// Theme +"THEME_OPTIONS" = "Opções de Temas"; + +"OLED_DARK_THEME" = "Modo escuro OLED"; +"OLED_DARK_THEME_2" = "Modo escuro OLED"; +"OLED_DARK_THEME_DESC" = "Verdadeiro tema escuro. Pode não funcionar corretamente em alguns casos. App restart is required after you enable/disable this option."; + +"OLD_DARK_THEME" = "Antigo tema escuro"; +"OLD_DARK_THEME_DESC" = "Tema escuro do YouTube antigo (tema cinza). A reinicialização do app é necessária."; + +"DEFAULT_THEME" = "Padrão"; +"DEFAULT_THEME_DESC" = "Tema escuro padrão do YouTube. A reinicialização do app é necessária."; + +"OLED_KEYBOARD" = "Teclado OLED"; +"OLED_KEYBOARD_DESC" = "Pode não funcionar corretamente em alguns casos. A reinicialização do app é necessária."; + +"LOW_CONTRAST_MODE" = "Modo de Baixo Contraste"; +"LOW_CONTRAST_MODE_DESC" = "Esta opção terá baixo contraste dos textos e botões, assim como era a antiga interface do YouTube. A reinicialização do app é necessária."; +"LCM_SELECTOR" = "Seleção do modo de baixo contraste"; +"DEFAULT_LOWCONTRASTMODE" = "(Padrão) LowContrastMode"; +"CUSTOM_LOWCONTRASTMODE" = "(Cor Personalizada) LowContrastMode"; + +// Miscellaneous +"MISCELLANEOUS" = "Diversos"; + +"PLAYBACK_IN_FEEDS" = "Reprodução nos feeds"; +"PLAYBACK_IN_FEEDS_ALWAYS_ON" = "Sempre ativada"; +"PLAYBACK_IN_FEEDS_WIFI_ONLY" = "Somente Wi-Fi"; +"PLAYBACK_IN_FEEDS_OFF" = "Desativada"; + +"NEW_SETTINGS_UI" = "Nova Interface de Configurações"; +"NEW_SETTINGS_UI_DESC" = "Usa a nova Interface de configurações agrupadas. Pode ocultar algumas configurações"; + +"ENABLE_YT_STARTUP_ANIMATION" = "Ative a animação de inicialização do YouTube"; +"ENABLE_YT_STARTUP_ANIMATION_DESC" = ""; + +"HIDE_MODERN_INTERFACE" = "Ocultar Interface Moderna (YTNoModernUI)"; +"HIDE_MODERN_INTERFACE_DESC" = "Ative esta opção para ocultar qualquer elemento moderno adicionado pelo YouTube. Remove a iluminação cinematogrática, design arredondado e muito mais. A reinicialização do app é necessária."; + +"IPAD_LAYOUT" = "Layout do iPad"; +"IPAD_LAYOUT_DESC" = "Use isso apenas se quiser ter o layout do iPad no seu iPhone/iPod atual. A reinicialização do app é necessária."; + +"IPHONE_LAYOUT" = "Layout do iPhone"; +"IPHONE_LAYOUT_DESC" = "Use isso apenas se quiser ter o layout do iPhone no seu iPad atual. A reinicialização do app é necessária."; + +"CAST_CONFIRM" = "Alerta de confirmação antes de transmitir (YTCastConfirm)"; +"CAST_CONFIRM_DESC" = "Mostra um alerta de confirmação antes da transmissão para evitar o sequestro acidental da TV."; +"CASTING" = "Transmissão"; +"MSG_ARE_YOU_SURE" = "Tem certeza de que deseja começar a transmitir?"; +"MSG_YES" = "Sim"; +"MSG_CANCEL" = "Cancelar"; + +"NEW_MINIPLAYER_STYLE" = "Novo estilo de barra de miniplayer (BigYTMiniPlayer)"; +"NEW_MINIPLAYER_STYLE_DESC" = "A reinicialização do app é necessária."; + +"HIDE_CAST_BUTTON" = "Ocultar o botão Transmitir"; +"HIDE_CAST_BUTTON_DESC" = "A reinicialização do app é necessária."; + +"VIDEO_PLAYER_BUTTON" = "Botão do Reprodutor de Vídeo"; +"VIDEO_PLAYER_BUTTON_DESC" = "Mostra um botão na barra de navegação para abrir vídeos baixados no Reprodutor de video da Apple"; + +"HIDE_SPONSORBLOCK_BUTTON" = "Ocultar o botão iSponsorBlock na barra de navegação"; +"HIDE_SPONSORBLOCK_BUTTON_DESC" = ""; + +"HIDE_HOME_TAB" = "Ocultar guia Início"; +"HIDE_HOME_TAB_DESC" = "Tenha cuidado ao ocultar todas as guias"; + +"FIX_CASTING" = "Corrigir Transmissão"; +"FIX_CASTING_DESC" = "Altera alguns sinalizadores AB para corrigir a transmissão"; + +"ENABLE_FLEX" = "Ativar FLEX"; +"ENABLE_FLEX_DESC" = "Ativa o FLEX para depuração (não recomendado). Deixe isso desligado, a menos que você saiba o que está fazendo."; + +// Version Spoofer +"APP_VERSION_SPOOFER_LITE" = "Ativar Falsificação da Versão do App (Lite)"; +"APP_VERSION_SPOOFER_LITE_DESC" = "Ative isto para usar a Falsificação de Versão (Lite) e selecione sua versão preferida abaixo. A reinicialização do app é necessária."; +"VERSION_SPOOFER_TITLE" = "Selecionar Versão Falsa"; + +// Other Localization +"APP_RESTART_DESC" = "A reinicialização do app é necessária."; +"CHANGE_APP_ICON" = "Mudar o Ícone do Aplicativo"; From 108deb8d6b1e812e7d0cac9e4c3c43a19fba8994 Mon Sep 17 00:00:00 2001 From: GitHub Actions Date: Sat, 31 Aug 2024 00:47:00 +0000 Subject: [PATCH 22/47] updated submodules --- Tweaks/protobuf | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Tweaks/protobuf b/Tweaks/protobuf index 890dd25..e4449ba 160000 --- a/Tweaks/protobuf +++ b/Tweaks/protobuf @@ -1 +1 @@ -Subproject commit 890dd25c7f6fbe9680b5467497235796bd4dcc9e +Subproject commit e4449ba24809b2043932edd0f7504dbdc0239e52 From 0d87774cd7b61a94f040ec9860c4e5db462e0a48 Mon Sep 17 00:00:00 2001 From: GitHub Actions Date: Sun, 1 Sep 2024 00:56:34 +0000 Subject: [PATCH 23/47] updated submodules --- Tweaks/iSponsorBlock | 2 +- Tweaks/protobuf | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Tweaks/iSponsorBlock b/Tweaks/iSponsorBlock index e3b2c44..5c0dcc3 160000 --- a/Tweaks/iSponsorBlock +++ b/Tweaks/iSponsorBlock @@ -1 +1 @@ -Subproject commit e3b2c4429099699b5a508bd2aa1c074efb34d866 +Subproject commit 5c0dcc3df0eba9f1f9028e1183f41e5b6e9a6434 diff --git a/Tweaks/protobuf b/Tweaks/protobuf index e4449ba..b915e9f 160000 --- a/Tweaks/protobuf +++ b/Tweaks/protobuf @@ -1 +1 @@ -Subproject commit e4449ba24809b2043932edd0f7504dbdc0239e52 +Subproject commit b915e9f44e9d979617d7738f7ddd7dd02ccd7c76 From d9a9ef556342b86c41b7bd61e45c4b545d078e60 Mon Sep 17 00:00:00 2001 From: DeciBelioS <96150975+Deci8BelioS@users.noreply.github.com> Date: Sun, 1 Sep 2024 18:41:09 +0200 Subject: [PATCH 24/47] Update Localizable.strings Spanish (#391) --- .../es.lproj/Localizable.strings | 78 +++++++++---------- 1 file changed, 39 insertions(+), 39 deletions(-) diff --git a/lang/YTLitePlus.bundle/es.lproj/Localizable.strings b/lang/YTLitePlus.bundle/es.lproj/Localizable.strings index d00268d..5f206cb 100644 --- a/lang/YTLitePlus.bundle/es.lproj/Localizable.strings +++ b/lang/YTLitePlus.bundle/es.lproj/Localizable.strings @@ -2,37 +2,37 @@ "VERSION" = "Versión de YTLitePlus: %@"; "VERSION_CHECK" = "Pulse para comprobar si hay actualizaciones."; -"COPY_SETTINGS" = "Copy Settings"; -"COPY_SETTINGS_DESC" = "Copy all current settings to the clipboard"; -"PASTE_SETTINGS" = "Paste Settings"; -"PASTE_SETTINGS_DESC" = "Paste settings from clipboard and apply"; -"PASTE_SETTINGS_ALERT" = "Apply settings from clipboard?"; -"EXPORT_SETTINGS" = "Export Settings"; -"EXPORT_SETTINGS_DESC" = "Exports all current settings into a .txt file"; -"IMPORT_SETTINGS" = "Import Settings"; -"IMPORT_SETTINGS_DESC" = "Press to import settings (.txt)"; -"REPLACE_COPY_AND_PASTE_BUTTONS" = "Replace 'Copy Settings' & 'Paste Settings' Buttons"; -"REPLACE_COPY_AND_PASTE_BUTTONS_DESC" = "Replaces the Buttons to 'Export Settings' and 'Import Settings'"; +"COPY_SETTINGS" = "Copiar Configuraciones"; +"COPY_SETTINGS_DESC" = "Copiar todas las configuraciones actuales al portapapeles"; +"PASTE_SETTINGS" = "Pegar Configuraciones"; +"PASTE_SETTINGS_DESC" = "Pegar configuraciones desde el portapapeles y aplicar"; +"PASTE_SETTINGS_ALERT" = "¿Aplicar configuraciones desde el portapapeles?"; +"EXPORT_SETTINGS" = "Exportar Configuraciones"; +"EXPORT_SETTINGS_DESC" = "Exportar todas las configuraciones actuales a un archivo .txt"; +"IMPORT_SETTINGS" = "Importar Configuraciones"; +"IMPORT_SETTINGS_DESC" = "Presiona para importar configuraciones (.txt)"; +"REPLACE_COPY_AND_PASTE_BUTTONS" = "Reemplazar Botones de 'Copiar Configuraciones' y 'Pegar Configuraciones'"; +"REPLACE_COPY_AND_PASTE_BUTTONS_DESC" = "Reemplaza los botones con 'Exportar Configuraciones' e 'Importar Configuraciones'"; -"VIDEO_PLAYER" = "Video Player (Beta)"; -"VIDEO_PLAYER_DESC" = "Open a downloaded video in the Apple player"; +"VIDEO_PLAYER" = "Reproductor de vídeo (Beta)"; +"VIDEO_PLAYER_DESC" = "Abrir un vídeo descargado en el reproductor de Apple"; // Player Gestures -"PLAYER_GESTURES_TOGGLE" = "Enable Player Gestures"; -"VOLUME" = "Volume"; -"BRIGHTNESS" = "Brightness"; -"SEEK" = "Seek"; -"DISABLED" = "Disabled"; -"DEADZONE" = "Deadzone"; -"DEADZONE_DESC" = "Minimum distance to move before a gesture is recognized"; -"SENSITIVITY" = "Sensitivity"; -"SENSITIVITY_DESC" = "Multiplier on volume and brightness gestures"; -"PLAYER_GESTURES_TITLE" = "Player Gestures"; -"PLAYER_GESTURES_DESC" = "Configure horizontal pan gestures for the player"; -"TOP_SECTION" = "Top Section"; -"MIDDLE_SECTION" = "Middle Section"; -"BOTTOM_SECTION" = "Bottom Section"; -"PLAYER_GESTURES_HAPTIC_FEEDBACK" = "Enable Haptic Feedback"; +"PLAYER_GESTURES_TOGGLE" = "Habilitar Gestos del Reproductor"; +"VOLUME" = "Volumen"; +"BRIGHTNESS" = "Brillo"; +"SEEK" = "Buscar"; +"DISABLED" = "Deshabilitado"; +"DEADZONE" = "Zona Muerta"; +"DEADZONE_DESC" = "Distancia mínima a mover antes de que se reconozca un gesto"; +"SENSITIVITY" = "Sensibilidad"; +"SENSITIVITY_DESC" = "Multiplicador en gestos de volumen y brillo"; +"PLAYER_GESTURES_TITLE" = "Gestos del Reproductor"; +"PLAYER_GESTURES_DESC" = "Configura los gestos de desplazamiento horizontal para el reproductor"; +"TOP_SECTION" = "Sección Superior"; +"MIDDLE_SECTION" = "Sección Media"; +"BOTTOM_SECTION" = "Sección Inferior"; +"PLAYER_GESTURES_HAPTIC_FEEDBACK" = "Habilitar Retroalimentación Háptica"; // Video controls overlay options "VIDEO_CONTROLS_OVERLAY_OPTIONS" = "Opciones de superposición de controles de vídeo"; @@ -79,20 +79,20 @@ "DISABLE_ENGAGEMENT_OVERLAY" = "Desactivar la superposición de compromiso a pantalla completa"; "DISABLE_ENGAGEMENT_OVERLAY_DESC" = "Desactivar el gesto de deslizar hacia arriba y la lista de vídeos sugeridos en pantalla completa"; -"HIDE_COMMENT_PREVIEWS_UNDER_PLAYER" = "Hide Comment previews under player"; -"HIDE_COMMENT_PREVIEWS_UNDER_PLAYER_DESC" = "Hide comment spoiler in comments button"; +"HIDE_COMMENT_PREVIEWS_UNDER_PLAYER" = "Ocultar vistas previas de comentarios debajo del reproductor"; +"HIDE_COMMENT_PREVIEWS_UNDER_PLAYER_DESC" = "Ocultar spoilers de comentarios en el botón de comentarios"; -"HIDE_AUTOPLAY_MINI_PREVIEW" = "Hide autoplay mini preview"; -"HIDE_AUTOPLAY_MINI_PREVIEW_DESC" = "Hide the small suggested video box near the title in fullscreen"; +"HIDE_AUTOPLAY_MINI_PREVIEW" = "Ocultar mini vista previa de reproducción automática"; +"HIDE_AUTOPLAY_MINI_PREVIEW_DESC" = "Ocultar la pequeña caja de vídeo sugerido cerca del título en pantalla completa"; "HIDE_HUD_MESSAGES" = "Ocultar mensajes HUD"; -"HIDE_HUD_MESSAGES_DESC" = "Ejemplo: CC está activado/desactivado, Vídeo en bucle está activado,..."; +"HIDE_HUD_MESSAGES_DESC" = "Ejemplo: CC está activado/desactivado, vídeo en bucle está activado,..."; -"HIDE_COLLAPSE_BUTTON" = "Hide Collapse Button"; -"HIDE_COLLAPSE_BUTTON_DESC" = "Hides the Arrow Collapse Button that was shown in the Top Left of the Video Player."; +"HIDE_COLLAPSE_BUTTON" = "Ocultar botón de colapso"; +"HIDE_COLLAPSE_BUTTON_DESC" = "Oculta el botón de colapso en forma de flecha que se mostraba en la parte superior izquierda del reproductor de vídeo."; -"HIDE_SPEED_TOAST" = "Hide Speed Toast"; -"HIDE_SPEED_TOAST_DESC" = "Hide the 2X Speed popup when holding the player"; +"HIDE_SPEED_TOAST" = "Ocultar notificación de velocidad"; +"HIDE_SPEED_TOAST_DESC" = "Oculta la notificación de velocidad 2X cuando se mantiene presionado el reproductor"; // App settings overlay options "APP_SETTINGS_OVERLAY_OPTIONS" = "Opciones de superposición de los ajustes de la aplicación"; @@ -165,8 +165,8 @@ "HIDE_CAST_BUTTON" = "Ocultar botón Emitir"; "HIDE_CAST_BUTTON_DESC" = "Es necesario reiniciar la aplicación"; -"VIDEO_PLAYER_BUTTON" = "Video Player Button"; -"VIDEO_PLAYER_BUTTON_DESC" = "Show a button in the navigation bar to open downloaded videos in the Apple player"; +"VIDEO_PLAYER_BUTTON" = "Botón del reproductor de vídeo"; +"VIDEO_PLAYER_BUTTON_DESC" = "Mostrar un botón en la barra de navegación para abrir vídeo descargados en el reproductor de Apple"; "HIDE_SPONSORBLOCK_BUTTON" = "Ocultar el botón iSponsorBlock en la barra de navegación"; "HIDE_SPONSORBLOCK_BUTTON_DESC" = ""; From 2134ae02d208a734ab0794a2e6dc5028b5d13610 Mon Sep 17 00:00:00 2001 From: ChuTuanVu <148454063+ChuTuanVu@users.noreply.github.com> Date: Sun, 1 Sep 2024 23:41:20 +0700 Subject: [PATCH 25/47] Update Vietnamese localization (#392) --- .../vi.lproj/Localizable.strings | 389 +++++++----------- 1 file changed, 140 insertions(+), 249 deletions(-) diff --git a/lang/YTLitePlus.bundle/vi.lproj/Localizable.strings b/lang/YTLitePlus.bundle/vi.lproj/Localizable.strings index 2f678de..538ba40 100644 --- a/lang/YTLitePlus.bundle/vi.lproj/Localizable.strings +++ b/lang/YTLitePlus.bundle/vi.lproj/Localizable.strings @@ -1,299 +1,190 @@ // Settings -"VERSION" = "Phiên bản của CercubePlus: %@"; +"VERSION" = "Phiên bản YTLitePlus: %@"; "VERSION_CHECK" = "Nhấn để kiểm tra cập nhật!"; -"COPY_SETTINGS" = "Copy Settings"; -"COPY_SETTINGS_DESC" = "Copy all current settings to the clipboard"; -"PASTE_SETTINGS" = "Paste Settings"; -"PASTE_SETTINGS_DESC" = "Paste settings from clipboard and apply"; -"PASTE_SETTINGS_ALERT" = "Apply settings from clipboard?"; -"EXPORT_SETTINGS" = "Export Settings"; -"EXPORT_SETTINGS_DESC" = "Exports all current settings into a .txt file"; -"IMPORT_SETTINGS" = "Import Settings"; -"IMPORT_SETTINGS_DESC" = "Press to import settings (.txt)"; -"REPLACE_COPY_AND_PASTE_BUTTONS" = "Replace 'Copy Settings' & 'Paste Settings' Buttons"; -"REPLACE_COPY_AND_PASTE_BUTTONS_DESC" = "Replaces the Buttons to 'Export Settings' and 'Import Settings'"; +"COPY_SETTINGS" = "Sao chép cài đặt"; +"COPY_SETTINGS_DESC" = "Sao chép cài đặt hiện tại vào bảng nhớ tạm"; +"PASTE_SETTINGS" = "Dán cài đặt"; +"PASTE_SETTINGS_DESC" = "Dán cài đặt từ bảng nhớ tạm và áp dụng"; +"PASTE_SETTINGS_ALERT" = "Áp dụng các cài đặt?"; +"EXPORT_SETTINGS" = "Xuất cài đặt"; +"EXPORT_SETTINGS_DESC" = "Xuất tất cài đặt hiện tại vào tệp .txt"; +"IMPORT_SETTINGS" = "Nhập cài đặt"; +"IMPORT_SETTINGS_DESC" = "Nhấn để nhập cài đặt (.txt)"; +"REPLACE_COPY_AND_PASTE_BUTTONS" = "Thay thế nút 'Sao chép cài đặt' và 'Dán cài đặt'"; +"REPLACE_COPY_AND_PASTE_BUTTONS_DESC" = "Thay thế các nút thành 'Xuất cài đặt' và 'Nhập cài đặt'"; -"VIDEO_PLAYER" = "Video Player (Beta)"; -"VIDEO_PLAYER_DESC" = "Open a downloaded video in the Apple player"; +"VIDEO_PLAYER" = "Trình phát video (Beta)"; +"VIDEO_PLAYER_DESC" = "Mở video đã tải xuống trong trình phát Apple"; // Player Gestures -"PLAYER_GESTURES_TOGGLE" = "Enable Player Gestures"; -"VOLUME" = "Volume"; -"BRIGHTNESS" = "Brightness"; -"SEEK" = "Seek"; -"DISABLED" = "Disabled"; -"DEADZONE" = "Deadzone"; -"DEADZONE_DESC" = "Minimum distance to move before a gesture is recognized"; -"SENSITIVITY" = "Sensitivity"; -"SENSITIVITY_DESC" = "Multiplier on volume and brightness gestures"; -"PLAYER_GESTURES_TITLE" = "Player Gestures"; -"PLAYER_GESTURES_DESC" = "Configure horizontal pan gestures for the player"; -"TOP_SECTION" = "Top Section"; -"MIDDLE_SECTION" = "Middle Section"; -"BOTTOM_SECTION" = "Bottom Section"; -"PLAYER_GESTURES_HAPTIC_FEEDBACK" = "Enable Haptic Feedback"; - -// Video player options -"VIDEO_PLAYER_OPTIONS" = "Tùy chọn trình phát video"; - -"SNAP_TO_CHAPTER" = "Vô hiệu hóa đính vào chương"; -"SNAP_TO_CHAPTER_DESC" = "Tắt tính năng tự động chuyển sang chương khi tìm kiếm trong video. Khởi động lại ứng dụng là bắt buộc."; - -"PINCH_TO_ZOOM" = "Vô hiệu hóa chụm để thu phóng"; -"PINCH_TO_ZOOM_DESC" = "Khởi động lại ứng dụng là bắt buộc."; - -"YT_MINIPLAYER" = "Bật trình phát mini cho tất cả các video trên YouTube"; -"YT_MINIPLAYER_DESC" = "Ví dụ: video dành cho trẻ em"; +"PLAYER_GESTURES_TOGGLE" = "Bật cử chỉ trình phát"; +"VOLUME" = "Âm lượng"; +"BRIGHTNESS" = "Độ sáng"; +"SEEK" = "Tua"; +"DISABLED" = "Vô hiệu hóa"; +"DEADZONE" = "Vùng chết"; +"DEADZONE_DESC" = "Khoảng cách tối thiểu để nhận diện cử chỉ"; +"SENSITIVITY" = "Độ nhạy"; +"SENSITIVITY_DESC" = "Hệ số nhân cho cử chỉ âm lượng và độ sáng"; +"PLAYER_GESTURES_TITLE" = "Cử chỉ trình phát"; +"PLAYER_GESTURES_DESC" = "Cấu hình cử chỉ vuốt ngang cho trình phát"; +"TOP_SECTION" = "Phần trên"; +"MIDDLE_SECTION" = "Phần giữa"; +"BOTTOM_SECTION" = "Phần dưới"; +"PLAYER_GESTURES_HAPTIC_FEEDBACK" = "Bật phản hồi xúc giác"; // Video controls overlay options -"VIDEO_CONTROLS_OVERLAY_OPTIONS" = "Tùy chọn lớp phủ điều khiển video"; +"VIDEO_CONTROLS_OVERLAY_OPTIONS" = "Lớp phủ video"; -"HIDE_CHANNEL_WATERMARK" = "Ẩn hình mờ kênh"; -"HIDE_CHANNEL_WATERMARK_DESC" = "Ẩn hình mờ của kênh trong lớp phủ điều khiển video. Khởi động lại ứng dụng là bắt buộc."; +"ENABLE_SHARE_BUTTON" = "Bật nút chia sẻ"; +"ENABLE_SHARE_BUTTON_DESC" = "Thêm nút chia sẻ trong trình phát video."; -"RED_PROGRESS_BAR" = "Thanh tiến trình màu đỏ"; -"RED_PROGRESS_BAR_DESC" = "Mang lại thanh tiến trình màu đỏ. Khởi động lại ứng dụng là bắt buộc."; +"ENABLE_SAVE_TO_PLAYLIST_BUTTON" = "Bật nút 'Lưu vào danh sách phát'"; +"ENABLE_SAVE_TO_PLAYLIST_BUTTON_DESC" = "Thêm nút 'Lưu vào danh sách phát' trong trình phát video."; -"DONT_EAT_MY_CONTENT" = "Ngăn Notch/Đảo trên nội dung video 2:1 (DontEatMyContent)"; -"DONT_EAT_MY_CONTENT_DESC" = "Ngăn notch/Dynamic Island nghiền ngẫm nội dung video 2:1 trên YouTube. Khởi động lại ứng dụng là bắt buộc."; +"HIDE_SHADOW_OVERLAY_BUTTONS" = "Ẩn bóng các nút"; +"HIDE_SHADOW_OVERLAY_BUTTONS_DESC" = "Ẩn bóng các nút Phát/Tạm dừng, Trước/Tiếp theo, Tua tới/Tua lại."; + +"HIDE_RIGHT_PANEL" = "Ẩn bảng bên phải ở chế độ toàn màn hình"; +"HIDE_RIGHT_PANEL_DESC" = "Điều nãy sẽ khiến bạn không thể xem nội dung mô tả, bình luận, v.v ở chế độ toàn màn hình. Cần khởi động lại ứng dụng."; "HIDE_HEATWAVES" = "Ẩn sóng nhiệt"; -"HIDE_HEATWAVES_DESC" = "Ẩn Sóng nhiệt trong trình phát video. Khởi động lại ứng dụng là bắt buộc."; +"HIDE_HEATWAVES_DESC" = "Ẩn sóng nhiệt khỏi trình phát video. Cần khởi động lại ứng dụng."; -"DISABLE_AMBIENT_PORTRAIT" = "Disable Ambient Mode (Portrait)"; -"DISABLE_AMBIENT_PORTRAIT_DESC" = "Disable lighting surrounding video title"; +"DISABLE_AMBIENT_PORTRAIT" = "Tắt chế độ môi trường (Chân dung)"; +"DISABLE_AMBIENT_PORTRAIT_DESC" = "Tắt ánh sáng xung quanh tiêu đề video"; -"DISABLE_AMBIENT_FULLSCREEN" = "Disable Ambient Mode (Fullscreen)"; -"DISABLE_AMBIENT_FULLSCREEN_DESC" = "Disable lighting surrouding video player"; +"DISABLE_AMBIENT_FULLSCREEN" = "Tắt chế độ môi trường (Toàn màn hình)"; +"DISABLE_AMBIENT_FULLSCREEN_DESC" = "Tắt ánh sáng xung quanh trình phát video"; -"FULLSCREEN_TO_THE_RIGHT" = "Fullscreen to the Right"; -"FULLSCREEN_TO_THE_RIGHT_DESC" = "Always enter fullscreen with home button on the right side."; +"FULLSCREEN_TO_THE_RIGHT" = "Toàn màn hình bên phải"; +"FULLSCREEN_TO_THE_RIGHT_DESC" = "Luôn vào chế độ toàn màn hình với nút home ở bên phải."; -"SEEK_ANYWHERE" = "Seek Anywhere Gesture"; -"SEEK_ANYWHERE_DESC" = "Hold and drag on the video player to seek. You must disable YTLite - Hold to speed"; +"SEEK_ANYWHERE" = "Tua ở mọi nơi"; +"SEEK_ANYWHERE_DESC" = "Giữ và kéo bất kỳ đâu trên trình phát video để tua. Bạn phải tắt tùy chỉnh YTLite - Giữ để tua"; -"ENABLE_TAP_TO_SEEK" = "Enable Tap To Seek"; -"ENABLE_TAP_TO_SEEK_DESC" = "Jump to anywhere in a video by single-tapping the seek bar"; +"ENABLE_TAP_TO_SEEK" = "Nhấn để tua"; +"ENABLE_TAP_TO_SEEK_DESC" = "Nhấn một lần vào thanh tua để tua đến bất kỳ vị trí nào trong video"; -"DISABLE_PULL_TO_FULLSCREEN_GESTURE" = "Disable pull-to-fullscreen gesture"; -"DISABLE_PULL_TO_FULLSCREEN_GESTURE_DESC" = "Disable the drag gesture to enter vertical fullscreen. Only applies to landscape videos."; +"DISABLE_PULL_TO_FULLSCREEN_GESTURE" = "Tắt cử chỉ kéo để vào toàn màn hình"; +"DISABLE_PULL_TO_FULLSCREEN_GESTURE_DESC" = "Tắt cử chỉ kéo để vào toàn màn hình dọc. Chỉ áp dụng cho video ngang."; -"ALWAYS_USE_REMAINING_TIME" = "Always use remaining time"; -"ALWAYS_USE_REMAINING_TIME_DESC" = "Change the default to show time remaining in the player bar."; +"ALWAYS_USE_REMAINING_TIME" = "Hiển thị thời gian còn lại"; +"ALWAYS_USE_REMAINING_TIME_DESC" = "Luôn hiển thị thời gian còn lại trên thanh trình phát video."; -"DISABLE_TOGGLE_TIME_REMAINING" = "Disable toggle time remaining"; -"DISABLE_TOGGLE_TIME_REMAINING_DESC" = "Disables changing time elapsed to time remaining. Use with other setting to always show remaining time."; +"DISABLE_TOGGLE_TIME_REMAINING" = "Tắt chuyển đổi thời gian còn lại"; +"DISABLE_TOGGLE_TIME_REMAINING_DESC" = "Tắt chức năng chuyển đổi từ thời gian đã phát sang thời gian còn lại. Sử dụng với cài đặt khác để luôn hiển thị thời gian còn lại."; -"DISABLE_ENGAGEMENT_OVERLAY" = "Disable fullscreen engagement overlay"; -"DISABLE_ENGAGEMENT_OVERLAY_DESC" = "Disable the swipe-up gesture and suggested videos list in fullscreen"; +"DISABLE_ENGAGEMENT_OVERLAY" = "Tắt lớp phủ tương tác toàn màn hình"; +"DISABLE_ENGAGEMENT_OVERLAY_DESC" = "Tắt cử chỉ vuốt lên để xem video gợi ý ở chế độ toàn màn hình"; -"HIDE_COMMENT_PREVIEWS_UNDER_PLAYER" = "Hide Comment previews under player"; -"HIDE_COMMENT_PREVIEWS_UNDER_PLAYER_DESC" = "Hide comment spoiler in comments button"; +"HIDE_COMMENT_PREVIEWS_UNDER_PLAYER" = "Ẩn bản xem trước bình luận dưới trình phát"; +"HIDE_COMMENT_PREVIEWS_UNDER_PLAYER_DESC" = "Ngăn tiết lộ nội dung bình luận trong nút bình luận"; -"HIDE_AUTOPLAY_MINI_PREVIEW" = "Hide autoplay mini preview"; -"HIDE_AUTOPLAY_MINI_PREVIEW_DESC" = "Hide the small suggested video box near the title in fullscreen"; +"HIDE_AUTOPLAY_MINI_PREVIEW" = "Ẩn bản xem trước tự động phát nhỏ"; +"HIDE_AUTOPLAY_MINI_PREVIEW_DESC" = "Ẩn hộp gợi ý video nhỏ gần tiêu đề ở chế độ toàn màn hình"; "HIDE_HUD_MESSAGES" = "Ẩn thông báo HUD"; -"HIDE_HUD_MESSAGES_DESC" = "Ví dụ: Đã bật/tắt phụ đề, Tính năng phát video lặp lại đang bật,..."; +"HIDE_HUD_MESSAGES_DESC" = "Ẩn các thông báo xuất hiện khi thực hiện các hành động (ví dụ: bật/tắt CC)"; -"HIDE_COLLAPSE_BUTTON" = "Hide Collapse Button"; -"HIDE_COLLAPSE_BUTTON_DESC" = "Hides the Arrow Collapse Button that was shown in the Top Left of the Video Player."; +"HIDE_COLLAPSE_BUTTON" = "Ẩn nút thu gọn"; +"HIDE_COLLAPSE_BUTTON_DESC" = "Ẩn nút thu gọn (mũi tên) xuất hiện ở góc trái trên của trình phát Video."; -"HIDE_SPEED_TOAST" = "Hide Speed Toast"; -"HIDE_SPEED_TOAST_DESC" = "Hide the 2X Speed popup when holding the player"; +"HIDE_SPEED_TOAST" = "Ẩn thông báo tốc độ"; +"HIDE_SPEED_TOAST_DESC" = "Ẩn thông báo tốc độ 2X khi giữ trình phát video"; -// Shorts controls overlay options -"SHORTS_CONTROLS_OVERLAY_OPTIONS" = "Tùy chọn lớp phủ điều khiển quần short"; +// App settings overlay options +"APP_SETTINGS_OVERLAY_OPTIONS" = "Lớp phủ cài đặt"; -"HIDE_SHORTS_CHANNEL_AVATAR" = "Ẩn hình đại diện của kênh Shorts"; -"HIDE_SHORTS_CHANNEL_AVATAR_DESC" = ""; - -"HIDE_SHORTS_LIKE_BUTTON" = "Ẩn nút thích Shorts"; -"HIDE_SHORTS_LIKE_BUTTON_DESC" = ""; - -"HIDE_SHORTS_DISLIKE_BUTTON" = "Ẩn nút không thích Shorts"; -"HIDE_SHORTS_DISLIKE_BUTTON_DESC" = ""; - -"HIDE_SHORTS_COMMENT_BUTTON" = "Ẩn nút bình luận Shorts"; -"HIDE_SHORTS_COMMENT_BUTTON_DESC" = ""; - -"HIDE_SHORTS_REMIX_BUTTON" = "Ẩn nút phối lại video ngắn"; -"HIDE_SHORTS_REMIX_BUTTON_DESC" = ""; - -"HIDE_SHORTS_SHARE_BUTTON" = "Ẩn nút chia sẻ Shorts"; -"HIDE_SHORTS_SHARE_BUTTON_DESC" = ""; - -"HIDE_SUBSCRIPTIONS" = "Hide Subscriptions button"; -"HIDE_SUBSCRIPTIONS_DESC" = "Hide Subscriptions button which shows up when paused."; - -"HIDE_SUPER_THANKS" = "Ẩn biểu ngữ Mua hàng Vô cùng cảm ơn"; -"HIDE_SUPER_THANKS_DESC" = "Ẩn biểu ngữ Mua hàng Vô cùng cảm ơn trong Shorts."; - -"DISABLE_RESUME_TO_SHORTS" = "Vô hiệu hóa sơ yếu lý lịch cho Shorts"; -"DISABLE_RESUME_TO_SHORTS_DESC" = "Nếu bạn đóng YouTube khi đang xem các video ngắn, thì các video ngắn đó sẽ không tự động phát vào lần tới khi bạn mở YouTube."; +"HIDE_ACCOUNT_SECTION" = "Ẩn cài đặt \"Chuyển đổi tài khoản\""; +"HIDE_AUTOPLAY_SECTION" = "Ẩn cài đặt \"Tự động phát\""; +"HIDE_TRYNEWFEATURES_SECTION" = "Ẩn cài đặt \"Thử các tính năng mới\""; +"HIDE_VIDEOQUALITYPREFERENCES_SECTION" = "Ẩn cài đặt \"Lựa chọn ưu tiên về chất lượng video\""; +"HIDE_NOTIFICATIONS_SECTION" = "Ẩn cài đặt \"Thông báo\""; +"HIDE_MANAGEALLHISTORY_SECTION" = "Ẩn cài đặt \"Quản lý toàn bộ nhật ký hoạt động\""; +"HIDE_YOURDATAINYOUTUBE_SECTION" = "Ẩn cài đặt \"Dữ liệu của bạn trong YouTube\""; +"HIDE_PRIVACY_SECTION" = "Ẩn cài đặt \"Quyền riêng tư\""; +"HIDE_LIVECHAT_SECTION" = "Ẩn cài đặt \"Trò chuyện trực tiếp\""; // Theme -"THEME_OPTIONS" = "Tùy chọn chủ đề"; +"THEME_OPTIONS" = "Tùy chọn giao diện"; -"OLED_DARK_THEME" = "Chủ đề tối OLED (Thử nghiệm)"; -"OLED_DARK_THEME_2" = "chủ đề tối OLED"; -"OLED_DARK_THEME_DESC" = "Chủ đề tối thực sự. Có thể không hoạt động chính xác trong một số trường hợp. Cần phải khởi động lại ứng dụng sau khi bạn bật/tắt tùy chọn này."; +"OLED_DARK_THEME" = "Giao diện tối OLED"; +"OLED_DARK_THEME_2" = "Giao diện tối OLED"; +"OLED_DARK_THEME_DESC" = "Giao diện tối thực sự. Có thể không hoạt động đúng trong một số trường hợp. Cần khởi động lại ứng dụng sau khi bạn bật/tắt tùy chọn này."; -"OLD_DARK_THEME" = "Chủ đề tối cũ"; -"OLD_DARK_THEME_DESC" = "Chủ đề tối cũ của YouTube (chủ đề màu xám). Khởi động lại ứng dụng là bắt buộc."; +"OLD_DARK_THEME" = "Giao diện tối cũ"; +"OLD_DARK_THEME_DESC" = "Giao diện tối cũ của YouTube (giao diện xám). Cần khởi động lại ứng dụng."; -"DEFAULT_THEME" = "Vỡ nợ"; -"DEFAULT_THEME_DESC" = "Chủ đề (er) tối mặc định của YouTube. Khởi động lại ứng dụng là bắt buộc."; +"DEFAULT_THEME" = "Mặc định"; +"DEFAULT_THEME_DESC" = "Giao diện mặc định của YouTube. Cần khởi động lại ứng dụng."; -"OLED_KEYBOARD" = "Bàn phím OLED (Thử nghiệm)"; -"OLED_KEYBOARD_DESC" = "Có thể không hoạt động chính xác trong một số trường hợp. Khởi động lại ứng dụng là bắt buộc."; - -// Customization Options -"CUSTOMIZATION_OPTIONS" = "Tùy chọn tùy chỉnh"; - -"HIDE_MODERN_INTERFACE" = "Ẩn giao diện hiện đại (YTNoModernUI)"; -"HIDE_MODERN_INTERFACE_DESC" = "Bật tính năng này để ẩn mọi Thành phần hiện đại do YouTube thêm vào. Loại bỏ Chế độ môi trường xung quanh, Thiết kế bo tròn và hơn thế nữa. Khởi động lại ứng dụng là bắt buộc."; - -"HIDE_YOUTUBE_LOGO" = "Ẩn biểu trưng YouTube"; -"HIDE_YOUTUBE_LOGO_DESC" = "thao tác này sẽ Ẩn Logo YouTube ở trên cùng bên trái của Giao diện. Khởi động lại ứng dụng là bắt buộc."; - -"HIDE_TAB_BAR_LABELS" = "Ẩn Nhãn trong Thanh Tab"; -"HIDE_TAB_BAR_LABELS_DESC" = "điều này sẽ Ẩn tất cả các nhãn trong Thanh tab. Khởi động lại ứng dụng là bắt buộc."; +"OLED_KEYBOARD" = "Bàn phím OLED"; +"OLED_KEYBOARD_DESC" = "Có thể hoạt động không đúng trong một số trường hợp. Cần khởi động lại ứng dụng."; "LOW_CONTRAST_MODE" = "Chế độ tương phản thấp"; -"LOW_CONTRAST_MODE_DESC" = "điều này sẽ tạo ra các văn bản và nút có độ tương phản thấp giống như Giao diện YouTube cũ. Khởi động lại ứng dụng là bắt buộc."; - -"RED_UI" = "Màu đỏ"; -"RED_UI_DESC" = "Giao diện người dùng màu đỏ (tắt mọi màu giao diện người dùng khác) Khởi động lại ứng dụng là bắt buộc."; - -"BLUE_UI" = "Giao diện người dùng màu xanh lam"; -"BLUE_UI_DESC" = "Giao diện người dùng màu xanh lam (tắt mọi màu giao diện người dùng khác) Khởi động lại ứng dụng là bắt buộc."; - -"GREEN_UI" = "Giao diện xanh"; -"GREEN_UI_DESC" = "Giao diện người dùng xanh (tắt mọi màu giao diện người dùng khác) Khởi động lại ứng dụng là bắt buộc."; - -"YELLOW_UI" = "Giao diện người dùng màu vàng"; -"YELLOW_UI_DESC" = "Giao diện người dùng màu vàng (tắt mọi màu giao diện người dùng khác) Khởi động lại ứng dụng là bắt buộc."; - -"ORANGE_UI" = "Giao diện người dùng màu cam"; -"ORANGE_UI_DESC" = "Giao diện người dùng màu cam (tắt mọi màu giao diện người dùng khác) Khởi động lại ứng dụng là bắt buộc."; - -"PURPLE_UI" = "Giao diện người dùng màu tím"; -"PURPLE_UI_DESC" = "Giao diện người dùng màu tím (tắt mọi màu giao diện người dùng khác) Khởi động lại ứng dụng là bắt buộc."; - -"PINK_UI" = "Giao diện người dùng màu hồng"; -"PINK_UI_DESC" = "Giao diện người dùng màu hồng (tắt mọi màu giao diện người dùng khác) Khởi động lại ứng dụng là bắt buộc."; +"LOW_CONTRAST_MODE_DESC" = "Tùy chọn này sẽ giảm tương phản của văn bản và nút giống như giao diện YouTube cũ. Cần khởi động lại ứng dụng."; +"LCM_SELECTOR" = "Chọn chế độ tương phản thấp"; +"DEFAULT_LOWCONTRASTMODE" = "(Mặc định) Chế độ tương phản thấp"; +"CUSTOM_LOWCONTRASTMODE" = "(Màu tùy chỉnh) Chế độ tương phản thấp"; // Miscellaneous -"MISCELLANEOUS" = "Điều khoản khác"; +"MISCELLANEOUS" = "Tùy chọn khác"; -"CAST_CONFIRM" = "Xác nhận cảnh báo trước khi truyền (YTCastConfirm)"; -"CAST_CONFIRM_DESC" = "Hiển thị cảnh báo xác nhận trước khi truyền để tránh vô tình chiếm quyền điều khiển TV."; -"CASTING" = "Đúc"; -"MSG_ARE_YOU_SURE" = "Bạn có chắc chắn muốn bắt đầu truyền không?"; -"MSG_YES" = "Đúng"; -"MSG_CANCEL" = "Hủy bỏ"; +"PLAYBACK_IN_FEEDS" = "Phát trong các trang danh sách video"; +"PLAYBACK_IN_FEEDS_ALWAYS_ON" = "Luôn bật"; +"PLAYBACK_IN_FEEDS_WIFI_ONLY" = "Chỉ Wi-Fi"; +"PLAYBACK_IN_FEEDS_OFF" = "Tắt"; -"DISABLE_HINTS" = "Tắt gợi ý"; -"DISABLE_HINTS_DESC" = "Tắt gợi ý tính năng từ YouTube thường hiển thị khi ứng dụng mới được cài đặt."; +"NEW_SETTINGS_UI" = "Giao diện cài đặt mới"; +"NEW_SETTINGS_UI_DESC" = "Nhóm các giao diện cài đặt. Một số cài đặt có thể bị ẩn"; -"ENABLE_FLEX" = "Kích hoạt FLEX"; -"ENABLE_FLEX_DESC" = "Bật FLEX để gỡ lỗi (không khuyến nghị). Bỏ qua điều này trừ khi bạn biết những gì bạn đang làm."; +"ENABLE_YT_STARTUP_ANIMATION" = "Hoạt ảnh khởi động YouTube"; +"ENABLE_YT_STARTUP_ANIMATION_DESC" = "Hiển thị hoạt ảnh khi mở YouTube"; -"FIX_GOOGLE_SIGNIN" = "Sửa lỗi Đăng nhập bằng Google (chỉ dành cho người dùng TrollStore)"; -"FIX_GOOGLE_SIGNIN_DESC" = "Chỉ bật tùy chọn này khi bạn không thể đăng nhập bằng tài khoản Google của mình và ứng dụng đã được cài đặt qua TrollStore. Nếu bạn có thể đăng nhập bình thường, hãy tắt nó đi. Khởi động lại ứng dụng là bắt buộc."; - -"HIDE_CHIP_BAR" = "Ẩn thanh trên"; -"HIDE_CHIP_BAR_DESC" = "Ẩn thanh trên trong nguồn cấp dữ liệu Trang chủ (Xu hướng, Âm nhạc, Trò chơi...) và nguồn cấp dữ liệu Đăng ký (Tất cả video, Tiếp tục xem...)."; - -"NEW_MINIPLAYER_STYLE" = "Phong cách thanh người chơi mini mới (BigYTMiniPlayer)"; -"NEW_MINIPLAYER_STYLE_DESC" = ""; - -"REPLACE_PREVIOUS_NEXT_BUTTON" = "Thay nút Previous và Next"; -"REPLACE_PREVIOUS_NEXT_BUTTON_DESC" = "Thay thế nút Trước và Tiếp theo bằng nút Tua đi và Tua lại. Khởi động lại ứng dụng là bắt buộc."; - -"HIDE_PREVIOUS_AND_NEXT_BUTTON" = "Ẩn nút Trước và Tiếp theo"; -"HIDE_PREVIOUS_AND_NEXT_BUTTON_DESC" = "Ẩn nút Trước đó và Tiếp theo trong lớp phủ điều khiển video."; - -"HIDE_SHORTS_VIDEOS" = "Ẩn video ngắn"; -"HIDE_SHORTS_VIDEOS_DESC" = "Ẩn video ngắn trong Trang chủ, được đề xuất..."; - -"HIDE_CERCUBE_BUTTON" = "Ẩn nút Cercube trong thanh Điều hướng"; -"HIDE_CERCUBE_BUTTON_DESC" = ""; - -"HIDE_CERCUBE_PIP_BUTTON" = "Ẩn nút PiP của Cercube"; -"HIDE_CERCUBE_PIP_BUTTON_DESC" = "Ẩn nút PiP của Cercube trong lớp phủ điều khiển video."; - -"HIDE_CERCUBE_DOWNLOAD_BUTTON" = "Ẩn nút Tải xuống của Cercube"; -"HIDE_CERCUBE_DOWNLOAD_BUTTON_DESC" = "Tùy chọn ẩn nút Tải xuống của Cercube đã được bật theo mặc định. bởi vì hiện tại bạn không thể tải xuống bất kỳ thứ gì vì Máy chủ tải xuống của Cercube đã biến mất."; - -"HIDE_CAST_BUTTON" = "Ẩn nút Truyền"; -"HIDE_CAST_BUTTON_DESC" = "Khởi động lại ứng dụng là bắt buộc."; - -"VIDEO_PLAYER_BUTTON" = "Video Player Button"; -"VIDEO_PLAYER_BUTTON_DESC" = "Show a button in the navigation bar to open downloaded videos in the Apple player"; - -"HIDE_HOVER_CARD" = "Ẩn thẻ di chuột trên Màn hình kết thúc (YTNoHoverCards)"; -"HIDE_HOVER_CARD_DESC" = "Ẩn màn hình kết thúc (hình thu nhỏ) của người tạo ở cuối video."; - -"HIDE_RIGHT_PANEL" = "Ẩn bảng điều khiển bên phải ở chế độ toàn màn hình"; -"HIDE_RIGHT_PANEL_DESC" = "Khởi động lại ứng dụng là bắt buộc."; - -"HIDE_SUBTITLES_BUTTON" = "Ẩn nút phụ đề"; -"HIDE_SUBTITLES_BUTTON_DESC" = "Ẩn nút Phụ đề trong lớp phủ điều khiển video."; - -"HIDE_AUTOPLAY_SWITCH" = "Ẩn công tắc Tự động phát"; -"HIDE_AUTOPLAY_SWITCH_DESC" = "Ẩn công tắc Tự động phát trong lớp phủ điều khiển video."; - -"AUTO_FULLSCREEN" = "Tự động toàn màn hình(YTAutoFullScreen)"; -"AUTO_FULLSCREEN_DESC" = "Tự động phát video ở chế độ toàn màn hình."; - -"HIDE_HUD_MESSAGES" = "Ẩn tin nhắn HUD"; -"HIDE_HUD_MESSAGES_DESC" = "Ví dụ: Bật/tắt CC, Bật vòng lặp video,..."; - -"HIDE_PAID_PROMOTION_CARDS" = "Ẩn thẻ Khuyến mại trả phí"; -"HIDE_PAID_PROMOTION_CARDS_DESC" = "Ẩn thẻ Bao gồm quảng cáo trả phí trong một số video."; - -"HIDE_NOTIFICATION_BUTTON" = "Ẩn nút Thông báo trong thanh Điều hướng"; -"HIDE_NOTIFICATION_BUTTON_DESC" = ""; - -"YT_RE_EXPLORE" = "Thay thế tab Shorts bằng tab Khám phá (YTReExplore)"; -"YT_RE_EXPLORE_DESC" = "Khởi động lại ứng dụng là bắt buộc."; +"HIDE_MODERN_INTERFACE" = "Giao diện YouTube cũ"; +"HIDE_MODERN_INTERFACE_DESC" = "Đưa giao diện cũ của YouTube trở lại từ phiên bản v17.38.10. Loại bỏ một số yếu tố bo tròn, chế độ môi trường, và các tính năng hiện đại khác. Cần khởi động lại ứng dụng."; "IPAD_LAYOUT" = "Bố cục iPad"; -"IPAD_LAYOUT_DESC" = "Chỉ sử dụng tùy chọn này nếu bạn muốn tải Bố cục iPad trên iPhone/iPod hiện tại của mình. Khởi động lại ứng dụng là bắt buộc."; +"IPAD_LAYOUT_DESC" = "Chỉ sử dụng nếu muốn có bố cục iPad trên iPhone/iPod của mình. Cần khởi động lại ứng dụng."; "IPHONE_LAYOUT" = "Bố cục iPhone"; -"IPHONE_LAYOUT_DESC" = "Chỉ sử dụng tùy chọn này nếu bạn muốn tải Bố cục iPhone trên iPad hiện tại của mình. Khởi động lại ứng dụng là bắt buộc."; +"IPHONE_LAYOUT_DESC" = "Chỉ sử dụng nếu muốn có bố cục iPhone trên iPad của mình. Cần khởi động lại ứng dụng."; -"CHANGE_APP_ICON" = "Change App Icon"; +"CAST_CONFIRM" = "Xác nhận trước khi truyền"; +"CAST_CONFIRM_DESC" = "Hiển thị thông báo xác nhận trước khi truyền."; +"CASTING" = "Truyền"; +"MSG_ARE_YOU_SURE" = "Bạn có chắc chắn muốn bắt đầu truyền không?"; +"MSG_YES" = "Có"; +"MSG_CANCEL" = "Không"; -// Newly added strings -"APP_RESTART_DESC": "Mô tả khi khởi động lại ứng dụng", -"CUSTOM_LOWCONTRASTMODE": "Chế độ độ tương phản thấp tùy chỉnh", -"APP_VERSION_SPOOFER_LITE": "Phiên bản giả lập ứng dụng nhẹ", -"PLAYBACK_IN_FEEDS_OFF": "Tắt phát trong các trang danh sách video", -"PLAYBACK_IN_FEEDS": "Phát trong các trang danh sách video", -"ENABLE_SHARE_BUTTON_DESC": "Mô tả nút chia sẻ", -"ENABLE_SAVE_TO_PLAYLIST_BUTTON": "Bật nút lưu vào danh sách phát", -"LCM_SELECTOR": "Trình chọn chế độ độ tương phản thấp", -"NEW_SETTINGS_UI_DESC": "Mô tả giao diện cài đặt mới", -"VERSION_SPOOFER_TITLE": "Tiêu đề giả lập phiên bản", -"HIDE_SPONSORBLOCK_BUTTON": "Ẩn nút SponsorBlock", -"ENABLE_SHARE_BUTTON": "Bật nút chia sẻ", -"ENABLE_SAVE_TO_PLAYLIST_BUTTON_DESC": "Mô tả nút lưu vào danh sách phát", -"HIDE_SHADOW_OVERLAY_BUTTONS": "Ẩn các nút lớp phủ bóng", -"APP_VERSION_SPOOFER_LITE_DESC": "Mô tả phiên bản giả lập ứng dụng nhẹ", -"DEFAULT_LOWCONTRASTMODE": "Chế độ độ tương phản thấp mặc định", -"APP_SETTINGS_OVERLAY_OPTIONS": "Tùy chọn lớp phủ cài đặt ứng dụng", -"HIDE_HOME_TAB_DESC": "Mô tả ẩn tab Trang chủ", -"PLAYBACK_IN_FEEDS_ALWAYS_ON": "Luôn bật phát trong các trang danh sách video", -"FIX_CASTING": "Sửa lỗi truyền phát", -"FIX_CASTING_DESC": "Mô tả sửa lỗi truyền phát", -"PLAYBACK_IN_FEEDS_WIFI_ONLY": "Phát trong các trang danh sách video chỉ qua WiFi", -"NEW_MINIPLAYER_STYLE_DESC": "Mô tả kiểu trình phát nhỏ mới", -"NEW_SETTINGS_UI": "Giao diện cài đặt mới", -"HIDE_HOME_TAB": "Ẩn tab Trang chủ", -"HIDE_SHADOW_OVERLAY_BUTTONS_DESC": "Mô tả ẩn các nút lớp phủ bóng", -"ENABLE_YT_STARTUP_ANIMATION": "Bật hoạt ảnh khởi động YouTube" +"NEW_MINIPLAYER_STYLE" = Trình phát thu nhỏ kiểu mới"; +"NEW_MINIPLAYER_STYLE_DESC" = "Thay thế trình phát thu nhỏ mặc định thành (BigYTMiniPlayer). Cần khởi động lại ứng dụng."; + +"HIDE_CAST_BUTTON" = "Ẩn nút truyền"; +"HIDE_CAST_BUTTON_DESC" = "Cần khởi động lại ứng dụng."; + +"VIDEO_PLAYER_BUTTON" = "Nút Trình phát video"; +"VIDEO_PLAYER_BUTTON_DESC" = "Hiển thị nút trong thanh điều hướng để mở video đã tải xuống trong trình phát Apple"; + +"HIDE_SPONSORBLOCK_BUTTON" = "Ẩn nút iSponsorBlock"; +"HIDE_SPONSORBLOCK_BUTTON_DESC" = "Ẩn cài đặt iSponsorBlock trên thanh điều hướng"; + +"HIDE_HOME_TAB" = "Ẩn tab Trang chủ"; +"HIDE_HOME_TAB_DESC" = "Hãy cẩn thận khi ẩn tất cả các tab"; + +"FIX_CASTING" = "Sửa truyền"; +"FIX_CASTING_DESC" = "Thay đổi một số cờ AB để sửa truyền"; + +"ENABLE_FLEX" = "Bật FLEX"; +"ENABLE_FLEX_DESC" = "Bật FLEX để gỡ lỗi (không khuyến khích). Để tùy chọn này tắt trừ khi bạn biết rõ bạn đang làm gì."; + +// Version Spoofer +"APP_VERSION_SPOOFER_LITE" = "Giả mạo phiên bản ứng dụng (Lite)"; +"APP_VERSION_SPOOFER_LITE_DESC" = "Bật tính năng này để làm giả phiên bản YouTube (Lite). Chọn phiên bản bạn ưa thích bên dưới. Cần khởi động lại ứng dụng."; +"VERSION_SPOOFER_TITLE" = "Chọn phiên bản giả mạo"; + +// Other Localization +"APP_RESTART_DESC" = "Cần khởi động lại ứng dụng."; +"CHANGE_APP_ICON" = "Đổi biểu tượng ứng dụng"; From 810aca2f162b13f764c20e58e8b122dacf474ebd Mon Sep 17 00:00:00 2001 From: Bryce Hackel <34104885+bhackel@users.noreply.github.com> Date: Sun, 1 Sep 2024 11:06:07 -0700 Subject: [PATCH 26/47] Disable warning popup --- YTLitePlus.h | 4 ++++ YTLitePlus.xm | 18 +++++++++++++----- 2 files changed, 17 insertions(+), 5 deletions(-) diff --git a/YTLitePlus.h b/YTLitePlus.h index 247af9e..1c3ead2 100644 --- a/YTLitePlus.h +++ b/YTLitePlus.h @@ -130,6 +130,10 @@ typedef NS_ENUM(NSUInteger, GestureSection) { @property (nonatomic, assign, readwrite) BOOL enableSnapToChapter; @end +// Hide YouTube Plus incompatibility warning popup - @bhackel +@interface HelperVC : UIViewController +@end + // Hide Autoplay Mini Preview - @bhackel @interface YTAutonavPreviewView : UIView @end diff --git a/YTLitePlus.xm b/YTLitePlus.xm index 093b70d..aa8b0bc 100644 --- a/YTLitePlus.xm +++ b/YTLitePlus.xm @@ -110,12 +110,8 @@ BOOL isSelf() { } %end -# pragma mark - Hide SponsorBlock Button +// Hide SponsorBlock Button in navigation bar %hook YTRightNavigationButtons -- (void)didMoveToWindow { - %orig; -} - - (void)layoutSubviews { %orig; if (IsEnabled(@"hideSponsorBlockButton_enabled")) { @@ -174,6 +170,18 @@ BOOL isSelf() { %end %end +// Disable YouTube Plus incompatibility warning popup - @bhackel +%hook HelperVC +- (void)viewDidLoad { + %orig; + // Check if it responds to the selector riskButtonTapped + if ([self respondsToSelector:@selector(riskButtonTapped)]) { + // Call the selector riskButtonTapped + [self performSelector:@selector(riskButtonTapped)]; + } +} +%end + // A/B flags %hook YTColdConfig From a3ac8e973584860a0ed643a52e710ae1973726b1 Mon Sep 17 00:00:00 2001 From: Bryce Hackel <34104885+bhackel@users.noreply.github.com> Date: Sun, 1 Sep 2024 11:19:09 -0700 Subject: [PATCH 27/47] Default disable iSponsorBlock --- YTLitePlus.xm | 54 ++++++++++++++++++++++----------------------------- 1 file changed, 23 insertions(+), 31 deletions(-) diff --git a/YTLitePlus.xm b/YTLitePlus.xm index aa8b0bc..4dd9a90 100644 --- a/YTLitePlus.xm +++ b/YTLitePlus.xm @@ -1319,36 +1319,28 @@ NSInteger pageStyle = 0; // Change the default value of some options NSArray *allKeys = [[[NSUserDefaults standardUserDefaults] dictionaryRepresentation] allKeys]; - if (![allKeys containsObject:@"RYD-ENABLED"]) { - [[NSUserDefaults standardUserDefaults] setBool:YES forKey:@"RYD-ENABLED"]; - } - if (![allKeys containsObject:@"YouPiPEnabled"]) { - [[NSUserDefaults standardUserDefaults] setBool:YES forKey:@"YouPiPEnabled"]; - } - if (![allKeys containsObject:@"newSettingsUI_enabled"]) { - [[NSUserDefaults standardUserDefaults] setBool:YES forKey:@"newSettingsUI_enabled"]; - } - if (![allKeys containsObject:@"fixCasting_enabled"]) { - [[NSUserDefaults standardUserDefaults] setBool:YES forKey:@"fixCasting_enabled"]; - } - // Default gestures as volume, brightness, seek - if (![allKeys containsObject:@"playerGestureTopSelection"]) { - [[NSUserDefaults standardUserDefaults] setInteger:GestureModeVolume forKey:@"playerGestureTopSelection"]; - } - if (![allKeys containsObject:@"playerGestureMiddleSelection"]) { - [[NSUserDefaults standardUserDefaults] setInteger:GestureModeBrightness forKey:@"playerGestureMiddleSelection"]; - } - if (![allKeys containsObject:@"playerGestureBottomSelection"]) { - [[NSUserDefaults standardUserDefaults] setInteger:GestureModeSeek forKey:@"playerGestureBottomSelection"]; - } - // Default configuration options for gestures - if (![allKeys containsObject:@"playerGesturesDeadzone"]) { - [[NSUserDefaults standardUserDefaults] setFloat:20.0 forKey:@"playerGesturesDeadzone"]; - } - if (![allKeys containsObject:@"playerGesturesSensitivity"]) { - [[NSUserDefaults standardUserDefaults] setFloat:1.0 forKey:@"playerGesturesSensitivity"]; - } - if (![allKeys containsObject:@"playerGesturesHapticFeedback_enabled"]) { - [[NSUserDefaults standardUserDefaults] setBool:YES forKey:@"playerGesturesHapticFeedback_enabled"]; + if (![allKeys containsObject:@"YTLPDidPerformFirstRunSetup"]) { + [[NSUserDefaults standardUserDefaults] setBool:YES forKey:@"YTLPDidPerformFirstRunSetup"]; + // Set iSponsorBlock to default disabled + NSString *documentsDirectory = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) firstObject]; + NSString *settingsPath = [documentsDirectory stringByAppendingPathComponent:@"iSponsorBlock.plist"]; + NSMutableDictionary *settings = [NSMutableDictionary dictionary]; + NSDictionary *existingSettings = [NSDictionary dictionaryWithContentsOfFile:settingsPath]; + [settings addEntriesFromDictionary:existingSettings]; + [settings setObject:@(NO) forKey:@"enabled"]; + [settings writeToFile:settingsPath atomically:YES]; + // Set miscellaneous YTLitePlus features to enabled + [[NSUserDefaults standardUserDefaults] setBool:YES forKey:@"RYD-ENABLED"]; + [[NSUserDefaults standardUserDefaults] setBool:YES forKey:@"YouPiPEnabled"]; + [[NSUserDefaults standardUserDefaults] setBool:YES forKey:@"newSettingsUI_enabled"]; + [[NSUserDefaults standardUserDefaults] setBool:YES forKey:@"fixCasting_enabled"]; + // Default gestures as volume, brightness, seek + [[NSUserDefaults standardUserDefaults] setInteger:GestureModeVolume forKey:@"playerGestureTopSelection"]; + [[NSUserDefaults standardUserDefaults] setInteger:GestureModeBrightness forKey:@"playerGestureMiddleSelection"]; + [[NSUserDefaults standardUserDefaults] setInteger:GestureModeSeek forKey:@"playerGestureBottomSelection"]; + // Default gestures options + [[NSUserDefaults standardUserDefaults] setFloat:20.0 forKey:@"playerGesturesDeadzone"]; + [[NSUserDefaults standardUserDefaults] setFloat:1.0 forKey:@"playerGesturesSensitivity"]; + [[NSUserDefaults standardUserDefaults] setBool:YES forKey:@"playerGesturesHapticFeedback_enabled"]; } } From 861a9fd30b2a32e34af08122af72f8a0c3ed28e0 Mon Sep 17 00:00:00 2001 From: Bryce Hackel <34104885+bhackel@users.noreply.github.com> Date: Sun, 1 Sep 2024 12:27:07 -0700 Subject: [PATCH 28/47] Remove OLED live chat fix --- YTLitePlus.xm | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/YTLitePlus.xm b/YTLitePlus.xm index 4dd9a90..aa57691 100644 --- a/YTLitePlus.xm +++ b/YTLitePlus.xm @@ -586,16 +586,6 @@ BOOL isTabSelected = NO; [self setNeedsLayout]; [self removeFromSuperview]; } - - // Live chat OLED dark mode - @bhackel - CGFloat alpha; - if ([[%c(YTLUserDefaults) standardUserDefaults] boolForKey:@"oledTheme"] // YTLite OLED Theme - && [self.accessibilityIdentifier isEqualToString:@"eml.live_chat_text_message"] // Live chat text message - && [self.backgroundColor getWhite:nil alpha:&alpha] // Check if color is grayscale and get alpha - && alpha != 0.0) // Ignore shorts live chat - { - self.backgroundColor = [UIColor blackColor]; - } } %end From d4f8a41ff2920e146d00e2fa731506c4875738b3 Mon Sep 17 00:00:00 2001 From: Bryce Hackel <34104885+bhackel@users.noreply.github.com> Date: Sun, 1 Sep 2024 13:10:51 -0700 Subject: [PATCH 29/47] Fix smoothing for player gestures --- YTLitePlus.xm | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/YTLitePlus.xm b/YTLitePlus.xm index aa57691..bfc82c8 100644 --- a/YTLitePlus.xm +++ b/YTLitePlus.xm @@ -689,7 +689,7 @@ BOOL isTabSelected = NO; // Variable used to smooth out the X translation static CGFloat smoothedTranslationX = 0; // Constant for the filter constant to change responsiveness - static const CGFloat filterConstant = 0.1; + // static const CGFloat filterConstant = 0.1; // Constant for the deadzone radius that can be changed in the settings static CGFloat deadzoneRadius = (CGFloat)GetFloat(@"playerGesturesDeadzone"); // Constant for the sensitivity factor that can be changed in the settings @@ -756,10 +756,10 @@ BOOL isTabSelected = NO; }; // Helper function to smooth out the X translation - CGFloat (^applyLowPassFilter)(CGFloat) = ^(CGFloat newTranslation) { - smoothedTranslationX = filterConstant * newTranslation + (1 - filterConstant) * smoothedTranslationX; - return smoothedTranslationX; - }; + // CGFloat (^applyLowPassFilter)(CGFloat) = ^(CGFloat newTranslation) { + // smoothedTranslationX = filterConstant * newTranslation + (1 - filterConstant) * smoothedTranslationX; + // return smoothedTranslationX; + // }; /***** Helper functions for running the selected gesture *****/ // Helper function to run any setup for the selected gesture mode @@ -923,7 +923,7 @@ BOOL isTabSelected = NO; // Adjust the X translation based on the value hit after exiting the deadzone adjustedTranslationX = translation.x - deadzoneStartingXTranslation; // Smooth the translation value - adjustedTranslationX = applyLowPassFilter(adjustedTranslationX); + // adjustedTranslationX = applyLowPassFilter(adjustedTranslationX); // Pass the adjusted translation to the selected gesture switch (gestureSection) { case GestureSectionTop: From c73cd63db47b0f9d0d1d3133cf35788853ccce7d Mon Sep 17 00:00:00 2001 From: Bryce Hackel <34104885+bhackel@users.noreply.github.com> Date: Sun, 1 Sep 2024 12:50:04 -0700 Subject: [PATCH 30/47] Cancel gesture after 1 second of inactivity --- YTLitePlus.xm | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/YTLitePlus.xm b/YTLitePlus.xm index bfc82c8..ae9a152 100644 --- a/YTLitePlus.xm +++ b/YTLitePlus.xm @@ -870,6 +870,13 @@ BOOL isTabSelected = NO; } // Deactive the activity flag isValidHorizontalPan = NO; + // Cancel this gesture if it has not activated after 1 second + dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(1 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{ + if (!isValidHorizontalPan && panGestureRecognizer.state != UIGestureRecognizerStateEnded) { + // Cancel the gesture by setting its state to UIGestureRecognizerStateCancelled + panGestureRecognizer.state = UIGestureRecognizerStateCancelled; + } + }); } // Handle changed gesture state by activating the gesture once it has exited the deadzone, From e66f68ebc676e3685d8c30d314701a7754e54197 Mon Sep 17 00:00:00 2001 From: GitHub Actions Date: Mon, 2 Sep 2024 00:49:42 +0000 Subject: [PATCH 31/47] updated submodules --- Tweaks/YTABConfig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Tweaks/YTABConfig b/Tweaks/YTABConfig index 184f678..a22ef4d 160000 --- a/Tweaks/YTABConfig +++ b/Tweaks/YTABConfig @@ -1 +1 @@ -Subproject commit 184f67837f48b3efd3c35ac88eb0d56f5e735d36 +Subproject commit a22ef4deb929c6622069290bb340d21b280f300e From 9d62ce75ce58b5ee4bf01022b7342bb679bf2b73 Mon Sep 17 00:00:00 2001 From: Bryce Hackel <34104885+bhackel@users.noreply.github.com> Date: Sun, 1 Sep 2024 14:45:20 -0700 Subject: [PATCH 32/47] Fix bugs --- Source/Settings.xm | 2 +- YTLitePlus.xm | 21 ++++++++++++--------- 2 files changed, 13 insertions(+), 10 deletions(-) diff --git a/Source/Settings.xm b/Source/Settings.xm index 418d6f4..864275b 100644 --- a/Source/Settings.xm +++ b/Source/Settings.xm @@ -396,7 +396,7 @@ static const NSInteger YTLiteSection = 789; // Toggle for haptic feedback BASIC_SWITCH(LOC(@"PLAYER_GESTURES_HAPTIC_FEEDBACK"), nil, @"playerGesturesHapticFeedback_enabled"), ]; - YTSettingsPickerViewController *picker = [[%c(YTSettingsPickerViewController) alloc] initWithNavTitle:LOC(@"Player Gestures (Beta)") pickerSectionTitle:nil rows:rows selectedItemIndex:NSNotFound parentResponder:[self parentResponder]]; + YTSettingsPickerViewController *picker = [[%c(YTSettingsPickerViewController) alloc] initWithNavTitle:LOC(@"PLAYER_GESTURES_TITLE") pickerSectionTitle:nil rows:rows selectedItemIndex:NSNotFound parentResponder:[self parentResponder]]; [settingsViewController pushViewController:picker]; return YES; }]; diff --git a/YTLitePlus.xm b/YTLitePlus.xm index ae9a152..766395a 100644 --- a/YTLitePlus.xm +++ b/YTLitePlus.xm @@ -172,14 +172,17 @@ BOOL isSelf() { // Disable YouTube Plus incompatibility warning popup - @bhackel %hook HelperVC -- (void)viewDidLoad { - %orig; - // Check if it responds to the selector riskButtonTapped - if ([self respondsToSelector:@selector(riskButtonTapped)]) { - // Call the selector riskButtonTapped - [self performSelector:@selector(riskButtonTapped)]; - } + +- (void)viewWillAppear:(BOOL)animated { + %orig; // Call the original implementation + + // // Hide the view before it appears + // self.view.hidden = YES; + + // // Optionally, you can remove the view from the superview to ensure it's gone + // [self.view removeFromSuperview]; } + %end @@ -1240,6 +1243,8 @@ NSInteger pageStyle = 0; %init; // Access YouGroupSettings methods dlopen([[NSString stringWithFormat:@"%@/Frameworks/YouGroupSettings.dylib", [[NSBundle mainBundle] bundlePath]] UTF8String], RTLD_LAZY); + // Access YouTube Plus methods + dlopen([[NSString stringWithFormat:@"%@/Frameworks/YTLite.dylib", [[NSBundle mainBundle] bundlePath]] UTF8String], RTLD_LAZY); if (IsEnabled(@"hideCastButton_enabled")) { %init(gHideCastButton); @@ -1322,8 +1327,6 @@ NSInteger pageStyle = 0; NSString *documentsDirectory = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) firstObject]; NSString *settingsPath = [documentsDirectory stringByAppendingPathComponent:@"iSponsorBlock.plist"]; NSMutableDictionary *settings = [NSMutableDictionary dictionary]; - NSDictionary *existingSettings = [NSDictionary dictionaryWithContentsOfFile:settingsPath]; - [settings addEntriesFromDictionary:existingSettings]; [settings setObject:@(NO) forKey:@"enabled"]; [settings writeToFile:settingsPath atomically:YES]; // Set miscellaneous YTLitePlus features to enabled From df0be8c66eb36872d2306d12ce51e7d355b5409a Mon Sep 17 00:00:00 2001 From: Bryce Hackel <34104885+bhackel@users.noreply.github.com> Date: Sun, 1 Sep 2024 19:31:28 -0700 Subject: [PATCH 33/47] Change hiding method --- YTLitePlus.xm | 46 +++++++++++++++++++++++++++++++++++++++------- 1 file changed, 39 insertions(+), 7 deletions(-) diff --git a/YTLitePlus.xm b/YTLitePlus.xm index 766395a..517fdd4 100644 --- a/YTLitePlus.xm +++ b/YTLitePlus.xm @@ -171,21 +171,53 @@ BOOL isSelf() { %end // Disable YouTube Plus incompatibility warning popup - @bhackel -%hook HelperVC +%hook UIView -- (void)viewWillAppear:(BOOL)animated { - %orig; // Call the original implementation +- (void)willMoveToWindow:(UIWindow *)newWindow { + UIResponder *responder = self; + while (responder) { + responder = [responder nextResponder]; + if ([responder isKindOfClass:NSClassFromString(@"HelperVC")]) { + // View belongs to HelperVC, now proceed with getting the UIButton - // // Hide the view before it appears - // self.view.hidden = YES; + if ([self.subviews count] > 4 && [[self.subviews objectAtIndex:4] isKindOfClass:[UIButton class]]) { + UIButton *button = [self.subviews objectAtIndex:4]; - // // Optionally, you can remove the view from the superview to ensure it's gone - // [self.view removeFromSuperview]; + // Access the _targetActions ivar using KVC (Key-Value Coding) + NSArray *targetActions = [button valueForKey:@"_targetActions"]; + + if ([targetActions count] > 0) { + id controlTargetAction = [targetActions objectAtIndex:0]; + + // Use KVC to get the _actionHandler (which is of type UIAction) + UIAction *actionHandler = [controlTargetAction valueForKey:@"_actionHandler"]; + + if (actionHandler && [actionHandler isKindOfClass:[UIAction class]]) { + // Access the handler property of UIAction + void (^handlerBlock)(void) = [actionHandler valueForKey:@"handler"]; + + // Invoke the handler block + if (handlerBlock) { + handlerBlock(); // Call the block + } + } + } + } + + // Prevent the view from being added to the window + [self removeFromSuperview]; + return; // Exit early to prevent further processing + } + } + + %orig(newWindow); // Call the original method if the view doesn't belong to HelperVC } %end + + // A/B flags %hook YTColdConfig - (BOOL)respectDeviceCaptionSetting { return NO; } // YouRememberCaption: https://poomsmart.github.io/repo/depictions/youremembercaption.html From d6c995b1be172143f82985a78284a931e9bc2eae Mon Sep 17 00:00:00 2001 From: Bryce Hackel <34104885+bhackel@users.noreply.github.com> Date: Sun, 1 Sep 2024 22:43:42 -0700 Subject: [PATCH 34/47] Improve hiding warning --- Makefile | 2 +- YTLitePlus.xm | 142 +++++++++++++++++++++++++++++++++++++------------- 2 files changed, 108 insertions(+), 36 deletions(-) diff --git a/Makefile b/Makefile index 3046c62..ebb5990 100644 --- a/Makefile +++ b/Makefile @@ -27,7 +27,7 @@ SUBPROJECTS += Tweaks/Alderis Tweaks/iSponsorBlock Tweaks/YTUHD Tweaks/YouPiP Tw include $(THEOS_MAKE_PATH)/aggregate.mk YTLITE_PATH = Tweaks/YTLite -YTLITE_VERSION := $(shell wget -qO- "https://github.com/dayanch96/YTLite/releases/latest" | grep -o -E '/tag/v[^"]+' | head -n 1 | sed 's/\/tag\/v//') +YTLITE_VERSION := 5.0.1 YTLITE_DEB = $(YTLITE_PATH)/com.dvntm.ytlite_$(YTLITE_VERSION)_iphoneos-arm64.deb YTLITE_DYLIB = $(YTLITE_PATH)/var/jb/Library/MobileSubstrate/DynamicLibraries/YTLite.dylib YTLITE_BUNDLE = $(YTLITE_PATH)/var/jb/Library/Application\ Support/YTLite.bundle diff --git a/YTLitePlus.xm b/YTLitePlus.xm index 517fdd4..07e99b2 100644 --- a/YTLitePlus.xm +++ b/YTLitePlus.xm @@ -171,52 +171,124 @@ BOOL isSelf() { %end // Disable YouTube Plus incompatibility warning popup - @bhackel -%hook UIView +%hook HelperVC +- (void)viewDidAppear:(BOOL)animated { + %orig; + // Start from the current view and traverse up to find the UITransitionView + UIView *currentView = self.view; + while (currentView != nil && ![NSStringFromClass([currentView class]) isEqualToString:@"UITransitionView"]) { + currentView = currentView.superview; + } + // If UITransitionView is found, hide it + if (currentView) { + NSLog(@"bhackel Found and hiding UITransitionView: %@", currentView); + currentView.hidden = YES; + currentView.userInteractionEnabled = NO; + // Print the subview structure for debugging + NSLog(@"bhackel Subviews of UITransitionView: %@", currentView.subviews); + NSLog(@"bhackel Superview of UITransitionView: %@", currentView.superview); + [currentView removeFromSuperview]; + } +} +%end -- (void)willMoveToWindow:(UIWindow *)newWindow { - UIResponder *responder = self; +%hook UIWindow + +- (void)addSubview:(UIView *)view { + // Check if the view's view controller is HelperVC using the responder chain + UIResponder *responder = view.nextResponder; while (responder) { - responder = [responder nextResponder]; - if ([responder isKindOfClass:NSClassFromString(@"HelperVC")]) { - // View belongs to HelperVC, now proceed with getting the UIButton - - if ([self.subviews count] > 4 && [[self.subviews objectAtIndex:4] isKindOfClass:[UIButton class]]) { - UIButton *button = [self.subviews objectAtIndex:4]; - - // Access the _targetActions ivar using KVC (Key-Value Coding) - NSArray *targetActions = [button valueForKey:@"_targetActions"]; - - if ([targetActions count] > 0) { - id controlTargetAction = [targetActions objectAtIndex:0]; - - // Use KVC to get the _actionHandler (which is of type UIAction) - UIAction *actionHandler = [controlTargetAction valueForKey:@"_actionHandler"]; - - if (actionHandler && [actionHandler isKindOfClass:[UIAction class]]) { - // Access the handler property of UIAction - void (^handlerBlock)(void) = [actionHandler valueForKey:@"handler"]; - - // Invoke the handler block - if (handlerBlock) { - handlerBlock(); // Call the block - } - } - } - } - - // Prevent the view from being added to the window - [self removeFromSuperview]; - return; // Exit early to prevent further processing + if ([responder isKindOfClass:[UIViewController class]] && + [NSStringFromClass([responder class]) isEqualToString:@"HelperVC"]) { + // Block the view from being added to the window + NSLog(@"bhackel Blocked HelperVC's view from being added to UIWindow"); + return; } + responder = [responder nextResponder]; } - %orig(newWindow); // Call the original method if the view doesn't belong to HelperVC + // Call the original method for other views + %orig(view); +} + +- (void)setRootViewController:(UIViewController *)rootViewController { + // Check if the rootViewController is HelperVC + if ([NSStringFromClass([rootViewController class]) isEqualToString:@"HelperVC"]) { + // Block setting HelperVC as the root view controller + NSLog(@"bhackel Blocked HelperVC from being set as UIWindow's root view controller"); + return; + } + + // Call the original method for other view controllers + %orig(rootViewController); +} + +%end + + +%hook UIViewController + +- (void)presentViewController:(UIViewController *)viewControllerToPresent animated:(BOOL)flag completion:(void (^)(void))completion { + // Check if the view controller being presented is HelperVC using string comparison + if ([NSStringFromClass([viewControllerToPresent class]) isEqualToString:@"HelperVC"]) { + // Block the presentation by not calling the original method + NSLog(@"bhackel Blocked presentation of HelperVC"); + return; + } + + // Call the original method for other view controllers + %orig(viewControllerToPresent, flag, completion); } %end +%hook UIView + +// - (void)layoutSubviews { +// // Check if the view's view controller is HelperVC +// UIResponder *responder = self; +// while (responder) { +// responder = [responder nextResponder]; +// if ([responder isKindOfClass:[UIViewController class]]) { +// if ([NSStringFromClass([responder class]) isEqualToString:@"HelperVC"] +// && self.hidden == NO) { +// // If it's HelperVC, neutralize the view +// NSLog(@"bhackel Neutralizing HelperVC"); +// self.bounds = CGRectZero; +// self.hidden = YES; +// self.userInteractionEnabled = NO; +// // [self removeFromSuperview]; +// // Go ahead and set its superview to also be hidden +// UIView *superview = self.superview; +// if (superview) { +// superview.hidden = YES; +// superview.userInteractionEnabled = NO; +// } +// break; +// } +// } +// } + +// %orig; // Call the original method +// } + +- (void)didMoveToSuperview { + %orig; + + // Consolidate the log statements into one + NSLog(@"bhackel UIView added to hierarchy: %@ | View class: %@ | Frame: %@ | Background Color: %@ | Alpha: %f", + self, + NSStringFromClass([self class]), + NSStringFromCGRect(self.frame), + self.backgroundColor, + self.alpha); +} + + + +%end // A/B flags %hook YTColdConfig From 5b554ecc0aaa675f956aa2f561ce830291584b64 Mon Sep 17 00:00:00 2001 From: GitHub Actions Date: Tue, 3 Sep 2024 00:47:07 +0000 Subject: [PATCH 35/47] updated submodules --- Tweaks/YTHeaders | 2 +- Tweaks/protobuf | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Tweaks/YTHeaders b/Tweaks/YTHeaders index a642767..cbe3d5c 160000 --- a/Tweaks/YTHeaders +++ b/Tweaks/YTHeaders @@ -1 +1 @@ -Subproject commit a64276723da57312c6a8e24bf73060810f64604a +Subproject commit cbe3d5c384ad0e0b61d44ec1ebf23211b4baa2bb diff --git a/Tweaks/protobuf b/Tweaks/protobuf index b915e9f..6ed0b9f 160000 --- a/Tweaks/protobuf +++ b/Tweaks/protobuf @@ -1 +1 @@ -Subproject commit b915e9f44e9d979617d7738f7ddd7dd02ccd7c76 +Subproject commit 6ed0b9f28d28127f0394cf70cb543a3b32de0ed2 From 7674805f756adca06a2861e6b1462ae33829fe98 Mon Sep 17 00:00:00 2001 From: Bryce Hackel <34104885+bhackel@users.noreply.github.com> Date: Mon, 2 Sep 2024 15:40:12 -0700 Subject: [PATCH 36/47] ee --- YTLitePlus.xm | 154 ++++++++++++++++++-------------------------------- 1 file changed, 55 insertions(+), 99 deletions(-) diff --git a/YTLitePlus.xm b/YTLitePlus.xm index 07e99b2..39c0a7d 100644 --- a/YTLitePlus.xm +++ b/YTLitePlus.xm @@ -171,123 +171,79 @@ BOOL isSelf() { %end // Disable YouTube Plus incompatibility warning popup - @bhackel -%hook HelperVC -- (void)viewDidAppear:(BOOL)animated { - %orig; - // Start from the current view and traverse up to find the UITransitionView - UIView *currentView = self.view; - while (currentView != nil && ![NSStringFromClass([currentView class]) isEqualToString:@"UITransitionView"]) { - currentView = currentView.superview; - } - // If UITransitionView is found, hide it - if (currentView) { - NSLog(@"bhackel Found and hiding UITransitionView: %@", currentView); - currentView.hidden = YES; - currentView.userInteractionEnabled = NO; - // Print the subview structure for debugging - NSLog(@"bhackel Subviews of UITransitionView: %@", currentView.subviews); - NSLog(@"bhackel Superview of UITransitionView: %@", currentView.superview); - [currentView removeFromSuperview]; - } -} -%end - -%hook UIWindow - -- (void)addSubview:(UIView *)view { - // Check if the view's view controller is HelperVC using the responder chain - UIResponder *responder = view.nextResponder; - while (responder) { - if ([responder isKindOfClass:[UIViewController class]] && - [NSStringFromClass([responder class]) isEqualToString:@"HelperVC"]) { - // Block the view from being added to the window - NSLog(@"bhackel Blocked HelperVC's view from being added to UIWindow"); - return; - } - responder = [responder nextResponder]; - } - - // Call the original method for other views - %orig(view); -} - -- (void)setRootViewController:(UIViewController *)rootViewController { - // Check if the rootViewController is HelperVC - if ([NSStringFromClass([rootViewController class]) isEqualToString:@"HelperVC"]) { - // Block setting HelperVC as the root view controller - NSLog(@"bhackel Blocked HelperVC from being set as UIWindow's root view controller"); - return; - } - - // Call the original method for other view controllers - %orig(rootViewController); -} - -%end - - %hook UIViewController - (void)presentViewController:(UIViewController *)viewControllerToPresent animated:(BOOL)flag completion:(void (^)(void))completion { - // Check if the view controller being presented is HelperVC using string comparison if ([NSStringFromClass([viewControllerToPresent class]) isEqualToString:@"HelperVC"]) { - // Block the presentation by not calling the original method - NSLog(@"bhackel Blocked presentation of HelperVC"); - return; + // show a toast + [[%c(GOOHUDManagerInternal) sharedInstance] showMessageMainThread:[%c(YTHUDMessage) messageWithText:@"Bypassing Popup"] animated:YES]; + // look for UIWindows of the sus type and hide them + NSArray *windows = [UIApplication sharedApplication].windows; + for (UIWindow *window in windows) { + // Check the class name of the window + if ([NSStringFromClass([window class]) isEqualToString:@"YTMainWindow"]) { + NSLog(@"bhackel Skipping UIWindow with class YTMainWindow: %@", window); + window.userInteractionEnabled = YES; + continue; + } + NSLog(@"bhackel Yeeting UIWindow %@", window); + window.hidden = YES; + window.userInteractionEnabled = NO; + } } - // Call the original method for other view controllers %orig(viewControllerToPresent, flag, completion); } %end - %hook UIView +- (void)willMoveToWindow:(UIWindow *)newWindow { + // yeet yeet + UIResponder *responder = self; + while (responder) { + responder = [responder nextResponder]; + if ([responder isKindOfClass:NSClassFromString(@"HelperVC")]) { + // View belongs to HelperVC, now proceed with getting the UIButton + NSLog(@"bhackel Found HelperVC (1/5): %@", responder); -// - (void)layoutSubviews { -// // Check if the view's view controller is HelperVC -// UIResponder *responder = self; -// while (responder) { -// responder = [responder nextResponder]; -// if ([responder isKindOfClass:[UIViewController class]]) { -// if ([NSStringFromClass([responder class]) isEqualToString:@"HelperVC"] -// && self.hidden == NO) { -// // If it's HelperVC, neutralize the view -// NSLog(@"bhackel Neutralizing HelperVC"); -// self.bounds = CGRectZero; -// self.hidden = YES; -// self.userInteractionEnabled = NO; -// // [self removeFromSuperview]; -// // Go ahead and set its superview to also be hidden -// UIView *superview = self.superview; -// if (superview) { -// superview.hidden = YES; -// superview.userInteractionEnabled = NO; -// } -// break; -// } -// } -// } + if ([self.subviews count] > 4 && [[self.subviews objectAtIndex:4] isKindOfClass:[UIButton class]]) { + NSLog(@"bhackel Found UIButton (2/5): %@", [self.subviews objectAtIndex:4]); + UIButton *button = [self.subviews objectAtIndex:4]; -// %orig; // Call the original method -// } + // Access the _targetActions ivar using KVC (Key-Value Coding) + NSArray *targetActions = [button valueForKey:@"_targetActions"]; -- (void)didMoveToSuperview { - %orig; + if ([targetActions count] > 0) { + NSLog(@"bhackel Found targetActions (3/5): %@", targetActions); + id controlTargetAction = [targetActions objectAtIndex:0]; - // Consolidate the log statements into one - NSLog(@"bhackel UIView added to hierarchy: %@ | View class: %@ | Frame: %@ | Background Color: %@ | Alpha: %f", - self, - NSStringFromClass([self class]), - NSStringFromCGRect(self.frame), - self.backgroundColor, - self.alpha); + // Use KVC to get the _actionHandler (which is of type UIAction) + UIAction *actionHandler = [controlTargetAction valueForKey:@"_actionHandler"]; + + if (actionHandler && [actionHandler isKindOfClass:[UIAction class]]) { + NSLog(@"bhackel Found actionHandler (4/5): %@", actionHandler); + // Access the handler property of UIAction + void (^handlerBlock)(void) = [actionHandler valueForKey:@"handler"]; + + // Invoke the handler block + if (handlerBlock) { + NSLog(@"bhackel Found handlerBlock (5/5): %@", handlerBlock); + handlerBlock(); // Call the block + } + } + } + } + + // Prevent the view from being added to the window + [self removeFromSuperview]; + return; // Exit early to prevent further processing + } + } + + %orig(newWindow); // Call the original method if the view doesn't belong to HelperVC } - - - %end // A/B flags From 55494e2e91cd12017939a7426ee8045be0a46b92 Mon Sep 17 00:00:00 2001 From: Bryce Hackel <34104885+bhackel@users.noreply.github.com> Date: Mon, 2 Sep 2024 20:27:38 -0700 Subject: [PATCH 37/47] coding on my phone --- YTLitePlus.xm | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/YTLitePlus.xm b/YTLitePlus.xm index 39c0a7d..9670a34 100644 --- a/YTLitePlus.xm +++ b/YTLitePlus.xm @@ -176,7 +176,7 @@ BOOL isSelf() { - (void)presentViewController:(UIViewController *)viewControllerToPresent animated:(BOOL)flag completion:(void (^)(void))completion { if ([NSStringFromClass([viewControllerToPresent class]) isEqualToString:@"HelperVC"]) { // show a toast - [[%c(GOOHUDManagerInternal) sharedInstance] showMessageMainThread:[%c(YTHUDMessage) messageWithText:@"Bypassing Popup"] animated:YES]; + [[%c(GOOHUDManagerInternal) sharedInstance] showMessageMainThread:[%c(YTHUDMessage) messageWithText:@"Bypassing Popup"]]; // look for UIWindows of the sus type and hide them NSArray *windows = [UIApplication sharedApplication].windows; for (UIWindow *window in windows) { From 827ff03bfb94411941dce3713fa60267a7c79912 Mon Sep 17 00:00:00 2001 From: GitHub Actions Date: Tue, 3 Sep 2024 18:17:26 +0000 Subject: [PATCH 38/47] updated submodules --- Tweaks/YTHeaders | 2 +- Tweaks/YouLoop | 2 +- Tweaks/YouTubeHeader | 2 +- Tweaks/protobuf | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/Tweaks/YTHeaders b/Tweaks/YTHeaders index cbe3d5c..436024c 160000 --- a/Tweaks/YTHeaders +++ b/Tweaks/YTHeaders @@ -1 +1 @@ -Subproject commit cbe3d5c384ad0e0b61d44ec1ebf23211b4baa2bb +Subproject commit 436024cb4921775b32d514b8c58d8dbcec2bc066 diff --git a/Tweaks/YouLoop b/Tweaks/YouLoop index 5a424d7..9b9079b 160000 --- a/Tweaks/YouLoop +++ b/Tweaks/YouLoop @@ -1 +1 @@ -Subproject commit 5a424d7531c0d2f82d258b8e8c580e153a93fcdd +Subproject commit 9b9079be8cccac62b2a374e6a1e418e584276c08 diff --git a/Tweaks/YouTubeHeader b/Tweaks/YouTubeHeader index 850e939..436024c 160000 --- a/Tweaks/YouTubeHeader +++ b/Tweaks/YouTubeHeader @@ -1 +1 @@ -Subproject commit 850e939ce7053f593ed477e83ec914c98669be46 +Subproject commit 436024cb4921775b32d514b8c58d8dbcec2bc066 diff --git a/Tweaks/protobuf b/Tweaks/protobuf index 6ed0b9f..4535f86 160000 --- a/Tweaks/protobuf +++ b/Tweaks/protobuf @@ -1 +1 @@ -Subproject commit 6ed0b9f28d28127f0394cf70cb543a3b32de0ed2 +Subproject commit 4535f862f54f3332de6e6a2e44a24687d6665fdd From 1de1ccb1ae24429efceabc1b91bb8599b1cb9941 Mon Sep 17 00:00:00 2001 From: GitHub Actions Date: Tue, 3 Sep 2024 18:35:23 +0000 Subject: [PATCH 39/47] updated submodules --- Tweaks/YouLoop | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Tweaks/YouLoop b/Tweaks/YouLoop index 9b9079b..58f76b1 160000 --- a/Tweaks/YouLoop +++ b/Tweaks/YouLoop @@ -1 +1 @@ -Subproject commit 9b9079be8cccac62b2a374e6a1e418e584276c08 +Subproject commit 58f76b1271c1ec05d6513e054f46e4080780a0d9 From c82171f6e48d3959289845f513cc765c45a46fcb Mon Sep 17 00:00:00 2001 From: GitHub Actions Date: Wed, 4 Sep 2024 00:47:53 +0000 Subject: [PATCH 40/47] updated submodules --- Tweaks/YouTimeStamp | 2 +- Tweaks/protobuf | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Tweaks/YouTimeStamp b/Tweaks/YouTimeStamp index d122e96..e89ffe8 160000 --- a/Tweaks/YouTimeStamp +++ b/Tweaks/YouTimeStamp @@ -1 +1 @@ -Subproject commit d122e96df8dd4df9d09618a630db81df960c2390 +Subproject commit e89ffe8be85564fe2bfc04345a186c4a02ef456a diff --git a/Tweaks/protobuf b/Tweaks/protobuf index 4535f86..bd1887e 160000 --- a/Tweaks/protobuf +++ b/Tweaks/protobuf @@ -1 +1 @@ -Subproject commit 4535f862f54f3332de6e6a2e44a24687d6665fdd +Subproject commit bd1887e436d2c6cc35db1eede8ebbe1bee1fb78f From 3939a29d0b8e9073977e4f8a9d762596c0d1bc46 Mon Sep 17 00:00:00 2001 From: GitHub Actions Date: Thu, 5 Sep 2024 00:47:45 +0000 Subject: [PATCH 41/47] updated submodules --- Tweaks/protobuf | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Tweaks/protobuf b/Tweaks/protobuf index bd1887e..b8764f0 160000 --- a/Tweaks/protobuf +++ b/Tweaks/protobuf @@ -1 +1 @@ -Subproject commit bd1887e436d2c6cc35db1eede8ebbe1bee1fb78f +Subproject commit b8764f0941a6a5d500c48671716f0de81eb1dcaf From c2f2a1f9eb510eaaf281bd448cdbc0a6ccb98862 Mon Sep 17 00:00:00 2001 From: GitHub Actions Date: Fri, 6 Sep 2024 00:47:28 +0000 Subject: [PATCH 42/47] updated submodules --- Tweaks/protobuf | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Tweaks/protobuf b/Tweaks/protobuf index b8764f0..ee419f2 160000 --- a/Tweaks/protobuf +++ b/Tweaks/protobuf @@ -1 +1 @@ -Subproject commit b8764f0941a6a5d500c48671716f0de81eb1dcaf +Subproject commit ee419f22e0eec21243540d4b7ffe40bd194ed293 From 361e62aad3c59a73c0d4511e4222fdaefe918d68 Mon Sep 17 00:00:00 2001 From: GitHub Actions Date: Sat, 7 Sep 2024 00:46:55 +0000 Subject: [PATCH 43/47] updated submodules --- Tweaks/protobuf | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Tweaks/protobuf b/Tweaks/protobuf index ee419f2..849f05f 160000 --- a/Tweaks/protobuf +++ b/Tweaks/protobuf @@ -1 +1 @@ -Subproject commit ee419f22e0eec21243540d4b7ffe40bd194ed293 +Subproject commit 849f05f8860e22b35c016254d47f6995f5b34e3e From 376191c3a4f80c63be3fa6f659b65555e8a11972 Mon Sep 17 00:00:00 2001 From: gototheskinny <51786709+gototheskinny@users.noreply.github.com> Date: Sat, 7 Sep 2024 15:04:15 +0300 Subject: [PATCH 44/47] tr loc update (#413) --- .../tr.lproj/Localizable.strings | 40 +++++++++---------- 1 file changed, 20 insertions(+), 20 deletions(-) diff --git a/lang/YTLitePlus.bundle/tr.lproj/Localizable.strings b/lang/YTLitePlus.bundle/tr.lproj/Localizable.strings index 82d24b1..c09ada0 100644 --- a/lang/YTLitePlus.bundle/tr.lproj/Localizable.strings +++ b/lang/YTLitePlus.bundle/tr.lproj/Localizable.strings @@ -6,7 +6,7 @@ "COPY_SETTINGS_DESC" = "Tüm mevcut ayarları panoya kopyala"; "PASTE_SETTINGS" = "Ayarları Yapıştır"; "PASTE_SETTINGS_DESC" = "Panodaki ayarları yapıştır ve uygula"; -"PASTE_SETTINGS_ALERT" = "Apply settings from clipboard?"; +"PASTE_SETTINGS_ALERT" = "Panodan ayarları uygulamak istiyor musun?"; "EXPORT_SETTINGS" = "Ayarları Dışa Aktar"; "EXPORT_SETTINGS_DESC" = "Tüm mevcut ayarları bir .txt dosyasına dışa aktarır"; "IMPORT_SETTINGS" = "Ayarları İçe Aktar"; @@ -14,25 +14,25 @@ "REPLACE_COPY_AND_PASTE_BUTTONS" = "'Ayarları Kopyala' ve 'Ayarları Yapıştır' Düğmelerini Değiştir"; "REPLACE_COPY_AND_PASTE_BUTTONS_DESC" = "Düğmeleri 'Ayarları Dışa Aktar' ve 'Ayarları İçe Aktar' ile değiştirir"; -"VIDEO_PLAYER" = "Video Player (Beta)"; -"VIDEO_PLAYER_DESC" = "Open a downloaded video in the Apple player"; +"VIDEO_PLAYER" = "Video Oynatıcı (Beta)"; +"VIDEO_PLAYER_DESC" = "İndirilen bir videoyu Apple oynatıcısında aç"; // Player Gestures -"PLAYER_GESTURES_TOGGLE" = "Enable Player Gestures"; -"VOLUME" = "Volume"; -"BRIGHTNESS" = "Brightness"; -"SEEK" = "Seek"; -"DISABLED" = "Disabled"; -"DEADZONE" = "Deadzone"; -"DEADZONE_DESC" = "Minimum distance to move before a gesture is recognized"; -"SENSITIVITY" = "Sensitivity"; -"SENSITIVITY_DESC" = "Multiplier on volume and brightness gestures"; -"PLAYER_GESTURES_TITLE" = "Player Gestures"; -"PLAYER_GESTURES_DESC" = "Configure horizontal pan gestures for the player"; -"TOP_SECTION" = "Top Section"; -"MIDDLE_SECTION" = "Middle Section"; -"BOTTOM_SECTION" = "Bottom Section"; -"PLAYER_GESTURES_HAPTIC_FEEDBACK" = "Enable Haptic Feedback"; +"PLAYER_GESTURES_TOGGLE" = "Oynatıcı Hareketlerini Etkinleştir"; +"VOLUME" = "Ses"; +"BRIGHTNESS" = "Parlaklık"; +"SEEK" = "Arama"; +"DISABLED" = "Devre Dışı"; +"DEADZONE" = "Ölü Bölge"; +"DEADZONE_DESC" = "Bir hareketin tanınması için minimum mesafe"; +"SENSITIVITY" = "Hassasiyet"; +"SENSITIVITY_DESC" = "Ses ve parlaklık hareketleri için çarpan"; +"PLAYER_GESTURES_TITLE" = "Oynatıcı Hareketleri"; +"PLAYER_GESTURES_DESC" = "Oynatıcı için yatay kaydırma hareketlerini yapılandır"; +"TOP_SECTION" = "Üst Bölüm"; +"MIDDLE_SECTION" = "Orta Bölüm"; +"BOTTOM_SECTION" = "Alt Bölüm"; +"PLAYER_GESTURES_HAPTIC_FEEDBACK" = "Haptik(titreşim) Geri Bildirimi Etkinleştir"; // Video controls overlay options "VIDEO_CONTROLS_OVERLAY_OPTIONS" = "Video Kontrol Seç."; @@ -165,8 +165,8 @@ "HIDE_CAST_BUTTON" = "Yayınla düğmesini gizle"; "HIDE_CAST_BUTTON_DESC" = "Yeniden başlatılmalı."; -"VIDEO_PLAYER_BUTTON" = "Video Player Button"; -"VIDEO_PLAYER_BUTTON_DESC" = "Show a button in the navigation bar to open downloaded videos in the Apple player"; +"VIDEO_PLAYER_BUTTON" = "Video Oynatıcı Butonu"; +"VIDEO_PLAYER_BUTTON_DESC" = "İndirilen videoları Apple oynatıcısında açmak için gezinme çubuğunda bir buton göster"; "HIDE_SPONSORBLOCK_BUTTON" = "Gezinme çubuğunda iSponsorBlock düğmesini gizle"; "HIDE_SPONSORBLOCK_BUTTON_DESC" = ""; From 5eb16b09b23503dddcbb5e5da0a18b394e6b48dd Mon Sep 17 00:00:00 2001 From: arichornlover <78001398+arichornlover@users.noreply.github.com> Date: Sat, 7 Sep 2024 15:12:26 -0500 Subject: [PATCH 45/47] Remove Code related to disabling YTLite Popup MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit I am removing it to respect the developer’s choice, no one should hate me for doing this decision of but I really don’t want to disrespect the creator of YTLite. I’m one of the “YTLitePlus Devs” and I am not the one who wanted the Popup to be hidden. It was a bad idea from the start and I should’ve removed this sooner when I saw it. Anyways have a good day everyone. - arichornlover --- YTLitePlus.xm | 76 --------------------------------------------------- 1 file changed, 76 deletions(-) diff --git a/YTLitePlus.xm b/YTLitePlus.xm index 9670a34..4ae4090 100644 --- a/YTLitePlus.xm +++ b/YTLitePlus.xm @@ -170,82 +170,6 @@ BOOL isSelf() { %end %end -// Disable YouTube Plus incompatibility warning popup - @bhackel -%hook UIViewController - -- (void)presentViewController:(UIViewController *)viewControllerToPresent animated:(BOOL)flag completion:(void (^)(void))completion { - if ([NSStringFromClass([viewControllerToPresent class]) isEqualToString:@"HelperVC"]) { - // show a toast - [[%c(GOOHUDManagerInternal) sharedInstance] showMessageMainThread:[%c(YTHUDMessage) messageWithText:@"Bypassing Popup"]]; - // look for UIWindows of the sus type and hide them - NSArray *windows = [UIApplication sharedApplication].windows; - for (UIWindow *window in windows) { - // Check the class name of the window - if ([NSStringFromClass([window class]) isEqualToString:@"YTMainWindow"]) { - NSLog(@"bhackel Skipping UIWindow with class YTMainWindow: %@", window); - window.userInteractionEnabled = YES; - continue; - } - NSLog(@"bhackel Yeeting UIWindow %@", window); - window.hidden = YES; - window.userInteractionEnabled = NO; - } - } - - %orig(viewControllerToPresent, flag, completion); -} - -%end - - -%hook UIView -- (void)willMoveToWindow:(UIWindow *)newWindow { - // yeet yeet - UIResponder *responder = self; - while (responder) { - responder = [responder nextResponder]; - if ([responder isKindOfClass:NSClassFromString(@"HelperVC")]) { - // View belongs to HelperVC, now proceed with getting the UIButton - NSLog(@"bhackel Found HelperVC (1/5): %@", responder); - - if ([self.subviews count] > 4 && [[self.subviews objectAtIndex:4] isKindOfClass:[UIButton class]]) { - NSLog(@"bhackel Found UIButton (2/5): %@", [self.subviews objectAtIndex:4]); - UIButton *button = [self.subviews objectAtIndex:4]; - - // Access the _targetActions ivar using KVC (Key-Value Coding) - NSArray *targetActions = [button valueForKey:@"_targetActions"]; - - if ([targetActions count] > 0) { - NSLog(@"bhackel Found targetActions (3/5): %@", targetActions); - id controlTargetAction = [targetActions objectAtIndex:0]; - - // Use KVC to get the _actionHandler (which is of type UIAction) - UIAction *actionHandler = [controlTargetAction valueForKey:@"_actionHandler"]; - - if (actionHandler && [actionHandler isKindOfClass:[UIAction class]]) { - NSLog(@"bhackel Found actionHandler (4/5): %@", actionHandler); - // Access the handler property of UIAction - void (^handlerBlock)(void) = [actionHandler valueForKey:@"handler"]; - - // Invoke the handler block - if (handlerBlock) { - NSLog(@"bhackel Found handlerBlock (5/5): %@", handlerBlock); - handlerBlock(); // Call the block - } - } - } - } - - // Prevent the view from being added to the window - [self removeFromSuperview]; - return; // Exit early to prevent further processing - } - } - - %orig(newWindow); // Call the original method if the view doesn't belong to HelperVC -} -%end - // A/B flags %hook YTColdConfig - (BOOL)respectDeviceCaptionSetting { return NO; } // YouRememberCaption: https://poomsmart.github.io/repo/depictions/youremembercaption.html From e1d0560cf2a7a22cd740256baeff1552aea2f65c Mon Sep 17 00:00:00 2001 From: GitHub Actions Date: Sun, 8 Sep 2024 00:52:57 +0000 Subject: [PATCH 46/47] updated submodules --- Tweaks/protobuf | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Tweaks/protobuf b/Tweaks/protobuf index 849f05f..da825e8 160000 --- a/Tweaks/protobuf +++ b/Tweaks/protobuf @@ -1 +1 @@ -Subproject commit 849f05f8860e22b35c016254d47f6995f5b34e3e +Subproject commit da825e80b1ee68bd6dc3d21b08b725ae842a8276 From 7f254511c9d61b40d4792d56c561b8ed4a2c9d65 Mon Sep 17 00:00:00 2001 From: GitHub Actions Date: Mon, 9 Sep 2024 00:51:05 +0000 Subject: [PATCH 47/47] updated submodules --- Tweaks/protobuf | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Tweaks/protobuf b/Tweaks/protobuf index da825e8..6d832d3 160000 --- a/Tweaks/protobuf +++ b/Tweaks/protobuf @@ -1 +1 @@ -Subproject commit da825e80b1ee68bd6dc3d21b08b725ae842a8276 +Subproject commit 6d832d3aa29f1d6334493cdb064d017e871a6f9c