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/shortsautoplay/ShortsAutoplayPatch : app/revanced/patcher/patch/BytecodePatch {
public static final field INSTANCE Lapp/revanced/patches/youtube/layout/shortsautoplay/ShortsAutoplayPatch;
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
Expand Up @@ -5,6 +5,8 @@ import app.revanced.patcher.patch.ResourcePatch
import app.revanced.patcher.patch.annotation.Patch
import app.revanced.patches.all.misc.resources.AddResourcesPatch
import app.revanced.patches.shared.misc.mapping.ResourceMappingPatch
import app.revanced.patches.shared.misc.settings.preference.PreferenceScreen
import app.revanced.patches.shared.misc.settings.preference.PreferenceScreen.Sorting
import app.revanced.patches.shared.misc.settings.preference.SwitchPreference
import app.revanced.patches.youtube.layout.hide.shorts.HideShortsComponentsPatch.hideShortsAppShortcut
import app.revanced.patches.youtube.layout.hide.shorts.HideShortsComponentsPatch.hideShortsWidget
Expand Down Expand Up @@ -34,40 +36,49 @@ object HideShortsComponentsResourcePatch : ResourcePatch() {
SwitchPreference("revanced_hide_shorts_subscriptions"),
SwitchPreference("revanced_hide_shorts_search"),

// Shorts player components.
// Ideally each group should be ordered similar to how they appear in the UI
// since this Setting menu currently uses the ordering used here.

// Vertical row of buttons on right side of the screen.
SwitchPreference("revanced_hide_shorts_like_fountain"),
SwitchPreference("revanced_hide_shorts_like_button"),
SwitchPreference("revanced_hide_shorts_dislike_button"),
SwitchPreference("revanced_hide_shorts_comments_button"),
SwitchPreference("revanced_hide_shorts_share_button"),
SwitchPreference("revanced_hide_shorts_remix_button"),
SwitchPreference("revanced_hide_shorts_sound_button"),

// Everything else.
SwitchPreference("revanced_hide_shorts_join_button"),
SwitchPreference("revanced_hide_shorts_subscribe_button"),
SwitchPreference("revanced_hide_shorts_paused_overlay_buttons"),
SwitchPreference("revanced_hide_shorts_save_sound_button"),
SwitchPreference("revanced_hide_shorts_use_template_button"),
SwitchPreference("revanced_hide_shorts_upcoming_button"),
SwitchPreference("revanced_hide_shorts_green_screen_button"),
SwitchPreference("revanced_hide_shorts_hashtag_button"),
SwitchPreference("revanced_hide_shorts_shop_button"),
SwitchPreference("revanced_hide_shorts_tagged_products"),
SwitchPreference("revanced_hide_shorts_stickers"),
SwitchPreference("revanced_hide_shorts_search_suggestions"),
SwitchPreference("revanced_hide_shorts_super_thanks_button"),
SwitchPreference("revanced_hide_shorts_location_label"),
SwitchPreference("revanced_hide_shorts_channel_bar"),
SwitchPreference("revanced_hide_shorts_info_panel"),
SwitchPreference("revanced_hide_shorts_full_video_link_label"),
SwitchPreference("revanced_hide_shorts_video_title"),
SwitchPreference("revanced_hide_shorts_sound_metadata_label"),
SwitchPreference("revanced_hide_shorts_navigation_bar"),
PreferenceScreen(
key = "revanced_shorts_player_screen",
sorting = Sorting.UNSORTED,
preferences = setOf(
// Shorts player components.
// Ideally each group should be ordered similar to how they appear in the UI

// Vertical row of buttons on right side of the screen.
SwitchPreference("revanced_hide_shorts_like_fountain"),
SwitchPreference("revanced_hide_shorts_like_button"),
SwitchPreference("revanced_hide_shorts_dislike_button"),
SwitchPreference("revanced_hide_shorts_comments_button"),
SwitchPreference("revanced_hide_shorts_share_button"),
SwitchPreference("revanced_hide_shorts_remix_button"),
SwitchPreference("revanced_hide_shorts_sound_button"),

// Upper and middle area of the player.
SwitchPreference("revanced_hide_shorts_join_button"),
SwitchPreference("revanced_hide_shorts_subscribe_button"),
SwitchPreference("revanced_hide_shorts_paused_overlay_buttons"),

// Suggested actions.
SwitchPreference("revanced_hide_shorts_save_sound_button"),
SwitchPreference("revanced_hide_shorts_use_template_button"),
SwitchPreference("revanced_hide_shorts_upcoming_button"),
SwitchPreference("revanced_hide_shorts_green_screen_button"),
SwitchPreference("revanced_hide_shorts_hashtag_button"),
SwitchPreference("revanced_hide_shorts_shop_button"),
SwitchPreference("revanced_hide_shorts_tagged_products"),
SwitchPreference("revanced_hide_shorts_search_suggestions"),
SwitchPreference("revanced_hide_shorts_super_thanks_button"),
SwitchPreference("revanced_hide_shorts_stickers"),

// Bottom of the screen.
SwitchPreference("revanced_hide_shorts_location_label"),
SwitchPreference("revanced_hide_shorts_channel_bar"),
SwitchPreference("revanced_hide_shorts_info_panel"),
SwitchPreference("revanced_hide_shorts_full_video_link_label"),
SwitchPreference("revanced_hide_shorts_video_title"),
SwitchPreference("revanced_hide_shorts_sound_metadata_label"),
SwitchPreference("revanced_hide_shorts_navigation_bar"),
)
)
)

if (hideShortsAppShortcut == true) {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,119 @@
package app.revanced.patches.youtube.layout.shortsautoplay

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.SwitchPreference
import app.revanced.patches.youtube.layout.shortsautoplay.fingerprints.ReelEnumConstructorFingerprint
import app.revanced.patches.youtube.layout.shortsautoplay.fingerprints.ReelPlaybackRepeatFingerprint
import app.revanced.patches.youtube.misc.integrations.IntegrationsPatch
import app.revanced.patches.youtube.misc.playservice.VersionCheckPatch
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 = "Shorts autoplay",
description = "Adds options to automatically play the next Short.",
dependencies = [
IntegrationsPatch::class,
SettingsPatch::class,
ResourceMappingPatch::class,
VersionCheckPatch::class,
],
compatiblePackages = [
CompatiblePackage(
"com.google.android.youtube",
[
"18.49.37",
"19.16.39",
"19.25.37",
"19.34.42",
],
),
],
)
@Suppress("unused")
object ShortsAutoplayPatch : BytecodePatch(
setOf(
MainActivityOnCreateFingerprint,
ReelEnumConstructorFingerprint,
ReelPlaybackRepeatFingerprint
)
) {
private const val INTEGRATIONS_CLASS_DESCRIPTOR =
"Lapp/revanced/integrations/youtube/patches/ShortsAutoplayPatch;"

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

SettingsPatch.PreferenceScreen.SHORTS.addPreferences(
SwitchPreference("revanced_shorts_autoplay")
)

if (VersionCheckPatch.is_19_34_or_greater) {
SettingsPatch.PreferenceScreen.SHORTS.addPreferences(
SwitchPreference("revanced_shorts_autoplay_background")
)
}

// 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 = it.scanResult.patternScanResult!!.startIndex

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"
&& reference.returnType == reelEnumClass
}.forEach { index ->
val register = getInstruction<OneRegisterInstruction>(index + 1).registerA

addInstructions(
index + 2,
"""
invoke-static {v$register}, $INTEGRATIONS_CLASS_DESCRIPTOR->changeShortsRepeatBehavior(Ljava/lang/Enum;)Ljava/lang/Enum;
move-result-object v$register
"""
)
}
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
package app.revanced.patches.youtube.layout.shortsautoplay.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
import kotlin.collections.listOf

internal object ReelEnumConstructorFingerprint : MethodFingerprint(
accessFlags = AccessFlags.STATIC or AccessFlags.CONSTRUCTOR,
opcodes = listOf(Opcode.RETURN_VOID),
strings = listOf(
"REEL_LOOP_BEHAVIOR_UNKNOWN",
"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.shortsautoplay.fingerprints

import app.revanced.patcher.fingerprint.MethodFingerprint

internal object ReelPlaybackRepeatFingerprint : MethodFingerprint(
parameters = listOf("L"),
returnType = "V",
strings = listOf("YoutubePlayerState is in throwing an Error.")
)
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ internal object VersionCheckPatch : ResourcePatch() {
var is_19_29_or_greater by Delegates.notNull<Boolean>()
var is_19_32_or_greater by Delegates.notNull<Boolean>()
var is_19_33_or_greater by Delegates.notNull<Boolean>()
var is_19_34_or_greater by Delegates.notNull<Boolean>()
var is_19_36_or_greater by Delegates.notNull<Boolean>()
var is_19_41_or_greater by Delegates.notNull<Boolean>()

Expand All @@ -46,6 +47,7 @@ internal object VersionCheckPatch : ResourcePatch() {
is_19_29_or_greater = 243005000 <= playStoreServicesVersion
is_19_32_or_greater = 243199000 <= playStoreServicesVersion
is_19_33_or_greater = 243405000 <= playStoreServicesVersion
is_19_34_or_greater = 243499000 <= playStoreServicesVersion
is_19_36_or_greater = 243705000 <= playStoreServicesVersion
is_19_41_or_greater = 244305000 <= playStoreServicesVersion
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -151,7 +151,6 @@ object SettingsPatch :
val SHORTS = Screen(
key = "revanced_settings_screen_06_shorts",
summaryKey = null,
sorting = Sorting.UNSORTED,
)
// Don't sort, because title sorting scatters the custom color preferences.
val SEEKBAR = Screen(
Expand Down
10 changes: 10 additions & 0 deletions src/main/resources/addresources/values/strings.xml
Original file line number Diff line number Diff line change
Expand Up @@ -602,6 +602,8 @@ This is because Crowdin requires temporarily flattening this file and removing t
<string name="revanced_hide_seekbar_thumbnail_summary_off">Thumbnail seekbar is shown</string>
</patch>
<patch id="layout.hide.shorts.HideShortsComponentsResourcePatch">
<string name="revanced_shorts_player_screen_title">Shorts player</string>
<string name="revanced_shorts_player_screen_summary">Hide or show components in the Shorts player</string>
<!-- 'home' should be translated using the same localized wording YouTube displays for the home tab. -->
<string name="revanced_hide_shorts_home_title">Hide Shorts in home feed</string>
<string name="revanced_hide_shorts_home_summary_on">Shorts in home feed are hidden</string>
Expand Down Expand Up @@ -997,6 +999,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.shortsautoplay.ShortsAutoplayPatch">
<string name="revanced_shorts_autoplay_title">Autoplay Shorts</string>
<string name="revanced_shorts_autoplay_summary_on">Shorts will autoplay</string>
<string name="revanced_shorts_autoplay_summary_off">Shorts will repeat</string>
<string name="revanced_shorts_autoplay_background_title">Autoplay Shorts background play</string>
<string name="revanced_shorts_autoplay_background_summary_on">Shorts background play will autoplay</string>
<string name="revanced_shorts_autoplay_background_summary_off">Shorts background play will repeat</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