fix(YouTube - Video playback): Overridden to default playback speed in onResume callback https://github.com/inotia00/ReVanced_Extended/issues/2896

This commit is contained in:
inotia00 2025-04-01 19:02:55 +09:00
parent 457dbfec6c
commit 64af7fd8b6
3 changed files with 119 additions and 51 deletions

View File

@ -3,10 +3,14 @@ package app.revanced.extension.youtube.patches.video;
import static app.revanced.extension.shared.utils.StringRef.str; import static app.revanced.extension.shared.utils.StringRef.str;
import static app.revanced.extension.youtube.shared.RootView.isShortsActive; import static app.revanced.extension.youtube.shared.RootView.isShortsActive;
import androidx.annotation.GuardedBy;
import androidx.annotation.NonNull; import androidx.annotation.NonNull;
import org.apache.commons.lang3.BooleanUtils; import org.apache.commons.lang3.BooleanUtils;
import java.util.LinkedHashMap;
import java.util.Map;
import app.revanced.extension.shared.settings.BooleanSetting; import app.revanced.extension.shared.settings.BooleanSetting;
import app.revanced.extension.shared.settings.FloatSetting; import app.revanced.extension.shared.settings.FloatSetting;
import app.revanced.extension.shared.utils.Logger; import app.revanced.extension.shared.utils.Logger;
@ -28,48 +32,61 @@ public class PlaybackSpeedPatch {
Settings.DISABLE_DEFAULT_PLAYBACK_SPEED_MUSIC.get(); Settings.DISABLE_DEFAULT_PLAYBACK_SPEED_MUSIC.get();
private static final long TOAST_DELAY_MILLISECONDS = 750; private static final long TOAST_DELAY_MILLISECONDS = 750;
private static long lastTimeSpeedChanged; private static long lastTimeSpeedChanged;
/**
* The last used playback speed.
* This value is used when the default playback speed is 'Auto'.
*/
private static float lastSelectedPlaybackSpeed = 1.0f; private static float lastSelectedPlaybackSpeed = 1.0f;
private static float lastSelectedShortsPlaybackSpeed = 1.0f;
private static volatile String channelId = ""; /**
private static volatile String videoId = ""; * The last regular video id.
private static boolean isLiveStream; */
private static String videoId = "";
private static volatile String channelIdShorts = ""; @GuardedBy("itself")
private static volatile String videoIdShorts = ""; private static final Map<String, Float> ignoredPlaybackSpeedVideoIds = new LinkedHashMap<>() {
private static boolean isLiveStreamShorts; private static final int NUMBER_OF_LAST_VIDEO_IDS_TO_TRACK = 3;
@Override
protected boolean removeEldestEntry(Map.Entry eldest) {
return size() > NUMBER_OF_LAST_VIDEO_IDS_TO_TRACK;
}
};
/** /**
* Injection point. * Injection point.
* This method is used to reset the playback speed to 1.0 when a general video is started, whether it is a live stream, music, or whitelist.
*/ */
public static void newVideoStarted(@NonNull String newlyLoadedChannelId, @NonNull String newlyLoadedChannelName, public static void newVideoStarted(@NonNull String newlyLoadedChannelId, @NonNull String newlyLoadedChannelName,
@NonNull String newlyLoadedVideoId, @NonNull String newlyLoadedVideoTitle, @NonNull String newlyLoadedVideoId, @NonNull String newlyLoadedVideoTitle,
final long newlyLoadedVideoLength, boolean newlyLoadedLiveStreamValue) { final long newlyLoadedVideoLength, boolean newlyLoadedLiveStreamValue) {
if (isShortsActive()) { if (isShortsActive()) {
channelIdShorts = newlyLoadedChannelId; return;
videoIdShorts = newlyLoadedVideoId;
isLiveStreamShorts = newlyLoadedLiveStreamValue;
Logger.printDebug(() -> "newVideoStarted: " + newlyLoadedVideoId);
} else {
channelId = newlyLoadedChannelId;
videoId = newlyLoadedVideoId;
isLiveStream = newlyLoadedLiveStreamValue;
Logger.printDebug(() -> "newShortsVideoStarted: " + newlyLoadedVideoId);
} }
} if (videoId.equals(newlyLoadedVideoId)) {
return;
}
videoId = newlyLoadedVideoId;
/** boolean isMusic = isMusic(newlyLoadedVideoId);
* Injection point. boolean isWhitelisted = Whitelist.isChannelWhitelistedPlaybackSpeed(newlyLoadedVideoId);
*/
public static void newShortsVideoStarted(@NonNull String newlyLoadedChannelId, @NonNull String newlyLoadedChannelName,
@NonNull String newlyLoadedVideoId, @NonNull String newlyLoadedVideoTitle,
final long newlyLoadedVideoLength, boolean newlyLoadedLiveStreamValue) {
channelIdShorts = newlyLoadedChannelId;
videoIdShorts = newlyLoadedVideoId;
isLiveStreamShorts = newlyLoadedLiveStreamValue;
Logger.printDebug(() -> "newShortsVideoStarted: " + newlyLoadedVideoId); if (newlyLoadedLiveStreamValue || isMusic || isWhitelisted) {
synchronized(ignoredPlaybackSpeedVideoIds) {
if (!ignoredPlaybackSpeedVideoIds.containsKey(newlyLoadedVideoId)) {
lastSelectedPlaybackSpeed = 1.0f;
ignoredPlaybackSpeedVideoIds.put(newlyLoadedVideoId, lastSelectedPlaybackSpeed);
VideoInformation.setPlaybackSpeed(lastSelectedPlaybackSpeed);
VideoInformation.overridePlaybackSpeed(lastSelectedPlaybackSpeed);
Logger.printDebug(() -> "changing playback speed to: 1.0, isLiveStream: " + newlyLoadedLiveStreamValue +
", isMusic: " + isMusic + ", isWhitelisted: " + isWhitelisted);
}
}
}
} }
/** /**
@ -98,32 +115,29 @@ public class PlaybackSpeedPatch {
/** /**
* Injection point. * Injection point.
* This method is called every second for regular videos and Shorts.
*/ */
public static float getPlaybackSpeed(float playbackSpeed) { public static float getPlaybackSpeed(float playbackSpeed) {
boolean isShorts = isShortsActive(); boolean isShorts = isShortsActive();
String currentChannelId = isShorts ? channelIdShorts : channelId;
String currentVideoId = isShorts ? videoIdShorts : videoId;
boolean currentVideoIsLiveStream = isShorts ? isLiveStreamShorts : isLiveStream;
boolean currentVideoIsWhitelisted = Whitelist.isChannelWhitelistedPlaybackSpeed(currentChannelId);
boolean currentVideoIsMusic = !isShorts && isMusic();
if (currentVideoIsLiveStream || currentVideoIsWhitelisted || currentVideoIsMusic) {
Logger.printDebug(() -> "changing playback speed to: 1.0");
VideoInformation.setPlaybackSpeed(1.0f);
return 1.0f;
}
float defaultPlaybackSpeed = isShorts ? DEFAULT_PLAYBACK_SPEED_SHORTS.get() : DEFAULT_PLAYBACK_SPEED.get(); float defaultPlaybackSpeed = isShorts ? DEFAULT_PLAYBACK_SPEED_SHORTS.get() : DEFAULT_PLAYBACK_SPEED.get();
if (defaultPlaybackSpeed < 0) { if (defaultPlaybackSpeed < 0) { // If the default playback speed is 'Auto', it will be overridden to the last used playback speed.
float finalPlaybackSpeed = isShorts ? playbackSpeed : lastSelectedPlaybackSpeed; float finalPlaybackSpeed = isShorts ? lastSelectedShortsPlaybackSpeed : lastSelectedPlaybackSpeed;
VideoInformation.overridePlaybackSpeed(finalPlaybackSpeed); VideoInformation.overridePlaybackSpeed(finalPlaybackSpeed);
Logger.printDebug(() -> "changing playback speed to: " + finalPlaybackSpeed); Logger.printDebug(() -> "changing playback speed to: " + finalPlaybackSpeed);
return finalPlaybackSpeed; return finalPlaybackSpeed;
} else { } else { // Otherwise the default playback speed is used.
if (isShorts) { synchronized (ignoredPlaybackSpeedVideoIds) {
VideoInformation.setPlaybackSpeed(defaultPlaybackSpeed); if (isShorts) {
// For Shorts, the VideoInformation.overridePlaybackSpeed() method is not used, so manually save the playback speed in VideoInformation.
VideoInformation.setPlaybackSpeed(defaultPlaybackSpeed);
} else if (ignoredPlaybackSpeedVideoIds.containsKey(videoId)) {
// For general videos, check whether the default video playback speed should not be applied.
Logger.printDebug(() -> "changing playback speed to: 1.0");
return 1.0f;
}
} }
Logger.printDebug(() -> "changing playback speed to: " + defaultPlaybackSpeed); Logger.printDebug(() -> "changing playback speed to: " + defaultPlaybackSpeed);
return defaultPlaybackSpeed; return defaultPlaybackSpeed;
} }
@ -138,6 +152,19 @@ public class PlaybackSpeedPatch {
public static void userSelectedPlaybackSpeed(float playbackSpeed) { public static void userSelectedPlaybackSpeed(float playbackSpeed) {
try { try {
boolean isShorts = isShortsActive(); boolean isShorts = isShortsActive();
// Saves the user-selected playback speed in the method.
if (isShorts) {
lastSelectedShortsPlaybackSpeed = playbackSpeed;
} else {
lastSelectedPlaybackSpeed = playbackSpeed;
// If the user has manually changed the playback speed, the whitelist has already been applied.
// If there is a videoId on the map, it will be removed.
synchronized (ignoredPlaybackSpeedVideoIds) {
ignoredPlaybackSpeedVideoIds.remove(videoId);
}
}
if (PatchStatus.RememberPlaybackSpeed()) { if (PatchStatus.RememberPlaybackSpeed()) {
BooleanSetting rememberPlaybackSpeedLastSelectedSetting = isShorts BooleanSetting rememberPlaybackSpeedLastSelectedSetting = isShorts
? Settings.REMEMBER_PLAYBACK_SPEED_SHORTS_LAST_SELECTED ? Settings.REMEMBER_PLAYBACK_SPEED_SHORTS_LAST_SELECTED
@ -178,15 +205,23 @@ public class PlaybackSpeedPatch {
} }
}, TOAST_DELAY_MILLISECONDS); }, TOAST_DELAY_MILLISECONDS);
} }
} else if (!isShorts) {
lastSelectedPlaybackSpeed = playbackSpeed;
} }
} catch (Exception ex) { } catch (Exception ex) {
Logger.printException(() -> "userSelectedPlaybackSpeed failure", ex); Logger.printException(() -> "userSelectedPlaybackSpeed failure", ex);
} }
} }
private static boolean isMusic() { /**
* Injection point.
*/
public static void onDismiss() {
synchronized (ignoredPlaybackSpeedVideoIds) {
ignoredPlaybackSpeedVideoIds.remove(videoId);
videoId = "";
}
}
private static boolean isMusic(String videoId) {
if (DISABLE_DEFAULT_PLAYBACK_SPEED_MUSIC && !videoId.isEmpty()) { if (DISABLE_DEFAULT_PLAYBACK_SPEED_MUSIC && !videoId.isEmpty()) {
try { try {
MusicRequest request = MusicRequest.getRequestForVideoId(videoId); MusicRequest request = MusicRequest.getRequestForVideoId(videoId);

View File

@ -3,12 +3,14 @@ package app.revanced.patches.youtube.utils.dismiss
import app.revanced.patcher.extensions.InstructionExtensions.addInstruction import app.revanced.patcher.extensions.InstructionExtensions.addInstruction
import app.revanced.patcher.extensions.InstructionExtensions.getInstruction import app.revanced.patcher.extensions.InstructionExtensions.getInstruction
import app.revanced.patcher.patch.bytecodePatch import app.revanced.patcher.patch.bytecodePatch
import app.revanced.patcher.util.proxy.mutableTypes.MutableMethod
import app.revanced.patches.youtube.utils.extension.Constants.EXTENSION_PATH import app.revanced.patches.youtube.utils.extension.Constants.EXTENSION_PATH
import app.revanced.patches.youtube.utils.extension.sharedExtensionPatch import app.revanced.patches.youtube.utils.extension.sharedExtensionPatch
import app.revanced.util.addStaticFieldToExtension import app.revanced.util.addStaticFieldToExtension
import app.revanced.util.findMethodOrThrow import app.revanced.util.findMethodOrThrow
import app.revanced.util.fingerprint.methodOrThrow import app.revanced.util.fingerprint.methodOrThrow
import app.revanced.util.getReference import app.revanced.util.getReference
import app.revanced.util.getWalkerMethod
import app.revanced.util.indexOfFirstInstructionOrThrow import app.revanced.util.indexOfFirstInstructionOrThrow
import app.revanced.util.indexOfFirstInstructionReversedOrThrow import app.revanced.util.indexOfFirstInstructionReversedOrThrow
import app.revanced.util.indexOfFirstLiteralInstructionOrThrow import app.revanced.util.indexOfFirstLiteralInstructionOrThrow
@ -21,6 +23,8 @@ import com.android.tools.smali.dexlib2.iface.reference.MethodReference
private const val EXTENSION_VIDEO_UTILS_CLASS_DESCRIPTOR = private const val EXTENSION_VIDEO_UTILS_CLASS_DESCRIPTOR =
"$EXTENSION_PATH/utils/VideoUtils;" "$EXTENSION_PATH/utils/VideoUtils;"
private lateinit var dismissMethod: MutableMethod
val dismissPlayerHookPatch = bytecodePatch( val dismissPlayerHookPatch = bytecodePatch(
description = "dismissPlayerHookPatch" description = "dismissPlayerHookPatch"
) { ) {
@ -36,6 +40,21 @@ val dismissPlayerHookPatch = bytecodePatch(
reference?.returnType == "V" && reference?.returnType == "V" &&
reference.parameterTypes.isEmpty() reference.parameterTypes.isEmpty()
} }
getWalkerMethod(dismissPlayerIndex).apply {
val jumpIndex = indexOfFirstInstructionReversedOrThrow {
opcode == Opcode.INVOKE_VIRTUAL &&
getReference<MethodReference>()?.returnType == "V"
}
getWalkerMethod(jumpIndex).apply {
val jumpIndex = indexOfFirstInstructionReversedOrThrow {
opcode == Opcode.INVOKE_VIRTUAL &&
getReference<MethodReference>()?.returnType == "V"
}
dismissMethod = getWalkerMethod(jumpIndex)
}
}
val dismissPlayerReference = val dismissPlayerReference =
getInstruction<ReferenceInstruction>(dismissPlayerIndex).reference as MethodReference getInstruction<ReferenceInstruction>(dismissPlayerIndex).reference as MethodReference
val dismissPlayerClass = dismissPlayerReference.definingClass val dismissPlayerClass = dismissPlayerReference.definingClass
@ -80,4 +99,16 @@ val dismissPlayerHookPatch = bytecodePatch(
} }
} }
} }
} }
/**
* This method is called when the video is closed.
*/
internal fun hookDismissObserver(descriptor: String) =
dismissMethod.apply {
println("Class: $definingClass Name: $name")
addInstruction(
0,
"invoke-static {}, $descriptor"
)
}

View File

@ -11,6 +11,8 @@ import app.revanced.patches.shared.customspeed.customPlaybackSpeedPatch
import app.revanced.patches.shared.litho.addLithoFilter import app.revanced.patches.shared.litho.addLithoFilter
import app.revanced.patches.shared.litho.lithoFilterPatch import app.revanced.patches.shared.litho.lithoFilterPatch
import app.revanced.patches.youtube.utils.compatibility.Constants.COMPATIBLE_PACKAGE import app.revanced.patches.youtube.utils.compatibility.Constants.COMPATIBLE_PACKAGE
import app.revanced.patches.youtube.utils.dismiss.dismissPlayerHookPatch
import app.revanced.patches.youtube.utils.dismiss.hookDismissObserver
import app.revanced.patches.youtube.utils.extension.Constants.COMPONENTS_PATH import app.revanced.patches.youtube.utils.extension.Constants.COMPONENTS_PATH
import app.revanced.patches.youtube.utils.extension.Constants.PATCH_STATUS_CLASS_DESCRIPTOR import app.revanced.patches.youtube.utils.extension.Constants.PATCH_STATUS_CLASS_DESCRIPTOR
import app.revanced.patches.youtube.utils.extension.Constants.VIDEO_PATH import app.revanced.patches.youtube.utils.extension.Constants.VIDEO_PATH
@ -25,7 +27,6 @@ import app.revanced.patches.youtube.utils.resourceid.sharedResourceIdPatch
import app.revanced.patches.youtube.utils.settings.ResourceUtils.addPreference import app.revanced.patches.youtube.utils.settings.ResourceUtils.addPreference
import app.revanced.patches.youtube.utils.settings.settingsPatch import app.revanced.patches.youtube.utils.settings.settingsPatch
import app.revanced.patches.youtube.video.information.hookBackgroundPlayVideoInformation import app.revanced.patches.youtube.video.information.hookBackgroundPlayVideoInformation
import app.revanced.patches.youtube.video.information.hookShortsVideoInformation
import app.revanced.patches.youtube.video.information.hookVideoInformation import app.revanced.patches.youtube.video.information.hookVideoInformation
import app.revanced.patches.youtube.video.information.onCreateHook import app.revanced.patches.youtube.video.information.onCreateHook
import app.revanced.patches.youtube.video.information.speedSelectionInsertMethod import app.revanced.patches.youtube.video.information.speedSelectionInsertMethod
@ -87,6 +88,7 @@ val videoPlaybackPatch = bytecodePatch(
), ),
flyoutMenuHookPatch, flyoutMenuHookPatch,
lithoFilterPatch, lithoFilterPatch,
dismissPlayerHookPatch,
playerTypeHookPatch, playerTypeHookPatch,
recyclerViewTreeObserverPatch, recyclerViewTreeObserverPatch,
shortsPlaybackPatch, shortsPlaybackPatch,
@ -183,9 +185,9 @@ val videoPlaybackPatch = bytecodePatch(
} }
hookBackgroundPlayVideoInformation("$EXTENSION_PLAYBACK_SPEED_CLASS_DESCRIPTOR->newVideoStarted(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;JZ)V") hookBackgroundPlayVideoInformation("$EXTENSION_PLAYBACK_SPEED_CLASS_DESCRIPTOR->newVideoStarted(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;JZ)V")
hookShortsVideoInformation("$EXTENSION_PLAYBACK_SPEED_CLASS_DESCRIPTOR->newShortsVideoStarted(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;JZ)V")
hookVideoInformation("$EXTENSION_PLAYBACK_SPEED_CLASS_DESCRIPTOR->newVideoStarted(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;JZ)V") hookVideoInformation("$EXTENSION_PLAYBACK_SPEED_CLASS_DESCRIPTOR->newVideoStarted(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;JZ)V")
hookPlayerResponseVideoId("$EXTENSION_PLAYBACK_SPEED_CLASS_DESCRIPTOR->fetchMusicRequest(Ljava/lang/String;Z)V") hookPlayerResponseVideoId("$EXTENSION_PLAYBACK_SPEED_CLASS_DESCRIPTOR->fetchMusicRequest(Ljava/lang/String;Z)V")
hookDismissObserver("$EXTENSION_PLAYBACK_SPEED_CLASS_DESCRIPTOR->onDismiss()V")
updatePatchStatus(PATCH_STATUS_CLASS_DESCRIPTOR, "RememberPlaybackSpeed") updatePatchStatus(PATCH_STATUS_CLASS_DESCRIPTOR, "RememberPlaybackSpeed")