Compare commits

..

1 commit

Author SHA1 Message Date
PlayDay
462702c174 fix(all/packagename): Fix package name validation and provider authority matching
Allow uppercase letters in package name validation regex, since valid Java package names can start with uppercase.
Broaden provider authority replacement to also match authorities that contain or end with the original package name, not just those prefixed by it.
2026-03-16 12:26:00 +01:00
51 changed files with 370 additions and 1145 deletions

View file

@ -32,7 +32,6 @@ jobs:
- name: Process strings
run: |
chmod -R 777 patches/src/main/resources
./gradlew processStringsFromCrowdin
env:
ORG_GRADLE_PROJECT_githubPackagesUsername: ${{ github.actor }}

View file

@ -1,61 +1,3 @@
# [6.1.0](https://github.com/ReVanced/revanced-patches/compare/v6.0.1...v6.1.0) (2026-03-18)
### Bug Fixes
* **Export internal data documents provider:** Correct S_IFLNK constant and symlink detection mask ([#6819](https://github.com/ReVanced/revanced-patches/issues/6819)) ([252617b](https://github.com/ReVanced/revanced-patches/commit/252617b8dd3f24e1ff9a04ba1d91b43dc29bd757))
* **YouTube - Custom branding:** Fix double icons and change default branding to ReVanced ([#6806](https://github.com/ReVanced/revanced-patches/issues/6806)) ([e51c529](https://github.com/ReVanced/revanced-patches/commit/e51c5292c171325e7cfa0f5ee85474d9b3961a34))
### Features
* Add `Spoof root of trust` and `Spoof keystore security level` patch ([#6751](https://github.com/ReVanced/revanced-patches/issues/6751)) ([4bc8c7c](https://github.com/ReVanced/revanced-patches/commit/4bc8c7c0f60a095533f07dc281f0320f8eb22f3c))
* **Announcements:** Support ReVanced API v5 announcements ([a05386e](https://github.com/ReVanced/revanced-patches/commit/a05386e8bc24c085b5c74f3674c402c5dd5ad468))
* Change contact email in patches about ([df1c3a4](https://github.com/ReVanced/revanced-patches/commit/df1c3a4a70fd2595d77b539299f1f7301bc60d24))
* **Instagram:** Add `Enable location sticker redesign` patch ([#6808](https://github.com/ReVanced/revanced-patches/issues/6808)) ([4b699da](https://github.com/ReVanced/revanced-patches/commit/4b699da220e5d1527c390792b6228e2d9cffedb7))
* **Spoof video streams:** Add Android Reel client to fix playback issues ([#6830](https://github.com/ReVanced/revanced-patches/issues/6830)) ([4b6c3e3](https://github.com/ReVanced/revanced-patches/commit/4b6c3e312328fbf6a1c7065e27d8ff04573e58be))
# [6.1.0-dev.4](https://github.com/ReVanced/revanced-patches/compare/v6.1.0-dev.3...v6.1.0-dev.4) (2026-03-18)
### Bug Fixes
* **YouTube - Custom branding:** Fix double icons and change default branding to ReVanced ([#6806](https://github.com/ReVanced/revanced-patches/issues/6806)) ([e51c529](https://github.com/ReVanced/revanced-patches/commit/e51c5292c171325e7cfa0f5ee85474d9b3961a34))
### Features
* Add `Spoof root of trust` and `Spoof keystore security level` patch ([#6751](https://github.com/ReVanced/revanced-patches/issues/6751)) ([4bc8c7c](https://github.com/ReVanced/revanced-patches/commit/4bc8c7c0f60a095533f07dc281f0320f8eb22f3c))
* **Instagram:** Add `Enable location sticker redesign` patch ([#6808](https://github.com/ReVanced/revanced-patches/issues/6808)) ([4b699da](https://github.com/ReVanced/revanced-patches/commit/4b699da220e5d1527c390792b6228e2d9cffedb7))
# [6.1.0-dev.3](https://github.com/ReVanced/revanced-patches/compare/v6.1.0-dev.2...v6.1.0-dev.3) (2026-03-18)
### Features
* **Spoof video streams:** Add Android Reel client to fix playback issues ([#6830](https://github.com/ReVanced/revanced-patches/issues/6830)) ([4b6c3e3](https://github.com/ReVanced/revanced-patches/commit/4b6c3e312328fbf6a1c7065e27d8ff04573e58be))
# [6.1.0-dev.2](https://github.com/ReVanced/revanced-patches/compare/v6.1.0-dev.1...v6.1.0-dev.2) (2026-03-17)
### Features
* **Announcements:** Support ReVanced API v5 announcements ([a05386e](https://github.com/ReVanced/revanced-patches/commit/a05386e8bc24c085b5c74f3674c402c5dd5ad468))
# [6.1.0-dev.1](https://github.com/ReVanced/revanced-patches/compare/v6.0.2-dev.1...v6.1.0-dev.1) (2026-03-16)
### Features
* Change contact email in patches about ([df1c3a4](https://github.com/ReVanced/revanced-patches/commit/df1c3a4a70fd2595d77b539299f1f7301bc60d24))
## [6.0.2-dev.1](https://github.com/ReVanced/revanced-patches/compare/v6.0.1...v6.0.2-dev.1) (2026-03-16)
### Bug Fixes
* **Export internal data documents provider:** Correct S_IFLNK constant and symlink detection mask ([#6819](https://github.com/ReVanced/revanced-patches/issues/6819)) ([252617b](https://github.com/ReVanced/revanced-patches/commit/252617b8dd3f24e1ff9a04ba1d91b43dc29bd757))
## [6.0.1](https://github.com/ReVanced/revanced-patches/compare/v6.0.0...v6.0.1) (2026-03-15)

View file

@ -31,10 +31,7 @@ public class InternalDataDocumentsProvider extends DocumentsProvider {
private static final String[] directoryColumns =
{"document_id", "mime_type", "_display_name", "last_modified", "flags",
"_size", "full_path", "lstat_info"};
@SuppressWarnings("OctalInteger")
private static final int S_IFMT = 0170000;
@SuppressWarnings("OctalInteger")
private static final int S_IFLNK = 0120000;
private static final int S_IFLNK = 0x8000;
private String packageName;
private File dataDirectory;
@ -50,7 +47,7 @@ public class InternalDataDocumentsProvider extends DocumentsProvider {
if (root.isDirectory()) {
try {
// Only delete recursively if the directory is not a symlink
if ((Os.lstat(root.getPath()).st_mode & S_IFMT) != S_IFLNK) {
if ((Os.lstat(root.getPath()).st_mode & S_IFLNK) != S_IFLNK) {
File[] files = root.listFiles();
if (files != null) {
for (File file : files) {
@ -327,7 +324,7 @@ public class InternalDataDocumentsProvider extends DocumentsProvider {
sb.append(";");
sb.append(lstat.st_gid);
// Append symlink target if it is a symlink
if ((lstat.st_mode & S_IFMT) == S_IFLNK) {
if ((lstat.st_mode & S_IFLNK) == S_IFLNK) {
sb.append(";");
sb.append(Os.readlink(path));
}

View file

@ -1,4 +1,4 @@
package app.revanced.extension.play;
package app.revanced.extension.playintegrity;
import android.content.Context;
import android.content.Intent;

View file

@ -1,7 +1,7 @@
package app.revanced.extension.music.patches.spoof;
import static app.revanced.extension.music.settings.Settings.SPOOF_VIDEO_STREAMS_CLIENT_TYPE;
import static app.revanced.extension.shared.spoof.ClientType.ANDROID_REEL;
import static app.revanced.extension.shared.spoof.ClientType.ANDROID_NO_SDK;
import static app.revanced.extension.shared.spoof.ClientType.ANDROID_VR_1_43_32;
import static app.revanced.extension.shared.spoof.ClientType.ANDROID_VR_1_61_48;
import static app.revanced.extension.shared.spoof.ClientType.VISIONOS;
@ -18,8 +18,8 @@ public class SpoofVideoStreamsPatch {
*/
public static void setClientOrderToUse() {
List<ClientType> availableClients = List.of(
ANDROID_REEL,
ANDROID_VR_1_43_32,
ANDROID_NO_SDK,
VISIONOS,
ANDROID_VR_1_61_48
);

View file

@ -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_REEL, true, parent(SPOOF_VIDEO_STREAMS));
ClientType.ANDROID_VR_1_43_32, true, parent(SPOOF_VIDEO_STREAMS));
public static final BooleanSetting FORCE_ORIGINAL_AUDIO = new BooleanSetting("revanced_force_original_audio", TRUE, true);
}

View file

@ -1,5 +1,5 @@
plugins {
alias(libs.plugins.android.library)
alias(libs.plugins.android.library)
}
android {
@ -19,6 +19,4 @@ android {
dependencies {
compileOnly(libs.annotation)
compileOnly(libs.okhttp)
compileOnly(libs.protobuf.javalite)
implementation(project(":extensions:shared:protobuf", configuration = "shadowRuntimeElements"))
}

View file

@ -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()
: 2;
: 1;
}
public static BrandingTheme getDefaultIconStyle() {
return userProvidedCustomIcon()
? BrandingTheme.CUSTOM
: BrandingTheme.ROUNDED;
: BrandingTheme.ORIGINAL;
}
/**

View file

@ -47,7 +47,7 @@ public class BaseSettings {
//
public static final BooleanSetting SPOOF_VIDEO_STREAMS = new BooleanSetting("revanced_spoof_video_streams", TRUE, true, "revanced_spoof_video_streams_user_dialog_message");
public static final BooleanSetting SPOOF_STREAMING_DATA_STATS_FOR_NERDS = new BooleanSetting("revanced_spoof_video_streams_stats_for_nerds", TRUE, parent(SPOOF_VIDEO_STREAMS));
public static final BooleanSetting SPOOF_STREAMING_DATA_STATS_FOR_NERDS = new BooleanSetting("revanced_spoof_streaming_data_stats_for_nerds", TRUE, parent(SPOOF_VIDEO_STREAMS));
public static final BooleanSetting SANITIZE_SHARING_LINKS = new BooleanSetting("revanced_sanitize_sharing_links", TRUE);
public static final BooleanSetting REPLACE_MUSIC_LINKS_WITH_YOUTUBE = new BooleanSetting("revanced_replace_music_with_youtube", FALSE);

View file

@ -9,34 +9,9 @@ import java.util.Locale;
import java.util.Objects;
import app.revanced.extension.shared.Logger;
import app.revanced.extension.shared.Utils;
@SuppressWarnings("ConstantLocale")
public enum ClientType {
/**
* Video not playable: Paid, Movie, Private, Age-restricted.
* Uses non-adaptive bitrate.
* AV1 codec available.
*/
ANDROID_REEL(
3,
"ANDROID",
"com.google.android.youtube",
Build.MANUFACTURER,
Build.MODEL,
"Android",
Build.VERSION.RELEASE,
String.valueOf(Build.VERSION.SDK_INT),
Build.ID,
"20.44.38",
// This client has been used by most open-source YouTube stream extraction tools since 2024, including NewPipe Extractor, SmartTube, and Grayjay.
// This client can log in, but if an access token is used in the request, GVS can more easily identify the request as coming from ReVanced.
// This means that the GVS server can strengthen its validation of the ANDROID_REEL client.
true,
true,
false,
"Android Reel"
),
/**
* Video not playable: Kids / Paid / Movie / Private / Age-restricted.
* This client can only be used when logged out.
@ -53,10 +28,10 @@ public enum ClientType {
// Android 12.1
"32",
"SQ3A.220605.009.A1",
"132.0.6808.3",
"1.61.48",
false,
false,
true,
"Android VR 1.61"
),
/**
@ -73,12 +48,39 @@ public enum ClientType {
ANDROID_VR_1_61_48.osVersion,
Objects.requireNonNull(ANDROID_VR_1_61_48.androidSdkVersion),
Objects.requireNonNull(ANDROID_VR_1_61_48.buildId),
"107.0.5284.2",
"1.43.32",
ANDROID_VR_1_61_48.useAuth,
ANDROID_VR_1_61_48.supportsMultiAudioTracks,
ANDROID_VR_1_61_48.usePlayerEndpoint,
"Android VR 1.43"
),
/**
* Video not playable: Paid / Movie / Private / Age-restricted.
* Note: The 'Authorization' key must be excluded from the header.
*
* According to TeamNewPipe in 2022, if the 'androidSdkVersion' field is missing,
* the GVS did not return a valid response:
* [NewPipe#8713 (comment)](https://github.com/TeamNewPipe/NewPipe/issues/8713#issuecomment-1207443550).
*
* According to the latest commit in yt-dlp, the GVS returns a valid response
* even if the 'androidSdkVersion' field is missing:
* [yt-dlp#14693](https://github.com/yt-dlp/yt-dlp/pull/14693).
*
* For some reason, PoToken is not required.
*/
ANDROID_NO_SDK(
3,
"ANDROID",
"",
"",
"",
Build.VERSION.RELEASE,
"20.05.46",
"com.google.android.youtube/20.05.46 (Linux; U; Android " + Build.VERSION.RELEASE + ") gzip",
false,
true,
"Android No SDK"
),
/**
* Cannot play livestreams and lacks HDR, but can play videos with music and labeled "for children".
* <a href="https://dumps.tadiphone.dev/dumps/google/barbet">Google Pixel 9 Pro Fold</a>
@ -93,10 +95,10 @@ public enum ClientType {
"15",
"35",
"AP3A.241005.015.A2",
"132.0.6779.0",
"23.47.101",
true,
false,
true,
"Android Studio"
),
/**
@ -112,8 +114,32 @@ public enum ClientType {
"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/18.0 Safari/605.1.15",
false,
false,
true,
"visionOS"
),
/**
* The device machine id for the iPad 6th Gen (iPad7,6).
* AV1 hardware decoding is not supported.
* See [this GitHub Gist](https://gist.github.com/adamawolf/3048717) for more information.
*
* Based on Google's actions to date, PoToken may not be required on devices with very low specs.
* For example, suppose the User-Agent for a PlayStation 3 (with 256MB of RAM) is used.
* Accessing 'Web' (https://www.youtube.com) will redirect to 'TV' (https://www.youtube.com/tv).
* 'TV' target devices with very low specs, such as embedded devices, game consoles, and blu-ray players, so PoToken is not required.
*
* For this reason, the device machine id for the iPad 6th Gen (with 2GB of RAM),
* the lowest spec device capable of running iPadOS 17, was used.
*/
IPADOS(5,
"IOS",
"Apple",
"iPad7,6",
"iPadOS",
"17.7.10.21H450",
"19.22.3",
"com.google.ios.youtube/19.22.3 (iPad7,6; U; CPU iPadOS 17_7_10 like Mac OS X; " + Locale.getDefault() + ")",
false,
true,
"iPadOS"
);
/**
@ -169,6 +195,13 @@ public enum ClientType {
@Nullable
private final String buildId;
/**
* Cronet release version, as found in decompiled client apk.
* Field is null if not applicable.
*/
@Nullable
private final String cronetVersion;
/**
* App version.
*/
@ -184,11 +217,6 @@ public enum ClientType {
*/
public final boolean supportsMultiAudioTracks;
/**
* If the client should use the player endpoint for stream extraction.
*/
public final boolean usePlayerEndpoint;
/**
* Friendly name displayed in stats for nerds.
*/
@ -206,10 +234,10 @@ public enum ClientType {
String osVersion,
@NonNull String androidSdkVersion,
@NonNull String buildId,
@NonNull String cronetVersion,
String clientVersion,
boolean useAuth,
boolean supportsMultiAudioTracks,
boolean usePlayerEndpoint,
String friendlyName) {
this.id = id;
this.clientName = clientName;
@ -220,20 +248,21 @@ public enum ClientType {
this.osVersion = osVersion;
this.androidSdkVersion = androidSdkVersion;
this.buildId = buildId;
this.cronetVersion = cronetVersion;
this.clientVersion = clientVersion;
this.useAuth = useAuth;
this.supportsMultiAudioTracks = supportsMultiAudioTracks;
this.usePlayerEndpoint = usePlayerEndpoint;
this.friendlyName = friendlyName;
Locale defaultLocale = Locale.getDefault();
this.userAgent = String.format("%s/%s (Linux; U; Android %s; %s; %s; Build/%s)",
this.userAgent = String.format("%s/%s (Linux; U; Android %s; %s; %s; Build/%s; Cronet/%s)",
packageName,
clientVersion,
osVersion,
defaultLocale,
deviceModel,
buildId
Objects.requireNonNull(buildId),
Objects.requireNonNull(cronetVersion)
);
Logger.printDebug(() -> "userAgent: " + this.userAgent);
}
@ -249,7 +278,6 @@ public enum ClientType {
String userAgent,
boolean useAuth,
boolean supportsMultiAudioTracks,
boolean usePlayerEndpoint,
String friendlyName) {
this.id = id;
this.clientName = clientName;
@ -261,10 +289,10 @@ public enum ClientType {
this.userAgent = userAgent;
this.useAuth = useAuth;
this.supportsMultiAudioTracks = supportsMultiAudioTracks;
this.usePlayerEndpoint = usePlayerEndpoint;
this.friendlyName = friendlyName;
this.packageName = null;
this.androidSdkVersion = null;
this.buildId = null;
this.cronetVersion = null;
}
}

View file

@ -5,6 +5,7 @@ import android.text.TextUtils;
import androidx.annotation.Nullable;
import java.nio.ByteBuffer;
import java.util.List;
import java.util.Map;
import java.util.Objects;
@ -38,7 +39,7 @@ public class SpoofVideoStreamsPatch {
@Nullable
private static volatile AppLanguage languageOverride;
private static volatile ClientType preferredClient = ClientType.ANDROID_REEL;
private static volatile ClientType preferredClient = ClientType.ANDROID_VR_1_43_32;
/**
* @return If this patch was included during patching.
@ -249,7 +250,7 @@ public class SpoofVideoStreamsPatch {
* Called after {@link #fetchStreams(String, Map)}.
*/
@Nullable
public static byte[] getStreamingData(String videoId) {
public static ByteBuffer getStreamingData(String videoId) {
if (SPOOF_STREAMING_DATA) {
try {
StreamingDataRequest request = StreamingDataRequest.getRequestForVideoId(videoId);

View file

@ -15,20 +15,13 @@ import app.revanced.extension.shared.spoof.ClientType;
import app.revanced.extension.shared.spoof.SpoofVideoStreamsPatch;
final class PlayerRoutes {
static final Route.CompiledRoute GET_PLAYER_STREAMING_DATA = new Route(
static final Route.CompiledRoute GET_STREAMING_DATA = new Route(
Route.Method.POST,
"player" +
"?fields=streamingData" +
"&alt=proto"
).compile();
static final Route.CompiledRoute GET_REEL_STREAMING_DATA = new Route(
Route.Method.POST,
"reel/reel_item_watch" +
"?fields=playerResponse.playabilityStatus,playerResponse.streamingData" +
"&alt=proto"
).compile();
private static final String YT_API_URL = "https://youtubei.googleapis.com/youtubei/v1/";
/**
@ -54,7 +47,6 @@ final class PlayerRoutes {
Locale streamLocale = language.getLocale();
JSONObject client = new JSONObject();
client.put("deviceMake", clientType.deviceMake);
client.put("deviceModel", clientType.deviceModel);
client.put("clientName", clientType.clientName);
@ -69,19 +61,9 @@ final class PlayerRoutes {
context.put("client", client);
innerTubeBody.put("context", context);
if (clientType.usePlayerEndpoint) {
innerTubeBody.put("contentCheckOk", true);
innerTubeBody.put("racyCheckOk", true);
innerTubeBody.put("videoId", videoId);
} else {
JSONObject playerRequest = new JSONObject();
playerRequest.put("contentCheckOk", true);
playerRequest.put("racyCheckOk", true);
playerRequest.put("videoId", videoId);
innerTubeBody.put("playerRequest", playerRequest);
innerTubeBody.put("disablePlayerResponse", false);
}
innerTubeBody.put("contentCheckOk", true);
innerTubeBody.put("racyCheckOk", true);
innerTubeBody.put("videoId", videoId);
} catch (JSONException e) {
Logger.printException(() -> "Failed to create innerTubeBody", e);
}

View file

@ -1,17 +1,18 @@
package app.revanced.extension.shared.spoof.requests;
import static app.revanced.extension.shared.ByteTrieSearch.convertStringsToBytes;
import static app.revanced.extension.shared.Utils.isNotEmpty;
import static app.revanced.extension.shared.spoof.requests.PlayerRoutes.GET_PLAYER_STREAMING_DATA;
import static app.revanced.extension.shared.spoof.requests.PlayerRoutes.GET_REEL_STREAMING_DATA;
import static app.revanced.extension.shared.spoof.requests.PlayerRoutes.GET_STREAMING_DATA;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import java.io.BufferedInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.HttpURLConnection;
import java.net.SocketTimeoutException;
import java.nio.ByteBuffer;
import java.nio.charset.StandardCharsets;
import java.util.Arrays;
import java.util.Collections;
@ -26,11 +27,6 @@ import java.util.concurrent.TimeoutException;
import app.revanced.extension.shared.ByteTrieSearch;
import app.revanced.extension.shared.Logger;
import app.revanced.extension.shared.Utils;
import app.revanced.extension.shared.innertube.PlayerResponseOuterClass;
import app.revanced.extension.shared.innertube.PlayerResponseOuterClass.PlayerResponse;
import app.revanced.extension.shared.innertube.PlayerResponseOuterClass.StreamingData;
import app.revanced.extension.shared.innertube.ReelItemWatchResponseOuterClass.ReelItemWatchResponse;
import app.revanced.extension.shared.requests.Route;
import app.revanced.extension.shared.settings.BaseSettings;
import app.revanced.extension.shared.spoof.ClientType;
@ -45,7 +41,7 @@ import app.revanced.extension.shared.spoof.ClientType;
*/
public class StreamingDataRequest {
private static volatile ClientType[] clientOrderToUse = ClientType.values();
private static volatile ClientType[] clientOrderToUse = ClientType.values();
public static void setClientOrderToUse(List<ClientType> availableClients, ClientType preferredClient) {
Objects.requireNonNull(preferredClient);
@ -115,7 +111,7 @@ public class StreamingDataRequest {
private final String videoId;
private final Future<byte[]> future;
private final Future<ByteBuffer> future;
private StreamingDataRequest(String videoId, Map<String, String> playerHeaders) {
Objects.requireNonNull(playerHeaders);
@ -138,12 +134,6 @@ public class StreamingDataRequest {
Logger.printInfo(() -> toastMessage, ex);
}
private static void handleDebugToast(String toastMessage, ClientType clientType) {
if (BaseSettings.DEBUG.get() && BaseSettings.DEBUG_TOAST_ON_ERROR.get()) {
Utils.showToastShort(String.format(toastMessage, clientType));
}
}
@Nullable
private static HttpURLConnection send(ClientType clientType,
String videoId,
@ -156,10 +146,7 @@ public class StreamingDataRequest {
final long startTime = System.currentTimeMillis();
try {
Route.CompiledRoute route = clientType.usePlayerEndpoint ?
GET_PLAYER_STREAMING_DATA : GET_REEL_STREAMING_DATA;
HttpURLConnection connection = PlayerRoutes.getPlayerResponseConnectionFromRoute(route, clientType);
HttpURLConnection connection = PlayerRoutes.getPlayerResponseConnectionFromRoute(GET_STREAMING_DATA, clientType);
connection.setConnectTimeout(HTTP_TIMEOUT_MILLISECONDS);
connection.setReadTimeout(HTTP_TIMEOUT_MILLISECONDS);
@ -216,7 +203,7 @@ public class StreamingDataRequest {
return null;
}
private static byte[] fetch(String videoId, Map<String, String> playerHeaders) {
private static ByteBuffer fetch(String videoId, Map<String, String> playerHeaders) {
final boolean debugEnabled = BaseSettings.DEBUG.get();
// Retry with different client if empty response body is received.
@ -227,11 +214,33 @@ public class StreamingDataRequest {
HttpURLConnection connection = send(clientType, videoId, playerHeaders, showErrorToast);
if (connection != null) {
byte[] playerResponseBuffer = buildPlayerResponseBuffer(clientType, connection);
if (playerResponseBuffer != null) {
lastSpoofedClientType = clientType;
try {
// gzip encoding doesn't response with content length (-1),
// but empty response body does.
if (connection.getContentLength() == 0) {
if (BaseSettings.DEBUG.get() && BaseSettings.DEBUG_TOAST_ON_ERROR.get()) {
Utils.showToastShort("Debug: Ignoring empty spoof stream client " + clientType);
}
} else {
try (InputStream inputStream = new BufferedInputStream(connection.getInputStream());
ByteArrayOutputStream baos = new ByteArrayOutputStream()) {
return playerResponseBuffer;
byte[] buffer = new byte[2048];
int bytesRead;
while ((bytesRead = inputStream.read(buffer)) >= 0) {
baos.write(buffer, 0, bytesRead);
}
if (clientType == ClientType.ANDROID_CREATOR && liveStreamBufferSearch.matches(buffer)) {
Logger.printDebug(() -> "Skipping Android Studio as video is a livestream: " + videoId);
} else {
lastSpoofedClientType = clientType;
return ByteBuffer.wrap(baos.toByteArray());
}
}
}
} catch (IOException ex) {
Logger.printException(() -> "Fetch failed while processing response data", ex);
}
}
}
@ -241,61 +250,12 @@ public class StreamingDataRequest {
return null;
}
@Nullable
private static byte[] buildPlayerResponseBuffer(ClientType clientType,
HttpURLConnection connection) {
// gzip encoding doesn't response with content length (-1),
// but empty response body does.
if (connection.getContentLength() == 0) {
handleDebugToast("Debug: Ignoring empty spoof stream client (%s)", clientType);
return null;
}
try (InputStream inputStream = connection.getInputStream()) {
PlayerResponse playerResponse = clientType.usePlayerEndpoint
? PlayerResponse.parseFrom(inputStream)
: ReelItemWatchResponse.parseFrom(inputStream).getPlayerResponse();
var playabilityStatus = playerResponse.getPlayabilityStatus();
if (playabilityStatus.getStatus() != PlayerResponseOuterClass.Status.OK) {
handleDebugToast("Debug: Ignoring unplayable video (%s)", clientType);
String reason = playabilityStatus.getReason();
if (isNotEmpty(reason)) {
Logger.printDebug(() -> String.format("Debug: Ignoring unplayable video (%s), reason: %s", clientType, reason));
}
return null;
}
PlayerResponse.Builder responseBuilder = playerResponse.toBuilder();
if (!playerResponse.hasStreamingData()) {
handleDebugToast("Debug: Ignoring empty streaming data (%s)", clientType);
return null;
}
// Android Studio only supports the HLS protocol for live streams.
// HLS protocol can theoretically be played with ExoPlayer,
// but the related code has not yet been implemented.
// If DASH protocol is not available, the client will be skipped.
StreamingData streamingData = playerResponse.getStreamingData();
if (streamingData.getAdaptiveFormatsCount() == 0) {
handleDebugToast("Debug: Ignoring empty adaptiveFormat (%s)", clientType);
return null;
}
return responseBuilder.build().toByteArray();
} catch (IOException ex) {
Logger.printException(() -> "Failed to write player response to buffer array", ex);
return null;
}
}
public boolean fetchCompleted() {
return future.isDone();
}
@Nullable
public byte[] getStream() {
public ByteBuffer getStream() {
try {
return future.get(MAX_MILLISECONDS_TO_WAIT_FOR_FETCH, TimeUnit.MILLISECONDS);
} catch (TimeoutException ex) {

View file

@ -1,55 +0,0 @@
import com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar
plugins {
kotlin("jvm")
alias(libs.plugins.protobuf)
alias(libs.plugins.shadow)
}
val shade: Configuration by configurations.creating {
configurations.getByName("compileClasspath").extendsFrom(this)
configurations.getByName("runtimeClasspath").extendsFrom(this)
}
dependencies {
compileOnly(libs.annotation)
compileOnly(libs.okhttp)
shade(libs.protobuf.javalite)
}
sourceSets {
// Make sure generated proto sources are compiled and end up in the shaded jar
main {
java.srcDir("$buildDir/generated/source/proto/main/java")
}
}
protobuf {
protoc {
artifact = libs.protobuf.protoc.get().toString()
}
generateProtoTasks {
all().forEach { task ->
task.builtins {
named("java") {
option("lite")
}
}
}
}
}
val shadowJar = tasks.named<ShadowJar>("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) } }

View file

@ -1,42 +0,0 @@
syntax = "proto3";
package app.revanced.extension.shared.innertube;
option optimize_for = LITE_RUNTIME;
option java_package = "app.revanced.extension.shared.innertube";
message PlayerResponse {
oneof data {
PlayabilityStatus playability_status = 2;
StreamingData streaming_data = 4;
}
}
message PlayabilityStatus {
Status status = 1;
string reason = 2;
}
enum Status {
OK = 0;
ERROR = 1;
UNPLAYABLE = 2;
LOGIN_REQUIRED = 3;
CONTENT_CHECK_REQUIRED = 4;
AGE_CHECK_REQUIRED = 5;
LIVE_STREAM_OFFLINE = 6;
FULLSCREEN_ONLY = 7;
GL_PLAYBACK_REQUIRED = 8;
AGE_VERIFICATION_REQUIRED = 9;
}
message StreamingData {
repeated Format formats = 2;
repeated Format adaptiveFormats = 3;
string serverAbrStreamingUrl = 15;
}
message Format {
string url = 2;
string signatureCipher = 48;
}

View file

@ -1,14 +0,0 @@
syntax = "proto3";
import "app/revanced/extension/shared/innertube/player_response.proto";
package app.revanced.extension.shared.innertube;
option optimize_for = LITE_RUNTIME;
option java_package = "app.revanced.extension.shared.innertube";
message ReelItemWatchResponse {
oneof data {
PlayerResponse player_response = 4;
}
}

View file

@ -14,9 +14,7 @@ import android.widget.LinearLayout;
import android.widget.TextView;
import app.revanced.extension.shared.ui.CustomDialog;
import org.json.JSONArray;
import org.json.JSONObject;
import java.io.IOException;
import java.net.HttpURLConnection;
@ -31,20 +29,9 @@ import app.revanced.extension.youtube.settings.Settings;
@SuppressWarnings("unused")
public final class AnnouncementsPatch {
private static final String[] ANNOUNCEMENT_TAGS = {
"\uD83C\uDF9E\uFE0F YouTube",
};
private AnnouncementsPatch() {
}
private static boolean hasSupportedTag(String wrapperTag) {
if (wrapperTag == null) return false;
for (var tag : ANNOUNCEMENT_TAGS) if (tag.equals(wrapperTag)) return true;
return false;
}
private static boolean isLatestAlready() throws IOException {
HttpURLConnection connection =
AnnouncementsRoutes.getAnnouncementsConnectionFromRoute(GET_LATEST_ANNOUNCEMENT_IDS);
@ -69,30 +56,19 @@ public final class AnnouncementsPatch {
var jsonString = Requester.parseStringAndDisconnect(connection);
var id = -1;
// Parse the ID. Fall-back to raw string if it fails.
int id = Settings.ANNOUNCEMENT_LAST_ID.defaultValue;
try {
final var announcementIDTagPairs = new JSONArray(jsonString);
if (announcementIDTagPairs.length() == 0) return true;
JSONObject latest = null;
for (int i = 0, entryCount = announcementIDTagPairs.length(); i < entryCount; i++) {
var pair = announcementIDTagPairs.optJSONObject(i);
if (pair != null && hasSupportedTag(pair.optString("tag", null))) {
latest = pair;
break;
}
}
if (latest == null || latest.isNull("id")) return true;
id = latest.getInt("id");
final var announcementIDs = new JSONArray(jsonString);
if (announcementIDs.length() == 0) return true;
id = announcementIDs.getJSONObject(0).getInt("id");
} catch (Throwable ex) {
Logger.printException(() -> "Failed to parse announcement ID", ex);
return true;
}
// Do not show the announcement, if the last announcement id is the same as the current one.
return Settings.ANNOUNCEMENT_LAST_ID.get().equals(id);
return Settings.ANNOUNCEMENT_LAST_ID.get() == id;
}
public static void showAnnouncement(final Activity context) {
@ -119,22 +95,7 @@ public final class AnnouncementsPatch {
LocalDateTime archivedAt = LocalDateTime.MAX;
Level level = Level.INFO;
try {
final var announcements = new JSONArray(jsonString);
JSONObject latestAnnouncement = null;
for (int i = 0, entryCount = announcements.length(); i < entryCount; i++) {
var announcementTagPair = announcements.optJSONObject(i);
if (announcementTagPair != null && hasSupportedTag(announcementTagPair.optString("tag", null))) {
latestAnnouncement = announcementTagPair;
break;
}
}
if (latestAnnouncement == null || latestAnnouncement.isNull("announcement")) {
Logger.printDebug(() -> "No YouTube announcement found in latest announcements response");
return;
}
final var announcement = latestAnnouncement.getJSONObject("announcement");
final var announcement = new JSONArray(jsonString).getJSONObject(0);
id = announcement.getInt("id");
title = announcement.getString("title");

View file

@ -9,9 +9,9 @@ import java.net.HttpURLConnection;
import static app.revanced.extension.shared.requests.Route.Method.GET;
public class AnnouncementsRoutes {
private static final String ANNOUNCEMENTS_PROVIDER = "https://api.revanced.app/v5";
public static final Route GET_LATEST_ANNOUNCEMENT_IDS = new Route(GET, "/announcements/latest/id");
public static final Route GET_LATEST_ANNOUNCEMENTS = new Route(GET, "/announcements/latest");
private static final String ANNOUNCEMENTS_PROVIDER = "https://api.revanced.app/v4";
public static final Route GET_LATEST_ANNOUNCEMENT_IDS = new Route(GET, "/announcements/latest/id?tag=\uD83C\uDF9E\uFE0F%20YouTube");
public static final Route GET_LATEST_ANNOUNCEMENTS = new Route(GET, "/announcements/latest?tag=\uD83C\uDF9E\uFE0F%20YouTube");
private AnnouncementsRoutes() {
}

View file

@ -1,9 +1,10 @@
package app.revanced.extension.youtube.patches.spoof;
import static app.revanced.extension.shared.spoof.ClientType.ANDROID_CREATOR;
import static app.revanced.extension.shared.spoof.ClientType.ANDROID_NO_SDK;
import static app.revanced.extension.shared.spoof.ClientType.ANDROID_VR_1_43_32;
import static app.revanced.extension.shared.spoof.ClientType.ANDROID_VR_1_61_48;
import static app.revanced.extension.shared.spoof.ClientType.ANDROID_REEL;
import static app.revanced.extension.shared.spoof.ClientType.IPADOS;
import static app.revanced.extension.shared.spoof.ClientType.VISIONOS;
import java.util.List;
@ -43,11 +44,11 @@ public class SpoofVideoStreamsPatch {
}
List<ClientType> availableClients = List.of(
ANDROID_REEL,
ANDROID_VR_1_43_32,
VISIONOS,
ANDROID_CREATOR
);
ANDROID_CREATOR,
ANDROID_VR_1_43_32,
ANDROID_NO_SDK,
IPADOS);
app.revanced.extension.shared.spoof.SpoofVideoStreamsPatch.setClientsToUse(
availableClients, client);

View file

@ -19,7 +19,7 @@ import app.revanced.extension.shared.spoof.ClientType;
import app.revanced.extension.youtube.settings.Settings;
@SuppressWarnings({"deprecation", "unused"})
public class SpoofVideoStreamsSideEffectsPreference extends Preference {
public class SpoofStreamingDataSideEffectsPreference extends Preference {
@Nullable
private ClientType currentClientType;
@ -33,19 +33,19 @@ public class SpoofVideoStreamsSideEffectsPreference extends Preference {
Utils.runOnMainThread(this::updateUI);
};
public SpoofVideoStreamsSideEffectsPreference(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
public SpoofStreamingDataSideEffectsPreference(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
super(context, attrs, defStyleAttr, defStyleRes);
}
public SpoofVideoStreamsSideEffectsPreference(Context context, AttributeSet attrs, int defStyleAttr) {
public SpoofStreamingDataSideEffectsPreference(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
public SpoofVideoStreamsSideEffectsPreference(Context context, AttributeSet attrs) {
public SpoofStreamingDataSideEffectsPreference(Context context, AttributeSet attrs) {
super(context, attrs);
}
public SpoofVideoStreamsSideEffectsPreference(Context context) {
public SpoofStreamingDataSideEffectsPreference(Context context) {
super(context);
}
@ -88,23 +88,27 @@ public class SpoofVideoStreamsSideEffectsPreference extends Preference {
+ '\n' + str("revanced_spoof_video_streams_about_no_stable_volume")
+ '\n' + str("revanced_spoof_video_streams_about_no_av1")
+ '\n' + str("revanced_spoof_video_streams_about_no_force_original_audio");
case ANDROID_REEL ->
summary = str("revanced_spoof_video_streams_about_playback_failure");
// VR 1.61 is not exposed in the UI and should never be reached here.
case ANDROID_VR_1_43_32, ANDROID_VR_1_61_48 ->
summary = str("revanced_spoof_video_streams_about_playback_failure")
+ '\n' + str("revanced_spoof_video_streams_about_no_audio_tracks")
summary = str("revanced_spoof_video_streams_about_no_audio_tracks")
+ '\n' + str("revanced_spoof_video_streams_about_no_stable_volume");
case VISIONOS -> summary = str("revanced_spoof_video_streams_about_experimental")
+ '\n' + str("revanced_spoof_video_streams_about_playback_failure")
+ '\n' + str("revanced_spoof_video_streams_about_no_audio_tracks")
+ '\n' + str("revanced_spoof_video_streams_about_no_av1");
case ANDROID_NO_SDK ->
summary = str("revanced_spoof_video_streams_about_playback_failure");
case IPADOS ->
summary = str("revanced_spoof_video_streams_about_playback_failure")
+ '\n' + str("revanced_spoof_video_streams_about_no_av1");
case VISIONOS ->
summary = str("revanced_spoof_video_streams_about_experimental")
+ '\n' + str("revanced_spoof_video_streams_about_no_audio_tracks")
+ '\n' + str("revanced_spoof_video_streams_about_no_av1");
default -> Logger.printException(() -> "Unknown client: " + clientType);
}
// Only Android Reel and Android VR supports 360° VR immersive mode.
if (!clientType.name().startsWith("ANDROID_VR") && clientType != ClientType.ANDROID_REEL) {
summary += '\n' + str("revanced_spoof_video_streams_about_no_immersive_mode");
// Only iPadOS can play children videos in incognito, but it commonly fails at 1 minute
// or doesn't start playback at all. List the side effect for other clients
// since they will fall over to iPadOS.
if (clientType != ClientType.IPADOS && clientType != ClientType.ANDROID_NO_SDK) {
summary += '\n' + str("revanced_spoof_video_streams_about_kids_videos");
}
// Use better formatting for bullet points.

View file

@ -4,4 +4,4 @@ org.gradle.parallel = true
android.useAndroidX = true
android.uniquePackageNames = false
kotlin.code.style = official
version = 6.1.0
version = 6.0.1

View file

@ -10,10 +10,6 @@ okhttp = "5.3.2"
retrofit = "3.0.0"
guava = "33.5.0-jre"
apksig = "9.0.1"
# TODO: Adjust once https://github.com/google/protobuf-gradle-plugin/pull/797 is merged.
protobuf = "master-SNAPSHOT"
protoc = "4.34.0"
shadow = "9.4.0"
[libraries]
annotation = { module = "androidx.annotation:annotation", version.ref = "annotation" }
@ -22,10 +18,6 @@ okhttp = { module = "com.squareup.okhttp3:okhttp", version.ref = "okhttp" }
retrofit = { module = "com.squareup.retrofit2:retrofit", version.ref = "retrofit" }
guava = { module = "com.google.guava:guava", version.ref = "guava" }
apksig = { group = "com.android.tools.build", name = "apksig", version.ref = "apksig" }
protobuf-javalite = { module = "com.google.protobuf:protobuf-javalite", version.ref = "protoc" }
protobuf-protoc = { module = "com.google.protobuf:protoc", version.ref = "protoc" }
[plugins]
android-library = { id = "com.android.library" }
protobuf = { id = "com.google.protobuf", version.ref = "protobuf" }
shadow = { id = "com.gradleup.shadow", version.ref = "shadow" }

View file

@ -89,14 +89,10 @@ public final class app/revanced/patches/all/misc/packagename/ChangePackageNamePa
public static final fun setOrGetFallbackPackageName (Ljava/lang/String;)Ljava/lang/String;
}
public final class app/revanced/patches/all/misc/play/DisablePlayIntegrityKt {
public final class app/revanced/patches/all/misc/playintegrity/DisablePlayIntegrityKt {
public static final fun getDisablePlayIntegrityPatch ()Lapp/revanced/patcher/patch/Patch;
}
public final class app/revanced/patches/all/misc/play/SpoofPlayAgeSignalsKt {
public static final fun getSpoofPlayAgeSignalsPatch ()Lapp/revanced/patcher/patch/Patch;
}
public final class app/revanced/patches/all/misc/resources/AddResourcesPatchKt {
public static final fun addResource (Ljava/lang/String;Lapp/revanced/util/resource/BaseResource;)Z
public static final fun addResources (Lapp/revanced/patcher/patch/Patch;Lkotlin/jvm/functions/Function1;)Z
@ -129,14 +125,6 @@ public final class app/revanced/patches/all/misc/spoof/EnableRomSignatureSpoofin
public static final fun getEnableROMSignatureSpoofingPatch ()Lapp/revanced/patcher/patch/Patch;
}
public final class app/revanced/patches/all/misc/spoof/SpoofKeystoreSecurityLevelPatchKt {
public static final fun getSpoofKeystoreSecurityLevelPatch ()Lapp/revanced/patcher/patch/Patch;
}
public final class app/revanced/patches/all/misc/spoof/SpoofRootOfTrustPatchKt {
public static final fun getSpoofRootOfTrustPatch ()Lapp/revanced/patcher/patch/Patch;
}
public final class app/revanced/patches/all/misc/targetSdk/SetTargetSdkVersion34Kt {
public static final fun getSetTargetSDKVersion34Patch ()Lapp/revanced/patcher/patch/Patch;
}
@ -377,10 +365,6 @@ public final class app/revanced/patches/instagram/story/flipping/DisableStoryAut
public static final fun getDisableStoryAutoFlippingPatch ()Lapp/revanced/patcher/patch/Patch;
}
public final class app/revanced/patches/instagram/story/locationsticker/EnableLocationStickerRedesignPatchKt {
public static final fun getEnableLocationStickerRedesignPatch ()Lapp/revanced/patcher/patch/Patch;
}
public final class app/revanced/patches/irplus/ad/RemoveAdsPatchKt {
public static final fun getRemoveAdsPatch ()Lapp/revanced/patcher/patch/Patch;
}

View file

@ -6,7 +6,7 @@ patches {
description = "Patches for ReVanced"
source = "git@github.com:revanced/revanced-patches.git"
author = "ReVanced"
contact = "patches@revanced.app"
contact = "contact@revanced.app"
website = "https://revanced.app"
license = "GNU General Public License v3.0"
}

View file

@ -6,8 +6,7 @@ import app.revanced.patcher.extensions.replaceInstruction
import app.revanced.patcher.patch.bytecodePatch
import app.revanced.patcher.patch.intOption
import app.revanced.patcher.patch.stringOption
import app.revanced.util.forEachInstructionAsSequence
import com.android.tools.smali.dexlib2.iface.instruction.Instruction
import app.revanced.patches.all.misc.transformation.transformInstructionsPatch
import com.android.tools.smali.dexlib2.iface.instruction.OneRegisterInstruction
import com.android.tools.smali.dexlib2.iface.instruction.ReferenceInstruction
import com.android.tools.smali.dexlib2.iface.reference.MethodReference
@ -34,8 +33,7 @@ val spoofSIMProviderPatch = bytecodePatch(
validator = { it: String? -> it == null || it.uppercase() in countries.values },
)
fun isMccMncValid(it: Int?) = it == null || (it in 10000..999999)
fun isNumericValid(it: String?, length: Int) = it.isNullOrBlank() || it.equals("random", true) || it.length == length
fun isMccMncValid(it: Int?): Boolean = it == null || (it >= 10000 && it <= 999999)
val networkCountryIso by isoCountryPatchOption("Network ISO country code")
@ -63,119 +61,46 @@ val spoofSIMProviderPatch = bytecodePatch(
description = "The full name of the SIM operator.",
)
val imei by stringOption(
name = "IMEI value",
description = "15-digit IMEI to spoof, blank to skip, or 'random'.",
validator = { isNumericValid(it, 15) },
)
val meid by stringOption(
name = "MEID value",
description = "14-char hex MEID to spoof, blank to skip, or 'random'.",
validator = { isNumericValid(it, 14) },
)
val imsi by stringOption(
name = "IMSI (Subscriber ID)",
description = "15-digit IMSI to spoof, blank to skip, or 'random'.",
validator = { isNumericValid(it, 15) },
)
val iccid by stringOption(
name = "ICCID (SIM Serial)",
description = "19-digit ICCID to spoof, blank to skip, or 'random'.",
validator = { isNumericValid(it, 19) },
)
val phone by stringOption(
name = "Phone number",
description = "Phone number to spoof, blank to skip, or 'random'.",
validator = { it.isNullOrBlank() || it.equals("random", ignoreCase = true) || it.startsWith("+") },
)
dependsOn(
bytecodePatch {
apply {
fun generateRandomNumeric(length: Int) = (1..length).map { ('0'..'9').random() }.joinToString("")
transformInstructionsPatch(
filterMap = { _, _, instruction, instructionIndex ->
if (instruction !is ReferenceInstruction) return@transformInstructionsPatch null
fun String?.computeSpoof(randomizer: () -> String): String? {
if (this.isNullOrBlank()) return null
if (this.equals("random", ignoreCase = true)) return randomizer()
return this
val reference = instruction.reference as? MethodReference ?: return@transformInstructionsPatch null
val match = MethodCall.entries.firstOrNull { search ->
MethodUtil.methodSignaturesMatch(reference, search.reference)
} ?: return@transformInstructionsPatch null
val replacement = when (match) {
MethodCall.NetworkCountryIso -> networkCountryIso?.lowercase()
MethodCall.NetworkOperator -> networkOperator?.toString()
MethodCall.NetworkOperatorName -> networkOperatorName
MethodCall.SimCountryIso -> simCountryIso?.lowercase()
MethodCall.SimOperator -> simOperator?.toString()
MethodCall.SimOperatorName -> simOperatorName
}
// Calculate the Luhn checksum (mod 10) to generate a valid 15th digit, standard for IMEI numbers.
// Structure of an IMEI is as follows:
// TAC (Type Allocation Code): First 8 digits (e.g., "86" + 6 digits)
// SNR (Serial Number): Next 6 digits
// CD (Check Digit): The 15th digit
val computedImei = imei.computeSpoof {
val prefix = "86" + generateRandomNumeric(12)
val sum = prefix.mapIndexed { i, c ->
var d = c.digitToInt()
// Double every second digit (index 1, 3, 5...).
if (i % 2 != 0) {
d *= 2
// If result is two digits (e.g. 14), sum them (1+4=5).
// This is mathematically equivalent to d - 9.
if (d > 9) d -= 9
}
d
}.sum()
// Append the calculated check digit to the 14-digit prefix.
prefix + ((10 - (sum % 10)) % 10)
}
val computedMeid = meid.computeSpoof { (1..14).map { "0123456789ABCDEF".random() }.joinToString("") }?.uppercase()
val computedImsi = imsi.computeSpoof { generateRandomNumeric(15) }
val computedIccid = iccid.computeSpoof { "89" + generateRandomNumeric(17) }
val computedPhone = phone.computeSpoof { "+" + generateRandomNumeric(11) }
forEachInstructionAsSequence(
match = { _, _, instruction, instructionIndex ->
if (instruction !is ReferenceInstruction) return@forEachInstructionAsSequence null
val reference = instruction.reference as? MethodReference ?: return@forEachInstructionAsSequence null
val match = MethodCall.entries.firstOrNull { search ->
MethodUtil.methodSignaturesMatch(reference, search.reference)
} ?: return@forEachInstructionAsSequence null
val replacement = when (match) {
MethodCall.NetworkCountryIso -> networkCountryIso?.lowercase()
MethodCall.NetworkOperator -> networkOperator?.toString()
MethodCall.NetworkOperatorName -> networkOperatorName
MethodCall.SimCountryIso -> simCountryIso?.lowercase()
MethodCall.SimOperator -> simOperator?.toString()
MethodCall.SimOperatorName -> simOperatorName
MethodCall.Imei, MethodCall.ImeiWithSlot, MethodCall.DeviceId, MethodCall.DeviceIdWithSlot -> computedImei
MethodCall.Meid, MethodCall.MeidWithSlot -> computedMeid
MethodCall.SubscriberId, MethodCall.SubscriberIdWithSlot -> computedImsi
MethodCall.SimSerialNumber, MethodCall.SimSerialNumberWithSlot -> computedIccid
MethodCall.Line1Number, MethodCall.Line1NumberWithSlot -> computedPhone
}
replacement?.let { instructionIndex to it }
},
transform = ::transformMethodCall
)
}
},
replacement?.let { instructionIndex to it }
},
transform = ::transformMethodCall,
),
)
}
private fun transformMethodCall(mutableMethod: MutableMethod, entry: Pair<Int, String>) {
val (index, value) = entry
val nextInstr = mutableMethod.getInstruction<Instruction>(index + 1)
private fun transformMethodCall(
mutableMethod: MutableMethod,
entry: Pair<Int, String>,
) {
val (instructionIndex, methodCallValue) = entry
if (nextInstr.opcode.name != "move-result-object") {
mutableMethod.replaceInstruction(index, "nop")
return
}
// Get the register which would have contained the return value
val register = mutableMethod.getInstruction<OneRegisterInstruction>(instructionIndex + 1).registerA
val register = (nextInstr as OneRegisterInstruction).registerA
mutableMethod.replaceInstruction(index, "const-string v$register, \"$value\"")
mutableMethod.replaceInstruction(index + 1, "nop")
// Replace the move-result instruction with our fake value
mutableMethod.replaceInstruction(
instructionIndex + 1,
"const-string v$register, \"$methodCallValue\"",
)
}
private enum class MethodCall(
@ -229,100 +154,4 @@ private enum class MethodCall(
"Ljava/lang/String;",
),
),
Imei(
ImmutableMethodReference(
"Landroid/telephony/TelephonyManager;",
"getImei",
emptyList(),
"Ljava/lang/String;"
),
),
ImeiWithSlot(
ImmutableMethodReference(
"Landroid/telephony/TelephonyManager;",
"getImei",
listOf("I"),
"Ljava/lang/String;"
),
),
DeviceId(
ImmutableMethodReference(
"Landroid/telephony/TelephonyManager;",
"getDeviceId",
emptyList(),
"Ljava/lang/String;"
),
),
DeviceIdWithSlot(
ImmutableMethodReference(
"Landroid/telephony/TelephonyManager;",
"getDeviceId",
listOf("I"),
"Ljava/lang/String;"
),
),
Meid(
ImmutableMethodReference(
"Landroid/telephony/TelephonyManager;",
"getMeid",
emptyList(),
"Ljava/lang/String;"
),
),
MeidWithSlot(
ImmutableMethodReference(
"Landroid/telephony/TelephonyManager;",
"getMeid",
listOf("I"),
"Ljava/lang/String;"
),
),
SubscriberId(
ImmutableMethodReference(
"Landroid/telephony/TelephonyManager;",
"getSubscriberId",
emptyList(),
"Ljava/lang/String;"
)
),
SubscriberIdWithSlot(
ImmutableMethodReference(
"Landroid/telephony/TelephonyManager;",
"getSubscriberId",
listOf("I"),
"Ljava/lang/String;"
)
),
SimSerialNumber(
ImmutableMethodReference(
"Landroid/telephony/TelephonyManager;",
"getSimSerialNumber",
emptyList(),
"Ljava/lang/String;"
)
),
SimSerialNumberWithSlot(
ImmutableMethodReference(
"Landroid/telephony/TelephonyManager;",
"getSimSerialNumber",
listOf("I"),
"Ljava/lang/String;"
)
),
Line1Number(
ImmutableMethodReference(
"Landroid/telephony/TelephonyManager;",
"getLine1Number",
emptyList(),
"Ljava/lang/String;"
)
),
Line1NumberWithSlot(
ImmutableMethodReference(
"Landroid/telephony/TelephonyManager;",
"getLine1Number",
listOf("I"),
"Ljava/lang/String;"
)
)
}

View file

@ -3,7 +3,7 @@ package app.revanced.patches.all.misc.connectivity.wifi.spoof
import app.revanced.patcher.patch.bytecodePatch
import app.revanced.patches.all.misc.transformation.IMethodCall
import app.revanced.patches.all.misc.transformation.filterMapInstruction35c
import app.revanced.util.forEachInstructionAsSequence
import app.revanced.patches.all.misc.transformation.transformInstructionsPatch
private const val EXTENSION_CLASS_DESCRIPTOR_PREFIX =
"Lapp/revanced/extension/all/misc/connectivity/wifi/spoof/SpoofWifiPatch"
@ -19,32 +19,29 @@ val spoofWiFiConnectionPatch = bytecodePatch(
extendWith("extensions/all/misc/connectivity/wifi/spoof/spoof-wifi.rve")
dependsOn(
bytecodePatch {
apply {
forEachInstructionAsSequence(
match = { classDef, _, instruction, instructionIndex ->
filterMapInstruction35c<MethodCall>(
EXTENSION_CLASS_DESCRIPTOR_PREFIX,
classDef,
instruction,
instructionIndex,
)
},
transform = { method, entry ->
val (methodType, instruction, instructionIndex) = entry
methodType.replaceInvokeVirtualWithExtension(
EXTENSION_CLASS_DESCRIPTOR,
method,
instruction,
instructionIndex,
)
})
}
},
transformInstructionsPatch(
filterMap = { classDef, _, instruction, instructionIndex ->
filterMapInstruction35c<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,
@ -92,13 +89,13 @@ private enum class MethodCall(
"Landroid/net/NetworkInfo;",
"getState",
arrayOf(),
$$"Landroid/net/NetworkInfo$State;",
"Landroid/net/NetworkInfo\$State;",
),
GetDetailedState(
"Landroid/net/NetworkInfo;",
"getDetailedState",
arrayOf(),
$$"Landroid/net/NetworkInfo$DetailedState;",
"Landroid/net/NetworkInfo\$DetailedState;",
),
IsActiveNetworkMetered(
"Landroid/net/ConnectivityManager;",
@ -135,7 +132,7 @@ private enum class MethodCall(
"registerBestMatchingNetworkCallback",
arrayOf(
"Landroid/net/NetworkRequest;",
$$"Landroid/net/ConnectivityManager$NetworkCallback;",
"Landroid/net/ConnectivityManager\$NetworkCallback;",
"Landroid/os/Handler;",
),
"V",
@ -143,19 +140,19 @@ private enum class MethodCall(
RegisterDefaultNetworkCallback1(
"Landroid/net/ConnectivityManager;",
"registerDefaultNetworkCallback",
arrayOf($$"Landroid/net/ConnectivityManager$NetworkCallback;"),
arrayOf("Landroid/net/ConnectivityManager\$NetworkCallback;"),
"V",
),
RegisterDefaultNetworkCallback2(
"Landroid/net/ConnectivityManager;",
"registerDefaultNetworkCallback",
arrayOf($$"Landroid/net/ConnectivityManager$NetworkCallback;", "Landroid/os/Handler;"),
arrayOf("Landroid/net/ConnectivityManager\$NetworkCallback;", "Landroid/os/Handler;"),
"V",
),
RegisterNetworkCallback1(
"Landroid/net/ConnectivityManager;",
"registerNetworkCallback",
arrayOf("Landroid/net/NetworkRequest;", $$"Landroid/net/ConnectivityManager$NetworkCallback;"),
arrayOf("Landroid/net/NetworkRequest;", "Landroid/net/ConnectivityManager\$NetworkCallback;"),
"V",
),
RegisterNetworkCallback2(
@ -169,7 +166,7 @@ private enum class MethodCall(
"registerNetworkCallback",
arrayOf(
"Landroid/net/NetworkRequest;",
$$"Landroid/net/ConnectivityManager$NetworkCallback;",
"Landroid/net/ConnectivityManager\$NetworkCallback;",
"Landroid/os/Handler;",
),
"V",
@ -177,13 +174,13 @@ private enum class MethodCall(
RequestNetwork1(
"Landroid/net/ConnectivityManager;",
"requestNetwork",
arrayOf("Landroid/net/NetworkRequest;", $$"Landroid/net/ConnectivityManager$NetworkCallback;"),
arrayOf("Landroid/net/NetworkRequest;", "Landroid/net/ConnectivityManager\$NetworkCallback;"),
"V",
),
RequestNetwork2(
"Landroid/net/ConnectivityManager;",
"requestNetwork",
arrayOf("Landroid/net/NetworkRequest;", $$"Landroid/net/ConnectivityManager$NetworkCallback;", "I"),
arrayOf("Landroid/net/NetworkRequest;", "Landroid/net/ConnectivityManager\$NetworkCallback;", "I"),
"V",
),
RequestNetwork3(
@ -191,7 +188,7 @@ private enum class MethodCall(
"requestNetwork",
arrayOf(
"Landroid/net/NetworkRequest;",
$$"Landroid/net/ConnectivityManager$NetworkCallback;",
"Landroid/net/ConnectivityManager\$NetworkCallback;",
"Landroid/os/Handler;",
),
"V",
@ -207,7 +204,7 @@ private enum class MethodCall(
"requestNetwork",
arrayOf(
"Landroid/net/NetworkRequest;",
$$"Landroid/net/ConnectivityManager$NetworkCallback;",
"Landroid/net/ConnectivityManager\$NetworkCallback;",
"Landroid/os/Handler;",
"I",
),
@ -216,7 +213,7 @@ private enum class MethodCall(
UnregisterNetworkCallback1(
"Landroid/net/ConnectivityManager;",
"unregisterNetworkCallback",
arrayOf($$"Landroid/net/ConnectivityManager$NetworkCallback;"),
arrayOf("Landroid/net/ConnectivityManager\$NetworkCallback;"),
"V",
),
UnregisterNetworkCallback2(

View file

@ -1,6 +1,9 @@
package app.revanced.patches.all.misc.packagename
import app.revanced.patcher.patch.*
import app.revanced.patcher.patch.Option
import app.revanced.patcher.patch.booleanOption
import app.revanced.patcher.patch.resourcePatch
import app.revanced.patcher.patch.stringOption
import app.revanced.util.asSequence
import app.revanced.util.getNode
import org.w3c.dom.Element
@ -13,7 +16,7 @@ private val packageNameOption = stringOption(
description = "The name of the package to rename the app to.",
required = true,
) {
it == "Default" || it!!.matches(Regex("^[a-z]\\w*(\\.[a-z]\\w*)+\$"))
it == "Default" || it!!.matches(Regex("^[a-zA-Z]\\w*(\\.[a-zA-Z]\\w*)+$"))
}
/**
@ -111,7 +114,7 @@ val changePackageNamePatch = resourcePatch(
val provider = node as Element
val authorities = provider.getAttribute("android:authorities")
if (!authorities.startsWith("$packageName.")) continue
if ("$packageName." !in authorities && !authorities.endsWith(packageName)) continue
provider.setAttribute("android:authorities", authorities.replace(packageName, newPackageName))
}

View file

@ -1,138 +0,0 @@
package app.revanced.patches.all.misc.play
import app.revanced.patcher.extensions.addInstructions
import app.revanced.patcher.extensions.getInstruction
import app.revanced.patcher.extensions.removeInstructions
import app.revanced.patcher.patch.bytecodePatch
import app.revanced.patcher.patch.intOption
import app.revanced.patcher.patch.option
import app.revanced.util.forEachInstructionAsSequence
import app.revanced.util.getReference
import com.android.tools.smali.dexlib2.iface.instruction.OneRegisterInstruction
import com.android.tools.smali.dexlib2.iface.instruction.ReferenceInstruction
import com.android.tools.smali.dexlib2.iface.reference.MethodReference
import com.android.tools.smali.dexlib2.immutable.reference.ImmutableMethodReference
@Suppress("unused")
val spoofPlayAgeSignalsPatch = bytecodePatch(
name = "Spoof Play Age Signals",
description = "Spoofs Google Play data about the user's age and verification status.",
use = false,
) {
val lowerAgeBound by intOption(
name = "Lower age bound",
description = "A positive integer.",
default = 18,
validator = { it == null || it > 0 },
)
val upperAgeBound by intOption(
name = "Upper age bound",
description = "A positive integer. Must be greater than the lower age bound.",
default = Int.MAX_VALUE,
validator = { it == null || it > lowerAgeBound!! },
)
val userStatus by intOption(
name = "User status",
description = "An integer representing the user status.",
default = UserStatus.VERIFIED.value,
values = UserStatus.entries.associate { it.name to it.value },
)
apply {
forEachInstructionAsSequence(match = { classDef, _, instruction, instructionIndex ->
// Avoid patching the library itself.
if (classDef.type.startsWith("Lcom/google/android/play/agesignals/")) return@forEachInstructionAsSequence null
// Keep method calls only.
val reference = instruction.getReference<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),
}

View file

@ -1,4 +1,4 @@
package app.revanced.patches.all.misc.play
package app.revanced.patches.all.misc.playintegrity
import app.revanced.patcher.extensions.replaceInstruction
import app.revanced.patcher.patch.bytecodePatch
@ -9,7 +9,7 @@ import com.android.tools.smali.dexlib2.iface.reference.MethodReference
import com.android.tools.smali.dexlib2.immutable.reference.ImmutableMethodReference
import com.android.tools.smali.dexlib2.util.MethodUtil
private const val EXTENSION_CLASS_DESCRIPTOR = "Lapp/revanced/extension/play/DisablePlayIntegrityPatch;"
private const val EXTENSION_CLASS_DESCRIPTOR = "Lapp/revanced/extension/playintegrity/DisablePlayIntegrityPatch;"
private val CONTEXT_BIND_SERVICE_METHOD_REFERENCE = ImmutableMethodReference(
"Landroid/content/Context;",

View file

@ -1,28 +0,0 @@
package app.revanced.patches.all.misc.spoof
import app.revanced.patcher.extensions.replaceInstructions
import app.revanced.patcher.patch.bytecodePatch
import app.revanced.util.forEachInstructionAsSequence
@Suppress("unused")
val spoofKeystoreSecurityLevelPatch = bytecodePatch(
name = "Spoof keystore security level",
description = "Forces apps to see Keymaster and Attestation security levels as 'StrongBox' (Level 2).",
use = false
) {
apply {
forEachInstructionAsSequence(
match = { _, method, _, _ ->
// Match methods by comparing the current method to a reference criteria.
val name = method.name.lowercase()
if (name.contains("securitylevel") && method.returnType == "I") method else null
},
transform = { mutableMethod, _ ->
// Ensure the method has an implementation before replacing.
if (mutableMethod.implementation?.instructions?.iterator()?.hasNext() == true) {
mutableMethod.replaceInstructions(0, "const/4 v0, 0x2\nreturn v0")
}
}
)
}
}

View file

@ -1,70 +0,0 @@
package app.revanced.patches.all.misc.spoof
import app.revanced.patcher.extensions.replaceInstructions
import app.revanced.patcher.patch.bytecodePatch
import app.revanced.util.forEachInstructionAsSequence
import com.android.tools.smali.dexlib2.iface.reference.MethodReference
import com.android.tools.smali.dexlib2.immutable.reference.ImmutableMethodReference
import com.android.tools.smali.dexlib2.util.MethodUtil
@Suppress("unused")
val spoofRootOfTrustPatch = bytecodePatch(
name = "Spoof root of trust",
description = "Spoofs device integrity states (Locked Bootloader, Verified OS) for apps that perform local certificate attestation.",
use = false
) {
apply {
forEachInstructionAsSequence(
match = { _, method, _, _ ->
MethodCall.entries.firstOrNull { MethodUtil.methodSignaturesMatch(method, it.reference) }
},
transform = { mutableMethod, methodCall ->
if (mutableMethod.implementation?.instructions?.iterator()?.hasNext() == true) {
mutableMethod.replaceInstructions(0, methodCall.replacementInstructions)
}
}
)
}
}
private enum class MethodCall(
val reference: MethodReference,
val replacementInstructions: String,
) {
IsDeviceLockedRootOfTrust(
ImmutableMethodReference(
"LRootOfTrust;",
"isDeviceLocked",
emptyList(),
"Z"
),
"const/4 v0, 0x1\nreturn v0",
),
GetVerifiedBootStateRootOfTrust(
ImmutableMethodReference(
"LRootOfTrust;",
"getVerifiedBootState",
emptyList(),
"I"
),
"const/4 v0, 0x0\nreturn v0",
),
IsDeviceLockedAttestation(
ImmutableMethodReference(
"LAttestation;",
"isDeviceLocked",
emptyList(),
"Z"
),
"const/4 v0, 0x1\nreturn v0",
),
GetVerifiedBootStateAttestation(
ImmutableMethodReference(
"LAttestation;",
"getVerifiedBootState",
emptyList(),
"I"
),
"const/4 v0, 0x0\nreturn v0",
),
}

View file

@ -1,20 +0,0 @@
package app.revanced.patches.instagram.story.locationsticker
import app.revanced.patcher.patch.bytecodePatch
import app.revanced.util.returnEarly
@Suppress("unused")
val enableLocationStickerRedesignPatch = bytecodePatch(
name = "Enable location sticker redesign",
description = "Unlocks the redesigned location sticker with additional style options.",
use = false,
) {
compatibleWith("com.instagram.android")
apply {
// The gate method reads a MobileConfig boolean flag and returns it directly.
// Returning early with true bypasses the flag check entirely,
// enabling the redesigned sticker styles regardless of server configuration.
locationStickerRedesignGateMethodMatch.method.returnEarly(true)
}
}

View file

@ -1,16 +0,0 @@
package app.revanced.patches.instagram.story.locationsticker
import app.revanced.patcher.composingFirstMethod
import app.revanced.patcher.instructions
import app.revanced.patcher.invoke
import app.revanced.patcher.patch.BytecodePatchContext
// MobileConfig boolean key that gates the redesigned location sticker styles.
// The method containing this constant reads the flag and returns it directly,
// making it the sole control point for the feature. The key is stable across
// app updates as MobileConfig keys are server-assigned constants.
private const val LOCATION_STICKER_REDESIGN_CONFIG_KEY = 0x8105a100041e0dL
internal val BytecodePatchContext.locationStickerRedesignGateMethodMatch by composingFirstMethod {
instructions(LOCATION_STICKER_REDESIGN_CONFIG_KEY())
}

View file

@ -3,6 +3,7 @@ package app.revanced.patches.shared.layout.branding
import app.revanced.com.android.tools.smali.dexlib2.mutable.MutableMethod
import app.revanced.patcher.extensions.addInstruction
import app.revanced.patcher.extensions.getInstruction
import app.revanced.patcher.firstImmutableClassDef
import app.revanced.patcher.patch.*
import app.revanced.patches.all.misc.packagename.setOrGetFallbackPackageName
import app.revanced.patches.all.misc.resources.addResources
@ -125,14 +126,14 @@ internal fun baseCustomBrandingPatch(
val getBuilderIndex = if (isYouTubeMusic) {
// YT Music the field is not a plain object type.
indexOfFirstInstructionOrThrow {
getReference<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;"
@ -147,11 +148,11 @@ internal fun baseCustomBrandingPatch(
).forEach { index ->
addInstructionsAtControlFlowLabel(
index,
$$"""
"""
move-object/from16 v0, p0
iget-object v0, v0, $$builderFieldName
check-cast v0, Landroid/app/Notification$Builder;
invoke-static { v0 }, $$EXTENSION_CLASS_DESCRIPTOR->setNotificationIcon(Landroid/app/Notification$Builder;)V
iget-object v0, v0, $builderFieldName
check-cast v0, Landroid/app/Notification${'$'}Builder;
invoke-static { v0 }, $EXTENSION_CLASS_DESCRIPTOR->setNotificationIcon(Landroid/app/Notification${'$'}Builder;)V
""",
)
}
@ -161,37 +162,16 @@ internal fun baseCustomBrandingPatch(
)
afterDependents {
val useCustomName = customName != null
val useCustomIcon = customIcon != null
val isRootInstall = setOrGetFallbackPackageName(originalAppPackageName) == originalAppPackageName
// Can only check if app is root installation by checking if change package name patch is in use.
// and can only do that in the afterDependents block here.
// The UI preferences cannot be selectively added here, because the settings afterDependents block
// may have already run and the settings are already wrote to file.
// Instead, show a warning if any patch option was used (A rooted device launcher ignores the manifest changes),
// and the non-functional in-app settings are removed on app startup by extension code.
if (isRootInstall && (useCustomName || useCustomIcon)) {
Logger.getLogger(this::class.java.name).warning(
"Custom branding does not work with root installation. No changes applied."
)
}
if (!isRootInstall || useCustomName) {
document("AndroidManifest.xml").use { document ->
val application = document.getElementsByTagName("application").item(0) as Element
application.setAttribute(
"android:label",
if (useCustomName) {
// Use custom name everywhere.
customName
} else {
// The YT application name can appear in some places alongside the system
// YouTube app, such as the settings app list and in the "open with" file picker.
// Because the YouTube app cannot be completely uninstalled and only disabled,
// use a custom name for this situation to disambiguate which app is which.
"@string/revanced_custom_branding_name_entry_2"
}
if (customName != null || customIcon != null) {
if (setOrGetFallbackPackageName(originalAppPackageName) == originalAppPackageName) {
Logger.getLogger(this::class.java.name).warning(
"Custom branding does not work with root installation. No changes applied.",
)
}
}
@ -332,19 +312,16 @@ internal fun baseCustomBrandingPatch(
activityAliasNameWithIntents,
).childNodes
// If user provides a custom icon, then change the application icon ('static' icon)
// which shows as the push notification for some devices, in the app settings,
// and as the icon for the apk before installing.
// This icon cannot be dynamically selected and this change must only be done if the
// user provides an icon otherwise there is no way to restore the original YouTube icon.
if (useCustomIcon) {
application.setAttribute(
"android:icon",
"@mipmap/revanced_launcher_custom"
)
}
// The YT application name can appear in some places alongside the system
// YouTube app, such as the settings app list and in the "open with" file picker.
// Because the YouTube app cannot be completely uninstalled and only disabled,
// use a custom name for this situation to disambiguate which app is which.
application.setAttribute(
"android:label",
"@string/revanced_custom_branding_name_entry_2",
)
val enabledNameIndex = if (useCustomName) numberOfPresetAppNames else 2 // 1 indexing.
val enabledNameIndex = if (useCustomName) numberOfPresetAppNames else 1 // 1 indexing.
val enabledIconIndex = if (useCustomIcon) iconStyleNames.size else 0 // 0 indexing.
for (appNameIndex in 1..numberOfPresetAppNames) {
@ -359,7 +336,7 @@ internal fun baseCustomBrandingPatch(
iconMipmapName = originalLauncherIconName,
appNameIndex = appNameIndex,
useCustomName = useCustomNameLabel,
enabled = false,
enabled = (appNameIndex == 1),
intentFilters,
),
)

View file

@ -169,13 +169,13 @@ internal fun spoofVideoStreamsPatch(
if-eqz v2, :disabled
# Get streaming data.
invoke-static { v2 }, $EXTENSION_CLASS_DESCRIPTOR->getStreamingData(Ljava/lang/String;)[B
invoke-static { v2 }, $EXTENSION_CLASS_DESCRIPTOR->getStreamingData(Ljava/lang/String;)Ljava/nio/ByteBuffer;
move-result-object v3
if-eqz v3, :disabled
# Parse streaming data.
sget-object v4, $playerProtoClass->a:$playerProtoClass
invoke-static { v4, v3 }, $protobufClass->parseFrom(${protobufClass}[B)$protobufClass
invoke-static { v4, v3 }, $protobufClass->parseFrom(${protobufClass}Ljava/nio/ByteBuffer;)$protobufClass
move-result-object v5
check-cast v5, $playerProtoClass

View file

@ -63,10 +63,10 @@ val spoofVideoStreamsPatch = spoofVideoStreamsPatch(
// Requires a key and title but the actual text is chosen at runtime.
key = "revanced_spoof_video_streams_about",
summaryKey = null,
tag = "app.revanced.extension.youtube.settings.preference.SpoofVideoStreamsSideEffectsPreference",
tag = "app.revanced.extension.youtube.settings.preference.SpoofStreamingDataSideEffectsPreference",
),
SwitchPreference("revanced_spoof_video_streams_av1"),
SwitchPreference("revanced_spoof_video_streams_stats_for_nerds"),
SwitchPreference("revanced_spoof_streaming_data_stats_for_nerds"),
),
),
)

View file

@ -25,7 +25,6 @@ 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>
@ -50,7 +49,6 @@ 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>
@ -133,8 +131,6 @@ 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
@ -222,12 +218,9 @@ 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>
@ -1155,7 +1148,6 @@ 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>
@ -1176,7 +1168,6 @@ 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>
@ -1197,7 +1188,6 @@ 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>
@ -1406,14 +1396,12 @@ 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í:
@ -1457,7 +1445,6 @@ 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>
@ -1496,7 +1483,6 @@ 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>
@ -1523,11 +1509,6 @@ 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>
@ -1558,17 +1539,11 @@ Přejetím roztáhnete nebo zavřete"</string>
<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">
<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.customBrandingPatch"/>
<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>
@ -1593,7 +1568,6 @@ 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 &amp; Původní náhledy</string>
<string name="revanced_alt_thumbnail_options_entry_3">DeArrow &amp; 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
@ -1637,11 +1611,7 @@ 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">
<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.audiofocus.pauseOnAudioInterruptPatch"/>
<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
@ -1796,11 +1766,7 @@ 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">
<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="layout.branding.customBrandingPatch"/>
<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>
@ -1927,7 +1893,6 @@ 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>

View file

@ -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">&lt;h5&gt;Diese App wurde offenbar nicht von Ihnen gepatcht.&lt;/h5&gt;&lt;br&gt;Diese App funktioniert möglicherweise nicht richtig, &lt;b&gt;könnte schädlich oder sogar gefährlich in der Verwendung sein&lt;/b&gt;.&lt; br&gt;&lt;br&gt;Diese Prüfungen deuten darauf hin, dass diese App vorab gepatcht wurde oder von jemandem bezogen wurde:&lt;br&gt;&lt;br&gt;&lt;small&gt;%1$s&lt;/small&gt;&lt;br&gt;Es wird dringend empfohlen, &lt;b&gt;diese App zu deinstallieren und selbst zu patchen&lt;/b&gt; um sicherzustellen, dass Sie eine validierte und sichere App verwenden.&lt;p&gt;&lt;br&gt;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 von ReVanced Manager installiert</string>
<string name="revanced_check_environment_manager_not_expected_installer">Nicht durch 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.&lt;br&gt;&lt;br&gt;Dies wird höchstwahrscheinlich durch einen DNS-Werbeblocker oder einen Netzwerkproxy verursacht.&lt;br&gt;&lt;br&gt;Um dies zu beheben, setze &lt;b&gt;s.youtube.com&lt;/b&gt; auf die Whitelist oder schalten Sie alle DNS-Blocker und Proxys aus.</string>
<string name="revanced_check_watch_history_domain_name_dialog_message">Ihr Verlauf wird nicht gespeichert.&lt;br&gt;&lt;br&gt;Dies wird höchstwahrscheinlich durch einen DNS-Werbeblocker oder einen Netzwerkproxy verursacht.&lt;br&gt;&lt;br&gt;Um dies zu beheben, setze &lt;b&gt;s.youtube.com&lt;/b&gt; auf die Whitelist oder schalten Sie alle DNS-Blocker und Proxies aus.</string>
<string name="revanced_check_watch_history_domain_name_dialog_ignore">Nicht wieder anzeigen</string>
</patch>
<patch id="misc.settings.settingsResourcePatch">
@ -1598,9 +1598,7 @@ 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">
<string name="revanced_pause_on_audio_interrupt_title">Pause bei Audiounterbrechung</string>
</patch>
<patch id="misc.audiofocus.pauseOnAudioInterruptPatch"/>
<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
@ -1755,9 +1753,7 @@ Die Videowiedergabe mit AV1 kann stottern oder Bilder überspringen."</string>
</patch>
</app>
<app id="music">
<patch id="layout.branding.customBrandingPatch">
<string name="revanced_custom_branding_name_entry_4">Musik</string>
</patch>
<patch id="layout.branding.customBrandingPatch"/>
<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>

View file

@ -238,9 +238,13 @@ 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>
@ -369,9 +373,13 @@ 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>
@ -387,9 +395,13 @@ 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>
@ -1619,8 +1631,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>

View file

@ -1514,11 +1514,6 @@ 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>
@ -1627,10 +1622,7 @@ 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">
<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.audiofocus.pauseOnAudioInterruptPatch"/>
<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

View file

@ -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 a ghintear le hintleacht shaorga\'</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_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 Intleacht Shaorga</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_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">na rannóga \'Áiteanna Réadmhaoine\', \'Cluichí\', \'Ceol\' agus \'Daoine\' i bhfolach</string>
<string name="revanced_hide_attributes_section_summary_on">ailt d\'áiteanna sonracha, Cluichí, Ceol agus Daoine a luaitear 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 an chúrsa\'</string>
<string name="revanced_hide_course_progress_section_title">Folaigh Dul chun cinn cú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 na rannóga Iniúchadh an chúrsa seo agus Iniúchadh na podchraoltaí</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_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 an chuid seo den chúrsa a iniúchadh</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_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 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_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_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 an chuid áiteanna feiceálacha</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_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 @@ Dfhé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">an cnaipe baile i bhfolach</string>
<string name="revanced_music_hide_navigation_bar_home_button_summary_on">cnaipe Baile folaithe</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 @@ Dfhé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 lipéad</string>
<string name="revanced_music_hide_get_premium_label_summary_off">Taispeántar an 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 @@ Dfhé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">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_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_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">Thug %s earráid ar ais, dfhé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_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_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">Seachfhreastalaí lonrúil</string>
<string name="revanced_block_embedded_ads_entry_2">Proxy 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"> bac ar fhógraí físe</string>
<string name="revanced_block_video_ads_summary_off">Tá fógraí físe díbhlocáilte</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>
</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 mhilleann</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_3">Taispeáin teachtaireachtaí scriosta mar théacs trasnaithe amach</string>
</patch>
<patch id="chat.autoclaim.autoClaimChannelPointsPatch">

View file

@ -29,7 +29,7 @@ Second \"item\" text"</string>
</patch>
<patch id="misc.dns.checkWatchHistoryDomainNameResolutionPatch"/>
<patch id="misc.settings.settingsResourcePatch">
<string name="revanced_settings_save">Spremi</string>
<string name="revanced_settings_save">Sačuvaj</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 je omogućena</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_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 GmsCorea. Trenutno koristite verziju %2$s.</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_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">Premium opcije kvalitete su skrivene</string>
<string name="revanced_hide_premium_video_quality_summary_off">Premium opcije kvalitete su prikazane</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>
</patch>
<patch id="interaction.seekbar.enableSlideToSeekPatch"/>
<patch id="misc.fix.playback.spoofVideoStreamsPatch">

View file

@ -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 pengaturan ini akan mencatat data tata letak tambahan, termasuk teks pada layar untuk beberapa komponen UI.
<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.
Ini dapat membantu mengidentifikasi komponen saat membuat penyaring khusus.

View file

@ -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>

View file

@ -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>

View file

@ -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">Ievietot</string>
<string name="revanced_settings_import">Importēt</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 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_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_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>

View file

@ -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">ReVanced</string>
<string name="revanced_custom_branding_icon_entry_2">shared.layout.branding.baseCustomBrandingPatch.revanced_custom_branding_icon_entry_2</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">Controles mislukt</string>
<string name="revanced_check_environment_failed_title">Controle 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">&lt;h5&gt;Deze app lijkt niet door u te zijn gepatcht.&lt;/h5&gt;&lt;br&gt;Deze app werkt mogelijk niet goed, is **mogelijk schadelijk of zelfs gevaarlijk om te gebruiken**.&lt;br&gt;&lt;br&gt;Deze checks geven aan dat deze app is gepatcht of van iemand anders is verkregen:&lt;br&gt;&lt;br&gt;&lt;small&gt;%1$s&lt;/small&gt;&lt;br&gt;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.&lt;p&gt;&lt;br&gt;Indien genegeerd, zal deze waarschuwing nog slechts twee keer worden getoond.</string>
@ -1592,7 +1592,6 @@ Het inschakelen hiervan kan ontbrekende afbeeldingen oplossen die in sommige reg
<string name="revanced_alt_thumbnail_options_entry_2">DeArrow &amp; Oorspronkelijke miniaturen</string>
<string name="revanced_alt_thumbnail_options_entry_3">DeArrow &amp; 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.
@ -1636,11 +1635,7 @@ 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">
<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.audiofocus.pauseOnAudioInterruptPatch"/>
<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
@ -1700,7 +1695,6 @@ 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>
@ -1795,11 +1789,7 @@ Het afspelen van video met AV1 kan haperen of frames overslaan."</string>
</patch>
</app>
<app id="music">
<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="layout.branding.customBrandingPatch"/>
<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>
@ -1926,7 +1916,6 @@ 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>

View file

@ -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>

View file

@ -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,16 +264,18 @@
</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">
@ -661,7 +663,8 @@
<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>

View file

@ -144,16 +144,6 @@ 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>
@ -910,10 +900,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 Reel' must be kept untranslated. -->
'Android No SDK' 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 >Reel\'"</string>
To show the Audio track menu, change \'Spoof video streams\' to \'Android No SDK\'"</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>
@ -1796,9 +1786,18 @@ 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_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>
<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>
</patch>
</app>
<app id="music">

View file

@ -2,6 +2,7 @@ rootProject.name = "revanced-patches"
pluginManagement {
repositories {
mavenLocal()
gradlePluginPortal()
google()
maven {
@ -9,16 +10,12 @@ pluginManagement {
url = uri("https://maven.pkg.github.com/revanced/revanced-patches-gradle-plugin")
credentials(PasswordCredentials::class)
}
// TODO: Remove once https://github.com/google/protobuf-gradle-plugin/pull/797 is merged.
maven { url = uri("https://jitpack.io") }
}
// TODO: Remove once https://github.com/google/protobuf-gradle-plugin/pull/797 is merged.
resolutionStrategy {
eachPlugin {
if (requested.id.id == "com.google.protobuf") {
useModule("com.github.ReVanced:protobuf-gradle-plugin:${requested.version}")
}
}
}
dependencyResolutionManagement {
repositories {
mavenLocal()
}
}
@ -36,4 +33,4 @@ settings {
}
}
include(":patches:stub")
include(":patches:stub")