Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

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

Merged
merged 8 commits into from
Oct 26, 2024
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 @@ -3,25 +3,48 @@
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;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -59,18 +59,24 @@ private static void loadCustomSpeeds() {
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);
Expand All @@ -89,10 +95,12 @@ private static boolean arrayContains(float[] array, float value) {
/**
* 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);
Expand All @@ -101,6 +109,7 @@ public static void initializeListPreference(ListPreference preference) {
i++;
}
}

preference.setEntries(preferenceListEntries);
preference.setEntryValues(preferenceListEntryValues);
}
Expand All @@ -111,52 +120,67 @@ public static void initializeListPreference(ListPreference preference) {
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;
}
} catch (Exception ex) {
Logger.printException(() -> "isPlaybackRateSelectorMenuVisible failure", ex);
}

View firstChild = recyclerView.getChildAt(0);
if (!(firstChild instanceof ViewGroup)) {
return;
}
ViewGroup PlaybackSpeedParentView = (ViewGroup) firstChild;
if (PlaybackSpeedParentView.getChildCount() != 8) {
return;
try {
if (PlaybackSpeedMenuFilterPatch.isOldPlaybackSpeedMenuVisible) {
if (hideLithoMenuAndShowOldSpeedMenu(recyclerView, 8)) {
PlaybackSpeedMenuFilterPatch.isOldPlaybackSpeedMenuVisible = false;
}
}
} catch (Exception ex) {
Logger.printException(() -> "isOldPlaybackSpeedMenuVisible failure", ex);
}
});
}

PlaybackSpeedMenuFilterPatch.isPlaybackSpeedMenuVisible = false;
private static boolean hideLithoMenuAndShowOldSpeedMenu(RecyclerView recyclerView, int expectedChildCount) {
if (recyclerView.getChildCount() == 0) {
return false;
}

ViewParent parentView3rd = Utils.getParentView(recyclerView, 3);
if (!(parentView3rd instanceof ViewGroup)) {
return;
}
ViewParent parentView4th = parentView3rd.getParent();
if (!(parentView4th instanceof ViewGroup)) {
return;
}
View firstChild = recyclerView.getChildAt(0);
if (!(firstChild instanceof ViewGroup)) {
return false;
}

ViewGroup PlaybackSpeedParentView = (ViewGroup) firstChild;
if (PlaybackSpeedParentView.getChildCount() != expectedChildCount) {
return false;
}

// 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();
ViewParent parentView3rd = Utils.getParentView(recyclerView, 3);
if (!(parentView3rd instanceof ViewGroup)) {
return true;
}

// 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);
ViewParent parentView4th = parentView3rd.getParent();
if (!(parentView4th instanceof ViewGroup)) {
return true;
}

// 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.
// 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();

// Close the new Playback speed menu and show the old one.
showOldPlaybackSpeedMenu();
} catch (Exception ex) {
Logger.printException(() -> "onFlyoutMenuCreate failure", ex);
}
});
// 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() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,17 +30,31 @@ public static void newVideoStarted(VideoInformation.PlaybackController ignoredPl
*/
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);
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down Expand Up @@ -378,3 +380,4 @@ public class Settings extends BaseSettings {
// endregion
}
}

Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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),
)

Expand Down Expand Up @@ -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.
Expand Down
5 changes: 4 additions & 1 deletion patches/src/main/resources/addresources/values/strings.xml
Original file line number Diff line number Diff line change
Expand Up @@ -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>
Expand Down
Loading