Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -7,23 +7,32 @@

package app.morphe.extension.youtube.patches;

import static app.morphe.extension.shared.StringRef.str;
import static app.morphe.extension.shared.ResourceUtils.getString;
import static app.morphe.extension.youtube.settings.Settings.OPEN_CHANNEL_OF_LIVE_AVATAR;

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

import com.facebook.litho.ComponentHost;

import java.lang.ref.WeakReference;
import java.util.Arrays;
import java.util.Map;
import java.util.Objects;
import java.util.function.UnaryOperator;
import java.util.regex.Pattern;
import java.util.stream.Collectors;

import app.morphe.extension.shared.Logger;
import app.morphe.extension.shared.Utils;
import app.morphe.extension.shared.spoof.SpoofVideoStreamsPatch;
import app.morphe.extension.shared.spoof.requests.PlayerRoutes;
import app.morphe.extension.shared.spoof.requests.StreamOrDetailsDataRequest;
import app.morphe.extension.youtube.shared.CreatorChannelState;
import app.morphe.extension.youtube.shared.PlayerType;
import app.morphe.extension.youtube.shared.ShortsPlayerState;

@SuppressWarnings("unused")
public final class OpenChannelOfLiveAvatarPatch {
Expand All @@ -36,12 +45,6 @@ public static void setMainActivity(Activity activity) {
mainActivityRef = new WeakReference<>(activity);
}

/**
* If you change the language in the app settings, a string from another language may be used.
* In this case, restarting the app will solve it.
*/
private static final String liveRingDescription = str("morphe_live_ring_description");

/**
* This key's value is the LithoView that opened the video (Live ring or Thumbnails).
*/
Expand All @@ -54,7 +57,18 @@ public static void setMainActivity(Activity activity) {
private static final String VIDEO_THUMBNAIL_VIEW_KEY =
"VideoPresenterConstants.VIDEO_THUMBNAIL_VIEW_KEY";

private static StreamOrDetailsDataRequest liveAvatarChannelRequest = null;
private static volatile StreamOrDetailsDataRequest liveAvatarChannelRequest;
private static volatile String lastLiveRingDescription;
private static volatile Pattern liveRingDescriptionPattern;
private static final UnaryOperator<String> stringNormalization =
s -> java.text.Normalizer.normalize(
s.toLowerCase(),
java.text.Normalizer.Form.NFD
).replaceAll(
"\\p{M}",
""
);

/**
* Injection point.
*
Expand All @@ -67,7 +81,8 @@ public static boolean openChannel(Map<Object, Object> playbackStartDescriptorMap
return false;
}
// Prevent a new request until the previous (if exists) is not done
if (liveAvatarChannelRequest != null && !liveAvatarChannelRequest.fetchIsDone()) {
StreamOrDetailsDataRequest request = liveAvatarChannelRequest;
if (request != null && !request.fetchIsDone()) {
return false;
}
// Video was opened by clicking the thumbnail
Expand All @@ -79,43 +94,70 @@ public static boolean openChannel(Map<Object, Object> playbackStartDescriptorMap
if (!(playbackStartDescriptorMap.get(ELEMENTS_SENDER_VIEW) instanceof ComponentHost componentHost)) {
return false;
}
// Check content description (accessibility labels) of the live ring.
final CharSequence contentDescription = componentHost.getContentDescription();
if (contentDescription == null) {
return false;
}

final boolean containsMatch = contentDescription.toString().contains(liveRingDescription);
Logger.printDebug(() -> "Litho description: " + contentDescription
+ "\ncontains Resource description: " + liveRingDescription
+ "\n" + containsMatch);
if (containsMatch) {
liveAvatarChannelRequest = SpoofVideoStreamsPatch.fetchDetails(
PlayerRoutes.GET_CHANNEL_FROM_ID,
videoId
);
Utils.runOnBackgroundThread(() -> {
if (liveAvatarChannelRequest.getStreamDetails() instanceof String channelID && !channelID.isEmpty()) {
Logger.printDebug(() -> "live avatar response: " + channelID);

Utils.runOnMainThread(() -> {
var context = mainActivityRef.get();
if (context != null) {
Intent videoChannelIntent = new Intent(Intent.ACTION_VIEW);
videoChannelIntent.setData(Uri.parse("https://www.youtube.com/channel/" + channelID));
videoChannelIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
videoChannelIntent.setPackage(context.getPackageName());
context.startActivity(videoChannelIntent);
PlayerType currentPlayer = PlayerType.getCurrent();
if (CreatorChannelState.isOpen() == (currentPlayer == PlayerType.WATCH_WHILE_MAXIMIZED)) {
final boolean containsMatch;

if (!ShortsPlayerState.isOpen()) {
// Check content description (accessibility labels) of the live ring.
final CharSequence contentDescriptionCharSequence = componentHost.getContentDescription();
if (contentDescriptionCharSequence == null) {
return false;
}
final String contentDescriptionString = contentDescriptionCharSequence.toString();

// If you change the language in the app settings, a string from another language may be used.
final String liveRingDescription = getString("morphe_live_ring_description");

if (!Objects.equals(lastLiveRingDescription, liveRingDescription)) {
liveRingDescriptionPattern = Pattern.compile(
Arrays.stream(stringNormalization.apply(liveRingDescription).split("\\s+"))
.map(Pattern::quote)
.collect(Collectors.joining(".*?"))
);
lastLiveRingDescription = liveRingDescription;
}

containsMatch = liveRingDescriptionPattern.matcher(
stringNormalization.apply(contentDescriptionString)
).find();

Logger.printDebug(() -> "Litho description: " + contentDescriptionString
+ "\ncontains Resource description: " + liveRingDescription
+ "\nmatch: " + containsMatch);
} else {
containsMatch = true;
}

if (containsMatch) {
liveAvatarChannelRequest = SpoofVideoStreamsPatch.fetchDetails(
PlayerRoutes.GET_CHANNEL_FROM_ID,
videoId
);
Utils.runOnBackgroundThread(() -> {
if (liveAvatarChannelRequest.getStreamDetails() instanceof String channelID && !channelID.isEmpty()) {
Logger.printDebug(() -> "live avatar response: " + channelID);

Utils.runOnMainThread(() -> {
var context = mainActivityRef.get();
if (context != null) {
Intent videoChannelIntent = new Intent(Intent.ACTION_VIEW);
videoChannelIntent.setData(Uri.parse("https://www.youtube.com/channel/" + channelID));
videoChannelIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
videoChannelIntent.setPackage(context.getPackageName());
context.startActivity(videoChannelIntent);
}
});
} else {
Logger.printDebug(() -> "Could not get channel ID, string parameter is null: " + videoId);
}
});
} else {
Logger.printDebug(() -> "Could not get channel ID, string parameter is null: " + videoId);
return true;
}
});
return true;
}
} catch (Exception ex) {
Logger.printException(() -> "fetchVideoInformation failure", ex);
Logger.printException(() -> "openChannel failure", ex);
}
return false;
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
package app.morphe.extension.youtube.patches;

import android.util.Log;
import android.view.View;

import androidx.annotation.Nullable;

import app.morphe.extension.youtube.shared.CreatorChannelState;
import app.morphe.extension.youtube.shared.PlayerType;
import app.morphe.extension.youtube.shared.ShortsPlayerState;
import app.morphe.extension.youtube.shared.VideoState;
Expand All @@ -28,6 +30,28 @@ public static void setVideoState(@Nullable Enum<?> youTubeVideoState) {
VideoState.setFromString(youTubeVideoState.name());
}

/**
* Injection point.
*
* Add a listener to the Tabs bar of creator channel View.
* Triggered when a Tabs bar is attached or detached to Windows.
*
* @param view shorts player overlay (R.layout.tabs_bar_text_tab_modern_type).
*/
public static void onCreatorChannelCreate(View view) {
view.addOnAttachStateChangeListener(new View.OnAttachStateChangeListener() {
@Override
public void onViewAttachedToWindow(@Nullable View v) {
CreatorChannelState.setOpen(true);
}

@Override
public void onViewDetachedFromWindow(@Nullable View v) {
CreatorChannelState.setOpen(false);
}
});
}

/**
* Injection point.
*
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -336,7 +336,8 @@ public LayoutComponentsFilter() {
"page_header.e"
);
channelProfileGroupList = new StringFilterGroupList();
channelProfileGroupList.addAll(new StringFilterGroup(
channelProfileGroupList.addAll(
new StringFilterGroup(
Settings.HIDE_COMMUNITY_BUTTON,
"community_button"
),
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
package app.morphe.extension.youtube.shared

import app.morphe.extension.shared.Logger

/**
* Creator channel state.
*/
class CreatorChannelState {
companion object {

@JvmStatic
fun setOpen(open: Boolean) {
if (isOpen != open) {
isOpen = open
Logger.printDebug { "CreatorChannelState open changed to: $isOpen" }
onChange(open)
}
}

@Volatile
private var isOpen = false

/**
* Shorts player state change listener.
*/
@JvmStatic
val onChange = Event<Boolean>()

/**
* If the Shorts player is currently open.
*/
@JvmStatic
fun isOpen(): Boolean {
return isOpen
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,8 @@ class ShortsPlayerState {
@JvmStatic
fun setOpen(open: Boolean) {
if (isOpen != open) {
Logger.printDebug { "ShortsPlayerState open changed to: $isOpen" }
isOpen = open
Logger.printDebug { "ShortsPlayerState open changed to: $isOpen" }
onChange(open)
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,15 @@ internal object PlayerTypeEnumFingerprint : Fingerprint(
)
)

internal object TabsBarTextTabFingerprint : Fingerprint(
accessFlags = listOf(AccessFlags.PUBLIC, AccessFlags.FINAL),
returnType = "Landroid/view/View;",
filters = listOf(
resourceLiteral(ResourceType.LAYOUT, "tabs_bar_text_tab_modern_type"),
opcode(Opcode.MOVE_RESULT_OBJECT, location = MatchAfterWithin(8))
)
)

internal object ReelWatchPagerFingerprint : Fingerprint(
accessFlags = listOf(AccessFlags.PUBLIC, AccessFlags.FINAL),
returnType = "Landroid/view/View;",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,18 @@ val playerTypeHookPatch = bytecodePatch(
"invoke-static { p1 }, $EXTENSION_CLASS->setPlayerType(Ljava/lang/Enum;)V",
)

TabsBarTextTabFingerprint.let {
it.method.apply {
val index = it.instructionMatches.last().index
val register = getInstruction<OneRegisterInstruction>(index).registerA

addInstruction(
index + 1,
"invoke-static { v$register }, $EXTENSION_CLASS->onCreatorChannelCreate(Landroid/view/View;)V"
)
}
}

ReelWatchPagerFingerprint.let {
it.method.apply {
val index = it.instructionMatches.last().index
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="morphe_live_ring_description">Tap to watch live</string>
<string name="morphe_live_ring_description">Tik om regstreeks te kyk</string>
</resources>
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="morphe_live_ring_description">লাইভ চাবলৈ টিপক</string>
<string name="morphe_live_ring_description">চেনেল লাইভ চাবলৈ টিপক</string>
</resources>
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="morphe_live_ring_description">Canlı baxmaq üçün toxunun</string>
<string name="morphe_live_ring_description">Canlı izləmək üçün toxunun</string>
</resources>
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="morphe_live_ring_description">Dodirnite da gledate uživo</string>
<string name="morphe_live_ring_description">Gledanje uživo dodirom</string>
</resources>
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="morphe_live_ring_description">"Toca per veure l'emissió en directe"</string>
<string name="morphe_live_ring_description">Toca per veure el canal</string>
</resources>
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="morphe_live_ring_description">Zum live Ansehen tippen</string>
<string name="morphe_live_ring_description">Tippen, um den Livestream</string>
</resources>
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="morphe_live_ring_description">Presiona para ver la transmisión en vivo</string>
<string name="morphe_live_ring_description">Presiona para mirar en vivo</string>
</resources>
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="morphe_live_ring_description">Toca para ver en directo</string>
<string name="morphe_live_ring_description">Presiona para mirar en vivo</string>
</resources>
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="morphe_live_ring_description">Puudutage, et otse vaadata</string>
<string name="morphe_live_ring_description">Puudutage, et vaadata reaalajas</string>
</resources>
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="morphe_live_ring_description">Sakatu hau bideoa zuzenean ikusteko</string>
<string name="morphe_live_ring_description">Sakatu hau zuzenean ikusteko</string>
</resources>
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="morphe_live_ring_description">Napauta katsoaksesi livenä</string>
<string name="morphe_live_ring_description">Katso livenä napauttamalla</string>
</resources>
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="morphe_live_ring_description">Touchez pour regarder en direct</string>
<string name="morphe_live_ring_description">Toucher pour regarder en direct</string>
</resources>
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="morphe_live_ring_description">Appuyer pour regarder en direct</string>
<string name="morphe_live_ring_description">Appuyez pour regarder en direct</string>
</resources>
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="morphe_live_ring_description">Toca para velo en directo</string>
<string name="morphe_live_ring_description">Tocar para ver a emisión en directo</string>
</resources>
Loading
Loading