From 769a2006ce8dc934dd5de0acc59ed6aa6985da4f Mon Sep 17 00:00:00 2001 From: LisoUseInAIKyrios <118716522+LisoUseInAIKyrios@users.noreply.github.com> Date: Mon, 7 Aug 2023 11:50:32 +0400 Subject: [PATCH 01/25] feat(YouTube - Debug): Logging of layout proto buffer strings (#456) --- .../integrations/patches/components/LithoFilterPatch.java | 7 ++++--- .../app/revanced/integrations/settings/SettingsEnum.java | 1 + 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/app/src/main/java/app/revanced/integrations/patches/components/LithoFilterPatch.java b/app/src/main/java/app/revanced/integrations/patches/components/LithoFilterPatch.java index b61c9df6..6b48a21b 100644 --- a/app/src/main/java/app/revanced/integrations/patches/components/LithoFilterPatch.java +++ b/app/src/main/java/app/revanced/integrations/patches/components/LithoFilterPatch.java @@ -342,9 +342,10 @@ public final class LithoFilterPatch { builder.append(identifier); builder.append(" Path: "); builder.append(path); - // TODO: allow turning on/off buffer logging with a debug setting? - builder.append(" BufferStrings: "); - findAsciiStrings(builder, protoBuffer); + if (SettingsEnum.DEBUG_PROTOBUFFER.getBoolean()) { + builder.append(" BufferStrings: "); + findAsciiStrings(builder, protoBuffer); + } return builder.toString(); } diff --git a/app/src/main/java/app/revanced/integrations/settings/SettingsEnum.java b/app/src/main/java/app/revanced/integrations/settings/SettingsEnum.java index ef07eff4..714ae162 100644 --- a/app/src/main/java/app/revanced/integrations/settings/SettingsEnum.java +++ b/app/src/main/java/app/revanced/integrations/settings/SettingsEnum.java @@ -191,6 +191,7 @@ public enum SettingsEnum { // Debugging DEBUG("revanced_debug", BOOLEAN, FALSE), DEBUG_STACKTRACE("revanced_debug_stacktrace", BOOLEAN, FALSE, parents(DEBUG)), + DEBUG_PROTOBUFFER("revanced_debug_protobuffer", BOOLEAN, FALSE, parents(DEBUG)), DEBUG_TOAST_ON_ERROR("revanced_debug_toast_on_error", BOOLEAN, TRUE, "revanced_debug_toast_on_error_user_dialog_message"), // ReturnYoutubeDislike From c1af6e699dbf7cfb5d6488476760e1ea24053f8f Mon Sep 17 00:00:00 2001 From: semantic-release-bot Date: Mon, 7 Aug 2023 07:54:50 +0000 Subject: [PATCH 02/25] chore(release): 0.116.0-dev.1 [skip ci] # [0.116.0-dev.1](https://github.com/ReVanced/revanced-integrations/compare/v0.115.1...v0.116.0-dev.1) (2023-08-07) ### Features * **YouTube - Debug:** Logging of layout proto buffer strings ([#456](https://github.com/ReVanced/revanced-integrations/issues/456)) ([769a200](https://github.com/ReVanced/revanced-integrations/commit/769a2006ce8dc934dd5de0acc59ed6aa6985da4f)) --- CHANGELOG.md | 7 +++++++ gradle.properties | 2 +- 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 4565de42..b8096002 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,10 @@ +# [0.116.0-dev.1](https://github.com/ReVanced/revanced-integrations/compare/v0.115.1...v0.116.0-dev.1) (2023-08-07) + + +### Features + +* **YouTube - Debug:** Logging of layout proto buffer strings ([#456](https://github.com/ReVanced/revanced-integrations/issues/456)) ([769a200](https://github.com/ReVanced/revanced-integrations/commit/769a2006ce8dc934dd5de0acc59ed6aa6985da4f)) + ## [0.115.1](https://github.com/ReVanced/revanced-integrations/compare/v0.115.0...v0.115.1) (2023-08-05) diff --git a/gradle.properties b/gradle.properties index 64a6cc5a..7e14a8d6 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1,4 +1,4 @@ org.gradle.parallel = true org.gradle.caching = true android.useAndroidX = true -version = 0.115.1 +version = 0.116.0-dev.1 From 8e5ca65286b8b62eaeff3bce5fa1d2fb5a198703 Mon Sep 17 00:00:00 2001 From: LisoUseInAIKyrios <118716522+LisoUseInAIKyrios@users.noreply.github.com> Date: Mon, 7 Aug 2023 18:34:42 +0400 Subject: [PATCH 03/25] feat(YouTube - Hide video action buttons): Hide individual action buttons (#451) --- .../patches/components/AdsFilter.java | 4 +- .../patches/components/ButtonsFilter.java | 82 ++++++++++++++---- .../components/LayoutComponentsFilter.java | 4 +- .../patches/components/LithoFilterPatch.java | 84 +++++++++++-------- .../PlaybackSpeedMenuFilterPatch.java | 2 +- .../PlayerFlyoutMenuItemsFilter.java | 4 +- .../patches/components/ShortsFilter.java | 2 +- .../VideoQualityMenuFilterPatch.java | 2 +- .../integrations/settings/SettingsEnum.java | 9 +- 9 files changed, 133 insertions(+), 60 deletions(-) diff --git a/app/src/main/java/app/revanced/integrations/patches/components/AdsFilter.java b/app/src/main/java/app/revanced/integrations/patches/components/AdsFilter.java index 3797f53a..9ccef8a5 100644 --- a/app/src/main/java/app/revanced/integrations/patches/components/AdsFilter.java +++ b/app/src/main/java/app/revanced/integrations/patches/components/AdsFilter.java @@ -99,12 +99,12 @@ public final class AdsFilter extends Filter { } @Override - public boolean isFiltered(String path, @Nullable String identifier, byte[] protobufBufferArray, + public boolean isFiltered(@Nullable String identifier, String path, byte[] protobufBufferArray, FilterGroupList matchedList, FilterGroup matchedGroup, int matchedIndex) { if (exceptions.matches(path)) return false; - return super.isFiltered(path, identifier, protobufBufferArray, matchedList, matchedGroup, matchedIndex); + return super.isFiltered(identifier, path, protobufBufferArray, matchedList, matchedGroup, matchedIndex); } /** diff --git a/app/src/main/java/app/revanced/integrations/patches/components/ButtonsFilter.java b/app/src/main/java/app/revanced/integrations/patches/components/ButtonsFilter.java index 5722e640..8c8e4546 100644 --- a/app/src/main/java/app/revanced/integrations/patches/components/ButtonsFilter.java +++ b/app/src/main/java/app/revanced/integrations/patches/components/ButtonsFilter.java @@ -1,59 +1,113 @@ package app.revanced.integrations.patches.components; +import android.os.Build; + import androidx.annotation.Nullable; +import androidx.annotation.RequiresApi; import app.revanced.integrations.settings.SettingsEnum; +@RequiresApi(api = Build.VERSION_CODES.N) final class ButtonsFilter extends Filter { + + private static final String VIDEO_ACTION_BAR_PATH = "video_action_bar.eml"; + private final StringFilterGroup actionBarRule; + private final StringFilterGroup bufferFilterPathRule; + private final ByteArrayFilterGroupList bufferButtonsGroupList = new ByteArrayFilterGroupList(); public ButtonsFilter() { actionBarRule = new StringFilterGroup( null, - "video_action_bar" + VIDEO_ACTION_BAR_PATH ); + identifierFilterGroups.addAll(actionBarRule); + + bufferFilterPathRule = new StringFilterGroup( + null, + "|CellType|CollectionType|CellType|ContainerType|button.eml|" + ); pathFilterGroups.addAll( new StringFilterGroup( SettingsEnum.HIDE_LIKE_DISLIKE_BUTTON, - "|like_button", - "dislike_button" + "|segmented_like_dislike_button" ), new StringFilterGroup( SettingsEnum.HIDE_DOWNLOAD_BUTTON, - "download_button" + "|download_button.eml|" ), new StringFilterGroup( SettingsEnum.HIDE_PLAYLIST_BUTTON, - "save_to_playlist_button" + "|save_to_playlist_button" ), new StringFilterGroup( SettingsEnum.HIDE_CLIP_BUTTON, "|clip_button.eml|" ), - new StringFilterGroup( - SettingsEnum.HIDE_ACTION_BUTTONS, - "ContainerType|video_action_button", - "|CellType|CollectionType|CellType|ContainerType|button.eml|" + bufferFilterPathRule + ); + + bufferButtonsGroupList.addAll( + new ByteArrayAsStringFilterGroup( + SettingsEnum.HIDE_LIVE_CHAT_BUTTON, + "yt_outline_message_bubble_overlap" ), - actionBarRule + new ByteArrayAsStringFilterGroup( + SettingsEnum.HIDE_REPORT_BUTTON, + "yt_outline_flag" + ), + new ByteArrayAsStringFilterGroup( + SettingsEnum.HIDE_SHARE_BUTTON, + "yt_outline_share" + ), + new ByteArrayAsStringFilterGroup( + SettingsEnum.HIDE_REMIX_BUTTON, + "yt_outline_youtube_shorts_plus" + ), + // Check for clip button both here and using a path filter, + // as there's a chance the path is a generic action button and won't contain 'clip_button' + new ByteArrayAsStringFilterGroup( + SettingsEnum.HIDE_CLIP_BUTTON, + "yt_outline_scissors" + ), + new ByteArrayAsStringFilterGroup( + SettingsEnum.HIDE_SHOP_BUTTON, + "yt_outline_bag" + ), + new ByteArrayAsStringFilterGroup( + SettingsEnum.HIDE_THANKS_BUTTON, + "yt_outline_dollar_sign_heart" + ) ); } private boolean isEveryFilterGroupEnabled() { - for (StringFilterGroup rule : pathFilterGroups) + for (FilterGroup rule : pathFilterGroups) + if (!rule.isEnabled()) return false; + + for (FilterGroup rule : bufferButtonsGroupList) if (!rule.isEnabled()) return false; return true; } @Override - public boolean isFiltered(String path, @Nullable String identifier, byte[] protobufBufferArray, + public boolean isFiltered(@Nullable String identifier, String path, byte[] protobufBufferArray, FilterGroupList matchedList, FilterGroup matchedGroup, int matchedIndex) { if (matchedGroup == actionBarRule) { - return isEveryFilterGroupEnabled(); + if (!isEveryFilterGroupEnabled()) { + return false; + } + } else if (matchedGroup == bufferFilterPathRule) { + if (!path.startsWith(VIDEO_ACTION_BAR_PATH)) { + return false; // Some other unknown button and not part of the player action buttons. + } + if (!bufferButtonsGroupList.check(protobufBufferArray).isFiltered()) { + return false; // Action button is not set to hide. + } } - return super.isFiltered(path, identifier, protobufBufferArray, matchedList, matchedGroup, matchedIndex); + return super.isFiltered(identifier, path, protobufBufferArray, matchedList, matchedGroup, matchedIndex); } } diff --git a/app/src/main/java/app/revanced/integrations/patches/components/LayoutComponentsFilter.java b/app/src/main/java/app/revanced/integrations/patches/components/LayoutComponentsFilter.java index 53860b9e..534c7a35 100644 --- a/app/src/main/java/app/revanced/integrations/patches/components/LayoutComponentsFilter.java +++ b/app/src/main/java/app/revanced/integrations/patches/components/LayoutComponentsFilter.java @@ -172,12 +172,12 @@ public final class LayoutComponentsFilter extends Filter { } @Override - public boolean isFiltered(String path, @Nullable String identifier, byte[] protobufBufferArray, + public boolean isFiltered(@Nullable String identifier, String path, byte[] protobufBufferArray, FilterGroupList matchedList, FilterGroup matchedGroup, int matchedIndex) { if (matchedGroup != custom && exceptions.matches(path)) return false; // Exceptions are not filtered. - return super.isFiltered(path, identifier, protobufBufferArray, matchedList, matchedGroup, matchedIndex); + return super.isFiltered(identifier, path, protobufBufferArray, matchedList, matchedGroup, matchedIndex); } diff --git a/app/src/main/java/app/revanced/integrations/patches/components/LithoFilterPatch.java b/app/src/main/java/app/revanced/integrations/patches/components/LithoFilterPatch.java index 6b48a21b..2429fca4 100644 --- a/app/src/main/java/app/revanced/integrations/patches/components/LithoFilterPatch.java +++ b/app/src/main/java/app/revanced/integrations/patches/components/LithoFilterPatch.java @@ -1,16 +1,26 @@ package app.revanced.integrations.patches.components; import android.os.Build; + import androidx.annotation.NonNull; import androidx.annotation.Nullable; import androidx.annotation.RequiresApi; -import app.revanced.integrations.settings.SettingsEnum; -import app.revanced.integrations.utils.*; import java.nio.ByteBuffer; -import java.util.*; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Iterator; +import java.util.List; +import java.util.Spliterator; import java.util.function.Consumer; +import app.revanced.integrations.settings.SettingsEnum; +import app.revanced.integrations.utils.ByteTrieSearch; +import app.revanced.integrations.utils.LogHelper; +import app.revanced.integrations.utils.ReVancedUtils; +import app.revanced.integrations.utils.StringTrieSearch; +import app.revanced.integrations.utils.TrieSearch; + abstract class FilterGroup { final static class FilterGroupResult { SettingsEnum setting; @@ -81,8 +91,7 @@ class StringFilterGroup extends FilterGroup { @Override public FilterGroupResult check(final String string) { - return new FilterGroupResult(setting, - (setting == null || setting.getBoolean()) && ReVancedUtils.containsAny(string, filters)); + return new FilterGroupResult(setting, isEnabled() && ReVancedUtils.containsAny(string, filters)); } } @@ -276,23 +285,12 @@ abstract class Filter { protected final StringFilterGroupList pathFilterGroups = new StringFilterGroupList(); protected final StringFilterGroupList identifierFilterGroups = new StringFilterGroupList(); - /** - * A collection of {@link ByteArrayFilterGroup} that are always searched for (no matter what). - * - * If possible, avoid adding values to this list and instead use a path or identifier filter - * for the item you are looking for. Then inside - * {@link #isFiltered(String, String, byte[], FilterGroupList, FilterGroup, int)}, - * the buffer can then be searched using using a different - * {@link ByteArrayFilterGroupList} or a {@link ByteArrayFilterGroup}. - * This way, the expensive buffer searching only occurs if the cheap and fast path/identifier is already found. - */ - protected final ByteArrayFilterGroupList protobufBufferFilterGroups = new ByteArrayFilterGroupList(); /** * Called after an enabled filter has been matched. * Default implementation is to always filter the matched item. * Subclasses can perform additional or different checks if needed. - * + *

* Method is called off the main thread. * * @param matchedList The list the group filter belongs to. @@ -301,15 +299,13 @@ abstract class Filter { * @return True if the litho item should be filtered out. */ @SuppressWarnings("rawtypes") - boolean isFiltered(String path, @Nullable String identifier, byte[] protobufBufferArray, + boolean isFiltered(@Nullable String identifier, String path, byte[] protobufBufferArray, FilterGroupList matchedList, FilterGroup matchedGroup, int matchedIndex) { if (SettingsEnum.DEBUG.getBoolean()) { if (pathFilterGroups == matchedList) { LogHelper.printDebug(() -> getClass().getSimpleName() + " Filtered path: " + path); } else if (identifierFilterGroups == matchedList) { LogHelper.printDebug(() -> getClass().getSimpleName() + " Filtered identifier: " + identifier); - } else if (protobufBufferFilterGroups == matchedList) { - LogHelper.printDebug(() -> getClass().getSimpleName() + " Filtered from protobuf-buffer"); } } return true; @@ -323,13 +319,14 @@ public final class LithoFilterPatch { * Simple wrapper to pass the litho parameters through the prefix search. */ private static final class LithoFilterParameters { - final String path; + @Nullable final String identifier; + final String path; final byte[] protoBuffer; - LithoFilterParameters(StringBuilder lithoPath, String lithoIdentifier, ByteBuffer protoBuffer) { - this.path = lithoPath.toString(); + LithoFilterParameters(@Nullable String lithoIdentifier, StringBuilder lithoPath, ByteBuffer protoBuffer) { this.identifier = lithoIdentifier; + this.path = lithoPath.toString(); this.protoBuffer = protoBuffer.array(); } @@ -367,7 +364,9 @@ public final class LithoFilterPatch { int value = buffer[end]; if (value < minimumAscii || value > maximumAscii || end == length - 1) { if (end - start >= minimumAsciiStringLength) { - builder.append(new String(buffer, start, end - start)); + for (int i = start; i < end; i++) { + builder.append((char) buffer[i]); + } builder.append(delimitingCharacter); } start = end + 1; @@ -383,22 +382,24 @@ public final class LithoFilterPatch { private static final StringTrieSearch pathSearchTree = new StringTrieSearch(); private static final StringTrieSearch identifierSearchTree = new StringTrieSearch(); - private static final ByteTrieSearch protoSearchTree = new ByteTrieSearch(); + + /** + * Because litho filtering is multi-threaded and the buffer is passed in from a different injection point, + * the buffer is saved to a ThreadLocal so each calling thread does not interfere with other threads. + */ + private static final ThreadLocal bufferThreadLocal = new ThreadLocal<>(); static { for (Filter filter : filters) { filterGroupLists(pathSearchTree, filter, filter.pathFilterGroups); filterGroupLists(identifierSearchTree, filter, filter.identifierFilterGroups); - filterGroupLists(protoSearchTree, filter, filter.protobufBufferFilterGroups); } LogHelper.printDebug(() -> "Using: " + pathSearchTree.numberOfPatterns() + " path filters" + " (" + pathSearchTree.getEstimatedMemorySize() + " KB), " + identifierSearchTree.numberOfPatterns() + " identifier filters" - + " (" + identifierSearchTree.getEstimatedMemorySize() + " KB), " - + protoSearchTree.numberOfPatterns() + " buffer filters" - + " (" + protoSearchTree.getEstimatedMemorySize() + " KB)"); + + " (" + identifierSearchTree.getEstimatedMemorySize() + " KB)"); } private static void filterGroupLists(TrieSearch pathSearchTree, @@ -411,7 +412,7 @@ public final class LithoFilterPatch { pathSearchTree.addPattern(pattern, (textSearched, matchedStartIndex, callbackParameter) -> { if (!group.isEnabled()) return false; LithoFilterParameters parameters = (LithoFilterParameters) callbackParameter; - return filter.isFiltered(parameters.path, parameters.identifier, parameters.protoBuffer, + return filter.isFiltered(parameters.identifier, parameters.path, parameters.protoBuffer, list, group, matchedStartIndex); } ); @@ -423,23 +424,36 @@ public final class LithoFilterPatch { * Injection point. Called off the main thread. */ @SuppressWarnings("unused") - public static boolean filter(@NonNull StringBuilder pathBuilder, @Nullable String lithoIdentifier, - @NonNull ByteBuffer protobufBuffer) { + public static void setProtoBuffer(@NonNull ByteBuffer protobufBuffer) { + bufferThreadLocal.set(protobufBuffer); + } + + /** + * Injection point. Called off the main thread, and commonly called by multiple threads at the same time. + */ + @SuppressWarnings("unused") + public static boolean filter(@Nullable String lithoIdentifier, @NonNull StringBuilder pathBuilder) { try { // It is assumed that protobufBuffer is empty as well in this case. if (pathBuilder.length() == 0) return false; - LithoFilterParameters parameter = new LithoFilterParameters(pathBuilder, lithoIdentifier, protobufBuffer); + ByteBuffer protobufBuffer = bufferThreadLocal.get(); + if (protobufBuffer == null) { + LogHelper.printException(() -> "Proto buffer is null"); // Should never happen + return false; + } + LithoFilterParameters parameter = new LithoFilterParameters(lithoIdentifier, pathBuilder, protobufBuffer); LogHelper.printDebug(() -> "Searching " + parameter); - if (pathSearchTree.matches(parameter.path, parameter)) return true; if (parameter.identifier != null) { if (identifierSearchTree.matches(parameter.identifier, parameter)) return true; } - if (protoSearchTree.matches(parameter.protoBuffer, parameter)) return true; + if (pathSearchTree.matches(parameter.path, parameter)) return true; } catch (Exception ex) { LogHelper.printException(() -> "Litho filter failure", ex); + } finally { + bufferThreadLocal.remove(); // Cleanup and remove the buffer. } return false; diff --git a/app/src/main/java/app/revanced/integrations/patches/components/PlaybackSpeedMenuFilterPatch.java b/app/src/main/java/app/revanced/integrations/patches/components/PlaybackSpeedMenuFilterPatch.java index ab2e63b4..2e93574f 100644 --- a/app/src/main/java/app/revanced/integrations/patches/components/PlaybackSpeedMenuFilterPatch.java +++ b/app/src/main/java/app/revanced/integrations/patches/components/PlaybackSpeedMenuFilterPatch.java @@ -15,7 +15,7 @@ public final class PlaybackSpeedMenuFilterPatch extends Filter { } @Override - boolean isFiltered(String path, @Nullable String identifier, byte[] protobufBufferArray, + boolean isFiltered(@Nullable String identifier, String path, byte[] protobufBufferArray, FilterGroupList matchedList, FilterGroup matchedGroup, int matchedIndex) { isPlaybackSpeedMenuVisible = true; diff --git a/app/src/main/java/app/revanced/integrations/patches/components/PlayerFlyoutMenuItemsFilter.java b/app/src/main/java/app/revanced/integrations/patches/components/PlayerFlyoutMenuItemsFilter.java index 3f435e08..9b627f97 100644 --- a/app/src/main/java/app/revanced/integrations/patches/components/PlayerFlyoutMenuItemsFilter.java +++ b/app/src/main/java/app/revanced/integrations/patches/components/PlayerFlyoutMenuItemsFilter.java @@ -62,12 +62,12 @@ public class PlayerFlyoutMenuItemsFilter extends Filter { } @Override - boolean isFiltered(String path, @Nullable String identifier, byte[] protobufBufferArray, + boolean isFiltered(@Nullable String identifier, String path, byte[] protobufBufferArray, FilterGroupList matchedList, FilterGroup matchedGroup, int matchedIndex) { // Only 1 group is added to the parent class, so the matched group must be the overflow menu. if (matchedIndex == 0 && flyoutFilterGroupList.check(protobufBufferArray).isFiltered()) { // Super class handles logging. - return super.isFiltered(path, identifier, protobufBufferArray, matchedList, matchedGroup, matchedIndex); + return super.isFiltered(identifier, path, protobufBufferArray, matchedList, matchedGroup, matchedIndex); } return false; } diff --git a/app/src/main/java/app/revanced/integrations/patches/components/ShortsFilter.java b/app/src/main/java/app/revanced/integrations/patches/components/ShortsFilter.java index 48542a06..fa97f2dd 100644 --- a/app/src/main/java/app/revanced/integrations/patches/components/ShortsFilter.java +++ b/app/src/main/java/app/revanced/integrations/patches/components/ShortsFilter.java @@ -64,7 +64,7 @@ public final class ShortsFilter extends Filter { } @Override - boolean isFiltered(String path, @Nullable String identifier, byte[] protobufBufferArray, + boolean isFiltered(@Nullable String identifier, String path, byte[] protobufBufferArray, FilterGroupList matchedList, FilterGroup matchedGroup, int matchedIndex) { if (matchedGroup == soundButton || matchedGroup == infoPanel || matchedGroup == channelBar) return true; diff --git a/app/src/main/java/app/revanced/integrations/patches/components/VideoQualityMenuFilterPatch.java b/app/src/main/java/app/revanced/integrations/patches/components/VideoQualityMenuFilterPatch.java index fb94de05..6199effa 100644 --- a/app/src/main/java/app/revanced/integrations/patches/components/VideoQualityMenuFilterPatch.java +++ b/app/src/main/java/app/revanced/integrations/patches/components/VideoQualityMenuFilterPatch.java @@ -17,7 +17,7 @@ public final class VideoQualityMenuFilterPatch extends Filter { } @Override - boolean isFiltered(String path, @Nullable String identifier, byte[] protobufBufferArray, + boolean isFiltered(@Nullable String identifier, String path, byte[] protobufBufferArray, FilterGroupList matchedList, FilterGroup matchedGroup, int matchedIndex) { isVideoQualityMenuVisible = true; diff --git a/app/src/main/java/app/revanced/integrations/settings/SettingsEnum.java b/app/src/main/java/app/revanced/integrations/settings/SettingsEnum.java index 714ae162..9c1a5958 100644 --- a/app/src/main/java/app/revanced/integrations/settings/SettingsEnum.java +++ b/app/src/main/java/app/revanced/integrations/settings/SettingsEnum.java @@ -89,10 +89,15 @@ public enum SettingsEnum { // Action buttons HIDE_LIKE_DISLIKE_BUTTON("revanced_hide_like_dislike_button", BOOLEAN, FALSE), + HIDE_LIVE_CHAT_BUTTON("revanced_hide_live_chat_button", BOOLEAN, FALSE), + HIDE_SHARE_BUTTON("revanced_hide_share_button", BOOLEAN, FALSE), + HIDE_REPORT_BUTTON("revanced_hide_report_button", BOOLEAN, FALSE), + HIDE_REMIX_BUTTON("revanced_hide_remix_button", BOOLEAN, TRUE), HIDE_DOWNLOAD_BUTTON("revanced_hide_download_button", BOOLEAN, FALSE), + HIDE_THANKS_BUTTON("revanced_hide_thanks_button", BOOLEAN, TRUE), + HIDE_CLIP_BUTTON("revanced_hide_clip_button", BOOLEAN, TRUE), HIDE_PLAYLIST_BUTTON("revanced_hide_playlist_button", BOOLEAN, FALSE), - HIDE_CLIP_BUTTON("revanced_hide_clip_button", BOOLEAN, FALSE, "revanced_hide_clip_button_user_dialog_message"), - HIDE_ACTION_BUTTONS("revanced_hide_action_buttons", BOOLEAN, FALSE), + HIDE_SHOP_BUTTON("revanced_hide_shop_button", BOOLEAN, TRUE), // Layout DISABLE_RESUMING_SHORTS_PLAYER("revanced_disable_resuming_shorts_player", BOOLEAN, FALSE), From 7ca6a080a3d99aa0146c4741f24efeb716267800 Mon Sep 17 00:00:00 2001 From: semantic-release-bot Date: Mon, 7 Aug 2023 14:38:00 +0000 Subject: [PATCH 04/25] chore(release): 0.116.0-dev.2 [skip ci] # [0.116.0-dev.2](https://github.com/ReVanced/revanced-integrations/compare/v0.116.0-dev.1...v0.116.0-dev.2) (2023-08-07) ### Features * **YouTube - Hide video action buttons:** Hide individual action buttons ([#451](https://github.com/ReVanced/revanced-integrations/issues/451)) ([8e5ca65](https://github.com/ReVanced/revanced-integrations/commit/8e5ca65286b8b62eaeff3bce5fa1d2fb5a198703)) --- CHANGELOG.md | 7 +++++++ gradle.properties | 2 +- 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index b8096002..7080fde3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,10 @@ +# [0.116.0-dev.2](https://github.com/ReVanced/revanced-integrations/compare/v0.116.0-dev.1...v0.116.0-dev.2) (2023-08-07) + + +### Features + +* **YouTube - Hide video action buttons:** Hide individual action buttons ([#451](https://github.com/ReVanced/revanced-integrations/issues/451)) ([8e5ca65](https://github.com/ReVanced/revanced-integrations/commit/8e5ca65286b8b62eaeff3bce5fa1d2fb5a198703)) + # [0.116.0-dev.1](https://github.com/ReVanced/revanced-integrations/compare/v0.115.1...v0.116.0-dev.1) (2023-08-07) diff --git a/gradle.properties b/gradle.properties index 7e14a8d6..eec1af67 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1,4 +1,4 @@ org.gradle.parallel = true org.gradle.caching = true android.useAndroidX = true -version = 0.116.0-dev.1 +version = 0.116.0-dev.2 From 97880eaf72b0ae746ae3851d51e36039244b01c0 Mon Sep 17 00:00:00 2001 From: LisoUseInAIKyrios <118716522+LisoUseInAIKyrios@users.noreply.github.com> Date: Tue, 8 Aug 2023 12:16:34 +0400 Subject: [PATCH 05/25] fix(YouTube - Player Flyout Menu): Change 'audio track flyout menu' to show up by default (#458) --- .../java/app/revanced/integrations/settings/SettingsEnum.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/src/main/java/app/revanced/integrations/settings/SettingsEnum.java b/app/src/main/java/app/revanced/integrations/settings/SettingsEnum.java index 9c1a5958..a5f25f9d 100644 --- a/app/src/main/java/app/revanced/integrations/settings/SettingsEnum.java +++ b/app/src/main/java/app/revanced/integrations/settings/SettingsEnum.java @@ -163,7 +163,7 @@ public enum SettingsEnum { HIDE_HELP_MENU("revanced_hide_player_flyout_help", BOOLEAN, TRUE), HIDE_SPEED_MENU("revanced_hide_player_flyout_speed", BOOLEAN, FALSE), HIDE_MORE_INFO_MENU("revanced_hide_player_flyout_more_info", BOOLEAN, TRUE), - HIDE_AUDIO_TRACK_MENU("revanced_hide_player_flyout_audio_track", BOOLEAN, TRUE), + HIDE_AUDIO_TRACK_MENU("revanced_hide_player_flyout_audio_track", BOOLEAN, FALSE), HIDE_WATCH_IN_VR_MENU("revanced_hide_player_flyout_watch_in_vr", BOOLEAN, TRUE), // Misc From 8648434f996abc4c4d0c23165459090d9960e2c9 Mon Sep 17 00:00:00 2001 From: semantic-release-bot Date: Tue, 8 Aug 2023 08:20:08 +0000 Subject: [PATCH 06/25] chore(release): 0.116.0-dev.3 [skip ci] # [0.116.0-dev.3](https://github.com/ReVanced/revanced-integrations/compare/v0.116.0-dev.2...v0.116.0-dev.3) (2023-08-08) ### Bug Fixes * **YouTube - Player Flyout Menu:** Change 'audio track flyout menu' to show up by default ([#458](https://github.com/ReVanced/revanced-integrations/issues/458)) ([97880ea](https://github.com/ReVanced/revanced-integrations/commit/97880eaf72b0ae746ae3851d51e36039244b01c0)) --- CHANGELOG.md | 7 +++++++ gradle.properties | 2 +- 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 7080fde3..1ba37eb9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,10 @@ +# [0.116.0-dev.3](https://github.com/ReVanced/revanced-integrations/compare/v0.116.0-dev.2...v0.116.0-dev.3) (2023-08-08) + + +### Bug Fixes + +* **YouTube - Player Flyout Menu:** Change 'audio track flyout menu' to show up by default ([#458](https://github.com/ReVanced/revanced-integrations/issues/458)) ([97880ea](https://github.com/ReVanced/revanced-integrations/commit/97880eaf72b0ae746ae3851d51e36039244b01c0)) + # [0.116.0-dev.2](https://github.com/ReVanced/revanced-integrations/compare/v0.116.0-dev.1...v0.116.0-dev.2) (2023-08-07) diff --git a/gradle.properties b/gradle.properties index eec1af67..1d260624 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1,4 +1,4 @@ org.gradle.parallel = true org.gradle.caching = true android.useAndroidX = true -version = 0.116.0-dev.2 +version = 0.116.0-dev.3 From 2ba44526d58a14282a323b0047db4a87748ed1b4 Mon Sep 17 00:00:00 2001 From: oSumAtrIX Date: Wed, 16 Aug 2023 09:14:08 +0200 Subject: [PATCH 07/25] build: update Gradle --- build.gradle.kts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.gradle.kts b/build.gradle.kts index be6fa2c9..7f2b9a0f 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -4,7 +4,7 @@ buildscript { mavenCentral() } dependencies { - classpath("com.android.tools.build:gradle:7.4.2") + classpath("com.android.tools.build:gradle:8.0.2") classpath("org.jetbrains.kotlin:kotlin-gradle-plugin:1.7.20") } } From efa60dc64a22afad3a02c94171bff17f2742ab8d Mon Sep 17 00:00:00 2001 From: oSumAtrIX Date: Wed, 16 Aug 2023 09:38:29 +0200 Subject: [PATCH 08/25] build: Bump dependencies --- app/build.gradle.kts | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/app/build.gradle.kts b/app/build.gradle.kts index 6e77d08b..0e98a7b6 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -5,7 +5,6 @@ plugins { android { compileSdk = 33 - buildToolsVersion = "33.0.1" namespace = "app.revanced.integrations" defaultConfig { @@ -44,7 +43,7 @@ android { dependencies { compileOnly(project(mapOf("path" to ":dummy"))) compileOnly("androidx.annotation:annotation:1.6.0") - compileOnly("androidx.appcompat:appcompat:1.7.0-alpha02") + compileOnly("androidx.appcompat:appcompat:1.7.0-alpha03") compileOnly("com.squareup.okhttp3:okhttp:5.0.0-alpha.11") compileOnly("com.squareup.retrofit2:retrofit:2.9.0") } From 218860734021ef89699de110ec2dcf63412761b9 Mon Sep 17 00:00:00 2001 From: LisoUseInAIKyrios <118716522+LisoUseInAIKyrios@users.noreply.github.com> Date: Wed, 16 Aug 2023 15:58:27 +0400 Subject: [PATCH 09/25] fix(YouTube - Litho Filter): Don't remove the buffer until the thread stops (#461) --- .../integrations/patches/components/LithoFilterPatch.java | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/app/src/main/java/app/revanced/integrations/patches/components/LithoFilterPatch.java b/app/src/main/java/app/revanced/integrations/patches/components/LithoFilterPatch.java index 2429fca4..de96efad 100644 --- a/app/src/main/java/app/revanced/integrations/patches/components/LithoFilterPatch.java +++ b/app/src/main/java/app/revanced/integrations/patches/components/LithoFilterPatch.java @@ -425,6 +425,10 @@ public final class LithoFilterPatch { */ @SuppressWarnings("unused") public static void setProtoBuffer(@NonNull ByteBuffer protobufBuffer) { + // Set the buffer to a thread local. The buffer will remain in memory, even after the call to #filter completes. + // This is intentional, as it appears the buffer can be set once and then filtered multiple times. + // The buffer will be cleared from memory after a new buffer is set by the same thread, + // or when the calling thread eventually dies. bufferThreadLocal.set(protobufBuffer); } @@ -452,8 +456,6 @@ public final class LithoFilterPatch { if (pathSearchTree.matches(parameter.path, parameter)) return true; } catch (Exception ex) { LogHelper.printException(() -> "Litho filter failure", ex); - } finally { - bufferThreadLocal.remove(); // Cleanup and remove the buffer. } return false; From 95dae4896eb750bc25b33fac202611ed01ea6b71 Mon Sep 17 00:00:00 2001 From: semantic-release-bot Date: Wed, 16 Aug 2023 12:01:54 +0000 Subject: [PATCH 10/25] chore(release): 0.116.0-dev.4 [skip ci] # [0.116.0-dev.4](https://github.com/ReVanced/revanced-integrations/compare/v0.116.0-dev.3...v0.116.0-dev.4) (2023-08-16) ### Bug Fixes * **YouTube - Litho Filter:** Don't remove the buffer until the thread stops ([#461](https://github.com/ReVanced/revanced-integrations/issues/461)) ([2188607](https://github.com/ReVanced/revanced-integrations/commit/218860734021ef89699de110ec2dcf63412761b9)) --- CHANGELOG.md | 7 +++++++ gradle.properties | 2 +- 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 1ba37eb9..82641d9a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,10 @@ +# [0.116.0-dev.4](https://github.com/ReVanced/revanced-integrations/compare/v0.116.0-dev.3...v0.116.0-dev.4) (2023-08-16) + + +### Bug Fixes + +* **YouTube - Litho Filter:** Don't remove the buffer until the thread stops ([#461](https://github.com/ReVanced/revanced-integrations/issues/461)) ([2188607](https://github.com/ReVanced/revanced-integrations/commit/218860734021ef89699de110ec2dcf63412761b9)) + # [0.116.0-dev.3](https://github.com/ReVanced/revanced-integrations/compare/v0.116.0-dev.2...v0.116.0-dev.3) (2023-08-08) diff --git a/gradle.properties b/gradle.properties index 1d260624..d1802b26 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1,4 +1,4 @@ org.gradle.parallel = true org.gradle.caching = true android.useAndroidX = true -version = 0.116.0-dev.3 +version = 0.116.0-dev.4 From d354356b2d9052bc0d388633ed9eb0206f7c2058 Mon Sep 17 00:00:00 2001 From: LisoUseInAIKyrios <118716522+LisoUseInAIKyrios@users.noreply.github.com> Date: Mon, 21 Aug 2023 10:28:20 +0400 Subject: [PATCH 11/25] feat(YouTube): Add `Alternative thumbnails` patch (#462) --- .../patches/AlternativeThumbnailsPatch.java | 409 ++++++++++++++++++ .../integrations/settings/SettingsEnum.java | 4 + .../settings/SharedPrefCategory.java | 16 +- .../integrations/utils/ByteTrieSearch.java | 12 +- .../integrations/utils/StringTrieSearch.java | 12 +- .../integrations/utils/TrieSearch.java | 27 +- .../org/chromium/net/UrlResponseInfo.java | 12 + 7 files changed, 481 insertions(+), 11 deletions(-) create mode 100644 app/src/main/java/app/revanced/integrations/patches/AlternativeThumbnailsPatch.java create mode 100644 dummy/src/main/java/org/chromium/net/UrlResponseInfo.java diff --git a/app/src/main/java/app/revanced/integrations/patches/AlternativeThumbnailsPatch.java b/app/src/main/java/app/revanced/integrations/patches/AlternativeThumbnailsPatch.java new file mode 100644 index 00000000..1e4d3f05 --- /dev/null +++ b/app/src/main/java/app/revanced/integrations/patches/AlternativeThumbnailsPatch.java @@ -0,0 +1,409 @@ +package app.revanced.integrations.patches; + +import androidx.annotation.GuardedBy; +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; + +import org.chromium.net.UrlResponseInfo; + +import java.net.HttpURLConnection; +import java.net.URL; +import java.util.HashMap; +import java.util.LinkedHashMap; +import java.util.Map; +import java.util.concurrent.ExecutionException; + +import app.revanced.integrations.settings.SettingsEnum; +import app.revanced.integrations.utils.LogHelper; +import app.revanced.integrations.utils.ReVancedUtils; + +/** + * Alternative YouTube thumbnails, showing the beginning/middle/end of the video. + * (ie: sd1.jpg, sd2.jpg, sd3.jpg). + * + * Has an additional option to use 'fast' thumbnails, + * where it forces sd thumbnail quality and skips verifying if the alt thumbnail image exists. + * The UI loading time will be the same or better than using the the original thumbnails, + * but thumbnails will initially fail to load for all live streams, unreleased, and occasionally very old videos. + * If a failed thumbnail load is reloaded (ie: scroll off, then on screen), then the original thumbnail + * is reloaded instead. Fast thumbnails requires using SD or lower thumbnail resolution, + * because a noticeable number of videos do not have hq720 and too many fail to load. + * + * Ideas for improvements: + * - Selectively allow using original thumbnails in some situations, + * such as videos subscription feed, watch history, or in search results. + * - Save to a temporary file the video id's verified to have alt thumbnails. + * This would speed up loading the watch history and users saved playlists. + */ +public final class AlternativeThumbnailsPatch { + + private enum ThumbnailQuality { + // In order of lowest to highest resolution. + DEFAULT("default", ""), // effective alt name is 1.jpg, 2.jpg, 3.jpg + MQDEFAULT("mqdefault", "mq"), + HQDEFAULT("hqdefault", "hq"), + SDDEFAULT("sddefault", "sd"), + HQ720("hq720", "hq720_"), + MAXRESDEFAULT("maxresdefault", "maxres"); + + /** + * Lookup map of original name to enum. + */ + private static final Map originalNameToEnum = new HashMap<>(); + + /** + * Lookup map of alt name to enum. ie: "hq720_1" to {@link #HQ720}. + */ + private static final Map altNameToEnum = new HashMap<>(); + + static { + for (ThumbnailQuality quality : values()) { + originalNameToEnum.put(quality.originalName, quality); + + for (int i = 1; i <= 3; i++) { + altNameToEnum.put(quality.altImageName + i, quality); + } + } + } + + /** + * Convert an alt image name to enum. + * ie: "hq720_2" returns {@link #HQ720}. + */ + @Nullable + static ThumbnailQuality altImageNameToQuality(@NonNull String altImageName) { + return altNameToEnum.get(altImageName); + } + + /** + * Original quality to effective alt quality to use. + * ie: If fast alt image is enabled, then "hq720" returns {@link #SDDEFAULT}. + */ + @Nullable + static ThumbnailQuality getQualityToUse(@NonNull String originalSize) { + ThumbnailQuality quality = originalNameToEnum.get(originalSize); + if (quality == null) { + return null; // Not a thumbnail for a regular video. + } + + final boolean useFastQuality = SettingsEnum.ALT_THUMBNAIL_FAST_QUALITY.getBoolean(); + switch (quality) { + case SDDEFAULT: + // SD alt images have somewhat worse quality with washed out color and poor contrast. + // But the 720 images look much better and don't suffer from these issues. + // For unknown reasons, the 720 thumbnails are used only for the home feed, + // while SD is used for the search and subscription feed + // (even though search and subscriptions use the exact same layout as the home feed). + // Of note, this image quality issue only appears with the alt thumbnail images, + // and the regular thumbnails have identical color/contrast quality for all sizes. + // Fix this by falling thru and upgrading SD to 720. + case HQ720: + if (useFastQuality) { + return SDDEFAULT; // SD is max resolution for fast alt images. + } + return HQ720; + case MAXRESDEFAULT: + if (useFastQuality) { + return SDDEFAULT; + } + return MAXRESDEFAULT; + default: + return quality; + } + } + + final String originalName; + final String altImageName; + + ThumbnailQuality(String originalName, String altImageName) { + this.originalName = originalName; + this.altImageName = altImageName; + } + + String getAltImageNameToUse() { + return altImageName + SettingsEnum.ALT_THUMBNAIL_TYPE.getInt(); + } + } + + /** + * Uses HTTP HEAD requests to verify and keep track of which thumbnail sizes + * are available and not available. + */ + private static class VerifiedQualities { + /** + * After a quality is verified as not available, how long until the quality is re-verified again. + * Used only if fast mode is not enabled. Intended for live streams and unreleased videos + * that are now finished and available (and thus, the alt thumbnails are also now available). + */ + private static final long NOT_AVAILABLE_TIMEOUT_MILLISECONDS = 10 * 60 * 1000; // 10 minutes. + + /** + * Cache used to verify if an alternative thumbnails exists for a given video id. + */ + @GuardedBy("itself") + private static final Map altVideoIdLookup = new LinkedHashMap<>(100) { + private static final int CACHE_LIMIT = 1000; + + @Override + protected boolean removeEldestEntry(Map.Entry eldest) { + return size() > CACHE_LIMIT; // Evict oldest entry if over the cache limit. + } + }; + + private static VerifiedQualities getVerifiedQualities(@NonNull String videoId, boolean returnNullIfDoesNotExist) { + synchronized (altVideoIdLookup) { + VerifiedQualities verified = altVideoIdLookup.get(videoId); + if (verified == null) { + if (returnNullIfDoesNotExist) { + return null; + } + verified = new VerifiedQualities(); + altVideoIdLookup.put(videoId, verified); + } + return verified; + } + } + + static boolean verifyAltThumbnailExist(@NonNull String videoId, @NonNull ThumbnailQuality quality, + @NonNull String imageUrl) { + VerifiedQualities verified = getVerifiedQualities(videoId, SettingsEnum.ALT_THUMBNAIL_FAST_QUALITY.getBoolean()); + if (verified == null) return true; // Fast alt thumbnails is enabled. + return verified.verifyYouTubeThumbnailExists(videoId, quality, imageUrl); + } + + static void setAltThumbnailDoesNotExist(@NonNull String videoId, @NonNull ThumbnailQuality quality) { + VerifiedQualities verified = getVerifiedQualities(videoId, false); + verified.setQualityVerified(videoId, quality, false); + } + + /** + * Highest quality verified as existing. + */ + @Nullable + ThumbnailQuality highestQualityVerified; + /** + * Lowest quality verified as not existing. + */ + @Nullable + ThumbnailQuality lowestQualityNotAvailable; + + /** + * System time, of when to invalidate {@link #lowestQualityNotAvailable}. + * Used only if fast mode is not enabled. + */ + long timeToReVerifyLowestQuality; + + synchronized void setQualityVerified(String videoId, ThumbnailQuality quality, boolean isVerified) { + if (isVerified) { + if (highestQualityVerified == null || highestQualityVerified.ordinal() < quality.ordinal()) { + highestQualityVerified = quality; + } + } else { + if (lowestQualityNotAvailable == null || lowestQualityNotAvailable.ordinal() > quality.ordinal()) { + lowestQualityNotAvailable = quality; + timeToReVerifyLowestQuality = System.currentTimeMillis() + NOT_AVAILABLE_TIMEOUT_MILLISECONDS; + } + LogHelper.printDebug(() -> quality + " not available for video: " + videoId); + } + } + + /** + * Verify if a video alt thumbnail exists. Does so by making a minimal HEAD http request. + */ + synchronized boolean verifyYouTubeThumbnailExists(@NonNull String videoId, @NonNull ThumbnailQuality quality, + @NonNull String imageUrl) { + if (highestQualityVerified != null && highestQualityVerified.ordinal() >= quality.ordinal()) { + return true; // Previously verified as existing. + } + + final boolean fastQuality = SettingsEnum.ALT_THUMBNAIL_FAST_QUALITY.getBoolean(); + if (lowestQualityNotAvailable != null && lowestQualityNotAvailable.ordinal() <= quality.ordinal()) { + if (fastQuality || System.currentTimeMillis() < timeToReVerifyLowestQuality) { + return false; // Previously verified as not existing. + } + // Enough time has passed, and should re-verify again. + LogHelper.printDebug(() -> "Resetting lowest verified quality for: " + videoId); + lowestQualityNotAvailable = null; + } + + if (fastQuality) { + return true; // Unknown if it exists or not. Use the URL anyways and update afterwards if loading fails. + } + + boolean imageFileFound; + try { + LogHelper.printDebug(() -> "Verifying image: " + imageUrl); + // This hooked code is running on a low priority thread, and it's slightly faster + // to run the url connection thru the integrations thread pool which runs at the highest priority. + final long start = System.currentTimeMillis(); + imageFileFound = ReVancedUtils.submitOnBackgroundThread(() -> { + final int connectionTimeoutMillis = 5000; + HttpURLConnection connection = (HttpURLConnection) new URL(imageUrl).openConnection(); + connection.setConnectTimeout(connectionTimeoutMillis); + connection.setReadTimeout(connectionTimeoutMillis); + connection.setRequestMethod("HEAD"); + // Even with a HEAD request, the response is the same size as a full GET request. + // Using an empty range fixes this. + connection.setRequestProperty("Range", "bytes=0-0"); + final int responseCode = connection.getResponseCode(); + if (responseCode == HttpURLConnection.HTTP_PARTIAL) { + String contentType = connection.getContentType(); + return (contentType != null && contentType.startsWith("image")); + } + if (responseCode != HttpURLConnection.HTTP_NOT_FOUND) { + LogHelper.printDebug(() -> "Unexpected response code: " + responseCode + " for url: " + imageUrl); + } + return false; + }).get(); + LogHelper.printDebug(() -> "Alt verification took: " + (System.currentTimeMillis() - start) + "ms"); + } catch (ExecutionException | InterruptedException ex) { + LogHelper.printInfo(() -> "Could not verify alt url: " + imageUrl, ex); + imageFileFound = false; + } + + setQualityVerified(videoId, quality, imageFileFound); + return imageFileFound; + } + } + + /** + * YouTube video thumbnail url, decoded into it's relevant parts. + */ + private static class DecodedThumbnailUrl { + /** + * YouTube thumbnail URL prefix. Can be '/vi/' or '/vi_webp/' + */ + private static final String YOUTUBE_THUMBNAIL_PREFIX = "https://i.ytimg.com/vi"; + + @Nullable + static DecodedThumbnailUrl decodeImageUrl(String url) { + final int videoIdStartIndex = url.indexOf('/', YOUTUBE_THUMBNAIL_PREFIX.length()) + 1; + if (videoIdStartIndex <= 0) return null; + final int videoIdEndIndex = url.indexOf('/', videoIdStartIndex); + if (videoIdEndIndex < 0) return null; + final int imageSizeStartIndex = videoIdEndIndex + 1; + final int imageSizeEndIndex = url.indexOf('.', imageSizeStartIndex); + if (imageSizeEndIndex < 0) return null; + int imageExtensionEndIndex = url.indexOf('?', imageSizeEndIndex); + if (imageExtensionEndIndex < 0) imageExtensionEndIndex = url.length(); + return new DecodedThumbnailUrl(url, videoIdStartIndex, videoIdEndIndex, + imageSizeStartIndex, imageSizeEndIndex, imageExtensionEndIndex); + } + + /** Full usable url, but stripped of any tracking information. */ + final String sanitizedUrl; + /** Url up to the video id. */ + final String urlPrefix; + final String videoId; + /** Quality, such as hq720 or sddefault. */ + final String imageQuality; + /** jpg or webp */ + final String imageExtension; + /** User view tracking parameters, only present on some images. */ + final String urlTrackingParameters; + + private DecodedThumbnailUrl(String fullUrl, int videoIdStartIndex, int videoIdEndIndex, + int imageSizeStartIndex, int imageSizeEndIndex, int imageExtensionEndIndex) { + sanitizedUrl = fullUrl.substring(0, imageExtensionEndIndex); + urlPrefix = fullUrl.substring(0, videoIdStartIndex); + videoId = fullUrl.substring(videoIdStartIndex, videoIdEndIndex); + imageQuality = fullUrl.substring(imageSizeStartIndex, imageSizeEndIndex); + imageExtension = fullUrl.substring(imageSizeEndIndex + 1, imageExtensionEndIndex); + urlTrackingParameters = (imageExtensionEndIndex == fullUrl.length()) + ? "" : fullUrl.substring(imageExtensionEndIndex); + } + } + + static { + // Fix any bad imported data. + final int altThumbnailType = SettingsEnum.ALT_THUMBNAIL_TYPE.getInt(); + if (altThumbnailType < 1 || altThumbnailType > 3) { + LogHelper.printException(() -> "Invalid alt thumbnail type: " + altThumbnailType); + SettingsEnum.ALT_THUMBNAIL_TYPE.saveValue(SettingsEnum.ALT_THUMBNAIL_TYPE.defaultValue); + } + } + + /** + * Injection point. Called off the main thread and by multiple threads at the same time. + * + * @param originalUrl Image url for all url images loaded, including video thumbnails. + */ + public static String overrideImageURL(String originalUrl) { + try { + if (!SettingsEnum.ALT_THUMBNAIL.getBoolean()) { + return originalUrl; + } + DecodedThumbnailUrl decodedUrl = DecodedThumbnailUrl.decodeImageUrl(originalUrl); + if (decodedUrl == null) { + return originalUrl; // Not a thumbnail. + } + + // Keep any tracking parameters out of the logs, and log only the base url. + LogHelper.printDebug(() -> "Original url: " + decodedUrl.sanitizedUrl); + + ThumbnailQuality qualityToUse = ThumbnailQuality.getQualityToUse(decodedUrl.imageQuality); + if (qualityToUse == null) return originalUrl; // Video is a short. + + // Images could be upgraded to webp if they are not already, but this fails quite often, + // especially for new videos uploaded in the last hour. + // And even if alt webp images do exist, sometimes they can load much slower than the original jpg alt images. + // (as much as 4x slower has been observed, despite the alt webp image being a smaller file). + + StringBuilder builder = new StringBuilder(originalUrl.length() + 2); + builder.append(decodedUrl.urlPrefix); + builder.append(decodedUrl.videoId).append('/'); + builder.append(qualityToUse.getAltImageNameToUse()); + builder.append('.').append(decodedUrl.imageExtension); + + String sanitizedReplacement = builder.toString(); + if (!VerifiedQualities.verifyAltThumbnailExist(decodedUrl.videoId, qualityToUse, sanitizedReplacement)) { + return originalUrl; + } + + LogHelper.printDebug(() -> "Replaced url: " + sanitizedReplacement); + + // URL tracking parameters. Presumably they are to determine if a user has viewed a thumbnail. + // This likely is used for recommendations, so they are retained if present. + builder.append(decodedUrl.urlTrackingParameters); + return builder.toString(); + } catch (Exception ex) { + LogHelper.printException(() -> "Alt thumbnails failure", ex); + return originalUrl; + } + } + + /** + * Injection point. + * + * Cronet considers all completed connections as a success, even if the response is 404 or 5xx. + */ + public static void handleCronetSuccess(@NonNull UrlResponseInfo responseInfo) { + try { + if (responseInfo.getHttpStatusCode() == 404 && SettingsEnum.ALT_THUMBNAIL.getBoolean()) { + // Fast alt thumbnails is enabled and the thumbnail is not available. + // The video is: + // - live stream + // - upcoming unreleased video + // - very old + // - very low view count + // Take note of this, so if the image reloads the original thumbnail will be used. + DecodedThumbnailUrl decodedUrl = DecodedThumbnailUrl.decodeImageUrl(responseInfo.getUrl()); + if (decodedUrl == null) { + return; // Not a thumbnail. + } + + ThumbnailQuality quality = ThumbnailQuality.altImageNameToQuality(decodedUrl.imageQuality); + if (quality == null) { + // Video is a short or unknown quality, but the url returned 404. Should never happen. + LogHelper.printDebug(() -> "Failed to load unknown url: " + decodedUrl.sanitizedUrl); + return; + } + + VerifiedQualities.setAltThumbnailDoesNotExist(decodedUrl.videoId, quality); + } + } catch (Exception ex) { + LogHelper.printException(() -> "Alt thumbnails callback failure", ex); + } + } + +} diff --git a/app/src/main/java/app/revanced/integrations/settings/SettingsEnum.java b/app/src/main/java/app/revanced/integrations/settings/SettingsEnum.java index a5f25f9d..6a2027b3 100644 --- a/app/src/main/java/app/revanced/integrations/settings/SettingsEnum.java +++ b/app/src/main/java/app/revanced/integrations/settings/SettingsEnum.java @@ -154,6 +154,10 @@ public enum SettingsEnum { HIDE_SHORTS_NAVIGATION_BAR("revanced_hide_shorts_navigation_bar", BOOLEAN, TRUE, true), HIDE_SHORTS("revanced_hide_shorts", BOOLEAN, FALSE, true), + ALT_THUMBNAIL("revanced_alt_thumbnail", BOOLEAN, FALSE), + ALT_THUMBNAIL_TYPE("revanced_alt_thumbnail_type", INTEGER, 2, parents(ALT_THUMBNAIL)), + ALT_THUMBNAIL_FAST_QUALITY("revanced_alt_thumbnail_fast_quality", BOOLEAN, FALSE, parents(ALT_THUMBNAIL)), + //Player flyout menu items HIDE_QUALITY_MENU("revanced_hide_player_flyout_quality", BOOLEAN, FALSE), HIDE_CAPTIONS_MENU("revanced_hide_player_flyout_captions", BOOLEAN, FALSE), diff --git a/app/src/main/java/app/revanced/integrations/settings/SharedPrefCategory.java b/app/src/main/java/app/revanced/integrations/settings/SharedPrefCategory.java index bded5469..17799e25 100644 --- a/app/src/main/java/app/revanced/integrations/settings/SharedPrefCategory.java +++ b/app/src/main/java/app/revanced/integrations/settings/SharedPrefCategory.java @@ -80,12 +80,24 @@ public enum SharedPrefCategory { @NonNull public String getString(@NonNull String key, @NonNull String _default) { Objects.requireNonNull(_default); - return preferences.getString(key, _default); + try { + return preferences.getString(key, _default); + } catch (ClassCastException ex) { + // Value stored is a completely different type (should never happen). + removeConflictingPreferenceKeyValue(key); + return _default; + } } public boolean getBoolean(@NonNull String key, boolean _default) { - return preferences.getBoolean(key, _default); + try { + return preferences.getBoolean(key, _default); + } catch (ClassCastException ex) { + // Value stored is a completely different type (should never happen). + removeConflictingPreferenceKeyValue(key); + return _default; + } } @NonNull diff --git a/app/src/main/java/app/revanced/integrations/utils/ByteTrieSearch.java b/app/src/main/java/app/revanced/integrations/utils/ByteTrieSearch.java index e83a921c..807f3f8a 100644 --- a/app/src/main/java/app/revanced/integrations/utils/ByteTrieSearch.java +++ b/app/src/main/java/app/revanced/integrations/utils/ByteTrieSearch.java @@ -30,9 +30,19 @@ public final class ByteTrieSearch extends TrieSearch { super.addPattern(pattern, pattern.length, Objects.requireNonNull(callback)); } + @Override + public boolean matches(@NonNull byte[] textToSearch, int startIndex, int endIndex, @Nullable Object callbackParameter) { + return super.matches(textToSearch, textToSearch.length, startIndex, endIndex, callbackParameter); + } + + @Override + public boolean matches(@NonNull byte[] textToSearch, int startIndex) { + return matches(textToSearch, startIndex, textToSearch.length, null); + } + @Override public boolean matches(@NonNull byte[] textToSearch, @Nullable Object callbackParameter) { - return super.matches(textToSearch, textToSearch.length, callbackParameter); + return matches(textToSearch,0, textToSearch.length, callbackParameter); } } diff --git a/app/src/main/java/app/revanced/integrations/utils/StringTrieSearch.java b/app/src/main/java/app/revanced/integrations/utils/StringTrieSearch.java index 7afafc60..1a1a0a9e 100644 --- a/app/src/main/java/app/revanced/integrations/utils/StringTrieSearch.java +++ b/app/src/main/java/app/revanced/integrations/utils/StringTrieSearch.java @@ -33,8 +33,18 @@ public final class StringTrieSearch extends TrieSearch { super.addPattern(pattern, pattern.length(), Objects.requireNonNull(callback)); } + @Override + public boolean matches(@NonNull String textToSearch, int startIndex, int endIndex, @Nullable Object callbackParameter) { + return super.matches(textToSearch, textToSearch.length(), startIndex, endIndex, callbackParameter); + } + @Override public boolean matches(@NonNull String textToSearch, @Nullable Object callbackParameter) { - return super.matches(textToSearch, textToSearch.length(), callbackParameter); + return matches(textToSearch, 0, textToSearch.length(), callbackParameter); + } + + @Override + public boolean matches(@NonNull String textToSearch, int startIndex) { + return matches(textToSearch, startIndex, textToSearch.length(), null); } } diff --git a/app/src/main/java/app/revanced/integrations/utils/TrieSearch.java b/app/src/main/java/app/revanced/integrations/utils/TrieSearch.java index 9d2db167..6458f07c 100644 --- a/app/src/main/java/app/revanced/integrations/utils/TrieSearch.java +++ b/app/src/main/java/app/revanced/integrations/utils/TrieSearch.java @@ -243,12 +243,17 @@ public abstract class TrieSearch { root.addPattern(pattern, patternLength, 0, callback); } - boolean matches(@NonNull T textToSearch, int textToSearchLength, @Nullable Object callbackParameter) { + final boolean matches(@NonNull T textToSearch, int textToSearchLength, int startIndex, int endIndex, + @Nullable Object callbackParameter) { + if (endIndex > textToSearchLength) { + throw new IllegalArgumentException("endIndex: " + endIndex + + " is greater than texToSearchLength: " + textToSearchLength); + } if (patterns.size() == 0) { return false; // No patterns were added. } - for (int i = 0; i < textToSearchLength; i++) { - if (root.matches(textToSearch, textToSearchLength, i, 0, callbackParameter)) return true; + for (int i = startIndex; i < endIndex; i++) { + if (root.matches(textToSearch, endIndex, i, 0, callbackParameter)) return true; } return false; } @@ -287,19 +292,27 @@ public abstract class TrieSearch { */ public abstract void addPattern(@NonNull T pattern, @NonNull TriePatternMatchedCallback callback); + /** * Searches through text, looking for any substring that matches any pattern in this tree. * * @param textToSearch Text to search through. + * @param startIndex Index to start searching, inclusive value. + * @param endIndex Index to stop matching, exclusive value. * @param callbackParameter Optional parameter passed to the callbacks. * @return If any pattern matched, and it's callback halted searching. */ + public abstract boolean matches(@NonNull T textToSearch, int startIndex, int endIndex, @Nullable Object callbackParameter); + + public abstract boolean matches(@NonNull T textToSearch, int startIndex); + public abstract boolean matches(@NonNull T textToSearch, @Nullable Object callbackParameter); - /** - * Identical to {@link #matches(Object, Object)} but with a null callback parameter. - */ + public final boolean matches(@NonNull T textToSearch, int startIndex, int endIndex) { + return matches(textToSearch, startIndex, endIndex, null); + } + public final boolean matches(@NonNull T textToSearch) { - return matches(textToSearch, null); + return matches(textToSearch, 0); } } diff --git a/dummy/src/main/java/org/chromium/net/UrlResponseInfo.java b/dummy/src/main/java/org/chromium/net/UrlResponseInfo.java new file mode 100644 index 00000000..8e341247 --- /dev/null +++ b/dummy/src/main/java/org/chromium/net/UrlResponseInfo.java @@ -0,0 +1,12 @@ +package org.chromium.net; + +//dummy class +public abstract class UrlResponseInfo { + + public abstract String getUrl(); + + public abstract int getHttpStatusCode(); + + // Add additional existing methods, if needed. + +} From 842f5f761620a4066f68a47d2184d1a4df52587e Mon Sep 17 00:00:00 2001 From: semantic-release-bot Date: Mon, 21 Aug 2023 06:32:26 +0000 Subject: [PATCH 12/25] chore(release): 0.116.0-dev.5 [skip ci] # [0.116.0-dev.5](https://github.com/ReVanced/revanced-integrations/compare/v0.116.0-dev.4...v0.116.0-dev.5) (2023-08-21) ### Features * **YouTube:** Add `Alternative thumbnails` patch ([#462](https://github.com/ReVanced/revanced-integrations/issues/462)) ([d354356](https://github.com/ReVanced/revanced-integrations/commit/d354356b2d9052bc0d388633ed9eb0206f7c2058)) --- CHANGELOG.md | 7 +++++++ gradle.properties | 2 +- 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 82641d9a..ae59d02e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,10 @@ +# [0.116.0-dev.5](https://github.com/ReVanced/revanced-integrations/compare/v0.116.0-dev.4...v0.116.0-dev.5) (2023-08-21) + + +### Features + +* **YouTube:** Add `Alternative thumbnails` patch ([#462](https://github.com/ReVanced/revanced-integrations/issues/462)) ([d354356](https://github.com/ReVanced/revanced-integrations/commit/d354356b2d9052bc0d388633ed9eb0206f7c2058)) + # [0.116.0-dev.4](https://github.com/ReVanced/revanced-integrations/compare/v0.116.0-dev.3...v0.116.0-dev.4) (2023-08-16) diff --git a/gradle.properties b/gradle.properties index d1802b26..25e26a35 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1,4 +1,4 @@ org.gradle.parallel = true org.gradle.caching = true android.useAndroidX = true -version = 0.116.0-dev.4 +version = 0.116.0-dev.5 From 33e3c6b061d900944a91df4759cef38d0beb443a Mon Sep 17 00:00:00 2001 From: oSumAtrIX Date: Wed, 23 Aug 2023 05:27:29 +0200 Subject: [PATCH 13/25] refactor: improve readability with comments --- .../patches/components/ButtonsFilter.java | 39 ++++++++++--------- 1 file changed, 20 insertions(+), 19 deletions(-) diff --git a/app/src/main/java/app/revanced/integrations/patches/components/ButtonsFilter.java b/app/src/main/java/app/revanced/integrations/patches/components/ButtonsFilter.java index 8c8e4546..f2ef0f56 100644 --- a/app/src/main/java/app/revanced/integrations/patches/components/ButtonsFilter.java +++ b/app/src/main/java/app/revanced/integrations/patches/components/ButtonsFilter.java @@ -9,22 +9,21 @@ import app.revanced.integrations.settings.SettingsEnum; @RequiresApi(api = Build.VERSION_CODES.N) final class ButtonsFilter extends Filter { - private static final String VIDEO_ACTION_BAR_PATH = "video_action_bar.eml"; - private final StringFilterGroup actionBarRule; - private final StringFilterGroup bufferFilterPathRule; + private final StringFilterGroup actionBarGroup; + private final StringFilterGroup bufferFilterPathGroup; private final ByteArrayFilterGroupList bufferButtonsGroupList = new ByteArrayFilterGroupList(); public ButtonsFilter() { - actionBarRule = new StringFilterGroup( + actionBarGroup = new StringFilterGroup( null, VIDEO_ACTION_BAR_PATH ); - identifierFilterGroups.addAll(actionBarRule); + identifierFilterGroups.addAll(actionBarGroup); - bufferFilterPathRule = new StringFilterGroup( + bufferFilterPathGroup = new StringFilterGroup( null, "|CellType|CollectionType|CellType|ContainerType|button.eml|" ); @@ -45,7 +44,7 @@ final class ButtonsFilter extends Filter { SettingsEnum.HIDE_CLIP_BUTTON, "|clip_button.eml|" ), - bufferFilterPathRule + bufferFilterPathGroup ); bufferButtonsGroupList.addAll( @@ -83,11 +82,11 @@ final class ButtonsFilter extends Filter { } private boolean isEveryFilterGroupEnabled() { - for (FilterGroup rule : pathFilterGroups) - if (!rule.isEnabled()) return false; + for (var group : pathFilterGroups) + if (!group.isEnabled()) return false; - for (FilterGroup rule : bufferButtonsGroupList) - if (!rule.isEnabled()) return false; + for (var group : bufferButtonsGroupList) + if (!group.isEnabled()) return false; return true; } @@ -95,17 +94,19 @@ final class ButtonsFilter extends Filter { @Override public boolean isFiltered(@Nullable String identifier, String path, byte[] protobufBufferArray, FilterGroupList matchedList, FilterGroup matchedGroup, int matchedIndex) { - if (matchedGroup == actionBarRule) { + // If the current matched group is the action bar group, + // in case every filter group is enabled, hide the action bar. + if (matchedGroup == actionBarGroup) { if (!isEveryFilterGroupEnabled()) { return false; } - } else if (matchedGroup == bufferFilterPathRule) { - if (!path.startsWith(VIDEO_ACTION_BAR_PATH)) { - return false; // Some other unknown button and not part of the player action buttons. - } - if (!bufferButtonsGroupList.check(protobufBufferArray).isFiltered()) { - return false; // Action button is not set to hide. - } + } else if (matchedGroup == bufferFilterPathGroup) { + // Make sure the current path is the right one + // to avoid false positives. + if (!path.startsWith(VIDEO_ACTION_BAR_PATH)) return false; + + // In case the group list has no match, return false. + if (!bufferButtonsGroupList.check(protobufBufferArray).isFiltered()) return false; } return super.isFiltered(identifier, path, protobufBufferArray, matchedList, matchedGroup, matchedIndex); From 7510c0632fecb8e9acdc1713a207bc24d91766ef Mon Sep 17 00:00:00 2001 From: oSumAtrIX Date: Wed, 23 Aug 2023 05:28:57 +0200 Subject: [PATCH 14/25] refactor: fix naming inconsistency --- .../patches/components/AdsFilter.java | 4 +-- .../patches/components/ButtonsFilter.java | 6 ++-- .../patches/components/CommentsFilter.java | 2 +- .../components/LayoutComponentsFilter.java | 4 +-- .../patches/components/LithoFilterPatch.java | 28 ++++++------------- .../PlaybackSpeedMenuFilterPatch.java | 2 +- .../PlayerFlyoutMenuItemsFilter.java | 2 +- .../patches/components/ShortsFilter.java | 8 +++--- .../VideoQualityMenuFilterPatch.java | 2 +- 9 files changed, 24 insertions(+), 34 deletions(-) diff --git a/app/src/main/java/app/revanced/integrations/patches/components/AdsFilter.java b/app/src/main/java/app/revanced/integrations/patches/components/AdsFilter.java index 9ccef8a5..40192d0d 100644 --- a/app/src/main/java/app/revanced/integrations/patches/components/AdsFilter.java +++ b/app/src/main/java/app/revanced/integrations/patches/components/AdsFilter.java @@ -86,7 +86,7 @@ public final class AdsFilter extends Filter { "cta_shelf_card" ); - this.pathFilterGroups.addAll( + this.pathFilterGroupList.addAll( generalAds, buttonedAd, merchandise, @@ -95,7 +95,7 @@ public final class AdsFilter extends Filter { webLinkPanel, movieAds ); - this.identifierFilterGroups.addAll(carouselAd); + this.identifierFilterGroupList.addAll(carouselAd); } @Override diff --git a/app/src/main/java/app/revanced/integrations/patches/components/ButtonsFilter.java b/app/src/main/java/app/revanced/integrations/patches/components/ButtonsFilter.java index f2ef0f56..19cddf3f 100644 --- a/app/src/main/java/app/revanced/integrations/patches/components/ButtonsFilter.java +++ b/app/src/main/java/app/revanced/integrations/patches/components/ButtonsFilter.java @@ -20,14 +20,14 @@ final class ButtonsFilter extends Filter { null, VIDEO_ACTION_BAR_PATH ); - identifierFilterGroups.addAll(actionBarGroup); + identifierFilterGroupList.addAll(actionBarGroup); bufferFilterPathGroup = new StringFilterGroup( null, "|CellType|CollectionType|CellType|ContainerType|button.eml|" ); - pathFilterGroups.addAll( + pathFilterGroupList.addAll( new StringFilterGroup( SettingsEnum.HIDE_LIKE_DISLIKE_BUTTON, "|segmented_like_dislike_button" @@ -82,7 +82,7 @@ final class ButtonsFilter extends Filter { } private boolean isEveryFilterGroupEnabled() { - for (var group : pathFilterGroups) + for (var group : pathFilterGroupList) if (!group.isEnabled()) return false; for (var group : bufferButtonsGroupList) diff --git a/app/src/main/java/app/revanced/integrations/patches/components/CommentsFilter.java b/app/src/main/java/app/revanced/integrations/patches/components/CommentsFilter.java index 22a9cba6..089fb948 100644 --- a/app/src/main/java/app/revanced/integrations/patches/components/CommentsFilter.java +++ b/app/src/main/java/app/revanced/integrations/patches/components/CommentsFilter.java @@ -18,7 +18,7 @@ final class CommentsFilter extends Filter { "comments_entry_point_simplebox" ); - this.pathFilterGroups.addAll( + this.pathFilterGroupList.addAll( comments, previewComment ); diff --git a/app/src/main/java/app/revanced/integrations/patches/components/LayoutComponentsFilter.java b/app/src/main/java/app/revanced/integrations/patches/components/LayoutComponentsFilter.java index 534c7a35..67811a89 100644 --- a/app/src/main/java/app/revanced/integrations/patches/components/LayoutComponentsFilter.java +++ b/app/src/main/java/app/revanced/integrations/patches/components/LayoutComponentsFilter.java @@ -142,7 +142,7 @@ public final class LayoutComponentsFilter extends Filter { "chips_shelf" ); - this.pathFilterGroups.addAll( + this.pathFilterGroupList.addAll( channelBar, communityPosts, paidContent, @@ -165,7 +165,7 @@ public final class LayoutComponentsFilter extends Filter { custom ); - this.identifierFilterGroups.addAll( + this.identifierFilterGroupList.addAll( graySeparator, chipsShelf ); diff --git a/app/src/main/java/app/revanced/integrations/patches/components/LithoFilterPatch.java b/app/src/main/java/app/revanced/integrations/patches/components/LithoFilterPatch.java index de96efad..90aa6781 100644 --- a/app/src/main/java/app/revanced/integrations/patches/components/LithoFilterPatch.java +++ b/app/src/main/java/app/revanced/integrations/patches/components/LithoFilterPatch.java @@ -1,26 +1,16 @@ package app.revanced.integrations.patches.components; import android.os.Build; - import androidx.annotation.NonNull; import androidx.annotation.Nullable; import androidx.annotation.RequiresApi; +import app.revanced.integrations.settings.SettingsEnum; +import app.revanced.integrations.utils.*; import java.nio.ByteBuffer; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Iterator; -import java.util.List; -import java.util.Spliterator; +import java.util.*; import java.util.function.Consumer; -import app.revanced.integrations.settings.SettingsEnum; -import app.revanced.integrations.utils.ByteTrieSearch; -import app.revanced.integrations.utils.LogHelper; -import app.revanced.integrations.utils.ReVancedUtils; -import app.revanced.integrations.utils.StringTrieSearch; -import app.revanced.integrations.utils.TrieSearch; - abstract class FilterGroup { final static class FilterGroupResult { SettingsEnum setting; @@ -283,8 +273,8 @@ abstract class Filter { * will never be called for any matches. */ - protected final StringFilterGroupList pathFilterGroups = new StringFilterGroupList(); - protected final StringFilterGroupList identifierFilterGroups = new StringFilterGroupList(); + protected final StringFilterGroupList pathFilterGroupList = new StringFilterGroupList(); + protected final StringFilterGroupList identifierFilterGroupList = new StringFilterGroupList(); /** * Called after an enabled filter has been matched. @@ -302,9 +292,9 @@ abstract class Filter { boolean isFiltered(@Nullable String identifier, String path, byte[] protobufBufferArray, FilterGroupList matchedList, FilterGroup matchedGroup, int matchedIndex) { if (SettingsEnum.DEBUG.getBoolean()) { - if (pathFilterGroups == matchedList) { + if (pathFilterGroupList == matchedList) { LogHelper.printDebug(() -> getClass().getSimpleName() + " Filtered path: " + path); - } else if (identifierFilterGroups == matchedList) { + } else if (identifierFilterGroupList == matchedList) { LogHelper.printDebug(() -> getClass().getSimpleName() + " Filtered identifier: " + identifier); } } @@ -391,8 +381,8 @@ public final class LithoFilterPatch { static { for (Filter filter : filters) { - filterGroupLists(pathSearchTree, filter, filter.pathFilterGroups); - filterGroupLists(identifierSearchTree, filter, filter.identifierFilterGroups); + filterGroupLists(pathSearchTree, filter, filter.pathFilterGroupList); + filterGroupLists(identifierSearchTree, filter, filter.identifierFilterGroupList); } LogHelper.printDebug(() -> "Using: " diff --git a/app/src/main/java/app/revanced/integrations/patches/components/PlaybackSpeedMenuFilterPatch.java b/app/src/main/java/app/revanced/integrations/patches/components/PlaybackSpeedMenuFilterPatch.java index 2e93574f..00b11af3 100644 --- a/app/src/main/java/app/revanced/integrations/patches/components/PlaybackSpeedMenuFilterPatch.java +++ b/app/src/main/java/app/revanced/integrations/patches/components/PlaybackSpeedMenuFilterPatch.java @@ -8,7 +8,7 @@ public final class PlaybackSpeedMenuFilterPatch extends Filter { public static volatile boolean isPlaybackSpeedMenuVisible; public PlaybackSpeedMenuFilterPatch() { - pathFilterGroups.addAll(new StringFilterGroup( + pathFilterGroupList.addAll(new StringFilterGroup( null, "playback_speed_sheet_content.eml-js" )); diff --git a/app/src/main/java/app/revanced/integrations/patches/components/PlayerFlyoutMenuItemsFilter.java b/app/src/main/java/app/revanced/integrations/patches/components/PlayerFlyoutMenuItemsFilter.java index 9b627f97..c394f84f 100644 --- a/app/src/main/java/app/revanced/integrations/patches/components/PlayerFlyoutMenuItemsFilter.java +++ b/app/src/main/java/app/revanced/integrations/patches/components/PlayerFlyoutMenuItemsFilter.java @@ -15,7 +15,7 @@ public class PlayerFlyoutMenuItemsFilter extends Filter { @RequiresApi(api = Build.VERSION_CODES.N) public PlayerFlyoutMenuItemsFilter() { - identifierFilterGroups.addAll(new StringFilterGroup(null, "overflow_menu_item.eml|")); + identifierFilterGroupList.addAll(new StringFilterGroup(null, "overflow_menu_item.eml|")); flyoutFilterGroupList.addAll( new ByteArrayAsStringFilterGroup( diff --git a/app/src/main/java/app/revanced/integrations/patches/components/ShortsFilter.java b/app/src/main/java/app/revanced/integrations/patches/components/ShortsFilter.java index fa97f2dd..cfe9ebd3 100644 --- a/app/src/main/java/app/revanced/integrations/patches/components/ShortsFilter.java +++ b/app/src/main/java/app/revanced/integrations/patches/components/ShortsFilter.java @@ -59,8 +59,8 @@ public final class ShortsFilter extends Filter { "shorts_pivot_item" ); - pathFilterGroups.addAll(joinButton, subscribeButton, channelBar, soundButton, infoPanel); - identifierFilterGroups.addAll(shorts, thanksButton); + pathFilterGroupList.addAll(joinButton, subscribeButton, channelBar, soundButton, infoPanel); + identifierFilterGroupList.addAll(shorts, thanksButton); } @Override @@ -69,11 +69,11 @@ public final class ShortsFilter extends Filter { if (matchedGroup == soundButton || matchedGroup == infoPanel || matchedGroup == channelBar) return true; // Filter the path only when reelChannelBar is visible. - if (pathFilterGroups == matchedList) { + if (pathFilterGroupList == matchedList) { return path.contains(REEL_CHANNEL_BAR_PATH); } - return identifierFilterGroups == matchedList; + return identifierFilterGroupList == matchedList; } public static void hideShortsShelf(final View shortsShelfView) { diff --git a/app/src/main/java/app/revanced/integrations/patches/components/VideoQualityMenuFilterPatch.java b/app/src/main/java/app/revanced/integrations/patches/components/VideoQualityMenuFilterPatch.java index 6199effa..8b5d9643 100644 --- a/app/src/main/java/app/revanced/integrations/patches/components/VideoQualityMenuFilterPatch.java +++ b/app/src/main/java/app/revanced/integrations/patches/components/VideoQualityMenuFilterPatch.java @@ -10,7 +10,7 @@ public final class VideoQualityMenuFilterPatch extends Filter { public static volatile boolean isVideoQualityMenuVisible; public VideoQualityMenuFilterPatch() { - pathFilterGroups.addAll(new StringFilterGroup( + pathFilterGroupList.addAll(new StringFilterGroup( SettingsEnum.SHOW_OLD_VIDEO_QUALITY_MENU, "quick_quality_sheet_content.eml-js" )); From 13afac906a0087ea7bc888c80e293f7b05c0a46e Mon Sep 17 00:00:00 2001 From: johnconner122 <107796137+johnconner122@users.noreply.github.com> Date: Wed, 23 Aug 2023 08:35:00 +0500 Subject: [PATCH 15/25] fix(YouTube - Hide Shorts components): Hide `shorts header` (#455) Co-authored-by: LisoUseInAIKyrios <118716522+LisoUseInAIKyrios@users.noreply.github.com> Co-authored-by: oSumAtrIX --- .../patches/components/ShortsFilter.java | 82 +++++++++++-------- 1 file changed, 47 insertions(+), 35 deletions(-) diff --git a/app/src/main/java/app/revanced/integrations/patches/components/ShortsFilter.java b/app/src/main/java/app/revanced/integrations/patches/components/ShortsFilter.java index cfe9ebd3..591221dc 100644 --- a/app/src/main/java/app/revanced/integrations/patches/components/ShortsFilter.java +++ b/app/src/main/java/app/revanced/integrations/patches/components/ShortsFilter.java @@ -12,45 +12,21 @@ import com.google.android.libraries.youtube.rendering.ui.pivotbar.PivotBar; import app.revanced.integrations.settings.SettingsEnum; public final class ShortsFilter extends Filter { - private static final String REEL_CHANNEL_BAR_PATH = "reel_channel_bar"; + private static final String REEL_CHANNEL_BAR_PATH = "reel_channel_bar.eml"; public static PivotBar pivotBar; // Set by patch. private final StringFilterGroup channelBar; private final StringFilterGroup soundButton; private final StringFilterGroup infoPanel; + private final StringFilterGroup shortsShelfHeader; public ShortsFilter() { - channelBar = new StringFilterGroup( - SettingsEnum.HIDE_SHORTS_CHANNEL_BAR, - REEL_CHANNEL_BAR_PATH - ); - - soundButton = new StringFilterGroup( - SettingsEnum.HIDE_SHORTS_SOUND_BUTTON, - "reel_pivot_button" - ); - - infoPanel = new StringFilterGroup( - SettingsEnum.HIDE_SHORTS_INFO_PANEL, - "shorts_info_panel_overview" - ); - - final var thanksButton = new StringFilterGroup( + // Home / subscription feed components. + var thanksButton = new StringFilterGroup( SettingsEnum.HIDE_SHORTS_THANKS_BUTTON, "suggested_action" ); - - final var subscribeButton = new StringFilterGroup( - SettingsEnum.HIDE_SHORTS_SUBSCRIBE_BUTTON, - "subscribe_button" - ); - - final var joinButton = new StringFilterGroup( - SettingsEnum.HIDE_SHORTS_JOIN_BUTTON, - "sponsor_button" - ); - - final var shorts = new StringFilterGroup( + var shorts = new StringFilterGroup( SettingsEnum.HIDE_SHORTS, "shorts_shelf", "inline_shorts", @@ -58,22 +34,58 @@ public final class ShortsFilter extends Filter { "shorts_video_cell", "shorts_pivot_item" ); + // Use a different filter group for this pattern, as it requires an additional check after matching. + shortsShelfHeader = new StringFilterGroup( + SettingsEnum.HIDE_SHORTS, + "shelf_header.eml" + ); + identifierFilterGroups.addAll(shorts, shortsShelfHeader, thanksButton); + + // Shorts player components. + var joinButton = new StringFilterGroup( + SettingsEnum.HIDE_SHORTS_JOIN_BUTTON, + "sponsor_button" + ); + var subscribeButton = new StringFilterGroup( + SettingsEnum.HIDE_SHORTS_SUBSCRIBE_BUTTON, + "subscribe_button" + ); + channelBar = new StringFilterGroup( + SettingsEnum.HIDE_SHORTS_CHANNEL_BAR, + REEL_CHANNEL_BAR_PATH + ); + soundButton = new StringFilterGroup( + SettingsEnum.HIDE_SHORTS_SOUND_BUTTON, + "reel_pivot_button" + ); + infoPanel = new StringFilterGroup( + SettingsEnum.HIDE_SHORTS_INFO_PANEL, + "shorts_info_panel_overview" + ); pathFilterGroupList.addAll(joinButton, subscribeButton, channelBar, soundButton, infoPanel); - identifierFilterGroupList.addAll(shorts, thanksButton); } @Override boolean isFiltered(@Nullable String identifier, String path, byte[] protobufBufferArray, FilterGroupList matchedList, FilterGroup matchedGroup, int matchedIndex) { - if (matchedGroup == soundButton || matchedGroup == infoPanel || matchedGroup == channelBar) return true; + if (matchedList == pathFilterGroupList) { + // Always filter if matched. + if (matchedGroup == soundButton || matchedGroup == infoPanel || matchedGroup == channelBar) + return super.isFiltered(path, identifier, protobufBufferArray, matchedList, matchedGroup, matchedIndex); - // Filter the path only when reelChannelBar is visible. - if (pathFilterGroupList == matchedList) { - return path.contains(REEL_CHANNEL_BAR_PATH); + // Filter other path groups from pathFilterGroupList, only when reelChannelBar is visible + // to avoid false positives. + if (!path.startsWith(REEL_CHANNEL_BAR_PATH)) + return false; + } else if (matchedGroup == shortsShelfHeader) { + // Because the header is used in watch history and possibly other places, check for the index, + // which is 0 when the shelf header is used for Shorts. + if (matchedIndex != 0) return false; } - return identifierFilterGroupList == matchedList; + // Super class handles logging. + return super.isFiltered(path, identifier, protobufBufferArray, matchedList, matchedGroup, matchedIndex); } public static void hideShortsShelf(final View shortsShelfView) { From ebd425b7ff6908384cfb38fc613ce9b5481e3ae1 Mon Sep 17 00:00:00 2001 From: LisoUseInAIKyrios <118716522+LisoUseInAIKyrios@users.noreply.github.com> Date: Wed, 23 Aug 2023 11:27:21 +0400 Subject: [PATCH 16/25] chore: fix build --- .../revanced/integrations/patches/components/ShortsFilter.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/src/main/java/app/revanced/integrations/patches/components/ShortsFilter.java b/app/src/main/java/app/revanced/integrations/patches/components/ShortsFilter.java index 591221dc..b6e1f3fa 100644 --- a/app/src/main/java/app/revanced/integrations/patches/components/ShortsFilter.java +++ b/app/src/main/java/app/revanced/integrations/patches/components/ShortsFilter.java @@ -39,7 +39,7 @@ public final class ShortsFilter extends Filter { SettingsEnum.HIDE_SHORTS, "shelf_header.eml" ); - identifierFilterGroups.addAll(shorts, shortsShelfHeader, thanksButton); + identifierFilterGroupList.addAll(shorts, shortsShelfHeader, thanksButton); // Shorts player components. From fbc6855c4a4c232c770c79f95990513db313c41d Mon Sep 17 00:00:00 2001 From: semantic-release-bot Date: Wed, 23 Aug 2023 07:31:11 +0000 Subject: [PATCH 17/25] chore(release): 0.116.0-dev.6 [skip ci] # [0.116.0-dev.6](https://github.com/ReVanced/revanced-integrations/compare/v0.116.0-dev.5...v0.116.0-dev.6) (2023-08-23) ### Bug Fixes * **YouTube - Hide Shorts components:** Hide `shorts header` ([#455](https://github.com/ReVanced/revanced-integrations/issues/455)) ([13afac9](https://github.com/ReVanced/revanced-integrations/commit/13afac906a0087ea7bc888c80e293f7b05c0a46e)) --- CHANGELOG.md | 7 +++++++ gradle.properties | 2 +- 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index ae59d02e..6621c751 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,10 @@ +# [0.116.0-dev.6](https://github.com/ReVanced/revanced-integrations/compare/v0.116.0-dev.5...v0.116.0-dev.6) (2023-08-23) + + +### Bug Fixes + +* **YouTube - Hide Shorts components:** Hide `shorts header` ([#455](https://github.com/ReVanced/revanced-integrations/issues/455)) ([13afac9](https://github.com/ReVanced/revanced-integrations/commit/13afac906a0087ea7bc888c80e293f7b05c0a46e)) + # [0.116.0-dev.5](https://github.com/ReVanced/revanced-integrations/compare/v0.116.0-dev.4...v0.116.0-dev.5) (2023-08-21) diff --git a/gradle.properties b/gradle.properties index 25e26a35..86daa409 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1,4 +1,4 @@ org.gradle.parallel = true org.gradle.caching = true android.useAndroidX = true -version = 0.116.0-dev.5 +version = 0.116.0-dev.6 From 420b9263b6e1cbb81aeb4985cf454b51912b51f8 Mon Sep 17 00:00:00 2001 From: semantic-release-bot Date: Wed, 23 Aug 2023 07:31:11 +0000 Subject: [PATCH 18/25] fix: use correct logging tags --- .../twitter/patches/hook/twifucker/TwiFucker.kt | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/app/src/main/java/app/revanced/twitter/patches/hook/twifucker/TwiFucker.kt b/app/src/main/java/app/revanced/twitter/patches/hook/twifucker/TwiFucker.kt index 9d47bc8f..5c4323e3 100644 --- a/app/src/main/java/app/revanced/twitter/patches/hook/twifucker/TwiFucker.kt +++ b/app/src/main/java/app/revanced/twitter/patches/hook/twifucker/TwiFucker.kt @@ -22,7 +22,7 @@ internal object TwiFucker { private fun JSONObject.jsonCheckAndRemoveRecommendedUsers() { if (jsonHasRecommendedUsers()) { - Log.d("revanced", "Handle recommended users: $this") + Log.d("ReVanced", "Handle recommended users: $this") jsonRemoveRecommendedUsers() } } @@ -35,7 +35,7 @@ internal object TwiFucker { private fun JSONObject.jsonCheckAndRemoveThreads() { if (jsonHasThreads()) { - Log.d("revabced", "Handle threads: $this") + Log.d("ReVanced", "Handle threads: $this") jsonRemoveThreads() } } @@ -92,7 +92,7 @@ internal object TwiFucker { val trendRemoveIndex = mutableListOf() forEachIndexed { trendIndex, trend -> if (trend.trendHasPromotedMetadata()) { - Log.d("revanced", "Handle trends ads $trendIndex $trend") + Log.d("ReVanced", "Handle trends ads $trendIndex $trend") trendRemoveIndex.add(trendIndex) } } @@ -119,7 +119,7 @@ internal object TwiFucker { entry.entryGetTrends()?.trendRemoveAds() if (entry.entryHasPromotedMetadata()) { - Log.d("revanced", "Handle timeline ads $entryIndex $entry") + Log.d("ReVanced", "Handle timeline ads $entryIndex $entry") removeIndex.add(entryIndex) } @@ -127,7 +127,7 @@ internal object TwiFucker { val contentItems = entry.entryGetContentItems() contentItems?.forEachIndexed inner@{ itemIndex, item -> if (item.entryHasPromotedMetadata()) { - Log.d("revanced", "Handle timeline replies ads $entryIndex $entry") + Log.d("ReVanced", "Handle timeline replies ads $entryIndex $entry") if (contentItems.length() == 1) { removeIndex.add(entryIndex) } else { @@ -150,7 +150,7 @@ internal object TwiFucker { forEachIndexed { entryIndex, entry -> if (entry.entryIsTweetDetailRelatedTweets()) { - Log.d("revanced", "Handle tweet detail related tweets $entryIndex $entry") + Log.d("ReVanced", "Handle tweet detail related tweets $entryIndex $entry") removeIndex.add(entryIndex) } } @@ -180,7 +180,7 @@ internal object TwiFucker { forEachIndexed { entryIndex, entry -> if (!entry.entryIsWhoToFollow()) return@forEachIndexed - Log.d("revanced", "Handle whoToFollow $entryIndex $entry") + Log.d("ReVanced", "Handle whoToFollow $entryIndex $entry") entryRemoveIndex.add(entryIndex) val items = entry.entryGetContentItems() @@ -188,7 +188,7 @@ internal object TwiFucker { items?.forEachIndexed { index, item -> item.itemContainsPromotedUser().let { if (it) { - Log.d("revanced", "Handle whoToFollow promoted user $index $item") + Log.d("ReVanced", "Handle whoToFollow promoted user $index $item") userRemoveIndex.add(index) } } From 61e961a9e313bb6f7ab6c8755e14e8071ab7f309 Mon Sep 17 00:00:00 2001 From: semantic-release-bot Date: Thu, 24 Aug 2023 21:07:32 +0000 Subject: [PATCH 19/25] chore(release): 0.116.0-dev.7 [skip ci] # [0.116.0-dev.7](https://github.com/ReVanced/revanced-integrations/compare/v0.116.0-dev.6...v0.116.0-dev.7) (2023-08-24) ### Bug Fixes * use correct logging tags ([420b926](https://github.com/ReVanced/revanced-integrations/commit/420b9263b6e1cbb81aeb4985cf454b51912b51f8)) --- CHANGELOG.md | 7 +++++++ gradle.properties | 2 +- 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 6621c751..e917c57e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,10 @@ +# [0.116.0-dev.7](https://github.com/ReVanced/revanced-integrations/compare/v0.116.0-dev.6...v0.116.0-dev.7) (2023-08-24) + + +### Bug Fixes + +* use correct logging tags ([420b926](https://github.com/ReVanced/revanced-integrations/commit/420b9263b6e1cbb81aeb4985cf454b51912b51f8)) + # [0.116.0-dev.6](https://github.com/ReVanced/revanced-integrations/compare/v0.116.0-dev.5...v0.116.0-dev.6) (2023-08-23) diff --git a/gradle.properties b/gradle.properties index 86daa409..e312b00f 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1,4 +1,4 @@ org.gradle.parallel = true org.gradle.caching = true android.useAndroidX = true -version = 0.116.0-dev.6 +version = 0.116.0-dev.7 From 44dccb3d658852e92aaf8d17e36dc205f1b3eb05 Mon Sep 17 00:00:00 2001 From: johnconner122 <107796137+johnconner122@users.noreply.github.com> Date: Sat, 26 Aug 2023 05:12:44 +0500 Subject: [PATCH 20/25] feat(YouTube): Add `Custom player overlay opacity` patch (#449) Co-authored-by: LisoUseInAIKyrios <118716522+LisoUseInAIKyrios@users.noreply.github.com> Co-authored-by: oSumAtrIX --- .../CustomPlayerOverlayOpacityPatch.java | 22 ++++++++++++++++ .../patches/HidePlayerOverlayPatch.java | 12 --------- .../integrations/settings/SettingsEnum.java | 25 +++++++------------ 3 files changed, 31 insertions(+), 28 deletions(-) create mode 100644 app/src/main/java/app/revanced/integrations/patches/CustomPlayerOverlayOpacityPatch.java delete mode 100644 app/src/main/java/app/revanced/integrations/patches/HidePlayerOverlayPatch.java diff --git a/app/src/main/java/app/revanced/integrations/patches/CustomPlayerOverlayOpacityPatch.java b/app/src/main/java/app/revanced/integrations/patches/CustomPlayerOverlayOpacityPatch.java new file mode 100644 index 00000000..a2b4a94c --- /dev/null +++ b/app/src/main/java/app/revanced/integrations/patches/CustomPlayerOverlayOpacityPatch.java @@ -0,0 +1,22 @@ +package app.revanced.integrations.patches; + +import android.widget.ImageView; + +import app.revanced.integrations.settings.SettingsEnum; +import app.revanced.integrations.utils.ReVancedUtils; + +public class CustomPlayerOverlayOpacityPatch { + private static final int DEFAULT_OPACITY = (int) SettingsEnum.PLAYER_OVERLAY_OPACITY.defaultValue; + + public static void changeOpacity(ImageView imageView) { + int opacity = SettingsEnum.PLAYER_OVERLAY_OPACITY.getInt(); + + if (opacity < 0 || opacity > 100) { + ReVancedUtils.showToastLong("Player overlay opacity must be between 0-100"); + SettingsEnum.PLAYER_OVERLAY_OPACITY.saveValue(DEFAULT_OPACITY); + opacity = DEFAULT_OPACITY; + } + + imageView.setImageAlpha((opacity * 255) / 100); + } +} diff --git a/app/src/main/java/app/revanced/integrations/patches/HidePlayerOverlayPatch.java b/app/src/main/java/app/revanced/integrations/patches/HidePlayerOverlayPatch.java deleted file mode 100644 index e734fb24..00000000 --- a/app/src/main/java/app/revanced/integrations/patches/HidePlayerOverlayPatch.java +++ /dev/null @@ -1,12 +0,0 @@ -package app.revanced.integrations.patches; - -import android.widget.ImageView; - -import app.revanced.integrations.settings.SettingsEnum; - -public class HidePlayerOverlayPatch { - public static void hidePlayerOverlay(ImageView view) { - if (!SettingsEnum.HIDE_PLAYER_OVERLAY.getBoolean()) return; - view.setImageResource(android.R.color.transparent); - } -} diff --git a/app/src/main/java/app/revanced/integrations/settings/SettingsEnum.java b/app/src/main/java/app/revanced/integrations/settings/SettingsEnum.java index 6a2027b3..51056609 100644 --- a/app/src/main/java/app/revanced/integrations/settings/SettingsEnum.java +++ b/app/src/main/java/app/revanced/integrations/settings/SettingsEnum.java @@ -1,26 +1,12 @@ package app.revanced.integrations.settings; -import static java.lang.Boolean.FALSE; -import static java.lang.Boolean.TRUE; -import static app.revanced.integrations.settings.SettingsEnum.ReturnType.BOOLEAN; -import static app.revanced.integrations.settings.SettingsEnum.ReturnType.FLOAT; -import static app.revanced.integrations.settings.SettingsEnum.ReturnType.INTEGER; -import static app.revanced.integrations.settings.SettingsEnum.ReturnType.LONG; -import static app.revanced.integrations.settings.SettingsEnum.ReturnType.STRING; -import static app.revanced.integrations.settings.SharedPrefCategory.RETURN_YOUTUBE_DISLIKE; -import static app.revanced.integrations.settings.SharedPrefCategory.SPONSOR_BLOCK; -import static app.revanced.integrations.utils.StringRef.str; - import android.content.Context; - import androidx.annotation.NonNull; import androidx.annotation.Nullable; - import app.revanced.integrations.sponsorblock.SponsorBlockSettings; +import app.revanced.integrations.utils.LogHelper; import app.revanced.integrations.utils.ReVancedUtils; import app.revanced.integrations.utils.StringRef; -import app.revanced.integrations.utils.LogHelper; - import org.json.JSONException; import org.json.JSONObject; @@ -29,6 +15,13 @@ import java.util.HashMap; import java.util.Map; import java.util.Objects; +import static app.revanced.integrations.settings.SettingsEnum.ReturnType.*; +import static app.revanced.integrations.settings.SharedPrefCategory.RETURN_YOUTUBE_DISLIKE; +import static app.revanced.integrations.settings.SharedPrefCategory.SPONSOR_BLOCK; +import static app.revanced.integrations.utils.StringRef.str; +import static java.lang.Boolean.FALSE; +import static java.lang.Boolean.TRUE; + public enum SettingsEnum { // External downloader @@ -100,6 +93,7 @@ public enum SettingsEnum { HIDE_SHOP_BUTTON("revanced_hide_shop_button", BOOLEAN, TRUE), // Layout + PLAYER_OVERLAY_OPACITY("revanced_player_overlay_opacity", INTEGER, 100, true), DISABLE_RESUMING_SHORTS_PLAYER("revanced_disable_resuming_shorts_player", BOOLEAN, FALSE), HIDE_ALBUM_CARDS("revanced_hide_album_cards", BOOLEAN, FALSE, true), HIDE_ARTIST_CARDS("revanced_hide_artist_cards", BOOLEAN, FALSE), @@ -120,7 +114,6 @@ public enum SettingsEnum { HIDE_INFO_CARDS("revanced_hide_info_cards", BOOLEAN, TRUE), HIDE_LOAD_MORE_BUTTON("revanced_hide_load_more_button", BOOLEAN, TRUE, true), HIDE_PLAYER_BUTTONS("revanced_hide_player_buttons", BOOLEAN, FALSE), - HIDE_PLAYER_OVERLAY("revanced_hide_player_overlay", BOOLEAN, FALSE, true), HIDE_PREVIEW_COMMENT("revanced_hide_preview_comment", BOOLEAN, FALSE, true), HIDE_SEEKBAR("revanced_hide_seekbar", BOOLEAN, FALSE), HIDE_SEEKBAR_THUMBNAIL("revanced_hide_seekbar_thumbnail", BOOLEAN, FALSE), From aff6e98f64634ca0d2f5d17ce4a4b234bef7d24a Mon Sep 17 00:00:00 2001 From: semantic-release-bot Date: Sat, 26 Aug 2023 00:15:59 +0000 Subject: [PATCH 21/25] chore(release): 0.116.0-dev.8 [skip ci] # [0.116.0-dev.8](https://github.com/ReVanced/revanced-integrations/compare/v0.116.0-dev.7...v0.116.0-dev.8) (2023-08-26) ### Features * **YouTube:** Add `Custom player overlay opacity` patch ([#449](https://github.com/ReVanced/revanced-integrations/issues/449)) ([44dccb3](https://github.com/ReVanced/revanced-integrations/commit/44dccb3d658852e92aaf8d17e36dc205f1b3eb05)) --- CHANGELOG.md | 7 +++++++ gradle.properties | 2 +- 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index e917c57e..4dec3179 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,10 @@ +# [0.116.0-dev.8](https://github.com/ReVanced/revanced-integrations/compare/v0.116.0-dev.7...v0.116.0-dev.8) (2023-08-26) + + +### Features + +* **YouTube:** Add `Custom player overlay opacity` patch ([#449](https://github.com/ReVanced/revanced-integrations/issues/449)) ([44dccb3](https://github.com/ReVanced/revanced-integrations/commit/44dccb3d658852e92aaf8d17e36dc205f1b3eb05)) + # [0.116.0-dev.7](https://github.com/ReVanced/revanced-integrations/compare/v0.116.0-dev.6...v0.116.0-dev.7) (2023-08-24) diff --git a/gradle.properties b/gradle.properties index e312b00f..34c6a4a6 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1,4 +1,4 @@ org.gradle.parallel = true org.gradle.caching = true android.useAndroidX = true -version = 0.116.0-dev.7 +version = 0.116.0-dev.8 From aa9d892cde59c39718529bc40a66b93a1cb78f82 Mon Sep 17 00:00:00 2001 From: oSumAtrIX Date: Sat, 26 Aug 2023 17:00:59 +0200 Subject: [PATCH 22/25] feat(YouTube): Support version 18.32.39 (#464) --- .../patches/OpenLinksExternallyPatch.java | 16 +++++++--------- 1 file changed, 7 insertions(+), 9 deletions(-) diff --git a/app/src/main/java/app/revanced/integrations/patches/OpenLinksExternallyPatch.java b/app/src/main/java/app/revanced/integrations/patches/OpenLinksExternallyPatch.java index 1640c7bf..bc23e1bd 100644 --- a/app/src/main/java/app/revanced/integrations/patches/OpenLinksExternallyPatch.java +++ b/app/src/main/java/app/revanced/integrations/patches/OpenLinksExternallyPatch.java @@ -4,16 +4,14 @@ import app.revanced.integrations.settings.SettingsEnum; public class OpenLinksExternallyPatch { /** - * Override 'android.support.customtabs.action.CustomTabsService', - * in order to open links in the default browser. This is done by returning an empty string, - * for the service that handles custom tabs in the Android support library - * which opens links in the default service instead. + * Return the intent to open links with. If empty, the link will be opened with the default browser. * - * @param original The original custom tabs service. - * @return The new, default service to open links with or the original service. + * @param originalIntent The original intent to open links with. + * @return The intent to open links with. Empty means the link will be opened with the default browser. */ - public static String enableExternalBrowser(String original) { - if (SettingsEnum.EXTERNAL_BROWSER.getBoolean()) original = ""; - return original; + public static String getIntent(String originalIntent) { + if (SettingsEnum.EXTERNAL_BROWSER.getBoolean()) return ""; + + return originalIntent; } } From a249b1648d6131ecf29b09fab38e23cfaea4831d Mon Sep 17 00:00:00 2001 From: semantic-release-bot Date: Sat, 26 Aug 2023 15:05:19 +0000 Subject: [PATCH 23/25] chore(release): 0.116.0-dev.9 [skip ci] # [0.116.0-dev.9](https://github.com/ReVanced/revanced-integrations/compare/v0.116.0-dev.8...v0.116.0-dev.9) (2023-08-26) ### Features * **YouTube:** Support version 18.32.39 ([#464](https://github.com/ReVanced/revanced-integrations/issues/464)) ([aa9d892](https://github.com/ReVanced/revanced-integrations/commit/aa9d892cde59c39718529bc40a66b93a1cb78f82)) --- CHANGELOG.md | 7 +++++++ gradle.properties | 2 +- 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 4dec3179..99b9e37b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,10 @@ +# [0.116.0-dev.9](https://github.com/ReVanced/revanced-integrations/compare/v0.116.0-dev.8...v0.116.0-dev.9) (2023-08-26) + + +### Features + +* **YouTube:** Support version 18.32.39 ([#464](https://github.com/ReVanced/revanced-integrations/issues/464)) ([aa9d892](https://github.com/ReVanced/revanced-integrations/commit/aa9d892cde59c39718529bc40a66b93a1cb78f82)) + # [0.116.0-dev.8](https://github.com/ReVanced/revanced-integrations/compare/v0.116.0-dev.7...v0.116.0-dev.8) (2023-08-26) diff --git a/gradle.properties b/gradle.properties index 34c6a4a6..540ca734 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1,4 +1,4 @@ org.gradle.parallel = true org.gradle.caching = true android.useAndroidX = true -version = 0.116.0-dev.8 +version = 0.116.0-dev.9 From d4570de0a791bd97c70ac46b84cbff75c6d0b274 Mon Sep 17 00:00:00 2001 From: oSumAtrIX Date: Sat, 26 Aug 2023 17:10:20 +0200 Subject: [PATCH 24/25] feat(YouTube): Add `Enable tablet layout` patch --- .../integrations/patches/EnableTabletLayoutPatch.java | 9 +++++++++ .../revanced/integrations/settings/SettingsEnum.java | 11 +---------- 2 files changed, 10 insertions(+), 10 deletions(-) create mode 100644 app/src/main/java/app/revanced/integrations/patches/EnableTabletLayoutPatch.java diff --git a/app/src/main/java/app/revanced/integrations/patches/EnableTabletLayoutPatch.java b/app/src/main/java/app/revanced/integrations/patches/EnableTabletLayoutPatch.java new file mode 100644 index 00000000..638abd23 --- /dev/null +++ b/app/src/main/java/app/revanced/integrations/patches/EnableTabletLayoutPatch.java @@ -0,0 +1,9 @@ +package app.revanced.integrations.patches; + +import app.revanced.integrations.settings.SettingsEnum; + +public final class EnableTabletLayoutPatch { + public static boolean enableTabletLayout() { + return SettingsEnum.TABLET_LAYOUT.getBoolean(); + } +} diff --git a/app/src/main/java/app/revanced/integrations/settings/SettingsEnum.java b/app/src/main/java/app/revanced/integrations/settings/SettingsEnum.java index 51056609..c7e6c432 100644 --- a/app/src/main/java/app/revanced/integrations/settings/SettingsEnum.java +++ b/app/src/main/java/app/revanced/integrations/settings/SettingsEnum.java @@ -127,9 +127,8 @@ public enum SettingsEnum { SPOOF_APP_VERSION("revanced_spoof_app_version", BOOLEAN, FALSE, true, "revanced_spoof_app_version_user_dialog_message"), SPOOF_APP_VERSION_TARGET("revanced_spoof_app_version_target", STRING, "17.08.35", true, parents(SPOOF_APP_VERSION)), USE_TABLET_MINIPLAYER("revanced_tablet_miniplayer", BOOLEAN, FALSE, true), + TABLET_LAYOUT("revanced_tablet_layout", BOOLEAN, FALSE, true, "revanced_tablet_layout_user_dialog_message"), WIDE_SEARCHBAR("revanced_wide_searchbar", BOOLEAN, FALSE, true), - @Deprecated - DEPRECATED_SEEKBAR_COLOR("revanced_seekbar_color", STRING, "#FF0000"), // TODO: delete this SEEKBAR_CUSTOM_COLOR("revanced_seekbar_custom_color", BOOLEAN, TRUE, true), SEEKBAR_CUSTOM_COLOR_VALUE("revanced_seekbar_custom_color_value", STRING, "#FF0000", true, parents(SEEKBAR_CUSTOM_COLOR)), HIDE_FILTER_BAR_FEED_IN_FEED("revanced_hide_filter_bar_feed_in_feed", BOOLEAN, FALSE, true), @@ -377,14 +376,6 @@ public enum SettingsEnum { // TODO: delete DEPRECATED_SHOW_OLD_VIDEO_QUALITY_MENU (When? anytime). migrateOldSettingToNew(DEPRECATED_SHOW_OLD_VIDEO_QUALITY_MENU, SHOW_OLD_VIDEO_QUALITY_MENU); - // TODO: delete this seekbar color migration code - String oldSeekbarColorValue = DEPRECATED_SEEKBAR_COLOR.getString(); - if (!oldSeekbarColorValue.equalsIgnoreCase((String) DEPRECATED_SEEKBAR_COLOR.defaultValue)) { - SEEKBAR_CUSTOM_COLOR_VALUE.saveValue(oldSeekbarColorValue); - SEEKBAR_CUSTOM_COLOR.saveValue(true); - DEPRECATED_SEEKBAR_COLOR.saveValue(DEPRECATED_SEEKBAR_COLOR.defaultValue); - } - // endregion } From acae4b1bc84f74841e68c2d79d760da205823705 Mon Sep 17 00:00:00 2001 From: semantic-release-bot Date: Sat, 26 Aug 2023 15:14:13 +0000 Subject: [PATCH 25/25] chore(release): 0.116.0-dev.10 [skip ci] # [0.116.0-dev.10](https://github.com/ReVanced/revanced-integrations/compare/v0.116.0-dev.9...v0.116.0-dev.10) (2023-08-26) ### Features * **YouTube:** Add `Enable tablet layout` patch ([d4570de](https://github.com/ReVanced/revanced-integrations/commit/d4570de0a791bd97c70ac46b84cbff75c6d0b274)) --- CHANGELOG.md | 7 +++++++ gradle.properties | 2 +- 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 99b9e37b..f768e349 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,10 @@ +# [0.116.0-dev.10](https://github.com/ReVanced/revanced-integrations/compare/v0.116.0-dev.9...v0.116.0-dev.10) (2023-08-26) + + +### Features + +* **YouTube:** Add `Enable tablet layout` patch ([d4570de](https://github.com/ReVanced/revanced-integrations/commit/d4570de0a791bd97c70ac46b84cbff75c6d0b274)) + # [0.116.0-dev.9](https://github.com/ReVanced/revanced-integrations/compare/v0.116.0-dev.8...v0.116.0-dev.9) (2023-08-26) diff --git a/gradle.properties b/gradle.properties index 540ca734..2ea3d552 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1,4 +1,4 @@ org.gradle.parallel = true org.gradle.caching = true android.useAndroidX = true -version = 0.116.0-dev.9 +version = 0.116.0-dev.10