Skip to content

Commit ef35ed7

Browse files
author
LisoUseInAIKyrios
committed
fix(YouTube - Shorts autoplay): Fix autoplay with YT 20.12
1 parent 4fd666b commit ef35ed7

File tree

3 files changed

+145
-6
lines changed

3 files changed

+145
-6
lines changed

extensions/youtube/src/main/java/app/revanced/extension/youtube/patches/ShortsAutoplayPatch.java

Lines changed: 26 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@
22

33
import android.app.Activity;
44

5+
import androidx.annotation.Nullable;
6+
57
import java.lang.ref.WeakReference;
68
import java.util.Objects;
79

@@ -76,7 +78,7 @@ public static void setYTShortsRepeatEnum(Enum<?> ytEnum) {
7678
/**
7779
* Injection point.
7880
*/
79-
public static Enum<?> changeShortsRepeatBehavior(Enum<?> original) {
81+
public static Enum<?> changeShortsRepeatBehavior(@Nullable Enum<?> original) {
8082
try {
8183
final boolean autoplay;
8284

@@ -98,17 +100,35 @@ public static Enum<?> changeShortsRepeatBehavior(Enum<?> original) {
98100
: ShortsLoopBehavior.REPEAT;
99101

100102
if (behavior.ytEnumValue != null) {
101-
Logger.printDebug(() -> behavior.ytEnumValue == original
102-
? "Changing Shorts repeat behavior from: " + original.name() + " to: " + behavior.ytEnumValue
103-
: "Behavior setting is same as original. Using original: " + original.name()
104-
);
103+
Logger.printDebug(() -> {
104+
String name = (original == null ? "unknown (null)" : original.name());
105+
return behavior == original
106+
? "Behavior setting is same as original. Using original: " + name
107+
: "Changing Shorts repeat behavior from: " + name + " to: " + behavior.name();
108+
});
105109

106110
return behavior.ytEnumValue;
107111
}
112+
113+
if (original == null) {
114+
// Cannot return null, as null is used to indicate Short was auto played.
115+
// Unpatched app replaces null with unknown enum type (appears to fix for bad api data).
116+
Enum<?> unknown = ShortsLoopBehavior.UNKNOWN.ytEnumValue;
117+
Logger.printDebug(() -> "Original is null, returning: " + unknown.name());
118+
return unknown;
119+
}
108120
} catch (Exception ex) {
109-
Logger.printException(() -> "changeShortsRepeatState failure", ex);
121+
Logger.printException(() -> "changeShortsRepeatBehavior failure", ex);
110122
}
111123

112124
return original;
113125
}
126+
127+
128+
/**
129+
* Injection point.
130+
*/
131+
public static boolean isAutoPlay(Enum<?> original) {
132+
return ShortsLoopBehavior.SINGLE_PLAY.ytEnumValue == original;
133+
}
114134
}

patches/src/main/kotlin/app/revanced/patches/youtube/layout/shortsautoplay/Fingerprints.kt

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,13 @@
11
package app.revanced.patches.youtube.layout.shortsautoplay
22

33
import app.revanced.patcher.fingerprint
4+
import app.revanced.util.getReference
5+
import app.revanced.util.indexOfFirstInstruction
46
import com.android.tools.smali.dexlib2.AccessFlags
57
import com.android.tools.smali.dexlib2.Opcode
8+
import com.android.tools.smali.dexlib2.iface.Method
9+
import com.android.tools.smali.dexlib2.iface.reference.FieldReference
10+
import com.android.tools.smali.dexlib2.iface.reference.MethodReference
611

712
internal val reelEnumConstructorFingerprint = fingerprint {
813
accessFlags(AccessFlags.STATIC, AccessFlags.CONSTRUCTOR)
@@ -20,3 +25,27 @@ internal val reelPlaybackRepeatFingerprint = fingerprint {
2025
parameters("L")
2126
strings("YoutubePlayerState is in throwing an Error.")
2227
}
28+
29+
internal val reelPlaybackFingerprint = fingerprint {
30+
accessFlags(AccessFlags.PUBLIC, AccessFlags.FINAL)
31+
returns("V")
32+
parameters("J")
33+
custom { method, _ ->
34+
indexOfMilliSecondsInstruction(method) >= 0 &&
35+
indexOfInitializationInstruction(method) >= 0
36+
}
37+
}
38+
39+
private fun indexOfMilliSecondsInstruction(method: Method) =
40+
method.indexOfFirstInstruction {
41+
getReference<FieldReference>()?.name == "MILLISECONDS"
42+
}
43+
44+
internal fun indexOfInitializationInstruction(method: Method) =
45+
method.indexOfFirstInstruction {
46+
val reference = getReference<MethodReference>()
47+
opcode == Opcode.INVOKE_DIRECT &&
48+
reference?.name == "<init>" &&
49+
reference.parameterTypes.size == 3 &&
50+
reference.parameterTypes.firstOrNull() == "I"
51+
}

patches/src/main/kotlin/app/revanced/patches/youtube/layout/shortsautoplay/ShortsAutoplayPatch.kt

Lines changed: 90 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,21 +2,32 @@ package app.revanced.patches.youtube.layout.shortsautoplay
22

33
import app.revanced.patcher.extensions.InstructionExtensions.addInstruction
44
import app.revanced.patcher.extensions.InstructionExtensions.addInstructions
5+
import app.revanced.patcher.extensions.InstructionExtensions.addInstructionsWithLabels
56
import app.revanced.patcher.extensions.InstructionExtensions.getInstruction
67
import app.revanced.patcher.patch.bytecodePatch
8+
import app.revanced.patcher.util.proxy.mutableTypes.MutableMethod.Companion.toMutable
79
import app.revanced.patches.all.misc.resources.addResources
810
import app.revanced.patches.shared.misc.mapping.resourceMappingPatch
911
import app.revanced.patches.shared.misc.settings.preference.SwitchPreference
1012
import app.revanced.patches.youtube.misc.extension.sharedExtensionPatch
1113
import app.revanced.patches.youtube.misc.playservice.is_19_34_or_greater
14+
import app.revanced.patches.youtube.misc.playservice.is_20_09_or_greater
1215
import app.revanced.patches.youtube.misc.playservice.versionCheckPatch
1316
import app.revanced.patches.youtube.misc.settings.PreferenceScreen
1417
import app.revanced.patches.youtube.misc.settings.settingsPatch
1518
import app.revanced.patches.youtube.shared.mainActivityOnCreateFingerprint
1619
import app.revanced.util.findInstructionIndicesReversedOrThrow
1720
import app.revanced.util.getReference
21+
import app.revanced.util.indexOfFirstInstructionOrThrow
22+
import com.android.tools.smali.dexlib2.AccessFlags
23+
import com.android.tools.smali.dexlib2.Opcode
24+
import com.android.tools.smali.dexlib2.builder.MutableMethodImplementation
1825
import com.android.tools.smali.dexlib2.iface.instruction.OneRegisterInstruction
26+
import com.android.tools.smali.dexlib2.iface.instruction.ReferenceInstruction
27+
import com.android.tools.smali.dexlib2.iface.reference.FieldReference
1928
import com.android.tools.smali.dexlib2.iface.reference.MethodReference
29+
import com.android.tools.smali.dexlib2.immutable.ImmutableMethod
30+
import com.android.tools.smali.dexlib2.immutable.ImmutableMethodParameter
2031

2132
private const val EXTENSION_CLASS_DESCRIPTOR = "Lapp/revanced/extension/youtube/patches/ShortsAutoplayPatch;"
2233

@@ -98,5 +109,84 @@ val shortsAutoplayPatch = bytecodePatch(
98109
)
99110
}
100111
}
112+
113+
// As of YouTube 20.09, Google has removed the code for 'Autoplay' and 'Pause' from this method.
114+
// Manually restore the removed 'Autoplay' code.
115+
if (is_20_09_or_greater) {
116+
// Variable names are only a rough guess of what these methods do.
117+
val userActionMethodIndex = indexOfInitializationInstruction(reelPlaybackFingerprint.method)
118+
val userActionMethodReference = reelPlaybackFingerprint.method
119+
.getInstruction<ReferenceInstruction>(userActionMethodIndex).reference as MethodReference
120+
val reelSequenceControllerMethodIndex = reelPlaybackFingerprint.method
121+
.indexOfFirstInstructionOrThrow(userActionMethodIndex, Opcode.INVOKE_VIRTUAL)
122+
val reelSequenceControllerMethodReference = reelPlaybackFingerprint.method
123+
.getInstruction<ReferenceInstruction>(reelSequenceControllerMethodIndex).reference as MethodReference
124+
125+
reelPlaybackRepeatFingerprint.method.apply {
126+
// Find the first call modified by extension code above.
127+
val extensionReturnResultIndex = indexOfFirstInstructionOrThrow {
128+
opcode == Opcode.INVOKE_STATIC &&
129+
getReference<MethodReference>()?.definingClass == EXTENSION_CLASS_DESCRIPTOR
130+
} + 1
131+
val enumRegister = getInstruction<OneRegisterInstruction>(extensionReturnResultIndex).registerA
132+
val getReelSequenceControllerIndex = indexOfFirstInstructionOrThrow(extensionReturnResultIndex) {
133+
val reference = getReference<FieldReference>()
134+
opcode == Opcode.IGET_OBJECT &&
135+
reference?.definingClass == definingClass &&
136+
reference.type == reelSequenceControllerMethodReference.definingClass
137+
}
138+
val getReelSequenceControllerReference =
139+
getInstruction<ReferenceInstruction>(getReelSequenceControllerIndex).reference
140+
141+
// Add a helper method to avoid finding multiple free registers.
142+
// If enum is autoplay then method performs autoplay and returns null,
143+
// otherwise returns the same enum.
144+
val helperClass = definingClass
145+
val helperName = "patch_handleAutoPlay"
146+
val helperReturnType = "Ljava/lang/Enum;"
147+
val helperMethod = ImmutableMethod(
148+
helperClass,
149+
helperName,
150+
listOf(ImmutableMethodParameter("Ljava/lang/Enum;", null, null)),
151+
helperReturnType,
152+
AccessFlags.PRIVATE.value,
153+
null,
154+
null,
155+
MutableMethodImplementation(7),
156+
).toMutable().apply {
157+
addInstructionsWithLabels(
158+
0,
159+
"""
160+
invoke-static { p1 }, $EXTENSION_CLASS_DESCRIPTOR->isAutoPlay(Ljava/lang/Enum;)Z
161+
move-result v0
162+
if-eqz v0, :ignore
163+
new-instance v0, ${userActionMethodReference.definingClass}
164+
const/4 v1, 0x3
165+
const/4 v2, 0x0
166+
invoke-direct { v0, v1, v2, v2 }, $userActionMethodReference
167+
iget-object v3, p0, $getReelSequenceControllerReference
168+
invoke-virtual { v3, v0 }, $reelSequenceControllerMethodReference
169+
const/4 v4, 0x0
170+
return-object v4
171+
:ignore
172+
return-object p1
173+
"""
174+
)
175+
}
176+
reelPlaybackRepeatFingerprint.classDef.methods.add(helperMethod)
177+
178+
addInstructionsWithLabels(
179+
extensionReturnResultIndex + 1,
180+
"""
181+
invoke-direct { p0, v$enumRegister }, $helperClass->$helperName(Ljava/lang/Enum;)$helperReturnType
182+
move-result-object v$enumRegister
183+
if-nez v$enumRegister, :ignore
184+
return-void # Autoplay was performed.
185+
:ignore
186+
nop
187+
"""
188+
)
189+
}
190+
}
101191
}
102192
}

0 commit comments

Comments
 (0)