diff --git a/.github/workflows/pull_strings.yml b/.github/workflows/pull_strings.yml index f2da51ec74..8fef9fe309 100644 --- a/.github/workflows/pull_strings.yml +++ b/.github/workflows/pull_strings.yml @@ -32,7 +32,6 @@ jobs: - name: Process strings run: | - chmod -R 777 patches/src/main/resources ./gradlew processStringsFromCrowdin env: ORG_GRADLE_PROJECT_githubPackagesUsername: ${{ github.actor }} diff --git a/CHANGELOG.md b/CHANGELOG.md index bfebce7e69..f558acff38 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,61 +1,3 @@ -# [6.1.0](https://github.com/ReVanced/revanced-patches/compare/v6.0.1...v6.1.0) (2026-03-18) - - -### Bug Fixes - -* **Export internal data documents provider:** Correct S_IFLNK constant and symlink detection mask ([#6819](https://github.com/ReVanced/revanced-patches/issues/6819)) ([252617b](https://github.com/ReVanced/revanced-patches/commit/252617b8dd3f24e1ff9a04ba1d91b43dc29bd757)) -* **YouTube - Custom branding:** Fix double icons and change default branding to ReVanced ([#6806](https://github.com/ReVanced/revanced-patches/issues/6806)) ([e51c529](https://github.com/ReVanced/revanced-patches/commit/e51c5292c171325e7cfa0f5ee85474d9b3961a34)) - - -### Features - -* Add `Spoof root of trust` and `Spoof keystore security level` patch ([#6751](https://github.com/ReVanced/revanced-patches/issues/6751)) ([4bc8c7c](https://github.com/ReVanced/revanced-patches/commit/4bc8c7c0f60a095533f07dc281f0320f8eb22f3c)) -* **Announcements:** Support ReVanced API v5 announcements ([a05386e](https://github.com/ReVanced/revanced-patches/commit/a05386e8bc24c085b5c74f3674c402c5dd5ad468)) -* Change contact email in patches about ([df1c3a4](https://github.com/ReVanced/revanced-patches/commit/df1c3a4a70fd2595d77b539299f1f7301bc60d24)) -* **Instagram:** Add `Enable location sticker redesign` patch ([#6808](https://github.com/ReVanced/revanced-patches/issues/6808)) ([4b699da](https://github.com/ReVanced/revanced-patches/commit/4b699da220e5d1527c390792b6228e2d9cffedb7)) -* **Spoof video streams:** Add Android Reel client to fix playback issues ([#6830](https://github.com/ReVanced/revanced-patches/issues/6830)) ([4b6c3e3](https://github.com/ReVanced/revanced-patches/commit/4b6c3e312328fbf6a1c7065e27d8ff04573e58be)) - -# [6.1.0-dev.4](https://github.com/ReVanced/revanced-patches/compare/v6.1.0-dev.3...v6.1.0-dev.4) (2026-03-18) - - -### Bug Fixes - -* **YouTube - Custom branding:** Fix double icons and change default branding to ReVanced ([#6806](https://github.com/ReVanced/revanced-patches/issues/6806)) ([e51c529](https://github.com/ReVanced/revanced-patches/commit/e51c5292c171325e7cfa0f5ee85474d9b3961a34)) - - -### Features - -* Add `Spoof root of trust` and `Spoof keystore security level` patch ([#6751](https://github.com/ReVanced/revanced-patches/issues/6751)) ([4bc8c7c](https://github.com/ReVanced/revanced-patches/commit/4bc8c7c0f60a095533f07dc281f0320f8eb22f3c)) -* **Instagram:** Add `Enable location sticker redesign` patch ([#6808](https://github.com/ReVanced/revanced-patches/issues/6808)) ([4b699da](https://github.com/ReVanced/revanced-patches/commit/4b699da220e5d1527c390792b6228e2d9cffedb7)) - -# [6.1.0-dev.3](https://github.com/ReVanced/revanced-patches/compare/v6.1.0-dev.2...v6.1.0-dev.3) (2026-03-18) - - -### Features - -* **Spoof video streams:** Add Android Reel client to fix playback issues ([#6830](https://github.com/ReVanced/revanced-patches/issues/6830)) ([4b6c3e3](https://github.com/ReVanced/revanced-patches/commit/4b6c3e312328fbf6a1c7065e27d8ff04573e58be)) - -# [6.1.0-dev.2](https://github.com/ReVanced/revanced-patches/compare/v6.1.0-dev.1...v6.1.0-dev.2) (2026-03-17) - - -### Features - -* **Announcements:** Support ReVanced API v5 announcements ([a05386e](https://github.com/ReVanced/revanced-patches/commit/a05386e8bc24c085b5c74f3674c402c5dd5ad468)) - -# [6.1.0-dev.1](https://github.com/ReVanced/revanced-patches/compare/v6.0.2-dev.1...v6.1.0-dev.1) (2026-03-16) - - -### Features - -* Change contact email in patches about ([df1c3a4](https://github.com/ReVanced/revanced-patches/commit/df1c3a4a70fd2595d77b539299f1f7301bc60d24)) - -## [6.0.2-dev.1](https://github.com/ReVanced/revanced-patches/compare/v6.0.1...v6.0.2-dev.1) (2026-03-16) - - -### Bug Fixes - -* **Export internal data documents provider:** Correct S_IFLNK constant and symlink detection mask ([#6819](https://github.com/ReVanced/revanced-patches/issues/6819)) ([252617b](https://github.com/ReVanced/revanced-patches/commit/252617b8dd3f24e1ff9a04ba1d91b43dc29bd757)) - ## [6.0.1](https://github.com/ReVanced/revanced-patches/compare/v6.0.0...v6.0.1) (2026-03-15) diff --git a/extensions/all/misc/directory/documentsprovider/export-internal-data-documents-provider/src/main/java/app/revanced/extension/all/misc/directory/documentsprovider/InternalDataDocumentsProvider.java b/extensions/all/misc/directory/documentsprovider/export-internal-data-documents-provider/src/main/java/app/revanced/extension/all/misc/directory/documentsprovider/InternalDataDocumentsProvider.java index ad9d48f6ec..f4e3a8e031 100644 --- a/extensions/all/misc/directory/documentsprovider/export-internal-data-documents-provider/src/main/java/app/revanced/extension/all/misc/directory/documentsprovider/InternalDataDocumentsProvider.java +++ b/extensions/all/misc/directory/documentsprovider/export-internal-data-documents-provider/src/main/java/app/revanced/extension/all/misc/directory/documentsprovider/InternalDataDocumentsProvider.java @@ -31,10 +31,7 @@ public class InternalDataDocumentsProvider extends DocumentsProvider { private static final String[] directoryColumns = {"document_id", "mime_type", "_display_name", "last_modified", "flags", "_size", "full_path", "lstat_info"}; - @SuppressWarnings("OctalInteger") - private static final int S_IFMT = 0170000; - @SuppressWarnings("OctalInteger") - private static final int S_IFLNK = 0120000; + private static final int S_IFLNK = 0x8000; private String packageName; private File dataDirectory; @@ -50,7 +47,7 @@ public class InternalDataDocumentsProvider extends DocumentsProvider { if (root.isDirectory()) { try { // Only delete recursively if the directory is not a symlink - if ((Os.lstat(root.getPath()).st_mode & S_IFMT) != S_IFLNK) { + if ((Os.lstat(root.getPath()).st_mode & S_IFLNK) != S_IFLNK) { File[] files = root.listFiles(); if (files != null) { for (File file : files) { @@ -327,7 +324,7 @@ public class InternalDataDocumentsProvider extends DocumentsProvider { sb.append(";"); sb.append(lstat.st_gid); // Append symlink target if it is a symlink - if ((lstat.st_mode & S_IFMT) == S_IFLNK) { + if ((lstat.st_mode & S_IFLNK) == S_IFLNK) { sb.append(";"); sb.append(Os.readlink(path)); } diff --git a/extensions/all/misc/disable-play-integrity/src/main/java/app/revanced/extension/play/DisablePlayIntegrityPatch.java b/extensions/all/misc/disable-play-integrity/src/main/java/app/revanced/extension/playintegrity/DisablePlayIntegrityPatch.java similarity index 92% rename from extensions/all/misc/disable-play-integrity/src/main/java/app/revanced/extension/play/DisablePlayIntegrityPatch.java rename to extensions/all/misc/disable-play-integrity/src/main/java/app/revanced/extension/playintegrity/DisablePlayIntegrityPatch.java index 4dd09f693f..a27e56be95 100644 --- a/extensions/all/misc/disable-play-integrity/src/main/java/app/revanced/extension/play/DisablePlayIntegrityPatch.java +++ b/extensions/all/misc/disable-play-integrity/src/main/java/app/revanced/extension/playintegrity/DisablePlayIntegrityPatch.java @@ -1,4 +1,4 @@ -package app.revanced.extension.play; +package app.revanced.extension.playintegrity; import android.content.Context; import android.content.Intent; diff --git a/extensions/music/src/main/java/app/revanced/extension/music/patches/spoof/SpoofVideoStreamsPatch.java b/extensions/music/src/main/java/app/revanced/extension/music/patches/spoof/SpoofVideoStreamsPatch.java index 46c85a8edd..ade26a30fb 100644 --- a/extensions/music/src/main/java/app/revanced/extension/music/patches/spoof/SpoofVideoStreamsPatch.java +++ b/extensions/music/src/main/java/app/revanced/extension/music/patches/spoof/SpoofVideoStreamsPatch.java @@ -1,7 +1,7 @@ package app.revanced.extension.music.patches.spoof; import static app.revanced.extension.music.settings.Settings.SPOOF_VIDEO_STREAMS_CLIENT_TYPE; -import static app.revanced.extension.shared.spoof.ClientType.ANDROID_REEL; +import static app.revanced.extension.shared.spoof.ClientType.ANDROID_NO_SDK; import static app.revanced.extension.shared.spoof.ClientType.ANDROID_VR_1_43_32; import static app.revanced.extension.shared.spoof.ClientType.ANDROID_VR_1_61_48; import static app.revanced.extension.shared.spoof.ClientType.VISIONOS; @@ -18,8 +18,8 @@ public class SpoofVideoStreamsPatch { */ public static void setClientOrderToUse() { List availableClients = List.of( - ANDROID_REEL, ANDROID_VR_1_43_32, + ANDROID_NO_SDK, VISIONOS, ANDROID_VR_1_61_48 ); diff --git a/extensions/music/src/main/java/app/revanced/extension/music/settings/Settings.java b/extensions/music/src/main/java/app/revanced/extension/music/settings/Settings.java index 7decd29b8a..b2f61541a4 100644 --- a/extensions/music/src/main/java/app/revanced/extension/music/settings/Settings.java +++ b/extensions/music/src/main/java/app/revanced/extension/music/settings/Settings.java @@ -35,7 +35,7 @@ public class Settings extends YouTubeAndMusicSettings { // Miscellaneous public static final EnumSetting SPOOF_VIDEO_STREAMS_CLIENT_TYPE = new EnumSetting<>("revanced_spoof_video_streams_client_type", - ClientType.ANDROID_REEL, true, parent(SPOOF_VIDEO_STREAMS)); + ClientType.ANDROID_VR_1_43_32, true, parent(SPOOF_VIDEO_STREAMS)); public static final BooleanSetting FORCE_ORIGINAL_AUDIO = new BooleanSetting("revanced_force_original_audio", TRUE, true); } diff --git a/extensions/shared/library/build.gradle.kts b/extensions/shared/library/build.gradle.kts index 8215e513ad..100de7ae14 100644 --- a/extensions/shared/library/build.gradle.kts +++ b/extensions/shared/library/build.gradle.kts @@ -1,5 +1,5 @@ plugins { - alias(libs.plugins.android.library) + alias(libs.plugins.android.library) } android { @@ -19,6 +19,4 @@ android { dependencies { compileOnly(libs.annotation) compileOnly(libs.okhttp) - compileOnly(libs.protobuf.javalite) - implementation(project(":extensions:shared:protobuf", configuration = "shadowRuntimeElements")) } diff --git a/extensions/shared/library/src/main/java/app/revanced/extension/shared/patches/CustomBrandingPatch.java b/extensions/shared/library/src/main/java/app/revanced/extension/shared/patches/CustomBrandingPatch.java index d13513e2df..0dc411f8b0 100644 --- a/extensions/shared/library/src/main/java/app/revanced/extension/shared/patches/CustomBrandingPatch.java +++ b/extensions/shared/library/src/main/java/app/revanced/extension/shared/patches/CustomBrandingPatch.java @@ -114,7 +114,7 @@ public class CustomBrandingPatch { /** * Injection point. - *

+ * * The total number of app name aliases, including dummy aliases. */ private static int numberOfPresetAppNames() { @@ -146,13 +146,13 @@ public class CustomBrandingPatch { public static int getDefaultAppNameIndex() { return userProvidedCustomName() ? numberOfPresetAppNames() - : 2; + : 1; } public static BrandingTheme getDefaultIconStyle() { return userProvidedCustomIcon() ? BrandingTheme.CUSTOM - : BrandingTheme.ROUNDED; + : BrandingTheme.ORIGINAL; } /** diff --git a/extensions/shared/library/src/main/java/app/revanced/extension/shared/settings/BaseSettings.java b/extensions/shared/library/src/main/java/app/revanced/extension/shared/settings/BaseSettings.java index 5fc4418366..2a3aef407f 100644 --- a/extensions/shared/library/src/main/java/app/revanced/extension/shared/settings/BaseSettings.java +++ b/extensions/shared/library/src/main/java/app/revanced/extension/shared/settings/BaseSettings.java @@ -47,7 +47,7 @@ public class BaseSettings { // public static final BooleanSetting SPOOF_VIDEO_STREAMS = new BooleanSetting("revanced_spoof_video_streams", TRUE, true, "revanced_spoof_video_streams_user_dialog_message"); - public static final BooleanSetting SPOOF_STREAMING_DATA_STATS_FOR_NERDS = new BooleanSetting("revanced_spoof_video_streams_stats_for_nerds", TRUE, parent(SPOOF_VIDEO_STREAMS)); + public static final BooleanSetting SPOOF_STREAMING_DATA_STATS_FOR_NERDS = new BooleanSetting("revanced_spoof_streaming_data_stats_for_nerds", TRUE, parent(SPOOF_VIDEO_STREAMS)); public static final BooleanSetting SANITIZE_SHARING_LINKS = new BooleanSetting("revanced_sanitize_sharing_links", TRUE); public static final BooleanSetting REPLACE_MUSIC_LINKS_WITH_YOUTUBE = new BooleanSetting("revanced_replace_music_with_youtube", FALSE); diff --git a/extensions/shared/library/src/main/java/app/revanced/extension/shared/spoof/ClientType.java b/extensions/shared/library/src/main/java/app/revanced/extension/shared/spoof/ClientType.java index 9abd430719..39076b562d 100644 --- a/extensions/shared/library/src/main/java/app/revanced/extension/shared/spoof/ClientType.java +++ b/extensions/shared/library/src/main/java/app/revanced/extension/shared/spoof/ClientType.java @@ -9,34 +9,9 @@ import java.util.Locale; import java.util.Objects; import app.revanced.extension.shared.Logger; -import app.revanced.extension.shared.Utils; @SuppressWarnings("ConstantLocale") public enum ClientType { - /** - * Video not playable: Paid, Movie, Private, Age-restricted. - * Uses non-adaptive bitrate. - * AV1 codec available. - */ - ANDROID_REEL( - 3, - "ANDROID", - "com.google.android.youtube", - Build.MANUFACTURER, - Build.MODEL, - "Android", - Build.VERSION.RELEASE, - String.valueOf(Build.VERSION.SDK_INT), - Build.ID, - "20.44.38", - // This client has been used by most open-source YouTube stream extraction tools since 2024, including NewPipe Extractor, SmartTube, and Grayjay. - // This client can log in, but if an access token is used in the request, GVS can more easily identify the request as coming from ReVanced. - // This means that the GVS server can strengthen its validation of the ANDROID_REEL client. - true, - true, - false, - "Android Reel" - ), /** * Video not playable: Kids / Paid / Movie / Private / Age-restricted. * This client can only be used when logged out. @@ -53,10 +28,10 @@ public enum ClientType { // Android 12.1 "32", "SQ3A.220605.009.A1", + "132.0.6808.3", "1.61.48", false, false, - true, "Android VR 1.61" ), /** @@ -73,12 +48,39 @@ public enum ClientType { ANDROID_VR_1_61_48.osVersion, Objects.requireNonNull(ANDROID_VR_1_61_48.androidSdkVersion), Objects.requireNonNull(ANDROID_VR_1_61_48.buildId), + "107.0.5284.2", "1.43.32", ANDROID_VR_1_61_48.useAuth, ANDROID_VR_1_61_48.supportsMultiAudioTracks, - ANDROID_VR_1_61_48.usePlayerEndpoint, "Android VR 1.43" ), + /** + * Video not playable: Paid / Movie / Private / Age-restricted. + * Note: The 'Authorization' key must be excluded from the header. + * + * According to TeamNewPipe in 2022, if the 'androidSdkVersion' field is missing, + * the GVS did not return a valid response: + * [NewPipe#8713 (comment)](https://github.com/TeamNewPipe/NewPipe/issues/8713#issuecomment-1207443550). + * + * According to the latest commit in yt-dlp, the GVS returns a valid response + * even if the 'androidSdkVersion' field is missing: + * [yt-dlp#14693](https://github.com/yt-dlp/yt-dlp/pull/14693). + * + * For some reason, PoToken is not required. + */ + ANDROID_NO_SDK( + 3, + "ANDROID", + "", + "", + "", + Build.VERSION.RELEASE, + "20.05.46", + "com.google.android.youtube/20.05.46 (Linux; U; Android " + Build.VERSION.RELEASE + ") gzip", + false, + true, + "Android No SDK" + ), /** * Cannot play livestreams and lacks HDR, but can play videos with music and labeled "for children". * Google Pixel 9 Pro Fold @@ -93,10 +95,10 @@ public enum ClientType { "15", "35", "AP3A.241005.015.A2", + "132.0.6779.0", "23.47.101", true, false, - true, "Android Studio" ), /** @@ -112,8 +114,32 @@ public enum ClientType { "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/18.0 Safari/605.1.15", false, false, - true, "visionOS" + ), + /** + * The device machine id for the iPad 6th Gen (iPad7,6). + * AV1 hardware decoding is not supported. + * See [this GitHub Gist](https://gist.github.com/adamawolf/3048717) for more information. + * + * Based on Google's actions to date, PoToken may not be required on devices with very low specs. + * For example, suppose the User-Agent for a PlayStation 3 (with 256MB of RAM) is used. + * Accessing 'Web' (https://www.youtube.com) will redirect to 'TV' (https://www.youtube.com/tv). + * 'TV' target devices with very low specs, such as embedded devices, game consoles, and blu-ray players, so PoToken is not required. + * + * For this reason, the device machine id for the iPad 6th Gen (with 2GB of RAM), + * the lowest spec device capable of running iPadOS 17, was used. + */ + IPADOS(5, + "IOS", + "Apple", + "iPad7,6", + "iPadOS", + "17.7.10.21H450", + "19.22.3", + "com.google.ios.youtube/19.22.3 (iPad7,6; U; CPU iPadOS 17_7_10 like Mac OS X; " + Locale.getDefault() + ")", + false, + true, + "iPadOS" ); /** @@ -169,6 +195,13 @@ public enum ClientType { @Nullable private final String buildId; + /** + * Cronet release version, as found in decompiled client apk. + * Field is null if not applicable. + */ + @Nullable + private final String cronetVersion; + /** * App version. */ @@ -184,11 +217,6 @@ public enum ClientType { */ public final boolean supportsMultiAudioTracks; - /** - * If the client should use the player endpoint for stream extraction. - */ - public final boolean usePlayerEndpoint; - /** * Friendly name displayed in stats for nerds. */ @@ -206,10 +234,10 @@ public enum ClientType { String osVersion, @NonNull String androidSdkVersion, @NonNull String buildId, + @NonNull String cronetVersion, String clientVersion, boolean useAuth, boolean supportsMultiAudioTracks, - boolean usePlayerEndpoint, String friendlyName) { this.id = id; this.clientName = clientName; @@ -220,20 +248,21 @@ public enum ClientType { this.osVersion = osVersion; this.androidSdkVersion = androidSdkVersion; this.buildId = buildId; + this.cronetVersion = cronetVersion; this.clientVersion = clientVersion; this.useAuth = useAuth; this.supportsMultiAudioTracks = supportsMultiAudioTracks; - this.usePlayerEndpoint = usePlayerEndpoint; this.friendlyName = friendlyName; Locale defaultLocale = Locale.getDefault(); - this.userAgent = String.format("%s/%s (Linux; U; Android %s; %s; %s; Build/%s)", + this.userAgent = String.format("%s/%s (Linux; U; Android %s; %s; %s; Build/%s; Cronet/%s)", packageName, clientVersion, osVersion, defaultLocale, deviceModel, - buildId + Objects.requireNonNull(buildId), + Objects.requireNonNull(cronetVersion) ); Logger.printDebug(() -> "userAgent: " + this.userAgent); } @@ -249,7 +278,6 @@ public enum ClientType { String userAgent, boolean useAuth, boolean supportsMultiAudioTracks, - boolean usePlayerEndpoint, String friendlyName) { this.id = id; this.clientName = clientName; @@ -261,10 +289,10 @@ public enum ClientType { this.userAgent = userAgent; this.useAuth = useAuth; this.supportsMultiAudioTracks = supportsMultiAudioTracks; - this.usePlayerEndpoint = usePlayerEndpoint; this.friendlyName = friendlyName; this.packageName = null; this.androidSdkVersion = null; this.buildId = null; + this.cronetVersion = null; } } diff --git a/extensions/shared/library/src/main/java/app/revanced/extension/shared/spoof/SpoofVideoStreamsPatch.java b/extensions/shared/library/src/main/java/app/revanced/extension/shared/spoof/SpoofVideoStreamsPatch.java index 0c861510fe..38fbac9938 100644 --- a/extensions/shared/library/src/main/java/app/revanced/extension/shared/spoof/SpoofVideoStreamsPatch.java +++ b/extensions/shared/library/src/main/java/app/revanced/extension/shared/spoof/SpoofVideoStreamsPatch.java @@ -5,6 +5,7 @@ import android.text.TextUtils; import androidx.annotation.Nullable; +import java.nio.ByteBuffer; import java.util.List; import java.util.Map; import java.util.Objects; @@ -38,7 +39,7 @@ public class SpoofVideoStreamsPatch { @Nullable private static volatile AppLanguage languageOverride; - private static volatile ClientType preferredClient = ClientType.ANDROID_REEL; + private static volatile ClientType preferredClient = ClientType.ANDROID_VR_1_43_32; /** * @return If this patch was included during patching. @@ -249,7 +250,7 @@ public class SpoofVideoStreamsPatch { * Called after {@link #fetchStreams(String, Map)}. */ @Nullable - public static byte[] getStreamingData(String videoId) { + public static ByteBuffer getStreamingData(String videoId) { if (SPOOF_STREAMING_DATA) { try { StreamingDataRequest request = StreamingDataRequest.getRequestForVideoId(videoId); diff --git a/extensions/shared/library/src/main/java/app/revanced/extension/shared/spoof/requests/PlayerRoutes.java b/extensions/shared/library/src/main/java/app/revanced/extension/shared/spoof/requests/PlayerRoutes.java index 959048d1e2..31e3f03034 100644 --- a/extensions/shared/library/src/main/java/app/revanced/extension/shared/spoof/requests/PlayerRoutes.java +++ b/extensions/shared/library/src/main/java/app/revanced/extension/shared/spoof/requests/PlayerRoutes.java @@ -15,20 +15,13 @@ import app.revanced.extension.shared.spoof.ClientType; import app.revanced.extension.shared.spoof.SpoofVideoStreamsPatch; final class PlayerRoutes { - static final Route.CompiledRoute GET_PLAYER_STREAMING_DATA = new Route( + static final Route.CompiledRoute GET_STREAMING_DATA = new Route( Route.Method.POST, "player" + "?fields=streamingData" + "&alt=proto" ).compile(); - static final Route.CompiledRoute GET_REEL_STREAMING_DATA = new Route( - Route.Method.POST, - "reel/reel_item_watch" + - "?fields=playerResponse.playabilityStatus,playerResponse.streamingData" + - "&alt=proto" - ).compile(); - private static final String YT_API_URL = "https://youtubei.googleapis.com/youtubei/v1/"; /** @@ -54,7 +47,6 @@ final class PlayerRoutes { Locale streamLocale = language.getLocale(); JSONObject client = new JSONObject(); - client.put("deviceMake", clientType.deviceMake); client.put("deviceModel", clientType.deviceModel); client.put("clientName", clientType.clientName); @@ -69,19 +61,9 @@ final class PlayerRoutes { context.put("client", client); innerTubeBody.put("context", context); - - if (clientType.usePlayerEndpoint) { - innerTubeBody.put("contentCheckOk", true); - innerTubeBody.put("racyCheckOk", true); - innerTubeBody.put("videoId", videoId); - } else { - JSONObject playerRequest = new JSONObject(); - playerRequest.put("contentCheckOk", true); - playerRequest.put("racyCheckOk", true); - playerRequest.put("videoId", videoId); - innerTubeBody.put("playerRequest", playerRequest); - innerTubeBody.put("disablePlayerResponse", false); - } + innerTubeBody.put("contentCheckOk", true); + innerTubeBody.put("racyCheckOk", true); + innerTubeBody.put("videoId", videoId); } catch (JSONException e) { Logger.printException(() -> "Failed to create innerTubeBody", e); } diff --git a/extensions/shared/library/src/main/java/app/revanced/extension/shared/spoof/requests/StreamingDataRequest.java b/extensions/shared/library/src/main/java/app/revanced/extension/shared/spoof/requests/StreamingDataRequest.java index fb8a8e79e8..8eb1eaaab5 100644 --- a/extensions/shared/library/src/main/java/app/revanced/extension/shared/spoof/requests/StreamingDataRequest.java +++ b/extensions/shared/library/src/main/java/app/revanced/extension/shared/spoof/requests/StreamingDataRequest.java @@ -1,17 +1,18 @@ package app.revanced.extension.shared.spoof.requests; import static app.revanced.extension.shared.ByteTrieSearch.convertStringsToBytes; -import static app.revanced.extension.shared.Utils.isNotEmpty; -import static app.revanced.extension.shared.spoof.requests.PlayerRoutes.GET_PLAYER_STREAMING_DATA; -import static app.revanced.extension.shared.spoof.requests.PlayerRoutes.GET_REEL_STREAMING_DATA; +import static app.revanced.extension.shared.spoof.requests.PlayerRoutes.GET_STREAMING_DATA; import androidx.annotation.NonNull; import androidx.annotation.Nullable; +import java.io.BufferedInputStream; +import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.InputStream; 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; @@ -26,11 +27,6 @@ import java.util.concurrent.TimeoutException; import app.revanced.extension.shared.ByteTrieSearch; import app.revanced.extension.shared.Logger; import app.revanced.extension.shared.Utils; -import app.revanced.extension.shared.innertube.PlayerResponseOuterClass; -import app.revanced.extension.shared.innertube.PlayerResponseOuterClass.PlayerResponse; -import app.revanced.extension.shared.innertube.PlayerResponseOuterClass.StreamingData; -import app.revanced.extension.shared.innertube.ReelItemWatchResponseOuterClass.ReelItemWatchResponse; -import app.revanced.extension.shared.requests.Route; import app.revanced.extension.shared.settings.BaseSettings; import app.revanced.extension.shared.spoof.ClientType; @@ -45,7 +41,7 @@ import app.revanced.extension.shared.spoof.ClientType; */ public class StreamingDataRequest { - private static volatile ClientType[] clientOrderToUse = ClientType.values(); + private static volatile ClientType[] clientOrderToUse = ClientType.values(); public static void setClientOrderToUse(List availableClients, ClientType preferredClient) { Objects.requireNonNull(preferredClient); @@ -115,7 +111,7 @@ public class StreamingDataRequest { private final String videoId; - private final Future future; + private final Future future; private StreamingDataRequest(String videoId, Map playerHeaders) { Objects.requireNonNull(playerHeaders); @@ -138,12 +134,6 @@ public class StreamingDataRequest { Logger.printInfo(() -> toastMessage, ex); } - private static void handleDebugToast(String toastMessage, ClientType clientType) { - if (BaseSettings.DEBUG.get() && BaseSettings.DEBUG_TOAST_ON_ERROR.get()) { - Utils.showToastShort(String.format(toastMessage, clientType)); - } - } - @Nullable private static HttpURLConnection send(ClientType clientType, String videoId, @@ -156,10 +146,7 @@ public class StreamingDataRequest { final long startTime = System.currentTimeMillis(); try { - Route.CompiledRoute route = clientType.usePlayerEndpoint ? - GET_PLAYER_STREAMING_DATA : GET_REEL_STREAMING_DATA; - - HttpURLConnection connection = PlayerRoutes.getPlayerResponseConnectionFromRoute(route, clientType); + HttpURLConnection connection = PlayerRoutes.getPlayerResponseConnectionFromRoute(GET_STREAMING_DATA, clientType); connection.setConnectTimeout(HTTP_TIMEOUT_MILLISECONDS); connection.setReadTimeout(HTTP_TIMEOUT_MILLISECONDS); @@ -216,7 +203,7 @@ public class StreamingDataRequest { return null; } - private static byte[] fetch(String videoId, Map playerHeaders) { + private static ByteBuffer fetch(String videoId, Map playerHeaders) { final boolean debugEnabled = BaseSettings.DEBUG.get(); // Retry with different client if empty response body is received. @@ -227,11 +214,33 @@ public class StreamingDataRequest { HttpURLConnection connection = send(clientType, videoId, playerHeaders, showErrorToast); if (connection != null) { - byte[] playerResponseBuffer = buildPlayerResponseBuffer(clientType, connection); - if (playerResponseBuffer != null) { - lastSpoofedClientType = clientType; + try { + // gzip encoding doesn't response with content length (-1), + // but empty response body does. + if (connection.getContentLength() == 0) { + if (BaseSettings.DEBUG.get() && BaseSettings.DEBUG_TOAST_ON_ERROR.get()) { + Utils.showToastShort("Debug: Ignoring empty spoof stream client " + clientType); + } + } else { + try (InputStream inputStream = new BufferedInputStream(connection.getInputStream()); + ByteArrayOutputStream baos = new ByteArrayOutputStream()) { - return playerResponseBuffer; + byte[] buffer = new byte[2048]; + int bytesRead; + while ((bytesRead = inputStream.read(buffer)) >= 0) { + baos.write(buffer, 0, bytesRead); + } + if (clientType == ClientType.ANDROID_CREATOR && liveStreamBufferSearch.matches(buffer)) { + Logger.printDebug(() -> "Skipping Android Studio as video is a livestream: " + videoId); + } else { + lastSpoofedClientType = clientType; + + return ByteBuffer.wrap(baos.toByteArray()); + } + } + } + } catch (IOException ex) { + Logger.printException(() -> "Fetch failed while processing response data", ex); } } } @@ -241,61 +250,12 @@ public class StreamingDataRequest { return null; } - @Nullable - private static byte[] buildPlayerResponseBuffer(ClientType clientType, - HttpURLConnection connection) { - // gzip encoding doesn't response with content length (-1), - // but empty response body does. - if (connection.getContentLength() == 0) { - handleDebugToast("Debug: Ignoring empty spoof stream client (%s)", clientType); - return null; - } - - try (InputStream inputStream = connection.getInputStream()) { - PlayerResponse playerResponse = clientType.usePlayerEndpoint - ? PlayerResponse.parseFrom(inputStream) - : ReelItemWatchResponse.parseFrom(inputStream).getPlayerResponse(); - - var playabilityStatus = playerResponse.getPlayabilityStatus(); - if (playabilityStatus.getStatus() != PlayerResponseOuterClass.Status.OK) { - handleDebugToast("Debug: Ignoring unplayable video (%s)", clientType); - String reason = playabilityStatus.getReason(); - if (isNotEmpty(reason)) { - Logger.printDebug(() -> String.format("Debug: Ignoring unplayable video (%s), reason: %s", clientType, reason)); - } - - return null; - } - - PlayerResponse.Builder responseBuilder = playerResponse.toBuilder(); - if (!playerResponse.hasStreamingData()) { - handleDebugToast("Debug: Ignoring empty streaming data (%s)", clientType); - return null; - } - - // Android Studio only supports the HLS protocol for live streams. - // HLS protocol can theoretically be played with ExoPlayer, - // but the related code has not yet been implemented. - // If DASH protocol is not available, the client will be skipped. - StreamingData streamingData = playerResponse.getStreamingData(); - if (streamingData.getAdaptiveFormatsCount() == 0) { - handleDebugToast("Debug: Ignoring empty adaptiveFormat (%s)", clientType); - return null; - } - - return responseBuilder.build().toByteArray(); - } catch (IOException ex) { - Logger.printException(() -> "Failed to write player response to buffer array", ex); - return null; - } - } - public boolean fetchCompleted() { return future.isDone(); } @Nullable - public byte[] getStream() { + public ByteBuffer getStream() { try { return future.get(MAX_MILLISECONDS_TO_WAIT_FOR_FETCH, TimeUnit.MILLISECONDS); } catch (TimeoutException ex) { diff --git a/extensions/shared/protobuf/build.gradle.kts b/extensions/shared/protobuf/build.gradle.kts deleted file mode 100644 index be3f7f6a08..0000000000 --- a/extensions/shared/protobuf/build.gradle.kts +++ /dev/null @@ -1,55 +0,0 @@ -import com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar - -plugins { - kotlin("jvm") - alias(libs.plugins.protobuf) - alias(libs.plugins.shadow) -} - -val shade: Configuration by configurations.creating { - configurations.getByName("compileClasspath").extendsFrom(this) - configurations.getByName("runtimeClasspath").extendsFrom(this) -} - -dependencies { - compileOnly(libs.annotation) - compileOnly(libs.okhttp) - shade(libs.protobuf.javalite) -} - -sourceSets { - // Make sure generated proto sources are compiled and end up in the shaded jar - main { - java.srcDir("$buildDir/generated/source/proto/main/java") - } -} - -protobuf { - protoc { - artifact = libs.protobuf.protoc.get().toString() - } - - generateProtoTasks { - all().forEach { task -> - task.builtins { - named("java") { - option("lite") - } - } - } - } -} - -val shadowJar = tasks.named("shadowJar") { - configurations = listOf(shade) - relocate("com.google.protobuf", "app.revanced.com.google.protobuf") -} - -configurations.named("runtimeElements") { - isCanBeConsumed = true - isCanBeResolved = false - - outgoing.artifacts.clear() - outgoing.artifact(shadowJar) -}!!.let { artifacts { add(it.name, shadowJar) } } - diff --git a/extensions/shared/protobuf/src/main/proto/app/revanced/extension/shared/innertube/player_response.proto b/extensions/shared/protobuf/src/main/proto/app/revanced/extension/shared/innertube/player_response.proto deleted file mode 100644 index 29f1a6d9bd..0000000000 --- a/extensions/shared/protobuf/src/main/proto/app/revanced/extension/shared/innertube/player_response.proto +++ /dev/null @@ -1,42 +0,0 @@ -syntax = "proto3"; - -package app.revanced.extension.shared.innertube; - -option optimize_for = LITE_RUNTIME; -option java_package = "app.revanced.extension.shared.innertube"; - -message PlayerResponse { - oneof data { - PlayabilityStatus playability_status = 2; - StreamingData streaming_data = 4; - } -} - -message PlayabilityStatus { - Status status = 1; - string reason = 2; -} - -enum Status { - OK = 0; - ERROR = 1; - UNPLAYABLE = 2; - LOGIN_REQUIRED = 3; - CONTENT_CHECK_REQUIRED = 4; - AGE_CHECK_REQUIRED = 5; - LIVE_STREAM_OFFLINE = 6; - FULLSCREEN_ONLY = 7; - GL_PLAYBACK_REQUIRED = 8; - AGE_VERIFICATION_REQUIRED = 9; -} - -message StreamingData { - repeated Format formats = 2; - repeated Format adaptiveFormats = 3; - string serverAbrStreamingUrl = 15; -} - -message Format { - string url = 2; - string signatureCipher = 48; -} diff --git a/extensions/shared/protobuf/src/main/proto/app/revanced/extension/shared/innertube/reel_item_watch_response.proto b/extensions/shared/protobuf/src/main/proto/app/revanced/extension/shared/innertube/reel_item_watch_response.proto deleted file mode 100644 index e74f142533..0000000000 --- a/extensions/shared/protobuf/src/main/proto/app/revanced/extension/shared/innertube/reel_item_watch_response.proto +++ /dev/null @@ -1,14 +0,0 @@ -syntax = "proto3"; - -import "app/revanced/extension/shared/innertube/player_response.proto"; - -package app.revanced.extension.shared.innertube; - -option optimize_for = LITE_RUNTIME; -option java_package = "app.revanced.extension.shared.innertube"; - -message ReelItemWatchResponse { - oneof data { - PlayerResponse player_response = 4; - } -} diff --git a/extensions/youtube/src/main/java/app/revanced/extension/youtube/patches/announcements/AnnouncementsPatch.java b/extensions/youtube/src/main/java/app/revanced/extension/youtube/patches/announcements/AnnouncementsPatch.java index 8e24d65ec5..1212416069 100644 --- a/extensions/youtube/src/main/java/app/revanced/extension/youtube/patches/announcements/AnnouncementsPatch.java +++ b/extensions/youtube/src/main/java/app/revanced/extension/youtube/patches/announcements/AnnouncementsPatch.java @@ -14,9 +14,7 @@ import android.widget.LinearLayout; import android.widget.TextView; import app.revanced.extension.shared.ui.CustomDialog; - import org.json.JSONArray; -import org.json.JSONObject; import java.io.IOException; import java.net.HttpURLConnection; @@ -31,20 +29,9 @@ import app.revanced.extension.youtube.settings.Settings; @SuppressWarnings("unused") public final class AnnouncementsPatch { - private static final String[] ANNOUNCEMENT_TAGS = { - "\uD83C\uDF9E\uFE0F YouTube", - }; - private AnnouncementsPatch() { } - private static boolean hasSupportedTag(String wrapperTag) { - if (wrapperTag == null) return false; - for (var tag : ANNOUNCEMENT_TAGS) if (tag.equals(wrapperTag)) return true; - - return false; - } - private static boolean isLatestAlready() throws IOException { HttpURLConnection connection = AnnouncementsRoutes.getAnnouncementsConnectionFromRoute(GET_LATEST_ANNOUNCEMENT_IDS); @@ -69,30 +56,19 @@ public final class AnnouncementsPatch { var jsonString = Requester.parseStringAndDisconnect(connection); - var id = -1; + // Parse the ID. Fall-back to raw string if it fails. + int id = Settings.ANNOUNCEMENT_LAST_ID.defaultValue; try { - final var announcementIDTagPairs = new JSONArray(jsonString); - if (announcementIDTagPairs.length() == 0) return true; - - JSONObject latest = null; - for (int i = 0, entryCount = announcementIDTagPairs.length(); i < entryCount; i++) { - var pair = announcementIDTagPairs.optJSONObject(i); - if (pair != null && hasSupportedTag(pair.optString("tag", null))) { - latest = pair; - break; - } - } - - if (latest == null || latest.isNull("id")) return true; - - id = latest.getInt("id"); + final var announcementIDs = new JSONArray(jsonString); + if (announcementIDs.length() == 0) return true; + + id = announcementIDs.getJSONObject(0).getInt("id"); } catch (Throwable ex) { Logger.printException(() -> "Failed to parse announcement ID", ex); - return true; } // Do not show the announcement, if the last announcement id is the same as the current one. - return Settings.ANNOUNCEMENT_LAST_ID.get().equals(id); + return Settings.ANNOUNCEMENT_LAST_ID.get() == id; } public static void showAnnouncement(final Activity context) { @@ -119,22 +95,7 @@ public final class AnnouncementsPatch { LocalDateTime archivedAt = LocalDateTime.MAX; Level level = Level.INFO; try { - final var announcements = new JSONArray(jsonString); - JSONObject latestAnnouncement = null; - for (int i = 0, entryCount = announcements.length(); i < entryCount; i++) { - var announcementTagPair = announcements.optJSONObject(i); - if (announcementTagPair != null && hasSupportedTag(announcementTagPair.optString("tag", null))) { - latestAnnouncement = announcementTagPair; - break; - } - } - - if (latestAnnouncement == null || latestAnnouncement.isNull("announcement")) { - Logger.printDebug(() -> "No YouTube announcement found in latest announcements response"); - return; - } - - final var announcement = latestAnnouncement.getJSONObject("announcement"); + final var announcement = new JSONArray(jsonString).getJSONObject(0); id = announcement.getInt("id"); title = announcement.getString("title"); diff --git a/extensions/youtube/src/main/java/app/revanced/extension/youtube/patches/announcements/requests/AnnouncementsRoutes.java b/extensions/youtube/src/main/java/app/revanced/extension/youtube/patches/announcements/requests/AnnouncementsRoutes.java index aa63ae864c..d89a2d3403 100644 --- a/extensions/youtube/src/main/java/app/revanced/extension/youtube/patches/announcements/requests/AnnouncementsRoutes.java +++ b/extensions/youtube/src/main/java/app/revanced/extension/youtube/patches/announcements/requests/AnnouncementsRoutes.java @@ -9,9 +9,9 @@ import java.net.HttpURLConnection; import static app.revanced.extension.shared.requests.Route.Method.GET; public class AnnouncementsRoutes { - private static final String ANNOUNCEMENTS_PROVIDER = "https://api.revanced.app/v5"; - public static final Route GET_LATEST_ANNOUNCEMENT_IDS = new Route(GET, "/announcements/latest/id"); - public static final Route GET_LATEST_ANNOUNCEMENTS = new Route(GET, "/announcements/latest"); + private static final String ANNOUNCEMENTS_PROVIDER = "https://api.revanced.app/v4"; + public static final Route GET_LATEST_ANNOUNCEMENT_IDS = new Route(GET, "/announcements/latest/id?tag=\uD83C\uDF9E\uFE0F%20YouTube"); + public static final Route GET_LATEST_ANNOUNCEMENTS = new Route(GET, "/announcements/latest?tag=\uD83C\uDF9E\uFE0F%20YouTube"); private AnnouncementsRoutes() { } diff --git a/extensions/youtube/src/main/java/app/revanced/extension/youtube/patches/spoof/SpoofVideoStreamsPatch.java b/extensions/youtube/src/main/java/app/revanced/extension/youtube/patches/spoof/SpoofVideoStreamsPatch.java index 21819a2828..2bf442a937 100644 --- a/extensions/youtube/src/main/java/app/revanced/extension/youtube/patches/spoof/SpoofVideoStreamsPatch.java +++ b/extensions/youtube/src/main/java/app/revanced/extension/youtube/patches/spoof/SpoofVideoStreamsPatch.java @@ -1,9 +1,10 @@ package app.revanced.extension.youtube.patches.spoof; import static app.revanced.extension.shared.spoof.ClientType.ANDROID_CREATOR; +import static app.revanced.extension.shared.spoof.ClientType.ANDROID_NO_SDK; import static app.revanced.extension.shared.spoof.ClientType.ANDROID_VR_1_43_32; import static app.revanced.extension.shared.spoof.ClientType.ANDROID_VR_1_61_48; -import static app.revanced.extension.shared.spoof.ClientType.ANDROID_REEL; +import static app.revanced.extension.shared.spoof.ClientType.IPADOS; import static app.revanced.extension.shared.spoof.ClientType.VISIONOS; import java.util.List; @@ -43,11 +44,11 @@ public class SpoofVideoStreamsPatch { } List availableClients = List.of( - ANDROID_REEL, - ANDROID_VR_1_43_32, VISIONOS, - ANDROID_CREATOR - ); + ANDROID_CREATOR, + ANDROID_VR_1_43_32, + ANDROID_NO_SDK, + IPADOS); app.revanced.extension.shared.spoof.SpoofVideoStreamsPatch.setClientsToUse( availableClients, client); diff --git a/extensions/youtube/src/main/java/app/revanced/extension/youtube/settings/preference/SpoofVideoStreamsSideEffectsPreference.java b/extensions/youtube/src/main/java/app/revanced/extension/youtube/settings/preference/SpoofStreamingDataSideEffectsPreference.java similarity index 75% rename from extensions/youtube/src/main/java/app/revanced/extension/youtube/settings/preference/SpoofVideoStreamsSideEffectsPreference.java rename to extensions/youtube/src/main/java/app/revanced/extension/youtube/settings/preference/SpoofStreamingDataSideEffectsPreference.java index 1faaaf0a3e..86802ee200 100644 --- a/extensions/youtube/src/main/java/app/revanced/extension/youtube/settings/preference/SpoofVideoStreamsSideEffectsPreference.java +++ b/extensions/youtube/src/main/java/app/revanced/extension/youtube/settings/preference/SpoofStreamingDataSideEffectsPreference.java @@ -19,7 +19,7 @@ import app.revanced.extension.shared.spoof.ClientType; import app.revanced.extension.youtube.settings.Settings; @SuppressWarnings({"deprecation", "unused"}) -public class SpoofVideoStreamsSideEffectsPreference extends Preference { +public class SpoofStreamingDataSideEffectsPreference extends Preference { @Nullable private ClientType currentClientType; @@ -33,19 +33,19 @@ public class SpoofVideoStreamsSideEffectsPreference extends Preference { Utils.runOnMainThread(this::updateUI); }; - public SpoofVideoStreamsSideEffectsPreference(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) { + public SpoofStreamingDataSideEffectsPreference(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) { super(context, attrs, defStyleAttr, defStyleRes); } - public SpoofVideoStreamsSideEffectsPreference(Context context, AttributeSet attrs, int defStyleAttr) { + public SpoofStreamingDataSideEffectsPreference(Context context, AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); } - public SpoofVideoStreamsSideEffectsPreference(Context context, AttributeSet attrs) { + public SpoofStreamingDataSideEffectsPreference(Context context, AttributeSet attrs) { super(context, attrs); } - public SpoofVideoStreamsSideEffectsPreference(Context context) { + public SpoofStreamingDataSideEffectsPreference(Context context) { super(context); } @@ -88,23 +88,27 @@ public class SpoofVideoStreamsSideEffectsPreference extends Preference { + '\n' + str("revanced_spoof_video_streams_about_no_stable_volume") + '\n' + str("revanced_spoof_video_streams_about_no_av1") + '\n' + str("revanced_spoof_video_streams_about_no_force_original_audio"); - case ANDROID_REEL -> - summary = str("revanced_spoof_video_streams_about_playback_failure"); // VR 1.61 is not exposed in the UI and should never be reached here. case ANDROID_VR_1_43_32, ANDROID_VR_1_61_48 -> - summary = str("revanced_spoof_video_streams_about_playback_failure") - + '\n' + str("revanced_spoof_video_streams_about_no_audio_tracks") + summary = str("revanced_spoof_video_streams_about_no_audio_tracks") + '\n' + str("revanced_spoof_video_streams_about_no_stable_volume"); - case VISIONOS -> summary = str("revanced_spoof_video_streams_about_experimental") - + '\n' + str("revanced_spoof_video_streams_about_playback_failure") - + '\n' + str("revanced_spoof_video_streams_about_no_audio_tracks") - + '\n' + str("revanced_spoof_video_streams_about_no_av1"); + case ANDROID_NO_SDK -> + summary = str("revanced_spoof_video_streams_about_playback_failure"); + case IPADOS -> + summary = str("revanced_spoof_video_streams_about_playback_failure") + + '\n' + str("revanced_spoof_video_streams_about_no_av1"); + case VISIONOS -> + summary = str("revanced_spoof_video_streams_about_experimental") + + '\n' + str("revanced_spoof_video_streams_about_no_audio_tracks") + + '\n' + str("revanced_spoof_video_streams_about_no_av1"); default -> Logger.printException(() -> "Unknown client: " + clientType); } - // Only Android Reel and Android VR supports 360° VR immersive mode. - if (!clientType.name().startsWith("ANDROID_VR") && clientType != ClientType.ANDROID_REEL) { - summary += '\n' + str("revanced_spoof_video_streams_about_no_immersive_mode"); + // Only iPadOS can play children videos in incognito, but it commonly fails at 1 minute + // or doesn't start playback at all. List the side effect for other clients + // since they will fall over to iPadOS. + if (clientType != ClientType.IPADOS && clientType != ClientType.ANDROID_NO_SDK) { + summary += '\n' + str("revanced_spoof_video_streams_about_kids_videos"); } // Use better formatting for bullet points. diff --git a/gradle.properties b/gradle.properties index d65f6857e0..f343fe4628 100644 --- a/gradle.properties +++ b/gradle.properties @@ -4,4 +4,4 @@ org.gradle.parallel = true android.useAndroidX = true android.uniquePackageNames = false kotlin.code.style = official -version = 6.1.0 +version = 6.0.1 diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 5db73fbcee..bea84117e6 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -10,10 +10,6 @@ okhttp = "5.3.2" retrofit = "3.0.0" guava = "33.5.0-jre" apksig = "9.0.1" -# TODO: Adjust once https://github.com/google/protobuf-gradle-plugin/pull/797 is merged. -protobuf = "master-SNAPSHOT" -protoc = "4.34.0" -shadow = "9.4.0" [libraries] annotation = { module = "androidx.annotation:annotation", version.ref = "annotation" } @@ -22,10 +18,6 @@ okhttp = { module = "com.squareup.okhttp3:okhttp", version.ref = "okhttp" } retrofit = { module = "com.squareup.retrofit2:retrofit", version.ref = "retrofit" } guava = { module = "com.google.guava:guava", version.ref = "guava" } apksig = { group = "com.android.tools.build", name = "apksig", version.ref = "apksig" } -protobuf-javalite = { module = "com.google.protobuf:protobuf-javalite", version.ref = "protoc" } -protobuf-protoc = { module = "com.google.protobuf:protoc", version.ref = "protoc" } [plugins] android-library = { id = "com.android.library" } -protobuf = { id = "com.google.protobuf", version.ref = "protobuf" } -shadow = { id = "com.gradleup.shadow", version.ref = "shadow" } diff --git a/patches/api/patches.api b/patches/api/patches.api index 4a8ed81323..be251e84a5 100644 --- a/patches/api/patches.api +++ b/patches/api/patches.api @@ -89,14 +89,10 @@ public final class app/revanced/patches/all/misc/packagename/ChangePackageNamePa public static final fun setOrGetFallbackPackageName (Ljava/lang/String;)Ljava/lang/String; } -public final class app/revanced/patches/all/misc/play/DisablePlayIntegrityKt { +public final class app/revanced/patches/all/misc/playintegrity/DisablePlayIntegrityKt { public static final fun getDisablePlayIntegrityPatch ()Lapp/revanced/patcher/patch/Patch; } -public final class app/revanced/patches/all/misc/play/SpoofPlayAgeSignalsKt { - public static final fun getSpoofPlayAgeSignalsPatch ()Lapp/revanced/patcher/patch/Patch; -} - public final class app/revanced/patches/all/misc/resources/AddResourcesPatchKt { public static final fun addResource (Ljava/lang/String;Lapp/revanced/util/resource/BaseResource;)Z public static final fun addResources (Lapp/revanced/patcher/patch/Patch;Lkotlin/jvm/functions/Function1;)Z @@ -129,14 +125,6 @@ public final class app/revanced/patches/all/misc/spoof/EnableRomSignatureSpoofin public static final fun getEnableROMSignatureSpoofingPatch ()Lapp/revanced/patcher/patch/Patch; } -public final class app/revanced/patches/all/misc/spoof/SpoofKeystoreSecurityLevelPatchKt { - public static final fun getSpoofKeystoreSecurityLevelPatch ()Lapp/revanced/patcher/patch/Patch; -} - -public final class app/revanced/patches/all/misc/spoof/SpoofRootOfTrustPatchKt { - public static final fun getSpoofRootOfTrustPatch ()Lapp/revanced/patcher/patch/Patch; -} - public final class app/revanced/patches/all/misc/targetSdk/SetTargetSdkVersion34Kt { public static final fun getSetTargetSDKVersion34Patch ()Lapp/revanced/patcher/patch/Patch; } @@ -377,10 +365,6 @@ public final class app/revanced/patches/instagram/story/flipping/DisableStoryAut public static final fun getDisableStoryAutoFlippingPatch ()Lapp/revanced/patcher/patch/Patch; } -public final class app/revanced/patches/instagram/story/locationsticker/EnableLocationStickerRedesignPatchKt { - public static final fun getEnableLocationStickerRedesignPatch ()Lapp/revanced/patcher/patch/Patch; -} - public final class app/revanced/patches/irplus/ad/RemoveAdsPatchKt { public static final fun getRemoveAdsPatch ()Lapp/revanced/patcher/patch/Patch; } diff --git a/patches/build.gradle.kts b/patches/build.gradle.kts index 87ac8faecd..b0adaf6da6 100644 --- a/patches/build.gradle.kts +++ b/patches/build.gradle.kts @@ -6,7 +6,7 @@ patches { description = "Patches for ReVanced" source = "git@github.com:revanced/revanced-patches.git" author = "ReVanced" - contact = "patches@revanced.app" + contact = "contact@revanced.app" website = "https://revanced.app" license = "GNU General Public License v3.0" } diff --git a/patches/src/main/kotlin/app/revanced/patches/all/misc/connectivity/telephony/sim/spoof/SpoofSimProviderPatch.kt b/patches/src/main/kotlin/app/revanced/patches/all/misc/connectivity/telephony/sim/spoof/SpoofSimProviderPatch.kt index cb5723de76..a7dfdf7bf4 100644 --- a/patches/src/main/kotlin/app/revanced/patches/all/misc/connectivity/telephony/sim/spoof/SpoofSimProviderPatch.kt +++ b/patches/src/main/kotlin/app/revanced/patches/all/misc/connectivity/telephony/sim/spoof/SpoofSimProviderPatch.kt @@ -6,8 +6,7 @@ import app.revanced.patcher.extensions.replaceInstruction import app.revanced.patcher.patch.bytecodePatch import app.revanced.patcher.patch.intOption import app.revanced.patcher.patch.stringOption -import app.revanced.util.forEachInstructionAsSequence -import com.android.tools.smali.dexlib2.iface.instruction.Instruction +import app.revanced.patches.all.misc.transformation.transformInstructionsPatch 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.MethodReference @@ -34,8 +33,7 @@ val spoofSIMProviderPatch = bytecodePatch( validator = { it: String? -> it == null || it.uppercase() in countries.values }, ) - fun isMccMncValid(it: Int?) = it == null || (it in 10000..999999) - fun isNumericValid(it: String?, length: Int) = it.isNullOrBlank() || it.equals("random", true) || it.length == length + fun isMccMncValid(it: Int?): Boolean = it == null || (it >= 10000 && it <= 999999) val networkCountryIso by isoCountryPatchOption("Network ISO country code") @@ -63,119 +61,46 @@ val spoofSIMProviderPatch = bytecodePatch( description = "The full name of the SIM operator.", ) - val imei by stringOption( - name = "IMEI value", - description = "15-digit IMEI to spoof, blank to skip, or 'random'.", - validator = { isNumericValid(it, 15) }, - ) - - val meid by stringOption( - name = "MEID value", - description = "14-char hex MEID to spoof, blank to skip, or 'random'.", - validator = { isNumericValid(it, 14) }, - ) - - val imsi by stringOption( - name = "IMSI (Subscriber ID)", - description = "15-digit IMSI to spoof, blank to skip, or 'random'.", - validator = { isNumericValid(it, 15) }, - ) - - val iccid by stringOption( - name = "ICCID (SIM Serial)", - description = "19-digit ICCID to spoof, blank to skip, or 'random'.", - validator = { isNumericValid(it, 19) }, - ) - - val phone by stringOption( - name = "Phone number", - description = "Phone number to spoof, blank to skip, or 'random'.", - validator = { it.isNullOrBlank() || it.equals("random", ignoreCase = true) || it.startsWith("+") }, - ) - dependsOn( - bytecodePatch { - apply { - fun generateRandomNumeric(length: Int) = (1..length).map { ('0'..'9').random() }.joinToString("") + transformInstructionsPatch( + filterMap = { _, _, instruction, instructionIndex -> + if (instruction !is ReferenceInstruction) return@transformInstructionsPatch null - fun String?.computeSpoof(randomizer: () -> String): String? { - if (this.isNullOrBlank()) return null - if (this.equals("random", ignoreCase = true)) return randomizer() - return this + val reference = instruction.reference as? MethodReference ?: return@transformInstructionsPatch null + + val match = MethodCall.entries.firstOrNull { search -> + MethodUtil.methodSignaturesMatch(reference, search.reference) + } ?: return@transformInstructionsPatch null + + val replacement = when (match) { + MethodCall.NetworkCountryIso -> networkCountryIso?.lowercase() + MethodCall.NetworkOperator -> networkOperator?.toString() + MethodCall.NetworkOperatorName -> networkOperatorName + MethodCall.SimCountryIso -> simCountryIso?.lowercase() + MethodCall.SimOperator -> simOperator?.toString() + MethodCall.SimOperatorName -> simOperatorName } - - // Calculate the Luhn checksum (mod 10) to generate a valid 15th digit, standard for IMEI numbers. - // Structure of an IMEI is as follows: - // TAC (Type Allocation Code): First 8 digits (e.g., "86" + 6 digits) - // SNR (Serial Number): Next 6 digits - // CD (Check Digit): The 15th digit - val computedImei = imei.computeSpoof { - val prefix = "86" + generateRandomNumeric(12) - - val sum = prefix.mapIndexed { i, c -> - var d = c.digitToInt() - // Double every second digit (index 1, 3, 5...). - if (i % 2 != 0) { - d *= 2 - // If result is two digits (e.g. 14), sum them (1+4=5). - // This is mathematically equivalent to d - 9. - if (d > 9) d -= 9 - } - d - }.sum() - // Append the calculated check digit to the 14-digit prefix. - prefix + ((10 - (sum % 10)) % 10) - } - - val computedMeid = meid.computeSpoof { (1..14).map { "0123456789ABCDEF".random() }.joinToString("") }?.uppercase() - val computedImsi = imsi.computeSpoof { generateRandomNumeric(15) } - val computedIccid = iccid.computeSpoof { "89" + generateRandomNumeric(17) } - val computedPhone = phone.computeSpoof { "+" + generateRandomNumeric(11) } - - forEachInstructionAsSequence( - match = { _, _, instruction, instructionIndex -> - if (instruction !is ReferenceInstruction) return@forEachInstructionAsSequence null - - val reference = instruction.reference as? MethodReference ?: return@forEachInstructionAsSequence null - - val match = MethodCall.entries.firstOrNull { search -> - MethodUtil.methodSignaturesMatch(reference, search.reference) - } ?: return@forEachInstructionAsSequence null - - val replacement = when (match) { - MethodCall.NetworkCountryIso -> networkCountryIso?.lowercase() - MethodCall.NetworkOperator -> networkOperator?.toString() - MethodCall.NetworkOperatorName -> networkOperatorName - MethodCall.SimCountryIso -> simCountryIso?.lowercase() - MethodCall.SimOperator -> simOperator?.toString() - MethodCall.SimOperatorName -> simOperatorName - MethodCall.Imei, MethodCall.ImeiWithSlot, MethodCall.DeviceId, MethodCall.DeviceIdWithSlot -> computedImei - MethodCall.Meid, MethodCall.MeidWithSlot -> computedMeid - MethodCall.SubscriberId, MethodCall.SubscriberIdWithSlot -> computedImsi - MethodCall.SimSerialNumber, MethodCall.SimSerialNumberWithSlot -> computedIccid - MethodCall.Line1Number, MethodCall.Line1NumberWithSlot -> computedPhone - } - replacement?.let { instructionIndex to it } - }, - transform = ::transformMethodCall - ) - } - }, + replacement?.let { instructionIndex to it } + }, + transform = ::transformMethodCall, + ), ) } -private fun transformMethodCall(mutableMethod: MutableMethod, entry: Pair) { - val (index, value) = entry - val nextInstr = mutableMethod.getInstruction(index + 1) +private fun transformMethodCall( + mutableMethod: MutableMethod, + entry: Pair, +) { + val (instructionIndex, methodCallValue) = entry - if (nextInstr.opcode.name != "move-result-object") { - mutableMethod.replaceInstruction(index, "nop") - return - } + // Get the register which would have contained the return value + val register = mutableMethod.getInstruction(instructionIndex + 1).registerA - val register = (nextInstr as OneRegisterInstruction).registerA - mutableMethod.replaceInstruction(index, "const-string v$register, \"$value\"") - mutableMethod.replaceInstruction(index + 1, "nop") + // Replace the move-result instruction with our fake value + mutableMethod.replaceInstruction( + instructionIndex + 1, + "const-string v$register, \"$methodCallValue\"", + ) } private enum class MethodCall( @@ -229,100 +154,4 @@ private enum class MethodCall( "Ljava/lang/String;", ), ), - Imei( - ImmutableMethodReference( - "Landroid/telephony/TelephonyManager;", - "getImei", - emptyList(), - "Ljava/lang/String;" - ), - ), - ImeiWithSlot( - ImmutableMethodReference( - "Landroid/telephony/TelephonyManager;", - "getImei", - listOf("I"), - "Ljava/lang/String;" - ), - ), - DeviceId( - ImmutableMethodReference( - "Landroid/telephony/TelephonyManager;", - "getDeviceId", - emptyList(), - "Ljava/lang/String;" - ), - ), - DeviceIdWithSlot( - ImmutableMethodReference( - "Landroid/telephony/TelephonyManager;", - "getDeviceId", - listOf("I"), - "Ljava/lang/String;" - ), - ), - Meid( - ImmutableMethodReference( - "Landroid/telephony/TelephonyManager;", - "getMeid", - emptyList(), - "Ljava/lang/String;" - ), - ), - MeidWithSlot( - ImmutableMethodReference( - "Landroid/telephony/TelephonyManager;", - "getMeid", - listOf("I"), - "Ljava/lang/String;" - ), - ), - SubscriberId( - ImmutableMethodReference( - "Landroid/telephony/TelephonyManager;", - "getSubscriberId", - emptyList(), - "Ljava/lang/String;" - ) - ), - SubscriberIdWithSlot( - ImmutableMethodReference( - "Landroid/telephony/TelephonyManager;", - "getSubscriberId", - listOf("I"), - "Ljava/lang/String;" - ) - ), - SimSerialNumber( - ImmutableMethodReference( - "Landroid/telephony/TelephonyManager;", - "getSimSerialNumber", - emptyList(), - "Ljava/lang/String;" - ) - ), - SimSerialNumberWithSlot( - ImmutableMethodReference( - "Landroid/telephony/TelephonyManager;", - "getSimSerialNumber", - listOf("I"), - "Ljava/lang/String;" - ) - ), - Line1Number( - ImmutableMethodReference( - "Landroid/telephony/TelephonyManager;", - "getLine1Number", - emptyList(), - "Ljava/lang/String;" - ) - ), - Line1NumberWithSlot( - ImmutableMethodReference( - "Landroid/telephony/TelephonyManager;", - "getLine1Number", - listOf("I"), - "Ljava/lang/String;" - ) - ) } diff --git a/patches/src/main/kotlin/app/revanced/patches/all/misc/connectivity/wifi/spoof/SpoofWifiPatch.kt b/patches/src/main/kotlin/app/revanced/patches/all/misc/connectivity/wifi/spoof/SpoofWifiPatch.kt index e00f37feaf..1666a27a8d 100644 --- a/patches/src/main/kotlin/app/revanced/patches/all/misc/connectivity/wifi/spoof/SpoofWifiPatch.kt +++ b/patches/src/main/kotlin/app/revanced/patches/all/misc/connectivity/wifi/spoof/SpoofWifiPatch.kt @@ -3,7 +3,7 @@ package app.revanced.patches.all.misc.connectivity.wifi.spoof import app.revanced.patcher.patch.bytecodePatch import app.revanced.patches.all.misc.transformation.IMethodCall import app.revanced.patches.all.misc.transformation.filterMapInstruction35c -import app.revanced.util.forEachInstructionAsSequence +import app.revanced.patches.all.misc.transformation.transformInstructionsPatch private const val EXTENSION_CLASS_DESCRIPTOR_PREFIX = "Lapp/revanced/extension/all/misc/connectivity/wifi/spoof/SpoofWifiPatch" @@ -19,32 +19,29 @@ val spoofWiFiConnectionPatch = bytecodePatch( extendWith("extensions/all/misc/connectivity/wifi/spoof/spoof-wifi.rve") dependsOn( - bytecodePatch { - apply { - forEachInstructionAsSequence( - match = { classDef, _, instruction, instructionIndex -> - filterMapInstruction35c( - EXTENSION_CLASS_DESCRIPTOR_PREFIX, - classDef, - instruction, - instructionIndex, - ) - }, - transform = { method, entry -> - val (methodType, instruction, instructionIndex) = entry - methodType.replaceInvokeVirtualWithExtension( - EXTENSION_CLASS_DESCRIPTOR, - method, - instruction, - instructionIndex, - ) - }) - } - }, + transformInstructionsPatch( + filterMap = { classDef, _, instruction, instructionIndex -> + filterMapInstruction35c( + EXTENSION_CLASS_DESCRIPTOR_PREFIX, + classDef, + instruction, + instructionIndex, + ) + }, + transform = { method, entry -> + val (methodType, instruction, instructionIndex) = entry + methodType.replaceInvokeVirtualWithExtension( + EXTENSION_CLASS_DESCRIPTOR, + method, + instruction, + instructionIndex, + ) + }, + ), ) } -// Information about method calls we want to replace. +// Information about method calls we want to replace @Suppress("unused") private enum class MethodCall( override val definedClassName: String, @@ -92,13 +89,13 @@ private enum class MethodCall( "Landroid/net/NetworkInfo;", "getState", arrayOf(), - $$"Landroid/net/NetworkInfo$State;", + "Landroid/net/NetworkInfo\$State;", ), GetDetailedState( "Landroid/net/NetworkInfo;", "getDetailedState", arrayOf(), - $$"Landroid/net/NetworkInfo$DetailedState;", + "Landroid/net/NetworkInfo\$DetailedState;", ), IsActiveNetworkMetered( "Landroid/net/ConnectivityManager;", @@ -135,7 +132,7 @@ private enum class MethodCall( "registerBestMatchingNetworkCallback", arrayOf( "Landroid/net/NetworkRequest;", - $$"Landroid/net/ConnectivityManager$NetworkCallback;", + "Landroid/net/ConnectivityManager\$NetworkCallback;", "Landroid/os/Handler;", ), "V", @@ -143,19 +140,19 @@ private enum class MethodCall( RegisterDefaultNetworkCallback1( "Landroid/net/ConnectivityManager;", "registerDefaultNetworkCallback", - arrayOf($$"Landroid/net/ConnectivityManager$NetworkCallback;"), + arrayOf("Landroid/net/ConnectivityManager\$NetworkCallback;"), "V", ), RegisterDefaultNetworkCallback2( "Landroid/net/ConnectivityManager;", "registerDefaultNetworkCallback", - arrayOf($$"Landroid/net/ConnectivityManager$NetworkCallback;", "Landroid/os/Handler;"), + arrayOf("Landroid/net/ConnectivityManager\$NetworkCallback;", "Landroid/os/Handler;"), "V", ), RegisterNetworkCallback1( "Landroid/net/ConnectivityManager;", "registerNetworkCallback", - arrayOf("Landroid/net/NetworkRequest;", $$"Landroid/net/ConnectivityManager$NetworkCallback;"), + arrayOf("Landroid/net/NetworkRequest;", "Landroid/net/ConnectivityManager\$NetworkCallback;"), "V", ), RegisterNetworkCallback2( @@ -169,7 +166,7 @@ private enum class MethodCall( "registerNetworkCallback", arrayOf( "Landroid/net/NetworkRequest;", - $$"Landroid/net/ConnectivityManager$NetworkCallback;", + "Landroid/net/ConnectivityManager\$NetworkCallback;", "Landroid/os/Handler;", ), "V", @@ -177,13 +174,13 @@ private enum class MethodCall( RequestNetwork1( "Landroid/net/ConnectivityManager;", "requestNetwork", - arrayOf("Landroid/net/NetworkRequest;", $$"Landroid/net/ConnectivityManager$NetworkCallback;"), + arrayOf("Landroid/net/NetworkRequest;", "Landroid/net/ConnectivityManager\$NetworkCallback;"), "V", ), RequestNetwork2( "Landroid/net/ConnectivityManager;", "requestNetwork", - arrayOf("Landroid/net/NetworkRequest;", $$"Landroid/net/ConnectivityManager$NetworkCallback;", "I"), + arrayOf("Landroid/net/NetworkRequest;", "Landroid/net/ConnectivityManager\$NetworkCallback;", "I"), "V", ), RequestNetwork3( @@ -191,7 +188,7 @@ private enum class MethodCall( "requestNetwork", arrayOf( "Landroid/net/NetworkRequest;", - $$"Landroid/net/ConnectivityManager$NetworkCallback;", + "Landroid/net/ConnectivityManager\$NetworkCallback;", "Landroid/os/Handler;", ), "V", @@ -207,7 +204,7 @@ private enum class MethodCall( "requestNetwork", arrayOf( "Landroid/net/NetworkRequest;", - $$"Landroid/net/ConnectivityManager$NetworkCallback;", + "Landroid/net/ConnectivityManager\$NetworkCallback;", "Landroid/os/Handler;", "I", ), @@ -216,7 +213,7 @@ private enum class MethodCall( UnregisterNetworkCallback1( "Landroid/net/ConnectivityManager;", "unregisterNetworkCallback", - arrayOf($$"Landroid/net/ConnectivityManager$NetworkCallback;"), + arrayOf("Landroid/net/ConnectivityManager\$NetworkCallback;"), "V", ), UnregisterNetworkCallback2( diff --git a/patches/src/main/kotlin/app/revanced/patches/all/misc/packagename/ChangePackageNamePatch.kt b/patches/src/main/kotlin/app/revanced/patches/all/misc/packagename/ChangePackageNamePatch.kt index 271b2fc0fe..9fdeb04c38 100644 --- a/patches/src/main/kotlin/app/revanced/patches/all/misc/packagename/ChangePackageNamePatch.kt +++ b/patches/src/main/kotlin/app/revanced/patches/all/misc/packagename/ChangePackageNamePatch.kt @@ -1,6 +1,9 @@ package app.revanced.patches.all.misc.packagename -import app.revanced.patcher.patch.* +import app.revanced.patcher.patch.Option +import app.revanced.patcher.patch.booleanOption +import app.revanced.patcher.patch.resourcePatch +import app.revanced.patcher.patch.stringOption import app.revanced.util.asSequence import app.revanced.util.getNode import org.w3c.dom.Element @@ -13,7 +16,7 @@ private val packageNameOption = stringOption( description = "The name of the package to rename the app to.", required = true, ) { - it == "Default" || it!!.matches(Regex("^[a-z]\\w*(\\.[a-z]\\w*)+\$")) + it == "Default" || it!!.matches(Regex("^[a-zA-Z]\\w*(\\.[a-zA-Z]\\w*)+$")) } /** @@ -111,7 +114,7 @@ val changePackageNamePatch = resourcePatch( val provider = node as Element val authorities = provider.getAttribute("android:authorities") - if (!authorities.startsWith("$packageName.")) continue + if ("$packageName." !in authorities && !authorities.endsWith(packageName)) continue provider.setAttribute("android:authorities", authorities.replace(packageName, newPackageName)) } diff --git a/patches/src/main/kotlin/app/revanced/patches/all/misc/play/SpoofPlayAgeSignals.kt b/patches/src/main/kotlin/app/revanced/patches/all/misc/play/SpoofPlayAgeSignals.kt deleted file mode 100644 index ccb41b81b4..0000000000 --- a/patches/src/main/kotlin/app/revanced/patches/all/misc/play/SpoofPlayAgeSignals.kt +++ /dev/null @@ -1,138 +0,0 @@ -package app.revanced.patches.all.misc.play - -import app.revanced.patcher.extensions.addInstructions -import app.revanced.patcher.extensions.getInstruction -import app.revanced.patcher.extensions.removeInstructions -import app.revanced.patcher.patch.bytecodePatch -import app.revanced.patcher.patch.intOption -import app.revanced.patcher.patch.option -import app.revanced.util.forEachInstructionAsSequence -import app.revanced.util.getReference -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.MethodReference -import com.android.tools.smali.dexlib2.immutable.reference.ImmutableMethodReference - -@Suppress("unused") -val spoofPlayAgeSignalsPatch = bytecodePatch( - name = "Spoof Play Age Signals", - description = "Spoofs Google Play data about the user's age and verification status.", - use = false, -) { - val lowerAgeBound by intOption( - name = "Lower age bound", - description = "A positive integer.", - default = 18, - validator = { it == null || it > 0 }, - ) - - val upperAgeBound by intOption( - name = "Upper age bound", - description = "A positive integer. Must be greater than the lower age bound.", - default = Int.MAX_VALUE, - validator = { it == null || it > lowerAgeBound!! }, - ) - - val userStatus by intOption( - name = "User status", - description = "An integer representing the user status.", - default = UserStatus.VERIFIED.value, - values = UserStatus.entries.associate { it.name to it.value }, - ) - - apply { - forEachInstructionAsSequence(match = { classDef, _, instruction, instructionIndex -> - // Avoid patching the library itself. - if (classDef.type.startsWith("Lcom/google/android/play/agesignals/")) return@forEachInstructionAsSequence null - - // Keep method calls only. - val reference = instruction.getReference() - ?: return@forEachInstructionAsSequence null - - val match = MethodCall.entries.firstOrNull { - reference == it.reference - } ?: return@forEachInstructionAsSequence null - - val replacement = when (match) { - MethodCall.AgeLower -> lowerAgeBound!! - MethodCall.AgeUpper -> upperAgeBound!! - MethodCall.UserStatus -> userStatus!! - } - - replacement.let { instructionIndex to it } - }, transform = { method, entry -> - val (instructionIndex, replacement) = entry - - // Get the register which would have contained the return value. - val register = method.getInstruction(instructionIndex + 1).registerA - - // Replace the call instructions with the spoofed value. - method.removeInstructions(instructionIndex, 2) - method.addInstructions( - instructionIndex, - """ - const v$register, $replacement - invoke-static { v$register }, Ljava/lang/Integer;->valueOf(I)Ljava/lang/Integer; - move-result-object v$register - """.trimIndent(), - ) - }) - } -} - -/** - * See [AgeSignalsResult](https://developer.android.com/google/play/age-signals/reference/com/google/android/play/agesignals/AgeSignalsResult). - */ -private enum class MethodCall( - val reference: MethodReference, -) { - AgeLower( - ImmutableMethodReference( - "Lcom/google/android/play/agesignals/AgeSignalsResult;", - "ageLower", - emptyList(), - "Ljava/lang/Integer;", - ), - ), - AgeUpper( - ImmutableMethodReference( - "Lcom/google/android/play/agesignals/AgeSignalsResult;", - "ageUpper", - emptyList(), - "Ljava/lang/Integer;", - ), - ), - UserStatus( - ImmutableMethodReference( - "Lcom/google/android/play/agesignals/AgeSignalsResult;", - "userStatus", - emptyList(), - "Ljava/lang/Integer;", - ), - ), -} - -/** - * All possible user verification statuses. - * - * See [AgeSignalsVerificationStatus](https://developer.android.com/google/play/age-signals/reference/com/google/android/play/agesignals/model/AgeSignalsVerificationStatus). - */ -private enum class UserStatus(val value: Int) { - /** The user provided their age, but it hasn't been verified yet. */ - DECLARED(5), - - /** The user is 18+. */ - VERIFIED(0), - - /** The user's guardian has set the age for him. */ - SUPERVISED(1), - - /** The user's guardian hasn't approved the significant changes yet. */ - SUPERVISED_APPROVAL_PENDING(2), - - /** The user's guardian has denied approval for one or more pending significant changes. */ - SUPERVISED_APPROVAL_DENIED(3), - - /** The user is not verified or supervised. */ - UNKNOWN(4), -} diff --git a/patches/src/main/kotlin/app/revanced/patches/all/misc/play/DisablePlayIntegrity.kt b/patches/src/main/kotlin/app/revanced/patches/all/misc/playintegrity/DisablePlayIntegrity.kt similarity index 95% rename from patches/src/main/kotlin/app/revanced/patches/all/misc/play/DisablePlayIntegrity.kt rename to patches/src/main/kotlin/app/revanced/patches/all/misc/playintegrity/DisablePlayIntegrity.kt index dd5dad79e4..12461fc40a 100644 --- a/patches/src/main/kotlin/app/revanced/patches/all/misc/play/DisablePlayIntegrity.kt +++ b/patches/src/main/kotlin/app/revanced/patches/all/misc/playintegrity/DisablePlayIntegrity.kt @@ -1,4 +1,4 @@ -package app.revanced.patches.all.misc.play +package app.revanced.patches.all.misc.playintegrity import app.revanced.patcher.extensions.replaceInstruction import app.revanced.patcher.patch.bytecodePatch @@ -9,7 +9,7 @@ import com.android.tools.smali.dexlib2.iface.reference.MethodReference import com.android.tools.smali.dexlib2.immutable.reference.ImmutableMethodReference import com.android.tools.smali.dexlib2.util.MethodUtil -private const val EXTENSION_CLASS_DESCRIPTOR = "Lapp/revanced/extension/play/DisablePlayIntegrityPatch;" +private const val EXTENSION_CLASS_DESCRIPTOR = "Lapp/revanced/extension/playintegrity/DisablePlayIntegrityPatch;" private val CONTEXT_BIND_SERVICE_METHOD_REFERENCE = ImmutableMethodReference( "Landroid/content/Context;", diff --git a/patches/src/main/kotlin/app/revanced/patches/all/misc/spoof/SpoofKeystoreSecurityLevelPatch.kt b/patches/src/main/kotlin/app/revanced/patches/all/misc/spoof/SpoofKeystoreSecurityLevelPatch.kt deleted file mode 100644 index b45cfb4e0d..0000000000 --- a/patches/src/main/kotlin/app/revanced/patches/all/misc/spoof/SpoofKeystoreSecurityLevelPatch.kt +++ /dev/null @@ -1,28 +0,0 @@ -package app.revanced.patches.all.misc.spoof - -import app.revanced.patcher.extensions.replaceInstructions -import app.revanced.patcher.patch.bytecodePatch -import app.revanced.util.forEachInstructionAsSequence - -@Suppress("unused") -val spoofKeystoreSecurityLevelPatch = bytecodePatch( - name = "Spoof keystore security level", - description = "Forces apps to see Keymaster and Attestation security levels as 'StrongBox' (Level 2).", - use = false -) { - apply { - forEachInstructionAsSequence( - match = { _, method, _, _ -> - // Match methods by comparing the current method to a reference criteria. - val name = method.name.lowercase() - if (name.contains("securitylevel") && method.returnType == "I") method else null - }, - transform = { mutableMethod, _ -> - // Ensure the method has an implementation before replacing. - if (mutableMethod.implementation?.instructions?.iterator()?.hasNext() == true) { - mutableMethod.replaceInstructions(0, "const/4 v0, 0x2\nreturn v0") - } - } - ) - } -} \ No newline at end of file diff --git a/patches/src/main/kotlin/app/revanced/patches/all/misc/spoof/SpoofRootOfTrustPatch.kt b/patches/src/main/kotlin/app/revanced/patches/all/misc/spoof/SpoofRootOfTrustPatch.kt deleted file mode 100644 index 6177b7e317..0000000000 --- a/patches/src/main/kotlin/app/revanced/patches/all/misc/spoof/SpoofRootOfTrustPatch.kt +++ /dev/null @@ -1,70 +0,0 @@ -package app.revanced.patches.all.misc.spoof - -import app.revanced.patcher.extensions.replaceInstructions -import app.revanced.patcher.patch.bytecodePatch -import app.revanced.util.forEachInstructionAsSequence -import com.android.tools.smali.dexlib2.iface.reference.MethodReference -import com.android.tools.smali.dexlib2.immutable.reference.ImmutableMethodReference -import com.android.tools.smali.dexlib2.util.MethodUtil - -@Suppress("unused") -val spoofRootOfTrustPatch = bytecodePatch( - name = "Spoof root of trust", - description = "Spoofs device integrity states (Locked Bootloader, Verified OS) for apps that perform local certificate attestation.", - use = false -) { - apply { - forEachInstructionAsSequence( - match = { _, method, _, _ -> - MethodCall.entries.firstOrNull { MethodUtil.methodSignaturesMatch(method, it.reference) } - }, - transform = { mutableMethod, methodCall -> - if (mutableMethod.implementation?.instructions?.iterator()?.hasNext() == true) { - mutableMethod.replaceInstructions(0, methodCall.replacementInstructions) - } - } - ) - } -} - -private enum class MethodCall( - val reference: MethodReference, - val replacementInstructions: String, -) { - IsDeviceLockedRootOfTrust( - ImmutableMethodReference( - "LRootOfTrust;", - "isDeviceLocked", - emptyList(), - "Z" - ), - "const/4 v0, 0x1\nreturn v0", - ), - GetVerifiedBootStateRootOfTrust( - ImmutableMethodReference( - "LRootOfTrust;", - "getVerifiedBootState", - emptyList(), - "I" - ), - "const/4 v0, 0x0\nreturn v0", - ), - IsDeviceLockedAttestation( - ImmutableMethodReference( - "LAttestation;", - "isDeviceLocked", - emptyList(), - "Z" - ), - "const/4 v0, 0x1\nreturn v0", - ), - GetVerifiedBootStateAttestation( - ImmutableMethodReference( - "LAttestation;", - "getVerifiedBootState", - emptyList(), - "I" - ), - "const/4 v0, 0x0\nreturn v0", - ), -} \ No newline at end of file diff --git a/patches/src/main/kotlin/app/revanced/patches/instagram/story/locationsticker/EnableLocationStickerRedesignPatch.kt b/patches/src/main/kotlin/app/revanced/patches/instagram/story/locationsticker/EnableLocationStickerRedesignPatch.kt deleted file mode 100644 index 443d60d155..0000000000 --- a/patches/src/main/kotlin/app/revanced/patches/instagram/story/locationsticker/EnableLocationStickerRedesignPatch.kt +++ /dev/null @@ -1,20 +0,0 @@ -package app.revanced.patches.instagram.story.locationsticker - -import app.revanced.patcher.patch.bytecodePatch -import app.revanced.util.returnEarly - -@Suppress("unused") -val enableLocationStickerRedesignPatch = bytecodePatch( - name = "Enable location sticker redesign", - description = "Unlocks the redesigned location sticker with additional style options.", - use = false, -) { - compatibleWith("com.instagram.android") - - apply { - // The gate method reads a MobileConfig boolean flag and returns it directly. - // Returning early with true bypasses the flag check entirely, - // enabling the redesigned sticker styles regardless of server configuration. - locationStickerRedesignGateMethodMatch.method.returnEarly(true) - } -} diff --git a/patches/src/main/kotlin/app/revanced/patches/instagram/story/locationsticker/Fingerprints.kt b/patches/src/main/kotlin/app/revanced/patches/instagram/story/locationsticker/Fingerprints.kt deleted file mode 100644 index 0972d692c9..0000000000 --- a/patches/src/main/kotlin/app/revanced/patches/instagram/story/locationsticker/Fingerprints.kt +++ /dev/null @@ -1,16 +0,0 @@ -package app.revanced.patches.instagram.story.locationsticker - -import app.revanced.patcher.composingFirstMethod -import app.revanced.patcher.instructions -import app.revanced.patcher.invoke -import app.revanced.patcher.patch.BytecodePatchContext - -// MobileConfig boolean key that gates the redesigned location sticker styles. -// The method containing this constant reads the flag and returns it directly, -// making it the sole control point for the feature. The key is stable across -// app updates as MobileConfig keys are server-assigned constants. -private const val LOCATION_STICKER_REDESIGN_CONFIG_KEY = 0x8105a100041e0dL - -internal val BytecodePatchContext.locationStickerRedesignGateMethodMatch by composingFirstMethod { - instructions(LOCATION_STICKER_REDESIGN_CONFIG_KEY()) -} diff --git a/patches/src/main/kotlin/app/revanced/patches/shared/layout/branding/BaseCustomBrandingPatch.kt b/patches/src/main/kotlin/app/revanced/patches/shared/layout/branding/BaseCustomBrandingPatch.kt index 9a583e2c7a..35baad31cf 100644 --- a/patches/src/main/kotlin/app/revanced/patches/shared/layout/branding/BaseCustomBrandingPatch.kt +++ b/patches/src/main/kotlin/app/revanced/patches/shared/layout/branding/BaseCustomBrandingPatch.kt @@ -3,6 +3,7 @@ package app.revanced.patches.shared.layout.branding import app.revanced.com.android.tools.smali.dexlib2.mutable.MutableMethod import app.revanced.patcher.extensions.addInstruction import app.revanced.patcher.extensions.getInstruction +import app.revanced.patcher.firstImmutableClassDef import app.revanced.patcher.patch.* import app.revanced.patches.all.misc.packagename.setOrGetFallbackPackageName import app.revanced.patches.all.misc.resources.addResources @@ -125,14 +126,14 @@ internal fun baseCustomBrandingPatch( val getBuilderIndex = if (isYouTubeMusic) { // YT Music the field is not a plain object type. indexOfFirstInstructionOrThrow { - getReference()?.type == $$"Landroid/app/Notification$Builder;" + getReference()?.type == "Landroid/app/Notification\$Builder;" } } else { // Find the field name of the notification builder. Field is an Object type. val builderCastIndex = indexOfFirstInstructionOrThrow { val reference = getReference() opcode == Opcode.CHECK_CAST && - reference?.type == $$"Landroid/app/Notification$Builder;" + reference?.type == "Landroid/app/Notification\$Builder;" } indexOfFirstInstructionReversedOrThrow(builderCastIndex) { getReference()?.type == "Ljava/lang/Object;" @@ -147,11 +148,11 @@ internal fun baseCustomBrandingPatch( ).forEach { index -> addInstructionsAtControlFlowLabel( index, - $$""" + """ move-object/from16 v0, p0 - iget-object v0, v0, $$builderFieldName - check-cast v0, Landroid/app/Notification$Builder; - invoke-static { v0 }, $$EXTENSION_CLASS_DESCRIPTOR->setNotificationIcon(Landroid/app/Notification$Builder;)V + iget-object v0, v0, $builderFieldName + check-cast v0, Landroid/app/Notification${'$'}Builder; + invoke-static { v0 }, $EXTENSION_CLASS_DESCRIPTOR->setNotificationIcon(Landroid/app/Notification${'$'}Builder;)V """, ) } @@ -161,37 +162,16 @@ internal fun baseCustomBrandingPatch( ) afterDependents { - val useCustomName = customName != null - val useCustomIcon = customIcon != null - val isRootInstall = setOrGetFallbackPackageName(originalAppPackageName) == originalAppPackageName - // Can only check if app is root installation by checking if change package name patch is in use. // and can only do that in the afterDependents block here. // The UI preferences cannot be selectively added here, because the settings afterDependents block // may have already run and the settings are already wrote to file. // Instead, show a warning if any patch option was used (A rooted device launcher ignores the manifest changes), // and the non-functional in-app settings are removed on app startup by extension code. - if (isRootInstall && (useCustomName || useCustomIcon)) { - Logger.getLogger(this::class.java.name).warning( - "Custom branding does not work with root installation. No changes applied." - ) - } - - if (!isRootInstall || useCustomName) { - document("AndroidManifest.xml").use { document -> - val application = document.getElementsByTagName("application").item(0) as Element - application.setAttribute( - "android:label", - if (useCustomName) { - // Use custom name everywhere. - customName - } else { - // The YT application name can appear in some places alongside the system - // YouTube app, such as the settings app list and in the "open with" file picker. - // Because the YouTube app cannot be completely uninstalled and only disabled, - // use a custom name for this situation to disambiguate which app is which. - "@string/revanced_custom_branding_name_entry_2" - } + if (customName != null || customIcon != null) { + if (setOrGetFallbackPackageName(originalAppPackageName) == originalAppPackageName) { + Logger.getLogger(this::class.java.name).warning( + "Custom branding does not work with root installation. No changes applied.", ) } } @@ -332,19 +312,16 @@ internal fun baseCustomBrandingPatch( activityAliasNameWithIntents, ).childNodes - // If user provides a custom icon, then change the application icon ('static' icon) - // which shows as the push notification for some devices, in the app settings, - // and as the icon for the apk before installing. - // This icon cannot be dynamically selected and this change must only be done if the - // user provides an icon otherwise there is no way to restore the original YouTube icon. - if (useCustomIcon) { - application.setAttribute( - "android:icon", - "@mipmap/revanced_launcher_custom" - ) - } + // The YT application name can appear in some places alongside the system + // YouTube app, such as the settings app list and in the "open with" file picker. + // Because the YouTube app cannot be completely uninstalled and only disabled, + // use a custom name for this situation to disambiguate which app is which. + application.setAttribute( + "android:label", + "@string/revanced_custom_branding_name_entry_2", + ) - val enabledNameIndex = if (useCustomName) numberOfPresetAppNames else 2 // 1 indexing. + val enabledNameIndex = if (useCustomName) numberOfPresetAppNames else 1 // 1 indexing. val enabledIconIndex = if (useCustomIcon) iconStyleNames.size else 0 // 0 indexing. for (appNameIndex in 1..numberOfPresetAppNames) { @@ -359,7 +336,7 @@ internal fun baseCustomBrandingPatch( iconMipmapName = originalLauncherIconName, appNameIndex = appNameIndex, useCustomName = useCustomNameLabel, - enabled = false, + enabled = (appNameIndex == 1), intentFilters, ), ) diff --git a/patches/src/main/kotlin/app/revanced/patches/shared/misc/spoof/SpoofVideoStreamsPatch.kt b/patches/src/main/kotlin/app/revanced/patches/shared/misc/spoof/SpoofVideoStreamsPatch.kt index 1c918b8104..7662718fc8 100644 --- a/patches/src/main/kotlin/app/revanced/patches/shared/misc/spoof/SpoofVideoStreamsPatch.kt +++ b/patches/src/main/kotlin/app/revanced/patches/shared/misc/spoof/SpoofVideoStreamsPatch.kt @@ -169,13 +169,13 @@ internal fun spoofVideoStreamsPatch( if-eqz v2, :disabled # Get streaming data. - invoke-static { v2 }, $EXTENSION_CLASS_DESCRIPTOR->getStreamingData(Ljava/lang/String;)[B + invoke-static { v2 }, $EXTENSION_CLASS_DESCRIPTOR->getStreamingData(Ljava/lang/String;)Ljava/nio/ByteBuffer; move-result-object v3 if-eqz v3, :disabled # Parse streaming data. sget-object v4, $playerProtoClass->a:$playerProtoClass - invoke-static { v4, v3 }, $protobufClass->parseFrom(${protobufClass}[B)$protobufClass + invoke-static { v4, v3 }, $protobufClass->parseFrom(${protobufClass}Ljava/nio/ByteBuffer;)$protobufClass move-result-object v5 check-cast v5, $playerProtoClass diff --git a/patches/src/main/kotlin/app/revanced/patches/youtube/misc/spoof/SpoofVideoStreamsPatch.kt b/patches/src/main/kotlin/app/revanced/patches/youtube/misc/spoof/SpoofVideoStreamsPatch.kt index 05a8f57caf..980e64888e 100644 --- a/patches/src/main/kotlin/app/revanced/patches/youtube/misc/spoof/SpoofVideoStreamsPatch.kt +++ b/patches/src/main/kotlin/app/revanced/patches/youtube/misc/spoof/SpoofVideoStreamsPatch.kt @@ -63,10 +63,10 @@ val spoofVideoStreamsPatch = spoofVideoStreamsPatch( // Requires a key and title but the actual text is chosen at runtime. key = "revanced_spoof_video_streams_about", summaryKey = null, - tag = "app.revanced.extension.youtube.settings.preference.SpoofVideoStreamsSideEffectsPreference", + tag = "app.revanced.extension.youtube.settings.preference.SpoofStreamingDataSideEffectsPreference", ), SwitchPreference("revanced_spoof_video_streams_av1"), - SwitchPreference("revanced_spoof_video_streams_stats_for_nerds"), + SwitchPreference("revanced_spoof_streaming_data_stats_for_nerds"), ), ), ) diff --git a/patches/src/main/resources/addresources/values-cs-rCZ/strings.xml b/patches/src/main/resources/addresources/values-cs-rCZ/strings.xml index d1ef713d1b..a4105694a5 100644 --- a/patches/src/main/resources/addresources/values-cs-rCZ/strings.xml +++ b/patches/src/main/resources/addresources/values-cs-rCZ/strings.xml @@ -25,7 +25,6 @@ Second \"item\" text" Vlastní Ikona aplikace Původní - ReVanced ReVanced minimální ReVanced škálované @@ -50,7 +49,6 @@ Second \"item\" text" Nastavení - ReVanced Opravdu chcete pokračovat? Uložit Výchozí @@ -133,8 +131,6 @@ Klepněte na tlačítko Pokračovat a povolte změny optimalizace." Falšovat video streamy Falšovat video streamy klienta, aby se předešlo problémům s přehráváním. - Falšovat video streamy - Falšovat video streamy klienta, aby se předešlo problémům s přehráváním Napodobovat video streamy "Video streamy jsou maskovány @@ -222,12 +218,9 @@ Povolením této možnosti se však budou zaznamenávat i některá uživatelsk Přísun Obecné Přehrávač - Shorts Lišta Ovládání gesty - Return YouTube Dislike Různé - Video Obnovit staré menu nastavení Staré menu nastavení se zobrazují Staré menu nastavení se nezobrazují @@ -1155,7 +1148,6 @@ Nastavení → Přehrávání → Automatické přehrávání dalšího videa"Načtěte video znovu, abyste hlasovali pomocí Return YouTube Dislike Skryto vlastníkem - Return YouTube Dislike Nelíbí se se zobrazují Nelíbí se se nezobrazují Zobrazit nelíbí se v Shorts @@ -1176,7 +1168,6 @@ Omezení: Počty „Nelíbí se mi“ se nemusí zobrazit v anonymním režimu"< Zobrazit toast, pokud API není dostupné Toast se zobrazí, pokud Return YouTube Dislike není dostupný Toast se nezobrazí, pokud Return YouTube Dislike není dostupný - ReturnYouTubeDislike.com Data jsou poskytována API Return YouTube Dislike. Klepnutím se dozvíte více Statistiky API ReturnYouTubeDislike tohoto zařízení @@ -1197,7 +1188,6 @@ Omezení: Počty „Nelíbí se mi“ se nemusí zobrazit v anonymním režimu"< %d milisekund - SponsorBlock Povolit SponsorBlock SponsorBlock je systém s participací komunity pro přeskakování otravných částí videí na YouTube Vzhled @@ -1406,14 +1396,12 @@ Jste připraveni k odeslání?" Průhlednost: Barva: O aplikaci - sponsor.ajay.app Data poskytuje rozhraní API SponsorBlock. Klepněte zde, abyste se dozvěděli více a zobrazili si soubory ke stažení pro další platformy Rozvržení formuláře Výchozí Telefon - Tablet Automobilový "Změny zahrnují: @@ -1457,7 +1445,6 @@ Pokud bude později vypnuta, doporučujeme vymazat data aplikace, aby se zabrán Playlisty Hledat Nakupování - Shorts Sport Odběry Trendy @@ -1496,7 +1483,6 @@ Omezení: Použití tlačítka zpět na panelu nástrojů nemusí fungovat"Vypnuto Výchozí Minimální - Tablet Moderní 1 Moderní 2 Moderní 3 @@ -1523,11 +1509,6 @@ Omezení: Použití tlačítka zpět na panelu nástrojů nemusí fungovat"Skrýt tlačítka překrytí Tlačítka překrytí jsou skrytá Tlačítka překrytí jsou zobrazena - Skrýt tlačítka rozbalit a zavřít - "Tlačítka jsou skrytá - -Přejetím roztáhnete nebo zavřete" - Tlačítka rozbalit a zavřít jsou zobrazena Skrýt podtexty Podtexty jsou skryty Podtexty jsou zobrazeny @@ -1558,17 +1539,11 @@ Přejetím roztáhnete nebo zavřete" Zvýrazněná barva posuvníku Neplatná hodnota barvy posuvníku - - YouTube ReVanced - YT ReVanced - YT - + Logo záhlaví Výchozí Běžné - Prémium - ReVanced ReVanced minimální Vlastní @@ -1593,7 +1568,6 @@ Povolení této funkce může opravit chybějící obrázky, které jsou v někt DeArrow & Původní náhledy DeArrow & Zachycení snímků Zachycení snímků - DeArrow "DeArrow poskytuje miniatury videí YouTube z crowdsourcingu. Tyto miniatury jsou často relevantnější než ty, které poskytuje YouTube Pokud je tato funkce povolena, budou adresy URL videí odeslány na server API a nebudou odesílány žádné další údaje. Pokud video nemá miniatury DeArrow, zobrazí se originální nebo statické snímky @@ -1637,11 +1611,7 @@ Klepnutím sem se dozvíte více o DeArrow" Smyčka videa je zapnuta Smyčka videa je vypnuta - - Pozastavit při přerušení zvuku - Přehrávání se pozastaví, když se přehrává jiný zvuk (např. navigace) - Sníží hlasitost při přehrávání ostatních zvuků - + Napodobovat rozměry zařízení "Rozměry zařízení jsou zfalšovány @@ -1796,11 +1766,7 @@ Přehrávání videa s AV1 se může sekat nebo vypadávat snímky." - - YT Music ReVanced - Music ReVanced - Hudba - + O aplikaci @@ -1927,7 +1893,6 @@ Přehrávání videa s AV1 se může sekat nebo vypadávat snímky." O ReVanced Blokování reklam Nastavení blokování reklam - Chat Nastavení chatu Různé Různé nastavení diff --git a/patches/src/main/resources/addresources/values-de-rDE/strings.xml b/patches/src/main/resources/addresources/values-de-rDE/strings.xml index abd7622a51..a7d6425153 100644 --- a/patches/src/main/resources/addresources/values-de-rDE/strings.xml +++ b/patches/src/main/resources/addresources/values-de-rDE/strings.xml @@ -35,14 +35,14 @@ Second \"item\" text" Ignorieren <h5>Diese App wurde offenbar nicht von Ihnen gepatcht.</h5><br>Diese App funktioniert möglicherweise nicht richtig, <b>könnte schädlich oder sogar gefährlich in der Verwendung sein</b>.< br><br>Diese Prüfungen deuten darauf hin, dass diese App vorab gepatcht wurde oder von jemandem bezogen wurde:<br><br><small>%1$s</small><br>Es wird dringend empfohlen, <b>diese App zu deinstallieren und selbst zu patchen</b> um sicherzustellen, dass Sie eine validierte und sichere App verwenden.<p><br>Wenn Sie diese Warnung ignorieren, wird sie nur zweimal angezeigt. Auf einem anderen Gerät gepatcht - Nicht von ReVanced Manager installiert + Nicht durch ReVanced Manager installiert Vor mehr als 10 Minuten gepatcht Vor %s Tagen gepatcht APK Erstellungsdatum ist beschädigt ReVanced Hinweis - Ihr Verlauf wird nicht gespeichert.<br><br>Dies wird höchstwahrscheinlich durch einen DNS-Werbeblocker oder einen Netzwerkproxy verursacht.<br><br>Um dies zu beheben, setze <b>s.youtube.com</b> auf die Whitelist oder schalten Sie alle DNS-Blocker und Proxys aus. + Ihr Verlauf wird nicht gespeichert.<br><br>Dies wird höchstwahrscheinlich durch einen DNS-Werbeblocker oder einen Netzwerkproxy verursacht.<br><br>Um dies zu beheben, setze <b>s.youtube.com</b> auf die Whitelist oder schalten Sie alle DNS-Blocker und Proxies aus. Nicht wieder anzeigen @@ -1598,9 +1598,7 @@ Tippen Sie hier, um mehr über DeArrow zu erfahren" Loop-Video ist aktiviert Loop-Video ist deaktiviert - - Pause bei Audiounterbrechung - + Spoof-Gerätegröße "Gerätemessungen gefälscht @@ -1755,9 +1753,7 @@ Die Videowiedergabe mit AV1 kann stottern oder Bilder überspringen." - - Musik - + Über diff --git a/patches/src/main/resources/addresources/values-el-rGR/strings.xml b/patches/src/main/resources/addresources/values-el-rGR/strings.xml index 9322f6183e..f8dcb2d550 100644 --- a/patches/src/main/resources/addresources/values-el-rGR/strings.xml +++ b/patches/src/main/resources/addresources/values-el-rGR/strings.xml @@ -238,9 +238,13 @@ Second \"item\" text" Η αναπαραγωγή παρασκηνίου είναι ενεργοποιημένη για τα Shorts - Ενότητα καταστήματος δημιουργού κάτω από την οθόνη αναπαραγωγής - Κρυμμένη - Εμφανίζεται + Ενότητα καταστήματος δημιουργού + Κρυμμένη + +Αφορά την ενότητα καταστήματος δημιουργού κάτω από την οθόνη αναπαραγωγής + Εμφανίζεται + +Αφορά την ενότητα καταστήματος δημιουργού κάτω από την οθόνη αναπαραγωγής Κάρτες άλμπουμ Κρυμμένες Εμφανίζονται @@ -369,9 +373,13 @@ Second \"item\" text" Συγχρονισμένες αντιδράσεις Κρυμμένες Εμφανίζονται - Τίτλος του βίντεο στην πλήρη οθόνη - Κρυμμένος - Εμφανίζεται + Τίτλος του βίντεο + Κρυμμένος + +Αφορά τον τίτλο του βίντεο στη λειτουργία πλήρους οθόνης + Εμφανίζεται + +Αφορά τον τίτλο του βίντεο στη λειτουργία πλήρους οθόνης Σύνοψη βίντεο που δημιουργήθηκε από AI Κρυμμένη Εμφανίζεται @@ -387,9 +395,13 @@ Second \"item\" text" Ενότητα «Πρόοδος μαθήματος» Κρυμμένη Εμφανίζεται - Ενότητες «Εξερευνήστε...» - Κρυμμένες. Αφορά τις ενότητες «Εξερευνήστε αυτή τη σειρά μαθημάτων» και «Εξερευνήστε το podcast» - Εμφανίζονται. Αφορά τις ενότητες «Εξερευνήστε αυτή τη σειρά μαθημάτων» και «Εξερευνήστε το podcast» + Ενότητες «Εξερεύνηση» + Κρυμμένες + +Αφορά τις ενότητες «Εξερευνήστε αυτή τη σειρά μαθημάτων» και «Εξερευνήστε το podcast» + Εμφανίζονται + +Αφορά τις ενότητες «Εξερευνήστε αυτή τη σειρά μαθημάτων» και «Εξερευνήστε το podcast» Ενότητα «Εξερευνήστε αυτή τη σειρά μαθημάτων» Κρυμμένη Εμφανίζεται @@ -1619,8 +1631,8 @@ Second \"item\" text" Εμφάνιση ανακοινώσεων ReVanced - Οι ανακοινώσεις εμφανίζονται κατά την εκκίνηση - Οι ανακοινώσεις δεν εμφανίζονται κατά την εκκίνηση + Οι ανακοινώσεις κατά την εκκίνηση εμφανίζονται + Οι ανακοινώσεις κατά την εκκίνηση δεν εμφανίζονται Εμφάνιση ανακοινώσεων κατά την εκκίνηση Αποτυχία σύνδεσης με τον πάροχο ανακοινώσεων Παράλειψη diff --git a/patches/src/main/resources/addresources/values-es-rES/strings.xml b/patches/src/main/resources/addresources/values-es-rES/strings.xml index c1a1e1d536..a8079d9ff1 100644 --- a/patches/src/main/resources/addresources/values-es-rES/strings.xml +++ b/patches/src/main/resources/addresources/values-es-rES/strings.xml @@ -1514,11 +1514,6 @@ El minireproductor se puede arrastrar fuera de la pantalla hacia la izquierda o Ocultar botones de superposición Los botones de superposición están ocultos Se muestran los botones de superposición - Ocultar botones de expandir y cerrar - "Los botones están ocultos - -Deslizar para expandir o cerrar" - Se muestran botones de expandir y cerrar Ocultar subtextos Los subtextos están ocultos Los subtextos se muestran @@ -1627,10 +1622,7 @@ Toca aquí para obtener más información sobre DeArrow" Bucle de vídeo activado Bucle de vídeo desactivado - - Pausar cuando se corte el audio - La reproducción se detiene cuando se reproduce otro audio (por ejemplo, navegación) - + Dimensiones del dispositivo "Las dimensiones del dispositivo están falsificadas diff --git a/patches/src/main/resources/addresources/values-ga-rIE/strings.xml b/patches/src/main/resources/addresources/values-ga-rIE/strings.xml index 9a78cc4c2d..569faafd9d 100644 --- a/patches/src/main/resources/addresources/values-ga-rIE/strings.xml +++ b/patches/src/main/resources/addresources/values-ga-rIE/strings.xml @@ -370,39 +370,39 @@ Mar sin féin, má chumasaíonn tú é seo, logálfar roinnt sonraí úsáideora Folaigh teideal físeáin Tá teideal an fhíseáin i bhfolach san fhorleagan seinnteora Taispeántar teideal an fhíseáin i bhforleagan an imreora - Folaigh \'Achoimre físe a ghintear le hintleacht shaorga\' + Folaigh \'Achoimre físeáin arna giniúint ag AI\' Tá an chuid achoimre físe IS-ghinte i bhfolach - Taispeántar an chuid achoimre físe a ghintear ag Intleacht Shaorga + Taispeántar an chuid achoimre físe a ghintear ag AI Folaigh Iarr Tá an rannán Iarratas i bhfolach Taispeántar an rannán Iarratas Folaigh Tréithe - Tá na rannóga \'Áiteanna Réadmhaoine\', \'Cluichí\', \'Ceol\' agus \'Daoine\' i bhfolach + Tá ailt d\'áiteanna sonracha, Cluichí, Ceol agus Daoine a luaitear i bhfolach Taispeántar ailt d\'áiteanna sonracha, Cluichí, Ceol agus Daoine a luaitear Folaigh Caibidlí Tá an chuid Caibidil i bhfolach Taispeántar alt na gcaibidlí - Folaigh \'Dul chun cinn an chúrsa\' + Folaigh ‘Dul chun cinn cúrsa’ Tá rannóg an dul chun cinn cúrsa i bhfolach Taispeántar rannóg an dul chun cinn cúrsa Folaigh Iniúchadh Tá rannóga Iniúchadh ar an gcúrsa seo agus Déan iniúchadh ar an bpodchraoladh i bhfolach - Taispeántar na rannóga Iniúchadh an chúrsa seo agus Iniúchadh na podchraoltaí + Taispeántar rannóga Iniúchadh ar an gcúrsa seo agus Déan iniúchadh ar an bpodchraoladh Folaigh ‘Déan iniúchadh ar an gcúrsa seo’ Tá rannóg Déan iniúchadh ar an gcúrsa seo i bhfolach - Taispeántar an chuid seo den chúrsa a iniúchadh + Taispeántar rannóg Déan iniúchadh ar an gcúrsa seo Folaigh \'Déan iniúchadh ar an bpodchraoladh\' Tá an chuid Déan iniúchadh ar an bpodchraoladh i bhfolach - Taispeántar an rannóg podchraoltaí a iniúchadh - Folaigh \'Iniúchadh a dhéanamh ar an bpodchraoladh\' - Tá an rannán podchraoltaí a iniúchadh i bhfolach - Taispeántar an rannóg podchraoltaí a iniúchadh + Taispeántar an chuid Déan iniúchadh ar an bpodchraoladh + Folaigh \'Déan iniúchadh ar an bpodchraoladh\' + Tá an chuid Déan iniúchadh ar an bpodchraoladh i bhfolach + Taispeántar an chuid Déan iniúchadh ar an bpodchraoladh Folaigh naisc le feiceáil Tá an chuid nasc le feiceáil i bhfolach Taispeántar an chuid nasc le feiceáil Folaigh ‘Áiteanna faoi Thrácht’ Tá rannóg na n-áiteanna faoi Thrácht i bhfolach - Taispeántar an chuid áiteanna feiceálacha + Taispeántar rannóg na n-áiteanna faoi Thrácht Folaigh físeáin le feiceáil Tá an chuid físeán le feiceáil i bhfolach Taispeántar an chuid físeán le feiceáil @@ -1847,7 +1847,7 @@ D’fhéadfadh sé go mbeadh stad nó go gcaillfí frámaí ag athsheinm físe l Folaigh nó athraigh cnaipí an bharra nascleanúna Folaigh Baile - Tá an cnaipe baile i bhfolach + Tá cnaipe Baile folaithe Taispeántar an cnaipe baile Folaigh Samplaí @@ -1875,7 +1875,7 @@ D’fhéadfadh sé go mbeadh stad nó go gcaillfí frámaí ag athsheinm físe l Folaigh an lipéad \'Faigh Music Premium\' Tá an lipéad i bhfolach - Taispeántar lipéad + Taispeántar an lipéad Folaigh an cnaipe uasghrádaithe @@ -1885,28 +1885,28 @@ D’fhéadfadh sé go mbeadh stad nó go gcaillfí frámaí ag athsheinm físe l - Blocáil fógraí fuaime - Tá fógraí fuaime blocáilte + Cuir bac ar fógraí fuaime + Cuirtear bac ar fhógraí fuaime Tá fógraí fuaime díbhlocáilte %s neamh-infheidhme, d\'fhéadfadh go dtaispeánfadh fógraí. Bain triail as seirbhís blocála fógraí a athrú sna socruithe. - Thug %s earráid ar ais, d’fhéadfadh fógraí a bheith le feiceáil. Bain triail as an tseirbhís blocála fógraí a athrú sna socruithe. + Tháinig earráid ar %s, d\'fhéadfadh go dtaispeánfadh fógraí. Bain triail as seirbhís blocála fógraí a athrú sna socruithe. Bloc ar fhógraí físe leabaithe Díchumasaithe - Seachfhreastalaí lonrúil + Proxy lonrúil Seachfhreastalaí PurpleAdBlock Bloc ar fhógraí físe - Tá bac ar fhógraí físe - Tá fógraí físe díbhlocáilte + Cuirtear bac ar fhógraí físe + Déantar fógraí físe a dhíbhlocáil Teachtaireacht scriosta Taispeáin teachtaireachtaí scriosta Ná taispeáin teachtaireachtaí scriosta - Folaigh teachtaireachtaí scriosta taobh thiar a mhilleann + Folaigh teachtaireachtaí scriosta taobh thiar a fhalsúa Taispeáin teachtaireachtaí scriosta mar théacs trasnaithe amach diff --git a/patches/src/main/resources/addresources/values-hr-rHR/strings.xml b/patches/src/main/resources/addresources/values-hr-rHR/strings.xml index 4134bb2469..69b5287bdb 100644 --- a/patches/src/main/resources/addresources/values-hr-rHR/strings.xml +++ b/patches/src/main/resources/addresources/values-hr-rHR/strings.xml @@ -29,7 +29,7 @@ Second \"item\" text" - Spremi + Sačuvaj Onemogući podebljane ikone Ikone nisu podebljane Ikone su podebljane @@ -41,7 +41,7 @@ Second \"item\" text" GmsCore Postavke povezane s GmsCoreom Provjeri ažuriranja za GmsCore - Provjera ažuriranja je omogućena + Provjera ažuriranja omogućena je Provjera ažuriranja je onemogućena Otvori postavke za GmsCore Postavke za GmsCore @@ -49,7 +49,7 @@ Second \"item\" text" MicroG GmsCore nije instaliran. Instalirajte ga. Potrebna je radnja Nije uspjela provjera ažuriranja za MicroG GmsCore - Dostupna je nova verzija (%1$s) MicroG GmsCorea. Trenutno koristite verziju %2$s. + Dostupna je nova verzija (%1$s) MicroG GmsCore. Trenutno koristite verziju %2$s. "MicroG GmsCore nema dopuštenje za rad u pozadini.\n\nSlijedite vodič \"Ne ubijaj moju aplikaciju\" za svoj telefon i primijenite upute na svoju MicroG instalaciju.\n\nOvo je potrebno da bi aplikacija radila." Otvori web-stranicu Odustani @@ -438,8 +438,8 @@ Već postoji" Sakrij opcije premium kvalitete - Premium opcije kvalitete su skrivene - Premium opcije kvalitete su prikazane + Opcije premium kvalitete su sakrivene + Opcije premium kvalitete su prikazane diff --git a/patches/src/main/resources/addresources/values-in-rID/strings.xml b/patches/src/main/resources/addresources/values-in-rID/strings.xml index 22fad62fe3..a0182ee1ba 100644 --- a/patches/src/main/resources/addresources/values-in-rID/strings.xml +++ b/patches/src/main/resources/addresources/values-in-rID/strings.xml @@ -188,7 +188,7 @@ Anda tidak akan diberi tahu tentang kejadian yang tidak terduga." Catatan protokol buffer Pencatatan debug termasuk buffer proto Pencatatan debug tidak menyertakan buffer proto - "Mengaktifkan pengaturan ini akan mencatat data tata letak tambahan, termasuk teks pada layar untuk beberapa komponen UI. + "Mengaktifkan setelan ini akan mencatat data tata letak tambahan, termasuk teks pada layar untuk beberapa komponen UI. Ini dapat membantu mengidentifikasi komponen saat membuat penyaring khusus. diff --git a/patches/src/main/resources/addresources/values-ja-rJP/strings.xml b/patches/src/main/resources/addresources/values-ja-rJP/strings.xml index 3709e8d3a4..2f418fa95f 100644 --- a/patches/src/main/resources/addresources/values-ja-rJP/strings.xml +++ b/patches/src/main/resources/addresources/values-ja-rJP/strings.xml @@ -1693,13 +1693,13 @@ Automotive レイアウト デフォルトの画質が変更された場合にトースト通知が表示されます デフォルトの画質が変更された場合にトースト通知は表示されません デフォルトの画質 (Wi-Fi) - デフォルトの画質 (モバイル) + デフォルトの画質 (携帯回線) ショートの画質の変更を保存 画質の変更はすべてのショート動画に適用されます 画質の変更は現在のショート動画にのみ適用されます デフォルトのショートの画質 (Wi-Fi) - デフォルトのショートの画質 (モバイル) - モバイル + デフォルトのショートの画質 (携帯回線) + 携帯回線 Wi-Fi デフォルトの画質の変更 (%1$s): %2$s ショートの画質の変更 (%1$s): %2$s diff --git a/patches/src/main/resources/addresources/values-ko-rKR/strings.xml b/patches/src/main/resources/addresources/values-ko-rKR/strings.xml index b79fccb0ea..886f8e9a8d 100644 --- a/patches/src/main/resources/addresources/values-ko-rKR/strings.xml +++ b/patches/src/main/resources/addresources/values-ko-rKR/strings.xml @@ -649,7 +649,7 @@ YouTube Premium 사용자라면 이 설정은 필요하지 않을 수 있습니 앱 패키지명을 입력하세요 기타 앱이 설치되지 않습니다 - %s 는 설치되어 있지 않습니다. 설치하세요. + %s 는 설치되어 있지 않습니다. 설치하세요 "패키지 이름이 '%s'인 설치된 앱을 찾을 수 없습니다 패키지 이름이 올바르고 앱이 설치되어 있는지 확인하세요" diff --git a/patches/src/main/resources/addresources/values-lv-rLV/strings.xml b/patches/src/main/resources/addresources/values-lv-rLV/strings.xml index 117d853fed..77874c64fc 100644 --- a/patches/src/main/resources/addresources/values-lv-rLV/strings.xml +++ b/patches/src/main/resources/addresources/values-lv-rLV/strings.xml @@ -57,7 +57,7 @@ Second \"item\" text" Nepieciešama restartēšana Lai šīs izmaiņas stātos spēkā, restartējiet lietotni. Restartēt - Ievietot + Importēt Kopēt ReVanced iestatījumi atiestatīti uz noklusējuma vērtībām Importēti %d iestatījumi @@ -641,8 +641,8 @@ Ierobežojumi: Jūsu instalētās ārējās lejupielādētāja lietotnes pakotnes nosaukums Ievadiet pakotnes nosaukumu Cits - Lietotne nav uzstādīta - %s nav uzstādīta. Lūdzu, uzstādiet to. + Lietotne nav instalēta + %s nav instalēts. Lūdzu, instalējiet to. "Nevarēja atrast instalēto lietotni ar pakotnes nosaukumu: %s Pārbaudiet, vai pakotnes nosaukums ir pareizs un lietotne ir instalēta" diff --git a/patches/src/main/resources/addresources/values-nl-rNL/strings.xml b/patches/src/main/resources/addresources/values-nl-rNL/strings.xml index a73508146f..1a9e2785ac 100644 --- a/patches/src/main/resources/addresources/values-nl-rNL/strings.xml +++ b/patches/src/main/resources/addresources/values-nl-rNL/strings.xml @@ -25,7 +25,7 @@ Second \"item\" text" Aangepast App-pictogram Origineel - ReVanced + shared.layout.branding.baseCustomBrandingPatch.revanced_custom_branding_icon_entry_2 ReVanced minimaal ReVanced geschaald @@ -33,7 +33,7 @@ Second \"item\" text" Aangepast - Controles mislukt + Controle mislukt Open officiële website Negeren <h5>Deze app lijkt niet door u te zijn gepatcht.</h5><br>Deze app werkt mogelijk niet goed, is **mogelijk schadelijk of zelfs gevaarlijk om te gebruiken**.<br><br>Deze checks geven aan dat deze app is gepatcht of van iemand anders is verkregen:<br><br><small>%1$s</small><br>Het is sterk aan te raden om **deze app te verwijderen en zelf te patchen** om er zeker van te zijn dat u een gevalideerde en veilige app gebruikt.<p><br>Indien genegeerd, zal deze waarschuwing nog slechts twee keer worden getoond. @@ -1592,7 +1592,6 @@ Het inschakelen hiervan kan ontbrekende afbeeldingen oplossen die in sommige reg DeArrow & Oorspronkelijke miniaturen DeArrow & Stilstaande opnames Stilstaande opnames - DeArrow "DeArrow biedt door de menigte gecrowdsourcede miniaturen voor YouTube-video's. Deze miniaturen zijn vaak relevanter dan die van YouTube. Als dit is ingeschakeld, worden video-URL's naar de API-server verzonden en worden geen andere gegevens verzonden. Als een video geen DeArrow-miniaturen heeft, worden de originele of stilstaande opnames weergegeven. @@ -1636,11 +1635,7 @@ Tik hier om meer te weten te komen over DeArrow" Loopvideo is ingeschakeld Loopvideo is uitgeschakeld - - Pauzeren bij audio-onderbreking - Afspelen wordt gepauzeerd wanneer andere audio wordt afgespeeld (bijv. navigatie) - Volume wordt zachter wanneer andere audio wordt afgespeeld - + Spoof apparaatdimensies "Apparaatdimensies gespoofed @@ -1700,7 +1695,6 @@ Het inschakelen hiervan kan hogere videokwaliteiten ontgrendelen" Standaardkwaliteit voor Shorts op wifi-netwerk Standaardkwaliteit voor Shorts op mobiel netwerk mobiel - wifi Standaard %1$s-kwaliteit gewijzigd naar: %2$s De kwaliteit van Shorts %1$s is gewijzigd in: %2$s @@ -1795,11 +1789,7 @@ Het afspelen van video met AV1 kan haperen of frames overslaan." - - YT Music ReVanced - Music ReVanced - Music - + Over @@ -1926,7 +1916,6 @@ Het afspelen van video met AV1 kan haperen of frames overslaan." Over ReVanced Advertentieblokkering Instellingen advertentieblokkering - Chat Chat-instellingen Overige Diverse instellingen diff --git a/patches/src/main/resources/addresources/values-zh-rTW/strings.xml b/patches/src/main/resources/addresources/values-zh-rTW/strings.xml index e8b3937112..d2fb68c602 100644 --- a/patches/src/main/resources/addresources/values-zh-rTW/strings.xml +++ b/patches/src/main/resources/addresources/values-zh-rTW/strings.xml @@ -91,7 +91,7 @@ Second \"item\" text" ReVanced 語言 "部分語言的翻譯可能缺少或不完整。 -若要翻譯新的語言或改善現有翻譯,請前往 translate.revanced.app" +如要翻譯新的語言或改善現有翻譯,請前往 translate.revanced.app" 應用程式語言 匯入/匯出 匯入/匯出 ReVanced 設定 @@ -312,8 +312,8 @@ Second \"item\" text" 票券架已顯示 隱藏影片推薦標籤 - 已隱藏搜尋結果中的「其他人也觀看」與「你可能也喜歡」標籤 - 已顯示搜尋結果中的「其他人也觀看」與「你可能也喜歡」標籤 + 搜尋結果中的「其他人也觀看」與「您可能也喜歡」標籤已隱藏 + 搜尋結果中的「其他人也觀看」與「您可能也喜歡」標籤已顯示 隱藏視覺間隔 視覺間隔已隱藏 視覺間隔已顯示 @@ -907,9 +907,9 @@ Second \"item\" text" 隱藏影片畫質選單 影片畫質選單已隱藏 影片畫質選單已顯示 - 隱藏「影片畫質」選單頁尾 - 已隱藏「影片畫質」選單頁尾 - 已顯示「影片畫質」選單頁尾 + 隱藏影片畫質選單頁尾 + 已隱藏影片畫質選單頁尾 + 已顯示影片畫質選單頁尾 隱藏「自動播放」按鈕 @@ -1670,8 +1670,8 @@ Second \"item\" text" 拖曳還原觸覺回饋已停用 拖曳還原觸覺回饋已啟用 停用輕觸並按住觸覺回饋 - 已停用輕觸並按住觸覺回饋 - 已啟用輕觸並按住觸覺回饋 + 輕觸並按住觸覺回饋已停用 + 輕觸並按住觸覺回饋已啟用 停用縮放震動 縮放觸覺回饋已停用 縮放觸覺回饋已啟用 @@ -1712,12 +1712,12 @@ Second \"item\" text" 顯示速度對話方塊按鈕 - 已顯示速度對話框按鈕。長按可將播放速度重設為預設值 + 速度對話框按鈕已顯示。長按可將播放速度重設為預設值 速度對話框按鈕未顯示 顯示畫質切換按鈕 - 已顯示影片畫質按鈕。長按可重設為預設畫質 + 影片畫質按鈕已顯示。長按可重設為預設畫質 影片畫質按鈕未顯示 @@ -1791,7 +1791,7 @@ AV1 視訊播放可能會卡頓或丟格。" • 影片可能會在 1:00 停止,或在某些地區無法播放 • 音軌選單遺失 • 沒有 AV1 影片解碼器 - • 無法使用平衡音量 + • 穩定音量無法使用 • 在登出或無痕模式下,兒童影片可能無法播放 • 強制原始音訊不可用 diff --git a/patches/src/main/resources/addresources/values/arrays.xml b/patches/src/main/resources/addresources/values/arrays.xml index e75cb6671a..230b9b8f38 100644 --- a/patches/src/main/resources/addresources/values/arrays.xml +++ b/patches/src/main/resources/addresources/values/arrays.xml @@ -220,14 +220,14 @@ - Android Reel Android VR visionOS + Android No SDK - ANDROID_REEL ANDROID_VR_1_43_32 VISIONOS + ANDROID_NO_SDK @@ -264,16 +264,18 @@ - Android Reel Android VR Android Studio + Android No SDK visionOS + iPadOS - ANDROID_REEL ANDROID_VR_1_43_32 ANDROID_CREATOR + ANDROID_NO_SDK VISIONOS + IPADOS @@ -661,7 +663,8 @@ cross-out - + + @string/revanced_block_embedded_ads_entry_1 diff --git a/patches/src/main/resources/addresources/values/strings.xml b/patches/src/main/resources/addresources/values/strings.xml index 28a4d71661..95da75bad4 100644 --- a/patches/src/main/resources/addresources/values/strings.xml +++ b/patches/src/main/resources/addresources/values/strings.xml @@ -144,16 +144,6 @@ If you are a YouTube Premium user, this setting may not be required" Playback may not work" Turning off this setting may cause playback issues. Default client - - • Experimental client and may stop working anytime - • Video may stop at 1:00, or may not be available in some regions - • Audio track menu is missing - • No AV1 video codec - • 360° VR immersive mode is not available - • Stable volume is not available - Spoofing side effects - - • Force original audio is not available Force original audio language @@ -910,10 +900,10 @@ Adjust volume by swiping vertically on the right side of the screen" Audio track menu is hidden Audio track menu is shown + 'Android No SDK' must be kept untranslated. --> "Audio track menu is hidden -To show the Audio track menu, change \'Spoof video streams\' to \'Android >Reel\'" +To show the Audio track menu, change \'Spoof video streams\' to \'Android No SDK\'" Hide Watch in VR Watch in VR menu is hidden @@ -1796,9 +1786,18 @@ Playback may stutter or drop frames" "Enabling this setting may use software AV1 decoding. Video playback with AV1 may stutter or drop frames." - Show in Stats for nerds - Client type is shown in Stats for nerds - Client is hidden in Stats for nerds + Spoofing side effects + • Experimental client and may stop working anytime + • Video may stop at 1:00, or may not be available in some regions + • Audio track menu is missing + • No AV1 video codec + • Stable volume is not available + • Kids videos may not play when logged out or in incognito mode + + • Force original audio is not available + Show in Stats for nerds + Client type is shown in Stats for nerds + Client is hidden in Stats for nerds diff --git a/settings.gradle.kts b/settings.gradle.kts index 1a95f2d066..167fb51c8b 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -2,6 +2,7 @@ rootProject.name = "revanced-patches" pluginManagement { repositories { + mavenLocal() gradlePluginPortal() google() maven { @@ -9,16 +10,12 @@ pluginManagement { url = uri("https://maven.pkg.github.com/revanced/revanced-patches-gradle-plugin") credentials(PasswordCredentials::class) } - // TODO: Remove once https://github.com/google/protobuf-gradle-plugin/pull/797 is merged. - maven { url = uri("https://jitpack.io") } } - // TODO: Remove once https://github.com/google/protobuf-gradle-plugin/pull/797 is merged. - resolutionStrategy { - eachPlugin { - if (requested.id.id == "com.google.protobuf") { - useModule("com.github.ReVanced:protobuf-gradle-plugin:${requested.version}") - } - } +} + +dependencyResolutionManagement { + repositories { + mavenLocal() } } @@ -36,4 +33,4 @@ settings { } } -include(":patches:stub") \ No newline at end of file +include(":patches:stub")