diff --git a/.github/workflows/discord_ping_on_release.yml b/.github/workflows/discord_ping_on_release.yml
index a826fcd43..5f5cd3d98 100644
--- a/.github/workflows/discord_ping_on_release.yml
+++ b/.github/workflows/discord_ping_on_release.yml
@@ -10,13 +10,33 @@ jobs:
steps:
- uses: sarisia/actions-status-discord@v1
if: always()
+ id: webhook # set id to reference output payload later
with:
- webhook: ${{ secrets.DISCORD_WEBHOOK_URL }}
- username: ReVanced Extended
- color: 0xff5252
+ ack_no_webhook: true # suppress warning
nodetail: true
notimestamp: true
+
+ username: ReVanced Extended
content: "<@&1271197877724643461>"
title: "Patches `${{ github.event.release.tag_name }}` has been released!"
description: |
- Click [here](${{ github.event.release.html_url }}) to read the changelog and release notes.
\ No newline at end of file
+ Click [here](${{ github.event.release.html_url }}) to read the changelog and release notes.
+
+ - run: npm install axios
+ - uses: actions/github-script@v7
+ env:
+ WEBHOOK_PAYLOAD: ${{ steps.webhook.outputs.payload }}
+ WEBHOOK_URL: ${{ secrets.DISCORD_WEBHOOK_URL }}
+ with:
+ script: |
+ const axios = require("axios")
+
+ const { WEBHOOK_PAYLOAD, WEBHOOK_URL } = process.env
+
+ const payload = JSON.parse(WEBHOOK_PAYLOAD)
+
+ // remove the color field to make it transparent
+ delete payload.embeds[0].color
+
+ // send to Discord
+ axios.post(WEBHOOK_URL, payload)
diff --git a/README.md b/README.md
index 66f9ce90e..1365d9be0 100644
--- a/README.md
+++ b/README.md
@@ -80,46 +80,46 @@ See the [documentation](https://github.com/inotia00/revanced-documentation#readm
| 💊 Patch | 📜 Description | 🏹 Target Version |
|:--------:|:--------------:|:-----------------:|
-| `Amoled` | Applies a pure black theme to some components. | 6.20.51 ~ 7.25.52 |
-| `Bitrate default value` | Sets the audio quality to 'Always High' when you first install the app. | 6.20.51 ~ 7.25.52 |
-| `Bypass image region restrictions` | Adds an option to use a different host for static images, so that images blocked in some countries can be received. | 6.20.51 ~ 7.25.52 |
-| `Certificate spoof` | Enables YouTube Music to work with Android Auto by spoofing the YouTube Music certificate. | 6.20.51 ~ 7.25.52 |
-| `Change share sheet` | Add option to change from in-app share sheet to system share sheet. | 6.20.51 ~ 7.25.52 |
-| `Change start page` | Adds an option to set which page the app opens in instead of the homepage. | 6.20.51 ~ 7.25.52 |
-| `Custom branding icon for YouTube Music` | Changes the YouTube Music app icon to the icon specified in patch options. | 6.20.51 ~ 7.25.52 |
-| `Custom branding name for YouTube Music` | Renames the YouTube Music app to the name specified in patch options. | 6.20.51 ~ 7.25.52 |
-| `Custom header for YouTube Music` | Applies a custom header in the top left corner within the app. | 6.20.51 ~ 7.25.52 |
-| `Disable Cairo splash animation` | Adds an option to disable Cairo splash animation. | 7.06.54 ~ 7.25.52 |
-| `Disable DRC audio` | Adds an option to disable DRC (Dynamic Range Compression) audio. | 6.20.51 ~ 7.25.52 |
-| `Disable auto captions` | Adds an option to disable captions from being automatically enabled. | 6.20.51 ~ 7.25.52 |
-| `Disable dislike redirection` | Adds an option to disable redirection to the next track when clicking the Dislike button. | 6.20.51 ~ 7.25.52 |
-| `Enable OPUS codec` | Adds an options to enable the OPUS audio codec if the player response includes. | 6.20.51 ~ 7.25.52 |
-| `Enable debug logging` | Adds an option to enable debug logging. | 6.20.51 ~ 7.25.52 |
-| `Enable landscape mode` | Adds an option to enable landscape mode when rotating the screen on phones. | 6.20.51 ~ 7.25.52 |
-| `Flyout menu components` | Adds options to hide or change flyout menu components. | 6.20.51 ~ 7.25.52 |
-| `GmsCore support` | Allows patched Google apps to run without root and under a different package name by using GmsCore instead of Google Play Services. | 6.20.51 ~ 7.25.52 |
-| `Hide account components` | Adds options to hide components related to the account menu. | 6.20.51 ~ 7.25.52 |
-| `Hide action bar components` | Adds options to hide action bar components and replace the offline download button with an external download button. | 6.20.51 ~ 7.25.52 |
-| `Hide ads` | Adds options to hide ads. | 6.20.51 ~ 7.25.52 |
-| `Hide layout components` | Adds options to hide general layout components. | 6.20.51 ~ 7.25.52 |
-| `Hide overlay filter` | Removes, at compile time, the dark overlay that appears when player flyout menus are open. | 6.20.51 ~ 7.25.52 |
-| `Hide player overlay filter` | Removes, at compile time, the dark overlay that appears when single-tapping in the player. | 6.20.51 ~ 7.25.52 |
-| `Navigation bar components` | Adds options to hide or change components related to the navigation bar. | 6.20.51 ~ 7.25.52 |
-| `Player components` | Adds options to hide or change components related to the player. | 6.20.51 ~ 7.25.52 |
-| `Remove background playback restrictions` | Removes restrictions on background playback, including for kids videos. | 6.20.51 ~ 7.25.52 |
-| `Remove viewer discretion dialog` | Adds an option to remove the dialog that appears when opening a video that has been age-restricted by accepting it automatically. This does not bypass the age restriction. | 6.20.51 ~ 7.25.52 |
-| `Restore old style library shelf` | Adds an option to return the Library tab to the old style. | 6.20.51 ~ 7.25.52 |
-| `Return YouTube Dislike` | Adds an option to show the dislike count of songs using the Return YouTube Dislike API. | 6.20.51 ~ 7.25.52 |
-| `Return YouTube Username` | Adds an option to replace YouTube handles with usernames in comments using YouTube Data API v3. | 6.20.51 ~ 7.25.52 |
-| `Sanitize sharing links` | Adds an option to remove tracking query parameters from URLs when sharing links. | 6.20.51 ~ 7.25.52 |
-| `Settings for YouTube Music` | Applies mandatory patches to implement ReVanced Extended settings into the application. | 6.20.51 ~ 7.25.52 |
-| `SponsorBlock` | Adds options to enable and configure SponsorBlock, which can skip undesired video segments, such as non-music sections. | 6.20.51 ~ 7.25.52 |
+| `Amoled` | Applies a pure black theme to some components. | 6.20.51 ~ 7.25.53 |
+| `Bitrate default value` | Sets the audio quality to 'Always High' when you first install the app. | 6.20.51 ~ 7.25.53 |
+| `Bypass image region restrictions` | Adds an option to use a different host for static images, so that images blocked in some countries can be received. | 6.20.51 ~ 7.25.53 |
+| `Certificate spoof` | Enables YouTube Music to work with Android Auto by spoofing the YouTube Music certificate. | 6.20.51 ~ 7.25.53 |
+| `Change share sheet` | Add option to change from in-app share sheet to system share sheet. | 6.20.51 ~ 7.25.53 |
+| `Change start page` | Adds an option to set which page the app opens in instead of the homepage. | 6.20.51 ~ 7.25.53 |
+| `Custom branding icon for YouTube Music` | Changes the YouTube Music app icon to the icon specified in patch options. | 6.20.51 ~ 7.25.53 |
+| `Custom branding name for YouTube Music` | Renames the YouTube Music app to the name specified in patch options. | 6.20.51 ~ 7.25.53 |
+| `Custom header for YouTube Music` | Applies a custom header in the top left corner within the app. | 6.20.51 ~ 7.25.53 |
+| `Disable Cairo splash animation` | Adds an option to disable Cairo splash animation. | 7.06.54 ~ 7.25.53 |
+| `Disable DRC audio` | Adds an option to disable DRC (Dynamic Range Compression) audio. | 6.20.51 ~ 7.25.53 |
+| `Disable auto captions` | Adds an option to disable captions from being automatically enabled. | 6.20.51 ~ 7.25.53 |
+| `Disable dislike redirection` | Adds an option to disable redirection to the next track when clicking the Dislike button. | 6.20.51 ~ 7.25.53 |
+| `Enable OPUS codec` | Adds an options to enable the OPUS audio codec if the player response includes. | 6.20.51 ~ 7.25.53 |
+| `Enable debug logging` | Adds an option to enable debug logging. | 6.20.51 ~ 7.25.53 |
+| `Enable landscape mode` | Adds an option to enable landscape mode when rotating the screen on phones. | 6.20.51 ~ 7.25.53 |
+| `Flyout menu components` | Adds options to hide or change flyout menu components. | 6.20.51 ~ 7.25.53 |
+| `GmsCore support` | Allows patched Google apps to run without root and under a different package name by using GmsCore instead of Google Play Services. | 6.20.51 ~ 7.25.53 |
+| `Hide account components` | Adds options to hide components related to the account menu. | 6.20.51 ~ 7.25.53 |
+| `Hide action bar components` | Adds options to hide action bar components and replace the offline download button with an external download button. | 6.20.51 ~ 7.25.53 |
+| `Hide ads` | Adds options to hide ads. | 6.20.51 ~ 7.25.53 |
+| `Hide layout components` | Adds options to hide general layout components. | 6.20.51 ~ 7.25.53 |
+| `Hide overlay filter` | Removes, at compile time, the dark overlay that appears when player flyout menus are open. | 6.20.51 ~ 7.25.53 |
+| `Hide player overlay filter` | Removes, at compile time, the dark overlay that appears when single-tapping in the player. | 6.20.51 ~ 7.25.53 |
+| `Navigation bar components` | Adds options to hide or change components related to the navigation bar. | 6.20.51 ~ 7.25.53 |
+| `Player components` | Adds options to hide or change components related to the player. | 6.20.51 ~ 7.25.53 |
+| `Remove background playback restrictions` | Removes restrictions on background playback, including for kids videos. | 6.20.51 ~ 7.25.53 |
+| `Remove viewer discretion dialog` | Adds an option to remove the dialog that appears when opening a video that has been age-restricted by accepting it automatically. This does not bypass the age restriction. | 6.20.51 ~ 7.25.53 |
+| `Restore old style library shelf` | Adds an option to return the Library tab to the old style. | 6.20.51 ~ 7.25.53 |
+| `Return YouTube Dislike` | Adds an option to show the dislike count of songs using the Return YouTube Dislike API. | 6.20.51 ~ 7.25.53 |
+| `Return YouTube Username` | Adds an option to replace YouTube handles with usernames in comments using YouTube Data API v3. | 6.20.51 ~ 7.25.53 |
+| `Sanitize sharing links` | Adds an option to remove tracking query parameters from URLs when sharing links. | 6.20.51 ~ 7.25.53 |
+| `Settings for YouTube Music` | Applies mandatory patches to implement ReVanced Extended settings into the application. | 6.20.51 ~ 7.25.53 |
+| `SponsorBlock` | Adds options to enable and configure SponsorBlock, which can skip undesired video segments, such as non-music sections. | 6.20.51 ~ 7.25.53 |
| `Spoof app version` | Adds options to spoof the YouTube Music client version. This can remove the radio mode restriction in Canadian regions or disable real-time lyrics. | 6.20.51 ~ 7.16.53 |
-| `Spoof client` | Adds options to spoof the client to allow playback. | 6.20.51 ~ 7.25.52 |
-| `Spoof streaming data` | Adds options to spoof the streaming data to allow playback. | 6.20.51 ~ 7.25.52 |
-| `Translations for YouTube Music` | Add translations or remove string resources. | 6.20.51 ~ 7.25.52 |
-| `Video playback` | Adds options to customize settings related to video playback, such as default video quality and playback speed. | 6.20.51 ~ 7.25.52 |
-| `Visual preferences icons for YouTube Music` | Adds icons to specific preferences in the settings. | 6.20.51 ~ 7.25.52 |
+| `Spoof client` | Adds options to spoof the client to allow playback. | 6.20.51 ~ 7.16.53 |
+| `Spoof streaming data` | Adds options to spoof the streaming data to allow playback. | 6.20.51 ~ 7.25.53 |
+| `Translations for YouTube Music` | Add translations or remove string resources. | 6.20.51 ~ 7.25.53 |
+| `Video playback` | Adds options to customize settings related to video playback, such as default video quality and playback speed. | 6.20.51 ~ 7.25.53 |
+| `Visual preferences icons for YouTube Music` | Adds icons to specific preferences in the settings. | 6.20.51 ~ 7.25.53 |
### [📦 `com.reddit.frontpage`](https://play.google.com/store/apps/details?id=com.reddit.frontpage)
@@ -180,7 +180,7 @@ Example:
"6.42.55",
"6.51.53",
"7.16.53",
- "7.25.52"
+ "7.25.53"
]
},
"options": []
diff --git a/extensions/shared/src/main/java/app/revanced/extension/music/patches/actionbar/ActionBarPatch.java b/extensions/shared/src/main/java/app/revanced/extension/music/patches/actionbar/ActionBarPatch.java
index 090565db6..d973918ee 100644
--- a/extensions/shared/src/main/java/app/revanced/extension/music/patches/actionbar/ActionBarPatch.java
+++ b/extensions/shared/src/main/java/app/revanced/extension/music/patches/actionbar/ActionBarPatch.java
@@ -8,6 +8,7 @@ import android.view.View;
import androidx.annotation.NonNull;
import app.revanced.extension.music.settings.Settings;
+import app.revanced.extension.music.utils.VideoUtils;
@SuppressWarnings("unused")
public class ActionBarPatch {
@@ -39,6 +40,15 @@ public class ActionBarPatch {
);
}
+ public static void inAppDownloadButtonOnClick(View view) {
+ if (!Settings.EXTERNAL_DOWNLOADER_ACTION_BUTTON.get()) {
+ return;
+ }
+
+ if (buttonType.equals(ActionButton.DOWNLOAD.name))
+ view.setOnClickListener(imageView -> VideoUtils.launchExternalDownloader());
+ }
+
public static void setButtonType(@NonNull Object obj) {
final String buttonType = obj.toString();
diff --git a/extensions/shared/src/main/java/app/revanced/extension/music/patches/misc/SpoofClientPatch.java b/extensions/shared/src/main/java/app/revanced/extension/music/patches/misc/SpoofClientPatch.java
index 19c5b6935..e3e651a36 100644
--- a/extensions/shared/src/main/java/app/revanced/extension/music/patches/misc/SpoofClientPatch.java
+++ b/extensions/shared/src/main/java/app/revanced/extension/music/patches/misc/SpoofClientPatch.java
@@ -2,11 +2,11 @@ package app.revanced.extension.music.patches.misc;
import app.revanced.extension.music.patches.misc.client.AppClient.ClientType;
import app.revanced.extension.music.settings.Settings;
-import app.revanced.extension.shared.patches.BlockRequestPatch;
@SuppressWarnings("unused")
-public class SpoofClientPatch extends BlockRequestPatch {
+public class SpoofClientPatch {
private static final ClientType CLIENT_TYPE = Settings.SPOOF_CLIENT_TYPE.get();
+ public static final boolean SPOOF_CLIENT = Settings.SPOOF_CLIENT.get();
/**
* Injection point.
diff --git a/extensions/shared/src/main/java/app/revanced/extension/music/patches/misc/client/AppClient.java b/extensions/shared/src/main/java/app/revanced/extension/music/patches/misc/client/AppClient.java
index 0a21f6196..487ef08c4 100644
--- a/extensions/shared/src/main/java/app/revanced/extension/music/patches/misc/client/AppClient.java
+++ b/extensions/shared/src/main/java/app/revanced/extension/music/patches/misc/client/AppClient.java
@@ -2,49 +2,78 @@ package app.revanced.extension.music.patches.misc.client;
import android.os.Build;
-import app.revanced.extension.music.settings.Settings;
-
public class AppClient {
- private static final String CLIENT_VERSION_ANDROID_MUSIC = Settings.SPOOF_CLIENT_LEGACY.get()
- ? "4.27.53" // Audio codec is MP4A.
- : "5.29.53"; // Audio codec is OPUS.
- private static final String OS_VERSION_ANDROID_MUSIC = Build.VERSION.RELEASE;
- private static final String USER_AGENT_ANDROID_MUSIC = "com.google.android.apps.youtube.music/" +
- CLIENT_VERSION_ANDROID_MUSIC +
- " (Linux; U; Android " +
- OS_VERSION_ANDROID_MUSIC +
- "; GB) gzip";
- private static final String CLIENT_VERSION_IOS_MUSIC = Settings.SPOOF_CLIENT_LEGACY.get()
- ? "4.27" // Audio codec is MP4A.
- : "7.31.2"; // Audio codec is OPUS.
+ // Audio codec is MP4A.
+ private static final String CLIENT_VERSION_ANDROID_MUSIC_4_27 = "4.27.53";
+
+ // Audio codec is OPUS.
+ private static final String CLIENT_VERSION_ANDROID_MUSIC_5_29 = "5.29.53";
+
+ private static final String PACKAGE_NAME_ANDROID_MUSIC = "com.google.android.apps.youtube.music";
+ private static final String DEVICE_MODEL_ANDROID_MUSIC = Build.MODEL;
+ private static final String OS_VERSION_ANDROID_MUSIC = Build.VERSION.RELEASE;
+
+ // Audio codec is MP4A.
+ private static final String CLIENT_VERSION_IOS_MUSIC_6_21 = "6.21";
+
+ // Audio codec is OPUS.
+ private static final String CLIENT_VERSION_IOS_MUSIC_7_04 = "7.04";
+
+ private static final String PACKAGE_NAME_IOS_MUSIC = "com.google.ios.youtubemusic";
private static final String DEVICE_MODEL_IOS_MUSIC = "iPhone14,3";
private static final String OS_VERSION_IOS_MUSIC = "15.7.1.19H117";
private static final String USER_AGENT_VERSION_IOS_MUSIC = "15_7_1";
- private static final String USER_AGENT_IOS_MUSIC = "com.google.ios.youtubemusic/" +
- CLIENT_VERSION_IOS_MUSIC +
- "(" +
- DEVICE_MODEL_IOS_MUSIC +
- "; U; CPU iOS " +
- USER_AGENT_VERSION_IOS_MUSIC +
- " like Mac OS X)";
private AppClient() {
}
+ private static String androidUserAgent(String clientVersion) {
+ return PACKAGE_NAME_ANDROID_MUSIC +
+ "/" +
+ clientVersion +
+ " (Linux; U; Android " +
+ OS_VERSION_ANDROID_MUSIC +
+ "; GB) gzip";
+ }
+
+ private static String iOSUserAgent(String clientVersion) {
+ return PACKAGE_NAME_IOS_MUSIC +
+ "/" +
+ clientVersion +
+ "(" +
+ DEVICE_MODEL_IOS_MUSIC +
+ "; U; CPU iOS " +
+ USER_AGENT_VERSION_IOS_MUSIC +
+ " like Mac OS X)";
+ }
+
public enum ClientType {
- ANDROID_MUSIC(21,
- Build.MODEL,
+ ANDROID_MUSIC_4_27(21,
+ DEVICE_MODEL_ANDROID_MUSIC,
OS_VERSION_ANDROID_MUSIC,
- USER_AGENT_ANDROID_MUSIC,
- CLIENT_VERSION_ANDROID_MUSIC
+ androidUserAgent(CLIENT_VERSION_ANDROID_MUSIC_4_27),
+ CLIENT_VERSION_ANDROID_MUSIC_4_27
),
- IOS_MUSIC(
+ ANDROID_MUSIC_5_29(21,
+ DEVICE_MODEL_ANDROID_MUSIC,
+ OS_VERSION_ANDROID_MUSIC,
+ androidUserAgent(CLIENT_VERSION_ANDROID_MUSIC_5_29),
+ CLIENT_VERSION_ANDROID_MUSIC_5_29
+ ),
+ IOS_MUSIC_6_21(
26,
DEVICE_MODEL_IOS_MUSIC,
OS_VERSION_IOS_MUSIC,
- USER_AGENT_IOS_MUSIC,
- CLIENT_VERSION_IOS_MUSIC
+ iOSUserAgent(CLIENT_VERSION_IOS_MUSIC_6_21),
+ CLIENT_VERSION_IOS_MUSIC_6_21
+ ),
+ IOS_MUSIC_7_04(
+ 26,
+ DEVICE_MODEL_IOS_MUSIC,
+ OS_VERSION_IOS_MUSIC,
+ iOSUserAgent(CLIENT_VERSION_IOS_MUSIC_7_04),
+ CLIENT_VERSION_IOS_MUSIC_7_04
);
/**
diff --git a/extensions/shared/src/main/java/app/revanced/extension/music/settings/Settings.java b/extensions/shared/src/main/java/app/revanced/extension/music/settings/Settings.java
index dfdedc6d2..77bac406c 100644
--- a/extensions/shared/src/main/java/app/revanced/extension/music/settings/Settings.java
+++ b/extensions/shared/src/main/java/app/revanced/extension/music/settings/Settings.java
@@ -180,8 +180,8 @@ public class Settings extends BaseSettings {
public static final BooleanSetting DISABLE_DRC_AUDIO = new BooleanSetting("revanced_disable_drc_audio", FALSE, true);
public static final BooleanSetting ENABLE_OPUS_CODEC = new BooleanSetting("revanced_enable_opus_codec", FALSE, true);
public static final BooleanSetting SETTINGS_IMPORT_EXPORT = new BooleanSetting("revanced_extended_settings_import_export", FALSE, false);
- public static final BooleanSetting SPOOF_CLIENT_LEGACY = new BooleanSetting("revanced_spoof_client_legacy", FALSE, true);
- public static final EnumSetting SPOOF_CLIENT_TYPE = new EnumSetting<>("revanced_spoof_client_type", ClientType.ANDROID_MUSIC, true);
+ public static final BooleanSetting SPOOF_CLIENT = new BooleanSetting("revanced_spoof_client", FALSE, true);
+ public static final EnumSetting SPOOF_CLIENT_TYPE = new EnumSetting<>("revanced_spoof_client_type", ClientType.IOS_MUSIC_6_21, true);
// PreferenceScreen: Return YouTube Dislike
diff --git a/extensions/shared/src/main/java/app/revanced/extension/shared/patches/BlockRequestPatch.java b/extensions/shared/src/main/java/app/revanced/extension/shared/patches/BlockRequestPatch.java
deleted file mode 100644
index d4c1c458e..000000000
--- a/extensions/shared/src/main/java/app/revanced/extension/shared/patches/BlockRequestPatch.java
+++ /dev/null
@@ -1,80 +0,0 @@
-package app.revanced.extension.shared.patches;
-
-import static app.revanced.extension.shared.patches.PatchStatus.SpoofClient;
-import static app.revanced.extension.shared.patches.PatchStatus.SpoofStreamingData;
-
-import android.net.Uri;
-
-import app.revanced.extension.shared.settings.BaseSettings;
-import app.revanced.extension.shared.utils.Logger;
-
-@SuppressWarnings("unused")
-public class BlockRequestPatch {
- /**
- * Used in YouTube and YouTube Music.
- */
- public static final boolean SPOOF_STREAMING_DATA = BaseSettings.SPOOF_STREAMING_DATA.get();
-
- /**
- * Used in YouTube Music.
- * Disabled by default.
- */
- public static final boolean SPOOF_CLIENT = BaseSettings.SPOOF_CLIENT.get();
-
- private static final boolean BLOCK_REQUEST = (SpoofStreamingData() && SPOOF_STREAMING_DATA) || (SpoofClient() && SPOOF_CLIENT);
-
- /**
- * Any unreachable ip address. Used to intentionally fail requests.
- */
- private static final String UNREACHABLE_HOST_URI_STRING = "https://127.0.0.0";
- private static final Uri UNREACHABLE_HOST_URI = Uri.parse(UNREACHABLE_HOST_URI_STRING);
-
- /**
- * Injection point.
- * Blocks /get_watch requests by returning an unreachable URI.
- *
- * @param playerRequestUri The URI of the player request.
- * @return An unreachable URI if the request is a /get_watch request, otherwise the original URI.
- */
- public static Uri blockGetWatchRequest(Uri playerRequestUri) {
- if (BLOCK_REQUEST) {
- try {
- String path = playerRequestUri.getPath();
-
- if (path != null && path.contains("get_watch")) {
- Logger.printDebug(() -> "Blocking 'get_watch' by returning unreachable uri");
-
- return UNREACHABLE_HOST_URI;
- }
- } catch (Exception ex) {
- Logger.printException(() -> "blockGetWatchRequest failure", ex);
- }
- }
-
- return playerRequestUri;
- }
-
- /**
- * Injection point.
- *
- * Blocks /initplayback requests.
- */
- public static String blockInitPlaybackRequest(String originalUrlString) {
- if (BLOCK_REQUEST) {
- try {
- var originalUri = Uri.parse(originalUrlString);
- String path = originalUri.getPath();
-
- if (path != null && path.contains("initplayback")) {
- Logger.printDebug(() -> "Blocking 'initplayback' by clearing query");
-
- return originalUri.buildUpon().clearQuery().build().toString();
- }
- } catch (Exception ex) {
- Logger.printException(() -> "blockInitPlaybackRequest failure", ex);
- }
- }
-
- return originalUrlString;
- }
-}
diff --git a/extensions/shared/src/main/java/app/revanced/extension/shared/patches/PatchStatus.java b/extensions/shared/src/main/java/app/revanced/extension/shared/patches/PatchStatus.java
index a90ce4e62..f5bcef97f 100644
--- a/extensions/shared/src/main/java/app/revanced/extension/shared/patches/PatchStatus.java
+++ b/extensions/shared/src/main/java/app/revanced/extension/shared/patches/PatchStatus.java
@@ -1,24 +1,18 @@
package app.revanced.extension.shared.patches;
-import app.revanced.extension.shared.patches.client.AppClient.ClientType;
-
@SuppressWarnings("unused")
public class PatchStatus {
public static boolean HideFullscreenAdsDefaultBoolean() {
return false;
}
- public static ClientType SpoofStreamingDataDefaultClient() {
- return ClientType.IOS;
- }
-
- public static boolean SpoofClient() {
- // Replace this with true If the Spoof client patch succeeds
- return false;
- }
-
public static boolean SpoofStreamingData() {
// Replace this with true If the Spoof streaming data patch succeeds
return false;
}
+
+ public static boolean SpoofStreamingDataAndroidOnlyDefaultBoolean() {
+ // Replace this with true If the Spoof streaming data patch succeeds in YouTube
+ return false;
+ }
}
diff --git a/extensions/shared/src/main/java/app/revanced/extension/shared/patches/client/AppClient.java b/extensions/shared/src/main/java/app/revanced/extension/shared/patches/client/AppClient.java
index 28cc29f83..67eee0854 100644
--- a/extensions/shared/src/main/java/app/revanced/extension/shared/patches/client/AppClient.java
+++ b/extensions/shared/src/main/java/app/revanced/extension/shared/patches/client/AppClient.java
@@ -6,8 +6,15 @@ import android.os.Build;
import androidx.annotation.Nullable;
+import app.revanced.extension.shared.settings.BaseSettings;
+
public class AppClient {
// IOS
+ /**
+ * Video not playable: Paid / Movie / Private / Age-restricted
+ * Note: Audio track available
+ */
+ private static final String PACKAGE_NAME_IOS = "com.google.ios.youtube";
/**
* The hardcoded client version of the iOS app used for InnerTube requests with this client.
*
@@ -29,15 +36,35 @@ public class AppClient {
private static final String DEVICE_MODEL_IOS = "iPhone16,2";
private static final String OS_VERSION_IOS = "17.7.2.21H221";
private static final String USER_AGENT_VERSION_IOS = "17_7_2";
- private static final String USER_AGENT_IOS = "com.google.ios.youtube/" +
- CLIENT_VERSION_IOS +
- "(" +
- DEVICE_MODEL_IOS +
- "; U; CPU iOS " +
- USER_AGENT_VERSION_IOS +
- " like Mac OS X)";
+ private static final String USER_AGENT_IOS =
+ iOSUserAgent(PACKAGE_NAME_IOS, CLIENT_VERSION_IOS);
+
+
+ // IOS UNPLUGGED
+ /**
+ * Video not playable: Paid / Movie
+ * Note: Audio track available
+ */
+ private static final String PACKAGE_NAME_IOS_UNPLUGGED = "com.google.ios.youtubeunplugged";
+ /**
+ * The hardcoded client version of the iOS app used for InnerTube requests with this client.
+ *
+ *
+ * It can be extracted by getting the latest release version of the app on
+ * the App
+ * Store page of the YouTube TV app, in the {@code What’s New} section.
+ *
+ */
+ private static final String CLIENT_VERSION_IOS_UNPLUGGED = "8.33";
+ private static final String USER_AGENT_IOS_UNPLUGGED =
+ iOSUserAgent(PACKAGE_NAME_IOS_UNPLUGGED, CLIENT_VERSION_IOS_UNPLUGGED);
+
// IOS MUSIC
+ /**
+ * Video not playable: All videos that can't be played on YouTube Music
+ */
+ private static final String PACKAGE_NAME_IOS_MUSIC = "com.google.ios.youtubemusic";
/**
* The hardcoded client version of the iOS app used for InnerTube requests with this client.
*
@@ -47,16 +74,21 @@ public class AppClient {
* Store page of the YouTube Music app, in the {@code What’s New} section.
*
*/
- private static final String CLIENT_VERSION_IOS_MUSIC = "7.31.2";
- private static final String USER_AGENT_IOS_MUSIC = "com.google.ios.youtubemusic/" +
- CLIENT_VERSION_IOS_MUSIC +
- "(" +
- DEVICE_MODEL_IOS +
- "; U; CPU iOS " +
- USER_AGENT_VERSION_IOS +
- " like Mac OS X)";
+ private static final String CLIENT_VERSION_IOS_MUSIC = "7.04";
+ private static final String USER_AGENT_IOS_MUSIC =
+ iOSUserAgent(PACKAGE_NAME_IOS_MUSIC, CLIENT_VERSION_IOS_MUSIC);
+
// ANDROID VR
+ /**
+ * Video not playable: Kids
+ * Note: Audio track is not available
+ *
+ * Package name for YouTube VR (Google DayDream): com.google.android.apps.youtube.vr (Deprecated)
+ * Package name for YouTube VR (Meta Quests): com.google.android.apps.youtube.vr.oculus
+ * Package name for YouTube VR (ByteDance Pico 4): com.google.android.apps.youtube.vr.pico
+ */
+ private static final String PACKAGE_NAME_ANDROID_VR = "com.google.android.apps.youtube.vr.oculus";
/**
* The hardcoded client version of the Android VR app used for InnerTube requests with this client.
*
@@ -66,7 +98,7 @@ public class AppClient {
* Store page of the YouTube app, in the {@code Additional details} section.
*
*/
- private static final String CLIENT_VERSION_ANDROID_VR = "1.61.47";
+ private static final String CLIENT_VERSION_ANDROID_VR = "1.61.48";
/**
* The device machine id for the Meta Quest 3, used to get opus codec with the Android VR client.
*
@@ -82,19 +114,17 @@ public class AppClient {
* but for some reason the build.props for the {@code Quest 3} state that the SDK version is 32.
*/
private static final String ANDROID_SDK_VERSION_ANDROID_VR = "32";
- /**
- * Package name for YouTube VR (Google DayDream): com.google.android.apps.youtube.vr (Deprecated)
- * Package name for YouTube VR (Meta Quests): com.google.android.apps.youtube.vr.oculus
- * Package name for YouTube VR (ByteDance Pico 4): com.google.android.apps.youtube.vr.pico
- */
- private static final String USER_AGENT_ANDROID_VR = "com.google.android.apps.youtube.vr.oculus/" +
- CLIENT_VERSION_ANDROID_VR +
- " (Linux; U; Android " +
- OS_VERSION_ANDROID_VR +
- "; GB) gzip";
+ private static final String USER_AGENT_ANDROID_VR =
+ androidUserAgent(PACKAGE_NAME_ANDROID_VR, CLIENT_VERSION_ANDROID_VR, OS_VERSION_ANDROID_VR);
+
// ANDROID UNPLUGGED
- private static final String CLIENT_VERSION_ANDROID_UNPLUGGED = "8.49.0";
+ /**
+ * Video not playable: Playlists / Music
+ * Note: Audio track is not available
+ */
+ private static final String PACKAGE_NAME_ANDROID_UNPLUGGED = "com.google.android.apps.youtube.unplugged";
+ private static final String CLIENT_VERSION_ANDROID_UNPLUGGED = "8.16.0";
/**
* The device machine id for the Chromecast with Google TV 4K.
*
@@ -106,15 +136,47 @@ public class AppClient {
private static final String DEVICE_MODEL_ANDROID_UNPLUGGED = "Google TV Streamer";
private static final String OS_VERSION_ANDROID_UNPLUGGED = "14";
private static final String ANDROID_SDK_VERSION_ANDROID_UNPLUGGED = "34";
- private static final String USER_AGENT_ANDROID_UNPLUGGED = "com.google.android.apps.youtube.unplugged/" +
- CLIENT_VERSION_ANDROID_UNPLUGGED +
- " (Linux; U; Android " +
- OS_VERSION_ANDROID_UNPLUGGED +
- "; GB) gzip";
+ private static final String USER_AGENT_ANDROID_UNPLUGGED =
+ androidUserAgent(PACKAGE_NAME_ANDROID_UNPLUGGED, CLIENT_VERSION_ANDROID_UNPLUGGED, OS_VERSION_ANDROID_UNPLUGGED);
+
+
+ // ANDROID CREATOR
+ /**
+ * Video not playable: Livestream
+ * Note: Audio track is not available
+ */
+ private static final String PACKAGE_NAME_ANDROID_CREATOR = "com.google.android.apps.youtube.creator";
+ private static final String CLIENT_VERSION_ANDROID_CREATOR = "24.14.101";
+ private static final String DEVICE_MODEL_ANDROID_CREATOR = Build.MODEL;
+ private static final String OS_VERSION_ANDROID_CREATOR = Build.VERSION.RELEASE;
+ private static final String ANDROID_SDK_VERSION_ANDROID_CREATOR = String.valueOf(Build.VERSION.SDK_INT);
+ private static final String USER_AGENT_ANDROID_CREATOR =
+ androidUserAgent(PACKAGE_NAME_ANDROID_CREATOR, CLIENT_VERSION_ANDROID_CREATOR, OS_VERSION_ANDROID_CREATOR);
+
private AppClient() {
}
+ private static String androidUserAgent(String packageName, String clientVersion, String osVersion) {
+ return packageName +
+ "/" +
+ clientVersion +
+ " (Linux; U; Android " +
+ osVersion +
+ "; GB) gzip";
+ }
+
+ private static String iOSUserAgent(String packageName, String clientVersion) {
+ return packageName +
+ "/" +
+ clientVersion +
+ "(" +
+ DEVICE_MODEL_IOS +
+ "; U; CPU iOS " +
+ USER_AGENT_VERSION_IOS +
+ " like Mac OS X)";
+ }
+
public enum ClientType {
IOS(5,
DEVICE_MODEL_IOS,
@@ -140,6 +202,22 @@ public class AppClient {
CLIENT_VERSION_ANDROID_UNPLUGGED,
true
),
+ ANDROID_CREATOR(14,
+ DEVICE_MODEL_ANDROID_CREATOR,
+ OS_VERSION_ANDROID_CREATOR,
+ USER_AGENT_ANDROID_CREATOR,
+ ANDROID_SDK_VERSION_ANDROID_CREATOR,
+ CLIENT_VERSION_ANDROID_CREATOR,
+ true
+ ),
+ IOS_UNPLUGGED(33,
+ DEVICE_MODEL_IOS,
+ OS_VERSION_IOS,
+ USER_AGENT_IOS_UNPLUGGED,
+ null,
+ CLIENT_VERSION_IOS_UNPLUGGED,
+ true
+ ),
IOS_MUSIC(
26,
DEVICE_MODEL_IOS,
@@ -208,8 +286,28 @@ public class AppClient {
this.canLogin = canLogin;
}
+ private static final ClientType[] CLIENT_ORDER_TO_USE_ANDROID = {
+ ANDROID_VR,
+ ANDROID_UNPLUGGED,
+ ANDROID_CREATOR,
+ };
+
+ private static final ClientType[] CLIENT_ORDER_TO_USE_DEFAULT = {
+ IOS,
+ ANDROID_VR,
+ ANDROID_UNPLUGGED,
+ IOS_UNPLUGGED,
+ IOS_MUSIC,
+ };
+
public final String getFriendlyName() {
return getString("revanced_spoof_streaming_data_type_entry_" + name().toLowerCase());
}
}
+
+ public static ClientType[] getAvailableClientTypes() {
+ return BaseSettings.SPOOF_STREAMING_DATA_ANDROID_ONLY.get()
+ ? ClientType.CLIENT_ORDER_TO_USE_ANDROID
+ : ClientType.CLIENT_ORDER_TO_USE_DEFAULT;
+ }
}
diff --git a/extensions/shared/src/main/java/app/revanced/extension/shared/patches/spoof/SpoofStreamingDataPatch.java b/extensions/shared/src/main/java/app/revanced/extension/shared/patches/spoof/SpoofStreamingDataPatch.java
index 318c0d971..f1ca98e32 100644
--- a/extensions/shared/src/main/java/app/revanced/extension/shared/patches/spoof/SpoofStreamingDataPatch.java
+++ b/extensions/shared/src/main/java/app/revanced/extension/shared/patches/spoof/SpoofStreamingDataPatch.java
@@ -1,33 +1,31 @@
package app.revanced.extension.shared.patches.spoof;
+import static app.revanced.extension.shared.patches.PatchStatus.SpoofStreamingData;
+
import android.net.Uri;
import android.text.TextUtils;
import androidx.annotation.Nullable;
-import com.google.protos.youtube.api.innertube.StreamingDataOuterClass$StreamingData;
-
-import java.lang.reflect.Field;
import java.nio.ByteBuffer;
import java.util.Collections;
import java.util.LinkedHashMap;
-import java.util.List;
import java.util.Map;
-import app.revanced.extension.shared.patches.BlockRequestPatch;
import app.revanced.extension.shared.patches.spoof.requests.StreamingDataRequest;
import app.revanced.extension.shared.settings.BaseSettings;
import app.revanced.extension.shared.utils.Logger;
import app.revanced.extension.shared.utils.Utils;
@SuppressWarnings("unused")
-public class SpoofStreamingDataPatch extends BlockRequestPatch {
+public class SpoofStreamingDataPatch {
+ public static final boolean SPOOF_STREAMING_DATA = SpoofStreamingData() && BaseSettings.SPOOF_STREAMING_DATA.get();
+
/**
- * Even if the default client is not iOS, videos that cannot be played on Android VR or Android TV will fall back to iOS.
- * Do not add a dependency that checks whether the default client is iOS or not.
+ * Any unreachable ip address. Used to intentionally fail requests.
*/
- private static final boolean SPOOF_STREAMING_DATA_SYNC_VIDEO_LENGTH =
- SPOOF_STREAMING_DATA && BaseSettings.SPOOF_STREAMING_DATA_SYNC_VIDEO_LENGTH.get();
+ private static final String UNREACHABLE_HOST_URI_STRING = "https://127.0.0.0";
+ private static final Uri UNREACHABLE_HOST_URI = Uri.parse(UNREACHABLE_HOST_URI_STRING);
/**
* Key: video id
@@ -43,6 +41,55 @@ public class SpoofStreamingDataPatch extends BlockRequestPatch {
}
});
+ /**
+ * Injection point.
+ * Blocks /get_watch requests by returning an unreachable URI.
+ *
+ * @param playerRequestUri The URI of the player request.
+ * @return An unreachable URI if the request is a /get_watch request, otherwise the original URI.
+ */
+ public static Uri blockGetWatchRequest(Uri playerRequestUri) {
+ if (SPOOF_STREAMING_DATA) {
+ try {
+ String path = playerRequestUri.getPath();
+
+ if (path != null && path.contains("get_watch")) {
+ Logger.printDebug(() -> "Blocking 'get_watch' by returning unreachable uri");
+
+ return UNREACHABLE_HOST_URI;
+ }
+ } catch (Exception ex) {
+ Logger.printException(() -> "blockGetWatchRequest failure", ex);
+ }
+ }
+
+ return playerRequestUri;
+ }
+
+ /**
+ * Injection point.
+ *
+ * Blocks /initplayback requests.
+ */
+ public static String blockInitPlaybackRequest(String originalUrlString) {
+ if (SPOOF_STREAMING_DATA) {
+ try {
+ var originalUri = Uri.parse(originalUrlString);
+ String path = originalUri.getPath();
+
+ if (path != null && path.contains("initplayback")) {
+ Logger.printDebug(() -> "Blocking 'initplayback' by clearing query");
+
+ return originalUri.buildUpon().clearQuery().build().toString();
+ }
+ } catch (Exception ex) {
+ Logger.printException(() -> "blockInitPlaybackRequest failure", ex);
+ }
+ }
+
+ return originalUrlString;
+ }
+
/**
* Injection point.
*/
@@ -133,29 +180,13 @@ public class SpoofStreamingDataPatch extends BlockRequestPatch {
*
* Called after {@link #getStreamingData(String)}.
*/
- public static void setApproxDurationMs(String videoId, String approxDurationMsFieldName,
- StreamingDataOuterClass$StreamingData originalStreamingData, StreamingDataOuterClass$StreamingData spoofedStreamingData) {
- if (SPOOF_STREAMING_DATA_SYNC_VIDEO_LENGTH) {
- if (formatsIsEmpty(spoofedStreamingData)) {
- List> originalFormats = getFormatsFromStreamingData(originalStreamingData);
- Long approxDurationMs = getApproxDurationMs(originalFormats, approxDurationMsFieldName);
- if (approxDurationMs != null) {
- approxDurationMsMap.put(videoId, approxDurationMs);
- Logger.printDebug(() -> "New approxDurationMs loaded, video id: " + videoId + ", video length: " + approxDurationMs);
- } else {
- Logger.printDebug(() -> "Ignoring as original approxDurationMs is not found, video id: " + videoId);
- }
- } else {
- Logger.printDebug(() -> "Ignoring as spoofed formats is not empty, video id: " + videoId);
- }
+ public static void setApproxDurationMs(String videoId, long approxDurationMs) {
+ if (approxDurationMs != Long.MAX_VALUE) {
+ approxDurationMsMap.put(videoId, approxDurationMs);
+ Logger.printDebug(() -> "New approxDurationMs loaded, video id: " + videoId + ", video length: " + approxDurationMs);
}
}
- /**
- * Looks like the initial value for the videoId field.
- */
- private static final String MASKED_VIDEO_ID = "zzzzzzzzzzz";
-
/**
* Injection point.
*
@@ -171,22 +202,16 @@ public class SpoofStreamingDataPatch extends BlockRequestPatch {
*
* Called after {@link #getStreamingData(String)}.
*/
- public static long getApproxDurationMsFromOriginalResponse(String videoId, long lengthMilliseconds) {
- if (SPOOF_STREAMING_DATA_SYNC_VIDEO_LENGTH) {
- try {
- if (videoId != null && !videoId.equals(MASKED_VIDEO_ID)) {
- Long approxDurationMs = approxDurationMsMap.get(videoId);
- if (approxDurationMs != null) {
- Logger.printDebug(() -> "Replacing video length from " + lengthMilliseconds + " to " + approxDurationMs + " , videoId: " + videoId);
- approxDurationMsMap.remove(videoId);
- return approxDurationMs;
- }
- }
- } catch (Exception ex) {
- Logger.printException(() -> "getOriginalFormats failure", ex);
+ public static long getApproxDurationMs(String videoId) {
+ if (SPOOF_STREAMING_DATA && videoId != null) {
+ final Long approxDurationMs = approxDurationMsMap.get(videoId);
+ if (approxDurationMs != null) {
+ Logger.printDebug(() -> "Replacing video length: " + approxDurationMs + " for videoId: " + videoId);
+ approxDurationMsMap.remove(videoId);
+ return approxDurationMs;
}
}
- return lengthMilliseconds;
+ return Long.MAX_VALUE;
}
/**
@@ -228,47 +253,4 @@ public class SpoofStreamingDataPatch extends BlockRequestPatch {
return videoFormat;
}
-
- // Utils
-
- private static boolean formatsIsEmpty(StreamingDataOuterClass$StreamingData streamingData) {
- List> formats = getFormatsFromStreamingData(streamingData);
- return formats == null || formats.size() == 0;
- }
-
- private static List> getFormatsFromStreamingData(StreamingDataOuterClass$StreamingData streamingData) {
- try {
- // Field e: 'formats'.
- // Field name is always 'e', regardless of the client version.
- Field field = streamingData.getClass().getDeclaredField("e");
- field.setAccessible(true);
- if (field.get(streamingData) instanceof List> list) {
- return list;
- }
- } catch (NoSuchFieldException | IllegalAccessException ex) {
- Logger.printException(() -> "Reflection error accessing formats", ex);
- }
- return null;
- }
-
- private static Long getApproxDurationMs(List> list, String approxDurationMsFieldName) {
- try {
- if (list != null) {
- var iterator = list.listIterator();
- if (iterator.hasNext()) {
- var formats = iterator.next();
- Field field = formats.getClass().getDeclaredField(approxDurationMsFieldName);
- field.setAccessible(true);
- if (field.get(formats) instanceof Long approxDurationMs) {
- return approxDurationMs;
- } else {
- Logger.printDebug(() -> "Field type is null: " + approxDurationMsFieldName);
- }
- }
- }
- } catch (NoSuchFieldException | IllegalAccessException ex) {
- Logger.printException(() -> "Reflection error accessing field: " + approxDurationMsFieldName, ex);
- }
- return null;
- }
}
diff --git a/extensions/shared/src/main/java/app/revanced/extension/shared/patches/spoof/requests/PlayerRoutes.java b/extensions/shared/src/main/java/app/revanced/extension/shared/patches/spoof/requests/PlayerRoutes.java
index f42e5443c..4eb16d20c 100644
--- a/extensions/shared/src/main/java/app/revanced/extension/shared/patches/spoof/requests/PlayerRoutes.java
+++ b/extensions/shared/src/main/java/app/revanced/extension/shared/patches/spoof/requests/PlayerRoutes.java
@@ -52,6 +52,10 @@ public final class PlayerRoutes {
client.put("osVersion", clientType.osVersion);
if (clientType.androidSdkVersion != null) {
client.put("androidSdkVersion", clientType.androidSdkVersion);
+ client.put("osName", "Android");
+ } else {
+ client.put("deviceMake", "Apple");
+ client.put("osName", "iOS");
}
client.put("hl", LOCALE_LANGUAGE);
diff --git a/extensions/shared/src/main/java/app/revanced/extension/shared/patches/spoof/requests/StreamingDataRequest.java b/extensions/shared/src/main/java/app/revanced/extension/shared/patches/spoof/requests/StreamingDataRequest.java
index 5637f392b..a76c8a2df 100644
--- a/extensions/shared/src/main/java/app/revanced/extension/shared/patches/spoof/requests/StreamingDataRequest.java
+++ b/extensions/shared/src/main/java/app/revanced/extension/shared/patches/spoof/requests/StreamingDataRequest.java
@@ -1,5 +1,6 @@
package app.revanced.extension.shared.patches.spoof.requests;
+import static app.revanced.extension.shared.patches.client.AppClient.getAvailableClientTypes;
import static app.revanced.extension.shared.patches.spoof.requests.PlayerRoutes.GET_STREAMING_DATA;
import androidx.annotation.NonNull;
@@ -13,6 +14,7 @@ import java.net.HttpURLConnection;
import java.net.SocketTimeoutException;
import java.nio.ByteBuffer;
import java.nio.charset.StandardCharsets;
+import java.util.Arrays;
import java.util.Collections;
import java.util.LinkedHashMap;
import java.util.Map;
@@ -80,16 +82,20 @@ public class StreamingDataRequest {
}
static {
- ClientType[] allClientTypes = ClientType.values();
+ ClientType[] allClientTypes = getAvailableClientTypes();
ClientType preferredClient = BaseSettings.SPOOF_STREAMING_DATA_TYPE.get();
- CLIENT_ORDER_TO_USE = new ClientType[allClientTypes.length];
- CLIENT_ORDER_TO_USE[0] = preferredClient;
+ if (Arrays.stream(allClientTypes).noneMatch(preferredClient::equals)) {
+ CLIENT_ORDER_TO_USE = allClientTypes;
+ } else {
+ CLIENT_ORDER_TO_USE = new ClientType[allClientTypes.length];
+ CLIENT_ORDER_TO_USE[0] = preferredClient;
- int i = 1;
- for (ClientType c : allClientTypes) {
- if (c != preferredClient) {
- CLIENT_ORDER_TO_USE[i++] = c;
+ int i = 1;
+ for (ClientType c : allClientTypes) {
+ if (c != preferredClient) {
+ CLIENT_ORDER_TO_USE[i++] = c;
+ }
}
}
}
diff --git a/extensions/shared/src/main/java/app/revanced/extension/shared/settings/BaseSettings.java b/extensions/shared/src/main/java/app/revanced/extension/shared/settings/BaseSettings.java
index 1be356f54..973550208 100644
--- a/extensions/shared/src/main/java/app/revanced/extension/shared/settings/BaseSettings.java
+++ b/extensions/shared/src/main/java/app/revanced/extension/shared/settings/BaseSettings.java
@@ -3,7 +3,7 @@ package app.revanced.extension.shared.settings;
import static java.lang.Boolean.FALSE;
import static java.lang.Boolean.TRUE;
import static app.revanced.extension.shared.patches.PatchStatus.HideFullscreenAdsDefaultBoolean;
-import static app.revanced.extension.shared.patches.PatchStatus.SpoofStreamingDataDefaultClient;
+import static app.revanced.extension.shared.patches.PatchStatus.SpoofStreamingDataAndroidOnlyDefaultBoolean;
import app.revanced.extension.shared.patches.ReturnYouTubeUsernamePatch.DisplayFormat;
import app.revanced.extension.shared.patches.client.AppClient.ClientType;
@@ -36,13 +36,9 @@ public class BaseSettings {
public static final EnumSetting RETURN_YOUTUBE_USERNAME_DISPLAY_FORMAT = new EnumSetting<>("revanced_return_youtube_username_display_format", DisplayFormat.USERNAME_ONLY, true);
public static final StringSetting RETURN_YOUTUBE_USERNAME_YOUTUBE_DATA_API_V3_DEVELOPER_KEY = new StringSetting("revanced_return_youtube_username_youtube_data_api_v3_developer_key", "", true, false);
- // Used only in YouTube Music.
- // Moved to a shared class to prevent YouTube from accessing YouTube Music's extension classes.
- public static final BooleanSetting SPOOF_CLIENT = new BooleanSetting("revanced_spoof_client", FALSE, true);
-
public static final BooleanSetting SPOOF_STREAMING_DATA = new BooleanSetting("revanced_spoof_streaming_data", TRUE, true, "revanced_spoof_streaming_data_user_dialog_message");
- public static final EnumSetting SPOOF_STREAMING_DATA_TYPE = new EnumSetting<>("revanced_spoof_streaming_data_type", SpoofStreamingDataDefaultClient(), true);
- public static final BooleanSetting SPOOF_STREAMING_DATA_SYNC_VIDEO_LENGTH = new BooleanSetting("revanced_spoof_streaming_data_sync_video_length", TRUE, true);
+ public static final EnumSetting SPOOF_STREAMING_DATA_TYPE = new EnumSetting<>("revanced_spoof_streaming_data_type", ClientType.ANDROID_VR, true);
+ public static final BooleanSetting SPOOF_STREAMING_DATA_ANDROID_ONLY = new BooleanSetting("revanced_spoof_streaming_data_android_only", SpoofStreamingDataAndroidOnlyDefaultBoolean(), true, "revanced_spoof_streaming_data_android_only_user_dialog_message");
public static final BooleanSetting SPOOF_STREAMING_DATA_STATS_FOR_NERDS = new BooleanSetting("revanced_spoof_streaming_data_stats_for_nerds", TRUE);
/**
diff --git a/extensions/shared/src/main/java/app/revanced/extension/youtube/patches/general/GeneralPatch.java b/extensions/shared/src/main/java/app/revanced/extension/youtube/patches/general/GeneralPatch.java
index dceb56170..0893398fb 100644
--- a/extensions/shared/src/main/java/app/revanced/extension/youtube/patches/general/GeneralPatch.java
+++ b/extensions/shared/src/main/java/app/revanced/extension/youtube/patches/general/GeneralPatch.java
@@ -246,8 +246,12 @@ public class GeneralPatch {
}
public static boolean useTranslucentNavigationStatusBar(boolean original) {
- if (Settings.DISABLE_TRANSLUCENT_STATUS_BAR.get()) {
- return false;
+ try {
+ if (Settings.DISABLE_TRANSLUCENT_STATUS_BAR.get()) {
+ return false;
+ }
+ } catch (Exception ex) {
+ Logger.printException(() -> "Failed to load useTranslucentNavigationStatusBar", ex);
}
return original;
@@ -260,22 +264,27 @@ public class GeneralPatch {
= Settings.DISABLE_TRANSLUCENT_NAVIGATION_BAR_DARK.get();
public static boolean useTranslucentNavigationButtons(boolean original) {
- // Feature requires Android 13+
- if (!isSDKAbove(33)) {
- return original;
- }
+ try {
+ // Feature requires Android 13+
+ if (!isSDKAbove(33)) {
+ return original;
+ }
- if (!DISABLE_TRANSLUCENT_NAVIGATION_BAR_DARK && !DISABLE_TRANSLUCENT_NAVIGATION_BAR_LIGHT) {
- return original;
- }
+ if (!DISABLE_TRANSLUCENT_NAVIGATION_BAR_DARK && !DISABLE_TRANSLUCENT_NAVIGATION_BAR_LIGHT) {
+ return original;
+ }
- if (DISABLE_TRANSLUCENT_NAVIGATION_BAR_DARK && DISABLE_TRANSLUCENT_NAVIGATION_BAR_LIGHT) {
- return false;
- }
+ if (DISABLE_TRANSLUCENT_NAVIGATION_BAR_DARK && DISABLE_TRANSLUCENT_NAVIGATION_BAR_LIGHT) {
+ return false;
+ }
- return Utils.isDarkModeEnabled()
- ? !DISABLE_TRANSLUCENT_NAVIGATION_BAR_DARK
- : !DISABLE_TRANSLUCENT_NAVIGATION_BAR_LIGHT;
+ return Utils.isDarkModeEnabled()
+ ? !DISABLE_TRANSLUCENT_NAVIGATION_BAR_DARK
+ : !DISABLE_TRANSLUCENT_NAVIGATION_BAR_LIGHT;
+ } catch (Exception ex) {
+ Logger.printException(() -> "Failed to load useTranslucentNavigationButtons", ex);
+ }
+ return original;
}
// endregion
diff --git a/extensions/shared/src/main/java/app/revanced/extension/youtube/settings/Settings.java b/extensions/shared/src/main/java/app/revanced/extension/youtube/settings/Settings.java
index 04c9a60f3..9d593c8f5 100644
--- a/extensions/shared/src/main/java/app/revanced/extension/youtube/settings/Settings.java
+++ b/extensions/shared/src/main/java/app/revanced/extension/youtube/settings/Settings.java
@@ -480,9 +480,9 @@ public class Settings extends BaseSettings {
public static final BooleanSetting SHORTS_CUSTOM_ACTIONS_REPEAT_STATE = new BooleanSetting("revanced_shorts_custom_actions_repeat_state", FALSE, true);
public static final BooleanSetting ENABLE_SHORTS_CUSTOM_ACTIONS_FLYOUT_MENU = new BooleanSetting("revanced_enable_shorts_custom_actions_flyout_menu", FALSE, true,
- parentsAny(SHORTS_CUSTOM_ACTIONS_COPY_VIDEO_URL, SHORTS_CUSTOM_ACTIONS_COPY_VIDEO_URL_TIMESTAMP, SHORTS_CUSTOM_ACTIONS_OPEN_VIDEO, SHORTS_CUSTOM_ACTIONS_REPEAT_STATE));
+ parentsAny(SHORTS_CUSTOM_ACTIONS_COPY_VIDEO_URL, SHORTS_CUSTOM_ACTIONS_COPY_VIDEO_URL_TIMESTAMP, SHORTS_CUSTOM_ACTIONS_EXTERNAL_DOWNLOADER, SHORTS_CUSTOM_ACTIONS_OPEN_VIDEO, SHORTS_CUSTOM_ACTIONS_REPEAT_STATE));
public static final BooleanSetting ENABLE_SHORTS_CUSTOM_ACTIONS_TOOLBAR = new BooleanSetting("revanced_enable_shorts_custom_actions_toolbar", FALSE, true,
- parentsAny(SHORTS_CUSTOM_ACTIONS_COPY_VIDEO_URL, SHORTS_CUSTOM_ACTIONS_COPY_VIDEO_URL_TIMESTAMP, SHORTS_CUSTOM_ACTIONS_OPEN_VIDEO, SHORTS_CUSTOM_ACTIONS_REPEAT_STATE));
+ parentsAny(SHORTS_CUSTOM_ACTIONS_COPY_VIDEO_URL, SHORTS_CUSTOM_ACTIONS_COPY_VIDEO_URL_TIMESTAMP, SHORTS_CUSTOM_ACTIONS_EXTERNAL_DOWNLOADER, SHORTS_CUSTOM_ACTIONS_OPEN_VIDEO, SHORTS_CUSTOM_ACTIONS_REPEAT_STATE));
// Experimental Flags
public static final BooleanSetting ENABLE_TIME_STAMP = new BooleanSetting("revanced_enable_shorts_time_stamp", FALSE, true);
diff --git a/extensions/shared/src/main/java/app/revanced/extension/youtube/settings/preference/SpoofStreamingDataDefaultClientListPreference.java b/extensions/shared/src/main/java/app/revanced/extension/youtube/settings/preference/SpoofStreamingDataDefaultClientListPreference.java
new file mode 100644
index 000000000..b3fabe111
--- /dev/null
+++ b/extensions/shared/src/main/java/app/revanced/extension/youtube/settings/preference/SpoofStreamingDataDefaultClientListPreference.java
@@ -0,0 +1,87 @@
+package app.revanced.extension.youtube.settings.preference;
+
+import static app.revanced.extension.shared.utils.ResourceUtils.getStringArray;
+
+import android.content.Context;
+import android.content.SharedPreferences;
+import android.preference.ListPreference;
+import android.preference.PreferenceManager;
+import android.util.AttributeSet;
+
+import app.revanced.extension.shared.patches.client.AppClient.ClientType;
+import app.revanced.extension.shared.settings.EnumSetting;
+import app.revanced.extension.shared.settings.Setting;
+import app.revanced.extension.shared.utils.Utils;
+import app.revanced.extension.youtube.settings.Settings;
+
+@SuppressWarnings({"unused", "deprecation"})
+public class SpoofStreamingDataDefaultClientListPreference extends ListPreference {
+
+ private final SharedPreferences.OnSharedPreferenceChangeListener listener = (sharedPreferences, str) -> {
+ // Because this listener may run before the ReVanced settings fragment updates Settings,
+ // this could show the prior config and not the current.
+ //
+ // Push this call to the end of the main run queue,
+ // so all other listeners are done and Settings is up to date.
+ Utils.runOnMainThread(this::updateUI);
+ };
+
+ public SpoofStreamingDataDefaultClientListPreference(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
+ super(context, attrs, defStyleAttr, defStyleRes);
+ }
+
+ public SpoofStreamingDataDefaultClientListPreference(Context context, AttributeSet attrs, int defStyleAttr) {
+ super(context, attrs, defStyleAttr);
+ }
+
+ public SpoofStreamingDataDefaultClientListPreference(Context context, AttributeSet attrs) {
+ super(context, attrs);
+ }
+
+ public SpoofStreamingDataDefaultClientListPreference(Context context) {
+ super(context);
+ }
+
+ private void addChangeListener() {
+ Setting.preferences.preferences.registerOnSharedPreferenceChangeListener(listener);
+ }
+
+ private void removeChangeListener() {
+ Setting.preferences.preferences.unregisterOnSharedPreferenceChangeListener(listener);
+ }
+
+ @Override
+ protected void onAttachedToHierarchy(PreferenceManager preferenceManager) {
+ super.onAttachedToHierarchy(preferenceManager);
+ updateUI();
+ addChangeListener();
+ }
+
+ @Override
+ protected void onPrepareForRemoval() {
+ super.onPrepareForRemoval();
+ removeChangeListener();
+ }
+
+ private void updateUI() {
+ final boolean spoofStreamingDataAndroidOnly = Settings.SPOOF_STREAMING_DATA_ANDROID_ONLY.get();
+ final String entryKey = spoofStreamingDataAndroidOnly
+ ? "revanced_spoof_streaming_data_type_android_entries"
+ : "revanced_spoof_streaming_data_type_android_ios_entries";
+ final String entryValueKey = spoofStreamingDataAndroidOnly
+ ? "revanced_spoof_streaming_data_type_android_entry_values"
+ : "revanced_spoof_streaming_data_type_android_ios_entry_values";
+ final String[] mEntries = getStringArray(entryKey);
+ final String[] mEntryValues = getStringArray(entryValueKey);
+ setEntries(mEntries);
+ setEntryValues(mEntryValues);
+
+ final EnumSetting clientType = Settings.SPOOF_STREAMING_DATA_TYPE;
+ final boolean isAndroid = clientType.get().name().startsWith("ANDROID");
+ if (spoofStreamingDataAndroidOnly && !isAndroid) {
+ clientType.resetToDefault();
+ }
+
+ setEnabled(Settings.SPOOF_STREAMING_DATA.get());
+ }
+}
diff --git a/extensions/shared/src/main/java/app/revanced/extension/youtube/settings/preference/SpoofStreamingDataSideEffectsPreference.java b/extensions/shared/src/main/java/app/revanced/extension/youtube/settings/preference/SpoofStreamingDataSideEffectsPreference.java
index e6e27a972..f8e062270 100644
--- a/extensions/shared/src/main/java/app/revanced/extension/youtube/settings/preference/SpoofStreamingDataSideEffectsPreference.java
+++ b/extensions/shared/src/main/java/app/revanced/extension/youtube/settings/preference/SpoofStreamingDataSideEffectsPreference.java
@@ -8,7 +8,6 @@ import android.preference.Preference;
import android.preference.PreferenceManager;
import android.util.AttributeSet;
-import app.revanced.extension.shared.patches.client.AppClient.ClientType;
import app.revanced.extension.shared.settings.Setting;
import app.revanced.extension.shared.utils.Utils;
import app.revanced.extension.youtube.settings.Settings;
@@ -63,11 +62,14 @@ public class SpoofStreamingDataSideEffectsPreference extends Preference {
}
private void updateUI() {
- final ClientType clientType = Settings.SPOOF_STREAMING_DATA_TYPE.get();
- final String summaryTextKey = clientType == ClientType.IOS &&
- !Settings.SPOOF_STREAMING_DATA_SYNC_VIDEO_LENGTH.get()
- ? "revanced_spoof_streaming_data_side_effects_ios_skip_sync_video_length"
- : "revanced_spoof_streaming_data_side_effects_" + clientType.name().toLowerCase();
+ final String clientName = Settings.SPOOF_STREAMING_DATA_TYPE.get().name().toLowerCase();
+ String summaryTextKey = "revanced_spoof_streaming_data_side_effects_";
+
+ if (Settings.SPOOF_STREAMING_DATA_ANDROID_ONLY.get()) {
+ summaryTextKey += "android";
+ } else {
+ summaryTextKey += clientName;
+ }
setSummary(str(summaryTextKey));
setEnabled(Settings.SPOOF_STREAMING_DATA.get());
diff --git a/gradle.properties b/gradle.properties
index 8949173dc..52e659b34 100644
--- a/gradle.properties
+++ b/gradle.properties
@@ -4,5 +4,5 @@ org.gradle.parallel = true
android.useAndroidX = true
kotlin.code.style = official
kotlin.jvm.target.validation.mode = IGNORE
-version = 5.1.1
+version = 5.1.2
diff --git a/patches.json b/patches.json
index be2b02d7e..bbbd7243b 100644
--- a/patches.json
+++ b/patches.json
@@ -44,7 +44,7 @@
"6.42.55",
"6.51.53",
"7.16.53",
- "7.25.52"
+ "7.25.53"
]
},
"options": []
@@ -60,7 +60,7 @@
"6.42.55",
"6.51.53",
"7.16.53",
- "7.25.52"
+ "7.25.53"
]
},
"options": []
@@ -76,7 +76,7 @@
"6.42.55",
"6.51.53",
"7.16.53",
- "7.25.52"
+ "7.25.53"
]
},
"options": []
@@ -109,7 +109,7 @@
"6.42.55",
"6.51.53",
"7.16.53",
- "7.25.52"
+ "7.25.53"
]
},
"options": []
@@ -118,7 +118,9 @@
"name": "Change package name",
"description": "Changes the package name for Reddit to the name specified in patch options.",
"use": false,
- "compatiblePackages": {},
+ "compatiblePackages": {
+ "com.reddit.frontpage": null
+ },
"options": [
{
"key": "packageNameReddit",
@@ -162,7 +164,7 @@
"6.42.55",
"6.51.53",
"7.16.53",
- "7.25.52"
+ "7.25.53"
]
},
"options": []
@@ -195,7 +197,7 @@
"6.42.55",
"6.51.53",
"7.16.53",
- "7.25.52"
+ "7.25.53"
]
},
"options": []
@@ -221,6 +223,7 @@
"name": "Change version code",
"description": "Changes the version code of the app to the value specified in patch options. Except when mounting, this can prevent app stores from updating the app and allow the app to be installed over an existing installation that has a higher version code. By default, the highest version code is set.",
"use": false,
+ "compatiblePackages": null,
"options": [
{
"key": "versionCode",
@@ -302,6 +305,7 @@
{
"key": "changeSplashIcon",
"default": true,
+ "values": null,
"title": "Change splash icons",
"description": "Apply the custom branding icon to the splash screen.",
"required": true
@@ -309,6 +313,7 @@
{
"key": "restoreOldSplashAnimation",
"default": true,
+ "values": null,
"title": "Restore old splash animation",
"description": "Restore the old style splash animation.",
"required": true
@@ -326,7 +331,7 @@
"6.42.55",
"6.51.53",
"7.16.53",
- "7.25.52"
+ "7.25.53"
]
},
"options": [
@@ -348,6 +353,7 @@
{
"key": "changeSplashIcon",
"default": true,
+ "values": null,
"title": "Change splash icons",
"description": "Apply the custom branding icon to the splash screen.",
"required": true
@@ -355,6 +361,7 @@
{
"key": "restoreOldSplashIcon",
"default": false,
+ "values": null,
"title": "Restore old splash icon",
"description": "Restore the old style splash icon.\n\nIf you enable both the old style splash icon and the Cairo splash animation,\n\nOld style splash icon will appear first and then the Cairo splash animation will start.",
"required": true
@@ -365,7 +372,9 @@
"name": "Custom branding name for Reddit",
"description": "Renames the Reddit app to the name specified in patch options.",
"use": false,
- "compatiblePackages": {},
+ "compatiblePackages": {
+ "com.reddit.frontpage": null
+ },
"options": [
{
"key": "appName",
@@ -422,7 +431,7 @@
"6.42.55",
"6.51.53",
"7.16.53",
- "7.25.52"
+ "7.25.53"
]
},
"options": [
@@ -473,6 +482,7 @@
{
"key": "doubleTapLengthArrays",
"default": "3, 5, 10, 15, 20, 30, 60, 120, 180",
+ "values": null,
"title": "Double-tap to seek values",
"description": "A list of custom Double-tap to seek lengths to be added, separated by commas.",
"required": true
@@ -518,7 +528,7 @@
"6.42.55",
"6.51.53",
"7.16.53",
- "7.25.52"
+ "7.25.53"
]
},
"options": [
@@ -559,7 +569,7 @@
"com.google.android.apps.youtube.music": [
"7.06.54",
"7.16.53",
- "7.25.52"
+ "7.25.53"
]
},
"options": []
@@ -575,7 +585,7 @@
"6.42.55",
"6.51.53",
"7.16.53",
- "7.25.52"
+ "7.25.53"
]
},
"options": []
@@ -625,7 +635,7 @@
"6.42.55",
"6.51.53",
"7.16.53",
- "7.25.52"
+ "7.25.53"
]
},
"options": []
@@ -658,7 +668,7 @@
"6.42.55",
"6.51.53",
"7.16.53",
- "7.25.52"
+ "7.25.53"
]
},
"options": []
@@ -701,7 +711,9 @@
"name": "Disable screenshot popup",
"description": "Adds an option to disable the popup that appears when taking a screenshot.",
"use": true,
- "compatiblePackages": {},
+ "compatiblePackages": {
+ "com.reddit.frontpage": null
+ },
"options": []
},
{
@@ -732,7 +744,7 @@
"6.42.55",
"6.51.53",
"7.16.53",
- "7.25.52"
+ "7.25.53"
]
},
"options": []
@@ -765,7 +777,7 @@
"6.42.55",
"6.51.53",
"7.16.53",
- "7.25.52"
+ "7.25.53"
]
},
"options": []
@@ -832,7 +844,7 @@
"6.42.55",
"6.51.53",
"7.16.53",
- "7.25.52"
+ "7.25.53"
]
},
"options": []
@@ -865,7 +877,7 @@
"6.42.55",
"6.51.53",
"7.16.53",
- "7.25.52"
+ "7.25.53"
]
},
"options": []
@@ -915,7 +927,7 @@
"6.42.55",
"6.51.53",
"7.16.53",
- "7.25.52"
+ "7.25.53"
]
},
"options": [
@@ -932,6 +944,7 @@
{
"key": "checkGmsCore",
"default": true,
+ "values": null,
"title": "Check GmsCore",
"description": "Check if GmsCore is installed on the device and has battery optimizations disabled when the app starts. \n\nIf GmsCore is not installed the app will not work, so disabling this is not recommended.",
"required": true
@@ -989,6 +1002,7 @@
{
"key": "checkGmsCore",
"default": true,
+ "values": null,
"title": "Check GmsCore",
"description": "Check if GmsCore is installed on the device and has battery optimizations disabled when the app starts. \n\nIf GmsCore is not installed the app will not work, so disabling this is not recommended.",
"required": true
@@ -1021,7 +1035,9 @@
"name": "Hide Recently Visited shelf",
"description": "Adds an option to hide the Recently Visited shelf in the sidebar.",
"use": true,
- "compatiblePackages": {},
+ "compatiblePackages": {
+ "com.reddit.frontpage": null
+ },
"options": []
},
{
@@ -1052,7 +1068,7 @@
"6.42.55",
"6.51.53",
"7.16.53",
- "7.25.52"
+ "7.25.53"
]
},
"options": []
@@ -1068,7 +1084,7 @@
"6.42.55",
"6.51.53",
"7.16.53",
- "7.25.52"
+ "7.25.53"
]
},
"options": []
@@ -1101,7 +1117,7 @@
"6.42.55",
"6.51.53",
"7.16.53",
- "7.25.52"
+ "7.25.53"
]
},
"options": []
@@ -1110,7 +1126,9 @@
"name": "Hide ads",
"description": "Adds options to hide ads.",
"use": true,
- "compatiblePackages": {},
+ "compatiblePackages": {
+ "com.reddit.frontpage": null
+ },
"options": []
},
{
@@ -1192,7 +1210,7 @@
"6.42.55",
"6.51.53",
"7.16.53",
- "7.25.52"
+ "7.25.53"
]
},
"options": []
@@ -1218,7 +1236,9 @@
"name": "Hide navigation buttons",
"description": "Adds options to hide buttons in the navigation bar.",
"use": false,
- "compatiblePackages": {},
+ "compatiblePackages": {
+ "com.reddit.frontpage": null
+ },
"options": []
},
{
@@ -1232,7 +1252,7 @@
"6.42.55",
"6.51.53",
"7.16.53",
- "7.25.52"
+ "7.25.53"
]
},
"options": []
@@ -1282,7 +1302,7 @@
"6.42.55",
"6.51.53",
"7.16.53",
- "7.25.52"
+ "7.25.53"
]
},
"options": []
@@ -1291,7 +1311,9 @@
"name": "Hide recommended communities shelf",
"description": "Adds an option to hide the recommended communities shelves in subreddits.",
"use": true,
- "compatiblePackages": {},
+ "compatiblePackages": {
+ "com.reddit.frontpage": null
+ },
"options": []
},
{
@@ -1313,6 +1335,7 @@
{
"key": "explore",
"default": false,
+ "values": null,
"title": "Hide Explore",
"description": "Hide Explore from shortcuts.",
"required": true
@@ -1320,6 +1343,7 @@
{
"key": "subscriptions",
"default": false,
+ "values": null,
"title": "Hide Subscriptions",
"description": "Hide Subscriptions from shortcuts.",
"required": true
@@ -1327,6 +1351,7 @@
{
"key": "search",
"default": false,
+ "values": null,
"title": "Hide Search",
"description": "Hide Search from shortcuts.",
"required": true
@@ -1334,6 +1359,7 @@
{
"key": "shorts",
"default": true,
+ "values": null,
"title": "Hide Shorts",
"description": "Hide Shorts from shortcuts.",
"required": true
@@ -1436,7 +1462,7 @@
"6.42.55",
"6.51.53",
"7.16.53",
- "7.25.52"
+ "7.25.53"
]
},
"options": []
@@ -1462,14 +1488,18 @@
"name": "Open links directly",
"description": "Adds an option to skip over redirection URLs in external links.",
"use": true,
- "compatiblePackages": {},
+ "compatiblePackages": {
+ "com.reddit.frontpage": null
+ },
"options": []
},
{
"name": "Open links externally",
"description": "Adds an option to always open links in your browser instead of in the in-app-browser.",
"use": true,
- "compatiblePackages": {},
+ "compatiblePackages": {
+ "com.reddit.frontpage": null
+ },
"options": []
},
{
@@ -1515,6 +1545,7 @@
{
"key": "widerButtonsSpace",
"default": false,
+ "values": null,
"title": "Wider between-buttons space",
"description": "Prevent adjacent button presses by increasing the horizontal spacing between buttons.",
"required": true
@@ -1522,6 +1553,7 @@
{
"key": "changeTopButtons",
"default": false,
+ "values": null,
"title": "Change top buttons",
"description": "Change the icons at the top of the player.",
"required": true
@@ -1539,7 +1571,7 @@
"6.42.55",
"6.51.53",
"7.16.53",
- "7.25.52"
+ "7.25.53"
]
},
"options": []
@@ -1565,7 +1597,9 @@
"name": "Premium icon",
"description": "Unlocks premium app icons.",
"use": true,
- "compatiblePackages": {},
+ "compatiblePackages": {
+ "com.reddit.frontpage": null
+ },
"options": []
},
{
@@ -1579,7 +1613,7 @@
"6.42.55",
"6.51.53",
"7.16.53",
- "7.25.52"
+ "7.25.53"
]
},
"options": []
@@ -1605,7 +1639,9 @@
"name": "Remove subreddit dialog",
"description": "Adds options to remove the NSFW community warning and notifications suggestion dialogs by dismissing them automatically.",
"use": true,
- "compatiblePackages": {},
+ "compatiblePackages": {
+ "com.reddit.frontpage": null
+ },
"options": []
},
{
@@ -1619,7 +1655,7 @@
"6.42.55",
"6.51.53",
"7.16.53",
- "7.25.52"
+ "7.25.53"
]
},
"options": []
@@ -1652,7 +1688,7 @@
"6.42.55",
"6.51.53",
"7.16.53",
- "7.25.52"
+ "7.25.53"
]
},
"options": []
@@ -1668,7 +1704,7 @@
"6.42.55",
"6.51.53",
"7.16.53",
- "7.25.52"
+ "7.25.53"
]
},
"options": []
@@ -1701,7 +1737,7 @@
"6.42.55",
"6.51.53",
"7.16.53",
- "7.25.52"
+ "7.25.53"
]
},
"options": []
@@ -1734,7 +1770,7 @@
"6.42.55",
"6.51.53",
"7.16.53",
- "7.25.52"
+ "7.25.53"
]
},
"options": []
@@ -1743,7 +1779,9 @@
"name": "Sanitize sharing links",
"description": "Adds an option to remove tracking query parameters from URLs when sharing links.",
"use": true,
- "compatiblePackages": {},
+ "compatiblePackages": {
+ "com.reddit.frontpage": null
+ },
"options": []
},
{
@@ -1784,11 +1822,14 @@
"name": "Settings for Reddit",
"description": "Applies mandatory patches to implement ReVanced Extended settings into the application.",
"use": true,
- "compatiblePackages": {},
+ "compatiblePackages": {
+ "com.reddit.frontpage": null
+ },
"options": [
{
"key": "settingsLabel",
"default": "ReVanced Extended",
+ "values": null,
"title": "RVX settings menu name",
"description": "The name of the RVX settings menu.",
"required": true
@@ -1845,6 +1886,7 @@
{
"key": "settingsLabel",
"default": "ReVanced Extended",
+ "values": null,
"title": "RVX settings label",
"description": "The name of the RVX settings menu.",
"required": true
@@ -1862,13 +1904,14 @@
"6.42.55",
"6.51.53",
"7.16.53",
- "7.25.52"
+ "7.25.53"
]
},
"options": [
{
"key": "settingsLabel",
"default": "ReVanced Extended",
+ "values": null,
"title": "RVX settings label",
"description": "The name of the RVX settings menu.",
"required": true
@@ -1903,7 +1946,7 @@
"6.42.55",
"6.51.53",
"7.16.53",
- "7.25.52"
+ "7.25.53"
]
},
"options": []
@@ -1927,6 +1970,7 @@
{
"key": "outlineIcon",
"default": false,
+ "values": null,
"title": "Outline icons",
"description": "Apply the outline icon.",
"required": true
@@ -1943,7 +1987,6 @@
"6.29.59",
"6.42.55",
"6.51.53",
- "7.06.54",
"7.16.53"
]
},
@@ -1976,8 +2019,7 @@
"6.29.59",
"6.42.55",
"6.51.53",
- "7.16.53",
- "7.25.52"
+ "7.16.53"
]
},
"options": []
@@ -1993,7 +2035,7 @@
"6.42.55",
"6.51.53",
"7.16.53",
- "7.25.52"
+ "7.25.53"
]
},
"options": []
@@ -2121,6 +2163,7 @@
{
"key": "customTranslations",
"default": "",
+ "values": null,
"title": "Custom translations",
"description": "The path to the \u0027strings.xml\u0027 file.\nPlease note that applying the \u0027strings.xml\u0027 file will overwrite all existing translations.",
"required": true
@@ -2128,6 +2171,7 @@
{
"key": "selectedTranslations",
"default": "ar, bg-rBG, de-rDE, el-rGR, es-rES, fr-rFR, hu-rHU, it-rIT, ja-rJP, ko-rKR, pl-rPL, pt-rBR, ru-rRU, tr-rTR, uk-rUA, vi-rVN, zh-rCN, zh-rTW",
+ "values": null,
"title": "Translations to add",
"description": "A list of translations to be added for the RVX settings, separated by commas.",
"required": true
@@ -2135,6 +2179,7 @@
{
"key": "selectedStringResources",
"default": "af, am, ar, ar-rXB, as, az, b+es+419, b+sr+Latn, be, bg, bn, bs, ca, cs, da, de, el, en-rAU, en-rCA, en-rGB, en-rIN, en-rXA, en-rXC, es, es-rUS, et, eu, fa, fi, fr, fr-rCA, gl, gu, hi, hr, hu, hy, id, in, is, it, iw, ja, ka, kk, km, kn, ko, ky, lo, lt, lv, mk, ml, mn, mr, ms, my, nb, ne, nl, no, or, pa, pl, pt, pt-rBR, pt-rPT, ro, ru, si, sk, sl, sq, sr, sv, sw, ta, te, th, tl, tr, uk, ur, uz, vi, zh, zh-rCN, zh-rHK, zh-rTW, zu",
+ "values": null,
"title": "String resources to keep",
"description": "A list of string resources to be kept, separated by commas.\nString resources not in the list will be removed from the app.\n\nDefault string resource, English, is not removed.",
"required": true
@@ -2152,13 +2197,14 @@
"6.42.55",
"6.51.53",
"7.16.53",
- "7.25.52"
+ "7.25.53"
]
},
"options": [
{
"key": "customTranslations",
"default": "",
+ "values": null,
"title": "Custom translations",
"description": "The path to the \u0027strings.xml\u0027 file.\nPlease note that applying the \u0027strings.xml\u0027 file will overwrite all existing translations.",
"required": true
@@ -2166,6 +2212,7 @@
{
"key": "selectedTranslations",
"default": "bg-rBG, bn, cs-rCZ, el-rGR, es-rES, fr-rFR, hu-rHU, id-rID, in, it-rIT, ja-rJP, ko-rKR, nl-rNL, pl-rPL, pt-rBR, ro-rRO, ru-rRU, tr-rTR, uk-rUA, vi-rVN, zh-rCN, zh-rTW",
+ "values": null,
"title": "Translations to add",
"description": "A list of translations to be added for the RVX settings, separated by commas.",
"required": true
@@ -2173,6 +2220,7 @@
{
"key": "selectedStringResources",
"default": "af, am, ar, ar-rXB, as, az, b+es+419, b+sr+Latn, be, bg, bn, bs, ca, cs, da, de, el, en-rAU, en-rCA, en-rGB, en-rIN, en-rXA, en-rXC, es, es-rUS, et, eu, fa, fi, fr, fr-rCA, gl, gu, hi, hr, hu, hy, id, in, is, it, iw, ja, ka, kk, km, kn, ko, ky, lo, lt, lv, mk, ml, mn, mr, ms, my, nb, ne, nl, no, or, pa, pl, pt, pt-rBR, pt-rPT, ro, ru, si, sk, sl, sq, sr, sv, sw, ta, te, th, tl, tr, uk, ur, uz, vi, zh, zh-rCN, zh-rHK, zh-rTW, zu",
+ "values": null,
"title": "String resources to keep",
"description": "A list of string resources to be kept, separated by commas.\nString resources not in the list will be removed from the app.\n\nDefault string resource, English, is not removed.",
"required": true
@@ -2190,7 +2238,7 @@
"6.42.55",
"6.51.53",
"7.16.53",
- "7.25.52"
+ "7.25.53"
]
},
"options": []
@@ -2246,6 +2294,7 @@
{
"key": "applyToAll",
"default": false,
+ "values": null,
"title": "Apply to all settings menu",
"description": "Whether to apply Visual preferences icons to all settings menus.\n\nIf true: icons are applied to the parent PreferenceScreen of YouTube settings, the parent PreferenceScreen of RVX settings and the RVX sub-settings (if supported).\n\nIf false: icons are applied only to the parent PreferenceScreen of YouTube settings and RVX settings.",
"required": true
@@ -2263,7 +2312,7 @@
"6.42.55",
"6.51.53",
"7.16.53",
- "7.25.52"
+ "7.25.53"
]
},
"options": [
diff --git a/patches/api/patches.api b/patches/api/patches.api
index 176079b40..d19702041 100644
--- a/patches/api/patches.api
+++ b/patches/api/patches.api
@@ -375,11 +375,6 @@ public final class app/revanced/patches/shared/ads/BaseAdsPatchKt {
public static final fun baseAdsPatch (Ljava/lang/String;Ljava/lang/String;)Lapp/revanced/patcher/patch/BytecodePatch;
}
-public final class app/revanced/patches/shared/blockrequest/BlockRequestPatchKt {
- public static final field EXTENSION_CLASS_DESCRIPTOR Ljava/lang/String;
- public static final fun getBlockRequestPatch ()Lapp/revanced/patcher/patch/BytecodePatch;
-}
-
public final class app/revanced/patches/shared/captions/BaseAutoCaptionsPatchKt {
public static final fun getBaseAutoCaptionsPatch ()Lapp/revanced/patcher/patch/BytecodePatch;
}
@@ -496,11 +491,14 @@ public final class app/revanced/patches/shared/spoof/appversion/BaseSpoofAppVers
public final class app/revanced/patches/shared/spoof/streamingdata/BaseSpoofStreamingDataPatchKt {
public static final field EXTENSION_CLASS_DESCRIPTOR Ljava/lang/String;
- public static final field STREAMING_DATA_INTERFACE Ljava/lang/String;
public static final fun baseSpoofStreamingDataPatch (Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function1;)Lapp/revanced/patcher/patch/BytecodePatch;
public static synthetic fun baseSpoofStreamingDataPatch$default (Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function1;ILjava/lang/Object;)Lapp/revanced/patcher/patch/BytecodePatch;
}
+public final class app/revanced/patches/shared/spoof/streamingdata/FingerprintsKt {
+ public static final field STREAMING_DATA_INTERFACE Ljava/lang/String;
+}
+
public final class app/revanced/patches/shared/spoof/useragent/BaseSpoofUserAgentPatchKt {
public static final fun baseSpoofUserAgentPatch (Ljava/lang/String;)Lapp/revanced/patcher/patch/BytecodePatch;
}
diff --git a/patches/src/main/kotlin/app/revanced/generator/JsonPatchesFileGenerator.kt b/patches/src/main/kotlin/app/revanced/generator/JsonPatchesFileGenerator.kt
index b06f3b3e5..8fcc2d203 100644
--- a/patches/src/main/kotlin/app/revanced/generator/JsonPatchesFileGenerator.kt
+++ b/patches/src/main/kotlin/app/revanced/generator/JsonPatchesFileGenerator.kt
@@ -28,7 +28,7 @@ internal class JsonPatchesFileGenerator : PatchesFileGenerator {
},
)
}.let {
- patchesJson.writeText(GsonBuilder().setPrettyPrinting().create().toJson(it))
+ patchesJson.writeText(GsonBuilder().serializeNulls().setPrettyPrinting().create().toJson(it))
}
}
diff --git a/patches/src/main/kotlin/app/revanced/patches/music/general/spoofappversion/SpoofAppVersionPatch.kt b/patches/src/main/kotlin/app/revanced/patches/music/general/spoofappversion/SpoofAppVersionPatch.kt
index d15a5a306..1cfc4789a 100644
--- a/patches/src/main/kotlin/app/revanced/patches/music/general/spoofappversion/SpoofAppVersionPatch.kt
+++ b/patches/src/main/kotlin/app/revanced/patches/music/general/spoofappversion/SpoofAppVersionPatch.kt
@@ -17,6 +17,7 @@ import app.revanced.patches.music.utils.settings.addPreferenceWithIntent
import app.revanced.patches.music.utils.settings.addSwitchPreference
import app.revanced.patches.music.utils.settings.settingsPatch
import app.revanced.patches.shared.spoof.appversion.baseSpoofAppVersionPatch
+import app.revanced.util.Utils.printWarn
import app.revanced.util.appendAppVersion
import app.revanced.util.findMethodOrThrow
@@ -64,7 +65,6 @@ val spoofAppVersionPatch = resourcePatch(
"6.29.59",
"6.42.55",
"6.51.53",
- "7.06.54",
"7.16.53",
),
)
@@ -78,7 +78,7 @@ val spoofAppVersionPatch = resourcePatch(
execute {
if (is_7_25_or_greater) {
- println("WARNING: \"${SPOOF_APP_VERSION.title}\" is not supported in this version. Use YouTube Music 7.24.51 or earlier.")
+ printWarn("\"${SPOOF_APP_VERSION.title}\" is not supported in this version. Use YouTube Music 7.24.51 or earlier.")
return@execute
}
if (is_7_17_or_greater) {
diff --git a/patches/src/main/kotlin/app/revanced/patches/music/layout/header/ChangeHeaderPatch.kt b/patches/src/main/kotlin/app/revanced/patches/music/layout/header/ChangeHeaderPatch.kt
index 7bfae51e2..db08f642f 100644
--- a/patches/src/main/kotlin/app/revanced/patches/music/layout/header/ChangeHeaderPatch.kt
+++ b/patches/src/main/kotlin/app/revanced/patches/music/layout/header/ChangeHeaderPatch.kt
@@ -9,6 +9,7 @@ import app.revanced.patches.music.utils.settings.ResourceUtils.getIconType
import app.revanced.patches.music.utils.settings.ResourceUtils.updatePatchStatus
import app.revanced.patches.music.utils.settings.settingsPatch
import app.revanced.util.ResourceGroup
+import app.revanced.util.Utils.printWarn
import app.revanced.util.Utils.trimIndentMultiline
import app.revanced.util.copyFile
import app.revanced.util.copyResources
@@ -154,7 +155,7 @@ val changeHeaderPatch = resourcePatch(
val customBrandingIconIncluded = customBrandingIconType != "default"
customHeader = customHeaderOption.valueOrThrow()
- val warnings = "WARNING: Invalid header path: $customHeader. Does not apply patches."
+ val warnings = "Invalid header path: $customHeader. Does not apply patches."
if (isPath) {
copyFile(
@@ -169,7 +170,7 @@ val changeHeaderPatch = resourcePatch(
}
}
} else {
- println(warnings)
+ printWarn(warnings)
}
updatePatchStatus(CUSTOM_HEADER_FOR_YOUTUBE_MUSIC)
diff --git a/patches/src/main/kotlin/app/revanced/patches/music/misc/splash/CairoSplashAnimationPatch.kt b/patches/src/main/kotlin/app/revanced/patches/music/misc/splash/CairoSplashAnimationPatch.kt
index db7ed91fb..ed45770dc 100644
--- a/patches/src/main/kotlin/app/revanced/patches/music/misc/splash/CairoSplashAnimationPatch.kt
+++ b/patches/src/main/kotlin/app/revanced/patches/music/misc/splash/CairoSplashAnimationPatch.kt
@@ -16,6 +16,7 @@ import app.revanced.patches.music.utils.settings.CategoryType
import app.revanced.patches.music.utils.settings.ResourceUtils.updatePatchStatus
import app.revanced.patches.music.utils.settings.addSwitchPreference
import app.revanced.patches.music.utils.settings.settingsPatch
+import app.revanced.util.Utils.printWarn
import app.revanced.util.fingerprint.injectLiteralInstructionBooleanCall
import app.revanced.util.fingerprint.methodOrThrow
import app.revanced.util.getReference
@@ -38,7 +39,7 @@ val cairoSplashAnimationPatch = bytecodePatch(
YOUTUBE_MUSIC_PACKAGE_NAME(
"7.06.54",
"7.16.53",
- "7.25.52",
+ "7.25.53",
),
)
@@ -50,7 +51,7 @@ val cairoSplashAnimationPatch = bytecodePatch(
execute {
if (!is_7_06_or_greater) {
- println("WARNING: \"${DISABLE_CAIRO_SPLASH_ANIMATION.title}\" is not supported in this version. Use YouTube Music 7.06.54 or later.")
+ printWarn("\"${DISABLE_CAIRO_SPLASH_ANIMATION.title}\" is not supported in this version. Use YouTube Music 7.06.54 or later.")
return@execute
} else if (!is_7_20_or_greater) {
cairoSplashAnimationConfigFingerprint.injectLiteralInstructionBooleanCall(
diff --git a/patches/src/main/kotlin/app/revanced/patches/music/utils/compatibility/Constants.kt b/patches/src/main/kotlin/app/revanced/patches/music/utils/compatibility/Constants.kt
index b5b59cf50..b964b90bb 100644
--- a/patches/src/main/kotlin/app/revanced/patches/music/utils/compatibility/Constants.kt
+++ b/patches/src/main/kotlin/app/revanced/patches/music/utils/compatibility/Constants.kt
@@ -14,7 +14,7 @@ internal object Constants {
"6.42.55", // This is the latest version that supports Android 7.0
"6.51.53", // This is the latest version of YouTube Music 6.xx.xx
"7.16.53", // This is the latest version that supports the 'Spoof app version' patch.
- "7.25.52", // This is the latest version supported by the RVX patch.
+ "7.25.53", // This is the latest version supported by the RVX patch.
)
)
}
\ No newline at end of file
diff --git a/patches/src/main/kotlin/app/revanced/patches/music/utils/fix/client/SpoofClientPatch.kt b/patches/src/main/kotlin/app/revanced/patches/music/utils/fix/client/SpoofClientPatch.kt
index d22922baf..af3dc5463 100644
--- a/patches/src/main/kotlin/app/revanced/patches/music/utils/fix/client/SpoofClientPatch.kt
+++ b/patches/src/main/kotlin/app/revanced/patches/music/utils/fix/client/SpoofClientPatch.kt
@@ -4,24 +4,23 @@ 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.extensions.InstructionExtensions.instructions
-import app.revanced.patcher.extensions.InstructionExtensions.replaceInstruction
import app.revanced.patcher.patch.PatchException
import app.revanced.patcher.patch.bytecodePatch
import app.revanced.patcher.util.proxy.mutableTypes.MutableMethod.Companion.toMutable
-import app.revanced.patches.music.utils.compatibility.Constants.COMPATIBLE_PACKAGE
+import app.revanced.patches.music.utils.compatibility.Constants
import app.revanced.patches.music.utils.extension.Constants.MISC_PATH
import app.revanced.patches.music.utils.patch.PatchList.SPOOF_CLIENT
import app.revanced.patches.music.utils.playbackSpeedBottomSheetFingerprint
+import app.revanced.patches.music.utils.playservice.is_7_25_or_greater
+import app.revanced.patches.music.utils.playservice.versionCheckPatch
import app.revanced.patches.music.utils.settings.CategoryType
import app.revanced.patches.music.utils.settings.ResourceUtils.updatePatchStatus
import app.revanced.patches.music.utils.settings.addPreferenceWithIntent
import app.revanced.patches.music.utils.settings.addSwitchPreference
import app.revanced.patches.music.utils.settings.settingsPatch
-import app.revanced.patches.shared.blockrequest.blockRequestPatch
import app.revanced.patches.shared.createPlayerRequestBodyWithModelFingerprint
-import app.revanced.patches.shared.extension.Constants.PATCHES_PATH
import app.revanced.patches.shared.indexOfModelInstruction
-import app.revanced.util.findMethodOrThrow
+import app.revanced.util.Utils.printWarn
import app.revanced.util.fingerprint.matchOrThrow
import app.revanced.util.fingerprint.methodOrThrow
import app.revanced.util.fingerprint.mutableClassOrThrow
@@ -53,14 +52,26 @@ val spoofClientPatch = bytecodePatch(
SPOOF_CLIENT.summary,
false,
) {
- dependsOn(
- settingsPatch,
- blockRequestPatch
+ compatibleWith(
+ Constants.YOUTUBE_MUSIC_PACKAGE_NAME(
+ "6.20.51",
+ "6.29.59",
+ "6.42.55",
+ "6.51.53",
+ "7.16.53",
+ ),
)
- compatibleWith(COMPATIBLE_PACKAGE)
+ dependsOn(
+ settingsPatch,
+ versionCheckPatch,
+ )
execute {
+ if (is_7_25_or_greater) {
+ printWarn("\"${SPOOF_CLIENT.title}\" is not supported in this version. Use YouTube Music 7.24.51 or earlier.")
+ return@execute
+ }
// region Get field references to be used below.
@@ -262,24 +273,11 @@ val spoofClientPatch = bytecodePatch(
// endregion
- findMethodOrThrow("$PATCHES_PATH/PatchStatus;") {
- name == "SpoofClient"
- }.replaceInstruction(
- 0,
- "const/4 v0, 0x1"
- )
-
addSwitchPreference(
CategoryType.MISC,
"revanced_spoof_client",
"false"
)
- addSwitchPreference(
- CategoryType.MISC,
- "revanced_spoof_client_legacy",
- "false",
- "revanced_spoof_client"
- )
addPreferenceWithIntent(
CategoryType.MISC,
"revanced_spoof_client_type",
diff --git a/patches/src/main/kotlin/app/revanced/patches/music/utils/fix/streamingdata/SpoofStreamingDataPatch.kt b/patches/src/main/kotlin/app/revanced/patches/music/utils/fix/streamingdata/SpoofStreamingDataPatch.kt
index 3b8ae37b3..a37eeeeb8 100644
--- a/patches/src/main/kotlin/app/revanced/patches/music/utils/fix/streamingdata/SpoofStreamingDataPatch.kt
+++ b/patches/src/main/kotlin/app/revanced/patches/music/utils/fix/streamingdata/SpoofStreamingDataPatch.kt
@@ -1,7 +1,5 @@
package app.revanced.patches.music.utils.fix.streamingdata
-import app.revanced.patcher.extensions.InstructionExtensions.getInstruction
-import app.revanced.patcher.extensions.InstructionExtensions.replaceInstruction
import app.revanced.patches.music.utils.compatibility.Constants.COMPATIBLE_PACKAGE
import app.revanced.patches.music.utils.compatibility.Constants.YOUTUBE_MUSIC_PACKAGE_NAME
import app.revanced.patches.music.utils.patch.PatchList.SPOOF_STREAMING_DATA
@@ -10,15 +8,8 @@ import app.revanced.patches.music.utils.settings.ResourceUtils.updatePatchStatus
import app.revanced.patches.music.utils.settings.addPreferenceWithIntent
import app.revanced.patches.music.utils.settings.addSwitchPreference
import app.revanced.patches.music.utils.settings.settingsPatch
-import app.revanced.patches.shared.extension.Constants.PATCHES_PATH
import app.revanced.patches.shared.spoof.streamingdata.baseSpoofStreamingDataPatch
import app.revanced.patches.shared.spoof.useragent.baseSpoofUserAgentPatch
-import app.revanced.util.findMethodOrThrow
-import com.android.tools.smali.dexlib2.iface.instruction.OneRegisterInstruction
-import com.android.tools.smali.dexlib2.iface.instruction.ReferenceInstruction
-import com.android.tools.smali.dexlib2.iface.reference.FieldReference
-
-private const val DEFAULT_CLIENT_TYPE = "ANDROID_VR"
@Suppress("unused")
val spoofStreamingDataPatch = baseSpoofStreamingDataPatch(
@@ -31,17 +22,6 @@ val spoofStreamingDataPatch = baseSpoofStreamingDataPatch(
)
},
{
- findMethodOrThrow("$PATCHES_PATH/PatchStatus;") {
- name == "SpoofStreamingDataDefaultClient"
- }.apply {
- val register = getInstruction(0).registerA
- val type = (getInstruction(0).reference as FieldReference).type
- replaceInstruction(
- 0,
- "sget-object v$register, $type->$DEFAULT_CLIENT_TYPE:$type"
- )
- }
-
addSwitchPreference(
CategoryType.MISC,
"revanced_spoof_streaming_data",
diff --git a/patches/src/main/kotlin/app/revanced/patches/reddit/ad/AdsPatch.kt b/patches/src/main/kotlin/app/revanced/patches/reddit/ad/AdsPatch.kt
index aff6e4335..d01e0bc6e 100644
--- a/patches/src/main/kotlin/app/revanced/patches/reddit/ad/AdsPatch.kt
+++ b/patches/src/main/kotlin/app/revanced/patches/reddit/ad/AdsPatch.kt
@@ -17,12 +17,10 @@ import app.revanced.util.fingerprint.methodOrThrow
import app.revanced.util.getReference
import app.revanced.util.getWalkerMethod
import app.revanced.util.indexOfFirstInstructionOrThrow
-import app.revanced.util.indexOfFirstStringInstructionOrThrow
-import com.android.tools.smali.dexlib2.Opcode
+import app.revanced.util.indexOfFirstStringInstruction
import com.android.tools.smali.dexlib2.iface.instruction.FiveRegisterInstruction
import com.android.tools.smali.dexlib2.iface.instruction.TwoRegisterInstruction
import com.android.tools.smali.dexlib2.iface.reference.FieldReference
-import com.android.tools.smali.dexlib2.iface.reference.MethodReference
private const val RESOURCE_FILE_PATH = "res/layout/merge_listheader_link_detail.xml"
@@ -113,13 +111,13 @@ val adsPatch = bytecodePatch(
// The new feeds work by inserting posts into lists.
// AdElementConverter is conveniently responsible for inserting all feed ads.
// By removing the appending instruction no ad posts gets appended to the feed.
- newAdPostFingerprint.methodOrThrow().apply {
- val stringIndex =
- indexOfFirstStringInstructionOrThrow("android_feed_freeform_render_variant")
- val targetIndex = indexOfFirstInstructionOrThrow(stringIndex) {
- opcode == Opcode.INVOKE_VIRTUAL
- && getReference()?.toString() == "Ljava/util/ArrayList;->add(Ljava/lang/Object;)Z"
- }
+ val newAdPostMethod = newAdPostFingerprint.second.methodOrNull
+ ?: newAdPostLegacyFingerprint.methodOrThrow()
+
+ newAdPostMethod.apply {
+ val startIndex =
+ 0.coerceAtLeast(indexOfFirstStringInstruction("android_feed_freeform_render_variant"))
+ val targetIndex = indexOfAddArrayListInstruction(this, startIndex)
val targetInstruction = getInstruction(targetIndex)
replaceInstruction(
diff --git a/patches/src/main/kotlin/app/revanced/patches/reddit/ad/Fingerprints.kt b/patches/src/main/kotlin/app/revanced/patches/reddit/ad/Fingerprints.kt
index 726e76933..ad7bd5971 100644
--- a/patches/src/main/kotlin/app/revanced/patches/reddit/ad/Fingerprints.kt
+++ b/patches/src/main/kotlin/app/revanced/patches/reddit/ad/Fingerprints.kt
@@ -6,6 +6,7 @@ import app.revanced.util.indexOfFirstInstruction
import app.revanced.util.or
import com.android.tools.smali.dexlib2.AccessFlags
import com.android.tools.smali.dexlib2.Opcode
+import com.android.tools.smali.dexlib2.iface.Method
import com.android.tools.smali.dexlib2.iface.reference.MethodReference
internal val commentAdsFingerprint = legacyFingerprint(
@@ -37,7 +38,7 @@ internal val adPostFingerprint = legacyFingerprint(
"children",
"uxExperiences"
),
- customFingerprint = { method, classDef ->
+ customFingerprint = { _, classDef ->
classDef.type.endsWith("/Listing;")
},
)
@@ -51,9 +52,27 @@ internal val newAdPostFingerprint = legacyFingerprint(
"android_feed_freeform_render_variant",
),
customFingerprint = { method, _ ->
- method.indexOfFirstInstruction {
- getReference()?.toString() == "Ljava/util/ArrayList;->add(Ljava/lang/Object;)Z"
- } >= 0
+ indexOfAddArrayListInstruction(method) >= 0
},
)
+internal val newAdPostLegacyFingerprint = legacyFingerprint(
+ name = "newAdPostLegacyFingerprint",
+ returnType = "L",
+ accessFlags = AccessFlags.PUBLIC or AccessFlags.FINAL,
+ opcodes = listOf(Opcode.INVOKE_VIRTUAL),
+ strings = listOf(
+ "chain",
+ "feedElement"
+ ),
+ customFingerprint = { method, classDef ->
+ classDef.sourceFile == "AdElementConverter.kt" &&
+ indexOfAddArrayListInstruction(method) >= 0
+ },
+)
+
+internal fun indexOfAddArrayListInstruction(method: Method, index: Int = 0) =
+ method.indexOfFirstInstruction(index) {
+ getReference()?.toString() == "Ljava/util/ArrayList;->add(Ljava/lang/Object;)Z"
+ }
+
diff --git a/patches/src/main/kotlin/app/revanced/patches/reddit/layout/branding/name/CustomBrandingNamePatch.kt b/patches/src/main/kotlin/app/revanced/patches/reddit/layout/branding/name/CustomBrandingNamePatch.kt
index f32ed7dc2..fee03bb0e 100644
--- a/patches/src/main/kotlin/app/revanced/patches/reddit/layout/branding/name/CustomBrandingNamePatch.kt
+++ b/patches/src/main/kotlin/app/revanced/patches/reddit/layout/branding/name/CustomBrandingNamePatch.kt
@@ -5,6 +5,7 @@ import app.revanced.patcher.patch.stringOption
import app.revanced.patches.reddit.utils.compatibility.Constants.COMPATIBLE_PACKAGE
import app.revanced.patches.reddit.utils.patch.PatchList.CUSTOM_BRANDING_NAME_FOR_REDDIT
import app.revanced.patches.reddit.utils.settings.updatePatchStatus
+import app.revanced.util.Utils.printInfo
import app.revanced.util.valueOrThrow
import java.io.FileWriter
import java.nio.file.Files
@@ -37,7 +38,7 @@ val customBrandingNamePatch = resourcePatch(
.valueOrThrow()
if (appName == ORIGINAL_APP_NAME) {
- println("INFO: App name will remain unchanged as it matches the original.")
+ printInfo("App name will remain unchanged as it matches the original.")
return@execute
}
diff --git a/patches/src/main/kotlin/app/revanced/patches/reddit/layout/branding/packagename/ChangePackageNamePatch.kt b/patches/src/main/kotlin/app/revanced/patches/reddit/layout/branding/packagename/ChangePackageNamePatch.kt
index 9201291d9..a795e5b56 100644
--- a/patches/src/main/kotlin/app/revanced/patches/reddit/layout/branding/packagename/ChangePackageNamePatch.kt
+++ b/patches/src/main/kotlin/app/revanced/patches/reddit/layout/branding/packagename/ChangePackageNamePatch.kt
@@ -5,6 +5,7 @@ import app.revanced.patcher.patch.stringOption
import app.revanced.patches.reddit.utils.compatibility.Constants.COMPATIBLE_PACKAGE
import app.revanced.patches.reddit.utils.patch.PatchList.CHANGE_PACKAGE_NAME
import app.revanced.patches.reddit.utils.settings.updatePatchStatus
+import app.revanced.util.Utils.printInfo
import app.revanced.util.valueOrThrow
import org.w3c.dom.Element
@@ -71,7 +72,7 @@ val changePackageNamePatch = resourcePatch(
.valueOrThrow()
if (redditPackageName == PACKAGE_NAME_REDDIT) {
- println("INFO: Package name will remain unchanged as it matches the original.")
+ printInfo("Package name will remain unchanged as it matches the original.")
return@execute
}
diff --git a/patches/src/main/kotlin/app/revanced/patches/reddit/layout/navigation/NavigationButtonsPatch.kt b/patches/src/main/kotlin/app/revanced/patches/reddit/layout/navigation/NavigationButtonsPatch.kt
index 0a084d6b4..df3c587f5 100644
--- a/patches/src/main/kotlin/app/revanced/patches/reddit/layout/navigation/NavigationButtonsPatch.kt
+++ b/patches/src/main/kotlin/app/revanced/patches/reddit/layout/navigation/NavigationButtonsPatch.kt
@@ -10,6 +10,7 @@ import app.revanced.patches.reddit.utils.patch.PatchList.HIDE_NAVIGATION_BUTTONS
import app.revanced.patches.reddit.utils.settings.is_2024_18_or_greater
import app.revanced.patches.reddit.utils.settings.settingsPatch
import app.revanced.patches.reddit.utils.settings.updatePatchStatus
+import app.revanced.util.Utils.printWarn
import app.revanced.util.fingerprint.methodOrThrow
import app.revanced.util.fingerprint.resolvable
import app.revanced.util.indexOfFirstInstructionOrThrow
@@ -34,7 +35,7 @@ val navigationButtonsPatch = bytecodePatch(
execute {
if (is_2024_18_or_greater) {
- println("WARNING: \"Hide navigation buttons\" patch is not supported in this version. Use Reddit 2024.17.0 or earlier.")
+ printWarn("\"Hide navigation buttons\" patch is not supported in this version. Use Reddit 2024.17.0 or earlier.")
return@execute
}
diff --git a/patches/src/main/kotlin/app/revanced/patches/shared/blockrequest/BlockRequestPatch.kt b/patches/src/main/kotlin/app/revanced/patches/shared/blockrequest/BlockRequestPatch.kt
deleted file mode 100644
index ae3d9a72c..000000000
--- a/patches/src/main/kotlin/app/revanced/patches/shared/blockrequest/BlockRequestPatch.kt
+++ /dev/null
@@ -1,57 +0,0 @@
-package app.revanced.patches.shared.blockrequest
-
-import app.revanced.patcher.extensions.InstructionExtensions.addInstructions
-import app.revanced.patcher.extensions.InstructionExtensions.getInstruction
-import app.revanced.patcher.patch.bytecodePatch
-import app.revanced.patches.shared.extension.Constants.PATCHES_PATH
-import app.revanced.util.fingerprint.matchOrThrow
-import app.revanced.util.fingerprint.methodOrThrow
-import com.android.tools.smali.dexlib2.iface.instruction.FiveRegisterInstruction
-import com.android.tools.smali.dexlib2.iface.instruction.OneRegisterInstruction
-
-const val EXTENSION_CLASS_DESCRIPTOR =
- "$PATCHES_PATH/BlockRequestPatch;"
-
-val blockRequestPatch = bytecodePatch(
- description = "blockRequestPatch"
-) {
- execute {
- // region Block /initplayback requests to fall back to /get_watch requests.
-
- buildInitPlaybackRequestFingerprint.matchOrThrow().let {
- it.method.apply {
- val moveUriStringIndex = it.patternMatch!!.startIndex
- val targetRegister =
- getInstruction(moveUriStringIndex).registerA
-
- addInstructions(
- moveUriStringIndex + 1,
- """
- invoke-static { v$targetRegister }, $EXTENSION_CLASS_DESCRIPTOR->blockInitPlaybackRequest(Ljava/lang/String;)Ljava/lang/String;
- move-result-object v$targetRegister
- """,
- )
- }
- }
-
- // endregion
-
- // region Block /get_watch requests to fall back to /player requests.
-
- buildPlayerRequestURIFingerprint.methodOrThrow().apply {
- val invokeToStringIndex = indexOfToStringInstruction(this)
- val uriRegister =
- getInstruction(invokeToStringIndex).registerC
-
- addInstructions(
- invokeToStringIndex,
- """
- invoke-static { v$uriRegister }, $EXTENSION_CLASS_DESCRIPTOR->blockGetWatchRequest(Landroid/net/Uri;)Landroid/net/Uri;
- move-result-object v$uriRegister
- """,
- )
- }
-
- // endregion
- }
-}
\ No newline at end of file
diff --git a/patches/src/main/kotlin/app/revanced/patches/shared/blockrequest/Fingerprints.kt b/patches/src/main/kotlin/app/revanced/patches/shared/blockrequest/Fingerprints.kt
deleted file mode 100644
index 30ed66ea0..000000000
--- a/patches/src/main/kotlin/app/revanced/patches/shared/blockrequest/Fingerprints.kt
+++ /dev/null
@@ -1,40 +0,0 @@
-package app.revanced.patches.shared.blockrequest
-
-import app.revanced.util.fingerprint.legacyFingerprint
-import app.revanced.util.getReference
-import app.revanced.util.indexOfFirstInstruction
-import com.android.tools.smali.dexlib2.Opcode
-import com.android.tools.smali.dexlib2.iface.Method
-import com.android.tools.smali.dexlib2.iface.reference.MethodReference
-
-internal val buildInitPlaybackRequestFingerprint = legacyFingerprint(
- name = "buildInitPlaybackRequestFingerprint",
- returnType = "Lorg/chromium/net/UrlRequest\$Builder;",
- opcodes = listOf(
- Opcode.MOVE_RESULT_OBJECT,
- Opcode.IGET_OBJECT, // Moves the request URI string to a register to build the request with.
- ),
- strings = listOf(
- "Content-Type",
- "Range",
- ),
-)
-
-internal val buildPlayerRequestURIFingerprint = legacyFingerprint(
- name = "buildPlayerRequestURIFingerprint",
- returnType = "Ljava/lang/String;",
- strings = listOf(
- "key",
- "asig",
- ),
- customFingerprint = { method, _ ->
- indexOfToStringInstruction(method) >= 0
- },
-)
-
-internal fun indexOfToStringInstruction(method: Method) =
- method.indexOfFirstInstruction {
- opcode == Opcode.INVOKE_VIRTUAL &&
- getReference().toString() == "Landroid/net/Uri;->toString()Ljava/lang/String;"
- }
-
diff --git a/patches/src/main/kotlin/app/revanced/patches/shared/extension/SharedExtensionPatch.kt b/patches/src/main/kotlin/app/revanced/patches/shared/extension/SharedExtensionPatch.kt
index d389463ab..40a0caf1b 100644
--- a/patches/src/main/kotlin/app/revanced/patches/shared/extension/SharedExtensionPatch.kt
+++ b/patches/src/main/kotlin/app/revanced/patches/shared/extension/SharedExtensionPatch.kt
@@ -35,16 +35,14 @@ class ExtensionHook internal constructor(
) {
context(BytecodePatchContext)
operator fun invoke(extensionClassDescriptor: String) {
- if (System.getenv("GITHUB_REPOSITORY") == null) {
- val insertIndex = insertIndexResolver(fingerprint.method)
- val contextRegister = contextRegisterResolver(fingerprint.method)
+ val insertIndex = insertIndexResolver(fingerprint.method)
+ val contextRegister = contextRegisterResolver(fingerprint.method)
- fingerprint.method.addInstruction(
- insertIndex,
- "invoke-static/range { $contextRegister .. $contextRegister }, " +
- "$extensionClassDescriptor->setContext(Landroid/content/Context;)V",
- )
- }
+ fingerprint.method.addInstruction(
+ insertIndex,
+ "invoke-static/range { $contextRegister .. $contextRegister }, " +
+ "$extensionClassDescriptor->setContext(Landroid/content/Context;)V",
+ )
}
}
diff --git a/patches/src/main/kotlin/app/revanced/patches/shared/spoof/streamingdata/BaseSpoofStreamingDataPatch.kt b/patches/src/main/kotlin/app/revanced/patches/shared/spoof/streamingdata/BaseSpoofStreamingDataPatch.kt
index 811e1c9b6..0ddda9abf 100644
--- a/patches/src/main/kotlin/app/revanced/patches/shared/spoof/streamingdata/BaseSpoofStreamingDataPatch.kt
+++ b/patches/src/main/kotlin/app/revanced/patches/shared/spoof/streamingdata/BaseSpoofStreamingDataPatch.kt
@@ -5,13 +5,13 @@ import app.revanced.patcher.extensions.InstructionExtensions.addInstructions
import app.revanced.patcher.extensions.InstructionExtensions.addInstructionsWithLabels
import app.revanced.patcher.extensions.InstructionExtensions.getInstruction
import app.revanced.patcher.extensions.InstructionExtensions.instructions
+import app.revanced.patcher.extensions.InstructionExtensions.removeInstruction
import app.revanced.patcher.extensions.InstructionExtensions.replaceInstruction
import app.revanced.patcher.patch.BytecodePatchBuilder
import app.revanced.patcher.patch.BytecodePatchContext
import app.revanced.patcher.patch.PatchException
import app.revanced.patcher.patch.bytecodePatch
import app.revanced.patcher.util.proxy.mutableTypes.MutableMethod.Companion.toMutable
-import app.revanced.patches.shared.blockrequest.blockRequestPatch
import app.revanced.patches.shared.extension.Constants.PATCHES_PATH
import app.revanced.patches.shared.extension.Constants.SPOOF_PATH
import app.revanced.patches.shared.formatStreamModelConstructorFingerprint
@@ -22,7 +22,7 @@ import app.revanced.util.fingerprint.injectLiteralInstructionBooleanCall
import app.revanced.util.fingerprint.matchOrThrow
import app.revanced.util.fingerprint.methodOrThrow
import app.revanced.util.getReference
-import app.revanced.util.indexOfFirstInstructionReversedOrThrow
+import app.revanced.util.indexOfFirstInstructionOrThrow
import com.android.tools.smali.dexlib2.AccessFlags
import com.android.tools.smali.dexlib2.Opcode
import com.android.tools.smali.dexlib2.builder.MutableMethodImplementation
@@ -37,10 +37,6 @@ import com.android.tools.smali.dexlib2.immutable.ImmutableMethodParameter
const val EXTENSION_CLASS_DESCRIPTOR =
"$SPOOF_PATH/SpoofStreamingDataPatch;"
-// In YouTube 17.34.36, this class is obfuscated.
-const val STREAMING_DATA_INTERFACE =
- "Lcom/google/protos/youtube/api/innertube/StreamingDataOuterClass${'$'}StreamingData;"
-
fun baseSpoofStreamingDataPatch(
block: BytecodePatchBuilder.() -> Unit = {},
executeBlock: BytecodePatchContext.() -> Unit = {},
@@ -48,11 +44,47 @@ fun baseSpoofStreamingDataPatch(
name = "Spoof streaming data",
description = "Adds options to spoof the streaming data to allow playback."
) {
- dependsOn(blockRequestPatch)
-
block()
execute {
+ // region Block /initplayback requests to fall back to /get_watch requests.
+
+ buildInitPlaybackRequestFingerprint.matchOrThrow().let {
+ it.method.apply {
+ val moveUriStringIndex = it.patternMatch!!.startIndex
+ val targetRegister =
+ getInstruction(moveUriStringIndex).registerA
+
+ addInstructions(
+ moveUriStringIndex + 1,
+ """
+ invoke-static { v$targetRegister }, $EXTENSION_CLASS_DESCRIPTOR->blockInitPlaybackRequest(Ljava/lang/String;)Ljava/lang/String;
+ move-result-object v$targetRegister
+ """,
+ )
+ }
+ }
+
+ // endregion
+
+ // region Block /get_watch requests to fall back to /player requests.
+
+ buildPlayerRequestURIFingerprint.methodOrThrow().apply {
+ val invokeToStringIndex = indexOfToStringInstruction(this)
+ val uriRegister =
+ getInstruction(invokeToStringIndex).registerC
+
+ addInstructions(
+ invokeToStringIndex,
+ """
+ invoke-static { v$uriRegister }, $EXTENSION_CLASS_DESCRIPTOR->blockGetWatchRequest(Landroid/net/Uri;)Landroid/net/Uri;
+ move-result-object v$uriRegister
+ """,
+ )
+ }
+
+ // endregion
+
// region Get replacement streams at player requests.
buildRequestFingerprint.methodOrThrow().apply {
@@ -84,18 +116,51 @@ fun baseSpoofStreamingDataPatch(
// region Replace the streaming data.
- val approxDurationMsFieldName = formatStreamModelConstructorFingerprint.matchOrThrow().let {
- with(it.method) {
- val approxDurationMsFieldIndex = it.patternMatch!!.startIndex
- (getInstruction(approxDurationMsFieldIndex).reference as FieldReference).name
+ val approxDurationMsReference = formatStreamModelConstructorFingerprint.matchOrThrow().let {
+ with (it.method) {
+ getInstruction(it.patternMatch!!.startIndex).reference
}
}
+ val streamingDataFormatsReference = with(videoStreamingDataConstructorFingerprint.methodOrThrow(videoStreamingDataToStringFingerprint)) {
+ val getFormatsFieldIndex = indexOfGetFormatsFieldInstruction(this)
+ val longMaxValueIndex = indexOfLongMaxValueInstruction(this, getFormatsFieldIndex)
+ val longMaxValueRegister = getInstruction(longMaxValueIndex).registerA
+ val videoIdIndex =
+ indexOfFirstInstructionOrThrow(longMaxValueIndex) {
+ val reference = getReference()
+ opcode == Opcode.IGET_OBJECT &&
+ reference?.type == "Ljava/lang/String;" &&
+ reference.definingClass == definingClass
+ }
+
+ val definingClassRegister =
+ getInstruction(videoIdIndex).registerB
+ val videoIdReference =
+ getInstruction(videoIdIndex).reference
+
+ addInstructions(
+ longMaxValueIndex + 1, """
+ # Get video id.
+ iget-object v$longMaxValueRegister, v$definingClassRegister, $videoIdReference
+
+ # Override approxDurationMs.
+ invoke-static { v$longMaxValueRegister }, $EXTENSION_CLASS_DESCRIPTOR->getApproxDurationMs(Ljava/lang/String;)J
+ move-result-wide v$longMaxValueRegister
+ """
+ )
+ removeInstruction(longMaxValueIndex)
+
+ getInstruction(getFormatsFieldIndex).reference
+ }
+
createStreamingDataFingerprint.matchOrThrow(createStreamingDataParentFingerprint)
.let { result ->
result.method.apply {
val setStreamDataMethodName = "patch_setStreamingData"
- val resultMethodType = result.classDef.type
+ val calcApproxDurationMsMethodName = "patch_calcApproxDurationMs"
+ val resultClassDef = result.classDef
+ val resultMethodType = resultClassDef.type
val setStreamingDataIndex = result.patternMatch!!.startIndex
val setStreamingDataField =
getInstruction(setStreamingDataIndex).getReference()
@@ -167,58 +232,83 @@ fun baseSpoofStreamingDataPatch(
iget-object v6, v5, $getStreamingDataField
if-eqz v6, :disabled
- # Get original streaming data.
- iget-object v0, p0, $setStreamingDataField
+ # Caculate approxDurationMs.
+ invoke-direct { p0, v2 }, $resultMethodType->$calcApproxDurationMsMethodName(Ljava/lang/String;)V
# Set spoofed streaming data.
iput-object v6, p0, $setStreamingDataField
- # Get video length from original streaming data and save to extension.
- const-string v5, "$approxDurationMsFieldName"
- invoke-static { v2, v5, v0, v6 }, $EXTENSION_CLASS_DESCRIPTOR->setApproxDurationMs(Ljava/lang/String;Ljava/lang/String;$STREAMING_DATA_INTERFACE$STREAMING_DATA_INTERFACE)V
-
:disabled
return-void
""",
)
},
)
+
+ resultClassDef.methods.add(
+ ImmutableMethod(
+ resultMethodType,
+ calcApproxDurationMsMethodName,
+ listOf(
+ ImmutableMethodParameter(
+ "Ljava/lang/String;",
+ annotations,
+ "videoId"
+ )
+ ),
+ "V",
+ AccessFlags.PRIVATE.value or AccessFlags.FINAL.value,
+ annotations,
+ null,
+ MutableMethodImplementation(12),
+ ).toMutable().apply {
+ addInstructionsWithLabels(
+ 0,
+ """
+ # Get video format list.
+ iget-object v0, p0, $setStreamingDataField
+ iget-object v0, v0, $streamingDataFormatsReference
+ invoke-interface {v0}, Ljava/util/List;->iterator()Ljava/util/Iterator;
+ move-result-object v0
+
+ # Initialize approxDurationMs field.
+ const-wide v1, 0x7fffffffffffffffL
+
+ :loop
+ # Loop over all video formats to get the approxDurationMs
+ invoke-interface {v0}, Ljava/util/Iterator;->hasNext()Z
+ move-result v3
+ const-wide/16 v4, 0x0
+
+ if-eqz v3, :exit
+ invoke-interface {v0}, Ljava/util/Iterator;->next()Ljava/lang/Object;
+ move-result-object v3
+ check-cast v3, ${(approxDurationMsReference as FieldReference).definingClass}
+
+ # Get approxDurationMs from format
+ iget-wide v6, v3, $approxDurationMsReference
+
+ # Compare with zero to make sure approxDurationMs is not negative
+ cmp-long v8, v6, v4
+ if-lez v8, :loop
+
+ # Only use the min value of approxDurationMs
+ invoke-static {v1, v2, v6, v7}, Ljava/lang/Math;->min(JJ)J
+ move-result-wide v1
+ goto :loop
+
+ :exit
+ # Save approxDurationMs to integrations
+ invoke-static { p1, v1, v2 }, $EXTENSION_CLASS_DESCRIPTOR->setApproxDurationMs(Ljava/lang/String;J)V
+
+ return-void
+ """,
+ )
+ },
+ )
}
}
- videoStreamingDataConstructorFingerprint.methodOrThrow(videoStreamingDataToStringFingerprint)
- .apply {
- val formatStreamModelInitIndex = indexOfFormatStreamModelInitInstruction(this)
- val videoIdIndex =
- indexOfFirstInstructionReversedOrThrow(formatStreamModelInitIndex) {
- val reference = getReference()
- opcode == Opcode.IGET_OBJECT &&
- reference?.type == "Ljava/lang/String;" &&
- reference.definingClass == definingClass
- }
- val definingClassRegister =
- getInstruction(videoIdIndex).registerB
- val videoIdReference =
- getInstruction(videoIdIndex).reference
-
- val toMillisIndex = indexOfToMillisInstruction(this)
- val freeRegister =
- getInstruction(toMillisIndex).registerC
- val lengthMillisecondsRegister =
- getInstruction(toMillisIndex + 1).registerA
-
- addInstructions(
- toMillisIndex + 2, """
- # Get video id.
- iget-object v$freeRegister, v$definingClassRegister, $videoIdReference
-
- # Override streaming data formats.
- invoke-static { v$freeRegister, v$lengthMillisecondsRegister, v${lengthMillisecondsRegister + 1} }, $EXTENSION_CLASS_DESCRIPTOR->getApproxDurationMsFromOriginalResponse(Ljava/lang/String;J)J
- move-result-wide v$lengthMillisecondsRegister
- """
- )
- }
-
// endregion
// region Remove /videoplayback request body to fix playback.
diff --git a/patches/src/main/kotlin/app/revanced/patches/shared/spoof/streamingdata/Fingerprints.kt b/patches/src/main/kotlin/app/revanced/patches/shared/spoof/streamingdata/Fingerprints.kt
index 6b8f1937e..36ae9360b 100644
--- a/patches/src/main/kotlin/app/revanced/patches/shared/spoof/streamingdata/Fingerprints.kt
+++ b/patches/src/main/kotlin/app/revanced/patches/shared/spoof/streamingdata/Fingerprints.kt
@@ -7,8 +7,45 @@ import app.revanced.util.or
import com.android.tools.smali.dexlib2.AccessFlags
import com.android.tools.smali.dexlib2.Opcode
import com.android.tools.smali.dexlib2.iface.Method
+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
+// In YouTube 17.34.36, this class is obfuscated.
+const val STREAMING_DATA_INTERFACE =
+ "Lcom/google/protos/youtube/api/innertube/StreamingDataOuterClass${'$'}StreamingData;"
+
+internal val buildInitPlaybackRequestFingerprint = legacyFingerprint(
+ name = "buildInitPlaybackRequestFingerprint",
+ returnType = "Lorg/chromium/net/UrlRequest\$Builder;",
+ opcodes = listOf(
+ Opcode.MOVE_RESULT_OBJECT,
+ Opcode.IGET_OBJECT, // Moves the request URI string to a register to build the request with.
+ ),
+ strings = listOf(
+ "Content-Type",
+ "Range",
+ ),
+)
+
+internal val buildPlayerRequestURIFingerprint = legacyFingerprint(
+ name = "buildPlayerRequestURIFingerprint",
+ returnType = "Ljava/lang/String;",
+ strings = listOf(
+ "key",
+ "asig",
+ ),
+ customFingerprint = { method, _ ->
+ indexOfToStringInstruction(method) >= 0
+ },
+)
+
+internal fun indexOfToStringInstruction(method: Method) =
+ method.indexOfFirstInstruction {
+ opcode == Opcode.INVOKE_VIRTUAL &&
+ getReference().toString() == "Landroid/net/Uri;->toString()Ljava/lang/String;"
+ }
+
internal val buildMediaDataSourceFingerprint = legacyFingerprint(
name = "buildMediaDataSourceFingerprint",
accessFlags = AccessFlags.PUBLIC or AccessFlags.CONSTRUCTOR,
@@ -108,11 +145,28 @@ internal val videoStreamingDataConstructorFingerprint = legacyFingerprint(
accessFlags = AccessFlags.PUBLIC or AccessFlags.CONSTRUCTOR,
returnType = "V",
customFingerprint = { method, _ ->
- indexOfFormatStreamModelInitInstruction(method) >= 0 &&
- indexOfToMillisInstruction(method) >= 0
+ indexOfGetFormatsFieldInstruction(method) >= 0 &&
+ indexOfLongMaxValueInstruction(method) >= 0 &&
+ indexOfFormatStreamModelInitInstruction(method) >= 0
},
)
+internal fun indexOfGetFormatsFieldInstruction(method: Method) =
+ method.indexOfFirstInstruction {
+ val reference = getReference()
+ opcode == Opcode.IGET_OBJECT &&
+ reference?.definingClass == STREAMING_DATA_INTERFACE &&
+ // Field e: 'formats'.
+ // Field name is always 'e', regardless of the client version.
+ reference.name == "e" &&
+ reference.type.startsWith("L")
+ }
+
+internal fun indexOfLongMaxValueInstruction(method: Method, index: Int = 0) =
+ method.indexOfFirstInstruction(index) {
+ (this as? WideLiteralInstruction)?.wideLiteral == Long.MAX_VALUE
+ }
+
internal fun indexOfFormatStreamModelInitInstruction(method: Method) =
method.indexOfFirstInstruction {
val reference = getReference()
@@ -121,13 +175,6 @@ internal fun indexOfFormatStreamModelInitInstruction(method: Method) =
reference.parameterTypes.size > 1
}
-internal fun indexOfToMillisInstruction(method: Method) =
- method.indexOfFirstInstruction {
- val reference = getReference()
- opcode == Opcode.INVOKE_VIRTUAL &&
- reference?.name == "toMillis"
- }
-
/**
* On YouTube, this class is 'Lcom/google/android/libraries/youtube/innertube/model/media/VideoStreamingData;'
* On YouTube Music, class names are obfuscated.
diff --git a/patches/src/main/kotlin/app/revanced/patches/youtube/general/toolbar/Fingerprints.kt b/patches/src/main/kotlin/app/revanced/patches/youtube/general/toolbar/Fingerprints.kt
index 00ac1b22f..6440b9e6e 100644
--- a/patches/src/main/kotlin/app/revanced/patches/youtube/general/toolbar/Fingerprints.kt
+++ b/patches/src/main/kotlin/app/revanced/patches/youtube/general/toolbar/Fingerprints.kt
@@ -155,6 +155,27 @@ internal val searchBarParentFingerprint = legacyFingerprint(
literals = listOf(voiceSearch),
)
+internal val voiceInputControllerParentFingerprint = legacyFingerprint(
+ name = "voiceInputControllerParentFingerprint",
+ returnType = "V",
+ accessFlags = AccessFlags.PUBLIC or AccessFlags.FINAL,
+ parameters = listOf("[B", "Z"),
+ strings = listOf("VoiceInputController"),
+)
+
+internal val voiceInputControllerFingerprint = legacyFingerprint(
+ name = "voiceInputControllerFingerprint",
+ returnType = "Z",
+ accessFlags = AccessFlags.PUBLIC or AccessFlags.FINAL,
+ parameters = emptyList(),
+ customFingerprint = { method, _ ->
+ method.indexOfFirstInstruction {
+ opcode == Opcode.INVOKE_VIRTUAL &&
+ getReference()?.name == "resolveActivity"
+ } >= 0
+ },
+)
+
internal val searchResultFingerprint = legacyFingerprint(
name = "searchResultFingerprint",
returnType = "Landroid/view/View;",
diff --git a/patches/src/main/kotlin/app/revanced/patches/youtube/general/toolbar/ToolBarComponentsPatch.kt b/patches/src/main/kotlin/app/revanced/patches/youtube/general/toolbar/ToolBarComponentsPatch.kt
index 1b24b1bf8..74f762141 100644
--- a/patches/src/main/kotlin/app/revanced/patches/youtube/general/toolbar/ToolBarComponentsPatch.kt
+++ b/patches/src/main/kotlin/app/revanced/patches/youtube/general/toolbar/ToolBarComponentsPatch.kt
@@ -18,7 +18,6 @@ import app.revanced.patches.youtube.utils.patch.PatchList.TOOLBAR_COMPONENTS
import app.revanced.patches.youtube.utils.playservice.versionCheckPatch
import app.revanced.patches.youtube.utils.resourceid.actionBarRingoBackground
import app.revanced.patches.youtube.utils.resourceid.sharedResourceIdPatch
-import app.revanced.patches.youtube.utils.resourceid.voiceSearch
import app.revanced.patches.youtube.utils.resourceid.ytOutlineVideoCamera
import app.revanced.patches.youtube.utils.resourceid.ytPremiumWordMarkHeader
import app.revanced.patches.youtube.utils.resourceid.ytWordMarkHeader
@@ -32,6 +31,7 @@ import app.revanced.util.doRecursively
import app.revanced.util.findInstructionIndicesReversedOrThrow
import app.revanced.util.findMethodOrThrow
import app.revanced.util.fingerprint.matchOrThrow
+import app.revanced.util.fingerprint.methodCall
import app.revanced.util.fingerprint.methodOrThrow
import app.revanced.util.fingerprint.mutableClassOrThrow
import app.revanced.util.getReference
@@ -332,8 +332,17 @@ val toolBarComponentsPatch = bytecodePatch(
searchResultFingerprint.matchOrThrow().let {
it.method.apply {
- val startIndex = indexOfFirstLiteralInstructionOrThrow(voiceSearch)
- val setOnClickListenerIndex = indexOfFirstInstructionOrThrow(startIndex) {
+ val voiceInputControllerActivityMethodCall =
+ voiceInputControllerFingerprint
+ .methodOrThrow(voiceInputControllerParentFingerprint)
+ .methodCall()
+
+ val voiceInputControllerActivityIndex =
+ indexOfFirstInstructionOrThrow {
+ opcode == Opcode.INVOKE_VIRTUAL &&
+ getReference()?.toString() == voiceInputControllerActivityMethodCall
+ }
+ val setOnClickListenerIndex = indexOfFirstInstructionOrThrow(voiceInputControllerActivityIndex) {
opcode == Opcode.INVOKE_VIRTUAL &&
getReference()?.name == "setOnClickListener"
}
diff --git a/patches/src/main/kotlin/app/revanced/patches/youtube/layout/actionbuttons/ShortsActionButtonsPatch.kt b/patches/src/main/kotlin/app/revanced/patches/youtube/layout/actionbuttons/ShortsActionButtonsPatch.kt
index 561eecab5..a955491e1 100644
--- a/patches/src/main/kotlin/app/revanced/patches/youtube/layout/actionbuttons/ShortsActionButtonsPatch.kt
+++ b/patches/src/main/kotlin/app/revanced/patches/youtube/layout/actionbuttons/ShortsActionButtonsPatch.kt
@@ -9,6 +9,7 @@ import app.revanced.patches.youtube.utils.playservice.versionCheckPatch
import app.revanced.patches.youtube.utils.settings.ResourceUtils.addPreference
import app.revanced.patches.youtube.utils.settings.settingsPatch
import app.revanced.util.ResourceGroup
+import app.revanced.util.Utils.printInfo
import app.revanced.util.copyResources
import app.revanced.util.inputStreamFromBundledResourceOrThrow
import app.revanced.util.lowerCaseOrThrow
@@ -63,7 +64,7 @@ val shortsActionButtonsPatch = resourcePatch(
.lowerCaseOrThrow()
if (iconType == YOUTUBE_ICON) {
- println("INFO: Shorts action buttons will remain unchanged as it matches the original.")
+ printInfo("Shorts action buttons will remain unchanged as it matches the original.")
addPreference(CUSTOM_SHORTS_ACTION_BUTTONS)
return@execute
}
diff --git a/patches/src/main/kotlin/app/revanced/patches/youtube/layout/branding/icon/CustomBrandingIconPatch.kt b/patches/src/main/kotlin/app/revanced/patches/youtube/layout/branding/icon/CustomBrandingIconPatch.kt
index 4abecb1a7..ededd6ab4 100644
--- a/patches/src/main/kotlin/app/revanced/patches/youtube/layout/branding/icon/CustomBrandingIconPatch.kt
+++ b/patches/src/main/kotlin/app/revanced/patches/youtube/layout/branding/icon/CustomBrandingIconPatch.kt
@@ -147,7 +147,7 @@ val customBrandingIconPatch = resourcePatch(
val copiedFiles = copyFile(
launcherIconResourceGroups,
appIcon,
- "WARNING: Invalid app icon path: $appIcon. Does not apply patches."
+ "Invalid app icon path: $appIcon. Does not apply patches."
)
if (copiedFiles)
updatePatchStatusIcon("custom")
diff --git a/patches/src/main/kotlin/app/revanced/patches/youtube/layout/header/ChangeHeaderPatch.kt b/patches/src/main/kotlin/app/revanced/patches/youtube/layout/header/ChangeHeaderPatch.kt
index b9c01c833..986292470 100644
--- a/patches/src/main/kotlin/app/revanced/patches/youtube/layout/header/ChangeHeaderPatch.kt
+++ b/patches/src/main/kotlin/app/revanced/patches/youtube/layout/header/ChangeHeaderPatch.kt
@@ -7,6 +7,7 @@ import app.revanced.patches.youtube.utils.patch.PatchList.CUSTOM_HEADER_FOR_YOUT
import app.revanced.patches.youtube.utils.settings.ResourceUtils.getIconType
import app.revanced.patches.youtube.utils.settings.settingsPatch
import app.revanced.util.ResourceGroup
+import app.revanced.util.Utils.printWarn
import app.revanced.util.Utils.trimIndentMultiline
import app.revanced.util.copyFile
import app.revanced.util.copyResources
@@ -125,7 +126,7 @@ val changeHeaderPatch = resourcePatch(
customBrandingIconType != "default" && customBrandingIconType != "custom"
customHeader = customHeaderOption.valueOrThrow()
- val warnings = "WARNING: Invalid header path: $customHeader. Does not apply patches."
+ val warnings = "Invalid header path: $customHeader. Does not apply patches."
if (isPath) {
copyFile(
@@ -140,7 +141,7 @@ val changeHeaderPatch = resourcePatch(
}
}
} else {
- println(warnings)
+ printWarn(warnings)
return@execute
}
diff --git a/patches/src/main/kotlin/app/revanced/patches/youtube/player/components/PlayerComponentsPatch.kt b/patches/src/main/kotlin/app/revanced/patches/youtube/player/components/PlayerComponentsPatch.kt
index cfa71953e..3934b2277 100644
--- a/patches/src/main/kotlin/app/revanced/patches/youtube/player/components/PlayerComponentsPatch.kt
+++ b/patches/src/main/kotlin/app/revanced/patches/youtube/player/components/PlayerComponentsPatch.kt
@@ -35,6 +35,7 @@ import app.revanced.patches.youtube.utils.youtubeControlsOverlayFingerprint
import app.revanced.patches.youtube.video.information.hookVideoInformation
import app.revanced.patches.youtube.video.information.videoInformationPatch
import app.revanced.util.REGISTER_TEMPLATE_REPLACEMENT
+import app.revanced.util.Utils.printWarn
import app.revanced.util.findMethodOrThrow
import app.revanced.util.fingerprint.injectLiteralInstructionBooleanCall
import app.revanced.util.fingerprint.injectLiteralInstructionViewCall
@@ -382,7 +383,7 @@ val playerComponentsPatch = bytecodePatch(
name == "onClick"
}.hookInitVideoPanel(0)
} else {
- println("WARNING: target Opcode not found in ${fingerprint.first}")
+ printWarn("target Opcode not found in ${fingerprint.first}")
}
}
}
diff --git a/patches/src/main/kotlin/app/revanced/patches/youtube/player/fullscreen/FullscreenComponentsPatch.kt b/patches/src/main/kotlin/app/revanced/patches/youtube/player/fullscreen/FullscreenComponentsPatch.kt
index 6c47216a9..f00ad80bd 100644
--- a/patches/src/main/kotlin/app/revanced/patches/youtube/player/fullscreen/FullscreenComponentsPatch.kt
+++ b/patches/src/main/kotlin/app/revanced/patches/youtube/player/fullscreen/FullscreenComponentsPatch.kt
@@ -28,6 +28,7 @@ import app.revanced.patches.youtube.utils.resourceid.sharedResourceIdPatch
import app.revanced.patches.youtube.utils.settings.ResourceUtils.addPreference
import app.revanced.patches.youtube.utils.settings.settingsPatch
import app.revanced.patches.youtube.utils.youtubeControlsOverlayFingerprint
+import app.revanced.util.Utils.printWarn
import app.revanced.util.findMethodOrThrow
import app.revanced.util.fingerprint.methodOrThrow
import app.revanced.util.fingerprint.mutableClassOrThrow
@@ -321,7 +322,7 @@ val fullscreenComponentsPatch = bytecodePatch(
settingArray += "SETTINGS: KEEP_LANDSCAPE_MODE"
} else {
- println("WARNING: \"Keep landscape mode\" is not supported in this version. Use YouTube 19.16.39 or earlier.")
+ printWarn("\"Keep landscape mode\" is not supported in this version. Use YouTube 19.16.39 or earlier.")
}
// endregion
diff --git a/patches/src/main/kotlin/app/revanced/patches/youtube/player/seekbar/SeekbarComponentsPatch.kt b/patches/src/main/kotlin/app/revanced/patches/youtube/player/seekbar/SeekbarComponentsPatch.kt
index c4691d7d3..49c3fb886 100644
--- a/patches/src/main/kotlin/app/revanced/patches/youtube/player/seekbar/SeekbarComponentsPatch.kt
+++ b/patches/src/main/kotlin/app/revanced/patches/youtube/player/seekbar/SeekbarComponentsPatch.kt
@@ -35,6 +35,7 @@ import app.revanced.patches.youtube.utils.settings.ResourceUtils.getContext
import app.revanced.patches.youtube.utils.settings.settingsPatch
import app.revanced.patches.youtube.utils.totalTimeFingerprint
import app.revanced.patches.youtube.video.information.videoInformationPatch
+import app.revanced.util.Utils.printWarn
import app.revanced.util.copyXmlNode
import app.revanced.util.findElementByAttributeValueOrThrow
import app.revanced.util.findMethodsOrThrow
@@ -468,7 +469,7 @@ val seekbarComponentsPatch = bytecodePatch(
updatePatchStatus(PATCH_STATUS_CLASS_DESCRIPTOR, "OldSeekbarThumbnailsDefaultBoolean")
} else {
- println("WARNING: \"Restore old seekbar thumbnails\" is not supported in this version. Use YouTube 19.16.39 or earlier.")
+ printWarn("\"Restore old seekbar thumbnails\" is not supported in this version. Use YouTube 19.16.39 or earlier.")
}
// endregion
diff --git a/patches/src/main/kotlin/app/revanced/patches/youtube/shorts/components/ShortsComponentPatch.kt b/patches/src/main/kotlin/app/revanced/patches/youtube/shorts/components/ShortsComponentPatch.kt
index 34402150f..76363b6df 100644
--- a/patches/src/main/kotlin/app/revanced/patches/youtube/shorts/components/ShortsComponentPatch.kt
+++ b/patches/src/main/kotlin/app/revanced/patches/youtube/shorts/components/ShortsComponentPatch.kt
@@ -28,6 +28,7 @@ import app.revanced.patches.youtube.utils.lottie.lottieAnimationViewHookPatch
import app.revanced.patches.youtube.utils.mainactivity.mainActivityResolvePatch
import app.revanced.patches.youtube.utils.navigation.addBottomBarContainerHook
import app.revanced.patches.youtube.utils.navigation.navigationBarHookPatch
+import app.revanced.patches.youtube.utils.patch.PatchList.HIDE_FEED_FLYOUT_MENU
import app.revanced.patches.youtube.utils.patch.PatchList.SHORTS_COMPONENTS
import app.revanced.patches.youtube.utils.playertype.playerTypeHookPatch
import app.revanced.patches.youtube.utils.playservice.is_18_31_or_greater
@@ -315,11 +316,8 @@ private val shortsCustomActionsPatch = bytecodePatch(
val insertIndex = charSequenceIndex + 2
- if (getInstruction(insertIndex).reference.toString()
- .startsWith("Lapp/revanced")
- ) {
+ if (HIDE_FEED_FLYOUT_MENU.included == true)
removeInstructions(insertIndex, 2)
- }
addInstructions(
insertIndex, """
diff --git a/patches/src/main/kotlin/app/revanced/patches/youtube/utils/fix/streamingdata/SpoofStreamingDataPatch.kt b/patches/src/main/kotlin/app/revanced/patches/youtube/utils/fix/streamingdata/SpoofStreamingDataPatch.kt
index a2ba66890..c82bbb612 100644
--- a/patches/src/main/kotlin/app/revanced/patches/youtube/utils/fix/streamingdata/SpoofStreamingDataPatch.kt
+++ b/patches/src/main/kotlin/app/revanced/patches/youtube/utils/fix/streamingdata/SpoofStreamingDataPatch.kt
@@ -1,5 +1,7 @@
package app.revanced.patches.youtube.utils.fix.streamingdata
+import app.revanced.patcher.extensions.InstructionExtensions.replaceInstruction
+import app.revanced.patches.shared.extension.Constants.PATCHES_PATH
import app.revanced.patches.shared.spoof.streamingdata.baseSpoofStreamingDataPatch
import app.revanced.patches.shared.spoof.useragent.baseSpoofUserAgentPatch
import app.revanced.patches.youtube.utils.compatibility.Constants.COMPATIBLE_PACKAGE
@@ -7,6 +9,7 @@ import app.revanced.patches.youtube.utils.compatibility.Constants.YOUTUBE_PACKAG
import app.revanced.patches.youtube.utils.patch.PatchList.SPOOF_STREAMING_DATA
import app.revanced.patches.youtube.utils.settings.ResourceUtils.addPreference
import app.revanced.patches.youtube.utils.settings.settingsPatch
+import app.revanced.util.findMethodOrThrow
val spoofStreamingDataPatch = baseSpoofStreamingDataPatch(
{
@@ -18,6 +21,13 @@ val spoofStreamingDataPatch = baseSpoofStreamingDataPatch(
)
},
{
+ findMethodOrThrow("$PATCHES_PATH/PatchStatus;") {
+ name == "SpoofStreamingDataAndroidOnlyDefaultBoolean"
+ }.replaceInstruction(
+ 0,
+ "const/4 v0, 0x1"
+ )
+
addPreference(
arrayOf(
"SETTINGS: SPOOF_STREAMING_DATA"
diff --git a/patches/src/main/kotlin/app/revanced/util/BytecodeUtils.kt b/patches/src/main/kotlin/app/revanced/util/BytecodeUtils.kt
index ad3f5468f..9f9c0989d 100644
--- a/patches/src/main/kotlin/app/revanced/util/BytecodeUtils.kt
+++ b/patches/src/main/kotlin/app/revanced/util/BytecodeUtils.kt
@@ -20,6 +20,7 @@ import app.revanced.patcher.util.proxy.mutableTypes.MutableMethod.Companion.toMu
import app.revanced.patches.shared.mapping.get
import app.revanced.patches.shared.mapping.resourceMappingPatch
import app.revanced.patches.shared.mapping.resourceMappings
+import app.revanced.util.Utils.printWarn
import com.android.tools.smali.dexlib2.AccessFlags
import com.android.tools.smali.dexlib2.Opcode
import com.android.tools.smali.dexlib2.iface.Method
@@ -135,7 +136,7 @@ internal fun MutableMethod.addInstructionsAtControlFlowLabel(
fun Method.indexOfFirstResourceId(resourceName: String): Int {
val resourceId = resourceMappings["id", resourceName]
if (resourceId == -1L) {
- println("WARNING: Could not find resource type: id name: $name")
+ printWarn("Could not find resource type: id name: $name")
return -1
}
return indexOfFirstLiteralInstruction(resourceId)
diff --git a/patches/src/main/kotlin/app/revanced/util/ResourceUtils.kt b/patches/src/main/kotlin/app/revanced/util/ResourceUtils.kt
index 89cef3780..89be81890 100644
--- a/patches/src/main/kotlin/app/revanced/util/ResourceUtils.kt
+++ b/patches/src/main/kotlin/app/revanced/util/ResourceUtils.kt
@@ -5,6 +5,7 @@ import app.revanced.patcher.patch.Patch
import app.revanced.patcher.patch.PatchException
import app.revanced.patcher.patch.ResourcePatchContext
import app.revanced.patcher.util.Document
+import app.revanced.util.Utils.printWarn
import org.w3c.dom.Element
import org.w3c.dom.Node
import org.w3c.dom.NodeList
@@ -202,7 +203,7 @@ fun ResourcePatchContext.copyFile(
return true
} catch (_: Exception) {
- println(warning)
+ printWarn(warning)
}
}
return false
diff --git a/patches/src/main/kotlin/app/revanced/util/Utils.kt b/patches/src/main/kotlin/app/revanced/util/Utils.kt
index 57f0edf03..e56cce162 100644
--- a/patches/src/main/kotlin/app/revanced/util/Utils.kt
+++ b/patches/src/main/kotlin/app/revanced/util/Utils.kt
@@ -1,8 +1,18 @@
package app.revanced.util
+import java.util.logging.Logger
+
internal object Utils {
internal fun String.trimIndentMultiline() =
this.split("\n")
.joinToString("\n") { it.trimIndent() } // Remove the leading whitespace from each line.
.trimIndent() // Remove the leading newline.
+
+ private val logger = Logger.getLogger(this::class.java.name)
+
+ internal fun printInfo(msg: String) =
+ logger.info(msg)
+
+ internal fun printWarn(msg: String) =
+ logger.warning(msg)
}
diff --git a/patches/src/main/resources/music/settings/host/values/arrays.xml b/patches/src/main/resources/music/settings/host/values/arrays.xml
index c0fcf769b..81986ed4c 100644
--- a/patches/src/main/resources/music/settings/host/values/arrays.xml
+++ b/patches/src/main/resources/music/settings/host/values/arrays.xml
@@ -56,12 +56,14 @@
- 4.27.53
- - @string/revanced_spoof_client_type_entry_android_music
- - @string/revanced_spoof_client_type_entry_ios_music
+ - @string/revanced_spoof_client_type_entry_ios_music_6_21
+ - @string/revanced_spoof_client_type_entry_android_music_5_29
+ - @string/revanced_spoof_client_type_entry_android_music_4_27
- - ANDROID_MUSIC
- - IOS_MUSIC
+ - IOS_MUSIC_6_21
+ - ANDROID_MUSIC_5_29
+ - ANDROID_MUSIC_4_27
- @string/revanced_spoof_streaming_data_type_entry_android_vr
diff --git a/patches/src/main/resources/music/settings/host/values/strings.xml b/patches/src/main/resources/music/settings/host/values/strings.xml
index 9278a9f5c..d04d6e15b 100644
--- a/patches/src/main/resources/music/settings/host/values/strings.xml
+++ b/patches/src/main/resources/music/settings/host/values/strings.xml
@@ -452,14 +452,13 @@ Tap the continue button and allow optimization changes."
"Spoof the client to prevent playback issues.
※ When used with 'Spoofing streaming data', playback issues may occur."
- Use old client
- "Spoofing with version 4.27.53.
-
-OPUS codec may not work."
Default client
- Defines a default client to spoofing.
- Android Music
- iOS Music
+ "Defines a default client to spoofing.
+
+※ When using the Android client, it is recommended to use it with 'Spoof app version'."
+ Android Music 4.27.53
+ Android Music 5.29.53
+ iOS Music 6.21
Spoof streaming data
"Spoof the streaming data to prevent playback issues.
diff --git a/patches/src/main/resources/music/translations/el-rGR/strings.xml b/patches/src/main/resources/music/translations/el-rGR/strings.xml
index eac2900ee..e332009ef 100644
--- a/patches/src/main/resources/music/translations/el-rGR/strings.xml
+++ b/patches/src/main/resources/music/translations/el-rGR/strings.xml
@@ -172,7 +172,7 @@
Παραποίηση έκδοσης εφαρμογής
"Παραποίηση έκδοσης εφαρμογής σε παλιότερη έκδοση.
-• Αυτό θα αλλάξει την εμφάνιση της εφαρμογής, αλλά πιθανότατα να προκύψουν άγνωστα θέματα.
+• Αυτό θα αλλάξει την εμφάνιση της εφαρμογής, αλλά πιθανότατα να προκύψουν άγνωστες παρενέργειες.
• Αν αργότερα γίνει απενεργοποίηση, η παλιά εμφάνιση μπορεί να παραμείνει μέχρι να διαγραφούν τα δεδομένα της εφαρμογής."
Έκδοση της εφαρμογής που θα χρησιμοποιηθεί
Επιλέξτε την έκδοση εφαρμογής που θα χρησιμοποιηθεί.
@@ -400,12 +400,16 @@
Πατήστε το κουμπί «Συνέχεια» και επιτρέψτε τις αλλαγές βελτιστοποίησης."
Συνέχεια
Παραποίηση προγράμματος πελάτη
- "Παραποίηση του προγράμματος πελάτη για την αποφυγή προβλημάτων αναπαραγωγής. Περιορισμοί:
-• Ο κωδικοποιητής ήχου OPUS ενδέχεται να μην υποστηρίζεται.
-• Οι μικρογραφίες προεπισκόπησης στη γραμμή προόδου ενδέχεται να μην υπάρχουν.
-• Το ιστορικό παρακολούθησης δεν λειτουργεί σε λογαριασμούς επωνυμίας (brand).
+ "Παραποίηση του προγράμματος πελάτη για την αποφυγή προβλημάτων αναπαραγωγής.
※ Αν ενεργοποιηθεί παράλληλα με τη λειτουργία «Παραποίηση δεδομένων ροής», ενδέχεται να εμφανιστούν προβλήματα αναπαραγωγής."
+ Προεπιλεγμένο πρόγραμμα πελάτη
+ "Καθορισμός ενός προεπιλεγμένου πρόγραμμα πελάτη για παραποίηση.
+
+※ Όταν ορίζεται πρόγραμμα πελάτη τύπου Android, συνιστάται να το χρησιμοποιήσετε μαζί με την λειτουργία «Παραποίηση έκδοσης εφαρμογής»."
+ Android Music 4.27.53
+ Android Music 5.29.53
+ iOS Music 6.21
Παραποίηση δεδομένων ροής
"Παραποίηση των δεδομένων ροής για την αποφυγή προβλημάτων αναπαραγωγής.
diff --git a/patches/src/main/resources/music/translations/hu-rHU/strings.xml b/patches/src/main/resources/music/translations/hu-rHU/strings.xml
index 868fb50e0..ccac0dbb1 100644
--- a/patches/src/main/resources/music/translations/hu-rHU/strings.xml
+++ b/patches/src/main/resources/music/translations/hu-rHU/strings.xml
@@ -89,6 +89,7 @@ Információ:
Rész menü elrejtése
Podcast menü elrejtése
Súgó & visszajelzés menü elrejtése
+ Fül elrejtése a Gyors elérés menüben
Következő lejátszása menü elrejtése
Minőség menü elrejtése
Eltávolítás a könyvtárból menü elrejtése
@@ -103,6 +104,7 @@ Információ:
Rádió indítás menü elrejtése
Statisztikák kockáknak menü elrejtése
Feliratkozás / Leiratkozás menü elrejtése
+ Kitűzés a Gyorshívóba menü elrejtése
Dalkredit menü elrejtése
Megtekintés folytatása
Folytatja a videót attól az időponttól, amikor átváltasz a Youtube-ra.
@@ -173,6 +175,7 @@ Ez nem kerüli meg a korhatárkorlátozást. Csak automatikusan elfogadja azt."<
Válaszd ki, hogy melyik alkalmazásverziót akarod használni.
4.27.53 - Letiltja a rádió módot Kanada területén
6.11.52 - Letiltja a valós idejű dalszövegeket
+ 7.16.53 - Régi menüsor visszaállítása
Navigációs sor
Fekete navigációs sor engedélyezése
@@ -240,8 +243,12 @@ Előfordulhat, hogy egyes funkciók nem működnek megfelelően a régi lejátsz
Általános menü elrejtése
Következő lejátszása menü elrejtése
Adatmegtakarító menü elejtése
+ Letöltések és tárhely menü elrejtése
Értesítések menü elrejtése
- Névjegy menü elrejtése
+ Adatvédelem és adatok elrejtése
+ Ajánlott elrejtése
+ Music Premium-tagság vásárlása menü elrejtése
+ A Youtube Music névjegye menü elrejtése
Videó
Egyéni lejátszási sebességek szerkesztése
@@ -249,11 +256,13 @@ Előfordulhat, hogy egyes funkciók nem működnek megfelelően a régi lejátsz
Lejátszási sebesség módosításainak megjegyzése
Megjegyzi az utoljára kiválasztott lejátszási sebességet.
Mutass egy felugró értesítést
+ Értesíts, ha változik a lejátszás sebessége.
Videó minőség megjegyzése
Megjegyezi a legutolsó videó minőséget, amit kiválasztottál.
Mutass egy felugró értesítést
- Az egyéni sebességnek kisebbnek kell lennie, mint %sx. Alapértelmezett értékek használata.
- Érvénytelen egyedi lejátszási sebesség. Használd az alap értékeket.
+ Értesíts ha meg változik a lejátszás minőségének beállítása.
+ Az egyéni sebességnek kisebbnek kell lennie, mint %sx.
+ Érvénytelen egyedi lejátszási sebesség.
Megváltoztatva az alap sebességet %s-re.
Az alapértelmezett mobiladat minőség módosítása a következőre %s.
Nem sikerült beállítani a minőséget.
@@ -266,6 +275,8 @@ Előfordulhat, hogy egyes funkciók nem működnek megfelelően a régi lejátsz
A nem tetszések százalékos arányát jeleníti meg a nem tetszések száma helyett.
Kompakt kedvelés gomb
Elrejti a kedvelés gomb elválasztóját.
+ Mutassa a becsült kedveléseket
+ Megjeleníti a becsült kedvelések számát a videóknál.
Köszöntő megjelenítése, ha az API nem elérhető
Megjelenik egy üzenet, ha a YouTube nem tetszések visszaállítása API nem érhető el.
Névjegy
@@ -277,12 +288,24 @@ Előfordulhat, hogy egyes funkciók nem működnek megfelelően a régi lejátsz
A nem tetszik funkció nem elérhető (%s).
Rejtett
+ Youtube felhasználónév visszaállítása
+ Youtube felhasználónév visszaállítása
+ A hozzászólásokban az azonosítót lecseréli a felhasználónevekre.
+ Megjelenítési formátum
+ Válaszd ki a felhasználónév megjelenítési formátumát.
Felhasználónév
- Felhasználónév (@kezelő)
- \@kezelő (felhasználónév)
- YouTube adat API kulcs
+ Felhasználónév (@azonosító)
+ \@azonosító (felhasználónév)
+ YouTube Data API kulcs
A fejlesztői kulcs a YouTube Data API v3 használatához.
A YouTube Data API-kulcsról
+ "YouTube Data API v3 fejlesztői kulcsra van szükség az azonosítók felhasználónevekre való cseréjéhez.
+
+Az ingyenes csomagban az API-kulcsok napi kvótája 10 000, és 1 kvótával 1 megjegyzéshez egy azonosító felhasználónévre cserélhető.
+
+Kattints ide az API-kulcs megszerzéséhez."
+ YouTube Data API v3 fejlesztői kulcs megszerzése
+ 1. Nyisd meg az <a href=%1$s>Új projekt létrehozását</a>.<br>2. Kattints a <b>LÉTREHOZÁS</b> gombra.<br>3. Lépj a <a href=%2$s>YouTube Data API v3</a> oldalára.<br>4. Kattints az <b>Engedélyezés</b> gombra.<br>5. Kattints a <b>HITELEZÉSI ADATOK LÉTREHOZÁSA</b> gombot.<br>6. Válaszd ki a <b>Nyilvános adatok</b> lehetőség.<br>7. Kattints a <b>KÖVETKEZŐ</b> gombra.<br>8. Másold ki az API-kulcsot.<br><br>※ Az API-kulcsot soha nem szabad megosztani másokkal, így az nem szerepel az importálási/exportálási beállításokban.
Szponzor Blokk
SzponsorBlokk engedélyezése
@@ -334,7 +357,7 @@ Előfordulhat, hogy egyes funkciók nem működnek megfelelően a régi lejátsz
Szín:
Szín módosítva.
Szín visszaállítva.
- Érvénytelen színkód. Visszaállítva alapra.
+ Érvénytelen színkód.
Szín visszaállítása
Az adatokat a SponsorBlock API biztosítja. Nyomj ide, ha többet szeretnél megtudni és megtekintenéd a letöltéseket más platformokra.
Névjegy
@@ -345,6 +368,10 @@ Előfordulhat, hogy egyes funkciók nem működnek megfelelően a régi lejátsz
Helyettesíti az egyes régiókban blokkolt tartományt, így a lejátszási lista miniatűrjei, csatorna avatarok stb. fogadhatóak.
Megosztási lap megváltoztatása
Váltás az alkalmazáson belüli megosztási lapról a rendszer megosztási lapjára.
+ Betöltési animáció kikapcsolása
+ Letiltja a betöltési animációt amikor az app indul.
+ DRC hang letiltása
+ Letiltja a hangra alkalmazott DRC-t (dinamikatartomány-kompresszió).
Hibanaplók engedélyezése
Kiírja a hibanaplót.
Hibakeresési puffer naplózásának engedélyezése
@@ -367,10 +394,37 @@ Ez szükséges az app működéséhez."
Weboldal megnyitása
"A GmsCore akkumulátor-optimalizálásokat le kell tiltani a problémák megelőzése érdekében.
-Nyomd meg a folytatás gombot, és tiltsd le az akkumulátor-optimalizálásokat."
+A GmsCore akkumulátor-optimalizálás letiltása nem fogyasztja jobban az akkumulátort.
+
+Nyomj a folytatás gombra, és engedélyezd az optimalizálási módosításokat."
Folytatás
+ Kliens hamisítása
+ "A kliens hamisítása a lejátszási problémák elkerülése érdekében.
+
+※ Az 'Adatfolyam meghamisítása' használata esetén lejátszási problémák léphetnek fel."
+ Alapértelmezett kliens
+ "Meghatározza az alapértelmezett klienst a hamisításhoz.
+
+※ Az Android kliens használata esetén ajánlott a 'Alkalmazás verziójának meghamisítása' használni."
+ Android Music 4.27.53
+ Android Music 5.29.53
+ iOS Music 6.21
+ Adatfolyam meghamisítása
+ "A lejátszási problémák megelőzése érdekében hamisítja a streaming-adatokat.
+
+※ A 'Kliens hamisítása' használata esetén lejátszási problémák léphetnek fel."
+ Alapértelmezett kliens
+ Meghatároz egy alapértelmezett klienst, amely streaming adatokat hív le.
+ Megjelenik a statisztikák kockáknakban
+ Megmutatja a streaming adatok lekérdezésére használt klienst a Statisztikák kockáknakban.
+ iOS
+ iOS Music
+ Android TV
+ Android VR
Megosztási linkek tisztítása
Linkek megosztásakor eltávolítja a nyomkövetési paramétereket az URL-ekből.
+ Alapértelmezett program beállítások megnyitása
+ A YouTube Music linkek megnyitásához az RVX Musicban engedélyezze a \'Támogatott linkek megnyitása\' opciót, és engedélyezze a támogatott webcímeket.
Beállítások Importálása / Exportálása
Beállítások importálása vagy exportálása.
Beállítások exportálása egy fájlba
diff --git a/patches/src/main/resources/music/translations/ja-rJP/strings.xml b/patches/src/main/resources/music/translations/ja-rJP/strings.xml
index e9433c7b8..c1300fea2 100644
--- a/patches/src/main/resources/music/translations/ja-rJP/strings.xml
+++ b/patches/src/main/resources/music/translations/ja-rJP/strings.xml
@@ -368,6 +368,7 @@ API キーの発行方法については、ここをタップしてください
アプリ内共有メニューからシステムの共有メニューに置き換えます。
Cairo スプラッシュアニメーションを無効にする
アプリ起動時のCairo のスプラッシュアニメーションを無効にします。
+ DRCオーディオを無効にする
デバッグログ
デバッグログを出力します。
デバッグバッファログを有効化
@@ -391,6 +392,9 @@ API キーの発行方法については、ここをタップしてください
• OPUSオーディオコーデックはサポートされていない可能性があります。
• シークバーのサムネイルが表示されない場合があります。
• 再生履歴はブランドアカウントでは動作しません。"
+ iOS
+ Android TV
+ Android VR
共有リンクのクリーンアップ
リンクを共有する際に、URL からトラッキングクエリパラメーターを削除します。
「デフォルトで開く」の設定
diff --git a/patches/src/main/resources/music/translations/ko-rKR/strings.xml b/patches/src/main/resources/music/translations/ko-rKR/strings.xml
index 2cb0476c0..0ff9670d8 100644
--- a/patches/src/main/resources/music/translations/ko-rKR/strings.xml
+++ b/patches/src/main/resources/music/translations/ko-rKR/strings.xml
@@ -129,7 +129,7 @@
보관함
구독
자동 자막 비활성화
- 자막 사용이 강제된 동영상에서 자막을 비활성화합니다.
+ 자막이 자동으로 활성화되지 않도록 설정합니다.
싫어요 리다이렉션 비활성화
\'싫어요 버튼을 누르면 다음 트랙으로 리다이렉션\'을 비활성화합니다.
가로 모드 활성화
@@ -384,7 +384,7 @@ API Key를 발급받는 방법을 보려면 여기를 누르세요."
"플레이어 응답에 OPUS 코덱이 포함된 경우에는 OPUS 코덱을 활성화합니다.
알림:
-• 최신 YT Music 클라이언트는 기본적으로 OPUS 오디오 코덱을 사용합니다.
+• 최신 YT Music 클라이언트는 기본적으로 OPUS 코덱을 사용합니다.
• 이 설정은 아주 오래된 클라이언트 사용자에게만 유효합니다."
GmsCore 열기
알림 수신을 위한 클라우드 메시징 설정을 할 수 있습니다.
@@ -403,14 +403,18 @@ API Key를 발급받는 방법을 보려면 여기를 누르세요."
클라이언트 변경
"클라이언트를 변경하여 재생 문제를 방지할 수 있습니다.
-알려진 문제점:
-• OPUS 코덱이 지원되지 않을 수 있습니다.
-• 재생바 썸네일이 표시되지 않을 수 있습니다.
-• 브랜드 계정에서는 시청 기록이 작동되지 않습니다."
+※ '스트리밍 데이터 변경'과 함께 사용할 경우에 재생 문제가 발생할 수 있습니다."
+ 기본 클라이언트
+ "변경할 기본 클라이언트를 정의합니다.
+
+※ Android 클라이언트를 사용할 경우에 '앱 버전 변경'과 함께 사용하는 것을 권장합니다."
+ Android Music 4.27.53
+ Android Music 5.29.53
+ iOS Music 6.21
스트리밍 데이터 변경
"스트리밍 데이터를 변경하여 재생 문제를 방지합니다.
-※ '클라이언트 변경'과 함께 사용하면, 재생 문제가 발생할 수 있습니다."
+※ '클라이언트 변경'과 함께 사용할 경우에 재생 문제가 발생할 수 있습니다."
기본 클라이언트
스트리밍 데이터를 가져오는 데 사용되는 기본 클라이언트를 정의할 수 있습니다.
동영상 통계에서 표시
diff --git a/patches/src/main/resources/music/translations/pl-rPL/strings.xml b/patches/src/main/resources/music/translations/pl-rPL/strings.xml
index dd82b83bb..b8b202749 100644
--- a/patches/src/main/resources/music/translations/pl-rPL/strings.xml
+++ b/patches/src/main/resources/music/translations/pl-rPL/strings.xml
@@ -404,13 +404,15 @@ Stuknij przycisk kontynuacji i zezwól na zmiany w optymalizacji."
Oszukuj klienta
"Oszukuj klienta, by zapobiec problemom z odtwarzaniem.
-Ograniczenia:
-• Kodek audio OPUS może nie być wspierany
-• Miniaturki podczas przewijania mogą nie być dostępne
-• Historia oglądania nie działa na kontach firmowych
-
※ Używane równolegle z opcją 'Oszukuj strumień danych', może powodować problemy z odtwarzaniem
"
+ Domyślny klient
+ "Definiuje domyślnego klienta do oszukiwania.
+
+※ Jeśli używasz klienta Androida, zaleca się korzystanie razem z opcją 'Oszukuj wersję aplikacji'."
+ Android Music 4.27.53
+ Android Music 5.29.53
+ iOS Music 6.21
Oszukuj strumień danych
"Oszukuj strumień danych, by zapobiec problemom z odtwarzaniem.
diff --git a/patches/src/main/resources/music/translations/pt-rBR/strings.xml b/patches/src/main/resources/music/translations/pt-rBR/strings.xml
index 3f0cecfe5..acbc47703 100644
--- a/patches/src/main/resources/music/translations/pt-rBR/strings.xml
+++ b/patches/src/main/resources/music/translations/pt-rBR/strings.xml
@@ -405,14 +405,8 @@ Toque no botão continuar e permita as alterações de otimização."
"Falsificar o cliente para evitar problemas de reprodução.
※ Quando usado com 'Dados de streaming falsos', podem ocorrer problemas de reprodução."
- Usar o cliente antigo
- "Falsificando com a versão 4.27.53.
-
-O codec OPUS pode não funcionar."
Cliente padrão
- Define um cliente padrão para falsificar.
- Android Music
- iOS Music
+ "Define um cliente padrão para falsificar."
Dados de streaming falsos
"Falsifique os dados de streaming para evitar problemas de reprodução.
diff --git a/patches/src/main/resources/music/translations/vi-rVN/strings.xml b/patches/src/main/resources/music/translations/vi-rVN/strings.xml
index a115da510..9814f38c7 100644
--- a/patches/src/main/resources/music/translations/vi-rVN/strings.xml
+++ b/patches/src/main/resources/music/translations/vi-rVN/strings.xml
@@ -395,23 +395,27 @@ Hãy làm theo hướng dẫn của 'Don't kill my app!' và tiến hành cài
Để ứng dụng hoạt động hiệu quả nhất."
Mở trang web
- "Tắt tối ưu hoá pin cho GmsCore để tránh các vấn đề phát sinh.
+ "Vui lòng tắt tối ưu hoá pin cho GmsCore để tránh phát sinh lỗi.
-Nhấn vào nút Tiếp tục và tắt tối ưu hóa pin."
+Tắt tối ưu hoá pin cho GmsCore sẽ không làm ảnh hưởng đáng kể tới thời lượng sử dụng pin.
+
+Nhấn vào Tiếp tục và cho phép thay đổi lựa chọn tối ưu hoá pin."
Tiếp tục
Giả mạo ứng dụng khách
"Giả mạo ứng dụng khách nhằm khắc phục sự cố phát.
-Hạn chế:
-• Codec âm thanh OPUS có thể không được hỗ trợ.
-• Hình thu nhỏ trên thanh tiến trình có thể không hiện hữu.
-• Nhật ký xem không hoạt động đối với tài khoản thương hiệu.
+※ Khi được sử dụng đồng thời với \"Giả mạo luồng dữ liệu trực tuyến\", có thể gây ra sự cố phát."
+ Ứng dụng khách mặc định
+ "Xác định ứng dụng khách mặc định để giả mạo.
-※ Khi được sử dụng cùng với \"Giả mạo luồng dữ liệu trực tuyến\", có thể gây ra sự cố phát."
+※ Khi sử dụng ứng dụng khách Android, tính năng này nên được sử dụng đồng thời với \"Giả mạo phiên bản ứng dụng\"."
+ Music Android 4.27.53
+ Music Android 5.29.53
+ Music iOS 6.21
Giả mạo luồng dữ liệu trực tuyến
"Giả mạo luồng dữ liệu trực tuyến nhằm khắc phục sự cố phát.
-※ Khi được sử dụng cùng với \"Giả mạo ứng dụng khách\", có thể gây ra sự cố phát."
+※ Khi được sử dụng đồng thời với \"Giả mạo ứng dụng khách\", có thể gây ra sự cố phát."
Ứng dụng khách mặc định
Xác định một ứng dụng khách mặc định để nạp luồng dữ liệu trực tuyến.
Hiển thị trong Thống kê chi tiết
diff --git a/patches/src/main/resources/youtube/settings/host/values/arrays.xml b/patches/src/main/resources/youtube/settings/host/values/arrays.xml
index d5f4ce10f..8538bf654 100644
--- a/patches/src/main/resources/youtube/settings/host/values/arrays.xml
+++ b/patches/src/main/resources/youtube/settings/host/values/arrays.xml
@@ -235,13 +235,23 @@
- HEART_TINT
- HIDDEN
-
- - @string/revanced_spoof_streaming_data_type_entry_ios
+
- @string/revanced_spoof_streaming_data_type_entry_android_unplugged
- @string/revanced_spoof_streaming_data_type_entry_android_vr
-
+
+ - ANDROID_UNPLUGGED
+ - ANDROID_VR
+
+
+ - @string/revanced_spoof_streaming_data_type_entry_ios
+ - @string/revanced_spoof_streaming_data_type_entry_ios_unplugged
+ - @string/revanced_spoof_streaming_data_type_entry_android_unplugged
+ - @string/revanced_spoof_streaming_data_type_entry_android_vr
+
+
- IOS
+ - IOS_UNPLUGGED
- ANDROID_UNPLUGGED
- ANDROID_VR
diff --git a/patches/src/main/resources/youtube/settings/host/values/strings.xml b/patches/src/main/resources/youtube/settings/host/values/strings.xml
index 28863227d..57025c2a6 100644
--- a/patches/src/main/resources/youtube/settings/host/values/strings.xml
+++ b/patches/src/main/resources/youtube/settings/host/values/strings.xml
@@ -1914,20 +1914,20 @@ Tap the continue button and allow optimization changes."
Default client
iOS
iOS Music
+ iOS TV
+ Android Creator
Android TV
Android VR
Spoofing side effects
• Not yet found.
- • Videos may end 1 second early.
- "• Audio track menu is missing.
-• Stable volume is not available."
- "• Audio track menu is missing.
-• Stable volume is not available."
- Sync video length before playback
- "Video length is synced before playback.
-Video length is exact value."
- "Video length is not synced before playback.
-Video length may be a rounded value."
+ • Movies or paid videos may not play.
+ "• Audio track menu is missing.
+• Stable volume is not available.
+• Disable forced auto audio tracks is not available."
+ Use Android clients only
+ Android clients are used to fetch streaming data.
+ Android and iOS clients are used to fetch streaming data.
+ Turning off this setting may cause video playback issues.
Show in Stats for nerds
Client used to fetch streaming data is shown in Stats for nerds.
Client used to fetch streaming data is hidden in Stats for nerds.
diff --git a/patches/src/main/resources/youtube/settings/xml/revanced_prefs.xml b/patches/src/main/resources/youtube/settings/xml/revanced_prefs.xml
index 0b04b9868..dbe7b8c0e 100644
--- a/patches/src/main/resources/youtube/settings/xml/revanced_prefs.xml
+++ b/patches/src/main/resources/youtube/settings/xml/revanced_prefs.xml
@@ -791,9 +791,9 @@
diff --git a/patches/src/main/resources/youtube/translations/ar/strings.xml b/patches/src/main/resources/youtube/translations/ar/strings.xml
index da5ddb49f..9ff0df85f 100644
--- a/patches/src/main/resources/youtube/translations/ar/strings.xml
+++ b/patches/src/main/resources/youtube/translations/ar/strings.xml
@@ -1786,20 +1786,19 @@
العميل الافتراضي
iOS
موسيقى iOS
+ iOS TV
+ Android Creator
Android TV
Android VR
التأثيرات الجانبية للتزييف
• لم يتم العثور عليه بعد.
- • قد تنتهي الفيديوهات قبل النهاية بـ 1 ثانية.
- "• قائمة المقطع الصوتي مفقودة.
-• مستوى الصوت الثابت غير متوفر."
- "• قائمة المقطع الصوتي مفقودة.
-• مستوى الصوت الثابت غير متوفر."
- مزامنة مدة الفيديو قبل التشغيل
- "تتم مزامنة مدة الفيديو قبل التشغيل.
-مدة الفيديو هي القيمة الدقيقة."
- "لا تتم مزامنة مدة الفيديو قبل التشغيل.
-قد تكون مدة الفيديو قيمة تقريبية."
+ • قد لا يتم تشغيل الأفلام أو الفيديوهات المدفوعة.
+ "• قائمة المقطع الصوتي مفقودة.
+• مستوى الصوت الثابت غير متاح.
+• تعطيل مقاطع الصوت التلقائية المفروضة غير متاح."
+ استخدام عملاء Android فقط
+ يتم استخدام عملاء Android لجلب البيانات المتدفقة.
+ يتم استخدام عملاء Android و iOS لجلب البيانات المتدفقة.
عرض في إحصاءات تقنية
يتم عرض العميل المستخدم لجلب بيانات البث في إحصاءات تقنية.
تم إخفاء العميل المستخدم لجلب بيانات البث في إحصاءات تقنية.
diff --git a/patches/src/main/resources/youtube/translations/bg-rBG/strings.xml b/patches/src/main/resources/youtube/translations/bg-rBG/strings.xml
index f04f783b4..a9164629d 100644
--- a/patches/src/main/resources/youtube/translations/bg-rBG/strings.xml
+++ b/patches/src/main/resources/youtube/translations/bg-rBG/strings.xml
@@ -1689,8 +1689,6 @@
Android TV
Android VR
Ефекти от замяната
- "• Липсва менюто за избор на аудио."
- "• Липсва менюто за избор на аудио."
Показване в \"Разширени статистики\"
Клиентът, използван за получаване на данни за потока, се показва в Статистика за системни администратори.
Клиентът, използван за получаване на данни за поток, е скрит в Статистика за системни администратори.
diff --git a/patches/src/main/resources/youtube/translations/el-rGR/strings.xml b/patches/src/main/resources/youtube/translations/el-rGR/strings.xml
index 472dfa4c2..4a6fe30b3 100644
--- a/patches/src/main/resources/youtube/translations/el-rGR/strings.xml
+++ b/patches/src/main/resources/youtube/translations/el-rGR/strings.xml
@@ -1801,20 +1801,19 @@ Playlists
Προεπιλογή
iOS
iOS Music
+ iOS TV
+ Android Creator
Android TV
Android VR
Παρενέργειες παραποίησης
• Δεν ανακαλύφθηκαν ακόμη.
- • Τα βίντεο μπορεί να τελειώνουν 1 δευτερόλεπτο νωρίτερα.
- "• Το μενού «Κομμάτι ήχου» λείπει.
-• Η λειτουργία «Σταθερή ένταση» δεν είναι διαθέσιμη."
- "• Το μενού «Κομμάτι ήχου» λείπει.
-• Η λειτουργία «Σταθερή ένταση» δεν είναι διαθέσιμη."
- Συγχρονισμός μήκους βίντεο πριν την αναπαραγωγή
- "Το μήκος του βίντεο συγχρονίζεται πριν την αναπαραγωγή.
-Το μήκος του βίντεο είναι ακριβής."
- "Το μήκος του βίντεο δεν συγχρονίζεται πριν την αναπαραγωγή.
-Το μήκος του βίντεο μπορεί να είναι στρογγυλεμένη τιμή."
+ • Οι ταινίες ή τα βίντεο επί πληρωμή ενδέχεται να μην αναπαράγονται.
+ "• Το μενού «Κομμάτι ήχου» λείπει.
+• Η λειτουργία «Σταθερή ένταση» δεν είναι διαθέσιμη.
+• Η λειτουργία «Απενεργοποίηση υποχρεωτικών κομματιών ήχου» δεν είναι διαθέσιμη."
+ Χρήση των προγραμμάτων πελάτη Android μόνο
+ Τα προγράμματα πελάτη Android χρησιμοποιούνται τη λήψη δεδομένων ροής.
+ Τα προγράμματα πελάτη Android και iOS χρησιμοποιούνται τη λήψη δεδομένων ροής.
Εμφάνιση στο «Στατιστικά για σπασίκλες»
Το πρόγραμμα πελάτη που χρησιμοποιείται για τη λήψη δεδομένων ροής εμφανίζεται στο μενού «Στατιστικά για σπασίκλες».
Το πρόγραμμα πελάτη που χρησιμοποιείται για τη λήψη δεδομένων ροής δεν εμφανίζεται στο μενού «Στατιστικά για σπασίκλες».
diff --git a/patches/src/main/resources/youtube/translations/es-rES/strings.xml b/patches/src/main/resources/youtube/translations/es-rES/strings.xml
index 3102cefa1..86aadb752 100644
--- a/patches/src/main/resources/youtube/translations/es-rES/strings.xml
+++ b/patches/src/main/resources/youtube/translations/es-rES/strings.xml
@@ -5,7 +5,7 @@
Tus controles se modifican porque un servicio de accesibilidad está activado.
ReVanced Extended
- Buscar %s
+ Buscar en %s
Restablecer valores predeterminados.
Funciones Experimentales
¿Quieres continuar?
@@ -320,6 +320,9 @@ Limitación: Es posible que el botón Atrás de la barra de herramientas no func
Desactivar animación de bienvenida
La animación de bienvenida está desactivada.
La animación de bienvenida está activada.
+ Desactivar barra de estado translúcida
+ La barra de estado es opaca.
+ La barra de estado es opaca o translúcida.
Activar pantalla de carga de degradado
La pantalla de carga de degradado está activada.
La pantalla de carga de degradado está desactivada.
@@ -358,6 +361,8 @@ Si se desactiva más tarde, se recomienda borrar los datos de la aplicación par
18.33.40 - Restaura la antigua barra de acción de Shorts
18.38.45 - Restaura el antiguo comportamiento de la calidad predeterminada de vídeo
18.48.39 - Desactiva la actualización en tiempo real de las visualizaciones y los me gusta
+ 19.26.42 - Desactivar icono de Cairo en la barra de navegación y herramientas
+ 19.33.37 - Restaurar el antiguo panel desplegable de velocidad de reproducción
Menú de cuenta
Ocultar o mostrar elementos en el menú de la cuenta y la pestaña Tú.
@@ -490,7 +495,12 @@ Nota: Al activar esto también se ocultan forzosamente los anuncios de vídeos."
Además, los anuncios ya no se bloquearán en Shorts.
Si este ajuste no surte efecto, prueba a cambiar al modo incógnito."
+ Desactivar barra translúcida luminosa
+ La barra de navegación en modo claro es opaca.
.
+ Desactivar barra translúcida oscura
+ La barra de navegación en modo oscuro es opaca.
+ La barra de navegación en modo oscuro es opaca o translúcida.
Ocultar barra de navegación
La barra de navegación está oculta.
La barra de navegación está visible.
@@ -650,6 +660,9 @@ Nota:
Ocultar paneles de información
Los paneles de información están ocultos.
Los paneles de información están visibles.
+ Ocultar resumen del chat en chat en directo
+ El resumen del chat está oculto.
+ El resumen del chat está visible.
Ocultar mensajes de chat en directo
Los mensajes de chat en directo están ocultos.\n\nEste ajuste se aplica también a los vídeos en directo de Shorts.
Los mensajes de chat en directo están visibles.\n\nEste ajuste se aplica también a los vídeos en directo de Shorts.
@@ -981,6 +994,7 @@ Toca y mantén para deshacer.
Información:
• Puede que no funcione en transmisiones."
Generar modo de lista
+ Todos los contenidos (Ordenar por tiempo, ascendente)
Todos los contenidos (Ordenar por tiempo)
Todos los contenidos (Ordenar por popular)
Solo vídeos (Ordenar por tiempo)
@@ -1143,6 +1157,7 @@ Información:
Visible en el historial de reproducciones.
Cambiar estado de repetición de Shorts
+ Cambiar estado de repetición de fondo de Shorts
Reproducción automática
Predeterminada
Pausar
@@ -1257,6 +1272,44 @@ Información:
Corazón (Tinte)
Oculto
+ Acciones personalizadas
+ Activar acciones personalizadas en menú desplegable
+ "Las acciones personalizadas están activadas en el menú desplegable.
+
+Limitaciones:
+• No funciona si la versión falsificada de la aplicación es 18.49.37 o anterior.
+• No funciona con transmisiones en directo."
+ Las acciones personalizadas están desactivadas en el menú desplegable.
+ Activar acciones personalizadas en barra de herramientas
+ "Las acciones personalizadas están activadas en la barra de herramientas.
+
+Mantén pulsado el botón Más para mostrar el cuadro de diálogo Acciones personalizadas."
+ Las acciones personalizadas están desactivadas en la barra de herramientas.
+ Acciones personalizadas
+ Copiar URL del vídeo
+ Mostrar menú de copiar URL del vídeo
+ El menú de copiar URL del vídeo está visible.
+ El menú de copiar URL del vídeo está oculto.
+ Copiar URL con marca de tiempo
+ Mostrar menú de copiar URL con marca de tiempo
+ El menú de copiar URL con marca de tiempo está visible.
+ El menú de copiar URL con marca de tiempo está oculto.
+ Descargador externo
+ Mostrar menú de descargador externo
+ El menú del descargador externo está visible.
+ El menú del descargador externo está oculto.
+ Abrir vídeo
+ Mostrar menú de abrir vídeo
+ El menú de abrir vídeo está visible.
+ El menú de abrir vídeo está oculto.
+ Estado de repetición
+ Mostrar menú de estado de repetición
+ El menú del estado de repetición está visible.
+ El menú del estado de repetición está oculto.
+ Acerca de las acciones personalizadas
+ "Esta función es aún experimental, por lo que no hay garantía de que funcione perfectamente.
+
+La mayoría de los errores no pueden solucionarse debido a las limitaciones del lado del cliente, así que utilízala solo con fines de prueba."
Activar marcas de tiempo
"La marca de tiempo está activada.
@@ -1280,6 +1333,10 @@ Problema conocido: Al tratarse de una función en fase de desarrollo por parte d
Reemplazar nombre de usuario del canal
Se utiliza el nombre del canal.
Se utiliza el nombre de usuario del canal.
+ Restaurar antiguo diseño del reproductor
+ "Se utiliza el antiguo diseño del reproductor.
+No hay márgenes en la parte superior e inferior del reproductor."
+ No se utiliza el antiguo diseño del reproductor.
Controles deslizantes
Activar gesto de brillo automático
@@ -1323,6 +1380,12 @@ Problema conocido: Al tratarse de una función en fase de desarrollo por parte d
Desactivar brillo HDR automático
El brillo HDR automático está desactivado.
El brillo HDR automático está activado.
+ Desactivar gestos del panel de visualización
+ El cambio a pantalla completa deslizando el dedo por la zona inferior del reproductor está desactivado.
+ El cambio a pantalla completa deslizando el dedo por la zona inferior del reproductor está activado.
+ Desactivar deslizar para cambiar el vídeo
+ Deslizar hacia arriba / abajo no reproducirá el vídeo siguiente / anterior.
+ Deslizar hacia arriba / abajo reproducirá el vídeo siguiente / anterior.
Automático
Vídeo
@@ -1706,13 +1769,20 @@ Pulsa el botón de continuar y desactiva las optimizaciones de la batería."Desactivar este ajuste puede causar problemas de reproducción de vídeo.
Cliente predeterminado
iOS
+ Música iOS
+ iOS TV
+ Creador de Android
Android TV
Android VR
Efectos secundarios de falsificación
- "• Falta el menú \"Pista de audio\".
-• \"Regular volumen\" no está disponible."
- "• Falta el menú \"Pista de audio\".
-• \"Regular volumen\" no está disponible."
+ • Aún no encontrado.
+ • Las películas o vídeos de pago no pueden reproducirse.
+ "• Falta el menú de pista de audio.
+• El volumen estable no está disponible.
+• Desactivar pistas de audio automáticas forzadas no está disponible."
+ Utilizar solo clientes Android
+ Los clientes Android se utilizan para obtener datos de streaming.
+ Los clientes Android e iOS se utilizan para obtener datos de streaming.
Mostrar en estadísticas para nerds
El cliente utilizado para obtener datos de transmisión se muestra en estadísticas para nerds.
El cliente utilizado para obtener datos de transmisión no se muestra en estadísticas para nerds.
diff --git a/patches/src/main/resources/youtube/translations/fr-rFR/strings.xml b/patches/src/main/resources/youtube/translations/fr-rFR/strings.xml
index d77d0fe6b..c925ff19f 100644
--- a/patches/src/main/resources/youtube/translations/fr-rFR/strings.xml
+++ b/patches/src/main/resources/youtube/translations/fr-rFR/strings.xml
@@ -1339,6 +1339,10 @@ Limitations :
Remplacer l\'identifiant de la chaîne
Le nom de la chaîne est utilisé.
L\'identifiant de la chaîne est utilisé.
+ Restaurer l\'ancienne interface du lecteur
+ "L'ancienne mise en page du lecteur est utilisée.
+Pas de marges en haut et en bas du lecteur."
+ L\'ancienne mise en page du lecteur n\'est pas utilisée.
Contrôles par gestes
Activer les gestes de luminosité auto
@@ -1385,6 +1389,7 @@ Limitations :
Désactiver les gestes inférieurs du lecteur
Le passage plein écran en faisant glisser vers le bas sous le lecteur vidéo est désactivé.
Le passage plein écran en faisant glisser vers le bas sous le lecteur vidéo est activé.
+ Désactiver les gestes pour changer de vidéo
Glisser vers le haut / bas ne lira pas la vidéo suivante / précédente.
Glisser vers le haut / bas pour lire la vidéo suivante / précédente.
Auto
@@ -1774,20 +1779,13 @@ Cliquez sur le bouton Continuer et autorisez les modifications d'optimisations."
Client par défaut
iOS
Musique iOS
+ TV iOS
Android TV
Android VR
Effets inconnus de la falsification
- • Pas encore trouvé.
- • Les vidéos peuvent se terminer avec 1 seconde d\'avance.
- "• Le menu 'Piste Audio' est manquant.
-• Le volume stable n'es pas disponible."
- "• Le menu 'Piste Audio' est manquant.
-• Le volume stable n'es pas disponible."
- Synchroniser la durée de la vidéo avant la lecture
- "La durée de la vidéo est synchronisée avant la lecture.
-La durée a une valeur exacte."
- "La durée de la vidéo n'est pas synchronisée avant la lecture.
-La durée peut avoir une valeur arrondie."
+ • Les vidéos privées conçues pour enfants peuvent ne pas être lues.
+ • Les vidéos privées conçues pour enfants peuvent ne pas être lues.
+• Les films ou vidéos payantes peuvent ne pas être lues.
Afficher dans \'Statistiques pour les nerds\'
Le client utilisé pour récupérer les données de lecture en direct est affiché dans \'Statistiques pour les nerds\'.
Le client utilisé pour récupérer les données de lecture en direct est masqué dans \'Statistiques pour les nerds\'.
diff --git a/patches/src/main/resources/youtube/translations/hu-rHU/strings.xml b/patches/src/main/resources/youtube/translations/hu-rHU/strings.xml
index e73f028b1..85713bdf4 100644
--- a/patches/src/main/resources/youtube/translations/hu-rHU/strings.xml
+++ b/patches/src/main/resources/youtube/translations/hu-rHU/strings.xml
@@ -1755,8 +1755,6 @@ Kattintson az API-kulcs kiadás folyamatának megtekintéséhez."
Android VR
Hamisítás mellékhatásai
• Még nem található.
- "• Az audiosáv menü hiányzik."
- "• Az audiosáv menü hiányzik."
Megjelenítés a statisztikában kockáknak
Az adatfolyam lekérésére használt kliens a statisztikában kockáknak látható.
Az adatfolyam lekérésére használt kliens a statisztikában kockáknak nem látható.
diff --git a/patches/src/main/resources/youtube/translations/it-rIT/strings.xml b/patches/src/main/resources/youtube/translations/it-rIT/strings.xml
index 14fd275c4..8c8c72705 100644
--- a/patches/src/main/resources/youtube/translations/it-rIT/strings.xml
+++ b/patches/src/main/resources/youtube/translations/it-rIT/strings.xml
@@ -312,9 +312,9 @@ Se l'interfaccia della schermata del riproduttore cambia a causa di modifiche la
Nota: il pulsante Indietro della barra degli strumenti potrebbe non funzionare."
La pagina iniziale cambia solo una volta.
- Disattiva le tracce audio automatiche forzate
- Le tracce audio automatiche forzate sono disattivate.
- Le tracce audio automatiche forzate sono attivate.
+ Disattiva la traccia audio automatica forzata
+ La traccia audio automatica forzata è disattivata.
+ La traccia audio automatica forzata è attivata.
Disattiva i sottotitoli automatici forzati
I sottotitoli automatici forzati sono disattivati.
I sottotitoli automatici forzati sono attivati.
@@ -1306,8 +1306,8 @@ Tocca e tieni premuto il pulsante Altro per visualizzare la finestra delle azion
Il menù Apri il Video è nascosto.
Stato di ripetizione
Mostra il menù Stato di Ripetizione
- Il menù Stato di Ripetizione è nascosto.
- Il menù Stato di Ripetizione è visibile.
+ Il menù Stato di Ripetizione è visibile.
+ Il menù Stato di Ripetizione è nascosto.
Informazioni sulle azioni personalizzate
"Questa impostazione è ancora sperimentale, quindi non c'è garanzia che funzionerà perfettamente.
@@ -1775,20 +1775,19 @@ Tocca il pulsante Continua e consenti le modifiche di ottimizzazione."
Client predefinito
iOS
iOS Music
+ iOS TV
+ Android Creator
Android TV
Android VR
Effetti collaterali del camuffamento
• Nessuno ancora trovato.
- • I video potrebbero terminare 1 secondo prima.
- "• Il menù Traccia Audio è mancante.
-• Il menù Volume Stabile è mancante."
- "• Il menù Traccia Audio è mancante.
-• Il menù Volume Stabile è mancante."
- Sincronizza la lunghezza del video prima della riproduzione
- "La lunghezza del video è sincronizzata prima della riproduzione.
-La lunghezza del video è il valore esatto."
- "La lunghezza del video non è sincronizzata prima della riproduzione.
-La lunghezza del video potrebbe essere un valore arrotondato."
+ • I film o i video a pagamento potrebbero non essere riprodotti.
+ "• Il menù Traccia Audio è mancante.
+• Il menù Volume Stabile è mancante.
+• La disattivazione della traccia audio automatica forzata non funziona."
+ Usa solo il client Android
+ Il client Android è usato per recuperare i dati in streaming.
+ I client Android e iOS sono usati per recuperare i dati in streaming.
Mostra nelle statistiche per nerd
Il client usato per recuperare i dati in streaming è visibile nelle statistiche per nerd.
Il client usato per recuperare i dati in streaming è nascosto nelle statistiche per nerd.
diff --git a/patches/src/main/resources/youtube/translations/ja-rJP/strings.xml b/patches/src/main/resources/youtube/translations/ja-rJP/strings.xml
index 8593fc7e8..0413fa87d 100644
--- a/patches/src/main/resources/youtube/translations/ja-rJP/strings.xml
+++ b/patches/src/main/resources/youtube/translations/ja-rJP/strings.xml
@@ -1777,20 +1777,13 @@ GmsCore の電池の最適化を無効にしても、バッテリーの使用に
偽装するクライアントの種類
iOS
iOS Music
+ iOS TV
Android TV
Android VR
ストリーミングデータを偽装することによる副作用
・まだ見つかっていません。
- ・動画が 1 秒早く終了する可能性があります。
- "・「音声トラック」メニューは表示されません。
-・「一定音量」は使用できません。"
- "・「音声トラック」メニューは表示されません。
-・「一定音量」は使用できません。"
- 再生前に動画の長さを同期
- "再生前に動画の長さを同期します。
-動画の長さは正確な値です。"
- "再生前に動画の長さを同期します。
-動画の長さは正確な値です。"
+ ・子供向け動画は再生できない可能性があります。
+・映画や有料動画は再生できない可能性があります。
統計情報に偽装したクライアントを表示
統計情報に偽装したストリーミングデータを表示します。
統計情報に偽装したストリーミングデータを表示します。
diff --git a/patches/src/main/resources/youtube/translations/ko-rKR/strings.xml b/patches/src/main/resources/youtube/translations/ko-rKR/strings.xml
index c545ddef6..8245552dc 100644
--- a/patches/src/main/resources/youtube/translations/ko-rKR/strings.xml
+++ b/patches/src/main/resources/youtube/translations/ko-rKR/strings.xml
@@ -315,11 +315,11 @@ DeArrow에 대해 자세히 알아보려면 여기를 누르세요."
알려진 문제점: 툴바에서 '뒤로 가기' 버튼이 작동되지 않을 수 있습니다."
앱 시작 페이지가 한 번만 변경됩니다.
자동 오디오 트랙 비활성화하기
- 오디오 트랙 사용이 강제된 동영상에서 오디오 트랙을 비활성화합니다.
- 오디오 트랙 사용이 강제된 동영상에서 오디오 트랙을 활성화합니다.
+ 자동 오디오 트랙을 비활성화합니다.
+ 자동 오디오 트랙을 활성화합니다.
자동 자막 비활성화하기
- 자막 사용이 강제된 동영상에서 자막을 비활성화합니다.
- 자막 사용이 강제된 동영상에서 자막을 활성화합니다.
+ 자동 자막을 비활성화합니다.
+ 자동 자막을 활성화합니다.
스플래시 애니메이션 비활성화하기
앱을 시작할 때, 스플래시 애니메이션을 비활성화합니다.
앱을 시작할 때, 스플래시 애니메이션을 활성화합니다.
@@ -410,7 +410,7 @@ DeArrow에 대해 자세히 알아보려면 여기를 누르세요."
미니 플레이어
앱 내에서 최소화된 플레이어의 스타일을 변경할 수 있습니다.
미니 플레이어 유형
- 비활성화됨
+ 사용하지 않음
기기 기본값 사용
최소화
태블릿
@@ -1780,18 +1780,19 @@ GmsCore를 배터리 최적화 목록에서 제외하더라도, 배터리 사용
기본 클라이언트
iOS
iOS Music
+ iOS TV
+ Android Creator
Android TV
Android VR
알려진 문제점
• 아직 발견되지 않았습니다.
- • 동영상이 1초 일찍 종료될 수 있습니다.
- "• 오디오 트랙 메뉴가 표시되지 않습니다.\n• 안정적인 볼륨 메뉴가 비활성화된 채로 잠겨있습니다."
- "• 오디오 트랙 메뉴가 표시되지 않습니다.\n• 안정적인 볼륨 메뉴가 비활성화된 채로 잠겨있습니다."
- 재생 전 동영상 길이 동기화하기
- "동영상 길이를 재생 전에 동기화합니다.
-동영상 길이는 정확한 값입니다."
- "동영상 길이를 재생 전에 동기화하지 않습니다.
-동영상 길이는 반올림된 값일 수 있습니다."
+ • 영화 또는 회원 전용 동영상과 같은 유료 동영상이 재생되지 않을 수 있습니다.
+ "• 오디오 트랙 메뉴가 표시되지 않습니다.
+• 안정적인 볼륨을 사용할 수 없습니다.
+• 자동 오디오 트랙을 비활성화할 수 없습니다."
+ Android 클라이언트만 사용하기
+ Android 클라이언트를 스트리밍 데이터를 가져오는 데 사용합니다.
+ Android 와 iOS 클라이언트를 스트리밍 데이터를 가져오는 데 사용합니다.
전문 통계에서 표시하기
\'스트리밍 데이터를 가져오는 데 사용되는 클라이언트\'가 전문 통계에서 표시됩니다.
\'스트리밍 데이터를 가져오는 데 사용되는 클라이언트\'가 전문 통계에서 숨겨집니다.
diff --git a/patches/src/main/resources/youtube/translations/pl-rPL/strings.xml b/patches/src/main/resources/youtube/translations/pl-rPL/strings.xml
index eb9046e8e..18f67e6a6 100644
--- a/patches/src/main/resources/youtube/translations/pl-rPL/strings.xml
+++ b/patches/src/main/resources/youtube/translations/pl-rPL/strings.xml
@@ -311,10 +311,10 @@ Jeśli układ ekranu odtwarzacza zmieni się w skutek zmian po stronie serwera,
Ograniczenie: Przycisk wstecz na pasku narzędzi może nie działać."
Strona główna zmienia się tylko raz
- Wymuszone ścieżki dźwiękowe
+ Automatyczne wymuszanie ścieżki dźwiękowej
Wyłączone
Włączone
- Wymuszone napisy
+ Automatyczne wymuszanie napisów
Wyłączone
Włączone
Animacja uruchamiania aplikacji
@@ -1333,6 +1333,9 @@ Ograniczenia:
Wyświetlanie tytułu kanału
Po nazwie
Po nicku
+ Przywrócić stary układ odtwarzacza
+ "Włączone"
+ Wyłączone
Sterowanie przesuwaniem
Automatyczna jasność przesuwaniem
@@ -1379,6 +1382,7 @@ Ograniczenia:
Pełny ekran gestem przesunięcia
Wejście w tryb pełnoekranowy przesunięciem w dół poniżej odtwarzacza filmu jest wyłączone
Wejście w tryb pełnoekranowy przesunięciem w dół poniżej odtwarzacza filmu jest włączone
+ Gest zmiany filmu przesunięciem w górę lub dół
Wyłączony
Włączony
Automatyczna
@@ -1768,18 +1772,20 @@ Stuknij przycisk kontynuacji i zezwól na zmiany w optymalizacji."
Domyślny klient
iOS
iOS Music
+ iOS TV
+ Kreator Androida
Android TV
Android VR
Efekty uboczne oszukiwania
• Jeszcze nie znaleziono
- • Filmy mogą kończyć się o sekundę wcześniej
- "• Brakuje menu od ścieżki dźwiękowej
-• Stabilna głośność jest niedostępna"
- "• Brakuje menu od ścieżki dźwiękowej
-• Stabilna głośność jest niedostępna"
- Synchronizuj długość filmu przez odtworzeniem
- "Włączone"
- "Wyłączone"
+ • Filmy kinowe lub płatne filmy mogą się nie odtwarzać
+ "• Brakuje menu od ścieżki dźwiękowej
+• Stabilna głośność nie jest dostępna
+• Wyłączenie automatycznego wymuszania ścieżki dźwiękowej nie jest dostępne"
+ Używaj wyłącznie klientów Androida
+ Tak
+ Nie
+ Wyłączenie tego ustawienia może spowodować problemy z odtwarzaniem filmów.
Informacja w statystykach dla nerdów
Widoczna
Ukryta
diff --git a/patches/src/main/resources/youtube/translations/pt-rBR/strings.xml b/patches/src/main/resources/youtube/translations/pt-rBR/strings.xml
index b627083a7..b7c30d974 100644
--- a/patches/src/main/resources/youtube/translations/pt-rBR/strings.xml
+++ b/patches/src/main/resources/youtube/translations/pt-rBR/strings.xml
@@ -1775,14 +1775,6 @@ Toque no botão continuar e desative as otimizações da bateria."
Android VR
Efeitos colaterais da falsificação
• Ainda não encontrado.
- • Os vídeos podem acabar 1 segundo antes.
- "• O menu de faixa de áudio está faltando."
- "• O menu de faixa de áudio está faltando."
- Sincronizar duração do vídeo antes da reprodução
- "A duração do vídeo é sincronizada antes da reprodução.
-A duração do vídeo é o valor exato."
- "A duração do vídeo não é sincronizada antes da reprodução.
-A duração do vídeo pode ser um valor arredondado."
Exibir em Estatísticas para nerds
O cliente usado para buscar dados de streaming é mostrado em Estatísticas para nerds.
O cliente usado para buscar dados de streaming está oculto em Estatísticas para nerds.
diff --git a/patches/src/main/resources/youtube/translations/ru-rRU/strings.xml b/patches/src/main/resources/youtube/translations/ru-rRU/strings.xml
index 078588434..5b11bf3ed 100644
--- a/patches/src/main/resources/youtube/translations/ru-rRU/strings.xml
+++ b/patches/src/main/resources/youtube/translations/ru-rRU/strings.xml
@@ -1352,6 +1352,10 @@ Shorts
Заменить псевдоним канала
Используется имя канала.
Используется псевдоним канала.
+ Старый интерфейс проигрывателя
+ "Старый интерфейс проигрывателя включен.
+Без отступов сверху и снизу плеера."
+ Старый интерфейс проигрывателя отключен.
Управление жестами
Управление автояркостью жестом
@@ -1400,6 +1404,7 @@ Shorts
Жесты панели просмотра
Полноэкранный режим жестом вниз отключен.
Полноэкранный режим жестом вниз включен.
+ Переключение видео жестом
Переключение видео жестами отключены.
Переключение видео жестами включены.
Авто
@@ -1790,19 +1795,19 @@ Shorts
Клиент по умолчанию
iOS
Музыка iOS
+ iOS TV
+ Редактор Android
Android TV
Android VR
Эффекты от подмены
• Еще не найдено.
- • Видео может закончиться 1 сек ранее. При подмене как iOS.
- "• Меню \"Звуковая дорожка\" не доступно."
- "• Меню \"Звуковая дорожка\" отсутствует.
-• Меню \"Постоянный уровень громкости\" недоступно."
- Синхронизация видео перед воспроизведением
- "Синхронизация видео перед воспроизведением включена.
-Точная продолжительность видео."
- "Синхронизация видео перед воспроизведением отключена.
-Округленная продолжительность видео."
+ • Платные видео и фильмы могут не проигрываться.
+ "• Меню аудио дорожки отсутствует.
+• Стабильная громкость недоступна.
+• Отключить принудительные автозвуковые дорожки недоступна."
+ Использовать только клиенты Android
+ Android клиенты используются для получения потоковых данных.
+ Android и iOS клиенты используются для получения потоковых данных.
Статистике для сисадминов
Клиент потоковых данных отображается в Статистике для сисадминов.
Клиент потоковых данных скрыт в Статистике для сисадминов.
diff --git a/patches/src/main/resources/youtube/translations/uk-rUA/strings.xml b/patches/src/main/resources/youtube/translations/uk-rUA/strings.xml
index 9d60fac2f..ce1410442 100644
--- a/patches/src/main/resources/youtube/translations/uk-rUA/strings.xml
+++ b/patches/src/main/resources/youtube/translations/uk-rUA/strings.xml
@@ -1338,6 +1338,10 @@
Замінити ідентифікатор каналу
Використовується назву каналу.
Використовується ідентифікатор каналу.
+ Відновити старий макет плеєра
+ "Старий макет плеєра використовується.
+Немає полів зверху та знизу плеєра."
+ Старий макет плеєра не використовується.
Керування жестами
Увімкнути автояскравість жестом
@@ -1384,6 +1388,7 @@
Вимкнути жести панелі перегляду
Перехід на повний екран при проведенні вниз під відеоплеєром вимкнено.
Перехід на повний екран при проведенні вниз під відеоплеєром увімкнено.
+ Вимкнути зміну відео проведенням
Проведення вгору / вниз не відтворюватиме наступне / попереднє відео.
Проведення вгору / вниз відтворюватиме наступне / попереднє відео.
Авто
@@ -1768,20 +1773,19 @@
Основний клієнт
iOS
Музика iOS
+ iOS TV
+ Розробник Android
Android TV
Android VR
Побічні ефекти імітування
• Ще не знайдено.
- • Відео можуть закінчуватися на 1 секунду раніше.
- "• Меню звукової доріжки відсутнє.
-• Стабілізація гучності недоступна."
- "• Меню звукової доріжки відсутнє.
-• Стабілізація гучності недоступна."
- Синхронізувати тривалість відео перед відтворенням
- "Довжину відео синхронізується перед відтворенням.
-Тривалість відео є точним значенням."
- "Довжину відео не синхронізується перед відтворенням.
-Тривалість відео може бути округленим значенням."
+ • Фільми чи платні відео можуть не відтворюватися.
+ "• Меню звукової доріжки відсутнє.
+• Стабілізація гучності недоступна.
+• Вимикання примусових звукових доріжок недоступне."
+ Використовувати лише клієнти Android
+ Використовується клієнти Android для отримання потокових даних.
+ Використовується клієнти Android та iOS для отримання потокових даних.
Показувати в Статистика для сисадмінів
Клієнт, що використовується для отримання даних трансляції показується у Статистика для сисадмінів.
Клієнт, що використовується для отримання даних трансляції приховано у Статистика для сисадмінів.
diff --git a/patches/src/main/resources/youtube/translations/vi-rVN/strings.xml b/patches/src/main/resources/youtube/translations/vi-rVN/strings.xml
index 7740c316d..fd0eb4912 100644
--- a/patches/src/main/resources/youtube/translations/vi-rVN/strings.xml
+++ b/patches/src/main/resources/youtube/translations/vi-rVN/strings.xml
@@ -982,7 +982,7 @@ Nhấn và giữ để sao chép URL video kèm theo dấu thời gian."
Nút Tắt tiếng
Nhấn để tắt tiếng của video hiện tại. Nhấn lần nữa để bật trở lại.
- Nút trình tải xuống bên ngoài
+ Nút Trình tải xuống bên ngoài
Nhấn để khởi chạy trình tải xuống bên ngoài.
Nút Tốc độ phát
"Nhấn để mở hộp thoại Tốc độ phát.
@@ -1521,7 +1521,7 @@ Nhấp vào đây để xem các bước phát hành khóa API."
1. <a href=%1$s>Tạo dự án mới</a>.<br>2. Ấn vào <b>CREATE</b>.<br>3. Đi tới <a href=%2$s>YouTube Data API v3</a>.<br>4. Ấn vào <b>ENABLE</b>.<br>5. Ấn vào <b>CREATE CREDENTIALS</b>.<br>6. Chọn <b>Public data</b>.<br>7. Ấn vào <b>NEXT</b>.<br>8. Sao chép mã khoá API.<br><br>※ Không nên chia sẻ mã khoá API với người khác, vì vậy chúng cũng không có mặt trong mục Nhập/Xuất cài đặt.
SponsorBlock
- Kích hoạt SponsorBlock
+ SponsorBlock
SponsorBlock là một tiện ích được đóng góp bởi cộng đồng giúp bỏ qua các phần gây khó chịu trong video trên YouTube.
Giao diện
@@ -1778,20 +1778,19 @@ Nhấn vào Tiếp tục và cho phép thay đổi lựa chọn tối ưu hoá p
Ứng dụng khách mặc định
iOS
Music iOS
+ TV iOS
+ Android Creator
Android TV
Android VR
Hạn chế
• Chưa tìm thấy.
- • Video có thể kết thúc sớm hơn 1 giây.
- "• Mục Bản âm thanh bị thiếu.
-• Âm lượng ổn định không khả dụng."
- "• Mục Bản âm thanh bị thiếu.
-• Âm lượng ổn định không khả dụng."
- Đồng bộ thời lượng video trước khi phát
- "Thời lượng video được đồng bộ trước khi phát.
-Lúc này thời lượng video là giá trị chính xác."
- "Thời lượng video không được đồng bộ trước khi phát.
-Lúc này thời lượng video có thể đã được làm tròn."
+ • Phim hoặc video trả phí có thể không phát được.
+ "• Mục Bản âm thanh bị thiếu.
+• Âm lượng ổn định không khả dụng.
+• Tắt buộc bản âm thanh tự động không khả dụng."
+ Chỉ sử dụng ứng dụng khách Android
+ Ứng dụng khách Android được sử dụng để nạp luồng dữ liệu trực tuyến.
+ Ứng dụng khách Android và iOS được sử dụng để nạp luồng dữ liệu trực tuyến.
Hiển thị trong Thống kê chi tiết
Ứng dụng khách sử dụng để nạp luồng dữ liệu trực tuyến được hiển thị trong Thống kê chi tiết.
Ứng dụng khách sử dụng để nạp luồng dữ liệu trực tuyến đã ẩn trong Thống kê chi tiết.
diff --git a/patches/src/main/resources/youtube/translations/zh-rCN/strings.xml b/patches/src/main/resources/youtube/translations/zh-rCN/strings.xml
index 261f2cb9a..81d1e8256 100644
--- a/patches/src/main/resources/youtube/translations/zh-rCN/strings.xml
+++ b/patches/src/main/resources/youtube/translations/zh-rCN/strings.xml
@@ -1651,8 +1651,6 @@
Android TV
Android VR
伪装副作用
- "• 音轨菜单缺失"
- "• 音轨菜单缺失"
显示统计信息
用于获取流媒体数据的客户端已在统计信息中显示
用于获取流媒体数据的客户端已在统计信息中隐藏
diff --git a/patches/src/main/resources/youtube/translations/zh-rTW/strings.xml b/patches/src/main/resources/youtube/translations/zh-rTW/strings.xml
index 433688650..f733605a0 100644
--- a/patches/src/main/resources/youtube/translations/zh-rTW/strings.xml
+++ b/patches/src/main/resources/youtube/translations/zh-rTW/strings.xml
@@ -1329,6 +1329,10 @@
更換頻道代號
頻道名稱已使用。
頻道代號已使用。
+ 恢復舊版播放器介面
+ "正在使用舊版播放器介面。
+播放器的上下邊緣無間距。"
+ 未使用舊版播放器介面。
滑動控制
啟用自動亮度手勢
@@ -1372,6 +1376,12 @@
停用 HDR 影片自動亮度
HDR 影片自動亮度已停用
HDR 影片自動亮度已啟用
+ 停用觀看面板手勢
+ 透過滑動播放器底部區域切換到全螢幕功能已停用。
+ 透過滑動播放器底部區域切換到全螢幕功能已啟用。
+ 停用滑動切換影片功能
+ 向上/向下滑動將不會播放下一個/上一個影片。
+ 向上/向下滑動將播放下一個/上一個影片。
自動
影片
@@ -1763,12 +1773,16 @@
預設客戶端
iOS
iOS 音樂
+ iOS TV
+ Android 創作者
Android 電視
Android VR
偽裝副作用
• 尚未找到。
- "• 音軌選單遺失。"
- "• 音軌選單遺失。"
+ • 電影或付費影片可能無法播放。
+ "• 音軌選單缺失。
+• 穩定音量不可用。
+• 停用強制自動音軌不可用。"
顯示統計資料
用於取得串流資料的用戶端顯示在統計資料中。
用於獲取串流資料的用戶端隱藏在統計資料中。