diff --git a/app/src/main/java/app/revanced/integrations/youtube/patches/spoof/ClientType.java b/app/src/main/java/app/revanced/integrations/youtube/patches/spoof/ClientType.java
new file mode 100644
index 0000000000..e99d5dc6b8
--- /dev/null
+++ b/app/src/main/java/app/revanced/integrations/youtube/patches/spoof/ClientType.java
@@ -0,0 +1,77 @@
+package app.revanced.integrations.youtube.patches.spoof;
+
+import static app.revanced.integrations.youtube.patches.spoof.DeviceHardwareSupport.allowAV1;
+import static app.revanced.integrations.youtube.patches.spoof.DeviceHardwareSupport.allowVP9;
+
+import android.os.Build;
+
+import app.revanced.integrations.shared.Utils;
+
+public enum ClientType {
+ // https://dumps.tadiphone.dev/dumps/oculus/eureka
+ IOS(5,
+ // iPhone 15 supports AV1 hardware decoding.
+ // Only use if this Android device also has hardware decoding.
+ allowAV1()
+ ? "iPhone16,2" // 15 Pro Max
+ : "iPhone11,4", // XS Max
+ // iOS 14+ forces VP9.
+ allowVP9()
+ ? "17.5.1.21F90"
+ : "13.7.17H35",
+ allowVP9()
+ ? "com.google.ios.youtube/19.10.7 (iPhone; U; CPU iOS 17_5_1 like Mac OS X)"
+ : "com.google.ios.youtube/19.10.7 (iPhone; U; CPU iOS 13_7 like Mac OS X)",
+ // Version number should be a valid iOS release.
+ // https://www.ipa4fun.com/history/185230
+ "19.10.7"
+ ),
+ ANDROID_VR(28,
+ "Quest 3",
+ "12",
+ "com.google.android.apps.youtube.vr.oculus/1.56.21 (Linux; U; Android 12; GB) gzip",
+ "1.56.21"
+ ),
+ @Deprecated() // Android spoofing in this context no longer works.
+ ANDROID(3,
+ Build.MODEL,
+ Build.VERSION.RELEASE,
+ String.format("com.google.android.youtube/%s (Linux; U; Android %s; GB) gzip",
+ Utils.getAppVersionName(), Build.VERSION.RELEASE),
+ Utils.getAppVersionName()
+ );
+
+ /**
+ * YouTube
+ * client type
+ */
+ public final int id;
+
+ /**
+ * Device model, equivalent to {@link Build#MODEL} (System property: ro.product.model)
+ */
+ public final String model;
+
+ /**
+ * Device OS version.
+ */
+ public final String osVersion;
+
+ /**
+ * Player user-agent.
+ */
+ public final String userAgent;
+
+ /**
+ * App version.
+ */
+ public final String appVersion;
+
+ ClientType(int id, String model, String osVersion, String userAgent, String appVersion) {
+ this.id = id;
+ this.model = model;
+ this.osVersion = osVersion;
+ this.userAgent = userAgent;
+ this.appVersion = appVersion;
+ }
+}
diff --git a/app/src/main/java/app/revanced/integrations/youtube/patches/spoof/DeviceHardwareSupport.java b/app/src/main/java/app/revanced/integrations/youtube/patches/spoof/DeviceHardwareSupport.java
new file mode 100644
index 0000000000..3d2cb7911c
--- /dev/null
+++ b/app/src/main/java/app/revanced/integrations/youtube/patches/spoof/DeviceHardwareSupport.java
@@ -0,0 +1,63 @@
+package app.revanced.integrations.youtube.patches.spoof;
+
+import android.media.MediaCodecInfo;
+import android.media.MediaCodecList;
+import android.os.Build;
+
+import app.revanced.integrations.shared.Logger;
+import app.revanced.integrations.youtube.settings.Settings;
+
+public class DeviceHardwareSupport {
+ private static final boolean DEVICE_HAS_HARDWARE_DECODING_VP9 = deviceHasVP9HardwareDecoding();
+ private static final boolean DEVICE_HAS_HARDWARE_DECODING_AV1 = deviceHasAV1HardwareDecoding();
+
+ private static boolean deviceHasVP9HardwareDecoding() {
+ MediaCodecList codecList = new MediaCodecList(MediaCodecList.ALL_CODECS);
+
+ for (MediaCodecInfo codecInfo : codecList.getCodecInfos()) {
+ final boolean isHardwareAccelerated = (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q)
+ ? codecInfo.isHardwareAccelerated()
+ : !codecInfo.getName().startsWith("OMX.google"); // Software decoder.
+ if (isHardwareAccelerated && !codecInfo.isEncoder()) {
+ for (String type : codecInfo.getSupportedTypes()) {
+ if (type.equalsIgnoreCase("video/x-vnd.on2.vp9")) {
+ Logger.printDebug(() -> "Device supports VP9 hardware decoding.");
+ return true;
+ }
+ }
+ }
+ }
+
+ Logger.printDebug(() -> "Device does not support VP9 hardware decoding.");
+ return false;
+ }
+
+ private static boolean deviceHasAV1HardwareDecoding() {
+ // It appears all devices with hardware AV1 are also Android 10 or newer.
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
+ MediaCodecList codecList = new MediaCodecList(MediaCodecList.ALL_CODECS);
+
+ for (MediaCodecInfo codecInfo : codecList.getCodecInfos()) {
+ if (codecInfo.isHardwareAccelerated() && !codecInfo.isEncoder()) {
+ for (String type : codecInfo.getSupportedTypes()) {
+ if (type.equalsIgnoreCase("video/av01")) {
+ Logger.printDebug(() -> "Device supports AV1 hardware decoding.");
+ return true;
+ }
+ }
+ }
+ }
+ }
+
+ Logger.printDebug(() -> "Device does not support AV1 hardware decoding.");
+ return false;
+ }
+
+ public static boolean allowVP9() {
+ return DEVICE_HAS_HARDWARE_DECODING_VP9 && !Settings.SPOOF_CLIENT_FORCE_AVC.get();
+ }
+
+ public static boolean allowAV1() {
+ return allowVP9() && DEVICE_HAS_HARDWARE_DECODING_AV1;
+ }
+}
diff --git a/app/src/main/java/app/revanced/integrations/youtube/patches/spoof/SpoofClientPatch.java b/app/src/main/java/app/revanced/integrations/youtube/patches/spoof/SpoofClientPatch.java
index e8746c18da..faadb781f7 100644
--- a/app/src/main/java/app/revanced/integrations/youtube/patches/spoof/SpoofClientPatch.java
+++ b/app/src/main/java/app/revanced/integrations/youtube/patches/spoof/SpoofClientPatch.java
@@ -1,12 +1,6 @@
package app.revanced.integrations.youtube.patches.spoof;
-import static app.revanced.integrations.youtube.patches.spoof.SpoofClientPatch.DeviceHardwareSupport.allowAV1;
-import static app.revanced.integrations.youtube.patches.spoof.SpoofClientPatch.DeviceHardwareSupport.allowVP9;
-
-import android.media.MediaCodecInfo;
-import android.media.MediaCodecList;
import android.net.Uri;
-import android.os.Build;
import androidx.annotation.Nullable;
@@ -29,10 +23,10 @@
@SuppressWarnings("unused")
public class SpoofClientPatch {
private static final boolean SPOOF_CLIENT = Settings.SPOOF_CLIENT.get();
- private static final ClientType SPOOF_CLIENT_TYPE = Settings.SPOOF_CLIENT_TYPE.get();
- private static final boolean SPOOF_IOS = SPOOF_CLIENT && SPOOF_CLIENT_TYPE == ClientType.IOS;
-
- private static final boolean SPOOF_STREAM = Settings.SPOOF_STREAM.get();
+ private static final SpoofClientStrategy SPOOF_STRATEGY = Settings.SPOOF_CLIENT_STRATEGY.get();
+ @Nullable
+ private static final ClientType SPOOF_CLIENT_TYPE = SPOOF_CLIENT ? null : SPOOF_STRATEGY.clientType;
+ private static final boolean SPOOF_STREAM = SPOOF_CLIENT && SPOOF_STRATEGY == SpoofClientStrategy.REPLACE_STREAMS;
/**
* Any unreachable ip address. Used to intentionally fail requests.
@@ -50,7 +44,7 @@ public class SpoofClientPatch {
* @return An unreachable URI if the request is a /get_watch request, otherwise the original URI.
*/
public static Uri blockGetWatchRequest(Uri playerRequestUri) {
- if (SPOOF_CLIENT || SPOOF_STREAM) {
+ if (SPOOF_CLIENT) {
try {
String path = playerRequestUri.getPath();
@@ -73,7 +67,7 @@ public static Uri blockGetWatchRequest(Uri playerRequestUri) {
* Blocks /initplayback requests.
*/
public static String blockInitPlaybackRequest(String originalUrlString) {
- if (SPOOF_CLIENT || SPOOF_STREAM) {
+ if (SPOOF_CLIENT) {
try {
var originalUri = Uri.parse(originalUrlString);
String path = originalUri.getPath();
@@ -94,51 +88,51 @@ public static String blockInitPlaybackRequest(String originalUrlString) {
/**
* Injection point.
*/
- public static int getClientTypeId(int originalClientTypeId) {
- return SPOOF_CLIENT ? SPOOF_CLIENT_TYPE.id : originalClientTypeId;
+ public static boolean isClientTypeSpoofingEnabled() {
+ return SPOOF_CLIENT_TYPE != null;
}
/**
* Injection point.
*/
- public static String getClientVersion(String originalClientVersion) {
- return SPOOF_CLIENT ? SPOOF_CLIENT_TYPE.appVersion : originalClientVersion;
+ public static boolean isSpoofStreamEnabled() {
+ return SPOOF_STREAM;
}
/**
* Injection point.
*/
- public static String getClientModel(String originalClientModel) {
- return SPOOF_CLIENT ? SPOOF_CLIENT_TYPE.model : originalClientModel;
+ public static int getClientTypeId(int originalClientTypeId) {
+ return SPOOF_CLIENT_TYPE != null ? SPOOF_CLIENT_TYPE.id : originalClientTypeId;
}
/**
* Injection point.
- * Fix video qualities missing, if spoofing to iOS by using the correct client OS version.
*/
- public static String getOsVersion(String originalOsVersion) {
- return SPOOF_CLIENT ? SPOOF_CLIENT_TYPE.osVersion : originalOsVersion;
+ public static String getClientVersion(String originalClientVersion) {
+ return SPOOF_CLIENT_TYPE != null ? SPOOF_CLIENT_TYPE.appVersion : originalClientVersion;
}
/**
* Injection point.
*/
- public static boolean enablePlayerGesture(boolean original) {
- return SPOOF_CLIENT || original;
+ public static String getClientModel(String originalClientModel) {
+ return SPOOF_CLIENT_TYPE != null ? SPOOF_CLIENT_TYPE.model : originalClientModel;
}
/**
* Injection point.
+ * Fix video qualities missing, if spoofing to iOS by using the correct client OS version.
*/
- public static boolean isClientSpoofingEnabled() {
- return SPOOF_CLIENT;
+ public static String getOsVersion(String originalOsVersion) {
+ return SPOOF_CLIENT_TYPE != null ? SPOOF_CLIENT_TYPE.osVersion : originalOsVersion;
}
/**
* Injection point.
*/
- public static boolean isSpoofStreamEnabled() {
- return SPOOF_STREAM;
+ public static boolean enablePlayerGesture(boolean original) {
+ return SPOOF_STRATEGY.enablePlayerGesture || original;
}
/**
@@ -147,7 +141,7 @@ public static boolean isSpoofStreamEnabled() {
* Return true to force create the playback speed menu.
*/
public static boolean forceCreatePlaybackSpeedMenu(boolean original) {
- return SPOOF_IOS || original;
+ return SPOOF_STRATEGY.forceCreatePlaybackSpeedMenu || original;
}
/**
@@ -156,7 +150,7 @@ public static boolean forceCreatePlaybackSpeedMenu(boolean original) {
* Return true to force enable audio background play.
*/
public static boolean overrideBackgroundAudioPlayback() {
- return SPOOF_IOS && BackgroundPlaybackPatch.playbackIsNotShort();
+ return SPOOF_STRATEGY.overrideBackgroundAudioPlayback && BackgroundPlaybackPatch.playbackIsNotShort();
}
/**
@@ -164,14 +158,15 @@ public static boolean overrideBackgroundAudioPlayback() {
*/
public static ExperimentalUrlRequest overrideUserAgent(ExperimentalUrlRequest.Builder builder,
String url, Map playerHeaders) {
- if (SPOOF_CLIENT || SPOOF_STREAM) {
+ if (SPOOF_CLIENT) {
Uri uri = Uri.parse(url);
String path = uri.getPath();
if (path != null && path.contains("player") && !path.contains("heartbeat")) {
- if (SPOOF_CLIENT) {
+ if (SPOOF_CLIENT_TYPE != null) {
Logger.printDebug(() -> "Overriding user agent for /player call");
builder.addHeader("User-Agent", SPOOF_CLIENT_TYPE.userAgent);
} else {
+ // Spoof stream.
String videoId = uri.getQueryParameter("id");
currentVideoStream = StreamingDataRequester.fetch(videoId, playerHeaders);
}
@@ -240,136 +235,43 @@ public static byte[] removeVideoPlaybackPostBody(Uri uri, int method, byte[] pos
return postData;
}
- // Must check for device features in a separate class and cannot place this code inside
- // the Patch or ClientType enum due to cyclic Setting references.
- static class DeviceHardwareSupport {
- private static final boolean DEVICE_HAS_HARDWARE_DECODING_VP9 = deviceHasVP9HardwareDecoding();
- private static final boolean DEVICE_HAS_HARDWARE_DECODING_AV1 = deviceHasAV1HardwareDecoding();
-
- private static boolean deviceHasVP9HardwareDecoding() {
- MediaCodecList codecList = new MediaCodecList(MediaCodecList.ALL_CODECS);
-
- for (MediaCodecInfo codecInfo : codecList.getCodecInfos()) {
- final boolean isHardwareAccelerated = (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q)
- ? codecInfo.isHardwareAccelerated()
- : !codecInfo.getName().startsWith("OMX.google"); // Software decoder.
- if (isHardwareAccelerated && !codecInfo.isEncoder()) {
- for (String type : codecInfo.getSupportedTypes()) {
- if (type.equalsIgnoreCase("video/x-vnd.on2.vp9")) {
- Logger.printDebug(() -> "Device supports VP9 hardware decoding.");
- return true;
- }
- }
- }
- }
-
- Logger.printDebug(() -> "Device does not support VP9 hardware decoding.");
- return false;
- }
-
- private static boolean deviceHasAV1HardwareDecoding() {
- // It appears all devices with hardware AV1 are also Android 10 or newer.
- if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
- MediaCodecList codecList = new MediaCodecList(MediaCodecList.ALL_CODECS);
-
- for (MediaCodecInfo codecInfo : codecList.getCodecInfos()) {
- if (codecInfo.isHardwareAccelerated() && !codecInfo.isEncoder()) {
- for (String type : codecInfo.getSupportedTypes()) {
- if (type.equalsIgnoreCase("video/av01")) {
- Logger.printDebug(() -> "Device supports AV1 hardware decoding.");
- return true;
- }
- }
- }
- }
- }
-
- Logger.printDebug(() -> "Device does not support AV1 hardware decoding.");
- return false;
- }
-
- static boolean allowVP9() {
- return DEVICE_HAS_HARDWARE_DECODING_VP9 && !Settings.SPOOF_CLIENT_IOS_FORCE_AVC.get();
- }
-
- static boolean allowAV1() {
- return allowVP9() && DEVICE_HAS_HARDWARE_DECODING_AV1;
+ public enum SpoofClientStrategy {
+ REPLACE_STREAMS(null,
+ false,
+ false,
+ false),
+ CLIENT_IOS(ClientType.IOS,
+ true,
+ true,
+ true),
+ CLIENT_ANDROID_VR(ClientType.ANDROID_VR,
+ false,
+ false,
+ false);
+
+ private final ClientType clientType;
+ private final boolean forceCreatePlaybackSpeedMenu;
+ private final boolean overrideBackgroundAudioPlayback;
+ private final boolean enablePlayerGesture;
+
+ SpoofClientStrategy(ClientType clientType,
+ boolean forceCreatePlaybackSpeedMenu,
+ boolean overrideBackgroundAudioPlayback,
+ boolean enablePlayerGesture) {
+ this.clientType = clientType;
+ this.forceCreatePlaybackSpeedMenu = forceCreatePlaybackSpeedMenu;
+ this.overrideBackgroundAudioPlayback = overrideBackgroundAudioPlayback;
+ this.enablePlayerGesture = enablePlayerGesture;
}
}
- public enum ClientType {
- // https://dumps.tadiphone.dev/dumps/oculus/eureka
- IOS(5,
- // iPhone 15 supports AV1 hardware decoding.
- // Only use if this Android device also has hardware decoding.
- allowAV1()
- ? "iPhone16,2" // 15 Pro Max
- : "iPhone11,4", // XS Max
- // iOS 14+ forces VP9.
- allowVP9()
- ? "17.5.1.21F90"
- : "13.7.17H35",
- allowVP9()
- ? "com.google.ios.youtube/19.10.7 (iPhone; U; CPU iOS 17_5_1 like Mac OS X)"
- : "com.google.ios.youtube/19.10.7 (iPhone; U; CPU iOS 13_7 like Mac OS X)",
- // Version number should be a valid iOS release.
- // https://www.ipa4fun.com/history/185230
- "19.10.7"
- ),
- ANDROID_VR(28,
- "Quest 3",
- "12",
- "com.google.android.apps.youtube.vr.oculus/1.56.21 (Linux; U; Android 12; GB) gzip",
- "1.56.21"
- ),
- ANDROID(3,
- Build.MODEL,
- Build.VERSION.RELEASE,
- String.format("com.google.android.youtube/%s (Linux; U; Android %s; GB) gzip",
- Utils.getAppVersionName(), Build.VERSION.RELEASE),
- Utils.getAppVersionName()
- );
-
- /**
- * YouTube
- * client type
- */
- public final int id;
-
- /**
- * Device model, equivalent to {@link Build#MODEL} (System property: ro.product.model)
- */
- public final String model;
-
- /**
- * Device OS version.
- */
- public final String osVersion;
-
- /**
- * Player user-agent.
- */
- public final String userAgent;
-
- /**
- * App version.
- */
- public final String appVersion;
-
- ClientType(int id, String model, String osVersion, String userAgent, String appVersion) {
- this.id = id;
- this.model = model;
- this.osVersion = osVersion;
- this.userAgent = userAgent;
- this.appVersion = appVersion;
- }
- }
-
- public static final class ForceiOSAVCAvailability implements Setting.Availability {
+ public static final class ForceAVCAvailability implements Setting.Availability {
@Override
public boolean isAvailable() {
- return (Settings.SPOOF_CLIENT.get() && Settings.SPOOF_CLIENT_TYPE.get() == ClientType.IOS)
- || Settings.SPOOF_STREAM.get();
+ if (!Settings.SPOOF_CLIENT.get()) return false;
+
+ SpoofClientStrategy strategy = Settings.SPOOF_CLIENT_STRATEGY.get();
+ return strategy == SpoofClientStrategy.CLIENT_IOS || strategy == SpoofClientStrategy.REPLACE_STREAMS;
}
}
}
diff --git a/app/src/main/java/app/revanced/integrations/youtube/patches/spoof/requests/PlayerRoutes.java b/app/src/main/java/app/revanced/integrations/youtube/patches/spoof/requests/PlayerRoutes.java
index fd0977d8a5..b3c590d6ef 100644
--- a/app/src/main/java/app/revanced/integrations/youtube/patches/spoof/requests/PlayerRoutes.java
+++ b/app/src/main/java/app/revanced/integrations/youtube/patches/spoof/requests/PlayerRoutes.java
@@ -1,6 +1,6 @@
package app.revanced.integrations.youtube.patches.spoof.requests;
-import app.revanced.integrations.youtube.patches.spoof.SpoofClientPatch.ClientType;
+import app.revanced.integrations.youtube.patches.spoof.ClientType;
import app.revanced.integrations.youtube.requests.Requester;
import app.revanced.integrations.youtube.requests.Route;
import app.revanced.integrations.shared.Logger;
diff --git a/app/src/main/java/app/revanced/integrations/youtube/patches/spoof/requests/StoryboardRendererRequester.java b/app/src/main/java/app/revanced/integrations/youtube/patches/spoof/requests/StoryboardRendererRequester.java
index 200d5fb0d1..b43f040f35 100644
--- a/app/src/main/java/app/revanced/integrations/youtube/patches/spoof/requests/StoryboardRendererRequester.java
+++ b/app/src/main/java/app/revanced/integrations/youtube/patches/spoof/requests/StoryboardRendererRequester.java
@@ -3,8 +3,8 @@
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import app.revanced.integrations.shared.settings.BaseSettings;
+import app.revanced.integrations.youtube.patches.spoof.ClientType;
import app.revanced.integrations.youtube.patches.spoof.StoryboardRenderer;
-import app.revanced.integrations.youtube.patches.spoof.SpoofClientPatch.ClientType;
import app.revanced.integrations.youtube.requests.Requester;
import app.revanced.integrations.shared.Logger;
import app.revanced.integrations.shared.Utils;
diff --git a/app/src/main/java/app/revanced/integrations/youtube/patches/spoof/requests/StreamingDataRequester.java b/app/src/main/java/app/revanced/integrations/youtube/patches/spoof/requests/StreamingDataRequester.java
index e00b333006..e5e102a900 100644
--- a/app/src/main/java/app/revanced/integrations/youtube/patches/spoof/requests/StreamingDataRequester.java
+++ b/app/src/main/java/app/revanced/integrations/youtube/patches/spoof/requests/StreamingDataRequester.java
@@ -20,7 +20,7 @@
import app.revanced.integrations.shared.Logger;
import app.revanced.integrations.shared.Utils;
import app.revanced.integrations.shared.settings.BaseSettings;
-import app.revanced.integrations.youtube.patches.spoof.SpoofClientPatch.ClientType;
+import app.revanced.integrations.youtube.patches.spoof.ClientType;
public class StreamingDataRequester {
diff --git a/app/src/main/java/app/revanced/integrations/youtube/settings/Settings.java b/app/src/main/java/app/revanced/integrations/youtube/settings/Settings.java
index 077811523c..d99517f306 100644
--- a/app/src/main/java/app/revanced/integrations/youtube/settings/Settings.java
+++ b/app/src/main/java/app/revanced/integrations/youtube/settings/Settings.java
@@ -6,7 +6,7 @@
import static app.revanced.integrations.youtube.patches.MiniplayerPatch.MiniplayerType;
import static app.revanced.integrations.youtube.patches.MiniplayerPatch.MiniplayerType.MODERN_1;
import static app.revanced.integrations.youtube.patches.MiniplayerPatch.MiniplayerType.MODERN_3;
-import static app.revanced.integrations.youtube.patches.spoof.SpoofClientPatch.ClientType;
+import static app.revanced.integrations.youtube.patches.spoof.SpoofClientPatch.SpoofClientStrategy;
import static app.revanced.integrations.youtube.sponsorblock.objects.CategoryBehaviour.*;
import java.util.Arrays;
@@ -256,9 +256,9 @@ public class Settings extends BaseSettings {
public static final BooleanSetting BYPASS_URL_REDIRECTS = new BooleanSetting("revanced_bypass_url_redirects", TRUE);
public static final BooleanSetting ANNOUNCEMENTS = new BooleanSetting("revanced_announcements", TRUE);
public static final BooleanSetting SPOOF_CLIENT = new BooleanSetting("revanced_spoof_client", TRUE, true,"revanced_spoof_client_user_dialog_message");
- public static final BooleanSetting SPOOF_CLIENT_IOS_FORCE_AVC = new BooleanSetting("revanced_spoof_client_ios_force_avc", FALSE, true,
- "revanced_spoof_client_ios_force_avc_user_dialog_message", new SpoofClientPatch.ForceiOSAVCAvailability());
- public static final EnumSetting SPOOF_CLIENT_TYPE = new EnumSetting<>("revanced_spoof_client_type", ClientType.IOS, true, parent(SPOOF_CLIENT));
+ public static final BooleanSetting SPOOF_CLIENT_FORCE_AVC = new BooleanSetting("revanced_spoof_client_force_avc", FALSE, true,
+ "revanced_spoof_client_force_avc_user_dialog_message", new SpoofClientPatch.ForceAVCAvailability());
+ public static final EnumSetting SPOOF_CLIENT_STRATEGY = new EnumSetting<>("revanced_spoof_client_strategy", SpoofClientStrategy.REPLACE_STREAMS, true, parent(SPOOF_CLIENT));
public static final BooleanSetting SPOOF_STREAM = new BooleanSetting("revanced_spoof_stream", FALSE, true);
@Deprecated
public static final StringSetting DEPRECATED_ANNOUNCEMENT_LAST_HASH = new StringSetting("revanced_announcement_last_hash", "");