Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(YouTube): Add Shorts autoplay patch #3794

Merged
6 changes: 6 additions & 0 deletions api/revanced-patches.api
Original file line number Diff line number Diff line change
Expand Up @@ -1832,6 +1832,12 @@ public final class app/revanced/patches/youtube/layout/seekbar/RestoreOldSeekbar
public synthetic fun execute (Lapp/revanced/patcher/data/Context;)V
}

public final class app/revanced/patches/youtube/layout/shortsrepeat/ChangeShortsRepeatPatch : app/revanced/patcher/patch/BytecodePatch {
public static final field INSTANCE Lapp/revanced/patches/youtube/layout/shortsrepeat/ChangeShortsRepeatPatch;
public fun execute (Lapp/revanced/patcher/data/BytecodeContext;)V
public synthetic fun execute (Lapp/revanced/patcher/data/Context;)V
}

public final class app/revanced/patches/youtube/layout/sponsorblock/SponsorBlockBytecodePatch : app/revanced/patcher/patch/BytecodePatch {
public static final field INSTANCE Lapp/revanced/patches/youtube/layout/sponsorblock/SponsorBlockBytecodePatch;
public fun execute (Lapp/revanced/patcher/data/BytecodeContext;)V
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,120 @@
package app.revanced.patches.youtube.layout.shortsrepeat

import app.revanced.patcher.data.BytecodeContext
import app.revanced.patcher.extensions.InstructionExtensions.addInstructions
import app.revanced.patcher.extensions.InstructionExtensions.getInstruction
import app.revanced.patcher.patch.BytecodePatch
import app.revanced.patcher.patch.annotation.CompatiblePackage
import app.revanced.patcher.patch.annotation.Patch
import app.revanced.patches.all.misc.resources.AddResourcesPatch
import app.revanced.patches.all.misc.resources.AddResourcesPatch.invoke
import app.revanced.patches.shared.misc.mapping.ResourceMappingPatch
import app.revanced.patches.shared.misc.settings.preference.ListPreference
import app.revanced.patches.youtube.layout.shortsrepeat.fingerprints.ReelEnumConstructorFingerprint
import app.revanced.patches.youtube.layout.shortsrepeat.fingerprints.ReelPlaybackRepeatFingerprint
import app.revanced.patches.youtube.misc.integrations.IntegrationsPatch
import app.revanced.patches.youtube.misc.settings.SettingsPatch
import app.revanced.patches.youtube.shared.fingerprints.MainActivityOnCreateFingerprint
import app.revanced.util.findOpcodeIndicesReversed
import app.revanced.util.getReference
import app.revanced.util.indexOfFirstInstructionOrThrow
import app.revanced.util.resultOrThrow
import com.android.tools.smali.dexlib2.Opcode
import com.android.tools.smali.dexlib2.iface.instruction.OneRegisterInstruction
import com.android.tools.smali.dexlib2.iface.reference.MethodReference

@Patch(
name = "Change Shorts repeat",
description = "Adds options to play Shorts once, repeat, or autoplay the next Short.",
dependencies = [
IntegrationsPatch::class,
SettingsPatch::class,
ResourceMappingPatch::class,
],
compatiblePackages = [
CompatiblePackage(
"com.google.android.youtube",
[
"18.49.37",
"19.16.39",
"19.25.37",
"19.34.42",
],
),
],
)
@Suppress("unused")
object ChangeShortsRepeatPatch : BytecodePatch(
setOf(
MainActivityOnCreateFingerprint,
ReelEnumConstructorFingerprint,
ReelPlaybackRepeatFingerprint
)
) {
private const val INTEGRATIONS_CLASS_DESCRIPTOR =
"Lapp/revanced/integrations/youtube/patches/ChangeShortsRepeatPatch;"

override fun execute(context: BytecodeContext) {
AddResourcesPatch(this::class)

SettingsPatch.PreferenceScreen.SHORTS.addPreferences(
ListPreference(
key = "revanced_shorts_repeat_behavior",
summaryKey = null,
),
ListPreference(
key = "revanced_shorts_background_repeat_behavior",
titleKey = "revanced_shorts_background_repeat_behavior_title",
summaryKey = null,
entriesKey = "revanced_shorts_repeat_behavior_entries",
entryValuesKey = "revanced_shorts_repeat_behavior_entry_values"
)
)

// Main activity is used to check if app is in pip mode.
MainActivityOnCreateFingerprint.resultOrThrow().mutableMethod.addInstructions(
0,
"invoke-static/range { p0 .. p0 }, $INTEGRATIONS_CLASS_DESCRIPTOR->" +
"setMainActivity(Landroid/app/Activity;)V",
)

val reelEnumClass: String

ReelEnumConstructorFingerprint.resultOrThrow().let {
reelEnumClass = it.classDef.type

it.mutableMethod.apply {
val insertIndex = indexOfFirstInstructionOrThrow(Opcode.RETURN_VOID)

addInstructions(
insertIndex,
"""
# Pass the first enum value to integrations.
# Any enum value of this type will work.
sget-object v0, $reelEnumClass->a:$reelEnumClass
invoke-static { v0 }, $INTEGRATIONS_CLASS_DESCRIPTOR->setYTShortsRepeatEnum(Ljava/lang/Enum;)V
"""
)
}
}

ReelPlaybackRepeatFingerprint.resultOrThrow().mutableMethod.apply {
// The behavior enums are looked up from an ordinal value to an enum type.
findOpcodeIndicesReversed {
val reference = getReference<MethodReference>()
reference?.definingClass == reelEnumClass
&& reference.parameterTypes.firstOrNull() == "I"
}.forEach { index ->
val register = getInstruction<OneRegisterInstruction>(index + 1).registerA

addInstructions(
index + 2,
"""
invoke-static {v$register}, $INTEGRATIONS_CLASS_DESCRIPTOR->changeShortsRepeatState(Ljava/lang/Enum;)Ljava/lang/Enum;
move-result-object v$register
"""
)
}
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
package app.revanced.patches.youtube.layout.shortsrepeat.fingerprints

import app.revanced.patcher.extensions.or
import app.revanced.patcher.fingerprint.MethodFingerprint
import com.android.tools.smali.dexlib2.AccessFlags
import kotlin.collections.listOf

internal object ReelEnumConstructorFingerprint : MethodFingerprint(
accessFlags = AccessFlags.STATIC or AccessFlags.CONSTRUCTOR,
returnType = "V",
strings = listOf(
"REEL_LOOP_BEHAVIOR_SINGLE_PLAY",
"REEL_LOOP_BEHAVIOR_REPEAT",
"REEL_LOOP_BEHAVIOR_END_SCREEN"
)
)
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
package app.revanced.patches.youtube.layout.shortsrepeat.fingerprints

import app.revanced.patcher.fingerprint.MethodFingerprint

internal object ReelPlaybackRepeatFingerprint : MethodFingerprint(
LisoUseInAIKyrios marked this conversation as resolved.
Show resolved Hide resolved
parameters = listOf("L"),
returnType = "V",
strings = listOf("YoutubePlayerState is in throwing an Error.")
)
15 changes: 15 additions & 0 deletions src/main/resources/addresources/values/arrays.xml
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,21 @@
<item>BROWSE</item>
</string-array>
</patch>
<patch id="layout.shortsrepeat.ChangeShortsRepeatPatch">
<string-array name="revanced_shorts_repeat_behavior_entries">
<item>@string/revanced_shorts_repeat_behavior_entry_1</item>
<item>@string/revanced_shorts_repeat_behavior_entry_2</item>
<item>@string/revanced_shorts_repeat_behavior_entry_3</item>
<item>@string/revanced_shorts_repeat_behavior_entry_4</item>
</string-array>
<string-array name="revanced_shorts_repeat_behavior_entry_values">
<!-- Enum names from Integrations -->
<item>UNKNOWN</item>
<item>REPEAT</item>
<item>SINGLE_PLAY</item>
<item>END_SCREEN</item>
</string-array>
</patch>
<patch id="layout.thumbnails.AlternativeThumbnailsPatch">
<string-array name="revanced_alt_thumbnail_options_entries">
<item>@string/revanced_alt_thumbnail_options_entry_1</item>
Expand Down
8 changes: 8 additions & 0 deletions src/main/resources/addresources/values/strings.xml
Original file line number Diff line number Diff line change
Expand Up @@ -997,6 +997,14 @@ This is because Crowdin requires temporarily flattening this file and removing t
<string name="revanced_disable_resuming_shorts_player_summary_on">Shorts player will not resume on app startup</string>
<string name="revanced_disable_resuming_shorts_player_summary_off">Shorts player will resume on app startup</string>
</patch>
<patch id="layout.shortsrepeat.ChangeShortsRepeatPatch">
<string name="revanced_shorts_repeat_behavior_title">Shorts repeat behavior</string>
<string name="revanced_shorts_repeat_behavior_entry_1">Default</string>
<string name="revanced_shorts_repeat_behavior_entry_2">Repeat</string>
<string name="revanced_shorts_repeat_behavior_entry_3">Autoplay</string>
<string name="revanced_shorts_repeat_behavior_entry_4">Pause</string>
<string name="revanced_shorts_background_repeat_behavior_title">Shorts repeat background behavior</string>
</patch>
<patch id="layout.tablet.EnableTabletLayoutPatch">
<string name="revanced_tablet_layout_title">Enable tablet layout</string>
<string name="revanced_tablet_layout_summary_on">Tablet layout is enabled</string>
Expand Down
Loading