Compare commits
11 commits
v6.1.0-dev
...
main
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
b632d0f042 | ||
|
|
fca2470990 | ||
|
|
66b6c8c8ed | ||
|
|
4b699da220 | ||
|
|
e19275fb7d | ||
|
|
4bc8c7c0f6 | ||
|
|
e51c5292c1 | ||
|
|
43688d0622 | ||
|
|
4b6c3e3123 | ||
|
|
14ea61355d | ||
|
|
90eebe082c |
46 changed files with 1063 additions and 348 deletions
1
.github/workflows/pull_strings.yml
vendored
1
.github/workflows/pull_strings.yml
vendored
|
|
@ -32,6 +32,7 @@ jobs:
|
|||
|
||||
- name: Process strings
|
||||
run: |
|
||||
chmod -R 777 patches/src/main/resources
|
||||
./gradlew processStringsFromCrowdin
|
||||
env:
|
||||
ORG_GRADLE_PROJECT_githubPackagesUsername: ${{ github.actor }}
|
||||
|
|
|
|||
37
CHANGELOG.md
37
CHANGELOG.md
|
|
@ -1,3 +1,40 @@
|
|||
# [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)
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
package app.revanced.extension.playintegrity;
|
||||
package app.revanced.extension.play;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
|
|
@ -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_NO_SDK;
|
||||
import static app.revanced.extension.shared.spoof.ClientType.ANDROID_REEL;
|
||||
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<ClientType> availableClients = List.of(
|
||||
ANDROID_REEL,
|
||||
ANDROID_VR_1_43_32,
|
||||
ANDROID_NO_SDK,
|
||||
VISIONOS,
|
||||
ANDROID_VR_1_61_48
|
||||
);
|
||||
|
|
|
|||
|
|
@ -35,7 +35,7 @@ public class Settings extends YouTubeAndMusicSettings {
|
|||
|
||||
// Miscellaneous
|
||||
public static final EnumSetting<ClientType> SPOOF_VIDEO_STREAMS_CLIENT_TYPE = new EnumSetting<>("revanced_spoof_video_streams_client_type",
|
||||
ClientType.ANDROID_VR_1_43_32, true, parent(SPOOF_VIDEO_STREAMS));
|
||||
ClientType.ANDROID_REEL, true, parent(SPOOF_VIDEO_STREAMS));
|
||||
|
||||
public static final BooleanSetting FORCE_ORIGINAL_AUDIO = new BooleanSetting("revanced_force_original_audio", TRUE, true);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
plugins {
|
||||
alias(libs.plugins.android.library)
|
||||
alias(libs.plugins.android.library)
|
||||
}
|
||||
|
||||
android {
|
||||
|
|
@ -19,4 +19,6 @@ android {
|
|||
dependencies {
|
||||
compileOnly(libs.annotation)
|
||||
compileOnly(libs.okhttp)
|
||||
compileOnly(libs.protobuf.javalite)
|
||||
implementation(project(":extensions:shared:protobuf", configuration = "shadowRuntimeElements"))
|
||||
}
|
||||
|
|
|
|||
|
|
@ -114,7 +114,7 @@ public class CustomBrandingPatch {
|
|||
|
||||
/**
|
||||
* Injection point.
|
||||
*
|
||||
* <p>
|
||||
* 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()
|
||||
: 1;
|
||||
: 2;
|
||||
}
|
||||
|
||||
public static BrandingTheme getDefaultIconStyle() {
|
||||
return userProvidedCustomIcon()
|
||||
? BrandingTheme.CUSTOM
|
||||
: BrandingTheme.ORIGINAL;
|
||||
: BrandingTheme.ROUNDED;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -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_streaming_data_stats_for_nerds", TRUE, parent(SPOOF_VIDEO_STREAMS));
|
||||
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 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);
|
||||
|
|
|
|||
|
|
@ -9,9 +9,34 @@ 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.
|
||||
|
|
@ -28,10 +53,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"
|
||||
),
|
||||
/**
|
||||
|
|
@ -48,39 +73,12 @@ 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".
|
||||
* <a href="https://dumps.tadiphone.dev/dumps/google/barbet">Google Pixel 9 Pro Fold</a>
|
||||
|
|
@ -95,10 +93,10 @@ public enum ClientType {
|
|||
"15",
|
||||
"35",
|
||||
"AP3A.241005.015.A2",
|
||||
"132.0.6779.0",
|
||||
"23.47.101",
|
||||
true,
|
||||
false,
|
||||
true,
|
||||
"Android Studio"
|
||||
),
|
||||
/**
|
||||
|
|
@ -114,32 +112,8 @@ 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,
|
||||
"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"
|
||||
"visionOS"
|
||||
);
|
||||
|
||||
/**
|
||||
|
|
@ -195,13 +169,6 @@ 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.
|
||||
*/
|
||||
|
|
@ -217,6 +184,11 @@ 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.
|
||||
*/
|
||||
|
|
@ -234,10 +206,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;
|
||||
|
|
@ -248,21 +220,20 @@ 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; Cronet/%s)",
|
||||
this.userAgent = String.format("%s/%s (Linux; U; Android %s; %s; %s; Build/%s)",
|
||||
packageName,
|
||||
clientVersion,
|
||||
osVersion,
|
||||
defaultLocale,
|
||||
deviceModel,
|
||||
Objects.requireNonNull(buildId),
|
||||
Objects.requireNonNull(cronetVersion)
|
||||
buildId
|
||||
);
|
||||
Logger.printDebug(() -> "userAgent: " + this.userAgent);
|
||||
}
|
||||
|
|
@ -278,6 +249,7 @@ public enum ClientType {
|
|||
String userAgent,
|
||||
boolean useAuth,
|
||||
boolean supportsMultiAudioTracks,
|
||||
boolean usePlayerEndpoint,
|
||||
String friendlyName) {
|
||||
this.id = id;
|
||||
this.clientName = clientName;
|
||||
|
|
@ -289,10 +261,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;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -5,7 +5,6 @@ import android.text.TextUtils;
|
|||
|
||||
import androidx.annotation.Nullable;
|
||||
|
||||
import java.nio.ByteBuffer;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
|
|
@ -39,7 +38,7 @@ public class SpoofVideoStreamsPatch {
|
|||
@Nullable
|
||||
private static volatile AppLanguage languageOverride;
|
||||
|
||||
private static volatile ClientType preferredClient = ClientType.ANDROID_VR_1_43_32;
|
||||
private static volatile ClientType preferredClient = ClientType.ANDROID_REEL;
|
||||
|
||||
/**
|
||||
* @return If this patch was included during patching.
|
||||
|
|
@ -250,7 +249,7 @@ public class SpoofVideoStreamsPatch {
|
|||
* Called after {@link #fetchStreams(String, Map)}.
|
||||
*/
|
||||
@Nullable
|
||||
public static ByteBuffer getStreamingData(String videoId) {
|
||||
public static byte[] getStreamingData(String videoId) {
|
||||
if (SPOOF_STREAMING_DATA) {
|
||||
try {
|
||||
StreamingDataRequest request = StreamingDataRequest.getRequestForVideoId(videoId);
|
||||
|
|
|
|||
|
|
@ -15,13 +15,20 @@ import app.revanced.extension.shared.spoof.ClientType;
|
|||
import app.revanced.extension.shared.spoof.SpoofVideoStreamsPatch;
|
||||
|
||||
final class PlayerRoutes {
|
||||
static final Route.CompiledRoute GET_STREAMING_DATA = new Route(
|
||||
static final Route.CompiledRoute GET_PLAYER_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/";
|
||||
|
||||
/**
|
||||
|
|
@ -47,6 +54,7 @@ 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);
|
||||
|
|
@ -61,9 +69,19 @@ final class PlayerRoutes {
|
|||
context.put("client", client);
|
||||
|
||||
innerTubeBody.put("context", context);
|
||||
innerTubeBody.put("contentCheckOk", true);
|
||||
innerTubeBody.put("racyCheckOk", true);
|
||||
innerTubeBody.put("videoId", videoId);
|
||||
|
||||
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);
|
||||
}
|
||||
} catch (JSONException e) {
|
||||
Logger.printException(() -> "Failed to create innerTubeBody", e);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,18 +1,17 @@
|
|||
package app.revanced.extension.shared.spoof.requests;
|
||||
|
||||
import static app.revanced.extension.shared.ByteTrieSearch.convertStringsToBytes;
|
||||
import static app.revanced.extension.shared.spoof.requests.PlayerRoutes.GET_STREAMING_DATA;
|
||||
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 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;
|
||||
|
|
@ -27,6 +26,11 @@ 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;
|
||||
|
||||
|
|
@ -41,7 +45,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<ClientType> availableClients, ClientType preferredClient) {
|
||||
Objects.requireNonNull(preferredClient);
|
||||
|
|
@ -111,7 +115,7 @@ public class StreamingDataRequest {
|
|||
|
||||
private final String videoId;
|
||||
|
||||
private final Future<ByteBuffer> future;
|
||||
private final Future<byte[]> future;
|
||||
|
||||
private StreamingDataRequest(String videoId, Map<String, String> playerHeaders) {
|
||||
Objects.requireNonNull(playerHeaders);
|
||||
|
|
@ -134,6 +138,12 @@ 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,
|
||||
|
|
@ -146,7 +156,10 @@ public class StreamingDataRequest {
|
|||
final long startTime = System.currentTimeMillis();
|
||||
|
||||
try {
|
||||
HttpURLConnection connection = PlayerRoutes.getPlayerResponseConnectionFromRoute(GET_STREAMING_DATA, clientType);
|
||||
Route.CompiledRoute route = clientType.usePlayerEndpoint ?
|
||||
GET_PLAYER_STREAMING_DATA : GET_REEL_STREAMING_DATA;
|
||||
|
||||
HttpURLConnection connection = PlayerRoutes.getPlayerResponseConnectionFromRoute(route, clientType);
|
||||
connection.setConnectTimeout(HTTP_TIMEOUT_MILLISECONDS);
|
||||
connection.setReadTimeout(HTTP_TIMEOUT_MILLISECONDS);
|
||||
|
||||
|
|
@ -203,7 +216,7 @@ public class StreamingDataRequest {
|
|||
return null;
|
||||
}
|
||||
|
||||
private static ByteBuffer fetch(String videoId, Map<String, String> playerHeaders) {
|
||||
private static byte[] fetch(String videoId, Map<String, String> playerHeaders) {
|
||||
final boolean debugEnabled = BaseSettings.DEBUG.get();
|
||||
|
||||
// Retry with different client if empty response body is received.
|
||||
|
|
@ -214,33 +227,11 @@ public class StreamingDataRequest {
|
|||
|
||||
HttpURLConnection connection = send(clientType, videoId, playerHeaders, showErrorToast);
|
||||
if (connection != null) {
|
||||
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()) {
|
||||
byte[] playerResponseBuffer = buildPlayerResponseBuffer(clientType, connection);
|
||||
if (playerResponseBuffer != null) {
|
||||
lastSpoofedClientType = clientType;
|
||||
|
||||
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);
|
||||
return playerResponseBuffer;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -250,12 +241,61 @@ 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 ByteBuffer getStream() {
|
||||
public byte[] getStream() {
|
||||
try {
|
||||
return future.get(MAX_MILLISECONDS_TO_WAIT_FOR_FETCH, TimeUnit.MILLISECONDS);
|
||||
} catch (TimeoutException ex) {
|
||||
|
|
|
|||
55
extensions/shared/protobuf/build.gradle.kts
Normal file
55
extensions/shared/protobuf/build.gradle.kts
Normal file
|
|
@ -0,0 +1,55 @@
|
|||
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>("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) } }
|
||||
|
||||
|
|
@ -0,0 +1,42 @@
|
|||
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;
|
||||
}
|
||||
|
|
@ -0,0 +1,14 @@
|
|||
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;
|
||||
}
|
||||
}
|
||||
|
|
@ -1,10 +1,9 @@
|
|||
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.IPADOS;
|
||||
import static app.revanced.extension.shared.spoof.ClientType.ANDROID_REEL;
|
||||
import static app.revanced.extension.shared.spoof.ClientType.VISIONOS;
|
||||
|
||||
import java.util.List;
|
||||
|
|
@ -44,11 +43,11 @@ public class SpoofVideoStreamsPatch {
|
|||
}
|
||||
|
||||
List<ClientType> availableClients = List.of(
|
||||
VISIONOS,
|
||||
ANDROID_CREATOR,
|
||||
ANDROID_REEL,
|
||||
ANDROID_VR_1_43_32,
|
||||
ANDROID_NO_SDK,
|
||||
IPADOS);
|
||||
VISIONOS,
|
||||
ANDROID_CREATOR
|
||||
);
|
||||
|
||||
app.revanced.extension.shared.spoof.SpoofVideoStreamsPatch.setClientsToUse(
|
||||
availableClients, client);
|
||||
|
|
|
|||
|
|
@ -19,7 +19,7 @@ import app.revanced.extension.shared.spoof.ClientType;
|
|||
import app.revanced.extension.youtube.settings.Settings;
|
||||
|
||||
@SuppressWarnings({"deprecation", "unused"})
|
||||
public class SpoofStreamingDataSideEffectsPreference extends Preference {
|
||||
public class SpoofVideoStreamsSideEffectsPreference extends Preference {
|
||||
|
||||
@Nullable
|
||||
private ClientType currentClientType;
|
||||
|
|
@ -33,19 +33,19 @@ public class SpoofStreamingDataSideEffectsPreference extends Preference {
|
|||
Utils.runOnMainThread(this::updateUI);
|
||||
};
|
||||
|
||||
public SpoofStreamingDataSideEffectsPreference(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
|
||||
public SpoofVideoStreamsSideEffectsPreference(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
|
||||
super(context, attrs, defStyleAttr, defStyleRes);
|
||||
}
|
||||
|
||||
public SpoofStreamingDataSideEffectsPreference(Context context, AttributeSet attrs, int defStyleAttr) {
|
||||
public SpoofVideoStreamsSideEffectsPreference(Context context, AttributeSet attrs, int defStyleAttr) {
|
||||
super(context, attrs, defStyleAttr);
|
||||
}
|
||||
|
||||
public SpoofStreamingDataSideEffectsPreference(Context context, AttributeSet attrs) {
|
||||
public SpoofVideoStreamsSideEffectsPreference(Context context, AttributeSet attrs) {
|
||||
super(context, attrs);
|
||||
}
|
||||
|
||||
public SpoofStreamingDataSideEffectsPreference(Context context) {
|
||||
public SpoofVideoStreamsSideEffectsPreference(Context context) {
|
||||
super(context);
|
||||
}
|
||||
|
||||
|
|
@ -88,27 +88,23 @@ public class SpoofStreamingDataSideEffectsPreference 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_no_audio_tracks")
|
||||
+ '\n' + str("revanced_spoof_video_streams_about_no_stable_volume");
|
||||
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");
|
||||
+ '\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");
|
||||
default -> Logger.printException(() -> "Unknown client: " + clientType);
|
||||
}
|
||||
|
||||
// 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");
|
||||
// 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");
|
||||
}
|
||||
|
||||
// Use better formatting for bullet points.
|
||||
|
|
@ -4,4 +4,4 @@ org.gradle.parallel = true
|
|||
android.useAndroidX = true
|
||||
android.uniquePackageNames = false
|
||||
kotlin.code.style = official
|
||||
version = 6.1.0-dev.2
|
||||
version = 6.1.0
|
||||
|
|
|
|||
|
|
@ -10,6 +10,10 @@ 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" }
|
||||
|
|
@ -18,6 +22,10 @@ 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" }
|
||||
|
|
|
|||
|
|
@ -89,10 +89,14 @@ 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/playintegrity/DisablePlayIntegrityKt {
|
||||
public final class app/revanced/patches/all/misc/play/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
|
||||
|
|
@ -125,6 +129,14 @@ 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;
|
||||
}
|
||||
|
|
@ -365,6 +377,10 @@ 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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -6,7 +6,8 @@ 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.patches.all.misc.transformation.transformInstructionsPatch
|
||||
import app.revanced.util.forEachInstructionAsSequence
|
||||
import com.android.tools.smali.dexlib2.iface.instruction.Instruction
|
||||
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
|
||||
|
|
@ -33,7 +34,8 @@ val spoofSIMProviderPatch = bytecodePatch(
|
|||
validator = { it: String? -> it == null || it.uppercase() in countries.values },
|
||||
)
|
||||
|
||||
fun isMccMncValid(it: Int?): Boolean = it == null || (it >= 10000 && it <= 999999)
|
||||
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
|
||||
|
||||
val networkCountryIso by isoCountryPatchOption("Network ISO country code")
|
||||
|
||||
|
|
@ -61,46 +63,119 @@ 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(
|
||||
transformInstructionsPatch(
|
||||
filterMap = { _, _, instruction, instructionIndex ->
|
||||
if (instruction !is ReferenceInstruction) return@transformInstructionsPatch null
|
||||
bytecodePatch {
|
||||
apply {
|
||||
fun generateRandomNumeric(length: Int) = (1..length).map { ('0'..'9').random() }.joinToString("")
|
||||
|
||||
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
|
||||
fun String?.computeSpoof(randomizer: () -> String): String? {
|
||||
if (this.isNullOrBlank()) return null
|
||||
if (this.equals("random", ignoreCase = true)) return randomizer()
|
||||
return this
|
||||
}
|
||||
replacement?.let { instructionIndex to it }
|
||||
},
|
||||
transform = ::transformMethodCall,
|
||||
),
|
||||
|
||||
// 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
|
||||
)
|
||||
}
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
private fun transformMethodCall(
|
||||
mutableMethod: MutableMethod,
|
||||
entry: Pair<Int, String>,
|
||||
) {
|
||||
val (instructionIndex, methodCallValue) = entry
|
||||
private fun transformMethodCall(mutableMethod: MutableMethod, entry: Pair<Int, String>) {
|
||||
val (index, value) = entry
|
||||
val nextInstr = mutableMethod.getInstruction<Instruction>(index + 1)
|
||||
|
||||
// Get the register which would have contained the return value
|
||||
val register = mutableMethod.getInstruction<OneRegisterInstruction>(instructionIndex + 1).registerA
|
||||
if (nextInstr.opcode.name != "move-result-object") {
|
||||
mutableMethod.replaceInstruction(index, "nop")
|
||||
return
|
||||
}
|
||||
|
||||
// Replace the move-result instruction with our fake value
|
||||
mutableMethod.replaceInstruction(
|
||||
instructionIndex + 1,
|
||||
"const-string v$register, \"$methodCallValue\"",
|
||||
)
|
||||
val register = (nextInstr as OneRegisterInstruction).registerA
|
||||
mutableMethod.replaceInstruction(index, "const-string v$register, \"$value\"")
|
||||
mutableMethod.replaceInstruction(index + 1, "nop")
|
||||
}
|
||||
|
||||
private enum class MethodCall(
|
||||
|
|
@ -154,4 +229,100 @@ 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;"
|
||||
)
|
||||
)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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.patches.all.misc.transformation.transformInstructionsPatch
|
||||
import app.revanced.util.forEachInstructionAsSequence
|
||||
|
||||
private const val EXTENSION_CLASS_DESCRIPTOR_PREFIX =
|
||||
"Lapp/revanced/extension/all/misc/connectivity/wifi/spoof/SpoofWifiPatch"
|
||||
|
|
@ -19,29 +19,32 @@ val spoofWiFiConnectionPatch = bytecodePatch(
|
|||
extendWith("extensions/all/misc/connectivity/wifi/spoof/spoof-wifi.rve")
|
||||
|
||||
dependsOn(
|
||||
transformInstructionsPatch(
|
||||
filterMap = { classDef, _, instruction, instructionIndex ->
|
||||
filterMapInstruction35c<MethodCall>(
|
||||
EXTENSION_CLASS_DESCRIPTOR_PREFIX,
|
||||
classDef,
|
||||
instruction,
|
||||
instructionIndex,
|
||||
)
|
||||
},
|
||||
transform = { method, entry ->
|
||||
val (methodType, instruction, instructionIndex) = entry
|
||||
methodType.replaceInvokeVirtualWithExtension(
|
||||
EXTENSION_CLASS_DESCRIPTOR,
|
||||
method,
|
||||
instruction,
|
||||
instructionIndex,
|
||||
)
|
||||
},
|
||||
),
|
||||
bytecodePatch {
|
||||
apply {
|
||||
forEachInstructionAsSequence(
|
||||
match = { classDef, _, instruction, instructionIndex ->
|
||||
filterMapInstruction35c<MethodCall>(
|
||||
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,
|
||||
|
|
@ -89,13 +92,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;",
|
||||
|
|
@ -132,7 +135,7 @@ private enum class MethodCall(
|
|||
"registerBestMatchingNetworkCallback",
|
||||
arrayOf(
|
||||
"Landroid/net/NetworkRequest;",
|
||||
"Landroid/net/ConnectivityManager\$NetworkCallback;",
|
||||
$$"Landroid/net/ConnectivityManager$NetworkCallback;",
|
||||
"Landroid/os/Handler;",
|
||||
),
|
||||
"V",
|
||||
|
|
@ -140,19 +143,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(
|
||||
|
|
@ -166,7 +169,7 @@ private enum class MethodCall(
|
|||
"registerNetworkCallback",
|
||||
arrayOf(
|
||||
"Landroid/net/NetworkRequest;",
|
||||
"Landroid/net/ConnectivityManager\$NetworkCallback;",
|
||||
$$"Landroid/net/ConnectivityManager$NetworkCallback;",
|
||||
"Landroid/os/Handler;",
|
||||
),
|
||||
"V",
|
||||
|
|
@ -174,13 +177,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(
|
||||
|
|
@ -188,7 +191,7 @@ private enum class MethodCall(
|
|||
"requestNetwork",
|
||||
arrayOf(
|
||||
"Landroid/net/NetworkRequest;",
|
||||
"Landroid/net/ConnectivityManager\$NetworkCallback;",
|
||||
$$"Landroid/net/ConnectivityManager$NetworkCallback;",
|
||||
"Landroid/os/Handler;",
|
||||
),
|
||||
"V",
|
||||
|
|
@ -204,7 +207,7 @@ private enum class MethodCall(
|
|||
"requestNetwork",
|
||||
arrayOf(
|
||||
"Landroid/net/NetworkRequest;",
|
||||
"Landroid/net/ConnectivityManager\$NetworkCallback;",
|
||||
$$"Landroid/net/ConnectivityManager$NetworkCallback;",
|
||||
"Landroid/os/Handler;",
|
||||
"I",
|
||||
),
|
||||
|
|
@ -213,7 +216,7 @@ private enum class MethodCall(
|
|||
UnregisterNetworkCallback1(
|
||||
"Landroid/net/ConnectivityManager;",
|
||||
"unregisterNetworkCallback",
|
||||
arrayOf("Landroid/net/ConnectivityManager\$NetworkCallback;"),
|
||||
arrayOf($$"Landroid/net/ConnectivityManager$NetworkCallback;"),
|
||||
"V",
|
||||
),
|
||||
UnregisterNetworkCallback2(
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
package app.revanced.patches.all.misc.playintegrity
|
||||
package app.revanced.patches.all.misc.play
|
||||
|
||||
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/playintegrity/DisablePlayIntegrityPatch;"
|
||||
private const val EXTENSION_CLASS_DESCRIPTOR = "Lapp/revanced/extension/play/DisablePlayIntegrityPatch;"
|
||||
|
||||
private val CONTEXT_BIND_SERVICE_METHOD_REFERENCE = ImmutableMethodReference(
|
||||
"Landroid/content/Context;",
|
||||
|
|
@ -0,0 +1,138 @@
|
|||
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<MethodReference>()
|
||||
?: 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<OneRegisterInstruction>(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),
|
||||
}
|
||||
|
|
@ -0,0 +1,28 @@
|
|||
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")
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,70 @@
|
|||
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",
|
||||
),
|
||||
}
|
||||
|
|
@ -0,0 +1,20 @@
|
|||
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)
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,16 @@
|
|||
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())
|
||||
}
|
||||
|
|
@ -3,7 +3,6 @@ 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
|
||||
|
|
@ -126,14 +125,14 @@ internal fun baseCustomBrandingPatch(
|
|||
val getBuilderIndex = if (isYouTubeMusic) {
|
||||
// YT Music the field is not a plain object type.
|
||||
indexOfFirstInstructionOrThrow {
|
||||
getReference<FieldReference>()?.type == "Landroid/app/Notification\$Builder;"
|
||||
getReference<FieldReference>()?.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<TypeReference>()
|
||||
opcode == Opcode.CHECK_CAST &&
|
||||
reference?.type == "Landroid/app/Notification\$Builder;"
|
||||
reference?.type == $$"Landroid/app/Notification$Builder;"
|
||||
}
|
||||
indexOfFirstInstructionReversedOrThrow(builderCastIndex) {
|
||||
getReference<FieldReference>()?.type == "Ljava/lang/Object;"
|
||||
|
|
@ -148,11 +147,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
|
||||
""",
|
||||
)
|
||||
}
|
||||
|
|
@ -162,16 +161,37 @@ 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 (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.",
|
||||
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"
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -312,16 +332,19 @@ internal fun baseCustomBrandingPatch(
|
|||
activityAliasNameWithIntents,
|
||||
).childNodes
|
||||
|
||||
// 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",
|
||||
)
|
||||
// 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"
|
||||
)
|
||||
}
|
||||
|
||||
val enabledNameIndex = if (useCustomName) numberOfPresetAppNames else 1 // 1 indexing.
|
||||
val enabledNameIndex = if (useCustomName) numberOfPresetAppNames else 2 // 1 indexing.
|
||||
val enabledIconIndex = if (useCustomIcon) iconStyleNames.size else 0 // 0 indexing.
|
||||
|
||||
for (appNameIndex in 1..numberOfPresetAppNames) {
|
||||
|
|
@ -336,7 +359,7 @@ internal fun baseCustomBrandingPatch(
|
|||
iconMipmapName = originalLauncherIconName,
|
||||
appNameIndex = appNameIndex,
|
||||
useCustomName = useCustomNameLabel,
|
||||
enabled = (appNameIndex == 1),
|
||||
enabled = false,
|
||||
intentFilters,
|
||||
),
|
||||
)
|
||||
|
|
|
|||
|
|
@ -169,13 +169,13 @@ internal fun spoofVideoStreamsPatch(
|
|||
if-eqz v2, :disabled
|
||||
|
||||
# Get streaming data.
|
||||
invoke-static { v2 }, $EXTENSION_CLASS_DESCRIPTOR->getStreamingData(Ljava/lang/String;)Ljava/nio/ByteBuffer;
|
||||
invoke-static { v2 }, $EXTENSION_CLASS_DESCRIPTOR->getStreamingData(Ljava/lang/String;)[B
|
||||
move-result-object v3
|
||||
if-eqz v3, :disabled
|
||||
|
||||
# Parse streaming data.
|
||||
sget-object v4, $playerProtoClass->a:$playerProtoClass
|
||||
invoke-static { v4, v3 }, $protobufClass->parseFrom(${protobufClass}Ljava/nio/ByteBuffer;)$protobufClass
|
||||
invoke-static { v4, v3 }, $protobufClass->parseFrom(${protobufClass}[B)$protobufClass
|
||||
move-result-object v5
|
||||
check-cast v5, $playerProtoClass
|
||||
|
||||
|
|
|
|||
|
|
@ -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.SpoofStreamingDataSideEffectsPreference",
|
||||
tag = "app.revanced.extension.youtube.settings.preference.SpoofVideoStreamsSideEffectsPreference",
|
||||
),
|
||||
SwitchPreference("revanced_spoof_video_streams_av1"),
|
||||
SwitchPreference("revanced_spoof_streaming_data_stats_for_nerds"),
|
||||
SwitchPreference("revanced_spoof_video_streams_stats_for_nerds"),
|
||||
),
|
||||
),
|
||||
)
|
||||
|
|
|
|||
|
|
@ -25,6 +25,7 @@ Second \"item\" text"</string>
|
|||
<string name="revanced_custom_branding_name_entry_5">Vlastní</string>
|
||||
<string name="revanced_custom_branding_icon_title">Ikona aplikace</string>
|
||||
<string name="revanced_custom_branding_icon_entry_1">Původní</string>
|
||||
<string name="revanced_custom_branding_icon_entry_2">ReVanced</string>
|
||||
<!-- Translation of this should be identical to revanced_header_logo_entry_5 -->
|
||||
<string name="revanced_custom_branding_icon_entry_3">ReVanced minimální</string>
|
||||
<string name="revanced_custom_branding_icon_entry_4">ReVanced škálované</string>
|
||||
|
|
@ -49,6 +50,7 @@ Second \"item\" text"</string>
|
|||
</patch>
|
||||
<patch id="misc.settings.settingsResourcePatch">
|
||||
<string name="revanced_settings_submenu_title">Nastavení</string>
|
||||
<string name="revanced_settings_title">ReVanced</string>
|
||||
<string name="revanced_settings_confirm_user_dialog_title">Opravdu chcete pokračovat?</string>
|
||||
<string name="revanced_settings_save">Uložit</string>
|
||||
<string name="revanced_settings_reset">Výchozí</string>
|
||||
|
|
@ -131,6 +133,8 @@ Klepněte na tlačítko Pokračovat a povolte změny optimalizace."</string>
|
|||
<patch id="misc.fix.playback.spoofVideoStreamsPatch">
|
||||
<string name="revanced_spoof_video_streams_screen_title">Falšovat video streamy</string>
|
||||
<string name="revanced_spoof_video_streams_screen_summary">Falšovat video streamy klienta, aby se předešlo problémům s přehráváním.</string>
|
||||
<string name="revanced_spoof_video_streams_screen_title">Falšovat video streamy</string>
|
||||
<string name="revanced_spoof_video_streams_screen_summary">Falšovat video streamy klienta, aby se předešlo problémům s přehráváním</string>
|
||||
<string name="revanced_spoof_video_streams_title">Napodobovat video streamy</string>
|
||||
<string name="revanced_spoof_video_streams_summary_on">"Video streamy jsou maskovány
|
||||
|
||||
|
|
@ -218,9 +222,12 @@ Povolením této možnosti se však budou zaznamenávat i některá uživatelsk
|
|||
<string name="revanced_settings_screen_03_feed_title">Přísun</string>
|
||||
<string name="revanced_settings_screen_04_general_title">Obecné</string>
|
||||
<string name="revanced_settings_screen_05_player_title">Přehrávač</string>
|
||||
<string name="revanced_settings_screen_06_shorts_title">Shorts</string>
|
||||
<string name="revanced_settings_screen_07_seekbar_title">Lišta</string>
|
||||
<string name="revanced_settings_screen_08_swipe_controls_title">Ovládání gesty</string>
|
||||
<string name="revanced_settings_screen_09_return_youtube_dislike_title">Return YouTube Dislike</string>
|
||||
<string name="revanced_settings_screen_11_misc_title">Různé</string>
|
||||
<string name="revanced_settings_screen_12_video_title">Video</string>
|
||||
<string name="revanced_restore_old_settings_menus_title">Obnovit staré menu nastavení</string>
|
||||
<string name="revanced_restore_old_settings_menus_summary_on">Staré menu nastavení se zobrazují</string>
|
||||
<string name="revanced_restore_old_settings_menus_summary_off">Staré menu nastavení se nezobrazují</string>
|
||||
|
|
@ -1148,6 +1155,7 @@ Nastavení → Přehrávání → Automatické přehrávání dalšího videa"</
|
|||
<string name="revanced_ryd_failure_ryd_enabled_while_playing_video_then_user_voted">Načtěte video znovu, abyste hlasovali pomocí Return YouTube Dislike</string>
|
||||
<!-- Video likes have been set to hidden by the video uploader. -->
|
||||
<string name="revanced_ryd_video_likes_hidden_by_video_owner">Skryto vlastníkem</string>
|
||||
<string name="revanced_ryd_enabled_title">Return YouTube Dislike</string>
|
||||
<string name="revanced_ryd_enabled_summary_on">Nelíbí se se zobrazují</string>
|
||||
<string name="revanced_ryd_enabled_summary_off">Nelíbí se se nezobrazují</string>
|
||||
<string name="revanced_ryd_shorts_title">Zobrazit nelíbí se v Shorts</string>
|
||||
|
|
@ -1168,6 +1176,7 @@ Omezení: Počty „Nelíbí se mi“ se nemusí zobrazit v anonymním režimu"<
|
|||
<string name="revanced_ryd_toast_on_connection_error_title">Zobrazit toast, pokud API není dostupné</string>
|
||||
<string name="revanced_ryd_toast_on_connection_error_summary_on">Toast se zobrazí, pokud Return YouTube Dislike není dostupný</string>
|
||||
<string name="revanced_ryd_toast_on_connection_error_summary_off">Toast se nezobrazí, pokud Return YouTube Dislike není dostupný</string>
|
||||
<string name="revanced_ryd_attribution_title">ReturnYouTubeDislike.com</string>
|
||||
<string name="revanced_ryd_attribution_summary">Data jsou poskytována API Return YouTube Dislike. Klepnutím se dozvíte více</string>
|
||||
<!-- Statistic strings are shown in the settings only when ReVanced debug mode is enabled. Typical users will never see these. -->
|
||||
<string name="revanced_ryd_statistics_category_title">Statistiky API ReturnYouTubeDislike tohoto zařízení</string>
|
||||
|
|
@ -1188,6 +1197,7 @@ Omezení: Počty „Nelíbí se mi“ se nemusí zobrazit v anonymním režimu"<
|
|||
<string name="revanced_ryd_statistics_millisecond_text">%d milisekund</string>
|
||||
</patch>
|
||||
<patch id="layout.sponsorblock.sponsorBlockResourcePatch">
|
||||
<string name="revanced_settings_screen_10_sponsorblock_title">SponsorBlock</string>
|
||||
<string name="revanced_sb_enable_sb">Povolit SponsorBlock</string>
|
||||
<string name="revanced_sb_enable_sb_sum">SponsorBlock je systém s participací komunity pro přeskakování otravných částí videí na YouTube</string>
|
||||
<string name="revanced_sb_appearance_category">Vzhled</string>
|
||||
|
|
@ -1396,12 +1406,14 @@ Jste připraveni k odeslání?"</string>
|
|||
<string name="revanced_sb_color_opacity_label">Průhlednost:</string>
|
||||
<string name="revanced_sb_color_dot_label">Barva:</string>
|
||||
<string name="revanced_sb_about_title">O aplikaci</string>
|
||||
<string name="revanced_sb_about_api_title">sponsor.ajay.app</string>
|
||||
<string name="revanced_sb_about_api_summary">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</string>
|
||||
</patch>
|
||||
<patch id="layout.formfactor.changeFormFactorPatch">
|
||||
<string name="revanced_change_form_factor_title">Rozvržení formuláře</string>
|
||||
<string name="revanced_change_form_factor_entry_1">Výchozí</string>
|
||||
<string name="revanced_change_form_factor_entry_2">Telefon</string>
|
||||
<string name="revanced_change_form_factor_entry_3">Tablet</string>
|
||||
<string name="revanced_change_form_factor_entry_4">Automobilový</string>
|
||||
<string name="revanced_change_form_factor_user_dialog_message">"Změny zahrnují:
|
||||
|
||||
|
|
@ -1445,6 +1457,7 @@ Pokud bude později vypnuta, doporučujeme vymazat data aplikace, aby se zabrán
|
|||
<string name="revanced_change_start_page_entry_playlists">Playlisty</string>
|
||||
<string name="revanced_change_start_page_entry_search">Hledat</string>
|
||||
<string name="revanced_change_start_page_entry_shopping">Nakupování</string>
|
||||
<string name="revanced_change_start_page_entry_shorts">Shorts</string>
|
||||
<string name="revanced_change_start_page_entry_sports">Sport</string>
|
||||
<string name="revanced_change_start_page_entry_subscriptions">Odběry</string>
|
||||
<string name="revanced_change_start_page_entry_trending">Trendy</string>
|
||||
|
|
@ -1483,6 +1496,7 @@ Omezení: Použití tlačítka zpět na panelu nástrojů nemusí fungovat"</str
|
|||
<string name="revanced_miniplayer_type_entry_0">Vypnuto</string>
|
||||
<string name="revanced_miniplayer_type_entry_1">Výchozí</string>
|
||||
<string name="revanced_miniplayer_type_entry_2">Minimální</string>
|
||||
<string name="revanced_miniplayer_type_entry_3">Tablet</string>
|
||||
<string name="revanced_miniplayer_type_entry_4">Moderní 1</string>
|
||||
<string name="revanced_miniplayer_type_entry_5">Moderní 2</string>
|
||||
<string name="revanced_miniplayer_type_entry_6">Moderní 3</string>
|
||||
|
|
@ -1509,6 +1523,11 @@ Omezení: Použití tlačítka zpět na panelu nástrojů nemusí fungovat"</str
|
|||
<string name="revanced_miniplayer_hide_overlay_buttons_title">Skrýt tlačítka překrytí</string>
|
||||
<string name="revanced_miniplayer_hide_overlay_buttons_summary_on">Tlačítka překrytí jsou skrytá</string>
|
||||
<string name="revanced_miniplayer_hide_overlay_buttons_summary_off">Tlačítka překrytí jsou zobrazena</string>
|
||||
<string name="revanced_miniplayer_hide_overlay_buttons_legacy_title">Skrýt tlačítka rozbalit a zavřít</string>
|
||||
<string name="revanced_miniplayer_hide_overlay_buttons_legacy_summary_on">"Tlačítka jsou skrytá
|
||||
|
||||
Přejetím roztáhnete nebo zavřete"</string>
|
||||
<string name="revanced_miniplayer_hide_overlay_buttons_legacy_summary_off">Tlačítka rozbalit a zavřít jsou zobrazena</string>
|
||||
<string name="revanced_miniplayer_hide_subtext_title">Skrýt podtexty</string>
|
||||
<string name="revanced_miniplayer_hide_subtext_summary_on">Podtexty jsou skryty</string>
|
||||
<string name="revanced_miniplayer_hide_subtext_summary_off">Podtexty jsou zobrazeny</string>
|
||||
|
|
@ -1539,11 +1558,17 @@ Omezení: Použití tlačítka zpět na panelu nástrojů nemusí fungovat"</str
|
|||
<string name="revanced_seekbar_custom_color_accent_summary">Zvýrazněná barva posuvníku</string>
|
||||
<string name="revanced_seekbar_custom_color_invalid">Neplatná hodnota barvy posuvníku</string>
|
||||
</patch>
|
||||
<patch id="layout.branding.customBrandingPatch"/>
|
||||
<patch id="layout.branding.customBrandingPatch">
|
||||
<string name="revanced_custom_branding_name_entry_2">YouTube ReVanced</string>
|
||||
<string name="revanced_custom_branding_name_entry_3">YT ReVanced</string>
|
||||
<string name="revanced_custom_branding_name_entry_4">YT</string>
|
||||
</patch>
|
||||
<patch id="layout.branding.changeHeaderPatch">
|
||||
<string name="revanced_header_logo_title">Logo záhlaví</string>
|
||||
<string name="revanced_header_logo_entry_1">Výchozí</string>
|
||||
<string name="revanced_header_logo_entry_2">Běžné</string>
|
||||
<string name="revanced_header_logo_entry_3">Prémium</string>
|
||||
<string name="revanced_header_logo_entry_4">ReVanced</string>
|
||||
<!-- Translation of this should be identical to revanced_custom_branding_icon_entry_3 -->
|
||||
<string name="revanced_header_logo_entry_5">ReVanced minimální</string>
|
||||
<string name="revanced_header_logo_entry_6">Vlastní</string>
|
||||
|
|
@ -1568,6 +1593,7 @@ Povolení této funkce může opravit chybějící obrázky, které jsou v někt
|
|||
<string name="revanced_alt_thumbnail_options_entry_2">DeArrow & Původní náhledy</string>
|
||||
<string name="revanced_alt_thumbnail_options_entry_3">DeArrow & Zachycení snímků</string>
|
||||
<string name="revanced_alt_thumbnail_options_entry_4">Zachycení snímků</string>
|
||||
<string name="revanced_alt_thumbnail_dearrow_about_title">DeArrow</string>
|
||||
<string name="revanced_alt_thumbnail_dearrow_about_summary">"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
|
||||
|
|
@ -1611,7 +1637,11 @@ Klepnutím sem se dozvíte více o DeArrow"</string>
|
|||
<string name="revanced_loop_video_button_toast_on">Smyčka videa je zapnuta</string>
|
||||
<string name="revanced_loop_video_button_toast_off">Smyčka videa je vypnuta</string>
|
||||
</patch>
|
||||
<patch id="misc.audiofocus.pauseOnAudioInterruptPatch"/>
|
||||
<patch id="misc.audiofocus.pauseOnAudioInterruptPatch">
|
||||
<string name="revanced_pause_on_audio_interrupt_title">Pozastavit při přerušení zvuku</string>
|
||||
<string name="revanced_pause_on_audio_interrupt_summary_on">Přehrávání se pozastaví, když se přehrává jiný zvuk (např. navigace)</string>
|
||||
<string name="revanced_pause_on_audio_interrupt_summary_off">Sníží hlasitost při přehrávání ostatních zvuků</string>
|
||||
</patch>
|
||||
<patch id="misc.dimensions.spoof.spoofDeviceDimensionsPatch">
|
||||
<string name="revanced_spoof_device_dimensions_title">Napodobovat rozměry zařízení</string>
|
||||
<string name="revanced_spoof_device_dimensions_summary_on">"Rozměry zařízení jsou zfalšovány
|
||||
|
|
@ -1766,7 +1796,11 @@ Přehrávání videa s AV1 se může sekat nebo vypadávat snímky."</string>
|
|||
</patch>
|
||||
</app>
|
||||
<app id="music">
|
||||
<patch id="layout.branding.customBrandingPatch"/>
|
||||
<patch id="layout.branding.customBrandingPatch">
|
||||
<string name="revanced_custom_branding_name_entry_2">YT Music ReVanced</string>
|
||||
<string name="revanced_custom_branding_name_entry_3">Music ReVanced</string>
|
||||
<string name="revanced_custom_branding_name_entry_4">Hudba</string>
|
||||
</patch>
|
||||
<patch id="misc.settings.settingsPatch">
|
||||
<!-- In languages where "About" is ambiguous, translate to "About ReVanced" (i.e., About this app). -->
|
||||
<string name="revanced_settings_music_screen_0_about_title">O aplikaci</string>
|
||||
|
|
@ -1893,6 +1927,7 @@ Přehrávání videa s AV1 se může sekat nebo vypadávat snímky."</string>
|
|||
<string name="revanced_about_summary">O ReVanced</string>
|
||||
<string name="revanced_ads_screen_title">Blokování reklam</string>
|
||||
<string name="revanced_ads_screen_summary">Nastavení blokování reklam</string>
|
||||
<string name="revanced_chat_screen_title">Chat</string>
|
||||
<string name="revanced_chat_screen_summary">Nastavení chatu</string>
|
||||
<string name="revanced_misc_screen_title">Různé</string>
|
||||
<string name="revanced_misc_screen_summary">Různé nastavení</string>
|
||||
|
|
|
|||
|
|
@ -35,14 +35,14 @@ Second \"item\" text"</string>
|
|||
<string name="revanced_check_environment_dialog_ignore_button">Ignorieren</string>
|
||||
<string name="revanced_check_environment_failed_message"><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.</string>
|
||||
<string name="revanced_check_environment_not_same_patching_device">Auf einem anderen Gerät gepatcht</string>
|
||||
<string name="revanced_check_environment_manager_not_expected_installer">Nicht durch ReVanced Manager installiert</string>
|
||||
<string name="revanced_check_environment_manager_not_expected_installer">Nicht von ReVanced Manager installiert</string>
|
||||
<string name="revanced_check_environment_not_near_patch_time">Vor mehr als 10 Minuten gepatcht</string>
|
||||
<string name="revanced_check_environment_not_near_patch_time_days">Vor %s Tagen gepatcht</string>
|
||||
<string name="revanced_check_environment_not_near_patch_time_invalid">APK Erstellungsdatum ist beschädigt</string>
|
||||
</patch>
|
||||
<patch id="misc.dns.checkWatchHistoryDomainNameResolutionPatch">
|
||||
<string name="revanced_check_watch_history_domain_name_dialog_title">ReVanced Hinweis</string>
|
||||
<string name="revanced_check_watch_history_domain_name_dialog_message">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.</string>
|
||||
<string name="revanced_check_watch_history_domain_name_dialog_message">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.</string>
|
||||
<string name="revanced_check_watch_history_domain_name_dialog_ignore">Nicht wieder anzeigen</string>
|
||||
</patch>
|
||||
<patch id="misc.settings.settingsResourcePatch">
|
||||
|
|
@ -1598,7 +1598,9 @@ Tippen Sie hier, um mehr über DeArrow zu erfahren"</string>
|
|||
<string name="revanced_loop_video_button_toast_on">Loop-Video ist aktiviert</string>
|
||||
<string name="revanced_loop_video_button_toast_off">Loop-Video ist deaktiviert</string>
|
||||
</patch>
|
||||
<patch id="misc.audiofocus.pauseOnAudioInterruptPatch"/>
|
||||
<patch id="misc.audiofocus.pauseOnAudioInterruptPatch">
|
||||
<string name="revanced_pause_on_audio_interrupt_title">Pause bei Audiounterbrechung</string>
|
||||
</patch>
|
||||
<patch id="misc.dimensions.spoof.spoofDeviceDimensionsPatch">
|
||||
<string name="revanced_spoof_device_dimensions_title">Spoof-Gerätegröße</string>
|
||||
<string name="revanced_spoof_device_dimensions_summary_on">"Gerätemessungen gefälscht
|
||||
|
|
@ -1753,7 +1755,9 @@ Die Videowiedergabe mit AV1 kann stottern oder Bilder überspringen."</string>
|
|||
</patch>
|
||||
</app>
|
||||
<app id="music">
|
||||
<patch id="layout.branding.customBrandingPatch"/>
|
||||
<patch id="layout.branding.customBrandingPatch">
|
||||
<string name="revanced_custom_branding_name_entry_4">Musik</string>
|
||||
</patch>
|
||||
<patch id="misc.settings.settingsPatch">
|
||||
<!-- In languages where "About" is ambiguous, translate to "About ReVanced" (i.e., About this app). -->
|
||||
<string name="revanced_settings_music_screen_0_about_title">Über</string>
|
||||
|
|
|
|||
|
|
@ -238,13 +238,9 @@ Second \"item\" text"</string>
|
|||
<string name="revanced_shorts_disable_background_playback_summary_off">Η αναπαραγωγή παρασκηνίου είναι ενεργοποιημένη για τα Shorts</string>
|
||||
</patch>
|
||||
<patch id="layout.hide.general.hideLayoutComponentsPatch">
|
||||
<string name="revanced_hide_creator_store_shelf_title">Ενότητα καταστήματος δημιουργού</string>
|
||||
<string name="revanced_hide_creator_store_shelf_summary_on">Κρυμμένη
|
||||
|
||||
Αφορά την ενότητα καταστήματος δημιουργού κάτω από την οθόνη αναπαραγωγής</string>
|
||||
<string name="revanced_hide_creator_store_shelf_summary_off">Εμφανίζεται
|
||||
|
||||
Αφορά την ενότητα καταστήματος δημιουργού κάτω από την οθόνη αναπαραγωγής</string>
|
||||
<string name="revanced_hide_creator_store_shelf_title">Ενότητα καταστήματος δημιουργού κάτω από την οθόνη αναπαραγωγής</string>
|
||||
<string name="revanced_hide_creator_store_shelf_summary_on">Κρυμμένη</string>
|
||||
<string name="revanced_hide_creator_store_shelf_summary_off">Εμφανίζεται</string>
|
||||
<string name="revanced_hide_album_cards_title">Κάρτες άλμπουμ</string>
|
||||
<string name="revanced_hide_album_cards_summary_on">Κρυμμένες</string>
|
||||
<string name="revanced_hide_album_cards_summary_off">Εμφανίζονται</string>
|
||||
|
|
@ -373,13 +369,9 @@ Second \"item\" text"</string>
|
|||
<string name="revanced_hide_timed_reactions_title">Συγχρονισμένες αντιδράσεις</string>
|
||||
<string name="revanced_hide_timed_reactions_summary_on">Κρυμμένες</string>
|
||||
<string name="revanced_hide_timed_reactions_summary_off">Εμφανίζονται</string>
|
||||
<string name="revanced_hide_video_title_title">Τίτλος του βίντεο</string>
|
||||
<string name="revanced_hide_video_title_summary_on">Κρυμμένος
|
||||
|
||||
Αφορά τον τίτλο του βίντεο στη λειτουργία πλήρους οθόνης</string>
|
||||
<string name="revanced_hide_video_title_summary_off">Εμφανίζεται
|
||||
|
||||
Αφορά τον τίτλο του βίντεο στη λειτουργία πλήρους οθόνης</string>
|
||||
<string name="revanced_hide_video_title_title">Τίτλος του βίντεο στην πλήρη οθόνη</string>
|
||||
<string name="revanced_hide_video_title_summary_on">Κρυμμένος</string>
|
||||
<string name="revanced_hide_video_title_summary_off">Εμφανίζεται</string>
|
||||
<string name="revanced_hide_ai_generated_video_summary_section_title">Σύνοψη βίντεο που δημιουργήθηκε από AI</string>
|
||||
<string name="revanced_hide_ai_generated_video_summary_section_summary_on">Κρυμμένη</string>
|
||||
<string name="revanced_hide_ai_generated_video_summary_section_summary_off">Εμφανίζεται</string>
|
||||
|
|
@ -395,13 +387,9 @@ Second \"item\" text"</string>
|
|||
<string name="revanced_hide_course_progress_section_title">Ενότητα «Πρόοδος μαθήματος»</string>
|
||||
<string name="revanced_hide_course_progress_section_summary_on">Κρυμμένη</string>
|
||||
<string name="revanced_hide_course_progress_section_summary_off">Εμφανίζεται</string>
|
||||
<string name="revanced_hide_explore_section_title">Ενότητες «Εξερεύνηση»</string>
|
||||
<string name="revanced_hide_explore_section_summary_on">Κρυμμένες
|
||||
|
||||
Αφορά τις ενότητες «Εξερευνήστε αυτή τη σειρά μαθημάτων» και «Εξερευνήστε το podcast»</string>
|
||||
<string name="revanced_hide_explore_section_summary_off">Εμφανίζονται
|
||||
|
||||
Αφορά τις ενότητες «Εξερευνήστε αυτή τη σειρά μαθημάτων» και «Εξερευνήστε το podcast»</string>
|
||||
<string name="revanced_hide_explore_section_title">Ενότητες «Εξερευνήστε...»</string>
|
||||
<string name="revanced_hide_explore_section_summary_on">Κρυμμένες. Αφορά τις ενότητες «Εξερευνήστε αυτή τη σειρά μαθημάτων» και «Εξερευνήστε το podcast»</string>
|
||||
<string name="revanced_hide_explore_section_summary_off">Εμφανίζονται. Αφορά τις ενότητες «Εξερευνήστε αυτή τη σειρά μαθημάτων» και «Εξερευνήστε το podcast»</string>
|
||||
<string name="revanced_hide_explore_course_section_title">Ενότητα «Εξερευνήστε αυτή τη σειρά μαθημάτων»</string>
|
||||
<string name="revanced_hide_explore_course_section_summary_on">Κρυμμένη</string>
|
||||
<string name="revanced_hide_explore_course_section_summary_off">Εμφανίζεται</string>
|
||||
|
|
@ -1631,8 +1619,8 @@ Second \"item\" text"</string>
|
|||
</patch>
|
||||
<patch id="misc.announcements.announcementsPatch">
|
||||
<string name="revanced_announcements_title">Εμφάνιση ανακοινώσεων ReVanced</string>
|
||||
<string name="revanced_announcements_summary_on">Οι ανακοινώσεις κατά την εκκίνηση εμφανίζονται</string>
|
||||
<string name="revanced_announcements_summary_off">Οι ανακοινώσεις κατά την εκκίνηση δεν εμφανίζονται</string>
|
||||
<string name="revanced_announcements_summary_on">Οι ανακοινώσεις εμφανίζονται κατά την εκκίνηση</string>
|
||||
<string name="revanced_announcements_summary_off">Οι ανακοινώσεις δεν εμφανίζονται κατά την εκκίνηση</string>
|
||||
<string name="revanced_announcements_enabled_summary">Εμφάνιση ανακοινώσεων κατά την εκκίνηση</string>
|
||||
<string name="revanced_announcements_connection_failed">Αποτυχία σύνδεσης με τον πάροχο ανακοινώσεων</string>
|
||||
<string name="revanced_announcements_dialog_dismiss">Παράλειψη</string>
|
||||
|
|
|
|||
|
|
@ -1514,6 +1514,11 @@ El minireproductor se puede arrastrar fuera de la pantalla hacia la izquierda o
|
|||
<string name="revanced_miniplayer_hide_overlay_buttons_title">Ocultar botones de superposición</string>
|
||||
<string name="revanced_miniplayer_hide_overlay_buttons_summary_on">Los botones de superposición están ocultos</string>
|
||||
<string name="revanced_miniplayer_hide_overlay_buttons_summary_off">Se muestran los botones de superposición</string>
|
||||
<string name="revanced_miniplayer_hide_overlay_buttons_legacy_title">Ocultar botones de expandir y cerrar</string>
|
||||
<string name="revanced_miniplayer_hide_overlay_buttons_legacy_summary_on">"Los botones están ocultos
|
||||
|
||||
Deslizar para expandir o cerrar"</string>
|
||||
<string name="revanced_miniplayer_hide_overlay_buttons_legacy_summary_off">Se muestran botones de expandir y cerrar</string>
|
||||
<string name="revanced_miniplayer_hide_subtext_title">Ocultar subtextos</string>
|
||||
<string name="revanced_miniplayer_hide_subtext_summary_on">Los subtextos están ocultos</string>
|
||||
<string name="revanced_miniplayer_hide_subtext_summary_off">Los subtextos se muestran</string>
|
||||
|
|
@ -1622,7 +1627,10 @@ Toca aquí para obtener más información sobre DeArrow"</string>
|
|||
<string name="revanced_loop_video_button_toast_on">Bucle de vídeo activado</string>
|
||||
<string name="revanced_loop_video_button_toast_off">Bucle de vídeo desactivado</string>
|
||||
</patch>
|
||||
<patch id="misc.audiofocus.pauseOnAudioInterruptPatch"/>
|
||||
<patch id="misc.audiofocus.pauseOnAudioInterruptPatch">
|
||||
<string name="revanced_pause_on_audio_interrupt_title">Pausar cuando se corte el audio</string>
|
||||
<string name="revanced_pause_on_audio_interrupt_summary_on">La reproducción se detiene cuando se reproduce otro audio (por ejemplo, navegación)</string>
|
||||
</patch>
|
||||
<patch id="misc.dimensions.spoof.spoofDeviceDimensionsPatch">
|
||||
<string name="revanced_spoof_device_dimensions_title">Dimensiones del dispositivo</string>
|
||||
<string name="revanced_spoof_device_dimensions_summary_on">"Las dimensiones del dispositivo están falsificadas
|
||||
|
|
|
|||
|
|
@ -370,39 +370,39 @@ Mar sin féin, má chumasaíonn tú é seo, logálfar roinnt sonraí úsáideora
|
|||
<string name="revanced_hide_video_title_title">Folaigh teideal físeáin</string>
|
||||
<string name="revanced_hide_video_title_summary_on">Tá teideal an fhíseáin i bhfolach san fhorleagan seinnteora</string>
|
||||
<string name="revanced_hide_video_title_summary_off">Taispeántar teideal an fhíseáin i bhforleagan an imreora</string>
|
||||
<string name="revanced_hide_ai_generated_video_summary_section_title">Folaigh \'Achoimre físeáin arna giniúint ag AI\'</string>
|
||||
<string name="revanced_hide_ai_generated_video_summary_section_title">Folaigh \'Achoimre físe a ghintear le hintleacht shaorga\'</string>
|
||||
<string name="revanced_hide_ai_generated_video_summary_section_summary_on">Tá an chuid achoimre físe IS-ghinte i bhfolach</string>
|
||||
<string name="revanced_hide_ai_generated_video_summary_section_summary_off">Taispeántar an chuid achoimre físe a ghintear ag AI</string>
|
||||
<string name="revanced_hide_ai_generated_video_summary_section_summary_off">Taispeántar an chuid achoimre físe a ghintear ag Intleacht Shaorga</string>
|
||||
<string name="revanced_hide_ask_section_title">Folaigh Iarr</string>
|
||||
<string name="revanced_hide_ask_section_summary_on">Tá an rannán Iarratas i bhfolach</string>
|
||||
<string name="revanced_hide_ask_section_summary_off">Taispeántar an rannán Iarratas</string>
|
||||
<string name="revanced_hide_attributes_section_title">Folaigh Tréithe</string>
|
||||
<string name="revanced_hide_attributes_section_summary_on">Tá ailt d\'áiteanna sonracha, Cluichí, Ceol agus Daoine a luaitear i bhfolach</string>
|
||||
<string name="revanced_hide_attributes_section_summary_on">Tá na rannóga \'Áiteanna Réadmhaoine\', \'Cluichí\', \'Ceol\' agus \'Daoine\' i bhfolach</string>
|
||||
<string name="revanced_hide_attributes_section_summary_off">Taispeántar ailt d\'áiteanna sonracha, Cluichí, Ceol agus Daoine a luaitear</string>
|
||||
<string name="revanced_hide_chapters_section_title">Folaigh Caibidlí</string>
|
||||
<string name="revanced_hide_chapters_section_summary_on">Tá an chuid Caibidil i bhfolach</string>
|
||||
<string name="revanced_hide_chapters_section_summary_off">Taispeántar alt na gcaibidlí</string>
|
||||
<string name="revanced_hide_course_progress_section_title">Folaigh ‘Dul chun cinn cúrsa’</string>
|
||||
<string name="revanced_hide_course_progress_section_title">Folaigh \'Dul chun cinn an chúrsa\'</string>
|
||||
<string name="revanced_hide_course_progress_section_summary_on">Tá rannóg an dul chun cinn cúrsa i bhfolach</string>
|
||||
<string name="revanced_hide_course_progress_section_summary_off">Taispeántar rannóg an dul chun cinn cúrsa</string>
|
||||
<string name="revanced_hide_explore_section_title">Folaigh Iniúchadh</string>
|
||||
<string name="revanced_hide_explore_section_summary_on">Tá rannóga Iniúchadh ar an gcúrsa seo agus Déan iniúchadh ar an bpodchraoladh i bhfolach</string>
|
||||
<string name="revanced_hide_explore_section_summary_off">Taispeántar rannóga Iniúchadh ar an gcúrsa seo agus Déan iniúchadh ar an bpodchraoladh</string>
|
||||
<string name="revanced_hide_explore_section_summary_off">Taispeántar na rannóga Iniúchadh an chúrsa seo agus Iniúchadh na podchraoltaí</string>
|
||||
<string name="revanced_hide_explore_course_section_title">Folaigh ‘Déan iniúchadh ar an gcúrsa seo’</string>
|
||||
<string name="revanced_hide_explore_course_section_summary_on">Tá rannóg Déan iniúchadh ar an gcúrsa seo i bhfolach</string>
|
||||
<string name="revanced_hide_explore_course_section_summary_off">Taispeántar rannóg Déan iniúchadh ar an gcúrsa seo</string>
|
||||
<string name="revanced_hide_explore_course_section_summary_off">Taispeántar an chuid seo den chúrsa a iniúchadh</string>
|
||||
<string name="revanced_hide_explore_podcast_section_title">Folaigh \'Déan iniúchadh ar an bpodchraoladh\'</string>
|
||||
<string name="revanced_hide_explore_podcast_section_summary_on">Tá an chuid Déan iniúchadh ar an bpodchraoladh i bhfolach</string>
|
||||
<string name="revanced_hide_explore_podcast_section_summary_off">Taispeántar an chuid Déan iniúchadh ar an bpodchraoladh</string>
|
||||
<string name="revanced_hide_podcast_section_title">Folaigh \'Déan iniúchadh ar an bpodchraoladh\'</string>
|
||||
<string name="revanced_hide_podcast_section_summary_on">Tá an chuid Déan iniúchadh ar an bpodchraoladh i bhfolach</string>
|
||||
<string name="revanced_hide_podcast_section_summary_off">Taispeántar an chuid Déan iniúchadh ar an bpodchraoladh</string>
|
||||
<string name="revanced_hide_explore_podcast_section_summary_off">Taispeántar an rannóg podchraoltaí a iniúchadh</string>
|
||||
<string name="revanced_hide_podcast_section_title">Folaigh \'Iniúchadh a dhéanamh ar an bpodchraoladh\'</string>
|
||||
<string name="revanced_hide_podcast_section_summary_on">Tá an rannán podchraoltaí a iniúchadh i bhfolach</string>
|
||||
<string name="revanced_hide_podcast_section_summary_off">Taispeántar an rannóg podchraoltaí a iniúchadh</string>
|
||||
<string name="revanced_hide_featured_links_section_title">Folaigh naisc le feiceáil</string>
|
||||
<string name="revanced_hide_featured_links_section_summary_on">Tá an chuid nasc le feiceáil i bhfolach</string>
|
||||
<string name="revanced_hide_featured_links_section_summary_off">Taispeántar an chuid nasc le feiceáil</string>
|
||||
<string name="revanced_hide_featured_places_section_title">Folaigh ‘Áiteanna faoi Thrácht’</string>
|
||||
<string name="revanced_hide_featured_places_section_summary_on">Tá rannóg na n-áiteanna faoi Thrácht i bhfolach</string>
|
||||
<string name="revanced_hide_featured_places_section_summary_off">Taispeántar rannóg na n-áiteanna faoi Thrácht</string>
|
||||
<string name="revanced_hide_featured_places_section_summary_off">Taispeántar an chuid áiteanna feiceálacha</string>
|
||||
<string name="revanced_hide_featured_videos_section_title">Folaigh físeáin le feiceáil</string>
|
||||
<string name="revanced_hide_featured_videos_section_summary_on">Tá an chuid físeán le feiceáil i bhfolach</string>
|
||||
<string name="revanced_hide_featured_videos_section_summary_off">Taispeántar an chuid físeán le feiceáil</string>
|
||||
|
|
@ -1847,7 +1847,7 @@ D’fhéadfadh sé go mbeadh stad nó go gcaillfí frámaí ag athsheinm físe l
|
|||
<string name="revanced_music_navigation_bar_screen_summary">Folaigh nó athraigh cnaipí an bharra nascleanúna</string>
|
||||
<!-- 'Home' should be translated using the same localized wording YouTube Music displays for the tab. -->
|
||||
<string name="revanced_music_hide_navigation_bar_home_button_title">Folaigh Baile</string>
|
||||
<string name="revanced_music_hide_navigation_bar_home_button_summary_on">Tá cnaipe Baile folaithe</string>
|
||||
<string name="revanced_music_hide_navigation_bar_home_button_summary_on">Tá an cnaipe baile i bhfolach</string>
|
||||
<string name="revanced_music_hide_navigation_bar_home_button_summary_off">Taispeántar an cnaipe baile</string>
|
||||
<!-- 'Samples' should be translated using the same localized wording YouTube Music displays for the tab. -->
|
||||
<string name="revanced_music_hide_navigation_bar_samples_button_title">Folaigh Samplaí</string>
|
||||
|
|
@ -1875,7 +1875,7 @@ D’fhéadfadh sé go mbeadh stad nó go gcaillfí frámaí ag athsheinm físe l
|
|||
<patch id="layout.premium.hideGetPremiumPatch">
|
||||
<string name="revanced_music_hide_get_premium_label_title">Folaigh an lipéad \'Faigh Music Premium\'</string>
|
||||
<string name="revanced_music_hide_get_premium_label_summary_on">Tá an lipéad i bhfolach</string>
|
||||
<string name="revanced_music_hide_get_premium_label_summary_off">Taispeántar an lipéad</string>
|
||||
<string name="revanced_music_hide_get_premium_label_summary_off">Taispeántar lipéad</string>
|
||||
</patch>
|
||||
<patch id="layout.upgradebutton.hideUpgradeButtonPatch">
|
||||
<string name="revanced_music_hide_upgrade_button_title">Folaigh an cnaipe uasghrádaithe</string>
|
||||
|
|
@ -1885,28 +1885,28 @@ D’fhéadfadh sé go mbeadh stad nó go gcaillfí frámaí ag athsheinm físe l
|
|||
</app>
|
||||
<app id="twitch">
|
||||
<patch id="ad.audio.audioAdsPatch">
|
||||
<string name="revanced_block_audio_ads_title">Cuir bac ar fógraí fuaime</string>
|
||||
<string name="revanced_block_audio_ads_summary_on">Cuirtear bac ar fhógraí fuaime</string>
|
||||
<string name="revanced_block_audio_ads_title">Blocáil fógraí fuaime</string>
|
||||
<string name="revanced_block_audio_ads_summary_on">Tá fógraí fuaime blocáilte</string>
|
||||
<string name="revanced_block_audio_ads_summary_off">Tá fógraí fuaime díbhlocáilte</string>
|
||||
</patch>
|
||||
<patch id="ad.embedded.embeddedAdsPatch">
|
||||
<string name="revanced_embedded_ads_service_unavailable">%s neamh-infheidhme, d\'fhéadfadh go dtaispeánfadh fógraí. Bain triail as seirbhís blocála fógraí a athrú sna socruithe.</string>
|
||||
<string name="revanced_embedded_ads_service_failed">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.</string>
|
||||
<string name="revanced_embedded_ads_service_failed">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.</string>
|
||||
<string name="revanced_block_embedded_ads_title">Bloc ar fhógraí físe leabaithe</string>
|
||||
<string name="revanced_block_embedded_ads_entry_1">Díchumasaithe</string>
|
||||
<string name="revanced_block_embedded_ads_entry_2">Proxy lonrúil</string>
|
||||
<string name="revanced_block_embedded_ads_entry_2">Seachfhreastalaí lonrúil</string>
|
||||
<string name="revanced_block_embedded_ads_entry_3">Seachfhreastalaí PurpleAdBlock</string>
|
||||
</patch>
|
||||
<patch id="ad.video.videoAdsPatch">
|
||||
<string name="revanced_block_video_ads_title">Bloc ar fhógraí físe</string>
|
||||
<string name="revanced_block_video_ads_summary_on">Cuirtear bac ar fhógraí físe</string>
|
||||
<string name="revanced_block_video_ads_summary_off">Déantar fógraí físe a dhíbhlocáil</string>
|
||||
<string name="revanced_block_video_ads_summary_on">Tá bac ar fhógraí físe</string>
|
||||
<string name="revanced_block_video_ads_summary_off">Tá fógraí físe díbhlocáilte</string>
|
||||
</patch>
|
||||
<patch id="chat.antidelete.showDeletedMessagesPatch">
|
||||
<string name="revanced_deleted_msg">Teachtaireacht scriosta</string>
|
||||
<string name="revanced_show_deleted_messages_title">Taispeáin teachtaireachtaí scriosta</string>
|
||||
<string name="revanced_show_deleted_messages_entry_1">Ná taispeáin teachtaireachtaí scriosta</string>
|
||||
<string name="revanced_show_deleted_messages_entry_2">Folaigh teachtaireachtaí scriosta taobh thiar a fhalsúa</string>
|
||||
<string name="revanced_show_deleted_messages_entry_2">Folaigh teachtaireachtaí scriosta taobh thiar a mhilleann</string>
|
||||
<string name="revanced_show_deleted_messages_entry_3">Taispeáin teachtaireachtaí scriosta mar théacs trasnaithe amach</string>
|
||||
</patch>
|
||||
<patch id="chat.autoclaim.autoClaimChannelPointsPatch">
|
||||
|
|
|
|||
|
|
@ -29,7 +29,7 @@ Second \"item\" text"</string>
|
|||
</patch>
|
||||
<patch id="misc.dns.checkWatchHistoryDomainNameResolutionPatch"/>
|
||||
<patch id="misc.settings.settingsResourcePatch">
|
||||
<string name="revanced_settings_save">Sačuvaj</string>
|
||||
<string name="revanced_settings_save">Spremi</string>
|
||||
<string name="revanced_settings_disable_bold_icons_title">Onemogući podebljane ikone</string>
|
||||
<string name="revanced_settings_disable_bold_icons_summary_on">Ikone nisu podebljane</string>
|
||||
<string name="revanced_settings_disable_bold_icons_summary_off">Ikone su podebljane</string>
|
||||
|
|
@ -41,7 +41,7 @@ Second \"item\" text"</string>
|
|||
<string name="revanced_gms_core_screen_title">GmsCore</string>
|
||||
<string name="revanced_gms_core_screen_summary">Postavke povezane s GmsCoreom</string>
|
||||
<string name="revanced_gms_core_check_updates_title">Provjeri ažuriranja za GmsCore</string>
|
||||
<string name="revanced_gms_core_check_updates_summary_on">Provjera ažuriranja omogućena je</string>
|
||||
<string name="revanced_gms_core_check_updates_summary_on">Provjera ažuriranja je omogućena</string>
|
||||
<string name="revanced_gms_core_check_updates_summary_off">Provjera ažuriranja je onemogućena</string>
|
||||
<string name="revanced_gms_core_settings_title">Otvori postavke za GmsCore</string>
|
||||
<string name="revanced_gms_core_settings_summary">Postavke za GmsCore</string>
|
||||
|
|
@ -49,7 +49,7 @@ Second \"item\" text"</string>
|
|||
<string name="revanced_gms_core_toast_not_installed_message">MicroG GmsCore nije instaliran. Instalirajte ga.</string>
|
||||
<string name="revanced_gms_core_dialog_title">Potrebna je radnja</string>
|
||||
<string name="revanced_gms_core_toast_update_check_failed_message">Nije uspjela provjera ažuriranja za MicroG GmsCore</string>
|
||||
<string name="revanced_gms_core_update_available_message">Dostupna je nova verzija (%1$s) MicroG GmsCore. Trenutno koristite verziju %2$s.</string>
|
||||
<string name="revanced_gms_core_update_available_message">Dostupna je nova verzija (%1$s) MicroG GmsCorea. Trenutno koristite verziju %2$s.</string>
|
||||
<string name="revanced_gms_core_dialog_not_whitelisted_not_allowed_in_background_message">"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."</string>
|
||||
<string name="revanced_gms_core_dialog_open_website_text">Otvori web-stranicu</string>
|
||||
<string name="revanced_gms_core_dialog_cancel_text">Odustani</string>
|
||||
|
|
@ -438,8 +438,8 @@ Već postoji"</string>
|
|||
<patch id="video.quality.advancedVideoQualityMenuPatch"/>
|
||||
<patch id="video.quality.hidePremiumVideoQualityPatch">
|
||||
<string name="revanced_hide_premium_video_quality_title">Sakrij opcije premium kvalitete</string>
|
||||
<string name="revanced_hide_premium_video_quality_summary_on">Opcije premium kvalitete su sakrivene</string>
|
||||
<string name="revanced_hide_premium_video_quality_summary_off">Opcije premium kvalitete su prikazane</string>
|
||||
<string name="revanced_hide_premium_video_quality_summary_on">Premium opcije kvalitete su skrivene</string>
|
||||
<string name="revanced_hide_premium_video_quality_summary_off">Premium opcije kvalitete su prikazane</string>
|
||||
</patch>
|
||||
<patch id="interaction.seekbar.enableSlideToSeekPatch"/>
|
||||
<patch id="misc.fix.playback.spoofVideoStreamsPatch">
|
||||
|
|
|
|||
|
|
@ -188,7 +188,7 @@ Anda tidak akan diberi tahu tentang kejadian yang tidak terduga."</string>
|
|||
<string name="revanced_debug_protocolbuffer_title">Catatan protokol buffer</string>
|
||||
<string name="revanced_debug_protocolbuffer_summary_on">Pencatatan debug termasuk buffer proto</string>
|
||||
<string name="revanced_debug_protocolbuffer_summary_off">Pencatatan debug tidak menyertakan buffer proto</string>
|
||||
<string name="revanced_debug_protocolbuffer_user_dialog_message">"Mengaktifkan setelan ini akan mencatat data tata letak tambahan, termasuk teks pada layar untuk beberapa komponen UI.
|
||||
<string name="revanced_debug_protocolbuffer_user_dialog_message">"Mengaktifkan pengaturan ini akan mencatat data tata letak tambahan, termasuk teks pada layar untuk beberapa komponen UI.
|
||||
|
||||
Ini dapat membantu mengidentifikasi komponen saat membuat penyaring khusus.
|
||||
|
||||
|
|
|
|||
|
|
@ -1693,13 +1693,13 @@ Automotive レイアウト
|
|||
<string name="revanced_remember_video_quality_last_selected_toast_summary_on">デフォルトの画質が変更された場合にトースト通知が表示されます</string>
|
||||
<string name="revanced_remember_video_quality_last_selected_toast_summary_off">デフォルトの画質が変更された場合にトースト通知は表示されません</string>
|
||||
<string name="revanced_video_quality_default_wifi_title">デフォルトの画質 (Wi-Fi)</string>
|
||||
<string name="revanced_video_quality_default_mobile_title">デフォルトの画質 (携帯回線)</string>
|
||||
<string name="revanced_video_quality_default_mobile_title">デフォルトの画質 (モバイル)</string>
|
||||
<string name="revanced_remember_shorts_quality_last_selected_title">ショートの画質の変更を保存</string>
|
||||
<string name="revanced_remember_shorts_quality_last_selected_summary_on">画質の変更はすべてのショート動画に適用されます</string>
|
||||
<string name="revanced_remember_shorts_quality_last_selected_summary_off">画質の変更は現在のショート動画にのみ適用されます</string>
|
||||
<string name="revanced_shorts_quality_default_wifi_title">デフォルトのショートの画質 (Wi-Fi)</string>
|
||||
<string name="revanced_shorts_quality_default_mobile_title">デフォルトのショートの画質 (携帯回線)</string>
|
||||
<string name="revanced_remember_video_quality_mobile">携帯回線</string>
|
||||
<string name="revanced_shorts_quality_default_mobile_title">デフォルトのショートの画質 (モバイル)</string>
|
||||
<string name="revanced_remember_video_quality_mobile">モバイル</string>
|
||||
<string name="revanced_remember_video_quality_wifi">Wi-Fi</string>
|
||||
<string name="revanced_remember_video_quality_toast">デフォルトの画質の変更 (%1$s): %2$s</string>
|
||||
<string name="revanced_remember_video_quality_toast_shorts">ショートの画質の変更 (%1$s): %2$s</string>
|
||||
|
|
|
|||
|
|
@ -649,7 +649,7 @@ YouTube Premium 사용자라면 이 설정은 필요하지 않을 수 있습니
|
|||
<string name="revanced_external_downloader_other_item_hint">앱 패키지명을 입력하세요</string>
|
||||
<string name="revanced_external_downloader_other_item">기타</string>
|
||||
<string name="revanced_external_downloader_not_found_title">앱이 설치되지 않습니다</string>
|
||||
<string name="revanced_external_downloader_not_installed_warning">%s 는 설치되어 있지 않습니다. 설치하세요</string>
|
||||
<string name="revanced_external_downloader_not_installed_warning">%s 는 설치되어 있지 않습니다. 설치하세요.</string>
|
||||
<string name="revanced_external_downloader_package_not_found_warning">"패키지 이름이 '%s'인 설치된 앱을 찾을 수 없습니다
|
||||
|
||||
패키지 이름이 올바르고 앱이 설치되어 있는지 확인하세요"</string>
|
||||
|
|
|
|||
|
|
@ -57,7 +57,7 @@ Second \"item\" text"</string>
|
|||
<string name="revanced_settings_restart_title">Nepieciešama restartēšana</string>
|
||||
<string name="revanced_settings_restart_dialog_message">Lai šīs izmaiņas stātos spēkā, restartējiet lietotni.</string>
|
||||
<string name="revanced_settings_restart">Restartēt</string>
|
||||
<string name="revanced_settings_import">Importēt</string>
|
||||
<string name="revanced_settings_import">Ievietot</string>
|
||||
<string name="revanced_settings_import_copy">Kopēt</string>
|
||||
<string name="revanced_settings_import_reset">ReVanced iestatījumi atiestatīti uz noklusējuma vērtībām</string>
|
||||
<string name="revanced_settings_import_success">Importēti %d iestatījumi</string>
|
||||
|
|
@ -641,8 +641,8 @@ Ierobežojumi:
|
|||
<string name="revanced_external_downloader_name_summary">Jūsu instalētās ārējās lejupielādētāja lietotnes pakotnes nosaukums</string>
|
||||
<string name="revanced_external_downloader_other_item_hint">Ievadiet pakotnes nosaukumu</string>
|
||||
<string name="revanced_external_downloader_other_item">Cits</string>
|
||||
<string name="revanced_external_downloader_not_found_title">Lietotne nav instalēta</string>
|
||||
<string name="revanced_external_downloader_not_installed_warning">%s nav instalēts. Lūdzu, instalējiet to.</string>
|
||||
<string name="revanced_external_downloader_not_found_title">Lietotne nav uzstādīta</string>
|
||||
<string name="revanced_external_downloader_not_installed_warning">%s nav uzstādīta. Lūdzu, uzstādiet to.</string>
|
||||
<string name="revanced_external_downloader_package_not_found_warning">"Nevarēja atrast instalēto lietotni ar pakotnes nosaukumu: %s
|
||||
|
||||
Pārbaudiet, vai pakotnes nosaukums ir pareizs un lietotne ir instalēta"</string>
|
||||
|
|
|
|||
|
|
@ -25,7 +25,7 @@ Second \"item\" text"</string>
|
|||
<string name="revanced_custom_branding_name_entry_5">Aangepast</string>
|
||||
<string name="revanced_custom_branding_icon_title">App-pictogram</string>
|
||||
<string name="revanced_custom_branding_icon_entry_1">Origineel</string>
|
||||
<string name="revanced_custom_branding_icon_entry_2">shared.layout.branding.baseCustomBrandingPatch.revanced_custom_branding_icon_entry_2</string>
|
||||
<string name="revanced_custom_branding_icon_entry_2">ReVanced</string>
|
||||
<!-- Translation of this should be identical to revanced_header_logo_entry_5 -->
|
||||
<string name="revanced_custom_branding_icon_entry_3">ReVanced minimaal</string>
|
||||
<string name="revanced_custom_branding_icon_entry_4">ReVanced geschaald</string>
|
||||
|
|
@ -33,7 +33,7 @@ Second \"item\" text"</string>
|
|||
<string name="revanced_custom_branding_icon_entry_5">Aangepast</string>
|
||||
</patch>
|
||||
<patch id="misc.checks.checkEnvironmentPatch">
|
||||
<string name="revanced_check_environment_failed_title">Controle mislukt</string>
|
||||
<string name="revanced_check_environment_failed_title">Controles mislukt</string>
|
||||
<string name="revanced_check_environment_dialog_open_official_source_button">Open officiële website</string>
|
||||
<string name="revanced_check_environment_dialog_ignore_button">Negeren</string>
|
||||
<string name="revanced_check_environment_failed_message"><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.</string>
|
||||
|
|
@ -1592,6 +1592,7 @@ Het inschakelen hiervan kan ontbrekende afbeeldingen oplossen die in sommige reg
|
|||
<string name="revanced_alt_thumbnail_options_entry_2">DeArrow & Oorspronkelijke miniaturen</string>
|
||||
<string name="revanced_alt_thumbnail_options_entry_3">DeArrow & Stilstaande opnames</string>
|
||||
<string name="revanced_alt_thumbnail_options_entry_4">Stilstaande opnames</string>
|
||||
<string name="revanced_alt_thumbnail_dearrow_about_title">DeArrow</string>
|
||||
<string name="revanced_alt_thumbnail_dearrow_about_summary">"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.
|
||||
|
|
@ -1635,7 +1636,11 @@ Tik hier om meer te weten te komen over DeArrow"</string>
|
|||
<string name="revanced_loop_video_button_toast_on">Loopvideo is ingeschakeld</string>
|
||||
<string name="revanced_loop_video_button_toast_off">Loopvideo is uitgeschakeld</string>
|
||||
</patch>
|
||||
<patch id="misc.audiofocus.pauseOnAudioInterruptPatch"/>
|
||||
<patch id="misc.audiofocus.pauseOnAudioInterruptPatch">
|
||||
<string name="revanced_pause_on_audio_interrupt_title">Pauzeren bij audio-onderbreking</string>
|
||||
<string name="revanced_pause_on_audio_interrupt_summary_on">Afspelen wordt gepauzeerd wanneer andere audio wordt afgespeeld (bijv. navigatie)</string>
|
||||
<string name="revanced_pause_on_audio_interrupt_summary_off">Volume wordt zachter wanneer andere audio wordt afgespeeld</string>
|
||||
</patch>
|
||||
<patch id="misc.dimensions.spoof.spoofDeviceDimensionsPatch">
|
||||
<string name="revanced_spoof_device_dimensions_title">Spoof apparaatdimensies</string>
|
||||
<string name="revanced_spoof_device_dimensions_summary_on">"Apparaatdimensies gespoofed
|
||||
|
|
@ -1695,6 +1700,7 @@ Het inschakelen hiervan kan hogere videokwaliteiten ontgrendelen"</string>
|
|||
<string name="revanced_shorts_quality_default_wifi_title">Standaardkwaliteit voor Shorts op wifi-netwerk</string>
|
||||
<string name="revanced_shorts_quality_default_mobile_title">Standaardkwaliteit voor Shorts op mobiel netwerk</string>
|
||||
<string name="revanced_remember_video_quality_mobile">mobiel</string>
|
||||
<string name="revanced_remember_video_quality_wifi">wifi</string>
|
||||
<string name="revanced_remember_video_quality_toast">Standaard %1$s-kwaliteit gewijzigd naar: %2$s</string>
|
||||
<string name="revanced_remember_video_quality_toast_shorts">De kwaliteit van Shorts %1$s is gewijzigd in: %2$s</string>
|
||||
</patch>
|
||||
|
|
@ -1789,7 +1795,11 @@ Het afspelen van video met AV1 kan haperen of frames overslaan."</string>
|
|||
</patch>
|
||||
</app>
|
||||
<app id="music">
|
||||
<patch id="layout.branding.customBrandingPatch"/>
|
||||
<patch id="layout.branding.customBrandingPatch">
|
||||
<string name="revanced_custom_branding_name_entry_2">YT Music ReVanced</string>
|
||||
<string name="revanced_custom_branding_name_entry_3">Music ReVanced</string>
|
||||
<string name="revanced_custom_branding_name_entry_4">Music</string>
|
||||
</patch>
|
||||
<patch id="misc.settings.settingsPatch">
|
||||
<!-- In languages where "About" is ambiguous, translate to "About ReVanced" (i.e., About this app). -->
|
||||
<string name="revanced_settings_music_screen_0_about_title">Over</string>
|
||||
|
|
@ -1916,6 +1926,7 @@ Het afspelen van video met AV1 kan haperen of frames overslaan."</string>
|
|||
<string name="revanced_about_summary">Over ReVanced</string>
|
||||
<string name="revanced_ads_screen_title">Advertentieblokkering</string>
|
||||
<string name="revanced_ads_screen_summary">Instellingen advertentieblokkering</string>
|
||||
<string name="revanced_chat_screen_title">Chat</string>
|
||||
<string name="revanced_chat_screen_summary">Chat-instellingen</string>
|
||||
<string name="revanced_misc_screen_title">Overige</string>
|
||||
<string name="revanced_misc_screen_summary">Diverse instellingen</string>
|
||||
|
|
|
|||
|
|
@ -91,7 +91,7 @@ Second \"item\" text"</string>
|
|||
<string name="revanced_language_title">ReVanced 語言</string>
|
||||
<string name="revanced_language_user_dialog_message">"部分語言的翻譯可能缺少或不完整。
|
||||
|
||||
如要翻譯新的語言或改善現有翻譯,請前往 translate.revanced.app"</string>
|
||||
若要翻譯新的語言或改善現有翻譯,請前往 translate.revanced.app"</string>
|
||||
<string name="revanced_language_DEFAULT">應用程式語言</string>
|
||||
<string name="revanced_pref_import_export_title">匯入/匯出</string>
|
||||
<string name="revanced_pref_import_export_summary">匯入/匯出 ReVanced 設定</string>
|
||||
|
|
@ -312,8 +312,8 @@ Second \"item\" text"</string>
|
|||
<string name="revanced_hide_ticket_shelf_summary_off">票券架已顯示</string>
|
||||
<!-- 'People also watched' and 'You might also like' should be translated using the same localized wording YouTube displays. -->
|
||||
<string name="revanced_hide_video_recommendation_labels_title">隱藏影片推薦標籤</string>
|
||||
<string name="revanced_hide_video_recommendation_labels_summary_on">搜尋結果中的「其他人也觀看」與「您可能也喜歡」標籤已隱藏</string>
|
||||
<string name="revanced_hide_video_recommendation_labels_summary_off">搜尋結果中的「其他人也觀看」與「您可能也喜歡」標籤已顯示</string>
|
||||
<string name="revanced_hide_video_recommendation_labels_summary_on">已隱藏搜尋結果中的「其他人也觀看」與「你可能也喜歡」標籤</string>
|
||||
<string name="revanced_hide_video_recommendation_labels_summary_off">已顯示搜尋結果中的「其他人也觀看」與「你可能也喜歡」標籤</string>
|
||||
<string name="revanced_hide_visual_spacer_title">隱藏視覺間隔</string>
|
||||
<string name="revanced_hide_visual_spacer_summary_on">視覺間隔已隱藏</string>
|
||||
<string name="revanced_hide_visual_spacer_summary_off">視覺間隔已顯示</string>
|
||||
|
|
@ -907,9 +907,9 @@ Second \"item\" text"</string>
|
|||
<string name="revanced_hide_player_flyout_video_quality_title">隱藏影片畫質選單</string>
|
||||
<string name="revanced_hide_player_flyout_video_quality_summary_on">影片畫質選單已隱藏</string>
|
||||
<string name="revanced_hide_player_flyout_video_quality_summary_off">影片畫質選單已顯示</string>
|
||||
<string name="revanced_hide_player_flyout_video_quality_footer_title">隱藏影片畫質選單頁尾</string>
|
||||
<string name="revanced_hide_player_flyout_video_quality_footer_summary_on">已隱藏影片畫質選單頁尾</string>
|
||||
<string name="revanced_hide_player_flyout_video_quality_footer_summary_off">已顯示影片畫質選單頁尾</string>
|
||||
<string name="revanced_hide_player_flyout_video_quality_footer_title">隱藏「影片畫質」選單頁尾</string>
|
||||
<string name="revanced_hide_player_flyout_video_quality_footer_summary_on">已隱藏「影片畫質」選單頁尾</string>
|
||||
<string name="revanced_hide_player_flyout_video_quality_footer_summary_off">已顯示「影片畫質」選單頁尾</string>
|
||||
</patch>
|
||||
<patch id="layout.buttons.overlay.hidePlayerOverlayButtonsPatch">
|
||||
<string name="revanced_hide_autoplay_button_title">隱藏「自動播放」按鈕</string>
|
||||
|
|
@ -1670,8 +1670,8 @@ Second \"item\" text"</string>
|
|||
<string name="revanced_disable_haptic_feedback_seek_undo_summary_on">拖曳還原觸覺回饋已停用</string>
|
||||
<string name="revanced_disable_haptic_feedback_seek_undo_summary_off">拖曳還原觸覺回饋已啟用</string>
|
||||
<string name="revanced_disable_haptic_feedback_tap_and_hold_title">停用輕觸並按住觸覺回饋</string>
|
||||
<string name="revanced_disable_haptic_feedback_tap_and_hold_summary_on">輕觸並按住觸覺回饋已停用</string>
|
||||
<string name="revanced_disable_haptic_feedback_tap_and_hold_summary_off">輕觸並按住觸覺回饋已啟用</string>
|
||||
<string name="revanced_disable_haptic_feedback_tap_and_hold_summary_on">已停用輕觸並按住觸覺回饋</string>
|
||||
<string name="revanced_disable_haptic_feedback_tap_and_hold_summary_off">已啟用輕觸並按住觸覺回饋</string>
|
||||
<string name="revanced_disable_haptic_feedback_zoom_title">停用縮放震動</string>
|
||||
<string name="revanced_disable_haptic_feedback_zoom_summary_on">縮放觸覺回饋已停用</string>
|
||||
<string name="revanced_disable_haptic_feedback_zoom_summary_off">縮放觸覺回饋已啟用</string>
|
||||
|
|
@ -1712,12 +1712,12 @@ Second \"item\" text"</string>
|
|||
</patch>
|
||||
<patch id="video.speed.button.playbackSpeedButtonPatch">
|
||||
<string name="revanced_playback_speed_dialog_button_title">顯示速度對話方塊按鈕</string>
|
||||
<string name="revanced_playback_speed_dialog_button_summary_on">速度對話框按鈕已顯示。長按可將播放速度重設為預設值</string>
|
||||
<string name="revanced_playback_speed_dialog_button_summary_on">已顯示速度對話框按鈕。長按可將播放速度重設為預設值</string>
|
||||
<string name="revanced_playback_speed_dialog_button_summary_off">速度對話框按鈕未顯示</string>
|
||||
</patch>
|
||||
<patch id="video.quality.button.videoQualityDialogButtonPatch">
|
||||
<string name="revanced_video_quality_dialog_button_title">顯示畫質切換按鈕</string>
|
||||
<string name="revanced_video_quality_dialog_button_summary_on">影片畫質按鈕已顯示。長按可重設為預設畫質</string>
|
||||
<string name="revanced_video_quality_dialog_button_summary_on">已顯示影片畫質按鈕。長按可重設為預設畫質</string>
|
||||
<string name="revanced_video_quality_dialog_button_summary_off">影片畫質按鈕未顯示</string>
|
||||
</patch>
|
||||
<patch id="video.speed.custom.customPlaybackSpeedPatch">
|
||||
|
|
@ -1791,7 +1791,7 @@ AV1 視訊播放可能會卡頓或丟格。"</string>
|
|||
<string name="revanced_spoof_video_streams_about_playback_failure">• 影片可能會在 1:00 停止,或在某些地區無法播放</string>
|
||||
<string name="revanced_spoof_video_streams_about_no_audio_tracks">• 音軌選單遺失</string>
|
||||
<string name="revanced_spoof_video_streams_about_no_av1">• 沒有 AV1 影片解碼器</string>
|
||||
<string name="revanced_spoof_video_streams_about_no_stable_volume">• 穩定音量無法使用</string>
|
||||
<string name="revanced_spoof_video_streams_about_no_stable_volume">• 無法使用平衡音量</string>
|
||||
<string name="revanced_spoof_video_streams_about_kids_videos">• 在登出或無痕模式下,兒童影片可能無法播放</string>
|
||||
<!-- "Force original audio" should use the same text as revanced_force_original_audio_title -->
|
||||
<string name="revanced_spoof_video_streams_about_no_force_original_audio">• 強制原始音訊不可用</string>
|
||||
|
|
|
|||
|
|
@ -220,14 +220,14 @@
|
|||
</patch>
|
||||
<patch id="misc.fix.playback.spoofVideoStreamsPatch">
|
||||
<string-array name="revanced_spoof_video_streams_client_type_entries">
|
||||
<item>Android Reel</item>
|
||||
<item>Android VR</item>
|
||||
<item>visionOS</item>
|
||||
<item>Android No SDK</item>
|
||||
</string-array>
|
||||
<string-array name="revanced_spoof_video_streams_client_type_entry_values">
|
||||
<item>ANDROID_REEL</item>
|
||||
<item>ANDROID_VR_1_43_32</item>
|
||||
<item>VISIONOS</item>
|
||||
<item>ANDROID_NO_SDK</item>
|
||||
</string-array>
|
||||
</patch>
|
||||
</app>
|
||||
|
|
@ -264,18 +264,16 @@
|
|||
</patch>
|
||||
<patch id="misc.fix.playback.spoofVideoStreamsPatch">
|
||||
<string-array name="revanced_spoof_video_streams_client_type_entries">
|
||||
<item>Android Reel</item>
|
||||
<item>Android VR</item>
|
||||
<item>Android Studio</item>
|
||||
<item>Android No SDK</item>
|
||||
<item>visionOS</item>
|
||||
<item>iPadOS</item>
|
||||
</string-array>
|
||||
<string-array name="revanced_spoof_video_streams_client_type_entry_values">
|
||||
<item>ANDROID_REEL</item>
|
||||
<item>ANDROID_VR_1_43_32</item>
|
||||
<item>ANDROID_CREATOR</item>
|
||||
<item>ANDROID_NO_SDK</item>
|
||||
<item>VISIONOS</item>
|
||||
<item>IPADOS</item>
|
||||
</string-array>
|
||||
</patch>
|
||||
<patch id="interaction.swipecontrols.swipeControlsResourcePatch">
|
||||
|
|
@ -663,8 +661,7 @@
|
|||
<item>cross-out</item>
|
||||
</string-array>
|
||||
</patch>
|
||||
<patch id="chat.autoclaim.autoClaimChannelPointsPatch">
|
||||
</patch>
|
||||
<patch id="chat.autoclaim.autoClaimChannelPointsPatch"></patch>
|
||||
<patch id="ad.embedded.embeddedAdsPatch">
|
||||
<string-array name="revanced_block_embedded_ads_entries">
|
||||
<item>@string/revanced_block_embedded_ads_entry_1</item>
|
||||
|
|
|
|||
|
|
@ -144,6 +144,16 @@ If you are a YouTube Premium user, this setting may not be required"</string>
|
|||
Playback may not work"</string>
|
||||
<string name="revanced_spoof_video_streams_user_dialog_message">Turning off this setting may cause playback issues.</string>
|
||||
<string name="revanced_spoof_video_streams_client_type_title">Default client</string>
|
||||
|
||||
<string name="revanced_spoof_video_streams_about_experimental">• Experimental client and may stop working anytime</string>
|
||||
<string name="revanced_spoof_video_streams_about_playback_failure">• Video may stop at 1:00, or may not be available in some regions</string>
|
||||
<string name="revanced_spoof_video_streams_about_no_audio_tracks">• Audio track menu is missing</string>
|
||||
<string name="revanced_spoof_video_streams_about_no_av1">• No AV1 video codec</string>
|
||||
<string name="revanced_spoof_video_streams_about_no_immersive_mode">• 360° VR immersive mode is not available</string>
|
||||
<string name="revanced_spoof_video_streams_about_no_stable_volume">• Stable volume is not available</string>
|
||||
<string name="revanced_spoof_video_streams_about_title">Spoofing side effects</string>
|
||||
<!-- "Force original audio" should use the same text as revanced_force_original_audio_title -->
|
||||
<string name="revanced_spoof_video_streams_about_no_force_original_audio">• Force original audio is not available</string>
|
||||
</patch>
|
||||
<patch id="misc.audio.forceOriginalAudioPatch">
|
||||
<string name="revanced_force_original_audio_title">Force original audio language</string>
|
||||
|
|
@ -900,10 +910,10 @@ Adjust volume by swiping vertically on the right side of the screen"</string>
|
|||
<string name="revanced_hide_player_flyout_audio_track_summary_on">Audio track menu is hidden</string>
|
||||
<string name="revanced_hide_player_flyout_audio_track_summary_off">Audio track menu is shown</string>
|
||||
<!-- 'Spoof video streams' should be the same translation used for 'revanced_spoof_video_streams_screen_title'.
|
||||
'Android No SDK' must be kept untranslated. -->
|
||||
'Android Reel' must be kept untranslated. -->
|
||||
<string name="revanced_hide_player_flyout_audio_track_not_available">"Audio track menu is hidden
|
||||
|
||||
To show the Audio track menu, change \'Spoof video streams\' to \'Android No SDK\'"</string>
|
||||
To show the Audio track menu, change \'Spoof video streams\' to \'Android >Reel\'"</string>
|
||||
<!-- 'Watch in VR' should be translated using the same localized wording YouTube displays for the menu item. -->
|
||||
<string name="revanced_hide_player_flyout_watch_in_vr_title">Hide Watch in VR</string>
|
||||
<string name="revanced_hide_player_flyout_watch_in_vr_summary_on">Watch in VR menu is hidden</string>
|
||||
|
|
@ -1786,18 +1796,9 @@ Playback may stutter or drop frames"</string>
|
|||
<string name="revanced_spoof_video_streams_av1_user_dialog_message">"Enabling this setting may use software AV1 decoding.
|
||||
|
||||
Video playback with AV1 may stutter or drop frames."</string>
|
||||
<string name="revanced_spoof_video_streams_about_title">Spoofing side effects</string>
|
||||
<string name="revanced_spoof_video_streams_about_experimental">• Experimental client and may stop working anytime</string>
|
||||
<string name="revanced_spoof_video_streams_about_playback_failure">• Video may stop at 1:00, or may not be available in some regions</string>
|
||||
<string name="revanced_spoof_video_streams_about_no_audio_tracks">• Audio track menu is missing</string>
|
||||
<string name="revanced_spoof_video_streams_about_no_av1">• No AV1 video codec</string>
|
||||
<string name="revanced_spoof_video_streams_about_no_stable_volume">• Stable volume is not available</string>
|
||||
<string name="revanced_spoof_video_streams_about_kids_videos">• Kids videos may not play when logged out or in incognito mode</string>
|
||||
<!-- "Force original audio" should use the same text as revanced_force_original_audio_title -->
|
||||
<string name="revanced_spoof_video_streams_about_no_force_original_audio">• Force original audio is not available</string>
|
||||
<string name="revanced_spoof_streaming_data_stats_for_nerds_title">Show in Stats for nerds</string>
|
||||
<string name="revanced_spoof_streaming_data_stats_for_nerds_summary_on">Client type is shown in Stats for nerds</string>
|
||||
<string name="revanced_spoof_streaming_data_stats_for_nerds_summary_off">Client is hidden in Stats for nerds</string>
|
||||
<string name="revanced_spoof_video_streams_stats_for_nerds_title">Show in Stats for nerds</string>
|
||||
<string name="revanced_spoof_video_streams_stats_for_nerds_summary_on">Client type is shown in Stats for nerds</string>
|
||||
<string name="revanced_spoof_video_streams_stats_for_nerds_summary_off">Client is hidden in Stats for nerds</string>
|
||||
</patch>
|
||||
</app>
|
||||
<app id="music">
|
||||
|
|
|
|||
|
|
@ -2,7 +2,6 @@ rootProject.name = "revanced-patches"
|
|||
|
||||
pluginManagement {
|
||||
repositories {
|
||||
mavenLocal()
|
||||
gradlePluginPortal()
|
||||
google()
|
||||
maven {
|
||||
|
|
@ -10,12 +9,16 @@ 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") }
|
||||
}
|
||||
}
|
||||
|
||||
dependencyResolutionManagement {
|
||||
repositories {
|
||||
mavenLocal()
|
||||
// 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}")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -33,4 +36,4 @@ settings {
|
|||
}
|
||||
}
|
||||
|
||||
include(":patches:stub")
|
||||
include(":patches:stub")
|
||||
Loading…
Add table
Add a link
Reference in a new issue