diff --git a/src/main/kotlin/app/revanced/patches/shared/settings/preference/impl/ArrayResource.kt b/src/main/kotlin/app/revanced/patches/shared/settings/preference/impl/ArrayResource.kt index 608dccca4..cf5b0b816 100644 --- a/src/main/kotlin/app/revanced/patches/shared/settings/preference/impl/ArrayResource.kt +++ b/src/main/kotlin/app/revanced/patches/shared/settings/preference/impl/ArrayResource.kt @@ -25,7 +25,7 @@ internal data class ArrayResource( resourceCallback?.invoke(item) this.appendChild(ownerDocument.createElement("item").also { itemNode -> - itemNode.textContent = "@string/${item.name}" + itemNode.textContent = item.value }) } } diff --git a/src/main/kotlin/app/revanced/patches/shared/settings/resource/patch/AbstractSettingsResourcePatch.kt b/src/main/kotlin/app/revanced/patches/shared/settings/resource/patch/AbstractSettingsResourcePatch.kt index f62c24b21..c8197a1a1 100644 --- a/src/main/kotlin/app/revanced/patches/shared/settings/resource/patch/AbstractSettingsResourcePatch.kt +++ b/src/main/kotlin/app/revanced/patches/shared/settings/resource/patch/AbstractSettingsResourcePatch.kt @@ -92,7 +92,7 @@ abstract class AbstractSettingsResourcePatch( * @param arrayResource The array resource to add. */ fun addArray(arrayResource: ArrayResource) = - arraysNode!!.addResource(arrayResource) { it.include() } + arraysNode!!.addResource(arrayResource) /** * Add a preference to the settings. diff --git a/src/main/kotlin/app/revanced/patches/twitch/ad/embedded/annotations/EmbeddedAdsCompatibility.kt b/src/main/kotlin/app/revanced/patches/twitch/ad/embedded/annotations/EmbeddedAdsCompatibility.kt deleted file mode 100644 index 4103c9405..000000000 --- a/src/main/kotlin/app/revanced/patches/twitch/ad/embedded/annotations/EmbeddedAdsCompatibility.kt +++ /dev/null @@ -1,10 +0,0 @@ -package app.revanced.patches.twitch.ad.embedded.annotations - -import app.revanced.patcher.annotation.Compatibility -import app.revanced.patcher.annotation.Package - -@Compatibility([Package("tv.twitch.android.app")]) -@Target(AnnotationTarget.CLASS) -@Retention(AnnotationRetention.RUNTIME) -internal annotation class EmbeddedAdsCompatibility - diff --git a/src/main/kotlin/app/revanced/patches/twitch/ad/embedded/fingerprints/CreateUsherClientFingerprint.kt b/src/main/kotlin/app/revanced/patches/twitch/ad/embedded/fingerprints/CreateUsherClientFingerprint.kt deleted file mode 100644 index 348044040..000000000 --- a/src/main/kotlin/app/revanced/patches/twitch/ad/embedded/fingerprints/CreateUsherClientFingerprint.kt +++ /dev/null @@ -1,9 +0,0 @@ -package app.revanced.patches.twitch.ad.embedded.fingerprints - -import app.revanced.patcher.fingerprint.method.impl.MethodFingerprint - -object CreateUsherClientFingerprint : MethodFingerprint( - customFingerprint = { method -> - method.definingClass.endsWith("Ltv/twitch/android/network/OkHttpClientFactory;") && method.name == "buildOkHttpClient" - } -) \ No newline at end of file diff --git a/src/main/kotlin/app/revanced/patches/twitch/ad/embedded/patch/EmbeddedAdsPatch.kt b/src/main/kotlin/app/revanced/patches/twitch/ad/embedded/patch/EmbeddedAdsPatch.kt deleted file mode 100644 index cf8f53e63..000000000 --- a/src/main/kotlin/app/revanced/patches/twitch/ad/embedded/patch/EmbeddedAdsPatch.kt +++ /dev/null @@ -1,78 +0,0 @@ -package app.revanced.patches.twitch.ad.embedded.patch - -import app.revanced.patcher.annotation.Description -import app.revanced.patcher.annotation.Name -import app.revanced.patcher.annotation.Version -import app.revanced.patcher.data.BytecodeContext -import app.revanced.patcher.extensions.MethodFingerprintExtensions.name -import app.revanced.patcher.extensions.addInstructions -import app.revanced.patcher.patch.BytecodePatch -import app.revanced.patcher.patch.PatchResult -import app.revanced.patcher.patch.PatchResultError -import app.revanced.patcher.patch.PatchResultSuccess -import app.revanced.patcher.patch.annotations.DependsOn -import app.revanced.patcher.patch.annotations.Patch -import app.revanced.patches.shared.settings.preference.impl.ArrayResource -import app.revanced.patches.shared.settings.preference.impl.ListPreference -import app.revanced.patches.shared.settings.preference.impl.StringResource -import app.revanced.patches.twitch.ad.embedded.annotations.EmbeddedAdsCompatibility -import app.revanced.patches.twitch.ad.embedded.fingerprints.CreateUsherClientFingerprint -import app.revanced.patches.twitch.ad.video.patch.VideoAdsPatch -import app.revanced.patches.twitch.misc.integrations.patch.IntegrationsPatch -import app.revanced.patches.twitch.misc.settings.bytecode.patch.SettingsPatch - -@Patch -@DependsOn([VideoAdsPatch::class, IntegrationsPatch::class, SettingsPatch::class]) -@Name("block-embedded-ads") -@Description("Blocks embedded steam ads using services like TTV.lol or PurpleAdBlocker.") -@EmbeddedAdsCompatibility -@Version("0.0.1") -class EmbeddedAdsPatch : BytecodePatch( - listOf(CreateUsherClientFingerprint) -) { - override fun execute(context: BytecodeContext): PatchResult { - val result = CreateUsherClientFingerprint.result ?: return PatchResultError("${CreateUsherClientFingerprint.name} not found") - - // Inject OkHttp3 application interceptor - result.mutableMethod.addInstructions( - 3, - """ - invoke-static {}, Lapp/revanced/twitch/patches/EmbeddedAdsPatch;->createRequestInterceptor()Lapp/revanced/twitch/api/RequestInterceptor; - move-result-object v2 - invoke-virtual {v0, v2}, Lokhttp3/OkHttpClient${"$"}Builder;->addInterceptor(Lokhttp3/Interceptor;)Lokhttp3/OkHttpClient${"$"}Builder; - """ - ) - - SettingsPatch.PreferenceScreen.ADS.SURESTREAM.addPreferences( - ListPreference( - "revanced_block_embedded_ads", - StringResource( - "revanced_block_embedded_ads", - "Block embedded video ads" - ), - ArrayResource( - "revanced_hls_proxies", - listOf( - StringResource("revanced_proxy_disabled", "Disabled"), - StringResource("revanced_proxy_ttv_lol", "TTV LOL proxy"), - StringResource("revanced_proxy_purpleadblock", "PurpleAdBlock proxy"), - ) - ), - ArrayResource( - "revanced_hls_proxies_values", - listOf( - StringResource("key_revanced_proxy_disabled", "disabled"), - StringResource("key_revanced_proxy_ttv_lol", "ttv-lol"), - StringResource("key_revanced_proxy_purpleadblock", "purpleadblock") - ) - ), - "ttv-lol" - ) - ) - - SettingsPatch.addString("revanced_embedded_ads_service_unavailable", "%s is unavailable. Ads may show. Try switching to another ad block service in settings.") - SettingsPatch.addString("revanced_embedded_ads_service_failed", "%s server returned an error. Ads may show. Try switching to another ad block service in settings.") - - return PatchResultSuccess() - } -} diff --git a/src/main/kotlin/app/revanced/patches/twitch/ad/shared/util/AbstractAdPatch.kt b/src/main/kotlin/app/revanced/patches/twitch/ad/shared/util/AbstractAdPatch.kt deleted file mode 100644 index dca265bc7..000000000 --- a/src/main/kotlin/app/revanced/patches/twitch/ad/shared/util/AbstractAdPatch.kt +++ /dev/null @@ -1,51 +0,0 @@ -package app.revanced.patches.twitch.ad.shared.util - -import app.revanced.patcher.data.BytecodeContext -import app.revanced.patcher.extensions.addInstructions -import app.revanced.patcher.extensions.instruction -import app.revanced.patcher.fingerprint.method.impl.MethodFingerprint -import app.revanced.patcher.patch.BytecodePatch -import app.revanced.patcher.util.smali.ExternalLabel - -abstract class AbstractAdPatch( - val conditionCall: String, - val skipLabelName: String, - internal val fingerprints: Iterable? = null, -) : BytecodePatch(fingerprints) { - - protected fun createConditionInstructions(register: String = "v0") = """ - invoke-static { }, $conditionCall - move-result $register - if-eqz $register, :$skipLabelName - """ - - protected data class ReturnMethod(val returnType: Char = 'V', val value: String = "") - - protected fun BytecodeContext.blockMethods(clazz: String, vararg methodNames: String, returnMethod: ReturnMethod = ReturnMethod()): Boolean { - - return with(findClass(clazz)?.mutableClass) { - this ?: return false - - this.methods.filter { methodNames.contains(it.name) }.forEach { - val retIntructions = when(returnMethod.returnType) { - 'V' -> "return-void" - 'Z' -> """ - const/4 v0, ${returnMethod.value} - return v0 - """ - else -> throw NotImplementedError() - } - it.addInstructions( - 0, - """ - ${createConditionInstructions("v0")} - $retIntructions - """, - listOf(ExternalLabel(skipLabelName, it.instruction(0))) - ) - } - true - } - } - -} \ No newline at end of file diff --git a/src/main/kotlin/app/revanced/patches/twitch/ad/video/fingerprints/GetReadyToShowAdFingerprint.kt b/src/main/kotlin/app/revanced/patches/twitch/ad/video/fingerprints/AdsManagerFingerprint.kt similarity index 50% rename from src/main/kotlin/app/revanced/patches/twitch/ad/video/fingerprints/GetReadyToShowAdFingerprint.kt rename to src/main/kotlin/app/revanced/patches/twitch/ad/video/fingerprints/AdsManagerFingerprint.kt index 3256ad024..770192cd0 100644 --- a/src/main/kotlin/app/revanced/patches/twitch/ad/video/fingerprints/GetReadyToShowAdFingerprint.kt +++ b/src/main/kotlin/app/revanced/patches/twitch/ad/video/fingerprints/AdsManagerFingerprint.kt @@ -1,9 +1,10 @@ package app.revanced.patches.twitch.ad.video.fingerprints + import app.revanced.patcher.fingerprint.method.impl.MethodFingerprint -object GetReadyToShowAdFingerprint : MethodFingerprint( +object AdsManagerFingerprint : MethodFingerprint( customFingerprint = { method -> - method.definingClass.endsWith("/StreamDisplayAdsPresenter;") && method.name == "getReadyToShowAdOrAbort" + method.definingClass.endsWith("AdsManagerImpl;") && method.name == "playAds" } ) \ No newline at end of file diff --git a/src/main/kotlin/app/revanced/patches/twitch/ad/video/patch/VideoAdsPatch.kt b/src/main/kotlin/app/revanced/patches/twitch/ad/video/patch/VideoAdsPatch.kt index 9b1197e78..5f2acf66d 100644 --- a/src/main/kotlin/app/revanced/patches/twitch/ad/video/patch/VideoAdsPatch.kt +++ b/src/main/kotlin/app/revanced/patches/twitch/ad/video/patch/VideoAdsPatch.kt @@ -6,6 +6,7 @@ import app.revanced.patcher.annotation.Version import app.revanced.patcher.data.BytecodeContext import app.revanced.patcher.extensions.addInstructions import app.revanced.patcher.extensions.instruction +import app.revanced.patcher.patch.BytecodePatch import app.revanced.patcher.patch.PatchResult import app.revanced.patcher.patch.PatchResultSuccess import app.revanced.patcher.patch.annotations.DependsOn @@ -13,11 +14,10 @@ import app.revanced.patcher.patch.annotations.Patch import app.revanced.patcher.util.smali.ExternalLabel import app.revanced.patches.shared.settings.preference.impl.StringResource import app.revanced.patches.shared.settings.preference.impl.SwitchPreference -import app.revanced.patches.twitch.ad.shared.util.AbstractAdPatch import app.revanced.patches.twitch.ad.video.annotations.VideoAdsCompatibility +import app.revanced.patches.twitch.ad.video.fingerprints.AdsManagerFingerprint import app.revanced.patches.twitch.ad.video.fingerprints.CheckAdEligibilityLambdaFingerprint import app.revanced.patches.twitch.ad.video.fingerprints.ContentConfigShowAdsFingerprint -import app.revanced.patches.twitch.ad.video.fingerprints.GetReadyToShowAdFingerprint import app.revanced.patches.twitch.misc.integrations.patch.IntegrationsPatch import app.revanced.patches.twitch.misc.settings.bytecode.patch.SettingsPatch @@ -27,63 +27,20 @@ import app.revanced.patches.twitch.misc.settings.bytecode.patch.SettingsPatch @Description("Blocks video ads in streams and VODs.") @VideoAdsCompatibility @Version("0.0.1") -class VideoAdsPatch : AbstractAdPatch( - "Lapp/revanced/twitch/patches/VideoAdsPatch;->shouldBlockVideoAds()Z", - "show_video_ads", +class VideoAdsPatch : BytecodePatch( listOf( ContentConfigShowAdsFingerprint, - CheckAdEligibilityLambdaFingerprint, - GetReadyToShowAdFingerprint + AdsManagerFingerprint, + CheckAdEligibilityLambdaFingerprint ) ) { + private fun createConditionInstructions(register: String = "v0") = """ + invoke-static { }, Lapp/revanced/twitch/patches/VideoAdsPatch;->shouldBlockVideoAds()Z + move-result $register + if-eqz $register, :show_video_ads + """ + override fun execute(context: BytecodeContext): PatchResult { - /* Amazon ads SDK */ - context.blockMethods( - "Lcom/amazon/ads/video/player/AdsManagerImpl;", - "playAds" - ) - - /* Twitch ads manager */ - context.blockMethods( - "Ltv/twitch/android/shared/ads/VideoAdManager;", - "checkAdEligibilityAndRequestAd", "requestAd", "requestAds" - ) - - /* Various ad presenters */ - context.blockMethods( - "Ltv/twitch/android/shared/ads/AdsPlayerPresenter;", - "requestAd", "requestFirstAd", "requestFirstAdIfEligible", "requestMidroll", "requestAdFromMultiAdFormatEvent" - ) - - context.blockMethods( - "Ltv/twitch/android/shared/ads/AdsVodPlayerPresenter;", - "requestAd", "requestFirstAd", - ) - - context.blockMethods( - "Ltv/twitch/android/feature/theatre/ads/AdEdgeAllocationPresenter;", - "parseAdAndCheckEligibility", "requestAdsAfterEligibilityCheck", "showAd", "bindMultiAdFormatAllocation" - ) - - /* A/B ad testing experiments */ - context.blockMethods( - "Ltv/twitch/android/provider/experiments/helpers/DisplayAdsExperimentHelper;", - "areDisplayAdsEnabled", - returnMethod = ReturnMethod('Z', "0") - ) - - context.blockMethods( - "Ltv/twitch/android/shared/ads/tracking/MultiFormatAdsTrackingExperiment;", - "shouldUseMultiAdFormatTracker", "shouldUseVideoAdTracker", - returnMethod = ReturnMethod('Z', "0") - ) - - context.blockMethods( - "Ltv/twitch/android/shared/ads/MultiformatAdsExperiment;", - "shouldDisableClientSideLivePreroll", "shouldDisableClientSideVodPreroll", - returnMethod = ReturnMethod('Z', "1") - ) - // Pretend our player is ineligible for all ads with(CheckAdEligibilityLambdaFingerprint.result!!) { mutableMethod.addInstructions( @@ -95,22 +52,7 @@ class VideoAdsPatch : AbstractAdPatch( move-result-object p0 return-object p0 """, - listOf(ExternalLabel(skipLabelName, mutableMethod.instruction(0))) - ) - } - - with(GetReadyToShowAdFingerprint.result!!) { - val adFormatDeclined = "Ltv/twitch/android/shared/display/ads/theatre/StreamDisplayAdsPresenter\$Action\$AdFormatDeclined;" - mutableMethod.addInstructions( - 0, - """ - ${createConditionInstructions()} - sget-object p2, $adFormatDeclined->INSTANCE:$adFormatDeclined - invoke-static {p1, p2}, Ltv/twitch/android/core/mvp/presenter/StateMachineKt;->plus(Ltv/twitch/android/core/mvp/presenter/PresenterState;Ltv/twitch/android/core/mvp/presenter/PresenterAction;)Ltv/twitch/android/core/mvp/presenter/StateAndAction; - move-result-object p1 - return-object p1 - """, - listOf(ExternalLabel(skipLabelName, mutableMethod.instruction(0))) + listOf(ExternalLabel("show_video_ads", mutableMethod.instruction(0))) ) } @@ -119,12 +61,24 @@ class VideoAdsPatch : AbstractAdPatch( mutableMethod.addInstructions(0, """ ${createConditionInstructions()} const/4 v0, 0 - :$skipLabelName + :show_video_ads return v0 """ ) } + // Block playAds call + with(AdsManagerFingerprint.result!!) { + mutableMethod.addInstructions( + 0, + """ + ${createConditionInstructions()} + return-void + """, + listOf(ExternalLabel("show_video_ads", mutableMethod.instruction(0))) + ) + } + SettingsPatch.PreferenceScreen.ADS.CLIENT_SIDE.addPreferences( SwitchPreference( "revanced_block_video_ads", diff --git a/src/main/kotlin/app/revanced/patches/twitch/misc/settings/bytecode/patch/SettingsPatch.kt b/src/main/kotlin/app/revanced/patches/twitch/misc/settings/bytecode/patch/SettingsPatch.kt index d0f8b7214..e2d0e51e5 100644 --- a/src/main/kotlin/app/revanced/patches/twitch/misc/settings/bytecode/patch/SettingsPatch.kt +++ b/src/main/kotlin/app/revanced/patches/twitch/misc/settings/bytecode/patch/SettingsPatch.kt @@ -173,7 +173,6 @@ class SettingsPatch : BytecodePatch( val GENERAL = CustomCategory("general", "General settings") val OTHER = CustomCategory("other", "Other settings") val CLIENT_SIDE = CustomCategory("client_ads", "Client-side ads") - val SURESTREAM = CustomCategory("surestream_ads", "Server-side surestream ads") internal inner class CustomCategory(key: String, title: String) : Screen.Category(key, title) { /* For Twitch, we need to load our CustomPreferenceCategory class instead of the default one. */