feat(YouTube): add support versions 19.19.39 ~ 19.20.34

This commit is contained in:
inotia00
2024-05-26 17:19:44 +09:00
parent 0b9ea6ac26
commit 029219a4cc
24 changed files with 443 additions and 109 deletions

View File

@ -8,10 +8,12 @@ import app.revanced.patcher.patch.BytecodePatch
import app.revanced.patches.shared.customspeed.fingerprints.SpeedArrayGeneratorFingerprint
import app.revanced.patches.shared.customspeed.fingerprints.SpeedLimiterFallBackFingerprint
import app.revanced.patches.shared.customspeed.fingerprints.SpeedLimiterFingerprint
import app.revanced.util.getTargetIndex
import app.revanced.util.getTargetIndexWithFieldReferenceType
import app.revanced.util.getTargetIndexWithMethodReferenceName
import app.revanced.util.indexOfFirstInstruction
import app.revanced.util.resultOrThrow
import com.android.tools.smali.dexlib2.Opcode
import com.android.tools.smali.dexlib2.iface.instruction.NarrowLiteralInstruction
import com.android.tools.smali.dexlib2.iface.instruction.OneRegisterInstruction
@ -71,7 +73,7 @@ abstract class BaseCustomPlaybackSpeedPatch(
val limiterMinConstIndex =
indexOfFirstInstruction { (this as? NarrowLiteralInstruction)?.narrowLiteral == 0.25f.toRawBits() }
val limiterMaxConstIndex =
indexOfFirstInstruction { (this as? NarrowLiteralInstruction)?.narrowLiteral == 2.0f.toRawBits() }
getTargetIndex(limiterMinConstIndex + 1, Opcode.CONST_HIGH16)
val limiterMinConstDestination =
getInstruction<OneRegisterInstruction>(limiterMinConstIndex).registerA

View File

@ -79,21 +79,24 @@ object TabletMiniPlayerPatch : BaseBytecodePatch(
}
}
// In ModernMiniPlayer, the drawables of the close button and expand button are reversed.
// OnClickListener appears to be applied normally, so this appears to be a bug in YouTube.
// To solve this, swap the drawables of the close and expand buttons.
// This Drawable will be used in multiple Classes, so instead of using LiteralValueFingerprint to patch only specific methods,
// Apply the patch to all methods where literals are used.
mapOf(
YtOutlineXWhite to "replaceCloseButtonDrawableId",
YtOutlinePiPWhite to "replaceExpandButtonDrawableId"
).forEach { (literal, methodName) ->
val smaliInstruction = """
// This issue has been fixed in YouTube 19.17.41+.
if (YtOutlineXWhite != -1L && YtOutlinePiPWhite != -1L) {
// In ModernMiniPlayer, the drawables of the close button and expand button are reversed.
// OnClickListener appears to be applied normally, so this appears to be a bug in YouTube.
// To solve this, swap the drawables of the close and expand buttons.
// This Drawable will be used in multiple Classes, so instead of using LiteralValueFingerprint to patch only specific methods,
// Apply the patch to all methods where literals are used.
mapOf(
YtOutlineXWhite to "replaceCloseButtonDrawableId",
YtOutlinePiPWhite to "replaceExpandButtonDrawableId"
).forEach { (literal, methodName) ->
val smaliInstruction = """
invoke-static {v$REGISTER_TEMPLATE_REPLACEMENT}, $GENERAL_CLASS_DESCRIPTOR->$methodName(I)I
move-result v$REGISTER_TEMPLATE_REPLACEMENT
"""
context.literalInstructionHook(literal, smaliInstruction)
context.literalInstructionHook(literal, smaliInstruction)
}
}
arrayOf(

View File

@ -16,20 +16,21 @@ import app.revanced.patches.youtube.player.buttons.fingerprints.YouTubeControlsO
import app.revanced.patches.youtube.utils.castbutton.CastButtonPatch
import app.revanced.patches.youtube.utils.compatibility.Constants.COMPATIBLE_PACKAGE
import app.revanced.patches.youtube.utils.fingerprints.LayoutConstructorFingerprint
import app.revanced.patches.youtube.utils.fix.fullscreen.FullscreenButtonViewStubPatch
import app.revanced.patches.youtube.utils.integrations.Constants.PLAYER_CLASS_DESCRIPTOR
import app.revanced.patches.youtube.utils.resourceid.SharedResourceIdPatch
import app.revanced.patches.youtube.utils.resourceid.SharedResourceIdPatch.AutoNavToggle
import app.revanced.patches.youtube.utils.resourceid.SharedResourceIdPatch.FullScreenButton
import app.revanced.patches.youtube.utils.resourceid.SharedResourceIdPatch.PlayerCollapseButton
import app.revanced.patches.youtube.utils.resourceid.SharedResourceIdPatch.TitleAnchor
import app.revanced.patches.youtube.utils.settings.SettingsPatch
import app.revanced.util.getTargetIndex
import app.revanced.util.getTargetIndexWithReference
import app.revanced.util.getWideLiteralInstructionIndex
import app.revanced.util.patch.BaseBytecodePatch
import app.revanced.util.resultOrThrow
import com.android.tools.smali.dexlib2.Opcode
import com.android.tools.smali.dexlib2.iface.instruction.FiveRegisterInstruction
import com.android.tools.smali.dexlib2.iface.instruction.OneRegisterInstruction
import com.android.tools.smali.dexlib2.iface.instruction.WideLiteralInstruction
import com.android.tools.smali.dexlib2.iface.instruction.formats.Instruction3rc
@Suppress("unused")
@ -38,6 +39,7 @@ object PlayerButtonsPatch : BaseBytecodePatch(
description = "Adds an option to hide buttons in the video player.",
dependencies = setOf(
CastButtonPatch::class,
FullscreenButtonViewStubPatch::class,
SettingsPatch::class,
SharedResourceIdPatch::class
),
@ -147,16 +149,22 @@ object PlayerButtonsPatch : BaseBytecodePatch(
FullScreenButtonFingerprint.resultOrThrow().let {
it.mutableMethod.apply {
val viewIndex = getTargetIndexWithReference("Landroid/widget/ImageView;->getResources()Landroid/content/res/Resources;")
val viewRegister = getInstruction<FiveRegisterInstruction>(viewIndex).registerC
val buttonCalls = implementation!!.instructions.withIndex()
.filter { instruction ->
(instruction.value as? WideLiteralInstruction)?.wideLiteral == FullScreenButton
}
val constIndex = buttonCalls.elementAt(buttonCalls.size - 1).index
val castIndex = getTargetIndex(constIndex, Opcode.CHECK_CAST)
val insertIndex = castIndex + 1
val insertRegister = getInstruction<OneRegisterInstruction>(castIndex).registerA
addInstructionsWithLabels(
viewIndex, """
invoke-static {v$viewRegister}, $PLAYER_CLASS_DESCRIPTOR->hideFullscreenButton(Landroid/widget/ImageView;)Landroid/widget/ImageView;
move-result-object v$viewRegister
if-nez v$viewRegister, :show
insertIndex, """
invoke-static {v$insertRegister}, $PLAYER_CLASS_DESCRIPTOR->hideFullscreenButton(Landroid/widget/ImageView;)Landroid/widget/ImageView;
move-result-object v$insertRegister
if-nez v$insertRegister, :show
return-void
""", ExternalLabel("show", getInstruction(viewIndex))
""", ExternalLabel("show", getInstruction(insertIndex))
)
}
}

View File

@ -2,6 +2,7 @@ package app.revanced.patches.youtube.player.buttons.fingerprints
import app.revanced.patcher.extensions.or
import app.revanced.patcher.fingerprint.MethodFingerprint
import app.revanced.patches.youtube.utils.resourceid.SharedResourceIdPatch.CfFullscreenButton
import app.revanced.patches.youtube.utils.resourceid.SharedResourceIdPatch.FadeDurationFast
import app.revanced.patches.youtube.utils.resourceid.SharedResourceIdPatch.FullScreenButton
import app.revanced.util.containsWideLiteralInstructionIndex
@ -11,8 +12,11 @@ internal object FullScreenButtonFingerprint : MethodFingerprint(
returnType = "V",
accessFlags = AccessFlags.PUBLIC or AccessFlags.FINAL,
parameters = listOf("Landroid/view/View;"),
customFingerprint = { methodDef, _ ->
methodDef.containsWideLiteralInstructionIndex(FadeDurationFast)
&& methodDef.containsWideLiteralInstructionIndex(FullScreenButton)
customFingerprint = handler@{ methodDef, _ ->
if (!methodDef.containsWideLiteralInstructionIndex(FullScreenButton))
return@handler false
methodDef.containsWideLiteralInstructionIndex(FadeDurationFast) // YouTube 18.29.38 ~ YouTube 19.18.41
|| methodDef.containsWideLiteralInstructionIndex(CfFullscreenButton) // YouTube 19.19.39 ~
},
)

View File

@ -20,14 +20,12 @@ import app.revanced.patches.youtube.player.components.fingerprints.InfoCardsInco
import app.revanced.patches.youtube.player.components.fingerprints.LayoutCircleFingerprint
import app.revanced.patches.youtube.player.components.fingerprints.LayoutIconFingerprint
import app.revanced.patches.youtube.player.components.fingerprints.LayoutVideoFingerprint
import app.revanced.patches.youtube.player.components.fingerprints.RestoreSlideToSeekBehaviorFingerprint
import app.revanced.patches.youtube.player.components.fingerprints.SeekEduContainerFingerprint
import app.revanced.patches.youtube.player.components.fingerprints.SpeedOverlayFingerprint
import app.revanced.patches.youtube.player.components.fingerprints.SpeedOverlayValueFingerprint
import app.revanced.patches.youtube.player.components.fingerprints.SuggestedActionsFingerprint
import app.revanced.patches.youtube.player.components.fingerprints.TouchAreaOnClickListenerFingerprint
import app.revanced.patches.youtube.player.components.fingerprints.WatermarkFingerprint
import app.revanced.patches.youtube.player.components.fingerprints.WatermarkParentFingerprint
import app.revanced.patches.youtube.player.speedoverlay.SpeedOverlayPatch
import app.revanced.patches.youtube.utils.compatibility.Constants.COMPATIBLE_PACKAGE
import app.revanced.patches.youtube.utils.controlsoverlay.ControlsOverlayConfigPatch
import app.revanced.patches.youtube.utils.fingerprints.YouTubeControlsOverlayFingerprint
@ -44,7 +42,6 @@ import app.revanced.util.getTargetIndex
import app.revanced.util.getTargetIndexReversed
import app.revanced.util.getTargetIndexWithMethodReferenceName
import app.revanced.util.getWideLiteralInstructionIndex
import app.revanced.util.literalInstructionBooleanHook
import app.revanced.util.patch.BaseBytecodePatch
import app.revanced.util.resultOrThrow
import com.android.tools.smali.dexlib2.Opcode
@ -64,6 +61,7 @@ object PlayerComponentsPatch : BaseBytecodePatch(
PlayerTypeHookPatch::class,
SettingsPatch::class,
SharedResourceIdPatch::class,
SpeedOverlayPatch::class,
SuggestedVideoEndScreenPatch::class
),
compatiblePackages = COMPATIBLE_PACKAGE,
@ -75,10 +73,7 @@ object PlayerComponentsPatch : BaseBytecodePatch(
LayoutCircleFingerprint,
LayoutIconFingerprint,
LayoutVideoFingerprint,
RestoreSlideToSeekBehaviorFingerprint,
SeekEduContainerFingerprint,
SpeedOverlayFingerprint,
SpeedOverlayValueFingerprint,
SuggestedActionsFingerprint,
TouchAreaOnClickListenerFingerprint,
WatermarkParentFingerprint,
@ -131,34 +126,6 @@ object PlayerComponentsPatch : BaseBytecodePatch(
// endregion
// region patch for disable speed overlay and speed overlay value
mapOf(
RestoreSlideToSeekBehaviorFingerprint to 45411329,
SpeedOverlayFingerprint to 45411330
).forEach { (fingerprint, literal) ->
fingerprint.literalInstructionBooleanHook(
literal,
"$PLAYER_CLASS_DESCRIPTOR->disableSpeedOverlay(Z)Z"
)
}
SpeedOverlayValueFingerprint.resultOrThrow().let {
it.mutableMethod.apply {
val index = it.scanResult.patternScanResult!!.startIndex
val register = getInstruction<TwoRegisterInstruction>(index).registerA
addInstructions(
index + 1, """
invoke-static {v$register}, $PLAYER_CLASS_DESCRIPTOR->speedOverlayValue(F)F
move-result v$register
"""
)
}
}
// endregion
// region patch for hide channel watermark
WatermarkFingerprint.resolve(

View File

@ -16,6 +16,7 @@ import app.revanced.patches.youtube.player.flyoutmenu.toggle.fingerprints.Stable
import app.revanced.patches.youtube.utils.compatibility.Constants.COMPATIBLE_PACKAGE
import app.revanced.patches.youtube.utils.integrations.Constants.PLAYER_CLASS_DESCRIPTOR
import app.revanced.patches.youtube.utils.settings.SettingsPatch
import app.revanced.util.getReference
import app.revanced.util.getStringInstructionIndex
import app.revanced.util.getTargetIndex
import app.revanced.util.getTargetIndexReversed
@ -26,6 +27,8 @@ import com.android.tools.smali.dexlib2.Opcode
import com.android.tools.smali.dexlib2.iface.instruction.FiveRegisterInstruction
import com.android.tools.smali.dexlib2.iface.instruction.OneRegisterInstruction
import com.android.tools.smali.dexlib2.iface.instruction.ReferenceInstruction
import com.android.tools.smali.dexlib2.iface.instruction.TwoRegisterInstruction
import com.android.tools.smali.dexlib2.iface.reference.FieldReference
@Suppress("unused")
object ChangeTogglePatch : BaseBytecodePatch(
@ -123,6 +126,12 @@ object ChangeTogglePatch : BaseBytecodePatch(
CinematicLightingFingerprint.resultOrThrow().let {
it.mutableMethod.apply {
val iGetIndex = indexOfFirstInstruction {
opcode == Opcode.IGET
&& getReference<FieldReference>()?.definingClass == definingClass
}
val classRegister = getInstruction<TwoRegisterInstruction>(iGetIndex).registerB
val stringIndex = getStringInstructionIndex("menu_item_cinematic_lighting")
val checkCastIndex = getTargetIndexReversed(stringIndex, Opcode.CHECK_CAST)
@ -158,7 +167,7 @@ object ChangeTogglePatch : BaseBytecodePatch(
invoke-static {v$freeRegisterE}, $PLAYER_CLASS_DESCRIPTOR->getToggleString(Ljava/lang/String;)Ljava/lang/String;
move-result-object v$freeRegisterE
:set_string
iget-object v$freeRegisterC, p0, $iGetObjectPrimaryReference
iget-object v$freeRegisterC, v$classRegister, $iGetObjectPrimaryReference
check-cast v$freeRegisterC, $checkCastReference
iget-object v$freeRegisterC, v$freeRegisterC, $iGetObjectSecondaryReference
const-string v$freeRegisterD, "menu_item_cinematic_lighting"

View File

@ -0,0 +1,224 @@
package app.revanced.patches.youtube.player.speedoverlay
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.addInstructionsWithLabels
import app.revanced.patcher.extensions.InstructionExtensions.getInstruction
import app.revanced.patcher.patch.BytecodePatch
import app.revanced.patcher.patch.PatchException
import app.revanced.patcher.patch.annotation.Patch
import app.revanced.patcher.util.proxy.mutableTypes.MutableMethod
import app.revanced.patcher.util.smali.ExternalLabel
import app.revanced.patches.youtube.player.speedoverlay.fingerprints.HorizontalTouchOffsetConstructorFingerprint
import app.revanced.patches.youtube.player.speedoverlay.fingerprints.NextGenWatchLayoutFingerprint
import app.revanced.patches.youtube.player.speedoverlay.fingerprints.RestoreSlideToSeekBehaviorFingerprint
import app.revanced.patches.youtube.player.speedoverlay.fingerprints.SlideToSeekMotionEventFingerprint
import app.revanced.patches.youtube.player.speedoverlay.fingerprints.SpeedOverlayFingerprint
import app.revanced.patches.youtube.player.speedoverlay.fingerprints.SpeedOverlayTextValueFingerprint
import app.revanced.patches.youtube.player.speedoverlay.fingerprints.SpeedOverlayValueFingerprint
import app.revanced.patches.youtube.utils.integrations.Constants.PLAYER_CLASS_DESCRIPTOR
import app.revanced.patches.youtube.utils.resourceid.SharedResourceIdPatch
import app.revanced.util.getReference
import app.revanced.util.getTargetIndex
import app.revanced.util.getTargetIndexReversed
import app.revanced.util.getTargetIndexWithMethodReferenceName
import app.revanced.util.getTargetIndexWithMethodReferenceNameReversed
import app.revanced.util.getWalkerMethod
import app.revanced.util.indexOfFirstInstruction
import app.revanced.util.literalInstructionBooleanHook
import app.revanced.util.resultOrThrow
import com.android.tools.smali.dexlib2.Opcode
import com.android.tools.smali.dexlib2.iface.instruction.FiveRegisterInstruction
import com.android.tools.smali.dexlib2.iface.instruction.NarrowLiteralInstruction
import com.android.tools.smali.dexlib2.iface.instruction.OneRegisterInstruction
import com.android.tools.smali.dexlib2.iface.instruction.ReferenceInstruction
import com.android.tools.smali.dexlib2.iface.instruction.TwoRegisterInstruction
import com.android.tools.smali.dexlib2.iface.reference.FieldReference
@Patch(dependencies = [SharedResourceIdPatch::class])
object SpeedOverlayPatch : BytecodePatch(
setOf(
HorizontalTouchOffsetConstructorFingerprint,
NextGenWatchLayoutFingerprint,
RestoreSlideToSeekBehaviorFingerprint,
SpeedOverlayFingerprint,
SpeedOverlayTextValueFingerprint,
SpeedOverlayValueFingerprint,
)
) {
override fun execute(context: BytecodeContext) {
val restoreSlideToSeekBehaviorFingerprintResult = RestoreSlideToSeekBehaviorFingerprint.result
val speedOverlayFingerprintResult = SpeedOverlayFingerprint.result
val speedOverlayValueFingerprintResult = SpeedOverlayValueFingerprint.result
val resolvable =
restoreSlideToSeekBehaviorFingerprintResult != null
&& speedOverlayFingerprintResult != null
&& speedOverlayValueFingerprintResult != null
if (resolvable) {
// Legacy method.
// Used on YouTube 18.29.38 ~ YouTube 19.17.41
// region patch for disable speed overlay
mapOf(
RestoreSlideToSeekBehaviorFingerprint to 45411329,
SpeedOverlayFingerprint to 45411330
).forEach { (fingerprint, literal) ->
fingerprint.result!!.let {
fingerprint.literalInstructionBooleanHook(
literal,
"$PLAYER_CLASS_DESCRIPTOR->disableSpeedOverlay(Z)Z"
)
}
}
// endregion
// region patch for custom speed overlay value
speedOverlayValueFingerprintResult!!.let {
it.mutableMethod.apply {
val index = it.scanResult.patternScanResult!!.startIndex
val register = getInstruction<TwoRegisterInstruction>(index).registerA
addInstructions(
index + 1, """
invoke-static {v$register}, $PLAYER_CLASS_DESCRIPTOR->speedOverlayValue(F)F
move-result v$register
"""
)
}
}
// endregion
} else {
// New method.
// Used on YouTube 19.18.41~
NextGenWatchLayoutFingerprint.resultOrThrow().mutableMethod.apply {
val booleanValueIndex = getTargetIndexWithMethodReferenceName("booleanValue")
val insertIndex = findIGetIndex(booleanValueIndex - 10, booleanValueIndex)
val insertInstruction = getInstruction<TwoRegisterInstruction>(insertIndex)
val insertReference = getInstruction<ReferenceInstruction>(insertIndex).reference
addInstruction(
insertIndex + 1,
"iget-object v${insertInstruction.registerA}, v${insertInstruction.registerB}, $insertReference"
)
val jumpIndex = findIGetIndex(booleanValueIndex, booleanValueIndex + 10)
hook(insertIndex + 1, insertInstruction.registerA, jumpIndex)
}
SlideToSeekMotionEventFingerprint.resolve(
context,
HorizontalTouchOffsetConstructorFingerprint.resultOrThrow().classDef
)
SlideToSeekMotionEventFingerprint.resultOrThrow().let {
it.mutableMethod.apply {
val scanResult = it.scanResult.patternScanResult!!
val slideToSeekBooleanIndex = scanResult.startIndex + 1
slideToSeekBooleanMethod = getWalkerMethod(context, slideToSeekBooleanIndex)
val jumpIndex = scanResult.endIndex + 1
val insertIndex = scanResult.endIndex - 1
val insertRegister = getInstruction<TwoRegisterInstruction>(insertIndex).registerA
hook(insertIndex, insertRegister, jumpIndex)
}
}
slideToSeekBooleanMethod.apply {
var insertIndex = getTargetIndex(Opcode.IGET_OBJECT)
var insertRegister = getInstruction<TwoRegisterInstruction>(insertIndex).registerA
var jumpIndex = getTargetIndexReversed(Opcode.INVOKE_VIRTUAL)
hook(insertIndex, insertRegister, jumpIndex)
val constructorMethod =
context.findClass(definingClass)?.mutableClass
?.methods?.find { method -> method.name == "<init>" }
?: throw PatchException("Could not find constructor method")
constructorMethod.apply {
val syntheticIndex = getTargetIndexReversed(Opcode.NEW_INSTANCE)
val syntheticClass = getInstruction<ReferenceInstruction>(syntheticIndex).reference.toString()
val syntheticMethod =
context.findClass(syntheticClass)?.mutableClass
?.methods?.find { method -> method.name == "run" }
?: throw PatchException("Could not find synthetic method")
syntheticMethod.apply {
val speedOverlayValueIndex =
indexOfFirstInstruction { (this as? NarrowLiteralInstruction)?.narrowLiteral == 2.0f.toRawBits() }
val speedOverlayValueRegister =
getInstruction<OneRegisterInstruction>(speedOverlayValueIndex).registerA
addInstructions(
speedOverlayValueIndex + 1, """
invoke-static {v$speedOverlayValueRegister}, $PLAYER_CLASS_DESCRIPTOR->speedOverlayValue(F)F
move-result v$speedOverlayValueRegister
"""
)
insertIndex = getTargetIndexWithMethodReferenceNameReversed(speedOverlayValueIndex, "removeCallbacks") + 1
insertRegister = getInstruction<FiveRegisterInstruction>(insertIndex - 1).registerC
jumpIndex = getTargetIndex(speedOverlayValueIndex, Opcode.RETURN_VOID) + 1
hook(insertIndex, insertRegister, jumpIndex)
}
}
}
SpeedOverlayTextValueFingerprint.resultOrThrow().let {
it.mutableMethod.apply {
val targetIndex = it.scanResult.patternScanResult!!.startIndex
val targetRegister = getInstruction<OneRegisterInstruction>(targetIndex).registerA
addInstructions(
targetIndex + 1, """
invoke-static {}, $PLAYER_CLASS_DESCRIPTOR->speedOverlayValue()D
move-result-wide v$targetRegister
"""
)
}
}
}
}
private lateinit var slideToSeekBooleanMethod: MutableMethod
// restore slide to seek
private fun MutableMethod.hook(
insertIndex: Int,
insertRegister: Int,
jumpIndex: Int
) {
addInstructionsWithLabels(
insertIndex,
"""
invoke-static {}, $PLAYER_CLASS_DESCRIPTOR->disableSpeedOverlay()Z
move-result v$insertRegister
if-eqz v$insertRegister, :disable
""", ExternalLabel("disable", getInstruction(jumpIndex))
)
}
private fun MutableMethod.findIGetIndex(
startIndex: Int,
endIndex: Int
): Int = implementation!!.instructions.let { instruction ->
startIndex + instruction.subList(startIndex, endIndex).indexOfFirst {
it.opcode == Opcode.IGET_OBJECT
&& it.getReference<FieldReference>()?.definingClass == definingClass
}
}
}

View File

@ -0,0 +1,11 @@
package app.revanced.patches.youtube.player.speedoverlay.fingerprints
import app.revanced.patcher.extensions.or
import app.revanced.patches.youtube.utils.resourceid.SharedResourceIdPatch.SeekEasyHorizontalTouchOffsetToStartScrubbing
import app.revanced.util.fingerprint.LiteralValueFingerprint
import com.android.tools.smali.dexlib2.AccessFlags
internal object HorizontalTouchOffsetConstructorFingerprint : LiteralValueFingerprint(
accessFlags = AccessFlags.PUBLIC or AccessFlags.CONSTRUCTOR,
literalSupplier = { SeekEasyHorizontalTouchOffsetToStartScrubbing }
)

View File

@ -0,0 +1,18 @@
package app.revanced.patches.youtube.player.speedoverlay.fingerprints
import app.revanced.patcher.extensions.or
import app.revanced.patcher.fingerprint.MethodFingerprint
import app.revanced.util.containsMethodReferenceNameInstructionIndex
import com.android.tools.smali.dexlib2.AccessFlags
internal object NextGenWatchLayoutFingerprint : MethodFingerprint(
returnType = "Z",
accessFlags = AccessFlags.PUBLIC or AccessFlags.FINAL,
parameters = emptyList(),
customFingerprint = handler@{ methodDef, _ ->
if (methodDef.definingClass != "Lcom/google/android/apps/youtube/app/watch/nextgenwatch/ui/NextGenWatchLayout;")
return@handler false
methodDef.containsMethodReferenceNameInstructionIndex("booleanValue")
}
)

View File

@ -1,4 +1,4 @@
package app.revanced.patches.youtube.player.components.fingerprints
package app.revanced.patches.youtube.player.speedoverlay.fingerprints
import app.revanced.util.fingerprint.LiteralValueFingerprint
import com.android.tools.smali.dexlib2.Opcode

View File

@ -0,0 +1,20 @@
package app.revanced.patches.youtube.player.speedoverlay.fingerprints
import app.revanced.patcher.extensions.or
import app.revanced.patcher.fingerprint.MethodFingerprint
import com.android.tools.smali.dexlib2.AccessFlags
import com.android.tools.smali.dexlib2.Opcode
internal object SlideToSeekMotionEventFingerprint : MethodFingerprint(
returnType = "Z",
accessFlags = AccessFlags.PUBLIC or AccessFlags.FINAL,
parameters = listOf("Landroid/view/View;", "Landroid/view/MotionEvent;"),
opcodes = listOf(
Opcode.SUB_FLOAT_2ADDR,
Opcode.INVOKE_VIRTUAL, // SlideToSeek Boolean method
Opcode.MOVE_RESULT,
Opcode.IF_NEZ,
Opcode.IGET_OBJECT, // insert index
Opcode.INVOKE_VIRTUAL
)
)

View File

@ -1,4 +1,4 @@
package app.revanced.patches.youtube.player.components.fingerprints
package app.revanced.patches.youtube.player.speedoverlay.fingerprints
import app.revanced.util.fingerprint.LiteralValueFingerprint
import com.android.tools.smali.dexlib2.Opcode

View File

@ -0,0 +1,14 @@
package app.revanced.patches.youtube.player.speedoverlay.fingerprints
import app.revanced.patcher.extensions.or
import app.revanced.util.fingerprint.ReferenceFingerprint
import com.android.tools.smali.dexlib2.AccessFlags
import com.android.tools.smali.dexlib2.Opcode
internal object SpeedOverlayTextValueFingerprint : ReferenceFingerprint(
returnType = "V",
accessFlags = AccessFlags.PRIVATE or AccessFlags.FINAL,
parameters = emptyList(),
opcodes = listOf(Opcode.CONST_WIDE_HIGH16),
reference = { "Ljava/math/BigDecimal;->signum()I" }
)

View File

@ -1,4 +1,4 @@
package app.revanced.patches.youtube.player.components.fingerprints
package app.revanced.patches.youtube.player.speedoverlay.fingerprints
import app.revanced.patcher.extensions.or
import app.revanced.util.fingerprint.LiteralValueFingerprint

View File

@ -28,6 +28,7 @@ import app.revanced.patches.youtube.utils.resourceid.SharedResourceIdPatch.ReelD
import app.revanced.patches.youtube.utils.resourceid.SharedResourceIdPatch.ReelDynShare
import app.revanced.patches.youtube.utils.resourceid.SharedResourceIdPatch.ReelForcedMuteButton
import app.revanced.patches.youtube.utils.resourceid.SharedResourceIdPatch.ReelPivotButton
import app.revanced.patches.youtube.utils.resourceid.SharedResourceIdPatch.ReelPlayerFooter
import app.revanced.patches.youtube.utils.resourceid.SharedResourceIdPatch.ReelRightDislikeIcon
import app.revanced.patches.youtube.utils.resourceid.SharedResourceIdPatch.ReelRightLikeIcon
import app.revanced.patches.youtube.utils.resourceid.SharedResourceIdPatch.RightComment
@ -44,6 +45,7 @@ import com.android.tools.smali.dexlib2.iface.instruction.FiveRegisterInstruction
import com.android.tools.smali.dexlib2.iface.instruction.OneRegisterInstruction
import com.android.tools.smali.dexlib2.iface.instruction.ReferenceInstruction
import com.android.tools.smali.dexlib2.iface.instruction.TwoRegisterInstruction
import com.android.tools.smali.dexlib2.iface.instruction.WideLiteralInstruction
import com.android.tools.smali.dexlib2.iface.reference.FieldReference
@Suppress("unused")
@ -150,8 +152,14 @@ object ShortsComponentPatch : BaseBytecodePatch(
}
} ?: ShortsPivotFingerprint.resultOrThrow().let {
it.mutableMethod.apply {
val targetIndex = getWideLiteralInstructionIndex(ReelPivotButton)
val constCalls = implementation!!.instructions.withIndex()
.filter { instruction ->
(instruction.value as? WideLiteralInstruction)?.wideLiteral == ReelPivotButton
}
val targetIndex = constCalls.elementAt(constCalls.size - 1).index
val insertIndex = getTargetIndexReversed(targetIndex, Opcode.INVOKE_STATIC) + 1
if (insertIndex == 0)
throw PatchException("insert index not found")
hideButtons(insertIndex, "hideShortsSoundButton(Ljava/lang/Object;)Ljava/lang/Object;")
}
@ -215,7 +223,7 @@ object ShortsComponentPatch : BaseBytecodePatch(
lateinit var subscriptionFieldReference: FieldReference
parentResult.mutableMethod.apply {
val targetIndex = getWideLiteralInstructionIndex(SharedResourceIdPatch.ReelPlayerFooter) - 1
val targetIndex = getWideLiteralInstructionIndex(ReelPlayerFooter) - 1
subscriptionFieldReference =
(getInstruction<ReferenceInstruction>(targetIndex)).reference as FieldReference
}

View File

@ -39,7 +39,7 @@ object ShortsNavigationBarPatch : BytecodePatch(
}
RenderBottomNavigationBarFingerprint.resultOrThrow().let {
val walkerMethod = it.getWalkerMethod(context, it.scanResult.patternScanResult!!.startIndex + 1)
val walkerMethod = it.getWalkerMethod(context, it.scanResult.patternScanResult!!.endIndex)
walkerMethod.addInstruction(
0,

View File

@ -4,13 +4,10 @@ import app.revanced.patcher.fingerprint.MethodFingerprint
import com.android.tools.smali.dexlib2.Opcode
internal object RenderBottomNavigationBarFingerprint : MethodFingerprint(
returnType = "V",
returnType = "Landroid/view/View;",
opcodes = listOf(
Opcode.CONST_STRING,
Opcode.INVOKE_VIRTUAL,
Opcode.INVOKE_VIRTUAL,
Opcode.MOVE_RESULT,
Opcode.IF_NEZ
Opcode.INVOKE_VIRTUAL
),
strings = listOf("r_ipl")
strings = listOf("r_pfcv")
)

View File

@ -1,9 +1,20 @@
package app.revanced.patches.youtube.shorts.components.fingerprints
import app.revanced.patcher.fingerprint.MethodFingerprint
import app.revanced.patches.youtube.utils.resourceid.SharedResourceIdPatch.ReelPivotButton
import app.revanced.util.fingerprint.LiteralValueFingerprint
import app.revanced.patches.youtube.utils.settings.SettingsPatch
import app.revanced.util.containsWideLiteralInstructionIndex
import app.revanced.util.getStringInstructionIndex
internal object ShortsPivotFingerprint : LiteralValueFingerprint(
internal object ShortsPivotFingerprint : MethodFingerprint(
returnType = "V",
literalSupplier = { ReelPivotButton }
customFingerprint = custom@{ methodDef, _ ->
if (!methodDef.containsWideLiteralInstructionIndex(ReelPivotButton))
return@custom false
if (!SettingsPatch.upward1912)
return@custom true
methodDef.getStringInstructionIndex("RHS is rendered through element view for Ads") > 0
}
)

View File

@ -7,12 +7,12 @@ import app.revanced.patcher.extensions.InstructionExtensions.removeInstruction
import app.revanced.patcher.patch.PatchException
import app.revanced.patcher.util.smali.ExternalLabel
import app.revanced.patches.youtube.shorts.startupshortsreset.fingerprints.UserWasInShortsABConfigFingerprint
import app.revanced.patches.youtube.shorts.startupshortsreset.fingerprints.UserWasInShortsABConfigFingerprint.indexOfOptionalInstruction
import app.revanced.patches.youtube.shorts.startupshortsreset.fingerprints.UserWasInShortsFingerprint
import app.revanced.patches.youtube.utils.compatibility.Constants.COMPATIBLE_PACKAGE
import app.revanced.patches.youtube.utils.integrations.Constants.SHORTS_CLASS_DESCRIPTOR
import app.revanced.patches.youtube.utils.settings.SettingsPatch
import app.revanced.util.getReference
import app.revanced.util.getTargetIndex
import app.revanced.util.getWalkerMethod
import app.revanced.util.indexOfFirstInstruction
import app.revanced.util.patch.BaseBytecodePatch
@ -20,7 +20,6 @@ import app.revanced.util.resultOrThrow
import com.android.tools.smali.dexlib2.Opcode
import com.android.tools.smali.dexlib2.iface.instruction.FiveRegisterInstruction
import com.android.tools.smali.dexlib2.iface.instruction.OneRegisterInstruction
import com.android.tools.smali.dexlib2.iface.instruction.TwoRegisterInstruction
import com.android.tools.smali.dexlib2.iface.reference.MethodReference
@Suppress("unused")
@ -36,23 +35,34 @@ object ResumingShortsOnStartupPatch : BaseBytecodePatch(
) {
override fun execute(context: BytecodeContext) {
UserWasInShortsABConfigFingerprint.resultOrThrow().let {
val walkerMethod = it.getWalkerMethod(context, it.scanResult.patternScanResult!!.startIndex)
UserWasInShortsABConfigFingerprint.resultOrThrow().mutableMethod.apply {
val startIndex = indexOfOptionalInstruction(this)
val walkerIndex = implementation!!.instructions.let {
val subListIndex = it.subList(startIndex, startIndex + 20).indexOfFirst { instruction ->
val reference = instruction.getReference<MethodReference>()
instruction.opcode == Opcode.INVOKE_VIRTUAL
&& reference?.returnType == "Z"
&& reference.definingClass != "Lj${'$'}/util/Optional;"
&& reference.parameterTypes.size == 0
}
if (subListIndex < 0)
throw PatchException("subListIndex not found")
startIndex + subListIndex
}
val walkerMethod = getWalkerMethod(context, walkerIndex)
// This method will only be called for the user being A/B tested.
// Presumably a method that processes the ProtoDataStore value (boolean) for the 'user_was_in_shorts' key.
walkerMethod.apply {
val insertIndex = getTargetIndex(Opcode.IGET_OBJECT)
val insertRegister = getInstruction<TwoRegisterInstruction>(insertIndex).registerA
addInstructionsWithLabels(
insertIndex, """
0, """
invoke-static {}, $SHORTS_CLASS_DESCRIPTOR->disableResumingStartupShortsPlayer()Z
move-result v$insertRegister
if-eqz v$insertRegister, :show
const/4 v$insertRegister, 0x0
return v$insertRegister
""", ExternalLabel("show", getInstruction(insertIndex))
move-result v0
if-eqz v0, :show
const/4 v0, 0x0
return v0
""", ExternalLabel("show", getInstruction(0))
)
}
}

View File

@ -1,21 +1,26 @@
package app.revanced.patches.youtube.shorts.startupshortsreset.fingerprints
import app.revanced.patcher.fingerprint.MethodFingerprint
import app.revanced.patches.youtube.shorts.startupshortsreset.fingerprints.UserWasInShortsABConfigFingerprint.indexOfOptionalInstruction
import app.revanced.util.getReference
import app.revanced.util.indexOfFirstInstruction
import com.android.tools.smali.dexlib2.Opcode
import com.android.tools.smali.dexlib2.iface.Method
import com.android.tools.smali.dexlib2.iface.reference.MethodReference
/**
* This fingerprint is compatible with all YouTube versions after v18.15.40.
*/
internal object UserWasInShortsABConfigFingerprint : MethodFingerprint(
returnType = "V",
opcodes = listOf(
Opcode.INVOKE_VIRTUAL,
Opcode.MOVE_RESULT,
Opcode.INVOKE_VIRTUAL,
Opcode.MOVE_RESULT,
Opcode.INVOKE_VIRTUAL,
Opcode.MOVE_RESULT,
Opcode.INVOKE_VIRTUAL
),
strings = listOf("Failed to get offline response: ")
)
strings = listOf("Failed to get offline response: "),
customFingerprint = { methodDef, _ ->
indexOfOptionalInstruction(methodDef) >= 0
}
) {
fun indexOfOptionalInstruction(methodDef: Method) =
methodDef.indexOfFirstInstruction {
opcode == Opcode.INVOKE_STATIC
&& getReference<MethodReference>().toString() == "Lj${'$'}/util/Optional;->of(Ljava/lang/Object;)Lj${'$'}/util/Optional;"
}
}

View File

@ -12,7 +12,9 @@ object Constants {
"18.38.44",
"18.48.39",
"19.05.36",
"19.16.39"
"19.16.39",
"19.19.39",
"19.20.34",
)
)
)

View File

@ -5,6 +5,7 @@ import app.revanced.patcher.extensions.InstructionExtensions.addInstruction
import app.revanced.patcher.extensions.InstructionExtensions.getInstruction
import app.revanced.patcher.patch.BytecodePatch
import app.revanced.patcher.patch.annotation.Patch
import app.revanced.patches.youtube.utils.fix.fullscreen.fingerprints.FullscreenButtonPositionFingerprint
import app.revanced.patches.youtube.utils.fix.fullscreen.fingerprints.FullscreenButtonViewStubFingerprint
import app.revanced.util.getTargetIndex
import app.revanced.util.getWideLiteralInstructionIndex
@ -15,7 +16,10 @@ import com.android.tools.smali.dexlib2.iface.instruction.OneRegisterInstruction
description = "Fixes an issue where overlay button patches were broken by the new layout."
)
object FullscreenButtonViewStubPatch : BytecodePatch(
setOf(FullscreenButtonViewStubFingerprint)
setOf(
FullscreenButtonPositionFingerprint,
FullscreenButtonViewStubFingerprint
)
) {
override fun execute(context: BytecodeContext) {
@ -23,15 +27,20 @@ object FullscreenButtonViewStubPatch : BytecodePatch(
* This issue only affects some versions of YouTube.
* Therefore, this patch only applies to versions that can resolve this fingerprint.
*/
FullscreenButtonViewStubFingerprint.result?.let {
it.mutableMethod.apply {
val targetIndex = getTargetIndex(getWideLiteralInstructionIndex(45617294), Opcode.MOVE_RESULT)
val targetRegister = getInstruction<OneRegisterInstruction>(targetIndex).registerA
mapOf(
FullscreenButtonViewStubFingerprint to 45617294,
FullscreenButtonPositionFingerprint to 45627640
).forEach { (fingerprint, literalValue) ->
fingerprint.result?.let {
it.mutableMethod.apply {
val targetIndex = getTargetIndex(getWideLiteralInstructionIndex(literalValue.toLong()), Opcode.MOVE_RESULT)
val targetRegister = getInstruction<OneRegisterInstruction>(targetIndex).registerA
addInstruction(
targetIndex + 1,
"const/4 v$targetRegister, 0x0"
)
addInstruction(
targetIndex + 1,
"const/4 v$targetRegister, 0x0"
)
}
}
}

View File

@ -0,0 +1,8 @@
package app.revanced.patches.youtube.utils.fix.fullscreen.fingerprints
import app.revanced.util.fingerprint.LiteralValueFingerprint
internal object FullscreenButtonPositionFingerprint : LiteralValueFingerprint(
returnType = "Z",
literalSupplier = { 45627640 }
)

View File

@ -34,6 +34,7 @@ object SharedResourceIdPatch : ResourcePatch() {
var BottomUiContainerStub = -1L
var CaptionToggleContainer = -1L
var CastMediaRouteButton = -1L
var CfFullscreenButton = -1L
var ChannelListSubMenu = -1L
var CompactLink = -1L
var CompactListItem = -1L
@ -83,6 +84,7 @@ object SharedResourceIdPatch : ResourcePatch() {
var RightComment = -1L
var ScrimOverlay = -1L
var Scrubbing = -1L
var SeekEasyHorizontalTouchOffsetToStartScrubbing = -1L
var SeekUndoEduOverlayStub = -1L
var SingleLoopEduSnackBarText = -1L
var SlidingDialogAnimation = -1L
@ -121,6 +123,7 @@ object SharedResourceIdPatch : ResourcePatch() {
BottomUiContainerStub = getId(ID, "bottom_ui_container_stub")
CaptionToggleContainer = getId(ID, "caption_toggle_container")
CastMediaRouteButton = getId(LAYOUT, "castmediaroutebutton")
CfFullscreenButton = getId(ID, "cf_fullscreen_button")
ChannelListSubMenu = getId(LAYOUT, "channel_list_sub_menu")
CompactLink = getId(LAYOUT, "compact_link")
CompactListItem = getId(LAYOUT, "compact_list_item")
@ -172,6 +175,7 @@ object SharedResourceIdPatch : ResourcePatch() {
RightComment = getId(DRAWABLE, "ic_right_comment_32c")
ScrimOverlay = getId(ID, "scrim_overlay")
Scrubbing = getId(DIMEN, "vertical_touch_offset_to_enter_fine_scrubbing")
SeekEasyHorizontalTouchOffsetToStartScrubbing = getId(DIMEN, "seek_easy_horizontal_touch_offset_to_start_scrubbing")
SeekUndoEduOverlayStub = getId(ID, "seek_undo_edu_overlay_stub")
SingleLoopEduSnackBarText = getId(STRING, "single_loop_edu_snackbar_text")
SlidingDialogAnimation = getId(STYLE, "SlidingDialogAnimation")