Skip to content

Commit

Permalink
perf: Use KMP to search Shorts video id (#109)
Browse files Browse the repository at this point in the history
* ci: workflow to ping Discord users when patches are released (#72)

* init: Workflow to notify discord users of releases

* Rename workflow

* chore (Background playback): Shorten description

* Revert "chore (Background playback): Shorten description"

This reverts commit 10661b8.

* Change message contents

* perf: Use KMP to search Shorts videoId

* Beautify

* Incorrect key-value

* Incorrect key-value

* Rename variable

* Comment

* Comment

* Comment

* Comment

* Beauty

* fix: video id is not updated in some case

* fix build error

* chore: Simplify

---------

Co-authored-by: KobeW50 <84587632+KobeW50@users.noreply.github.com>
Co-authored-by: inotia00 <108592928+inotia00@users.noreply.github.com>
  • Loading branch information
3 people authored Jan 20, 2025
1 parent 6bb067f commit a6f8596
Show file tree
Hide file tree
Showing 2 changed files with 40 additions and 18 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@
import androidx.annotation.Nullable;

import java.util.LinkedHashMap;
import java.util.LinkedHashSet;
import java.util.Map;

import app.revanced.extension.shared.patches.components.ByteArrayFilterGroup;
Expand Down Expand Up @@ -36,11 +35,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,8 +101,11 @@ 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)) {
Logger.printDebug(() -> "New Shorts video id: " + videoId);
// Put a placeholder first
lastVideoIds.put(videoId, null);
lastVideoIds.put(videoId, new ByteArrayFilterGroup(null, videoId));
}
}
} catch (Exception ex) {
Expand All @@ -114,7 +117,12 @@ public static void newPlayerResponseVideoId(String videoId, boolean isShortAndOp
* 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) {
private static boolean byteArrayContainsString(@NonNull byte[] array, @NonNull String text,
@Nullable ByteArrayFilterGroup videoIdFilter) {
// If a video filter is available, check it first.
if (videoIdFilter != null) {
return videoIdFilter.check(array).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++) {
Expand Down Expand Up @@ -154,12 +162,14 @@ 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)) {
for (Map.Entry<String, ByteArrayFilterGroup> entry : lastVideoIds.entrySet()) {
final String videoId = entry.getKey();
final ByteArrayFilterGroup videoIdFilter = entry.getValue();
if (byteArrayContainsString(protobufBufferArray, videoId, videoIdFilter)) {
return videoId;
}
}

return null;
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@
import androidx.annotation.Nullable;

import java.util.LinkedHashMap;
import java.util.LinkedHashSet;
import java.util.Map;

import app.revanced.extension.shared.patches.components.ByteArrayFilterGroup;
Expand All @@ -30,11 +29,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,7 +117,11 @@ public static void newPlayerResponseVideoId(String videoId, boolean isShortAndOp
return;
}
synchronized (lastVideoIds) {
lastVideoIds.putIfAbsent(videoId, Boolean.TRUE);
if (!lastVideoIds.containsKey(videoId)) {
// Put a placeholder first
lastVideoIds.put(videoId, null);
lastVideoIds.put(videoId, new ByteArrayFilterGroup(null, videoId));
}
}
} catch (Exception ex) {
Logger.printException(() -> "newPlayerResponseVideoId failure", ex);
Expand All @@ -129,7 +133,12 @@ public static void newPlayerResponseVideoId(String videoId, boolean isShortAndOp
* 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) {
private static boolean byteArrayContainsString(@NonNull byte[] array, @NonNull String text,
@Nullable ByteArrayFilterGroup videoIdFilter) {
// If a video filter is available, check it first.
if (videoIdFilter != null) {
return videoIdFilter.check(array).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++) {
Expand Down Expand Up @@ -164,9 +173,12 @@ 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 String videoId = entry.getKey();
final ByteArrayFilterGroup videoIdFilter = entry.getValue();
if (byteArrayContainsString(protobufBufferArray, videoId, videoIdFilter)) {
setShortsVideoId(videoId, false);
return;
}
}
}
Expand Down

0 comments on commit a6f8596

Please sign in to comment.