mirror of
https://github.com/ReVanced/revanced-patches.git
synced 2026-01-11 13:46:17 +00:00
fix(YouTube - Spoof video streams): Do not use Android Creator for livestreams
This commit is contained in:
@@ -1,6 +1,4 @@
|
|||||||
package app.revanced.extension.youtube;
|
package app.revanced.extension.shared;
|
||||||
|
|
||||||
import androidx.annotation.NonNull;
|
|
||||||
|
|
||||||
import java.nio.charset.StandardCharsets;
|
import java.nio.charset.StandardCharsets;
|
||||||
|
|
||||||
@@ -39,7 +37,7 @@ public final class ByteTrieSearch extends TrieSearch<byte[]> {
|
|||||||
return replacement;
|
return replacement;
|
||||||
}
|
}
|
||||||
|
|
||||||
public ByteTrieSearch(@NonNull byte[]... patterns) {
|
public ByteTrieSearch(byte[]... patterns) {
|
||||||
super(new ByteTrieNode(), patterns);
|
super(new ByteTrieNode(), patterns);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1,6 +1,4 @@
|
|||||||
package app.revanced.extension.youtube;
|
package app.revanced.extension.shared;
|
||||||
|
|
||||||
import androidx.annotation.NonNull;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Text pattern searching using a prefix tree (trie).
|
* Text pattern searching using a prefix tree (trie).
|
||||||
@@ -28,7 +26,7 @@ public final class StringTrieSearch extends TrieSearch<String> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public StringTrieSearch(@NonNull String... patterns) {
|
public StringTrieSearch(String... patterns) {
|
||||||
super(new StringTrieNode(), patterns);
|
super(new StringTrieNode(), patterns);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1,6 +1,5 @@
|
|||||||
package app.revanced.extension.youtube;
|
package app.revanced.extension.shared;
|
||||||
|
|
||||||
import androidx.annotation.NonNull;
|
|
||||||
import androidx.annotation.Nullable;
|
import androidx.annotation.Nullable;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
@@ -8,8 +7,7 @@ import java.util.Collections;
|
|||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Objects;
|
import java.util.Objects;
|
||||||
|
|
||||||
/**
|
/**Searches for a group of different patterns using a trie (prefix tree).
|
||||||
* Searches for a group of different patterns using a trie (prefix tree).
|
|
||||||
* Can significantly speed up searching for multiple patterns.
|
* Can significantly speed up searching for multiple patterns.
|
||||||
*/
|
*/
|
||||||
public abstract class TrieSearch<T> {
|
public abstract class TrieSearch<T> {
|
||||||
@@ -136,7 +134,7 @@ public abstract class TrieSearch<T> {
|
|||||||
* @param patternLength Length of the pattern.
|
* @param patternLength Length of the pattern.
|
||||||
* @param callback Callback, where a value of NULL indicates to always accept a pattern match.
|
* @param callback Callback, where a value of NULL indicates to always accept a pattern match.
|
||||||
*/
|
*/
|
||||||
private void addPattern(@NonNull T pattern, int patternIndex, int patternLength,
|
private void addPattern(T pattern, int patternIndex, int patternLength,
|
||||||
@Nullable TriePatternMatchedCallback<T> callback) {
|
@Nullable TriePatternMatchedCallback<T> callback) {
|
||||||
if (patternIndex == patternLength) { // Reached the end of the pattern.
|
if (patternIndex == patternLength) { // Reached the end of the pattern.
|
||||||
if (endOfPatternCallback == null) {
|
if (endOfPatternCallback == null) {
|
||||||
@@ -308,13 +306,13 @@ public abstract class TrieSearch<T> {
|
|||||||
private final List<T> patterns = new ArrayList<>();
|
private final List<T> patterns = new ArrayList<>();
|
||||||
|
|
||||||
@SafeVarargs
|
@SafeVarargs
|
||||||
TrieSearch(@NonNull TrieNode<T> root, @NonNull T... patterns) {
|
TrieSearch(TrieNode<T> root, T... patterns) {
|
||||||
this.root = Objects.requireNonNull(root);
|
this.root = Objects.requireNonNull(root);
|
||||||
addPatterns(patterns);
|
addPatterns(patterns);
|
||||||
}
|
}
|
||||||
|
|
||||||
@SafeVarargs
|
@SafeVarargs
|
||||||
public final void addPatterns(@NonNull T... patterns) {
|
public final void addPatterns(T... patterns) {
|
||||||
for (T pattern : patterns) {
|
for (T pattern : patterns) {
|
||||||
addPattern(pattern);
|
addPattern(pattern);
|
||||||
}
|
}
|
||||||
@@ -325,7 +323,7 @@ public abstract class TrieSearch<T> {
|
|||||||
*
|
*
|
||||||
* @param pattern Pattern to add. Calling this with a zero length pattern does nothing.
|
* @param pattern Pattern to add. Calling this with a zero length pattern does nothing.
|
||||||
*/
|
*/
|
||||||
public void addPattern(@NonNull T pattern) {
|
public void addPattern(T pattern) {
|
||||||
addPattern(pattern, root.getTextLength(pattern), null);
|
addPattern(pattern, root.getTextLength(pattern), null);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -333,31 +331,31 @@ public abstract class TrieSearch<T> {
|
|||||||
* @param pattern Pattern to add. Calling this with a zero length pattern does nothing.
|
* @param pattern Pattern to add. Calling this with a zero length pattern does nothing.
|
||||||
* @param callback Callback to determine if searching should halt when a match is found.
|
* @param callback Callback to determine if searching should halt when a match is found.
|
||||||
*/
|
*/
|
||||||
public void addPattern(@NonNull T pattern, @NonNull TriePatternMatchedCallback<T> callback) {
|
public void addPattern(T pattern, TriePatternMatchedCallback<T> callback) {
|
||||||
addPattern(pattern, root.getTextLength(pattern), Objects.requireNonNull(callback));
|
addPattern(pattern, root.getTextLength(pattern), Objects.requireNonNull(callback));
|
||||||
}
|
}
|
||||||
|
|
||||||
void addPattern(@NonNull T pattern, int patternLength, @Nullable TriePatternMatchedCallback<T> callback) {
|
void addPattern(T pattern, int patternLength, @Nullable TriePatternMatchedCallback<T> callback) {
|
||||||
if (patternLength == 0) return; // Nothing to match
|
if (patternLength == 0) return; // Nothing to match
|
||||||
|
|
||||||
patterns.add(pattern);
|
patterns.add(pattern);
|
||||||
root.addPattern(pattern, 0, patternLength, callback);
|
root.addPattern(pattern, 0, patternLength, callback);
|
||||||
}
|
}
|
||||||
|
|
||||||
public final boolean matches(@NonNull T textToSearch) {
|
public final boolean matches(T textToSearch) {
|
||||||
return matches(textToSearch, 0);
|
return matches(textToSearch, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean matches(@NonNull T textToSearch, @NonNull Object callbackParameter) {
|
public boolean matches(T textToSearch, Object callbackParameter) {
|
||||||
return matches(textToSearch, 0, root.getTextLength(textToSearch),
|
return matches(textToSearch, 0, root.getTextLength(textToSearch),
|
||||||
Objects.requireNonNull(callbackParameter));
|
Objects.requireNonNull(callbackParameter));
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean matches(@NonNull T textToSearch, int startIndex) {
|
public boolean matches(T textToSearch, int startIndex) {
|
||||||
return matches(textToSearch, startIndex, root.getTextLength(textToSearch));
|
return matches(textToSearch, startIndex, root.getTextLength(textToSearch));
|
||||||
}
|
}
|
||||||
|
|
||||||
public final boolean matches(@NonNull T textToSearch, int startIndex, int endIndex) {
|
public final boolean matches(T textToSearch, int startIndex, int endIndex) {
|
||||||
return matches(textToSearch, startIndex, endIndex, null);
|
return matches(textToSearch, startIndex, endIndex, null);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -370,11 +368,11 @@ public abstract class TrieSearch<T> {
|
|||||||
* @param callbackParameter Optional parameter passed to the callbacks.
|
* @param callbackParameter Optional parameter passed to the callbacks.
|
||||||
* @return If any pattern matched, and it's callback halted searching.
|
* @return If any pattern matched, and it's callback halted searching.
|
||||||
*/
|
*/
|
||||||
public boolean matches(@NonNull T textToSearch, int startIndex, int endIndex, @Nullable Object callbackParameter) {
|
public boolean matches(T textToSearch, int startIndex, int endIndex, @Nullable Object callbackParameter) {
|
||||||
return matches(textToSearch, root.getTextLength(textToSearch), startIndex, endIndex, callbackParameter);
|
return matches(textToSearch, root.getTextLength(textToSearch), startIndex, endIndex, callbackParameter);
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean matches(@NonNull T textToSearch, int textToSearchLength, int startIndex, int endIndex,
|
private boolean matches(T textToSearch, int textToSearchLength, int startIndex, int endIndex,
|
||||||
@Nullable Object callbackParameter) {
|
@Nullable Object callbackParameter) {
|
||||||
if (endIndex > textToSearchLength) {
|
if (endIndex > textToSearchLength) {
|
||||||
throw new IllegalArgumentException("endIndex: " + endIndex
|
throw new IllegalArgumentException("endIndex: " + endIndex
|
||||||
@@ -1,5 +1,6 @@
|
|||||||
package app.revanced.extension.shared.spoof.requests;
|
package app.revanced.extension.shared.spoof.requests;
|
||||||
|
|
||||||
|
import static app.revanced.extension.shared.ByteTrieSearch.convertStringsToBytes;
|
||||||
import static app.revanced.extension.shared.spoof.requests.PlayerRoutes.GET_STREAMING_DATA;
|
import static app.revanced.extension.shared.spoof.requests.PlayerRoutes.GET_STREAMING_DATA;
|
||||||
|
|
||||||
import androidx.annotation.NonNull;
|
import androidx.annotation.NonNull;
|
||||||
@@ -13,12 +14,18 @@ import java.net.HttpURLConnection;
|
|||||||
import java.net.SocketTimeoutException;
|
import java.net.SocketTimeoutException;
|
||||||
import java.nio.ByteBuffer;
|
import java.nio.ByteBuffer;
|
||||||
import java.nio.charset.StandardCharsets;
|
import java.nio.charset.StandardCharsets;
|
||||||
import java.util.*;
|
import java.util.Arrays;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.LinkedHashMap;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.Objects;
|
||||||
import java.util.concurrent.ExecutionException;
|
import java.util.concurrent.ExecutionException;
|
||||||
import java.util.concurrent.Future;
|
import java.util.concurrent.Future;
|
||||||
import java.util.concurrent.TimeUnit;
|
import java.util.concurrent.TimeUnit;
|
||||||
import java.util.concurrent.TimeoutException;
|
import java.util.concurrent.TimeoutException;
|
||||||
|
|
||||||
|
import app.revanced.extension.shared.ByteTrieSearch;
|
||||||
import app.revanced.extension.shared.Logger;
|
import app.revanced.extension.shared.Logger;
|
||||||
import app.revanced.extension.shared.Utils;
|
import app.revanced.extension.shared.Utils;
|
||||||
import app.revanced.extension.shared.settings.BaseSettings;
|
import app.revanced.extension.shared.settings.BaseSettings;
|
||||||
@@ -93,6 +100,16 @@ public class StreamingDataRequest {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Strings found in the response if the video is a livestream.
|
||||||
|
*/
|
||||||
|
private static final ByteTrieSearch liveStreamBufferSearch = new ByteTrieSearch(
|
||||||
|
convertStringsToBytes(
|
||||||
|
"yt_live_broadcast",
|
||||||
|
"yt_premiere_broadcast"
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
private static volatile ClientType lastSpoofedClientType;
|
private static volatile ClientType lastSpoofedClientType;
|
||||||
|
|
||||||
public static String getLastSpoofedClientName() {
|
public static String getLastSpoofedClientName() {
|
||||||
@@ -221,9 +238,13 @@ public class StreamingDataRequest {
|
|||||||
while ((bytesRead = inputStream.read(buffer)) >= 0) {
|
while ((bytesRead = inputStream.read(buffer)) >= 0) {
|
||||||
baos.write(buffer, 0, bytesRead);
|
baos.write(buffer, 0, bytesRead);
|
||||||
}
|
}
|
||||||
lastSpoofedClientType = clientType;
|
if (clientType == ClientType.ANDROID_CREATOR && liveStreamBufferSearch.matches(buffer)) {
|
||||||
|
Logger.printDebug(() -> "Skipping Android Studio as video is a livestream: " + videoId);
|
||||||
|
} else {
|
||||||
|
lastSpoofedClientType = clientType;
|
||||||
|
|
||||||
return ByteBuffer.wrap(baos.toByteArray());
|
return ByteBuffer.wrap(baos.toByteArray());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} catch (IOException ex) {
|
} catch (IOException ex) {
|
||||||
|
|||||||
@@ -10,7 +10,7 @@ import java.util.List;
|
|||||||
|
|
||||||
import app.revanced.extension.shared.Logger;
|
import app.revanced.extension.shared.Logger;
|
||||||
import app.revanced.extension.shared.Utils;
|
import app.revanced.extension.shared.Utils;
|
||||||
import app.revanced.extension.youtube.StringTrieSearch;
|
import app.revanced.extension.shared.StringTrieSearch;
|
||||||
import app.revanced.extension.youtube.settings.Settings;
|
import app.revanced.extension.youtube.settings.Settings;
|
||||||
|
|
||||||
@SuppressWarnings("unused")
|
@SuppressWarnings("unused")
|
||||||
|
|||||||
@@ -14,7 +14,7 @@ import java.util.regex.Pattern;
|
|||||||
|
|
||||||
import app.revanced.extension.shared.Logger;
|
import app.revanced.extension.shared.Logger;
|
||||||
import app.revanced.extension.shared.Utils;
|
import app.revanced.extension.shared.Utils;
|
||||||
import app.revanced.extension.youtube.ByteTrieSearch;
|
import app.revanced.extension.shared.ByteTrieSearch;
|
||||||
import app.revanced.extension.youtube.settings.Settings;
|
import app.revanced.extension.youtube.settings.Settings;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
package app.revanced.extension.youtube.patches.components;
|
package app.revanced.extension.youtube.patches.components;
|
||||||
|
|
||||||
import app.revanced.extension.youtube.StringTrieSearch;
|
import app.revanced.extension.shared.StringTrieSearch;
|
||||||
import app.revanced.extension.youtube.settings.Settings;
|
import app.revanced.extension.youtube.settings.Settings;
|
||||||
import app.revanced.extension.youtube.shared.PlayerType;
|
import app.revanced.extension.youtube.shared.PlayerType;
|
||||||
|
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ import androidx.annotation.NonNull;
|
|||||||
|
|
||||||
import app.revanced.extension.shared.Logger;
|
import app.revanced.extension.shared.Logger;
|
||||||
import app.revanced.extension.shared.settings.BooleanSetting;
|
import app.revanced.extension.shared.settings.BooleanSetting;
|
||||||
import app.revanced.extension.youtube.ByteTrieSearch;
|
import app.revanced.extension.shared.ByteTrieSearch;
|
||||||
|
|
||||||
abstract class FilterGroup<T> {
|
abstract class FilterGroup<T> {
|
||||||
final static class FilterGroupResult {
|
final static class FilterGroupResult {
|
||||||
|
|||||||
@@ -5,9 +5,9 @@ import androidx.annotation.NonNull;
|
|||||||
import java.util.*;
|
import java.util.*;
|
||||||
import java.util.function.Consumer;
|
import java.util.function.Consumer;
|
||||||
|
|
||||||
import app.revanced.extension.youtube.ByteTrieSearch;
|
import app.revanced.extension.shared.ByteTrieSearch;
|
||||||
import app.revanced.extension.youtube.StringTrieSearch;
|
import app.revanced.extension.shared.StringTrieSearch;
|
||||||
import app.revanced.extension.youtube.TrieSearch;
|
import app.revanced.extension.shared.TrieSearch;
|
||||||
|
|
||||||
abstract class FilterGroupList<V, T extends FilterGroup<V>> implements Iterable<T> {
|
abstract class FilterGroupList<V, T extends FilterGroup<V>> implements Iterable<T> {
|
||||||
|
|
||||||
|
|||||||
@@ -14,9 +14,9 @@ import java.util.concurrent.atomic.AtomicReference;
|
|||||||
|
|
||||||
import app.revanced.extension.shared.Logger;
|
import app.revanced.extension.shared.Logger;
|
||||||
import app.revanced.extension.shared.Utils;
|
import app.revanced.extension.shared.Utils;
|
||||||
import app.revanced.extension.youtube.ByteTrieSearch;
|
import app.revanced.extension.shared.ByteTrieSearch;
|
||||||
import app.revanced.extension.youtube.StringTrieSearch;
|
import app.revanced.extension.shared.StringTrieSearch;
|
||||||
import app.revanced.extension.youtube.TrieSearch;
|
import app.revanced.extension.shared.TrieSearch;
|
||||||
import app.revanced.extension.youtube.settings.Settings;
|
import app.revanced.extension.youtube.settings.Settings;
|
||||||
import app.revanced.extension.youtube.shared.NavigationBar;
|
import app.revanced.extension.youtube.shared.NavigationBar;
|
||||||
import app.revanced.extension.youtube.shared.PlayerType;
|
import app.revanced.extension.youtube.shared.PlayerType;
|
||||||
|
|||||||
@@ -10,7 +10,7 @@ import androidx.annotation.Nullable;
|
|||||||
|
|
||||||
import app.revanced.extension.shared.Logger;
|
import app.revanced.extension.shared.Logger;
|
||||||
import app.revanced.extension.shared.Utils;
|
import app.revanced.extension.shared.Utils;
|
||||||
import app.revanced.extension.youtube.StringTrieSearch;
|
import app.revanced.extension.shared.StringTrieSearch;
|
||||||
import app.revanced.extension.youtube.patches.ChangeHeaderPatch;
|
import app.revanced.extension.youtube.patches.ChangeHeaderPatch;
|
||||||
import app.revanced.extension.youtube.settings.Settings;
|
import app.revanced.extension.youtube.settings.Settings;
|
||||||
import app.revanced.extension.youtube.shared.NavigationBar;
|
import app.revanced.extension.youtube.shared.NavigationBar;
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ import java.util.List;
|
|||||||
|
|
||||||
import app.revanced.extension.shared.Logger;
|
import app.revanced.extension.shared.Logger;
|
||||||
import app.revanced.extension.shared.settings.BaseSettings;
|
import app.revanced.extension.shared.settings.BaseSettings;
|
||||||
import app.revanced.extension.youtube.StringTrieSearch;
|
import app.revanced.extension.shared.StringTrieSearch;
|
||||||
import app.revanced.extension.youtube.settings.Settings;
|
import app.revanced.extension.youtube.settings.Settings;
|
||||||
|
|
||||||
@SuppressWarnings("unused")
|
@SuppressWarnings("unused")
|
||||||
|
|||||||
@@ -12,7 +12,7 @@ import app.revanced.extension.youtube.patches.ReturnYouTubeDislikePatch;
|
|||||||
import app.revanced.extension.youtube.patches.VideoInformation;
|
import app.revanced.extension.youtube.patches.VideoInformation;
|
||||||
import app.revanced.extension.youtube.settings.Settings;
|
import app.revanced.extension.youtube.settings.Settings;
|
||||||
import app.revanced.extension.shared.Logger;
|
import app.revanced.extension.shared.Logger;
|
||||||
import app.revanced.extension.youtube.TrieSearch;
|
import app.revanced.extension.shared.TrieSearch;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Searches for video id's in the proto buffer of Shorts dislike.
|
* Searches for video id's in the proto buffer of Shorts dislike.
|
||||||
|
|||||||
@@ -22,11 +22,9 @@ public class SpoofVideoStreamsPatch {
|
|||||||
List<ClientType> availableClients = List.of(
|
List<ClientType> availableClients = List.of(
|
||||||
ANDROID_VR_1_61_48,
|
ANDROID_VR_1_61_48,
|
||||||
VISIONOS,
|
VISIONOS,
|
||||||
IPADOS,
|
|
||||||
// Creator must be next to last, because livestreams fetch successfully but don't playback.
|
|
||||||
ANDROID_CREATOR,
|
ANDROID_CREATOR,
|
||||||
// VR 1.43 must be last as spoof streaming data handles it slightly differently.
|
ANDROID_VR_1_43_32,
|
||||||
ANDROID_VR_1_43_32
|
IPADOS
|
||||||
);
|
);
|
||||||
|
|
||||||
StreamingDataRequest.setClientOrderToUse(availableClients,
|
StreamingDataRequest.setClientOrderToUse(availableClients,
|
||||||
|
|||||||
Reference in New Issue
Block a user