From 9a1e6ca178d9833ee2c681fb130b9290a4e89cd8 Mon Sep 17 00:00:00 2001
From: MarcaD <152095496+MarcaDian@users.noreply.github.com>
Date: Sun, 1 Jun 2025 12:12:56 +0300
Subject: [PATCH] feat(YouTube - Playback Speed): Use modern custom speed
dialog (#5069)
Co-authored-by: LisoUseInAIKyrios <118716522+LisoUseInAIKyrios@users.noreply.github.com>
---
.../shared/spoof/requests/PlayerRoutes.java | 4 +-
.../extension/youtube/ThemeHelper.java | 8 +
.../patches/AlternativeThumbnailsPatch.java | 2 +-
.../patches/ReturnYouTubeDislikePatch.java | 11 +
.../youtube/patches/VideoInformation.java | 19 +
.../PlaybackSpeedMenuFilterPatch.java | 20 +-
.../speed/CustomPlaybackSpeedPatch.java | 533 ++++++++++++++++--
.../speed/RememberPlaybackSpeedPatch.java | 8 +-
.../patches/theme/SeekbarColorPatch.java | 22 +-
.../extension/youtube/settings/Settings.java | 2 +-
.../PlaybackSpeedDialogButton.java | 27 +-
.../youtube/video/information/Fingerprints.kt | 10 +
.../information/VideoInformationPatch.kt | 70 +++
.../speed/custom/CustomPlaybackSpeedPatch.kt | 107 +---
.../video/speed/custom/Fingerprints.kt | 21 -
.../kotlin/app/revanced/util/BytecodeUtils.kt | 39 ++
.../resources/addresources/values/strings.xml | 3 +-
17 files changed, 697 insertions(+), 209 deletions(-)
diff --git a/extensions/shared/library/src/main/java/app/revanced/extension/shared/spoof/requests/PlayerRoutes.java b/extensions/shared/library/src/main/java/app/revanced/extension/shared/spoof/requests/PlayerRoutes.java
index f45e890d5..5179b3e5f 100644
--- a/extensions/shared/library/src/main/java/app/revanced/extension/shared/spoof/requests/PlayerRoutes.java
+++ b/extensions/shared/library/src/main/java/app/revanced/extension/shared/spoof/requests/PlayerRoutes.java
@@ -71,9 +71,7 @@ final class PlayerRoutes {
return innerTubeBody.toString();
}
- /**
- * @noinspection SameParameterValue
- */
+ @SuppressWarnings("SameParameterValue")
static HttpURLConnection getPlayerResponseConnectionFromRoute(Route.CompiledRoute route, ClientType clientType) throws IOException {
var connection = Requester.getConnectionFromCompiledRoute(YT_API_URL, route);
diff --git a/extensions/youtube/src/main/java/app/revanced/extension/youtube/ThemeHelper.java b/extensions/youtube/src/main/java/app/revanced/extension/youtube/ThemeHelper.java
index 0177c9cbc..d6594f20d 100644
--- a/extensions/youtube/src/main/java/app/revanced/extension/youtube/ThemeHelper.java
+++ b/extensions/youtube/src/main/java/app/revanced/extension/youtube/ThemeHelper.java
@@ -105,6 +105,14 @@ public class ThemeHelper {
return isDarkTheme() ? getLightThemeColor() : getDarkThemeColor();
}
+ public static int getDialogBackgroundColor() {
+ final String colorName = isDarkTheme()
+ ? "yt_black1"
+ : "yt_white1";
+
+ return Utils.getColorFromString(colorName);
+ }
+
public static int getToolbarBackgroundColor() {
final String colorName = isDarkTheme()
? "yt_black3"
diff --git a/extensions/youtube/src/main/java/app/revanced/extension/youtube/patches/AlternativeThumbnailsPatch.java b/extensions/youtube/src/main/java/app/revanced/extension/youtube/patches/AlternativeThumbnailsPatch.java
index c670a79de..a4dd50fcc 100644
--- a/extensions/youtube/src/main/java/app/revanced/extension/youtube/patches/AlternativeThumbnailsPatch.java
+++ b/extensions/youtube/src/main/java/app/revanced/extension/youtube/patches/AlternativeThumbnailsPatch.java
@@ -686,7 +686,7 @@ public final class AlternativeThumbnailsPatch {
? "" : fullUrl.substring(imageExtensionEndIndex);
}
- /** @noinspection SameParameterValue */
+ @SuppressWarnings("SameParameterValue")
String createStillsUrl(@NonNull ThumbnailQuality qualityToUse, boolean includeViewTracking) {
// Images could be upgraded to webp if they are not already, but this fails quite often,
// especially for new videos uploaded in the last hour.
diff --git a/extensions/youtube/src/main/java/app/revanced/extension/youtube/patches/ReturnYouTubeDislikePatch.java b/extensions/youtube/src/main/java/app/revanced/extension/youtube/patches/ReturnYouTubeDislikePatch.java
index b43eb77ab..862847410 100644
--- a/extensions/youtube/src/main/java/app/revanced/extension/youtube/patches/ReturnYouTubeDislikePatch.java
+++ b/extensions/youtube/src/main/java/app/revanced/extension/youtube/patches/ReturnYouTubeDislikePatch.java
@@ -365,6 +365,11 @@ public class ReturnYouTubeDislikePatch {
if (videoId.equals(lastPrefetchedVideoId)) {
return;
}
+ if (!Utils.isNetworkConnected()) {
+ Logger.printDebug(() -> "Cannot pre-fetch RYD, network is not connected");
+ lastPrefetchedVideoId = null;
+ return;
+ }
final boolean videoIdIsShort = VideoInformation.lastPlayerResponseIsShort();
// Shorts shelf in home and subscription feed causes player response hook to be called,
@@ -419,6 +424,12 @@ public class ReturnYouTubeDislikePatch {
}
Logger.printDebug(() -> "New video id: " + videoId + " playerType: " + currentPlayerType);
+ if (!Utils.isNetworkConnected()) {
+ Logger.printDebug(() -> "Cannot fetch RYD, network is not connected");
+ currentVideoData = null;
+ return;
+ }
+
ReturnYouTubeDislike data = ReturnYouTubeDislike.getFetchForVideoId(videoId);
// Pre-emptively set the data to short status.
// Required to prevent Shorts data from being used on a minimized video in incognito mode.
diff --git a/extensions/youtube/src/main/java/app/revanced/extension/youtube/patches/VideoInformation.java b/extensions/youtube/src/main/java/app/revanced/extension/youtube/patches/VideoInformation.java
index 6df9a9095..99d8a5b6a 100644
--- a/extensions/youtube/src/main/java/app/revanced/extension/youtube/patches/VideoInformation.java
+++ b/extensions/youtube/src/main/java/app/revanced/extension/youtube/patches/VideoInformation.java
@@ -354,4 +354,23 @@ public final class VideoInformation {
return videoTime >= videoLength && videoLength > 0;
}
+ /**
+ * Overrides the current playback speed.
+ * Rest of the implementation added by patch.
+ */
+ public static void overridePlaybackSpeed(float speedOverride) {
+ Logger.printDebug(() -> "Overriding playback speed to: " + speedOverride);
+ }
+
+ /**
+ * Injection point.
+ *
+ * @param newlyLoadedPlaybackSpeed The current playback speed.
+ */
+ public static void setPlaybackSpeed(float newlyLoadedPlaybackSpeed) {
+ if (playbackSpeed != newlyLoadedPlaybackSpeed) {
+ Logger.printDebug(() -> "Video speed changed: " + newlyLoadedPlaybackSpeed);
+ playbackSpeed = newlyLoadedPlaybackSpeed;
+ }
+ }
}
diff --git a/extensions/youtube/src/main/java/app/revanced/extension/youtube/patches/components/PlaybackSpeedMenuFilterPatch.java b/extensions/youtube/src/main/java/app/revanced/extension/youtube/patches/components/PlaybackSpeedMenuFilterPatch.java
index d630ee9ed..531578a8b 100644
--- a/extensions/youtube/src/main/java/app/revanced/extension/youtube/patches/components/PlaybackSpeedMenuFilterPatch.java
+++ b/extensions/youtube/src/main/java/app/revanced/extension/youtube/patches/components/PlaybackSpeedMenuFilterPatch.java
@@ -10,18 +10,11 @@ import app.revanced.extension.youtube.settings.Settings;
*/
public final class PlaybackSpeedMenuFilterPatch extends Filter {
- /**
- * Old litho based speed selection menu.
- */
- public static volatile boolean isOldPlaybackSpeedMenuVisible;
-
/**
* 0.05x speed selection menu.
*/
public static volatile boolean isPlaybackRateSelectorMenuVisible;
- private final StringFilterGroup oldPlaybackMenuGroup;
-
public PlaybackSpeedMenuFilterPatch() {
// 0.05x litho speed menu.
var playbackRateSelectorGroup = new StringFilterGroup(
@@ -29,22 +22,13 @@ public final class PlaybackSpeedMenuFilterPatch extends Filter {
"playback_rate_selector_menu_sheet.eml-js"
);
- // Old litho based speed menu.
- oldPlaybackMenuGroup = new StringFilterGroup(
- Settings.CUSTOM_SPEED_MENU,
- "playback_speed_sheet_content.eml-js");
-
- addPathCallbacks(playbackRateSelectorGroup, oldPlaybackMenuGroup);
+ addPathCallbacks(playbackRateSelectorGroup);
}
@Override
boolean isFiltered(@Nullable String identifier, String path, byte[] protobufBufferArray,
StringFilterGroup matchedGroup, FilterContentType contentType, int contentIndex) {
- if (matchedGroup == oldPlaybackMenuGroup) {
- isOldPlaybackSpeedMenuVisible = true;
- } else {
- isPlaybackRateSelectorMenuVisible = true;
- }
+ isPlaybackRateSelectorMenuVisible = true;
return false;
}
diff --git a/extensions/youtube/src/main/java/app/revanced/extension/youtube/patches/playback/speed/CustomPlaybackSpeedPatch.java b/extensions/youtube/src/main/java/app/revanced/extension/youtube/patches/playback/speed/CustomPlaybackSpeedPatch.java
index 9b6224106..4c509bccb 100644
--- a/extensions/youtube/src/main/java/app/revanced/extension/youtube/patches/playback/speed/CustomPlaybackSpeedPatch.java
+++ b/extensions/youtube/src/main/java/app/revanced/extension/youtube/patches/playback/speed/CustomPlaybackSpeedPatch.java
@@ -1,24 +1,57 @@
package app.revanced.extension.youtube.patches.playback.speed;
import static app.revanced.extension.shared.StringRef.str;
+import static app.revanced.extension.shared.Utils.dipToPixels;
+import android.annotation.SuppressLint;
+import android.app.Dialog;
+import android.content.Context;
+import android.content.res.Configuration;
+import android.graphics.Canvas;
+import android.graphics.ColorFilter;
+import android.graphics.Paint;
+import android.graphics.PixelFormat;
+import android.graphics.PorterDuff;
+import android.graphics.Rect;
+import android.graphics.Typeface;
+import android.graphics.drawable.Drawable;
+import android.graphics.drawable.ShapeDrawable;
+import android.graphics.drawable.shapes.RoundRectShape;
+import android.icu.text.NumberFormat;
import android.support.v7.widget.RecyclerView;
+import android.view.animation.Animation;
+import android.view.Gravity;
import android.view.View;
import android.view.ViewGroup;
import android.view.ViewParent;
+import android.view.Window;
+import android.view.WindowManager;
+import android.widget.Button;
+import android.widget.FrameLayout;
+import android.widget.GridLayout;
+import android.widget.LinearLayout;
+import android.widget.SeekBar;
+import android.widget.TextView;
+import java.lang.ref.WeakReference;
import java.util.Arrays;
+import java.util.function.Function;
import app.revanced.extension.shared.Logger;
import app.revanced.extension.shared.Utils;
+import app.revanced.extension.youtube.ThemeHelper;
+import app.revanced.extension.youtube.patches.VideoInformation;
import app.revanced.extension.youtube.patches.components.PlaybackSpeedMenuFilterPatch;
import app.revanced.extension.youtube.settings.Settings;
+import app.revanced.extension.youtube.shared.PlayerType;
+import kotlin.Unit;
+import kotlin.jvm.functions.Function1;
@SuppressWarnings("unused")
public class CustomPlaybackSpeedPatch {
/**
- * Maximum playback speed, exclusive value. Custom speeds must be less than this value.
+ * Maximum playback speed, inclusive. Custom speeds must be this or less.
*
* Going over 8x does not increase the actual playback speed any higher,
* and the UI selector starts flickering and acting weird.
@@ -26,6 +59,11 @@ public class CustomPlaybackSpeedPatch {
*/
public static final float PLAYBACK_SPEED_MAXIMUM = 8;
+ /**
+ * Scale used to convert user speed to {@link android.widget.ProgressBar#setProgress(int)}.
+ */
+ private static final float PROGRESS_BAR_VALUE_SCALE = 100;
+
/**
* Tap and hold speed.
*/
@@ -34,16 +72,28 @@ public class CustomPlaybackSpeedPatch {
/**
* Custom playback speeds.
*/
- public static float[] customPlaybackSpeeds;
+ public static final float[] customPlaybackSpeeds;
/**
- * The last time the old playback menu was forcefully called.
+ * Formats speeds to UI strings.
*/
- private static long lastTimeOldPlaybackMenuInvoked;
+ private static final NumberFormat speedFormatter = NumberFormat.getNumberInstance();
+
+ /**
+ * Weak reference to the currently open dialog.
+ */
+ private static WeakReference