Skip to content

Commit

Permalink
perf: Use KMP to search Shorts videoId
Browse files Browse the repository at this point in the history
  • Loading branch information
YT-Advanced committed Jan 10, 2025
1 parent 2bed142 commit 8bdf785
Show file tree
Hide file tree
Showing 2 changed files with 29 additions and 59 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -36,11 +36,12 @@
public final class ReturnYouTubeDislikeFilterPatch extends Filter {

/**
* Last unique video id's loaded. Value is ignored and Map is treated as a Set.
* Cannot use {@link LinkedHashSet} because it's missing #removeEldestEntry().
* Last unique video id's loaded.
* Key is a String represeting the video id.
* Value is a ByteArrayFilterGroup used for performing KMP pattern searching.
*/
@GuardedBy("itself")
private static final Map<String, Boolean> lastVideoIds = new LinkedHashMap<>() {
private static final Map<String, ByteArrayFilterGroup> lastVideoIds = new LinkedHashMap<>() {
/**
* Number of video id's to keep track of for searching thru the buffer.
* A minimum value of 3 should be sufficient, but check a few more just in case.
Expand Down Expand Up @@ -101,36 +102,17 @@ public static void newPlayerResponseVideoId(String videoId, boolean isShortAndOp
return;
}
synchronized (lastVideoIds) {
if (lastVideoIds.put(videoId, Boolean.TRUE) == null) {
Logger.printDebug(() -> "New Short video id: " + videoId);
}
if (lastVideoIds.containsKey(videoId)) return;
Logger.printDebug(() -> "New Shorts video id: " + videoId);

final ByteArrayFilterGroup videoIdFilter = new ByteArrayFilterGroup(null, videoId);
lastVideoIds.put(videoId, videoIdFilter);
}
} catch (Exception ex) {
Logger.printException(() -> "newPlayerResponseVideoId failure", ex);
}
}

/**
* This could use {@link TrieSearch}, but since the patterns are constantly changing
* the overhead of updating the Trie might negate the search performance gain.
*/
private static boolean byteArrayContainsString(@NonNull byte[] array, @NonNull String text) {
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) {
Expand All @@ -154,13 +136,15 @@ public boolean isFiltered(String path, @Nullable String identifier, String allVa
@Nullable
private String findVideoId(byte[] protobufBufferArray) {
synchronized (lastVideoIds) {
for (String videoId : lastVideoIds.keySet()) {
if (byteArrayContainsString(protobufBufferArray, videoId)) {
return videoId;
for (Map.Entry<String, ByteArrayFilterGroup> entry : lastVideoIds.entrySet()) {
final ByteArrayFilterGroup videoIdFilter = entry.getValue();

if (videoIdFilter.check(protobufBufferArray).isFiltered()) {
return entry.getKey(); // Return videoId
}
}

return null;
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -30,11 +30,12 @@ public final class ShortsCustomActionsFilter extends Filter {
SHORTS_CUSTOM_ACTIONS_FLYOUT_MENU_ENABLED || SHORTS_CUSTOM_ACTIONS_TOOLBAR_ENABLED;

/**
* Last unique video id's loaded. Value is ignored and Map is treated as a Set.
* Cannot use {@link LinkedHashSet} because it's missing #removeEldestEntry().
* Last unique video id's loaded.
* Key is a String represeting the video id.
* Value is a ByteArrayFilterGroup used for performing KMP pattern searching.
*/
@GuardedBy("itself")
private static final Map<String, Boolean> lastVideoIds = new LinkedHashMap<>() {
private static final Map<String, ByteArrayFilterGroup> lastVideoIds = new LinkedHashMap<>() {
/**
* Number of video id's to keep track of for searching thru the buffer.
* A minimum value of 3 should be sufficient, but check a few more just in case.
Expand Down Expand Up @@ -117,35 +118,16 @@ public static void newPlayerResponseVideoId(String videoId, boolean isShortAndOp
return;
}
synchronized (lastVideoIds) {
lastVideoIds.putIfAbsent(videoId, Boolean.TRUE);
if (lastVideoIds.containsKey(videoId)) return;

final ByteArrayFilterGroup videoIdFilter = new ByteArrayFilterGroup(null, videoId);
lastVideoIds.put(videoId, videoIdFilter);
}
} catch (Exception ex) {
Logger.printException(() -> "newPlayerResponseVideoId failure", ex);
}
}


/**
* This could use {@link TrieSearch}, but since the patterns are constantly changing
* the overhead of updating the Trie might negate the search performance gain.
*/
private static boolean byteArrayContainsString(@NonNull byte[] array, @NonNull String text) {
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) {
Expand All @@ -164,9 +146,13 @@ public boolean isFiltered(String path, @Nullable String identifier, String allVa

private void findVideoId(byte[] protobufBufferArray) {
synchronized (lastVideoIds) {
for (String videoId : lastVideoIds.keySet()) {
if (byteArrayContainsString(protobufBufferArray, videoId)) {
for (Map.Entry<String, ByteArrayFilterGroup> entry : lastVideoIds.entrySet()) {
final ByteArrayFilterGroup videoIdFilter = entry.getValue();

if (videoIdFilter.check(protobufBufferArray).isFiltered()) {
final String videoId = entry.getKey();
setShortsVideoId(videoId, false);
return;
}
}
}
Expand Down

0 comments on commit 8bdf785

Please sign in to comment.