1
mirror of https://github.com/revanced/revanced-integrations synced 2025-11-21 18:35:37 +01:00

Compare commits

...

4 Commits

Author SHA1 Message Date
semantic-release-bot
aa6f591141 chore(release): 1.0.0-dev.4 [skip ci]
# [1.0.0-dev.4](https://github.com/ReVanced/revanced-integrations/compare/v1.0.0-dev.3...v1.0.0-dev.4) (2023-12-04)

### Bug Fixes

* **YouTube - Return YouTube Dislike:** Prevent the first Short opened from freezing the UI ([#532](https://github.com/ReVanced/revanced-integrations/issues/532)) ([0bb8669](0bb86694e2))
2023-12-04 08:49:53 +00:00
LisoUseInAIKyrios
0bb86694e2 fix(YouTube - Return YouTube Dislike): Prevent the first Short opened from freezing the UI (#532) 2023-12-04 10:47:29 +02:00
semantic-release-bot
d484f35127 chore(release): 1.0.0-dev.3 [skip ci]
# [1.0.0-dev.3](https://github.com/ReVanced/revanced-integrations/compare/v1.0.0-dev.2...v1.0.0-dev.3) (2023-12-03)

### Bug Fixes

* **YouTube - SponsorBlock:** Prevent autoplay from stopping to work ([f4e2d56](f4e2d56b18))
2023-12-03 18:33:33 +00:00
oSumAtrIX
f4e2d56b18 fix(YouTube - SponsorBlock): Prevent autoplay from stopping to work 2023-12-03 19:30:56 +01:00
9 changed files with 115 additions and 42 deletions

View File

@@ -1,3 +1,17 @@
# [1.0.0-dev.4](https://github.com/ReVanced/revanced-integrations/compare/v1.0.0-dev.3...v1.0.0-dev.4) (2023-12-04)
### Bug Fixes
* **YouTube - Return YouTube Dislike:** Prevent the first Short opened from freezing the UI ([#532](https://github.com/ReVanced/revanced-integrations/issues/532)) ([0bb8669](https://github.com/ReVanced/revanced-integrations/commit/0bb86694e24a6a41edee62f5ef1bb80fe7bc3f19))
# [1.0.0-dev.3](https://github.com/ReVanced/revanced-integrations/compare/v1.0.0-dev.2...v1.0.0-dev.3) (2023-12-03)
### Bug Fixes
* **YouTube - SponsorBlock:** Prevent autoplay from stopping to work ([f4e2d56](https://github.com/ReVanced/revanced-integrations/commit/f4e2d56b181fee4d693dea1dfe81974237e4eff7))
# [1.0.0-dev.2](https://github.com/ReVanced/revanced-integrations/compare/v1.0.0-dev.1...v1.0.0-dev.2) (2023-12-03)

View File

@@ -9,6 +9,7 @@ import android.widget.TextView;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import app.revanced.integrations.patches.components.ReturnYouTubeDislikeFilterPatch;
import app.revanced.integrations.patches.spoof.SpoofAppVersionPatch;
import app.revanced.integrations.returnyoutubedislike.ReturnYouTubeDislike;
import app.revanced.integrations.settings.SettingsEnum;
import app.revanced.integrations.shared.PlayerType;
@@ -27,19 +28,25 @@ import static app.revanced.integrations.returnyoutubedislike.ReturnYouTubeDislik
* Handles all interaction of UI patch components.
*
* Known limitation:
* Litho based Shorts player can experience temporarily frozen video playback if the RYD fetch takes too long.
* The implementation of Shorts litho requires blocking the loading the first Short until RYD has completed.
* This is because it modifies the dislikes text synchronously, and if the RYD fetch has
* not completed yet then the UI will be temporarily frozen.
*
* Temporary work around:
* Enable app spoofing to version 18.33.40 or older, as that uses a non litho Shorts player.
*
* Permanent fix (yet to be implemented), either of:
* - Modify patch to hook onto the Shorts Litho TextView, and update the dislikes asynchronously.
* - Find a way to force Litho to rebuild it's component tree
* (and use that hook to force the shorts dislikes to update after the fetch is completed).
* A (yet to be implemented) solution that fixes this problem. Any one of:
* - Modify patch to hook onto the Shorts Litho TextView, and update the dislikes text asynchronously.
* - Find a way to force Litho to rebuild it's component tree,
* and use that hook to force the shorts dislikes to update after the fetch is completed.
* - Hook into the dislikes button image view, and replace the dislikes thumb down image with a
* generated image of the number of dislikes, then update the image asynchronously. This Could
* also be used for the regular video player to give a better UI layout and completely remove
* the need for the Rolling Number patches.
*/
@SuppressWarnings("unused")
public class ReturnYouTubeDislikePatch {
public static final boolean IS_SPOOFING_TO_NON_LITHO_SHORTS_PLAYER =
SpoofAppVersionPatch.isSpoofingToEqualOrLessThan("18.33.40");
/**
* RYD data for the current video on screen.
*/
@@ -549,26 +556,46 @@ public class ReturnYouTubeDislikePatch {
// Video Id and voting hooks (all players).
//
private static volatile boolean lastPlayerResponseWasShort;
/**
* Injection point. Uses 'playback response' video id hook to preload RYD.
*/
public static void preloadVideoId(@NonNull String videoId, boolean videoIsOpeningOrPlaying) {
public static void preloadVideoId(@NonNull String videoId, boolean isShortAndOpeningOrPlaying) {
try {
// Shorts shelf in home and subscription feed causes player response hook to be called,
// and the 'is opening/playing' parameter will be false.
// This hook will be called again when the Short is actually opened.
if (!videoIsOpeningOrPlaying || !SettingsEnum.RYD_ENABLED.getBoolean()) {
return;
}
if (!SettingsEnum.RYD_SHORTS.getBoolean() && PlayerType.getCurrent().isNoneHiddenOrSlidingMinimized()) {
if (!SettingsEnum.RYD_ENABLED.getBoolean()) {
return;
}
if (videoId.equals(lastPrefetchedVideoId)) {
return;
}
final boolean videoIdIsShort = VideoInformation.lastVideoIdIsShort();
// Shorts shelf in home and subscription feed causes player response hook to be called,
// and the 'is opening/playing' parameter will be false.
// This hook will be called again when the Short is actually opened.
if (videoIdIsShort && (!isShortAndOpeningOrPlaying || !SettingsEnum.RYD_SHORTS.getBoolean())) {
return;
}
final boolean waitForFetchToComplete = !IS_SPOOFING_TO_NON_LITHO_SHORTS_PLAYER
&& videoIdIsShort && !lastPlayerResponseWasShort;
lastPlayerResponseWasShort = videoIdIsShort;
lastPrefetchedVideoId = videoId;
LogHelper.printDebug(() -> "Prefetching RYD for video: " + videoId);
ReturnYouTubeDislike.getFetchForVideoId(videoId);
ReturnYouTubeDislike fetch = ReturnYouTubeDislike.getFetchForVideoId(videoId);
if (waitForFetchToComplete && !fetch.fetchCompleted()) {
// This call is off the main thread, so wait until the RYD fetch completely finishes,
// otherwise if this returns before the fetch completes then the UI can
// become frozen when the main thread tries to modify the litho Shorts dislikes and
// it must wait for the fetch.
// Only need to do this for the first Short opened, as the next Short to swipe to
// are preloaded in the background.
//
// If an asynchronous litho Shorts solution is found, then this blocking call should be removed.
LogHelper.printDebug(() -> "Waiting for prefetch to complete: " + videoId);
fetch.getFetchData(10000); // Use any arbitrarily large max wait time.
}
} catch (Exception ex) {
LogHelper.printException(() -> "preloadVideoId failure", ex);
}

View File

@@ -17,6 +17,10 @@ import java.util.Objects;
public final class VideoInformation {
private static final float DEFAULT_YOUTUBE_PLAYBACK_SPEED = 1.0f;
private static final String SEEK_METHOD_NAME = "seekTo";
/**
* Prefix present in all Short player parameters signature.
*/
private static final String SHORTS_PLAYER_PARAMETERS = "8AEB";
private static WeakReference<Object> playerControllerRef;
private static Method seekMethod;
@@ -28,6 +32,7 @@ public final class VideoInformation {
@NonNull
private static volatile String playerResponseVideoId = "";
private static volatile boolean videoIdIsShort;
/**
* The current playback speed
@@ -65,12 +70,33 @@ public final class VideoInformation {
}
}
/**
* @return If the player parameters are for a Short.
*/
public static boolean playerParametersAreShort(@NonNull String parameters) {
return parameters.startsWith(SHORTS_PLAYER_PARAMETERS);
}
/**
* Injection point.
*/
public static String newPlayerResponseSignature(@NonNull String signature, boolean isShortAndOpeningOrPlaying) {
final boolean isShort = playerParametersAreShort(signature);
if (!isShort || isShortAndOpeningOrPlaying) {
if (videoIdIsShort != isShort) {
videoIdIsShort = isShort;
LogHelper.printDebug(() -> "videoIdIsShort: " + isShort);
}
}
return signature; // Return the original value since we are observing and not modifying.
}
/**
* Injection point. Called off the main thread.
*
* @param videoId The id of the last video loaded.
*/
public static void setPlayerResponseVideoId(@NonNull String videoId, boolean videoIsOpeningOrPlaying) {
public static void setPlayerResponseVideoId(@NonNull String videoId, boolean isShortAndOpeningOrPlaying) {
if (!playerResponseVideoId.equals(videoId)) {
LogHelper.printDebug(() -> "New player response video id: " + videoId);
playerResponseVideoId = videoId;
@@ -136,7 +162,7 @@ public final class VideoInformation {
final long videoLength = getVideoLength();
// Prevent issues such as play/ pause button or autoplay not working.
final long seekToMilliseconds = millisecond > videoLength ? Integer.MAX_VALUE : millisecond;
final long seekToMilliseconds = Math.min(millisecond, VideoInformation.getVideoLength() - 250);
ReVancedUtils.verifyOnMainThread();
try {
@@ -155,9 +181,9 @@ public final class VideoInformation {
}
/**
* Id of the current video playing. Includes Shorts.
* Id of the last video opened. Includes Shorts.
*
* @return The id of the video. Empty string if not set yet.
* @return The id of the video, or an empty string if no videos have been opened yet.
*/
@NonNull
public static String getVideoId() {
@@ -166,20 +192,30 @@ public final class VideoInformation {
/**
* Differs from {@link #videoId} as this is the video id for the
* last player response received, which may not be the current video playing.
* last player response received, which may not be the last video opened.
* <p>
* If Shorts are loading the background, this commonly will be
* different from the Short that is currently on screen.
* <p>
* For most use cases, you should instead use {@link #getVideoId()}.
*
* @return The id of the last video loaded. Empty string if not set yet.
* @return The id of the last video loaded, or an empty string if no videos have been loaded yet.
*/
@NonNull
public static String getPlayerResponseVideoId() {
return playerResponseVideoId;
}
/**
* @return If the last player response video id _that was opened_ was a Short.
* <p>
* Note: This value returned may not match the status of {@link #getPlayerResponseVideoId()}
* since that includes player responses for videos not opened.
*/
public static boolean lastVideoIdIsShort() {
return videoIdIsShort;
}
/**
* @return The current playback speed.
*/

View File

@@ -53,14 +53,14 @@ public final class ReturnYouTubeDislikeFilterPatch extends Filter {
/**
* Injection point.
*/
public static void newPlayerResponseVideoId(String videoId, boolean videoIsOpeningOrPlaying) {
public static void newPlayerResponseVideoId(String videoId, boolean isShortAndOpeningOrPlaying) {
try {
if (!videoIsOpeningOrPlaying || !SettingsEnum.RYD_SHORTS.getBoolean()) {
if (!isShortAndOpeningOrPlaying || !SettingsEnum.RYD_SHORTS.getBoolean()) {
return;
}
synchronized (lastVideoIds) {
if (lastVideoIds.put(videoId, Boolean.TRUE) == null) {
LogHelper.printDebug(() -> "New video id: " + videoId);
LogHelper.printDebug(() -> "New Short video id: " + videoId);
}
}
} catch (Exception ex) {

View File

@@ -37,11 +37,6 @@ public class SpoofSignaturePatch {
*/
private static final String SCRIM_PARAMETER = "SAFgAXgB";
/**
* Parameters used in YouTube Shorts.
*/
private static final String SHORTS_PLAYER_PARAMETERS = "8AEB";
/**
* Last video id loaded. Used to prevent reloading the same spec multiple times.
*/
@@ -62,7 +57,7 @@ public class SpoofSignaturePatch {
*
* @param parameters Original protobuf parameter value.
*/
public static String spoofParameter(String parameters) {
public static String spoofParameter(String parameters, boolean isShortAndOpeningOrPlaying) {
try {
LogHelper.printDebug(() -> "Original protobuf parameter value: " + parameters);
@@ -74,7 +69,7 @@ public class SpoofSignaturePatch {
if (useOriginalStoryboardRenderer = parameters.length() > 150) return parameters;
// Shorts do not need to be spoofed.
if (useOriginalStoryboardRenderer = parameters.startsWith(SHORTS_PLAYER_PARAMETERS)) {
if (useOriginalStoryboardRenderer = VideoInformation.playerParametersAreShort(parameters)) {
isPlayingShorts = true;
return parameters;
}

View File

@@ -62,12 +62,12 @@ public class ReturnYouTubeDislikeApi {
* How long to wait until API calls are resumed, if the API requested a back off.
* No clear guideline of how long to wait until resuming.
*/
private static final int BACKOFF_RATE_LIMIT_MILLISECONDS = 4 * 60 * 1000; // 4 Minutes.
private static final int BACKOFF_RATE_LIMIT_MILLISECONDS = 5 * 60 * 1000; // 5 Minutes.
/**
* How long to wait until API calls are resumed, if any connection error occurs.
*/
private static final int BACKOFF_CONNECTION_ERROR_MILLISECONDS = 60 * 1000; // 60 Seconds.
private static final int BACKOFF_CONNECTION_ERROR_MILLISECONDS = 2 * 60 * 1000; // 2 Minutes.
/**
* If non zero, then the system time of when API calls can resume.

View File

@@ -13,7 +13,6 @@ import android.preference.PreferenceScreen;
import android.preference.SwitchPreference;
import app.revanced.integrations.patches.ReturnYouTubeDislikePatch;
import app.revanced.integrations.patches.spoof.SpoofAppVersionPatch;
import app.revanced.integrations.returnyoutubedislike.ReturnYouTubeDislike;
import app.revanced.integrations.returnyoutubedislike.requests.ReturnYouTubeDislikeApi;
import app.revanced.integrations.settings.SettingsEnum;
@@ -21,9 +20,6 @@ import app.revanced.integrations.settings.SharedPrefCategory;
public class ReturnYouTubeDislikeSettingsFragment extends PreferenceFragment {
private static final boolean IS_SPOOFING_TO_NON_LITHO_SHORTS_PLAYER =
SpoofAppVersionPatch.isSpoofingToEqualOrLessThan("18.33.40");
/**
* If dislikes are shown on Shorts.
*/
@@ -79,7 +75,7 @@ public class ReturnYouTubeDislikeSettingsFragment extends PreferenceFragment {
shortsPreference.setChecked(SettingsEnum.RYD_SHORTS.getBoolean());
shortsPreference.setTitle(str("revanced_ryd_shorts_title"));
String shortsSummary = str("revanced_ryd_shorts_summary_on",
IS_SPOOFING_TO_NON_LITHO_SHORTS_PLAYER
ReturnYouTubeDislikePatch.IS_SPOOFING_TO_NON_LITHO_SHORTS_PLAYER
? ""
: "\n\n" + str("revanced_ryd_shorts_summary_disclaimer"));
shortsPreference.setSummaryOn(shortsSummary);

View File

@@ -1,10 +1,11 @@
package app.revanced.integrations.shared
import app.revanced.integrations.patches.VideoInformation
import app.revanced.integrations.utils.Event
import app.revanced.integrations.utils.LogHelper
/**
* WatchWhile player type
* WatchWhile player type.
*/
enum class PlayerType {
/**
@@ -83,6 +84,8 @@ enum class PlayerType {
* Does not include the first moment after a short is opened when a regular video is minimized on screen,
* or while watching a short with a regular video present on a spoofed 16.x version of YouTube.
* To include those situations instead use [isNoneHiddenOrMinimized].
*
* @see VideoInformation
*/
fun isNoneOrHidden(): Boolean {
return this == NONE || this == HIDDEN
@@ -99,6 +102,7 @@ enum class PlayerType {
* though a Short is being opened or is on screen (see [isNoneHiddenOrMinimized]).
*
* @return If nothing, a Short, or a regular video is sliding off screen to a dismissed or hidden state.
* @see VideoInformation
*/
fun isNoneHiddenOrSlidingMinimized(): Boolean {
return isNoneOrHidden() || this == WATCH_WHILE_SLIDING_MINIMIZED_DISMISSED
@@ -117,6 +121,7 @@ enum class PlayerType {
*
* @return If nothing, a Short, a regular video is sliding off screen to a dismissed or hidden state,
* a regular video is minimized (and a new video is not being opened).
* @see VideoInformation
*/
fun isNoneHiddenOrMinimized(): Boolean {
return isNoneHiddenOrSlidingMinimized() || this == WATCH_WHILE_MINIMIZED

View File

@@ -1,4 +1,4 @@
org.gradle.parallel = true
org.gradle.caching = true
android.useAndroidX = true
version = 1.0.0-dev.2
version = 1.0.0-dev.4