diff --git a/src/main/kotlin/app/revanced/patches/twitch/ad/video/annotations/VideoAdsCompatibility.kt b/src/main/kotlin/app/revanced/patches/twitch/ad/video/annotations/VideoAdsCompatibility.kt new file mode 100644 index 000000000..60c4d169c --- /dev/null +++ b/src/main/kotlin/app/revanced/patches/twitch/ad/video/annotations/VideoAdsCompatibility.kt @@ -0,0 +1,10 @@ +package app.revanced.patches.twitch.ad.video.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 VideoAdsCompatibility + diff --git a/src/main/kotlin/app/revanced/patches/twitch/ad/video/fingerprints/AdsManagerFingerprint.kt b/src/main/kotlin/app/revanced/patches/twitch/ad/video/fingerprints/AdsManagerFingerprint.kt new file mode 100644 index 000000000..535da8bfe --- /dev/null +++ b/src/main/kotlin/app/revanced/patches/twitch/ad/video/fingerprints/AdsManagerFingerprint.kt @@ -0,0 +1,16 @@ +package app.revanced.patches.twitch.ad.video.fingerprints + +import app.revanced.patcher.annotation.Name +import app.revanced.patcher.annotation.Version + +import app.revanced.patcher.fingerprint.method.impl.MethodFingerprint +import app.revanced.patches.twitch.ad.video.annotations.VideoAdsCompatibility + +@Name("ads-manager-play-fingerprint") +@VideoAdsCompatibility +@Version("0.0.1") +object AdsManagerFingerprint : MethodFingerprint( + customFingerprint = { method -> + 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/fingerprints/CheckAdEligibilityLambdaFingerprint.kt b/src/main/kotlin/app/revanced/patches/twitch/ad/video/fingerprints/CheckAdEligibilityLambdaFingerprint.kt new file mode 100644 index 000000000..298abe479 --- /dev/null +++ b/src/main/kotlin/app/revanced/patches/twitch/ad/video/fingerprints/CheckAdEligibilityLambdaFingerprint.kt @@ -0,0 +1,22 @@ +package app.revanced.patches.twitch.ad.video.fingerprints + +import app.revanced.patcher.annotation.Name +import app.revanced.patcher.annotation.Version +import app.revanced.patcher.extensions.or + +import app.revanced.patcher.fingerprint.method.impl.MethodFingerprint +import app.revanced.patches.twitch.ad.video.annotations.VideoAdsCompatibility +import org.jf.dexlib2.AccessFlags + +@Name("check-ad-eligibility-fingerprint") +@VideoAdsCompatibility +@Version("0.0.1") +object CheckAdEligibilityLambdaFingerprint : MethodFingerprint( + "L", + AccessFlags.PRIVATE or AccessFlags.FINAL or AccessFlags.STATIC, + listOf("L", "L", "L"), + customFingerprint = { method -> + method.definingClass.endsWith("AdEligibilityFetcher;") && + method.name.contains("shouldRequestAd") + } +) \ No newline at end of file diff --git a/src/main/kotlin/app/revanced/patches/twitch/ad/video/fingerprints/ContentConfigShowAdsFingerprint.kt b/src/main/kotlin/app/revanced/patches/twitch/ad/video/fingerprints/ContentConfigShowAdsFingerprint.kt new file mode 100644 index 000000000..af878a6e1 --- /dev/null +++ b/src/main/kotlin/app/revanced/patches/twitch/ad/video/fingerprints/ContentConfigShowAdsFingerprint.kt @@ -0,0 +1,16 @@ +package app.revanced.patches.twitch.ad.video.fingerprints + +import app.revanced.patcher.annotation.Name +import app.revanced.patcher.annotation.Version + +import app.revanced.patcher.fingerprint.method.impl.MethodFingerprint +import app.revanced.patches.twitch.ad.video.annotations.VideoAdsCompatibility + +@Name("content-config-show-ads-fingerprint") +@VideoAdsCompatibility +@Version("0.0.1") +object ContentConfigShowAdsFingerprint : MethodFingerprint( + customFingerprint = { method -> + method.definingClass.endsWith("ContentConfigData;") && method.name == "getShowAds" + } +) \ 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 new file mode 100644 index 000000000..e2ae8440e --- /dev/null +++ b/src/main/kotlin/app/revanced/patches/twitch/ad/video/patch/VideoAdsPatch.kt @@ -0,0 +1,60 @@ +package app.revanced.patches.twitch.ad.video.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.addInstruction +import app.revanced.patcher.extensions.addInstructions +import app.revanced.patcher.patch.BytecodePatch +import app.revanced.patcher.patch.PatchResult +import app.revanced.patcher.patch.PatchResultSuccess +import app.revanced.patcher.patch.annotations.Patch +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 + +@Patch +@Name("block-video-ads") +@Description("Blocks video ads in streams and VODs.") +@VideoAdsCompatibility +@Version("0.0.1") +class VideoAdsPatch : BytecodePatch( + listOf( + ContentConfigShowAdsFingerprint, + AdsManagerFingerprint, + CheckAdEligibilityLambdaFingerprint + ) +) { + override fun execute(context: BytecodeContext): PatchResult { + // Pretend our player is ineligible for all ads + with(CheckAdEligibilityLambdaFingerprint.result!!) { + mutableMethod.addInstructions( + 0, + """ + const/4 v0, 0 + invoke-static {v0}, Lio/reactivex/Single;->just(Ljava/lang/Object;)Lio/reactivex/Single; + move-result-object p0 + return-object p0 + """ + ) + } + + // Spoof showAds JSON field + with(ContentConfigShowAdsFingerprint.result!!) { + mutableMethod.addInstructions(0, """ + const/4 v0, 0 + return v0 + """ + ) + } + + // Block playAds call + with(AdsManagerFingerprint.result!!) { + mutableMethod.addInstruction(0, "return-void") + } + + return PatchResultSuccess() + } +}