diff --git a/extensions/music/src/main/java/app/revanced/extension/music/spoof/SpoofClientPatch.java b/extensions/music/src/main/java/app/revanced/extension/music/spoof/SpoofClientPatch.java deleted file mode 100644 index 05c14bb30..000000000 --- a/extensions/music/src/main/java/app/revanced/extension/music/spoof/SpoofClientPatch.java +++ /dev/null @@ -1,27 +0,0 @@ -package app.revanced.extension.music.spoof; - -/** - * @noinspection unused - */ -public class SpoofClientPatch { - private static final int CLIENT_TYPE_ID = 26; - private static final String CLIENT_VERSION = "6.21"; - private static final String DEVICE_MODEL = "iPhone16,2"; - private static final String OS_VERSION = "17.7.2.21H221"; - - public static int getClientId() { - return CLIENT_TYPE_ID; - } - - public static String getClientVersion() { - return CLIENT_VERSION; - } - - public static String getClientModel() { - return DEVICE_MODEL; - } - - public static String getOsVersion() { - return OS_VERSION; - } -} \ No newline at end of file diff --git a/extensions/shared/library/src/main/java/app/revanced/extension/shared/settings/BaseSettings.java b/extensions/shared/library/src/main/java/app/revanced/extension/shared/settings/BaseSettings.java index 6b9665277..6a6c31113 100644 --- a/extensions/shared/library/src/main/java/app/revanced/extension/shared/settings/BaseSettings.java +++ b/extensions/shared/library/src/main/java/app/revanced/extension/shared/settings/BaseSettings.java @@ -34,6 +34,6 @@ public class BaseSettings { public static final BooleanSetting SPOOF_VIDEO_STREAMS_IOS_FORCE_AVC = new BooleanSetting("revanced_spoof_video_streams_ios_force_avc", FALSE, true, "revanced_spoof_video_streams_ios_force_avc_user_dialog_message", new SpoofiOSAvailability()); // Client type must be last spoof setting due to cyclic references. - public static final EnumSetting SPOOF_VIDEO_STREAMS_CLIENT_TYPE = new EnumSetting<>("revanced_spoof_video_streams_client_type", ClientType.ANDROID_UNPLUGGED, true, parent(SPOOF_VIDEO_STREAMS)); + public static final EnumSetting SPOOF_VIDEO_STREAMS_CLIENT_TYPE = new EnumSetting<>("revanced_spoof_video_streams_client_type", ClientType.ANDROID_VR_NO_AUTH, true, parent(SPOOF_VIDEO_STREAMS)); } diff --git a/extensions/shared/library/src/main/java/app/revanced/extension/shared/spoof/ClientType.java b/extensions/shared/library/src/main/java/app/revanced/extension/shared/spoof/ClientType.java index 9f0d17363..39d6ad823 100644 --- a/extensions/shared/library/src/main/java/app/revanced/extension/shared/spoof/ClientType.java +++ b/extensions/shared/library/src/main/java/app/revanced/extension/shared/spoof/ClientType.java @@ -96,22 +96,6 @@ public enum ClientType { forceAVC() ? "iOS TV Force AVC" : "iOS TV" - ), - ANDROID_VR_AUTH( - ANDROID_VR_NO_AUTH.id, - ANDROID_VR_NO_AUTH.clientName, - ANDROID_VR_NO_AUTH.packageName, - ANDROID_VR_NO_AUTH.deviceMake, - ANDROID_VR_NO_AUTH.deviceModel, - ANDROID_VR_NO_AUTH.osName, - ANDROID_VR_NO_AUTH.osVersion, - ANDROID_VR_NO_AUTH.androidSdkVersion, - ANDROID_VR_NO_AUTH.buildId, - ANDROID_VR_NO_AUTH.cronetVersion, - ANDROID_VR_NO_AUTH.clientVersion, - ANDROID_VR_NO_AUTH.requiresAuth, - true, - "Android VR Auth" ); private static boolean forceAVC() { diff --git a/extensions/youtube/src/main/java/app/revanced/extension/youtube/settings/preference/SpoofStreamingDataSideEffectsPreference.java b/extensions/youtube/src/main/java/app/revanced/extension/youtube/settings/preference/SpoofStreamingDataSideEffectsPreference.java index 904103f92..6ea249a4f 100644 --- a/extensions/youtube/src/main/java/app/revanced/extension/youtube/settings/preference/SpoofStreamingDataSideEffectsPreference.java +++ b/extensions/youtube/src/main/java/app/revanced/extension/youtube/settings/preference/SpoofStreamingDataSideEffectsPreference.java @@ -86,7 +86,7 @@ public class SpoofStreamingDataSideEffectsPreference extends Preference { String summary = str(key + "_summary"); // Android VR supports AV1 but all other clients do not. - if (clientType != ClientType.ANDROID_VR_AUTH && clientType != ClientType.ANDROID_VR_NO_AUTH) { + if (clientType != ClientType.ANDROID_VR_NO_AUTH) { summary += '\n' + str("revanced_spoof_video_streams_about_no_av1"); } diff --git a/patches/api/patches.api b/patches/api/patches.api index b75ba4b04..b222e63bd 100644 --- a/patches/api/patches.api +++ b/patches/api/patches.api @@ -392,14 +392,20 @@ public final class app/revanced/patches/music/misc/gms/GmsCoreSupportPatchKt { public static final fun getGmsCoreSupportPatch ()Lapp/revanced/patcher/patch/BytecodePatch; } -public final class app/revanced/patches/music/misc/spoof/SpoofClientPatchKt { - public static final fun getSpoofClientPatch ()Lapp/revanced/patcher/patch/BytecodePatch; +public final class app/revanced/patches/music/misc/spoof/SpoofVideoStreamsKt { + public static final fun getSpoofVideoStreamsPatch ()Lapp/revanced/patcher/patch/BytecodePatch; } public final class app/revanced/patches/music/misc/spoof/UserAgentClientSpoofPatchKt { public static final fun getUserAgentClientSpoofPatch ()Lapp/revanced/patcher/patch/BytecodePatch; } +public final class app/revanced/patches/music/playservice/VersionCheckPatchKt { + public static final fun getVersionCheckPatch ()Lapp/revanced/patcher/patch/ResourcePatch; + public static final fun is_7_33_or_greater ()Z + public static final fun is_8_11_or_greater ()Z +} + public final class app/revanced/patches/myexpenses/misc/pro/UnlockProPatchKt { public static final fun getUnlockProPatch ()Lapp/revanced/patcher/patch/BytecodePatch; } diff --git a/patches/src/main/kotlin/app/revanced/patches/music/ad/video/HideVideoAds.kt b/patches/src/main/kotlin/app/revanced/patches/music/ad/video/HideVideoAds.kt index 27c981256..4c89bfb03 100644 --- a/patches/src/main/kotlin/app/revanced/patches/music/ad/video/HideVideoAds.kt +++ b/patches/src/main/kotlin/app/revanced/patches/music/ad/video/HideVideoAds.kt @@ -8,7 +8,11 @@ val hideVideoAdsPatch = bytecodePatch( name = "Hide music video ads", description = "Hides ads that appear while listening to or streaming music videos, podcasts, or songs.", ) { - compatibleWith("com.google.android.apps.youtube.music") + compatibleWith( + "com.google.android.apps.youtube.music"( + "7.29.52" + ) + ) execute { navigate(showVideoAdsParentFingerprint.originalMethod) diff --git a/patches/src/main/kotlin/app/revanced/patches/music/audio/exclusiveaudio/EnableExclusiveAudioPlayback.kt b/patches/src/main/kotlin/app/revanced/patches/music/audio/exclusiveaudio/EnableExclusiveAudioPlayback.kt index 6ae5217f4..9834f649e 100644 --- a/patches/src/main/kotlin/app/revanced/patches/music/audio/exclusiveaudio/EnableExclusiveAudioPlayback.kt +++ b/patches/src/main/kotlin/app/revanced/patches/music/audio/exclusiveaudio/EnableExclusiveAudioPlayback.kt @@ -8,7 +8,11 @@ val enableExclusiveAudioPlaybackPatch = bytecodePatch( name = "Enable exclusive audio playback", description = "Enables the option to play audio without video.", ) { - compatibleWith("com.google.android.apps.youtube.music") + compatibleWith( + "com.google.android.apps.youtube.music"( + "7.29.52" + ) + ) execute { allowExclusiveAudioPlaybackFingerprint.method.returnEarly(true) diff --git a/patches/src/main/kotlin/app/revanced/patches/music/interaction/permanentrepeat/PermanentRepeatPatch.kt b/patches/src/main/kotlin/app/revanced/patches/music/interaction/permanentrepeat/PermanentRepeatPatch.kt index cbcf83c5d..bfcdaba38 100644 --- a/patches/src/main/kotlin/app/revanced/patches/music/interaction/permanentrepeat/PermanentRepeatPatch.kt +++ b/patches/src/main/kotlin/app/revanced/patches/music/interaction/permanentrepeat/PermanentRepeatPatch.kt @@ -11,7 +11,11 @@ val permanentRepeatPatch = bytecodePatch( description = "Permanently remember your repeating preference even if the playlist ends or another track is played.", use = false, ) { - compatibleWith("com.google.android.apps.youtube.music") + compatibleWith( + "com.google.android.apps.youtube.music"( + "7.29.52" + ) + ) execute { val startIndex = repeatTrackFingerprint.patternMatch!!.endIndex diff --git a/patches/src/main/kotlin/app/revanced/patches/music/interaction/permanentshuffle/PermanentShufflePatch.kt b/patches/src/main/kotlin/app/revanced/patches/music/interaction/permanentshuffle/PermanentShufflePatch.kt index 1e4d81d3d..39ffa319a 100644 --- a/patches/src/main/kotlin/app/revanced/patches/music/interaction/permanentshuffle/PermanentShufflePatch.kt +++ b/patches/src/main/kotlin/app/revanced/patches/music/interaction/permanentshuffle/PermanentShufflePatch.kt @@ -9,7 +9,11 @@ val permanentShufflePatch = bytecodePatch( description = "Permanently remember your shuffle preference " + "even if the playlist ends or another track is played." ) { - compatibleWith("com.google.android.apps.youtube.music") + compatibleWith( + "com.google.android.apps.youtube.music"( + "7.29.52" + ) + ) execute { disableShuffleFingerprint.method.addInstruction(0, "return-void") diff --git a/patches/src/main/kotlin/app/revanced/patches/music/layout/compactheader/HideCategoryBar.kt b/patches/src/main/kotlin/app/revanced/patches/music/layout/compactheader/HideCategoryBar.kt index 2128722e7..78ef86f50 100644 --- a/patches/src/main/kotlin/app/revanced/patches/music/layout/compactheader/HideCategoryBar.kt +++ b/patches/src/main/kotlin/app/revanced/patches/music/layout/compactheader/HideCategoryBar.kt @@ -12,7 +12,11 @@ val hideCategoryBar = bytecodePatch( description = "Hides the category bar at the top of the homepage.", use = false, ) { - compatibleWith("com.google.android.apps.youtube.music") + compatibleWith( + "com.google.android.apps.youtube.music"( + "7.29.52" + ) + ) execute { constructCategoryBarFingerprint.method.apply { diff --git a/patches/src/main/kotlin/app/revanced/patches/music/layout/premium/HideGetPremiumPatch.kt b/patches/src/main/kotlin/app/revanced/patches/music/layout/premium/HideGetPremiumPatch.kt index 23965cea4..7ba3260ef 100644 --- a/patches/src/main/kotlin/app/revanced/patches/music/layout/premium/HideGetPremiumPatch.kt +++ b/patches/src/main/kotlin/app/revanced/patches/music/layout/premium/HideGetPremiumPatch.kt @@ -11,7 +11,11 @@ val hideGetPremiumPatch = bytecodePatch( name = "Hide 'Get Music Premium' label", description = "Hides the \"Get Music Premium\" label from the account menu and settings.", ) { - compatibleWith("com.google.android.apps.youtube.music") + compatibleWith( + "com.google.android.apps.youtube.music"( + "7.29.52" + ) + ) execute { hideGetPremiumFingerprint.method.apply { diff --git a/patches/src/main/kotlin/app/revanced/patches/music/layout/upgradebutton/RemoveUpgradeButtonPatch.kt b/patches/src/main/kotlin/app/revanced/patches/music/layout/upgradebutton/RemoveUpgradeButtonPatch.kt index 22878b05f..426ab8046 100644 --- a/patches/src/main/kotlin/app/revanced/patches/music/layout/upgradebutton/RemoveUpgradeButtonPatch.kt +++ b/patches/src/main/kotlin/app/revanced/patches/music/layout/upgradebutton/RemoveUpgradeButtonPatch.kt @@ -18,7 +18,11 @@ val removeUpgradeButtonPatch = bytecodePatch( name = "Remove upgrade button", description = "Removes the upgrade tab from the pivot bar.", ) { - compatibleWith("com.google.android.apps.youtube.music") + compatibleWith( + "com.google.android.apps.youtube.music"( + "7.29.52" + ) + ) execute { pivotBarConstructorFingerprint.method.apply { diff --git a/patches/src/main/kotlin/app/revanced/patches/music/misc/androidauto/BypassCertificateChecksPatch.kt b/patches/src/main/kotlin/app/revanced/patches/music/misc/androidauto/BypassCertificateChecksPatch.kt index 32ee6894d..a08e1fceb 100644 --- a/patches/src/main/kotlin/app/revanced/patches/music/misc/androidauto/BypassCertificateChecksPatch.kt +++ b/patches/src/main/kotlin/app/revanced/patches/music/misc/androidauto/BypassCertificateChecksPatch.kt @@ -8,7 +8,11 @@ val bypassCertificateChecksPatch = bytecodePatch( name = "Bypass certificate checks", description = "Bypasses certificate checks which prevent YouTube Music from working on Android Auto.", ) { - compatibleWith("com.google.android.apps.youtube.music") + compatibleWith( + "com.google.android.apps.youtube.music"( + "7.29.52" + ) + ) execute { checkCertificateFingerprint.method.returnEarly(true) diff --git a/patches/src/main/kotlin/app/revanced/patches/music/misc/backgroundplayback/BackgroundPlaybackPatch.kt b/patches/src/main/kotlin/app/revanced/patches/music/misc/backgroundplayback/BackgroundPlaybackPatch.kt index 90ddf6644..ab0fe132d 100644 --- a/patches/src/main/kotlin/app/revanced/patches/music/misc/backgroundplayback/BackgroundPlaybackPatch.kt +++ b/patches/src/main/kotlin/app/revanced/patches/music/misc/backgroundplayback/BackgroundPlaybackPatch.kt @@ -8,7 +8,11 @@ val backgroundPlaybackPatch = bytecodePatch( name = "Remove background playback restrictions", description = "Removes restrictions on background playback, including playing kids videos in the background.", ) { - compatibleWith("com.google.android.apps.youtube.music") + compatibleWith( + "com.google.android.apps.youtube.music"( + "7.29.52" + ) + ) execute { kidsBackgroundPlaybackPolicyControllerFingerprint.method.addInstruction( diff --git a/patches/src/main/kotlin/app/revanced/patches/music/misc/gms/GmsCoreSupportPatch.kt b/patches/src/main/kotlin/app/revanced/patches/music/misc/gms/GmsCoreSupportPatch.kt index 2cb49fd5b..0fa223b23 100644 --- a/patches/src/main/kotlin/app/revanced/patches/music/misc/gms/GmsCoreSupportPatch.kt +++ b/patches/src/main/kotlin/app/revanced/patches/music/misc/gms/GmsCoreSupportPatch.kt @@ -4,7 +4,7 @@ import app.revanced.patcher.patch.Option import app.revanced.patches.music.misc.extension.sharedExtensionPatch import app.revanced.patches.music.misc.gms.Constants.MUSIC_PACKAGE_NAME import app.revanced.patches.music.misc.gms.Constants.REVANCED_MUSIC_PACKAGE_NAME -import app.revanced.patches.music.misc.spoof.spoofClientPatch +import app.revanced.patches.music.misc.spoof.spoofVideoStreamsPatch import app.revanced.patches.shared.castContextFetchFingerprint import app.revanced.patches.shared.misc.gms.gmsCoreSupportPatch import app.revanced.patches.shared.primeMethodFingerprint @@ -21,7 +21,7 @@ val gmsCoreSupportPatch = gmsCoreSupportPatch( extensionPatch = sharedExtensionPatch, gmsCoreSupportResourcePatchFactory = ::gmsCoreSupportResourcePatch, ) { - dependsOn(spoofClientPatch) + dependsOn(spoofVideoStreamsPatch) compatibleWith(MUSIC_PACKAGE_NAME) } diff --git a/patches/src/main/kotlin/app/revanced/patches/music/misc/spoof/Fingerprints.kt b/patches/src/main/kotlin/app/revanced/patches/music/misc/spoof/Fingerprints.kt deleted file mode 100644 index abf19cc95..000000000 --- a/patches/src/main/kotlin/app/revanced/patches/music/misc/spoof/Fingerprints.kt +++ /dev/null @@ -1,39 +0,0 @@ -package app.revanced.patches.music.misc.spoof - -import app.revanced.patcher.fingerprint -import com.android.tools.smali.dexlib2.AccessFlags -import com.android.tools.smali.dexlib2.Opcode - -internal val playerRequestConstructorFingerprint = fingerprint { - accessFlags(AccessFlags.PUBLIC, AccessFlags.CONSTRUCTOR) - strings("player") -} - -/** - * Matches using the class found in [playerRequestConstructorFingerprint]. - */ -internal val createPlayerRequestBodyFingerprint = fingerprint { - parameters("L") - returns("V") - opcodes( - Opcode.CHECK_CAST, - Opcode.IGET, - Opcode.AND_INT_LIT16, - ) - strings("ms") -} - -/** - * Used to get a reference to other clientInfo fields. - */ -internal val setClientInfoFieldsFingerprint = fingerprint { - returns("L") - strings("Google Inc.") -} - -/** - * Used to get a reference to the clientInfo and clientInfo.clientVersion field. - */ -internal val setClientInfoClientVersionFingerprint = fingerprint { - strings("10.29") -} diff --git a/patches/src/main/kotlin/app/revanced/patches/music/misc/spoof/SpoofClientPatch.kt b/patches/src/main/kotlin/app/revanced/patches/music/misc/spoof/SpoofClientPatch.kt deleted file mode 100644 index f34797f03..000000000 --- a/patches/src/main/kotlin/app/revanced/patches/music/misc/spoof/SpoofClientPatch.kt +++ /dev/null @@ -1,105 +0,0 @@ -package app.revanced.patches.music.misc.spoof - -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.patch.bytecodePatch -import app.revanced.patcher.util.proxy.mutableTypes.MutableMethod.Companion.toMutable -import app.revanced.patches.music.misc.extension.sharedExtensionPatch -import app.revanced.util.getReference -import com.android.tools.smali.dexlib2.AccessFlags -import com.android.tools.smali.dexlib2.Opcode -import com.android.tools.smali.dexlib2.builder.MutableMethodImplementation -import com.android.tools.smali.dexlib2.iface.instruction.OneRegisterInstruction -import com.android.tools.smali.dexlib2.iface.reference.FieldReference -import com.android.tools.smali.dexlib2.iface.reference.TypeReference -import com.android.tools.smali.dexlib2.immutable.ImmutableMethod -import com.android.tools.smali.dexlib2.immutable.ImmutableMethodParameter - -private const val EXTENSION_CLASS_DESCRIPTOR = - "Lapp/revanced/extension/music/spoof/SpoofClientPatch;" - -// TODO: Replace this patch with spoofVideoStreamsPatch once possible. -val spoofClientPatch = bytecodePatch( - name = "Spoof client", - description = "Spoofs the client to fix playback.", -) { - compatibleWith("com.google.android.apps.youtube.music") - - dependsOn( - sharedExtensionPatch, - // TODO: Add settingsPatch - userAgentClientSpoofPatch, - ) - - execute { - val playerRequestClass = playerRequestConstructorFingerprint.classDef - - val createPlayerRequestBodyMatch = createPlayerRequestBodyFingerprint.match(playerRequestClass) - - val clientInfoContainerClass = createPlayerRequestBodyMatch.method - .getInstruction(createPlayerRequestBodyMatch.patternMatch!!.startIndex) - .getReference()!!.type - - val clientInfoField = setClientInfoClientVersionFingerprint.method.instructions.first { - it.opcode == Opcode.IPUT_OBJECT && it.getReference()!!.definingClass == clientInfoContainerClass - }.getReference()!! - - val setClientInfoFieldInstructions = setClientInfoFieldsFingerprint.method.instructions.filter { - (it.opcode == Opcode.IPUT_OBJECT || it.opcode == Opcode.IPUT) && - it.getReference()!!.definingClass == clientInfoField.type - }.map { it.getReference()!! } - - // Offsets are known for the fields in the clientInfo object. - val clientIdField = setClientInfoFieldInstructions[0] - val clientModelField = setClientInfoFieldInstructions[5] - val osVersionField = setClientInfoFieldInstructions[7] - val clientVersionField = setClientInfoClientVersionFingerprint.method - .getInstruction(setClientInfoClientVersionFingerprint.stringMatches!!.first().index + 1) - .getReference() - - // Helper method to spoof the client info. - val spoofClientInfoMethod = ImmutableMethod( - playerRequestClass.type, - "spoofClientInfo", - listOf(ImmutableMethodParameter(clientInfoContainerClass, null, null)), - "V", - AccessFlags.PRIVATE.value or AccessFlags.STATIC.value, - null, - null, - MutableMethodImplementation(3), - ).toMutable().also(playerRequestClass.methods::add).apply { - addInstructions( - """ - iget-object v0, p0, $clientInfoField - - invoke-static { }, $EXTENSION_CLASS_DESCRIPTOR->getClientId()I - move-result v1 - iput v1, v0, $clientIdField - - invoke-static { }, $EXTENSION_CLASS_DESCRIPTOR->getClientModel()Ljava/lang/String; - move-result-object v1 - iput-object v1, v0, $clientModelField - - invoke-static { }, $EXTENSION_CLASS_DESCRIPTOR->getClientVersion()Ljava/lang/String; - move-result-object v1 - iput-object v1, v0, $clientVersionField - - invoke-static { }, $EXTENSION_CLASS_DESCRIPTOR->getOsVersion()Ljava/lang/String; - move-result-object v1 - iput-object v1, v0, $osVersionField - - return-void - """, - ) - } - - createPlayerRequestBodyMatch.method.apply { - val checkCastIndex = createPlayerRequestBodyMatch.patternMatch!!.startIndex - val clientInfoContainerRegister = getInstruction(checkCastIndex).registerA - - addInstruction(checkCastIndex + 1, "invoke-static {v$clientInfoContainerRegister}, $spoofClientInfoMethod") - } - } -} diff --git a/patches/src/main/kotlin/app/revanced/patches/music/misc/spoof/SpoofVideoStreams.kt b/patches/src/main/kotlin/app/revanced/patches/music/misc/spoof/SpoofVideoStreams.kt new file mode 100644 index 000000000..10c65dae1 --- /dev/null +++ b/patches/src/main/kotlin/app/revanced/patches/music/misc/spoof/SpoofVideoStreams.kt @@ -0,0 +1,21 @@ +package app.revanced.patches.music.misc.spoof + +import app.revanced.patches.music.playservice.is_7_33_or_greater +import app.revanced.patches.music.playservice.is_8_11_or_greater +import app.revanced.patches.music.playservice.versionCheckPatch +import app.revanced.patches.shared.misc.spoof.spoofVideoStreamsPatch + +val spoofVideoStreamsPatch = spoofVideoStreamsPatch( + block = { + compatibleWith( + "com.google.android.apps.youtube.music"( + "7.29.52" + ) + ) + + dependsOn(versionCheckPatch, userAgentClientSpoofPatch) + }, + fixMediaFetchHotConfigChanges = { true }, + fixMediaFetchHotConfigAlternativeChanges = { is_8_11_or_greater }, + fixParsePlaybackResponseFeatureFlag = { is_7_33_or_greater } +) \ No newline at end of file diff --git a/patches/src/main/kotlin/app/revanced/patches/music/playservice/VersionCheckPatch.kt b/patches/src/main/kotlin/app/revanced/patches/music/playservice/VersionCheckPatch.kt new file mode 100644 index 000000000..86fbdc132 --- /dev/null +++ b/patches/src/main/kotlin/app/revanced/patches/music/playservice/VersionCheckPatch.kt @@ -0,0 +1,25 @@ +@file:Suppress("ktlint:standard:property-naming") + +package app.revanced.patches.music.playservice + +import app.revanced.patcher.patch.resourcePatch +import app.revanced.util.findPlayStoreServicesVersion + +var is_7_33_or_greater = false + private set +var is_8_11_or_greater = false + private set + +val versionCheckPatch = resourcePatch( + description = "Uses the Play Store service version to find the major/minor version of the YouTube Music target app.", +) { + execute { + // The app version is missing from the decompiled manifest, + // so instead use the Google Play services version and compare against specific releases. + val playStoreServicesVersion = findPlayStoreServicesVersion() + + // All bug fix releases always seem to use the same play store version as the minor version. + is_7_33_or_greater = 245199000 <= playStoreServicesVersion + is_8_11_or_greater = 251199000 <= playStoreServicesVersion + } +} diff --git a/patches/src/main/kotlin/app/revanced/patches/shared/misc/spoof/Fingerprints.kt b/patches/src/main/kotlin/app/revanced/patches/shared/misc/spoof/Fingerprints.kt index e6169bacb..cbeea3f78 100644 --- a/patches/src/main/kotlin/app/revanced/patches/shared/misc/spoof/Fingerprints.kt +++ b/patches/src/main/kotlin/app/revanced/patches/shared/misc/spoof/Fingerprints.kt @@ -148,7 +148,7 @@ internal val mediaFetchHotConfigFingerprint = fingerprint { literal { MEDIA_FETCH_HOT_CONFIG_FEATURE_FLAG } } -// 20.10+ +// YT 20.10+, YT Music 8.11+ internal const val MEDIA_FETCH_HOT_CONFIG_ALTERNATIVE_FEATURE_FLAG = 45683169L internal val mediaFetchHotConfigAlternativeFingerprint = fingerprint { @@ -162,7 +162,6 @@ internal val mediaFetchHotConfigAlternativeFingerprint = fingerprint { internal const val PLAYBACK_START_CHECK_ENDPOINT_USED_FEATURE_FLAG = 45665455L internal val playbackStartDescriptorFeatureFlagFingerprint = fingerprint { - accessFlags(AccessFlags.PUBLIC, AccessFlags.FINAL) parameters() returns("Z") literal { PLAYBACK_START_CHECK_ENDPOINT_USED_FEATURE_FLAG } diff --git a/patches/src/main/kotlin/app/revanced/patches/shared/misc/spoof/SpoofVideoStreamsPatch.kt b/patches/src/main/kotlin/app/revanced/patches/shared/misc/spoof/SpoofVideoStreamsPatch.kt index 78deda394..4cb3ffac3 100644 --- a/patches/src/main/kotlin/app/revanced/patches/shared/misc/spoof/SpoofVideoStreamsPatch.kt +++ b/patches/src/main/kotlin/app/revanced/patches/shared/misc/spoof/SpoofVideoStreamsPatch.kt @@ -53,9 +53,8 @@ fun spoofVideoStreamsPatch( // region Block /initplayback requests to fall back to /get_watch requests. - val moveUriStringIndex = buildInitPlaybackRequestFingerprint.patternMatch!!.startIndex - buildInitPlaybackRequestFingerprint.method.apply { + val moveUriStringIndex = buildInitPlaybackRequestFingerprint.patternMatch!!.startIndex val targetRegister = getInstruction(moveUriStringIndex).registerA addInstructions( @@ -63,7 +62,7 @@ fun spoofVideoStreamsPatch( """ invoke-static { v$targetRegister }, $EXTENSION_CLASS_DESCRIPTOR->blockInitPlaybackRequest(Ljava/lang/String;)Ljava/lang/String; move-result-object v$targetRegister - """, + """ ) } @@ -71,9 +70,8 @@ fun spoofVideoStreamsPatch( // region Block /get_watch requests to fall back to /player requests. - val invokeToStringIndex = buildPlayerRequestURIFingerprint.patternMatch!!.startIndex - buildPlayerRequestURIFingerprint.method.apply { + val invokeToStringIndex = buildPlayerRequestURIFingerprint.patternMatch!!.startIndex val uriRegister = getInstruction(invokeToStringIndex).registerC addInstructions( @@ -81,7 +79,7 @@ fun spoofVideoStreamsPatch( """ invoke-static { v$uriRegister }, $EXTENSION_CLASS_DESCRIPTOR->blockGetWatchRequest(Landroid/net/Uri;)Landroid/net/Uri; move-result-object v$uriRegister - """, + """ ) } @@ -178,9 +176,9 @@ fun spoofVideoStreamsPatch( :disabled return-void - """, + """ ) - }, + } ) } @@ -199,17 +197,17 @@ fun spoofVideoStreamsPatch( addInstructions( targetIndex, """ - # Field a: Stream uri. - # Field c: Http method. - # Field d: Post data. - move-object v0, p0 # method has over 15 registers and must copy p0 to a lower register. - iget-object v1, v0, $definingClass->a:Landroid/net/Uri; - iget v2, v0, $definingClass->c:I - iget-object v3, v0, $definingClass->d:[B - invoke-static { v1, v2, v3 }, $EXTENSION_CLASS_DESCRIPTOR->removeVideoPlaybackPostBody(Landroid/net/Uri;I[B)[B - move-result-object v1 - iput-object v1, v0, $definingClass->d:[B - """, + # Field a: Stream uri. + # Field c: Http method. + # Field d: Post data. + move-object v0, p0 # method has over 15 registers and must copy p0 to a lower register. + iget-object v1, v0, $definingClass->a:Landroid/net/Uri; + iget v2, v0, $definingClass->c:I + iget-object v3, v0, $definingClass->d:[B + invoke-static { v1, v2, v3 }, $EXTENSION_CLASS_DESCRIPTOR->removeVideoPlaybackPostBody(Landroid/net/Uri;I[B)[B + move-result-object v1 + iput-object v1, v0, $definingClass->d:[B + """ ) } diff --git a/patches/src/main/kotlin/app/revanced/patches/youtube/misc/playservice/VersionCheckPatch.kt b/patches/src/main/kotlin/app/revanced/patches/youtube/misc/playservice/VersionCheckPatch.kt index 4b29ca62c..86621209c 100644 --- a/patches/src/main/kotlin/app/revanced/patches/youtube/misc/playservice/VersionCheckPatch.kt +++ b/patches/src/main/kotlin/app/revanced/patches/youtube/misc/playservice/VersionCheckPatch.kt @@ -3,7 +3,7 @@ package app.revanced.patches.youtube.misc.playservice import app.revanced.patcher.patch.resourcePatch -import app.revanced.util.findElementByAttributeValueOrThrow +import app.revanced.util.findPlayStoreServicesVersion @Deprecated("19.34.42 is the lowest supported version") var is_19_03_or_greater = false @@ -77,12 +77,7 @@ val versionCheckPatch = resourcePatch( execute { // The app version is missing from the decompiled manifest, // so instead use the Google Play services version and compare against specific releases. - val playStoreServicesVersion = document("res/values/integers.xml").use { document -> - document.documentElement.childNodes.findElementByAttributeValueOrThrow( - "name", - "google_play_services_version", - ).textContent.toInt() - } + val playStoreServicesVersion = findPlayStoreServicesVersion() // All bug fix releases always seem to use the same play store version as the minor version. is_19_03_or_greater = 240402000 <= playStoreServicesVersion diff --git a/patches/src/main/kotlin/app/revanced/util/ResourceUtils.kt b/patches/src/main/kotlin/app/revanced/util/ResourceUtils.kt index 9eb4115e5..61d7be350 100644 --- a/patches/src/main/kotlin/app/revanced/util/ResourceUtils.kt +++ b/patches/src/main/kotlin/app/revanced/util/ResourceUtils.kt @@ -178,3 +178,15 @@ internal fun Element.copyAttributesFrom(oldContainer: Element) { setAttribute(attr.name, attr.value) } } + +/** + * @return The play store services version. + */ +internal fun ResourcePatchContext.findPlayStoreServicesVersion(): Int = + document("res/values/integers.xml").use { document -> + document.documentElement.childNodes.findElementByAttributeValueOrThrow( + "name", + "google_play_services_version", + ).textContent.toInt() + } +