diff --git a/extensions/shared/src/main/java/app/revanced/extension/youtube/patches/components/ReturnYouTubeDislikeFilterPatch.java b/extensions/shared/src/main/java/app/revanced/extension/youtube/patches/components/ReturnYouTubeDislikeFilterPatch.java index cd87cfc760..2a66b8f064 100644 --- a/extensions/shared/src/main/java/app/revanced/extension/youtube/patches/components/ReturnYouTubeDislikeFilterPatch.java +++ b/extensions/shared/src/main/java/app/revanced/extension/youtube/patches/components/ReturnYouTubeDislikeFilterPatch.java @@ -104,6 +104,8 @@ public static void newPlayerResponseVideoId(String videoId, boolean isShortAndOp synchronized (lastVideoIds) { if (lastVideoIds.containsKey(videoId)) return; Logger.printDebug(() -> "New Shorts video id: " + videoId); + // Put a placeholder first + lastVideoIds.put(videoId, null); final ByteArrayFilterGroup videoIdFilter = new ByteArrayFilterGroup(null, videoId); lastVideoIds.put(videoId, videoIdFilter); @@ -113,6 +115,31 @@ public static void newPlayerResponseVideoId(String videoId, boolean isShortAndOp } } + /** + * Do pattern search to find the videoId in O(n + m) + * If the filter group haven't built yet, fallback to linear search: O(n * m) + */ + private static boolean byteArrayContainsString(@NonNull byte[] array, @NonNull String text, + @Nullable ByteArrayFilterGroup videoIdFilter) { + if (videoIdFilter != null) { // Use pattern search if possible + return videoIdFilter.check(protobufBufferArray).isFiltered(); + } + for (int i = 0, lastArrayStartIndex = array.length - text.length(); i <= lastArrayStartIndex; i++) { + boolean found = true; + for (int j = 0, textLength = text.length(); j < textLength; j++) { + if (array[i + j] != (byte) text.charAt(j)) { + found = false; + break; + } + } + if (found) { + return true; + } + } + + return false; + } + @Override public boolean isFiltered(String path, @Nullable String identifier, String allValue, byte[] protobufBufferArray, StringFilterGroup matchedGroup, FilterContentType contentType, int contentIndex) { @@ -137,14 +164,14 @@ public boolean isFiltered(String path, @Nullable String identifier, String allVa private String findVideoId(byte[] protobufBufferArray) { synchronized (lastVideoIds) { for (Map.Entry entry : lastVideoIds.entrySet()) { + final String videoId = entry.getKey(); final ByteArrayFilterGroup videoIdFilter = entry.getValue(); - - if (videoIdFilter.check(protobufBufferArray).isFiltered()) { - return entry.getKey(); // Return videoId + if (byteArrayContainsString(protobufBufferArray, videoId, videoIdFilter)) { + return videoId; } } - - return null; } + + return null; } } diff --git a/extensions/shared/src/main/java/app/revanced/extension/youtube/patches/components/ShortsCustomActionsFilter.java b/extensions/shared/src/main/java/app/revanced/extension/youtube/patches/components/ShortsCustomActionsFilter.java index 2725f881fb..eb3cbb351f 100644 --- a/extensions/shared/src/main/java/app/revanced/extension/youtube/patches/components/ShortsCustomActionsFilter.java +++ b/extensions/shared/src/main/java/app/revanced/extension/youtube/patches/components/ShortsCustomActionsFilter.java @@ -116,6 +116,8 @@ public static void newPlayerResponseVideoId(String videoId, boolean isShortAndOp } synchronized (lastVideoIds) { if (lastVideoIds.containsKey(videoId)) return; + // Put a placeholder first + lastVideoIds.put(videoId, null); final ByteArrayFilterGroup videoIdFilter = new ByteArrayFilterGroup(null, videoId); lastVideoIds.put(videoId, videoIdFilter); @@ -125,6 +127,31 @@ public static void newPlayerResponseVideoId(String videoId, boolean isShortAndOp } } + /** + * Do pattern search to find the videoId in O(n + m) + * If the filter group haven't built yet, fallback to linear search: O(n * m) + */ + private static boolean byteArrayContainsString(@NonNull byte[] array, @NonNull String text, + @Nullable ByteArrayFilterGroup videoIdFilter) { + if (videoIdFilter != null) { // Use pattern search if possible + return videoIdFilter.check(protobufBufferArray).isFiltered(); + } + for (int i = 0, lastArrayStartIndex = array.length - text.length(); i <= lastArrayStartIndex; i++) { + boolean found = true; + for (int j = 0, textLength = text.length(); j < textLength; j++) { + if (array[i + j] != (byte) text.charAt(j)) { + found = false; + break; + } + } + if (found) { + return true; + } + } + + return false; + } + @Override public boolean isFiltered(String path, @Nullable String identifier, String allValue, byte[] protobufBufferArray, StringFilterGroup matchedGroup, FilterContentType contentType, int contentIndex) { @@ -144,10 +171,9 @@ public boolean isFiltered(String path, @Nullable String identifier, String allVa private void findVideoId(byte[] protobufBufferArray) { synchronized (lastVideoIds) { for (Map.Entry entry : lastVideoIds.entrySet()) { + final String videoId = entry.getKey(); final ByteArrayFilterGroup videoIdFilter = entry.getValue(); - - if (videoIdFilter.check(protobufBufferArray).isFiltered()) { - final String videoId = entry.getKey(); + if (byteArrayContainsString(protobufBufferArray, videoId, videoIdFilter)) { setShortsVideoId(videoId, false); return; }