mirror of
https://github.com/inotia00/revanced-patches.git
synced 2025-04-29 22:24:31 +02:00
fix(YouTube - Change live ring click action): Channel does not open when the live ring of Shorts live stream is clicked
This commit is contained in:
parent
21b8b48e47
commit
1151a9a5be
@ -1,9 +1,10 @@
|
||||
package app.revanced.extension.youtube.patches.general;
|
||||
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.annotation.NonNull;
|
||||
|
||||
import com.facebook.litho.ComponentHost;
|
||||
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
|
||||
import app.revanced.extension.shared.utils.Logger;
|
||||
import app.revanced.extension.youtube.patches.general.requests.VideoDetailsRequest;
|
||||
@ -15,37 +16,70 @@ public final class OpenChannelOfLiveAvatarPatch {
|
||||
private static final boolean CHANGE_LIVE_RING_CLICK_ACTION =
|
||||
Settings.CHANGE_LIVE_RING_CLICK_ACTION.get();
|
||||
|
||||
private static final AtomicBoolean engagementPanelOpen = new AtomicBoolean(false);
|
||||
private static volatile String videoId = "";
|
||||
|
||||
/**
|
||||
* This key's value is the LithoView that opened the video (Live ring or Thumbnails).
|
||||
*/
|
||||
private static final String ELEMENTS_SENDER_VIEW =
|
||||
"com.google.android.libraries.youtube.rendering.elements.sender_view";
|
||||
|
||||
/**
|
||||
* If the video is open by clicking live ring, this key does not exists.
|
||||
*/
|
||||
private static final String VIDEO_THUMBNAIL_VIEW_KEY =
|
||||
"VideoPresenterConstants.VIDEO_THUMBNAIL_VIEW_KEY";
|
||||
|
||||
public static void showEngagementPanel(@Nullable Object object) {
|
||||
engagementPanelOpen.set(object != null);
|
||||
/**
|
||||
* Injection point.
|
||||
*
|
||||
* @param playbackStartDescriptorMap map containing information about PlaybackStartDescriptor
|
||||
* @param newlyLoadedVideoId id of the current video
|
||||
*/
|
||||
public static void fetchChannelId(@NonNull Map<Object, Object> playbackStartDescriptorMap, String newlyLoadedVideoId) {
|
||||
try {
|
||||
if (!CHANGE_LIVE_RING_CLICK_ACTION) {
|
||||
return;
|
||||
}
|
||||
// Video id is empty
|
||||
if (newlyLoadedVideoId.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
// Video was opened by clicking the thumbnail
|
||||
if (playbackStartDescriptorMap.containsKey(VIDEO_THUMBNAIL_VIEW_KEY)) {
|
||||
return;
|
||||
}
|
||||
// If the video was opened in the watch history, there is no VIDEO_THUMBNAIL_VIEW_KEY
|
||||
// In this case, check the view that opened the video (Live ring is litho)
|
||||
if (!(playbackStartDescriptorMap.get(ELEMENTS_SENDER_VIEW) instanceof ComponentHost componentHost)) {
|
||||
return;
|
||||
}
|
||||
// Child count of other litho Views such as Thumbnail and Watch history: 2
|
||||
// Child count of live ring: 1
|
||||
if (componentHost.getChildCount() != 1) {
|
||||
return;
|
||||
}
|
||||
// Fetch channel id
|
||||
videoId = newlyLoadedVideoId;
|
||||
VideoDetailsRequest.fetchRequestIfNeeded(newlyLoadedVideoId);
|
||||
} catch (Exception ex) {
|
||||
Logger.printException(() -> "fetchVideoInformation failure", ex);
|
||||
}
|
||||
}
|
||||
|
||||
public static void hideEngagementPanel() {
|
||||
engagementPanelOpen.compareAndSet(true, false);
|
||||
}
|
||||
|
||||
public static boolean openChannelOfLiveAvatar() {
|
||||
public static boolean openChannel() {
|
||||
try {
|
||||
if (!CHANGE_LIVE_RING_CLICK_ACTION) {
|
||||
return false;
|
||||
}
|
||||
if (engagementPanelOpen.get()) {
|
||||
return false;
|
||||
}
|
||||
// If it is not fetch, the video id is empty
|
||||
if (videoId.isEmpty()) {
|
||||
return false;
|
||||
}
|
||||
VideoDetailsRequest request = VideoDetailsRequest.getRequestForVideoId(videoId);
|
||||
if (request != null) {
|
||||
String channelId = request.getInfo();
|
||||
// Open the channel
|
||||
if (channelId != null) {
|
||||
videoId = "";
|
||||
VideoUtils.openChannel(channelId);
|
||||
@ -53,33 +87,9 @@ public final class OpenChannelOfLiveAvatarPatch {
|
||||
}
|
||||
}
|
||||
} catch (Exception ex) {
|
||||
Logger.printException(() -> "openChannelOfLiveAvatar failure", ex);
|
||||
Logger.printException(() -> "openChannel failure", ex);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public static void openChannelOfLiveAvatar(Map<Object, Object> playbackStartDescriptorMap, String newlyLoadedVideoId) {
|
||||
try {
|
||||
if (!CHANGE_LIVE_RING_CLICK_ACTION) {
|
||||
return;
|
||||
}
|
||||
if (playbackStartDescriptorMap == null) {
|
||||
return;
|
||||
}
|
||||
if (playbackStartDescriptorMap.containsKey(VIDEO_THUMBNAIL_VIEW_KEY)) {
|
||||
return;
|
||||
}
|
||||
if (engagementPanelOpen.get()) {
|
||||
return;
|
||||
}
|
||||
if (newlyLoadedVideoId.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
videoId = newlyLoadedVideoId;
|
||||
VideoDetailsRequest.fetchRequestIfNeeded(newlyLoadedVideoId);
|
||||
} catch (Exception ex) {
|
||||
Logger.printException(() -> "openChannelOfLiveAvatar failure", ex);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -0,0 +1,19 @@
|
||||
package com.facebook.litho;
|
||||
|
||||
import android.content.Context;
|
||||
import android.view.ViewGroup;
|
||||
|
||||
/**
|
||||
* "CompileOnly" class
|
||||
* <p>
|
||||
* This class will not be included and "replaced" by the real package's class.
|
||||
*/
|
||||
public class ComponentHost extends ViewGroup {
|
||||
public ComponentHost(Context context) {
|
||||
super(context);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onLayout(boolean changed, int l, int t, int r, int b) {
|
||||
}
|
||||
}
|
@ -4,15 +4,19 @@ 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.patcher.util.proxy.mutableTypes.MutableMethod
|
||||
import app.revanced.patches.youtube.utils.compatibility.Constants.COMPATIBLE_PACKAGE
|
||||
import app.revanced.patches.youtube.utils.engagement.engagementPanelHookPatch
|
||||
import app.revanced.patches.youtube.utils.engagement.hookEngagementPanelState
|
||||
import app.revanced.patches.youtube.utils.extension.Constants.GENERAL_PATH
|
||||
import app.revanced.patches.youtube.utils.patch.PatchList.CHANGE_LIVE_RING_CLICK_ACTION
|
||||
import app.revanced.patches.youtube.utils.playservice.is_19_25_or_greater
|
||||
import app.revanced.patches.youtube.utils.playservice.versionCheckPatch
|
||||
import app.revanced.patches.youtube.utils.settings.ResourceUtils.addPreference
|
||||
import app.revanced.patches.youtube.utils.settings.settingsPatch
|
||||
import app.revanced.patches.youtube.video.playbackstart.PLAYBACK_START_DESCRIPTOR_CLASS_DESCRIPTOR
|
||||
import app.revanced.patches.youtube.video.playbackstart.playbackStartDescriptorPatch
|
||||
import app.revanced.patches.youtube.video.playbackstart.playbackStartVideoIdReference
|
||||
import app.revanced.patches.youtube.video.playbackstart.shortsPlaybackStartIntentFingerprint
|
||||
import app.revanced.patches.youtube.video.playbackstart.shortsPlaybackStartIntentLegacyFingerprint
|
||||
import app.revanced.util.fingerprint.methodOrThrow
|
||||
import app.revanced.util.getReference
|
||||
import app.revanced.util.indexOfFirstInstructionOrThrow
|
||||
@ -20,7 +24,10 @@ import app.revanced.util.indexOfFirstInstructionReversedOrThrow
|
||||
import com.android.tools.smali.dexlib2.Opcode
|
||||
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.instruction.ReferenceInstruction
|
||||
import com.android.tools.smali.dexlib2.iface.instruction.TwoRegisterInstruction
|
||||
import com.android.tools.smali.dexlib2.iface.reference.MethodReference
|
||||
import com.android.tools.smali.dexlib2.iface.reference.TypeReference
|
||||
|
||||
private const val EXTENSION_CLASS_DESCRIPTOR =
|
||||
"$GENERAL_PATH/OpenChannelOfLiveAvatarPatch;"
|
||||
@ -35,13 +42,11 @@ val openChannelOfLiveAvatarPatch = bytecodePatch(
|
||||
dependsOn(
|
||||
settingsPatch,
|
||||
playbackStartDescriptorPatch,
|
||||
engagementPanelHookPatch,
|
||||
versionCheckPatch,
|
||||
)
|
||||
|
||||
execute {
|
||||
|
||||
hookEngagementPanelState(EXTENSION_CLASS_DESCRIPTOR)
|
||||
|
||||
clientSettingEndpointFingerprint.methodOrThrow().apply {
|
||||
val eqzIndex = indexOfFirstInstructionReversedOrThrow(Opcode.IF_EQZ)
|
||||
var freeIndex = indexOfFirstInstructionReversedOrThrow(eqzIndex, Opcode.NEW_INSTANCE)
|
||||
@ -49,7 +54,7 @@ val openChannelOfLiveAvatarPatch = bytecodePatch(
|
||||
|
||||
addInstructionsWithLabels(
|
||||
eqzIndex, """
|
||||
invoke-static { }, $EXTENSION_CLASS_DESCRIPTOR->openChannelOfLiveAvatar()Z
|
||||
invoke-static { }, $EXTENSION_CLASS_DESCRIPTOR->openChannel()Z
|
||||
move-result v$freeRegister
|
||||
if-eqz v$freeRegister, :ignore
|
||||
return-void
|
||||
@ -76,11 +81,82 @@ val openChannelOfLiveAvatarPatch = bytecodePatch(
|
||||
playbackStartIndex + 1, """
|
||||
invoke-virtual { v$playbackStartRegister }, $playbackStartVideoIdReference
|
||||
move-result-object v$freeRegister
|
||||
invoke-static { v$mapRegister, v$freeRegister }, $EXTENSION_CLASS_DESCRIPTOR->openChannelOfLiveAvatar(Ljava/util/Map;Ljava/lang/String;)V
|
||||
invoke-static { v$mapRegister, v$freeRegister }, $EXTENSION_CLASS_DESCRIPTOR->fetchChannelId(Ljava/util/Map;Ljava/lang/String;)V
|
||||
"""
|
||||
)
|
||||
}
|
||||
|
||||
fun MutableMethod.openChannel() =
|
||||
implementation!!.instructions
|
||||
.withIndex()
|
||||
.filter { (_, instruction) ->
|
||||
val reference = (instruction as? ReferenceInstruction)?.reference
|
||||
instruction.opcode == Opcode.NEW_INSTANCE &&
|
||||
reference is TypeReference &&
|
||||
reference.type == "Landroid/os/Bundle;"
|
||||
}
|
||||
.map { (index, _) -> index }
|
||||
.reversed()
|
||||
.forEach { index ->
|
||||
val register = getInstruction<OneRegisterInstruction>(index).registerA
|
||||
|
||||
addInstructionsWithLabels(
|
||||
index, """
|
||||
invoke-static { }, $EXTENSION_CLASS_DESCRIPTOR->openChannel()Z
|
||||
move-result v$register
|
||||
if-eqz v$register, :ignore
|
||||
return-void
|
||||
:ignore
|
||||
nop
|
||||
"""
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
fun fetchChannelIdInstructions(
|
||||
playbackStartRegister: Int,
|
||||
mapRegister: Int,
|
||||
videoIdRegister: Int,
|
||||
) =
|
||||
"""
|
||||
invoke-virtual { v$playbackStartRegister }, $playbackStartVideoIdReference
|
||||
move-result-object v$videoIdRegister
|
||||
invoke-static { v$mapRegister, v$videoIdRegister }, $EXTENSION_CLASS_DESCRIPTOR->fetchChannelId(Ljava/util/Map;Ljava/lang/String;)V
|
||||
"""
|
||||
|
||||
if (is_19_25_or_greater) {
|
||||
shortsPlaybackStartIntentFingerprint.methodOrThrow().apply {
|
||||
openChannel()
|
||||
|
||||
addInstructionsWithLabels(
|
||||
0, """
|
||||
move-object/from16 v0, p1
|
||||
move-object/from16 v1, p2
|
||||
${fetchChannelIdInstructions(0, 1, 2)}
|
||||
"""
|
||||
)
|
||||
}
|
||||
} else {
|
||||
shortsPlaybackStartIntentLegacyFingerprint.methodOrThrow().apply {
|
||||
openChannel()
|
||||
|
||||
val playbackStartIndex = indexOfFirstInstructionOrThrow {
|
||||
getReference<MethodReference>()?.returnType == PLAYBACK_START_DESCRIPTOR_CLASS_DESCRIPTOR
|
||||
}
|
||||
val mapIndex = indexOfFirstInstructionReversedOrThrow(playbackStartIndex, Opcode.IPUT)
|
||||
val mapRegister = getInstruction<TwoRegisterInstruction>(mapIndex).registerA
|
||||
val playbackStartRegister = getInstruction<OneRegisterInstruction>(playbackStartIndex + 1).registerA
|
||||
val videoIdRegister = getInstruction<FiveRegisterInstruction>(playbackStartIndex).registerC
|
||||
|
||||
addInstructionsWithLabels(
|
||||
playbackStartIndex + 2, """
|
||||
move-object/from16 v$mapRegister, p2
|
||||
${fetchChannelIdInstructions(playbackStartRegister, mapRegister, videoIdRegister)}
|
||||
"""
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
// region add settings
|
||||
|
||||
addPreference(
|
||||
|
@ -187,41 +187,3 @@ internal val shortsFullscreenFeatureFingerprint = legacyFingerprint(
|
||||
accessFlags = AccessFlags.PUBLIC or AccessFlags.FINAL,
|
||||
literals = listOf(FULLSCREEN_FEATURE_FLAG),
|
||||
)
|
||||
|
||||
// Pre 19.25
|
||||
internal val shortsPlaybackIntentLegacyFingerprint = legacyFingerprint(
|
||||
name = "shortsPlaybackIntentLegacyFingerprint",
|
||||
returnType = "V",
|
||||
parameters = listOf(
|
||||
"L",
|
||||
"Ljava/util/Map;",
|
||||
"J",
|
||||
"Ljava/lang/String;",
|
||||
"Z",
|
||||
"Ljava/util/Map;"
|
||||
),
|
||||
strings = listOf(
|
||||
// None of these strings are unique.
|
||||
"com.google.android.apps.youtube.app.endpoint.flags",
|
||||
"ReelWatchFragmentArgs",
|
||||
"reels_fragment_descriptor"
|
||||
)
|
||||
)
|
||||
|
||||
internal val shortsPlaybackIntentFingerprint = legacyFingerprint(
|
||||
name = "shortsPlaybackIntentFingerprint",
|
||||
accessFlags = AccessFlags.PROTECTED or AccessFlags.FINAL,
|
||||
returnType = "V",
|
||||
parameters = listOf(
|
||||
"Lcom/google/android/libraries/youtube/player/model/PlaybackStartDescriptor;",
|
||||
"Ljava/util/Map;",
|
||||
"J",
|
||||
"Ljava/lang/String;"
|
||||
),
|
||||
strings = listOf(
|
||||
// None of these strings are unique.
|
||||
"com.google.android.apps.youtube.app.endpoint.flags",
|
||||
"ReelWatchFragmentArgs",
|
||||
"reels_fragment_descriptor"
|
||||
)
|
||||
)
|
||||
|
@ -65,6 +65,8 @@ import app.revanced.patches.youtube.video.information.videoInformationPatch
|
||||
import app.revanced.patches.youtube.video.playbackstart.PLAYBACK_START_DESCRIPTOR_CLASS_DESCRIPTOR
|
||||
import app.revanced.patches.youtube.video.playbackstart.playbackStartDescriptorPatch
|
||||
import app.revanced.patches.youtube.video.playbackstart.playbackStartVideoIdReference
|
||||
import app.revanced.patches.youtube.video.playbackstart.shortsPlaybackStartIntentFingerprint
|
||||
import app.revanced.patches.youtube.video.playbackstart.shortsPlaybackStartIntentLegacyFingerprint
|
||||
import app.revanced.patches.youtube.video.videoid.hookPlayerResponseVideoId
|
||||
import app.revanced.patches.youtube.video.videoid.videoIdPatch
|
||||
import app.revanced.util.REGISTER_TEMPLATE_REPLACEMENT
|
||||
@ -894,7 +896,7 @@ val shortsComponentPatch = bytecodePatch(
|
||||
"""
|
||||
|
||||
if (is_19_25_or_greater) {
|
||||
shortsPlaybackIntentFingerprint.methodOrThrow().addInstructionsWithLabels(
|
||||
shortsPlaybackStartIntentFingerprint.methodOrThrow().addInstructionsWithLabels(
|
||||
0,
|
||||
"""
|
||||
move-object/from16 v0, p1
|
||||
@ -902,7 +904,7 @@ val shortsComponentPatch = bytecodePatch(
|
||||
"""
|
||||
)
|
||||
} else {
|
||||
shortsPlaybackIntentLegacyFingerprint.methodOrThrow().apply {
|
||||
shortsPlaybackStartIntentLegacyFingerprint.methodOrThrow().apply {
|
||||
val index = indexOfFirstInstructionOrThrow {
|
||||
getReference<MethodReference>()?.returnType == PLAYBACK_START_DESCRIPTOR_CLASS_DESCRIPTOR
|
||||
}
|
||||
|
@ -1,6 +1,8 @@
|
||||
package app.revanced.patches.youtube.video.playbackstart
|
||||
|
||||
import app.revanced.util.fingerprint.legacyFingerprint
|
||||
import app.revanced.util.or
|
||||
import com.android.tools.smali.dexlib2.AccessFlags
|
||||
|
||||
const val PLAYBACK_START_DESCRIPTOR_CLASS_DESCRIPTOR =
|
||||
"Lcom/google/android/libraries/youtube/player/model/PlaybackStartDescriptor;"
|
||||
@ -16,4 +18,42 @@ internal val playbackStartFeatureFlagFingerprint = legacyFingerprint(
|
||||
literals = listOf(45380134L)
|
||||
)
|
||||
|
||||
internal val shortsPlaybackStartIntentFingerprint = legacyFingerprint(
|
||||
name = "shortsPlaybackStartIntentFingerprint",
|
||||
accessFlags = AccessFlags.PROTECTED or AccessFlags.FINAL,
|
||||
returnType = "V",
|
||||
parameters = listOf(
|
||||
"Lcom/google/android/libraries/youtube/player/model/PlaybackStartDescriptor;",
|
||||
"Ljava/util/Map;",
|
||||
"J",
|
||||
"Ljava/lang/String;"
|
||||
),
|
||||
strings = listOf(
|
||||
// None of these strings are unique.
|
||||
"com.google.android.apps.youtube.app.endpoint.flags",
|
||||
"ReelWatchFragmentArgs",
|
||||
"reels_fragment_descriptor"
|
||||
)
|
||||
)
|
||||
|
||||
// Pre 19.25
|
||||
internal val shortsPlaybackStartIntentLegacyFingerprint = legacyFingerprint(
|
||||
name = "shortsPlaybackStartIntentLegacyFingerprint",
|
||||
returnType = "V",
|
||||
parameters = listOf(
|
||||
"L",
|
||||
"Ljava/util/Map;",
|
||||
"J",
|
||||
"Ljava/lang/String;",
|
||||
"Z",
|
||||
"Ljava/util/Map;"
|
||||
),
|
||||
strings = listOf(
|
||||
// None of these strings are unique.
|
||||
"com.google.android.apps.youtube.app.endpoint.flags",
|
||||
"ReelWatchFragmentArgs",
|
||||
"reels_fragment_descriptor"
|
||||
)
|
||||
)
|
||||
|
||||
|
||||
|
@ -2,7 +2,7 @@ package app.revanced.patches.youtube.video.playbackstart
|
||||
|
||||
import app.revanced.patcher.extensions.InstructionExtensions.getInstruction
|
||||
import app.revanced.patcher.patch.bytecodePatch
|
||||
import app.revanced.patches.youtube.utils.resourceid.sharedResourceIdPatch
|
||||
import app.revanced.patches.youtube.utils.extension.sharedExtensionPatch
|
||||
import app.revanced.util.fingerprint.methodOrThrow
|
||||
import app.revanced.util.getReference
|
||||
import app.revanced.util.indexOfFirstInstructionOrThrow
|
||||
@ -15,7 +15,7 @@ internal lateinit var playbackStartVideoIdReference: Reference
|
||||
val playbackStartDescriptorPatch = bytecodePatch(
|
||||
description = "playbackStartDescriptorPatch"
|
||||
) {
|
||||
dependsOn(sharedResourceIdPatch)
|
||||
dependsOn(sharedExtensionPatch)
|
||||
|
||||
execute {
|
||||
// Find the obfuscated method name for PlaybackStartDescriptor.videoId()
|
||||
|
@ -451,7 +451,7 @@ This does not bypass the age restriction. It just accepts it automatically."</st
|
||||
<string name="revanced_change_live_ring_click_action_title">Change live ring click action</string>
|
||||
<string name="revanced_change_live_ring_click_action_summary_on">"Channel opens when the live ring is clicked.
|
||||
|
||||
Limitation: Live ring of the Vertical live streams (Shorts live streams) cannot open a channel."</string>
|
||||
Limitation: When the Shorts live stream is opened in regular player due to the 'Open Shorts in regular player' setting, channel does not open."</string>
|
||||
<string name="revanced_change_live_ring_click_action_summary_off">Live stream opens when the live ring is clicked.</string>
|
||||
<string name="revanced_spoof_app_version_title">Spoof app version</string>
|
||||
<string name="revanced_spoof_app_version_summary_on">Version spoofed</string>
|
||||
|
Loading…
x
Reference in New Issue
Block a user