mirror of
https://github.com/ReVanced/revanced-patches.git
synced 2024-11-12 18:04:26 +01:00
fix(YouTube - Playback speed): Restore old playback speed menu (#3817)
This commit is contained in:
parent
5988b75975
commit
806b21093e
6 changed files with 135 additions and 66 deletions
|
@ -3,25 +3,48 @@ package app.revanced.extension.youtube.patches.components;
|
|||
import androidx.annotation.Nullable;
|
||||
|
||||
import app.revanced.extension.youtube.patches.playback.speed.CustomPlaybackSpeedPatch;
|
||||
import app.revanced.extension.youtube.settings.Settings;
|
||||
|
||||
/**
|
||||
* Abuse LithoFilter for {@link CustomPlaybackSpeedPatch}.
|
||||
*/
|
||||
public final class PlaybackSpeedMenuFilterPatch extends Filter {
|
||||
// Must be volatile or synchronized, as litho filtering runs off main thread and this field is then access from the main thread.
|
||||
public static volatile boolean isPlaybackSpeedMenuVisible;
|
||||
|
||||
/**
|
||||
* 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() {
|
||||
addPathCallbacks(new StringFilterGroup(
|
||||
null,
|
||||
"playback_speed_sheet_content.eml-js"
|
||||
));
|
||||
// 0.05x litho speed menu.
|
||||
var playbackRateSelectorGroup = new StringFilterGroup(
|
||||
Settings.CUSTOM_SPEED_MENU,
|
||||
"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);
|
||||
}
|
||||
|
||||
@Override
|
||||
boolean isFiltered(@Nullable String identifier, String path, byte[] protobufBufferArray,
|
||||
StringFilterGroup matchedGroup, FilterContentType contentType, int contentIndex) {
|
||||
isPlaybackSpeedMenuVisible = true;
|
||||
if (matchedGroup == oldPlaybackMenuGroup) {
|
||||
isOldPlaybackSpeedMenuVisible = true;
|
||||
} else {
|
||||
isPlaybackRateSelectorMenuVisible = true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
|
|
@ -59,18 +59,24 @@ public class CustomPlaybackSpeedPatch {
|
|||
if (speedStrings.length == 0) {
|
||||
throw new IllegalArgumentException();
|
||||
}
|
||||
|
||||
customPlaybackSpeeds = new float[speedStrings.length];
|
||||
for (int i = 0, length = speedStrings.length; i < length; i++) {
|
||||
final float speed = Float.parseFloat(speedStrings[i]);
|
||||
if (speed <= 0 || arrayContains(customPlaybackSpeeds, speed)) {
|
||||
|
||||
int i = 0;
|
||||
for (String speedString : speedStrings) {
|
||||
final float speedFloat = Float.parseFloat(speedString);
|
||||
if (speedFloat <= 0 || arrayContains(customPlaybackSpeeds, speedFloat)) {
|
||||
throw new IllegalArgumentException();
|
||||
}
|
||||
if (speed >= MAXIMUM_PLAYBACK_SPEED) {
|
||||
|
||||
if (speedFloat >= MAXIMUM_PLAYBACK_SPEED) {
|
||||
resetCustomSpeeds(str("revanced_custom_playback_speeds_invalid", MAXIMUM_PLAYBACK_SPEED));
|
||||
loadCustomSpeeds();
|
||||
return;
|
||||
}
|
||||
customPlaybackSpeeds[i] = speed;
|
||||
|
||||
customPlaybackSpeeds[i] = speedFloat;
|
||||
i++;
|
||||
}
|
||||
} catch (Exception ex) {
|
||||
Logger.printInfo(() -> "parse error", ex);
|
||||
|
@ -89,10 +95,12 @@ public class CustomPlaybackSpeedPatch {
|
|||
/**
|
||||
* Initialize a settings preference list with the available playback speeds.
|
||||
*/
|
||||
@SuppressWarnings("deprecation")
|
||||
public static void initializeListPreference(ListPreference preference) {
|
||||
if (preferenceListEntries == null) {
|
||||
preferenceListEntries = new String[customPlaybackSpeeds.length];
|
||||
preferenceListEntryValues = new String[customPlaybackSpeeds.length];
|
||||
|
||||
int i = 0;
|
||||
for (float speed : customPlaybackSpeeds) {
|
||||
String speedString = String.valueOf(speed);
|
||||
|
@ -101,6 +109,7 @@ public class CustomPlaybackSpeedPatch {
|
|||
i++;
|
||||
}
|
||||
}
|
||||
|
||||
preference.setEntries(preferenceListEntries);
|
||||
preference.setEntryValues(preferenceListEntryValues);
|
||||
}
|
||||
|
@ -111,54 +120,69 @@ public class CustomPlaybackSpeedPatch {
|
|||
public static void onFlyoutMenuCreate(RecyclerView recyclerView) {
|
||||
recyclerView.getViewTreeObserver().addOnDrawListener(() -> {
|
||||
try {
|
||||
// For some reason, the custom playback speed flyout panel is activated when the user opens the share panel. (A/B tests)
|
||||
// Check the child count of playback speed flyout panel to prevent this issue.
|
||||
// Child count of playback speed flyout panel is always 8.
|
||||
if (!PlaybackSpeedMenuFilterPatch.isPlaybackSpeedMenuVisible || recyclerView.getChildCount() == 0) {
|
||||
if (PlaybackSpeedMenuFilterPatch.isPlaybackRateSelectorMenuVisible) {
|
||||
if (hideLithoMenuAndShowOldSpeedMenu(recyclerView, 5)) {
|
||||
PlaybackSpeedMenuFilterPatch.isPlaybackRateSelectorMenuVisible = false;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
View firstChild = recyclerView.getChildAt(0);
|
||||
if (!(firstChild instanceof ViewGroup)) {
|
||||
return;
|
||||
}
|
||||
ViewGroup PlaybackSpeedParentView = (ViewGroup) firstChild;
|
||||
if (PlaybackSpeedParentView.getChildCount() != 8) {
|
||||
return;
|
||||
}
|
||||
|
||||
PlaybackSpeedMenuFilterPatch.isPlaybackSpeedMenuVisible = false;
|
||||
|
||||
ViewParent parentView3rd = Utils.getParentView(recyclerView, 3);
|
||||
if (!(parentView3rd instanceof ViewGroup)) {
|
||||
return;
|
||||
}
|
||||
ViewParent parentView4th = parentView3rd.getParent();
|
||||
if (!(parentView4th instanceof ViewGroup)) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Dismiss View [R.id.touch_outside] is the 1st ChildView of the 4th ParentView.
|
||||
// This only shows in phone layout.
|
||||
final var touchInsidedView = ((ViewGroup) parentView4th).getChildAt(0);
|
||||
touchInsidedView.setSoundEffectsEnabled(false);
|
||||
touchInsidedView.performClick();
|
||||
|
||||
// In tablet layout there is no Dismiss View, instead we just hide all two parent views.
|
||||
((ViewGroup) parentView3rd).setVisibility(View.GONE);
|
||||
((ViewGroup) parentView4th).setVisibility(View.GONE);
|
||||
|
||||
// This works without issues for both tablet and phone layouts,
|
||||
// So no code is needed to check whether the current device is a tablet or phone.
|
||||
|
||||
// Close the new Playback speed menu and show the old one.
|
||||
showOldPlaybackSpeedMenu();
|
||||
} catch (Exception ex) {
|
||||
Logger.printException(() -> "onFlyoutMenuCreate failure", ex);
|
||||
Logger.printException(() -> "isPlaybackRateSelectorMenuVisible failure", ex);
|
||||
}
|
||||
|
||||
try {
|
||||
if (PlaybackSpeedMenuFilterPatch.isOldPlaybackSpeedMenuVisible) {
|
||||
if (hideLithoMenuAndShowOldSpeedMenu(recyclerView, 8)) {
|
||||
PlaybackSpeedMenuFilterPatch.isOldPlaybackSpeedMenuVisible = false;
|
||||
}
|
||||
}
|
||||
} catch (Exception ex) {
|
||||
Logger.printException(() -> "isOldPlaybackSpeedMenuVisible failure", ex);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private static boolean hideLithoMenuAndShowOldSpeedMenu(RecyclerView recyclerView, int expectedChildCount) {
|
||||
if (recyclerView.getChildCount() == 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
View firstChild = recyclerView.getChildAt(0);
|
||||
if (!(firstChild instanceof ViewGroup)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
ViewGroup PlaybackSpeedParentView = (ViewGroup) firstChild;
|
||||
if (PlaybackSpeedParentView.getChildCount() != expectedChildCount) {
|
||||
return false;
|
||||
}
|
||||
|
||||
ViewParent parentView3rd = Utils.getParentView(recyclerView, 3);
|
||||
if (!(parentView3rd instanceof ViewGroup)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
ViewParent parentView4th = parentView3rd.getParent();
|
||||
if (!(parentView4th instanceof ViewGroup)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// Dismiss View [R.id.touch_outside] is the 1st ChildView of the 4th ParentView.
|
||||
// This only shows in phone layout.
|
||||
final var touchInsidedView = ((ViewGroup) parentView4th).getChildAt(0);
|
||||
touchInsidedView.setSoundEffectsEnabled(false);
|
||||
touchInsidedView.performClick();
|
||||
|
||||
// In tablet layout there is no Dismiss View, instead we just hide all two parent views.
|
||||
((ViewGroup) parentView3rd).setVisibility(View.GONE);
|
||||
((ViewGroup) parentView4th).setVisibility(View.GONE);
|
||||
|
||||
// Close the litho speed menu and show the old one.
|
||||
showOldPlaybackSpeedMenu();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public static void showOldPlaybackSpeedMenu() {
|
||||
// This method is sometimes used multiple times.
|
||||
// To prevent this, ignore method reuse within 1 second.
|
||||
|
|
|
@ -30,17 +30,31 @@ public final class RememberPlaybackSpeedPatch {
|
|||
*/
|
||||
public static void userSelectedPlaybackSpeed(float playbackSpeed) {
|
||||
if (Settings.REMEMBER_PLAYBACK_SPEED_LAST_SELECTED.get()) {
|
||||
Settings.PLAYBACK_SPEED_DEFAULT.save(playbackSpeed);
|
||||
// With the 0.05x menu, if the speed is set by integrations to higher than 2.0x
|
||||
// then the menu will allow increasing without bounds but the max speed is
|
||||
// still capped to under 8.0x.
|
||||
playbackSpeed = Math.min(playbackSpeed, CustomPlaybackSpeedPatch.MAXIMUM_PLAYBACK_SPEED - 0.05f);
|
||||
|
||||
// Prevent toast spamming if using the 0.05x adjustments.
|
||||
// Show exactly one toast after the user stops interacting with the speed menu.
|
||||
final long now = System.currentTimeMillis();
|
||||
lastTimeSpeedChanged = now;
|
||||
|
||||
final float finalPlaybackSpeed = playbackSpeed;
|
||||
Utils.runOnMainThreadDelayed(() -> {
|
||||
if (lastTimeSpeedChanged == now) {
|
||||
Utils.showToastLong(str("revanced_remember_playback_speed_toast", (playbackSpeed + "x")));
|
||||
} // else, the user made additional speed adjustments and this call is outdated.
|
||||
if (lastTimeSpeedChanged != now) {
|
||||
// The user made additional speed adjustments and this call is outdated.
|
||||
return;
|
||||
}
|
||||
|
||||
if (Settings.PLAYBACK_SPEED_DEFAULT.get() == finalPlaybackSpeed) {
|
||||
// User changed to a different speed and immediately changed back.
|
||||
// Or the user is going past 8.0x in the glitched out 0.05x menu.
|
||||
return;
|
||||
}
|
||||
Settings.PLAYBACK_SPEED_DEFAULT.save(finalPlaybackSpeed);
|
||||
|
||||
Utils.showToastLong(str("revanced_remember_playback_speed_toast", (finalPlaybackSpeed + "x")));
|
||||
}, TOAST_DELAY_MILLISECONDS);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -28,7 +28,9 @@ public class Settings extends BaseSettings {
|
|||
public static final BooleanSetting REMEMBER_VIDEO_QUALITY_LAST_SELECTED = new BooleanSetting("revanced_remember_video_quality_last_selected", FALSE);
|
||||
public static final IntegerSetting VIDEO_QUALITY_DEFAULT_WIFI = new IntegerSetting("revanced_video_quality_default_wifi", -2);
|
||||
public static final IntegerSetting VIDEO_QUALITY_DEFAULT_MOBILE = new IntegerSetting("revanced_video_quality_default_mobile", -2);
|
||||
// Speed
|
||||
public static final BooleanSetting REMEMBER_PLAYBACK_SPEED_LAST_SELECTED = new BooleanSetting("revanced_remember_playback_speed_last_selected", FALSE);
|
||||
public static final BooleanSetting CUSTOM_SPEED_MENU = new BooleanSetting("revanced_custom_speed_menu", TRUE);
|
||||
public static final FloatSetting PLAYBACK_SPEED_DEFAULT = new FloatSetting("revanced_playback_speed_default", 1.0f);
|
||||
public static final StringSetting CUSTOM_PLAYBACK_SPEEDS = new StringSetting("revanced_custom_playback_speeds",
|
||||
"0.25\n0.5\n0.75\n0.9\n0.95\n1.0\n1.05\n1.1\n1.25\n1.5\n1.75\n2.0\n3.0\n4.0\n5.0", true);
|
||||
|
@ -378,3 +380,4 @@ public class Settings extends BaseSettings {
|
|||
// endregion
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -15,6 +15,7 @@ import app.revanced.patches.shared.misc.mapping.get
|
|||
import app.revanced.patches.shared.misc.mapping.resourceMappingPatch
|
||||
import app.revanced.patches.shared.misc.mapping.resourceMappings
|
||||
import app.revanced.patches.shared.misc.settings.preference.InputType
|
||||
import app.revanced.patches.shared.misc.settings.preference.SwitchPreference
|
||||
import app.revanced.patches.shared.misc.settings.preference.TextPreference
|
||||
import app.revanced.patches.youtube.misc.extension.sharedExtensionPatch
|
||||
import app.revanced.patches.youtube.misc.litho.filter.addLithoFilter
|
||||
|
@ -71,6 +72,7 @@ internal val customPlaybackSpeedPatch = bytecodePatch(
|
|||
addResources("youtube", "video.speed.custom.customPlaybackSpeedPatch")
|
||||
|
||||
PreferenceScreen.VIDEO.addPreferences(
|
||||
SwitchPreference("revanced_custom_speed_menu"),
|
||||
TextPreference("revanced_custom_playback_speeds", inputType = InputType.TEXT_MULTI_LINE),
|
||||
)
|
||||
|
||||
|
@ -108,18 +110,18 @@ internal val customPlaybackSpeedPatch = bytecodePatch(
|
|||
|
||||
// Override the min/max speeds that can be used.
|
||||
speedLimiterMatch.mutableMethod.apply {
|
||||
val limiterMinConstIndex = indexOfFirstLiteralInstructionOrThrow(0.25f.toRawBits().toLong())
|
||||
var limiterMaxConstIndex = indexOfFirstLiteralInstruction(2.0f.toRawBits().toLong())
|
||||
val limitMinIndex = indexOfFirstLiteralInstructionOrThrow(0.25f.toRawBits().toLong())
|
||||
var limitMaxIndex = indexOfFirstLiteralInstruction(2.0f.toRawBits().toLong())
|
||||
// Newer targets have 4x max speed.
|
||||
if (limiterMaxConstIndex < 0) {
|
||||
limiterMaxConstIndex = indexOfFirstLiteralInstructionOrThrow(4.0f.toRawBits().toLong())
|
||||
if (limitMaxIndex < 0) {
|
||||
limitMaxIndex = indexOfFirstLiteralInstructionOrThrow(4.0f.toRawBits().toLong())
|
||||
}
|
||||
|
||||
val limiterMinConstDestination = getInstruction<OneRegisterInstruction>(limiterMinConstIndex).registerA
|
||||
val limiterMaxConstDestination = getInstruction<OneRegisterInstruction>(limiterMaxConstIndex).registerA
|
||||
val limitMinRegister = getInstruction<OneRegisterInstruction>(limitMinIndex).registerA
|
||||
val limitMaxRegister = getInstruction<OneRegisterInstruction>(limitMaxIndex).registerA
|
||||
|
||||
replaceInstruction(limiterMinConstIndex, "const/high16 v$limiterMinConstDestination, 0.0f")
|
||||
replaceInstruction(limiterMaxConstIndex, "const/high16 v$limiterMaxConstDestination, 10.0f")
|
||||
replaceInstruction(limitMinIndex, "const/high16 v$limitMinRegister, 0.0f")
|
||||
replaceInstruction(limitMaxIndex, "const/high16 v$limitMaxRegister, 8.0f")
|
||||
}
|
||||
|
||||
// Add a static INSTANCE field to the class.
|
||||
|
|
|
@ -1172,8 +1172,11 @@ This is because Crowdin requires temporarily flattening this file and removing t
|
|||
<string name="revanced_playback_speed_dialog_button_summary_off">Button is not shown</string>
|
||||
</patch>
|
||||
<patch id="video.speed.custom.customPlaybackSpeedPatch">
|
||||
<string name="revanced_custom_speed_menu_title">Custom playback speed menu</string>
|
||||
<string name="revanced_custom_speed_menu_summary_on">Custom speed menu is shown</string>
|
||||
<string name="revanced_custom_speed_menu_summary_off">Custom speed menu is not shown</string>
|
||||
<string name="revanced_custom_playback_speeds_title">Custom playback speeds</string>
|
||||
<string name="revanced_custom_playback_speeds_summary">Add or change the available playback speeds</string>
|
||||
<string name="revanced_custom_playback_speeds_summary">Add or change the custom playback speeds</string>
|
||||
<string name="revanced_custom_playback_speeds_invalid">Custom speeds must be less than %s. Using default values.</string>
|
||||
<string name="revanced_custom_playback_speeds_parse_exception">Invalid custom playback speeds. Using default values.</string>
|
||||
</patch>
|
||||
|
|
Loading…
Reference in a new issue