fix(YouTube - Playback speed): Restore old playback speed menu (#3817)

This commit is contained in:
LisoUseInAIKyrios 2024-10-26 16:53:01 -04:00 committed by oSumAtrIX
parent 5988b75975
commit 806b21093e
No known key found for this signature in database
GPG Key ID: A9B3094ACDB604B4
6 changed files with 135 additions and 66 deletions

View File

@ -3,25 +3,48 @@ package app.revanced.extension.youtube.patches.components;
import androidx.annotation.Nullable; import androidx.annotation.Nullable;
import app.revanced.extension.youtube.patches.playback.speed.CustomPlaybackSpeedPatch; import app.revanced.extension.youtube.patches.playback.speed.CustomPlaybackSpeedPatch;
import app.revanced.extension.youtube.settings.Settings;
/** /**
* Abuse LithoFilter for {@link CustomPlaybackSpeedPatch}. * Abuse LithoFilter for {@link CustomPlaybackSpeedPatch}.
*/ */
public final class PlaybackSpeedMenuFilterPatch extends Filter { 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() { public PlaybackSpeedMenuFilterPatch() {
addPathCallbacks(new StringFilterGroup( // 0.05x litho speed menu.
null, var playbackRateSelectorGroup = new StringFilterGroup(
"playback_speed_sheet_content.eml-js" 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 @Override
boolean isFiltered(@Nullable String identifier, String path, byte[] protobufBufferArray, boolean isFiltered(@Nullable String identifier, String path, byte[] protobufBufferArray,
StringFilterGroup matchedGroup, FilterContentType contentType, int contentIndex) { StringFilterGroup matchedGroup, FilterContentType contentType, int contentIndex) {
isPlaybackSpeedMenuVisible = true; if (matchedGroup == oldPlaybackMenuGroup) {
isOldPlaybackSpeedMenuVisible = true;
} else {
isPlaybackRateSelectorMenuVisible = true;
}
return false; return false;
} }

View File

@ -59,18 +59,24 @@ public class CustomPlaybackSpeedPatch {
if (speedStrings.length == 0) { if (speedStrings.length == 0) {
throw new IllegalArgumentException(); throw new IllegalArgumentException();
} }
customPlaybackSpeeds = new float[speedStrings.length]; customPlaybackSpeeds = new float[speedStrings.length];
for (int i = 0, length = speedStrings.length; i < length; i++) {
final float speed = Float.parseFloat(speedStrings[i]); int i = 0;
if (speed <= 0 || arrayContains(customPlaybackSpeeds, speed)) { for (String speedString : speedStrings) {
final float speedFloat = Float.parseFloat(speedString);
if (speedFloat <= 0 || arrayContains(customPlaybackSpeeds, speedFloat)) {
throw new IllegalArgumentException(); throw new IllegalArgumentException();
} }
if (speed >= MAXIMUM_PLAYBACK_SPEED) {
if (speedFloat >= MAXIMUM_PLAYBACK_SPEED) {
resetCustomSpeeds(str("revanced_custom_playback_speeds_invalid", MAXIMUM_PLAYBACK_SPEED)); resetCustomSpeeds(str("revanced_custom_playback_speeds_invalid", MAXIMUM_PLAYBACK_SPEED));
loadCustomSpeeds(); loadCustomSpeeds();
return; return;
} }
customPlaybackSpeeds[i] = speed;
customPlaybackSpeeds[i] = speedFloat;
i++;
} }
} catch (Exception ex) { } catch (Exception ex) {
Logger.printInfo(() -> "parse error", ex); Logger.printInfo(() -> "parse error", ex);
@ -89,10 +95,12 @@ public class CustomPlaybackSpeedPatch {
/** /**
* Initialize a settings preference list with the available playback speeds. * Initialize a settings preference list with the available playback speeds.
*/ */
@SuppressWarnings("deprecation")
public static void initializeListPreference(ListPreference preference) { public static void initializeListPreference(ListPreference preference) {
if (preferenceListEntries == null) { if (preferenceListEntries == null) {
preferenceListEntries = new String[customPlaybackSpeeds.length]; preferenceListEntries = new String[customPlaybackSpeeds.length];
preferenceListEntryValues = new String[customPlaybackSpeeds.length]; preferenceListEntryValues = new String[customPlaybackSpeeds.length];
int i = 0; int i = 0;
for (float speed : customPlaybackSpeeds) { for (float speed : customPlaybackSpeeds) {
String speedString = String.valueOf(speed); String speedString = String.valueOf(speed);
@ -101,6 +109,7 @@ public class CustomPlaybackSpeedPatch {
i++; i++;
} }
} }
preference.setEntries(preferenceListEntries); preference.setEntries(preferenceListEntries);
preference.setEntryValues(preferenceListEntryValues); preference.setEntryValues(preferenceListEntryValues);
} }
@ -111,54 +120,69 @@ public class CustomPlaybackSpeedPatch {
public static void onFlyoutMenuCreate(RecyclerView recyclerView) { public static void onFlyoutMenuCreate(RecyclerView recyclerView) {
recyclerView.getViewTreeObserver().addOnDrawListener(() -> { recyclerView.getViewTreeObserver().addOnDrawListener(() -> {
try { try {
// For some reason, the custom playback speed flyout panel is activated when the user opens the share panel. (A/B tests) if (PlaybackSpeedMenuFilterPatch.isPlaybackRateSelectorMenuVisible) {
// Check the child count of playback speed flyout panel to prevent this issue. if (hideLithoMenuAndShowOldSpeedMenu(recyclerView, 5)) {
// Child count of playback speed flyout panel is always 8. PlaybackSpeedMenuFilterPatch.isPlaybackRateSelectorMenuVisible = false;
if (!PlaybackSpeedMenuFilterPatch.isPlaybackSpeedMenuVisible || recyclerView.getChildCount() == 0) { }
return; 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) { } 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() { public static void showOldPlaybackSpeedMenu() {
// This method is sometimes used multiple times. // This method is sometimes used multiple times.
// To prevent this, ignore method reuse within 1 second. // To prevent this, ignore method reuse within 1 second.

View File

@ -30,17 +30,31 @@ public final class RememberPlaybackSpeedPatch {
*/ */
public static void userSelectedPlaybackSpeed(float playbackSpeed) { public static void userSelectedPlaybackSpeed(float playbackSpeed) {
if (Settings.REMEMBER_PLAYBACK_SPEED_LAST_SELECTED.get()) { 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. // Prevent toast spamming if using the 0.05x adjustments.
// Show exactly one toast after the user stops interacting with the speed menu. // Show exactly one toast after the user stops interacting with the speed menu.
final long now = System.currentTimeMillis(); final long now = System.currentTimeMillis();
lastTimeSpeedChanged = now; lastTimeSpeedChanged = now;
final float finalPlaybackSpeed = playbackSpeed;
Utils.runOnMainThreadDelayed(() -> { Utils.runOnMainThreadDelayed(() -> {
if (lastTimeSpeedChanged == now) { if (lastTimeSpeedChanged != now) {
Utils.showToastLong(str("revanced_remember_playback_speed_toast", (playbackSpeed + "x"))); // The user made additional speed adjustments and this call is outdated.
} // else, 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); }, TOAST_DELAY_MILLISECONDS);
} }
} }

View File

@ -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 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_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); 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 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 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", 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); "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 // endregion
} }
} }

View File

@ -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.resourceMappingPatch
import app.revanced.patches.shared.misc.mapping.resourceMappings 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.InputType
import app.revanced.patches.shared.misc.settings.preference.SwitchPreference
import app.revanced.patches.shared.misc.settings.preference.TextPreference import app.revanced.patches.shared.misc.settings.preference.TextPreference
import app.revanced.patches.youtube.misc.extension.sharedExtensionPatch import app.revanced.patches.youtube.misc.extension.sharedExtensionPatch
import app.revanced.patches.youtube.misc.litho.filter.addLithoFilter import app.revanced.patches.youtube.misc.litho.filter.addLithoFilter
@ -71,6 +72,7 @@ internal val customPlaybackSpeedPatch = bytecodePatch(
addResources("youtube", "video.speed.custom.customPlaybackSpeedPatch") addResources("youtube", "video.speed.custom.customPlaybackSpeedPatch")
PreferenceScreen.VIDEO.addPreferences( PreferenceScreen.VIDEO.addPreferences(
SwitchPreference("revanced_custom_speed_menu"),
TextPreference("revanced_custom_playback_speeds", inputType = InputType.TEXT_MULTI_LINE), 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. // Override the min/max speeds that can be used.
speedLimiterMatch.mutableMethod.apply { speedLimiterMatch.mutableMethod.apply {
val limiterMinConstIndex = indexOfFirstLiteralInstructionOrThrow(0.25f.toRawBits().toLong()) val limitMinIndex = indexOfFirstLiteralInstructionOrThrow(0.25f.toRawBits().toLong())
var limiterMaxConstIndex = indexOfFirstLiteralInstruction(2.0f.toRawBits().toLong()) var limitMaxIndex = indexOfFirstLiteralInstruction(2.0f.toRawBits().toLong())
// Newer targets have 4x max speed. // Newer targets have 4x max speed.
if (limiterMaxConstIndex < 0) { if (limitMaxIndex < 0) {
limiterMaxConstIndex = indexOfFirstLiteralInstructionOrThrow(4.0f.toRawBits().toLong()) limitMaxIndex = indexOfFirstLiteralInstructionOrThrow(4.0f.toRawBits().toLong())
} }
val limiterMinConstDestination = getInstruction<OneRegisterInstruction>(limiterMinConstIndex).registerA val limitMinRegister = getInstruction<OneRegisterInstruction>(limitMinIndex).registerA
val limiterMaxConstDestination = getInstruction<OneRegisterInstruction>(limiterMaxConstIndex).registerA val limitMaxRegister = getInstruction<OneRegisterInstruction>(limitMaxIndex).registerA
replaceInstruction(limiterMinConstIndex, "const/high16 v$limiterMinConstDestination, 0.0f") replaceInstruction(limitMinIndex, "const/high16 v$limitMinRegister, 0.0f")
replaceInstruction(limiterMaxConstIndex, "const/high16 v$limiterMaxConstDestination, 10.0f") replaceInstruction(limitMaxIndex, "const/high16 v$limitMaxRegister, 8.0f")
} }
// Add a static INSTANCE field to the class. // Add a static INSTANCE field to the class.

View File

@ -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> <string name="revanced_playback_speed_dialog_button_summary_off">Button is not shown</string>
</patch> </patch>
<patch id="video.speed.custom.customPlaybackSpeedPatch"> <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_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_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> <string name="revanced_custom_playback_speeds_parse_exception">Invalid custom playback speeds. Using default values.</string>
</patch> </patch>