no settings or remember speed patches

This commit is contained in:
Ulises M 2026-02-09 21:21:49 -08:00
parent ef052c0d8f
commit 0ad2464cba
19 changed files with 214 additions and 139 deletions

View file

@ -1,11 +1,7 @@
package app.revanced.patches.tiktok.feedfilter
import app.revanced.patcher.extensions.InstructionExtensions.addInstruction
import app.revanced.patcher.extensions.InstructionExtensions.instructions
import app.revanced.patcher.extensions.InstructionExtensions.addInstructions
import app.revanced.patcher.patch.bytecodePatch
import app.revanced.patches.tiktok.misc.extension.sharedExtensionPatch
import app.revanced.patches.tiktok.misc.settings.settingsPatch
import app.revanced.patches.tiktok.misc.settings.settingsStatusLoadFingerprint
import com.android.tools.smali.dexlib2.Opcode
import com.android.tools.smali.dexlib2.iface.instruction.OneRegisterInstruction
@ -17,33 +13,43 @@ val feedFilterPatch = bytecodePatch(
description = "Removes ads, livestreams, stories, image videos " +
"and videos with a specific amount of views or likes from the feed.",
) {
dependsOn(
sharedExtensionPatch,
settingsPatch,
)
compatibleWith(
"com.ss.android.ugc.trill"("36.5.4"),
"com.zhiliaoapp.musically"("36.5.4"),
"com.ss.android.ugc.trill"("43.8.3"),
"com.zhiliaoapp.musically"("43.8.3"),
)
execute {
arrayOf(
feedApiServiceLIZFingerprint.method to "$EXTENSION_CLASS_DESCRIPTOR->filter(Lcom/ss/android/ugc/aweme/feed/model/FeedItemList;)V",
followFeedFingerprint.method to "$EXTENSION_CLASS_DESCRIPTOR->filter(Lcom/ss/android/ugc/aweme/follow/presenter/FollowFeedList;)V"
).forEach { (method, filterSignature) ->
val returnInstruction = method.instructions.first { it.opcode == Opcode.RETURN_OBJECT }
val register = (returnInstruction as OneRegisterInstruction).registerA
method.addInstruction(
returnInstruction.location.index,
"invoke-static { v$register }, $filterSignature"
)
feedItemListGetItemsFingerprint.method.let { method ->
val returnIndices = method.implementation!!.instructions.withIndex()
.filter { it.value.opcode == Opcode.RETURN_OBJECT }
.map { it.index }
.toList()
returnIndices.asReversed().forEach { returnIndex ->
method.addInstructions(
returnIndex,
"invoke-static {p0}, $EXTENSION_CLASS_DESCRIPTOR->filter(Lcom/ss/android/ugc/aweme/feed/model/FeedItemList;)V"
)
}
}
settingsStatusLoadFingerprint.method.addInstruction(
0,
"invoke-static {}, Lapp/revanced/extension/tiktok/settings/SettingsStatus;->enableFeedFilter()V",
)
}
followFeedFingerprint.method.let { method ->
val returnIndices = method.implementation!!.instructions.withIndex()
.filter { it.value.opcode == Opcode.RETURN_OBJECT }
.map { it.index }
.toList()
}
returnIndices.asReversed().forEach { returnIndex ->
val register = (method.implementation!!.instructions[returnIndex] as OneRegisterInstruction).registerA
method.addInstructions(
returnIndex,
"""
if-nez v$register, :skip
invoke-static/range { v$register .. v$register }, $EXTENSION_CLASS_DESCRIPTOR->filter(Lcom/ss/android/ugc/aweme/follow/presenter/FollowFeedList;)V
:skip
"""
)
}
}
}
}

View file

@ -2,21 +2,22 @@ package app.revanced.patches.tiktok.feedfilter
import app.revanced.patcher.fingerprint
import com.android.tools.smali.dexlib2.AccessFlags
import com.android.tools.smali.dexlib2.Opcode
internal val feedApiServiceLIZFingerprint = fingerprint {
internal val feedItemListGetItemsFingerprint = fingerprint {
accessFlags(AccessFlags.PUBLIC)
returns("Ljava/util/List;")
custom { method, classDef ->
classDef.endsWith("/FeedApiService;") && method.name == "fetchFeedList"
classDef.endsWith("/FeedItemList;") &&
method.name == "getItems" &&
method.parameterTypes.isEmpty()
}
}
internal val followFeedFingerprint = fingerprint {
accessFlags(AccessFlags.PUBLIC, AccessFlags.STATIC)
returns("Lcom/ss/android/ugc/aweme/follow/presenter/FollowFeedList;")
strings("getFollowFeedList")
opcodes(
Opcode.INVOKE_INTERFACE_RANGE,
Opcode.MOVE_RESULT_OBJECT,
Opcode.INVOKE_INTERFACE
)
custom { method, _ ->
method.parameterTypes.size == 2
}
}

View file

@ -16,8 +16,8 @@ val rememberClearDisplayPatch = bytecodePatch(
description = "Remembers the clear display configurations in between videos.",
) {
compatibleWith(
"com.ss.android.ugc.trill"("36.5.4"),
"com.zhiliaoapp.musically"("36.5.4"),
"com.ss.android.ugc.trill"("43.8.3"),
"com.zhiliaoapp.musically"("43.8.3"),
)
execute {

View file

@ -7,7 +7,6 @@ import app.revanced.patcher.extensions.InstructionExtensions.getInstruction
import app.revanced.patcher.extensions.InstructionExtensions.removeInstructions
import app.revanced.patcher.patch.bytecodePatch
import app.revanced.patches.tiktok.misc.extension.sharedExtensionPatch
import app.revanced.patches.tiktok.misc.settings.settingsPatch
import app.revanced.patches.tiktok.misc.settings.settingsStatusLoadFingerprint
import app.revanced.util.findInstructionIndicesReversedOrThrow
import app.revanced.util.getReference
@ -25,12 +24,11 @@ val downloadsPatch = bytecodePatch(
) {
dependsOn(
sharedExtensionPatch,
settingsPatch,
)
compatibleWith(
"com.ss.android.ugc.trill"("36.5.4"),
"com.zhiliaoapp.musically"("36.5.4"),
"com.ss.android.ugc.trill"("43.8.3"),
"com.zhiliaoapp.musically"("43.8.3"),
)
execute {

View file

@ -10,8 +10,8 @@ internal val getSpeedFingerprint = fingerprint {
}
internal val setSpeedFingerprint = fingerprint {
accessFlags(AccessFlags.PUBLIC, AccessFlags.STATIC)
returns("V")
parameters("Ljava/lang/String;", "Lcom/ss/android/ugc/aweme/feed/model/Aweme;", "F")
strings("enterFrom")
accessFlags(AccessFlags.PUBLIC, AccessFlags.FINAL)
returns("Ljava/lang/Object;")
strings("playback_speed")
custom { method, _ -> method.name == "invoke" && method.parameterTypes.isEmpty() }
}

View file

@ -16,6 +16,7 @@ val playbackSpeedPatch = bytecodePatch(
name = "Playback speed",
description = "Enables the playback speed option for all videos and " +
"retains the speed configurations in between videos.",
use = false
) {
compatibleWith(
"com.ss.android.ugc.trill"("36.5.4"),

View file

@ -5,21 +5,22 @@ import com.android.tools.smali.dexlib2.AccessFlags
import com.android.tools.smali.dexlib2.Opcode
internal val urlShorteningFingerprint = fingerprint {
accessFlags(AccessFlags.PUBLIC, AccessFlags.STATIC, AccessFlags.FINAL)
returns("LX/")
accessFlags(AccessFlags.PUBLIC, AccessFlags.STATIC)
returns("V")
parameters(
"I",
"L",
"Ljava/lang/String;",
"Ljava/util/List;",
"Ljava/lang/String;",
"Ljava/lang/String;"
"Z",
"I"
)
opcodes(Opcode.RETURN_OBJECT)
opcodes(Opcode.RETURN_VOID)
// Same Kotlin intrinsics literal on both variants.
strings("getShortShareUrlObservab\u2026ongUrl, subBizSceneValue)")
strings("share_link_id", "invitation_scene")
custom { method, _ ->
// LIZLLL is obfuscated by ProGuard/R8, but stable across both TikTok and Musically.
method.name == "LIZLLL"
method.parameterTypes.size == 6
}
}

View file

@ -26,58 +26,19 @@ val sanitizeShareUrlsPatch = bytecodePatch(
dependsOn(sharedExtensionPatch)
compatibleWith(
"com.ss.android.ugc.trill"("36.5.4"),
"com.zhiliaoapp.musically"("36.5.4"),
"com.ss.android.ugc.trill"("43.8.3"),
"com.zhiliaoapp.musically"("43.8.3"),
)
execute {
urlShorteningFingerprint.method.apply {
val invokeIndex = indexOfFirstInstructionOrThrow {
val ref = getReference<MethodReference>()
ref?.name == "LIZ" && ref.definingClass.startsWith("LX/")
}
val longUrlRegister = implementation!!.registerCount - 6 + 3
val moveResultIndex = indexOfFirstInstructionOrThrow(invokeIndex, Opcode.MOVE_RESULT_OBJECT)
val urlRegister = getInstruction<OneRegisterInstruction>(moveResultIndex).registerA
// Resolve Observable wrapper classes at runtime
val observableWrapperIndex = indexOfFirstInstructionOrThrow(Opcode.NEW_INSTANCE)
val observableWrapperClass = getInstruction<ReferenceInstruction>(observableWrapperIndex)
.reference.toString()
val observableFactoryIndex = indexOfFirstInstructionOrThrow {
val ref = getReference<MethodReference>()
ref?.name == "LJ" && ref.definingClass.startsWith("LX/")
}
val observableFactoryRef = getInstruction<ReferenceInstruction>(observableFactoryIndex)
.reference as MethodReference
val observableFactoryClass = observableFactoryRef.definingClass
val observableInterfaceType = observableFactoryRef.parameterTypes.first()
val observableReturnType = observableFactoryRef.returnType
val wrapperRegister = findFreeRegister(moveResultIndex + 1, urlRegister)
// Check setting and conditionally sanitize share URL.
addInstructionsWithLabels(
moveResultIndex + 1,
addInstructions(
0,
"""
invoke-static {}, $EXTENSION_CLASS_DESCRIPTOR->shouldSanitize()Z
move-result v$wrapperRegister
if-eqz v$wrapperRegister, :skip_sanitization
invoke-static { p1 }, $EXTENSION_CLASS_DESCRIPTOR->sanitizeShareUrl(Ljava/lang/String;)Ljava/lang/String;
move-result-object v$urlRegister
# Wrap sanitized URL and return early to bypass ShareExtService
new-instance v$wrapperRegister, $observableWrapperClass
invoke-direct { v$wrapperRegister, v$urlRegister }, $observableWrapperClass-><init>(Ljava/lang/String;)V
invoke-static { v$wrapperRegister }, $observableFactoryClass->LJ($observableInterfaceType)$observableReturnType
move-result-object v$urlRegister
return-object v$urlRegister
:skip_sanitization
nop
invoke-static/range { v$longUrlRegister .. v$longUrlRegister }, $EXTENSION_CLASS_DESCRIPTOR->sanitizeShareUrl(Ljava/lang/String;)Ljava/lang/String;
move-result-object v$longUrlRegister
"""
)
}