diff --git a/extensions/shared/src/main/java/app/revanced/extension/music/patches/misc/SpoofClientPatch.java b/extensions/shared/src/main/java/app/revanced/extension/music/patches/misc/SpoofClientPatch.java
index 4cbd401a3..19c5b6935 100644
--- a/extensions/shared/src/main/java/app/revanced/extension/music/patches/misc/SpoofClientPatch.java
+++ b/extensions/shared/src/main/java/app/revanced/extension/music/patches/misc/SpoofClientPatch.java
@@ -1,49 +1,19 @@
package app.revanced.extension.music.patches.misc;
+import app.revanced.extension.music.patches.misc.client.AppClient.ClientType;
+import app.revanced.extension.music.settings.Settings;
import app.revanced.extension.shared.patches.BlockRequestPatch;
@SuppressWarnings("unused")
public class SpoofClientPatch extends BlockRequestPatch {
- private static final int CLIENT_ID_IOS_MUSIC = 26;
-
- /**
- * 1. {@link BlockRequestPatch} is not required, as the litho component is not applied to the video action bar and flyout menu.
- * 2. Audio codec is MP4A.
- */
- private static final String CLIENT_VERSION_IOS_MUSIC_6_21 = "6.21";
-
- /**
- * 1. {@link BlockRequestPatch} is required, as the layout used in iOS should be prevented from being applied to the video action bar and flyout menu.
- * 2. Audio codec is OPUS.
- */
- private static final String CLIENT_VERSION_IOS_MUSIC_7_31 = "7.31.2";
-
- /**
- * Starting with YouTube Music 7.17.51+, the litho component has been applied to the video action bar.
- *
- * So if {@code CLIENT_VERSION_IOS_MUSIC_6_21} is used in YouTube Music 7.17.51+,
- * the video action bar will not load properly.
- */
- private static final String CLIENT_VERSION_IOS_MUSIC = IS_7_17_OR_GREATER
- ? CLIENT_VERSION_IOS_MUSIC_7_31
- : CLIENT_VERSION_IOS_MUSIC_6_21;
- private static final String DEVICE_MODEL_IOS_MUSIC = "iPhone16,2";
- private static final String OS_VERSION_IOS_MUSIC = "17.7.2.21H221";
- private static final String USER_AGENT_VERSION_IOS_MUSIC = "17_7_2";
- private static final String USER_AGENT_IOS_MUSIC = "com.google.ios.youtubemusic/" +
- CLIENT_VERSION_IOS_MUSIC +
- "(" +
- DEVICE_MODEL_IOS_MUSIC +
- "; U; CPU iOS " +
- USER_AGENT_VERSION_IOS_MUSIC +
- " like Mac OS X)";
+ private static final ClientType CLIENT_TYPE = Settings.SPOOF_CLIENT_TYPE.get();
/**
* Injection point.
*/
public static int getClientTypeId(int originalClientTypeId) {
if (SPOOF_CLIENT) {
- return CLIENT_ID_IOS_MUSIC;
+ return CLIENT_TYPE.id;
}
return originalClientTypeId;
@@ -54,7 +24,7 @@ public class SpoofClientPatch extends BlockRequestPatch {
*/
public static String getClientVersion(String originalClientVersion) {
if (SPOOF_CLIENT) {
- return CLIENT_VERSION_IOS_MUSIC;
+ return CLIENT_TYPE.clientVersion;
}
return originalClientVersion;
@@ -65,7 +35,7 @@ public class SpoofClientPatch extends BlockRequestPatch {
*/
public static String getClientModel(String originalClientModel) {
if (SPOOF_CLIENT) {
- return DEVICE_MODEL_IOS_MUSIC;
+ return CLIENT_TYPE.deviceModel;
}
return originalClientModel;
@@ -76,7 +46,7 @@ public class SpoofClientPatch extends BlockRequestPatch {
*/
public static String getOsVersion(String originalOsVersion) {
if (SPOOF_CLIENT) {
- return OS_VERSION_IOS_MUSIC;
+ return CLIENT_TYPE.osVersion;
}
return originalOsVersion;
@@ -87,7 +57,7 @@ public class SpoofClientPatch extends BlockRequestPatch {
*/
public static String getUserAgent(String originalUserAgent) {
if (SPOOF_CLIENT) {
- return USER_AGENT_IOS_MUSIC;
+ return CLIENT_TYPE.userAgent;
}
return originalUserAgent;
diff --git a/extensions/shared/src/main/java/app/revanced/extension/music/patches/misc/client/AppClient.java b/extensions/shared/src/main/java/app/revanced/extension/music/patches/misc/client/AppClient.java
new file mode 100644
index 000000000..0a21f6196
--- /dev/null
+++ b/extensions/shared/src/main/java/app/revanced/extension/music/patches/misc/client/AppClient.java
@@ -0,0 +1,89 @@
+package app.revanced.extension.music.patches.misc.client;
+
+import android.os.Build;
+
+import app.revanced.extension.music.settings.Settings;
+
+public class AppClient {
+ private static final String CLIENT_VERSION_ANDROID_MUSIC = Settings.SPOOF_CLIENT_LEGACY.get()
+ ? "4.27.53" // Audio codec is MP4A.
+ : "5.29.53"; // Audio codec is OPUS.
+ private static final String OS_VERSION_ANDROID_MUSIC = Build.VERSION.RELEASE;
+ private static final String USER_AGENT_ANDROID_MUSIC = "com.google.android.apps.youtube.music/" +
+ CLIENT_VERSION_ANDROID_MUSIC +
+ " (Linux; U; Android " +
+ OS_VERSION_ANDROID_MUSIC +
+ "; GB) gzip";
+
+ private static final String CLIENT_VERSION_IOS_MUSIC = Settings.SPOOF_CLIENT_LEGACY.get()
+ ? "4.27" // Audio codec is MP4A.
+ : "7.31.2"; // Audio codec is OPUS.
+ private static final String DEVICE_MODEL_IOS_MUSIC = "iPhone14,3";
+ private static final String OS_VERSION_IOS_MUSIC = "15.7.1.19H117";
+ private static final String USER_AGENT_VERSION_IOS_MUSIC = "15_7_1";
+ private static final String USER_AGENT_IOS_MUSIC = "com.google.ios.youtubemusic/" +
+ CLIENT_VERSION_IOS_MUSIC +
+ "(" +
+ DEVICE_MODEL_IOS_MUSIC +
+ "; U; CPU iOS " +
+ USER_AGENT_VERSION_IOS_MUSIC +
+ " like Mac OS X)";
+
+ private AppClient() {
+ }
+
+ public enum ClientType {
+ ANDROID_MUSIC(21,
+ Build.MODEL,
+ OS_VERSION_ANDROID_MUSIC,
+ USER_AGENT_ANDROID_MUSIC,
+ CLIENT_VERSION_ANDROID_MUSIC
+ ),
+ IOS_MUSIC(
+ 26,
+ DEVICE_MODEL_IOS_MUSIC,
+ OS_VERSION_IOS_MUSIC,
+ USER_AGENT_IOS_MUSIC,
+ CLIENT_VERSION_IOS_MUSIC
+ );
+
+ /**
+ * YouTube
+ * client type
+ */
+ public final int id;
+
+ /**
+ * Device model, equivalent to {@link Build#MODEL} (System property: ro.product.model)
+ */
+ public final String deviceModel;
+
+ /**
+ * Device OS version.
+ */
+ public final String osVersion;
+
+ /**
+ * Player user-agent.
+ */
+ public final String userAgent;
+
+ /**
+ * App version.
+ */
+ public final String clientVersion;
+
+ ClientType(int id,
+ String deviceModel,
+ String osVersion,
+ String userAgent,
+ String clientVersion
+ ) {
+ this.id = id;
+ this.deviceModel = deviceModel;
+ this.clientVersion = clientVersion;
+ this.osVersion = osVersion;
+ this.userAgent = userAgent;
+ }
+ }
+}
diff --git a/extensions/shared/src/main/java/app/revanced/extension/music/settings/Settings.java b/extensions/shared/src/main/java/app/revanced/extension/music/settings/Settings.java
index 50b9c8f35..dfdedc6d2 100644
--- a/extensions/shared/src/main/java/app/revanced/extension/music/settings/Settings.java
+++ b/extensions/shared/src/main/java/app/revanced/extension/music/settings/Settings.java
@@ -6,10 +6,12 @@ import static app.revanced.extension.music.sponsorblock.objects.CategoryBehaviou
import androidx.annotation.NonNull;
+import app.revanced.extension.music.patches.misc.client.AppClient.ClientType;
import app.revanced.extension.music.patches.utils.PatchStatus;
import app.revanced.extension.music.sponsorblock.SponsorBlockSettings;
import app.revanced.extension.shared.settings.BaseSettings;
import app.revanced.extension.shared.settings.BooleanSetting;
+import app.revanced.extension.shared.settings.EnumSetting;
import app.revanced.extension.shared.settings.FloatSetting;
import app.revanced.extension.shared.settings.IntegerSetting;
import app.revanced.extension.shared.settings.LongSetting;
@@ -178,6 +180,8 @@ public class Settings extends BaseSettings {
public static final BooleanSetting DISABLE_DRC_AUDIO = new BooleanSetting("revanced_disable_drc_audio", FALSE, true);
public static final BooleanSetting ENABLE_OPUS_CODEC = new BooleanSetting("revanced_enable_opus_codec", FALSE, true);
public static final BooleanSetting SETTINGS_IMPORT_EXPORT = new BooleanSetting("revanced_extended_settings_import_export", FALSE, false);
+ public static final BooleanSetting SPOOF_CLIENT_LEGACY = new BooleanSetting("revanced_spoof_client_legacy", FALSE, true);
+ public static final EnumSetting SPOOF_CLIENT_TYPE = new EnumSetting<>("revanced_spoof_client_type", ClientType.ANDROID_MUSIC, true);
// PreferenceScreen: Return YouTube Dislike
@@ -248,6 +252,7 @@ public class Settings extends BaseSettings {
SB_API_URL.key,
SETTINGS_IMPORT_EXPORT.key,
SPOOF_APP_VERSION_TARGET.key,
+ SPOOF_CLIENT_TYPE.key,
SPOOF_STREAMING_DATA_TYPE.key,
RETURN_YOUTUBE_USERNAME_ABOUT.key,
RETURN_YOUTUBE_USERNAME_DISPLAY_FORMAT.key,
diff --git a/extensions/shared/src/main/java/app/revanced/extension/music/settings/preference/ReVancedPreferenceFragment.java b/extensions/shared/src/main/java/app/revanced/extension/music/settings/preference/ReVancedPreferenceFragment.java
index 615551f59..313588ef9 100644
--- a/extensions/shared/src/main/java/app/revanced/extension/music/settings/preference/ReVancedPreferenceFragment.java
+++ b/extensions/shared/src/main/java/app/revanced/extension/music/settings/preference/ReVancedPreferenceFragment.java
@@ -12,6 +12,7 @@ import static app.revanced.extension.music.settings.Settings.RETURN_YOUTUBE_USER
import static app.revanced.extension.music.settings.Settings.SB_API_URL;
import static app.revanced.extension.music.settings.Settings.SETTINGS_IMPORT_EXPORT;
import static app.revanced.extension.music.settings.Settings.SPOOF_APP_VERSION_TARGET;
+import static app.revanced.extension.music.settings.Settings.SPOOF_CLIENT_TYPE;
import static app.revanced.extension.music.utils.ExtendedUtils.getDialogBuilder;
import static app.revanced.extension.music.utils.ExtendedUtils.getLayoutParams;
import static app.revanced.extension.music.utils.RestartUtils.showRestartDialog;
@@ -160,6 +161,7 @@ public class ReVancedPreferenceFragment extends PreferenceFragment {
}
} else if (settings instanceof EnumSetting> enumSetting) {
if (settings.equals(RETURN_YOUTUBE_USERNAME_DISPLAY_FORMAT)
+ || settings.equals(SPOOF_CLIENT_TYPE)
|| settings.equals(SPOOF_STREAMING_DATA_TYPE)) {
ResettableListPreference.showDialog(mActivity, enumSetting, 0);
}
diff --git a/extensions/shared/src/main/java/app/revanced/extension/shared/patches/BlockRequestPatch.java b/extensions/shared/src/main/java/app/revanced/extension/shared/patches/BlockRequestPatch.java
index 8e7837e1d..d4c1c458e 100644
--- a/extensions/shared/src/main/java/app/revanced/extension/shared/patches/BlockRequestPatch.java
+++ b/extensions/shared/src/main/java/app/revanced/extension/shared/patches/BlockRequestPatch.java
@@ -1,11 +1,12 @@
package app.revanced.extension.shared.patches;
+import static app.revanced.extension.shared.patches.PatchStatus.SpoofClient;
+import static app.revanced.extension.shared.patches.PatchStatus.SpoofStreamingData;
+
import android.net.Uri;
-import app.revanced.extension.music.settings.Settings;
import app.revanced.extension.shared.settings.BaseSettings;
import app.revanced.extension.shared.utils.Logger;
-import app.revanced.extension.shared.utils.PackageUtils;
@SuppressWarnings("unused")
public class BlockRequestPatch {
@@ -18,14 +19,9 @@ public class BlockRequestPatch {
* Used in YouTube Music.
* Disabled by default.
*/
- public static final boolean SPOOF_CLIENT = Settings.SPOOF_CLIENT.get();
+ public static final boolean SPOOF_CLIENT = BaseSettings.SPOOF_CLIENT.get();
- /**
- * Used in YouTube Music.
- */
- public static final boolean IS_7_17_OR_GREATER = PackageUtils.getAppVersionName().compareTo("7.17.00") >= 0;
-
- private static final boolean BLOCK_REQUEST = SPOOF_STREAMING_DATA || (SPOOF_CLIENT && IS_7_17_OR_GREATER);
+ private static final boolean BLOCK_REQUEST = (SpoofStreamingData() && SPOOF_STREAMING_DATA) || (SpoofClient() && SPOOF_CLIENT);
/**
* Any unreachable ip address. Used to intentionally fail requests.
diff --git a/extensions/shared/src/main/java/app/revanced/extension/shared/patches/PatchStatus.java b/extensions/shared/src/main/java/app/revanced/extension/shared/patches/PatchStatus.java
index 5006cb9cd..a90ce4e62 100644
--- a/extensions/shared/src/main/java/app/revanced/extension/shared/patches/PatchStatus.java
+++ b/extensions/shared/src/main/java/app/revanced/extension/shared/patches/PatchStatus.java
@@ -11,4 +11,14 @@ public class PatchStatus {
public static ClientType SpoofStreamingDataDefaultClient() {
return ClientType.IOS;
}
+
+ public static boolean SpoofClient() {
+ // Replace this with true If the Spoof client patch succeeds
+ return false;
+ }
+
+ public static boolean SpoofStreamingData() {
+ // Replace this with true If the Spoof streaming data patch succeeds
+ return false;
+ }
}
diff --git a/patches/src/main/kotlin/app/revanced/patches/music/utils/fix/client/SpoofClientPatch.kt b/patches/src/main/kotlin/app/revanced/patches/music/utils/fix/client/SpoofClientPatch.kt
index e0001e9e9..d22922baf 100644
--- a/patches/src/main/kotlin/app/revanced/patches/music/utils/fix/client/SpoofClientPatch.kt
+++ b/patches/src/main/kotlin/app/revanced/patches/music/utils/fix/client/SpoofClientPatch.kt
@@ -4,6 +4,7 @@ import app.revanced.patcher.extensions.InstructionExtensions.addInstruction
import app.revanced.patcher.extensions.InstructionExtensions.addInstructions
import app.revanced.patcher.extensions.InstructionExtensions.getInstruction
import app.revanced.patcher.extensions.InstructionExtensions.instructions
+import app.revanced.patcher.extensions.InstructionExtensions.replaceInstruction
import app.revanced.patcher.patch.PatchException
import app.revanced.patcher.patch.bytecodePatch
import app.revanced.patcher.util.proxy.mutableTypes.MutableMethod.Companion.toMutable
@@ -13,11 +14,14 @@ import app.revanced.patches.music.utils.patch.PatchList.SPOOF_CLIENT
import app.revanced.patches.music.utils.playbackSpeedBottomSheetFingerprint
import app.revanced.patches.music.utils.settings.CategoryType
import app.revanced.patches.music.utils.settings.ResourceUtils.updatePatchStatus
+import app.revanced.patches.music.utils.settings.addPreferenceWithIntent
import app.revanced.patches.music.utils.settings.addSwitchPreference
import app.revanced.patches.music.utils.settings.settingsPatch
import app.revanced.patches.shared.blockrequest.blockRequestPatch
import app.revanced.patches.shared.createPlayerRequestBodyWithModelFingerprint
+import app.revanced.patches.shared.extension.Constants.PATCHES_PATH
import app.revanced.patches.shared.indexOfModelInstruction
+import app.revanced.util.findMethodOrThrow
import app.revanced.util.fingerprint.matchOrThrow
import app.revanced.util.fingerprint.methodOrThrow
import app.revanced.util.fingerprint.mutableClassOrThrow
@@ -258,11 +262,29 @@ val spoofClientPatch = bytecodePatch(
// endregion
+ findMethodOrThrow("$PATCHES_PATH/PatchStatus;") {
+ name == "SpoofClient"
+ }.replaceInstruction(
+ 0,
+ "const/4 v0, 0x1"
+ )
+
addSwitchPreference(
CategoryType.MISC,
"revanced_spoof_client",
"false"
)
+ addSwitchPreference(
+ CategoryType.MISC,
+ "revanced_spoof_client_legacy",
+ "false",
+ "revanced_spoof_client"
+ )
+ addPreferenceWithIntent(
+ CategoryType.MISC,
+ "revanced_spoof_client_type",
+ "revanced_spoof_client",
+ )
updatePatchStatus(SPOOF_CLIENT)
diff --git a/patches/src/main/kotlin/app/revanced/patches/shared/spoof/streamingdata/BaseSpoofStreamingDataPatch.kt b/patches/src/main/kotlin/app/revanced/patches/shared/spoof/streamingdata/BaseSpoofStreamingDataPatch.kt
index 35c06d749..81da85344 100644
--- a/patches/src/main/kotlin/app/revanced/patches/shared/spoof/streamingdata/BaseSpoofStreamingDataPatch.kt
+++ b/patches/src/main/kotlin/app/revanced/patches/shared/spoof/streamingdata/BaseSpoofStreamingDataPatch.kt
@@ -5,15 +5,18 @@ import app.revanced.patcher.extensions.InstructionExtensions.addInstructions
import app.revanced.patcher.extensions.InstructionExtensions.addInstructionsWithLabels
import app.revanced.patcher.extensions.InstructionExtensions.getInstruction
import app.revanced.patcher.extensions.InstructionExtensions.instructions
+import app.revanced.patcher.extensions.InstructionExtensions.replaceInstruction
import app.revanced.patcher.patch.BytecodePatchBuilder
import app.revanced.patcher.patch.BytecodePatchContext
import app.revanced.patcher.patch.PatchException
import app.revanced.patcher.patch.bytecodePatch
import app.revanced.patcher.util.proxy.mutableTypes.MutableMethod.Companion.toMutable
import app.revanced.patches.shared.blockrequest.blockRequestPatch
+import app.revanced.patches.shared.extension.Constants.PATCHES_PATH
import app.revanced.patches.shared.extension.Constants.SPOOF_PATH
import app.revanced.patches.shared.formatStreamModelConstructorFingerprint
import app.revanced.util.findInstructionIndicesReversedOrThrow
+import app.revanced.util.findMethodOrThrow
import app.revanced.util.fingerprint.definingClassOrThrow
import app.revanced.util.fingerprint.injectLiteralInstructionBooleanCall
import app.revanced.util.fingerprint.matchOrThrow
@@ -269,6 +272,13 @@ fun baseSpoofStreamingDataPatch(
// endregion
+ findMethodOrThrow("$PATCHES_PATH/PatchStatus;") {
+ name == "SpoofStreamingData"
+ }.replaceInstruction(
+ 0,
+ "const/4 v0, 0x1"
+ )
+
executeBlock()
}
diff --git a/patches/src/main/resources/music/settings/host/values/arrays.xml b/patches/src/main/resources/music/settings/host/values/arrays.xml
index d388339a5..c0fcf769b 100644
--- a/patches/src/main/resources/music/settings/host/values/arrays.xml
+++ b/patches/src/main/resources/music/settings/host/values/arrays.xml
@@ -55,6 +55,14 @@
- 6.11.52
- 4.27.53
+
+ - @string/revanced_spoof_client_type_entry_android_music
+ - @string/revanced_spoof_client_type_entry_ios_music
+
+
+ - ANDROID_MUSIC
+ - IOS_MUSIC
+
- @string/revanced_spoof_streaming_data_type_entry_android_vr
- @string/revanced_spoof_streaming_data_type_entry_ios
diff --git a/patches/src/main/resources/music/settings/host/values/strings.xml b/patches/src/main/resources/music/settings/host/values/strings.xml
index 797cd1aa1..9278a9f5c 100644
--- a/patches/src/main/resources/music/settings/host/values/strings.xml
+++ b/patches/src/main/resources/music/settings/host/values/strings.xml
@@ -451,12 +451,15 @@ Tap the continue button and allow optimization changes."
Spoof client
"Spoof the client to prevent playback issues.
-Limitations:
-• OPUS audio codec may not be supported.
-• Seekbar thumbnail may not be present.
-• Watch history does not work with a brand account.
-
※ When used with 'Spoofing streaming data', playback issues may occur."
+ Use old client
+ "Spoofing with version 4.27.53.
+
+OPUS codec may not work."
+ Default client
+ Defines a default client to spoofing.
+ Android Music
+ iOS Music
Spoof streaming data
"Spoof the streaming data to prevent playback issues.