Compare commits

..

28 Commits

Author SHA1 Message Date
semantic-release-bot
edf20e397d chore: Release v5.23.0-dev.6 [skip ci]
# [5.23.0-dev.6](https://github.com/ReVanced/revanced-patches/compare/v5.23.0-dev.5...v5.23.0-dev.6) (2025-05-06)

### Bug Fixes

* Correct incorrect fingerprint ([5f05414](5f0541407c))
2025-05-06 10:50:54 +00:00
oSumAtrIX
5f0541407c fix: Correct incorrect fingerprint 2025-05-06 12:47:06 +02:00
semantic-release-bot
56b7ba9ba7 chore: Release v5.23.0-dev.5 [skip ci]
# [5.23.0-dev.5](https://github.com/ReVanced/revanced-patches/compare/v5.23.0-dev.4...v5.23.0-dev.5) (2025-05-06)

### Bug Fixes

* **Spotify - Unlock Spotify Premium:** Remove pop up premium ads ([#4842](https://github.com/ReVanced/revanced-patches/issues/4842)) ([5028c1a](5028c1acb3))

### Features

* **Pandora:** Add `Disable audio ads` and `Unlimited skips` patch ([#4841](https://github.com/ReVanced/revanced-patches/issues/4841)) ([f4f36ff](f4f36ff273))
* **Prime Video:** Add `Skip ads` patch ([#4824](https://github.com/ReVanced/revanced-patches/issues/4824)) ([f8bdf74](f8bdf744ab))
2025-05-06 07:44:30 +00:00
hoodles
f8bdf744ab feat(Prime Video): Add Skip ads patch (#4824) 2025-05-06 11:40:45 +04:00
hoodles
f4f36ff273 feat(Pandora): Add Disable audio ads and Unlimited skips patch (#4841)
Co-authored-by: Nuckyz <61953774+Nuckyz@users.noreply.github.com>
2025-05-06 11:39:07 +04:00
Nuckyz
5028c1acb3 fix(Spotify - Unlock Spotify Premium): Remove pop up premium ads (#4842) 2025-05-06 11:35:47 +04:00
semantic-release-bot
555c9a5823 chore: Release v5.23.0-dev.4 [skip ci]
# [5.23.0-dev.4](https://github.com/ReVanced/revanced-patches/compare/v5.23.0-dev.3...v5.23.0-dev.4) (2025-05-06)

### Features

* **Spotify:** Add `Sanitize sharing links` patch ([#4829](https://github.com/ReVanced/revanced-patches/issues/4829)) ([777957e](777957e2d0))
2025-05-06 07:35:22 +00:00
Dawid Krajcarz
777957e2d0 feat(Spotify): Add Sanitize sharing links patch (#4829) 2025-05-06 11:31:56 +04:00
github-actions[bot]
b3316a5915 chore: Sync translations (#4915) 2025-05-06 11:31:12 +04:00
semantic-release-bot
2ca2bb7692 chore: Release v5.23.0-dev.3 [skip ci]
# [5.23.0-dev.3](https://github.com/ReVanced/revanced-patches/compare/v5.23.0-dev.2...v5.23.0-dev.3) (2025-05-05)

### Bug Fixes

* **YouTube:** Simplify litho filtering patch ([#4910](https://github.com/ReVanced/revanced-patches/issues/4910)) ([23fd720](23fd720fa7))
2025-05-05 11:28:44 +00:00
LisoUseInAIKyrios
23fd720fa7 fix(YouTube): Simplify litho filtering patch (#4910) 2025-05-05 15:25:25 +04:00
semantic-release-bot
1f08586ae8 chore: Release v5.23.0-dev.2 [skip ci]
# [5.23.0-dev.2](https://github.com/ReVanced/revanced-patches/compare/v5.23.0-dev.1...v5.23.0-dev.2) (2025-05-04)

### Bug Fixes

* **YouTube:** Improve litho filtering performance ([#4904](https://github.com/ReVanced/revanced-patches/issues/4904)) ([60fdf4c](60fdf4c44c))
2025-05-04 09:58:25 +00:00
LisoUseInAIKyrios
60fdf4c44c fix(YouTube): Improve litho filtering performance (#4904) 2025-05-04 13:55:12 +04:00
semantic-release-bot
63f3342815 chore: Release v5.23.0-dev.1 [skip ci]
# [5.23.0-dev.1](https://github.com/ReVanced/revanced-patches/compare/v5.22.0...v5.23.0-dev.1) (2025-05-02)

### Features

* **Lightroom:** Constrain patches to last working version ([858c59d](858c59d728))
2025-05-02 13:02:25 +00:00
oSumAtrIX
858c59d728 feat(Lightroom): Constrain patches to last working version 2025-05-02 14:58:57 +02:00
semantic-release-bot
5debf9936d chore: Release v5.22.0 [skip ci]
# [5.22.0](https://github.com/ReVanced/revanced-patches/compare/v5.21.0...v5.22.0) (2025-05-01)

### Bug Fixes

* **TikTok - Feed filter:** Hide ads in following feed ([#4844](https://github.com/ReVanced/revanced-patches/issues/4844)) ([b2453fe](b2453fecfc))
* **YouTube - Hide layout components:** Hide new type of community posts ([#4888](https://github.com/ReVanced/revanced-patches/issues/4888)) ([9b1013e](9b1013e1c2))
* **YouTube - Hide Shorts components:** Hide action buttons A/B button layout ([#4889](https://github.com/ReVanced/revanced-patches/issues/4889)) ([75d6cd7](75d6cd7c7b))
* **YouTube - Shorts autoplay:** Fix autoplay with YT 20.12 ([ef35ed7](ef35ed7335))
* **YouTube - Spoof app version:** Do not hide spoof version in general settings menu ([#4861](https://github.com/ReVanced/revanced-patches/issues/4861)) ([f69eab3](f69eab3e3b))

### Features

* **TikTok - Feed Filter:** Remove TikTok Shop from feed. ([#4851](https://github.com/ReVanced/revanced-patches/issues/4851)) ([72e0c01](72e0c01922))
* **YouTube - GmsCore support:** Show troubleshooting in app text if the user recently changed their account details ([#4879](https://github.com/ReVanced/revanced-patches/issues/4879)) ([45b5a51](45b5a51da3))
2025-05-01 07:03:38 +00:00
LisoUseInAIKyrios
f1b85d20a1 chore: Merge branch dev to main (#4864) 2025-05-01 11:00:16 +04:00
github-actions[bot]
37d0de5e93 chore: Sync translations (#4894) 2025-05-01 10:59:24 +04:00
semantic-release-bot
96d08d5eb7 chore: Release v5.22.0-dev.4 [skip ci]
# [5.22.0-dev.4](https://github.com/ReVanced/revanced-patches/compare/v5.22.0-dev.3...v5.22.0-dev.4) (2025-04-30)

### Bug Fixes

* **YouTube - Hide layout components:** Hide new type of community posts ([#4888](https://github.com/ReVanced/revanced-patches/issues/4888)) ([9b1013e](9b1013e1c2))
* **YouTube - Hide Shorts components:** Hide action buttons A/B button layout ([#4889](https://github.com/ReVanced/revanced-patches/issues/4889)) ([75d6cd7](75d6cd7c7b))
2025-04-30 22:11:17 +00:00
Bceez
9b1013e1c2 fix(YouTube - Hide layout components): Hide new type of community posts (#4888) 2025-05-01 02:07:52 +04:00
LisoUseInAIKyrios
75d6cd7c7b fix(YouTube - Hide Shorts components): Hide action buttons A/B button layout (#4889) 2025-05-01 02:07:32 +04:00
github-actions[bot]
5a17f5e1c1 chore: Sync translations (#4890) 2025-05-01 02:04:21 +04:00
MarcaD
1d16de6617 bug(YouTube - Theme): Fix white system navigation bar in the ReVanced settings (#4875) 2025-04-29 23:27:14 +04:00
github-actions[bot]
aee7cba46d chore: Sync translations (#4884) 2025-04-29 23:26:47 +04:00
semantic-release-bot
ec3faf30a8 chore: Release v5.22.0-dev.3 [skip ci]
# [5.22.0-dev.3](https://github.com/ReVanced/revanced-patches/compare/v5.22.0-dev.2...v5.22.0-dev.3) (2025-04-29)

### Features

* **YouTube - GmsCore support:** Show troubleshooting in app text if the user recently changed their account details ([#4879](https://github.com/ReVanced/revanced-patches/issues/4879)) ([45b5a51](45b5a51da3))
2025-04-29 09:40:18 +00:00
LisoUseInAIKyrios
45b5a51da3 feat(YouTube - GmsCore support): Show troubleshooting in app text if the user recently changed their account details (#4879) 2025-04-29 13:36:29 +04:00
semantic-release-bot
8abf176bc9 chore: Release v5.22.0-dev.2 [skip ci]
# [5.22.0-dev.2](https://github.com/ReVanced/revanced-patches/compare/v5.22.0-dev.1...v5.22.0-dev.2) (2025-04-27)

### Bug Fixes

* **YouTube - Shorts autoplay:** Fix autoplay with YT 20.12 ([ef35ed7](ef35ed7335))
2025-04-27 14:28:33 +00:00
LisoUseInAIKyrios
ef35ed7335 fix(YouTube - Shorts autoplay): Fix autoplay with YT 20.12 2025-04-27 18:24:37 +04:00
136 changed files with 1763 additions and 680 deletions

View File

@@ -1,3 +1,90 @@
# [5.23.0-dev.6](https://github.com/ReVanced/revanced-patches/compare/v5.23.0-dev.5...v5.23.0-dev.6) (2025-05-06)
### Bug Fixes
* Correct incorrect fingerprint ([c3bab89](https://github.com/ReVanced/revanced-patches/commit/c3bab89fc4189e38c10eee0caa36289de7e29dfa))
# [5.23.0-dev.5](https://github.com/ReVanced/revanced-patches/compare/v5.23.0-dev.4...v5.23.0-dev.5) (2025-05-06)
### Bug Fixes
* **Spotify - Unlock Spotify Premium:** Remove pop up premium ads ([#4842](https://github.com/ReVanced/revanced-patches/issues/4842)) ([00aa200](https://github.com/ReVanced/revanced-patches/commit/00aa2000ba2eef15a0dd827c2bd84c2e85c412e0))
### Features
* **Pandora:** Add `Disable audio ads` and `Unlimited skips` patch ([#4841](https://github.com/ReVanced/revanced-patches/issues/4841)) ([0cf7a4c](https://github.com/ReVanced/revanced-patches/commit/0cf7a4c6be615ed0a52a6bacf87592f5f43ff575))
* **Prime Video:** Add `Skip ads` patch ([#4824](https://github.com/ReVanced/revanced-patches/issues/4824)) ([bb672c4](https://github.com/ReVanced/revanced-patches/commit/bb672c4674ddc201b8b2648c3906cfc31ef43f10))
# [5.23.0-dev.4](https://github.com/ReVanced/revanced-patches/compare/v5.23.0-dev.3...v5.23.0-dev.4) (2025-05-06)
### Features
* **Spotify:** Add `Sanitize sharing links` patch ([#4829](https://github.com/ReVanced/revanced-patches/issues/4829)) ([2e3511d](https://github.com/ReVanced/revanced-patches/commit/2e3511d03c8198bbdb9336888df038a33fb3ab8c))
# [5.23.0-dev.3](https://github.com/ReVanced/revanced-patches/compare/v5.23.0-dev.2...v5.23.0-dev.3) (2025-05-05)
### Bug Fixes
* **YouTube:** Simplify litho filtering patch ([#4910](https://github.com/ReVanced/revanced-patches/issues/4910)) ([bd53955](https://github.com/ReVanced/revanced-patches/commit/bd53955df738bb7b819eb91a3e776e9d2ca5c74a))
# [5.23.0-dev.2](https://github.com/ReVanced/revanced-patches/compare/v5.23.0-dev.1...v5.23.0-dev.2) (2025-05-04)
### Bug Fixes
* **YouTube:** Improve litho filtering performance ([#4904](https://github.com/ReVanced/revanced-patches/issues/4904)) ([7b43986](https://github.com/ReVanced/revanced-patches/commit/7b43986871a68e5cb43331d2fb2fdb9ef67438ad))
# [5.23.0-dev.1](https://github.com/ReVanced/revanced-patches/compare/v5.22.0...v5.23.0-dev.1) (2025-05-02)
### Features
* **Lightroom:** Constrain patches to last working version ([efef03b](https://github.com/ReVanced/revanced-patches/commit/efef03b80da21552d0d8be6913faba64e4fb5ed1))
# [5.22.0](https://github.com/ReVanced/revanced-patches/compare/v5.21.0...v5.22.0) (2025-05-01)
### Bug Fixes
* **TikTok - Feed filter:** Hide ads in following feed ([#4844](https://github.com/ReVanced/revanced-patches/issues/4844)) ([c255ac1](https://github.com/ReVanced/revanced-patches/commit/c255ac18e0b2dcf917bd0559876be5a2a81023db))
* **YouTube - Hide layout components:** Hide new type of community posts ([#4888](https://github.com/ReVanced/revanced-patches/issues/4888)) ([f0c9c35](https://github.com/ReVanced/revanced-patches/commit/f0c9c35778ab43a99149ee5ad0ccfd8aeb09f638))
* **YouTube - Hide Shorts components:** Hide action buttons A/B button layout ([#4889](https://github.com/ReVanced/revanced-patches/issues/4889)) ([9dcd3d3](https://github.com/ReVanced/revanced-patches/commit/9dcd3d35dddf019547ab6ce431bac7a5a8a4c291))
* **YouTube - Shorts autoplay:** Fix autoplay with YT 20.12 ([06b35b2](https://github.com/ReVanced/revanced-patches/commit/06b35b2a7d7371915881e8f430c32ce15fa224de))
* **YouTube - Spoof app version:** Do not hide spoof version in general settings menu ([#4861](https://github.com/ReVanced/revanced-patches/issues/4861)) ([f459c3c](https://github.com/ReVanced/revanced-patches/commit/f459c3c7fae3a1b8addf3354488dcef9f95255cc))
### Features
* **TikTok - Feed Filter:** Remove TikTok Shop from feed. ([#4851](https://github.com/ReVanced/revanced-patches/issues/4851)) ([f198bec](https://github.com/ReVanced/revanced-patches/commit/f198bece653e3e1adf083129dedb77c1d1a633d7))
* **YouTube - GmsCore support:** Show troubleshooting in app text if the user recently changed their account details ([#4879](https://github.com/ReVanced/revanced-patches/issues/4879)) ([ab4bdc8](https://github.com/ReVanced/revanced-patches/commit/ab4bdc8a2519cee15f79bf95d89e7ea56ea464ee))
# [5.22.0-dev.4](https://github.com/ReVanced/revanced-patches/compare/v5.22.0-dev.3...v5.22.0-dev.4) (2025-04-30)
### Bug Fixes
* **YouTube - Hide layout components:** Hide new type of community posts ([#4888](https://github.com/ReVanced/revanced-patches/issues/4888)) ([f0c9c35](https://github.com/ReVanced/revanced-patches/commit/f0c9c35778ab43a99149ee5ad0ccfd8aeb09f638))
* **YouTube - Hide Shorts components:** Hide action buttons A/B button layout ([#4889](https://github.com/ReVanced/revanced-patches/issues/4889)) ([9dcd3d3](https://github.com/ReVanced/revanced-patches/commit/9dcd3d35dddf019547ab6ce431bac7a5a8a4c291))
# [5.22.0-dev.3](https://github.com/ReVanced/revanced-patches/compare/v5.22.0-dev.2...v5.22.0-dev.3) (2025-04-29)
### Features
* **YouTube - GmsCore support:** Show troubleshooting in app text if the user recently changed their account details ([#4879](https://github.com/ReVanced/revanced-patches/issues/4879)) ([ab4bdc8](https://github.com/ReVanced/revanced-patches/commit/ab4bdc8a2519cee15f79bf95d89e7ea56ea464ee))
# [5.22.0-dev.2](https://github.com/ReVanced/revanced-patches/compare/v5.22.0-dev.1...v5.22.0-dev.2) (2025-04-27)
### Bug Fixes
* **YouTube - Shorts autoplay:** Fix autoplay with YT 20.12 ([06b35b2](https://github.com/ReVanced/revanced-patches/commit/06b35b2a7d7371915881e8f430c32ce15fa224de))
# [5.22.0-dev.1](https://github.com/ReVanced/revanced-patches/compare/v5.21.0...v5.22.0-dev.1) (2025-04-26)

View File

@@ -0,0 +1,4 @@
dependencies {
compileOnly(project(":extensions:shared:library"))
compileOnly(project(":extensions:primevideo:stub"))
}

View File

@@ -0,0 +1 @@
<manifest/>

View File

@@ -0,0 +1,36 @@
package app.revanced.extension.primevideo.ads;
import com.amazon.avod.fsm.SimpleTrigger;
import com.amazon.avod.media.ads.AdBreak;
import com.amazon.avod.media.ads.internal.state.AdBreakTrigger;
import com.amazon.avod.media.ads.internal.state.AdEnabledPlayerTriggerType;
import com.amazon.avod.media.playback.VideoPlayer;
import com.amazon.avod.media.ads.internal.state.ServerInsertedAdBreakState;
import app.revanced.extension.shared.Logger;
@SuppressWarnings("unused")
public final class SkipAdsPatch {
public static void enterServerInsertedAdBreakState(ServerInsertedAdBreakState state, AdBreakTrigger trigger, VideoPlayer player) {
try {
AdBreak adBreak = trigger.getBreak();
// There are two scenarios when entering the original method:
// 1. Player naturally entered an ad break while watching a video.
// 2. User is skipped/scrubbed to a position on the timeline. If seek position is past an ad break,
// user is forced to watch an ad before continuing.
//
// Scenario 2 is indicated by trigger.getSeekStartPosition() != null, so skip directly to the scrubbing
// target. Otherwise, just calculate when the ad break should end and skip to there.
if (trigger.getSeekStartPosition() != null)
player.seekTo(trigger.getSeekTarget().getTotalMilliseconds());
else
player.seekTo(player.getCurrentPosition() + adBreak.getDurationExcludingAux().getTotalMilliseconds());
// Send "end of ads" trigger to state machine so everything doesn't get whacky.
state.doTrigger(new SimpleTrigger(AdEnabledPlayerTriggerType.NO_MORE_ADS_SKIP_TRANSITION));
} catch (Exception ex) {
Logger.printException(() -> "Failed skipping ads", ex);
}
}
}

View File

@@ -0,0 +1,17 @@
plugins {
id(libs.plugins.android.library.get().pluginId)
}
android {
namespace = "app.revanced.extension"
compileSdk = 34
defaultConfig {
minSdk = 21
}
compileOptions {
sourceCompatibility = JavaVersion.VERSION_11
targetCompatibility = JavaVersion.VERSION_11
}
}

View File

@@ -0,0 +1 @@
<manifest/>

View File

@@ -0,0 +1,6 @@
package com.amazon.avod.fsm;
public final class SimpleTrigger<T> implements Trigger<T> {
public SimpleTrigger(T triggerType) {
}
}

View File

@@ -0,0 +1,7 @@
package com.amazon.avod.fsm;
public abstract class StateBase<S, T> {
// This method orginally has protected access (modified in patch code).
public void doTrigger(Trigger<T> trigger) {
}
}

View File

@@ -0,0 +1,4 @@
package com.amazon.avod.fsm;
public interface Trigger<T> {
}

View File

@@ -0,0 +1,7 @@
package com.amazon.avod.media;
public final class TimeSpan {
public long getTotalMilliseconds() {
throw new UnsupportedOperationException();
}
}

View File

@@ -0,0 +1,7 @@
package com.amazon.avod.media.ads;
import com.amazon.avod.media.TimeSpan;
public interface AdBreak {
TimeSpan getDurationExcludingAux();
}

View File

@@ -0,0 +1,4 @@
package com.amazon.avod.media.ads.internal.state;
public abstract class AdBreakState extends AdEnabledPlaybackState {
}

View File

@@ -0,0 +1,18 @@
package com.amazon.avod.media.ads.internal.state;
import com.amazon.avod.media.ads.AdBreak;
import com.amazon.avod.media.TimeSpan;
public class AdBreakTrigger {
public AdBreak getBreak() {
throw new UnsupportedOperationException();
}
public TimeSpan getSeekTarget() {
throw new UnsupportedOperationException();
}
public TimeSpan getSeekStartPosition() {
throw new UnsupportedOperationException();
}
}

View File

@@ -0,0 +1,8 @@
package com.amazon.avod.media.ads.internal.state;
import com.amazon.avod.fsm.StateBase;
import com.amazon.avod.media.playback.state.PlayerStateType;
import com.amazon.avod.media.playback.state.trigger.PlayerTriggerType;
public class AdEnabledPlaybackState extends StateBase<PlayerStateType, PlayerTriggerType> {
}

View File

@@ -0,0 +1,5 @@
package com.amazon.avod.media.ads.internal.state;
public enum AdEnabledPlayerTriggerType {
NO_MORE_ADS_SKIP_TRANSITION
}

View File

@@ -0,0 +1,4 @@
package com.amazon.avod.media.ads.internal.state;
public class ServerInsertedAdBreakState extends AdBreakState {
}

View File

@@ -0,0 +1,7 @@
package com.amazon.avod.media.playback;
public interface VideoPlayer {
long getCurrentPosition();
void seekTo(long positionMs);
}

View File

@@ -0,0 +1,4 @@
package com.amazon.avod.media.playback.state;
public interface PlayerStateType {
}

View File

@@ -0,0 +1,4 @@
package com.amazon.avod.media.playback.state.trigger;
public interface PlayerTriggerType {
}

View File

@@ -20,9 +20,7 @@ import androidx.annotation.RequiresApi;
import java.net.MalformedURLException;
import java.net.URL;
/**
* @noinspection unused
*/
@SuppressWarnings("unused")
public class GmsCoreSupport {
private static final String PACKAGE_NAME_YOUTUBE = "com.google.android.youtube";
private static final String PACKAGE_NAME_YOUTUBE_MUSIC = "com.google.android.apps.youtube.music";

View File

@@ -0,0 +1,43 @@
package app.revanced.extension.spotify.misc.privacy;
import android.net.Uri;
import java.util.List;
import app.revanced.extension.shared.Logger;
@SuppressWarnings("unused")
public final class SanitizeSharingLinksPatch {
/**
* Parameters that are considered undesirable and should be stripped away.
*/
private static final List<String> SHARE_PARAMETERS_TO_REMOVE = List.of(
"si", // Share tracking parameter.
"utm_source" // Share source, such as "copy-link".
);
/**
* Injection point.
*/
public static String sanitizeUrl(String url) {
try {
Uri uri = Uri.parse(url);
Uri.Builder builder = uri.buildUpon().clearQuery();
for (String paramName : uri.getQueryParameterNames()) {
if (!SHARE_PARAMETERS_TO_REMOVE.contains(paramName)) {
for (String value : uri.getQueryParameters(paramName)) {
builder.appendQueryParameter(paramName, value);
}
}
}
return builder.build().toString();
} catch (Exception ex) {
Logger.printException(() -> "sanitizeUrl failure", ex);
return url;
}
}
}

View File

@@ -2,6 +2,8 @@ package app.revanced.extension.youtube;
import android.app.Activity;
import android.graphics.Color;
import android.os.Build;
import android.view.Window;
import androidx.annotation.Nullable;
@@ -102,4 +104,21 @@ public class ThemeHelper {
return Utils.getColorFromString(colorName);
}
/**
* Sets the system navigation bar color for the activity.
* Applies the background color obtained from {@link #getBackgroundColor()} to the navigation bar.
* For Android 10 (API 29) and above, enforces navigation bar contrast to ensure visibility.
*/
public static void setNavigationBarColor(@Nullable Window window) {
if (window == null) {
Logger.printDebug(() -> "Cannot set navigation bar color, window is null");
return;
}
window.setNavigationBarColor(getBackgroundColor());
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
window.setNavigationBarContrastEnforced(true);
}
}
}

View File

@@ -0,0 +1,28 @@
package app.revanced.extension.youtube.patches;
import static app.revanced.extension.shared.StringRef.sf;
import app.revanced.extension.shared.Logger;
import app.revanced.extension.shared.Utils;
@SuppressWarnings("unused")
public class AccountCredentialsInvalidTextPatch {
/**
* Injection point.
*/
public static String getOfflineNetworkErrorString(String original) {
try {
if (Utils.isNetworkConnected()) {
Logger.printDebug(() -> "Network appears to be online, but app is showing offline error");
return '\n' + sf("microg_offline_account_login_error").toString();
}
Logger.printDebug(() -> "Network is offline");
} catch (Exception ex) {
Logger.printException(() -> "getOfflineNetworkErrorString failure", ex);
}
return original;
}
}

View File

@@ -2,6 +2,8 @@ package app.revanced.extension.youtube.patches;
import android.app.Activity;
import androidx.annotation.Nullable;
import java.lang.ref.WeakReference;
import java.util.Objects;
@@ -76,7 +78,7 @@ public class ShortsAutoplayPatch {
/**
* Injection point.
*/
public static Enum<?> changeShortsRepeatBehavior(Enum<?> original) {
public static Enum<?> changeShortsRepeatBehavior(@Nullable Enum<?> original) {
try {
final boolean autoplay;
@@ -98,17 +100,35 @@ public class ShortsAutoplayPatch {
: ShortsLoopBehavior.REPEAT;
if (behavior.ytEnumValue != null) {
Logger.printDebug(() -> behavior.ytEnumValue == original
? "Changing Shorts repeat behavior from: " + original.name() + " to: " + behavior.ytEnumValue
: "Behavior setting is same as original. Using original: " + original.name()
);
Logger.printDebug(() -> {
String name = (original == null ? "unknown (null)" : original.name());
return behavior == original
? "Behavior setting is same as original. Using original: " + name
: "Changing Shorts repeat behavior from: " + name + " to: " + behavior.name();
});
return behavior.ytEnumValue;
}
if (original == null) {
// Cannot return null, as null is used to indicate Short was auto played.
// Unpatched app replaces null with unknown enum type (appears to fix for bad api data).
Enum<?> unknown = ShortsLoopBehavior.UNKNOWN.ytEnumValue;
Logger.printDebug(() -> "Original is null, returning: " + unknown.name());
return unknown;
}
} catch (Exception ex) {
Logger.printException(() -> "changeShortsRepeatState failure", ex);
Logger.printException(() -> "changeShortsRepeatBehavior failure", ex);
}
return original;
}
/**
* Injection point.
*/
public static boolean isAutoPlay(Enum<?> original) {
return ShortsLoopBehavior.SINGLE_PLAY.ytEnumValue == original;
}
}

View File

@@ -75,7 +75,10 @@ public final class LayoutComponentsFilter extends Filter {
"post_base_wrapper_slim.eml",
"poll_post_root.eml",
"videos_post_root.eml",
"post_shelf_slim.eml"
"post_shelf_slim.eml",
"videos_post_responsive_root.eml",
"text_post_responsive_root.eml",
"poll_post_responsive_root.eml"
);
final var communityGuidelines = new StringFilterGroup(

View File

@@ -87,6 +87,10 @@ public final class LithoFilterPatch {
* the buffer is saved to a ThreadLocal so each calling thread does not interfere with other threads.
*/
private static final ThreadLocal<ByteBuffer> bufferThreadLocal = new ThreadLocal<>();
/**
* Results of calling {@link #filter(String, StringBuilder)}.
*/
private static final ThreadLocal<Boolean> filterResult = new ThreadLocal<>();
static {
for (Filter filter : filters) {
@@ -140,11 +144,22 @@ public final class LithoFilterPatch {
}
}
/**
* Injection point.
*/
public static boolean shouldFilter() {
Boolean shouldFilter = filterResult.get();
return shouldFilter != null && shouldFilter;
}
/**
* Injection point. Called off the main thread, and commonly called by multiple threads at the same time.
*/
@SuppressWarnings("unused")
public static boolean filter(@Nullable String lithoIdentifier, @NonNull StringBuilder pathBuilder) {
public static void filter(@Nullable String lithoIdentifier, StringBuilder pathBuilder) {
filterResult.set(handleFiltering(lithoIdentifier, pathBuilder));
}
private static boolean handleFiltering(@Nullable String lithoIdentifier, StringBuilder pathBuilder) {
try {
if (pathBuilder.length() == 0) {
return false;

View File

@@ -1,6 +1,5 @@
package app.revanced.extension.youtube.patches.components;
import static app.revanced.extension.shared.Utils.hideViewUnderCondition;
import static app.revanced.extension.youtube.shared.NavigationBar.NavigationButton;
import android.view.View;
@@ -52,6 +51,7 @@ public final class ShortsFilter extends Filter {
private final StringFilterGroup suggestedAction;
private final ByteArrayFilterGroupList suggestedActionsGroupList = new ByteArrayFilterGroupList();
private final StringFilterGroup shortsActionBar;
private final StringFilterGroup actionButton;
private final ByteArrayFilterGroupList videoActionButtonGroupList = new ByteArrayFilterGroupList();
@@ -141,6 +141,16 @@ public final class ShortsFilter extends Filter {
"like_fountain.eml"
);
StringFilterGroup likeButton = new StringFilterGroup(
Settings.HIDE_SHORTS_LIKE_BUTTON,
"shorts_like_button.eml"
);
StringFilterGroup dislikeButton = new StringFilterGroup(
Settings.HIDE_SHORTS_DISLIKE_BUTTON,
"shorts_dislike_button.eml"
);
joinButton = new StringFilterGroup(
Settings.HIDE_SHORTS_JOIN_BUTTON,
"sponsor_button"
@@ -156,9 +166,15 @@ public final class ShortsFilter extends Filter {
"reel_player_disclosure.eml"
);
shortsActionBar = new StringFilterGroup(
null,
"shorts_action_bar.eml"
);
actionButton = new StringFilterGroup(
null,
"shorts_video_action_button.eml"
// Can be simply 'button.eml' or 'shorts_video_action_button.eml'
"button.eml"
);
suggestedAction = new StringFilterGroup(
@@ -167,27 +183,16 @@ public final class ShortsFilter extends Filter {
);
addPathCallbacks(
shortsCompactFeedVideoPath, suggestedAction, actionButton, joinButton, subscribeButton,
paidPromotionButton, pausedOverlayButtons, channelBar, fullVideoLinkLabel, videoTitle,
reelSoundMetadata, soundButton, infoPanel, stickers, likeFountain
shortsCompactFeedVideoPath, joinButton, subscribeButton, paidPromotionButton,
shortsActionBar, suggestedAction, pausedOverlayButtons, channelBar,
fullVideoLinkLabel, videoTitle, reelSoundMetadata, soundButton, infoPanel,
stickers, likeFountain, likeButton, dislikeButton
);
//
// Action buttons
// All other action buttons.
//
videoActionButtonGroupList.addAll(
// This also appears as the path item 'shorts_like_button.eml'
new ByteArrayFilterGroup(
Settings.HIDE_SHORTS_LIKE_BUTTON,
"reel_like_button",
"reel_like_toggled_button"
),
// This also appears as the path item 'shorts_dislike_button.eml'
new ByteArrayFilterGroup(
Settings.HIDE_SHORTS_DISLIKE_BUTTON,
"reel_dislike_button",
"reel_dislike_toggled_button"
),
new ByteArrayFilterGroup(
Settings.HIDE_SHORTS_COMMENTS_BUTTON,
"reel_comment_button"
@@ -286,9 +291,11 @@ public final class ShortsFilter extends Filter {
return false;
}
// Video action buttons (like, dislike, comment, share, remix) have the same path.
if (matchedGroup == actionButton) {
if (videoActionButtonGroupList.check(protobufBufferArray).isFiltered()) {
// Video action buttons (comment, share, remix) have the same path.
// Like and dislike are separate path filters and don't require buffer searching.
if (matchedGroup == shortsActionBar) {
if (actionButton.check(path).isFiltered()
&& videoActionButtonGroupList.check(protobufBufferArray).isFiltered()) {
return super.isFiltered(identifier, path, protobufBufferArray, matchedGroup, contentType, contentIndex);
}
return false;
@@ -392,37 +399,6 @@ public final class ShortsFilter extends Filter {
return original;
}
// region Hide the buttons in older versions of YouTube. New versions use Litho.
public static void hideLikeButton(final View likeButtonView) {
// Cannot set the visibility to gone for like/dislike,
// as some other unknown YT code also sets the visibility after this hook.
//
// Setting the view to 0dp works, but that leaves a blank space where
// the button was (only relevant for dislikes button).
//
// Instead remove the view from the parent.
Utils.hideViewByRemovingFromParentUnderCondition(Settings.HIDE_SHORTS_LIKE_BUTTON, likeButtonView);
}
public static void hideDislikeButton(final View dislikeButtonView) {
Utils.hideViewByRemovingFromParentUnderCondition(Settings.HIDE_SHORTS_DISLIKE_BUTTON, dislikeButtonView);
}
public static void hideShortsCommentsButton(final View commentsButtonView) {
hideViewUnderCondition(Settings.HIDE_SHORTS_COMMENTS_BUTTON, commentsButtonView);
}
public static void hideShortsRemixButton(final View remixButtonView) {
hideViewUnderCondition(Settings.HIDE_SHORTS_REMIX_BUTTON, remixButtonView);
}
public static void hideShortsShareButton(final View shareButtonView) {
hideViewUnderCondition(Settings.HIDE_SHORTS_SHARE_BUTTON, shareButtonView);
}
// endregion
public static void setNavigationBar(PivotBar view) {
pivotBarRef = new WeakReference<>(view);
}

View File

@@ -84,6 +84,7 @@ public class LicenseActivityHook {
public static void initialize(Activity licenseActivity) {
try {
ThemeHelper.setActivityTheme(licenseActivity);
ThemeHelper.setNavigationBarColor(licenseActivity.getWindow());
licenseActivity.setContentView(getResourceIdentifier(
"revanced_settings_with_toolbar", "layout"));
@@ -126,7 +127,7 @@ public class LicenseActivityHook {
// This is required to fix submenu title alignment issue with Android ASOP 15+
ViewGroup toolBarParent = activity.findViewById(
getResourceIdentifier("revanced_toolbar_parent", "id"));
ViewGroup dummyToolbar = Utils.getChildViewByResourceName(toolBarParent,"revanced_toolbar");
ViewGroup dummyToolbar = Utils.getChildViewByResourceName(toolBarParent, "revanced_toolbar");
toolbarLayoutParams = dummyToolbar.getLayoutParams();
toolBarParent.removeView(dummyToolbar);
@@ -149,5 +150,4 @@ public class LicenseActivityHook {
toolBarParent.addView(toolbar, 0);
}
}
}

View File

@@ -138,6 +138,9 @@ public class ReVancedPreferenceFragment extends AbstractPreferenceFragment {
.findViewById(android.R.id.content)
.getParent();
// Fix the system navigation bar color for submenus.
ThemeHelper.setNavigationBarColor(preferenceScreenDialog.getWindow());
// Fix edge-to-edge screen with Android 15 and YT 19.45+
// https://developer.android.com/develop/ui/views/layout/edge-to-edge#system-bars-insets
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {

View File

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

View File

@@ -380,6 +380,14 @@ public final class app/revanced/patches/openinghours/misc/fix/crash/FixCrashPatc
public static final fun getFixCrashPatch ()Lapp/revanced/patcher/patch/BytecodePatch;
}
public final class app/revanced/patches/pandora/ads/DisableAudioAdsPatchKt {
public static final fun getDisableAudioAdsPatch ()Lapp/revanced/patcher/patch/BytecodePatch;
}
public final class app/revanced/patches/pandora/misc/EnableUnlimitedSkipsPatchKt {
public static final fun getEnableUnlimitedSkipsPatch ()Lapp/revanced/patcher/patch/BytecodePatch;
}
public final class app/revanced/patches/photomath/detection/deviceid/SpoofDeviceIdPatchKt {
public static final fun getGetDeviceIdPatch ()Lapp/revanced/patcher/patch/BytecodePatch;
}
@@ -412,6 +420,14 @@ public final class app/revanced/patches/pixiv/ads/HideAdsPatchKt {
public static final fun getHideAdsPatch ()Lapp/revanced/patcher/patch/BytecodePatch;
}
public final class app/revanced/patches/primevideo/ads/SkipAdsPatchKt {
public static final fun getSkipAdsPatch ()Lapp/revanced/patcher/patch/BytecodePatch;
}
public final class app/revanced/patches/primevideo/misc/extension/ExtensionPatchKt {
public static final fun getSharedExtensionPatch ()Lapp/revanced/patcher/patch/BytecodePatch;
}
public final class app/revanced/patches/protonmail/signature/RemoveSentFromSignaturePatchKt {
public static final fun getRemoveSentFromSignaturePatch ()Lapp/revanced/patcher/patch/ResourcePatch;
}
@@ -852,6 +868,10 @@ public final class app/revanced/patches/spotify/misc/fix/SpoofSignaturePatchKt {
public static final fun getSpoofSignaturePatch ()Lapp/revanced/patcher/patch/BytecodePatch;
}
public final class app/revanced/patches/spotify/misc/privacy/SanitizeSharingLinksPatchKt {
public static final fun getSanitizeSharingLinksPatch ()Lapp/revanced/patcher/patch/BytecodePatch;
}
public final class app/revanced/patches/spotify/navbar/PremiumNavbarTabPatchKt {
public static final fun getPremiumNavbarTabPatch ()Lapp/revanced/patcher/patch/BytecodePatch;
}
@@ -1534,6 +1554,7 @@ public final class app/revanced/patches/yuka/misc/unlockpremium/UnlockPremiumPat
public final class app/revanced/util/BytecodeUtilsKt {
public static final fun addInstructionsAtControlFlowLabel (Lapp/revanced/patcher/util/proxy/mutableTypes/MutableMethod;ILjava/lang/String;)V
public static final fun addInstructionsAtControlFlowLabel (Lapp/revanced/patcher/util/proxy/mutableTypes/MutableMethod;ILjava/lang/String;[Lapp/revanced/patcher/util/smali/ExternalLabel;)V
public static final fun containsLiteralInstruction (Lcom/android/tools/smali/dexlib2/iface/Method;D)Z
public static final fun containsLiteralInstruction (Lcom/android/tools/smali/dexlib2/iface/Method;F)Z
public static final fun containsLiteralInstruction (Lcom/android/tools/smali/dexlib2/iface/Method;J)Z

View File

@@ -7,7 +7,7 @@ import app.revanced.patcher.patch.bytecodePatch
val disableMandatoryLoginPatch = bytecodePatch(
name = "Disable mandatory login",
) {
compatibleWith("com.adobe.lrmobile")
compatibleWith("com.adobe.lrmobile"("10.0.2"))
execute {
isLoggedInFingerprint.method.apply {

View File

@@ -7,7 +7,7 @@ import app.revanced.patcher.patch.bytecodePatch
val unlockPremiumPatch = bytecodePatch(
name = "Unlock premium",
) {
compatibleWith("com.adobe.lrmobile")
compatibleWith("com.adobe.lrmobile"("10.0.2"))
execute {
// Set hasPremium = true.

View File

@@ -0,0 +1,30 @@
package app.revanced.patches.pandora.ads
import app.revanced.patcher.extensions.InstructionExtensions.addInstruction
import app.revanced.patcher.extensions.InstructionExtensions.getInstruction
import app.revanced.patcher.patch.bytecodePatch
import app.revanced.patches.pandora.shared.constructUserDataFingerprint
import app.revanced.util.indexOfFirstInstructionOrThrow
import com.android.tools.smali.dexlib2.Opcode
import com.android.tools.smali.dexlib2.iface.instruction.OneRegisterInstruction
@Suppress("unused")
val disableAudioAdsPatch = bytecodePatch(
name = "Disable audio ads",
) {
compatibleWith("com.pandora.android")
execute {
constructUserDataFingerprint.method.apply {
// First match is "hasAudioAds".
val hasAudioAdsStringIndex = constructUserDataFingerprint.stringMatches!!.first().index
val moveResultIndex = indexOfFirstInstructionOrThrow(hasAudioAdsStringIndex, Opcode.MOVE_RESULT)
val hasAudioAdsRegister = getInstruction<OneRegisterInstruction>(moveResultIndex).registerA
addInstruction(
moveResultIndex + 1,
"const/4 v$hasAudioAdsRegister, 0"
)
}
}
}

View File

@@ -0,0 +1,31 @@
package app.revanced.patches.pandora.misc
import app.revanced.patcher.extensions.InstructionExtensions.addInstruction
import app.revanced.patcher.extensions.InstructionExtensions.getInstruction
import app.revanced.patcher.patch.bytecodePatch
import app.revanced.patches.pandora.shared.constructUserDataFingerprint
import app.revanced.util.indexOfFirstInstructionOrThrow
import com.android.tools.smali.dexlib2.Opcode
import com.android.tools.smali.dexlib2.iface.instruction.OneRegisterInstruction
@Suppress("unused")
val enableUnlimitedSkipsPatch = bytecodePatch(
name = "Enable unlimited skips",
) {
compatibleWith("com.pandora.android")
execute {
constructUserDataFingerprint.method.apply {
// Last match is "skipLimitBehavior".
val skipLimitBehaviorStringIndex = constructUserDataFingerprint.stringMatches!!.last().index
val moveResultObjectIndex =
indexOfFirstInstructionOrThrow(skipLimitBehaviorStringIndex, Opcode.MOVE_RESULT_OBJECT)
val skipLimitBehaviorRegister = getInstruction<OneRegisterInstruction>(moveResultObjectIndex).registerA
addInstruction(
moveResultObjectIndex + 1,
"const-string v$skipLimitBehaviorRegister, \"unlimited\""
)
}
}
}

View File

@@ -0,0 +1,7 @@
package app.revanced.patches.pandora.shared
import app.revanced.patcher.fingerprint
internal val constructUserDataFingerprint = fingerprint {
strings("hasAudioAds", "skipLimitBehavior")
}

View File

@@ -0,0 +1,33 @@
package app.revanced.patches.primevideo.ads
import app.revanced.patcher.fingerprint
import com.android.tools.smali.dexlib2.AccessFlags
import com.android.tools.smali.dexlib2.Opcode
internal val enterServerInsertedAdBreakStateFingerprint = fingerprint {
accessFlags(AccessFlags.PUBLIC)
parameters("Lcom/amazon/avod/fsm/Trigger;")
returns("V")
opcodes(
Opcode.INVOKE_VIRTUAL,
Opcode.MOVE_RESULT_OBJECT,
Opcode.CONST_4,
Opcode.CONST_4
)
custom { method, classDef ->
method.name == "enter" && classDef.type == "Lcom/amazon/avod/media/ads/internal/state/ServerInsertedAdBreakState;"
}
}
internal val doTriggerFingerprint = fingerprint {
accessFlags(AccessFlags.PROTECTED)
returns("V")
opcodes(
Opcode.IGET_OBJECT,
Opcode.INVOKE_INTERFACE,
Opcode.RETURN_VOID
)
custom { method, classDef ->
method.name == "doTrigger" && classDef.type == "Lcom/amazon/avod/fsm/StateBase;"
}
}

View File

@@ -0,0 +1,45 @@
package app.revanced.patches.primevideo.ads
import app.revanced.patcher.extensions.InstructionExtensions.addInstructions
import app.revanced.patcher.extensions.InstructionExtensions.getInstruction
import app.revanced.patcher.patch.bytecodePatch
import app.revanced.patches.primevideo.misc.extension.sharedExtensionPatch
import com.android.tools.smali.dexlib2.AccessFlags
import com.android.tools.smali.dexlib2.iface.instruction.OneRegisterInstruction
@Suppress("unused")
val skipAdsPatch = bytecodePatch(
name = "Skip ads",
description = "Automatically skips video stream ads.",
) {
compatibleWith("com.amazon.avod.thirdpartyclient"("3.0.403.257"))
dependsOn(sharedExtensionPatch)
// Skip all the logic in ServerInsertedAdBreakState.enter(), which plays all the ad clips in this
// ad break. Instead, force the video player to seek over the entire break and reset the state machine.
execute {
// Force doTrigger() access to public so we can call it from our extension.
doTriggerFingerprint.method.accessFlags = AccessFlags.PUBLIC.value;
val getPlayerIndex = enterServerInsertedAdBreakStateFingerprint.patternMatch!!.startIndex
enterServerInsertedAdBreakStateFingerprint.method.apply {
// Get register that stores VideoPlayer:
// invoke-virtual ->getPrimaryPlayer()
// move-result-object { playerRegister }
val playerRegister = getInstruction<OneRegisterInstruction>(getPlayerIndex + 1).registerA
// Reuse the params from the original method:
// p0 = ServerInsertedAdBreakState
// p1 = AdBreakTrigger
addInstructions(
getPlayerIndex + 2,
"""
invoke-static { p0, p1, v$playerRegister }, Lapp/revanced/extension/primevideo/ads/SkipAdsPatch;->enterServerInsertedAdBreakState(Lcom/amazon/avod/media/ads/internal/state/ServerInsertedAdBreakState;Lcom/amazon/avod/media/ads/internal/state/AdBreakTrigger;Lcom/amazon/avod/media/playback/VideoPlayer;)V
return-void
"""
)
}
}
}

View File

@@ -0,0 +1,5 @@
package app.revanced.patches.primevideo.misc.extension
import app.revanced.patches.shared.misc.extension.sharedExtensionPatch
val sharedExtensionPatch = sharedExtensionPatch("primevideo", applicationInitHook)

View File

@@ -0,0 +1,9 @@
package app.revanced.patches.primevideo.misc.extension
import app.revanced.patches.shared.misc.extension.extensionHook
internal val applicationInitHook = extensionHook {
custom { method, classDef ->
method.name == "onCreate" && classDef.endsWith("/SplashScreenActivity;")
}
}

View File

@@ -2,8 +2,12 @@ package app.revanced.patches.spotify.misc
import app.revanced.patcher.fingerprint
import app.revanced.patches.spotify.misc.extension.IS_SPOTIFY_LEGACY_APP_TARGET
import app.revanced.util.getReference
import app.revanced.util.indexOfFirstInstruction
import com.android.tools.smali.dexlib2.AccessFlags
import com.android.tools.smali.dexlib2.Opcode
import com.android.tools.smali.dexlib2.iface.reference.FieldReference
import com.android.tools.smali.dexlib2.iface.reference.TypeReference
internal val accountAttributeFingerprint = fingerprint {
custom { _, classDef ->
@@ -15,7 +19,7 @@ internal val accountAttributeFingerprint = fingerprint {
}
}
internal val productStateProtoFingerprint = fingerprint {
internal val productStateProtoGetMapFingerprint = fingerprint {
returns("Ljava/util/Map;")
custom { _, classDef ->
classDef.type == if (IS_SPOTIFY_LEGACY_APP_TARGET) {
@@ -56,16 +60,40 @@ internal val readPlayerOptionOverridesFingerprint = fingerprint {
}
}
internal val homeSectionFingerprint = fingerprint {
custom { _, classDef -> classDef.endsWith("homeapi/proto/Section;") }
}
internal val protobufListsFingerprint = fingerprint {
accessFlags(AccessFlags.PUBLIC, AccessFlags.STATIC)
custom { method, _ -> method.name == "emptyProtobufList" }
}
internal val homeStructureFingerprint = fingerprint {
opcodes(Opcode.IGET_OBJECT, Opcode.RETURN_OBJECT)
custom { _, classDef -> classDef.endsWith("homeapi/proto/HomeStructure;") }
internal val protobufListRemoveFingerprint = fingerprint {
custom { method, _ -> method.name == "remove" }
}
internal val homeSectionFingerprint = fingerprint {
custom { _, classDef -> classDef.endsWith("homeapi/proto/Section;") }
}
internal val homeStructureGetSectionsFingerprint = fingerprint {
custom { method, classDef ->
classDef.endsWith("homeapi/proto/HomeStructure;") && method.indexOfFirstInstruction {
opcode == Opcode.IGET_OBJECT && getReference<FieldReference>()?.name == "sections_"
} >= 0
}
}
internal fun reactivexFunctionApplyWithClassInitFingerprint(className: String) = fingerprint {
returns("Ljava/lang/Object;")
parameters("Ljava/lang/Object;")
custom { method, _ -> method.name == "apply" && method.indexOfFirstInstruction {
opcode == Opcode.NEW_INSTANCE && getReference<TypeReference>()?.type?.endsWith(className) == true
} >= 0
}
}
internal const val PENDRAGON_JSON_FETCH_MESSAGE_REQUEST_CLASS_NAME = "FetchMessageRequest;"
internal val pendragonJsonFetchMessageRequestFingerprint =
reactivexFunctionApplyWithClassInitFingerprint(PENDRAGON_JSON_FETCH_MESSAGE_REQUEST_CLASS_NAME)
internal const val PENDRAGON_PROTO_FETCH_MESSAGE_LIST_REQUEST_CLASS_NAME = "FetchMessageListRequest;"
internal val pendragonProtoFetchMessageListRequestFingerprint =
reactivexFunctionApplyWithClassInitFingerprint(PENDRAGON_PROTO_FETCH_MESSAGE_LIST_REQUEST_CLASS_NAME)

View File

@@ -60,4 +60,4 @@ val spoofPackageInfoPatch = bytecodePatch(
// endregion
}
}
}
}

View File

@@ -0,0 +1,41 @@
package app.revanced.patches.spotify.misc.privacy
import app.revanced.patcher.fingerprint
import app.revanced.util.literal
import com.android.tools.smali.dexlib2.AccessFlags
internal val shareCopyUrlFingerprint = fingerprint {
returns("Ljava/lang/Object;")
parameters("Ljava/lang/Object;")
strings("clipboard", "Spotify Link")
custom { method, _ ->
method.name == "invokeSuspend"
}
}
internal val shareCopyUrlLegacyFingerprint = fingerprint {
returns("Ljava/lang/Object;")
parameters("Ljava/lang/Object;")
strings("clipboard", "createNewSession failed")
custom { method, _ ->
method.name == "apply"
}
}
internal val formatAndroidShareSheetUrlFingerprint = fingerprint {
accessFlags(AccessFlags.PUBLIC, AccessFlags.STATIC)
returns("Ljava/lang/String;")
parameters("L", "Ljava/lang/String;")
literal {
'\n'.code.toLong()
}
}
internal val formatAndroidShareSheetUrlLegacyFingerprint = fingerprint {
accessFlags(AccessFlags.PUBLIC)
returns("Ljava/lang/String;")
parameters("Lcom/spotify/share/social/sharedata/ShareData;", "Ljava/lang/String;")
literal {
'\n'.code.toLong()
}
}

View File

@@ -0,0 +1,70 @@
package app.revanced.patches.spotify.misc.privacy
import app.revanced.patcher.Fingerprint
import app.revanced.patcher.extensions.InstructionExtensions.addInstructions
import app.revanced.patcher.extensions.InstructionExtensions.getInstruction
import app.revanced.patcher.patch.bytecodePatch
import app.revanced.patches.spotify.misc.extension.IS_SPOTIFY_LEGACY_APP_TARGET
import app.revanced.patches.spotify.misc.extension.sharedExtensionPatch
import app.revanced.util.getReference
import app.revanced.util.indexOfFirstInstructionOrThrow
import com.android.tools.smali.dexlib2.iface.instruction.FiveRegisterInstruction
import com.android.tools.smali.dexlib2.iface.reference.MethodReference
private const val EXTENSION_CLASS_DESCRIPTOR =
"Lapp/revanced/extension/spotify/misc/privacy/SanitizeSharingLinksPatch;"
@Suppress("unused")
val sanitizeSharingLinksPatch = bytecodePatch(
name = "Sanitize sharing links",
description = "Removes the tracking query parameters from links before they are shared.",
) {
compatibleWith("com.spotify.music")
dependsOn(sharedExtensionPatch)
execute {
val extensionMethodDescriptor = "$EXTENSION_CLASS_DESCRIPTOR->" +
"sanitizeUrl(Ljava/lang/String;)Ljava/lang/String;"
val copyFingerprint = if (IS_SPOTIFY_LEGACY_APP_TARGET) {
shareCopyUrlLegacyFingerprint
} else {
shareCopyUrlFingerprint
}
copyFingerprint.method.apply {
val newPlainTextInvokeIndex = indexOfFirstInstructionOrThrow {
getReference<MethodReference>()?.name == "newPlainText"
}
val register = getInstruction<FiveRegisterInstruction>(newPlainTextInvokeIndex).registerD
addInstructions(
newPlainTextInvokeIndex,
"""
invoke-static { v$register }, $extensionMethodDescriptor
move-result-object v$register
"""
)
}
// Android native share sheet is used for all other quick share types (X, WhatsApp, etc).
val shareUrlParameter : String
val shareSheetFingerprint : Fingerprint
if (IS_SPOTIFY_LEGACY_APP_TARGET) {
shareSheetFingerprint = formatAndroidShareSheetUrlLegacyFingerprint
shareUrlParameter = "p2"
} else {
shareSheetFingerprint = formatAndroidShareSheetUrlFingerprint
shareUrlParameter = "p1"
}
shareSheetFingerprint.method.addInstructions(
0,
"""
invoke-static { $shareUrlParameter }, $extensionMethodDescriptor
move-result-object $shareUrlParameter
"""
)
}
}

View File

@@ -14,4 +14,4 @@ internal val mainActivityOnCreateFingerprint = fingerprint {
method.name == "onCreate" && (classDef.type == SPOTIFY_MAIN_ACTIVITY
|| classDef.type == SPOTIFY_MAIN_ACTIVITY_LEGACY)
}
}
}

View File

@@ -25,11 +25,6 @@ internal val shortsBottomBarContainerFingerprint = fingerprint {
literal { bottomBarContainer }
}
internal val createShortsButtonsFingerprint = fingerprint {
returns("V")
literal { reelPlayerRightCellButtonHeight }
}
internal val renderBottomNavigationBarFingerprint = fingerprint {
returns("V")
parameters("Ljava/lang/String;")

View File

@@ -6,7 +6,6 @@ import app.revanced.patcher.extensions.InstructionExtensions.getInstruction
import app.revanced.patcher.patch.booleanOption
import app.revanced.patcher.patch.bytecodePatch
import app.revanced.patcher.patch.resourcePatch
import app.revanced.patcher.util.proxy.mutableTypes.MutableMethod
import app.revanced.patches.all.misc.resources.addResources
import app.revanced.patches.all.misc.resources.addResourcesPatch
import app.revanced.patches.shared.misc.mapping.get
@@ -22,14 +21,14 @@ import app.revanced.patches.youtube.misc.playservice.is_19_41_or_greater
import app.revanced.patches.youtube.misc.playservice.versionCheckPatch
import app.revanced.patches.youtube.misc.settings.PreferenceScreen
import app.revanced.patches.youtube.misc.settings.settingsPatch
import app.revanced.util.*
import com.android.tools.smali.dexlib2.Opcode
import com.android.tools.smali.dexlib2.iface.instruction.FiveRegisterInstruction
import app.revanced.util.findElementByAttributeValueOrThrow
import app.revanced.util.forEachLiteralValueInstruction
import app.revanced.util.getReference
import app.revanced.util.indexOfFirstInstructionOrThrow
import app.revanced.util.indexOfFirstLiteralInstruction
import com.android.tools.smali.dexlib2.iface.instruction.OneRegisterInstruction
import com.android.tools.smali.dexlib2.iface.reference.MethodReference
internal var reelPlayerRightCellButtonHeight = -1L
private set
internal var bottomBarContainer = -1L
private set
internal var reelPlayerRightPivotV2Size = -1L
@@ -137,11 +136,6 @@ private val hideShortsComponentsResourcePatch = resourcePatch {
}
}
reelPlayerRightCellButtonHeight = resourceMappings[
"dimen",
"reel_player_right_cell_button_height",
]
bottomBarContainer = resourceMappings[
"id",
"bottom_bar_container",
@@ -186,15 +180,6 @@ val hideShortsComponentsPatch = bytecodePatch(
hideShortsWidgetOption()
execute {
// region Hide the Shorts buttons in older versions of YouTube.
// Some Shorts buttons are views, hide them by setting their visibility to GONE.
ShortsButtons.entries.forEach { button -> button.injectHideCall(createShortsButtonsFingerprint.method) }
// endregion
// region Hide the Shorts buttons in newer versions of YouTube.
addLithoFilter(FILTER_CLASS_DESCRIPTOR)
forEachLiteralValueInstruction(
@@ -211,7 +196,7 @@ val hideShortsComponentsPatch = bytecodePatch(
"""
invoke-static { v$sizeRegister }, $FILTER_CLASS_DESCRIPTOR->getSoundButtonSize(I)I
move-result v$sizeRegister
""",
"""
)
}
@@ -261,31 +246,10 @@ val hideShortsComponentsPatch = bytecodePatch(
"""
invoke-static { v$heightRegister }, $FILTER_CLASS_DESCRIPTOR->getNavigationBarHeight(I)I
move-result v$heightRegister
""",
"""
)
}
// endregion
}
}
private enum class ShortsButtons(private val resourceName: String, private val methodName: String) {
LIKE("reel_dyn_like", "hideLikeButton"),
DISLIKE("reel_dyn_dislike", "hideDislikeButton"),
COMMENTS("reel_dyn_comment", "hideShortsCommentsButton"),
REMIX("reel_dyn_remix", "hideShortsRemixButton"),
SHARE("reel_dyn_share", "hideShortsShareButton"),
;
fun injectHideCall(method: MutableMethod) {
val referencedIndex = method.indexOfFirstResourceIdOrThrow(resourceName)
val setIdIndex = method.indexOfFirstInstructionOrThrow(referencedIndex) {
opcode == Opcode.INVOKE_VIRTUAL && getReference<MethodReference>()?.name == "setId"
}
val viewRegister = method.getInstruction<FiveRegisterInstruction>(setIdIndex).registerC
method.injectHideViewCall(setIdIndex + 1, viewRegister, FILTER_CLASS_DESCRIPTOR, methodName)
}
}

View File

@@ -5,18 +5,6 @@ import app.revanced.util.literal
import com.android.tools.smali.dexlib2.AccessFlags
import com.android.tools.smali.dexlib2.Opcode
internal val conversionContextFingerprint = fingerprint {
returns("Ljava/lang/String;")
parameters()
strings(
", widthConstraint=",
", heightConstraint=",
", templateLoggerFactory=",
", rootDisposableContainer=",
"ConversionContext{containerInternal=",
)
}
internal val dislikeFingerprint = fingerprint {
returns("V")
strings("like/dislike")

View File

@@ -18,6 +18,7 @@ import app.revanced.patches.youtube.misc.playservice.versionCheckPatch
import app.revanced.patches.youtube.misc.settings.addSettingPreference
import app.revanced.patches.youtube.misc.settings.newIntent
import app.revanced.patches.youtube.misc.settings.settingsPatch
import app.revanced.patches.youtube.shared.conversionContextFingerprintToString
import app.revanced.patches.youtube.shared.rollingNumberTextViewAnimationUpdateFingerprint
import app.revanced.patches.youtube.video.videoid.hookPlayerResponseVideoId
import app.revanced.patches.youtube.video.videoid.hookVideoId
@@ -113,11 +114,11 @@ val returnYouTubeDislikePatch = bytecodePatch(
// This hook handles all situations, as it's where the created Spans are stored and later reused.
// Find the field name of the conversion context.
val conversionContextField = textComponentConstructorFingerprint.originalClassDef.fields.find {
it.type == conversionContextFingerprint.originalClassDef.type
it.type == conversionContextFingerprintToString.originalClassDef.type
} ?: throw PatchException("Could not find conversion context field")
textComponentLookupFingerprint.match(textComponentConstructorFingerprint.originalClassDef)
textComponentLookupFingerprint.method.apply {
.method.apply {
// Find the instruction for creating the text data object.
val textDataClassType = textComponentDataFingerprint.originalClassDef.type
@@ -160,12 +161,12 @@ val returnYouTubeDislikePatch = bytecodePatch(
addInstructionsAtControlFlowLabel(
insertIndex,
"""
# Copy conversion context
move-object/from16 v$tempRegister, p0
iget-object v$tempRegister, v$tempRegister, $conversionContextField
invoke-static { v$tempRegister, v$charSequenceRegister }, $EXTENSION_CLASS_DESCRIPTOR->onLithoTextLoaded(Ljava/lang/Object;Ljava/lang/CharSequence;)Ljava/lang/CharSequence;
move-result-object v$charSequenceRegister
""",
# Copy conversion context
move-object/from16 v$tempRegister, p0
iget-object v$tempRegister, v$tempRegister, $conversionContextField
invoke-static { v$tempRegister, v$charSequenceRegister }, $EXTENSION_CLASS_DESCRIPTOR->onLithoTextLoaded(Ljava/lang/Object;Ljava/lang/CharSequence;)Ljava/lang/CharSequence;
move-result-object v$charSequenceRegister
"""
)
}
@@ -201,11 +202,9 @@ val returnYouTubeDislikePatch = bytecodePatch(
val charSequenceFieldReference =
getInstruction<ReferenceInstruction>(dislikesIndex).reference
val registerCount = implementation!!.registerCount
val conversionContextRegister = implementation!!.registerCount - parameters.size + 1
// This register is being overwritten, so it is free to use.
val freeRegister = registerCount - 1
val conversionContextRegister = registerCount - parameters.size + 1
val freeRegister = findFreeRegister(insertIndex, charSequenceInstanceRegister, conversionContextRegister)
addInstructions(
insertIndex,

View File

@@ -1,8 +1,13 @@
package app.revanced.patches.youtube.layout.shortsautoplay
import app.revanced.patcher.fingerprint
import app.revanced.util.getReference
import app.revanced.util.indexOfFirstInstruction
import com.android.tools.smali.dexlib2.AccessFlags
import com.android.tools.smali.dexlib2.Opcode
import com.android.tools.smali.dexlib2.iface.Method
import com.android.tools.smali.dexlib2.iface.reference.FieldReference
import com.android.tools.smali.dexlib2.iface.reference.MethodReference
internal val reelEnumConstructorFingerprint = fingerprint {
accessFlags(AccessFlags.STATIC, AccessFlags.CONSTRUCTOR)
@@ -20,3 +25,27 @@ internal val reelPlaybackRepeatFingerprint = fingerprint {
parameters("L")
strings("YoutubePlayerState is in throwing an Error.")
}
internal val reelPlaybackFingerprint = fingerprint {
accessFlags(AccessFlags.PUBLIC, AccessFlags.FINAL)
returns("V")
parameters("J")
custom { method, _ ->
indexOfMilliSecondsInstruction(method) >= 0 &&
indexOfInitializationInstruction(method) >= 0
}
}
private fun indexOfMilliSecondsInstruction(method: Method) =
method.indexOfFirstInstruction {
getReference<FieldReference>()?.name == "MILLISECONDS"
}
internal fun indexOfInitializationInstruction(method: Method) =
method.indexOfFirstInstruction {
val reference = getReference<MethodReference>()
opcode == Opcode.INVOKE_DIRECT &&
reference?.name == "<init>" &&
reference.parameterTypes.size == 3 &&
reference.parameterTypes.firstOrNull() == "I"
}

View File

@@ -2,21 +2,32 @@ package app.revanced.patches.youtube.layout.shortsautoplay
import app.revanced.patcher.extensions.InstructionExtensions.addInstruction
import app.revanced.patcher.extensions.InstructionExtensions.addInstructions
import app.revanced.patcher.extensions.InstructionExtensions.addInstructionsWithLabels
import app.revanced.patcher.extensions.InstructionExtensions.getInstruction
import app.revanced.patcher.patch.bytecodePatch
import app.revanced.patcher.util.proxy.mutableTypes.MutableMethod.Companion.toMutable
import app.revanced.patches.all.misc.resources.addResources
import app.revanced.patches.shared.misc.mapping.resourceMappingPatch
import app.revanced.patches.shared.misc.settings.preference.SwitchPreference
import app.revanced.patches.youtube.misc.extension.sharedExtensionPatch
import app.revanced.patches.youtube.misc.playservice.is_19_34_or_greater
import app.revanced.patches.youtube.misc.playservice.is_20_09_or_greater
import app.revanced.patches.youtube.misc.playservice.versionCheckPatch
import app.revanced.patches.youtube.misc.settings.PreferenceScreen
import app.revanced.patches.youtube.misc.settings.settingsPatch
import app.revanced.patches.youtube.shared.mainActivityOnCreateFingerprint
import app.revanced.util.findInstructionIndicesReversedOrThrow
import app.revanced.util.getReference
import app.revanced.util.indexOfFirstInstructionOrThrow
import com.android.tools.smali.dexlib2.AccessFlags
import com.android.tools.smali.dexlib2.Opcode
import com.android.tools.smali.dexlib2.builder.MutableMethodImplementation
import com.android.tools.smali.dexlib2.iface.instruction.OneRegisterInstruction
import com.android.tools.smali.dexlib2.iface.instruction.ReferenceInstruction
import com.android.tools.smali.dexlib2.iface.reference.FieldReference
import com.android.tools.smali.dexlib2.iface.reference.MethodReference
import com.android.tools.smali.dexlib2.immutable.ImmutableMethod
import com.android.tools.smali.dexlib2.immutable.ImmutableMethodParameter
private const val EXTENSION_CLASS_DESCRIPTOR = "Lapp/revanced/extension/youtube/patches/ShortsAutoplayPatch;"
@@ -98,5 +109,84 @@ val shortsAutoplayPatch = bytecodePatch(
)
}
}
// As of YouTube 20.09, Google has removed the code for 'Autoplay' and 'Pause' from this method.
// Manually restore the removed 'Autoplay' code.
if (is_20_09_or_greater) {
// Variable names are only a rough guess of what these methods do.
val userActionMethodIndex = indexOfInitializationInstruction(reelPlaybackFingerprint.method)
val userActionMethodReference = reelPlaybackFingerprint.method
.getInstruction<ReferenceInstruction>(userActionMethodIndex).reference as MethodReference
val reelSequenceControllerMethodIndex = reelPlaybackFingerprint.method
.indexOfFirstInstructionOrThrow(userActionMethodIndex, Opcode.INVOKE_VIRTUAL)
val reelSequenceControllerMethodReference = reelPlaybackFingerprint.method
.getInstruction<ReferenceInstruction>(reelSequenceControllerMethodIndex).reference as MethodReference
reelPlaybackRepeatFingerprint.method.apply {
// Find the first call modified by extension code above.
val extensionReturnResultIndex = indexOfFirstInstructionOrThrow {
opcode == Opcode.INVOKE_STATIC &&
getReference<MethodReference>()?.definingClass == EXTENSION_CLASS_DESCRIPTOR
} + 1
val enumRegister = getInstruction<OneRegisterInstruction>(extensionReturnResultIndex).registerA
val getReelSequenceControllerIndex = indexOfFirstInstructionOrThrow(extensionReturnResultIndex) {
val reference = getReference<FieldReference>()
opcode == Opcode.IGET_OBJECT &&
reference?.definingClass == definingClass &&
reference.type == reelSequenceControllerMethodReference.definingClass
}
val getReelSequenceControllerReference =
getInstruction<ReferenceInstruction>(getReelSequenceControllerIndex).reference
// Add a helper method to avoid finding multiple free registers.
// If enum is autoplay then method performs autoplay and returns null,
// otherwise returns the same enum.
val helperClass = definingClass
val helperName = "patch_handleAutoPlay"
val helperReturnType = "Ljava/lang/Enum;"
val helperMethod = ImmutableMethod(
helperClass,
helperName,
listOf(ImmutableMethodParameter("Ljava/lang/Enum;", null, null)),
helperReturnType,
AccessFlags.PRIVATE.value,
null,
null,
MutableMethodImplementation(7),
).toMutable().apply {
addInstructionsWithLabels(
0,
"""
invoke-static { p1 }, $EXTENSION_CLASS_DESCRIPTOR->isAutoPlay(Ljava/lang/Enum;)Z
move-result v0
if-eqz v0, :ignore
new-instance v0, ${userActionMethodReference.definingClass}
const/4 v1, 0x3
const/4 v2, 0x0
invoke-direct { v0, v1, v2, v2 }, $userActionMethodReference
iget-object v3, p0, $getReelSequenceControllerReference
invoke-virtual { v3, v0 }, $reelSequenceControllerMethodReference
const/4 v4, 0x0
return-object v4
:ignore
return-object p1
"""
)
}
reelPlaybackRepeatFingerprint.classDef.methods.add(helperMethod)
addInstructionsWithLabels(
extensionReturnResultIndex + 1,
"""
invoke-direct { p0, v$enumRegister }, $helperClass->$helperName(Ljava/lang/Enum;)$helperReturnType
move-result-object v$enumRegister
if-nez v$enumRegister, :ignore
return-void # Autoplay was performed.
:ignore
nop
"""
)
}
}
}
}

View File

@@ -0,0 +1,83 @@
package app.revanced.patches.youtube.misc.gms
import app.revanced.patcher.extensions.InstructionExtensions.addInstructions
import app.revanced.patcher.extensions.InstructionExtensions.getInstruction
import app.revanced.patcher.patch.bytecodePatch
import app.revanced.patcher.patch.resourcePatch
import app.revanced.patches.all.misc.resources.addResources
import app.revanced.patches.all.misc.resources.addResourcesPatch
import app.revanced.patches.shared.misc.mapping.get
import app.revanced.patches.shared.misc.mapping.resourceMappings
import app.revanced.patches.youtube.misc.extension.sharedExtensionPatch
import app.revanced.util.getReference
import app.revanced.util.indexOfFirstInstructionOrThrow
import app.revanced.util.indexOfFirstLiteralInstructionOrThrow
import com.android.tools.smali.dexlib2.iface.instruction.OneRegisterInstruction
import com.android.tools.smali.dexlib2.iface.reference.MethodReference
private const val EXTENSION_CLASS_DESCRIPTOR =
"Lapp/revanced/extension/youtube/patches/AccountCredentialsInvalidTextPatch;"
internal var ic_offline_no_content_upside_down = -1L
private set
internal var offline_no_content_body_text_not_offline_eligible = -1L
private set
private val accountCredentialsInvalidTextResourcePatch = resourcePatch {
execute {
ic_offline_no_content_upside_down = resourceMappings[
"drawable",
"ic_offline_no_content_upside_down"
]
offline_no_content_body_text_not_offline_eligible = resourceMappings[
"string",
"offline_no_content_body_text_not_offline_eligible"
]
}
}
internal val accountCredentialsInvalidTextPatch = bytecodePatch {
dependsOn(
sharedExtensionPatch,
accountCredentialsInvalidTextResourcePatch,
addResourcesPatch
)
execute {
addResources("youtube", "misc.gms.accountCredentialsInvalidTextPatch")
// If the user recently changed their account password,
// the app can show "You're offline. Check your internet connection."
// even when the internet is available. For this situation
// YouTube + MicroG shows an offline error message.
//
// Change the error text to inform the user to uninstall and reinstall MicroG.
// The user can also fix this by deleting the MicroG account but
// MicroG accounts look almost identical to Google device accounts
// and it's more foolproof to instead uninstall/reinstall.
arrayOf(
specificNetworkErrorViewControllerFingerprint,
loadingFrameLayoutControllerFingerprint
).forEach { fingerprint ->
fingerprint.method.apply {
val resourceIndex = indexOfFirstLiteralInstructionOrThrow(
offline_no_content_body_text_not_offline_eligible
)
val getStringIndex = indexOfFirstInstructionOrThrow(resourceIndex) {
val reference = getReference<MethodReference>()
reference?.name == "getString"
}
val register = getInstruction<OneRegisterInstruction>(getStringIndex + 1).registerA
addInstructions(
getStringIndex + 2,
"""
invoke-static { v$register }, $EXTENSION_CLASS_DESCRIPTOR->getOfflineNetworkErrorString(Ljava/lang/String;)Ljava/lang/String;
move-result-object v$register
"""
)
}
}
}
}

View File

@@ -0,0 +1,27 @@
package app.revanced.patches.youtube.misc.gms
import app.revanced.patcher.fingerprint
import app.revanced.util.containsLiteralInstruction
import com.android.tools.smali.dexlib2.AccessFlags
internal val specificNetworkErrorViewControllerFingerprint = fingerprint {
accessFlags(AccessFlags.PUBLIC, AccessFlags.FINAL)
returns("V")
parameters()
custom { method, _ ->
method.containsLiteralInstruction(ic_offline_no_content_upside_down)
&& method.containsLiteralInstruction(offline_no_content_body_text_not_offline_eligible)
}
}
// It's not clear if this second class is ever used and it may be dead code,
// but it the layout image/text is identical to the network error fingerprint above.
internal val loadingFrameLayoutControllerFingerprint = fingerprint {
accessFlags(AccessFlags.PUBLIC, AccessFlags.FINAL)
returns("V")
parameters("L")
custom { method, _ ->
method.containsLiteralInstruction(ic_offline_no_content_upside_down)
&& method.containsLiteralInstruction(offline_no_content_body_text_not_offline_eligible)
}
}

View File

@@ -68,5 +68,5 @@ private fun gmsCoreSupportResourcePatch(
)
},
) {
dependsOn(settingsPatch, addResourcesPatch)
dependsOn(settingsPatch, addResourcesPatch, accountCredentialsInvalidTextPatch)
}

View File

@@ -5,10 +5,6 @@ import app.revanced.util.literal
import com.android.tools.smali.dexlib2.AccessFlags
import com.android.tools.smali.dexlib2.Opcode
/**
* In 19.17 and earlier, this resolves to the same method as [readComponentIdentifierFingerprint].
* In 19.18+ this resolves to a different method.
*/
internal val componentContextParserFingerprint = fingerprint {
strings(
"TreeNode result must be set.",
@@ -17,11 +13,21 @@ internal val componentContextParserFingerprint = fingerprint {
)
}
/**
* Resolves to the class found in [componentContextParserFingerprint].
* When patching 19.16 this fingerprint matches the same method as [componentContextParserFingerprint].
*/
internal val componentContextSubParserFingerprint = fingerprint {
strings(
"Number of bits must be positive"
)
}
internal val lithoFilterFingerprint = fingerprint {
accessFlags(AccessFlags.STATIC, AccessFlags.CONSTRUCTOR)
returns("V")
custom { _, classDef ->
classDef.endsWith("LithoFilterPatch;")
classDef.endsWith("/LithoFilterPatch;")
}
}
@@ -37,14 +43,6 @@ internal val protobufBufferReferenceFingerprint = fingerprint {
)
}
/**
* In 19.17 and earlier, this resolves to the same method as [componentContextParserFingerprint].
* In 19.18+ this resolves to a different method.
*/
internal val readComponentIdentifierFingerprint = fingerprint {
strings("Number of bits must be positive")
}
internal val emptyComponentFingerprint = fingerprint {
accessFlags(AccessFlags.PRIVATE, AccessFlags.CONSTRUCTOR)
parameters()

View File

@@ -4,25 +4,25 @@ package app.revanced.patches.youtube.misc.litho.filter
import app.revanced.patcher.extensions.InstructionExtensions.addInstruction
import app.revanced.patcher.extensions.InstructionExtensions.addInstructions
import app.revanced.patcher.extensions.InstructionExtensions.addInstructionsWithLabels
import app.revanced.patcher.extensions.InstructionExtensions.getInstruction
import app.revanced.patcher.extensions.InstructionExtensions.removeInstructions
import app.revanced.patcher.extensions.InstructionExtensions.replaceInstruction
import app.revanced.patcher.patch.bytecodePatch
import app.revanced.patcher.util.smali.ExternalLabel
import app.revanced.patches.youtube.misc.extension.sharedExtensionPatch
import app.revanced.patches.youtube.misc.playservice.is_19_18_or_greater
import app.revanced.patches.youtube.misc.playservice.is_19_25_or_greater
import app.revanced.patches.youtube.misc.playservice.is_20_05_or_greater
import app.revanced.patches.youtube.misc.playservice.versionCheckPatch
import app.revanced.patches.youtube.shared.conversionContextFingerprintToString
import app.revanced.util.addInstructionsAtControlFlowLabel
import app.revanced.util.findFreeRegister
import app.revanced.util.findInstructionIndicesReversedOrThrow
import app.revanced.util.getReference
import app.revanced.util.indexOfFirstInstructionOrThrow
import app.revanced.util.indexOfFirstInstructionReversedOrThrow
import com.android.tools.smali.dexlib2.AccessFlags
import com.android.tools.smali.dexlib2.Opcode
import com.android.tools.smali.dexlib2.iface.instruction.OneRegisterInstruction
import com.android.tools.smali.dexlib2.iface.instruction.TwoRegisterInstruction
import com.android.tools.smali.dexlib2.iface.instruction.ReferenceInstruction
import com.android.tools.smali.dexlib2.iface.reference.FieldReference
import com.android.tools.smali.dexlib2.iface.reference.MethodReference
@@ -53,42 +53,33 @@ val lithoFilterPatch = bytecodePatch(
* The buffer is a large byte array that represents the component tree.
* This byte array is searched for strings that indicate the current component.
*
* The following pseudocode shows how the patch works:
* All modifications done here must allow all the original code to still execute
* even when filtering, otherwise memory leaks or poor app performance may occur.
*
* The following pseudocode shows how this patch works:
*
* class SomeOtherClass {
* // Called before ComponentContextParser.parseBytesToComponentContext method.
* // Called before ComponentContextParser.parseComponent() method.
* public void someOtherMethod(ByteBuffer byteBuffer) {
* ExtensionClass.setProtoBuffer(byteBuffer); // Inserted by this patch.
* ...
* }
* }
*
* When patching 19.17 and earlier:
*
* class ComponentContextParser {
* public ComponentContext ReadComponentIdentifierFingerprint(...) {
* public Component parseComponent() {
* ...
* if (extensionClass.filter(identifier, pathBuilder)); // Inserted by this patch.
*
* // Checks if the component should be filtered.
* // Sets a thread local with the filtering result.
* extensionClass.filter(identifier, pathBuilder); // Inserted by this patch.
*
* ...
*
* if (extensionClass.shouldFilter()) { // Inserted by this patch.
* return emptyComponent;
* ...
* }
* }
*
* When patching 19.18 and later:
*
* class ComponentContextParser {
* public ComponentContext parseBytesToComponentContext(...) {
* ...
* if (ReadComponentIdentifierFingerprint() == null); // Inserted by this patch.
* return emptyComponent;
* ...
* }
*
* public ComponentIdentifierObj readComponentIdentifier(...) {
* ...
* if (extensionClass.filter(identifier, pathBuilder)); // Inserted by this patch.
* return null;
* ...
* }
* return originalUnpatchedComponent; // Original code.
* }
* }
*/
@@ -103,7 +94,7 @@ val lithoFilterPatch = bytecodePatch(
2,
"""
new-instance v1, $classDescriptor
invoke-direct {v1}, $classDescriptor-><init>()V
invoke-direct { v1 }, $classDescriptor-><init>()V
const/16 v2, ${filterCount++}
aput-object v1, v0, v2
""",
@@ -115,110 +106,105 @@ val lithoFilterPatch = bytecodePatch(
protobufBufferReferenceFingerprint.method.addInstruction(
0,
" invoke-static { p2 }, $EXTENSION_CLASS_DESCRIPTOR->setProtoBuffer(Ljava/nio/ByteBuffer;)V",
"invoke-static { p2 }, $EXTENSION_CLASS_DESCRIPTOR->setProtoBuffer(Ljava/nio/ByteBuffer;)V",
)
// endregion
// region Hook the method that parses bytes into a ComponentContext.
val readComponentMethod = readComponentIdentifierFingerprint.originalMethod
// Get the only static method in the class.
val builderMethodDescriptor = emptyComponentFingerprint.classDef.methods.first { method ->
AccessFlags.STATIC.isSet(method.accessFlags)
}
// Only one field.
val emptyComponentField = classBy { classDef ->
builderMethodDescriptor.returnType == classDef.type
}!!.immutableClass.fields.single()
// Returns an empty component instead of the original component.
fun createReturnEmptyComponentInstructions(register: Int): String =
"""
move-object/from16 v$register, p1
invoke-static { v$register }, $builderMethodDescriptor
move-result-object v$register
iget-object v$register, v$register, $emptyComponentField
return-object v$register
"""
// Allow the method to run to completion, and override the
// return value with an empty component if it should be filtered.
// It is important to allow the original code to always run to completion,
// otherwise memory leaks and poor app performance can occur.
//
// The extension filtering result needs to be saved off somewhere, but cannot
// save to a class field since the target class is called by multiple threads.
// It would be great if there was a way to change the register count of the
// method implementation and save the result to a high register to later use
// in the method, but there is no simple way to do that.
// Instead save the extension filter result to a thread local and check the
// filtering result at each method return index.
// String field for the litho identifier.
componentContextParserFingerprint.method.apply {
// 19.18 and later require patching 2 methods instead of one.
// Otherwise the modifications done here are the same for all targets.
if (is_19_18_or_greater) {
// Get the method name of the ReadComponentIdentifierFingerprint call.
val readComponentMethodCallIndex = indexOfFirstInstructionOrThrow {
val reference = getReference<MethodReference>()
reference?.definingClass == readComponentMethod.definingClass &&
reference.name == readComponentMethod.name
val conversionContextClass = conversionContextFingerprintToString.originalClassDef
val conversionContextIdentifierField = componentContextSubParserFingerprint.match(
componentContextParserFingerprint.originalClassDef
).let {
// Identifier field is loaded just before the string declaration.
val index = it.method.indexOfFirstInstructionReversedOrThrow(
it.stringMatches!!.first().index
) {
val reference = getReference<FieldReference>()
reference?.definingClass == conversionContextClass.type
&& reference.type == "Ljava/lang/String;"
}
// Result of read component, and also a free register.
val register = getInstruction<OneRegisterInstruction>(readComponentMethodCallIndex + 1).registerA
// Insert after 'move-result-object'
val insertHookIndex = readComponentMethodCallIndex + 2
// Return an EmptyComponent instead of the original component if the filterState method returns true.
addInstructionsWithLabels(
insertHookIndex,
"""
if-nez v$register, :unfiltered
# Component was filtered in ReadComponentIdentifierFingerprint hook
${createReturnEmptyComponentInstructions(register)}
""",
ExternalLabel("unfiltered", getInstruction(insertHookIndex)),
)
it.method.getInstruction<ReferenceInstruction>(index).getReference<FieldReference>()
}
}
// endregion
// StringBuilder field for the litho path.
val conversionContextPathBuilderField = conversionContextClass.fields
.single { field -> field.type == "Ljava/lang/StringBuilder;" }
// region Read component then store the result.
val conversionContextResultIndex = indexOfFirstInstructionOrThrow {
val reference = getReference<MethodReference>()
reference?.returnType == conversionContextClass.type
} + 1
readComponentIdentifierFingerprint.method.apply {
val insertHookIndex = indexOfFirstInstructionOrThrow {
opcode == Opcode.IPUT_OBJECT &&
getReference<FieldReference>()?.type == "Ljava/lang/StringBuilder;"
}
val stringBuilderRegister = getInstruction<TwoRegisterInstruction>(insertHookIndex).registerA
// Identifier is saved to a field just before the string builder.
val identifierRegister = getInstruction<TwoRegisterInstruction>(
indexOfFirstInstructionReversedOrThrow(insertHookIndex) {
opcode == Opcode.IPUT_OBJECT &&
getReference<FieldReference>()?.type == "Ljava/lang/String;"
},
val conversionContextResultRegister = getInstruction<OneRegisterInstruction>(
conversionContextResultIndex
).registerA
val freeRegister = findFreeRegister(insertHookIndex, identifierRegister, stringBuilderRegister)
val invokeFilterInstructions = """
invoke-static { v$identifierRegister, v$stringBuilderRegister }, $EXTENSION_CLASS_DESCRIPTOR->filter(Ljava/lang/String;Ljava/lang/StringBuilder;)Z
move-result v$freeRegister
if-eqz v$freeRegister, :unfiltered
"""
addInstructionsWithLabels(
insertHookIndex,
if (is_19_18_or_greater) {
"""
$invokeFilterInstructions
# Return null, and the ComponentContextParserFingerprint hook
# handles returning an empty component.
const/4 v$freeRegister, 0x0
return-object v$freeRegister
"""
} else {
"""
$invokeFilterInstructions
${createReturnEmptyComponentInstructions(freeRegister)}
"""
},
ExternalLabel("unfiltered", getInstruction(insertHookIndex)),
val identifierRegister = findFreeRegister(
conversionContextResultIndex, conversionContextResultRegister
)
val stringBuilderRegister = findFreeRegister(
conversionContextResultIndex, conversionContextResultRegister, identifierRegister
)
// Check if the component should be filtered, and save the result to a thread local.
addInstructionsAtControlFlowLabel(
conversionContextResultIndex + 1,
"""
iget-object v$identifierRegister, v$conversionContextResultRegister, $conversionContextIdentifierField
iget-object v$stringBuilderRegister, v$conversionContextResultRegister, $conversionContextPathBuilderField
invoke-static { v$identifierRegister, v$stringBuilderRegister }, $EXTENSION_CLASS_DESCRIPTOR->filter(Ljava/lang/String;Ljava/lang/StringBuilder;)V
"""
)
// Get the only static method in the class.
val builderMethodDescriptor = emptyComponentFingerprint.classDef.methods.single {
method -> AccessFlags.STATIC.isSet(method.accessFlags)
}
// Only one field.
val emptyComponentField = classBy { classDef ->
classDef.type == builderMethodDescriptor.returnType
}!!.immutableClass.fields.single()
// Check at each return value if the component is filtered,
// and return an empty component if filtering is needed.
findInstructionIndicesReversedOrThrow(Opcode.RETURN_OBJECT).forEach { returnIndex ->
val freeRegister = findFreeRegister(returnIndex)
addInstructionsAtControlFlowLabel(
returnIndex,
"""
invoke-static { }, $EXTENSION_CLASS_DESCRIPTOR->shouldFilter()Z
move-result v$freeRegister
if-eqz v$freeRegister, :unfiltered
move-object/from16 v$freeRegister, p1
invoke-static { v$freeRegister }, $builderMethodDescriptor
move-result-object v$freeRegister
iget-object v$freeRegister, v$freeRegister, $emptyComponentField
return-object v$freeRegister
:unfiltered
nop
"""
)
}
}
// endregion

View File

@@ -4,6 +4,21 @@ import app.revanced.patcher.fingerprint
import com.android.tools.smali.dexlib2.AccessFlags
import com.android.tools.smali.dexlib2.Opcode
internal val conversionContextFingerprintToString = fingerprint {
parameters()
strings(
"ConversionContext{containerInternal=",
", widthConstraint=",
", heightConstraint=",
", templateLoggerFactory=",
", rootDisposableContainer=",
", identifierProperty="
)
custom { method, _ ->
method.name == "toString"
}
}
internal val autoRepeatFingerprint = fingerprint {
accessFlags(AccessFlags.PUBLIC, AccessFlags.FINAL)
returns("V")

View File

@@ -11,12 +11,14 @@ import app.revanced.patcher.patch.BytecodePatchContext
import app.revanced.patcher.patch.PatchException
import app.revanced.patcher.util.proxy.mutableTypes.MutableClass
import app.revanced.patcher.util.proxy.mutableTypes.MutableMethod
import app.revanced.patcher.util.smali.ExternalLabel
import app.revanced.patches.shared.misc.mapping.get
import app.revanced.patches.shared.misc.mapping.resourceMappingPatch
import app.revanced.patches.shared.misc.mapping.resourceMappings
import app.revanced.util.InstructionUtils.Companion.branchOpcodes
import app.revanced.util.InstructionUtils.Companion.returnOpcodes
import app.revanced.util.InstructionUtils.Companion.writeOpcodes
import com.android.tools.smali.dexlib2.AccessFlags
import com.android.tools.smali.dexlib2.Opcode
import com.android.tools.smali.dexlib2.Opcode.*
import com.android.tools.smali.dexlib2.iface.Method
@@ -168,6 +170,15 @@ internal val Instruction.isBranchInstruction: Boolean
internal val Instruction.isReturnInstruction: Boolean
get() = this.opcode in returnOpcodes
/**
* Adds public [AccessFlags] and removes private and protected flags (if present).
*/
internal fun Int.toPublicAccessFlags() : Int {
return this.or(AccessFlags.PUBLIC.value)
.and(AccessFlags.PROTECTED.value.inv())
.and(AccessFlags.PRIVATE.value.inv())
}
/**
* Find the [MutableMethod] from a given [Method] in a [MutableClass].
*
@@ -207,6 +218,26 @@ fun MutableMethod.injectHideViewCall(
"invoke-static { v$viewRegister }, $classDescriptor->$targetMethod(Landroid/view/View;)V",
)
/**
* Inserts instructions at a given index, using the existing control flow label at that index.
* Inserted instructions can have it's own control flow labels as well.
*
* Effectively this changes the code from:
* :label
* (original code)
*
* Into:
* :label
* (patch code)
* (original code)
*/
// TODO: delete this on next major version bump.
fun MutableMethod.addInstructionsAtControlFlowLabel(
insertIndex: Int,
instructions: String
) = addInstructionsAtControlFlowLabel(insertIndex, instructions, *arrayOf<ExternalLabel>())
/**
* Inserts instructions at a given index, using the existing control flow label at that index.
* Inserted instructions can have it's own control flow labels as well.
@@ -223,13 +254,14 @@ fun MutableMethod.injectHideViewCall(
fun MutableMethod.addInstructionsAtControlFlowLabel(
insertIndex: Int,
instructions: String,
vararg externalLabels: ExternalLabel
) {
// Duplicate original instruction and add to +1 index.
addInstruction(insertIndex + 1, getInstruction(insertIndex))
// Add patch code at same index as duplicated instruction,
// so it uses the original instruction control flow label.
addInstructionsWithLabels(insertIndex + 1, instructions)
addInstructionsWithLabels(insertIndex + 1, instructions, *externalLabels)
// Remove original non duplicated instruction.
removeInstruction(insertIndex)

View File

@@ -197,6 +197,8 @@ Second \"item\" text"</string>
</patch>
<patch id="misc.gms.gmsCoreSupportResourcePatch">
</patch>
<patch id="misc.gms.accountCredentialsInvalidTextPatch">
</patch>
<patch id="misc.links.bypassURLRedirectsPatch">
</patch>
<patch id="misc.links.openLinksExternallyPatch">

View File

@@ -197,6 +197,8 @@ Second \"item\" text"</string>
</patch>
<patch id="misc.gms.gmsCoreSupportResourcePatch">
</patch>
<patch id="misc.gms.accountCredentialsInvalidTextPatch">
</patch>
<patch id="misc.links.bypassURLRedirectsPatch">
</patch>
<patch id="misc.links.openLinksExternallyPatch">

View File

@@ -1311,6 +1311,9 @@ Second \"item\" text"</string>
<string name="microg_settings_title">إعدادات GmsCore</string>
<string name="microg_settings_summary">إعدادات لـ GmsCore</string>
</patch>
<patch id="misc.gms.accountCredentialsInvalidTextPatch">
<string name="microg_offline_account_login_error">إذا قمت مؤخرًا بتغيير تفاصيل تسجيل الدخول إلى حسابك، فأزل تثبيت MicroG ثم أعد تثبيته.</string>
</patch>
<patch id="misc.links.bypassURLRedirectsPatch">
<string name="revanced_bypass_url_redirects_title">تجاوز إعادة توجيه URL</string>
<string name="revanced_bypass_url_redirects_summary_on">تم تجاوز إعادة توجيه عنوان URL</string>

View File

@@ -199,6 +199,8 @@ Second \"item\" text"</string>
</patch>
<patch id="misc.gms.gmsCoreSupportResourcePatch">
</patch>
<patch id="misc.gms.accountCredentialsInvalidTextPatch">
</patch>
<patch id="misc.links.bypassURLRedirectsPatch">
</patch>
<patch id="misc.links.openLinksExternallyPatch">

View File

@@ -1310,6 +1310,9 @@ Bunu aktivləşdirmə daha yüksək video keyfiyyətləri əngəlin silə bilər
<string name="microg_settings_title">GmsCore Tənzimləmələri</string>
<string name="microg_settings_summary">GmsCore üçün Tənzimləmələr</string>
</patch>
<patch id="misc.gms.accountCredentialsInvalidTextPatch">
<string name="microg_offline_account_login_error">Bu yaxınlarda hesabınıza giriş məlumatlarınızı dəyişmisinizsə, MicroG-ni silin və təkrar quraşdırın.</string>
</patch>
<patch id="misc.links.bypassURLRedirectsPatch">
<string name="revanced_bypass_url_redirects_title">URL yönləndirmələrini ötür</string>
<string name="revanced_bypass_url_redirects_summary_on">URL yönləndirmələri ötürülür</string>

View File

@@ -1312,6 +1312,9 @@ Second \"item\" text"</string>
<string name="microg_settings_title">Налады GmsCore</string>
<string name="microg_settings_summary">Налады для GmsCore</string>
</patch>
<patch id="misc.gms.accountCredentialsInvalidTextPatch">
<string name="microg_offline_account_login_error">Калі вы нядаўна змянілі даныя для ўваходу ў свой уліковы запіс, выдаліце і пераўсталюйце MicroG.</string>
</patch>
<patch id="misc.links.bypassURLRedirectsPatch">
<string name="revanced_bypass_url_redirects_title">Абыход URL-перанакіраванняў</string>
<string name="revanced_bypass_url_redirects_summary_on">Перанакіраванне URL абыходзіць</string>

View File

@@ -1311,6 +1311,9 @@ Second \"item\" text"</string>
<string name="microg_settings_title">GmsCore Настройки</string>
<string name="microg_settings_summary">Настройки на GmsCore</string>
</patch>
<patch id="misc.gms.accountCredentialsInvalidTextPatch">
<string name="microg_offline_account_login_error">Ако наскоро сте променили данните си за вход в профила, деинсталирайте и инсталирайте отново MicroG.</string>
</patch>
<patch id="misc.links.bypassURLRedirectsPatch">
<string name="revanced_bypass_url_redirects_title">Заобикаляне на URL пренасочване</string>
<string name="revanced_bypass_url_redirects_summary_on">URL пренасочванията се заобикалят</string>

View File

@@ -1311,6 +1311,9 @@ DeArrow সম্পর্কে আরও জানতে এখানে ট
<string name="microg_settings_title">GmsCore সেটিং</string>
<string name="microg_settings_summary">GmsCore এর জন্য সেটিং</string>
</patch>
<patch id="misc.gms.accountCredentialsInvalidTextPatch">
<string name="microg_offline_account_login_error">আপনি যদি সম্প্রতি আপনার অ্যাকাউন্ট লগইন বিশদ পরিবর্তন করে থাকেন, তবে MicroG আনইনস্টল করুন এবং পুনরায় ইনস্টল করুন।</string>
</patch>
<patch id="misc.links.bypassURLRedirectsPatch">
<string name="revanced_bypass_url_redirects_title">URL পুনঃনির্দেশ বাইপাস করুন</string>
<string name="revanced_bypass_url_redirects_summary_on">URL পুনঃনির্দেশ বাইপাস করছে</string>

View File

@@ -197,6 +197,8 @@ Second \"item\" text"</string>
</patch>
<patch id="misc.gms.gmsCoreSupportResourcePatch">
</patch>
<patch id="misc.gms.accountCredentialsInvalidTextPatch">
</patch>
<patch id="misc.links.bypassURLRedirectsPatch">
</patch>
<patch id="misc.links.openLinksExternallyPatch">

View File

@@ -1310,6 +1310,9 @@ Si actives aquesta opció, es poden desbloquejar qualitats de vídeo més altes"
<string name="microg_settings_title">Configuració de GmsCore</string>
<string name="microg_settings_summary">Configuració de GmsCore</string>
</patch>
<patch id="misc.gms.accountCredentialsInvalidTextPatch">
<string name="microg_offline_account_login_error">Si recentment heu canviat les dades d\'inici de sessió del vostre compte, desinstal·leu i torneu a instal·lar MicroG.</string>
</patch>
<patch id="misc.links.bypassURLRedirectsPatch">
<string name="revanced_bypass_url_redirects_title">Omet les redireccions d\'URL</string>
<string name="revanced_bypass_url_redirects_summary_on">Les redireccions d\'URL s\'ometent</string>

View File

@@ -1310,6 +1310,9 @@ Povolením této funkce lze odemknout vyšší kvality videa"</string>
<string name="microg_settings_title">Nastavení GmsCore</string>
<string name="microg_settings_summary">Nastavení pro GmsCore</string>
</patch>
<patch id="misc.gms.accountCredentialsInvalidTextPatch">
<string name="microg_offline_account_login_error">Pokud jste nedávno změnili přihlašovací údaje svého účtu, odinstalujte a znovu nainstalujte MicroG.</string>
</patch>
<patch id="misc.links.bypassURLRedirectsPatch">
<string name="revanced_bypass_url_redirects_title">Obcházet přesměrování URL</string>
<string name="revanced_bypass_url_redirects_summary_on">Přesměrování URL jsou obcházena</string>

View File

@@ -1252,6 +1252,9 @@ Aktivering af dette kan låse op for højere videokvalitet"</string>
<string name="microg_settings_title">GmsCore Indstillinger</string>
<string name="microg_settings_summary">Indstillinger for GmsCore</string>
</patch>
<patch id="misc.gms.accountCredentialsInvalidTextPatch">
<string name="microg_offline_account_login_error">Hvis du for nylig har ændret dine kontooplysninger, skal du afinstallere og geninstallere MicroG.</string>
</patch>
<patch id="misc.links.bypassURLRedirectsPatch">
<string name="revanced_bypass_url_redirects_title">Bypass URL omdirigeringer</string>
<string name="revanced_bypass_url_redirects_summary_on">URL omdirigeringer er omgået</string>

View File

@@ -1304,6 +1304,9 @@ Durch Aktivieren dieser Option können höhere Videoqualitäten freigeschaltet w
<string name="microg_settings_title">GmsCore Einstellungen</string>
<string name="microg_settings_summary">Einstellungen für GmsCore</string>
</patch>
<patch id="misc.gms.accountCredentialsInvalidTextPatch">
<string name="microg_offline_account_login_error">Wenn Sie kürzlich Ihre Kontoanmeldedaten geändert haben, deinstallieren Sie MicroG und installieren Sie es erneut.</string>
</patch>
<patch id="misc.links.bypassURLRedirectsPatch">
<string name="revanced_bypass_url_redirects_title">URL-Weiterleitungen umgehen</string>
<string name="revanced_bypass_url_redirects_summary_on">URL-Umleitungen werden umgangen</string>

View File

@@ -86,7 +86,7 @@ Second \"item\" text"</string>
<string name="revanced_settings_screen_03_feed_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_07_seekbar_title">Γραμμή προόδου βίντεο</string>
<string name="revanced_settings_screen_07_seekbar_title">Γραμμή προόδου</string>
<string name="revanced_settings_screen_08_swipe_controls_title">Έλεγχος με σάρωση οθόνης</string>
<string name="revanced_settings_screen_11_misc_title">Διάφορα</string>
<string name="revanced_settings_screen_12_video_title">Βίντεο</string>
@@ -1310,6 +1310,9 @@ Second \"item\" text"</string>
<string name="microg_settings_title">Ρυθμίσεις GmsCore</string>
<string name="microg_settings_summary">Ρυθμίσεις για το MicroG GmsCore</string>
</patch>
<patch id="misc.gms.accountCredentialsInvalidTextPatch">
<string name="microg_offline_account_login_error">Εάν αλλάξατε πρόσφατα τα στοιχεία σύνδεσης του λογαριασμού σας, απεγκαταστήστε και εγκαταστήστε ξανά το MicroG.</string>
</patch>
<patch id="misc.links.bypassURLRedirectsPatch">
<string name="revanced_bypass_url_redirects_title">Παράκαμψη ανακατευθύνσεων συνδέσμων</string>
<string name="revanced_bypass_url_redirects_summary_on">Οι ανακατευθύνσεις συνδέσμων URL παρακάμπτονται</string>

View File

@@ -1299,6 +1299,9 @@ Habilitar esto puede desbloquear calidades de vídeo más altas"</string>
<string name="microg_settings_title">Ajustes de GmsCore</string>
<string name="microg_settings_summary">Configuración de GmsCore</string>
</patch>
<patch id="misc.gms.accountCredentialsInvalidTextPatch">
<string name="microg_offline_account_login_error">Si has cambiado recientemente los datos de inicio de sesión de tu cuenta, desinstala y vuelve a instalar MicroG.</string>
</patch>
<patch id="misc.links.bypassURLRedirectsPatch">
<string name="revanced_bypass_url_redirects_title">Evitar redirecciones de URL</string>
<string name="revanced_bypass_url_redirects_summary_on">Se omiten las redirecciones de URL</string>

View File

@@ -1311,6 +1311,9 @@ Selle lubamine võib avada kõrgema video kvaliteedi"</string>
<string name="microg_settings_title">GmsCore seaded</string>
<string name="microg_settings_summary">GmsCore seaded</string>
</patch>
<patch id="misc.gms.accountCredentialsInvalidTextPatch">
<string name="microg_offline_account_login_error">Kui sa hiljuti muutsid oma konto sisselogimisandmeid, siis eemalda ja installeeri MicroG uuesti.</string>
</patch>
<patch id="misc.links.bypassURLRedirectsPatch">
<string name="revanced_bypass_url_redirects_title">Ümbersuunamiste URL-id mööda minna</string>
<string name="revanced_bypass_url_redirects_summary_on">Ümbersuunamiste URL-id mööda minnakse</string>

View File

@@ -197,6 +197,8 @@ Second \"item\" text"</string>
</patch>
<patch id="misc.gms.gmsCoreSupportResourcePatch">
</patch>
<patch id="misc.gms.accountCredentialsInvalidTextPatch">
</patch>
<patch id="misc.links.bypassURLRedirectsPatch">
</patch>
<patch id="misc.links.openLinksExternallyPatch">

View File

@@ -217,6 +217,8 @@ Second \"item\" text"</string>
</patch>
<patch id="misc.gms.gmsCoreSupportResourcePatch">
</patch>
<patch id="misc.gms.accountCredentialsInvalidTextPatch">
</patch>
<patch id="misc.links.bypassURLRedirectsPatch">
</patch>
<patch id="misc.links.openLinksExternallyPatch">

View File

@@ -460,15 +460,29 @@ Säädä äänenvoimakkuutta pyyhkäisemällä pystysuoraan näytön oikealta pu
<string name="revanced_swipe_lowest_value_enable_auto_brightness_summary_on">Automaattinen kirkkaus otetaan käyttöön pyyhkäisemällä alhaisimpaan arvoon</string>
<string name="revanced_swipe_lowest_value_enable_auto_brightness_summary_off">Pienimpään arvoon alas pyyhkäiseminen ei ota käyttöön automaattista kirkkautta</string>
<string name="revanced_swipe_lowest_value_enable_auto_brightness_overlay_text">Automaattinen</string>
<string name="revanced_swipe_overlay_timeout_title">Pyyhkäisyikkunan aikakatkaisu</string>
<string name="revanced_swipe_overlay_timeout_title">Pyyhkäisypeittokuvan aikakatkaisu</string>
<string name="revanced_swipe_overlay_timeout_summary">Kuinka monta millisekuntia ikkuna on näkyvissä</string>
<string name="revanced_swipe_overlay_background_opacity_title">Pyyhkäisypeittokuvan taustan läpinäkymättömyys</string>
<string name="revanced_swipe_overlay_background_opacity_summary">Läpinäkymättömyysarvo 0100 välillä</string>
<string name="revanced_swipe_overlay_background_opacity_invalid_toast">Pyyhkäisyn läpinäkymättömyyden on oltava välillä 0100</string>
<string name="revanced_swipe_overlay_background_opacity_invalid_toast">Pyyhkäisypeittokuvan läpinäkymättömyyden tulee olla 0100 välillä</string>
<string name="revanced_swipe_overlay_progress_color_title">Pyyhkäisypeittokuvan edistymispalkin väri</string>
<string name="revanced_swipe_overlay_progress_color_summary">Äänenvoimakkuuden ja kirkkauden säätimien edistymispalkin väri</string>
<string name="revanced_swipe_overlay_progress_color_invalid_toast">Virheellinen edistymispalkin väri</string>
<string name="revanced_swipe_text_overlay_size_title">Pyyhkäisypeittokuvan tekstin koko</string>
<string name="revanced_swipe_text_overlay_size_summary">Pyyhkäisypeittokuvan tekstin koko 130 välillä</string>
<string name="revanced_swipe_text_overlay_size_invalid_toast">Tekstin koon tulee olla 130 välillä</string>
<string name="revanced_swipe_threshold_title">Pyyhkäisyn kynnysraja</string>
<string name="revanced_swipe_threshold_summary">Pyyhkäisyä varten tarvittavan kynnyksen määrä</string>
<string name="revanced_swipe_volume_sensitivity_title">Äänenvoimakkuuden pyyhkäisyn herkkyys</string>
<string name="revanced_swipe_volume_sensitivity_summary">Kuinka paljon äänenvoimakkuus muuttuu pyyhkäisyä kohden</string>
<string name="revanced_swipe_overlay_style_title">Pyyhkäisypeittokuvan tyyli</string>
<string name="revanced_swipe_overlay_style_entry_1">Vaakasuuntainen peittokuva</string>
<string name="revanced_swipe_overlay_style_entry_2">Vaakasuuntainen peittokuva (minimaalinen ylhäällä)</string>
<string name="revanced_swipe_overlay_style_entry_3">Vaakasuuntainen peittokuva (minimaalinen keskellä)</string>
<string name="revanced_swipe_overlay_style_entry_4">Pyöreä peittokuva</string>
<string name="revanced_swipe_overlay_style_entry_5">Pyöreä peittokuva (minimaalinen)</string>
<string name="revanced_swipe_overlay_style_entry_6">Pystysuuntainen peittokuva</string>
<string name="revanced_swipe_overlay_style_entry_7">Pystysuuntainen peittokuva (minimaalinen)</string>
<string name="revanced_swipe_change_video_title">Ota videon vaihto pyyhkäisemällä käyttöön</string>
<string name="revanced_swipe_change_video_summary_on">Pyyhkäisemällä kokoruututilassa siirrytään seuraavaan/edelliseen videoon</string>
<string name="revanced_swipe_change_video_summary_off">Pyyhkäisemällä kokoruututilassa ei siirrytä seuraavaan/edelliseen videoon</string>
@@ -802,7 +816,7 @@ Asetukset → Toisto → Toista seuraava video automaattisesti"</string>
<patch id="layout.player.overlay.customPlayerOverlayOpacityResourcePatch">
<string name="revanced_player_overlay_opacity_title">Soittimen peittokuvan läpinäkymättömyys</string>
<string name="revanced_player_overlay_opacity_summary">Läpinäkymättömyysarvo välillä 0100, jossa 0 on läpinäkyvä</string>
<string name="revanced_player_overlay_opacity_invalid_toast">Soittimen peittokuvan läpinäkymättömyyden on oltava välillä 0100</string>
<string name="revanced_player_overlay_opacity_invalid_toast">Soittimen peittokuvan läpinäkymättömyyden tulee olla 0100 välillä</string>
</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. -->
@@ -903,7 +917,7 @@ Tämä ominaisuus toimii parhaiten, kun videon laatu on 720p tai alhaisempi ja k
<string name="revanced_sb_enable_create_segment_sum_off">Luo uusi osio -painiketta ei näytetä</string>
<string name="revanced_sb_general_adjusting">Uuden osion ajoituksen säätö</string>
<string name="revanced_sb_general_adjusting_sum">Kuinka monta millisekuntia ajansäätöpainikkeet liikkuvat uusia osioita luotaessa</string>
<string name="revanced_sb_general_adjusting_invalid">Arvon on oltava positiivinen luku</string>
<string name="revanced_sb_general_adjusting_invalid">Arvon tulee olla positiivinen luku</string>
<string name="revanced_sb_guidelines_preference_title">Näytä ohjeet</string>
<string name="revanced_sb_guidelines_preference_sum">Ohjeet sisältävät sääntöjä ja vinkkejä uusien osioiden luomiseen</string>
<string name="revanced_sb_guidelines_popup_title">Noudata ohjeita</string>
@@ -1201,10 +1215,10 @@ Pyyhkäise laajentaaksesi tai sulkeaksesi"</string>
<string name="revanced_miniplayer_hide_rewind_forward_summary_off">Eteenpäin ja taaksepäin näytetään</string>
<string name="revanced_miniplayer_width_dip_title">Aloituskoko</string>
<string name="revanced_miniplayer_width_dip_summary">Alkuperäinen näyttökoko pikseleinä</string>
<string name="revanced_miniplayer_width_dip_invalid_toast">Pikselikoon on oltava välillä %1$s ja %2$s</string>
<string name="revanced_miniplayer_width_dip_invalid_toast">Pikselikoon tulee olla %1$s ja %2$s välillä</string>
<string name="revanced_miniplayer_opacity_title">Peittokuvan läpinäkymättömyys</string>
<string name="revanced_miniplayer_opacity_summary">Läpinäkymättömyysarvo välillä 0100, jossa 0 on läpinäkyvä</string>
<string name="revanced_miniplayer_opacity_invalid_toast">Minisoittimen peittokuvan läpinäkymättömyyden on oltava välillä 0100</string>
<string name="revanced_miniplayer_opacity_invalid_toast">Minisoittimen peittokuvan läpinäkymättömyyden tulee olla 0100 välillä</string>
</patch>
<patch id="layout.theme.themePatch">
<string name="revanced_gradient_loading_screen_title">Ota liukuvärillinen latausruutu käyttöön</string>
@@ -1294,6 +1308,9 @@ Tämä voi avata korkealaatuisemmat videot"</string>
<string name="microg_settings_title">GmsCore-asetukset</string>
<string name="microg_settings_summary">GmsCoren asetukset</string>
</patch>
<patch id="misc.gms.accountCredentialsInvalidTextPatch">
<string name="microg_offline_account_login_error">Jos olet äskettäin muuttanut tilisi kirjautumistietoja, poista ja asenna MicroG uudelleen.</string>
</patch>
<patch id="misc.links.bypassURLRedirectsPatch">
<string name="revanced_bypass_url_redirects_title">Ohita URL-osoitteen uudelleenohjaukset</string>
<string name="revanced_bypass_url_redirects_summary_on">URL-osoitteen uudelleenohjaukset ohitetaan</string>
@@ -1350,7 +1367,7 @@ Tämä voi avata korkealaatuisemmat videot"</string>
<string name="revanced_custom_speed_menu_summary_off">Omaa nopeusvalikkoa ei näytetä</string>
<string name="revanced_custom_playback_speeds_title">Omat toistonopeudet</string>
<string name="revanced_custom_playback_speeds_summary">Lisää tai muuta omia toistonopeuksia</string>
<string name="revanced_custom_playback_speeds_invalid">Omien nopeuksien on oltava alle %s</string>
<string name="revanced_custom_playback_speeds_invalid">Omien nopeuksien tulee olla alle %s</string>
<string name="revanced_custom_playback_speeds_parse_exception">Virheelliset omat toistonopeudet</string>
<string name="revanced_custom_playback_speeds_auto">Automaattinen</string>
<string name="revanced_speed_tap_and_hold_title">Oma napauta ja pidä pohjassa -nopeus</string>

View File

@@ -1309,6 +1309,9 @@ Ang pagpapagana nito ay maaaring magbukas ng mas mataas na kalidad ng video"</st
<string name="microg_settings_title">Mga Setting ng GmsCore</string>
<string name="microg_settings_summary">Mga setting para sa GmsCore</string>
</patch>
<patch id="misc.gms.accountCredentialsInvalidTextPatch">
<string name="microg_offline_account_login_error">Kung kamakailan mo lang binago ang mga detalye sa pag-login sa iyong account, i-uninstall at muling i-install ang MicroG.</string>
</patch>
<patch id="misc.links.bypassURLRedirectsPatch">
<string name="revanced_bypass_url_redirects_title">I-bypass ang mga pag-redirect ng URL</string>
<string name="revanced_bypass_url_redirects_summary_on">Ang mga pag-redirect ng URL ay na-bypass</string>

View File

@@ -1310,6 +1310,9 @@ Activer cette option peut déverrouiller des qualités vidéo supérieures"</str
<string name="microg_settings_title">Paramètres GmsCore</string>
<string name="microg_settings_summary">Paramètres relatifs à GmsCore</string>
</patch>
<patch id="misc.gms.accountCredentialsInvalidTextPatch">
<string name="microg_offline_account_login_error">Si vous avez récemment modifié vos informations de connexion, désinstallez et réinstallez MicroG.</string>
</patch>
<patch id="misc.links.bypassURLRedirectsPatch">
<string name="revanced_bypass_url_redirects_title">Contourner les redirections d\'URL</string>
<string name="revanced_bypass_url_redirects_summary_on">Les redirections d\'URL sont contournées</string>

View File

@@ -1311,6 +1311,9 @@ Is féidir le seo caighdeáin físeáin níos airde a dhíghlasáil"</string>
<string name="microg_settings_title">Socruithe GmsCore</string>
<string name="microg_settings_summary">Socruithe le haghaidh GmsCore</string>
</patch>
<patch id="misc.gms.accountCredentialsInvalidTextPatch">
<string name="microg_offline_account_login_error">Má dathraigh tú sonraí logála isteach do chuntais le déanaí, ansin díshuiteáil agus athshuiteáil MicroG.</string>
</patch>
<patch id="misc.links.bypassURLRedirectsPatch">
<string name="revanced_bypass_url_redirects_title">Atreoracha seachbhóthar URL</string>
<string name="revanced_bypass_url_redirects_summary_on">Seachnaítear atreoruithe URL</string>

View File

@@ -197,6 +197,8 @@ Second \"item\" text"</string>
</patch>
<patch id="misc.gms.gmsCoreSupportResourcePatch">
</patch>
<patch id="misc.gms.accountCredentialsInvalidTextPatch">
</patch>
<patch id="misc.links.bypassURLRedirectsPatch">
</patch>
<patch id="misc.links.openLinksExternallyPatch">

View File

@@ -197,6 +197,8 @@ Second \"item\" text"</string>
</patch>
<patch id="misc.gms.gmsCoreSupportResourcePatch">
</patch>
<patch id="misc.gms.accountCredentialsInvalidTextPatch">
</patch>
<patch id="misc.links.bypassURLRedirectsPatch">
</patch>
<patch id="misc.links.openLinksExternallyPatch">

View File

@@ -197,6 +197,8 @@ Second \"item\" text"</string>
</patch>
<patch id="misc.gms.gmsCoreSupportResourcePatch">
</patch>
<patch id="misc.gms.accountCredentialsInvalidTextPatch">
</patch>
<patch id="misc.links.bypassURLRedirectsPatch">
</patch>
<patch id="misc.links.openLinksExternallyPatch">

View File

@@ -197,6 +197,8 @@ Second \"item\" text"</string>
</patch>
<patch id="misc.gms.gmsCoreSupportResourcePatch">
</patch>
<patch id="misc.gms.accountCredentialsInvalidTextPatch">
</patch>
<patch id="misc.links.bypassURLRedirectsPatch">
</patch>
<patch id="misc.links.openLinksExternallyPatch">

View File

@@ -1308,6 +1308,9 @@ Ez a beállítás lehetővé teszi a magasabb videóminőségek feloldását"</s
<string name="microg_settings_title">GmsCore beállítások</string>
<string name="microg_settings_summary">A GmsCore beállításai</string>
</patch>
<patch id="misc.gms.accountCredentialsInvalidTextPatch">
<string name="microg_offline_account_login_error">Ha nemrég módosította a fiókja bejelentkezési adatait, távolítsa el, majd telepítse újra a MicroG-t.</string>
</patch>
<patch id="misc.links.bypassURLRedirectsPatch">
<string name="revanced_bypass_url_redirects_title">URL átirányítások kikerülése</string>
<string name="revanced_bypass_url_redirects_summary_on">URL átirányítások kikerülve</string>

View File

@@ -1311,6 +1311,9 @@ Mini-player-ը կարող է գրավվել էկրանից դուրս՝ դեպի
<string name="microg_settings_title">GmsCore կարգավորումներ</string>
<string name="microg_settings_summary">GmsCore-ի կարգավորումներ</string>
</patch>
<patch id="misc.gms.accountCredentialsInvalidTextPatch">
<string name="microg_offline_account_login_error">Եթե վերջերս փոխել եք ձեր հաշվի մուտքի տվյալները, ապա ջնջեք և նորից տեղադրեք MicroG-ն:</string>
</patch>
<patch id="misc.links.bypassURLRedirectsPatch">
<string name="revanced_bypass_url_redirects_title">URL-ի վերահղումների հնարավորությունը անջատել</string>
<string name="revanced_bypass_url_redirects_summary_on">URL-ի վերահղումները անջատված են</string>

View File

@@ -1310,6 +1310,9 @@ Mengaktifkan ini dapat membuka kualitas video yang lebih tinggi"</string>
<string name="microg_settings_title">Pengaturan GmsCore</string>
<string name="microg_settings_summary">Pengaturan untuk GmsCore</string>
</patch>
<patch id="misc.gms.accountCredentialsInvalidTextPatch">
<string name="microg_offline_account_login_error">Jika Anda baru saja mengubah detail login akun Anda, hapus pemasangan dan ulangi memasang MicroG.</string>
</patch>
<patch id="misc.links.bypassURLRedirectsPatch">
<string name="revanced_bypass_url_redirects_title">Abaikan pengalihan URL</string>
<string name="revanced_bypass_url_redirects_summary_on">Pengalihan URL diabaikan</string>

View File

@@ -197,6 +197,8 @@ Second \"item\" text"</string>
</patch>
<patch id="misc.gms.gmsCoreSupportResourcePatch">
</patch>
<patch id="misc.gms.accountCredentialsInvalidTextPatch">
</patch>
<patch id="misc.links.bypassURLRedirectsPatch">
</patch>
<patch id="misc.links.openLinksExternallyPatch">

View File

@@ -1310,6 +1310,9 @@ Abilitare questa opzione può sbloccare qualità video più elevate"</string>
<string name="microg_settings_title">Impostazioni di GmsCore</string>
<string name="microg_settings_summary">Impostazioni per GmsCore</string>
</patch>
<patch id="misc.gms.accountCredentialsInvalidTextPatch">
<string name="microg_offline_account_login_error">Se di recente hai modificato i dettagli di accesso al tuo account, disinstalla e reinstalla MicroG.</string>
</patch>
<patch id="misc.links.bypassURLRedirectsPatch">
<string name="revanced_bypass_url_redirects_title">Aggira reindirizzamenti dell\'URL</string>
<string name="revanced_bypass_url_redirects_summary_on">I reindirizzamenti URL sono aggirati</string>

View File

@@ -1294,6 +1294,8 @@ Second \"item\" text"</string>
<string name="microg_settings_title">הגדרות GmsCore</string>
<string name="microg_settings_summary">הגדרות עבור GmsCore</string>
</patch>
<patch id="misc.gms.accountCredentialsInvalidTextPatch">
</patch>
<patch id="misc.links.bypassURLRedirectsPatch">
<string name="revanced_bypass_url_redirects_title">עקוף הפניות מחדש של URL</string>
<string name="revanced_bypass_url_redirects_summary_on">הפניות מחדש של URL נעקפות</string>

View File

@@ -64,7 +64,7 @@ Second \"item\" text"</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="gms_core_toast_not_installed_message">MicroG GmsCore がインストールされていません。インストールしてください。</string>
<string name="gms_core_dialog_title">必ず実行してください</string>
<string name="gms_core_dialog_not_whitelisted_not_allowed_in_background_message">"MicroG GmsCore はバックグラウンドで実行するための権限を持っていません。
<string name="gms_core_dialog_not_whitelisted_not_allowed_in_background_message">"MicroG GmsCore はバックグラウンドで動くための権限を持っていません。
下記ウェブサイト「Don't kill my app」の携帯電話メーカー別のガイドに従い、MicroG GmsCore に対するデバイスの設定を変更してください。
@@ -80,7 +80,7 @@ MicroG GmsCore に対する電池の最適化を無効にしても、バッテ
</app>
<app id="youtube">
<patch id="misc.settings.settingsPatch">
<string name="revanced_settings_screen_00_about_title">このアプリについて</string>
<string name="revanced_settings_screen_00_about_title">ReVanced について</string>
<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>
@@ -313,14 +313,14 @@ MicroG GmsCore に対する電池の最適化を無効にしても、バッテ
<string name="revanced_hide_keyword_content_screen_title">キーワード フィルタ</string>
<string name="revanced_hide_keyword_content_screen_summary">フィード、検索結果、関連動画に表示される動画をキーワードで除外するフィルタの設定</string>
<string name="revanced_hide_keyword_content_home_title">キーワードに合致する動画をホームフィードから除外する</string>
<string name="revanced_hide_keyword_content_home_summary_on">キーワードに合致する動画はホームフィードに表示されません</string>
<string name="revanced_hide_keyword_content_home_summary_off">ホームフィードはキーワードでフィルタリングされません</string>
<string name="revanced_hide_keyword_content_home_summary_on">キーワードに合致する動画はホームフィードおよび関連動画に表示されません</string>
<string name="revanced_hide_keyword_content_home_summary_off">ホームフィードおよび関連動画はキーワードでフィルタリングされません</string>
<string name="revanced_hide_keyword_content_search_title">キーワードに合致する動画を検索結果から除外する</string>
<string name="revanced_hide_keyword_content_search_summary_on">キーワードに合致する動画は検索結果に表示されません</string>
<string name="revanced_hide_keyword_content_search_summary_off">検索結果はキーワードでフィルタリングされません</string>
<string name="revanced_hide_keyword_content_subscriptions_title">キーワードに合致する動画を登録チャンネル フィードから除外する</string>
<string name="revanced_hide_keyword_content_subscriptions_summary_on">キーワードに合致する動画は登録チャンネル フィードに表示されません</string>
<string name="revanced_hide_keyword_content_subscriptions_summary_off">登録チャンネル フィードの動画はキーワードでフィルタリングされません</string>
<string name="revanced_hide_keyword_content_subscriptions_summary_off">登録チャンネル フィードはキーワードでフィルタリングされません</string>
<string name="revanced_hide_keyword_content_phrases_title">キーワード</string>
<!-- For localization, it is preferred, but not required, if 'LeBlanc' is replaced with a localized name or a familiar word that has upper case letters in the middle of the word.
This is because keywords can be in any language, and showing an example in the localized script helps convey this. -->
@@ -854,7 +854,7 @@ MicroG GmsCore に対する電池の最適化を無効にしても、バッテ
<string name="revanced_ryd_toast_on_connection_error_title">API 利用不可時にトーストを表示</string>
<string name="revanced_ryd_toast_on_connection_error_summary_on">Return YouTube Dislike が利用できない場合、トースト ポップアップが表示されます</string>
<string name="revanced_ryd_toast_on_connection_error_summary_off">Return YouTube Dislike が利用できない場合でもトースト ポップアップは表示されません</string>
<string name="revanced_ryd_about">このアプリについて</string>
<string name="revanced_ryd_about">Return YouTube Dislike について</string>
<string name="revanced_ryd_attribution_summary">このデータはReturn YouTube Dislike APIによって提供されています。詳細はここをタップしてください</string>
<!-- Statistic strings are shown in the settings only when ReVanced debug mode is enabled. Typical users will never see these. -->
<string name="revanced_ryd_statistics_category_title">このデバイスでのReturnYouTubeDislike API 統計情報</string>
@@ -1087,7 +1087,7 @@ MicroG GmsCore に対する電池の最適化を無効にしても、バッテ
<string name="revanced_sb_color_invalid">色の値が無効です</string>
<string name="revanced_sb_reset_color">色をリセット</string>
<string name="revanced_sb_reset">リセット</string>
<string name="revanced_sb_about">このアプリについて</string>
<string name="revanced_sb_about">SponsorBlock について</string>
<string name="revanced_sb_about_api_sum">SponsorBlock APIによって提供されるデータです。詳細はこちらをタップしてください。</string>
</patch>
<patch id="layout.formfactor.changeFormFactorPatch">
@@ -1301,7 +1301,7 @@ Automotive レイアウト
より高画質な映像がアンロックされる可能性がありますが、動画のカクつき、バッテリー寿命の悪化、予期せぬ副作用が
発生するかもしれません"</string>
<string name="revanced_spoof_device_dimensions_summary_off">"端末の画面サイズは偽装されていません
<string name="revanced_spoof_device_dimensions_summary_off">"デバイスの画面サイズは偽装されていません
この機能を有効にすると、より高画質な映像がアンロックされる可能性があります"</string>
<string name="revanced_spoof_device_dimensions_user_dialog_message">この機能を有効にすると、動画のカクつき、バッテリー寿命の悪化、および予期せぬ副作用を引き起こす可能性があります。</string>
@@ -1310,6 +1310,9 @@ Automotive レイアウト
<string name="microg_settings_title">GmsCore 設定</string>
<string name="microg_settings_summary">GmsCore の設定</string>
</patch>
<patch id="misc.gms.accountCredentialsInvalidTextPatch">
<string name="microg_offline_account_login_error">最近アカウントのログイン情報を変更した場合は、MicroG をアンインストールして再インストールしてください。</string>
</patch>
<patch id="misc.links.bypassURLRedirectsPatch">
<string name="revanced_bypass_url_redirects_title">URL リダイレクトを回避する</string>
<string name="revanced_bypass_url_redirects_summary_on">YouTube の URL リダイレクトを経由せずにリンクへ移動します</string>
@@ -1406,10 +1409,10 @@ Automotive レイアウト
<string name="revanced_spoof_video_streams_client_type_title">デフォルトのクライアント</string>
<string name="revanced_spoof_video_streams_ios_force_avc_title">iOS クライアントで AVC (H.264) を強制的に使用する</string>
<string name="revanced_spoof_video_streams_ios_force_avc_summary_on">ビデオ コーデックは強制的に AVC (H.264) が使用されます</string>
<string name="revanced_spoof_video_streams_ios_force_avc_summary_off">ビデオコーデックは自動的に決定されます</string>
<string name="revanced_spoof_video_streams_ios_force_avc_user_dialog_message">"この機能を有効にすると、バッテリー寿命が延び、動画のカクつきが改善さる可能性があります。
<string name="revanced_spoof_video_streams_ios_force_avc_summary_off">ビデオ コーデックは自動的に決定されます</string>
<string name="revanced_spoof_video_streams_ios_force_avc_user_dialog_message">"この機能を有効にすると、バッテリー寿命が延びたり、動画のカクつきが改善さたりする可能性があります。
AVC 最大解像度 1080p であり、Opus オーディオ コーデックは使用できず、動画再生時 VP9 や AV1 より多くの通信量を使用します。"</string>
AVC は、最大解像度 1080p であり、Opus オーディオ コーデックが利用できず、動画再生時の通信量が VP9 や AV1 より多くなります。"</string>
<string name="revanced_spoof_video_streams_about_ios_tv_title">iOS クライアントの副作用</string>
<string name="revanced_spoof_video_streams_about_ios_tv_summary">"• 映画や有料動画が再生されない可能性があります
•「 一定音量」が利用できません

View File

@@ -197,6 +197,8 @@ Second \"item\" text"</string>
</patch>
<patch id="misc.gms.gmsCoreSupportResourcePatch">
</patch>
<patch id="misc.gms.accountCredentialsInvalidTextPatch">
</patch>
<patch id="misc.links.bypassURLRedirectsPatch">
</patch>
<patch id="misc.links.openLinksExternallyPatch">

View File

@@ -197,6 +197,8 @@ Second \"item\" text"</string>
</patch>
<patch id="misc.gms.gmsCoreSupportResourcePatch">
</patch>
<patch id="misc.gms.accountCredentialsInvalidTextPatch">
</patch>
<patch id="misc.links.bypassURLRedirectsPatch">
</patch>
<patch id="misc.links.openLinksExternallyPatch">

View File

@@ -197,6 +197,8 @@ Second \"item\" text"</string>
</patch>
<patch id="misc.gms.gmsCoreSupportResourcePatch">
</patch>
<patch id="misc.gms.accountCredentialsInvalidTextPatch">
</patch>
<patch id="misc.links.bypassURLRedirectsPatch">
</patch>
<patch id="misc.links.openLinksExternallyPatch">

View File

@@ -197,6 +197,8 @@ Second \"item\" text"</string>
</patch>
<patch id="misc.gms.gmsCoreSupportResourcePatch">
</patch>
<patch id="misc.gms.accountCredentialsInvalidTextPatch">
</patch>
<patch id="misc.links.bypassURLRedirectsPatch">
</patch>
<patch id="misc.links.openLinksExternallyPatch">

View File

@@ -24,7 +24,7 @@ Second \"item\" text"</string>
<string name="revanced_check_environment_failed_title">환경 검사에 실패함</string>
<string name="revanced_check_environment_dialog_open_official_source_button">공식 홈페이지 열기</string>
<string name="revanced_check_environment_dialog_ignore_button">닫기</string>
<string name="revanced_check_environment_failed_message">&lt;h5&gt;이 앱은 사용자가 패치하지 않은 것 같습니다.&lt;/h5&gt;&lt;br&gt;이 앱은 제대로 작동하지 않을 수 있으며, &lt;b&gt;사용 시 해롭거나 심지어 위험할 수도 있습니다&lt;/b&gt;.&lt;br&gt;&lt;br&gt;이러한 검사는 이 앱이 사전에 패치되었거나 다른 사람으로부터 받은 것임을 의미합니다:&lt;br&gt;&lt;br&gt;&lt;small&gt;%1$s&lt;/small&gt;&lt;br&gt;검증되고 안전한 앱을 사용하고 있는지 확인하려면 &lt;b&gt;이 앱을 제거하고 직접 패치하는 것&lt;/b&gt;을 강력히 권장합니다.&lt;p&gt;&lt;br&gt;이 경고는 두 번만 표시됩니다</string>
<string name="revanced_check_environment_failed_message">&lt;h5&gt;이 앱은 사용자가 패치하지 않은 것 같습니다.&lt;/h5&gt;&lt;br&gt;이 앱은 제대로 작동하지 않을 수 있으며, &lt;b&gt;사용 시 해롭거나 심지어 위험할 수도 있습니다&lt;/b&gt;.&lt;br&gt;&lt;br&gt;이러한 검사는 이 앱이 사전에 패치되었거나 다른 사람으로부터 받은 것임을 의미합니다:&lt;br&gt;&lt;br&gt;&lt;small&gt;%1$s&lt;/small&gt;&lt;br&gt;검증되고 안전한 앱을 사용하고 있는지 확인하려면 &lt;b&gt;이 앱을 제거하고 직접 패치하는 것&lt;/b&gt;을 강력히 권장합니다.&lt;p&gt;&lt;br&gt;이 경고는 두 번만 표시됩니다.</string>
<string name="revanced_check_environment_not_same_patching_device">다른 기기에서 패치됨</string>
<string name="revanced_check_environment_manager_not_expected_installer">ReVanced Manager에 의해 설치되지 않음</string>
<string name="revanced_check_environment_not_near_patch_time">10분 이상 전에 패치됨</string>
@@ -46,9 +46,9 @@ Second \"item\" text"</string>
<string name="revanced_show_menu_icons_summary_on">설정 아이콘을 표시합니다</string>
<string name="revanced_show_menu_icons_summary_off">설정 아이콘을 표시하지 않습니다</string>
<string name="revanced_language_title">ReVanced 언어</string>
<string name="revanced_language_user_dialog_message">"일부 언어의 번역이 누락되었거나 완료되지 않았을 수 있습니다
<string name="revanced_language_user_dialog_message">"일부 언어의 번역이 누락되었거나 완료되지 않았을 수 있습니다.
새로운 언어를 번역하려면 translate.revanced.app를 방문하세요"</string>
새로운 언어를 번역하려면 translate.revanced.app를 방문하세요."</string>
<string name="revanced_language_DEFAULT">앱 언어</string>
<string name="revanced_pref_import_export_title">가져오기 / 내보내기</string>
<string name="revanced_pref_import_export_summary">ReVanced 설정을 가져오거나 내보낼 수 있습니다</string>
@@ -62,13 +62,13 @@ Second \"item\" text"</string>
</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. -->
<string name="gms_core_toast_not_installed_message">MicroG GmsCore가 설치되어 있지 않습니다. 설치하세요</string>
<string name="gms_core_toast_not_installed_message">MicroG GmsCore가 설치되어 있지 않습니다. 설치하세요.</string>
<string name="gms_core_dialog_title">필수 조치</string>
<string name="gms_core_dialog_not_whitelisted_not_allowed_in_background_message">"MicroG GmsCore에 백그라운드에서 실행할 수 있는 권한이 없습니다
<string name="gms_core_dialog_not_whitelisted_not_allowed_in_background_message">"MicroG GmsCore에 백그라운드에서 실행할 수 있는 권한이 없습니다.
이 기기에 대한 \"Don't kill my app\" 가이드를 읽어보고, MicroG 설치 지침을 적용하세요
이 기기에 대한 \"Don't kill my app\" 가이드를 읽어보고, MicroG 설치 지침을 적용하세요.
앱이 실행하려면 이 과정이 필요합니다"</string>
앱이 실행하려면 이 과정이 필요합니다."</string>
<string name="gms_core_dialog_open_website_text">웹사이트 열기</string>
<string name="gms_core_dialog_not_whitelisted_using_battery_optimizations_message">"MicroG GmsCore 앱 배터리 최적화를 비활성화(제한 없음)하여 앱 문제를 방지할 수 있습니다.
@@ -114,9 +114,9 @@ MicroG 앱 배터리 최적화를 비활성화(제한 없음)하더라도, 배
<string name="revanced_debug_toast_on_error_title">ReVanced 오류 팝업 메시지 표시하기</string>
<string name="revanced_debug_toast_on_error_summary_on">오류가 발생하면 팝업 메시지를 표시합니다</string>
<string name="revanced_debug_toast_on_error_summary_off">오류가 발생하면 팝업 메시지를 표시하지 않습니다</string>
<string name="revanced_debug_toast_on_error_user_dialog_message">"오류 메시지를 비활성화하면 모든 ReVanced 오류 알림이 숨겨집니다
<string name="revanced_debug_toast_on_error_user_dialog_message">"오류 메시지를 비활성화하면 모든 ReVanced 오류 알림이 숨겨집니다.
예상되지 않은 이벤트에 대한 알림을 받지 못할 수 있습니다"</string>
예상되지 않은 이벤트에 대한 알림을 받지 못할 수 있습니다."</string>
</patch>
<patch id="layout.hide.general.hideLayoutComponentsPatch">
<string name="revanced_hide_album_cards_title">음악 앨범 카드 숨기기</string>
@@ -299,9 +299,9 @@ MicroG 앱 배터리 최적화를 비활성화(제한 없음)하더라도, 배
<string name="revanced_hide_doodles_title">YouTube Doodles 숨기기</string>
<string name="revanced_hide_doodles_summary_on">YouTube Doodles가 숨겨집니다\n• Doodles: 기념일 로고 헤더</string>
<string name="revanced_hide_doodles_summary_off">YouTube Doodles가 표시됩니다\n• Doodles: 기념일 로고 헤더</string>
<string name="revanced_hide_doodles_user_dialog_message">"YouTube Doodles는 공휴일이나 기념일 등, 그날에 맞춘 디자인으로 변경되는 왼쪽 상단의 YouTube 헤더를 말합니다
<string name="revanced_hide_doodles_user_dialog_message">"YouTube Doodles는 공휴일이나 기념일 등, 그날에 맞춘 디자인으로 변경되는 왼쪽 상단의 YouTube 헤더를 말합니다.
현재 거주하는 지역에서 YouTube Doodles가 표시되어 있는데 이 설정이 활성화되어 있는 경우에는 검색창 아래에 표시되는 카테고리 바도 숨겨집니다"</string>
현재 거주하는 지역에서 YouTube Doodles가 표시되어 있는데 이 설정이 활성화되어 있는 경우에는 검색창 아래에 표시되는 카테고리 바도 숨겨집니다."</string>
<string name="revanced_custom_filter_screen_title">사용자 정의 필터</string>
<string name="revanced_custom_filter_screen_summary">사용자 정의 필터를 사용하여 구성요소를 숨길 수 있습니다</string>
<string name="revanced_custom_filter_title">사용자 정의 필터 활성화하기</string>
@@ -413,7 +413,7 @@ MicroG 앱 배터리 최적화를 비활성화(제한 없음)하더라도, 배
<string name="revanced_remove_viewer_discretion_dialog_title">시청 경고 다이얼로그 제거하기</string>
<string name="revanced_remove_viewer_discretion_dialog_summary_on">다음 동영상을 시청하기 전에 표시되는 시청 경고 다이얼로그를 제거합니다:\n• 연령 제한 동영상\n• 자살 또는 자해와 관련된 동영상, etc.</string>
<string name="revanced_remove_viewer_discretion_dialog_summary_off">다음 동영상을 시청하기 전에 표시되는 시청 경고 다이얼로그를 제거하지 않습니다:\n• 연령 제한 동영상\n• 자살 또는 자해와 관련된 동영상, etc.</string>
<string name="revanced_remove_viewer_discretion_dialog_user_dialog_message">이 설정은 다이얼로그를 자동으로 허용하기만 하며 연령 제한(성인인증 절차)을 우회할 수 없습니다</string>
<string name="revanced_remove_viewer_discretion_dialog_user_dialog_message">이 설정은 다이얼로그를 자동으로 허용하기만 하며 연령 제한(성인인증 절차)을 우회할 수 없습니다.</string>
</patch>
<patch id="interaction.downloads.downloadsResourcePatch">
<string name="revanced_external_downloader_screen_title">외부 다운로드</string>
@@ -475,7 +475,7 @@ MicroG 앱 배터리 최적화를 비활성화(제한 없음)하더라도, 배
<string name="revanced_swipe_volume_sensitivity_title">볼륨 스와이프 민감도</string>
<string name="revanced_swipe_volume_sensitivity_summary">스와이프할 때마다 볼륨이 얼마나 변경되는지를 지정할 수 있습니다</string>
<string name="revanced_swipe_overlay_style_title">스와이프 오버레이 스타일</string>
<string name="revanced_swipe_overlay_style_entry_1">바형 오버레이</string>
<string name="revanced_swipe_overlay_style_entry_1">가로 오버레이</string>
<string name="revanced_swipe_overlay_style_entry_2">가로 오버레이 (최소화 - 상단)</string>
<string name="revanced_swipe_overlay_style_entry_3">가로 오버레이 (최소화 - 중앙)</string>
<string name="revanced_swipe_overlay_style_entry_4">원형 오버레이</string>
@@ -564,16 +564,16 @@ MicroG 앱 배터리 최적화를 비활성화(제한 없음)하더라도, 배
알려진 문제점:
• 동영상 광고가 강제로 숨겨집니다"</string>
<string name="revanced_switch_create_with_notifications_button_summary_off">만들기 버튼과 알림 버튼의 위치를 교환하지 않습니다\n\n알려진 문제점:\n• 서버에서 더 많은 광고가 로드될 수 있습니다\n• Shorts 광고가 더 이상 숨겨지지 않습니다</string>
<string name="revanced_switch_create_with_notifications_button_user_dialog_message">"이 설정을 비활성화하면 Shorts 광고가 차단되지 않습니다
<string name="revanced_switch_create_with_notifications_button_user_dialog_message">"이 설정을 비활성화하면 Shorts 광고가 차단되지 않습니다.
이 설정을 변경해도 적용되지 않는다면 시크릿 모드로 전환해 보세요"</string>
이 설정을 변경해도 적용되지 않는다면 시크릿 모드로 전환해 보세요."</string>
<string name="revanced_hide_navigation_button_labels_title">하단바 버튼 라벨 숨기기</string>
<string name="revanced_hide_navigation_button_labels_summary_on">라벨이 숨겨집니다</string>
<string name="revanced_hide_navigation_button_labels_summary_off">라벨이 표시됩니다</string>
<string name="revanced_disable_translucent_status_bar_title">반투명 상태바 비활성화하기</string>
<string name="revanced_disable_translucent_status_bar_summary_on">상태바가 불투명합니다</string>
<string name="revanced_disable_translucent_status_bar_summary_off">상태바가 불투명하거나 반투명합니다</string>
<string name="revanced_disable_translucent_status_bar_user_dialog_message">일부 기기에서는 이 기능을 활성화하면 시스템 네비게이션 바가 투명하게 변경될 수 있습니다</string>
<string name="revanced_disable_translucent_status_bar_user_dialog_message">일부 기기에서는 이 기능을 활성화하면 시스템 네비게이션 바가 투명하게 변경될 수 있습니다.</string>
<string name="revanced_disable_translucent_navigation_bar_light_title">밝은 반투명 하단바 비활성화하기</string>
<string name="revanced_disable_translucent_navigation_bar_light_summary_on">밝은 테마에서 하단바가 불투명합니다</string>
<string name="revanced_disable_translucent_navigation_bar_light_summary_off">밝은 테마에서 하단바가 불투명하거나 반투명합니다</string>
@@ -788,7 +788,7 @@ MicroG 앱 배터리 최적화를 비활성화(제한 없음)하더라도, 배
</patch>
<patch id="layout.hide.endscreensuggestion.hideEndScreenSuggestedVideoPatch">
<string name="revanced_end_screen_suggested_video_title">최종 화면에서 다음 재생 추천 동영상 숨기기</string>
<string name="revanced_end_screen_suggested_video_summary_on">"자동재생이 꺼져 있으면 최종 화면에서 다음 재생 추천 동영상이 숨겨집니다.
<string name="revanced_end_screen_suggested_video_summary_on">"자동재생이 꺼져 있으면 최종 화면에서 다음 재생 추천 동영상이 숨겨집니다
자동재생은 YouTube 설정에서 변경할 수 있습니다:
설정 → 재생 → 다음 동영상 자동재생"</string>
@@ -883,11 +883,11 @@ MicroG 앱 배터리 최적화를 비활성화(제한 없음)하더라도, 배
<string name="revanced_seekbar_thumbnails_high_quality_summary_off">일반 화질 재생바 썸네일을 활성화합니다</string>
<string name="revanced_seekbar_thumbnails_high_quality_legacy_summary_on">전체 화면 고화질 재생바 썸네일을 활성화합니다</string>
<string name="revanced_seekbar_thumbnails_high_quality_legacy_summary_off">전체 화면 일반 화질 재생바 썸네일을 활성화합니다</string>
<string name="revanced_seekbar_thumbnails_high_quality_dialog_message">"이 설정을 활성화하면 재생바 썸네일이 없는 실시간 스트림의 썸네일도 복원됩니다
<string name="revanced_seekbar_thumbnails_high_quality_dialog_message">"이 설정을 활성화하면 재생바 썸네일이 없는 실시간 스트림의 썸네일도 복원됩니다.
재생바 썸네일에는 현재 동영상과 동일한 화질 값이 사용됩니다
재생바 썸네일에는 현재 동영상과 동일한 화질 값이 사용됩니다.
이 설정은 동영상 화질 값이 720p 이하이고 인터넷 연결 상태가 매우 빠를 때 가장 잘 작동합니다"</string>
이 설정은 동영상 화질 값이 720p 이하이고 인터넷 연결 상태가 매우 빠를 때 가장 잘 작동합니다."</string>
<string name="revanced_restore_old_seekbar_thumbnails_title">이전 재생바 썸네일 복원하기</string>
<string name="revanced_restore_old_seekbar_thumbnails_summary_on">재생바 상단에서 최소화된 썸네일을 표시합니다</string>
<string name="revanced_restore_old_seekbar_thumbnails_summary_off">플레이어에서 전체 화면으로 된 썸네일을 표시합니다</string>
@@ -1034,7 +1034,7 @@ MicroG 앱 배터리 최적화를 비활성화(제한 없음)하더라도, 배
<!-- A segment start and end time, such as "02:10 to 03:40" -->
<string name="revanced_sb_vote_segment_time_to_from">%1$s ~ %2$s</string>
<string name="revanced_sb_new_segment_choose_category">구간 카테고리를 선택하세요</string>
<string name="revanced_sb_new_segment_disabled_category">이 카테고리는 비활성화되어 있습니다. 제출하려면 설정에서 활성화해야 합니다</string>
<string name="revanced_sb_new_segment_disabled_category">이 카테고리는 비활성화되어 있습니다. 제출하려면 설정에서 활성화해야 합니다.</string>
<string name="revanced_sb_new_segment_title">새 SponsorBlock 구간</string>
<string name="revanced_sb_new_segment_mark_time_as_question">%s 을 구간의 시작 또는 끝으로 설정하시겠습니까?</string>
<string name="revanced_sb_new_segment_mark_start">시작</string>
@@ -1109,11 +1109,11 @@ MicroG 앱 배터리 최적화를 비활성화(제한 없음)하더라도, 배
<string name="revanced_spoof_app_version_title">앱 버전 변경하기</string>
<string name="revanced_spoof_app_version_summary_on">앱 버전을 변경합니다</string>
<string name="revanced_spoof_app_version_summary_off">앱 버전을 변경하지 않습니다</string>
<string name="revanced_spoof_app_version_user_dialog_message">"앱 버전을 YouTube 이전 앱 버전으로 변경합니다
<string name="revanced_spoof_app_version_user_dialog_message">"앱 버전을 YouTube 이전 앱 버전으로 변경합니다.
이 경우 앱 레이아웃과 기능이 변경되지만 알려지지 않은 문제점이 발생할 수 있습니다
이 경우 앱 레이아웃과 기능이 변경되지만 알려지지 않은 문제점이 발생할 수 있습니다.
나중에 이 설정을 비활성화하면 앱 레이아웃 버그를 방지하기 위해 앱 데이터를 지우는 것이 좋습니다"</string>
나중에 이 설정을 비활성화하면 앱 레이아웃 버그를 방지하기 위해 앱 데이터를 지우는 것이 좋습니다."</string>
<!-- 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 -->
<string name="revanced_spoof_app_version_target_title">변경할 앱 버전</string>
@@ -1308,12 +1308,15 @@ DeArrow에 대해 자세히 알아보려면 여기를 누르세요"</string>
<string name="revanced_spoof_device_dimensions_summary_off">"기기 크기 정보를 변경하지 않습니다
이 설정을 활성화하면 더 높은 화질 동영상 값을 잠금 해제할 수 있습니다"</string>
<string name="revanced_spoof_device_dimensions_user_dialog_message">이 설정을 활성화하면 동영상 재생이 끊기거나 배터리 수명이 단축되고 알려지지 않은 문제점이 발생할 수 있습니다</string>
<string name="revanced_spoof_device_dimensions_user_dialog_message">이 설정을 활성화하면 동영상 재생이 끊기거나 배터리 수명이 단축되고 알려지지 않은 문제점이 발생할 수 있습니다.</string>
</patch>
<patch id="misc.gms.gmsCoreSupportResourcePatch">
<string name="microg_settings_title">GmsCore 설정</string>
<string name="microg_settings_summary">알림 수신을 위한 클라우드 메시징을 설정할 수 있습니다</string>
</patch>
<patch id="misc.gms.accountCredentialsInvalidTextPatch">
<string name="microg_offline_account_login_error">최근에 계정 로그인 정보를 변경했다면 MicroG를 제거하고 다시 설치하세요.</string>
</patch>
<patch id="misc.links.bypassURLRedirectsPatch">
<string name="revanced_bypass_url_redirects_title">리다이렉션 없이 링크 바로 열기</string>
<string name="revanced_bypass_url_redirects_summary_on">앱 내에서 외부 링크를 열 때, URL 리다이렉션(youtube.com/redirect)을 거치지 않고 연결됩니다</string>
@@ -1405,22 +1408,22 @@ DeArrow에 대해 자세히 알아보려면 여기를 누르세요"</string>
<string name="revanced_spoof_video_streams_summary_on">스트리밍 데이터를 변경합니다</string>
<string name="revanced_spoof_video_streams_summary_off">"스트리밍 데이터를 변경하지 않습니다
동영상 재생 문제가 발생할 수 있습니다"</string>
<string name="revanced_spoof_video_streams_user_dialog_message">이 설정을 비활성화하면 동영상 재생 문제가 발생할 수 있습니다</string>
<string name="revanced_spoof_video_streams_user_dialog_message">이 설정을 비활성화하면 동영상 재생 문제가 발생할 수 있습니다.</string>
<string name="revanced_spoof_video_streams_client_type_title">기본 클라이언트</string>
<string name="revanced_spoof_video_streams_ios_force_avc_title">iOS AVC (H.264) 강제로 활성화하기</string>
<string name="revanced_spoof_video_streams_ios_force_avc_summary_on">동영상 코덱을 AVC (H.264)로 강제로 활성화합니다\n\n• 일부 VP9 코덱 동영상에서 제거되었던 화질 값이 표시될 수 있습니다\n• 최대 화질 값이 1080p이므로, 초고화질 동영상을 재생할 수 없습니다\n• HDR 동영상을 재생할 수 없습니다</string>
<string name="revanced_spoof_video_streams_ios_force_avc_summary_off">동영상 코덱을 자동으로 결정합니다\n\n• 예전에 업로드된 동영상을 재생했는데 VP9 코덱 응답을 받았을 경우, 일부 화질값이 제거되어 360p와 1080p(Premium 기능)만 선택가능할 수 있거나 화질 메뉴를 선택불가능할 수 있습니다</string>
<string name="revanced_spoof_video_streams_ios_force_avc_user_dialog_message">"이 설정를 활성화하면 배터리 수명이 향상되고, 동영상 재생 끊김 문제가 해결될 수 있습니다
<string name="revanced_spoof_video_streams_ios_force_avc_user_dialog_message">"이 설정를 활성화하면 배터리 수명이 향상되고, 동영상 재생 끊김 문제가 해결될 수 있습니다.
AVC의 최대 화질 값은 1080p이고, OPUS 코덱을 사용불가 및 HDR 동영상을 재생할 수 없으며, 동영상을 재생했을 경우에는 VP9 또는 AV1보다 더 많은 모바일 데이터를 사용되오니 주의하세요"</string>
AVC의 최대 화질 값은 1080p이고, OPUS 코덱을 사용불가 및 HDR 동영상을 재생할 수 없으며, 동영상을 재생했을 경우에는 VP9 또는 AV1보다 더 많은 모바일 데이터를 사용되오니 주의하세요."</string>
<string name="revanced_spoof_video_streams_about_ios_tv_title">알려진 문제점</string>
<string name="revanced_spoof_video_streams_about_ios_tv_summary">"• 영화 또는 유료 동영상이 재생되지 않을 수 있습니다.
<string name="revanced_spoof_video_streams_about_ios_tv_summary">"• 영화 또는 유료 동영상이 재생되지 않을 수 있습니다
• 안정적인 볼륨을 사용할 수 없습니다
• 동영상이 1초 일찍 종료될 수 있습니다"</string>
<string name="revanced_spoof_video_streams_about_android_title">알려진 문제점</string>
<string name="revanced_spoof_video_streams_about_android_summary">"• 오디오 트랙 메뉴가 표시되지 않습니다
• 안정적인 볼륨을 사용할 수 없습니다
• 원본 오디오 트랙를 강제로 활성화할 수 없습니다."</string>
• 원본 오디오 트랙를 강제로 활성화할 수 없습니다"</string>
<string name="revanced_spoof_video_streams_about_no_av1">• AV1 코덱이 지원되지 않습니다</string>
<string name="revanced_spoof_video_streams_about_kids_videos">• Kids 동영상은 로그인을 하지 않았거나 시크릿 모드에서는 재생되지 않을 수 있습니다</string>
<string name="revanced_spoof_streaming_data_stats_for_nerds_title">전문 통계에서 표시하기</string>
@@ -1436,8 +1439,8 @@ AVC의 최대 화질 값은 1080p이고, OPUS 코덱을 사용불가 및 HDR 동
<string name="revanced_block_audio_ads_summary_off">음성 광고를 차단하지 않습니다</string>
</patch>
<patch id="ad.embedded.embeddedAdsPatch">
<string name="revanced_embedded_ads_service_unavailable">%s 를 차단할 수 없기 때문에 광고가 표시될 것입니다. 설정에서 다른 광고 차단 서비스로 전환해 보세요</string>
<string name="revanced_embedded_ads_service_failed">%s 에서 오류가 발생했기 때문에 광고가 표시될 것입니다. 설정에서 다른 광고 차단 서비스로 전환해 보세요</string>
<string name="revanced_embedded_ads_service_unavailable">%s 를 차단할 수 없기 때문에 광고가 표시될 것입니다. 설정에서 다른 광고 차단 서비스로 전환해 보세요.</string>
<string name="revanced_embedded_ads_service_failed">%s 에서 오류가 발생했기 때문에 광고가 표시될 것입니다. 설정에서 다른 광고 차단 서비스로 전환해 보세요.</string>
<string name="revanced_block_embedded_ads_title">광고 차단 Proxy 서버 사용하기</string>
<string name="revanced_block_embedded_ads_entry_1">사용 안함</string>
<string name="revanced_block_embedded_ads_entry_2">Luminous Proxy</string>

View File

@@ -197,6 +197,8 @@ Second \"item\" text"</string>
</patch>
<patch id="misc.gms.gmsCoreSupportResourcePatch">
</patch>
<patch id="misc.gms.accountCredentialsInvalidTextPatch">
</patch>
<patch id="misc.links.bypassURLRedirectsPatch">
</patch>
<patch id="misc.links.openLinksExternallyPatch">

View File

@@ -197,6 +197,8 @@ Second \"item\" text"</string>
</patch>
<patch id="misc.gms.gmsCoreSupportResourcePatch">
</patch>
<patch id="misc.gms.accountCredentialsInvalidTextPatch">
</patch>
<patch id="misc.links.bypassURLRedirectsPatch">
</patch>
<patch id="misc.links.openLinksExternallyPatch">

View File

@@ -1310,6 +1310,9 @@ Gali būti atrakinta aukštesnės vaizdo įrašų kokybės, bet galite patirti v
<string name="microg_settings_title">„GmsCore“ nustatymai</string>
<string name="microg_settings_summary">„GmsCore“ nustatymai</string>
</patch>
<patch id="misc.gms.accountCredentialsInvalidTextPatch">
<string name="microg_offline_account_login_error">Jei neseniai pakeitėte savo paskyros prisijungimo duomenis, išdiekite ir iš naujo įdiekite „MicroG“.</string>
</patch>
<patch id="misc.links.bypassURLRedirectsPatch">
<string name="revanced_bypass_url_redirects_title">Aplenkti URL nukreipimus</string>
<string name="revanced_bypass_url_redirects_summary_on">URL nukreipimai aplenkiami</string>

View File

@@ -1310,6 +1310,9 @@ Var tikt atbloķētas augstākas video kvalitātes, taču var rasties video atsk
<string name="microg_settings_title">GmsCore iestatījumi</string>
<string name="microg_settings_summary">GmsCore iestatījumi</string>
</patch>
<patch id="misc.gms.accountCredentialsInvalidTextPatch">
<string name="microg_offline_account_login_error">Ja nesen mainījāt sava konta pieteikšanās informāciju, atinstalējiet un atkārtoti instalējiet MicroG.</string>
</patch>
<patch id="misc.links.bypassURLRedirectsPatch">
<string name="revanced_bypass_url_redirects_title">Aiziet garām URL pāradresācijai</string>
<string name="revanced_bypass_url_redirects_summary_on">URL pāradresācija tiek apiet</string>

Some files were not shown because too many files have changed in this diff Show More