mirror of
https://github.com/revanced/revanced-patches.git
synced 2025-04-29 22:24:27 +02:00
fix(YouTube - Shorts autoplay): Fix autoplay with YT 20.12
This commit is contained in:
parent
a1207c9784
commit
06b35b2a7d
@ -2,6 +2,8 @@ package app.revanced.extension.youtube.patches;
|
|||||||
|
|
||||||
import android.app.Activity;
|
import android.app.Activity;
|
||||||
|
|
||||||
|
import androidx.annotation.Nullable;
|
||||||
|
|
||||||
import java.lang.ref.WeakReference;
|
import java.lang.ref.WeakReference;
|
||||||
import java.util.Objects;
|
import java.util.Objects;
|
||||||
|
|
||||||
@ -76,7 +78,7 @@ public class ShortsAutoplayPatch {
|
|||||||
/**
|
/**
|
||||||
* Injection point.
|
* Injection point.
|
||||||
*/
|
*/
|
||||||
public static Enum<?> changeShortsRepeatBehavior(Enum<?> original) {
|
public static Enum<?> changeShortsRepeatBehavior(@Nullable Enum<?> original) {
|
||||||
try {
|
try {
|
||||||
final boolean autoplay;
|
final boolean autoplay;
|
||||||
|
|
||||||
@ -98,17 +100,35 @@ public class ShortsAutoplayPatch {
|
|||||||
: ShortsLoopBehavior.REPEAT;
|
: ShortsLoopBehavior.REPEAT;
|
||||||
|
|
||||||
if (behavior.ytEnumValue != null) {
|
if (behavior.ytEnumValue != null) {
|
||||||
Logger.printDebug(() -> behavior.ytEnumValue == original
|
Logger.printDebug(() -> {
|
||||||
? "Changing Shorts repeat behavior from: " + original.name() + " to: " + behavior.ytEnumValue
|
String name = (original == null ? "unknown (null)" : original.name());
|
||||||
: "Behavior setting is same as original. Using original: " + original.name()
|
return behavior == original
|
||||||
);
|
? "Behavior setting is same as original. Using original: " + name
|
||||||
|
: "Changing Shorts repeat behavior from: " + name + " to: " + behavior.name();
|
||||||
|
});
|
||||||
|
|
||||||
return behavior.ytEnumValue;
|
return behavior.ytEnumValue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (original == null) {
|
||||||
|
// Cannot return null, as null is used to indicate Short was auto played.
|
||||||
|
// Unpatched app replaces null with unknown enum type (appears to fix for bad api data).
|
||||||
|
Enum<?> unknown = ShortsLoopBehavior.UNKNOWN.ytEnumValue;
|
||||||
|
Logger.printDebug(() -> "Original is null, returning: " + unknown.name());
|
||||||
|
return unknown;
|
||||||
|
}
|
||||||
} catch (Exception ex) {
|
} catch (Exception ex) {
|
||||||
Logger.printException(() -> "changeShortsRepeatState failure", ex);
|
Logger.printException(() -> "changeShortsRepeatBehavior failure", ex);
|
||||||
}
|
}
|
||||||
|
|
||||||
return original;
|
return original;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Injection point.
|
||||||
|
*/
|
||||||
|
public static boolean isAutoPlay(Enum<?> original) {
|
||||||
|
return ShortsLoopBehavior.SINGLE_PLAY.ytEnumValue == original;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,8 +1,13 @@
|
|||||||
package app.revanced.patches.youtube.layout.shortsautoplay
|
package app.revanced.patches.youtube.layout.shortsautoplay
|
||||||
|
|
||||||
import app.revanced.patcher.fingerprint
|
import app.revanced.patcher.fingerprint
|
||||||
|
import app.revanced.util.getReference
|
||||||
|
import app.revanced.util.indexOfFirstInstruction
|
||||||
import com.android.tools.smali.dexlib2.AccessFlags
|
import com.android.tools.smali.dexlib2.AccessFlags
|
||||||
import com.android.tools.smali.dexlib2.Opcode
|
import com.android.tools.smali.dexlib2.Opcode
|
||||||
|
import com.android.tools.smali.dexlib2.iface.Method
|
||||||
|
import com.android.tools.smali.dexlib2.iface.reference.FieldReference
|
||||||
|
import com.android.tools.smali.dexlib2.iface.reference.MethodReference
|
||||||
|
|
||||||
internal val reelEnumConstructorFingerprint = fingerprint {
|
internal val reelEnumConstructorFingerprint = fingerprint {
|
||||||
accessFlags(AccessFlags.STATIC, AccessFlags.CONSTRUCTOR)
|
accessFlags(AccessFlags.STATIC, AccessFlags.CONSTRUCTOR)
|
||||||
@ -20,3 +25,27 @@ internal val reelPlaybackRepeatFingerprint = fingerprint {
|
|||||||
parameters("L")
|
parameters("L")
|
||||||
strings("YoutubePlayerState is in throwing an Error.")
|
strings("YoutubePlayerState is in throwing an Error.")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
internal val reelPlaybackFingerprint = fingerprint {
|
||||||
|
accessFlags(AccessFlags.PUBLIC, AccessFlags.FINAL)
|
||||||
|
returns("V")
|
||||||
|
parameters("J")
|
||||||
|
custom { method, _ ->
|
||||||
|
indexOfMilliSecondsInstruction(method) >= 0 &&
|
||||||
|
indexOfInitializationInstruction(method) >= 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun indexOfMilliSecondsInstruction(method: Method) =
|
||||||
|
method.indexOfFirstInstruction {
|
||||||
|
getReference<FieldReference>()?.name == "MILLISECONDS"
|
||||||
|
}
|
||||||
|
|
||||||
|
internal fun indexOfInitializationInstruction(method: Method) =
|
||||||
|
method.indexOfFirstInstruction {
|
||||||
|
val reference = getReference<MethodReference>()
|
||||||
|
opcode == Opcode.INVOKE_DIRECT &&
|
||||||
|
reference?.name == "<init>" &&
|
||||||
|
reference.parameterTypes.size == 3 &&
|
||||||
|
reference.parameterTypes.firstOrNull() == "I"
|
||||||
|
}
|
||||||
|
@ -2,21 +2,32 @@ package app.revanced.patches.youtube.layout.shortsautoplay
|
|||||||
|
|
||||||
import app.revanced.patcher.extensions.InstructionExtensions.addInstruction
|
import app.revanced.patcher.extensions.InstructionExtensions.addInstruction
|
||||||
import app.revanced.patcher.extensions.InstructionExtensions.addInstructions
|
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.extensions.InstructionExtensions.getInstruction
|
||||||
import app.revanced.patcher.patch.bytecodePatch
|
import app.revanced.patcher.patch.bytecodePatch
|
||||||
|
import app.revanced.patcher.util.proxy.mutableTypes.MutableMethod.Companion.toMutable
|
||||||
import app.revanced.patches.all.misc.resources.addResources
|
import app.revanced.patches.all.misc.resources.addResources
|
||||||
import app.revanced.patches.shared.misc.mapping.resourceMappingPatch
|
import app.revanced.patches.shared.misc.mapping.resourceMappingPatch
|
||||||
import app.revanced.patches.shared.misc.settings.preference.SwitchPreference
|
import app.revanced.patches.shared.misc.settings.preference.SwitchPreference
|
||||||
import app.revanced.patches.youtube.misc.extension.sharedExtensionPatch
|
import app.revanced.patches.youtube.misc.extension.sharedExtensionPatch
|
||||||
import app.revanced.patches.youtube.misc.playservice.is_19_34_or_greater
|
import app.revanced.patches.youtube.misc.playservice.is_19_34_or_greater
|
||||||
|
import app.revanced.patches.youtube.misc.playservice.is_20_09_or_greater
|
||||||
import app.revanced.patches.youtube.misc.playservice.versionCheckPatch
|
import app.revanced.patches.youtube.misc.playservice.versionCheckPatch
|
||||||
import app.revanced.patches.youtube.misc.settings.PreferenceScreen
|
import app.revanced.patches.youtube.misc.settings.PreferenceScreen
|
||||||
import app.revanced.patches.youtube.misc.settings.settingsPatch
|
import app.revanced.patches.youtube.misc.settings.settingsPatch
|
||||||
import app.revanced.patches.youtube.shared.mainActivityOnCreateFingerprint
|
import app.revanced.patches.youtube.shared.mainActivityOnCreateFingerprint
|
||||||
import app.revanced.util.findInstructionIndicesReversedOrThrow
|
import app.revanced.util.findInstructionIndicesReversedOrThrow
|
||||||
import app.revanced.util.getReference
|
import app.revanced.util.getReference
|
||||||
|
import app.revanced.util.indexOfFirstInstructionOrThrow
|
||||||
|
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.OneRegisterInstruction
|
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.reference.FieldReference
|
||||||
import com.android.tools.smali.dexlib2.iface.reference.MethodReference
|
import com.android.tools.smali.dexlib2.iface.reference.MethodReference
|
||||||
|
import com.android.tools.smali.dexlib2.immutable.ImmutableMethod
|
||||||
|
import com.android.tools.smali.dexlib2.immutable.ImmutableMethodParameter
|
||||||
|
|
||||||
private const val EXTENSION_CLASS_DESCRIPTOR = "Lapp/revanced/extension/youtube/patches/ShortsAutoplayPatch;"
|
private const val EXTENSION_CLASS_DESCRIPTOR = "Lapp/revanced/extension/youtube/patches/ShortsAutoplayPatch;"
|
||||||
|
|
||||||
@ -98,5 +109,84 @@ val shortsAutoplayPatch = bytecodePatch(
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// As of YouTube 20.09, Google has removed the code for 'Autoplay' and 'Pause' from this method.
|
||||||
|
// Manually restore the removed 'Autoplay' code.
|
||||||
|
if (is_20_09_or_greater) {
|
||||||
|
// Variable names are only a rough guess of what these methods do.
|
||||||
|
val userActionMethodIndex = indexOfInitializationInstruction(reelPlaybackFingerprint.method)
|
||||||
|
val userActionMethodReference = reelPlaybackFingerprint.method
|
||||||
|
.getInstruction<ReferenceInstruction>(userActionMethodIndex).reference as MethodReference
|
||||||
|
val reelSequenceControllerMethodIndex = reelPlaybackFingerprint.method
|
||||||
|
.indexOfFirstInstructionOrThrow(userActionMethodIndex, Opcode.INVOKE_VIRTUAL)
|
||||||
|
val reelSequenceControllerMethodReference = reelPlaybackFingerprint.method
|
||||||
|
.getInstruction<ReferenceInstruction>(reelSequenceControllerMethodIndex).reference as MethodReference
|
||||||
|
|
||||||
|
reelPlaybackRepeatFingerprint.method.apply {
|
||||||
|
// Find the first call modified by extension code above.
|
||||||
|
val extensionReturnResultIndex = indexOfFirstInstructionOrThrow {
|
||||||
|
opcode == Opcode.INVOKE_STATIC &&
|
||||||
|
getReference<MethodReference>()?.definingClass == EXTENSION_CLASS_DESCRIPTOR
|
||||||
|
} + 1
|
||||||
|
val enumRegister = getInstruction<OneRegisterInstruction>(extensionReturnResultIndex).registerA
|
||||||
|
val getReelSequenceControllerIndex = indexOfFirstInstructionOrThrow(extensionReturnResultIndex) {
|
||||||
|
val reference = getReference<FieldReference>()
|
||||||
|
opcode == Opcode.IGET_OBJECT &&
|
||||||
|
reference?.definingClass == definingClass &&
|
||||||
|
reference.type == reelSequenceControllerMethodReference.definingClass
|
||||||
|
}
|
||||||
|
val getReelSequenceControllerReference =
|
||||||
|
getInstruction<ReferenceInstruction>(getReelSequenceControllerIndex).reference
|
||||||
|
|
||||||
|
// Add a helper method to avoid finding multiple free registers.
|
||||||
|
// If enum is autoplay then method performs autoplay and returns null,
|
||||||
|
// otherwise returns the same enum.
|
||||||
|
val helperClass = definingClass
|
||||||
|
val helperName = "patch_handleAutoPlay"
|
||||||
|
val helperReturnType = "Ljava/lang/Enum;"
|
||||||
|
val helperMethod = ImmutableMethod(
|
||||||
|
helperClass,
|
||||||
|
helperName,
|
||||||
|
listOf(ImmutableMethodParameter("Ljava/lang/Enum;", null, null)),
|
||||||
|
helperReturnType,
|
||||||
|
AccessFlags.PRIVATE.value,
|
||||||
|
null,
|
||||||
|
null,
|
||||||
|
MutableMethodImplementation(7),
|
||||||
|
).toMutable().apply {
|
||||||
|
addInstructionsWithLabels(
|
||||||
|
0,
|
||||||
|
"""
|
||||||
|
invoke-static { p1 }, $EXTENSION_CLASS_DESCRIPTOR->isAutoPlay(Ljava/lang/Enum;)Z
|
||||||
|
move-result v0
|
||||||
|
if-eqz v0, :ignore
|
||||||
|
new-instance v0, ${userActionMethodReference.definingClass}
|
||||||
|
const/4 v1, 0x3
|
||||||
|
const/4 v2, 0x0
|
||||||
|
invoke-direct { v0, v1, v2, v2 }, $userActionMethodReference
|
||||||
|
iget-object v3, p0, $getReelSequenceControllerReference
|
||||||
|
invoke-virtual { v3, v0 }, $reelSequenceControllerMethodReference
|
||||||
|
const/4 v4, 0x0
|
||||||
|
return-object v4
|
||||||
|
:ignore
|
||||||
|
return-object p1
|
||||||
|
"""
|
||||||
|
)
|
||||||
|
}
|
||||||
|
reelPlaybackRepeatFingerprint.classDef.methods.add(helperMethod)
|
||||||
|
|
||||||
|
addInstructionsWithLabels(
|
||||||
|
extensionReturnResultIndex + 1,
|
||||||
|
"""
|
||||||
|
invoke-direct { p0, v$enumRegister }, $helperClass->$helperName(Ljava/lang/Enum;)$helperReturnType
|
||||||
|
move-result-object v$enumRegister
|
||||||
|
if-nez v$enumRegister, :ignore
|
||||||
|
return-void # Autoplay was performed.
|
||||||
|
:ignore
|
||||||
|
nop
|
||||||
|
"""
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user