mirror of
https://github.com/inotia00/revanced-patches.git
synced 2025-06-12 13:17:46 +02:00
feat(YouTube/Alternative thumbnails): matches official ReVanced code
This commit is contained in:
@ -3,18 +3,30 @@ package app.revanced.patches.youtube.alternativethumbnails.general
|
||||
import app.revanced.patcher.data.BytecodeContext
|
||||
import app.revanced.patcher.extensions.InstructionExtensions.addInstruction
|
||||
import app.revanced.patcher.extensions.InstructionExtensions.addInstructions
|
||||
import app.revanced.patcher.extensions.InstructionExtensions.getInstructions
|
||||
import app.revanced.patcher.fingerprint.MethodFingerprint
|
||||
import app.revanced.patcher.patch.BytecodePatch
|
||||
import app.revanced.patcher.patch.annotation.CompatiblePackage
|
||||
import app.revanced.patcher.patch.annotation.Patch
|
||||
import app.revanced.patches.youtube.alternativethumbnails.general.fingerprints.CronetURLRequestCallbackOnResponseStartedFingerprint
|
||||
import app.revanced.patches.youtube.alternativethumbnails.general.fingerprints.CronetURLRequestCallbackOnSucceededFingerprint
|
||||
import app.revanced.patcher.util.proxy.mutableTypes.MutableMethod
|
||||
import app.revanced.patcher.util.proxy.mutableTypes.MutableMethod.Companion.toMutable
|
||||
import app.revanced.patches.youtube.alternativethumbnails.general.fingerprints.MessageDigestImageUrlFingerprint
|
||||
import app.revanced.patches.youtube.alternativethumbnails.general.fingerprints.MessageDigestImageUrlParentFingerprint
|
||||
import app.revanced.patches.youtube.alternativethumbnails.general.fingerprints.cronet.RequestFingerprint
|
||||
import app.revanced.patches.youtube.alternativethumbnails.general.fingerprints.cronet.request.callback.OnFailureFingerprint
|
||||
import app.revanced.patches.youtube.alternativethumbnails.general.fingerprints.cronet.request.callback.OnResponseStartedFingerprint
|
||||
import app.revanced.patches.youtube.alternativethumbnails.general.fingerprints.cronet.request.callback.OnSucceededFingerprint
|
||||
import app.revanced.patches.youtube.utils.integrations.Constants.ALTERNATIVE_THUMBNAILS
|
||||
import app.revanced.patches.youtube.utils.settings.SettingsPatch
|
||||
import app.revanced.patches.youtube.utils.settings.SettingsPatch.contexts
|
||||
import app.revanced.util.copyXmlNode
|
||||
import app.revanced.util.exception
|
||||
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.ReferenceInstruction
|
||||
import com.android.tools.smali.dexlib2.iface.reference.FieldReference
|
||||
import com.android.tools.smali.dexlib2.immutable.ImmutableMethod
|
||||
|
||||
@Patch(
|
||||
name = "Alternative thumbnails",
|
||||
@ -50,53 +62,123 @@ import app.revanced.util.exception
|
||||
@Suppress("unused")
|
||||
object AlternativeThumbnailsPatch : BytecodePatch(
|
||||
setOf(
|
||||
CronetURLRequestCallbackOnResponseStartedFingerprint,
|
||||
MessageDigestImageUrlParentFingerprint
|
||||
MessageDigestImageUrlParentFingerprint,
|
||||
OnResponseStartedFingerprint,
|
||||
RequestFingerprint,
|
||||
)
|
||||
) {
|
||||
private lateinit var loadImageUrlMethod: MutableMethod
|
||||
private var loadImageUrlIndex = 0
|
||||
|
||||
private lateinit var loadImageSuccessCallbackMethod: MutableMethod
|
||||
private var loadImageSuccessCallbackIndex = 0
|
||||
|
||||
private lateinit var loadImageErrorCallbackMethod: MutableMethod
|
||||
private var loadImageErrorCallbackIndex = 0
|
||||
|
||||
/**
|
||||
* @param highPriority If the hook should be called before all other hooks.
|
||||
*/
|
||||
@Suppress("SameParameterValue")
|
||||
private fun addImageUrlHook(targetMethodClass: String, highPriority: Boolean) {
|
||||
loadImageUrlMethod.addInstructions(
|
||||
if (highPriority) 0 else loadImageUrlIndex,
|
||||
"""
|
||||
invoke-static { p1 }, $targetMethodClass->overrideImageURL(Ljava/lang/String;)Ljava/lang/String;
|
||||
move-result-object p1
|
||||
"""
|
||||
)
|
||||
loadImageUrlIndex += 2
|
||||
}
|
||||
|
||||
/**
|
||||
* If a connection completed, which includes normal 200 responses but also includes
|
||||
* status 404 and other error like http responses.
|
||||
*/
|
||||
@Suppress("SameParameterValue")
|
||||
private fun addImageUrlSuccessCallbackHook(targetMethodClass: String) {
|
||||
loadImageSuccessCallbackMethod.addInstruction(
|
||||
loadImageSuccessCallbackIndex++,
|
||||
"invoke-static { p1, p2 }, $targetMethodClass->handleCronetSuccess(" +
|
||||
"Lorg/chromium/net/UrlRequest;Lorg/chromium/net/UrlResponseInfo;)V"
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* If a connection outright failed to complete any connection.
|
||||
*/
|
||||
@Suppress("SameParameterValue")
|
||||
private fun addImageUrlErrorCallbackHook(targetMethodClass: String) {
|
||||
loadImageErrorCallbackMethod.addInstruction(
|
||||
loadImageErrorCallbackIndex++,
|
||||
"invoke-static { p1, p2, p3 }, $targetMethodClass->handleCronetFailure(" +
|
||||
"Lorg/chromium/net/UrlRequest;Lorg/chromium/net/UrlResponseInfo;Ljava/io/IOException;)V"
|
||||
)
|
||||
}
|
||||
|
||||
override fun execute(context: BytecodeContext) {
|
||||
|
||||
/**
|
||||
* Hook should should come first.
|
||||
*/
|
||||
MessageDigestImageUrlParentFingerprint.result?.let { parentResult ->
|
||||
MessageDigestImageUrlFingerprint.also {
|
||||
it.resolve(
|
||||
context,
|
||||
parentResult.classDef
|
||||
)
|
||||
}.result?.let {
|
||||
it.mutableMethod.apply {
|
||||
fun MethodFingerprint.getResultOrThrow() =
|
||||
result ?: throw exception
|
||||
|
||||
fun MethodFingerprint.alsoResolve(fingerprint: MethodFingerprint) =
|
||||
also { resolve(context, fingerprint.getResultOrThrow().classDef) }.getResultOrThrow()
|
||||
|
||||
fun MethodFingerprint.resolveAndLetMutableMethod(
|
||||
fingerprint: MethodFingerprint,
|
||||
block: (MutableMethod) -> Unit
|
||||
) = alsoResolve(fingerprint).also { block(it.mutableMethod) }
|
||||
|
||||
MessageDigestImageUrlFingerprint.resolveAndLetMutableMethod(MessageDigestImageUrlParentFingerprint) {
|
||||
loadImageUrlMethod = it
|
||||
addImageUrlHook(ALTERNATIVE_THUMBNAILS, true)
|
||||
}
|
||||
|
||||
OnSucceededFingerprint.resolveAndLetMutableMethod(OnResponseStartedFingerprint) {
|
||||
loadImageSuccessCallbackMethod = it
|
||||
addImageUrlSuccessCallbackHook(ALTERNATIVE_THUMBNAILS)
|
||||
}
|
||||
|
||||
OnFailureFingerprint.resolveAndLetMutableMethod(OnResponseStartedFingerprint) {
|
||||
loadImageErrorCallbackMethod = it
|
||||
addImageUrlErrorCallbackHook(ALTERNATIVE_THUMBNAILS)
|
||||
}
|
||||
|
||||
// The URL is required for the failure callback hook, but the URL field is obfuscated.
|
||||
// Add a helper get method that returns the URL field.
|
||||
RequestFingerprint.getResultOrThrow().apply {
|
||||
// The url is the only string field that is set inside the constructor.
|
||||
val urlFieldInstruction = mutableMethod.getInstructions().first {
|
||||
if (it.opcode != Opcode.IPUT_OBJECT)
|
||||
return@first false
|
||||
|
||||
val reference = (it as ReferenceInstruction).reference as FieldReference
|
||||
reference.type == "Ljava/lang/String;"
|
||||
} as ReferenceInstruction
|
||||
|
||||
val urlFieldName = (urlFieldInstruction.reference as FieldReference).name
|
||||
val definingClass = RequestFingerprint.IMPLEMENTATION_CLASS_NAME
|
||||
val addedMethodName = "getHookedUrl"
|
||||
mutableClass.methods.add(
|
||||
ImmutableMethod(
|
||||
definingClass,
|
||||
addedMethodName,
|
||||
emptyList(),
|
||||
"Ljava/lang/String;",
|
||||
AccessFlags.PUBLIC.value,
|
||||
null,
|
||||
null,
|
||||
MutableMethodImplementation(2)
|
||||
).toMutable().apply {
|
||||
addInstructions(
|
||||
0, """
|
||||
invoke-static { p1 }, $ALTERNATIVE_THUMBNAILS->overrideImageURL(Ljava/lang/String;)Ljava/lang/String;
|
||||
move-result-object p1
|
||||
"""
|
||||
iget-object v0, p0, $definingClass->${urlFieldName}:Ljava/lang/String;
|
||||
return-object v0
|
||||
"""
|
||||
)
|
||||
}
|
||||
} ?: throw MessageDigestImageUrlFingerprint.exception
|
||||
} ?: throw MessageDigestImageUrlParentFingerprint.exception
|
||||
|
||||
|
||||
/**
|
||||
* If a connection completed, which includes normal 200 responses but also includes
|
||||
* status 404 and other error like http responses.
|
||||
*/
|
||||
CronetURLRequestCallbackOnResponseStartedFingerprint.result?.let { parentResult ->
|
||||
CronetURLRequestCallbackOnSucceededFingerprint.also {
|
||||
it.resolve(
|
||||
context,
|
||||
parentResult.classDef
|
||||
)
|
||||
}.result?.let {
|
||||
it.mutableMethod.apply {
|
||||
addInstruction(
|
||||
0,
|
||||
"invoke-static { p2 }, $ALTERNATIVE_THUMBNAILS->handleCronetSuccess(Lorg/chromium/net/UrlResponseInfo;)V"
|
||||
)
|
||||
}
|
||||
} ?: throw CronetURLRequestCallbackOnSucceededFingerprint.exception
|
||||
} ?: throw CronetURLRequestCallbackOnResponseStartedFingerprint.exception
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Copy arrays
|
||||
|
@ -4,7 +4,7 @@ import app.revanced.patcher.extensions.or
|
||||
import app.revanced.patcher.fingerprint.MethodFingerprint
|
||||
import com.android.tools.smali.dexlib2.AccessFlags
|
||||
|
||||
object MessageDigestImageUrlFingerprint : MethodFingerprint(
|
||||
internal object MessageDigestImageUrlFingerprint : MethodFingerprint(
|
||||
accessFlags = AccessFlags.PUBLIC or AccessFlags.CONSTRUCTOR,
|
||||
parameters = listOf("Ljava/lang/String;", "L")
|
||||
parameters = listOf("Ljava/lang/String;", "L")
|
||||
)
|
@ -4,9 +4,9 @@ import app.revanced.patcher.extensions.or
|
||||
import app.revanced.patcher.fingerprint.MethodFingerprint
|
||||
import com.android.tools.smali.dexlib2.AccessFlags
|
||||
|
||||
object MessageDigestImageUrlParentFingerprint : MethodFingerprint(
|
||||
internal object MessageDigestImageUrlParentFingerprint : MethodFingerprint(
|
||||
accessFlags = AccessFlags.PUBLIC or AccessFlags.FINAL,
|
||||
returnType = "Ljava/lang/String;",
|
||||
returnType = "Ljava/lang/String;",
|
||||
parameters = emptyList(),
|
||||
strings = listOf("@#&=*+-_.,:!?()/~'%;\$"),
|
||||
)
|
@ -0,0 +1,16 @@
|
||||
package app.revanced.patches.youtube.alternativethumbnails.general.fingerprints.cronet
|
||||
|
||||
import app.revanced.patcher.extensions.or
|
||||
import app.revanced.patcher.fingerprint.MethodFingerprint
|
||||
import app.revanced.patches.youtube.alternativethumbnails.general.fingerprints.cronet.RequestFingerprint.IMPLEMENTATION_CLASS_NAME
|
||||
import com.android.tools.smali.dexlib2.AccessFlags
|
||||
|
||||
internal object RequestFingerprint : MethodFingerprint(
|
||||
returnType = "V",
|
||||
accessFlags = AccessFlags.PUBLIC or AccessFlags.CONSTRUCTOR,
|
||||
customFingerprint = { _, classDef ->
|
||||
classDef.type == IMPLEMENTATION_CLASS_NAME
|
||||
}
|
||||
) {
|
||||
const val IMPLEMENTATION_CLASS_NAME = "Lorg/chromium/net/impl/CronetUrlRequest;"
|
||||
}
|
@ -0,0 +1,18 @@
|
||||
package app.revanced.patches.youtube.alternativethumbnails.general.fingerprints.cronet.request.callback
|
||||
|
||||
import app.revanced.patcher.extensions.or
|
||||
import app.revanced.patcher.fingerprint.MethodFingerprint
|
||||
import com.android.tools.smali.dexlib2.AccessFlags
|
||||
|
||||
internal object OnFailureFingerprint : MethodFingerprint(
|
||||
returnType = "V",
|
||||
accessFlags = AccessFlags.PUBLIC or AccessFlags.FINAL,
|
||||
parameters = listOf(
|
||||
"Lorg/chromium/net/UrlRequest;",
|
||||
"Lorg/chromium/net/UrlResponseInfo;",
|
||||
"Lorg/chromium/net/CronetException;"
|
||||
),
|
||||
customFingerprint = { methodDef, _ ->
|
||||
methodDef.name == "onFailed"
|
||||
}
|
||||
)
|
@ -1,14 +1,14 @@
|
||||
package app.revanced.patches.youtube.alternativethumbnails.general.fingerprints
|
||||
package app.revanced.patches.youtube.alternativethumbnails.general.fingerprints.cronet.request.callback
|
||||
|
||||
import app.revanced.patcher.extensions.or
|
||||
import app.revanced.patcher.fingerprint.MethodFingerprint
|
||||
import com.android.tools.smali.dexlib2.AccessFlags
|
||||
|
||||
// Acts as a parent fingerprint.
|
||||
object CronetURLRequestCallbackOnResponseStartedFingerprint : MethodFingerprint(
|
||||
internal object OnResponseStartedFingerprint : MethodFingerprint(
|
||||
returnType = "V",
|
||||
accessFlags = AccessFlags.PUBLIC or AccessFlags.FINAL,
|
||||
parameters = listOf("Lorg/chromium/net/UrlRequest;", "Lorg/chromium/net/UrlResponseInfo;"),
|
||||
parameters = listOf("Lorg/chromium/net/UrlRequest;", "Lorg/chromium/net/UrlResponseInfo;"),
|
||||
strings = listOf(
|
||||
"Content-Length",
|
||||
"Content-Type",
|
@ -1,13 +1,13 @@
|
||||
package app.revanced.patches.youtube.alternativethumbnails.general.fingerprints
|
||||
package app.revanced.patches.youtube.alternativethumbnails.general.fingerprints.cronet.request.callback
|
||||
|
||||
import app.revanced.patcher.extensions.or
|
||||
import app.revanced.patcher.fingerprint.MethodFingerprint
|
||||
import com.android.tools.smali.dexlib2.AccessFlags
|
||||
|
||||
object CronetURLRequestCallbackOnSucceededFingerprint : MethodFingerprint(
|
||||
internal object OnSucceededFingerprint : MethodFingerprint(
|
||||
returnType = "V",
|
||||
accessFlags = AccessFlags.PUBLIC or AccessFlags.FINAL,
|
||||
parameters = listOf("Lorg/chromium/net/UrlRequest;", "Lorg/chromium/net/UrlResponseInfo;"),
|
||||
parameters = listOf("Lorg/chromium/net/UrlRequest;", "Lorg/chromium/net/UrlResponseInfo;"),
|
||||
customFingerprint = { methodDef, _ ->
|
||||
methodDef.name == "onSucceeded"
|
||||
}
|
Reference in New Issue
Block a user