mirror of
https://github.com/ReVanced/revanced-patches.git
synced 2026-01-16 07:43:56 +00:00
Compare commits
29 Commits
v5.33.0-de
...
v5.34.0-de
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
182829d51c | ||
|
|
61824ade23 | ||
|
|
ff4308e961 | ||
|
|
b5eb13c0a8 | ||
|
|
b702dceda0 | ||
|
|
d616652058 | ||
|
|
c3e571e765 | ||
|
|
30176a3318 | ||
|
|
9c0638d128 | ||
|
|
d7eb6e87a5 | ||
|
|
562e005772 | ||
|
|
f61218de52 | ||
|
|
a19b670e19 | ||
|
|
300d816350 | ||
|
|
63d64a5c87 | ||
|
|
0cfc31c8f7 | ||
|
|
a28891e5f3 | ||
|
|
36036b082d | ||
|
|
1bc63e50a7 | ||
|
|
4b2b5e3029 | ||
|
|
9afa7d2ac6 | ||
|
|
1a8146dbc8 | ||
|
|
178eed7fcd | ||
|
|
621292644c | ||
|
|
1dd01cf54a | ||
|
|
8c31374c53 | ||
|
|
2e177a8839 | ||
|
|
cfffd422f8 | ||
|
|
37aab8382e |
107
CHANGELOG.md
107
CHANGELOG.md
@@ -1,3 +1,110 @@
|
||||
# [5.34.0-dev.4](https://github.com/ReVanced/revanced-patches/compare/v5.34.0-dev.3...v5.34.0-dev.4) (2025-08-10)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **YouTube - Hide layout components:** Do not hide community posts on channel profiles ([#5634](https://github.com/ReVanced/revanced-patches/issues/5634)) ([9e3d5a2](https://github.com/ReVanced/revanced-patches/commit/9e3d5a2b36106479470f3f69920518b57e8c4dca))
|
||||
|
||||
# [5.34.0-dev.3](https://github.com/ReVanced/revanced-patches/compare/v5.34.0-dev.2...v5.34.0-dev.3) (2025-08-09)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **pixiv - Hide ads:** Constrain patch to last working app target ([d8ea56c](https://github.com/ReVanced/revanced-patches/commit/d8ea56ca4be47df1c43f96ec41b91c800f1d9daf))
|
||||
|
||||
# [5.34.0-dev.2](https://github.com/ReVanced/revanced-patches/compare/v5.34.0-dev.1...v5.34.0-dev.2) (2025-08-09)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **Backdrops:** Remove broken patch that is no longer supported ([#5627](https://github.com/ReVanced/revanced-patches/issues/5627)) ([ebb8332](https://github.com/ReVanced/revanced-patches/commit/ebb83320838aa99dd4417d45a50333dd42c1218a))
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* **YouTube - Playback speed:** Show current playback speed on player speed dialog button ([#5607](https://github.com/ReVanced/revanced-patches/issues/5607)) ([279436a](https://github.com/ReVanced/revanced-patches/commit/279436a3657b50f98bb4cc64dc88dc14e422f204))
|
||||
|
||||
# [5.34.0-dev.1](https://github.com/ReVanced/revanced-patches/compare/v5.33.0...v5.34.0-dev.1) (2025-08-08)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **Twitch:** Constrain patches to last working app targets ([#5373](https://github.com/ReVanced/revanced-patches/issues/5373)) ([29a4748](https://github.com/ReVanced/revanced-patches/commit/29a47481c4efa209a3a53df60613b59a73adbe07))
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* **Instagram:** Support latest app version ([#5611](https://github.com/ReVanced/revanced-patches/issues/5611)) ([26fe690](https://github.com/ReVanced/revanced-patches/commit/26fe690dfbefe6c412c5f81f208a3b1d2fbd7a0a))
|
||||
|
||||
# [5.33.0](https://github.com/ReVanced/revanced-patches/compare/v5.32.0...v5.33.0) (2025-08-05)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **Messenger - Hide Facebook button:** Support the latest app version ([#5590](https://github.com/ReVanced/revanced-patches/issues/5590)) ([0cab98d](https://github.com/ReVanced/revanced-patches/commit/0cab98df1689dbf7a042f18f4a961d47da1430ad))
|
||||
* **NFC Tools:** Remove broken patch that is no longer supported ([#5584](https://github.com/ReVanced/revanced-patches/issues/5584)) ([cd3a6be](https://github.com/ReVanced/revanced-patches/commit/cd3a6be75c6bd3cc33c0b17a044bd6147f27b5ce))
|
||||
* **YouTube - Force original audio:** Disable a/b feature flag that forces localized audio ([#5582](https://github.com/ReVanced/revanced-patches/issues/5582)) ([9fe13ee](https://github.com/ReVanced/revanced-patches/commit/9fe13ee1af104c009efd19b826adef375e48e191))
|
||||
* **YouTube - Litho filter:** Correctly filter identifier of older YouTube targets ([bf29d69](https://github.com/ReVanced/revanced-patches/commit/bf29d6909e389819bad878ad3b94bbc90d823cc9))
|
||||
* **YouTube - Playback speed:** Use old speed menu for player button if enabled ([1e8f436](https://github.com/ReVanced/revanced-patches/commit/1e8f4368e117f4b278c24709231cb32546e46dc0))
|
||||
* **YouTube - Video quality:** Fix 144p default not always used ([2f7483a](https://github.com/ReVanced/revanced-patches/commit/2f7483a2d789c28a243b58bb7a252c0d590858ee))
|
||||
* **YouTube - Video quality:** Fix dialog quality list check mark not always shown ([295f0f2](https://github.com/ReVanced/revanced-patches/commit/295f0f216b5e8aa9d68457862e73e312b7342703))
|
||||
* **YouTube - Video quality:** Fix wrong qualities sometimes shown in player button dialog ([7378ae3](https://github.com/ReVanced/revanced-patches/commit/7378ae3c5fc88f91bf5cd6db47c6cd170a8c5a4f))
|
||||
* **YouTube - Video quality:** Use 1080p enhanced bitrate for Premium users ([#5565](https://github.com/ReVanced/revanced-patches/issues/5565)) ([bd3ace0](https://github.com/ReVanced/revanced-patches/commit/bd3ace0bd04ccd0369adb49d63aa0cf986402346))
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* **ORF ON:** Add `Remove root detection` patch ([#5551](https://github.com/ReVanced/revanced-patches/issues/5551)) ([6c6aa35](https://github.com/ReVanced/revanced-patches/commit/6c6aa35411a139dddc3a15dd757fbeded5d1a0a3))
|
||||
* **YouTube - Playback speed:** Add "Restore old playback speed menu" option ([#5552](https://github.com/ReVanced/revanced-patches/issues/5552)) ([b01f15b](https://github.com/ReVanced/revanced-patches/commit/b01f15b9acb0427aed99b0141ae271831b7936bf))
|
||||
* **YouTube:** Add player button to change video quality ([#5435](https://github.com/ReVanced/revanced-patches/issues/5435)) ([d5f51bf](https://github.com/ReVanced/revanced-patches/commit/d5f51bf400dd22626ff65d7563b6fde70d53fb25))
|
||||
|
||||
|
||||
### Performance Improvements
|
||||
|
||||
* **YouTube:** Filter identifier callback only on root component creation ([#5558](https://github.com/ReVanced/revanced-patches/issues/5558)) ([ccac46e](https://github.com/ReVanced/revanced-patches/commit/ccac46eebc2e14b094454e37ef4461d48a62c53f))
|
||||
|
||||
# [5.33.0-dev.13](https://github.com/ReVanced/revanced-patches/compare/v5.33.0-dev.12...v5.33.0-dev.13) (2025-08-05)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **Messenger - Hide Facebook button:** Support the latest app version ([#5590](https://github.com/ReVanced/revanced-patches/issues/5590)) ([0cab98d](https://github.com/ReVanced/revanced-patches/commit/0cab98df1689dbf7a042f18f4a961d47da1430ad))
|
||||
|
||||
# [5.33.0-dev.12](https://github.com/ReVanced/revanced-patches/compare/v5.33.0-dev.11...v5.33.0-dev.12) (2025-08-04)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **YouTube - Video quality:** Fix dialog quality list check mark not always shown ([295f0f2](https://github.com/ReVanced/revanced-patches/commit/295f0f216b5e8aa9d68457862e73e312b7342703))
|
||||
|
||||
# [5.33.0-dev.11](https://github.com/ReVanced/revanced-patches/compare/v5.33.0-dev.10...v5.33.0-dev.11) (2025-08-04)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **YouTube - Video quality:** Fix 144p default not always used ([2f7483a](https://github.com/ReVanced/revanced-patches/commit/2f7483a2d789c28a243b58bb7a252c0d590858ee))
|
||||
|
||||
# [5.33.0-dev.10](https://github.com/ReVanced/revanced-patches/compare/v5.33.0-dev.9...v5.33.0-dev.10) (2025-08-04)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **YouTube - Video quality:** Fix wrong qualities sometimes shown in player button dialog ([7378ae3](https://github.com/ReVanced/revanced-patches/commit/7378ae3c5fc88f91bf5cd6db47c6cd170a8c5a4f))
|
||||
|
||||
# [5.33.0-dev.9](https://github.com/ReVanced/revanced-patches/compare/v5.33.0-dev.8...v5.33.0-dev.9) (2025-08-04)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **YouTube - Force original audio:** Disable a/b feature flag that forces localized audio ([#5582](https://github.com/ReVanced/revanced-patches/issues/5582)) ([9fe13ee](https://github.com/ReVanced/revanced-patches/commit/9fe13ee1af104c009efd19b826adef375e48e191))
|
||||
|
||||
# [5.33.0-dev.8](https://github.com/ReVanced/revanced-patches/compare/v5.33.0-dev.7...v5.33.0-dev.8) (2025-08-03)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **NFC Tools:** Remove broken patch that is no longer supported ([#5584](https://github.com/ReVanced/revanced-patches/issues/5584)) ([cd3a6be](https://github.com/ReVanced/revanced-patches/commit/cd3a6be75c6bd3cc33c0b17a044bd6147f27b5ce))
|
||||
|
||||
# [5.33.0-dev.7](https://github.com/ReVanced/revanced-patches/compare/v5.33.0-dev.6...v5.33.0-dev.7) (2025-08-03)
|
||||
|
||||
|
||||
|
||||
@@ -329,7 +329,7 @@ public class Utils {
|
||||
return (R) child;
|
||||
}
|
||||
|
||||
throw new IllegalArgumentException("View with resource name '" + str + "' not found");
|
||||
throw new IllegalArgumentException("View with resource name not found: " + str);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -1,16 +1,20 @@
|
||||
package app.revanced.extension.youtube
|
||||
|
||||
import app.revanced.extension.shared.Logger
|
||||
import java.util.Collections
|
||||
|
||||
/**
|
||||
* generic event provider class
|
||||
*/
|
||||
class Event<T> {
|
||||
private val eventListeners = mutableSetOf<(T) -> Unit>()
|
||||
private val eventListeners = Collections.synchronizedSet(mutableSetOf<(T) -> Unit>())
|
||||
|
||||
operator fun plusAssign(observer: (T) -> Unit) {
|
||||
addObserver(observer)
|
||||
}
|
||||
|
||||
fun addObserver(observer: (T) -> Unit) {
|
||||
Logger.printDebug { "Adding observer: $observer" }
|
||||
eventListeners.add(observer)
|
||||
}
|
||||
|
||||
@@ -23,7 +27,8 @@ class Event<T> {
|
||||
}
|
||||
|
||||
operator fun invoke(value: T) {
|
||||
for (observer in eventListeners)
|
||||
for (observer in eventListeners) {
|
||||
observer.invoke(value)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -24,6 +24,16 @@ public class ForceOriginalAudioPatch {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Injection point.
|
||||
*/
|
||||
public static boolean ignoreDefaultAudioStream(boolean original) {
|
||||
if (Settings.FORCE_ORIGINAL_AUDIO.get()) {
|
||||
return false;
|
||||
}
|
||||
return original;
|
||||
}
|
||||
|
||||
/**
|
||||
* Injection point.
|
||||
*/
|
||||
@@ -50,7 +60,6 @@ public class ForceOriginalAudioPatch {
|
||||
return isOriginal;
|
||||
} catch (Exception ex) {
|
||||
Logger.printException(() -> "isDefaultAudioStream failure", ex);
|
||||
|
||||
return isDefault;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,12 +1,18 @@
|
||||
package app.revanced.extension.youtube.patches;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
|
||||
import com.google.android.libraries.youtube.innertube.model.media.VideoQuality;
|
||||
|
||||
import java.lang.ref.WeakReference;
|
||||
import java.util.Arrays;
|
||||
import java.util.Objects;
|
||||
|
||||
import app.revanced.extension.shared.Logger;
|
||||
import app.revanced.extension.shared.Utils;
|
||||
import app.revanced.extension.youtube.Event;
|
||||
import app.revanced.extension.youtube.shared.ShortsPlayerState;
|
||||
import app.revanced.extension.youtube.shared.VideoState;
|
||||
|
||||
/**
|
||||
@@ -16,11 +22,31 @@ import app.revanced.extension.youtube.shared.VideoState;
|
||||
public final class VideoInformation {
|
||||
|
||||
public interface PlaybackController {
|
||||
// Methods are added to YT classes during patching.
|
||||
boolean seekTo(long videoTime);
|
||||
void seekToRelative(long videoTimeOffset);
|
||||
// Methods are added during patching.
|
||||
boolean patch_seekTo(long videoTime);
|
||||
void patch_seekToRelative(long videoTimeOffset);
|
||||
}
|
||||
|
||||
/**
|
||||
* Interface to use obfuscated methods.
|
||||
*/
|
||||
public interface VideoQualityMenuInterface {
|
||||
// Method is added during patching.
|
||||
void patch_setQuality(VideoQuality quality);
|
||||
}
|
||||
|
||||
/**
|
||||
* Video resolution of the automatic quality option..
|
||||
*/
|
||||
public static final int AUTOMATIC_VIDEO_QUALITY_VALUE = -2;
|
||||
|
||||
/**
|
||||
* All quality names are the same for all languages.
|
||||
* VideoQuality also has a resolution enum that can be used if needed.
|
||||
*/
|
||||
public static final String VIDEO_QUALITY_1080P_PREMIUM_NAME = "1080p Premium";
|
||||
|
||||
|
||||
private static final float DEFAULT_YOUTUBE_PLAYBACK_SPEED = 1.0f;
|
||||
/**
|
||||
* Prefix present in all Short player parameters signature.
|
||||
@@ -30,12 +56,10 @@ public final class VideoInformation {
|
||||
private static WeakReference<PlaybackController> playerControllerRef = new WeakReference<>(null);
|
||||
private static WeakReference<PlaybackController> mdxPlayerDirectorRef = new WeakReference<>(null);
|
||||
|
||||
@NonNull
|
||||
private static String videoId = "";
|
||||
private static long videoLength = 0;
|
||||
private static long videoTime = -1;
|
||||
|
||||
@NonNull
|
||||
private static volatile String playerResponseVideoId = "";
|
||||
private static volatile boolean playerResponseVideoIdIsShort;
|
||||
private static volatile boolean videoIdIsShort;
|
||||
@@ -45,6 +69,44 @@ public final class VideoInformation {
|
||||
*/
|
||||
private static float playbackSpeed = DEFAULT_YOUTUBE_PLAYBACK_SPEED;
|
||||
|
||||
private static int desiredVideoResolution = AUTOMATIC_VIDEO_QUALITY_VALUE;
|
||||
|
||||
private static boolean qualityNeedsUpdating;
|
||||
|
||||
/**
|
||||
* The available qualities of the current video.
|
||||
*/
|
||||
@Nullable
|
||||
private static VideoQuality[] currentQualities;
|
||||
|
||||
/**
|
||||
* The current quality of the video playing.
|
||||
* This is always the actual quality even if Automatic quality is active.
|
||||
*/
|
||||
@Nullable
|
||||
private static VideoQuality currentQuality;
|
||||
|
||||
/**
|
||||
* The current VideoQualityMenuInterface, set during setVideoQuality.
|
||||
*/
|
||||
@Nullable
|
||||
private static VideoQualityMenuInterface currentMenuInterface;
|
||||
|
||||
/**
|
||||
* Callback for when the current quality changes.
|
||||
*/
|
||||
public static final Event<VideoQuality> onQualityChange = new Event<>();
|
||||
|
||||
@Nullable
|
||||
public static VideoQuality[] getCurrentQualities() {
|
||||
return currentQualities;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public static VideoQuality getCurrentQuality() {
|
||||
return currentQuality;
|
||||
}
|
||||
|
||||
/**
|
||||
* Injection point.
|
||||
*
|
||||
@@ -52,12 +114,18 @@ public final class VideoInformation {
|
||||
*/
|
||||
public static void initialize(@NonNull PlaybackController playerController) {
|
||||
try {
|
||||
Logger.printDebug(() -> "newVideoStarted");
|
||||
|
||||
playerControllerRef = new WeakReference<>(Objects.requireNonNull(playerController));
|
||||
videoTime = -1;
|
||||
videoLength = 0;
|
||||
playbackSpeed = DEFAULT_YOUTUBE_PLAYBACK_SPEED;
|
||||
desiredVideoResolution = AUTOMATIC_VIDEO_QUALITY_VALUE;
|
||||
currentQualities = null;
|
||||
currentMenuInterface = null;
|
||||
setCurrentQuality(null);
|
||||
} catch (Exception ex) {
|
||||
Logger.printException(() -> "Failed to initialize", ex);
|
||||
Logger.printException(() -> "initialize failure", ex);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -197,14 +265,14 @@ public final class VideoInformation {
|
||||
if (controller == null) {
|
||||
Logger.printDebug(() -> "Cannot seekTo because player controller is null");
|
||||
} else {
|
||||
if (controller.seekTo(adjustedSeekTime)) return true;
|
||||
if (controller.patch_seekTo(adjustedSeekTime)) return true;
|
||||
Logger.printDebug(() -> "seekTo did not succeeded. Trying MXD.");
|
||||
// Else the video is loading or changing videos, or video is casting to a different device.
|
||||
}
|
||||
|
||||
// Try calling the seekTo method of the MDX player director (called when casting).
|
||||
// The difference has to be a different second mark in order to avoid infinite skip loops
|
||||
// as the Lounge API only supports seconds.
|
||||
// as the Lounge API only supports whole seconds.
|
||||
if (adjustedSeekTime / 1000 == videoTime / 1000) {
|
||||
Logger.printDebug(() -> "Skipping seekTo for MDX because seek time is too small "
|
||||
+ "(" + (adjustedSeekTime - videoTime) + "ms)");
|
||||
@@ -217,9 +285,9 @@ public final class VideoInformation {
|
||||
return false;
|
||||
}
|
||||
|
||||
return controller.seekTo(adjustedSeekTime);
|
||||
return controller.patch_seekTo(adjustedSeekTime);
|
||||
} catch (Exception ex) {
|
||||
Logger.printException(() -> "Failed to seek", ex);
|
||||
Logger.printException(() -> "seekTo failure", ex);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -239,7 +307,7 @@ public final class VideoInformation {
|
||||
if (controller == null) {
|
||||
Logger.printDebug(() -> "Cannot seek relative as player controller is null");
|
||||
} else {
|
||||
controller.seekToRelative(seekTime);
|
||||
controller.patch_seekToRelative(seekTime);
|
||||
}
|
||||
|
||||
// Adjust the fine adjustment function so it's at least 1 second before/after.
|
||||
@@ -255,10 +323,10 @@ public final class VideoInformation {
|
||||
if (controller == null) {
|
||||
Logger.printDebug(() -> "Cannot seek relative as MXD player controller is null");
|
||||
} else {
|
||||
controller.seekToRelative(adjustedSeekTime);
|
||||
controller.patch_seekToRelative(adjustedSeekTime);
|
||||
}
|
||||
} catch (Exception ex) {
|
||||
Logger.printException(() -> "Failed to seek relative", ex);
|
||||
Logger.printException(() -> "seekToRelative failure", ex);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -373,4 +441,123 @@ public final class VideoInformation {
|
||||
playbackSpeed = newlyLoadedPlaybackSpeed;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param resolution The desired video quality resolution to use.
|
||||
*/
|
||||
public static void setDesiredVideoResolution(int resolution) {
|
||||
Utils.verifyOnMainThread();
|
||||
Logger.printDebug(() -> "Setting desired video resolution: " + resolution);
|
||||
desiredVideoResolution = resolution;
|
||||
qualityNeedsUpdating = true;
|
||||
}
|
||||
|
||||
private static void setCurrentQuality(@Nullable VideoQuality quality) {
|
||||
Utils.verifyOnMainThread();
|
||||
if (currentQuality != quality) {
|
||||
Logger.printDebug(() -> "Current quality changed to: " + quality);
|
||||
currentQuality = quality;
|
||||
onQualityChange.invoke(quality);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Forcefully changes the video quality of the currently playing video.
|
||||
*/
|
||||
public static void changeQuality(VideoQuality quality) {
|
||||
Utils.verifyOnMainThread();
|
||||
|
||||
if (currentMenuInterface == null) {
|
||||
Logger.printException(() -> "Cannot change quality, menu interface is null");
|
||||
return;
|
||||
}
|
||||
currentMenuInterface.patch_setQuality(quality);
|
||||
}
|
||||
|
||||
/**
|
||||
* Injection point. Fixes bad data used by YouTube.
|
||||
*/
|
||||
public static int fixVideoQualityResolution(String name, int quality) {
|
||||
final int correctQuality = 480;
|
||||
if (name.equals("480p") && quality != correctQuality) {
|
||||
return correctQuality;
|
||||
}
|
||||
|
||||
return quality;
|
||||
}
|
||||
|
||||
/**
|
||||
* Injection point.
|
||||
*
|
||||
* @param qualities Video qualities available, ordered from largest to smallest, with index 0 being the 'automatic' value of -2
|
||||
* @param originalQualityIndex quality index to use, as chosen by YouTube
|
||||
*/
|
||||
public static int setVideoQuality(VideoQuality[] qualities, VideoQualityMenuInterface menu, int originalQualityIndex) {
|
||||
try {
|
||||
Utils.verifyOnMainThread();
|
||||
currentMenuInterface = menu;
|
||||
|
||||
final boolean availableQualitiesChanged = (currentQualities == null)
|
||||
|| !Arrays.equals(currentQualities, qualities);
|
||||
if (availableQualitiesChanged) {
|
||||
currentQualities = qualities;
|
||||
Logger.printDebug(() -> "VideoQualities: " + Arrays.toString(currentQualities));
|
||||
}
|
||||
|
||||
VideoQuality updatedCurrentQuality = qualities[originalQualityIndex];
|
||||
if (updatedCurrentQuality.patch_getResolution() != AUTOMATIC_VIDEO_QUALITY_VALUE
|
||||
&& (currentQuality == null || currentQuality != updatedCurrentQuality)) {
|
||||
setCurrentQuality(updatedCurrentQuality);
|
||||
}
|
||||
|
||||
final int preferredQuality = desiredVideoResolution;
|
||||
if (preferredQuality == AUTOMATIC_VIDEO_QUALITY_VALUE) {
|
||||
return originalQualityIndex; // Nothing to do.
|
||||
}
|
||||
|
||||
// After changing videos the qualities can initially be for the prior video.
|
||||
// If the qualities have changed and the default is not auto then an update is needed.
|
||||
if (qualityNeedsUpdating) {
|
||||
qualityNeedsUpdating = false;
|
||||
} else if (!availableQualitiesChanged) {
|
||||
return originalQualityIndex;
|
||||
}
|
||||
|
||||
// Find the highest quality that is equal to or less than the preferred.
|
||||
int i = 0;
|
||||
final int lastQualityIndex = qualities.length - 1;
|
||||
for (VideoQuality quality : qualities) {
|
||||
final int qualityResolution = quality.patch_getResolution();
|
||||
if ((qualityResolution != AUTOMATIC_VIDEO_QUALITY_VALUE && qualityResolution <= preferredQuality)
|
||||
// Use the lowest video quality if the default is lower than all available.
|
||||
|| i == lastQualityIndex) {
|
||||
final boolean qualityNeedsChange = (i != originalQualityIndex);
|
||||
Logger.printDebug(() -> qualityNeedsChange
|
||||
? "Changing video quality from: " + updatedCurrentQuality + " to: " + quality
|
||||
: "Video is already the preferred quality: " + quality
|
||||
);
|
||||
|
||||
// On first load of a new regular video, if the video is already the
|
||||
// desired quality then the quality flyout will show 'Auto' (ie: Auto (720p)).
|
||||
//
|
||||
// To prevent user confusion, set the video index even if the
|
||||
// quality is already correct so the UI picker will not display "Auto".
|
||||
//
|
||||
// Only change Shorts quality if the quality actually needs to change,
|
||||
// because the "auto" option is not shown in the flyout
|
||||
// and setting the same quality again can cause the Short to restart.
|
||||
if (qualityNeedsChange || !ShortsPlayerState.isOpen()) {
|
||||
changeQuality(quality);
|
||||
return i;
|
||||
}
|
||||
|
||||
return originalQualityIndex;
|
||||
}
|
||||
i++;
|
||||
}
|
||||
} catch (Exception ex) {
|
||||
Logger.printException(() -> "setVideoQuality failure", ex);
|
||||
}
|
||||
return originalQualityIndex;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -32,6 +32,7 @@ public final class LayoutComponentsFilter extends Filter {
|
||||
);
|
||||
|
||||
private final StringTrieSearch exceptions = new StringTrieSearch();
|
||||
private final StringFilterGroup communityPosts;
|
||||
private final StringFilterGroup surveys;
|
||||
private final StringFilterGroup notifyMe;
|
||||
private final StringFilterGroup singleItemInformationPanel;
|
||||
@@ -68,7 +69,7 @@ public final class LayoutComponentsFilter extends Filter {
|
||||
|
||||
// Paths.
|
||||
|
||||
final var communityPosts = new StringFilterGroup(
|
||||
communityPosts = new StringFilterGroup(
|
||||
Settings.HIDE_COMMUNITY_POSTS,
|
||||
"post_base_wrapper", // may be obsolete and no longer needed.
|
||||
"text_post_root.eml",
|
||||
@@ -325,6 +326,12 @@ public final class LayoutComponentsFilter extends Filter {
|
||||
return channelProfileBuffer.check(buffer).isFiltered();
|
||||
}
|
||||
|
||||
if (matchedGroup == communityPosts && NavigationBar.isBackButtonVisible()) {
|
||||
// Allow community posts on channel profile page,
|
||||
// or if viewing an individual channel in the feed.
|
||||
return false;
|
||||
}
|
||||
|
||||
if (exceptions.matches(path)) return false; // Exceptions are not filtered.
|
||||
|
||||
if (matchedGroup == compactChannelBarInner) {
|
||||
|
||||
@@ -3,13 +3,8 @@ package app.revanced.extension.youtube.patches.playback.quality;
|
||||
import static app.revanced.extension.shared.StringRef.str;
|
||||
import static app.revanced.extension.shared.Utils.NetworkType;
|
||||
|
||||
import androidx.annotation.Nullable;
|
||||
|
||||
import com.google.android.libraries.youtube.innertube.model.media.VideoQuality;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
import app.revanced.extension.shared.Logger;
|
||||
import app.revanced.extension.shared.Utils;
|
||||
import app.revanced.extension.shared.settings.BooleanSetting;
|
||||
@@ -17,69 +12,15 @@ import app.revanced.extension.shared.settings.IntegerSetting;
|
||||
import app.revanced.extension.youtube.patches.VideoInformation;
|
||||
import app.revanced.extension.youtube.settings.Settings;
|
||||
import app.revanced.extension.youtube.shared.ShortsPlayerState;
|
||||
import app.revanced.extension.youtube.videoplayer.VideoQualityDialogButton;
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
public class RememberVideoQualityPatch {
|
||||
|
||||
/**
|
||||
* Interface to use obfuscated methods.
|
||||
*/
|
||||
public interface VideoQualityMenuInterface {
|
||||
void patch_setQuality(VideoQuality quality);
|
||||
}
|
||||
|
||||
/**
|
||||
* Video resolution of the automatic quality option..
|
||||
*/
|
||||
public static final int AUTOMATIC_VIDEO_QUALITY_VALUE = -2;
|
||||
|
||||
/**
|
||||
* All quality names are the same for all languages.
|
||||
* VideoQuality also has a resolution enum that can be used if needed.
|
||||
*/
|
||||
public static final String VIDEO_QUALITY_1080P_PREMIUM_NAME = "1080p Premium";
|
||||
|
||||
private static final IntegerSetting videoQualityWifi = Settings.VIDEO_QUALITY_DEFAULT_WIFI;
|
||||
private static final IntegerSetting videoQualityMobile = Settings.VIDEO_QUALITY_DEFAULT_MOBILE;
|
||||
private static final IntegerSetting shortsQualityWifi = Settings.SHORTS_QUALITY_DEFAULT_WIFI;
|
||||
private static final IntegerSetting shortsQualityMobile = Settings.SHORTS_QUALITY_DEFAULT_MOBILE;
|
||||
|
||||
private static boolean qualityNeedsUpdating;
|
||||
|
||||
/**
|
||||
* The available qualities of the current video.
|
||||
*/
|
||||
@Nullable
|
||||
private static List<VideoQuality> currentQualities;
|
||||
|
||||
/**
|
||||
* The current quality of the video playing.
|
||||
* This is always the actual quality even if Automatic quality is active.
|
||||
*/
|
||||
@Nullable
|
||||
private static VideoQuality currentQuality;
|
||||
|
||||
/**
|
||||
* The current VideoQualityMenuInterface, set during setVideoQuality.
|
||||
*/
|
||||
@Nullable
|
||||
private static VideoQualityMenuInterface currentMenuInterface;
|
||||
|
||||
@Nullable
|
||||
public static List<VideoQuality> getCurrentQualities() {
|
||||
return currentQualities;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public static VideoQuality getCurrentQuality() {
|
||||
return currentQuality;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public static VideoQualityMenuInterface getCurrentMenuInterface() {
|
||||
return currentMenuInterface;
|
||||
}
|
||||
|
||||
public static boolean shouldRememberVideoQuality() {
|
||||
BooleanSetting preference = ShortsPlayerState.isOpen()
|
||||
@@ -129,91 +70,17 @@ public class RememberVideoQualityPatch {
|
||||
|
||||
/**
|
||||
* Injection point.
|
||||
*
|
||||
* @param qualities Video qualities available, ordered from largest to smallest, with index 0 being the 'automatic' value of -2
|
||||
* @param originalQualityIndex quality index to use, as chosen by YouTube
|
||||
*/
|
||||
public static int setVideoQuality(VideoQuality[] qualities, VideoQualityMenuInterface menu, int originalQualityIndex) {
|
||||
try {
|
||||
Utils.verifyOnMainThread();
|
||||
currentMenuInterface = menu;
|
||||
|
||||
final boolean availableQualitiesChanged = currentQualities == null
|
||||
|| currentQualities.size() != qualities.length;
|
||||
if (availableQualitiesChanged) {
|
||||
currentQualities = Arrays.asList(qualities);
|
||||
Logger.printDebug(() -> "VideoQualities: " + currentQualities);
|
||||
}
|
||||
|
||||
VideoQuality updatedCurrentQuality = qualities[originalQualityIndex];
|
||||
if (updatedCurrentQuality.patch_getResolution() != AUTOMATIC_VIDEO_QUALITY_VALUE &&
|
||||
(currentQuality == null
|
||||
|| !currentQuality.patch_getQualityName().equals(updatedCurrentQuality.patch_getQualityName()))) {
|
||||
currentQuality = updatedCurrentQuality;
|
||||
Logger.printDebug(() -> "Current quality changed to: " + updatedCurrentQuality);
|
||||
|
||||
VideoQualityDialogButton.updateButtonIcon(updatedCurrentQuality);
|
||||
}
|
||||
|
||||
final int preferredQuality = getDefaultQualityResolution();
|
||||
if (preferredQuality == AUTOMATIC_VIDEO_QUALITY_VALUE) {
|
||||
return originalQualityIndex; // Nothing to do.
|
||||
}
|
||||
|
||||
// After changing videos the qualities can initially be for the prior video.
|
||||
// If the qualities have changed and the default is not auto then an update is needed.
|
||||
if (!qualityNeedsUpdating && !availableQualitiesChanged) {
|
||||
return originalQualityIndex;
|
||||
}
|
||||
qualityNeedsUpdating = false;
|
||||
|
||||
// Find the highest quality that is equal to or less than the preferred.
|
||||
int i = 0;
|
||||
for (VideoQuality quality : qualities) {
|
||||
final int qualityResolution = quality.patch_getResolution();
|
||||
if (qualityResolution != AUTOMATIC_VIDEO_QUALITY_VALUE && qualityResolution <= preferredQuality) {
|
||||
final boolean qualityNeedsChange = (i != originalQualityIndex);
|
||||
Logger.printDebug(() -> qualityNeedsChange
|
||||
? "Changing video quality from: " + updatedCurrentQuality + " to: " + quality
|
||||
: "Video is already the preferred quality: " + quality
|
||||
);
|
||||
|
||||
// On first load of a new regular video, if the video is already the
|
||||
// desired quality then the quality flyout will show 'Auto' (ie: Auto (720p)).
|
||||
//
|
||||
// To prevent user confusion, set the video index even if the
|
||||
// quality is already correct so the UI picker will not display "Auto".
|
||||
//
|
||||
// Only change Shorts quality if the quality actually needs to change,
|
||||
// because the "auto" option is not shown in the flyout
|
||||
// and setting the same quality again can cause the Short to restart.
|
||||
if (qualityNeedsChange || !ShortsPlayerState.isOpen()) {
|
||||
menu.patch_setQuality(qualities[i]);
|
||||
return i;
|
||||
}
|
||||
|
||||
return originalQualityIndex;
|
||||
}
|
||||
i++;
|
||||
}
|
||||
} catch (Exception ex) {
|
||||
Logger.printException(() -> "setVideoQuality failure", ex);
|
||||
}
|
||||
return originalQualityIndex;
|
||||
}
|
||||
|
||||
/**
|
||||
* Injection point.
|
||||
* @param userSelectedQualityIndex Element index of {@link #currentQualities}.
|
||||
* @param userSelectedQualityIndex Element index of {@link VideoInformation#getCurrentQualities()}.
|
||||
*/
|
||||
public static void userChangedShortsQuality(int userSelectedQualityIndex) {
|
||||
try {
|
||||
if (shouldRememberVideoQuality()) {
|
||||
VideoQuality[] currentQualities = VideoInformation.getCurrentQualities();
|
||||
if (currentQualities == null) {
|
||||
Logger.printDebug(() -> "Cannot save default quality, qualities is null");
|
||||
return;
|
||||
}
|
||||
VideoQuality quality = currentQualities.get(userSelectedQualityIndex);
|
||||
VideoQuality quality = currentQualities[userSelectedQualityIndex];
|
||||
saveDefaultQuality(quality.patch_getResolution());
|
||||
}
|
||||
} catch (Exception ex) {
|
||||
@@ -227,6 +94,7 @@ public class RememberVideoQualityPatch {
|
||||
*/
|
||||
public static void userChangedQuality(int videoResolution) {
|
||||
Utils.verifyOnMainThread();
|
||||
Logger.printDebug(() -> "User changed quality to: " + videoResolution);
|
||||
|
||||
if (shouldRememberVideoQuality()) {
|
||||
saveDefaultQuality(videoResolution);
|
||||
@@ -237,27 +105,6 @@ public class RememberVideoQualityPatch {
|
||||
* Injection point.
|
||||
*/
|
||||
public static void newVideoStarted(VideoInformation.PlaybackController ignoredPlayerController) {
|
||||
Utils.verifyOnMainThread();
|
||||
|
||||
Logger.printDebug(() -> "newVideoStarted");
|
||||
currentQualities = null;
|
||||
currentQuality = null;
|
||||
currentMenuInterface = null;
|
||||
qualityNeedsUpdating = true;
|
||||
|
||||
// Hide the quality button until playback starts and the qualities are available.
|
||||
VideoQualityDialogButton.updateButtonIcon(null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Injection point. Fixes bad data used by YouTube.
|
||||
*/
|
||||
public static int fixVideoQualityResolution(String name, int quality) {
|
||||
final int correctQuality = 480;
|
||||
if (name.equals("480p") && quality != correctQuality) {
|
||||
return correctQuality;
|
||||
}
|
||||
|
||||
return quality;
|
||||
VideoInformation.setDesiredVideoResolution(getDefaultQualityResolution());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -100,7 +100,8 @@ public class CustomPlaybackSpeedPatch {
|
||||
private static WeakReference<Dialog> currentDialog = new WeakReference<>(null);
|
||||
|
||||
static {
|
||||
// Cap at 2 decimals (rounds automatically).
|
||||
// Use same 2 digit format as built in speed picker,
|
||||
speedFormatter.setMinimumFractionDigits(2);
|
||||
speedFormatter.setMaximumFractionDigits(2);
|
||||
|
||||
final float holdSpeed = Settings.SPEED_TAP_AND_HOLD.get();
|
||||
@@ -321,7 +322,7 @@ public class CustomPlaybackSpeedPatch {
|
||||
TextView currentSpeedText = new TextView(context);
|
||||
float currentSpeed = VideoInformation.getPlaybackSpeed();
|
||||
// Initially show with only 0 minimum digits, so 1.0 shows as 1x
|
||||
currentSpeedText.setText(formatSpeedStringX(currentSpeed, 0));
|
||||
currentSpeedText.setText(formatSpeedStringX(currentSpeed));
|
||||
currentSpeedText.setTextColor(Utils.getAppForegroundColor());
|
||||
currentSpeedText.setTextSize(16);
|
||||
currentSpeedText.setTypeface(Typeface.DEFAULT_BOLD);
|
||||
@@ -398,10 +399,11 @@ public class CustomPlaybackSpeedPatch {
|
||||
return null;
|
||||
}
|
||||
|
||||
VideoInformation.overridePlaybackSpeed(roundedSpeed);
|
||||
RememberPlaybackSpeedPatch.userSelectedPlaybackSpeed(roundedSpeed);
|
||||
currentSpeedText.setText(formatSpeedStringX(roundedSpeed, 2)); // Update display.
|
||||
currentSpeedText.setText(formatSpeedStringX(roundedSpeed)); // Update display.
|
||||
speedSlider.setProgress(speedToProgressValue(roundedSpeed)); // Update slider.
|
||||
|
||||
RememberPlaybackSpeedPatch.userSelectedPlaybackSpeed(roundedSpeed);
|
||||
VideoInformation.overridePlaybackSpeed(roundedSpeed);
|
||||
return null;
|
||||
};
|
||||
|
||||
@@ -437,7 +439,7 @@ public class CustomPlaybackSpeedPatch {
|
||||
gridParams.setMargins(0, 0, 0, 0); // No margins around GridLayout.
|
||||
gridLayout.setLayoutParams(gridParams);
|
||||
|
||||
// For all buttons show at least 1 zero in decimal (2 -> "2.0").
|
||||
// For button use 1 digit minimum.
|
||||
speedFormatter.setMinimumFractionDigits(1);
|
||||
|
||||
// Add buttons for each preset playback speed.
|
||||
@@ -455,7 +457,7 @@ public class CustomPlaybackSpeedPatch {
|
||||
|
||||
// Create speed button.
|
||||
Button speedButton = new Button(context, null, 0);
|
||||
speedButton.setText(speedFormatter.format(speed)); // Do not use 'x' speed format.
|
||||
speedButton.setText(speedFormatter.format(speed));
|
||||
speedButton.setTextColor(Utils.getAppForegroundColor());
|
||||
speedButton.setTextSize(12);
|
||||
speedButton.setAllCaps(false);
|
||||
@@ -498,6 +500,9 @@ public class CustomPlaybackSpeedPatch {
|
||||
gridLayout.addView(buttonContainer);
|
||||
}
|
||||
|
||||
// Restore 2 digit minimum.
|
||||
speedFormatter.setMinimumFractionDigits(2);
|
||||
|
||||
// Add in-rows speed buttons layout to main layout.
|
||||
mainLayout.addView(gridLayout);
|
||||
|
||||
@@ -631,8 +636,7 @@ public class CustomPlaybackSpeedPatch {
|
||||
* @param speed The playback speed value to format.
|
||||
* @return A string representation of the speed with 'x' (e.g. "1.25x" or "1.00x").
|
||||
*/
|
||||
private static String formatSpeedStringX(float speed, int minimumFractionDigits) {
|
||||
speedFormatter.setMinimumFractionDigits(minimumFractionDigits);
|
||||
private static String formatSpeedStringX(float speed) {
|
||||
return speedFormatter.format(speed) + 'x';
|
||||
}
|
||||
|
||||
|
||||
@@ -26,6 +26,7 @@ public class CreateSegmentButton {
|
||||
controlsView,
|
||||
"revanced_sb_create_segment_button",
|
||||
null,
|
||||
null,
|
||||
CreateSegmentButton::shouldBeShown,
|
||||
v -> SponsorBlockViewController.toggleNewSegmentLayoutVisibility(),
|
||||
null
|
||||
|
||||
@@ -28,6 +28,7 @@ public class VotingButton {
|
||||
controlsView,
|
||||
"revanced_sb_voting_button",
|
||||
null,
|
||||
null,
|
||||
VotingButton::shouldBeShown,
|
||||
v -> SponsorBlockUtils.onVotingClicked(v.getContext()),
|
||||
null
|
||||
|
||||
@@ -7,7 +7,6 @@ import androidx.annotation.Nullable;
|
||||
import app.revanced.extension.shared.Logger;
|
||||
import app.revanced.extension.youtube.patches.CopyVideoUrlPatch;
|
||||
import app.revanced.extension.youtube.settings.Settings;
|
||||
import app.revanced.extension.youtube.shared.PlayerType;
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
public class CopyVideoUrlButton {
|
||||
@@ -23,6 +22,7 @@ public class CopyVideoUrlButton {
|
||||
controlsView,
|
||||
"revanced_copy_video_url_button",
|
||||
"revanced_copy_video_url_button_placeholder",
|
||||
null,
|
||||
Settings.COPY_VIDEO_URL::get,
|
||||
view -> CopyVideoUrlPatch.copyUrl(false),
|
||||
view -> {
|
||||
|
||||
@@ -7,7 +7,6 @@ import androidx.annotation.Nullable;
|
||||
import app.revanced.extension.shared.Logger;
|
||||
import app.revanced.extension.youtube.patches.CopyVideoUrlPatch;
|
||||
import app.revanced.extension.youtube.settings.Settings;
|
||||
import app.revanced.extension.youtube.shared.PlayerType;
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
public class CopyVideoUrlTimestampButton {
|
||||
@@ -23,6 +22,7 @@ public class CopyVideoUrlTimestampButton {
|
||||
controlsView,
|
||||
"revanced_copy_video_url_timestamp_button",
|
||||
"revanced_copy_video_url_timestamp_button_placeholder",
|
||||
null,
|
||||
Settings.COPY_VIDEO_URL_TIMESTAMP::get,
|
||||
view -> CopyVideoUrlPatch.copyUrl(true),
|
||||
view -> {
|
||||
|
||||
@@ -23,6 +23,7 @@ public class ExternalDownloadButton {
|
||||
controlsView,
|
||||
"revanced_external_download_button",
|
||||
"revanced_external_download_button_placeholder",
|
||||
null,
|
||||
Settings.EXTERNAL_DOWNLOADER::get,
|
||||
ExternalDownloadButton::onDownloadClick,
|
||||
null
|
||||
|
||||
@@ -4,16 +4,26 @@ import android.view.View;
|
||||
|
||||
import androidx.annotation.Nullable;
|
||||
|
||||
import java.text.DecimalFormat;
|
||||
|
||||
import app.revanced.extension.shared.Logger;
|
||||
import app.revanced.extension.shared.Utils;
|
||||
import app.revanced.extension.youtube.patches.VideoInformation;
|
||||
import app.revanced.extension.youtube.patches.playback.speed.CustomPlaybackSpeedPatch;
|
||||
import app.revanced.extension.youtube.settings.Settings;
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
public class PlaybackSpeedDialogButton {
|
||||
|
||||
@Nullable
|
||||
private static PlayerControlButton instance;
|
||||
|
||||
private static final DecimalFormat speedDecimalFormatter = new DecimalFormat();
|
||||
static {
|
||||
speedDecimalFormatter.setMinimumFractionDigits(1);
|
||||
speedDecimalFormatter.setMaximumFractionDigits(2);
|
||||
}
|
||||
|
||||
/**
|
||||
* Injection point.
|
||||
*/
|
||||
@@ -21,8 +31,10 @@ public class PlaybackSpeedDialogButton {
|
||||
try {
|
||||
instance = new PlayerControlButton(
|
||||
controlsView,
|
||||
"revanced_playback_speed_dialog_button_container",
|
||||
"revanced_playback_speed_dialog_button",
|
||||
"revanced_playback_speed_dialog_button_placeholder",
|
||||
"revanced_playback_speed_dialog_button_text",
|
||||
Settings.PLAYBACK_SPEED_DIALOG_BUTTON::get,
|
||||
view -> {
|
||||
try {
|
||||
@@ -37,11 +49,11 @@ public class PlaybackSpeedDialogButton {
|
||||
},
|
||||
view -> {
|
||||
try {
|
||||
final float defaultSpeed = Settings.PLAYBACK_SPEED_DEFAULT.get();
|
||||
final float speed = (!Settings.REMEMBER_PLAYBACK_SPEED_LAST_SELECTED.get() ||
|
||||
VideoInformation.getPlaybackSpeed() == Settings.PLAYBACK_SPEED_DEFAULT.get())
|
||||
VideoInformation.getPlaybackSpeed() == defaultSpeed)
|
||||
? 1.0f
|
||||
: Settings.PLAYBACK_SPEED_DEFAULT.get();
|
||||
|
||||
: defaultSpeed;
|
||||
VideoInformation.overridePlaybackSpeed(speed);
|
||||
} catch (Exception ex) {
|
||||
Logger.printException(() -> "speed button reset failure", ex);
|
||||
@@ -49,22 +61,53 @@ public class PlaybackSpeedDialogButton {
|
||||
return true;
|
||||
}
|
||||
);
|
||||
|
||||
// Set the appropriate icon.
|
||||
updateButtonAppearance();
|
||||
} catch (Exception ex) {
|
||||
Logger.printException(() -> "initializeButton failure", ex);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* injection point
|
||||
* Injection point.
|
||||
*/
|
||||
public static void setVisibilityImmediate(boolean visible) {
|
||||
if (instance != null) instance.setVisibilityImmediate(visible);
|
||||
if (instance != null) {
|
||||
instance.setVisibilityImmediate(visible);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* injection point
|
||||
* Injection point.
|
||||
*/
|
||||
public static void setVisibility(boolean visible, boolean animated) {
|
||||
if (instance != null) instance.setVisibility(visible, animated);
|
||||
if (instance != null) {
|
||||
instance.setVisibility(visible, animated);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Injection point.
|
||||
*/
|
||||
public static void videoSpeedChanged(float currentVideoSpeed) {
|
||||
updateButtonAppearance();
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates the button's appearance, including icon and text overlay.
|
||||
*/
|
||||
private static void updateButtonAppearance() {
|
||||
if (instance == null) return;
|
||||
|
||||
try {
|
||||
Utils.verifyOnMainThread();
|
||||
|
||||
String speedText = speedDecimalFormatter.format(VideoInformation.getPlaybackSpeed());
|
||||
instance.setTextOverlay(speedText);
|
||||
Logger.printDebug(() -> "Updated playback speed button text to: " + speedText);
|
||||
} catch (Exception ex) {
|
||||
Logger.printException(() -> "updateButtonAppearance failure", ex);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,6 +3,7 @@ package app.revanced.extension.youtube.videoplayer;
|
||||
import android.view.View;
|
||||
import android.view.animation.Animation;
|
||||
import android.widget.ImageView;
|
||||
import android.widget.TextView;
|
||||
|
||||
import androidx.annotation.Nullable;
|
||||
|
||||
@@ -14,11 +15,12 @@ import app.revanced.extension.youtube.shared.PlayerType;
|
||||
import kotlin.Unit;
|
||||
|
||||
public class PlayerControlButton {
|
||||
public interface PlayerControlButtonVisibility {
|
||||
|
||||
public interface PlayerControlButtonStatus {
|
||||
/**
|
||||
* @return If the button should be shown when the player overlay is visible.
|
||||
*/
|
||||
boolean shouldBeShown();
|
||||
boolean buttonEnabled();
|
||||
}
|
||||
|
||||
private static final int fadeInDuration;
|
||||
@@ -44,23 +46,46 @@ public class PlayerControlButton {
|
||||
fadeOutImmediate.setDuration(Utils.getResourceInteger("fade_duration_fast"));
|
||||
}
|
||||
|
||||
private final WeakReference<View> containerRef;
|
||||
private final WeakReference<View> buttonRef;
|
||||
/**
|
||||
* Empty view with the same layout size as the button. Used to fill empty space while the
|
||||
* fade out animation runs. Without this the chapter titles overlapping the button when fading out.
|
||||
*/
|
||||
private final WeakReference<View> placeHolderRef;
|
||||
private final PlayerControlButtonVisibility visibilityCheck;
|
||||
private final WeakReference<TextView> textOverlayRef;
|
||||
private final PlayerControlButtonStatus enabledStatus;
|
||||
private boolean isVisible;
|
||||
|
||||
public PlayerControlButton(View controlsViewGroup,
|
||||
String imageViewButtonId,
|
||||
String buttonId,
|
||||
@Nullable String placeholderId,
|
||||
PlayerControlButtonVisibility buttonVisibility,
|
||||
@Nullable String textOverlayId,
|
||||
PlayerControlButtonStatus enabledStatus,
|
||||
View.OnClickListener onClickListener,
|
||||
@Nullable View.OnLongClickListener longClickListener) {
|
||||
ImageView imageView = Utils.getChildViewByResourceName(controlsViewGroup, imageViewButtonId);
|
||||
imageView.setVisibility(View.GONE);
|
||||
this(controlsViewGroup, buttonId, buttonId, placeholderId, textOverlayId,
|
||||
enabledStatus, onClickListener, longClickListener);
|
||||
}
|
||||
|
||||
public PlayerControlButton(View controlsViewGroup,
|
||||
String viewToHide,
|
||||
String buttonId,
|
||||
@Nullable String placeholderId,
|
||||
@Nullable String textOverlayId,
|
||||
PlayerControlButtonStatus enabledStatus,
|
||||
View.OnClickListener onClickListener,
|
||||
@Nullable View.OnLongClickListener longClickListener) {
|
||||
View containerView = Utils.getChildViewByResourceName(controlsViewGroup, viewToHide);
|
||||
containerView.setVisibility(View.GONE);
|
||||
containerRef = new WeakReference<>(containerView);
|
||||
|
||||
View button = Utils.getChildViewByResourceName(controlsViewGroup, buttonId);
|
||||
button.setOnClickListener(onClickListener);
|
||||
if (longClickListener != null) {
|
||||
button.setOnLongClickListener(longClickListener);
|
||||
}
|
||||
buttonRef = new WeakReference<>(button);
|
||||
|
||||
View tempPlaceholder = null;
|
||||
if (placeholderId != null) {
|
||||
@@ -69,19 +94,19 @@ public class PlayerControlButton {
|
||||
}
|
||||
placeHolderRef = new WeakReference<>(tempPlaceholder);
|
||||
|
||||
imageView.setOnClickListener(onClickListener);
|
||||
if (longClickListener != null) {
|
||||
imageView.setOnLongClickListener(longClickListener);
|
||||
TextView tempTextOverlay = null;
|
||||
if (textOverlayId != null) {
|
||||
tempTextOverlay = Utils.getChildViewByResourceName(controlsViewGroup, textOverlayId);
|
||||
}
|
||||
textOverlayRef = new WeakReference<>(tempTextOverlay);
|
||||
|
||||
visibilityCheck = buttonVisibility;
|
||||
buttonRef = new WeakReference<>(imageView);
|
||||
this.enabledStatus = enabledStatus;
|
||||
isVisible = false;
|
||||
|
||||
// Update the visibility after the player type changes.
|
||||
// This ensures that button animations are cleared and their states are updated correctly
|
||||
// when switching between states like minimized, maximized, or fullscreen, preventing
|
||||
// "stuck" animations or incorrect visibility. Without this fix the issue is most noticable
|
||||
// "stuck" animations or incorrect visibility. Without this fix the issue is most noticeable
|
||||
// when maximizing type 3 miniplayer.
|
||||
PlayerType.getOnChange().addObserver((PlayerType type) -> {
|
||||
playerTypeChanged(type);
|
||||
@@ -111,33 +136,33 @@ public class PlayerControlButton {
|
||||
if (isVisible == visible) return;
|
||||
isVisible = visible;
|
||||
|
||||
View button = buttonRef.get();
|
||||
if (button == null) return;
|
||||
View container = containerRef.get();
|
||||
if (container == null) return;
|
||||
|
||||
View placeholder = placeHolderRef.get();
|
||||
final boolean shouldBeShown = visibilityCheck.shouldBeShown();
|
||||
final boolean buttonEnabled = enabledStatus.buttonEnabled();
|
||||
|
||||
if (visible && shouldBeShown) {
|
||||
button.clearAnimation();
|
||||
if (visible && buttonEnabled) {
|
||||
container.clearAnimation();
|
||||
if (animated) {
|
||||
button.startAnimation(PlayerControlButton.fadeInAnimation);
|
||||
container.startAnimation(fadeInAnimation);
|
||||
}
|
||||
button.setVisibility(View.VISIBLE);
|
||||
container.setVisibility(View.VISIBLE);
|
||||
|
||||
if (placeholder != null) {
|
||||
placeholder.setVisibility(View.GONE);
|
||||
}
|
||||
} else {
|
||||
if (button.getVisibility() == View.VISIBLE) {
|
||||
button.clearAnimation();
|
||||
if (container.getVisibility() == View.VISIBLE) {
|
||||
container.clearAnimation();
|
||||
if (animated) {
|
||||
button.startAnimation(PlayerControlButton.fadeOutAnimation);
|
||||
container.startAnimation(fadeOutAnimation);
|
||||
}
|
||||
button.setVisibility(View.GONE);
|
||||
container.setVisibility(View.GONE);
|
||||
}
|
||||
|
||||
if (placeholder != null) {
|
||||
placeholder.setVisibility(shouldBeShown
|
||||
placeholder.setVisibility(buttonEnabled
|
||||
? View.VISIBLE
|
||||
: View.GONE);
|
||||
}
|
||||
@@ -155,36 +180,37 @@ public class PlayerControlButton {
|
||||
return;
|
||||
}
|
||||
|
||||
View button = buttonRef.get();
|
||||
if (button == null) return;
|
||||
View container = containerRef.get();
|
||||
if (container == null) return;
|
||||
|
||||
button.clearAnimation();
|
||||
container.clearAnimation();
|
||||
View placeholder = placeHolderRef.get();
|
||||
|
||||
if (visibilityCheck.shouldBeShown()) {
|
||||
if (enabledStatus.buttonEnabled()) {
|
||||
if (isVisible) {
|
||||
button.setVisibility(View.VISIBLE);
|
||||
container.setVisibility(View.VISIBLE);
|
||||
if (placeholder != null) placeholder.setVisibility(View.GONE);
|
||||
} else {
|
||||
button.setVisibility(View.GONE);
|
||||
container.setVisibility(View.GONE);
|
||||
if (placeholder != null) placeholder.setVisibility(View.VISIBLE);
|
||||
}
|
||||
} else {
|
||||
button.setVisibility(View.GONE);
|
||||
container.setVisibility(View.GONE);
|
||||
if (placeholder != null) placeholder.setVisibility(View.GONE);
|
||||
}
|
||||
}
|
||||
|
||||
public void hide() {
|
||||
Utils.verifyOnMainThread();
|
||||
if (!isVisible) return;
|
||||
|
||||
Utils.verifyOnMainThread();
|
||||
View view = buttonRef.get();
|
||||
View view = containerRef.get();
|
||||
if (view == null) return;
|
||||
view.setVisibility(View.GONE);
|
||||
|
||||
view = placeHolderRef.get();
|
||||
if (view != null) view.setVisibility(View.GONE);
|
||||
View placeHolder = placeHolderRef.get();
|
||||
if (placeHolder != null) view.setVisibility(View.GONE);
|
||||
|
||||
isVisible = false;
|
||||
}
|
||||
|
||||
@@ -193,50 +219,20 @@ public class PlayerControlButton {
|
||||
* @param resourceId Drawable identifier, or zero to hide the icon.
|
||||
*/
|
||||
public void setIcon(int resourceId) {
|
||||
try {
|
||||
View button = buttonRef.get();
|
||||
if (button instanceof ImageView imageButton) {
|
||||
imageButton.setImageResource(resourceId);
|
||||
}
|
||||
} catch (Exception ex) {
|
||||
Logger.printException(() -> "setIcon failure", ex);
|
||||
View button = buttonRef.get();
|
||||
if (button instanceof ImageView imageButton) {
|
||||
imageButton.setImageResource(resourceId);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Starts an animation on the button.
|
||||
* @param animation The animation to apply.
|
||||
* Sets the text to be displayed on the text overlay.
|
||||
* @param text The text to set on the overlay, or null to clear the text.
|
||||
*/
|
||||
public void startAnimation(Animation animation) {
|
||||
try {
|
||||
View button = buttonRef.get();
|
||||
if (button != null) {
|
||||
button.startAnimation(animation);
|
||||
}
|
||||
} catch (Exception ex) {
|
||||
Logger.printException(() -> "startAnimation failure", ex);
|
||||
public void setTextOverlay(CharSequence text) {
|
||||
TextView textOverlay = textOverlayRef.get();
|
||||
if (textOverlay != null) {
|
||||
textOverlay.setText(text);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Clears any animation on the button.
|
||||
*/
|
||||
public void clearAnimation() {
|
||||
try {
|
||||
View button = buttonRef.get();
|
||||
if (button != null) {
|
||||
button.clearAnimation();
|
||||
}
|
||||
} catch (Exception ex) {
|
||||
Logger.printException(() -> "clearAnimation failure", ex);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the View associated with this button.
|
||||
* @return The button View.
|
||||
*/
|
||||
public View getView() {
|
||||
return buttonRef.get();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,9 +2,8 @@ package app.revanced.extension.youtube.videoplayer;
|
||||
|
||||
import static app.revanced.extension.shared.StringRef.str;
|
||||
import static app.revanced.extension.shared.Utils.dipToPixels;
|
||||
import static app.revanced.extension.youtube.patches.playback.quality.RememberVideoQualityPatch.AUTOMATIC_VIDEO_QUALITY_VALUE;
|
||||
import static app.revanced.extension.youtube.patches.playback.quality.RememberVideoQualityPatch.VIDEO_QUALITY_1080P_PREMIUM_NAME;
|
||||
import static app.revanced.extension.youtube.patches.playback.quality.RememberVideoQualityPatch.VideoQualityMenuInterface;
|
||||
import static app.revanced.extension.youtube.patches.VideoInformation.AUTOMATIC_VIDEO_QUALITY_VALUE;
|
||||
import static app.revanced.extension.youtube.patches.VideoInformation.VIDEO_QUALITY_1080P_PREMIUM_NAME;
|
||||
|
||||
import android.app.Dialog;
|
||||
import android.content.Context;
|
||||
@@ -14,6 +13,7 @@ import android.graphics.drawable.shapes.RoundRectShape;
|
||||
import android.text.Spannable;
|
||||
import android.text.SpannableStringBuilder;
|
||||
import android.text.style.ForegroundColorSpan;
|
||||
import android.text.style.UnderlineSpan;
|
||||
import android.view.Gravity;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.MotionEvent;
|
||||
@@ -39,73 +39,25 @@ import java.util.List;
|
||||
|
||||
import app.revanced.extension.shared.Logger;
|
||||
import app.revanced.extension.shared.Utils;
|
||||
import app.revanced.extension.youtube.patches.VideoInformation;
|
||||
import app.revanced.extension.youtube.patches.playback.quality.RememberVideoQualityPatch;
|
||||
import app.revanced.extension.youtube.settings.Settings;
|
||||
import kotlin.Unit;
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
public class VideoQualityDialogButton {
|
||||
|
||||
private static final int DRAWABLE_LD = getDrawableIdentifier("revanced_video_quality_dialog_button_ld");
|
||||
private static final int DRAWABLE_SD = getDrawableIdentifier("revanced_video_quality_dialog_button_sd");
|
||||
private static final int DRAWABLE_HD = getDrawableIdentifier("revanced_video_quality_dialog_button_hd");
|
||||
private static final int DRAWABLE_FHD = getDrawableIdentifier("revanced_video_quality_dialog_button_fhd");
|
||||
private static final int DRAWABLE_FHD_PLUS = getDrawableIdentifier("revanced_video_quality_dialog_button_fhd_plus");
|
||||
private static final int DRAWABLE_QHD = getDrawableIdentifier("revanced_video_quality_dialog_button_qhd");
|
||||
private static final int DRAWABLE_4K = getDrawableIdentifier("revanced_video_quality_dialog_button_4k");
|
||||
private static final int DRAWABLE_UNKNOWN = getDrawableIdentifier("revanced_video_quality_dialog_button_unknown");
|
||||
|
||||
@Nullable
|
||||
private static PlayerControlButton instance;
|
||||
|
||||
/**
|
||||
* The current resource name of the button icon.
|
||||
*/
|
||||
private static int currentIconResource;
|
||||
@Nullable
|
||||
private static CharSequence currentOverlayText;
|
||||
|
||||
private static int getDrawableIdentifier(String resourceName) {
|
||||
final int resourceId = Utils.getResourceIdentifier(resourceName, "drawable");
|
||||
if (resourceId == 0) Logger.printException(() -> "Could not find resource: " + resourceName);
|
||||
return resourceId;
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates the button icon based on the current video quality.
|
||||
*/
|
||||
public static void updateButtonIcon(@Nullable VideoQuality quality) {
|
||||
try {
|
||||
Utils.verifyOnMainThread();
|
||||
if (instance == null) return;
|
||||
|
||||
final int resolution = quality == null
|
||||
? AUTOMATIC_VIDEO_QUALITY_VALUE // Video is still loading.
|
||||
: quality.patch_getResolution();
|
||||
|
||||
final int iconResource = switch (resolution) {
|
||||
case 144, 240, 360 -> DRAWABLE_LD;
|
||||
case 480 -> DRAWABLE_SD;
|
||||
case 720 -> DRAWABLE_HD;
|
||||
case 1080 -> VIDEO_QUALITY_1080P_PREMIUM_NAME.equals(quality.patch_getQualityName())
|
||||
? DRAWABLE_FHD_PLUS
|
||||
: DRAWABLE_FHD;
|
||||
case 1440 -> DRAWABLE_QHD;
|
||||
case 2160 -> DRAWABLE_4K;
|
||||
default -> DRAWABLE_UNKNOWN;
|
||||
};
|
||||
|
||||
if (iconResource != currentIconResource) {
|
||||
currentIconResource = iconResource;
|
||||
|
||||
Utils.runOnMainThreadDelayed(() -> {
|
||||
if (iconResource != currentIconResource) {
|
||||
Logger.printDebug(() -> "Ignoring stale button update to: " + quality);
|
||||
return;
|
||||
}
|
||||
instance.setIcon(iconResource);
|
||||
}, 100);
|
||||
}
|
||||
} catch (Exception ex) {
|
||||
Logger.printException(() -> "updateButtonIcon failure", ex);
|
||||
}
|
||||
static {
|
||||
VideoInformation.onQualityChange.addObserver((@Nullable VideoQuality quality) -> {
|
||||
updateButtonText(quality);
|
||||
return Unit.INSTANCE;
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -115,8 +67,10 @@ public class VideoQualityDialogButton {
|
||||
try {
|
||||
instance = new PlayerControlButton(
|
||||
controlsView,
|
||||
"revanced_video_quality_dialog_button_container",
|
||||
"revanced_video_quality_dialog_button",
|
||||
"revanced_video_quality_dialog_button_placeholder",
|
||||
"revanced_video_quality_dialog_button_text",
|
||||
Settings.VIDEO_QUALITY_DIALOG_BUTTON::get,
|
||||
view -> {
|
||||
try {
|
||||
@@ -127,9 +81,8 @@ public class VideoQualityDialogButton {
|
||||
},
|
||||
view -> {
|
||||
try {
|
||||
List<VideoQuality> qualities = RememberVideoQualityPatch.getCurrentQualities();
|
||||
VideoQualityMenuInterface menu = RememberVideoQualityPatch.getCurrentMenuInterface();
|
||||
if (qualities == null || menu == null) {
|
||||
VideoQuality[] qualities = VideoInformation.getCurrentQualities();
|
||||
if (qualities == null) {
|
||||
Logger.printDebug(() -> "Cannot reset quality, videoQualities is null");
|
||||
return true;
|
||||
}
|
||||
@@ -140,7 +93,7 @@ public class VideoQualityDialogButton {
|
||||
final int resolution = quality.patch_getResolution();
|
||||
if (resolution != AUTOMATIC_VIDEO_QUALITY_VALUE && resolution <= defaultResolution) {
|
||||
Logger.printDebug(() -> "Resetting quality to: " + quality);
|
||||
menu.patch_setQuality(quality);
|
||||
VideoInformation.changeQuality(quality);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -156,8 +109,8 @@ public class VideoQualityDialogButton {
|
||||
}
|
||||
);
|
||||
|
||||
// Set initial icon.
|
||||
updateButtonIcon(RememberVideoQualityPatch.getCurrentQuality());
|
||||
// Set initial text.
|
||||
updateButtonText(VideoInformation.getCurrentQuality());
|
||||
} catch (Exception ex) {
|
||||
Logger.printException(() -> "initializeButton failure", ex);
|
||||
}
|
||||
@@ -181,33 +134,76 @@ public class VideoQualityDialogButton {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates the button text based on the current video quality.
|
||||
*/
|
||||
public static void updateButtonText(@Nullable VideoQuality quality) {
|
||||
try {
|
||||
Utils.verifyOnMainThread();
|
||||
if (instance == null) return;
|
||||
|
||||
final int resolution = quality == null
|
||||
? AUTOMATIC_VIDEO_QUALITY_VALUE // Video is still loading.
|
||||
: quality.patch_getResolution();
|
||||
|
||||
SpannableStringBuilder text = new SpannableStringBuilder();
|
||||
String qualityText = switch (resolution) {
|
||||
case AUTOMATIC_VIDEO_QUALITY_VALUE -> "";
|
||||
case 144, 240, 360 -> "LD";
|
||||
case 480 -> "SD";
|
||||
case 720 -> "HD";
|
||||
case 1080 -> "FHD";
|
||||
case 1440 -> "QHD";
|
||||
case 2160 -> "4K";
|
||||
default -> "?"; // Should never happen.
|
||||
};
|
||||
|
||||
text.append(qualityText);
|
||||
if (resolution == 1080 && VIDEO_QUALITY_1080P_PREMIUM_NAME.equals(quality.patch_getQualityName())) {
|
||||
// Underline the entire "FHD" text for 1080p Premium.
|
||||
text.setSpan(new UnderlineSpan(), 0, qualityText.length(), Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
|
||||
}
|
||||
|
||||
currentOverlayText = text;
|
||||
Utils.runOnMainThreadDelayed(() -> {
|
||||
if (currentOverlayText != text) {
|
||||
Logger.printDebug(() -> "Ignoring stale button text update of: " + text);
|
||||
return;
|
||||
}
|
||||
instance.setTextOverlay(text);
|
||||
}, 100);
|
||||
} catch (Exception ex) {
|
||||
Logger.printException(() -> "updateButtonText failure", ex);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Shows a dialog with available video qualities, excluding Auto, with a title showing the current quality.
|
||||
*/
|
||||
private static void showVideoQualityDialog(Context context) {
|
||||
try {
|
||||
List<VideoQuality> currentQualities = RememberVideoQualityPatch.getCurrentQualities();
|
||||
VideoQuality currentQuality = RememberVideoQualityPatch.getCurrentQuality();
|
||||
VideoQuality[] currentQualities = VideoInformation.getCurrentQualities();
|
||||
VideoQuality currentQuality = VideoInformation.getCurrentQuality();
|
||||
if (currentQualities == null || currentQuality == null) {
|
||||
Logger.printDebug(() -> "Cannot show qualities dialog, videoQualities is null");
|
||||
return;
|
||||
}
|
||||
if (currentQualities.size() < 2) {
|
||||
if (currentQualities.length < 2) {
|
||||
// Should never happen.
|
||||
Logger.printException(() -> "Cannot show qualities dialog, no qualities available");
|
||||
return;
|
||||
}
|
||||
|
||||
VideoQualityMenuInterface menu = RememberVideoQualityPatch.getCurrentMenuInterface();
|
||||
if (menu == null) {
|
||||
Logger.printDebug(() -> "Cannot show qualities dialog, menu is null");
|
||||
return;
|
||||
// -1 adjustment for automatic quality at first index.
|
||||
int listViewSelectedIndex = -1;
|
||||
for (VideoQuality quality : currentQualities) {
|
||||
if (quality.patch_getQualityName().equals(currentQuality.patch_getQualityName())) {
|
||||
break;
|
||||
}
|
||||
listViewSelectedIndex++;
|
||||
}
|
||||
|
||||
// -1 adjustment for automatic quality at first index.
|
||||
final int listViewSelectedIndex = currentQualities.indexOf(currentQuality) - 1;
|
||||
|
||||
List<String> qualityLabels = new ArrayList<>(currentQualities.size() - 1);
|
||||
List<String> qualityLabels = new ArrayList<>(currentQualities.length - 1);
|
||||
for (VideoQuality availableQuality : currentQualities) {
|
||||
if (availableQuality.patch_getResolution() != AUTOMATIC_VIDEO_QUALITY_VALUE) {
|
||||
qualityLabels.add(availableQuality.patch_getQualityName());
|
||||
@@ -310,16 +306,9 @@ public class VideoQualityDialogButton {
|
||||
listView.setOnItemClickListener((parent, view, which, id) -> {
|
||||
try {
|
||||
final int originalIndex = which + 1; // Adjust for automatic.
|
||||
VideoQuality selectedQuality = currentQualities.get(originalIndex);
|
||||
Logger.printDebug(() -> "User clicked on quality: " + selectedQuality);
|
||||
|
||||
if (RememberVideoQualityPatch.shouldRememberVideoQuality()) {
|
||||
RememberVideoQualityPatch.saveDefaultQuality(selectedQuality.patch_getResolution());
|
||||
}
|
||||
// Don't update button icon now. Icon will update when the actual
|
||||
// quality is changed by YT. This is needed to ensure the icon is correct
|
||||
// if YT ignores changing from 1080p Premium to regular 1080p.
|
||||
menu.patch_setQuality(selectedQuality);
|
||||
VideoQuality selectedQuality = currentQualities[originalIndex];
|
||||
RememberVideoQualityPatch.userChangedQuality(selectedQuality.patch_getResolution());
|
||||
VideoInformation.changeQuality(selectedQuality);
|
||||
|
||||
dialog.dismiss();
|
||||
} catch (Exception ex) {
|
||||
@@ -350,9 +339,12 @@ public class VideoQualityDialogButton {
|
||||
portraitWidth = Math.min(
|
||||
portraitWidth,
|
||||
context.getResources().getDisplayMetrics().heightPixels);
|
||||
// Limit height in landscape mode.
|
||||
params.height = Utils.percentageHeightToPixels(80);
|
||||
} else {
|
||||
params.height = WindowManager.LayoutParams.WRAP_CONTENT;
|
||||
}
|
||||
params.width = portraitWidth;
|
||||
params.height = WindowManager.LayoutParams.WRAP_CONTENT;
|
||||
window.setAttributes(params);
|
||||
window.setBackgroundDrawable(null);
|
||||
}
|
||||
@@ -422,6 +414,15 @@ public class VideoQualityDialogButton {
|
||||
}
|
||||
|
||||
private static class CustomQualityAdapter extends ArrayAdapter<String> {
|
||||
private static final int CUSTOM_LIST_ITEM_CHECKED_ID = Utils.getResourceIdentifier(
|
||||
"revanced_custom_list_item_checked", "layout");
|
||||
private static final int CHECK_ICON_ID = Utils.getResourceIdentifier(
|
||||
"revanced_check_icon", "id");
|
||||
private static final int CHECK_ICON_PLACEHOLDER_ID = Utils.getResourceIdentifier(
|
||||
"revanced_check_icon_placeholder", "id");
|
||||
private static final int ITEM_TEXT_ID = Utils.getResourceIdentifier(
|
||||
"revanced_item_text", "id");
|
||||
|
||||
private int selectedPosition = -1;
|
||||
|
||||
public CustomQualityAdapter(@NonNull Context context, @NonNull List<String> objects) {
|
||||
@@ -440,20 +441,14 @@ public class VideoQualityDialogButton {
|
||||
|
||||
if (convertView == null) {
|
||||
convertView = LayoutInflater.from(getContext()).inflate(
|
||||
Utils.getResourceIdentifier("revanced_custom_list_item_checked", "layout"),
|
||||
CUSTOM_LIST_ITEM_CHECKED_ID,
|
||||
parent,
|
||||
false
|
||||
);
|
||||
viewHolder = new ViewHolder();
|
||||
viewHolder.checkIcon = convertView.findViewById(
|
||||
Utils.getResourceIdentifier("revanced_check_icon", "id")
|
||||
);
|
||||
viewHolder.placeholder = convertView.findViewById(
|
||||
Utils.getResourceIdentifier("revanced_check_icon_placeholder", "id")
|
||||
);
|
||||
viewHolder.textView = convertView.findViewById(
|
||||
Utils.getResourceIdentifier("revanced_item_text", "id")
|
||||
);
|
||||
viewHolder.checkIcon = convertView.findViewById(CHECK_ICON_ID);
|
||||
viewHolder.placeholder = convertView.findViewById(CHECK_ICON_PLACEHOLDER_ID);
|
||||
viewHolder.textView = convertView.findViewById(ITEM_TEXT_ID);
|
||||
convertView.setTag(viewHolder);
|
||||
} else {
|
||||
viewHolder = (ViewHolder) convertView.getTag();
|
||||
|
||||
@@ -3,4 +3,4 @@ org.gradle.jvmargs = -Xms512M -Xmx2048M
|
||||
org.gradle.parallel = true
|
||||
android.useAndroidX = true
|
||||
kotlin.code.style = official
|
||||
version = 5.33.0-dev.7
|
||||
version = 5.34.0-dev.4
|
||||
|
||||
@@ -1660,12 +1660,12 @@ public final class app/revanced/patches/youtube/video/quality/RememberVideoQuali
|
||||
public static final fun getRememberVideoQualityPatch ()Lapp/revanced/patcher/patch/BytecodePatch;
|
||||
}
|
||||
|
||||
public final class app/revanced/patches/youtube/video/quality/VideoQualityPatchKt {
|
||||
public static final fun getVideoQualityPatch ()Lapp/revanced/patcher/patch/BytecodePatch;
|
||||
public final class app/revanced/patches/youtube/video/quality/VideoQualityDialogButtonPatchKt {
|
||||
public static final fun getVideoQualityDialogButtonPatch ()Lapp/revanced/patcher/patch/BytecodePatch;
|
||||
}
|
||||
|
||||
public final class app/revanced/patches/youtube/video/quality/button/VideoQualityDialogButtonPatchKt {
|
||||
public static final fun getVideoQualityButtonPatch ()Lapp/revanced/patcher/patch/BytecodePatch;
|
||||
public final class app/revanced/patches/youtube/video/quality/VideoQualityPatchKt {
|
||||
public static final fun getVideoQualityPatch ()Lapp/revanced/patcher/patch/BytecodePatch;
|
||||
}
|
||||
|
||||
public final class app/revanced/patches/youtube/video/speed/PlaybackSpeedPatchKt {
|
||||
|
||||
@@ -3,6 +3,7 @@ package app.revanced.patches.backdrops.misc.pro
|
||||
import app.revanced.patcher.fingerprint
|
||||
import com.android.tools.smali.dexlib2.Opcode
|
||||
|
||||
@Deprecated("Fingerprint no longer resolves and will soon be deleted.")
|
||||
internal val proUnlockFingerprint = fingerprint {
|
||||
opcodes(
|
||||
Opcode.INVOKE_VIRTUAL,
|
||||
|
||||
@@ -6,9 +6,8 @@ import app.revanced.patcher.patch.bytecodePatch
|
||||
import com.android.tools.smali.dexlib2.iface.instruction.OneRegisterInstruction
|
||||
|
||||
@Suppress("unused")
|
||||
val proUnlockPatch = bytecodePatch(
|
||||
name = "Pro unlock",
|
||||
) {
|
||||
@Deprecated("This patch no longer works and will soon be deleted.")
|
||||
val proUnlockPatch = bytecodePatch{
|
||||
compatibleWith("com.backdrops.wallpapers")
|
||||
|
||||
execute {
|
||||
|
||||
@@ -8,7 +8,7 @@ val signatureCheckPatch = bytecodePatch(
|
||||
name = "Disable signature check",
|
||||
description = "Disables the signature check that causes the app to crash on startup."
|
||||
) {
|
||||
compatibleWith("com.instagram.android"("378.0.0.52.68"))
|
||||
compatibleWith("com.instagram.android")
|
||||
|
||||
execute {
|
||||
isValidSignatureMethodFingerprint
|
||||
|
||||
@@ -5,6 +5,5 @@ import app.revanced.patcher.fingerprint
|
||||
internal val isFacebookButtonEnabledFingerprint = fingerprint {
|
||||
parameters()
|
||||
returns("Z")
|
||||
strings("com.facebook.messaging.inbox.tab.plugins.core.tabtoolbarbutton." +
|
||||
"facebookbutton.facebooktoolbarbutton.FacebookButtonTabButtonImplementation")
|
||||
strings("FacebookButtonTabButtonImplementation")
|
||||
}
|
||||
|
||||
@@ -3,6 +3,7 @@ package app.revanced.patches.music.layout.compactheader
|
||||
import app.revanced.patcher.extensions.InstructionExtensions.addInstructions
|
||||
import app.revanced.patcher.extensions.InstructionExtensions.getInstruction
|
||||
import app.revanced.patcher.patch.bytecodePatch
|
||||
import app.revanced.util.findFreeRegister
|
||||
import com.android.tools.smali.dexlib2.iface.instruction.OneRegisterInstruction
|
||||
|
||||
@Suppress("unused")
|
||||
@@ -17,13 +18,14 @@ val hideCategoryBar = bytecodePatch(
|
||||
constructCategoryBarFingerprint.method.apply {
|
||||
val insertIndex = constructCategoryBarFingerprint.patternMatch!!.startIndex
|
||||
val register = getInstruction<OneRegisterInstruction>(insertIndex - 1).registerA
|
||||
val freeRegister = findFreeRegister(insertIndex, register)
|
||||
|
||||
addInstructions(
|
||||
insertIndex,
|
||||
"""
|
||||
const/16 v2, 0x8
|
||||
invoke-virtual {v$register, v2}, Landroid/view/View;->setVisibility(I)V
|
||||
""",
|
||||
const/16 v$freeRegister, 0x8
|
||||
invoke-virtual { v$register, v$freeRegister }, Landroid/view/View;->setVisibility(I)V
|
||||
"""
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,6 +3,7 @@ package app.revanced.patches.nfctoolsse.misc.pro
|
||||
import com.android.tools.smali.dexlib2.AccessFlags
|
||||
import app.revanced.patcher.fingerprint
|
||||
|
||||
@Deprecated("This patch no longer works and will soon be deleted.")
|
||||
internal val isLicenseRegisteredFingerprint = fingerprint {
|
||||
accessFlags(AccessFlags.PUBLIC)
|
||||
returns("Z")
|
||||
|
||||
@@ -4,9 +4,8 @@ import app.revanced.patcher.extensions.InstructionExtensions.addInstructions
|
||||
import app.revanced.patcher.patch.bytecodePatch
|
||||
|
||||
@Suppress("unused")
|
||||
val unlockProPatch = bytecodePatch(
|
||||
name = "Unlock pro",
|
||||
) {
|
||||
@Deprecated("This patch no longer works and will soon be deleted.")
|
||||
val unlockProPatch = bytecodePatch{
|
||||
compatibleWith("com.wakdev.apps.nfctools.se")
|
||||
|
||||
execute {
|
||||
|
||||
@@ -1,21 +1,15 @@
|
||||
package app.revanced.patches.pixiv.ads
|
||||
|
||||
import app.revanced.patcher.extensions.InstructionExtensions.addInstructions
|
||||
import app.revanced.patcher.patch.bytecodePatch
|
||||
import app.revanced.util.returnEarly
|
||||
|
||||
@Suppress("unused")
|
||||
val hideAdsPatch = bytecodePatch(
|
||||
name = "Hide ads",
|
||||
) {
|
||||
compatibleWith("jp.pxv.android")
|
||||
compatibleWith("jp.pxv.android"("6.141.1"))
|
||||
|
||||
execute {
|
||||
shouldShowAdsFingerprint.method.addInstructions(
|
||||
0,
|
||||
"""
|
||||
const/4 v0, 0x0
|
||||
return v0
|
||||
""",
|
||||
)
|
||||
shouldShowAdsFingerprint.method.returnEarly(false)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -21,7 +21,7 @@ val audioAdsPatch = bytecodePatch(
|
||||
addResourcesPatch,
|
||||
)
|
||||
|
||||
compatibleWith("tv.twitch.android.app")
|
||||
compatibleWith("tv.twitch.android.app"("16.9.1", "25.3.0"))
|
||||
|
||||
execute {
|
||||
addResources("twitch", "ad.audio.audioAdsPatch")
|
||||
|
||||
@@ -19,7 +19,7 @@ val embeddedAdsPatch = bytecodePatch(
|
||||
settingsPatch,
|
||||
)
|
||||
|
||||
compatibleWith("tv.twitch.android.app")
|
||||
compatibleWith("tv.twitch.android.app"("16.9.1", "25.3.0"))
|
||||
|
||||
execute {
|
||||
addResources("twitch", "ad.embedded.embeddedAdsPatch")
|
||||
|
||||
@@ -155,7 +155,5 @@ val videoAdsPatch = bytecodePatch(
|
||||
},
|
||||
)
|
||||
|
||||
compatibleWith(
|
||||
"tv.twitch.android.app",
|
||||
)
|
||||
compatibleWith("tv.twitch.android.app"("16.9.1", "25.3.0"))
|
||||
}
|
||||
|
||||
@@ -22,7 +22,7 @@ val showDeletedMessagesPatch = bytecodePatch(
|
||||
addResourcesPatch,
|
||||
)
|
||||
|
||||
compatibleWith("tv.twitch.android.app")
|
||||
compatibleWith("tv.twitch.android.app"("16.9.1", "25.3.0"))
|
||||
|
||||
fun createSpoilerConditionInstructions(register: String = "v0") = """
|
||||
invoke-static {}, Lapp/revanced/extension/twitch/patches/ShowDeletedMessagesPatch;->shouldUseSpoiler()Z
|
||||
|
||||
@@ -20,7 +20,7 @@ val autoClaimChannelPointsPatch = bytecodePatch(
|
||||
addResourcesPatch,
|
||||
)
|
||||
|
||||
compatibleWith("tv.twitch.android.app")
|
||||
compatibleWith("tv.twitch.android.app"("16.9.1", "25.3.0"))
|
||||
|
||||
execute {
|
||||
addResources("twitch", "chat.autoclaim.autoClaimChannelPointsPatch")
|
||||
|
||||
@@ -20,7 +20,7 @@ val debugModePatch = bytecodePatch(
|
||||
addResourcesPatch,
|
||||
)
|
||||
|
||||
compatibleWith("tv.twitch.android.app")
|
||||
compatibleWith("tv.twitch.android.app"("16.9.1", "25.3.0"))
|
||||
|
||||
execute {
|
||||
addResources("twitch", "debug.debugModePatch")
|
||||
|
||||
@@ -48,7 +48,7 @@ val settingsPatch = bytecodePatch(
|
||||
settingsPatch(preferences = preferences),
|
||||
)
|
||||
|
||||
compatibleWith("tv.twitch.android.app")
|
||||
compatibleWith("tv.twitch.android.app"("16.9.1"))
|
||||
|
||||
execute {
|
||||
addResources("twitch", "misc.settings.settingsPatch")
|
||||
|
||||
@@ -282,7 +282,7 @@ val playerControlsPatch = bytecodePatch(
|
||||
// The change to support this is simple and only requires adding buttons to both layout files,
|
||||
// but for now force this different layout off since it's still an experimental test.
|
||||
if (is_19_35_or_greater) {
|
||||
playerBottomControlsExploderFeatureFlagFingerprint.method.returnEarly()
|
||||
playerBottomControlsExploderFeatureFlagFingerprint.method.returnLate(false)
|
||||
}
|
||||
|
||||
// A/B test of new top overlay controls. Two different layouts can be used:
|
||||
|
||||
@@ -1,23 +1,27 @@
|
||||
package app.revanced.patches.youtube.video.audio
|
||||
|
||||
import app.revanced.patcher.fingerprint
|
||||
import app.revanced.util.containsLiteralInstruction
|
||||
import com.android.tools.smali.dexlib2.AccessFlags
|
||||
|
||||
internal val streamingModelBuilderFingerprint = fingerprint {
|
||||
internal val formatStreamModelToStringFingerprint = fingerprint {
|
||||
accessFlags(AccessFlags.PUBLIC, AccessFlags.FINAL)
|
||||
returns("L")
|
||||
strings("vprng")
|
||||
returns("Ljava/lang/String;")
|
||||
custom { method, classDef ->
|
||||
method.name == "toString" && classDef.type ==
|
||||
"Lcom/google/android/libraries/youtube/innertube/model/media/FormatStreamModel;"
|
||||
}
|
||||
}
|
||||
|
||||
internal val menuItemAudioTrackFingerprint = fingerprint {
|
||||
accessFlags(AccessFlags.PUBLIC, AccessFlags.FINAL)
|
||||
parameters("L")
|
||||
returns("V")
|
||||
strings("menu_item_audio_track")
|
||||
internal const val AUDIO_STREAM_IGNORE_DEFAULT_FEATURE_FLAG = 45666189L
|
||||
|
||||
internal val selectAudioStreamFingerprint = fingerprint {
|
||||
accessFlags(AccessFlags.PUBLIC, AccessFlags.STATIC)
|
||||
returns("L")
|
||||
custom { method, _ ->
|
||||
method.parameters.size > 2 // Method has a large number of parameters and may change.
|
||||
&& method.parameters[1].type == "Lcom/google/android/libraries/youtube/innertube/model/media/PlayerConfigModel;"
|
||||
&& method.containsLiteralInstruction(AUDIO_STREAM_IGNORE_DEFAULT_FEATURE_FLAG)
|
||||
}
|
||||
}
|
||||
|
||||
internal val audioStreamingTypeSelector = fingerprint {
|
||||
accessFlags(AccessFlags.PRIVATE, AccessFlags.FINAL)
|
||||
returns("L")
|
||||
strings("raw") // String is not unique
|
||||
}
|
||||
@@ -5,22 +5,22 @@ import app.revanced.patcher.extensions.InstructionExtensions.addInstructionsWith
|
||||
import app.revanced.patcher.extensions.InstructionExtensions.getInstruction
|
||||
import app.revanced.patcher.patch.bytecodePatch
|
||||
import app.revanced.patcher.util.proxy.mutableTypes.MutableField.Companion.toMutable
|
||||
import app.revanced.patcher.util.proxy.mutableTypes.MutableMethod
|
||||
import app.revanced.patcher.util.proxy.mutableTypes.MutableMethod.Companion.toMutable
|
||||
import app.revanced.patches.all.misc.resources.addResources
|
||||
import app.revanced.patches.all.misc.resources.addResourcesPatch
|
||||
import app.revanced.patches.shared.misc.settings.preference.SwitchPreference
|
||||
import app.revanced.patches.youtube.misc.extension.sharedExtensionPatch
|
||||
import app.revanced.patches.youtube.misc.playservice.is_20_07_or_greater
|
||||
import app.revanced.patches.youtube.misc.playservice.versionCheckPatch
|
||||
import app.revanced.patches.youtube.misc.settings.PreferenceScreen
|
||||
import app.revanced.patches.youtube.misc.settings.settingsPatch
|
||||
import app.revanced.util.getReference
|
||||
import app.revanced.util.findMethodFromToString
|
||||
import app.revanced.util.indexOfFirstInstructionOrThrow
|
||||
import app.revanced.util.insertLiteralOverride
|
||||
import com.android.tools.smali.dexlib2.AccessFlags
|
||||
import com.android.tools.smali.dexlib2.Opcode
|
||||
import com.android.tools.smali.dexlib2.builder.MutableMethodImplementation
|
||||
import com.android.tools.smali.dexlib2.iface.Method
|
||||
import com.android.tools.smali.dexlib2.iface.instruction.OneRegisterInstruction
|
||||
import com.android.tools.smali.dexlib2.iface.reference.MethodReference
|
||||
import com.android.tools.smali.dexlib2.immutable.ImmutableField
|
||||
import com.android.tools.smali.dexlib2.immutable.ImmutableMethod
|
||||
import com.android.tools.smali.dexlib2.immutable.ImmutableMethodParameter
|
||||
@@ -37,6 +37,7 @@ val forceOriginalAudioPatch = bytecodePatch(
|
||||
sharedExtensionPatch,
|
||||
settingsPatch,
|
||||
addResourcesPatch,
|
||||
versionCheckPatch
|
||||
)
|
||||
|
||||
compatibleWith(
|
||||
@@ -60,100 +61,93 @@ val forceOriginalAudioPatch = bytecodePatch(
|
||||
)
|
||||
)
|
||||
|
||||
fun Method.firstFormatStreamingModelCall(
|
||||
returnType: String = "Ljava/lang/String;"
|
||||
): MutableMethod {
|
||||
val audioTrackIdIndex = indexOfFirstInstructionOrThrow {
|
||||
val reference = getReference<MethodReference>()
|
||||
reference?.definingClass == "Lcom/google/android/libraries/youtube/innertube/model/media/FormatStreamModel;"
|
||||
&& reference.returnType == returnType
|
||||
}
|
||||
|
||||
return navigate(this).to(audioTrackIdIndex).stop()
|
||||
// Disable feature flag that ignores the default track flag
|
||||
// and instead overrides to the user region language.
|
||||
if (is_20_07_or_greater) {
|
||||
selectAudioStreamFingerprint.method.insertLiteralOverride(
|
||||
AUDIO_STREAM_IGNORE_DEFAULT_FEATURE_FLAG,
|
||||
"$EXTENSION_CLASS_DESCRIPTOR->ignoreDefaultAudioStream(Z)Z"
|
||||
)
|
||||
}
|
||||
|
||||
// Accessor methods of FormatStreamModel have no string constants and
|
||||
// opcodes are identical to other methods in the same class,
|
||||
// so must walk from another class that use the methods.
|
||||
val isDefaultMethod = streamingModelBuilderFingerprint.originalMethod.firstFormatStreamingModelCall("Z")
|
||||
val audioTrackIdMethod = menuItemAudioTrackFingerprint.originalMethod.firstFormatStreamingModelCall()
|
||||
val audioTrackDisplayNameMethod = audioStreamingTypeSelector.originalMethod.firstFormatStreamingModelCall()
|
||||
val formatStreamModelClass = proxy(classes.first {
|
||||
it.type == audioTrackIdMethod.definingClass
|
||||
}).mutableClass
|
||||
formatStreamModelToStringFingerprint.let {
|
||||
val isDefaultAudioTrackMethod = it.originalMethod.findMethodFromToString("isDefaultAudioTrack=")
|
||||
val audioTrackDisplayNameMethod = it.originalMethod.findMethodFromToString("audioTrackDisplayName=")
|
||||
val audioTrackIdMethod = it.originalMethod.findMethodFromToString("audioTrackId=")
|
||||
|
||||
formatStreamModelClass.apply {
|
||||
// Add a new field to store the override.
|
||||
val helperFieldName = "isDefaultAudioTrackOverride"
|
||||
fields.add(
|
||||
ImmutableField(
|
||||
type,
|
||||
helperFieldName,
|
||||
"Ljava/lang/Boolean;",
|
||||
// Boolean is a 100% immutable class (all fields are final)
|
||||
// and safe to write to a shared field without volatile/synchronization,
|
||||
// but without volatile the field can show stale data
|
||||
// and the same field is calculated more than once by different threads.
|
||||
AccessFlags.PRIVATE.value or AccessFlags.VOLATILE.value,
|
||||
it.classDef.apply {
|
||||
// Add a new field to store the override.
|
||||
val helperFieldName = "patch_isDefaultAudioTrackOverride"
|
||||
fields.add(
|
||||
ImmutableField(
|
||||
type,
|
||||
helperFieldName,
|
||||
"Ljava/lang/Boolean;",
|
||||
// Boolean is a 100% immutable class (all fields are final)
|
||||
// and safe to write to a shared field without volatile/synchronization,
|
||||
// but without volatile the field can show stale data
|
||||
// and the same field is calculated more than once by different threads.
|
||||
AccessFlags.PRIVATE.value or AccessFlags.VOLATILE.value,
|
||||
null,
|
||||
null,
|
||||
null
|
||||
).toMutable()
|
||||
)
|
||||
|
||||
// Add a helper method because the isDefaultAudioTrack() has only 2 registers and 3 are needed.
|
||||
val helperMethodClass = type
|
||||
val helperMethodName = "patch_isDefaultAudioTrack"
|
||||
val helperMethod = ImmutableMethod(
|
||||
helperMethodClass,
|
||||
helperMethodName,
|
||||
listOf(ImmutableMethodParameter("Z", null, null)),
|
||||
"Z",
|
||||
AccessFlags.PRIVATE.value,
|
||||
null,
|
||||
null,
|
||||
null
|
||||
).toMutable()
|
||||
)
|
||||
MutableMethodImplementation(6),
|
||||
).toMutable().apply {
|
||||
addInstructionsWithLabels(
|
||||
0,
|
||||
"""
|
||||
iget-object v0, p0, $helperMethodClass->$helperFieldName:Ljava/lang/Boolean;
|
||||
if-eqz v0, :call_extension
|
||||
invoke-virtual { v0 }, Ljava/lang/Boolean;->booleanValue()Z
|
||||
move-result v3
|
||||
return v3
|
||||
|
||||
:call_extension
|
||||
invoke-virtual { p0 }, $audioTrackIdMethod
|
||||
move-result-object v1
|
||||
|
||||
invoke-virtual { p0 }, $audioTrackDisplayNameMethod
|
||||
move-result-object v2
|
||||
|
||||
invoke-static { p1, v1, v2 }, $EXTENSION_CLASS_DESCRIPTOR->isDefaultAudioStream(ZLjava/lang/String;Ljava/lang/String;)Z
|
||||
move-result v3
|
||||
|
||||
invoke-static { v3 }, Ljava/lang/Boolean;->valueOf(Z)Ljava/lang/Boolean;
|
||||
move-result-object v0
|
||||
iput-object v0, p0, $helperMethodClass->$helperFieldName:Ljava/lang/Boolean;
|
||||
return v3
|
||||
"""
|
||||
)
|
||||
}
|
||||
methods.add(helperMethod)
|
||||
|
||||
// Add a helper method because the isDefaultAudioTrack() has only 2 registers and 3 are needed.
|
||||
val helperMethodClass = type
|
||||
val helperMethodName = "extension_isDefaultAudioTrack"
|
||||
val helperMethod = ImmutableMethod(
|
||||
helperMethodClass,
|
||||
helperMethodName,
|
||||
listOf(ImmutableMethodParameter("Z", null, null)),
|
||||
"Z",
|
||||
AccessFlags.PRIVATE.value,
|
||||
null,
|
||||
null,
|
||||
MutableMethodImplementation(6),
|
||||
).toMutable().apply {
|
||||
addInstructionsWithLabels(
|
||||
0,
|
||||
"""
|
||||
iget-object v0, p0, $helperMethodClass->$helperFieldName:Ljava/lang/Boolean;
|
||||
if-eqz v0, :call_extension
|
||||
invoke-virtual { v0 }, Ljava/lang/Boolean;->booleanValue()Z
|
||||
move-result v3
|
||||
return v3
|
||||
|
||||
:call_extension
|
||||
invoke-virtual { p0 }, $audioTrackIdMethod
|
||||
move-result-object v1
|
||||
|
||||
invoke-virtual { p0 }, $audioTrackDisplayNameMethod
|
||||
move-result-object v2
|
||||
|
||||
invoke-static { p1, v1, v2 }, $EXTENSION_CLASS_DESCRIPTOR->isDefaultAudioStream(ZLjava/lang/String;Ljava/lang/String;)Z
|
||||
move-result v3
|
||||
|
||||
invoke-static { v3 }, Ljava/lang/Boolean;->valueOf(Z)Ljava/lang/Boolean;
|
||||
move-result-object v0
|
||||
iput-object v0, p0, $helperMethodClass->$helperFieldName:Ljava/lang/Boolean;
|
||||
return v3
|
||||
"""
|
||||
)
|
||||
}
|
||||
methods.add(helperMethod)
|
||||
// Modify isDefaultAudioTrack() to call extension helper method.
|
||||
isDefaultAudioTrackMethod.apply {
|
||||
val index = indexOfFirstInstructionOrThrow(Opcode.RETURN)
|
||||
val register = getInstruction<OneRegisterInstruction>(index).registerA
|
||||
|
||||
// Modify isDefaultAudioTrack() to call extension helper method.
|
||||
isDefaultMethod.apply {
|
||||
val index = indexOfFirstInstructionOrThrow(Opcode.RETURN)
|
||||
val register = getInstruction<OneRegisterInstruction>(index).registerA
|
||||
|
||||
addInstructions(
|
||||
index,
|
||||
"""
|
||||
invoke-direct { p0, v$register }, $helperMethodClass->$helperMethodName(Z)Z
|
||||
move-result v$register
|
||||
"""
|
||||
)
|
||||
addInstructions(
|
||||
index,
|
||||
"""
|
||||
invoke-direct { p0, v$register }, $helperMethodClass->$helperMethodName(Z)Z
|
||||
move-result v$register
|
||||
"""
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -63,7 +63,7 @@ val disableHdrPatch = bytecodePatch(
|
||||
return v0
|
||||
:useHdr
|
||||
nop
|
||||
"""
|
||||
"""
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -133,3 +133,46 @@ internal val playbackSpeedClassFingerprint = fingerprint {
|
||||
)
|
||||
strings("PLAYBACK_RATE_MENU_BOTTOM_SHEET_FRAGMENT")
|
||||
}
|
||||
|
||||
|
||||
internal const val YOUTUBE_VIDEO_QUALITY_CLASS_TYPE = "Lcom/google/android/libraries/youtube/innertube/model/media/VideoQuality;"
|
||||
|
||||
internal val videoQualityFingerprint = fingerprint {
|
||||
accessFlags(AccessFlags.PUBLIC, AccessFlags.CONSTRUCTOR)
|
||||
parameters(
|
||||
"I", // Resolution.
|
||||
"Ljava/lang/String;", // Human readable resolution: "480p", "1080p Premium", etc
|
||||
"Z",
|
||||
"L"
|
||||
)
|
||||
custom { _, classDef ->
|
||||
classDef.type == YOUTUBE_VIDEO_QUALITY_CLASS_TYPE
|
||||
}
|
||||
}
|
||||
|
||||
internal val videoQualitySetterFingerprint = fingerprint {
|
||||
accessFlags(AccessFlags.PUBLIC, AccessFlags.FINAL)
|
||||
returns("V")
|
||||
parameters("[L", "I", "Z")
|
||||
opcodes(
|
||||
Opcode.IF_EQZ,
|
||||
Opcode.INVOKE_VIRTUAL,
|
||||
Opcode.MOVE_RESULT_OBJECT,
|
||||
Opcode.INVOKE_VIRTUAL,
|
||||
Opcode.IPUT_BOOLEAN,
|
||||
)
|
||||
strings("menu_item_video_quality")
|
||||
}
|
||||
|
||||
/**
|
||||
* Matches with the class found in [videoQualitySetterFingerprint].
|
||||
*/
|
||||
internal val setVideoQualityFingerprint = fingerprint {
|
||||
returns("V")
|
||||
parameters("L")
|
||||
opcodes(
|
||||
Opcode.IGET_OBJECT,
|
||||
Opcode.IPUT_OBJECT,
|
||||
Opcode.IGET_OBJECT,
|
||||
)
|
||||
}
|
||||
|
||||
@@ -38,7 +38,9 @@ import com.android.tools.smali.dexlib2.util.MethodUtil
|
||||
|
||||
private const val EXTENSION_CLASS_DESCRIPTOR = "Lapp/revanced/extension/youtube/patches/VideoInformation;"
|
||||
private const val EXTENSION_PLAYER_INTERFACE =
|
||||
"Lapp/revanced/extension/youtube/patches/VideoInformation${'$'}PlaybackController;"
|
||||
"Lapp/revanced/extension/youtube/patches/VideoInformation\$PlaybackController;"
|
||||
private const val EXTENSION_VIDEO_QUALITY_MENU_INTERFACE =
|
||||
"Lapp/revanced/extension/youtube/patches/VideoInformation\$VideoQualityMenuInterface;"
|
||||
|
||||
private lateinit var playerInitMethod: MutableMethod
|
||||
private var playerInitInsertIndex = -1
|
||||
@@ -83,7 +85,6 @@ val videoInformationPatch = bytecodePatch(
|
||||
)
|
||||
|
||||
execute {
|
||||
|
||||
playerInitMethod = playerInitFingerprint.classDef.methods.first { MethodUtil.isConstructor(it) }
|
||||
|
||||
// Find the location of the first invoke-direct call and extract the register storing the 'this' object reference.
|
||||
@@ -93,9 +94,6 @@ val videoInformationPatch = bytecodePatch(
|
||||
playerInitInsertRegister = playerInitMethod.getInstruction<FiveRegisterInstruction>(initThisIndex).registerC
|
||||
playerInitInsertIndex = initThisIndex + 1
|
||||
|
||||
// Hook the player controller for use through the extension.
|
||||
onCreateHook(EXTENSION_CLASS_DESCRIPTOR, "initialize")
|
||||
|
||||
val seekFingerprintResultMethod = seekFingerprint.match(playerInitFingerprint.originalClassDef).method
|
||||
val seekRelativeFingerprintResultMethod =
|
||||
seekRelativeFingerprint.match(playerInitFingerprint.originalClassDef).method
|
||||
@@ -272,6 +270,131 @@ val videoInformationPatch = bytecodePatch(
|
||||
speedSelectionValueRegister = getInstruction<TwoRegisterInstruction>(index).registerA
|
||||
}
|
||||
|
||||
videoQualityFingerprint.let {
|
||||
// Fix bad data used by YouTube.
|
||||
it.method.addInstructions(
|
||||
0,
|
||||
"""
|
||||
invoke-static { p2, p1 }, $EXTENSION_CLASS_DESCRIPTOR->fixVideoQualityResolution(Ljava/lang/String;I)I
|
||||
move-result p1
|
||||
"""
|
||||
)
|
||||
|
||||
// Add methods to access obfuscated quality fields.
|
||||
it.classDef.apply {
|
||||
methods.add(
|
||||
ImmutableMethod(
|
||||
type,
|
||||
"patch_getQualityName",
|
||||
listOf(),
|
||||
"Ljava/lang/String;",
|
||||
AccessFlags.PUBLIC.value or AccessFlags.FINAL.value,
|
||||
null,
|
||||
null,
|
||||
MutableMethodImplementation(2),
|
||||
).toMutable().apply {
|
||||
// Only one string field.
|
||||
val qualityNameField = fields.single { field ->
|
||||
field.type == "Ljava/lang/String;"
|
||||
}
|
||||
|
||||
addInstructions(
|
||||
0,
|
||||
"""
|
||||
iget-object v0, p0, $qualityNameField
|
||||
return-object v0
|
||||
"""
|
||||
)
|
||||
}
|
||||
)
|
||||
|
||||
methods.add(
|
||||
ImmutableMethod(
|
||||
type,
|
||||
"patch_getResolution",
|
||||
listOf(),
|
||||
"I",
|
||||
AccessFlags.PUBLIC.value or AccessFlags.FINAL.value,
|
||||
null,
|
||||
null,
|
||||
MutableMethodImplementation(2),
|
||||
).toMutable().apply {
|
||||
val resolutionField = fields.single { field ->
|
||||
field.type == "I"
|
||||
}
|
||||
|
||||
addInstructions(
|
||||
0,
|
||||
"""
|
||||
iget v0, p0, $resolutionField
|
||||
return v0
|
||||
"""
|
||||
)
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
// Detect video quality changes and override the current quality.
|
||||
setVideoQualityFingerprint.match(
|
||||
videoQualitySetterFingerprint.originalClassDef
|
||||
).let { match ->
|
||||
// This instruction refers to the field with the type that contains the setQuality method.
|
||||
val onItemClickListenerClassReference = match.method
|
||||
.getInstruction<ReferenceInstruction>(0).reference
|
||||
val setQualityFieldReference = match.method
|
||||
.getInstruction<ReferenceInstruction>(1).reference as FieldReference
|
||||
|
||||
proxy(
|
||||
classes.find { classDef ->
|
||||
classDef.type == setQualityFieldReference.type
|
||||
}!!
|
||||
).mutableClass.apply {
|
||||
// Add interface and helper methods to allow extension code to call obfuscated methods.
|
||||
interfaces.add(EXTENSION_VIDEO_QUALITY_MENU_INTERFACE)
|
||||
|
||||
methods.add(
|
||||
ImmutableMethod(
|
||||
type,
|
||||
"patch_setQuality",
|
||||
listOf(
|
||||
ImmutableMethodParameter(YOUTUBE_VIDEO_QUALITY_CLASS_TYPE, null, null)
|
||||
),
|
||||
"V",
|
||||
AccessFlags.PUBLIC.value or AccessFlags.FINAL.value,
|
||||
null,
|
||||
null,
|
||||
MutableMethodImplementation(2),
|
||||
).toMutable().apply {
|
||||
val setQualityMenuIndexMethod = methods.single { method ->
|
||||
method.parameterTypes.firstOrNull() == YOUTUBE_VIDEO_QUALITY_CLASS_TYPE
|
||||
}
|
||||
|
||||
addInstructions(
|
||||
0,
|
||||
"""
|
||||
invoke-virtual { p0, p1 }, $setQualityMenuIndexMethod
|
||||
return-void
|
||||
"""
|
||||
)
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
videoQualitySetterFingerprint.method.addInstructions(
|
||||
0,
|
||||
"""
|
||||
# Get object instance to invoke setQuality method.
|
||||
iget-object v0, p0, $onItemClickListenerClassReference
|
||||
iget-object v0, v0, $setQualityFieldReference
|
||||
|
||||
invoke-static { p1, v0, p2 }, $EXTENSION_CLASS_DESCRIPTOR->setVideoQuality([$YOUTUBE_VIDEO_QUALITY_CLASS_TYPE${EXTENSION_VIDEO_QUALITY_MENU_INTERFACE}I)I
|
||||
move-result p2
|
||||
"""
|
||||
)
|
||||
}
|
||||
|
||||
onCreateHook(EXTENSION_CLASS_DESCRIPTOR, "initialize")
|
||||
videoSpeedChangedHook(EXTENSION_CLASS_DESCRIPTOR, "videoSpeedChanged")
|
||||
userSelectedPlaybackSpeedHook(EXTENSION_CLASS_DESCRIPTOR, "userSelectedPlaybackSpeed")
|
||||
}
|
||||
@@ -282,8 +405,8 @@ private fun addSeekInterfaceMethods(targetClass: MutableClass, seekToMethod: Met
|
||||
targetClass.interfaces.add(EXTENSION_PLAYER_INTERFACE)
|
||||
|
||||
arrayOf(
|
||||
Triple(seekToMethod, "seekTo", true),
|
||||
Triple(seekToRelativeMethod, "seekToRelative", false),
|
||||
Triple(seekToMethod, "patch_seekTo", true),
|
||||
Triple(seekToRelativeMethod, "patch_seekToRelative", false),
|
||||
).forEach { (method, name, returnsBoolean) ->
|
||||
// Add interface method.
|
||||
// Get enum type for the seek helper method.
|
||||
|
||||
@@ -5,34 +5,6 @@ import app.revanced.util.literal
|
||||
import com.android.tools.smali.dexlib2.AccessFlags
|
||||
import com.android.tools.smali.dexlib2.Opcode
|
||||
|
||||
internal const val YOUTUBE_VIDEO_QUALITY_CLASS_TYPE = "Lcom/google/android/libraries/youtube/innertube/model/media/VideoQuality;"
|
||||
|
||||
internal val videoQualityFingerprint = fingerprint {
|
||||
accessFlags(AccessFlags.PUBLIC, AccessFlags.CONSTRUCTOR)
|
||||
parameters(
|
||||
"I", // Resolution.
|
||||
"Ljava/lang/String;", // Human readable resolution: "480p", "1080p Premium", etc
|
||||
"Z",
|
||||
"L"
|
||||
)
|
||||
custom { _, classDef ->
|
||||
classDef.type == YOUTUBE_VIDEO_QUALITY_CLASS_TYPE
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Matches with the class found in [videoQualitySetterFingerprint].
|
||||
*/
|
||||
internal val setVideoQualityFingerprint = fingerprint {
|
||||
returns("V")
|
||||
parameters("L")
|
||||
opcodes(
|
||||
Opcode.IGET_OBJECT,
|
||||
Opcode.IPUT_OBJECT,
|
||||
Opcode.IGET_OBJECT,
|
||||
)
|
||||
}
|
||||
|
||||
internal val videoQualityItemOnClickParentFingerprint = fingerprint {
|
||||
returns("V")
|
||||
strings("VIDEO_QUALITIES_MENU_BOTTOM_SHEET_FRAGMENT")
|
||||
@@ -54,19 +26,6 @@ internal val videoQualityItemOnClickFingerprint = fingerprint {
|
||||
}
|
||||
}
|
||||
|
||||
internal val videoQualitySetterFingerprint = fingerprint {
|
||||
accessFlags(AccessFlags.PUBLIC, AccessFlags.FINAL)
|
||||
returns("V")
|
||||
parameters("[L", "I", "Z")
|
||||
opcodes(
|
||||
Opcode.IF_EQZ,
|
||||
Opcode.INVOKE_VIRTUAL,
|
||||
Opcode.MOVE_RESULT_OBJECT,
|
||||
Opcode.INVOKE_VIRTUAL,
|
||||
Opcode.IPUT_BOOLEAN,
|
||||
)
|
||||
strings("menu_item_video_quality")
|
||||
}
|
||||
|
||||
internal val videoQualityMenuOptionsFingerprint = fingerprint {
|
||||
accessFlags(AccessFlags.STATIC)
|
||||
|
||||
@@ -1,10 +1,8 @@
|
||||
package app.revanced.patches.youtube.video.quality
|
||||
|
||||
import app.revanced.patcher.extensions.InstructionExtensions.addInstruction
|
||||
import app.revanced.patcher.extensions.InstructionExtensions.addInstructions
|
||||
import app.revanced.patcher.extensions.InstructionExtensions.getInstruction
|
||||
import app.revanced.patcher.patch.bytecodePatch
|
||||
import app.revanced.patcher.util.proxy.mutableTypes.MutableMethod.Companion.toMutable
|
||||
import app.revanced.patches.all.misc.resources.addResources
|
||||
import app.revanced.patches.all.misc.resources.addResourcesPatch
|
||||
import app.revanced.patches.shared.misc.settings.preference.ListPreference
|
||||
@@ -15,18 +13,10 @@ import app.revanced.patches.youtube.misc.settings.settingsPatch
|
||||
import app.revanced.patches.youtube.shared.videoQualityChangedFingerprint
|
||||
import app.revanced.patches.youtube.video.information.onCreateHook
|
||||
import app.revanced.patches.youtube.video.information.videoInformationPatch
|
||||
import com.android.tools.smali.dexlib2.AccessFlags
|
||||
import com.android.tools.smali.dexlib2.builder.MutableMethodImplementation
|
||||
import com.android.tools.smali.dexlib2.iface.instruction.ReferenceInstruction
|
||||
import com.android.tools.smali.dexlib2.iface.instruction.TwoRegisterInstruction
|
||||
import com.android.tools.smali.dexlib2.iface.reference.FieldReference
|
||||
import com.android.tools.smali.dexlib2.immutable.ImmutableMethod
|
||||
import com.android.tools.smali.dexlib2.immutable.ImmutableMethodParameter
|
||||
|
||||
private const val EXTENSION_CLASS_DESCRIPTOR =
|
||||
"Lapp/revanced/extension/youtube/patches/playback/quality/RememberVideoQualityPatch;"
|
||||
private const val EXTENSION_VIDEO_QUALITY_MENU_INTERFACE =
|
||||
"Lapp/revanced/extension/youtube/patches/playback/quality/RememberVideoQualityPatch\$VideoQualityMenuInterface;"
|
||||
|
||||
val rememberVideoQualityPatch = bytecodePatch {
|
||||
dependsOn(
|
||||
@@ -69,131 +59,6 @@ val rememberVideoQualityPatch = bytecodePatch {
|
||||
|
||||
onCreateHook(EXTENSION_CLASS_DESCRIPTOR, "newVideoStarted")
|
||||
|
||||
videoQualityFingerprint.let {
|
||||
// Fix bad data used by YouTube.
|
||||
it.method.addInstructions(
|
||||
0,
|
||||
"""
|
||||
invoke-static { p2, p1 }, $EXTENSION_CLASS_DESCRIPTOR->fixVideoQualityResolution(Ljava/lang/String;I)I
|
||||
move-result p1
|
||||
"""
|
||||
)
|
||||
|
||||
// Add methods to access obfuscated quality fields.
|
||||
it.classDef.apply {
|
||||
methods.add(
|
||||
ImmutableMethod(
|
||||
type,
|
||||
"patch_getQualityName",
|
||||
listOf(),
|
||||
"Ljava/lang/String;",
|
||||
AccessFlags.PUBLIC.value or AccessFlags.FINAL.value,
|
||||
null,
|
||||
null,
|
||||
MutableMethodImplementation(2),
|
||||
).toMutable().apply {
|
||||
// Only one string field.
|
||||
val qualityNameField = fields.single { field ->
|
||||
field.type == "Ljava/lang/String;"
|
||||
}
|
||||
|
||||
addInstructions(
|
||||
0,
|
||||
"""
|
||||
iget-object v0, p0, $qualityNameField
|
||||
return-object v0
|
||||
"""
|
||||
)
|
||||
}
|
||||
)
|
||||
|
||||
methods.add(
|
||||
ImmutableMethod(
|
||||
type,
|
||||
"patch_getResolution",
|
||||
listOf(),
|
||||
"I",
|
||||
AccessFlags.PUBLIC.value or AccessFlags.FINAL.value,
|
||||
null,
|
||||
null,
|
||||
MutableMethodImplementation(2),
|
||||
).toMutable().apply {
|
||||
val resolutionField = fields.single { field ->
|
||||
field.type == "I"
|
||||
}
|
||||
|
||||
addInstructions(
|
||||
0,
|
||||
"""
|
||||
iget v0, p0, $resolutionField
|
||||
return v0
|
||||
"""
|
||||
)
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
// Inject a call to set the remembered quality once a video loads.
|
||||
setVideoQualityFingerprint.match(
|
||||
videoQualitySetterFingerprint.originalClassDef
|
||||
).let { match ->
|
||||
// This instruction refers to the field with the type that contains the setQuality method.
|
||||
val instructions = match.method.implementation!!.instructions
|
||||
val onItemClickListenerClassReference =
|
||||
(instructions.elementAt(0) as ReferenceInstruction).reference
|
||||
val setQualityFieldReference =
|
||||
((instructions.elementAt(1) as ReferenceInstruction).reference) as FieldReference
|
||||
|
||||
proxy(
|
||||
classes.find { classDef ->
|
||||
classDef.type == setQualityFieldReference.type
|
||||
}!!
|
||||
).mutableClass.apply {
|
||||
// Add interface and helper methods to allow extension code to call obfuscated methods.
|
||||
interfaces.add(EXTENSION_VIDEO_QUALITY_MENU_INTERFACE)
|
||||
|
||||
methods.add(
|
||||
ImmutableMethod(
|
||||
type,
|
||||
"patch_setQuality",
|
||||
listOf(
|
||||
ImmutableMethodParameter(YOUTUBE_VIDEO_QUALITY_CLASS_TYPE, null, null)
|
||||
),
|
||||
"V",
|
||||
AccessFlags.PUBLIC.value or AccessFlags.FINAL.value,
|
||||
null,
|
||||
null,
|
||||
MutableMethodImplementation(2),
|
||||
).toMutable().apply {
|
||||
val setQualityMenuIndexMethod = methods.single { method ->
|
||||
method.parameterTypes.firstOrNull() == YOUTUBE_VIDEO_QUALITY_CLASS_TYPE
|
||||
}
|
||||
|
||||
addInstructions(
|
||||
0,
|
||||
"""
|
||||
invoke-virtual { p0, p1 }, $setQualityMenuIndexMethod
|
||||
return-void
|
||||
"""
|
||||
)
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
videoQualitySetterFingerprint.method.addInstructions(
|
||||
0,
|
||||
"""
|
||||
# Get object instance to invoke setQuality method.
|
||||
iget-object v0, p0, $onItemClickListenerClassReference
|
||||
iget-object v0, v0, $setQualityFieldReference
|
||||
|
||||
invoke-static { p1, v0, p2 }, $EXTENSION_CLASS_DESCRIPTOR->setVideoQuality([$YOUTUBE_VIDEO_QUALITY_CLASS_TYPE${EXTENSION_VIDEO_QUALITY_MENU_INTERFACE}I)I
|
||||
move-result p2
|
||||
"""
|
||||
)
|
||||
}
|
||||
|
||||
// Inject a call to remember the selected quality for Shorts.
|
||||
videoQualityItemOnClickFingerprint.match(
|
||||
videoQualityItemOnClickParentFingerprint.classDef
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
package app.revanced.patches.youtube.video.quality.button
|
||||
package app.revanced.patches.youtube.video.quality
|
||||
|
||||
import app.revanced.patcher.patch.bytecodePatch
|
||||
import app.revanced.patcher.patch.resourcePatch
|
||||
@@ -9,7 +9,6 @@ import app.revanced.patches.youtube.misc.extension.sharedExtensionPatch
|
||||
import app.revanced.patches.youtube.misc.playercontrols.*
|
||||
import app.revanced.patches.youtube.misc.settings.PreferenceScreen
|
||||
import app.revanced.patches.youtube.misc.settings.settingsPatch
|
||||
import app.revanced.patches.youtube.video.quality.rememberVideoQualityPatch
|
||||
import app.revanced.util.ResourceGroup
|
||||
import app.revanced.util.copyResources
|
||||
|
||||
@@ -21,14 +20,7 @@ private val videoQualityButtonResourcePatch = resourcePatch {
|
||||
"qualitybutton",
|
||||
ResourceGroup(
|
||||
"drawable",
|
||||
"revanced_video_quality_dialog_button_ld.xml",
|
||||
"revanced_video_quality_dialog_button_sd.xml",
|
||||
"revanced_video_quality_dialog_button_hd.xml",
|
||||
"revanced_video_quality_dialog_button_fhd.xml",
|
||||
"revanced_video_quality_dialog_button_fhd_plus.xml",
|
||||
"revanced_video_quality_dialog_button_qhd.xml",
|
||||
"revanced_video_quality_dialog_button_4k.xml",
|
||||
"revanced_video_quality_dialog_button_unknown.xml",
|
||||
"revanced_video_quality_dialog_button_rectangle.xml",
|
||||
),
|
||||
)
|
||||
|
||||
@@ -39,7 +31,7 @@ private val videoQualityButtonResourcePatch = resourcePatch {
|
||||
private const val QUALITY_BUTTON_CLASS_DESCRIPTOR =
|
||||
"Lapp/revanced/extension/youtube/videoplayer/VideoQualityDialogButton;"
|
||||
|
||||
val videoQualityButtonPatch = bytecodePatch(
|
||||
val videoQualityDialogButtonPatch = bytecodePatch(
|
||||
description = "Adds the option to display video quality dialog button in the video player.",
|
||||
) {
|
||||
dependsOn(
|
||||
@@ -52,7 +44,7 @@ val videoQualityButtonPatch = bytecodePatch(
|
||||
)
|
||||
|
||||
execute {
|
||||
addResources("youtube", "video.quality.button.videoQualityButtonPatch")
|
||||
addResources("youtube", "video.quality.button.videoQualityDialogButtonPatch")
|
||||
|
||||
PreferenceScreen.PLAYER.addPreferences(
|
||||
SwitchPreference("revanced_video_quality_dialog_button"),
|
||||
@@ -5,7 +5,6 @@ import app.revanced.patches.shared.misc.settings.preference.BasePreference
|
||||
import app.revanced.patches.shared.misc.settings.preference.PreferenceCategory
|
||||
import app.revanced.patches.shared.misc.settings.preference.PreferenceScreenPreference.Sorting
|
||||
import app.revanced.patches.youtube.misc.settings.PreferenceScreen
|
||||
import app.revanced.patches.youtube.video.quality.button.videoQualityButtonPatch
|
||||
|
||||
/**
|
||||
* Video quality settings. Used to organize all speed related settings together.
|
||||
@@ -20,7 +19,7 @@ val videoQualityPatch = bytecodePatch(
|
||||
dependsOn(
|
||||
rememberVideoQualityPatch,
|
||||
advancedVideoQualityMenuPatch,
|
||||
videoQualityButtonPatch,
|
||||
videoQualityDialogButtonPatch,
|
||||
)
|
||||
|
||||
compatibleWith(
|
||||
|
||||
@@ -9,6 +9,9 @@ import app.revanced.patches.youtube.misc.extension.sharedExtensionPatch
|
||||
import app.revanced.patches.youtube.misc.playercontrols.*
|
||||
import app.revanced.patches.youtube.misc.settings.PreferenceScreen
|
||||
import app.revanced.patches.youtube.misc.settings.settingsPatch
|
||||
import app.revanced.patches.youtube.video.information.userSelectedPlaybackSpeedHook
|
||||
import app.revanced.patches.youtube.video.information.videoInformationPatch
|
||||
import app.revanced.patches.youtube.video.information.videoSpeedChangedHook
|
||||
import app.revanced.patches.youtube.video.speed.custom.customPlaybackSpeedPatch
|
||||
import app.revanced.util.ResourceGroup
|
||||
import app.revanced.util.copyResources
|
||||
@@ -21,8 +24,8 @@ private val playbackSpeedButtonResourcePatch = resourcePatch {
|
||||
"speedbutton",
|
||||
ResourceGroup(
|
||||
"drawable",
|
||||
"revanced_playback_speed_dialog_button.xml",
|
||||
),
|
||||
"revanced_playback_speed_dialog_button_rectangle.xml"
|
||||
)
|
||||
)
|
||||
|
||||
addBottomControl("speedbutton")
|
||||
@@ -42,6 +45,7 @@ val playbackSpeedButtonPatch = bytecodePatch(
|
||||
customPlaybackSpeedPatch,
|
||||
playbackSpeedButtonResourcePatch,
|
||||
playerControlsPatch,
|
||||
videoInformationPatch,
|
||||
)
|
||||
|
||||
execute {
|
||||
@@ -53,5 +57,8 @@ val playbackSpeedButtonPatch = bytecodePatch(
|
||||
|
||||
initializeBottomControl(SPEED_BUTTON_CLASS_DESCRIPTOR)
|
||||
injectVisibilityCheckCall(SPEED_BUTTON_CLASS_DESCRIPTOR)
|
||||
|
||||
videoSpeedChangedHook(SPEED_BUTTON_CLASS_DESCRIPTOR, "videoSpeedChanged")
|
||||
userSelectedPlaybackSpeedHook(SPEED_BUTTON_CLASS_DESCRIPTOR, "videoSpeedChanged")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -32,7 +32,10 @@ import com.android.tools.smali.dexlib2.iface.instruction.RegisterRangeInstructio
|
||||
import com.android.tools.smali.dexlib2.iface.instruction.ThreeRegisterInstruction
|
||||
import com.android.tools.smali.dexlib2.iface.instruction.TwoRegisterInstruction
|
||||
import com.android.tools.smali.dexlib2.iface.instruction.WideLiteralInstruction
|
||||
import com.android.tools.smali.dexlib2.iface.reference.FieldReference
|
||||
import com.android.tools.smali.dexlib2.iface.reference.MethodReference
|
||||
import com.android.tools.smali.dexlib2.iface.reference.Reference
|
||||
import com.android.tools.smali.dexlib2.iface.reference.StringReference
|
||||
import com.android.tools.smali.dexlib2.immutable.ImmutableField
|
||||
import com.android.tools.smali.dexlib2.util.MethodUtil
|
||||
import java.util.EnumSet
|
||||
@@ -171,6 +174,79 @@ internal val Instruction.isBranchInstruction: Boolean
|
||||
internal val Instruction.isReturnInstruction: Boolean
|
||||
get() = this.opcode in returnOpcodes
|
||||
|
||||
/**
|
||||
* Find the instruction index used for a toString() StringBuilder write of a given String name.
|
||||
*
|
||||
* @param fieldName The name of the field to find. Partial matches are allowed.
|
||||
*/
|
||||
private fun Method.findInstructionIndexFromToString(fieldName: String) : Int {
|
||||
val stringIndex = indexOfFirstInstruction {
|
||||
val reference = getReference<StringReference>()
|
||||
reference?.string?.contains(fieldName) == true
|
||||
}
|
||||
if (stringIndex < 0) {
|
||||
throw IllegalArgumentException("Could not find usage of string: '$fieldName'")
|
||||
}
|
||||
val stringRegister = getInstruction<OneRegisterInstruction>(stringIndex).registerA
|
||||
|
||||
// Find use of the string with a StringBuilder.
|
||||
val stringUsageIndex = indexOfFirstInstruction(stringIndex) {
|
||||
val reference = getReference<MethodReference>()
|
||||
reference?.definingClass == "Ljava/lang/StringBuilder;" &&
|
||||
(this as? FiveRegisterInstruction)?.registerD == stringRegister
|
||||
}
|
||||
if (stringUsageIndex < 0) {
|
||||
throw IllegalArgumentException("Could not find StringBuilder usage in: $this")
|
||||
}
|
||||
|
||||
// Find the next usage of StringBuilder, which should be the desired field.
|
||||
val fieldUsageIndex = indexOfFirstInstruction(stringUsageIndex + 1) {
|
||||
val reference = getReference<MethodReference>()
|
||||
reference?.definingClass == "Ljava/lang/StringBuilder;" && reference.name == "append"
|
||||
}
|
||||
if (fieldUsageIndex < 0) {
|
||||
// Should never happen.
|
||||
throw IllegalArgumentException("Could not find StringBuilder append usage in: $this")
|
||||
}
|
||||
val fieldUsageRegister = getInstruction<FiveRegisterInstruction>(fieldUsageIndex).registerD
|
||||
|
||||
// Look backwards up the method to find the instruction that sets the register.
|
||||
var fieldSetIndex = indexOfFirstInstructionReversedOrThrow(fieldUsageIndex - 1) {
|
||||
fieldUsageRegister == writeRegister
|
||||
}
|
||||
|
||||
// If the field is a method call, then adjust from MOVE_RESULT to the method call.
|
||||
val fieldSetOpcode = getInstruction(fieldSetIndex).opcode
|
||||
if (fieldSetOpcode == MOVE_RESULT ||
|
||||
fieldSetOpcode == MOVE_RESULT_WIDE ||
|
||||
fieldSetOpcode == MOVE_RESULT_OBJECT) {
|
||||
fieldSetIndex--
|
||||
}
|
||||
|
||||
return fieldSetIndex
|
||||
}
|
||||
|
||||
/**
|
||||
* Find the method used for a toString() StringBuilder write of a given String name.
|
||||
*
|
||||
* @param fieldName The name of the field to find. Partial matches are allowed.
|
||||
*/
|
||||
context(BytecodePatchContext)
|
||||
internal fun Method.findMethodFromToString(fieldName: String) : MutableMethod {
|
||||
val methodUsageIndex = findInstructionIndexFromToString(fieldName)
|
||||
return navigate(this).to(methodUsageIndex).stop()
|
||||
}
|
||||
|
||||
/**
|
||||
* Find the field used for a toString() StringBuilder write of a given String name.
|
||||
*
|
||||
* @param fieldName The name of the field to find. Partial matches are allowed.
|
||||
*/
|
||||
internal fun Method.findFieldFromToString(fieldName: String) : FieldReference {
|
||||
val methodUsageIndex = findInstructionIndexFromToString(fieldName)
|
||||
return getInstruction<ReferenceInstruction>(methodUsageIndex).getReference<FieldReference>()!!
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds public [AccessFlags] and removes private and protected flags (if present).
|
||||
*/
|
||||
@@ -594,7 +670,7 @@ fun Method.indexOfFirstInstructionReversed(targetOpcode: Opcode): Int = indexOfF
|
||||
|
||||
/**
|
||||
* Get the index of matching instruction,
|
||||
* starting from and [startIndex] and searching down.
|
||||
* starting from [startIndex] and searching down.
|
||||
*
|
||||
* @param startIndex Optional starting index to search down from. Searching includes the start index.
|
||||
* @return The index of the instruction.
|
||||
@@ -617,7 +693,7 @@ fun Method.indexOfFirstInstructionReversedOrThrow(targetOpcode: Opcode): Int = i
|
||||
|
||||
/**
|
||||
* Get the index of matching instruction,
|
||||
* starting from and [startIndex] and searching down.
|
||||
* starting from [startIndex] and searching down.
|
||||
*
|
||||
* @param startIndex Optional starting index to search down from. Searching includes the start index.
|
||||
* @return The index of the instruction.
|
||||
|
||||
@@ -222,6 +222,8 @@ Second \"item\" text"</string>
|
||||
</patch>
|
||||
<patch id="video.speed.button.playbackSpeedButtonPatch">
|
||||
</patch>
|
||||
<patch id="video.quality.button.videoQualityButtonPatch">
|
||||
</patch>
|
||||
<patch id="video.speed.custom.customPlaybackSpeedPatch">
|
||||
</patch>
|
||||
<patch id="video.speed.remember.rememberPlaybackSpeedPatch">
|
||||
|
||||
@@ -222,6 +222,8 @@ Second \"item\" text"</string>
|
||||
</patch>
|
||||
<patch id="video.speed.button.playbackSpeedButtonPatch">
|
||||
</patch>
|
||||
<patch id="video.quality.button.videoQualityButtonPatch">
|
||||
</patch>
|
||||
<patch id="video.speed.custom.customPlaybackSpeedPatch">
|
||||
</patch>
|
||||
<patch id="video.speed.remember.rememberPlaybackSpeedPatch">
|
||||
|
||||
@@ -1465,6 +1465,11 @@ Second \"item\" text"</string>
|
||||
<string name="revanced_playback_speed_dialog_button_summary_on">الزر معروض. انقر مع الاستمرار لإعادة ضبط سرعة التشغيل إلى الوضع الافتراضي</string>
|
||||
<string name="revanced_playback_speed_dialog_button_summary_off">لا يتم عرض الزر</string>
|
||||
</patch>
|
||||
<patch id="video.quality.button.videoQualityButtonPatch">
|
||||
<string name="revanced_video_quality_dialog_button_title">عرض زر جودة الفيديو</string>
|
||||
<string name="revanced_video_quality_dialog_button_summary_on">الزر معروض. انقر مع الاستمرار لإعادة تعيين الجودة إلى الافتراضي</string>
|
||||
<string name="revanced_video_quality_dialog_button_summary_off">الزر غير معروض</string>
|
||||
</patch>
|
||||
<patch id="video.speed.custom.customPlaybackSpeedPatch">
|
||||
<string name="revanced_custom_speed_menu_title">قائمة سرعة التشغيل المخصصة</string>
|
||||
<string name="revanced_custom_speed_menu_summary_on">يتم عرض قائمة سرعة التشغيل المخصصة</string>
|
||||
|
||||
@@ -224,6 +224,8 @@ Second \"item\" text"</string>
|
||||
</patch>
|
||||
<patch id="video.speed.button.playbackSpeedButtonPatch">
|
||||
</patch>
|
||||
<patch id="video.quality.button.videoQualityButtonPatch">
|
||||
</patch>
|
||||
<patch id="video.speed.custom.customPlaybackSpeedPatch">
|
||||
</patch>
|
||||
<patch id="video.speed.remember.rememberPlaybackSpeedPatch">
|
||||
|
||||
@@ -1464,6 +1464,11 @@ Bunu aktivləşdirmə daha yüksək video keyfiyyətləri əngəlin silə bilər
|
||||
<string name="revanced_playback_speed_dialog_button_summary_on">Düymə göstərilir. Oynatma sürətin standart olaraq qaytarmaq üçün toxunub saxla</string>
|
||||
<string name="revanced_playback_speed_dialog_button_summary_off">Düymə göstərilmir</string>
|
||||
</patch>
|
||||
<patch id="video.quality.button.videoQualityButtonPatch">
|
||||
<string name="revanced_video_quality_dialog_button_title">Video keyfiyyəti düyməsini göstər</string>
|
||||
<string name="revanced_video_quality_dialog_button_summary_on">Düymə görünür. Keyfiyyəti ilkin vəziyyətinə qaytarmaq üçün toxunub saxlayın</string>
|
||||
<string name="revanced_video_quality_dialog_button_summary_off">Düymə görünmür</string>
|
||||
</patch>
|
||||
<patch id="video.speed.custom.customPlaybackSpeedPatch">
|
||||
<string name="revanced_custom_speed_menu_title">Fərdi oynatma sürəti siyahısı</string>
|
||||
<string name="revanced_custom_speed_menu_summary_on">Fərdi sürət siyahısı göstərilir</string>
|
||||
|
||||
@@ -1466,6 +1466,11 @@ Second \"item\" text"</string>
|
||||
<string name="revanced_playback_speed_dialog_button_summary_on">Кнопка паказана. Націсніце і ўтрымлівайце, каб скінуць хуткасць прайгравання да стандартнай</string>
|
||||
<string name="revanced_playback_speed_dialog_button_summary_off">Кнопка не паказваецца</string>
|
||||
</patch>
|
||||
<patch id="video.quality.button.videoQualityButtonPatch">
|
||||
<string name="revanced_video_quality_dialog_button_title">Паказаць кнопку якасці відэа</string>
|
||||
<string name="revanced_video_quality_dialog_button_summary_on">Кнопка паказана. Націсніце і ўтрымлівайце, каб скінуць якасць да па змаўчанні</string>
|
||||
<string name="revanced_video_quality_dialog_button_summary_off">Кнопка не паказваецца</string>
|
||||
</patch>
|
||||
<patch id="video.speed.custom.customPlaybackSpeedPatch">
|
||||
<string name="revanced_custom_speed_menu_title">Меню пользовательской скорости воспроизведения</string>
|
||||
<string name="revanced_custom_speed_menu_summary_on">Меню пользовательской скорости отображается</string>
|
||||
|
||||
@@ -1465,6 +1465,11 @@ Second \"item\" text"</string>
|
||||
<string name="revanced_playback_speed_dialog_button_summary_on">Бутонът е показан. Докоснете и задръжте, за да върнете скоростта на възпроизвеждане към стойността по подразбиране</string>
|
||||
<string name="revanced_playback_speed_dialog_button_summary_off">Бутонът не е показан</string>
|
||||
</patch>
|
||||
<patch id="video.quality.button.videoQualityButtonPatch">
|
||||
<string name="revanced_video_quality_dialog_button_title">Покажи бутона за качество на видеото</string>
|
||||
<string name="revanced_video_quality_dialog_button_summary_on">Бутонът е показан. Докоснете и задръжте, за да възстановите качеството до подразбиране</string>
|
||||
<string name="revanced_video_quality_dialog_button_summary_off">Бутонът не е показан</string>
|
||||
</patch>
|
||||
<patch id="video.speed.custom.customPlaybackSpeedPatch">
|
||||
<string name="revanced_custom_speed_menu_title">Менюто за потребителска скорост</string>
|
||||
<string name="revanced_custom_speed_menu_summary_on">Менюто за потребителска скорост се показва</string>
|
||||
|
||||
@@ -1461,6 +1461,11 @@ DeArrow সম্পর্কে আরও জানতে এখানে ট
|
||||
<string name="revanced_playback_speed_dialog_button_summary_on">বোতামটি দেখানো হয়েছে। প্লেব্যাক স্পীড ডিফল্টে রিসেট করতে ট্যাপ করে ধরে রাখুন।</string>
|
||||
<string name="revanced_playback_speed_dialog_button_summary_off">বোতাম প্রদর্শিত হয়নি</string>
|
||||
</patch>
|
||||
<patch id="video.quality.button.videoQualityButtonPatch">
|
||||
<string name="revanced_video_quality_dialog_button_title">ভিডিও গুণমান বোতাম দেখান</string>
|
||||
<string name="revanced_video_quality_dialog_button_summary_on">বোতামটি দেখানো হয়েছে। গুণমান ডিফল্টে রিসেট করতে ট্যাপ করে ধরে রাখুন।</string>
|
||||
<string name="revanced_video_quality_dialog_button_summary_off">বোতামটি দেখানো হয়নি।</string>
|
||||
</patch>
|
||||
<patch id="video.speed.custom.customPlaybackSpeedPatch">
|
||||
<string name="revanced_custom_speed_menu_title">কাস্টম প্লেব্যাক গতি মেনু</string>
|
||||
<string name="revanced_custom_speed_menu_summary_on">কাস্টম স্পিড মেনু দেখানো হচ্ছে</string>
|
||||
|
||||
@@ -222,6 +222,8 @@ Second \"item\" text"</string>
|
||||
</patch>
|
||||
<patch id="video.speed.button.playbackSpeedButtonPatch">
|
||||
</patch>
|
||||
<patch id="video.quality.button.videoQualityButtonPatch">
|
||||
</patch>
|
||||
<patch id="video.speed.custom.customPlaybackSpeedPatch">
|
||||
</patch>
|
||||
<patch id="video.speed.remember.rememberPlaybackSpeedPatch">
|
||||
|
||||
@@ -222,6 +222,8 @@ Second \"item\" text"</string>
|
||||
</patch>
|
||||
<patch id="video.speed.button.playbackSpeedButtonPatch">
|
||||
</patch>
|
||||
<patch id="video.quality.button.videoQualityButtonPatch">
|
||||
</patch>
|
||||
<patch id="video.speed.custom.customPlaybackSpeedPatch">
|
||||
</patch>
|
||||
<patch id="video.speed.remember.rememberPlaybackSpeedPatch">
|
||||
|
||||
@@ -1465,6 +1465,11 @@ Povolením této funkce lze odemknout vyšší kvality videa"</string>
|
||||
<string name="revanced_playback_speed_dialog_button_summary_on">Tlačítko je zobrazeno. Klepnutím a podržením obnovíte výchozí rychlost přehrávání</string>
|
||||
<string name="revanced_playback_speed_dialog_button_summary_off">Tlačítko se nezobrazuje</string>
|
||||
</patch>
|
||||
<patch id="video.quality.button.videoQualityButtonPatch">
|
||||
<string name="revanced_video_quality_dialog_button_title">Zobrazit tlačítko kvality videa</string>
|
||||
<string name="revanced_video_quality_dialog_button_summary_on">Tlačítko je zobrazeno. Klepnutím a podržením obnovíte kvalitu na výchozí</string>
|
||||
<string name="revanced_video_quality_dialog_button_summary_off">Tlačítko není zobrazeno.</string>
|
||||
</patch>
|
||||
<patch id="video.speed.custom.customPlaybackSpeedPatch">
|
||||
<string name="revanced_custom_speed_menu_title">Menu vlastní rychlosti přehrávání</string>
|
||||
<string name="revanced_custom_speed_menu_summary_on">Menu vlastní rychlosti se zobrazuje</string>
|
||||
|
||||
@@ -1467,6 +1467,11 @@ Aktivering af dette kan låse op for højere videokvalitet"</string>
|
||||
<string name="revanced_playback_speed_dialog_button_summary_on">Knappen vises. Tryk og hold for at nulstille afspilningshastigheden til standard.</string>
|
||||
<string name="revanced_playback_speed_dialog_button_summary_off">Knap vises ikke</string>
|
||||
</patch>
|
||||
<patch id="video.quality.button.videoQualityButtonPatch">
|
||||
<string name="revanced_video_quality_dialog_button_title">Vis videokvalitetsknap</string>
|
||||
<string name="revanced_video_quality_dialog_button_summary_on">Knap vises. Tryk og hold nede for at nulstille kvaliteten til standard</string>
|
||||
<string name="revanced_video_quality_dialog_button_summary_off">Knappen vises ikke</string>
|
||||
</patch>
|
||||
<patch id="video.speed.custom.customPlaybackSpeedPatch">
|
||||
<string name="revanced_custom_speed_menu_title">Tilpasset afspilningshastighed menu</string>
|
||||
<string name="revanced_custom_speed_menu_summary_on">Tilpasset hastighed menu er vist</string>
|
||||
|
||||
@@ -1460,6 +1460,11 @@ Durch Aktivieren dieser Option können höhere Videoqualitäten freigeschaltet w
|
||||
<string name="revanced_playback_speed_dialog_button_summary_on">Die Schaltfläche wird angezeigt. Tippen und halten, um die Wiedergabegeschwindigkeit auf die Standardeinstellung zurückzusetzen.</string>
|
||||
<string name="revanced_playback_speed_dialog_button_summary_off">Button wird nicht angezeigt</string>
|
||||
</patch>
|
||||
<patch id="video.quality.button.videoQualityButtonPatch">
|
||||
<string name="revanced_video_quality_dialog_button_title">Videoqualität-Schaltfläche anzeigen</string>
|
||||
<string name="revanced_video_quality_dialog_button_summary_on">Schaltfläche wird angezeigt. Tippen und halten, um die Qualität auf Standard zurückzusetzen</string>
|
||||
<string name="revanced_video_quality_dialog_button_summary_off">Schaltfläche wird nicht angezeigt</string>
|
||||
</patch>
|
||||
<patch id="video.speed.custom.customPlaybackSpeedPatch">
|
||||
<string name="revanced_custom_speed_menu_title">Benutzerdefiniertes Wiedergabegeschwindigkeitsmenü</string>
|
||||
<string name="revanced_custom_speed_menu_summary_on">Benutzerdefiniertes Geschwindigkeitsmenü wird angezeigt</string>
|
||||
|
||||
@@ -1464,6 +1464,11 @@ Second \"item\" text"</string>
|
||||
<string name="revanced_playback_speed_dialog_button_summary_on">Το κουμπί εμφανίζεται. Πατήστε παρατεταμένα για επαναφορά της ταχύτητας αναπαραγωγής στην προεπιλογή</string>
|
||||
<string name="revanced_playback_speed_dialog_button_summary_off">Το κουμπί δεν εμφανίζεται</string>
|
||||
</patch>
|
||||
<patch id="video.quality.button.videoQualityButtonPatch">
|
||||
<string name="revanced_video_quality_dialog_button_title">Εμφάνιση κουμπιού αλλαγής ποιότητας βίντεο</string>
|
||||
<string name="revanced_video_quality_dialog_button_summary_on">Το κουμπί εμφανίζεται. Πατήστε παρατεταμένα για επαναφορά της ποιότητας στην προεπιλογή</string>
|
||||
<string name="revanced_video_quality_dialog_button_summary_off">Το κουμπί δεν εμφανίζεται</string>
|
||||
</patch>
|
||||
<patch id="video.speed.custom.customPlaybackSpeedPatch">
|
||||
<string name="revanced_custom_speed_menu_title">Μενού προσαρμοσμένης ταχύτητας αναπαραγωγής</string>
|
||||
<string name="revanced_custom_speed_menu_summary_on">Το μενού προσαρμοσμένης ταχύτητας αναπαραγωγής εμφανίζεται</string>
|
||||
|
||||
@@ -1456,6 +1456,11 @@ Habilitar esto puede desbloquear calidades de vídeo más altas"</string>
|
||||
<string name="revanced_playback_speed_dialog_button_summary_on">Se muestra el botón. Mantén pulsado para restablecer la velocidad de reproducción predeterminada</string>
|
||||
<string name="revanced_playback_speed_dialog_button_summary_off">El botón no se muestra</string>
|
||||
</patch>
|
||||
<patch id="video.quality.button.videoQualityButtonPatch">
|
||||
<string name="revanced_video_quality_dialog_button_title">Mostrar botón de calidad de video</string>
|
||||
<string name="revanced_video_quality_dialog_button_summary_on">Botón visible. Toca y mantén para restablecer la calidad a los valores predeterminados</string>
|
||||
<string name="revanced_video_quality_dialog_button_summary_off">Botón no visible</string>
|
||||
</patch>
|
||||
<patch id="video.speed.custom.customPlaybackSpeedPatch">
|
||||
<string name="revanced_custom_speed_menu_title">Menú de velocidad de reproducción personalizada</string>
|
||||
<string name="revanced_custom_speed_menu_summary_on">Menú de velocidad personalizado se muestra</string>
|
||||
|
||||
@@ -1465,6 +1465,11 @@ Selle lubamine võib avada kõrgema video kvaliteedi"</string>
|
||||
<string name="revanced_playback_speed_dialog_button_summary_on">Nupp on nähtaval. Puudutage ja hoidke all, et taastada taasesituse kiirus vaikeväärtusele</string>
|
||||
<string name="revanced_playback_speed_dialog_button_summary_off">Nuppi ei kuvata</string>
|
||||
</patch>
|
||||
<patch id="video.quality.button.videoQualityButtonPatch">
|
||||
<string name="revanced_video_quality_dialog_button_title">Näita video kvaliteedi nuppu</string>
|
||||
<string name="revanced_video_quality_dialog_button_summary_on">Nupp on nähtaval. Kvaliteedi lähtestamiseks vaikeseadeteks puudutage ja hoidke</string>
|
||||
<string name="revanced_video_quality_dialog_button_summary_off">Nuppu ei kuvata</string>
|
||||
</patch>
|
||||
<patch id="video.speed.custom.customPlaybackSpeedPatch">
|
||||
<string name="revanced_custom_speed_menu_title">Kohandatud taasesituse kiiruse menüü</string>
|
||||
<string name="revanced_custom_speed_menu_summary_on">Kohandatud kiiruse menüü kuvatakse</string>
|
||||
|
||||
@@ -222,6 +222,8 @@ Second \"item\" text"</string>
|
||||
</patch>
|
||||
<patch id="video.speed.button.playbackSpeedButtonPatch">
|
||||
</patch>
|
||||
<patch id="video.quality.button.videoQualityButtonPatch">
|
||||
</patch>
|
||||
<patch id="video.speed.custom.customPlaybackSpeedPatch">
|
||||
</patch>
|
||||
<patch id="video.speed.remember.rememberPlaybackSpeedPatch">
|
||||
|
||||
@@ -272,6 +272,8 @@ Second \"item\" text"</string>
|
||||
</patch>
|
||||
<patch id="video.speed.button.playbackSpeedButtonPatch">
|
||||
</patch>
|
||||
<patch id="video.quality.button.videoQualityButtonPatch">
|
||||
</patch>
|
||||
<patch id="video.speed.custom.customPlaybackSpeedPatch">
|
||||
</patch>
|
||||
<patch id="video.speed.remember.rememberPlaybackSpeedPatch">
|
||||
|
||||
@@ -1465,6 +1465,11 @@ Tämä voi avata korkealaatuisemmat videot"</string>
|
||||
<string name="revanced_playback_speed_dialog_button_summary_on">Painike näytetään. Napauta ja pidä pohjassa palauttaaksesi soiton nopeuden oletukseksi</string>
|
||||
<string name="revanced_playback_speed_dialog_button_summary_off">Painiketta ei näytetä</string>
|
||||
</patch>
|
||||
<patch id="video.quality.button.videoQualityButtonPatch">
|
||||
<string name="revanced_video_quality_dialog_button_title">Näytä videolaatupainike</string>
|
||||
<string name="revanced_video_quality_dialog_button_summary_on">Painike näkyy. Paina pitkään palauttaaksesi laadun oletukseksi</string>
|
||||
<string name="revanced_video_quality_dialog_button_summary_off">Painiketta ei näytetä</string>
|
||||
</patch>
|
||||
<patch id="video.speed.custom.customPlaybackSpeedPatch">
|
||||
<string name="revanced_custom_speed_menu_title">Mukautettu toistonopeusvalikko</string>
|
||||
<string name="revanced_custom_speed_menu_summary_on">Mukautettu nopeusvalikko näytetään</string>
|
||||
|
||||
@@ -1463,6 +1463,11 @@ Ang pagpapagana nito ay maaaring magbukas ng mas mataas na kalidad ng video"</st
|
||||
<string name="revanced_playback_speed_dialog_button_summary_on">Ipinapakita ang button. I-tap at i-hold para i-reset ang bilis ng pag-playback sa default</string>
|
||||
<string name="revanced_playback_speed_dialog_button_summary_off">Hindi ipinapakita ang button</string>
|
||||
</patch>
|
||||
<patch id="video.quality.button.videoQualityButtonPatch">
|
||||
<string name="revanced_video_quality_dialog_button_title">Ipakita ang button ng kalidad ng video</string>
|
||||
<string name="revanced_video_quality_dialog_button_summary_on">Nakalabas ang button. Pindutin nang matagal para ibalik sa default ang kalidad</string>
|
||||
<string name="revanced_video_quality_dialog_button_summary_off">Hindi ipinapakita ang buton</string>
|
||||
</patch>
|
||||
<patch id="video.speed.custom.customPlaybackSpeedPatch">
|
||||
<string name="revanced_custom_speed_menu_title">Menu ng pasadyang bilis ng pag-playback</string>
|
||||
<string name="revanced_custom_speed_menu_summary_on">Ipinapakita ang menu ng pasadyang bilis</string>
|
||||
|
||||
@@ -1466,6 +1466,11 @@ Activer cette option peut déverrouiller des qualités vidéo supérieures"</str
|
||||
<string name="revanced_playback_speed_dialog_button_summary_on">Le bouton est affiché. Appuyez longuement dessus pour rétablir la vitesse de lecture par défaut.</string>
|
||||
<string name="revanced_playback_speed_dialog_button_summary_off">Le bouton n\'est pas affiché</string>
|
||||
</patch>
|
||||
<patch id="video.quality.button.videoQualityButtonPatch">
|
||||
<string name="revanced_video_quality_dialog_button_title">Afficher le bouton de qualité vidéo</string>
|
||||
<string name="revanced_video_quality_dialog_button_summary_on">Le bouton est affiché. Appuyez longuement pour rétablir la qualité par défaut.</string>
|
||||
<string name="revanced_video_quality_dialog_button_summary_off">Le bouton n\'est pas affiché</string>
|
||||
</patch>
|
||||
<patch id="video.speed.custom.customPlaybackSpeedPatch">
|
||||
<string name="revanced_custom_speed_menu_title">Menu de vitesse de lecture personnalisée</string>
|
||||
<string name="revanced_custom_speed_menu_summary_on">Le menu de vitesse personnalisée est affiché</string>
|
||||
|
||||
@@ -1465,6 +1465,11 @@ Is féidir le seo caighdeáin físeáin níos airde a dhíghlasáil"</string>
|
||||
<string name="revanced_playback_speed_dialog_button_summary_on">Taispeántar an cnaipe. Tapáil agus coinnigh chun luas athsheinm a athshocrú go réamhshocrú</string>
|
||||
<string name="revanced_playback_speed_dialog_button_summary_off">Ní thaispeántar an cnaipe</string>
|
||||
</patch>
|
||||
<patch id="video.quality.button.videoQualityButtonPatch">
|
||||
<string name="revanced_video_quality_dialog_button_title">Taispeáin cnaipe cáilíochta físeáin</string>
|
||||
<string name="revanced_video_quality_dialog_button_summary_on">Tá cnaipe le feiceáil. Tapáil agus coinnigh chun cáilíocht a athshocrú mar réamhshocrú</string>
|
||||
<string name="revanced_video_quality_dialog_button_summary_off">Níl cnaipe le feiceáil</string>
|
||||
</patch>
|
||||
<patch id="video.speed.custom.customPlaybackSpeedPatch">
|
||||
<string name="revanced_custom_speed_menu_title">Roghchlár luas athsheinm saincheaptha</string>
|
||||
<string name="revanced_custom_speed_menu_summary_on">Taispeántar roghchlár luais saincheaptha</string>
|
||||
|
||||
@@ -222,6 +222,8 @@ Second \"item\" text"</string>
|
||||
</patch>
|
||||
<patch id="video.speed.button.playbackSpeedButtonPatch">
|
||||
</patch>
|
||||
<patch id="video.quality.button.videoQualityButtonPatch">
|
||||
</patch>
|
||||
<patch id="video.speed.custom.customPlaybackSpeedPatch">
|
||||
</patch>
|
||||
<patch id="video.speed.remember.rememberPlaybackSpeedPatch">
|
||||
|
||||
@@ -222,6 +222,8 @@ Second \"item\" text"</string>
|
||||
</patch>
|
||||
<patch id="video.speed.button.playbackSpeedButtonPatch">
|
||||
</patch>
|
||||
<patch id="video.quality.button.videoQualityButtonPatch">
|
||||
</patch>
|
||||
<patch id="video.speed.custom.customPlaybackSpeedPatch">
|
||||
</patch>
|
||||
<patch id="video.speed.remember.rememberPlaybackSpeedPatch">
|
||||
|
||||
@@ -222,6 +222,8 @@ Second \"item\" text"</string>
|
||||
</patch>
|
||||
<patch id="video.speed.button.playbackSpeedButtonPatch">
|
||||
</patch>
|
||||
<patch id="video.quality.button.videoQualityButtonPatch">
|
||||
</patch>
|
||||
<patch id="video.speed.custom.customPlaybackSpeedPatch">
|
||||
</patch>
|
||||
<patch id="video.speed.remember.rememberPlaybackSpeedPatch">
|
||||
|
||||
@@ -222,6 +222,8 @@ Second \"item\" text"</string>
|
||||
</patch>
|
||||
<patch id="video.speed.button.playbackSpeedButtonPatch">
|
||||
</patch>
|
||||
<patch id="video.quality.button.videoQualityButtonPatch">
|
||||
</patch>
|
||||
<patch id="video.speed.custom.customPlaybackSpeedPatch">
|
||||
</patch>
|
||||
<patch id="video.speed.remember.rememberPlaybackSpeedPatch">
|
||||
|
||||
@@ -1462,6 +1462,11 @@ Ez a beállítás lehetővé teszi a magasabb videóminőségek feloldását"</s
|
||||
<string name="revanced_playback_speed_dialog_button_summary_on">A gomb látható. Tartsa lenyomva a lejátszási sebesség alapértelmezettre állításához</string>
|
||||
<string name="revanced_playback_speed_dialog_button_summary_off">A gomb nem látható</string>
|
||||
</patch>
|
||||
<patch id="video.quality.button.videoQualityButtonPatch">
|
||||
<string name="revanced_video_quality_dialog_button_title">Videóminőség gomb megjelenítése</string>
|
||||
<string name="revanced_video_quality_dialog_button_summary_on">A gomb látható. Tartsa lenyomva a minőséget az alapértelmezettre visszaállításához</string>
|
||||
<string name="revanced_video_quality_dialog_button_summary_off">A gomb nem látható</string>
|
||||
</patch>
|
||||
<patch id="video.speed.custom.customPlaybackSpeedPatch">
|
||||
<string name="revanced_custom_speed_menu_title">Egyedi lejátszási sebesség menü</string>
|
||||
<string name="revanced_custom_speed_menu_summary_on">Megjelenik az egyéni sebesség menü</string>
|
||||
|
||||
@@ -1466,6 +1466,11 @@ Mini-player-ը կարող է գրավվել էկրանից դուրս՝ դեպի
|
||||
<string name="revanced_playback_speed_dialog_button_summary_on">Կոճակը ցուցադրվում է: Հպեք և պահեք՝ նվագարկման արագությունը լռելյայնի վերականգնելու համար</string>
|
||||
<string name="revanced_playback_speed_dialog_button_summary_off">Կոճակը չի ցուցադրվում</string>
|
||||
</patch>
|
||||
<patch id="video.quality.button.videoQualityButtonPatch">
|
||||
<string name="revanced_video_quality_dialog_button_title">Ցուցադրել տեսանյութի որակի կոճակը</string>
|
||||
<string name="revanced_video_quality_dialog_button_summary_on">Կոճակը ցուցադրված է։ Հպեք և պահեք՝ որակը լռելյայնին վերականգնելու համար</string>
|
||||
<string name="revanced_video_quality_dialog_button_summary_off">Կոճակը ցուցադրված չէ։</string>
|
||||
</patch>
|
||||
<patch id="video.speed.custom.customPlaybackSpeedPatch">
|
||||
<string name="revanced_custom_speed_menu_title">Տեսանյութի արագության հարմարեցված մենյու</string>
|
||||
<string name="revanced_custom_speed_menu_summary_on">Հարմարեցված արագության մենյուը ցուցադրվում է</string>
|
||||
|
||||
@@ -1464,6 +1464,11 @@ Mengaktifkan ini dapat membuka kualitas video yang lebih tinggi"</string>
|
||||
<string name="revanced_playback_speed_dialog_button_summary_on">Tombol ditampilkan. Ketuk dan tahan untuk mengatur ulang kecepatan pemutaran ke bawaan</string>
|
||||
<string name="revanced_playback_speed_dialog_button_summary_off">Tombol tidak ditampilkan</string>
|
||||
</patch>
|
||||
<patch id="video.quality.button.videoQualityButtonPatch">
|
||||
<string name="revanced_video_quality_dialog_button_title">Tampilkan tombol kualitas video</string>
|
||||
<string name="revanced_video_quality_dialog_button_summary_on">Tombol ditampilkan. Ketuk dan tahan untuk mengatur ulang kualitas ke bawaan</string>
|
||||
<string name="revanced_video_quality_dialog_button_summary_off">Tombol tidak ditampilkan</string>
|
||||
</patch>
|
||||
<patch id="video.speed.custom.customPlaybackSpeedPatch">
|
||||
<string name="revanced_custom_speed_menu_title">Menu kecepatan pemutaran khusus</string>
|
||||
<string name="revanced_custom_speed_menu_summary_on">Menu kecepatan khusus ditampilkan</string>
|
||||
|
||||
@@ -222,6 +222,8 @@ Second \"item\" text"</string>
|
||||
</patch>
|
||||
<patch id="video.speed.button.playbackSpeedButtonPatch">
|
||||
</patch>
|
||||
<patch id="video.quality.button.videoQualityButtonPatch">
|
||||
</patch>
|
||||
<patch id="video.speed.custom.customPlaybackSpeedPatch">
|
||||
</patch>
|
||||
<patch id="video.speed.remember.rememberPlaybackSpeedPatch">
|
||||
|
||||
@@ -1464,6 +1464,11 @@ Abilitare questa opzione può sbloccare qualità video più elevate"</string>
|
||||
<string name="revanced_playback_speed_dialog_button_summary_on">Il pulsante è visualizzato. Tieni premuto per ripristinare la velocità di riproduzione predefinita</string>
|
||||
<string name="revanced_playback_speed_dialog_button_summary_off">Il pulsante non è visibile</string>
|
||||
</patch>
|
||||
<patch id="video.quality.button.videoQualityButtonPatch">
|
||||
<string name="revanced_video_quality_dialog_button_title">Mostra il pulsante qualità video</string>
|
||||
<string name="revanced_video_quality_dialog_button_summary_on">Il pulsante è visualizzato. Tocca e tieni premuto per ripristinare la qualità predefinita</string>
|
||||
<string name="revanced_video_quality_dialog_button_summary_off">Il pulsante non è visibile.</string>
|
||||
</patch>
|
||||
<patch id="video.speed.custom.customPlaybackSpeedPatch">
|
||||
<string name="revanced_custom_speed_menu_title">Menu di velocità di riproduzione personalizzato</string>
|
||||
<string name="revanced_custom_speed_menu_summary_on">Il menu di velocità personalizzato è visibile</string>
|
||||
|
||||
@@ -1467,6 +1467,11 @@ Second \"item\" text"</string>
|
||||
<string name="revanced_playback_speed_dialog_button_summary_on">הלחצן מוצג. יש להקיש ולהחזיק כדי לאפס את מהירות ההפעלה לברירת מחדל</string>
|
||||
<string name="revanced_playback_speed_dialog_button_summary_off">הלחצן אינו מוצג</string>
|
||||
</patch>
|
||||
<patch id="video.quality.button.videoQualityButtonPatch">
|
||||
<string name="revanced_video_quality_dialog_button_title">הצג כפתור איכות וידאו</string>
|
||||
<string name="revanced_video_quality_dialog_button_summary_on">הלחצן מוצג. גע והחזק כדי לאפס את האיכות לברירת מחדל</string>
|
||||
<string name="revanced_video_quality_dialog_button_summary_off">הלחצן אינו מוצג</string>
|
||||
</patch>
|
||||
<patch id="video.speed.custom.customPlaybackSpeedPatch">
|
||||
<string name="revanced_custom_speed_menu_title">תפריט מהירות הפעלה מותאם אישית</string>
|
||||
<string name="revanced_custom_speed_menu_summary_on">תפריט מהירות מותאם אישית מוצג</string>
|
||||
|
||||
@@ -107,8 +107,8 @@ GmsCore の電池の最適化を無効にしても、バッテリーの使用に
|
||||
</patch>
|
||||
<patch id="misc.backgroundplayback.backgroundPlaybackPatch">
|
||||
<string name="revanced_shorts_disable_background_playback_title">ショートのバックグラウンド再生を無効化</string>
|
||||
<string name="revanced_shorts_disable_background_playback_summary_on">ショートのバックグラウンド再生は無効です</string>
|
||||
<string name="revanced_shorts_disable_background_playback_summary_off">ショートのバックグラウンド再生は有効です</string>
|
||||
<string name="revanced_shorts_disable_background_playback_summary_on">ショート動画のバックグラウンド再生は無効です</string>
|
||||
<string name="revanced_shorts_disable_background_playback_summary_off">ショート動画のバックグラウンド再生は有効です</string>
|
||||
</patch>
|
||||
<patch id="misc.debugging.enableDebuggingPatch">
|
||||
<string name="revanced_debug_screen_title">デバッグ</string>
|
||||
@@ -301,9 +301,9 @@ GmsCore の電池の最適化を無効にしても、バッテリーの使用に
|
||||
<string name="revanced_hide_for_you_shelf_title">「おすすめ」欄を非表示</string>
|
||||
<string name="revanced_hide_for_you_shelf_summary_on">「おすすめ」欄は表示されません</string>
|
||||
<string name="revanced_hide_for_you_shelf_summary_off">「おすすめ」欄は表示されます</string>
|
||||
<string name="revanced_hide_links_preview_title">リンク集のプレビューを非表示</string>
|
||||
<string name="revanced_hide_links_preview_summary_on">リンク集のプレビューは表示されません</string>
|
||||
<string name="revanced_hide_links_preview_summary_off">リンク集のプレビューは表示されます</string>
|
||||
<string name="revanced_hide_links_preview_title">リンクのプレビューを非表示</string>
|
||||
<string name="revanced_hide_links_preview_summary_on">リンクのプレビューは表示されません</string>
|
||||
<string name="revanced_hide_links_preview_summary_off">リンクのプレビューは表示されます</string>
|
||||
<string name="revanced_hide_members_shelf_title">メンバー欄を非表示</string>
|
||||
<string name="revanced_hide_members_shelf_summary_on">メンバー欄は表示されません</string>
|
||||
<string name="revanced_hide_members_shelf_summary_off">メンバー欄は表示されます</string>
|
||||
@@ -356,21 +356,21 @@ GmsCore の電池の最適化を無効にしても、バッテリーの使用に
|
||||
<!-- 'Component path builder strings' is the technical name for identifying the Litho UI layout items to hide. This is an advanced feature and most users will never use this. -->
|
||||
<string name="revanced_custom_filter_strings_summary">非表示にしたい component の path builder string を改行区切りで入力します</string>
|
||||
<string name="revanced_custom_filter_toast_invalid_syntax">無効なカスタムフィルタ: %s</string>
|
||||
<string name="revanced_hide_keyword_content_screen_title">キーワード フィルタ</string>
|
||||
<string name="revanced_hide_keyword_content_screen_summary">キーワードフィルタを使用してフィード、検索結果に表示される動画を非表示にします</string>
|
||||
<string name="revanced_hide_keyword_content_screen_title">キーワードでコンテンツを非表示</string>
|
||||
<string name="revanced_hide_keyword_content_screen_summary">キーワード フィルタを使用してフィード、検索結果に表示される動画を非表示にします</string>
|
||||
<string name="revanced_hide_keyword_content_home_title">ホームの動画をキーワードで非表示</string>
|
||||
<string name="revanced_hide_keyword_content_home_summary_on">ホームタブの動画は、キーワードでフィルタリングされます</string>
|
||||
<string name="revanced_hide_keyword_content_home_summary_off">ホームタブの動画は、キーワードでフィルタリングされません</string>
|
||||
<string name="revanced_hide_keyword_content_home_summary_on">ホームタブの動画はキーワードでフィルタリングされます</string>
|
||||
<string name="revanced_hide_keyword_content_home_summary_off">ホームタブの動画はキーワードでフィルタリングされません</string>
|
||||
<string name="revanced_hide_keyword_content_search_title">検索結果をキーワードで非表示</string>
|
||||
<string name="revanced_hide_keyword_content_search_summary_on">検索結果は、キーワードでフィルタリングされます</string>
|
||||
<string name="revanced_hide_keyword_content_search_summary_off">検索結果は、キーワードでフィルタリングされません</string>
|
||||
<string name="revanced_hide_keyword_content_search_summary_on">検索結果はキーワードでフィルタリングされます</string>
|
||||
<string name="revanced_hide_keyword_content_search_summary_off">検索結果はキーワードでフィルタリングされません</string>
|
||||
<string name="revanced_hide_keyword_content_subscriptions_title">登録チャンネルの動画をキーワードで非表示</string>
|
||||
<string name="revanced_hide_keyword_content_subscriptions_summary_on">登録チャンネルタブの動画は、キーワードでフィルタリングされます</string>
|
||||
<string name="revanced_hide_keyword_content_subscriptions_summary_off">登録チャンネルタブの動画は、キーワードでフィルタリングされません</string>
|
||||
<string name="revanced_hide_keyword_content_subscriptions_summary_on">登録チャンネルタブの動画はキーワードでフィルタリングされます</string>
|
||||
<string name="revanced_hide_keyword_content_subscriptions_summary_off">登録チャンネルタブの動画はキーワードでフィルタリングされません</string>
|
||||
<string name="revanced_hide_keyword_content_phrases_title">キーワード</string>
|
||||
<!-- For localization, it is preferred, but not required, if 'LeBlanc' is replaced with a localized name or a familiar word that has upper case letters in the middle of the word.
|
||||
This is because keywords can be in any language, and showing an example in the localized script helps convey this. -->
|
||||
<string name="revanced_hide_keyword_content_phrases_summary">"キーワードとなる単語やフレーズを改行区切りで入力します
|
||||
<string name="revanced_hide_keyword_content_phrases_summary">"キーワードとなる単語やフレーズのリスト (改行区切り)
|
||||
|
||||
動画のタイトルまたはチャンネル名にキーワードが含まれる動画が非表示の対象になります
|
||||
|
||||
@@ -379,7 +379,7 @@ GmsCore の電池の最適化を無効にしても、バッテリーの使用に
|
||||
<string name="revanced_hide_keyword_content_about_summary">"ホーム / 登録チャンネル / 検索結果でキーワードに合致する動画を非表示にします
|
||||
|
||||
制限事項
|
||||
• ショート動画はチャンネル名で除外されない
|
||||
• ショート動画はチャンネル名で非表示にできない
|
||||
• 一部の UI コンポーネントが残ってしまう場合がある
|
||||
• キーワードを検索したとき、結果が表示されない場合がある"</string>
|
||||
<string name="revanced_hide_keyword_content_about_whole_words_title">単語全体で合致</string>
|
||||
@@ -475,9 +475,9 @@ GmsCore の電池の最適化を無効にしても、バッテリーの使用に
|
||||
<string name="revanced_external_downloader_other_item">その他</string>
|
||||
<string name="revanced_external_downloader_not_found_title">アプリがインストールされていません</string>
|
||||
<string name="revanced_external_downloader_not_installed_warning">%s はインストールされていません。インストールしてください。</string>
|
||||
<string name="revanced_external_downloader_package_not_found_warning">"パッケージ名 %s のインストール済みアプリが見つかりませんでした
|
||||
<string name="revanced_external_downloader_package_not_found_warning">"パッケージ名「%s」のインストール済みアプリが見つかりませんでした
|
||||
|
||||
パッケージ名が正しいこと、およびアプリがインストールされていることを確認してください"</string>
|
||||
パッケージ名が正しいこと、アプリがインストールされていることを確認してください"</string>
|
||||
<string name="revanced_external_downloader_empty_warning">パッケージ名は空欄にはできません</string>
|
||||
</patch>
|
||||
<patch id="interaction.seekbar.disablePreciseSeekingGesturePatch">
|
||||
@@ -1235,9 +1235,9 @@ Automotive レイアウト
|
||||
<string name="revanced_change_start_page_always_summary_off">スタート画面の変更は、アプリ起動時にのみ適用されます</string>
|
||||
</patch>
|
||||
<patch id="layout.startupshortsreset.disableResumingShortsOnStartupPatch">
|
||||
<string name="revanced_disable_resuming_shorts_player_title">ショート動画プレーヤーの再開を無効にする</string>
|
||||
<string name="revanced_disable_resuming_shorts_player_summary_on">ショート動画プレーヤーは、アプリ起動時に再開されます</string>
|
||||
<string name="revanced_disable_resuming_shorts_player_summary_off">ショート動画プレーヤーは、アプリの起動時に再開されません</string>
|
||||
<string name="revanced_disable_resuming_shorts_player_title">ショート プレーヤーの再開を無効化</string>
|
||||
<string name="revanced_disable_resuming_shorts_player_summary_on">アプリ起動時にショート プレーヤーは再開されません</string>
|
||||
<string name="revanced_disable_resuming_shorts_player_summary_off">アプリ起動時にショート プレーヤーが再開されます</string>
|
||||
</patch>
|
||||
<patch id="layout.shortsplayer.shortsPlayerTypePatch">
|
||||
<string name="revanced_shorts_player_type_title">ショート動画を開くプレーヤー</string>
|
||||
@@ -1348,8 +1348,8 @@ Automotive レイアウト
|
||||
|
||||
詳細については、ここをタップしてください"</string>
|
||||
<string name="revanced_alt_thumbnail_dearrow_connection_toast_title">API 利用不可時にトーストを表示</string>
|
||||
<string name="revanced_alt_thumbnail_dearrow_connection_toast_summary_on">DeArrow が利用できない場合は、トーストポップアップが表示されます</string>
|
||||
<string name="revanced_alt_thumbnail_dearrow_connection_toast_summary_off">DeArrow が利用できない場合でも、トースト ポップアップは表示されません</string>
|
||||
<string name="revanced_alt_thumbnail_dearrow_connection_toast_summary_on">DeArrow が利用できない場合にトースト通知が表示されます</string>
|
||||
<string name="revanced_alt_thumbnail_dearrow_connection_toast_summary_off">DeArrow が利用できない場合にトースト通知は表示されません</string>
|
||||
<string name="revanced_alt_thumbnail_dearrow_api_url_title">DeArrow API のエンドポイント</string>
|
||||
<string name="revanced_alt_thumbnail_dearrow_api_url_summary">DeArrow がサムネイルのキャッシュを取得するエンドポイントの URL</string>
|
||||
<string name="revanced_alt_thumbnail_stills_about_title">静止画サムネイル</string>
|
||||
@@ -1461,10 +1461,15 @@ Automotive レイアウト
|
||||
<string name="revanced_remember_video_quality_toast_shorts">ショートの画質 (%1$s): %2$s</string>
|
||||
</patch>
|
||||
<patch id="video.speed.button.playbackSpeedButtonPatch">
|
||||
<string name="revanced_playback_speed_dialog_button_title">再生速度設定ボタンを表示する</string>
|
||||
<string name="revanced_playback_speed_dialog_button_summary_on">ボタンはオーバーレイに表示されます。長押しすると、再生速度がデフォルトの値にリセットされます</string>
|
||||
<string name="revanced_playback_speed_dialog_button_title">再生速度設定ボタンを表示</string>
|
||||
<string name="revanced_playback_speed_dialog_button_summary_on">ボタンがオーバーレイに表示されます。長押しすると、再生速度がデフォルトの値にリセットされます</string>
|
||||
<string name="revanced_playback_speed_dialog_button_summary_off">ボタンはオーバーレイに表示されません</string>
|
||||
</patch>
|
||||
<patch id="video.quality.button.videoQualityButtonPatch">
|
||||
<string name="revanced_video_quality_dialog_button_title">画質設定ボタンを表示</string>
|
||||
<string name="revanced_video_quality_dialog_button_summary_on">ボタンがオーバーレイに表示されます。長押しすると、画質がデフォルトの値にリセットされます</string>
|
||||
<string name="revanced_video_quality_dialog_button_summary_off">ボタンはオーバーレイに表示されません</string>
|
||||
</patch>
|
||||
<patch id="video.speed.custom.customPlaybackSpeedPatch">
|
||||
<string name="revanced_custom_speed_menu_title">カスタム再生速度メニュー</string>
|
||||
<string name="revanced_custom_speed_menu_summary_on">カスタム再生速度リストが再生速度メニューに表示されます</string>
|
||||
@@ -1497,7 +1502,7 @@ Automotive レイアウト
|
||||
</patch>
|
||||
<patch id="video.quality.advancedVideoQualityMenuPatch">
|
||||
<string name="revanced_advanced_video_quality_menu_title">画質の詳細設定メニューを表示</string>
|
||||
<string name="revanced_advanced_video_quality_menu_summary_on">画質メニューとして詳細設定メニューが表示されます</string>
|
||||
<string name="revanced_advanced_video_quality_menu_summary_on">詳細設定メニューが画質メニューとして表示されます</string>
|
||||
<string name="revanced_advanced_video_quality_menu_summary_off">通常の画質メニューが表示されます</string>
|
||||
</patch>
|
||||
<patch id="interaction.seekbar.enableSlideToSeekPatch">
|
||||
|
||||
@@ -222,6 +222,8 @@ Second \"item\" text"</string>
|
||||
</patch>
|
||||
<patch id="video.speed.button.playbackSpeedButtonPatch">
|
||||
</patch>
|
||||
<patch id="video.quality.button.videoQualityButtonPatch">
|
||||
</patch>
|
||||
<patch id="video.speed.custom.customPlaybackSpeedPatch">
|
||||
</patch>
|
||||
<patch id="video.speed.remember.rememberPlaybackSpeedPatch">
|
||||
|
||||
@@ -222,6 +222,8 @@ Second \"item\" text"</string>
|
||||
</patch>
|
||||
<patch id="video.speed.button.playbackSpeedButtonPatch">
|
||||
</patch>
|
||||
<patch id="video.quality.button.videoQualityButtonPatch">
|
||||
</patch>
|
||||
<patch id="video.speed.custom.customPlaybackSpeedPatch">
|
||||
</patch>
|
||||
<patch id="video.speed.remember.rememberPlaybackSpeedPatch">
|
||||
|
||||
@@ -222,6 +222,8 @@ Second \"item\" text"</string>
|
||||
</patch>
|
||||
<patch id="video.speed.button.playbackSpeedButtonPatch">
|
||||
</patch>
|
||||
<patch id="video.quality.button.videoQualityButtonPatch">
|
||||
</patch>
|
||||
<patch id="video.speed.custom.customPlaybackSpeedPatch">
|
||||
</patch>
|
||||
<patch id="video.speed.remember.rememberPlaybackSpeedPatch">
|
||||
|
||||
@@ -237,6 +237,8 @@ Second \"item\" text"</string>
|
||||
</patch>
|
||||
<patch id="video.speed.button.playbackSpeedButtonPatch">
|
||||
</patch>
|
||||
<patch id="video.quality.button.videoQualityButtonPatch">
|
||||
</patch>
|
||||
<patch id="video.speed.custom.customPlaybackSpeedPatch">
|
||||
</patch>
|
||||
<patch id="video.speed.remember.rememberPlaybackSpeedPatch">
|
||||
|
||||
@@ -1472,6 +1472,11 @@ DeArrow에 대해 자세히 알아보려면 여기를 누르세요"</string>
|
||||
<string name="revanced_playback_speed_dialog_button_summary_on">버튼을 표시합니다\n\n• 버튼을 길게 누르면 동영상 재생 속도가 기본값으로 초기화됩니다</string>
|
||||
<string name="revanced_playback_speed_dialog_button_summary_off">버튼을 표시하지 않습니다</string>
|
||||
</patch>
|
||||
<patch id="video.quality.button.videoQualityButtonPatch">
|
||||
<string name="revanced_video_quality_dialog_button_title">동영상 화질 버튼 표시하기</string>
|
||||
<string name="revanced_video_quality_dialog_button_summary_on">버튼을 표시합니다\n\n• 버튼을 길게 누르면 화질이 기본값으로 초기화됩니다</string>
|
||||
<string name="revanced_video_quality_dialog_button_summary_off">버튼을 표시하지 않습니다</string>
|
||||
</patch>
|
||||
<patch id="video.speed.custom.customPlaybackSpeedPatch">
|
||||
<string name="revanced_custom_speed_menu_title">사용자 정의 동영상 재생 속도 활성화하기</string>
|
||||
<string name="revanced_custom_speed_menu_summary_on">사용자 정의 동영상 재생 속도를 활성화합니다</string>
|
||||
|
||||
@@ -222,6 +222,8 @@ Second \"item\" text"</string>
|
||||
</patch>
|
||||
<patch id="video.speed.button.playbackSpeedButtonPatch">
|
||||
</patch>
|
||||
<patch id="video.quality.button.videoQualityButtonPatch">
|
||||
</patch>
|
||||
<patch id="video.speed.custom.customPlaybackSpeedPatch">
|
||||
</patch>
|
||||
<patch id="video.speed.remember.rememberPlaybackSpeedPatch">
|
||||
|
||||
@@ -222,6 +222,8 @@ Second \"item\" text"</string>
|
||||
</patch>
|
||||
<patch id="video.speed.button.playbackSpeedButtonPatch">
|
||||
</patch>
|
||||
<patch id="video.quality.button.videoQualityButtonPatch">
|
||||
</patch>
|
||||
<patch id="video.speed.custom.customPlaybackSpeedPatch">
|
||||
</patch>
|
||||
<patch id="video.speed.remember.rememberPlaybackSpeedPatch">
|
||||
|
||||
@@ -1464,6 +1464,11 @@ Gali būti atrakinta aukštesnės vaizdo įrašų kokybės, bet galite patirti v
|
||||
<string name="revanced_playback_speed_dialog_button_summary_on">Mygtukas rodomas. Palieskite ir palaikykite, kad atkūrimo greitį nustatytumėte į numatytąjį.</string>
|
||||
<string name="revanced_playback_speed_dialog_button_summary_off">Mygtukas nerodomas</string>
|
||||
</patch>
|
||||
<patch id="video.quality.button.videoQualityButtonPatch">
|
||||
<string name="revanced_video_quality_dialog_button_title">Rodyti vaizdo kokybės mygtuką</string>
|
||||
<string name="revanced_video_quality_dialog_button_summary_on">Mygtukas rodomas. Palaikykite nuspaudę, kad atstatytumėte kokybę į numatytąją</string>
|
||||
<string name="revanced_video_quality_dialog_button_summary_off">Mygtukas nerodomas</string>
|
||||
</patch>
|
||||
<patch id="video.speed.custom.customPlaybackSpeedPatch">
|
||||
<string name="revanced_custom_speed_menu_title">Tinkintas atkūrimo greičio meniu</string>
|
||||
<string name="revanced_custom_speed_menu_summary_on">Tinkintas greičio meniu rodomas</string>
|
||||
|
||||
@@ -1466,6 +1466,11 @@ Var tikt atbloķētas augstākas video kvalitātes, taču var rasties video atsk
|
||||
<string name="revanced_playback_speed_dialog_button_summary_on">Poga ir redzama. Pieskarieties un turiet, lai atiestatītu atskaņošanas ātrumu uz noklusējuma</string>
|
||||
<string name="revanced_playback_speed_dialog_button_summary_off">Poga netiek rādīta</string>
|
||||
</patch>
|
||||
<patch id="video.quality.button.videoQualityButtonPatch">
|
||||
<string name="revanced_video_quality_dialog_button_title">Rādīt video kvalitātes pogu</string>
|
||||
<string name="revanced_video_quality_dialog_button_summary_on">Poga tiek rādīta. Pieskarieties un turiet, lai atiestatītu kvalitāti uz noklusējuma</string>
|
||||
<string name="revanced_video_quality_dialog_button_summary_off">Poga netiek rādīta</string>
|
||||
</patch>
|
||||
<patch id="video.speed.custom.customPlaybackSpeedPatch">
|
||||
<string name="revanced_custom_speed_menu_title">Pielāgotas atskaņošanas ātruma izvēlne</string>
|
||||
<string name="revanced_custom_speed_menu_summary_on">Pielāgotas ātruma izvēlne tiek rādīta</string>
|
||||
|
||||
@@ -222,6 +222,8 @@ Second \"item\" text"</string>
|
||||
</patch>
|
||||
<patch id="video.speed.button.playbackSpeedButtonPatch">
|
||||
</patch>
|
||||
<patch id="video.quality.button.videoQualityButtonPatch">
|
||||
</patch>
|
||||
<patch id="video.speed.custom.customPlaybackSpeedPatch">
|
||||
</patch>
|
||||
<patch id="video.speed.remember.rememberPlaybackSpeedPatch">
|
||||
|
||||
@@ -222,6 +222,8 @@ Second \"item\" text"</string>
|
||||
</patch>
|
||||
<patch id="video.speed.button.playbackSpeedButtonPatch">
|
||||
</patch>
|
||||
<patch id="video.quality.button.videoQualityButtonPatch">
|
||||
</patch>
|
||||
<patch id="video.speed.custom.customPlaybackSpeedPatch">
|
||||
</patch>
|
||||
<patch id="video.speed.remember.rememberPlaybackSpeedPatch">
|
||||
|
||||
@@ -222,6 +222,8 @@ Second \"item\" text"</string>
|
||||
</patch>
|
||||
<patch id="video.speed.button.playbackSpeedButtonPatch">
|
||||
</patch>
|
||||
<patch id="video.quality.button.videoQualityButtonPatch">
|
||||
</patch>
|
||||
<patch id="video.speed.custom.customPlaybackSpeedPatch">
|
||||
</patch>
|
||||
<patch id="video.speed.remember.rememberPlaybackSpeedPatch">
|
||||
|
||||
@@ -222,6 +222,8 @@ Second \"item\" text"</string>
|
||||
</patch>
|
||||
<patch id="video.speed.button.playbackSpeedButtonPatch">
|
||||
</patch>
|
||||
<patch id="video.quality.button.videoQualityButtonPatch">
|
||||
</patch>
|
||||
<patch id="video.speed.custom.customPlaybackSpeedPatch">
|
||||
</patch>
|
||||
<patch id="video.speed.remember.rememberPlaybackSpeedPatch">
|
||||
|
||||
@@ -223,6 +223,8 @@ Second \"item\" text"</string>
|
||||
</patch>
|
||||
<patch id="video.speed.button.playbackSpeedButtonPatch">
|
||||
</patch>
|
||||
<patch id="video.quality.button.videoQualityButtonPatch">
|
||||
</patch>
|
||||
<patch id="video.speed.custom.customPlaybackSpeedPatch">
|
||||
</patch>
|
||||
<patch id="video.speed.remember.rememberPlaybackSpeedPatch">
|
||||
|
||||
@@ -222,6 +222,8 @@ Second \"item\" text"</string>
|
||||
</patch>
|
||||
<patch id="video.speed.button.playbackSpeedButtonPatch">
|
||||
</patch>
|
||||
<patch id="video.quality.button.videoQualityButtonPatch">
|
||||
</patch>
|
||||
<patch id="video.speed.custom.customPlaybackSpeedPatch">
|
||||
</patch>
|
||||
<patch id="video.speed.remember.rememberPlaybackSpeedPatch">
|
||||
|
||||
@@ -222,6 +222,8 @@ Second \"item\" text"</string>
|
||||
</patch>
|
||||
<patch id="video.speed.button.playbackSpeedButtonPatch">
|
||||
</patch>
|
||||
<patch id="video.quality.button.videoQualityButtonPatch">
|
||||
</patch>
|
||||
<patch id="video.speed.custom.customPlaybackSpeedPatch">
|
||||
</patch>
|
||||
<patch id="video.speed.remember.rememberPlaybackSpeedPatch">
|
||||
|
||||
@@ -21,6 +21,8 @@ Second \"item\" text"</string>
|
||||
<resources>
|
||||
<app id="shared">
|
||||
<patch id="misc.checks.checkEnvironmentPatch">
|
||||
<string name="revanced_check_environment_manager_not_expected_installer">ReVanced Manager द्वारा स्थापित छैन</string>
|
||||
<string name="revanced_check_environment_not_near_patch_time_invalid">APK निर्माण मिति खराब भएको छ</string>
|
||||
</patch>
|
||||
<patch id="misc.settings.settingsResourcePatch">
|
||||
<!-- Settings about dialog. -->
|
||||
@@ -222,6 +224,8 @@ Second \"item\" text"</string>
|
||||
</patch>
|
||||
<patch id="video.speed.button.playbackSpeedButtonPatch">
|
||||
</patch>
|
||||
<patch id="video.quality.button.videoQualityButtonPatch">
|
||||
</patch>
|
||||
<patch id="video.speed.custom.customPlaybackSpeedPatch">
|
||||
</patch>
|
||||
<patch id="video.speed.remember.rememberPlaybackSpeedPatch">
|
||||
|
||||
@@ -1463,6 +1463,11 @@ Het inschakelen hiervan kan hogere videokwaliteiten ontgrendelen"</string>
|
||||
<string name="revanced_playback_speed_dialog_button_summary_on">De knop wordt weergegeven. Tik en houd vast om de afspeelsnelheid terug te zetten naar de standaardwaarde</string>
|
||||
<string name="revanced_playback_speed_dialog_button_summary_off">Knop wordt niet weergegeven</string>
|
||||
</patch>
|
||||
<patch id="video.quality.button.videoQualityButtonPatch">
|
||||
<string name="revanced_video_quality_dialog_button_title">Toon videokwaliteitknop</string>
|
||||
<string name="revanced_video_quality_dialog_button_summary_on">Knop wordt weergegeven. Tik en houd vast om de kwaliteit terug te zetten naar standaard</string>
|
||||
<string name="revanced_video_quality_dialog_button_summary_off">Knop wordt niet weergegeven</string>
|
||||
</patch>
|
||||
<patch id="video.speed.custom.customPlaybackSpeedPatch">
|
||||
<string name="revanced_custom_speed_menu_title">Menu voor aangepaste afspeelsnelheid</string>
|
||||
<string name="revanced_custom_speed_menu_summary_on">Menu voor aangepaste snelheid wordt weergegeven</string>
|
||||
|
||||
@@ -222,6 +222,8 @@ Second \"item\" text"</string>
|
||||
</patch>
|
||||
<patch id="video.speed.button.playbackSpeedButtonPatch">
|
||||
</patch>
|
||||
<patch id="video.quality.button.videoQualityButtonPatch">
|
||||
</patch>
|
||||
<patch id="video.speed.custom.customPlaybackSpeedPatch">
|
||||
</patch>
|
||||
<patch id="video.speed.remember.rememberPlaybackSpeedPatch">
|
||||
|
||||
@@ -222,6 +222,8 @@ Second \"item\" text"</string>
|
||||
</patch>
|
||||
<patch id="video.speed.button.playbackSpeedButtonPatch">
|
||||
</patch>
|
||||
<patch id="video.quality.button.videoQualityButtonPatch">
|
||||
</patch>
|
||||
<patch id="video.speed.custom.customPlaybackSpeedPatch">
|
||||
</patch>
|
||||
<patch id="video.speed.remember.rememberPlaybackSpeedPatch">
|
||||
|
||||
@@ -1461,6 +1461,11 @@ Włączenie tego może odblokować wyższe jakości wideo"</string>
|
||||
<string name="revanced_playback_speed_dialog_button_summary_on">Przycisk jest widoczny. Dotknij i przytrzymaj, aby zresetować prędkość odtwarzania do domyślnej</string>
|
||||
<string name="revanced_playback_speed_dialog_button_summary_off">Przycisk nie jest widoczny</string>
|
||||
</patch>
|
||||
<patch id="video.quality.button.videoQualityButtonPatch">
|
||||
<string name="revanced_video_quality_dialog_button_title">Pokaż przycisk jakości wideo</string>
|
||||
<string name="revanced_video_quality_dialog_button_summary_on">Przycisk jest widoczny. Dotknij i przytrzymaj, aby zresetować jakość do wartości domyślnych</string>
|
||||
<string name="revanced_video_quality_dialog_button_summary_off">Przycisk nie jest widoczny</string>
|
||||
</patch>
|
||||
<patch id="video.speed.custom.customPlaybackSpeedPatch">
|
||||
<string name="revanced_custom_speed_menu_title">Niestandardowe menu prędkości odtwarzania</string>
|
||||
<string name="revanced_custom_speed_menu_summary_on">Niestandardowe menu prędkości jest widoczne</string>
|
||||
|
||||
@@ -1464,6 +1464,11 @@ Habilitar isso pode desbloquear qualidades de vídeo mais altas"</string>
|
||||
<string name="revanced_playback_speed_dialog_button_summary_on">O botão é exibido. Toque e mantenha pressionado para redefinir a velocidade de reprodução para o padrão</string>
|
||||
<string name="revanced_playback_speed_dialog_button_summary_off">Botão não está visível</string>
|
||||
</patch>
|
||||
<patch id="video.quality.button.videoQualityButtonPatch">
|
||||
<string name="revanced_video_quality_dialog_button_title">Mostrar botão de qualidade de vídeo</string>
|
||||
<string name="revanced_video_quality_dialog_button_summary_on">O botão é exibido. Toque e segure para redefinir a qualidade para o padrão</string>
|
||||
<string name="revanced_video_quality_dialog_button_summary_off">O botão não é exibido</string>
|
||||
</patch>
|
||||
<patch id="video.speed.custom.customPlaybackSpeedPatch">
|
||||
<string name="revanced_custom_speed_menu_title">Menu de velocidade de reprodução personalizado</string>
|
||||
<string name="revanced_custom_speed_menu_summary_on">O menu de velocidade personalizado é mostrado</string>
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user