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 edad594b4..eb02ca092 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,25 +1,32 @@ package app.revanced.extension.music.patches.misc; -import app.revanced.extension.music.settings.Settings; +import app.revanced.extension.shared.patches.BlockRequestPatch; @SuppressWarnings("unused") -public class SpoofClientPatch { +public class SpoofClientPatch extends BlockRequestPatch { private static final int CLIENT_ID_IOS_MUSIC = 26; + /** - * The hardcoded client version of the iOS app used for InnerTube requests with this client. - * + * 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. *

- * It can be extracted by getting the latest release version of the app on - * the App - * Store page of the YouTube app, in the {@code What¡¯s New} section. - *

- */ - private static final String CLIENT_VERSION_IOS_MUSIC = "6.21"; - /** - * See this GitHub Gist for more - * information. - *

+ * 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_6_21 + : CLIENT_VERSION_IOS_MUSIC_7_31; 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"; @@ -31,13 +38,11 @@ public class SpoofClientPatch { USER_AGENT_VERSION_IOS_MUSIC + " like Mac OS X)"; - private static final boolean SPOOF_CLIENT_ENABLED = Settings.SPOOF_CLIENT.get(); - /** * Injection point. */ public static int getClientTypeId(int originalClientTypeId) { - if (SPOOF_CLIENT_ENABLED) { + if (SPOOF_CLIENT) { return CLIENT_ID_IOS_MUSIC; } @@ -48,7 +53,7 @@ public class SpoofClientPatch { * Injection point. */ public static String getClientVersion(String originalClientVersion) { - if (SPOOF_CLIENT_ENABLED) { + if (SPOOF_CLIENT) { return CLIENT_VERSION_IOS_MUSIC; } @@ -59,7 +64,7 @@ public class SpoofClientPatch { * Injection point. */ public static String getClientModel(String originalClientModel) { - if (SPOOF_CLIENT_ENABLED) { + if (SPOOF_CLIENT) { return DEVICE_MODEL_IOS_MUSIC; } @@ -70,7 +75,7 @@ public class SpoofClientPatch { * Injection point. */ public static String getOsVersion(String originalOsVersion) { - if (SPOOF_CLIENT_ENABLED) { + if (SPOOF_CLIENT) { return OS_VERSION_IOS_MUSIC; } @@ -81,7 +86,7 @@ public class SpoofClientPatch { * Injection point. */ public static String getUserAgent(String originalUserAgent) { - if (SPOOF_CLIENT_ENABLED) { + if (SPOOF_CLIENT) { return USER_AGENT_IOS_MUSIC; } @@ -92,16 +97,19 @@ public class SpoofClientPatch { * Injection point. */ public static boolean isClientSpoofingEnabled() { - return SPOOF_CLIENT_ENABLED; + return SPOOF_CLIENT; } /** * Injection point. + *

* When spoofing the client to iOS, the playback speed menu is missing from the player response. + * This fix is required because playback speed is not available in YouTube Music Podcasts. + *

* Return true to force create the playback speed menu. */ public static boolean forceCreatePlaybackSpeedMenu(boolean original) { - if (SPOOF_CLIENT_ENABLED) { + if (SPOOF_CLIENT) { return true; } return original; 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 19d940bd0..8e56282cf 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 @@ -175,7 +175,6 @@ public class Settings extends BaseSettings { public static final BooleanSetting DISABLE_CAIRO_SPLASH_ANIMATION = new BooleanSetting("revanced_disable_cairo_splash_animation", FALSE, true); 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 SPOOF_CLIENT = new BooleanSetting("revanced_spoof_client", FALSE, true); public static final BooleanSetting SETTINGS_IMPORT_EXPORT = new BooleanSetting("revanced_extended_settings_import_export", FALSE, false); @@ -239,6 +238,7 @@ public class Settings extends BaseSettings { SB_API_URL.key, SETTINGS_IMPORT_EXPORT.key, SPOOF_APP_VERSION_TARGET.key, + SPOOF_STREAMING_DATA_TYPE.key, RETURN_YOUTUBE_USERNAME_ABOUT.key, RETURN_YOUTUBE_USERNAME_DISPLAY_FORMAT.key, RETURN_YOUTUBE_USERNAME_YOUTUBE_DATA_API_V3_DEVELOPER_KEY.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 70ef1de5a..f6787d62a 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 @@ -17,6 +17,7 @@ import static app.revanced.extension.music.utils.ExtendedUtils.getLayoutParams; import static app.revanced.extension.music.utils.RestartUtils.showRestartDialog; import static app.revanced.extension.shared.settings.BaseSettings.RETURN_YOUTUBE_USERNAME_DISPLAY_FORMAT; import static app.revanced.extension.shared.settings.BaseSettings.RETURN_YOUTUBE_USERNAME_YOUTUBE_DATA_API_V3_DEVELOPER_KEY; +import static app.revanced.extension.shared.settings.BaseSettings.SPOOF_STREAMING_DATA_TYPE; import static app.revanced.extension.shared.settings.Setting.getSettingFromPath; import static app.revanced.extension.shared.utils.ResourceUtils.getStringArray; import static app.revanced.extension.shared.utils.StringRef.str; @@ -158,7 +159,8 @@ public class ReVancedPreferenceFragment extends PreferenceFragment { Logger.printDebug(() -> "Failed to find the right value: " + dataString); } } else if (settings instanceof EnumSetting enumSetting) { - if (settings.equals(RETURN_YOUTUBE_USERNAME_DISPLAY_FORMAT)) { + if (settings.equals(RETURN_YOUTUBE_USERNAME_DISPLAY_FORMAT) + || 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 new file mode 100644 index 000000000..297ad3e5d --- /dev/null +++ b/extensions/shared/src/main/java/app/revanced/extension/shared/patches/BlockRequestPatch.java @@ -0,0 +1,84 @@ +package app.revanced.extension.shared.patches; + +import android.net.Uri; + +import app.revanced.extension.music.settings.Settings; +import app.revanced.extension.shared.utils.Logger; +import app.revanced.extension.shared.utils.PackageUtils; +import app.revanced.extension.shared.settings.BaseSettings; + +@SuppressWarnings("unused") +public class BlockRequestPatch { + /** + * Used in YouTube and YouTube Music. + */ + public static final boolean SPOOF_STREAMING_DATA = BaseSettings.SPOOF_STREAMING_DATA.get(); + + /** + * Used in YouTube Music. + * Disabled by default. + */ + public static final boolean SPOOF_CLIENT = Settings.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); + + /** + * Any unreachable ip address. Used to intentionally fail requests. + */ + private static final String UNREACHABLE_HOST_URI_STRING = "https://127.0.0.0"; + private static final Uri UNREACHABLE_HOST_URI = Uri.parse(UNREACHABLE_HOST_URI_STRING); + + /** + * Injection point. + * Blocks /get_watch requests by returning an unreachable URI. + * + * @param playerRequestUri The URI of the player request. + * @return An unreachable URI if the request is a /get_watch request, otherwise the original URI. + */ + public static Uri blockGetWatchRequest(Uri playerRequestUri) { + if (BLOCK_REQUEST) { + try { + String path = playerRequestUri.getPath(); + + if (path != null && path.contains("get_watch")) { + Logger.printDebug(() -> "Blocking 'get_watch' by returning unreachable uri"); + + return UNREACHABLE_HOST_URI; + } + } catch (Exception ex) { + Logger.printException(() -> "blockGetWatchRequest failure", ex); + } + } + + return playerRequestUri; + } + + /** + * Injection point. + *

+ * Blocks /initplayback requests. + */ + public static String blockInitPlaybackRequest(String originalUrlString) { + if (BLOCK_REQUEST) { + try { + var originalUri = Uri.parse(originalUrlString); + String path = originalUri.getPath(); + + if (path != null && path.contains("initplayback")) { + Logger.printDebug(() -> "Blocking 'initplayback' by clearing query"); + + return originalUri.buildUpon().clearQuery().build().toString(); + } + } catch (Exception ex) { + Logger.printException(() -> "blockInitPlaybackRequest failure", ex); + } + } + + return originalUrlString; + } +} diff --git a/extensions/shared/src/main/java/app/revanced/extension/shared/patches/client/AppClient.java b/extensions/shared/src/main/java/app/revanced/extension/shared/patches/client/AppClient.java index 80033c845..2f6102346 100644 --- a/extensions/shared/src/main/java/app/revanced/extension/shared/patches/client/AppClient.java +++ b/extensions/shared/src/main/java/app/revanced/extension/shared/patches/client/AppClient.java @@ -41,19 +41,19 @@ public class AppClient { USER_AGENT_VERSION_IOS + " like Mac OS X)"; - // IOS_MUSIC + // IOS MUSIC /** * The hardcoded client version of the iOS app used for InnerTube requests with this client. * *

* It can be extracted by getting the latest release version of the app on * the App - * Store page of the YouTube app, in the {@code What’s New} section. + * Store page of the YouTube Music app, in the {@code What’s New} section. *

*/ private static final String CLIENT_VERSION_IOS_MUSIC = "7.31.2"; private static final String USER_AGENT_IOS_MUSIC = "com.google.ios.youtubemusic/" + - CLIENT_VERSION_IOS + + CLIENT_VERSION_IOS_MUSIC + "(" + DEVICE_MODEL_IOS + "; U; CPU iOS " + diff --git a/extensions/shared/src/main/java/app/revanced/extension/shared/patches/spoof/SpoofStreamingDataPatch.java b/extensions/shared/src/main/java/app/revanced/extension/shared/patches/spoof/SpoofStreamingDataPatch.java index 2bb2a233b..b3294aac3 100644 --- a/extensions/shared/src/main/java/app/revanced/extension/shared/patches/spoof/SpoofStreamingDataPatch.java +++ b/extensions/shared/src/main/java/app/revanced/extension/shared/patches/spoof/SpoofStreamingDataPatch.java @@ -8,68 +8,14 @@ import androidx.annotation.Nullable; import java.nio.ByteBuffer; import java.util.Map; +import app.revanced.extension.shared.patches.BlockRequestPatch; import app.revanced.extension.shared.utils.Logger; import app.revanced.extension.shared.utils.Utils; import app.revanced.extension.shared.settings.BaseSettings; import app.revanced.extension.shared.patches.spoof.requests.StreamingDataRequest; @SuppressWarnings("unused") -public class SpoofStreamingDataPatch { - private static final boolean SPOOF_STREAMING_DATA = BaseSettings.SPOOF_STREAMING_DATA.get(); - /** - * Any unreachable ip address. Used to intentionally fail requests. - */ - private static final String UNREACHABLE_HOST_URI_STRING = "https://127.0.0.0"; - private static final Uri UNREACHABLE_HOST_URI = Uri.parse(UNREACHABLE_HOST_URI_STRING); - - /** - * Injection point. - * Blocks /get_watch requests by returning an unreachable URI. - * - * @param playerRequestUri The URI of the player request. - * @return An unreachable URI if the request is a /get_watch request, otherwise the original URI. - */ - public static Uri blockGetWatchRequest(Uri playerRequestUri) { - if (SPOOF_STREAMING_DATA) { - try { - String path = playerRequestUri.getPath(); - - if (path != null && path.contains("get_watch")) { - Logger.printDebug(() -> "Blocking 'get_watch' by returning unreachable uri"); - - return UNREACHABLE_HOST_URI; - } - } catch (Exception ex) { - Logger.printException(() -> "blockGetWatchRequest failure", ex); - } - } - - return playerRequestUri; - } - - /** - * Injection point. - *

- * Blocks /initplayback requests. - */ - public static String blockInitPlaybackRequest(String originalUrlString) { - if (SPOOF_STREAMING_DATA) { - try { - var originalUri = Uri.parse(originalUrlString); - String path = originalUri.getPath(); - - if (path != null && path.contains("initplayback")) { - Logger.printDebug(() -> "Blocking 'initplayback' by clearing query"); - - return originalUri.buildUpon().clearQuery().build().toString(); - } - } catch (Exception ex) { - Logger.printException(() -> "blockInitPlaybackRequest failure", ex); - } - } - - return originalUrlString; - } +public class SpoofStreamingDataPatch extends BlockRequestPatch { /** * Injection point. diff --git a/extensions/shared/src/main/java/app/revanced/extension/shared/settings/BaseSettings.java b/extensions/shared/src/main/java/app/revanced/extension/shared/settings/BaseSettings.java index 904465ea1..ba2c43503 100644 --- a/extensions/shared/src/main/java/app/revanced/extension/shared/settings/BaseSettings.java +++ b/extensions/shared/src/main/java/app/revanced/extension/shared/settings/BaseSettings.java @@ -37,6 +37,10 @@ public class BaseSettings { public static final EnumSetting RETURN_YOUTUBE_USERNAME_DISPLAY_FORMAT = new EnumSetting<>("revanced_return_youtube_username_display_format", DisplayFormat.USERNAME_ONLY, true); public static final StringSetting RETURN_YOUTUBE_USERNAME_YOUTUBE_DATA_API_V3_DEVELOPER_KEY = new StringSetting("revanced_return_youtube_username_youtube_data_api_v3_developer_key", "", true, false); + // Used only in YouTube Music. + // Moved to a shared class to prevent YouTube from accessing YouTube Music's extension classes. + public static final BooleanSetting SPOOF_CLIENT = new BooleanSetting("revanced_spoof_client", FALSE, true); + public static final BooleanSetting SPOOF_STREAMING_DATA = new BooleanSetting("revanced_spoof_streaming_data", TRUE, true, "revanced_spoof_streaming_data_user_dialog_message"); public static final EnumSetting SPOOF_STREAMING_DATA_TYPE = new EnumSetting<>("revanced_spoof_streaming_data_type", SpoofStreamingDataDefaultClient(), true); public static final BooleanSetting SPOOF_STREAMING_DATA_STATS_FOR_NERDS = new BooleanSetting("revanced_spoof_streaming_data_stats_for_nerds", TRUE); 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 5b3cfe920..4b0fed168 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 @@ -15,6 +15,7 @@ import app.revanced.patches.music.utils.settings.CategoryType import app.revanced.patches.music.utils.settings.ResourceUtils.updatePatchStatus 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.indexOfModelInstruction import app.revanced.util.fingerprint.matchOrThrow @@ -48,7 +49,10 @@ val spoofClientPatch = bytecodePatch( SPOOF_CLIENT.summary, false, ) { - dependsOn(settingsPatch) + dependsOn( + settingsPatch, + blockRequestPatch + ) compatibleWith(COMPATIBLE_PACKAGE) @@ -211,6 +215,8 @@ val spoofClientPatch = bytecodePatch( // endregion + // region fix for playback speed menu is not available in Podcasts + playbackSpeedBottomSheetFingerprint.mutableClassOrThrow().let { val onItemClickMethod = it.methods.find { method -> method.name == "onItemClick" } @@ -244,6 +250,8 @@ val spoofClientPatch = bytecodePatch( } } + // endregion + addSwitchPreference( CategoryType.MISC, "revanced_spoof_client", diff --git a/patches/src/main/kotlin/app/revanced/patches/music/utils/fix/streamingdata/SpoofStreamingDataPatch.kt b/patches/src/main/kotlin/app/revanced/patches/music/utils/fix/streamingdata/SpoofStreamingDataPatch.kt index cbd66305e..3b8ae37b3 100644 --- a/patches/src/main/kotlin/app/revanced/patches/music/utils/fix/streamingdata/SpoofStreamingDataPatch.kt +++ b/patches/src/main/kotlin/app/revanced/patches/music/utils/fix/streamingdata/SpoofStreamingDataPatch.kt @@ -7,6 +7,7 @@ import app.revanced.patches.music.utils.compatibility.Constants.YOUTUBE_MUSIC_PA import app.revanced.patches.music.utils.patch.PatchList.SPOOF_STREAMING_DATA 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.extension.Constants.PATCHES_PATH @@ -19,6 +20,7 @@ import com.android.tools.smali.dexlib2.iface.reference.FieldReference private const val DEFAULT_CLIENT_TYPE = "ANDROID_VR" +@Suppress("unused") val spoofStreamingDataPatch = baseSpoofStreamingDataPatch( { compatibleWith(COMPATIBLE_PACKAGE) @@ -45,6 +47,11 @@ val spoofStreamingDataPatch = baseSpoofStreamingDataPatch( "revanced_spoof_streaming_data", "true" ) + addPreferenceWithIntent( + CategoryType.MISC, + "revanced_spoof_streaming_data_type", + "revanced_spoof_streaming_data" + ) addSwitchPreference( CategoryType.MISC, "revanced_spoof_streaming_data_stats_for_nerds", diff --git a/patches/src/main/kotlin/app/revanced/patches/shared/blockrequest/BlockRequestPatch.kt b/patches/src/main/kotlin/app/revanced/patches/shared/blockrequest/BlockRequestPatch.kt new file mode 100644 index 000000000..ae3d9a72c --- /dev/null +++ b/patches/src/main/kotlin/app/revanced/patches/shared/blockrequest/BlockRequestPatch.kt @@ -0,0 +1,57 @@ +package app.revanced.patches.shared.blockrequest + +import app.revanced.patcher.extensions.InstructionExtensions.addInstructions +import app.revanced.patcher.extensions.InstructionExtensions.getInstruction +import app.revanced.patcher.patch.bytecodePatch +import app.revanced.patches.shared.extension.Constants.PATCHES_PATH +import app.revanced.util.fingerprint.matchOrThrow +import app.revanced.util.fingerprint.methodOrThrow +import com.android.tools.smali.dexlib2.iface.instruction.FiveRegisterInstruction +import com.android.tools.smali.dexlib2.iface.instruction.OneRegisterInstruction + +const val EXTENSION_CLASS_DESCRIPTOR = + "$PATCHES_PATH/BlockRequestPatch;" + +val blockRequestPatch = bytecodePatch( + description = "blockRequestPatch" +) { + execute { + // region Block /initplayback requests to fall back to /get_watch requests. + + buildInitPlaybackRequestFingerprint.matchOrThrow().let { + it.method.apply { + val moveUriStringIndex = it.patternMatch!!.startIndex + val targetRegister = + getInstruction(moveUriStringIndex).registerA + + addInstructions( + moveUriStringIndex + 1, + """ + invoke-static { v$targetRegister }, $EXTENSION_CLASS_DESCRIPTOR->blockInitPlaybackRequest(Ljava/lang/String;)Ljava/lang/String; + move-result-object v$targetRegister + """, + ) + } + } + + // endregion + + // region Block /get_watch requests to fall back to /player requests. + + buildPlayerRequestURIFingerprint.methodOrThrow().apply { + val invokeToStringIndex = indexOfToStringInstruction(this) + val uriRegister = + getInstruction(invokeToStringIndex).registerC + + addInstructions( + invokeToStringIndex, + """ + invoke-static { v$uriRegister }, $EXTENSION_CLASS_DESCRIPTOR->blockGetWatchRequest(Landroid/net/Uri;)Landroid/net/Uri; + move-result-object v$uriRegister + """, + ) + } + + // endregion + } +} \ No newline at end of file diff --git a/patches/src/main/kotlin/app/revanced/patches/shared/blockrequest/Fingerprints.kt b/patches/src/main/kotlin/app/revanced/patches/shared/blockrequest/Fingerprints.kt new file mode 100644 index 000000000..30ed66ea0 --- /dev/null +++ b/patches/src/main/kotlin/app/revanced/patches/shared/blockrequest/Fingerprints.kt @@ -0,0 +1,40 @@ +package app.revanced.patches.shared.blockrequest + +import app.revanced.util.fingerprint.legacyFingerprint +import app.revanced.util.getReference +import app.revanced.util.indexOfFirstInstruction +import com.android.tools.smali.dexlib2.Opcode +import com.android.tools.smali.dexlib2.iface.Method +import com.android.tools.smali.dexlib2.iface.reference.MethodReference + +internal val buildInitPlaybackRequestFingerprint = legacyFingerprint( + name = "buildInitPlaybackRequestFingerprint", + returnType = "Lorg/chromium/net/UrlRequest\$Builder;", + opcodes = listOf( + Opcode.MOVE_RESULT_OBJECT, + Opcode.IGET_OBJECT, // Moves the request URI string to a register to build the request with. + ), + strings = listOf( + "Content-Type", + "Range", + ), +) + +internal val buildPlayerRequestURIFingerprint = legacyFingerprint( + name = "buildPlayerRequestURIFingerprint", + returnType = "Ljava/lang/String;", + strings = listOf( + "key", + "asig", + ), + customFingerprint = { method, _ -> + indexOfToStringInstruction(method) >= 0 + }, +) + +internal fun indexOfToStringInstruction(method: Method) = + method.indexOfFirstInstruction { + opcode == Opcode.INVOKE_VIRTUAL && + getReference().toString() == "Landroid/net/Uri;->toString()Ljava/lang/String;" + } + 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 956a1818e..cb7570a04 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 @@ -9,6 +9,7 @@ import app.revanced.patcher.patch.BytecodePatchContext import app.revanced.patcher.patch.PatchException import app.revanced.patcher.patch.bytecodePatch import app.revanced.patcher.util.smali.ExternalLabel +import app.revanced.patches.shared.blockrequest.blockRequestPatch import app.revanced.patches.shared.extension.Constants.SPOOF_PATH import app.revanced.util.findInstructionIndicesReversedOrThrow import app.revanced.util.fingerprint.definingClassOrThrow @@ -32,47 +33,11 @@ fun baseSpoofStreamingDataPatch( name = "Spoof streaming data", description = "Adds options to spoof the streaming data to allow playback." ) { + dependsOn(blockRequestPatch) + block() execute { - // region Block /initplayback requests to fall back to /get_watch requests. - - buildInitPlaybackRequestFingerprint.matchOrThrow().let { - it.method.apply { - val moveUriStringIndex = it.patternMatch!!.startIndex - val targetRegister = - getInstruction(moveUriStringIndex).registerA - - addInstructions( - moveUriStringIndex + 1, - """ - invoke-static { v$targetRegister }, $EXTENSION_CLASS_DESCRIPTOR->blockInitPlaybackRequest(Ljava/lang/String;)Ljava/lang/String; - move-result-object v$targetRegister - """, - ) - } - } - - // endregion - - // region Block /get_watch requests to fall back to /player requests. - - buildPlayerRequestURIFingerprint.methodOrThrow().apply { - val invokeToStringIndex = indexOfToStringInstruction(this) - val uriRegister = - getInstruction(invokeToStringIndex).registerC - - addInstructions( - invokeToStringIndex, - """ - invoke-static { v$uriRegister }, $EXTENSION_CLASS_DESCRIPTOR->blockGetWatchRequest(Landroid/net/Uri;)Landroid/net/Uri; - move-result-object v$uriRegister - """, - ) - } - - // endregion - // region Get replacement streams at player requests. buildRequestFingerprint.methodOrThrow().apply { diff --git a/patches/src/main/kotlin/app/revanced/patches/shared/spoof/streamingdata/Fingerprints.kt b/patches/src/main/kotlin/app/revanced/patches/shared/spoof/streamingdata/Fingerprints.kt index 45e2739df..caf2d88fd 100644 --- a/patches/src/main/kotlin/app/revanced/patches/shared/spoof/streamingdata/Fingerprints.kt +++ b/patches/src/main/kotlin/app/revanced/patches/shared/spoof/streamingdata/Fingerprints.kt @@ -7,22 +7,8 @@ import app.revanced.util.or import com.android.tools.smali.dexlib2.AccessFlags import com.android.tools.smali.dexlib2.Opcode import com.android.tools.smali.dexlib2.iface.Method -import com.android.tools.smali.dexlib2.iface.reference.FieldReference import com.android.tools.smali.dexlib2.iface.reference.MethodReference -internal val buildInitPlaybackRequestFingerprint = legacyFingerprint( - name = "buildInitPlaybackRequestFingerprint", - returnType = "Lorg/chromium/net/UrlRequest\$Builder;", - opcodes = listOf( - Opcode.MOVE_RESULT_OBJECT, - Opcode.IGET_OBJECT, // Moves the request URI string to a register to build the request with. - ), - strings = listOf( - "Content-Type", - "Range", - ), -) - internal val buildMediaDataSourceFingerprint = legacyFingerprint( name = "buildMediaDataSourceFingerprint", accessFlags = AccessFlags.PUBLIC or AccessFlags.CONSTRUCTOR, @@ -41,24 +27,6 @@ internal val buildMediaDataSourceFingerprint = legacyFingerprint( ) ) -internal val buildPlayerRequestURIFingerprint = legacyFingerprint( - name = "buildPlayerRequestURIFingerprint", - returnType = "Ljava/lang/String;", - strings = listOf( - "key", - "asig", - ), - customFingerprint = { method, _ -> - indexOfToStringInstruction(method) >= 0 - }, -) - -internal fun indexOfToStringInstruction(method: Method) = - method.indexOfFirstInstruction { - opcode == Opcode.INVOKE_VIRTUAL && - getReference().toString() == "Landroid/net/Uri;->toString()Ljava/lang/String;" - } - internal val buildRequestFingerprint = legacyFingerprint( name = "buildRequestFingerprint", customFingerprint = { method, _ -> 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 aaaaac2e9..d388339a5 100644 --- a/patches/src/main/resources/music/settings/host/values/arrays.xml +++ b/patches/src/main/resources/music/settings/host/values/arrays.xml @@ -55,4 +55,14 @@ 6.11.52 4.27.53 + + @string/revanced_spoof_streaming_data_type_entry_android_vr + @string/revanced_spoof_streaming_data_type_entry_ios + @string/revanced_spoof_streaming_data_type_entry_ios_music + + + ANDROID_VR + IOS + IOS_MUSIC + 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 311b6504d..f9a2a969e 100644 --- a/patches/src/main/resources/music/settings/host/values/strings.xml +++ b/patches/src/main/resources/music/settings/host/values/strings.xml @@ -452,10 +452,16 @@ Tap on the continue button and disable battery optimizations." Limitations: • OPUS audio codec may not be supported. • Seekbar thumbnail may not be present. -• Watch history does not work with a brand account." +• Watch history does not work with a brand account. + +※ When used with 'Spoofing streaming data', playback issues may occur." Spoof streaming data - Spoof the streaming data to prevent playback issues. + "Spoof the streaming data to prevent playback issues. + +※ When used with 'Spoof client', playback issues may occur." + Default client + Defines a default client that fetches streaming data. Show in Stats for nerds Shows the client used to fetch streaming data in Stats for nerds. iOS