feat(Spotify): Add Change lyrics provider
patch (#4937)
This commit is contained in:
@ -1,11 +1,14 @@
|
||||
package app.revanced.extension.spotify.shared;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
import app.revanced.extension.shared.Logger;
|
||||
import app.revanced.extension.shared.Utils;
|
||||
|
||||
public final class ComponentFilters {
|
||||
|
||||
public interface ComponentFilter {
|
||||
@NonNull
|
||||
String getFilterValue();
|
||||
String getFilterRepresentation();
|
||||
default boolean filterUnavailable() {
|
||||
@ -20,7 +23,8 @@ public final class ComponentFilters {
|
||||
// Android resources are always positive, so -1 is a valid sentinel value to indicate it has not been loaded.
|
||||
// 0 is returned when a resource has not been found.
|
||||
private int resourceId = -1;
|
||||
private String stringfiedResourceId = null;
|
||||
@Nullable
|
||||
private String stringfiedResourceId;
|
||||
|
||||
public ResourceIdComponentFilter(String resourceName, String resourceType) {
|
||||
this.resourceName = resourceName;
|
||||
@ -34,6 +38,7 @@ public final class ComponentFilters {
|
||||
return resourceId;
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public String getFilterValue() {
|
||||
if (stringfiedResourceId == null) {
|
||||
@ -66,6 +71,7 @@ public final class ComponentFilters {
|
||||
this.string = string;
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public String getFilterValue() {
|
||||
return string;
|
||||
|
@ -921,6 +921,10 @@ public final class app/revanced/patches/spotify/misc/fix/login/FixFacebookLoginP
|
||||
public static final fun getFixFacebookLoginPatch ()Lapp/revanced/patcher/patch/BytecodePatch;
|
||||
}
|
||||
|
||||
public final class app/revanced/patches/spotify/misc/lyrics/ChangeLyricsProviderPatchKt {
|
||||
public static final fun getChangeLyricsProviderPatch ()Lapp/revanced/patcher/patch/BytecodePatch;
|
||||
}
|
||||
|
||||
public final class app/revanced/patches/spotify/misc/privacy/SanitizeSharingLinksPatchKt {
|
||||
public static final fun getSanitizeSharingLinksPatch ()Lapp/revanced/patcher/patch/BytecodePatch;
|
||||
}
|
||||
|
@ -0,0 +1,123 @@
|
||||
package app.revanced.patches.spotify.misc.lyrics
|
||||
|
||||
import app.revanced.patcher.extensions.InstructionExtensions.addInstruction
|
||||
import app.revanced.patcher.extensions.InstructionExtensions.getInstruction
|
||||
import app.revanced.patcher.extensions.InstructionExtensions.replaceInstruction
|
||||
import app.revanced.patcher.patch.bytecodePatch
|
||||
import app.revanced.patcher.patch.stringOption
|
||||
import app.revanced.patcher.util.proxy.mutableTypes.MutableMethod
|
||||
import app.revanced.patches.spotify.shared.IS_SPOTIFY_LEGACY_APP_TARGET
|
||||
import app.revanced.util.getReference
|
||||
import app.revanced.util.indexOfFirstInstructionOrThrow
|
||||
import app.revanced.util.indexOfFirstInstructionReversedOrThrow
|
||||
import com.android.tools.smali.dexlib2.builder.instruction.BuilderInstruction35c
|
||||
import com.android.tools.smali.dexlib2.iface.instruction.FiveRegisterInstruction
|
||||
import com.android.tools.smali.dexlib2.iface.reference.MethodReference
|
||||
import com.android.tools.smali.dexlib2.immutable.reference.ImmutableMethodReference
|
||||
import java.net.InetAddress
|
||||
import java.net.URI
|
||||
import java.net.URISyntaxException
|
||||
import java.net.UnknownHostException
|
||||
import java.util.logging.Logger
|
||||
|
||||
@Suppress("unused")
|
||||
val changeLyricsProviderPatch = bytecodePatch(
|
||||
name = "Change lyrics provider",
|
||||
description = "Changes the lyrics provider to a custom one.",
|
||||
use = false,
|
||||
) {
|
||||
compatibleWith("com.spotify.music")
|
||||
|
||||
val lyricsProviderHost by stringOption(
|
||||
key = "lyricsProviderHost",
|
||||
default = "lyrics.natanchiodi.fr",
|
||||
title = "Lyrics provider host",
|
||||
description = "The domain name or IP address of a custom lyrics provider.",
|
||||
required = false,
|
||||
) {
|
||||
// Fix bad data if the user enters a URL (https://whatever.com/path).
|
||||
val host = try {
|
||||
URI(it!!).host ?: it
|
||||
} catch (e: URISyntaxException) {
|
||||
return@stringOption false
|
||||
}
|
||||
|
||||
// Do a courtesy check if the host can be resolved.
|
||||
// If it does not resolve, then print a warning but use the host anyway.
|
||||
// Unresolvable hosts should not be rejected, since the patching environment
|
||||
// may not allow network connections or the network may be down.
|
||||
try {
|
||||
InetAddress.getByName(host)
|
||||
} catch (e: UnknownHostException) {
|
||||
Logger.getLogger(this::class.java.name).warning(
|
||||
"Host \"$host\" did not resolve to any domain."
|
||||
)
|
||||
}
|
||||
true
|
||||
}
|
||||
|
||||
execute {
|
||||
if (IS_SPOTIFY_LEGACY_APP_TARGET) {
|
||||
Logger.getLogger(this::class.java.name).severe(
|
||||
"Change lyrics provider patch is not supported for this target version."
|
||||
)
|
||||
return@execute
|
||||
}
|
||||
|
||||
val httpClientBuilderMethod = httpClientBuilderFingerprint.originalMethod
|
||||
|
||||
// region Create a modified copy of the HTTP client builder method with the custom lyrics provider host.
|
||||
val patchedHttpClientBuilderMethod = with(httpClientBuilderMethod) {
|
||||
val invokeBuildUrlIndex = indexOfFirstInstructionOrThrow {
|
||||
getReference<MethodReference>()?.returnType == "Lokhttp3/HttpUrl;"
|
||||
}
|
||||
val setUrlBuilderHostIndex = indexOfFirstInstructionReversedOrThrow(invokeBuildUrlIndex) {
|
||||
val reference = getReference<MethodReference>()
|
||||
reference?.definingClass == "Lokhttp3/HttpUrl${"$"}Builder;" &&
|
||||
reference.parameterTypes.firstOrNull() == "Ljava/lang/String;"
|
||||
}
|
||||
val hostRegister = getInstruction<FiveRegisterInstruction>(setUrlBuilderHostIndex).registerD
|
||||
|
||||
MutableMethod(this).apply {
|
||||
name = "rv_getCustomLyricsProviderHttpClient"
|
||||
addInstruction(
|
||||
setUrlBuilderHostIndex,
|
||||
"const-string v$hostRegister, \"$lyricsProviderHost\""
|
||||
)
|
||||
|
||||
// Add the patched method to the class.
|
||||
httpClientBuilderFingerprint.classDef.methods.add(this)
|
||||
}
|
||||
}
|
||||
//endregion
|
||||
|
||||
// region Replace the call to the HTTP client builder method used exclusively for lyrics by the modified one.
|
||||
getLyricsHttpClientFingerprint(httpClientBuilderMethod).method.apply {
|
||||
val getLyricsHttpClientIndex = indexOfFirstInstructionOrThrow {
|
||||
getReference<MethodReference>() == httpClientBuilderMethod
|
||||
}
|
||||
val getLyricsHttpClientInstruction = getInstruction<BuilderInstruction35c>(getLyricsHttpClientIndex)
|
||||
|
||||
// Replace the original method call with a call to our patched method.
|
||||
replaceInstruction(
|
||||
getLyricsHttpClientIndex,
|
||||
BuilderInstruction35c(
|
||||
getLyricsHttpClientInstruction.opcode,
|
||||
getLyricsHttpClientInstruction.registerCount,
|
||||
getLyricsHttpClientInstruction.registerC,
|
||||
getLyricsHttpClientInstruction.registerD,
|
||||
getLyricsHttpClientInstruction.registerE,
|
||||
getLyricsHttpClientInstruction.registerF,
|
||||
getLyricsHttpClientInstruction.registerG,
|
||||
ImmutableMethodReference(
|
||||
patchedHttpClientBuilderMethod.definingClass,
|
||||
patchedHttpClientBuilderMethod.name, // Only difference from the original method.
|
||||
patchedHttpClientBuilderMethod.parameters,
|
||||
patchedHttpClientBuilderMethod.returnType
|
||||
)
|
||||
)
|
||||
)
|
||||
}
|
||||
//endregion
|
||||
}
|
||||
}
|
@ -0,0 +1,21 @@
|
||||
package app.revanced.patches.spotify.misc.lyrics
|
||||
|
||||
import app.revanced.patcher.fingerprint
|
||||
import app.revanced.util.getReference
|
||||
import app.revanced.util.indexOfFirstInstruction
|
||||
import com.android.tools.smali.dexlib2.iface.reference.MethodReference
|
||||
|
||||
internal val httpClientBuilderFingerprint = fingerprint {
|
||||
strings("client == null", "scheduler == null")
|
||||
}
|
||||
|
||||
internal fun getLyricsHttpClientFingerprint(httpClientBuilderMethodReference: MethodReference) =
|
||||
fingerprint {
|
||||
returns(httpClientBuilderMethodReference.returnType)
|
||||
parameters()
|
||||
custom { method, _ ->
|
||||
method.indexOfFirstInstruction {
|
||||
getReference<MethodReference>() == httpClientBuilderMethodReference
|
||||
} >= 0
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user