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 Open Shorts in regular player patch #4153

Merged
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
package app.revanced.extension.youtube.patches;

import android.app.Activity;
import android.content.Intent;
import android.net.Uri;

import java.lang.ref.WeakReference;

import app.revanced.extension.shared.Logger;
import app.revanced.extension.youtube.settings.Settings;

@SuppressWarnings("unused")
public class OpenShortsInRegularPlayer {

private static WeakReference<Activity> mainActivityRef = new WeakReference<>(null);

/**
* Injection point.
*/
public static void setMainActivity(Activity activity) {
mainActivityRef = new WeakReference<>(activity);
}

/**
* Injection point.
*/
public static boolean openShort(String videoID) {
LisoUseInAIKyrios marked this conversation as resolved.
Show resolved Hide resolved
try {
if (!Settings.OPEN_SHORTS_IN_REGULAR_PLAYER.get()) {
return false;
}

// Can use the application context and add intent flags of
// FLAG_ACTIVITY_NEW_TASK and FLAG_ACTIVITY_CLEAR_TOP
// But the activity context seems to fix random app crashes
// if Shorts urls are opened outside the app.
var context = mainActivityRef.get();

Intent videoPlayerIntent = new Intent(
Intent.ACTION_VIEW,
Uri.parse("https://youtube.com/watch?v=" + videoID)
);

videoPlayerIntent.setPackage(context.getPackageName());

context.startActivity(videoPlayerIntent);
return true;
} catch (Exception ex) {
Logger.printException(() -> "openShort failure", ex);
return false;
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -224,6 +224,7 @@ public class Settings extends BaseSettings {
// Shorts
public static final BooleanSetting DISABLE_RESUMING_SHORTS_PLAYER = new BooleanSetting("revanced_disable_resuming_shorts_player", FALSE);
public static final BooleanSetting DISABLE_SHORTS_BACKGROUND_PLAYBACK = new BooleanSetting("revanced_shorts_disable_background_playback", FALSE);
public static final BooleanSetting OPEN_SHORTS_IN_REGULAR_PLAYER = new BooleanSetting("revanced_open_shorts_in_regular_player", FALSE);
public static final BooleanSetting HIDE_SHORTS_CHANNEL_BAR = new BooleanSetting("revanced_hide_shorts_channel_bar", FALSE);
public static final BooleanSetting HIDE_SHORTS_COMMENTS_BUTTON = new BooleanSetting("revanced_hide_shorts_comments_button", FALSE);
public static final BooleanSetting HIDE_SHORTS_DISLIKE_BUTTON = new BooleanSetting("revanced_hide_shorts_dislike_button", FALSE);
Expand Down
4 changes: 4 additions & 0 deletions patches/api/patches.api
Original file line number Diff line number Diff line change
Expand Up @@ -1210,6 +1210,10 @@ public final class app/revanced/patches/youtube/layout/shortsautoplay/ShortsAuto
public static final fun getShortsAutoplayPatch ()Lapp/revanced/patcher/patch/BytecodePatch;
}

public final class app/revanced/patches/youtube/layout/shortsbypass/OpenShortsInRegularPlayerKt {
public static final fun getOpenShortsInRegularPlayer ()Lapp/revanced/patcher/patch/BytecodePatch;
}

public final class app/revanced/patches/youtube/layout/sponsorblock/SponsorBlockPatchKt {
public static final fun getSponsorBlockPatch ()Lapp/revanced/patcher/patch/BytecodePatch;
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
package app.revanced.patches.youtube.layout.shortsbypass

import app.revanced.patcher.fingerprint
import app.revanced.util.literal
import com.android.tools.smali.dexlib2.AccessFlags

/**
* Purpose of this method is not clear, and it's only used to identify
* the obfuscated name of the videoId() method in PlaybackStartDescriptor.
LisoUseInAIKyrios marked this conversation as resolved.
Show resolved Hide resolved
*/
internal val playbackStartFeatureFlagFingerprint = fingerprint {
returns("Z")
parameters(
"Lcom/google/android/libraries/youtube/player/model/PlaybackStartDescriptor;",
)
literal {
45380134L
}
}

// Pre 19.45
internal val playbackStartDescriptorLegacyFingerprint = fingerprint {
LisoUseInAIKyrios marked this conversation as resolved.
Show resolved Hide resolved
accessFlags(AccessFlags.PUBLIC, AccessFlags.FINAL)
returns("V")
parameters(
"L",
"Ljava/util/Map;",
"J",
"Ljava/lang/String;",
"Z",
"Ljava/util/Map;"
)
strings(
// None of these strings are unique.
"com.google.android.apps.youtube.app.endpoint.flags",
"ReelWatchFragmentArgs",
"reels_fragment_descriptor"
)
}

internal val playbackStartDescriptorFingerprint = fingerprint {
LisoUseInAIKyrios marked this conversation as resolved.
Show resolved Hide resolved
returns("V")
parameters(
"Lcom/google/android/libraries/youtube/player/model/PlaybackStartDescriptor;",
"Ljava/util/Map;",
"J",
"Ljava/lang/String;"
)
strings(
// None of these strings are unique.
"com.google.android.apps.youtube.app.endpoint.flags",
"ReelWatchFragmentArgs",
"reels_fragment_descriptor"
)
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,112 @@
package app.revanced.patches.youtube.layout.shortsbypass

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.patches.all.misc.resources.addResources
import app.revanced.patches.all.misc.resources.addResourcesPatch
import app.revanced.patches.shared.misc.settings.preference.SwitchPreference
import app.revanced.patches.youtube.misc.extension.sharedExtensionPatch
import app.revanced.patches.youtube.misc.playservice.is_19_25_or_greater
import app.revanced.patches.youtube.misc.settings.PreferenceScreen
import app.revanced.patches.youtube.misc.settings.settingsPatch
import app.revanced.patches.youtube.shared.mainActivityOnCreateFingerprint
import app.revanced.util.getReference
import app.revanced.util.indexOfFirstInstructionOrThrow
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.reference.MethodReference

private const val EXTENSION_CLASS_DESCRIPTOR =
"Lapp/revanced/extension/youtube/patches/OpenShortsInRegularPlayer;"

@Suppress("unused")
val openShortsInRegularPlayer = bytecodePatch(
LisoUseInAIKyrios marked this conversation as resolved.
Show resolved Hide resolved
name = "Open Shorts in player",
description = "Adds an option to open Shorts in the regular video player.",
) {
dependsOn(
LisoUseInAIKyrios marked this conversation as resolved.
Show resolved Hide resolved
sharedExtensionPatch,
settingsPatch,
addResourcesPatch,
)

compatibleWith(
"com.google.android.youtube"(
"18.38.44",
"18.49.37",
"19.16.39",
"19.25.37",
"19.34.42",
"19.43.41",
"19.45.38",
"19.46.42",
),
)

execute {
addResources("youtube", "layout.shortsbypass.openShortsInRegularPlayer")

PreferenceScreen.SHORTS.addPreferences(
SwitchPreference("revanced_open_shorts_in_regular_player"),
)

// Main activity is used to open Shorts links.
mainActivityOnCreateFingerprint.method.addInstructions(
1,
"invoke-static/range { p0 .. p0 }, ${EXTENSION_CLASS_DESCRIPTOR}->" +
"setMainActivity(Landroid/app/Activity;)V",
)

// Find the obfuscated method name for PlaybackStartDescriptor.videoId()
val playbackStartVideoIdMethodName = playbackStartFeatureFlagFingerprint.method.let {
val stringMethodIndex = it.indexOfFirstInstructionOrThrow {
val reference = getReference<MethodReference>()
reference?.definingClass == "Lcom/google/android/libraries/youtube/player/model/PlaybackStartDescriptor;"
&& reference.returnType == "Ljava/lang/String;"
}

navigate(it).to(stringMethodIndex).stop().name
}

fun extensionInstructions(playbackStartRegister: Int, freeRegister: Int) =
"""
invoke-virtual { v$playbackStartRegister }, Lcom/google/android/libraries/youtube/player/model/PlaybackStartDescriptor;->$playbackStartVideoIdMethodName()Ljava/lang/String;
move-result-object v$freeRegister
invoke-static { v$freeRegister }, $EXTENSION_CLASS_DESCRIPTOR->openShort(Ljava/lang/String;)Z
move-result v$freeRegister
if-eqz v$freeRegister, :disabled
return-void

:disabled
nop
"""

if (!is_19_25_or_greater) {
playbackStartDescriptorLegacyFingerprint.method.apply {
val index = indexOfFirstInstructionOrThrow {
getReference<MethodReference>()?.returnType ==
"Lcom/google/android/libraries/youtube/player/model/PlaybackStartDescriptor;"
}
val freeRegister = getInstruction<FiveRegisterInstruction>(index).registerC
val playbackStartRegister = getInstruction<OneRegisterInstruction>(index + 1).registerA

addInstructionsWithLabels(
index + 2,
extensionInstructions(playbackStartRegister, freeRegister)
)
}

return@execute
}

playbackStartDescriptorFingerprint.method.addInstructionsWithLabels(
0,
"""
move-object/from16 v0, p1
${extensionInstructions(0, 0)}
"""
)
}
}
5 changes: 5 additions & 0 deletions patches/src/main/resources/addresources/values/strings.xml
Original file line number Diff line number Diff line change
Expand Up @@ -1058,6 +1058,11 @@ If later turned off, it is recommended to clear the app data to prevent UI bugs.
<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.shortsbypass.openShortsInRegularPlayer">
<string name="revanced_open_shorts_in_regular_player_title">Open Shorts in regular player</string>
<string name="revanced_open_shorts_in_regular_player_summary_on">Shorts open in the regular video player</string>
<string name="revanced_open_shorts_in_regular_player_summary_off">Shorts open in the Shorts player</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>
Expand Down
Loading