diff --git a/src/main/kotlin/app/revanced/patches/spotify/audio/annotation/DisableCaptureRestrictionCompatibility.kt b/src/main/kotlin/app/revanced/patches/spotify/audio/annotation/DisableCaptureRestrictionCompatibility.kt new file mode 100644 index 00000000..3aad515b --- /dev/null +++ b/src/main/kotlin/app/revanced/patches/spotify/audio/annotation/DisableCaptureRestrictionCompatibility.kt @@ -0,0 +1,12 @@ +package app.revanced.patches.spotify.audio.annotation + +import app.revanced.patcher.annotation.Compatibility +import app.revanced.patcher.annotation.Package + +@Compatibility( + [Package("com.spotify.music")] +) +@Target(AnnotationTarget.CLASS) +@Retention(AnnotationRetention.RUNTIME) +internal annotation class DisableCaptureRestrictionCompatibility + diff --git a/src/main/kotlin/app/revanced/patches/spotify/audio/bytecode/patch/DisableCaptureRestrictionBytecodePatch.kt b/src/main/kotlin/app/revanced/patches/spotify/audio/bytecode/patch/DisableCaptureRestrictionBytecodePatch.kt new file mode 100644 index 00000000..a6e60bdd --- /dev/null +++ b/src/main/kotlin/app/revanced/patches/spotify/audio/bytecode/patch/DisableCaptureRestrictionBytecodePatch.kt @@ -0,0 +1,53 @@ +package app.revanced.patches.spotify.audio.bytecode.patch + +import app.revanced.patcher.annotation.Description +import app.revanced.patcher.annotation.Name +import app.revanced.patcher.annotation.Version +import app.revanced.patcher.data.impl.BytecodeData +import app.revanced.patcher.extensions.instruction +import app.revanced.patcher.extensions.replaceInstruction +import app.revanced.patcher.patch.PatchResult +import app.revanced.patcher.patch.PatchResultSuccess +import app.revanced.patcher.patch.annotations.DependsOn +import app.revanced.patcher.patch.annotations.Patch +import app.revanced.patcher.patch.impl.BytecodePatch +import app.revanced.patcher.util.proxy.mutableTypes.MutableMethod +import app.revanced.patches.spotify.audio.annotation.DisableCaptureRestrictionCompatibility +import app.revanced.patches.spotify.audio.fingerprints.DisableCaptureRestrictionAudioDriverFingerprint +import app.revanced.patches.spotify.audio.resource.patch.DisableCaptureRestrictionResourcePatch +import org.jf.dexlib2.iface.instruction.Instruction +import org.jf.dexlib2.iface.instruction.OneRegisterInstruction + +@Patch +@Name("disable-capture-restriction") +@DependsOn([DisableCaptureRestrictionResourcePatch::class]) +@Description("Allows capturing Spotify's audio output while screen sharing or screen recording.") +@DisableCaptureRestrictionCompatibility +@Version("0.0.1") +class DisableCaptureRestrictionBytecodePatch : BytecodePatch( + listOf( + DisableCaptureRestrictionAudioDriverFingerprint + ) +) { + private fun MutableMethod.replaceConstant4Instruction(index: Int, instruction: Instruction, with: Int) { + val register = (instruction as OneRegisterInstruction).registerA + this.replaceInstruction( + index, "const/4 v$register, $with" + ) + } + + override fun execute(data: BytecodeData): PatchResult { + val method = DisableCaptureRestrictionAudioDriverFingerprint.result!!.mutableMethod + + // Replace constant that contains the capture policy parameter for AudioAttributesBuilder.setAllowedCapturePolicy() + val instruction = method.instruction(CONST_INSTRUCTION_POSITION) + method.replaceConstant4Instruction(CONST_INSTRUCTION_POSITION, instruction, ALLOW_CAPTURE_BY_ALL) + + return PatchResultSuccess() + } + + private companion object { + const val CONST_INSTRUCTION_POSITION = 2 + const val ALLOW_CAPTURE_BY_ALL = 0x01 + } +} \ No newline at end of file diff --git a/src/main/kotlin/app/revanced/patches/spotify/audio/fingerprints/DisableCaptureRestrictionAudioDriverFingerprint.kt b/src/main/kotlin/app/revanced/patches/spotify/audio/fingerprints/DisableCaptureRestrictionAudioDriverFingerprint.kt new file mode 100644 index 00000000..fcd16852 --- /dev/null +++ b/src/main/kotlin/app/revanced/patches/spotify/audio/fingerprints/DisableCaptureRestrictionAudioDriverFingerprint.kt @@ -0,0 +1,21 @@ +package app.revanced.patches.spotify.audio.fingerprints + +import app.revanced.patcher.annotation.Name +import app.revanced.patcher.annotation.Version +import app.revanced.patcher.fingerprint.method.annotation.DirectPatternScanMethod +import app.revanced.patcher.fingerprint.method.annotation.MatchingMethod +import app.revanced.patcher.fingerprint.method.impl.MethodFingerprint +import app.revanced.patches.spotify.audio.annotation.DisableCaptureRestrictionCompatibility + +@Name("disable-capture-restriction-audio-driver-fingerprint") +@MatchingMethod( + "Lcom/spotify/playback/playbacknative/AudioDriver;", "constructAudioAttributes" +) +@DirectPatternScanMethod +@DisableCaptureRestrictionCompatibility +@Version("0.0.1") +object DisableCaptureRestrictionAudioDriverFingerprint : MethodFingerprint( + customFingerprint = { methodDef -> + methodDef.definingClass == "Lcom/spotify/playback/playbacknative/AudioDriver;" && methodDef.name == "constructAudioAttributes" + } +) \ No newline at end of file diff --git a/src/main/kotlin/app/revanced/patches/spotify/audio/resource/patch/DisableCaptureRestrictionResourcePatch.kt b/src/main/kotlin/app/revanced/patches/spotify/audio/resource/patch/DisableCaptureRestrictionResourcePatch.kt new file mode 100644 index 00000000..85c39ef4 --- /dev/null +++ b/src/main/kotlin/app/revanced/patches/spotify/audio/resource/patch/DisableCaptureRestrictionResourcePatch.kt @@ -0,0 +1,35 @@ +package app.revanced.patches.spotify.audio.resource.patch + +import app.revanced.patcher.annotation.Description +import app.revanced.patcher.annotation.Name +import app.revanced.patcher.annotation.Version +import app.revanced.patcher.data.impl.ResourceData +import app.revanced.patcher.patch.PatchResult +import app.revanced.patcher.patch.PatchResultSuccess +import app.revanced.patcher.patch.annotations.DependsOn +import app.revanced.patcher.patch.impl.ResourcePatch +import app.revanced.patches.spotify.audio.annotation.DisableCaptureRestrictionCompatibility +import app.revanced.patches.youtube.misc.manifest.patch.FixLocaleConfigErrorPatch +import org.w3c.dom.Element + +@Name("disable-capture-restriction-resource-patch") +@Description("Sets allowAudioPlaybackCapture in manifest to true.") +@DisableCaptureRestrictionCompatibility +@Version("0.0.1") +class DisableCaptureRestrictionResourcePatch : ResourcePatch() { + override fun execute(data: ResourceData): PatchResult { + // create an xml editor instance + data.xmlEditor["AndroidManifest.xml"].use { dom -> + // get the application node + val applicationNode = dom + .file + .getElementsByTagName("application") + .item(0) as Element + + // set allowAudioPlaybackCapture attribute to true + applicationNode.setAttribute("android:allowAudioPlaybackCapture", "true") + } + + return PatchResultSuccess() + } +} \ No newline at end of file