Compare commits

...

14 Commits

Author SHA1 Message Date
semantic-release-bot
5d2c21540c chore: Release v5.29.1-dev.1 [skip ci]
## [5.29.1-dev.1](https://github.com/ReVanced/revanced-patches/compare/v5.29.0...v5.29.1-dev.1) (2025-06-26)

### Bug Fixes

* **Spotify:** Add `Spoof client` patch to fix various issues by using a web platform access token ([#5173](https://github.com/ReVanced/revanced-patches/issues/5173)) ([1a8aacd](1a8aacdff6))
2025-06-26 17:56:10 +00:00
oSumAtrIX
1a8aacdff6 fix(Spotify): Add Spoof client patch to fix various issues by using a web platform access token (#5173)
Co-authored-by: Nuckyz <61953774+Nuckyz@users.noreply.github.com>
Co-authored-by: brosssh <tiabroch@gmail.com>
Co-authored-by: Dawid Krajcarz <80264606+drobotk@users.noreply.github.com>
Co-authored-by: LisoUseInAIKyrios <118716522+LisoUseInAIKyrios@users.noreply.github.com>
2025-06-26 19:51:18 +02:00
semantic-release-bot
1804bd9bfc chore: Release v5.29.0 [skip ci]
# [5.29.0](https://github.com/ReVanced/revanced-patches/compare/v5.28.0...v5.29.0) (2025-06-26)

### Bug Fixes

* Add scrollable content to modern style settings dialogs ([#5211](https://github.com/ReVanced/revanced-patches/issues/5211)) ([2b62fc2](2b62fc2224))
* **Google Photos:** Resolve startup crash for Android 5.0 devices ([7be3741](7be374100b))
* **YouTube - Hide ads:** Hide new type of product ad in video description ([#5225](https://github.com/ReVanced/revanced-patches/issues/5225)) ([b656976](b65697603d))
* **YouTube - Hide layout components:** Fix "Hide video description attributes" ([#5250](https://github.com/ReVanced/revanced-patches/issues/5250)) ([978c244](978c24458b))
* **YouTube - Hide Shorts components:** Fix "Hide Use this sound button" ([#5233](https://github.com/ReVanced/revanced-patches/issues/5233)) ([a678f17](a678f178e1))
* **YouTube - Hide Shorts components:** Fix "Hide Use this template button" ([#5249](https://github.com/ReVanced/revanced-patches/issues/5249)) ([957bece](957bece3e9))
* **YouTube:** Always use single threaded layout to resolve layout bugs in unpatched YouTube ([#5226](https://github.com/ReVanced/revanced-patches/issues/5226)) ([ccd1691](ccd169121a))
* **YouTube:** Fix refactoring app startup exception ([0dbd058](0dbd058099))

### Features

* Add `Spoof app signature` patch ([#5158](https://github.com/ReVanced/revanced-patches/issues/5158)) ([fb83e58](fb83e58f79))
* **Cricbuzz:** Add `Hide ads` patch ([#4998](https://github.com/ReVanced/revanced-patches/issues/4998)) ([558bf8b](558bf8bca8))
* **Crunchyroll:** Add `Hide ads` patch ([#5201](https://github.com/ReVanced/revanced-patches/issues/5201)) ([d338989](d338989cb4))
* **YouTube - Hide Shorts components:** Add `Hide Effects button` ([#5255](https://github.com/ReVanced/revanced-patches/issues/5255)) ([29c86ac](29c86ac6a3))
* **YouTube - Hide video action buttons:** Add `Hide Stop ads` ([#5245](https://github.com/ReVanced/revanced-patches/issues/5245)) ([0e63f49](0e63f49e13))
* **YouTube:** Add an option to disable toasts when changing default playback speed or quality ([#5230](https://github.com/ReVanced/revanced-patches/issues/5230)) ([6b719df](6b719dfcd7))
* **YouTube:** Support version `20.13.41` ([#5253](https://github.com/ReVanced/revanced-patches/issues/5253)) ([439ca37](439ca37e99))
2025-06-26 08:06:08 +00:00
LisoUseInAIKyrios
7eb4e62762 chore: Merge branch dev to main (#5227) 2025-06-26 12:02:33 +04:00
LisoUseInAIKyrios
b8e10b5c1f chore: Fix api dump 2025-06-26 11:51:54 +04:00
github-actions[bot]
a7c11b9b08 chore: Sync translations (#5258) 2025-06-26 11:50:50 +04:00
semantic-release-bot
443c0a74d5 chore: Release v5.29.0-dev.11 [skip ci]
# [5.29.0-dev.11](https://github.com/ReVanced/revanced-patches/compare/v5.29.0-dev.10...v5.29.0-dev.11) (2025-06-26)

### Features

* **Cricbuzz:** Add `Hide ads` patch ([#4998](https://github.com/ReVanced/revanced-patches/issues/4998)) ([558bf8b](558bf8bca8))
2025-06-26 07:13:07 +00:00
github-actions[bot]
84a0f7f7d7 chore: Sync translations (#5257) 2025-06-26 11:10:05 +04:00
Sourav Agrawal
558bf8bca8 feat(Cricbuzz): Add Hide ads patch (#4998) 2025-06-26 11:08:22 +04:00
semantic-release-bot
e22d4e6a4b chore: Release v5.29.0-dev.10 [skip ci]
# [5.29.0-dev.10](https://github.com/ReVanced/revanced-patches/compare/v5.29.0-dev.9...v5.29.0-dev.10) (2025-06-25)

### Features

* **YouTube - Hide Shorts components:** Add `Hide Effects button` ([#5255](https://github.com/ReVanced/revanced-patches/issues/5255)) ([29c86ac](29c86ac6a3))
2025-06-25 08:59:45 +00:00
LisoUseInAIKyrios
a07f946633 chore: Fix typo 2025-06-25 12:56:42 +04:00
LisoUseInAIKyrios
29c86ac6a3 feat(YouTube - Hide Shorts components): Add Hide Effects button (#5255) 2025-06-25 12:54:59 +04:00
semantic-release-bot
19cf5667d8 chore: Release v5.29.0-dev.9 [skip ci]
# [5.29.0-dev.9](https://github.com/ReVanced/revanced-patches/compare/v5.29.0-dev.8...v5.29.0-dev.9) (2025-06-25)

### Features

* Add `Spoof app signature` patch ([#5158](https://github.com/ReVanced/revanced-patches/issues/5158)) ([fb83e58](fb83e58f79))
2025-06-25 07:16:33 +00:00
Markus Probst
fb83e58f79 feat: Add Spoof app signature patch (#5158) 2025-06-25 11:13:32 +04:00
82 changed files with 1736 additions and 491 deletions

View File

@@ -1,3 +1,56 @@
## [5.29.1-dev.1](https://github.com/ReVanced/revanced-patches/compare/v5.29.0...v5.29.1-dev.1) (2025-06-26)
### Bug Fixes
* **Spotify:** Add `Spoof client` patch to fix various issues by using a web platform access token ([#5173](https://github.com/ReVanced/revanced-patches/issues/5173)) ([b7b75bb](https://github.com/ReVanced/revanced-patches/commit/b7b75bb9d8d5fd505121e752b8a20e61ff28d1b2))
# [5.29.0](https://github.com/ReVanced/revanced-patches/compare/v5.28.0...v5.29.0) (2025-06-26)
### Bug Fixes
* Add scrollable content to modern style settings dialogs ([#5211](https://github.com/ReVanced/revanced-patches/issues/5211)) ([e6876d5](https://github.com/ReVanced/revanced-patches/commit/e6876d510d28f6a3a41ec1722a033b3e30a22c65))
* **Google Photos:** Resolve startup crash for Android 5.0 devices ([0294533](https://github.com/ReVanced/revanced-patches/commit/0294533c4d9a321aea086eedb4e46385ae9a026e))
* **YouTube - Hide ads:** Hide new type of product ad in video description ([#5225](https://github.com/ReVanced/revanced-patches/issues/5225)) ([1e2efad](https://github.com/ReVanced/revanced-patches/commit/1e2efad7b2714c395ed6b0a77cbbf8a2265df520))
* **YouTube - Hide layout components:** Fix "Hide video description attributes" ([#5250](https://github.com/ReVanced/revanced-patches/issues/5250)) ([2f22d45](https://github.com/ReVanced/revanced-patches/commit/2f22d45eb80745ac64fbea44c8055ebe7925a586))
* **YouTube - Hide Shorts components:** Fix "Hide Use this sound button" ([#5233](https://github.com/ReVanced/revanced-patches/issues/5233)) ([5d6ec9e](https://github.com/ReVanced/revanced-patches/commit/5d6ec9e94a6221a0f32762d5bede893e9e7457fc))
* **YouTube - Hide Shorts components:** Fix "Hide Use this template button" ([#5249](https://github.com/ReVanced/revanced-patches/issues/5249)) ([b399ecb](https://github.com/ReVanced/revanced-patches/commit/b399ecbb6a222d82dd5e4b3417c9f7eff4324adb))
* **YouTube:** Always use single threaded layout to resolve layout bugs in unpatched YouTube ([#5226](https://github.com/ReVanced/revanced-patches/issues/5226)) ([1f539b1](https://github.com/ReVanced/revanced-patches/commit/1f539b1396526b2c767d77a804bd0308ee4a42ec))
* **YouTube:** Fix refactoring app startup exception ([1b00c90](https://github.com/ReVanced/revanced-patches/commit/1b00c907f4b90f4659afb4a54ba61ac2835b460d))
### Features
* Add `Spoof app signature` patch ([#5158](https://github.com/ReVanced/revanced-patches/issues/5158)) ([78b25aa](https://github.com/ReVanced/revanced-patches/commit/78b25aa4e87ec3f9df1d57831b48a39029969416))
* **Cricbuzz:** Add `Hide ads` patch ([#4998](https://github.com/ReVanced/revanced-patches/issues/4998)) ([83ccfa8](https://github.com/ReVanced/revanced-patches/commit/83ccfa8e1b5d5a44c55ef659484acf3cc08d3346))
* **Crunchyroll:** Add `Hide ads` patch ([#5201](https://github.com/ReVanced/revanced-patches/issues/5201)) ([46b4398](https://github.com/ReVanced/revanced-patches/commit/46b4398fd6ca223391ed8f497a8347c2313421b7))
* **YouTube - Hide Shorts components:** Add `Hide Effects button` ([#5255](https://github.com/ReVanced/revanced-patches/issues/5255)) ([240897a](https://github.com/ReVanced/revanced-patches/commit/240897a94008ce9a148c87bb41b978d553d5a6f5))
* **YouTube - Hide video action buttons:** Add `Hide Stop ads` ([#5245](https://github.com/ReVanced/revanced-patches/issues/5245)) ([274dcc6](https://github.com/ReVanced/revanced-patches/commit/274dcc676e009be63eb6970de33abacd34dc6560))
* **YouTube:** Add an option to disable toasts when changing default playback speed or quality ([#5230](https://github.com/ReVanced/revanced-patches/issues/5230)) ([c68cde3](https://github.com/ReVanced/revanced-patches/commit/c68cde3a896450874cc571be5c4723387db96032))
* **YouTube:** Support version `20.13.41` ([#5253](https://github.com/ReVanced/revanced-patches/issues/5253)) ([d284c3d](https://github.com/ReVanced/revanced-patches/commit/d284c3dd3277430b6885e7c27ee02d062dcefc85))
# [5.29.0-dev.11](https://github.com/ReVanced/revanced-patches/compare/v5.29.0-dev.10...v5.29.0-dev.11) (2025-06-26)
### Features
* **Cricbuzz:** Add `Hide ads` patch ([#4998](https://github.com/ReVanced/revanced-patches/issues/4998)) ([83ccfa8](https://github.com/ReVanced/revanced-patches/commit/83ccfa8e1b5d5a44c55ef659484acf3cc08d3346))
# [5.29.0-dev.10](https://github.com/ReVanced/revanced-patches/compare/v5.29.0-dev.9...v5.29.0-dev.10) (2025-06-25)
### Features
* **YouTube - Hide Shorts components:** Add `Hide Effects button` ([#5255](https://github.com/ReVanced/revanced-patches/issues/5255)) ([240897a](https://github.com/ReVanced/revanced-patches/commit/240897a94008ce9a148c87bb41b978d553d5a6f5))
# [5.29.0-dev.9](https://github.com/ReVanced/revanced-patches/compare/v5.29.0-dev.8...v5.29.0-dev.9) (2025-06-25)
### Features
* Add `Spoof app signature` patch ([#5158](https://github.com/ReVanced/revanced-patches/issues/5158)) ([78b25aa](https://github.com/ReVanced/revanced-patches/commit/78b25aa4e87ec3f9df1d57831b48a39029969416))
# [5.29.0-dev.8](https://github.com/ReVanced/revanced-patches/compare/v5.29.0-dev.7...v5.29.0-dev.8) (2025-06-25)

3
build.gradle.kts Normal file
View File

@@ -0,0 +1,3 @@
plugins {
alias(libs.plugins.android.library) apply false
}

View File

@@ -1,5 +1,5 @@
plugins {
id(libs.plugins.android.library.get().pluginId)
alias(libs.plugins.android.library)
}
android {

View File

@@ -1,5 +1,5 @@
plugins {
id(libs.plugins.android.library.get().pluginId)
alias(libs.plugins.android.library)
}
android {

View File

@@ -1,5 +1,5 @@
plugins {
id(libs.plugins.android.library.get().pluginId)
alias(libs.plugins.android.library)
}
android {

View File

@@ -1,5 +1,5 @@
plugins {
id(libs.plugins.android.library.get().pluginId)
alias(libs.plugins.android.library)
}
android {

View File

@@ -1,5 +1,5 @@
plugins {
id("com.android.library")
alias(libs.plugins.android.library)
}
android {

View File

@@ -1,7 +1,15 @@
plugins {
alias(libs.plugins.protobuf)
}
dependencies {
compileOnly(project(":extensions:shared:library"))
compileOnly(project(":extensions:spotify:stub"))
compileOnly(libs.annotation)
implementation(project(":extensions:spotify:utils"))
implementation(libs.nanohttpd)
implementation(libs.protobuf.javalite)
}
android {
@@ -14,3 +22,19 @@ android {
targetCompatibility = JavaVersion.VERSION_1_8
}
}
protobuf {
protoc {
artifact = libs.protobuf.protoc.get().toString()
}
generateProtoTasks {
all().forEach { task ->
task.builtins {
create("java") {
option("lite")
}
}
}
}
}

View File

@@ -0,0 +1,153 @@
package app.revanced.extension.spotify.misc.fix;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import app.revanced.extension.shared.Logger;
import app.revanced.extension.spotify.login5.v4.proto.Login5.*;
import com.google.protobuf.ByteString;
import com.google.protobuf.MessageLite;
import fi.iki.elonen.NanoHTTPD;
import java.io.ByteArrayInputStream;
import java.io.FilterInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.Objects;
import static fi.iki.elonen.NanoHTTPD.Response.Status.INTERNAL_ERROR;
class LoginRequestListener extends NanoHTTPD {
LoginRequestListener(int port) {
super(port);
}
@NonNull
@Override
public Response serve(IHTTPSession request) {
Logger.printInfo(() -> "Serving request for URI: " + request.getUri());
InputStream requestBodyInputStream = getRequestBodyInputStream(request);
LoginRequest loginRequest;
try {
loginRequest = LoginRequest.parseFrom(requestBodyInputStream);
} catch (IOException e) {
Logger.printException(() -> "Failed to parse LoginRequest", e);
return newResponse(INTERNAL_ERROR);
}
MessageLite loginResponse;
// A request may be made concurrently by Spotify,
// however a webview can only handle one request at a time due to singleton cookie manager.
// Therefore, synchronize to ensure that only one webview handles the request at a time.
synchronized (this) {
loginResponse = getLoginResponse(loginRequest);
}
if (loginResponse != null) {
return newResponse(Response.Status.OK, loginResponse);
}
return newResponse(INTERNAL_ERROR);
}
@Nullable
private static LoginResponse getLoginResponse(@NonNull LoginRequest loginRequest) {
Session session;
boolean isInitialLogin = !loginRequest.hasStoredCredential();
if (isInitialLogin) {
Logger.printInfo(() -> "Received request for initial login");
session = WebApp.currentSession; // Session obtained from WebApp.login.
} else {
Logger.printInfo(() -> "Received request to restore saved session");
session = Session.read(loginRequest.getStoredCredential().getUsername());
}
return toLoginResponse(session, isInitialLogin);
}
private static LoginResponse toLoginResponse(Session session, boolean isInitialLogin) {
LoginResponse.Builder builder = LoginResponse.newBuilder();
if (session == null) {
if (isInitialLogin) {
Logger.printInfo(() -> "Session is null, returning try again later error for initial login");
builder.setError(LoginError.TRY_AGAIN_LATER);
} else {
Logger.printInfo(() -> "Session is null, returning invalid credentials error for stored credential login");
builder.setError(LoginError.INVALID_CREDENTIALS);
}
} else if (session.username == null) {
Logger.printInfo(() -> "Session username is null, returning invalid credentials error");
builder.setError(LoginError.INVALID_CREDENTIALS);
} else if (session.accessTokenExpired()) {
Logger.printInfo(() -> "Access token has expired, renewing session");
WebApp.renewSession(session.cookies);
return toLoginResponse(WebApp.currentSession, isInitialLogin);
} else {
session.save();
Logger.printInfo(() -> "Returning session for username: " + session.username);
builder.setOk(LoginOk.newBuilder()
.setUsername(session.username)
.setAccessToken(session.accessToken)
.setStoredCredential(ByteString.fromHex("00")) // Placeholder, as it cannot be null or empty.
.setAccessTokenExpiresIn(session.accessTokenExpiresInSeconds())
.build());
}
return builder.build();
}
@NonNull
private static InputStream limitedInputStream(InputStream inputStream, long contentLength) {
return new FilterInputStream(inputStream) {
private long remaining = contentLength;
@Override
public int read() throws IOException {
if (remaining <= 0) return -1;
int result = super.read();
if (result != -1) remaining--;
return result;
}
@Override
public int read(byte[] b, int off, int len) throws IOException {
if (remaining <= 0) return -1;
len = (int) Math.min(len, remaining);
int result = super.read(b, off, len);
if (result != -1) remaining -= result;
return result;
}
};
}
@NonNull
private static InputStream getRequestBodyInputStream(@NonNull IHTTPSession request) {
long requestContentLength =
Long.parseLong(Objects.requireNonNull(request.getHeaders().get("content-length")));
return limitedInputStream(request.getInputStream(), requestContentLength);
}
@SuppressWarnings("SameParameterValue")
@NonNull
private static Response newResponse(Response.Status status) {
return newResponse(status, null);
}
@NonNull
private static Response newResponse(Response.IStatus status, MessageLite messageLite) {
if (messageLite == null) {
return newFixedLengthResponse(status, "application/x-protobuf", null);
}
byte[] messageBytes = messageLite.toByteArray();
InputStream stream = new ByteArrayInputStream(messageBytes);
return newFixedLengthResponse(status, "application/x-protobuf", stream, messageBytes.length);
}
}

View File

@@ -0,0 +1,124 @@
package app.revanced.extension.spotify.misc.fix;
import android.content.SharedPreferences;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import app.revanced.extension.shared.Logger;
import app.revanced.extension.shared.Utils;
import org.json.JSONException;
import org.json.JSONObject;
import static android.content.Context.MODE_PRIVATE;
class Session {
/**
* Username of the account. Null if this session does not have an authenticated user.
*/
@Nullable
final String username;
/**
* Access token for this session.
*/
final String accessToken;
/**
* Session expiration timestamp in milliseconds.
*/
final Long expirationTime;
/**
* Authentication cookies for this session.
*/
final String cookies;
/**
* @param username Username of the account. Empty if this session does not have an authenticated user.
* @param accessToken Access token for this session.
* @param cookies Authentication cookies for this session.
*/
Session(@Nullable String username, String accessToken, String cookies) {
this(username, accessToken, System.currentTimeMillis() + 60 * 60 * 1000, cookies);
}
private Session(@Nullable String username, String accessToken, long expirationTime, String cookies) {
this.username = username;
this.accessToken = accessToken;
this.expirationTime = expirationTime;
this.cookies = cookies;
}
/**
* @return The number of milliseconds until the access token expires.
*/
long accessTokenExpiresInMillis() {
long currentTime = System.currentTimeMillis();
return expirationTime - currentTime;
}
/**
* @return The number of seconds until the access token expires.
*/
int accessTokenExpiresInSeconds() {
return (int) accessTokenExpiresInMillis() / 1000;
}
/**
* @return True if the access token has expired, false otherwise.
*/
boolean accessTokenExpired() {
return accessTokenExpiresInMillis() <= 0;
}
void save() {
Logger.printInfo(() -> "Saving session: " + this);
SharedPreferences.Editor editor = Utils.getContext().getSharedPreferences("revanced", MODE_PRIVATE).edit();
String json;
try {
json = new JSONObject()
.put("accessToken", accessToken)
.put("expirationTime", expirationTime)
.put("cookies", cookies).toString();
} catch (JSONException ex) {
Logger.printException(() -> "Failed to convert session to stored credential", ex);
return;
}
editor.putString("session_" + username, json);
editor.apply();
}
@Nullable
static Session read(String username) {
Logger.printInfo(() -> "Reading saved session for username: " + username);
SharedPreferences sharedPreferences = Utils.getContext().getSharedPreferences("revanced", MODE_PRIVATE);
String savedJson = sharedPreferences.getString("session_" + username, null);
if (savedJson == null) {
Logger.printInfo(() -> "No session found in shared preferences");
return null;
}
try {
JSONObject json = new JSONObject(savedJson);
String accessToken = json.getString("accessToken");
long expirationTime = json.getLong("expirationTime");
String cookies = json.getString("cookies");
return new Session(username, accessToken, expirationTime, cookies);
} catch (JSONException ex) {
Logger.printException(() -> "Failed to read session from shared preferences", ex);
return null;
}
}
@NonNull
@Override
public String toString() {
return "Session(" +
"username=" + username +
", accessToken=" + accessToken +
", expirationTime=" + expirationTime +
", cookies=" + cookies +
')';
}
}

View File

@@ -0,0 +1,42 @@
package app.revanced.extension.spotify.misc.fix;
import android.view.LayoutInflater;
import app.revanced.extension.shared.Logger;
@SuppressWarnings("unused")
public class SpoofClientPatch {
private static LoginRequestListener listener;
/**
* Injection point.
* <br>
* Start login server.
*/
public static void listen(int port) {
if (listener != null) {
Logger.printInfo(() -> "Listener already running on port " + port);
return;
}
try {
listener = new LoginRequestListener(port);
listener.start();
Logger.printInfo(() -> "Listener running on port " + port);
} catch (Exception ex) {
Logger.printException(() -> "listen failure", ex);
}
}
/**
* Injection point.
* <br>
* Launch login web view.
*/
public static void login(LayoutInflater inflater) {
try {
WebApp.login(inflater.getContext());
} catch (Exception ex) {
Logger.printException(() -> "login failure", ex);
}
}
}

View File

@@ -0,0 +1,267 @@
package app.revanced.extension.spotify.misc.fix;
import android.annotation.SuppressLint;
import android.app.Dialog;
import android.content.Context;
import android.graphics.Bitmap;
import android.os.Build;
import android.view.*;
import android.webkit.*;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
import app.revanced.extension.shared.Logger;
import app.revanced.extension.shared.Utils;
import app.revanced.extension.spotify.UserAgent;
class WebApp {
private static final String OPEN_SPOTIFY_COM = "open.spotify.com";
private static final String OPEN_SPOTIFY_COM_URL = "https://" + OPEN_SPOTIFY_COM;
private static final String OPEN_SPOTIFY_COM_PREFERENCES_URL = OPEN_SPOTIFY_COM_URL + "/preferences";
private static final String ACCOUNTS_SPOTIFY_COM_LOGIN_URL = "https://accounts.spotify.com/login?continue=" +
"https%3A%2F%2Fopen.spotify.com%2Fpreferences";
private static final int GET_SESSION_TIMEOUT_SECONDS = 10;
private static final String JAVASCRIPT_INTERFACE_NAME = "androidInterface";
private static final String USER_AGENT = getWebUserAgent();
/**
* Current webview in use. Any use of the object must be done on the main thread.
*/
@SuppressLint("StaticFieldLeak")
private static volatile WebView currentWebView;
/**
* A session obtained from the webview after logging in or renewing the session.
*/
@Nullable
static volatile Session currentSession;
static void login(Context context) {
Logger.printInfo(() -> "Starting login");
Dialog dialog = new Dialog(context, android.R.style.Theme_Black_NoTitleBar_Fullscreen);
// Ensure that the keyboard does not cover the webview content.
Window window = dialog.getWindow();
//noinspection StatementWithEmptyBody
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
window.getDecorView().setOnApplyWindowInsetsListener((v, insets) -> {
v.setPadding(0, 0, 0, insets.getInsets(WindowInsets.Type.ime()).bottom);
return WindowInsets.CONSUMED;
});
} else {
// TODO: Implement for lower Android versions.
}
newWebView(
// Can't use Utils.getContext() here, because autofill won't work.
// See https://stackoverflow.com/a/79182053/11213244.
context,
new WebViewCallback() {
@Override
void onInitialized(WebView webView) {
// Ensure that cookies are cleared before loading the login page.
CookieManager.getInstance().removeAllCookies((anyRemoved) -> {
Logger.printInfo(() -> "Loading URL: " + ACCOUNTS_SPOTIFY_COM_LOGIN_URL);
webView.loadUrl(ACCOUNTS_SPOTIFY_COM_LOGIN_URL);
});
dialog.setCancelable(false);
dialog.setContentView(webView);
dialog.show();
}
@Override
void onLoggedIn(String cookies) {
dialog.dismiss();
}
@Override
void onReceivedSession(WebView webView, Session session) {
Logger.printInfo(() -> "Received session from login: " + session);
currentSession = session;
currentWebView = null;
webView.stopLoading();
webView.destroy();
}
}
);
}
static void renewSession(String cookies) {
Logger.printInfo(() -> "Renewing session with cookies: " + cookies);
CountDownLatch getSessionLatch = new CountDownLatch(1);
newWebView(
Utils.getContext(),
new WebViewCallback() {
@Override
public void onInitialized(WebView webView) {
Logger.printInfo(() -> "Loading URL: " + OPEN_SPOTIFY_COM_PREFERENCES_URL +
" with cookies: " + cookies);
setCookies(cookies);
webView.loadUrl(OPEN_SPOTIFY_COM_PREFERENCES_URL);
}
@Override
public void onReceivedSession(WebView webView, Session session) {
Logger.printInfo(() -> "Received session: " + session);
currentSession = session;
getSessionLatch.countDown();
currentWebView = null;
webView.stopLoading();
webView.destroy();
}
}
);
try {
final boolean isAcquired = getSessionLatch.await(GET_SESSION_TIMEOUT_SECONDS, TimeUnit.SECONDS);
if (!isAcquired) {
Logger.printException(() -> "Failed to retrieve session within " + GET_SESSION_TIMEOUT_SECONDS + " seconds");
}
} catch (InterruptedException e) {
Logger.printException(() -> "Interrupted while waiting to retrieve session", e);
Thread.currentThread().interrupt();
}
// Cleanup.
currentWebView = null;
}
/**
* All methods are called on the main thread.
*/
abstract static class WebViewCallback {
void onInitialized(WebView webView) {
}
void onLoggedIn(String cookies) {
}
void onReceivedSession(WebView webView, Session session) {
}
}
@SuppressLint("SetJavaScriptEnabled")
private static void newWebView(
Context context,
WebViewCallback webViewCallback
) {
Utils.runOnMainThreadNowOrLater(() -> {
WebView webView = currentWebView;
if (webView != null) {
// Old webview is still hanging around.
// Could happen if the network request failed and thus no callback is made.
// But in practice this never happens.
Logger.printException(() -> "Cleaning up prior webview");
webView.stopLoading();
webView.destroy();
}
webView = new WebView(context);
WebSettings settings = webView.getSettings();
settings.setDomStorageEnabled(true);
settings.setJavaScriptEnabled(true);
settings.setUserAgentString(USER_AGENT);
// WebViewClient is always called off the main thread,
// but callback interface methods are called on the main thread.
webView.setWebViewClient(new WebViewClient() {
@Override
public WebResourceResponse shouldInterceptRequest(WebView view, WebResourceRequest request) {
if (OPEN_SPOTIFY_COM.equals(request.getUrl().getHost())) {
Utils.runOnMainThread(() -> webViewCallback.onLoggedIn(getCurrentCookies()));
}
return super.shouldInterceptRequest(view, request);
}
@Override
public void onPageStarted(WebView view, String url, Bitmap favicon) {
Logger.printInfo(() -> "Page started loading: " + url);
if (!url.startsWith(OPEN_SPOTIFY_COM_URL)) {
return;
}
Logger.printInfo(() -> "Evaluating script to get session on url: " + url);
String getSessionScript = "Object.defineProperty(Object.prototype, \"_username\", {" +
" configurable: true," +
" set(username) {" +
" accessToken = this._builder?.accessToken;" +
" if (accessToken) {" +
" " + JAVASCRIPT_INTERFACE_NAME + ".getSession(username, accessToken);" +
" delete Object.prototype._username;" +
" }" +
" " +
" Object.defineProperty(this, \"_username\", {" +
" configurable: true," +
" enumerable: true," +
" writable: true," +
" value: username" +
" })" +
" " +
" }" +
"});";
view.evaluateJavascript(getSessionScript, null);
}
});
final WebView callbackWebView = webView;
webView.addJavascriptInterface(new Object() {
@SuppressWarnings("unused")
@JavascriptInterface
public void getSession(String username, String accessToken) {
Session session = new Session(username, accessToken, getCurrentCookies());
Utils.runOnMainThread(() -> webViewCallback.onReceivedSession(callbackWebView, session));
}
}, JAVASCRIPT_INTERFACE_NAME);
currentWebView = webView;
CookieManager.getInstance().removeAllCookies((anyRemoved) -> {
Logger.printInfo(() -> "WebView initialized with user agent: " + USER_AGENT);
webViewCallback.onInitialized(currentWebView);
});
});
}
private static String getWebUserAgent() {
String userAgentString = WebSettings.getDefaultUserAgent(Utils.getContext());
try {
return new UserAgent(userAgentString)
.withCommentReplaced("Android", "Windows NT 10.0; Win64; x64")
.withoutProduct("Mobile")
.toString();
} catch (IllegalArgumentException e) {
userAgentString = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) " +
"AppleWebKit/537.36 (KHTML, like Gecko) Chrome/137.0.0.0 Safari/537.36 Edge/137.0.0.0";
String fallback = userAgentString;
Logger.printException(() -> "Failed to get user agent, falling back to " + fallback, e);
}
return userAgentString;
}
private static String getCurrentCookies() {
CookieManager cookieManager = CookieManager.getInstance();
return cookieManager.getCookie(OPEN_SPOTIFY_COM_URL);
}
private static void setCookies(@NonNull String cookies) {
CookieManager cookieManager = CookieManager.getInstance();
String[] cookiesList = cookies.split(";");
for (String cookie : cookiesList) {
cookieManager.setCookie(OPEN_SPOTIFY_COM_URL, cookie);
}
}
}

View File

@@ -0,0 +1,43 @@
syntax = "proto3";
package spotify.login5.v4;
option optimize_for = LITE_RUNTIME;
option java_package = "app.revanced.extension.spotify.login5.v4.proto";
message StoredCredential {
string username = 1;
bytes data = 2;
}
message LoginRequest {
oneof login_method {
StoredCredential stored_credential = 100;
}
}
message LoginOk {
string username = 1;
string access_token = 2;
bytes stored_credential = 3;
int32 access_token_expires_in = 4;
}
message LoginResponse {
oneof response {
LoginOk ok = 1;
LoginError error = 2;
}
}
enum LoginError {
UNKNOWN_ERROR = 0;
INVALID_CREDENTIALS = 1;
BAD_REQUEST = 2;
UNSUPPORTED_LOGIN_PROTOCOL = 3;
TIMEOUT = 4;
UNKNOWN_IDENTIFIER = 5;
TOO_MANY_ATTEMPTS = 6;
INVALID_PHONENUMBER = 7;
TRY_AGAIN_LATER = 8;
}

View File

@@ -1,5 +1,5 @@
plugins {
id(libs.plugins.android.library.get().pluginId)
alias(libs.plugins.android.library)
}
android {

View File

@@ -0,0 +1,19 @@
plugins {
java
antlr
}
dependencies {
antlr(libs.antlr4)
}
java {
sourceCompatibility = JavaVersion.VERSION_1_8
targetCompatibility = JavaVersion.VERSION_1_8
}
tasks {
generateGrammarSource {
arguments = listOf("-visitor")
}
}

View File

@@ -0,0 +1,35 @@
grammar UserAgent;
@header { package app.revanced.extension.spotify; }
userAgent
: product (WS product)* EOF
;
product
: name ('/' version)? (WS comment)?
;
name
: STRING
;
version
: STRING ('.' STRING)*
;
comment
: COMMENT
;
COMMENT
: '(' ~ ')'* ')'
;
STRING
: [a-zA-Z0-9]+
;
WS
: [ \r\n]+
;

View File

@@ -0,0 +1,60 @@
package app.revanced.extension.spotify;
import org.antlr.v4.runtime.CharStream;
import org.antlr.v4.runtime.CharStreams;
import org.antlr.v4.runtime.CommonTokenStream;
import org.antlr.v4.runtime.TokenStreamRewriter;
import org.antlr.v4.runtime.tree.ParseTreeWalker;
public class UserAgent {
private final UserAgentParser.UserAgentContext tree;
private final TokenStreamRewriter rewriter;
private final ParseTreeWalker walker;
public UserAgent(String userAgentString) {
CharStream input = CharStreams.fromString(userAgentString);
UserAgentLexer lexer = new UserAgentLexer(input);
CommonTokenStream tokens = new CommonTokenStream(lexer);
tree = new UserAgentParser(tokens).userAgent();
walker = new ParseTreeWalker();
rewriter = new TokenStreamRewriter(tokens);
}
public UserAgent withoutProduct(String name) {
walker.walk(new UserAgentBaseListener() {
@Override
public void exitProduct(UserAgentParser.ProductContext ctx) {
if (!ctx.name().getText().contains(name)) return;
int startIndex = ctx.getStart().getTokenIndex();
if (startIndex != 0) startIndex -= 1; // Also remove the preceding whitespace.
int stopIndex = ctx.getStop().getTokenIndex();
rewriter.delete(startIndex, stopIndex);
}
}, tree);
return new UserAgent(rewriter.getText().trim());
}
public UserAgent withCommentReplaced(String containing, String replacement) {
walker.walk(new UserAgentBaseListener() {
@Override
public void exitComment(UserAgentParser.CommentContext ctx) {
if (ctx.getText().contains(containing)) {
rewriter.replace(ctx.getStart(), ctx.getStop(), "(" + replacement + ")");
}
}
}, tree);
return new UserAgent(rewriter.getText());
}
@Override
public String toString() {
return rewriter.getText();
}
}

View File

@@ -1,5 +1,5 @@
plugins {
id(libs.plugins.android.library.get().pluginId)
alias(libs.plugins.android.library)
}
android {

View File

@@ -1,5 +1,5 @@
plugins {
id(libs.plugins.android.library.get().pluginId)
alias(libs.plugins.android.library)
}
android {

View File

@@ -1,7 +1,7 @@
android.namespace = "app.revanced.extension"
plugins {
id(libs.plugins.android.library.get().pluginId)
alias(libs.plugins.android.library)
}
android {

View File

@@ -1,5 +1,5 @@
plugins {
id(libs.plugins.android.library.get().pluginId)
alias(libs.plugins.android.library)
}
android {

View File

@@ -274,6 +274,11 @@ public final class ShortsFilter extends Filter {
Settings.HIDE_SHORTS_UPCOMING_BUTTON,
"yt_outline_bell_"
),
new ByteArrayFilterGroup(
Settings.HIDE_SHORTS_EFFECT_BUTTON,
// https://www.gstatic.com/youtube/effects/xeno/arcade/effects/icons/
"/arcade/effects/icons/"
),
new ByteArrayFilterGroup(
Settings.HIDE_SHORTS_GREEN_SCREEN_BUTTON,
"greenscreen_temp"

View File

@@ -267,6 +267,7 @@ public class Settings extends BaseSettings {
public static final BooleanSetting HIDE_SHORTS_COMMENTS_BUTTON = new BooleanSetting("revanced_hide_shorts_comments_button", FALSE);
public static final BooleanSetting HIDE_SHORTS_DISLIKE_BUTTON = new BooleanSetting("revanced_hide_shorts_dislike_button", FALSE);
public static final BooleanSetting HIDE_SHORTS_FULL_VIDEO_LINK_LABEL = new BooleanSetting("revanced_hide_shorts_full_video_link_label", FALSE);
public static final BooleanSetting HIDE_SHORTS_EFFECT_BUTTON = new BooleanSetting("revanced_hide_shorts_effect_button", TRUE);
public static final BooleanSetting HIDE_SHORTS_GREEN_SCREEN_BUTTON = new BooleanSetting("revanced_hide_shorts_green_screen_button", TRUE);
public static final BooleanSetting HIDE_SHORTS_NEW_POSTS_BUTTON = new BooleanSetting("revanced_hide_shorts_new_posts_button", TRUE);
public static final BooleanSetting HIDE_SHORTS_HASHTAG_BUTTON = new BooleanSetting("revanced_hide_shorts_hashtag_button", TRUE);

View File

@@ -1,5 +1,5 @@
plugins {
id(libs.plugins.android.library.get().pluginId)
alias(libs.plugins.android.library)
}
android {

View File

@@ -3,4 +3,4 @@ org.gradle.jvmargs = -Xms512M -Xmx2048M
org.gradle.parallel = true
android.useAndroidX = true
kotlin.code.style = official
version = 5.29.0-dev.8
version = 5.29.1-dev.1

View File

@@ -11,14 +11,25 @@ appcompat = "1.7.0"
okhttp = "5.0.0-alpha.14"
retrofit = "2.11.0"
guava = "33.4.0-jre"
protobuf-javalite = "4.31.1"
protoc = "4.31.1"
protobuf = "0.9.5"
antlr4 = "4.13.2"
nanohttpd = "2.3.1"
apksig = "8.10.1"
[libraries]
annotation = { module = "androidx.annotation:annotation", version.ref = "annotation" }
antlr4 = { module = "org.antlr:antlr4", version.ref = "antlr4" }
appcompat = { group = "androidx.appcompat", name = "appcompat", version.ref = "appcompat" }
nanohttpd = { module = "org.nanohttpd:nanohttpd", version.ref = "nanohttpd" }
okhttp = { module = "com.squareup.okhttp3:okhttp", version.ref = "okhttp" }
protobuf-javalite = { module = "com.google.protobuf:protobuf-javalite", version.ref = "protobuf-javalite" }
protobuf-protoc = { module = "com.google.protobuf:protoc", version.ref = "protoc" }
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" }
[plugins]
android-library = { id = "com.android.library", version.ref = "agp" }
android-library = { id = "com.android.library" }
protobuf = { id = "com.google.protobuf", version.ref = "protobuf" }

View File

@@ -1,8 +1,6 @@
#Mon Jun 16 14:39:32 CEST 2025
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
distributionSha256Sum=d725d707bfabd4dfdc958c624003b3c80accc03f7037b5122c4b1d0ef15cecab
distributionUrl=https\://services.gradle.org/distributions/gradle-8.9-bin.zip
networkTimeout=10000
validateDistributionUrl=true
distributionUrl=https\://services.gradle.org/distributions/gradle-8.14.2-bin.zip
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists

View File

@@ -116,6 +116,10 @@ public final class app/revanced/patches/all/misc/shortcut/sharetargets/RemoveSha
public static final fun getRemoveShareTargetsPatch ()Lapp/revanced/patcher/patch/ResourcePatch;
}
public final class app/revanced/patches/all/misc/spoof/SignatureSpoofPatchKt {
public static final fun getSignatureSpoofPatch ()Lapp/revanced/patcher/patch/ResourcePatch;
}
public final class app/revanced/patches/all/misc/targetSdk/SetTargetSdkVersion34Kt {
public static final fun getSetTargetSdkVersion34 ()Lapp/revanced/patcher/patch/ResourcePatch;
}
@@ -160,6 +164,10 @@ public final class app/revanced/patches/cieid/restrictions/root/BypassRootChecks
public static final fun getBypassRootChecksPatch ()Lapp/revanced/patcher/patch/BytecodePatch;
}
public final class app/revanced/patches/cricbuzz/ads/DisableAdsPatchKt {
public static final fun getDisableAdsPatch ()Lapp/revanced/patcher/patch/BytecodePatch;
}
public final class app/revanced/patches/crunchyroll/ads/HideAdsPatchKt {
public static final fun getHideAdsPatch ()Lapp/revanced/patcher/patch/BytecodePatch;
}
@@ -660,17 +668,39 @@ public final class app/revanced/patches/shared/misc/gms/GmsCoreSupportPatchKt {
public static synthetic fun gmsCoreSupportResourcePatch$default (Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Lapp/revanced/patcher/patch/Option;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function1;ILjava/lang/Object;)Lapp/revanced/patcher/patch/ResourcePatch;
}
public final class app/revanced/patches/shared/misc/hex/HexPatchKt {
public static final fun hexPatch (Lkotlin/jvm/functions/Function0;)Lapp/revanced/patcher/patch/RawResourcePatch;
public final class app/revanced/patches/shared/misc/hex/HexPatchBuilder : java/util/Set, kotlin/jvm/internal/markers/KMappedMarker {
public fun <init> ()V
public fun add (Lapp/revanced/patches/shared/misc/hex/Replacement;)Z
public synthetic fun add (Ljava/lang/Object;)Z
public fun addAll (Ljava/util/Collection;)Z
public final fun asPatternTo (Ljava/lang/String;Ljava/lang/String;)Lkotlin/Pair;
public fun clear ()V
public fun contains (Lapp/revanced/patches/shared/misc/hex/Replacement;)Z
public final fun contains (Ljava/lang/Object;)Z
public fun containsAll (Ljava/util/Collection;)Z
public fun getSize ()I
public final fun inFile (Lkotlin/Pair;Ljava/lang/String;)V
public fun isEmpty ()Z
public fun iterator ()Ljava/util/Iterator;
public fun remove (Ljava/lang/Object;)Z
public fun removeAll (Ljava/util/Collection;)Z
public fun retainAll (Ljava/util/Collection;)Z
public final fun size ()I
public fun toArray ()[Ljava/lang/Object;
public fun toArray ([Ljava/lang/Object;)[Ljava/lang/Object;
}
public final class app/revanced/patches/shared/misc/hex/HexPatchBuilderKt {
public static final fun hexPatch (ZLkotlin/jvm/functions/Function0;)Lapp/revanced/patcher/patch/RawResourcePatch;
public static final fun hexPatch (ZLkotlin/jvm/functions/Function1;)Lapp/revanced/patcher/patch/RawResourcePatch;
public static synthetic fun hexPatch$default (ZLkotlin/jvm/functions/Function0;ILjava/lang/Object;)Lapp/revanced/patcher/patch/RawResourcePatch;
public static synthetic fun hexPatch$default (ZLkotlin/jvm/functions/Function1;ILjava/lang/Object;)Lapp/revanced/patcher/patch/RawResourcePatch;
}
public final class app/revanced/patches/shared/misc/hex/Replacement {
public static final field Companion Lapp/revanced/patches/shared/misc/hex/Replacement$Companion;
public fun <init> (Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V
public final fun replacePattern ([B)V
}
public final class app/revanced/patches/shared/misc/hex/Replacement$Companion {
public fun <init> ([B[BLjava/lang/String;)V
public final fun getReplacementBytesPadded ()[B
}
public final class app/revanced/patches/shared/misc/mapping/ResourceElement {
@@ -914,6 +944,10 @@ public final class app/revanced/patches/spotify/misc/extension/ExtensionPatchKt
public static final fun getSharedExtensionPatch ()Lapp/revanced/patcher/patch/BytecodePatch;
}
public final class app/revanced/patches/spotify/misc/fix/SpoofClientPatchKt {
public static final fun getSpoofClientPatch ()Lapp/revanced/patcher/patch/BytecodePatch;
}
public final class app/revanced/patches/spotify/misc/fix/SpoofPackageInfoPatchKt {
public static final fun getSpoofPackageInfoPatch ()Lapp/revanced/patcher/patch/BytecodePatch;
}

View File

@@ -15,6 +15,9 @@ patches {
dependencies {
// Required due to smali, or build fails. Can be removed once smali is bumped.
implementation(libs.guava)
implementation(libs.apksig)
// Android API stubs defined here.
compileOnly(project(":patches:stub"))
}

View File

@@ -3,7 +3,7 @@ package app.revanced.patches.all.misc.hex
import app.revanced.patcher.patch.PatchException
import app.revanced.patcher.patch.rawResourcePatch
import app.revanced.patcher.patch.stringsOption
import app.revanced.patches.shared.misc.hex.Replacement
import app.revanced.patches.shared.misc.hex.HexPatchBuilder
import app.revanced.patches.shared.misc.hex.hexPatch
import app.revanced.util.Utils.trimIndentMultiline
@@ -13,44 +13,42 @@ val hexPatch = rawResourcePatch(
description = "Replaces a hexadecimal patterns of bytes of files in an APK.",
use = false,
) {
// TODO: Instead of stringArrayOption, use a custom option type to work around
// https://github.com/ReVanced/revanced-library/issues/48.
// Replace the custom option type with a stringArrayOption once the issue is resolved.
val replacements by stringsOption(
key = "replacements",
title = "Replacements",
description = """
Hexadecimal patterns to search for and replace with another in a target file.
A pattern is a sequence of case insensitive strings, each representing hexadecimal bytes, separated by spaces.
An example pattern is 'aa 01 02 FF'.
Every pattern must be followed by a pipe ('|'), the replacement pattern,
another pipe ('|'), and the path to the file to make the changes in relative to the APK root.
The replacement pattern must have the same length as the original pattern.
The replacement pattern must be shorter or equal in length to the pattern.
Full example of a valid input:
'aa 01 02 FF|00 00 00 00|path/to/file'
Full example of a valid replacement:
'01 02 aa FF|03 04|path/to/file'
""".trimIndentMultiline(),
required = true,
)
dependsOn(
hexPatch {
replacements!!.map { from ->
val (pattern, replacementPattern, targetFilePath) = try {
from.split("|", limit = 3)
} catch (e: Exception) {
throw PatchException(
"Invalid input: $from.\n" +
"Every pattern must be followed by a pipe ('|'), " +
"the replacement pattern, another pipe ('|'), " +
"and the path to the file to make the changes in relative to the APK root. ",
)
hexPatch(
block = fun HexPatchBuilder.() {
replacements!!.forEach { replacement ->
try {
val (pattern, replacementPattern, targetFilePath) = replacement.split("|", limit = 3)
pattern asPatternTo replacementPattern inFile targetFilePath
} catch (e: Exception) {
throw PatchException(
"Invalid replacement: $replacement.\n" +
"Every pattern must be followed by a pipe ('|'), " +
"the replacement pattern, another pipe ('|'), " +
"and the path to the file to make the changes in relative to the APK root. ",
)
}
}
Replacement(pattern, replacementPattern, targetFilePath)
}.toSet()
},
},
)
)
}

View File

@@ -0,0 +1,95 @@
package app.revanced.patches.all.misc.spoof
import app.revanced.patcher.patch.resourcePatch
import app.revanced.patcher.patch.stringOption
import app.revanced.util.getNode
import com.android.apksig.ApkVerifier
import com.android.apksig.apk.ApkFormatException
import org.w3c.dom.Element
import java.io.ByteArrayInputStream
import java.io.IOException
import java.nio.file.Files
import java.nio.file.InvalidPathException
import java.nio.file.attribute.BasicFileAttributes
import java.security.NoSuchAlgorithmException
import java.security.cert.CertificateException
import java.security.cert.CertificateFactory
import java.util.*
import kotlin.io.path.Path
val signatureSpoofPatch = resourcePatch(
name = "Spoof app signature",
description = "Spoofs the app signature via the \"fake-signature\" meta key. " +
"This patch only works with patched device roms.",
use = false,
) {
val signature by stringOption(
key = "spoofedAppSignature",
title = "Signature",
validator = { signature ->
optionToSignature(signature) != null
},
description = "The hex-encoded signature or path to an apk file with the desired signature",
required = true,
)
execute {
document("AndroidManifest.xml").use { document ->
val manifest = document.getNode("manifest") as Element
val fakeSignaturePermission = document.createElement("uses-permission")
fakeSignaturePermission.setAttribute("android:name", "android.permission.FAKE_PACKAGE_SIGNATURE")
manifest.appendChild(fakeSignaturePermission)
val application = document.getNode("application") ?: {
val child = document.createElement("application")
manifest.appendChild(child)
child
} as Element;
val fakeSignatureMetadata = document.createElement("meta-data")
fakeSignatureMetadata.setAttribute("android:name", "fake-signature")
fakeSignatureMetadata.setAttribute("android:value", optionToSignature(signature))
application.appendChild(fakeSignatureMetadata)
}
}
}
internal fun optionToSignature(signature: String?): String? {
if (signature == null) {
return null;
}
try {
// TODO: Replace with signature.hexToByteArray when stable in kotlin
val signatureBytes = HexFormat.of()
.parseHex(signature)
val factory = CertificateFactory.getInstance("X.509")
factory.generateCertificate(ByteArrayInputStream(signatureBytes))
return signature;
} catch (_: IllegalArgumentException) {
} catch (_: CertificateException) {
}
try {
val signaturePath = Path(signature)
if (!Files.readAttributes(signaturePath, BasicFileAttributes::class.java).isRegularFile) {
return null;
}
val verifier = ApkVerifier.Builder(signaturePath.toFile())
.build()
val result = verifier.verify()
if (result.isVerifiedUsingV3Scheme) {
return HexFormat.of().formatHex(result.v3SchemeSigners[0].certificate.encoded)
} else if (result.isVerifiedUsingV2Scheme) {
return HexFormat.of().formatHex(result.v2SchemeSigners[0].certificate.encoded)
} else if (result.isVerifiedUsingV1Scheme) {
return HexFormat.of().formatHex(result.v1SchemeSigners[0].certificate.encoded)
}
return null;
} catch (_: IOException) {
} catch (_: InvalidPathException) {
} catch (_: ApkFormatException) {
} catch (_: NoSuchAlgorithmException) {
} catch (_: IllegalArgumentException) {}
return null;
}

View File

@@ -0,0 +1,27 @@
package app.revanced.patches.cricbuzz.ads
import app.revanced.patcher.extensions.InstructionExtensions.addInstruction
import app.revanced.patcher.extensions.InstructionExtensions.getInstruction
import app.revanced.patcher.patch.bytecodePatch
import app.revanced.util.indexOfFirstInstructionOrThrow
import com.android.tools.smali.dexlib2.Opcode
import com.android.tools.smali.dexlib2.iface.instruction.OneRegisterInstruction
@Suppress("unused")
val disableAdsPatch = bytecodePatch (
name = "Hide ads",
) {
compatibleWith("com.cricbuzz.android"("6.23.02"))
execute {
userStateSwitchFingerprint.method.apply {
val opcodeIndex = indexOfFirstInstructionOrThrow(Opcode.MOVE_RESULT_OBJECT)
val register = getInstruction<OneRegisterInstruction>(opcodeIndex).registerA
addInstruction(
opcodeIndex + 1,
"const-string v$register, \"ACTIVE\""
)
}
}
}

View File

@@ -0,0 +1,9 @@
package app.revanced.patches.cricbuzz.ads
import app.revanced.patcher.fingerprint
import com.android.tools.smali.dexlib2.Opcode
internal val userStateSwitchFingerprint = fingerprint {
strings("key.user.state", "NA")
opcodes(Opcode.SPARSE_SWITCH)
}

View File

@@ -1,123 +0,0 @@
package app.revanced.patches.shared.misc.hex
import app.revanced.patcher.patch.PatchException
import app.revanced.patcher.patch.rawResourcePatch
import kotlin.math.max
// The replacements being passed using a function is intended.
// Previously the replacements were a property of the patch. Getter were being delegated to that property.
// This late evaluation was being leveraged in app.revanced.patches.all.misc.hex.HexPatch.
// Without the function, the replacements would be evaluated at the time of patch creation.
// This isn't possible because the delegated property is not accessible at that time.
fun hexPatch(replacementsSupplier: () -> Set<Replacement>) = rawResourcePatch {
execute {
replacementsSupplier().groupBy { it.targetFilePath }.forEach { (targetFilePath, replacements) ->
val targetFile = try {
get(targetFilePath, true)
} catch (e: Exception) {
throw PatchException("Could not find target file: $targetFilePath")
}
// TODO: Use a file channel to read and write the file instead of reading the whole file into memory,
// in order to reduce memory usage.
val targetFileBytes = targetFile.readBytes()
replacements.forEach { replacement ->
replacement.replacePattern(targetFileBytes)
}
targetFile.writeBytes(targetFileBytes)
}
}
}
/**
* Represents a pattern to search for and its replacement pattern.
*
* @property pattern The pattern to search for.
* @property replacementPattern The pattern to replace the [pattern] with.
* @property targetFilePath The path to the file to make the changes in relative to the APK root.
*/
class Replacement(
private val pattern: String,
replacementPattern: String,
internal val targetFilePath: String,
) {
private val patternBytes = pattern.toByteArrayPattern()
private val replacementPattern = replacementPattern.toByteArrayPattern()
init {
if (this.patternBytes.size != this.replacementPattern.size) {
throw PatchException("Pattern and replacement pattern must have the same length: $pattern")
}
}
/**
* Replaces the [patternBytes] with the [replacementPattern] in the [targetFileBytes].
*
* @param targetFileBytes The bytes of the file to make the changes in.
*/
fun replacePattern(targetFileBytes: ByteArray) {
val startIndex = indexOfPatternIn(targetFileBytes)
if (startIndex == -1) {
throw PatchException("Pattern not found in target file: $pattern")
}
replacementPattern.copyInto(targetFileBytes, startIndex)
}
// TODO: Allow searching in a file channel instead of a byte array to reduce memory usage.
/**
* Returns the index of the first occurrence of [patternBytes] in the haystack
* using the Boyer-Moore algorithm.
*
* @param haystack The array to search in.
*
* @return The index of the first occurrence of the [patternBytes] in the haystack or -1
* if the [patternBytes] is not found.
*/
private fun indexOfPatternIn(haystack: ByteArray): Int {
val needle = patternBytes
val haystackLength = haystack.size - 1
val needleLength = needle.size - 1
val right = IntArray(256) { -1 }
for (i in 0 until needleLength) right[needle[i].toInt().and(0xFF)] = i
var skip: Int
for (i in 0..haystackLength - needleLength) {
skip = 0
for (j in needleLength - 1 downTo 0) {
if (needle[j] != haystack[i + j]) {
skip = max(1, j - right[haystack[i + j].toInt().and(0xFF)])
break
}
}
if (skip == 0) return i
}
return -1
}
companion object {
/**
* Convert a string representing a pattern of hexadecimal bytes to a byte array.
*
* @return The byte array representing the pattern.
* @throws PatchException If the pattern is invalid.
*/
private fun String.toByteArrayPattern() = try {
split(" ").map { it.toInt(16).toByte() }.toByteArray()
} catch (e: NumberFormatException) {
throw PatchException(
"Could not parse pattern: $this. A pattern is a sequence of case insensitive strings " +
"representing hexadecimal bytes separated by spaces",
e,
)
}
}
}

View File

@@ -0,0 +1,154 @@
package app.revanced.patches.shared.misc.hex
import app.revanced.patcher.patch.PatchException
import app.revanced.patcher.patch.rawResourcePatch
import kotlin.collections.component1
import kotlin.collections.component2
import kotlin.math.max
fun hexPatch(ignoreMissingTargetFiles: Boolean = false, block: HexPatchBuilder.() -> Unit) =
hexPatch(ignoreMissingTargetFiles, fun(): Set<Replacement> = HexPatchBuilder().apply(block))
@Suppress("JavaDefaultMethodsNotOverriddenByDelegation")
class HexPatchBuilder internal constructor(
private val replacements: MutableSet<Replacement> = mutableSetOf(),
) : Set<Replacement> by replacements {
infix fun String.asPatternTo(replacementPattern: String) = byteArrayOf(this) to byteArrayOf(replacementPattern)
infix fun <T> Pair<T, T>.inFile(filePath: String) {
if (first is String && second is String) {
val first = first as String
val second = second as String
replacements += Replacement(
first.toByteArray(), second.toByteArray(),
filePath
)
} else if (first is ByteArray && second is ByteArray) {
val first = first as ByteArray
val second = second as ByteArray
replacements += Replacement(first, second, filePath)
} else {
throw PatchException("Unsupported types for pattern and replacement: $first, $second")
}
}
}
// The replacements being passed using a function is intended.
// Previously the replacements were a property of the patch. Getter were being delegated to that property.
// This late evaluation was being leveraged in app.revanced.patches.all.misc.hex.HexPatch.
// Without the function, the replacements would be evaluated at the time of patch creation.
// This isn't possible because the delegated property is not accessible at that time.
@Deprecated("Use the hexPatch function with the builder parameter instead.")
fun hexPatch(ignoreMissingTargetFiles: Boolean = false, replacementsSupplier: () -> Set<Replacement>) =
rawResourcePatch {
execute {
replacementsSupplier().groupBy { it.targetFilePath }.forEach { (targetFilePath, replacements) ->
val targetFile = get(targetFilePath, true)
if (ignoreMissingTargetFiles && !targetFile.exists()) return@forEach
// TODO: Use a file channel to read and write the file instead of reading the whole file into memory,
// in order to reduce memory usage.
val targetFileBytes = targetFile.readBytes()
replacements.forEach { it.replacePattern(targetFileBytes) }
targetFile.writeBytes(targetFileBytes)
}
}
}
/**
* Represents a pattern to search for and its replacement pattern in a file.
*
* @property bytes The bytes to search for.
* @property replacementBytes The bytes to replace the [bytes] with.
* @property targetFilePath The path to the file to make the changes in relative to the APK root.
*/
class Replacement(
private val bytes: ByteArray,
replacementBytes: ByteArray,
internal val targetFilePath: String,
) {
val replacementBytesPadded = replacementBytes + ByteArray(bytes.size - replacementBytes.size)
@Deprecated("Use the constructor with ByteArray parameters instead.")
constructor(
pattern: String,
replacementPattern: String,
targetFilePath: String,
) : this(
byteArrayOf(pattern),
byteArrayOf(replacementPattern),
targetFilePath
)
/**
* Replaces the [bytes] with the [replacementBytes] in the [targetFileBytes].
*
* @param targetFileBytes The bytes of the file to make the changes in.
*/
internal fun replacePattern(targetFileBytes: ByteArray) {
val startIndex = indexOfPatternIn(targetFileBytes)
if (startIndex == -1) {
throw PatchException(
"Pattern not found in target file: " +
bytes.joinToString(" ") { "%02x".format(it) }
)
}
replacementBytesPadded.copyInto(targetFileBytes, startIndex)
}
// TODO: Allow searching in a file channel instead of a byte array to reduce memory usage.
/**
* Returns the index of the first occurrence of [bytes] in the haystack
* using the Boyer-Moore algorithm.
*
* @param haystack The array to search in.
*
* @return The index of the first occurrence of the [bytes] in the haystack or -1
* if the [bytes] is not found.
*/
private fun indexOfPatternIn(haystack: ByteArray): Int {
val needle = bytes
val haystackLength = haystack.size - 1
val needleLength = needle.size - 1
val right = IntArray(256) { -1 }
for (i in 0 until needleLength) right[needle[i].toInt().and(0xFF)] = i
var skip: Int
for (i in 0..haystackLength - needleLength) {
skip = 0
for (j in needleLength - 1 downTo 0) {
if (needle[j] != haystack[i + j]) {
skip = max(1, j - right[haystack[i + j].toInt().and(0xFF)])
break
}
}
if (skip == 0) return i
}
return -1
}
}
/**
* Convert a string representing a pattern of hexadecimal bytes to a byte array.
*
* @return The byte array representing the pattern.
* @throws PatchException If the pattern is invalid.
*/
private fun byteArrayOf(pattern: String) = try {
pattern.split(" ").map { it.toInt(16).toByte() }.toByteArray()
} catch (e: NumberFormatException) {
throw PatchException(
"Could not parse pattern: $pattern. A pattern is a sequence of case insensitive strings " +
"representing hexadecimal bytes separated by spaces",
e,
)
}

View File

@@ -1,9 +1,21 @@
package app.revanced.patches.spotify.misc.fix
import app.revanced.patcher.fingerprint
import com.android.tools.smali.dexlib2.AccessFlags
internal val getPackageInfoFingerprint = fingerprint {
strings(
"Failed to get the application signatures"
)
}
internal val startLiborbitFingerprint = fingerprint {
strings("/liborbit-jni-spotify.so")
}
internal val startupPageLayoutInflateFingerprint = fingerprint {
accessFlags(AccessFlags.PUBLIC, AccessFlags.FINAL)
returns("Landroid/view/View;")
parameters("Landroid/view/LayoutInflater;", "Landroid/view/ViewGroup;", "Landroid/os/Bundle;")
strings("blueprintContainer", "gradient", "valuePropositionTextView")
}

View File

@@ -0,0 +1,122 @@
package app.revanced.patches.spotify.misc.fix
import app.revanced.patcher.extensions.InstructionExtensions.addInstruction
import app.revanced.patcher.extensions.InstructionExtensions.addInstructions
import app.revanced.patcher.extensions.InstructionExtensions.getInstruction
import app.revanced.patcher.extensions.InstructionExtensions.replaceInstruction
import app.revanced.patcher.patch.bytecodePatch
import app.revanced.patcher.patch.intOption
import app.revanced.patches.shared.misc.hex.HexPatchBuilder
import app.revanced.patches.shared.misc.hex.hexPatch
import app.revanced.patches.spotify.misc.extension.sharedExtensionPatch
import app.revanced.util.findInstructionIndicesReversedOrThrow
import app.revanced.util.getReference
import app.revanced.util.indexOfFirstInstructionReversedOrThrow
import com.android.tools.smali.dexlib2.Opcode
import com.android.tools.smali.dexlib2.iface.instruction.OneRegisterInstruction
import com.android.tools.smali.dexlib2.iface.reference.MethodReference
internal const val EXTENSION_CLASS_DESCRIPTOR = "Lapp/revanced/extension/spotify/misc/fix/SpoofClientPatch;"
@Suppress("unused")
val spoofClientPatch = bytecodePatch(
name = "Spoof client",
description = "Spoofs the client to fix various functions of the app.",
) {
val port by intOption(
key = "port",
default = 4345,
title = " Login request listener port",
description = "The port to use for the listener that intercepts and handles login requests. " +
"Port must be between 0 and 65535.",
required = true,
validator = {
it!!
!(it < 0 || it > 65535)
}
)
dependsOn(
sharedExtensionPatch,
hexPatch(ignoreMissingTargetFiles = true, block = fun HexPatchBuilder.() {
listOf(
"arm64-v8a",
"armeabi-v7a",
"x86",
"x86_64"
).forEach { architecture ->
"https://login5.spotify.com/v3/login" to "http://127.0.0.1:$port/v3/login" inFile
"lib/$architecture/liborbit-jni-spotify.so"
"https://login5.spotify.com/v4/login" to "http://127.0.0.1:$port/v4/login" inFile
"lib/$architecture/liborbit-jni-spotify.so"
}
})
)
compatibleWith("com.spotify.music")
execute {
getPackageInfoFingerprint.method.apply {
// region Spoof signature.
val failedToGetSignaturesStringIndex =
getPackageInfoFingerprint.stringMatches!!.first().index
val concatSignaturesIndex = indexOfFirstInstructionReversedOrThrow(
failedToGetSignaturesStringIndex,
Opcode.MOVE_RESULT_OBJECT,
)
val signatureRegister = getInstruction<OneRegisterInstruction>(concatSignaturesIndex).registerA
val expectedSignature = "d6a6dced4a85f24204bf9505ccc1fce114cadb32"
replaceInstruction(concatSignaturesIndex, "const-string v$signatureRegister, \"$expectedSignature\"")
// endregion
// region Spoof installer name.
val expectedInstallerName = "com.android.vending"
findInstructionIndicesReversedOrThrow {
val reference = getReference<MethodReference>()
reference?.name == "getInstallerPackageName" || reference?.name == "getInstallingPackageName"
}.forEach { index ->
val returnObjectIndex = index + 1
val installerPackageNameRegister = getInstruction<OneRegisterInstruction>(
returnObjectIndex
).registerA
addInstruction(
returnObjectIndex + 1,
"const-string v$installerPackageNameRegister, \"$expectedInstallerName\""
)
}
// endregion
}
startLiborbitFingerprint.method.addInstructions(
0,
"""
const/16 v0, $port
invoke-static { v0 }, $EXTENSION_CLASS_DESCRIPTOR->listen(I)V
"""
)
startupPageLayoutInflateFingerprint.method.apply {
val openLoginWebViewDescriptor =
"$EXTENSION_CLASS_DESCRIPTOR->login(Landroid/view/LayoutInflater;)V"
addInstructions(
0,
"""
move-object/from16 v3, p1
invoke-static { v3 }, $openLoginWebViewDescriptor
"""
)
}
}
}

View File

@@ -1,63 +1,11 @@
package app.revanced.patches.spotify.misc.fix
import app.revanced.patcher.extensions.InstructionExtensions.addInstruction
import app.revanced.patcher.extensions.InstructionExtensions.getInstruction
import app.revanced.patcher.extensions.InstructionExtensions.replaceInstruction
import app.revanced.patcher.patch.bytecodePatch
import app.revanced.util.findInstructionIndicesReversedOrThrow
import app.revanced.util.getReference
import app.revanced.util.indexOfFirstInstructionReversedOrThrow
import com.android.tools.smali.dexlib2.Opcode
import com.android.tools.smali.dexlib2.iface.instruction.OneRegisterInstruction
import com.android.tools.smali.dexlib2.iface.reference.MethodReference
@Deprecated("Superseded by spoofClientPatch", ReplaceWith("spoofClientPatch"))
@Suppress("unused")
val spoofPackageInfoPatch = bytecodePatch(
name = "Spoof package info",
description = "Spoofs the package info of the app to fix various functions of the app.",
) {
compatibleWith("com.spotify.music")
execute {
getPackageInfoFingerprint.method.apply {
// region Spoof signature.
val failedToGetSignaturesStringIndex =
getPackageInfoFingerprint.stringMatches!!.first().index
val concatSignaturesIndex = indexOfFirstInstructionReversedOrThrow(
failedToGetSignaturesStringIndex,
Opcode.MOVE_RESULT_OBJECT,
)
val signatureRegister = getInstruction<OneRegisterInstruction>(concatSignaturesIndex).registerA
val expectedSignature = "d6a6dced4a85f24204bf9505ccc1fce114cadb32"
replaceInstruction(concatSignaturesIndex, "const-string v$signatureRegister, \"$expectedSignature\"")
// endregion
// region Spoof installer name.
val expectedInstallerName = "com.android.vending"
findInstructionIndicesReversedOrThrow {
val reference = getReference<MethodReference>()
reference?.name == "getInstallerPackageName" || reference?.name == "getInstallingPackageName"
}.forEach { index ->
val returnObjectIndex = index + 1
val installerPackageNameRegister = getInstruction<OneRegisterInstruction>(
returnObjectIndex
).registerA
addInstruction(
returnObjectIndex + 1,
"const-string v$installerPackageNameRegister, \"$expectedInstallerName\""
)
}
// endregion
}
}
dependsOn(spoofClientPatch)
}

View File

@@ -2,10 +2,10 @@ package app.revanced.patches.spotify.misc.fix
import app.revanced.patcher.patch.bytecodePatch
@Deprecated("Superseded by spoofPackageInfoPatch", ReplaceWith("spoofPackageInfoPatch"))
@Deprecated("Superseded by spoofClientPatch", ReplaceWith("spoofClientPatch"))
@Suppress("unused")
val spoofSignaturePatch = bytecodePatch(
description = "Spoofs the signature of the app fix various functions of the app.",
) {
dependsOn(spoofPackageInfoPatch)
dependsOn(spoofClientPatch)
}

View File

@@ -97,6 +97,7 @@ private val hideShortsComponentsResourcePatch = resourcePatch {
SwitchPreference("revanced_hide_shorts_use_sound_button"),
SwitchPreference("revanced_hide_shorts_use_template_button"),
SwitchPreference("revanced_hide_shorts_upcoming_button"),
SwitchPreference("revanced_hide_shorts_effect_button"),
SwitchPreference("revanced_hide_shorts_green_screen_button"),
SwitchPreference("revanced_hide_shorts_hashtag_button"),
SwitchPreference("revanced_hide_shorts_new_posts_button"),

View File

@@ -773,6 +773,9 @@ Second \"item\" text"</string>
<string name="revanced_hide_shorts_upcoming_button_title">إخفاء زر القادم</string>
<string name="revanced_hide_shorts_upcoming_button_summary_on">تم إخفاء زر القادم</string>
<string name="revanced_hide_shorts_upcoming_button_summary_off">يتم عرض زر القادم</string>
<string name="revanced_hide_shorts_effect_button_title">إخفاء زر التأثير</string>
<string name="revanced_hide_shorts_effect_button_summary_on">زر التأثير مخفي</string>
<string name="revanced_hide_shorts_effect_button_summary_off">زر التأثير معروض</string>
<string name="revanced_hide_shorts_green_screen_button_title">إخفاء زر الشاشة الخضراء</string>
<string name="revanced_hide_shorts_green_screen_button_summary_on">تم إخفاء زر الشاشة الخضراء</string>
<string name="revanced_hide_shorts_green_screen_button_summary_off">يتم عرض زر الشاشة الخضراء</string>

View File

@@ -773,6 +773,9 @@ Audio trek seçimin göstərmək üçün \"Video axınları saxtalaşdır\"ı iO
<string name="revanced_hide_shorts_upcoming_button_title">Yaxınlaşan düyməsini gizlət</string>
<string name="revanced_hide_shorts_upcoming_button_summary_on">\"Yaxınlaşan\" düyməsi gizlidir</string>
<string name="revanced_hide_shorts_upcoming_button_summary_off">\"Yaxınlaşan\" düyməsi göstərilir</string>
<string name="revanced_hide_shorts_effect_button_title">Effekt düyməsini gizlət</string>
<string name="revanced_hide_shorts_effect_button_summary_on">Effekt düyməsi gizlidir</string>
<string name="revanced_hide_shorts_effect_button_summary_off">Effekt düyməsi görünür</string>
<string name="revanced_hide_shorts_green_screen_button_title">Yaşıl ekran düyməsini gizlət</string>
<string name="revanced_hide_shorts_green_screen_button_summary_on">\"Yaşıl ekran\" düyməsi gizlidir</string>
<string name="revanced_hide_shorts_green_screen_button_summary_off">\"Yaşıl ekran\" düyməsi göstərilir</string>

View File

@@ -773,6 +773,9 @@ Second \"item\" text"</string>
<string name="revanced_hide_shorts_upcoming_button_title">Схаваць кнопку «Наступныя»</string>
<string name="revanced_hide_shorts_upcoming_button_summary_on">Кнопка Будущие ролики скрыта</string>
<string name="revanced_hide_shorts_upcoming_button_summary_off">Кнопка Будущие ролики отображается</string>
<string name="revanced_hide_shorts_effect_button_title">Схаваць кнопку эфекту</string>
<string name="revanced_hide_shorts_effect_button_summary_on">Кнопка эфекту схавана</string>
<string name="revanced_hide_shorts_effect_button_summary_off">Кнопка эфекту паказана</string>
<string name="revanced_hide_shorts_green_screen_button_title">Схаваць кнопку «Зялёны экран»</string>
<string name="revanced_hide_shorts_green_screen_button_summary_on">Кнопка с зелёным экраном Shorts скрыта</string>
<string name="revanced_hide_shorts_green_screen_button_summary_off">Кнопка с зелёным экраном Shorts отображается</string>

View File

@@ -773,6 +773,9 @@ Second \"item\" text"</string>
<string name="revanced_hide_shorts_upcoming_button_title">Скриване на бутона Upcoming</string>
<string name="revanced_hide_shorts_upcoming_button_summary_on">Бутон \"Предстоящи събития\" е скрит</string>
<string name="revanced_hide_shorts_upcoming_button_summary_off">Бутон \"Предстоящи събития\" се показва</string>
<string name="revanced_hide_shorts_effect_button_title">Скрий бутона за ефект</string>
<string name="revanced_hide_shorts_effect_button_summary_on">Бутонът за ефекти е скрит</string>
<string name="revanced_hide_shorts_effect_button_summary_off">Бутонът за ефекти е видим</string>
<string name="revanced_hide_shorts_green_screen_button_title">Скриване на бутона Green screen</string>
<string name="revanced_hide_shorts_green_screen_button_summary_on">Бутон \"Зелен екран\" е скрит</string>
<string name="revanced_hide_shorts_green_screen_button_summary_off">Бутон \"Зелен екран\" се показва</string>

View File

@@ -769,6 +769,9 @@ MicroG-এর জন্য ব্যাটারি অপ্টিমাইজ
<string name="revanced_hide_shorts_upcoming_button_title">আসন্ন বোতামটি লুকান</string>
<string name="revanced_hide_shorts_upcoming_button_summary_on">আগামী বোতাম লুকানো আছে</string>
<string name="revanced_hide_shorts_upcoming_button_summary_off">আগামী বোতাম দেখানো হচ্ছে</string>
<string name="revanced_hide_shorts_effect_button_title">ইফেক্ট বোতাম লুকান</string>
<string name="revanced_hide_shorts_effect_button_summary_on">ইফেক্ট বাটন লুকানো আছে</string>
<string name="revanced_hide_shorts_effect_button_summary_off">ইফেক্ট বাটন দেখানো আছে</string>
<string name="revanced_hide_shorts_green_screen_button_title">গ্রিন স্ক্রিন বোতামটি লুকান</string>
<string name="revanced_hide_shorts_green_screen_button_summary_on">সবুজ পর্দা বোতাম লুকানো আছে</string>
<string name="revanced_hide_shorts_green_screen_button_summary_off">সবুজ পর্দা বোতাম দেখানো হচ্ছে</string>

View File

@@ -773,6 +773,9 @@ Chcete-li zobrazit nabídku zvukové stopy, změňte možnost „Zfalšovat stre
<string name="revanced_hide_shorts_upcoming_button_title">Skrýt tlačítko Nadcházející</string>
<string name="revanced_hide_shorts_upcoming_button_summary_on">Tlačítko \"Další\" je skryté</string>
<string name="revanced_hide_shorts_upcoming_button_summary_off">Tlačítko \"Další\" je zobrazeno</string>
<string name="revanced_hide_shorts_effect_button_title">Skrýt tlačítko efektu</string>
<string name="revanced_hide_shorts_effect_button_summary_on">Tlačítko Efekt je skryté</string>
<string name="revanced_hide_shorts_effect_button_summary_off">Tlačítko Efekt je zobrazeno</string>
<string name="revanced_hide_shorts_green_screen_button_title">Skrýt tlačítko Zelená obrazovka</string>
<string name="revanced_hide_shorts_green_screen_button_summary_on">Tlačítko zelené obrazovky je skryté</string>
<string name="revanced_hide_shorts_green_screen_button_summary_off">Tlačítko zelené obrazovky je zobrazeno</string>

View File

@@ -773,6 +773,9 @@ For at vise lydspormenuen skal du ændre \"Spoof videostream\" til iOS TV"</stri
<string name="revanced_hide_shorts_upcoming_button_title">Skjul knappen Kommende</string>
<string name="revanced_hide_shorts_upcoming_button_summary_on">Kommende knap er skjult</string>
<string name="revanced_hide_shorts_upcoming_button_summary_off">Kommende knap vises</string>
<string name="revanced_hide_shorts_effect_button_title">Skjul effektknap</string>
<string name="revanced_hide_shorts_effect_button_summary_on">Effektknap er skjult</string>
<string name="revanced_hide_shorts_effect_button_summary_off">Effektknap er vist</string>
<string name="revanced_hide_shorts_green_screen_button_title">Skjul knappen Grøn skærm</string>
<string name="revanced_hide_shorts_green_screen_button_summary_on">Grøn skærmknap er skjult</string>
<string name="revanced_hide_shorts_green_screen_button_summary_off">Grøn skærmknap vises</string>

View File

@@ -766,6 +766,9 @@ Um das Audiotrack-Menü anzuzeigen, ändere \"Video-Streams fälschen\" zu iOS T
<string name="revanced_hide_shorts_upcoming_button_title">\'Demnächst\'-Button ausblenden</string>
<string name="revanced_hide_shorts_upcoming_button_summary_on">Kommender Button ist ausgeblendet</string>
<string name="revanced_hide_shorts_upcoming_button_summary_off">Kommende Schaltfläche wird angezeigt</string>
<string name="revanced_hide_shorts_effect_button_title">Effekt-Schaltfläche ausblenden</string>
<string name="revanced_hide_shorts_effect_button_summary_on">Effekt-Taste ist ausgeblendet</string>
<string name="revanced_hide_shorts_effect_button_summary_off">Effekt-Taste ist eingeblendet</string>
<string name="revanced_hide_shorts_green_screen_button_title">Green-Screen-Button ausblenden</string>
<string name="revanced_hide_shorts_green_screen_button_summary_on">Grünbildschirm-Taste ist ausgeblendet</string>
<string name="revanced_hide_shorts_green_screen_button_summary_off">Grünbildschirm-Taste wird angezeigt</string>

View File

@@ -775,6 +775,9 @@ Second \"item\" text"</string>
<string name="revanced_hide_shorts_upcoming_button_title">Κουμπί επερχόμενης πρεμιέρας/ζωντανής ροής</string>
<string name="revanced_hide_shorts_upcoming_button_summary_on">Κρυμμένο</string>
<string name="revanced_hide_shorts_upcoming_button_summary_off">Εμφανίζεται</string>
<string name="revanced_hide_shorts_effect_button_title">Κουμπί «Εφέ»</string>
<string name="revanced_hide_shorts_effect_button_summary_on">Κρυμμένο</string>
<string name="revanced_hide_shorts_effect_button_summary_off">Εμφανίζεται</string>
<string name="revanced_hide_shorts_green_screen_button_title">Κουμπί «Green screen»</string>
<string name="revanced_hide_shorts_green_screen_button_summary_on">Κρυμμένο</string>
<string name="revanced_hide_shorts_green_screen_button_summary_off">Εμφανίζεται</string>

View File

@@ -770,6 +770,9 @@ Para mostrar el menú de la pista de audio, cambia \"Suplantar transmisiones de
<string name="revanced_hide_shorts_upcoming_button_title">Ocultar el botón Próximos</string>
<string name="revanced_hide_shorts_upcoming_button_summary_on">El botón próximo está oculto</string>
<string name="revanced_hide_shorts_upcoming_button_summary_off">Se muestra el botón próximo</string>
<string name="revanced_hide_shorts_effect_button_title">Ocultar botón de efecto</string>
<string name="revanced_hide_shorts_effect_button_summary_on">El botón de efecto está oculto</string>
<string name="revanced_hide_shorts_effect_button_summary_off">El botón de efecto está visible</string>
<string name="revanced_hide_shorts_green_screen_button_title">Ocultar el botón Pantalla verde</string>
<string name="revanced_hide_shorts_green_screen_button_summary_on">El botón verde de pantalla está oculto</string>
<string name="revanced_hide_shorts_green_screen_button_summary_off">Se muestra el botón verde de pantalla</string>

View File

@@ -772,6 +772,9 @@ Heliriba menüü kuvamiseks muutke valikut „Võltsitud videovoogedastus“ vä
<string name="revanced_hide_shorts_upcoming_button_title">Peida Eelseisvad nupp</string>
<string name="revanced_hide_shorts_upcoming_button_summary_on">Eelseisva nuppu on peidetud</string>
<string name="revanced_hide_shorts_upcoming_button_summary_off">Eelseisva nuppu on nähtav</string>
<string name="revanced_hide_shorts_effect_button_title">Peida efekti nupp</string>
<string name="revanced_hide_shorts_effect_button_summary_on">Efekti nupp on peidetud</string>
<string name="revanced_hide_shorts_effect_button_summary_off">Efekti nupp on nähtav</string>
<string name="revanced_hide_shorts_green_screen_button_title">Peida Roheline ekraan nupp</string>
<string name="revanced_hide_shorts_green_screen_button_summary_on">Rohelise ekraani nupp on peidetud</string>
<string name="revanced_hide_shorts_green_screen_button_summary_off">Rohelise ekraani nupp on nähtav</string>

View File

@@ -770,6 +770,9 @@ Upang ipakita ang menu ng Audio track, baguhin ang 'Spoof video streams' sa iOS
<string name="revanced_hide_shorts_upcoming_button_title">Itago ang Upcoming button</string>
<string name="revanced_hide_shorts_upcoming_button_summary_on">Nakatago ang pindutan ng \"Upcoming\"</string>
<string name="revanced_hide_shorts_upcoming_button_summary_off">Ipinapakita ang pindutan ng \"Upcoming\"</string>
<string name="revanced_hide_shorts_effect_button_title">Itago ang Button ng Epekto</string>
<string name="revanced_hide_shorts_effect_button_summary_on">Nakatago ang button ng epekto</string>
<string name="revanced_hide_shorts_effect_button_summary_off">Nakikita ang button ng epekto</string>
<string name="revanced_hide_shorts_green_screen_button_title">Itago ang Green screen button</string>
<string name="revanced_hide_shorts_green_screen_button_summary_on">Nakatago ang pindutan ng \"Green screen\"</string>
<string name="revanced_hide_shorts_green_screen_button_summary_off">Ipinapakita ang pindutan ng \"Green screen\"</string>

View File

@@ -534,9 +534,9 @@ Réglez le volume en balayant verticalement sur le côté droit de l'écran"</st
<string name="revanced_hide_share_button_summary_on">Le bouton Partager est masqué</string>
<string name="revanced_hide_share_button_summary_off">Le bouton Partager est affiché</string>
<!-- 'Stop ads' should be translated with the same localized wording that YouTube displays. -->
<string name="revanced_hide_stop_ads_button_title">Masquer l\'arrêt des publicités</string>
<string name="revanced_hide_stop_ads_button_summary_on">Le bouton Arrêter les publicités est masqué</string>
<string name="revanced_hide_stop_ads_button_summary_off">Le bouton Arrêter les publicités est affiché</string>
<string name="revanced_hide_stop_ads_button_title">Masquer \"Zéro annonce\"</string>
<string name="revanced_hide_stop_ads_button_summary_on">Le bouton Zéro annonce est masqué</string>
<string name="revanced_hide_stop_ads_button_summary_off">Le bouton Zéro annonce est affiché</string>
<!-- 'Report' should be translated with the same localized wording that YouTube displays.
This button usually appears only on live streams. -->
<string name="revanced_hide_report_button_title">Masquer \"Signaler\"</string>
@@ -564,7 +564,7 @@ Réglez le volume en balayant verticalement sur le côté droit de l'écran"</st
<string name="revanced_hide_clip_button_summary_on">Le bouton Clip est masqué</string>
<string name="revanced_hide_clip_button_summary_off">Le bouton Clip est affiché</string>
<!-- 'Save' should be translated with the same localized wording that YouTube displays. -->
<string name="revanced_hide_save_button_title">Masquer Enregistrer</string>
<string name="revanced_hide_save_button_title">Masquer \"Enregistrer\"</string>
<string name="revanced_hide_save_button_summary_on">Le bouton Enregistrer est masqué</string>
<string name="revanced_hide_save_button_summary_off">Le bouton Enregistrer est affiché</string>
</patch>
@@ -773,6 +773,9 @@ Pour afficher le menu Piste audio, définissez \"Falsifier les flux vidéo\" sur
<string name="revanced_hide_shorts_upcoming_button_title">Masquer le bouton Diffusion prévue</string>
<string name="revanced_hide_shorts_upcoming_button_summary_on">Le bouton Diffusion prévue est masqué</string>
<string name="revanced_hide_shorts_upcoming_button_summary_off">Le bouton Diffusion prévue est affiché</string>
<string name="revanced_hide_shorts_effect_button_title">Masquer le bouton d\'effet</string>
<string name="revanced_hide_shorts_effect_button_summary_on">Le bouton d\'effet est masqué</string>
<string name="revanced_hide_shorts_effect_button_summary_off">Le bouton d\'effet est affiché</string>
<string name="revanced_hide_shorts_green_screen_button_title">Masquer le bouton Écran vert</string>
<string name="revanced_hide_shorts_green_screen_button_summary_on">Le bouton Écran vert est masqué</string>
<string name="revanced_hide_shorts_green_screen_button_summary_off">Le bouton Écran vert est affiché</string>
@@ -1179,7 +1182,9 @@ Si désactivé ultérieurement, il est recommandé d'effacer les données de l'a
<string name="revanced_change_start_page_entry_music">Musique</string>
<string name="revanced_change_start_page_entry_news">Actualités</string>
<string name="revanced_change_start_page_entry_notifications">Notifications</string>
<string name="revanced_change_start_page_entry_playlists">Playlists</string>
<string name="revanced_change_start_page_entry_search">Recherche</string>
<string name="revanced_change_start_page_entry_shopping">Shopping</string>
<string name="revanced_change_start_page_entry_sports">Sport</string>
<string name="revanced_change_start_page_entry_subscriptions">Abonnements</string>
<string name="revanced_change_start_page_entry_trending">Tendances</string>
@@ -1406,6 +1411,7 @@ Activer cette option peut déverrouiller des qualités vidéo supérieures"</str
<string name="revanced_remember_shorts_quality_last_selected_summary_off">Les changements de qualité s\'appliquent uniquement au Short actuel</string>
<string name="revanced_shorts_quality_default_wifi_title">Qualité par défaut des Shorts sur les réseaux Wi-Fi</string>
<string name="revanced_shorts_quality_default_mobile_title">Qualité par défaut des Shorts sur le réseau mobile</string>
<string name="revanced_remember_video_quality_mobile">mobile</string>
<string name="revanced_remember_video_quality_wifi">Wi-Fi</string>
<string name="revanced_remember_video_quality_toast">Nouvelle qualité %1$s par défaut : %2$s</string>
<string name="revanced_remember_video_quality_toast_shorts">Nouvelle qualité %1$s des Shorts par défaut : %2$s</string>
@@ -1527,6 +1533,7 @@ AVC a une résolution maximale de 1080p et ne prend pas en charge le codec audio
<string name="revanced_about_summary">À propos de ReVanced</string>
<string name="revanced_ads_screen_title">Annonces</string>
<string name="revanced_ads_screen_summary">Paramètres de blocage des annonces</string>
<string name="revanced_chat_screen_title">Chat</string>
<string name="revanced_chat_screen_summary">Paramètres du chat</string>
<string name="revanced_misc_screen_title">Divers</string>
<string name="revanced_misc_screen_summary">Paramètres divers</string>

View File

@@ -773,6 +773,9 @@ Chun roghchlár na rian fuaime a thaispeáint, athraigh 'Srutháin físeáin bhr
<string name="revanced_hide_shorts_upcoming_button_title">Folaigh cnaipe \'Le teacht\'</string>
<string name="revanced_hide_shorts_upcoming_button_summary_on">Tá cnaipe atá le teacht i bhfolach</string>
<string name="revanced_hide_shorts_upcoming_button_summary_off">Taispeántar an cnaipe atá le teacht</string>
<string name="revanced_hide_shorts_effect_button_title">Folaigh cnaipe éifeachta</string>
<string name="revanced_hide_shorts_effect_button_summary_on">Tá an cnaipe Éifeacht i bhfolach</string>
<string name="revanced_hide_shorts_effect_button_summary_off">Tá an cnaipe Éifeacht taispeánta</string>
<string name="revanced_hide_shorts_green_screen_button_title">Folaigh cnaipe \'Scáileán glas\'</string>
<string name="revanced_hide_shorts_green_screen_button_summary_on">Tá cnaipe an scáileáin glas i bhfolach</string>
<string name="revanced_hide_shorts_green_screen_button_summary_off">Taispeántar cnaipe an scáileáin glas</string>

View File

@@ -773,6 +773,9 @@ Az audiosáv menü megjelenítéséhez módosítsa a \"Videófolyamok hamisítá
<string name="revanced_hide_shorts_upcoming_button_title">Következő videó gomb elrejtése</string>
<string name="revanced_hide_shorts_upcoming_button_summary_on">A közelgő gomb el van rejtve</string>
<string name="revanced_hide_shorts_upcoming_button_summary_off">Megjelenik a Közelgő gomb</string>
<string name="revanced_hide_shorts_effect_button_title">Effekt gomb elrejtése</string>
<string name="revanced_hide_shorts_effect_button_summary_on">Az effekt gomb el van rejtve</string>
<string name="revanced_hide_shorts_effect_button_summary_off">Az effekt gomb látható</string>
<string name="revanced_hide_shorts_green_screen_button_title">Zöld háttér gomb elrejtése</string>
<string name="revanced_hide_shorts_green_screen_button_summary_on">A zöld képernyő gomb el van rejtve</string>
<string name="revanced_hide_shorts_green_screen_button_summary_off">A zöld képernyő gomb látható</string>

View File

@@ -773,6 +773,9 @@ MicroG-ի համար մարտկոցի օպտիմալացումը անջատել
<string name="revanced_hide_shorts_upcoming_button_title">Թաքցնել Առաջիկա կոճակը</string>
<string name="revanced_hide_shorts_upcoming_button_summary_on">\"Upcoming\" կոճակը թաքցված է</string>
<string name="revanced_hide_shorts_upcoming_button_summary_off">\"Upcoming\" կոճակը երևում է</string>
<string name="revanced_hide_shorts_effect_button_title">Թաքցնել Էֆեկտ կոճակը</string>
<string name="revanced_hide_shorts_effect_button_summary_on">Էֆեկտի կոճակը թաքնված է</string>
<string name="revanced_hide_shorts_effect_button_summary_off">Էֆեկտի կոճակը ցուցադրված է</string>
<string name="revanced_hide_shorts_green_screen_button_title">Թաքցնել Կանաչ էկրան կոճակը</string>
<string name="revanced_hide_shorts_green_screen_button_summary_on">\"Green Screen\" կոճակը թաքցված է</string>
<string name="revanced_hide_shorts_green_screen_button_summary_off">\"Green Screen\" կոճակը երևում է</string>

View File

@@ -535,8 +535,8 @@ Menyesuaikan volume dengan mengusap secara vertikal di sisi kanan layar"</string
<string name="revanced_hide_share_button_summary_off">Tombol bagikan ditampilkan</string>
<!-- 'Stop ads' should be translated with the same localized wording that YouTube displays. -->
<string name="revanced_hide_stop_ads_button_title">Sembunyikan Hentikan iklan</string>
<string name="revanced_hide_stop_ads_button_summary_on">Tombol berhenti iklan disembunyikan</string>
<string name="revanced_hide_stop_ads_button_summary_off">Tombol berhenti iklan ditampilkan</string>
<string name="revanced_hide_stop_ads_button_summary_on">Tombol hentikan iklan disembunyikan</string>
<string name="revanced_hide_stop_ads_button_summary_off">Tombol hentikan iklan ditampilkan</string>
<!-- 'Report' should be translated with the same localized wording that YouTube displays.
This button usually appears only on live streams. -->
<string name="revanced_hide_report_button_title">Sembunyikan Laporkan</string>
@@ -764,15 +764,18 @@ Untuk menampilkan menu trek Audio, ubah 'Spoof aliran video' ke iOS TV"</string>
<string name="revanced_hide_shorts_save_sound_button_title">Sembunyikan Simpan musik</string>
<string name="revanced_hide_shorts_save_sound_button_summary_on">Tombol simpan musik disembunyikan</string>
<string name="revanced_hide_shorts_save_sound_button_summary_off">Tombol simpan musik ditampilkan</string>
<string name="revanced_hide_shorts_use_sound_button_title">Sembunyikan tombol gunakan suara ini</string>
<string name="revanced_hide_shorts_use_sound_button_title">Sembunyikan tombol Gunakan suara ini</string>
<string name="revanced_hide_shorts_use_sound_button_summary_on">Tombol gunakan suara ini disembunyikan</string>
<string name="revanced_hide_shorts_use_sound_button_summary_off">Tombol gunakan suara ini ditampilkan</string>
<string name="revanced_hide_shorts_use_template_button_title">Sembunyikan tombol Gunakan template ini</string>
<string name="revanced_hide_shorts_use_template_button_summary_on">Tombol gunakan templat ini disembunyikan</string>
<string name="revanced_hide_shorts_use_template_button_summary_off">Tombol Gunakan template ini ditampilkan</string>
<string name="revanced_hide_shorts_use_template_button_summary_on">Tombol gunakan template ini disembunyikan</string>
<string name="revanced_hide_shorts_use_template_button_summary_off">Tombol gunakan template ini ditampilkan</string>
<string name="revanced_hide_shorts_upcoming_button_title">Sembunyikan tombol Mendatang</string>
<string name="revanced_hide_shorts_upcoming_button_summary_on">Tombol yang akan datang disembunyikan</string>
<string name="revanced_hide_shorts_upcoming_button_summary_off">Tombol yang akan datang ditampilkan</string>
<string name="revanced_hide_shorts_effect_button_title">Sembunyikan tombol Efek</string>
<string name="revanced_hide_shorts_effect_button_summary_on">Tombol Efek disembunyikan</string>
<string name="revanced_hide_shorts_effect_button_summary_off">Tombol Efek ditampilkan</string>
<string name="revanced_hide_shorts_green_screen_button_title">Sembunyikan tombol Layar Hijau</string>
<string name="revanced_hide_shorts_green_screen_button_summary_on">Tombol layar hijau disembunyikan</string>
<string name="revanced_hide_shorts_green_screen_button_summary_off">Tombol layar hijau ditampilkan</string>

View File

@@ -773,6 +773,9 @@ Per mostrare il menu della traccia audio, cambia \"Spoof video streams\" in iOS
<string name="revanced_hide_shorts_upcoming_button_title">Nascondi il pulsante Prossimamente</string>
<string name="revanced_hide_shorts_upcoming_button_summary_on">Il pulsante Imminente è nascosto</string>
<string name="revanced_hide_shorts_upcoming_button_summary_off">Il pulsante Imminente è visibile</string>
<string name="revanced_hide_shorts_effect_button_title">Nascondi pulsante effetto</string>
<string name="revanced_hide_shorts_effect_button_summary_on">Il pulsante Effetto è nascosto</string>
<string name="revanced_hide_shorts_effect_button_summary_off">Il pulsante Effetto è mostrato</string>
<string name="revanced_hide_shorts_green_screen_button_title">Nascondi il pulsante Green Screen</string>
<string name="revanced_hide_shorts_green_screen_button_summary_on">Il pulsante Schermo verde è nascosto</string>
<string name="revanced_hide_shorts_green_screen_button_summary_off">Il pulsante Schermo verde è visibile</string>

View File

@@ -773,6 +773,9 @@ Second \"item\" text"</string>
<string name="revanced_hide_shorts_upcoming_button_title">הסתר לחצן \'בקרוב\'</string>
<string name="revanced_hide_shorts_upcoming_button_summary_on">לחצן \'בקרוב\' מוסתר</string>
<string name="revanced_hide_shorts_upcoming_button_summary_off">לחצן \'בקרוב\' מוצג</string>
<string name="revanced_hide_shorts_effect_button_title">הסתר כפתור אפקט</string>
<string name="revanced_hide_shorts_effect_button_summary_on">כפתור האפקט מוסתר</string>
<string name="revanced_hide_shorts_effect_button_summary_off">כפתור האפקט מוצג</string>
<string name="revanced_hide_shorts_green_screen_button_title">הסתר לחצן מסך ירוק</string>
<string name="revanced_hide_shorts_green_screen_button_summary_on">לחצן מסך ירוק מוסתר</string>
<string name="revanced_hide_shorts_green_screen_button_summary_off">לחצן מסך ירוק מוצג</string>

View File

@@ -91,7 +91,7 @@ GmsCore の電池の最適化を無効にしても、バッテリーの使用に
<string name="revanced_settings_screen_01_ads_title">広告</string>
<string name="revanced_settings_screen_02_alt_thumbnails_title">代替サムネイル</string>
<string name="revanced_settings_screen_03_feed_title">フィード</string>
<string name="revanced_settings_screen_04_general_title"></string>
<string name="revanced_settings_screen_04_general_title"></string>
<string name="revanced_settings_screen_05_player_title">プレーヤー</string>
<string name="revanced_settings_screen_06_shorts_title">ショート</string>
<string name="revanced_settings_screen_07_seekbar_title">シークバー</string>
@@ -774,6 +774,9 @@ GmsCore の電池の最適化を無効にしても、バッテリーの使用に
<string name="revanced_hide_shorts_upcoming_button_title">今後の動画ボタンを非表示</string>
<string name="revanced_hide_shorts_upcoming_button_summary_on">今後の動画ボタンは表示されません</string>
<string name="revanced_hide_shorts_upcoming_button_summary_off">今後の動画ボタンは表示されます</string>
<string name="revanced_hide_shorts_effect_button_title">効果ボタンを非表示</string>
<string name="revanced_hide_shorts_effect_button_summary_on">効果ボタンは表示されません</string>
<string name="revanced_hide_shorts_effect_button_summary_off">効果ボタンは表示されます</string>
<string name="revanced_hide_shorts_green_screen_button_title">グリーンスクリーン ボタンを非表示</string>
<string name="revanced_hide_shorts_green_screen_button_summary_on">グリーンスクリーン ボタンは表示されません</string>
<string name="revanced_hide_shorts_green_screen_button_summary_off">グリーンスクリーン ボタンは表示されます</string>

View File

@@ -773,6 +773,9 @@ MicroG 앱 배터리 최적화를 비활성화(제한 없음)하더라도, 배
<string name="revanced_hide_shorts_upcoming_button_title">예정 버튼 숨기기</string>
<string name="revanced_hide_shorts_upcoming_button_summary_on">(Premiere 또는 실시간) 예정 버튼이 숨겨집니다</string>
<string name="revanced_hide_shorts_upcoming_button_summary_off">(Premiere 또는 실시간) 예정 버튼이 표시됩니다</string>
<string name="revanced_hide_shorts_effect_button_title">효과 버튼 숨기기</string>
<string name="revanced_hide_shorts_effect_button_summary_on">효과 버튼이 숨겨집니다</string>
<string name="revanced_hide_shorts_effect_button_summary_off">효과 버튼이 표시됩니다</string>
<string name="revanced_hide_shorts_green_screen_button_title">그린 스크린 버튼 숨기기</string>
<string name="revanced_hide_shorts_green_screen_button_summary_on">그린 스크린 버튼이 숨겨집니다</string>
<string name="revanced_hide_shorts_green_screen_button_summary_off">그린 스크린 버튼이 표시됩니다</string>

View File

@@ -771,6 +771,9 @@ Jei pakeitus šį nustatymą neįsigalioja, pabandykite perjungti į inkognito r
<string name="revanced_hide_shorts_upcoming_button_title">Paslėpti mygtuką „Ateina\"</string>
<string name="revanced_hide_shorts_upcoming_button_summary_on">\"Ateinančių\" mygtukas paslėptas</string>
<string name="revanced_hide_shorts_upcoming_button_summary_off">\"Ateinančių\" mygtukas rodomas</string>
<string name="revanced_hide_shorts_effect_button_title">Slėpti efekto mygtuką</string>
<string name="revanced_hide_shorts_effect_button_summary_on">Efekto mygtukas paslėptas</string>
<string name="revanced_hide_shorts_effect_button_summary_off">Efekto mygtukas rodomas</string>
<string name="revanced_hide_shorts_green_screen_button_title">Paslėpti mygtuką „Žalias ekranas\"</string>
<string name="revanced_hide_shorts_green_screen_button_summary_on">\"Žaliojo ekrano\" mygtukas paslėptas</string>
<string name="revanced_hide_shorts_green_screen_button_summary_off">\"Žaliojo ekrano\" mygtukas rodomas</string>

View File

@@ -773,6 +773,9 @@ Lai parādītu audio celiņu izvēlni, mainiet \"Video straumju viltošana\" uz
<string name="revanced_hide_shorts_upcoming_button_title">Paslēpt pogu \"Gaidāmie\"</string>
<string name="revanced_hide_shorts_upcoming_button_summary_on">Nākamās pogas ir paslēptas</string>
<string name="revanced_hide_shorts_upcoming_button_summary_off">Nākamās pogas ir redzamas</string>
<string name="revanced_hide_shorts_effect_button_title">Paslēpt efekta pogu</string>
<string name="revanced_hide_shorts_effect_button_summary_on">Efekta poga ir paslēpta</string>
<string name="revanced_hide_shorts_effect_button_summary_off">Efekta poga ir redzama</string>
<string name="revanced_hide_shorts_green_screen_button_title">Paslēpt pogu \"Zaļais ekrāns\"</string>
<string name="revanced_hide_shorts_green_screen_button_summary_on">Zaļā ekrāna poga ir paslēpta</string>
<string name="revanced_hide_shorts_green_screen_button_summary_off">Zaļā ekrāna poga ir redzama</string>

View File

@@ -773,6 +773,9 @@ Om het audiotrackmenu weer te geven, wijzigt u 'Videostreams vervalsen' in iOS T
<string name="revanced_hide_shorts_upcoming_button_title">Verberg de knop \"Aanstaande\"</string>
<string name="revanced_hide_shorts_upcoming_button_summary_on">De \"Volgende\" knop is verborgen</string>
<string name="revanced_hide_shorts_upcoming_button_summary_off">De \"Volgende\" knop is zichtbaar</string>
<string name="revanced_hide_shorts_effect_button_title">Verberg effectknop</string>
<string name="revanced_hide_shorts_effect_button_summary_on">Effectknop is verborgen</string>
<string name="revanced_hide_shorts_effect_button_summary_off">Effectknop is weergegeven</string>
<string name="revanced_hide_shorts_green_screen_button_title">Verberg de knop \"Groen scherm\"</string>
<string name="revanced_hide_shorts_green_screen_button_summary_on">De knop \"Groen scherm\" is verborgen</string>
<string name="revanced_hide_shorts_green_screen_button_summary_off">De knop \"Groen scherm\" is zichtbaar</string>

View File

@@ -769,6 +769,9 @@ Aby pokazać menu ścieżki audio, zmień opcję „Fałszuj strumienie wideo”
<string name="revanced_hide_shorts_upcoming_button_title">Ukryj przycisk \"Nadchodzące\"</string>
<string name="revanced_hide_shorts_upcoming_button_summary_on">Przycisk nadchodzących jest ukryty</string>
<string name="revanced_hide_shorts_upcoming_button_summary_off">Przycisk nadchodzących jest widoczny</string>
<string name="revanced_hide_shorts_effect_button_title">Ukryj przycisk efektu</string>
<string name="revanced_hide_shorts_effect_button_summary_on">Przycisk efektu jest ukryty</string>
<string name="revanced_hide_shorts_effect_button_summary_off">Przycisk efektu jest widoczny</string>
<string name="revanced_hide_shorts_green_screen_button_title">Ukryj przycisk \"Zielony ekran\"</string>
<string name="revanced_hide_shorts_green_screen_button_summary_on">Przycisk od greenscreena jest ukryty</string>
<string name="revanced_hide_shorts_green_screen_button_summary_off">Przycisk od greenscreena jest widoczny</string>

View File

@@ -773,6 +773,9 @@ Para exibir o menu da faixa de áudio, altere \"Spoof video streams\" para iOS T
<string name="revanced_hide_shorts_upcoming_button_title">Ocultar botão Próximos</string>
<string name="revanced_hide_shorts_upcoming_button_summary_on">O botão \"próximo\" está oculto</string>
<string name="revanced_hide_shorts_upcoming_button_summary_off">O botão \"próximo\" é mostrado</string>
<string name="revanced_hide_shorts_effect_button_title">Ocultar botão de Efeito</string>
<string name="revanced_hide_shorts_effect_button_summary_on">O botão de efeito está oculto</string>
<string name="revanced_hide_shorts_effect_button_summary_off">O botão de efeito é exibido</string>
<string name="revanced_hide_shorts_green_screen_button_title">Ocultar botão Tela verde</string>
<string name="revanced_hide_shorts_green_screen_button_summary_on">O botão de tela verde está oculto</string>
<string name="revanced_hide_shorts_green_screen_button_summary_off">O botão de tela verde será exibido</string>

View File

@@ -773,6 +773,9 @@ Para mostrar o menu da faixa de áudio, altere \"Spoof video streams\" para iOS
<string name="revanced_hide_shorts_upcoming_button_title">Ocultar botão de próximos</string>
<string name="revanced_hide_shorts_upcoming_button_summary_on">O botão \"vir\" está oculto</string>
<string name="revanced_hide_shorts_upcoming_button_summary_off">O próximo botão será exibido</string>
<string name="revanced_hide_shorts_effect_button_title">Ocultar botão de efeito</string>
<string name="revanced_hide_shorts_effect_button_summary_on">O botão Efeito está oculto</string>
<string name="revanced_hide_shorts_effect_button_summary_off">O botão Efeito está exibido</string>
<string name="revanced_hide_shorts_green_screen_button_title">Botão de ecrã verde está oculto</string>
<string name="revanced_hide_shorts_green_screen_button_summary_on">O botão de ecrã verde está oculto</string>
<string name="revanced_hide_shorts_green_screen_button_summary_off">Botão de ecrã verde é mostrado</string>

View File

@@ -773,6 +773,9 @@ Pentru a afișa meniul pentru pista audio, schimbați opțiunea „Falsifică fl
<string name="revanced_hide_shorts_upcoming_button_title">Ascunde butonul Următor</string>
<string name="revanced_hide_shorts_upcoming_button_summary_on">Butonul următor este ascuns</string>
<string name="revanced_hide_shorts_upcoming_button_summary_off">Următorul buton este afișat</string>
<string name="revanced_hide_shorts_effect_button_title">Ascunde butonul Efect</string>
<string name="revanced_hide_shorts_effect_button_summary_on">Butonul de efect este ascuns</string>
<string name="revanced_hide_shorts_effect_button_summary_off">Butonul de efect este afișat</string>
<string name="revanced_hide_shorts_green_screen_button_title">Ascunde butonul Ecran verde</string>
<string name="revanced_hide_shorts_green_screen_button_summary_on">Butonul de ecran verde este ascuns</string>
<string name="revanced_hide_shorts_green_screen_button_summary_off">Butonul de ecran verde este afișat</string>

View File

@@ -773,6 +773,9 @@ Second \"item\" text"</string>
<string name="revanced_hide_shorts_upcoming_button_title">Скрыть кнопку \"Предстоящие события\"</string>
<string name="revanced_hide_shorts_upcoming_button_summary_on">Кнопка \"Предстоящие события\" в Shorts скрыта</string>
<string name="revanced_hide_shorts_upcoming_button_summary_off">Кнопка \"Предстоящие события\" в Shorts показана</string>
<string name="revanced_hide_shorts_effect_button_title">Скрыть кнопку \"Эффект\"</string>
<string name="revanced_hide_shorts_effect_button_summary_on">Кнопка \"Эффект\" скрыта</string>
<string name="revanced_hide_shorts_effect_button_summary_off">Кнопка \"Эффект\" показана</string>
<string name="revanced_hide_shorts_green_screen_button_title">Скрыть кнопку \"Зеленый экран\"</string>
<string name="revanced_hide_shorts_green_screen_button_summary_on">Кнопка \"Зеленый экран\" в Shorts скрыта</string>
<string name="revanced_hide_shorts_green_screen_button_summary_off">Кнопка \"Зеленый экран\" в Shorts показана</string>

View File

@@ -766,6 +766,9 @@ Ak chcete zobraziť ponuku zvukovej stopy, zmeňte možnosť „Oklamať videost
<string name="revanced_hide_shorts_upcoming_button_title">Skryť tlačidlo Pripravované</string>
<string name="revanced_hide_shorts_upcoming_button_summary_on">Tlačidlo pre časté videá bude skryté</string>
<string name="revanced_hide_shorts_upcoming_button_summary_off">Tlačidlo pre časté videá bude zobrazené</string>
<string name="revanced_hide_shorts_effect_button_title">Skryť tlačidlo efektu</string>
<string name="revanced_hide_shorts_effect_button_summary_on">Tlačidlo efektu je skryté</string>
<string name="revanced_hide_shorts_effect_button_summary_off">Tlačidlo efektu je zobrazené</string>
<string name="revanced_hide_shorts_green_screen_button_title">Skryť tlačidlo Zelená obrazovka</string>
<string name="revanced_hide_shorts_green_screen_button_summary_on">Tlačidlo pre zelené pozadie bude skryté</string>
<string name="revanced_hide_shorts_green_screen_button_summary_off">Tlačidlo pre zelené pozadie bude zobrazené</string>

View File

@@ -773,6 +773,9 @@ Opomba: Omogočanje tega tudi prisilno skrije video oglase"</string>
<string name="revanced_hide_shorts_upcoming_button_title">Skrij gumb \'Prihajajoči\'</string>
<string name="revanced_hide_shorts_upcoming_button_summary_on">Gumb \"Prihajajoči\" je skrit</string>
<string name="revanced_hide_shorts_upcoming_button_summary_off">Gumb \"Prihajajoči\" je prikazan</string>
<string name="revanced_hide_shorts_effect_button_title">Skrij gumb učinka</string>
<string name="revanced_hide_shorts_effect_button_summary_on">Gumb za učinek je skrit</string>
<string name="revanced_hide_shorts_effect_button_summary_off">Gumb za učinek je prikazan</string>
<string name="revanced_hide_shorts_green_screen_button_title">Skrij gumb \'Zelena scena\'</string>
<string name="revanced_hide_shorts_green_screen_button_summary_on">Gumb za zeleno ozadje je skrit</string>
<string name="revanced_hide_shorts_green_screen_button_summary_off">Gumb za zeleno ozadje je prikazan</string>

View File

@@ -773,6 +773,9 @@ Për të shfaqur menunë e pistës audio, ndryshoni 'Falsifiko transmetimet vide
<string name="revanced_hide_shorts_upcoming_button_title">Fshih butonin Në vazhdim</string>
<string name="revanced_hide_shorts_upcoming_button_summary_on">Butoni \"Të ardhshme\" është i fshehur</string>
<string name="revanced_hide_shorts_upcoming_button_summary_off">Butoni \"Të ardhshme\" është i dukshëm</string>
<string name="revanced_hide_shorts_effect_button_title">Fshih butonin Efekt</string>
<string name="revanced_hide_shorts_effect_button_summary_on">Butoni i efektit është fshehur</string>
<string name="revanced_hide_shorts_effect_button_summary_off">Butoni i efektit është shfaqur</string>
<string name="revanced_hide_shorts_green_screen_button_title">Fsheh butonin Ekran jeshil</string>
<string name="revanced_hide_shorts_green_screen_button_summary_on">Butoni \"Ekrani i gjelbër\" është i fshehur</string>
<string name="revanced_hide_shorts_green_screen_button_summary_off">Butoni \"Ekrani i gjelbër\" është i dukshëm</string>

View File

@@ -534,9 +534,9 @@ Podesite jačinu zvuka prevlačenjem vertikalno na desnoj strani ekrana"</string
<string name="revanced_hide_share_button_summary_on">Dugme „Deli” je skriveno</string>
<string name="revanced_hide_share_button_summary_off">Dugme „Deli” je prikazano</string>
<!-- 'Stop ads' should be translated with the same localized wording that YouTube displays. -->
<string name="revanced_hide_stop_ads_button_title">Sakrij Zaustavi oglase</string>
<string name="revanced_hide_stop_ads_button_summary_on">Dugme za zaustavljanje oglasa je skriveno</string>
<string name="revanced_hide_stop_ads_button_summary_off">Dugme za zaustavljanje oglasa je prikazano</string>
<string name="revanced_hide_stop_ads_button_title">Sakrij dugme „Zaustavi oglase</string>
<string name="revanced_hide_stop_ads_button_summary_on">Dugme „Zaustavi oglase” je skriveno</string>
<string name="revanced_hide_stop_ads_button_summary_off">Dugme „Zaustavi oglase” je prikazano</string>
<!-- 'Report' should be translated with the same localized wording that YouTube displays.
This button usually appears only on live streams. -->
<string name="revanced_hide_report_button_title">Sakrij dugme „Prijavi”</string>
@@ -564,7 +564,7 @@ Podesite jačinu zvuka prevlačenjem vertikalno na desnoj strani ekrana"</string
<string name="revanced_hide_clip_button_summary_on">Dugme „Klip” je skriveno</string>
<string name="revanced_hide_clip_button_summary_off">Dugme „Klip” je prikazano</string>
<!-- 'Save' should be translated with the same localized wording that YouTube displays. -->
<string name="revanced_hide_save_button_title">Sakrij Sačuvaj</string>
<string name="revanced_hide_save_button_title">Sakrij dugme „Sačuvaj</string>
<string name="revanced_hide_save_button_summary_on">Dugme „Sačuvaj” je skriveno</string>
<string name="revanced_hide_save_button_summary_off">Dugme „Sačuvaj” je prikazano</string>
</patch>
@@ -773,6 +773,9 @@ Da biste prikazali meni „Audio snimak”, promenite opciju „Lažirani video
<string name="revanced_hide_shorts_upcoming_button_title">Sakrij dugme „Predstojeće”</string>
<string name="revanced_hide_shorts_upcoming_button_summary_on">Dugme „Predstojeće” je skriveno</string>
<string name="revanced_hide_shorts_upcoming_button_summary_off">Dugme „Predstojeće” je prikazano</string>
<string name="revanced_hide_shorts_effect_button_title">Sakrij dugme „Efekat”</string>
<string name="revanced_hide_shorts_effect_button_summary_on">Dugme „Efekat” je skriveno</string>
<string name="revanced_hide_shorts_effect_button_summary_off">Dugme „Efekat” je prikazano</string>
<string name="revanced_hide_shorts_green_screen_button_title">Sakrij dugme „Zeleni ekran”</string>
<string name="revanced_hide_shorts_green_screen_button_summary_on">Dugme „Zeleni ekran” je skriveno</string>
<string name="revanced_hide_shorts_green_screen_button_summary_off">Dugme „Zeleni ekran” je prikazano</string>

View File

@@ -534,9 +534,9 @@ Second \"item\" text"</string>
<string name="revanced_hide_share_button_summary_on">Дугме „Дели” је скривено</string>
<string name="revanced_hide_share_button_summary_off">Дугме „Дели” је приказано</string>
<!-- 'Stop ads' should be translated with the same localized wording that YouTube displays. -->
<string name="revanced_hide_stop_ads_button_title">Сакриј Заустави огласе</string>
<string name="revanced_hide_stop_ads_button_summary_on">Дугме „Заустави огласе је скривено</string>
<string name="revanced_hide_stop_ads_button_summary_off">Дугме „Заустави огласе је приказано</string>
<string name="revanced_hide_stop_ads_button_title">Сакриј дугме „Заустави огласе</string>
<string name="revanced_hide_stop_ads_button_summary_on">Дугме „Заустави огласе је скривено</string>
<string name="revanced_hide_stop_ads_button_summary_off">Дугме „Заустави огласе је приказано</string>
<!-- 'Report' should be translated with the same localized wording that YouTube displays.
This button usually appears only on live streams. -->
<string name="revanced_hide_report_button_title">Сакриј дугме „Пријави”</string>
@@ -564,9 +564,9 @@ Second \"item\" text"</string>
<string name="revanced_hide_clip_button_summary_on">Дугме „Клип” је скривено</string>
<string name="revanced_hide_clip_button_summary_off">Дугме „Клип” је приказано</string>
<!-- 'Save' should be translated with the same localized wording that YouTube displays. -->
<string name="revanced_hide_save_button_title">Сакриј Чувај</string>
<string name="revanced_hide_save_button_summary_on">Дугме „Чувај је скривено</string>
<string name="revanced_hide_save_button_summary_off">Дугме „Чувај је приказано</string>
<string name="revanced_hide_save_button_title">Сакриј дугме „Сачувај</string>
<string name="revanced_hide_save_button_summary_on">Дугме „Сачувај је скривено</string>
<string name="revanced_hide_save_button_summary_off">Дугме „Сачувај је приказано</string>
</patch>
<patch id="layout.buttons.navigation.navigationButtonsPatch">
<string name="revanced_navigation_buttons_screen_title">Дугмад навигације</string>
@@ -773,6 +773,9 @@ Second \"item\" text"</string>
<string name="revanced_hide_shorts_upcoming_button_title">Сакриј дугме „Предстојеће”</string>
<string name="revanced_hide_shorts_upcoming_button_summary_on">Дугме „Предстојеће” је скривено</string>
<string name="revanced_hide_shorts_upcoming_button_summary_off">Дугме „Предстојеће” је приказано</string>
<string name="revanced_hide_shorts_effect_button_title">Сакриј дугме „Ефекат”</string>
<string name="revanced_hide_shorts_effect_button_summary_on">Дугме „Ефекат” је скривено</string>
<string name="revanced_hide_shorts_effect_button_summary_off">Дугме „Ефекат” је приказано</string>
<string name="revanced_hide_shorts_green_screen_button_title">Сакриј дугме „Зелени екран”</string>
<string name="revanced_hide_shorts_green_screen_button_summary_on">Дугме „Зелени екран” је скривено</string>
<string name="revanced_hide_shorts_green_screen_button_summary_off">Дугме „Зелени екран” је приказано</string>

View File

@@ -773,6 +773,9 @@ För att visa ljudspårsmenyn, ändra \"Spoof video streams\" till iOS TV"</stri
<string name="revanced_hide_shorts_upcoming_button_title">Dölj knapp \'Kommande\'</string>
<string name="revanced_hide_shorts_upcoming_button_summary_on">Kommande knapp är dold</string>
<string name="revanced_hide_shorts_upcoming_button_summary_off">Kommande knapp visas</string>
<string name="revanced_hide_shorts_effect_button_title">Dölj effektknapp</string>
<string name="revanced_hide_shorts_effect_button_summary_on">Effektknappen är dold</string>
<string name="revanced_hide_shorts_effect_button_summary_off">Effektknappen visas</string>
<string name="revanced_hide_shorts_green_screen_button_title">Dölj grön skärm-knapp</string>
<string name="revanced_hide_shorts_green_screen_button_summary_on">Grön skärm-knappen är dold</string>
<string name="revanced_hide_shorts_green_screen_button_summary_off">Grön skärmknapp visas</string>

View File

@@ -771,6 +771,9 @@ Second \"item\" text"</string>
<string name="revanced_hide_shorts_upcoming_button_title">ซ่อนปุ่มที่กำลังจะมาถึง</string>
<string name="revanced_hide_shorts_upcoming_button_summary_on">ปุ่ม «Upcoming» ซ่อนอยู่</string>
<string name="revanced_hide_shorts_upcoming_button_summary_off">ปุ่ม «Upcoming» แสดงอยู่</string>
<string name="revanced_hide_shorts_effect_button_title">ซ่อนปุ่มเอฟเฟกต์</string>
<string name="revanced_hide_shorts_effect_button_summary_on">ปุ่มเอฟเฟกต์ถูกซ่อน</string>
<string name="revanced_hide_shorts_effect_button_summary_off">ปุ่มเอฟเฟกต์แสดงอยู่</string>
<string name="revanced_hide_shorts_green_screen_button_title">ซ่อนปุ่มกรีนสกรีน</string>
<string name="revanced_hide_shorts_green_screen_button_summary_on">ปุ่ม Green Screen ถูกซ่อน</string>
<string name="revanced_hide_shorts_green_screen_button_summary_off">ปุ่ม Green Screen แสดงขึ้น</string>

View File

@@ -773,6 +773,9 @@ Ses parçası menüsünü göstermek için \"Video akışlarını taklit et\" ay
<string name="revanced_hide_shorts_upcoming_button_title">Gelecek düğmesini gizle</string>
<string name="revanced_hide_shorts_upcoming_button_summary_on">Gelecek düğmesi gizli</string>
<string name="revanced_hide_shorts_upcoming_button_summary_off">Gelecek düğmesi görünür</string>
<string name="revanced_hide_shorts_effect_button_title">Efekt düğmesini gizle</string>
<string name="revanced_hide_shorts_effect_button_summary_on">Efekt düğmesi gizli</string>
<string name="revanced_hide_shorts_effect_button_summary_off">Efekt düğmesi gösteriliyor</string>
<string name="revanced_hide_shorts_green_screen_button_title">Yeşil ekran düğmesini gizle</string>
<string name="revanced_hide_shorts_green_screen_button_summary_on">Yeşil ekran düğmesi gizli</string>
<string name="revanced_hide_shorts_green_screen_button_summary_off">Yeşil ekran düğmesi görünür</string>

View File

@@ -774,6 +774,9 @@ Second \"item\" text"</string>
<string name="revanced_hide_shorts_upcoming_button_title">Приховати \"Запланована прем\'єра\"</string>
<string name="revanced_hide_shorts_upcoming_button_summary_on">Кнопки \"Запланована прем\'єра\" та \"Незабаром прямий ефір\" приховано</string>
<string name="revanced_hide_shorts_upcoming_button_summary_off">Кнопки \"Запланована прем\'єра\" та \"Незабаром прямий ефір\" показуються</string>
<string name="revanced_hide_shorts_effect_button_title">Приховати \"Ефект\"</string>
<string name="revanced_hide_shorts_effect_button_summary_on">Кнопку \"Ефект\" приховано</string>
<string name="revanced_hide_shorts_effect_button_summary_off">Кнопка \"Ефект\" показується</string>
<string name="revanced_hide_shorts_green_screen_button_title">Приховати \"Зелений екран\"</string>
<string name="revanced_hide_shorts_green_screen_button_summary_on">Кнопку \"Зелений екран\" приховано</string>
<string name="revanced_hide_shorts_green_screen_button_summary_off">Кнопка \"Зелений екран\" показується</string>

View File

@@ -143,7 +143,7 @@ Bạn sẽ không được thông báo về bất kỳ sự kiện bất ngờ n
<string name="revanced_debug_logs_clear_toast">Đã xóa nhật ký</string>
</patch>
<patch id="layout.hide.general.hideLayoutComponentsPatch">
<string name="revanced_hide_album_cards_title">Ẩn các thẻ album</string>
<string name="revanced_hide_album_cards_title">Ẩn thẻ album</string>
<string name="revanced_hide_album_cards_summary_on">Các thẻ album đã bị ẩn</string>
<string name="revanced_hide_album_cards_summary_off">Các thẻ album được hiển thị</string>
<string name="revanced_hide_crowdfunding_box_title">Ẩn hộp chiến dịch gây quỹ</string>
@@ -155,7 +155,7 @@ Bạn sẽ không được thông báo về bất kỳ sự kiện bất ngờ n
<string name="revanced_hide_channel_watermark_title">Ẩn hình mờ của kênh</string>
<string name="revanced_hide_channel_watermark_summary_on">Hình mờ đã bị ẩn</string>
<string name="revanced_hide_channel_watermark_summary_off">Hình mờ được hiển thị</string>
<string name="revanced_hide_horizontal_shelves_title">Ẩn các kệ ngang</string>
<string name="revanced_hide_horizontal_shelves_title">Ẩn kệ ngang</string>
<string name="revanced_hide_horizontal_shelves_summary_on">"Kệ đã bị ẩn, chẳng hạn như:
• Tin tức nóng
• Tiếp tục xem
@@ -237,10 +237,10 @@ Bạn sẽ không được thông báo về bất kỳ sự kiện bất ngờ n
<string name="revanced_hide_playables_title">Ẩn Chơi game trên Youtube</string>
<string name="revanced_hide_playables_summary_on">Chơi game đã bị ẩn</string>
<string name="revanced_hide_playables_summary_off">Chơi game được hiển thị</string>
<string name="revanced_hide_quick_actions_title">Ẩn các hành động nhanh khi ở toàn màn hình</string>
<string name="revanced_hide_quick_actions_summary_on">Hành động nhanh đã bị ẩn</string>
<string name="revanced_hide_quick_actions_summary_off">Hành động nhanh được hiển thị</string>
<string name="revanced_hide_related_videos_title">Ẩn các video liên quan trong hành động nhanh</string>
<string name="revanced_hide_quick_actions_title">Ẩn tác vụ nhanh trong chế độ toàn màn hình</string>
<string name="revanced_hide_quick_actions_summary_on">Các tác vụ nhanh đã bị ẩn</string>
<string name="revanced_hide_quick_actions_summary_off">Các tác vụ nhanh được hiển thị</string>
<string name="revanced_hide_related_videos_title">Ẩn các video liên quan trong tác vụ nhanh</string>
<string name="revanced_hide_related_videos_summary_on">Các video liên quan đã bị ẩn</string>
<string name="revanced_hide_related_videos_summary_off">Các video liên quan được hiển thị</string>
<string name="revanced_hide_image_shelf_title">Ẩn kệ hình ảnh từ kết quả tìm kiếm</string>
@@ -380,43 +380,43 @@ Hạn chế
<string name="revanced_hide_fullscreen_ads_title">Ẩn quảng cáo toàn màn hình</string>
<string name="revanced_hide_fullscreen_ads_summary_on">"Quảng cáo toàn màn hình đã bị ẩn
Tính năng này chỉ khả dụng cho các thiết bị cũ hơn"</string>
Tính năng này chỉ khả dụng trên các thiết bị cũ"</string>
<string name="revanced_hide_fullscreen_ads_summary_off">Quảng cáo toàn màn hình được hiển thị</string>
<string name="revanced_hide_paid_promotion_label_title">Ẩn nhãn quảng cáo được tài trợ</string>
<string name="revanced_hide_paid_promotion_label_summary_on">Nhãn quảng cáo được tài trợ đã bị ẩn</string>
<string name="revanced_hide_paid_promotion_label_summary_off">Nhãn quảng cáo được tài trợ được hiển thị</string>
<string name="revanced_hide_paid_promotion_label_title">Ẩn nhãn quảng cáo trả phí</string>
<string name="revanced_hide_paid_promotion_label_summary_on">Nhãn quảng cáo trả phí đã bị ẩn</string>
<string name="revanced_hide_paid_promotion_label_summary_off">Nhãn quảng cáo trả phí được hiển thị</string>
<string name="revanced_hide_self_sponsor_ads_title">Ẩn thẻ tự tài trợ</string>
<string name="revanced_hide_self_sponsor_ads_summary_on">Thẻ được tài trợ đã bị ẩn</string>
<string name="revanced_hide_self_sponsor_ads_summary_off">Thẻ được tài trợ được hiển thị</string>
<string name="revanced_hide_self_sponsor_ads_summary_on">Các thẻ tự tài trợ đã bị ẩn</string>
<string name="revanced_hide_self_sponsor_ads_summary_off">Các thẻ tự tài trợ được hiển thị</string>
<string name="revanced_hide_products_banner_title">Ẩn biểu ngữ \'Xem sản phẩm\'</string>
<string name="revanced_hide_products_banner_summary_on">Biểu ngữ đã bị ẩn</string>
<string name="revanced_hide_products_banner_summary_off">Biểu ngữ được hiển thị</string>
<string name="revanced_hide_end_screen_store_banner_title">Ẩn biểu ngữ cửa hàng ở cuối màn hình</string>
<string name="revanced_hide_end_screen_store_banner_summary_on">Biểu ngữ cửa hàng đã bị ẩn</string>
<string name="revanced_hide_end_screen_store_banner_summary_off">Biểu ngữ cửa hàng được hiển thị</string>
<string name="revanced_hide_player_store_shelf_title">Ẩn kệ cửa hàng</string>
<string name="revanced_hide_player_store_shelf_title">Ẩn kệ cửa hàng của trình phát</string>
<string name="revanced_hide_player_store_shelf_summary_on">Kệ cửa hàng đã bị ẩn</string>
<string name="revanced_hide_player_store_shelf_summary_off">Kệ cửa hàng được hiển thị</string>
<string name="revanced_hide_shopping_links_title">Ẩn các liên kết mua sắm</string>
<string name="revanced_hide_shopping_links_summary_on">Các liên kết mua sắm trong phần mô tả video đã bị ẩn</string>
<string name="revanced_hide_shopping_links_summary_off">Các liên kết mua sắm trong phần mô tả video được hiển thị</string>
<string name="revanced_hide_shopping_links_summary_on">Liên kết mua sắm trong mô tả video đã bị ẩn</string>
<string name="revanced_hide_shopping_links_summary_off">Liên kết mua sắm trong mô tả video được hiển thị</string>
<!-- 'Visit store' should be translated with the same localized wording that YouTube displays. -->
<string name="revanced_hide_visit_store_button_title">Ẩn nút \'Chuyển đến cửa hàng\'</string>
<string name="revanced_hide_visit_store_button_summary_on">Nút trên trang kênh đã bị ẩn</string>
<string name="revanced_hide_visit_store_button_summary_off">Nút trên trang kênh được hiển thị</string>
<string name="revanced_hide_web_search_results_title">Ẩn kết quả tìm kiếm t web</string>
<string name="revanced_hide_web_search_results_summary_on">Kết quả tìm kiếm t web đã bị ẩn</string>
<string name="revanced_hide_web_search_results_summary_off">Kết quả tìm kiếm t web được hiển thị</string>
<string name="revanced_hide_merchandise_banners_title">Ẩn biểu ngữ hàng hoá</string>
<string name="revanced_hide_merchandise_banners_summary_on">Biểu ngữ hàng hóa đã bị ẩn</string>
<string name="revanced_hide_merchandise_banners_summary_off">Biểu ngữ hàng hóa được hiển thị</string>
<string name="revanced_hide_web_search_results_title">Ẩn kết quả tìm kiếm trên web</string>
<string name="revanced_hide_web_search_results_summary_on">Kết quả tìm kiếm trên web đã bị ẩn</string>
<string name="revanced_hide_web_search_results_summary_off">Kết quả tìm kiếm trên web được hiển thị</string>
<string name="revanced_hide_merchandise_banners_title">Ẩn biểu ngữ sản phẩm</string>
<string name="revanced_hide_merchandise_banners_summary_on">Biểu ngữ sản phẩm đã bị ẩn</string>
<string name="revanced_hide_merchandise_banners_summary_off">Biểu ngữ sản phẩm được hiển thị</string>
<!-- Translations of this should not be longer than the original English text, otherwise the text can be clipped and not entirely shown. -->
<string name="revanced_hide_fullscreen_ads_feature_not_available_toast">Ẩn QC toàn màn hình chỉ hoạt động với các thiết bị cũ</string>
<string name="revanced_hide_fullscreen_ads_feature_not_available_toast">Ẩn QC toàn màn hình chỉ hoạt động trên thiết bị cũ</string>
</patch>
<patch id="ad.getpremium.hideGetPremiumPatch">
<string name="revanced_hide_get_premium_title">Ẩn quảng cáo YouTube Premium</string>
<string name="revanced_hide_get_premium_summary_on">Quảng cáo Youtube Premium bên dưới video đã bị ẩn</string>
<string name="revanced_hide_get_premium_summary_off">Quảng cáo Youtube Premium bên dưới video được hiển thị</string>
<string name="revanced_hide_get_premium_title">Ẩn quảng cáo khuyến mãi YouTube Premium</string>
<string name="revanced_hide_get_premium_summary_on">Quảng cáo Youtube Premium bên dưới trình phát video đã bị ẩn</string>
<string name="revanced_hide_get_premium_summary_off">Quảng cáo Youtube Premium bên dưới trình phát video được hiển thị</string>
</patch>
<patch id="ad.video.videoAdsPatch">
<string name="revanced_hide_video_ads_title">Ẩn quảng cáo trên video</string>
@@ -424,34 +424,34 @@ Tính năng này chỉ khả dụng cho các thiết bị cũ hơn"</string>
<string name="revanced_hide_video_ads_summary_off">Quảng cáo trên video được hiển thị</string>
</patch>
<patch id="interaction.copyvideourl.copyVideoUrlResourcePatch">
<string name="revanced_share_copy_url_success">Đã chép URL vào bảng nhớ tạm</string>
<string name="revanced_share_copy_url_timestamp_success">Đã chép URL với dấu thời gian</string>
<string name="revanced_share_copy_url_success">Đã sao chép URL vào bảng nhớ tạm</string>
<string name="revanced_share_copy_url_timestamp_success">Đã sao chép URL kèm dấu thời gian</string>
<string name="revanced_copy_video_url_title">Hiện nút sao chép URL video</string>
<string name="revanced_copy_video_url_summary_on">Nút được hiển thị. Chạm để sao chép URL video. Chạm và giữ để sao chép với dấu thời gian</string>
<string name="revanced_copy_video_url_summary_on">Nút được hiển thị. Nhấn để sao chép URL video. Nhấn và giữ để sao chép kèm dấu thời gian</string>
<string name="revanced_copy_video_url_summary_off">Nút không được hiển thị</string>
<string name="revanced_copy_video_url_timestamp_title">Hiện nút sao chép URL với dấu thời gian</string>
<string name="revanced_copy_video_url_timestamp_summary_on">Nút được hiển thị. Chạm để sao chép video URL với dấu thời gian. Chạm và giữ để sao chép video không kèm theo dấu thời gian</string>
<string name="revanced_copy_video_url_timestamp_title">Hiện nút sao chép URL kèm dấu thời gian</string>
<string name="revanced_copy_video_url_timestamp_summary_on">Nút được hiển thị. Nhấn để sao chép URL video kèm dấu thời gian. Nhấn giữ để sao chép không kèm dấu thời gian</string>
<string name="revanced_copy_video_url_timestamp_summary_off">Nút không được hiển thị</string>
</patch>
<patch id="interaction.dialog.removeViewerDiscretionDialogPatch">
<string name="revanced_remove_viewer_discretion_dialog_title">Loại bỏ hộp thoại cảnh báo trước khi xem</string>
<string name="revanced_remove_viewer_discretion_dialog_summary_on">Hộp thoại sẽ bị loại bỏ</string>
<string name="revanced_remove_viewer_discretion_dialog_summary_off">Hộp thoại sẽ được hiển thị</string>
<string name="revanced_remove_viewer_discretion_dialog_user_dialog_message">Điều này sẽ không qua mặt hạn chế độ tuổi. Nó chỉ tự động chấp nhận.</string>
<string name="revanced_remove_viewer_discretion_dialog_user_dialog_message">Tuỳ chọn này chỉ tự động chấp nhận mà không bỏ qua giới hạn độ tuổi.</string>
</patch>
<patch id="interaction.downloads.downloadsResourcePatch">
<string name="revanced_external_downloader_screen_title">Tải xuống bên ngoài</string>
<string name="revanced_external_downloader_screen_summary">Các thiết lập trình tải xuống bên ngoài</string>
<string name="revanced_external_downloader_screen_summary">Thiết lập để sử dụng trình tải xuống bên ngoài</string>
<string name="revanced_external_downloader_title">Hiện nút tải xuống bên ngoài</string>
<string name="revanced_external_downloader_summary_on">Nút tải xuống trong trình phát đã được hiển thị</string>
<string name="revanced_external_downloader_summary_off">Nút tải xuống trong trình phát không được hiển thị</string>
<!-- 'download action button' should be translated using the same wording as the translation of 'revanced_hide_download_button_title' -->
<string name="revanced_external_downloader_action_button_title">Thay thế nút hành động Tải xuống</string>
<string name="revanced_external_downloader_action_button_summary_on">Nút tải xuống mở trình tải xuống bên ngoài</string>
<string name="revanced_external_downloader_action_button_summary_off">Nút tải xuống mở trình tải xuống nội bộ ứng dụng</string>
<string name="revanced_external_downloader_action_button_title">Ghi đè nút Tải xuống</string>
<string name="revanced_external_downloader_action_button_summary_on">Nút tải xuống mở trình tải xuống bên ngoài của bạn</string>
<string name="revanced_external_downloader_action_button_summary_off">Nút tải xuống mở trình tải xuống trong ứng dụng gốc</string>
<string name="revanced_external_downloader_name_title">Tên gói trình tải xuống</string>
<string name="revanced_external_downloader_name_summary">Tên gói của ứng dụng tải xuống bên ngoài đã cài đặt của bạn, chẳng hạn như NewPipe hoặc Seal</string>
<string name="revanced_external_downloader_not_installed_warning">%s chưa được cài đặt. Hãy cài đặt .</string>
<string name="revanced_external_downloader_name_summary">Tên gói ứng dụng trình tải xuống bên ngoài đã cài đặt của bạn, chẳng hạn như NewPipe hoặc Seal</string>
<string name="revanced_external_downloader_not_installed_warning">%s chưa được cài đặt. Vui lòng cài đặt ứng dụng.</string>
</patch>
<patch id="interaction.seekbar.disablePreciseSeekingGesturePatch">
<string name="revanced_disable_precise_seeking_gesture_title">Tắt cử chỉ tua chính xác</string>
@@ -465,12 +465,12 @@ Tính năng này chỉ khả dụng cho các thiết bị cũ hơn"</string>
</patch>
<patch id="interaction.swipecontrols.swipeControlsResourcePatch">
<string name="revanced_swipe_brightness_title">Bật cử chỉ độ sáng</string>
<string name="revanced_swipe_brightness_summary_on">"Vuốt chỉnh độ sáng toàn màn hình đã bật
<string name="revanced_swipe_brightness_summary_on">"Vuốt độ sáng toàn màn hình đã được bật
Điều chỉnh độ sáng bằng cách vuốt dọc ở bên trái màn hình"</string>
<string name="revanced_swipe_brightness_summary_off">Vuốt độ sáng toàn màn hình đã tắt</string>
<string name="revanced_swipe_volume_title">Bật cử chỉ âm lượng</string>
<string name="revanced_swipe_volume_summary_on">"Vuốt âm lượng toàn màn hình đã bật
<string name="revanced_swipe_volume_summary_on">"Vuốt âm lượng toàn màn hình đã được bật
Điều chỉnh âm lượng bằng cách vuốt dọc ở bên phải màn hình"</string>
<string name="revanced_swipe_volume_summary_off">Vuốt âm lượng toàn màn hình đã tắt</string>
@@ -502,7 +502,7 @@ Tính năng này chỉ khả dụng cho các thiết bị cũ hơn"</string>
<string name="revanced_swipe_threshold_title">Độ rộng ngưỡng vuốt</string>
<string name="revanced_swipe_threshold_summary">Độ rộng của ngưỡng vuốt để thực hiện cử chỉ vuốt</string>
<string name="revanced_swipe_volume_sensitivity_title">Độ nhạy vuốt âm lượng</string>
<string name="revanced_swipe_volume_sensitivity_summary">Mức âm lượng thay đổi trên mỗi lần vuốt</string>
<string name="revanced_swipe_volume_sensitivity_summary">Mức âm lượng thay đổi mỗi lần vuốt</string>
<string name="revanced_swipe_overlay_style_title">Kiểu lớp phủ vuốt</string>
<string name="revanced_swipe_overlay_style_entry_1">Lớp phủ ngang</string>
<string name="revanced_swipe_overlay_style_entry_2">Lớp phủ ngang (tối giản - trên cùng)</string>
@@ -511,9 +511,9 @@ Tính năng này chỉ khả dụng cho các thiết bị cũ hơn"</string>
<string name="revanced_swipe_overlay_style_entry_5">Lớp phủ tròn (tối giản)</string>
<string name="revanced_swipe_overlay_style_entry_6">Lớp phủ dọc</string>
<string name="revanced_swipe_overlay_style_entry_7">Lớp phủ dọc (tối giản)</string>
<string name="revanced_swipe_change_video_title">Bật vuốt để thay đổi video</string>
<string name="revanced_swipe_change_video_summary_on">Vuốt trong chế độ toàn màn hình sẽ chuyển sang video tiếp theo/trước</string>
<string name="revanced_swipe_change_video_summary_off">Vuốt trong chế độ toàn màn hình sẽ không chuyển sang video tiếp theo/trước</string>
<string name="revanced_swipe_change_video_title">Bật vuốt để chuyển video</string>
<string name="revanced_swipe_change_video_summary_on">Vuốt trong chế độ toàn màn hình sẽ chuyển sang video tiếp theo/trước đó</string>
<string name="revanced_swipe_change_video_summary_off">Vuốt trong chế độ toàn màn hình sẽ không chuyển sang video tiếp theo/trước đó</string>
</patch>
<patch id="layout.autocaptions.autoCaptionsPatch">
<string name="revanced_disable_auto_captions_title">Tắt phụ đề tự động</string>
@@ -521,8 +521,8 @@ Tính năng này chỉ khả dụng cho các thiết bị cũ hơn"</string>
<string name="revanced_disable_auto_captions_summary_off">Phụ đề tự động đã bật</string>
</patch>
<patch id="layout.buttons.action.hideButtonsPatch">
<string name="revanced_hide_buttons_screen_title">Các nút hành động</string>
<string name="revanced_hide_buttons_screen_summary">Ẩn hoặc hin nút dưới video</string>
<string name="revanced_hide_buttons_screen_title">Nút tác vụ</string>
<string name="revanced_hide_buttons_screen_summary">Ẩn hoặc hin thị các nút bên dưới video</string>
<string name="revanced_disable_like_subscribe_glow_title">Tắt hiệu ứng phát sáng nút Thích và Đăng ký</string>
<string name="revanced_disable_like_subscribe_glow_summary_on">Nút Thích và Đăng ký sẽ không phát sáng khi được tương tác</string>
<string name="revanced_disable_like_subscribe_glow_summary_off">Nút Thích và Đăng ký sẽ phát sáng khi được tương tác</string>
@@ -585,21 +585,21 @@ Tính năng này chỉ khả dụng cho các thiết bị cũ hơn"</string>
<string name="revanced_hide_create_button_summary_off">Nút tạo được hiển thị</string>
<!-- 'Subscriptions' should be translated using the same localized wording YouTube displays the tab. -->
<string name="revanced_hide_subscriptions_button_title">Ẩn Kênh đăng ký</string>
<string name="revanced_hide_subscriptions_button_summary_on">Nút kênh đăng ký đã bị ẩn</string>
<string name="revanced_hide_subscriptions_button_summary_on">Nút kênh đăng ký đã bị ẩn</string>
<string name="revanced_hide_subscriptions_button_summary_off">Nút kênh đăng ký được hiển thị</string>
<string name="revanced_hide_notifications_button_title">Ẩn Thông báo</string>
<string name="revanced_hide_notifications_button_summary_on">Nút Thông báo đã bị ẩn</string>
<string name="revanced_hide_notifications_button_summary_off">Nút Thông báo được hiển thị</string>
<string name="revanced_hide_notifications_button_summary_on">Nút thông báo đã bị ẩn</string>
<string name="revanced_hide_notifications_button_summary_off">Nút thông báo được hiển thị</string>
<!-- 'Notifications' should be translated using the same localized wording YouTube displays the tab. -->
<string name="revanced_switch_create_with_notifications_button_title">Chuyển vị nút Tạo với nút Thông báo</string>
<string name="revanced_switch_create_with_notifications_button_summary_on">"Nút tạo được chuyển đổi với nút Thông báo
<string name="revanced_switch_create_with_notifications_button_summary_on">"Nút Tạo được đổi với nút Thông báo
Lưu ý: Bật tính năng này cũng sẽ tự động ẩn quảng cáo video"</string>
<string name="revanced_switch_create_with_notifications_button_summary_off">Nút Tạo không chuyển vị với nút Thông báo</string>
Lưu ý: Bật tính năng này cũng ẩn quảng cáo video"</string>
<string name="revanced_switch_create_with_notifications_button_summary_off">Nút Tạo không đổi với nút Thông báo</string>
<string name="revanced_switch_create_with_notifications_button_user_dialog_message">"Tắt cài đặt này cũng sẽ tắt chặn quảng cáo trên Shorts.
Nếu thay đổi cài đặt này không có hiệu lực, hãy thử chuyển sang chế độ Ẩn danh."</string>
<string name="revanced_hide_navigation_button_labels_title">Ẩn các nhãn nút điều hướng</string>
<string name="revanced_hide_navigation_button_labels_title">Ẩn nhãn nút điều hướng</string>
<string name="revanced_hide_navigation_button_labels_summary_on">Các nhãn đã bị ẩn</string>
<string name="revanced_hide_navigation_button_labels_summary_off">Các nhãn được hiển thị</string>
<string name="revanced_disable_translucent_status_bar_title">Vô hiệu hóa thanh trạng thái trong suốt</string>
@@ -626,21 +626,21 @@ Nếu thay đổi cài đặt này không có hiệu lực, hãy thử chuyển
<string name="revanced_hide_player_flyout_additional_settings_summary_off">Trình đơn cài đặt bổ sung được hiển thị</string>
<!-- 'Sleep timer' should be translated using the same localized wording YouTube displays for the menu item. -->
<string name="revanced_hide_player_flyout_sleep_timer_title">Ẩn Hẹn giờ ngủ</string>
<string name="revanced_hide_player_flyout_sleep_timer_summary_on">Trình đơn Hẹn giờ ngủ đã bị ẩn</string>
<string name="revanced_hide_player_flyout_sleep_timer_summary_off">Trình đơn Hẹn giờ ngủ được hiển thị</string>
<string name="revanced_hide_player_flyout_sleep_timer_summary_on">Trình đơn hẹn giờ ngủ đã bị ẩn</string>
<string name="revanced_hide_player_flyout_sleep_timer_summary_off">Trình đơn hẹn giờ ngủ được hiển thị</string>
<!-- 'Loop video' should be translated using the same localized wording YouTube displays for the menu item. -->
<string name="revanced_hide_player_flyout_loop_video_title">Ẩn lặp video</string>
<string name="revanced_hide_player_flyout_loop_video_summary_on">Trình đơn lặp video đã bị ẩn</string>
<string name="revanced_hide_player_flyout_loop_video_summary_off">Trình đơn lặp video được hiển thị</string>
<string name="revanced_hide_player_flyout_loop_video_title">Ẩn lặp lại video</string>
<string name="revanced_hide_player_flyout_loop_video_summary_on">Trình đơn lặp lại video đã bị ẩn</string>
<string name="revanced_hide_player_flyout_loop_video_summary_off">Trình đơn lặp lại video được hiển thị</string>
<!-- 'Ambient mode' should be translated using the same localized wording YouTube displays for the menu item. -->
<string name="revanced_hide_player_flyout_ambient_mode_title">Ẩn Chế độ môi trường</string>
<string name="revanced_hide_player_flyout_ambient_mode_summary_on">Trình đơn chế độ môi trường đã bị ẩn</string>
<string name="revanced_hide_player_flyout_ambient_mode_summary_off">Trình đơn chế độ môi trường được hiển thị</string>
<string name="revanced_hide_player_flyout_stable_volume_title">Ẩn Âm lượng ổn định</string>
<string name="revanced_hide_player_flyout_stable_volume_summary_off">Trình đơn Âm lượng ổn định được hiển thị</string>
<string name="revanced_hide_player_flyout_stable_volume_summary_on">Trình đơn Âm lượng ổn định đã bị ẩn</string>
<string name="revanced_hide_player_flyout_stable_volume_summary_off">Trình đơn âm lượng ổn định được hiển thị</string>
<string name="revanced_hide_player_flyout_stable_volume_summary_on">Trình đơn âm lượng ổn định đã bị ẩn</string>
<!-- 'Help & feedback' should be translated using the same localized wording YouTube displays for the menu item. -->
<string name="revanced_hide_player_flyout_help_title">Ẩn Trợ giúp &amp; Phản hồi</string>
<string name="revanced_hide_player_flyout_help_title">Ẩn Trợ giúp &amp; phản hồi</string>
<string name="revanced_hide_player_flyout_help_summary_on">Trình đơn trợ giúp &amp; phản hồi đã bị ẩn</string>
<string name="revanced_hide_player_flyout_help_summary_off">Trình đơn trợ giúp &amp; phản hồi được hiển thị</string>
<!-- 'Playback speed' should be translated using the same localized wording YouTube displays for the menu item. -->
@@ -665,9 +665,9 @@ Nếu thay đổi cài đặt này không có hiệu lực, hãy thử chuyển
Để hiển thị trình đơn Bản âm thanh, hãy thay đổi 'Giả mạo luồng phát video' thành iOS TV"</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">Ẩn Xem trong thực tế ảo</string>
<string name="revanced_hide_player_flyout_watch_in_vr_summary_on">Trình đơn xem trong thực tế ảo đã bị ẩn</string>
<string name="revanced_hide_player_flyout_watch_in_vr_summary_off">Trình đơn xem trong thực tế ảo được hiển thị</string>
<string name="revanced_hide_player_flyout_watch_in_vr_title">Ẩn Xem ở chế độ thực tế ảo</string>
<string name="revanced_hide_player_flyout_watch_in_vr_summary_on">Trình đơn xem ở chế độ thực tế ảo đã bị ẩn</string>
<string name="revanced_hide_player_flyout_watch_in_vr_summary_off">Trình đơn xem ở chế độ thực tế ảo được hiển thị</string>
<string name="revanced_hide_player_flyout_video_quality_footer_title">Ẩn chân trình đơn chất lượng video</string>
<string name="revanced_hide_player_flyout_video_quality_footer_summary_on">Chân trình đơn chất lượng video đã bị ẩn</string>
<string name="revanced_hide_player_flyout_video_quality_footer_summary_off">Chân trình đơn chất lượng video được hiển thị</string>
@@ -773,6 +773,9 @@ Nếu thay đổi cài đặt này không có hiệu lực, hãy thử chuyển
<string name="revanced_hide_shorts_upcoming_button_title">Ẩn nút Sắp diễn ra</string>
<string name="revanced_hide_shorts_upcoming_button_summary_on">Nút sắp diễn ra đã bị ẩn</string>
<string name="revanced_hide_shorts_upcoming_button_summary_off">Nút sắp diễn ra được hiển thị</string>
<string name="revanced_hide_shorts_effect_button_title">Ẩn nút Hiệu ứng</string>
<string name="revanced_hide_shorts_effect_button_summary_on">Nút hiệu ứng đã bị ẩn</string>
<string name="revanced_hide_shorts_effect_button_summary_off">Nút hiệu ứng được hiển thị</string>
<string name="revanced_hide_shorts_green_screen_button_title">Ẩn nút Phông xanh</string>
<string name="revanced_hide_shorts_green_screen_button_summary_on">Nút phông xanh đã bị ẩn</string>
<string name="revanced_hide_shorts_green_screen_button_summary_off">Nút phông xanh được hiển thị</string>
@@ -956,13 +959,13 @@ Tính năng này hoạt động tốt nhất với chất lượng video 720p tr
<string name="revanced_sb_enable_auto_hide_skip_segment_button">Tự động ẩn nút Bỏ qua</string>
<string name="revanced_sb_enable_auto_hide_skip_segment_button_sum_on">Nút bỏ qua ẩn sau vài giây</string>
<string name="revanced_sb_enable_auto_hide_skip_segment_button_sum_off">Nút bỏ qua được hiển thị cho toàn bộ phân đoạn</string>
<string name="revanced_sb_general_skiptoast">Hiện một thông báo nổi khi bỏ qua</string>
<string name="revanced_sb_general_skiptoast_sum_on">Hiển thị thông báo nổi mỗi khi tự động bỏ qua phân đoạn. Nhấn vào đây để xem ví dụ</string>
<string name="revanced_sb_general_skiptoast">Hiện thông báo nổi khi bỏ qua</string>
<string name="revanced_sb_general_skiptoast_sum_on">Thông báo nổi được hiển thị mỗi khi tự động bỏ qua một phân đoạn. Nhấn vào đây để xem ví dụ</string>
<string name="revanced_sb_general_skiptoast_sum_off">Thông báo nổi không được hiển thị. Nhấn vào đây để xem ví dụ</string>
<string name="revanced_sb_general_time_without">Hiện thời lượng video không có phân đoạn</string>
<string name="revanced_sb_general_time_without_sum_on">Thời lượng video trừ đi tất cả phân đoạn, được hiển thị trong dấu ngoặc đơn bên cạnh thời lượng đầy đủ của video</string>
<string name="revanced_sb_general_time_without_sum_off">Thời lượng đầy đủ của video được hiện</string>
<string name="revanced_sb_create_segment_category">Tạo các phân đoạn mới</string>
<string name="revanced_sb_general_time_without_sum_off">Thời lượng đầy đủ của video được hiển thị</string>
<string name="revanced_sb_create_segment_category">Tạo phân đoạn mới</string>
<string name="revanced_sb_enable_create_segment">Hiện nút Tạo phân đoạn mới</string>
<string name="revanced_sb_enable_create_segment_sum_on">Nút tạo phân đoạn mới đã được hiển thị</string>
<string name="revanced_sb_enable_create_segment_sum_off">Nút tạo phân đoạn mới không được hiển thị</string>
@@ -1006,23 +1009,23 @@ ID người dùng của bạn giống như mật khẩu và không bao giờ đ
<string name="revanced_sb_settings_revanced_export_user_id_warning_dismiss">Không hiện lại</string>
<string name="revanced_sb_diff_segments">Thay đổi hành vi phân đoạn</string>
<string name="revanced_sb_segments_sponsor">Nhà tài trợ</string>
<string name="revanced_sb_segments_sponsor_sum">Quảng cáo, giới thiệu được trả tiền và quảng cáo trực tiếp. Không phải tự quảng cáo hoặc lời giới thiệu miễn phí đến các chiến dịch/nhà sáng tạo/trang web/sản phẩm mà họ yêu thích</string>
<string name="revanced_sb_segments_selfpromo">Quảng cáo không được trả phí/Tự quảng cáo</string>
<string name="revanced_sb_segments_selfpromo_sum">Tương tự như Quảng cáo nhưng không có lợi nhuận hoặc tự quảng bá. Bao gồm các phần về hàng hóa, quyên góp hoặc thông tin về những người họ đã hợp tác</string>
<string name="revanced_sb_segments_interaction">Nhắc nh tương tác (Đăng ký)</string>
<string name="revanced_sb_segments_sponsor_sum">Quảng cáo trả phí, giới thiệu trả phí và quảng cáo trực tiếp. Không dùng cho mục đích tự quảng cáo hoặc quảng bá miễn phí cho các tổ chức/nhà sáng tạo/trang web/sản phẩm mà họ yêu thích</string>
<string name="revanced_sb_segments_selfpromo">Không được trả phí / Tự quảng cáo</string>
<string name="revanced_sb_segments_selfpromo_sum">Tương tự như \"Nhà tài trợ\" nhưng không có lợi nhuận hoặc tự quảng bá. Bao gồm các phần về sản phẩm, quyên góp hoặc thông tin về những người họ đã hợp tác cùng</string>
<string name="revanced_sb_segments_interaction">Lời nhắc tương tác (Đăng ký)</string>
<string name="revanced_sb_segments_interaction_sum">Một lời nhắc ngắn về thích, đăng ký hoặc theo dõi họ ở giữa nội dung. Nếu nó dài hoặc về một điều gì đó cụ thể, nó nên phân loại vào tự quảng cáo</string>
<string name="revanced_sb_segments_highlight">Nổi bật</string>
<string name="revanced_sb_segments_highlight_sum">Phần của video mà hầu hết mọi người đang tìm kiếm</string>
<string name="revanced_sb_segments_intro">Gián đoạn/Giới thiệu</string>
<string name="revanced_sb_segments_intro_sum">Một khoảng thời gian không có nội dung thực tế. Có thể là tạm dừng, khung hình tĩnh hoặc hoạt ảnh lặp lại. Không bao gồm các phần chuyển tiếp chứa thông tin</string>
<string name="revanced_sb_segments_highlight_sum">Phần video mà hầu hết mọi người đang tìm kiếm</string>
<string name="revanced_sb_segments_intro">Gián đoạn / Giới thiệu</string>
<string name="revanced_sb_segments_intro_sum">Một khoảng thời gian không có nội dung thực tế. Có thể là tạm dừng, khung hình tĩnh hoặc hoạt ảnh lặp lại. Không bao gồm các phần chuyển tiếp chứa thông tin</string>
<string name="revanced_sb_segments_outro">Màn hình kết thúc / Danh đề</string>
<string name="revanced_sb_segments_outro_sum">Danh đề hoặc màn hình kết thúc của Youtube xuất hiện. Không dành cho phần kết chứa thông tin</string>
<string name="revanced_sb_segments_preview">Đoạn xem trước/Tóm tắt/Gây chú ý</string>
<string name="revanced_sb_segments_preview_sum">Tập hợp các đoạn cắt thể hiện nhũng gì tiếp theo hoặc sẽ xảy ra trong video hoặc loạt video, nơi mà tất cả thông tin được lặp lại ở nơi khác</string>
<string name="revanced_sb_segments_filler">Phân đoạn lạc đề - nhảm nhí/Câu đùa hài hước</string>
<string name="revanced_sb_segments_filler_sum">Phân cảnh được thêm vào chỉ để dông dài hoặc hài hước mà không cần thiết để hiểu nội dung chính của video. Không bao gồm phân đoạn cung cấp bối cảnh hoặc chi tiết nền</string>
<string name="revanced_sb_segments_outro_sum">Danh đề hoặc khi màn hình kết thúc của YouTube xuất hiện. Không dành cho phần kết chứa thông tin</string>
<string name="revanced_sb_segments_preview">Xem trước / Tóm tắt / Gây chú ý</string>
<string name="revanced_sb_segments_preview_sum">Tuyển tập các đoạn cắt cho thấy nhng gì sắp diễn ra hoặc đã xảy ra trong video hoặc trong các video khác của một sê-ri, trong đó tất cả thông tin được lặp lại ở nơi khác</string>
<string name="revanced_sb_segments_filler">Nội dung thừa / Lạc đề / Câu đùa</string>
<string name="revanced_sb_segments_filler_sum">Các cảnh phụ chỉ được thêm vào để làm đầy thời lượng hoặc gây hài, không cần thiết để hiểu nội dung chính của video. Không bao gồm các phân đoạn cung cấp bối cảnh hoặc chi tiết nền</string>
<string name="revanced_sb_segments_nomusic">Âm nhạc: Phần không phải âm nhạc</string>
<string name="revanced_sb_segments_nomusic_sum">Chỉ dùng cho video về âm nhạc. Phần của video mà không có nhạc, tách biệt với danh mục khác</string>
<string name="revanced_sb_segments_nomusic_sum">Chỉ dành cho video âm nhạc. Các đoạn video âm nhạc không có nhạc, chưa được phân loại vào danh mục khác</string>
<string name="revanced_sb_skip_button_compact">Bỏ qua</string>
<string name="revanced_sb_skip_button_compact_highlight">Nổi bật</string>
<string name="revanced_sb_skip_button_sponsor">Bỏ qua nhà tài trợ</string>
@@ -1041,7 +1044,7 @@ ID người dùng của bạn giống như mật khẩu và không bao giờ đ
<string name="revanced_sb_skip_button_unsubmitted">Bỏ qua phân đoạn</string>
<string name="revanced_sb_skipped_sponsor">Đã bỏ qua nhà tài trợ</string>
<string name="revanced_sb_skipped_selfpromo">Đã bỏ qua tự quảng cáo</string>
<string name="revanced_sb_skipped_interaction">Đã bỏ qua nhắc nhở phiền phức</string>
<string name="revanced_sb_skipped_interaction">Đã bỏ qua lời nhắc phiền toái</string>
<string name="revanced_sb_skipped_highlight">Đã bỏ qua đến phần nổi bật</string>
<string name="revanced_sb_skipped_intro_beginning">Đã bỏ qua giới thiệu</string>
<string name="revanced_sb_skipped_intro_middle">Đã bỏ qua phần gián đoạn</string>
@@ -1050,7 +1053,7 @@ ID người dùng của bạn giống như mật khẩu và không bao giờ đ
<string name="revanced_sb_skipped_preview_beginning">Đã bỏ qua xem trước</string>
<string name="revanced_sb_skipped_preview_middle">Đã bỏ qua xem trước</string>
<string name="revanced_sb_skipped_preview_end">Đã bỏ qua tóm tắt</string>
<string name="revanced_sb_skipped_filler">Đã bỏ qua phân đoạn lạc đề - nhảm nhí</string>
<string name="revanced_sb_skipped_filler">Đã bỏ qua đoạn lạc đề</string>
<string name="revanced_sb_skipped_nomusic">Đã bỏ qua phần không phải âm nhạc</string>
<string name="revanced_sb_skipped_unsubmitted">Đã bỏ qua phân đoạn chưa gửi</string>
<string name="revanced_sb_skipped_multiple_segments">Đã bỏ qua nhiều phân đoạn</string>
@@ -1100,9 +1103,9 @@ ID người dùng của bạn giống như mật khẩu và không bao giờ đ
Bạn đã sẵn sàng gửi?"</string>
<string name="revanced_sb_new_segment_start_is_before_end">Thời gian bắt đầu phải trước thời gian kết thúc</string>
<string name="revanced_sb_new_segment_mark_locations_first">Đánh dấu hai vị trí đầu - cuối trên thanh thời gian trước</string>
<string name="revanced_sb_new_segment_mark_locations_first">Đánh dấu hai vị trí trên thanh thời gian trước</string>
<string name="revanced_sb_new_segment_preview_segment_first">Hãy xem trước phân đoạn để đảm bảo rằng nó bỏ qua suôn sẻ</string>
<string name="revanced_sb_new_segment_edit_by_hand_title">Chỉnh sửa thời gian của phân đoạn theo cách thủ công</string>
<string name="revanced_sb_new_segment_edit_by_hand_title">Chỉnh sửa thời gian của phân đoạn thủ công</string>
<string name="revanced_sb_new_segment_edit_by_hand_content">Bạn có muốn thay đổi thời gian bắt đầu hoặc kết thúc của phân đoạn không?</string>
<string name="revanced_sb_new_segment_edit_by_hand_parse_error">Thời gian đã đặt không hợp lệ</string>
<string name="revanced_sb_stats_title">Thống kê</string>
@@ -1227,12 +1230,12 @@ Hạn chế: Sử dụng nút quay lại trên thanh công cụ có thể không
<string name="revanced_miniplayer_rounded_corners_title">Bật góc bo tròn</string>
<string name="revanced_miniplayer_rounded_corners_summary_on">Góc được bo tròn</string>
<string name="revanced_miniplayer_rounded_corners_summary_off">Góc vuông</string>
<string name="revanced_miniplayer_double_tap_action_title">Bật nhấp đúp và chụm để thay đổi kích thước</string>
<string name="revanced_miniplayer_double_tap_action_summary_on">"Thao tác nhấn đúp và chụm để thay đổi kích thước đã được bật
<string name="revanced_miniplayer_double_tap_action_title">Bật chạm hai lần và chụm để thay đổi kích thước</string>
<string name="revanced_miniplayer_double_tap_action_summary_on">"Đã bật thao tác chạm hai lần và chụm để thay đổi kích thước
Nhấn đúp để tăng kích thước trình phát thu nhỏ
Nhấn đúp lại để khôi phục kích thước ban đầu"</string>
<string name="revanced_miniplayer_double_tap_action_summary_off">Chạm đôi và chụm để thay đổi kích thước đã tắt</string>
Chạm hai lần để tăng kích thước trình phát thu nhỏ
Chạm hai lần nữa để khôi phục kích thước ban đầu"</string>
<string name="revanced_miniplayer_double_tap_action_summary_off">Đã tắt thao tác chạm hai lần và chụm để thay đổi kích thước</string>
<string name="revanced_miniplayer_drag_and_drop_title">Bật kéo và thả</string>
<string name="revanced_miniplayer_drag_and_drop_summary_on">"Kéo và thả đã được bật
@@ -1243,7 +1246,7 @@ Trình phát thu nhỏ có thể được kéo đến bất kỳ góc nào của
Trình phát thu nhỏ có thể được kéo ra mép màn hình sang bên trái hoặc phải"</string>
<string name="revanced_miniplayer_horizontal_drag_summary_off">Cử chỉ kéo ngang đã tắt</string>
<string name="revanced_miniplayer_hide_overlay_buttons_title">Ẩn các nút lớp phủ</string>
<string name="revanced_miniplayer_hide_overlay_buttons_title">Ẩn nút lớp phủ</string>
<string name="revanced_miniplayer_hide_overlay_buttons_summary_on">Các nút lớp phủ đã bị ẩn</string>
<string name="revanced_miniplayer_hide_overlay_buttons_summary_off">Các nút lớp phủ được hiển thị</string>
<string name="revanced_miniplayer_hide_overlay_buttons_legacy_title">Ẩn các nút mở rộng và đóng</string>

View File

@@ -773,6 +773,9 @@ Second \"item\" text"</string>
<string name="revanced_hide_shorts_upcoming_button_title">隐藏「即将发布」按钮</string>
<string name="revanced_hide_shorts_upcoming_button_summary_on">即将上映按钮已隐藏</string>
<string name="revanced_hide_shorts_upcoming_button_summary_off">即将上映按钮已显示</string>
<string name="revanced_hide_shorts_effect_button_title">隐藏特效按钮</string>
<string name="revanced_hide_shorts_effect_button_summary_on">特效按钮已隐藏</string>
<string name="revanced_hide_shorts_effect_button_summary_off">特效按钮已显示</string>
<string name="revanced_hide_shorts_green_screen_button_title">隐藏「绿幕」按钮</string>
<string name="revanced_hide_shorts_green_screen_button_summary_on">绿屏按钮已隐藏</string>
<string name="revanced_hide_shorts_green_screen_button_summary_off">绿屏按钮已显示</string>

View File

@@ -19,26 +19,26 @@ Second \"item\" text"</string>
-->
<resources>
<app id="shared">
<patch id="misc.checks.checkEnvironmentPatch">
</patch>
<patch id="misc.settings.settingsResourcePatch">
<!-- <app id="shared"> -->
<!-- <patch id="misc.checks.checkEnvironmentPatch"> -->
<!-- </patch> -->
<!-- <patch id="misc.settings.settingsResourcePatch"> -->
<!-- Settings about dialog. -->
<!-- NOTE: the about strings above are duplicated in the TikTok about screen code,
and changes made here must also be made there. -->
</patch>
<patch id="misc.gms.gmsCoreSupportResourcePatch">
<!-- </patch> -->
<!-- <patch id="misc.gms.gmsCoreSupportResourcePatch"> -->
<!-- Translations of this should not be longer than the original English text, otherwise the text can be clipped and not entirely shown. -->
</patch>
</app>
<app id="youtube">
<patch id="misc.settings.settingsPatch">
</patch>
<patch id="misc.backgroundplayback.backgroundPlaybackPatch">
</patch>
<patch id="misc.debugging.enableDebuggingPatch">
</patch>
<patch id="layout.hide.general.hideLayoutComponentsPatch">
<!-- </patch> -->
<!-- </app> -->
<!-- <app id="youtube"> -->
<!-- <patch id="misc.settings.settingsPatch"> -->
<!-- </patch> -->
<!-- <patch id="misc.backgroundplayback.backgroundPlaybackPatch"> -->
<!-- </patch> -->
<!-- <patch id="misc.debugging.enableDebuggingPatch"> -->
<!-- </patch> -->
<!-- <patch id="layout.hide.general.hideLayoutComponentsPatch"> -->
<!-- 'Join' should be translated using the same localized wording YouTube displays.
This appears in the video player for certain videos. -->
<!-- 'For you' should be translated using the same localized wording YouTube displays. -->
@@ -53,31 +53,31 @@ Second \"item\" text"</string>
This is because keywords can be in any language, and showing an example in the localized script helps convey this. -->
<!-- Translations _must_ use a localized example. For languages that do not use spaces between words (Chinese, Japanese, etc.) the English AI example should be used since no localized examples exist. Or if using machine translations, or if nobody wants to think of a localized example, then the English 'ai' example should be left as-is. -->
<!-- Translations of this should not be longer than the original English text, otherwise the text can be clipped and not entirely shown. -->
</patch>
<patch id="ad.general.hideAdsResourcePatch">
<!-- </patch> -->
<!-- <patch id="ad.general.hideAdsResourcePatch"> -->
<!-- 'Visit store' should be translated with the same localized wording that YouTube displays. -->
<!-- Translations of this should not be longer than the original English text, otherwise the text can be clipped and not entirely shown. -->
</patch>
<patch id="ad.getpremium.hideGetPremiumPatch">
</patch>
<patch id="ad.video.videoAdsPatch">
</patch>
<patch id="interaction.copyvideourl.copyVideoUrlResourcePatch">
</patch>
<patch id="interaction.dialog.removeViewerDiscretionDialogPatch">
</patch>
<patch id="interaction.downloads.downloadsResourcePatch">
<!-- </patch> -->
<!-- <patch id="ad.getpremium.hideGetPremiumPatch"> -->
<!-- </patch> -->
<!-- <patch id="ad.video.videoAdsPatch"> -->
<!-- </patch> -->
<!-- <patch id="interaction.copyvideourl.copyVideoUrlResourcePatch"> -->
<!-- </patch> -->
<!-- <patch id="interaction.dialog.removeViewerDiscretionDialogPatch"> -->
<!-- </patch> -->
<!-- <patch id="interaction.downloads.downloadsResourcePatch"> -->
<!-- 'download action button' should be translated using the same wording as the translation of 'revanced_hide_download_button_title' -->
</patch>
<patch id="interaction.seekbar.disablePreciseSeekingGesturePatch">
</patch>
<patch id="interaction.seekbar.enableSeekbarTappingPatch">
</patch>
<patch id="interaction.swipecontrols.swipeControlsResourcePatch">
</patch>
<patch id="layout.autocaptions.autoCaptionsPatch">
</patch>
<patch id="layout.buttons.action.hideButtonsPatch">
<!-- </patch> -->
<!-- <patch id="interaction.seekbar.disablePreciseSeekingGesturePatch"> -->
<!-- </patch> -->
<!-- <patch id="interaction.seekbar.enableSeekbarTappingPatch"> -->
<!-- </patch> -->
<!-- <patch id="interaction.swipecontrols.swipeControlsResourcePatch"> -->
<!-- </patch> -->
<!-- <patch id="layout.autocaptions.autoCaptionsPatch"> -->
<!-- </patch> -->
<!-- <patch id="layout.buttons.action.hideButtonsPatch"> -->
<!-- 'Share' should be translated with the same localized wording that YouTube displays. -->
<!-- 'Stop ads' should be translated with the same localized wording that YouTube displays. -->
<!-- 'Report' should be translated with the same localized wording that YouTube displays.
@@ -89,15 +89,15 @@ Second \"item\" text"</string>
Button only shows if the user ip is from specific region such as the USA or EU. -->
<!-- 'Clip' should be translated with the same localized wording that YouTube displays. -->
<!-- 'Save' should be translated with the same localized wording that YouTube displays. -->
</patch>
<patch id="layout.buttons.navigation.navigationButtonsPatch">
<!-- </patch> -->
<!-- <patch id="layout.buttons.navigation.navigationButtonsPatch"> -->
<!-- 'Home' should be translated using the same localized wording YouTube displays for the tab. -->
<!-- 'Shorts' should be translated using the same localized wording YouTube displays the tab. -->
<!-- The Create button has no display name. Translate normally. -->
<!-- 'Subscriptions' should be translated using the same localized wording YouTube displays the tab. -->
<!-- 'Notifications' should be translated using the same localized wording YouTube displays the tab. -->
</patch>
<patch id="layout.hide.player.flyoutmenupanel.hidePlayerFlyoutMenuPatch">
<!-- </patch> -->
<!-- <patch id="layout.hide.player.flyoutmenupanel.hidePlayerFlyoutMenuPatch"> -->
<!-- 'Captions' should be translated using the same localized wording YouTube displays for the menu item. -->
<!-- 'Additional settings' should be translated using the same localized wording YouTube displays for the menu item. -->
<!-- 'Sleep timer' should be translated using the same localized wording YouTube displays for the menu item. -->
@@ -111,141 +111,141 @@ Second \"item\" text"</string>
<!-- 'Audio track' should be translated using the same localized wording YouTube displays for the menu item. -->
<!-- 'Spoof video streams' should be the same translation used for revanced_spoof_video_streams_screen_title -->
<!-- 'Watch in VR' should be translated using the same localized wording YouTube displays for the menu item. -->
</patch>
<patch id="layout.buttons.overlay.hidePlayerOverlayButtonsPatch">
<!-- </patch> -->
<!-- <patch id="layout.buttons.overlay.hidePlayerOverlayButtonsPatch"> -->
<!-- This button does not display any text, but 'captions' should be translated using the same wording used as the translation of 'revanced_hide_player_flyout_captions_title' -->
</patch>
<patch id="layout.hide.endscreencards.hideEndscreenCardsResourcePatch">
</patch>
<patch id="layout.hide.fullscreenambientmode.disableFullscreenAmbientModePatch">
</patch>
<patch id="layout.hide.infocards.hideInfocardsResourcePatch">
</patch>
<patch id="layout.hide.rollingnumber.disableRollingNumberAnimationPatch">
</patch>
<patch id="layout.hide.seekbar.hideSeekbarPatch">
</patch>
<patch id="layout.hide.shorts.hideShortsComponentsResourcePatch">
<!-- </patch> -->
<!-- <patch id="layout.hide.endscreencards.hideEndscreenCardsResourcePatch"> -->
<!-- </patch> -->
<!-- <patch id="layout.hide.fullscreenambientmode.disableFullscreenAmbientModePatch"> -->
<!-- </patch> -->
<!-- <patch id="layout.hide.infocards.hideInfocardsResourcePatch"> -->
<!-- </patch> -->
<!-- <patch id="layout.hide.rollingnumber.disableRollingNumberAnimationPatch"> -->
<!-- </patch> -->
<!-- <patch id="layout.hide.seekbar.hideSeekbarPatch"> -->
<!-- </patch> -->
<!-- <patch id="layout.hide.shorts.hideShortsComponentsResourcePatch"> -->
<!-- 'home' should be translated using the same localized wording YouTube displays for the home tab. -->
<!-- 'subscription' should be translated using the same localized wording YouTube displays for the subscription tab. -->
<!-- 'join' should be translated using the same localized wording YouTube displays for the button. -->
<!-- 'subscribe' should be translated using the same localized wording YouTube displays for the button. -->
<!-- 'remix' should be translated using the same localized wording YouTube displays for the button. -->
<!-- 'share' should be translated using the same localized wording YouTube displays for the button. -->
</patch>
<patch id="layout.hide.endscreensuggestion.hideEndScreenSuggestedVideoPatch">
</patch>
<patch id="layout.hide.relatedvideooverlay.hideRelatedVideoOverlayPatch">
</patch>
<patch id="layout.hide.time.hideTimestampPatch">
</patch>
<patch id="layout.panels.popup.playerPopupPanelsPatch">
</patch>
<patch id="layout.player.fullscreen.exitFullscreenPatch">
</patch>
<patch id="layout.player.fullscreen.openVideosFullscreen">
</patch>
<patch id="layout.player.overlay.customPlayerOverlayOpacityResourcePatch">
</patch>
<patch id="layout.returnyoutubedislike.returnYouTubeDislikePatch">
<!-- </patch> -->
<!-- <patch id="layout.hide.endscreensuggestion.hideEndScreenSuggestedVideoPatch"> -->
<!-- </patch> -->
<!-- <patch id="layout.hide.relatedvideooverlay.hideRelatedVideoOverlayPatch"> -->
<!-- </patch> -->
<!-- <patch id="layout.hide.time.hideTimestampPatch"> -->
<!-- </patch> -->
<!-- <patch id="layout.panels.popup.playerPopupPanelsPatch"> -->
<!-- </patch> -->
<!-- <patch id="layout.player.fullscreen.exitFullscreenPatch"> -->
<!-- </patch> -->
<!-- <patch id="layout.player.fullscreen.openVideosFullscreen"> -->
<!-- </patch> -->
<!-- <patch id="layout.player.overlay.customPlayerOverlayOpacityResourcePatch"> -->
<!-- </patch> -->
<!-- <patch id="layout.returnyoutubedislike.returnYouTubeDislikePatch"> -->
<!-- Toast shown if network connection times out. Translations of this should not be longer than the original English or the text can be clipped and not entirely shown. -->
<!-- Toast shown if the user enables RYD while a video is opened, and then tries to vote for the video. -->
<!-- Video likes have been set to hidden by the video uploader. -->
<!-- Translations should use language similar to 'revanced_sb_enable_compact_skip_button' -->
<!-- Statistic strings are shown in the settings only when ReVanced debug mode is enabled. Typical users will never see these. -->
</patch>
<patch id="layout.searchbar.wideSearchbarPatch">
</patch>
<patch id="layout.seekbar.seekbarThumbnailsPatch">
</patch>
<patch id="layout.sponsorblock.sponsorBlockResourcePatch">
<!-- </patch> -->
<!-- <patch id="layout.searchbar.wideSearchbarPatch"> -->
<!-- </patch> -->
<!-- <patch id="layout.seekbar.seekbarThumbnailsPatch"> -->
<!-- </patch> -->
<!-- <patch id="layout.sponsorblock.sponsorBlockResourcePatch"> -->
<!-- Translations should use language similar to 'revanced_ryd_compact_layout_title' -->
<!-- Toast shown if network connection times out. Translations of this should not be longer than the original English or the text can be clipped and not entirely shown. -->
<!-- A segment start and end time, such as "02:10 to 03:40" -->
<!-- Shown in the settings preferences, and translations can be any text length. -->
</patch>
<patch id="layout.formfactor.changeFormFactorPatch">
</patch>
<patch id="layout.spoofappversion.spoofAppVersionPatch">
<!-- </patch> -->
<!-- <patch id="layout.formfactor.changeFormFactorPatch"> -->
<!-- </patch> -->
<!-- <patch id="layout.spoofappversion.spoofAppVersionPatch"> -->
<!-- It is ideal, but not required, if the text here appears is alphabetically after the text used for 'revanced_spoof_app_version_title'.
This is because the 'General layout' menu uses alphabetic sorting, and it functionally works better if the spoof target selector appears below the 'Spoof app version' UI switch -->
</patch>
<patch id="layout.startpage.changeStartPagePatch">
</patch>
<patch id="layout.startupshortsreset.disableResumingShortsOnStartupPatch">
</patch>
<patch id="layout.shortsplayer.shortsPlayerTypePatch">
</patch>
<patch id="layout.shortsautoplay.shortsAutoplayPatch">
</patch>
<patch id="layout.miniplayer.miniplayerPatch">
</patch>
<patch id="layout.theme.themePatch">
</patch>
<patch id="layout.thumbnails.bypassImageRegionRestrictionsPatch">
</patch>
<patch id="layout.thumbnails.alternativeThumbnailsPatch">
<!-- </patch> -->
<!-- <patch id="layout.startpage.changeStartPagePatch"> -->
<!-- </patch> -->
<!-- <patch id="layout.startupshortsreset.disableResumingShortsOnStartupPatch"> -->
<!-- </patch> -->
<!-- <patch id="layout.shortsplayer.shortsPlayerTypePatch"> -->
<!-- </patch> -->
<!-- <patch id="layout.shortsautoplay.shortsAutoplayPatch"> -->
<!-- </patch> -->
<!-- <patch id="layout.miniplayer.miniplayerPatch"> -->
<!-- </patch> -->
<!-- <patch id="layout.theme.themePatch"> -->
<!-- </patch> -->
<!-- <patch id="layout.thumbnails.bypassImageRegionRestrictionsPatch"> -->
<!-- </patch> -->
<!-- <patch id="layout.thumbnails.alternativeThumbnailsPatch"> -->
<!-- 'Home' should be translated using the same localized wording YouTube displays for the home tab. -->
<!-- 'Subscription' should be translated using the same localized wording YouTube displays for the subscription tab. -->
<!-- 'You' should be translated using the same localized wording YouTube displays for the You (library) tab. -->
<!-- Translations of this should not be longer than the original English text, otherwise the text can be clipped and not entirely shown. -->
</patch>
<patch id="misc.announcements.announcementsPatch">
</patch>
<patch id="misc.dns.checkWatchHistoryDomainNameResolutionPatch">
</patch>
<patch id="misc.autorepeat.autoRepeatPatch">
</patch>
<patch id="misc.dimensions.spoof.spoofDeviceDimensionsPatch">
</patch>
<patch id="misc.gms.gmsCoreSupportResourcePatch">
</patch>
<patch id="misc.hapticfeedback.disableHapticFeedbackPatch">
</patch>
<patch id="misc.gms.accountCredentialsInvalidTextPatch">
</patch>
<patch id="misc.links.bypassURLRedirectsPatch">
</patch>
<patch id="misc.links.openLinksExternallyPatch">
</patch>
<patch id="misc.privacy.removeTrackingQueryParameterPatch">
</patch>
<patch id="video.audio.forceOriginalAudioPatch">
<!-- </patch> -->
<!-- <patch id="misc.announcements.announcementsPatch"> -->
<!-- </patch> -->
<!-- <patch id="misc.dns.checkWatchHistoryDomainNameResolutionPatch"> -->
<!-- </patch> -->
<!-- <patch id="misc.autorepeat.autoRepeatPatch"> -->
<!-- </patch> -->
<!-- <patch id="misc.dimensions.spoof.spoofDeviceDimensionsPatch"> -->
<!-- </patch> -->
<!-- <patch id="misc.gms.gmsCoreSupportResourcePatch"> -->
<!-- </patch> -->
<!-- <patch id="misc.hapticfeedback.disableHapticFeedbackPatch"> -->
<!-- </patch> -->
<!-- <patch id="misc.gms.accountCredentialsInvalidTextPatch"> -->
<!-- </patch> -->
<!-- <patch id="misc.links.bypassURLRedirectsPatch"> -->
<!-- </patch> -->
<!-- <patch id="misc.links.openLinksExternallyPatch"> -->
<!-- </patch> -->
<!-- <patch id="misc.privacy.removeTrackingQueryParameterPatch"> -->
<!-- </patch> -->
<!-- <patch id="video.audio.forceOriginalAudioPatch"> -->
<!-- 'Spoof video streams' should be the same translation used for revanced_spoof_video_streams_screen_title -->
</patch>
<patch id="video.quality.rememberVideoQualityPatch">
<!-- </patch> -->
<!-- <patch id="video.quality.rememberVideoQualityPatch"> -->
<!-- Translations should use the same text as revanced_custom_playback_speeds_auto -->
</patch>
<patch id="video.speed.button.playbackSpeedButtonPatch">
</patch>
<patch id="video.speed.custom.customPlaybackSpeedPatch">
</patch>
<patch id="video.speed.remember.rememberPlaybackSpeedPatch">
</patch>
<patch id="video.hdr.disableHdrPatch">
</patch>
<patch id="video.quality.advancedVideoQualityMenuPatch">
</patch>
<patch id="interaction.seekbar.enableSlideToSeekPatch">
</patch>
<patch id="misc.fix.playback.spoofVideoStreamsPatch">
</patch>
</app>
<app id="twitch">
<patch id="ad.audio.audioAdsPatch">
</patch>
<patch id="ad.embedded.embeddedAdsPatch">
</patch>
<patch id="ad.video.videoAdsPatch">
</patch>
<patch id="chat.antidelete.showDeletedMessagesPatch">
</patch>
<patch id="chat.autoclaim.autoClaimChannelPointsPatch">
</patch>
<patch id="debug.debugModePatch">
<!-- </patch> -->
<!-- <patch id="video.speed.button.playbackSpeedButtonPatch"> -->
<!-- </patch> -->
<!-- <patch id="video.speed.custom.customPlaybackSpeedPatch"> -->
<!-- </patch> -->
<!-- <patch id="video.speed.remember.rememberPlaybackSpeedPatch"> -->
<!-- </patch> -->
<!-- <patch id="video.hdr.disableHdrPatch"> -->
<!-- </patch> -->
<!-- <patch id="video.quality.advancedVideoQualityMenuPatch"> -->
<!-- </patch> -->
<!-- <patch id="interaction.seekbar.enableSlideToSeekPatch"> -->
<!-- </patch> -->
<!-- <patch id="misc.fix.playback.spoofVideoStreamsPatch"> -->
<!-- </patch> -->
<!-- </app> -->
<!-- <app id="twitch"> -->
<!-- <patch id="ad.audio.audioAdsPatch"> -->
<!-- </patch> -->
<!-- <patch id="ad.embedded.embeddedAdsPatch"> -->
<!-- </patch> -->
<!-- <patch id="ad.video.videoAdsPatch"> -->
<!-- </patch> -->
<!-- <patch id="chat.antidelete.showDeletedMessagesPatch"> -->
<!-- </patch> -->
<!-- <patch id="chat.autoclaim.autoClaimChannelPointsPatch"> -->
<!-- </patch> -->
<!-- <patch id="debug.debugModePatch"> -->
<!-- Twitch specific internal debug mode, and not the same as 'revanced_debug_title' -->
</patch>
<patch id="misc.settings.settingsPatch">
</patch>
</app>
<!-- </patch> -->
<!-- <patch id="misc.settings.settingsPatch"> -->
<!-- </patch> -->
<!-- </app> -->
</resources>

View File

@@ -835,6 +835,9 @@ To show the Audio track menu, change \'Spoof video streams\' to iOS TV"</string>
<string name="revanced_hide_shorts_upcoming_button_title">Hide Upcoming button</string>
<string name="revanced_hide_shorts_upcoming_button_summary_on">Upcoming button is hidden</string>
<string name="revanced_hide_shorts_upcoming_button_summary_off">Upcoming button is shown</string>
<string name="revanced_hide_shorts_effect_button_title">Hide Effect button</string>
<string name="revanced_hide_shorts_effect_button_summary_on">Effect button is hidden</string>
<string name="revanced_hide_shorts_effect_button_summary_off">Effect button is shown</string>
<string name="revanced_hide_shorts_green_screen_button_title">Hide Green screen button</string>
<string name="revanced_hide_shorts_green_screen_button_summary_on">Green screen button is hidden</string>
<string name="revanced_hide_shorts_green_screen_button_summary_off">Green screen button is shown</string>