Merge branch 'dev' into revanced-extended

This commit is contained in:
inotia00 2025-03-16 13:07:53 +09:00
commit ae69aca189
121 changed files with 3488 additions and 1282 deletions

View File

@ -15,7 +15,7 @@ See the [documentation](https://github.com/inotia00/revanced-documentation#readm
| `Ambient mode control` | Adds options to disable Ambient mode and to bypass Ambient mode restrictions. | 18.29.38 ~ 19.44.39 |
| `Bypass URL redirects` | Adds an option to bypass URL redirects and open the original URL directly. | 18.29.38 ~ 19.44.39 |
| `Bypass image region restrictions` | Adds an option to use a different host for static images, so that images blocked in some countries can be received. | 18.29.38 ~ 19.44.39 |
| `Change layout` | Adds an option to change the dp in order to use a tablet or phone layout. | 18.29.38 ~ 19.44.39 |
| `Change form factor` | Adds an option to change the UI appearance to a phone, tablet, or automotive device. | 18.29.38 ~ 19.44.39 |
| `Change live ring click action` | Adds an option to open the channel instead of the live stream when clicking on the live ring. | 18.29.38 ~ 19.44.39 |
| `Change player flyout menu toggles` | Adds an option to use text toggles instead of switch toggles within the additional settings menu. | 18.29.38 ~ 19.44.39 |
| `Change share sheet` | Adds an option to change the in-app share sheet to the system share sheet. | 18.29.38 ~ 19.44.39 |
@ -84,48 +84,49 @@ See the [documentation](https://github.com/inotia00/revanced-documentation#readm
| 💊 Patch | 📜 Description | 🏹 Target Version |
|:--------:|:--------------:|:-----------------:|
| `Bitrate default value` | Sets the audio quality to 'Always High' when you first install the app. | 6.20.51 ~ 8.05.51 |
| `Bypass image region restrictions` | Adds an option to use a different host for static images, so that images blocked in some countries can be received. | 6.20.51 ~ 8.05.51 |
| `Certificate spoof` | Enables YouTube Music to work with Android Auto by spoofing the YouTube Music certificate. | 6.20.51 ~ 8.05.51 |
| `Change share sheet` | Adds an option to change the in-app share sheet to the system share sheet. | 6.20.51 ~ 8.05.51 |
| `Change start page` | Adds an option to set which page the app opens in instead of the homepage. | 6.20.51 ~ 8.05.51 |
| `Custom branding icon for YouTube Music` | Changes the YouTube Music app icon to the icon specified in patch options. | 6.20.51 ~ 8.05.51 |
| `Custom branding name for YouTube Music` | Changes the YouTube Music app name to the name specified in patch options. | 6.20.51 ~ 8.05.51 |
| `Custom header for YouTube Music` | Applies a custom header in the top left corner within the app. | 6.20.51 ~ 8.05.51 |
| `Dark theme` | Changes the app's dark theme to the values specified in patch options. | 6.20.51 ~ 8.05.51 |
| `Disable Cairo splash animation` | Adds an option to disable Cairo splash animation. | 7.06.54 ~ 8.05.51 |
| `Disable DRC audio` | Adds an option to disable DRC (Dynamic Range Compression) audio. | 6.20.51 ~ 8.05.51 |
| `Disable QUIC protocol` | Adds an option to disable CronetEngine's QUIC protocol. | 6.20.51 ~ 8.05.51 |
| `Disable dislike redirection` | Adds an option to disable redirection to the next track when clicking the Dislike button. | 6.20.51 ~ 8.05.51 |
| `Disable forced auto captions` | Adds an option to disable captions from being automatically enabled. | 6.20.51 ~ 8.05.51 |
| `Disable music video in album` | Adds option to redirect music videos from albums for non-premium users. | 6.20.51 ~ 8.05.51 |
| `Enable OPUS codec` | Adds an option to enable the OPUS audio codec if the player response includes it. | 6.20.51 ~ 8.05.51 |
| `Enable debug logging` | Adds an option to enable debug logging. | 6.20.51 ~ 8.05.51 |
| `Enable landscape mode` | Adds an option to enable landscape mode when rotating the screen on phones. | 6.20.51 ~ 8.05.51 |
| `Flyout menu components` | Adds options to hide or change flyout menu components. | 6.20.51 ~ 8.05.51 |
| `GmsCore support` | Allows patched Google apps to run without root and under a different package name by using GmsCore instead of Google Play Services. | 6.20.51 ~ 8.05.51 |
| `Hide account components` | Adds options to hide components related to the account menu. | 6.20.51 ~ 8.05.51 |
| `Hide action bar components` | Adds options to hide action bar components and replace the offline download button with an external download button. | 6.20.51 ~ 8.05.51 |
| `Hide ads` | Adds options to hide ads. | 6.20.51 ~ 8.05.51 |
| `Hide layout components` | Adds options to hide general layout components. | 6.20.51 ~ 8.05.51 |
| `Hide overlay filter` | Removes, at compile time, the dark overlay that appears when player flyout menus are open. | 6.20.51 ~ 8.05.51 |
| `Hide player overlay filter` | Removes, at compile time, the dark overlay that appears when single-tapping in the player. | 6.20.51 ~ 8.05.51 |
| `Navigation bar components` | Adds options to hide or change components related to the navigation bar. | 6.20.51 ~ 8.05.51 |
| `Player components` | Adds options to hide or change components related to the player. | 6.20.51 ~ 8.05.51 |
| `Remove background playback restrictions` | Removes restrictions on background playback, including for kids videos. | 6.20.51 ~ 8.05.51 |
| `Remove viewer discretion dialog` | Adds an option to remove the dialog that appears when opening a video that has been age-restricted by accepting it automatically. This does not bypass the age restriction. | 6.20.51 ~ 8.05.51 |
| `Restore old style library shelf` | Adds an option to return the Library tab to the old style. | 6.20.51 ~ 8.05.51 |
| `Return YouTube Dislike` | Adds an option to show the dislike count of songs using the Return YouTube Dislike API. | 6.20.51 ~ 8.05.51 |
| `Return YouTube Username` | Adds an option to replace YouTube handles with usernames in comments using YouTube Data API v3. | 6.20.51 ~ 8.05.51 |
| `Sanitize sharing links` | Adds an option to sanitize sharing links by removing tracking query parameters. | 6.20.51 ~ 8.05.51 |
| `Settings for YouTube Music` | Applies mandatory patches to implement ReVanced Extended settings into the application. | 6.20.51 ~ 8.05.51 |
| `SponsorBlock` | Adds options to enable and configure SponsorBlock, which can skip undesired video segments, such as non-music sections. | 6.20.51 ~ 8.05.51 |
| `Bitrate default value` | Sets the audio quality to 'Always High' when you first install the app. | 6.20.51 ~ 8.10.51 |
| `Bypass image region restrictions` | Adds an option to use a different host for static images, so that images blocked in some countries can be received. | 6.20.51 ~ 8.10.51 |
| `Certificate spoof` | Enables YouTube Music to work with Android Auto by spoofing the YouTube Music certificate. | 6.20.51 ~ 8.10.51 |
| `Change share sheet` | Adds an option to change the in-app share sheet to the system share sheet. | 6.20.51 ~ 8.10.51 |
| `Change start page` | Adds an option to set which page the app opens in instead of the homepage. | 6.20.51 ~ 8.10.51 |
| `Custom branding icon for YouTube Music` | Changes the YouTube Music app icon to the icon specified in patch options. | 6.20.51 ~ 8.10.51 |
| `Custom branding name for YouTube Music` | Changes the YouTube Music app name to the name specified in patch options. | 6.20.51 ~ 8.10.51 |
| `Custom header for YouTube Music` | Applies a custom header in the top left corner within the app. | 6.20.51 ~ 8.10.51 |
| `Dark theme` | Changes the app's dark theme to the values specified in patch options. | 6.20.51 ~ 8.10.51 |
| `Disable Cairo splash animation` | Adds an option to disable Cairo splash animation. | 7.06.54 ~ 8.10.51 |
| `Disable DRC audio` | Adds an option to disable DRC (Dynamic Range Compression) audio. | 6.20.51 ~ 8.10.51 |
| `Disable QUIC protocol` | Adds an option to disable CronetEngine's QUIC protocol. | 6.20.51 ~ 8.10.51 |
| `Disable dislike redirection` | Adds an option to disable redirection to the next track when clicking the Dislike button. | 6.20.51 ~ 8.10.51 |
| `Disable forced auto captions` | Adds an option to disable captions from being automatically enabled. | 6.20.51 ~ 8.10.51 |
| `Disable music video in album` | Adds option to redirect music videos from albums for non-premium users. | 6.20.51 ~ 8.10.51 |
| `Enable OPUS codec` | Adds an option to enable the OPUS audio codec if the player response includes it. | 6.20.51 ~ 8.10.51 |
| `Enable debug logging` | Adds an option to enable debug logging. | 6.20.51 ~ 8.10.51 |
| `Enable landscape mode` | Adds an option to enable landscape mode when rotating the screen on phones. | 6.20.51 ~ 8.10.51 |
| `Flyout menu components` | Adds options to hide or change flyout menu components. | 6.20.51 ~ 8.10.51 |
| `GmsCore support` | Allows patched Google apps to run without root and under a different package name by using GmsCore instead of Google Play Services. | 6.20.51 ~ 8.10.51 |
| `Hide account components` | Adds options to hide components related to the account menu. | 6.20.51 ~ 8.10.51 |
| `Hide action bar components` | Adds options to hide action bar components and replace the offline download button with an external download button. | 6.20.51 ~ 8.10.51 |
| `Hide ads` | Adds options to hide ads. | 6.20.51 ~ 8.10.51 |
| `Hide layout components` | Adds options to hide general layout components. | 6.20.51 ~ 8.10.51 |
| `Hide overlay filter` | Removes, at compile time, the dark overlay that appears when player flyout menus are open. | 6.20.51 ~ 8.10.51 |
| `Hide player overlay filter` | Removes, at compile time, the dark overlay that appears when single-tapping in the player. | 6.20.51 ~ 8.10.51 |
| `Navigation bar components` | Adds options to hide or change components related to the navigation bar. | 6.20.51 ~ 8.10.51 |
| `Player components` | Adds options to hide or change components related to the player. | 6.20.51 ~ 8.10.51 |
| `Remove background playback restrictions` | Removes restrictions on background playback, including for kids videos. | 6.20.51 ~ 8.10.51 |
| `Remove viewer discretion dialog` | Adds an option to remove the dialog that appears when opening a video that has been age-restricted by accepting it automatically. This does not bypass the age restriction. | 6.20.51 ~ 8.10.51 |
| `Restore old style library shelf` | Adds an option to return the Library tab to the old style. | 6.20.51 ~ 8.10.51 |
| `Return YouTube Dislike` | Adds an option to show the dislike count of songs using the Return YouTube Dislike API. | 6.20.51 ~ 8.10.51 |
| `Return YouTube Username` | Adds an option to replace YouTube handles with usernames in comments using YouTube Data API v3. | 6.20.51 ~ 8.10.51 |
| `Sanitize sharing links` | Adds an option to sanitize sharing links by removing tracking query parameters. | 6.20.51 ~ 8.10.51 |
| `Settings for YouTube Music` | Applies mandatory patches to implement ReVanced Extended settings into the application. | 6.20.51 ~ 8.10.51 |
| `SponsorBlock` | Adds options to enable and configure SponsorBlock, which can skip undesired video segments, such as non-music sections. | 6.20.51 ~ 8.10.51 |
| `Spoof app version` | Adds options to spoof the YouTube Music client version. This can be used to restore old UI elements and features. | 6.51.53 ~ 7.16.53 |
| `Spoof client` | Adds options to spoof the client to allow playback. | 6.20.51 ~ 8.05.51 |
| `Translations for YouTube Music` | Add translations or remove string resources. | 6.20.51 ~ 8.05.51 |
| `Video playback` | Adds options to customize settings related to video playback, such as default video quality and playback speed. | 6.20.51 ~ 8.05.51 |
| `Visual preferences icons for YouTube Music` | Adds icons to specific preferences in the settings. | 6.20.51 ~ 8.05.51 |
| `Watch history` | Adds an option to change the domain of the watch history or check its status. | 6.20.51 ~ 8.05.51 |
| `Spoof client` | Adds options to spoof the client to allow playback. | 6.20.51 ~ 8.10.51 |
| `Spoof player parameter` | Adds options to spoof player parameter to allow playback. | 6.20.51 ~ 8.10.51 |
| `Translations for YouTube Music` | Add translations or remove string resources. | 6.20.51 ~ 8.10.51 |
| `Video playback` | Adds options to customize settings related to video playback, such as default video quality and playback speed. | 6.20.51 ~ 8.10.51 |
| `Visual preferences icons for YouTube Music` | Adds icons to specific preferences in the settings. | 6.20.51 ~ 8.10.51 |
| `Watch history` | Adds an option to change the domain of the watch history or check its status. | 6.20.51 ~ 8.10.51 |
</details>
### [📦 `com.reddit.frontpage`](https://play.google.com/store/apps/details?id=com.reddit.frontpage)
@ -187,7 +188,8 @@ Example:
"6.51.53",
"7.16.53",
"7.25.53",
"8.05.51"
"8.05.51",
"8.10.51"
]
},
"options": []

View File

@ -8,6 +8,7 @@ import androidx.annotation.NonNull;
import org.apache.commons.lang3.StringUtils;
import app.revanced.extension.music.settings.Settings;
import app.revanced.extension.music.utils.ExtendedUtils;
import app.revanced.extension.shared.utils.Logger;
import app.revanced.extension.shared.utils.ResourceUtils;
@ -40,27 +41,17 @@ public final class ChangeStartPagePatch {
/**
* Intent extra.
*/
SEARCH("", 1, "Eh4IBRDTnQEYmgMiEwiZn+H0r5WLAxVV5OcDHcHRBmPqpd25AQA=");
SEARCH("");
@NonNull
final String browseId;
final int shortcutType;
/**
* Unique identifier for shortcut (Base64).
*/
@NonNull
final String shortcutId;
StartPage(@NonNull String browseId) {
this(browseId, 0, "");
this.browseId = browseId;
}
StartPage(@NonNull String browseId, int shortcutType, @NonNull String shortcutId) {
this.browseId = browseId;
this.shortcutType = shortcutType;
this.shortcutId = shortcutId;
public final String getBrowseId() {
return this.browseId;
}
}
@ -69,12 +60,6 @@ public final class ChangeStartPagePatch {
*/
private static final String ACTION_MAIN = "android.intent.action.MAIN";
private static final String SHORTCUT_ACTION = "com.google.android.youtube.music.action.shortcut";
private static final String SHORTCUT_CLASS_DESCRIPTOR = "com.google.android.apps.youtube.music.activities.InternalMusicActivity";
private static final String SHORTCUT_TYPE = "com.google.android.youtube.music.action.shortcut_type";
private static final StartPage START_PAGE = Settings.CHANGE_START_PAGE.get();
public static String overrideBrowseId(@NonNull String browseId) {
@ -96,20 +81,13 @@ public final class ChangeStartPagePatch {
" as the current activity is not the entry point of the application");
return;
}
final String overrideShortcutId = START_PAGE.shortcutId;
if (overrideShortcutId.isEmpty()) {
if (START_PAGE != StartPage.SEARCH) {
return;
}
Activity mActivity = ResourceUtils.getActivity();
if (mActivity == null) {
return;
}
if (mActivity != null) {
Logger.printDebug(() -> "Changing intent action to " + START_PAGE.name());
intent.setAction(SHORTCUT_ACTION);
intent.setClassName(mActivity, SHORTCUT_CLASS_DESCRIPTOR);
intent.setPackage(mActivity.getPackageName());
intent.putExtra(SHORTCUT_TYPE, START_PAGE.shortcutType);
intent.putExtra(SHORTCUT_ACTION, overrideShortcutId);
ExtendedUtils.setSearchIntent(mActivity, intent);
}
}
}

View File

@ -159,7 +159,7 @@ public class AlbumMusicVideoPatch {
VideoUtils.openInYouTubeMusic(songId);
}, 1000);
VideoUtils.runOnMainThreadDelayed(() -> isVideoLaunched.compareAndSet(true, false), 1500);
VideoUtils.runOnMainThreadDelayed(() -> isVideoLaunched.compareAndSet(true, false), 2500);
} catch (Exception ex) {
Logger.printException(() -> "openMusic failure", ex);
}

View File

@ -4,11 +4,12 @@ import app.revanced.extension.music.settings.Settings;
@SuppressWarnings("unused")
public class DrcAudioPatch {
private static final boolean DISABLE_DRC_AUDIO = Settings.DISABLE_DRC_AUDIO.get();
public static float disableDrcAudio(float original) {
if (!Settings.DISABLE_DRC_AUDIO.get()) {
return original;
}
if (DISABLE_DRC_AUDIO) {
return 0f;
}
return original;
}
}

View File

@ -0,0 +1,174 @@
package app.revanced.extension.music.patches.misc;
import static app.revanced.extension.music.shared.VideoInformation.parameterIsAgeRestricted;
import static app.revanced.extension.music.shared.VideoInformation.parameterIsSample;
import androidx.annotation.GuardedBy;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import org.apache.commons.lang3.BooleanUtils;
import java.util.Arrays;
import java.util.LinkedHashMap;
import java.util.Map;
import app.revanced.extension.music.settings.Settings;
import app.revanced.extension.music.shared.VideoInformation;
import app.revanced.extension.shared.utils.Logger;
@SuppressWarnings("unused")
public class SpoofPlayerParameterPatch {
/**
* Used in YouTube Music.
*/
private static final boolean SPOOF_PLAYER_PARAMETER = Settings.SPOOF_PLAYER_PARAMETER.get();
/**
* Parameter to fix playback issues.
* Used in YouTube Music Samples.
*/
private static final String PLAYER_PARAMETER_SAMPLES =
"8AEB2AUBogYVAUY4C8W9wrM-FdhjSW4MnCgH44uhkAcI";
/**
* Parameter to fix playback issues.
* Used in YouTube Shorts.
*/
private static final String PLAYER_PARAMETER_SHORTS =
"8AEByAMkuAQ0ogYVAePzwRN3uesV1sPI2x4-GkDYlvqUkAcC";
/**
* On app first start, the first video played usually contains a single non-default window setting value
* and all other subtitle settings for the video are (incorrect) default Samples window settings.
* For this situation, the Samples settings must be replaced.
* <p>
* But some videos use multiple text positions on screen (such as youtu.be/3hW1rMNC89o),
* and by chance many of the subtitles uses window positions that match a default Samples position.
* To handle these videos, selectively allowing the Samples specific window settings to 'pass thru' unchanged,
* but only if the video contains multiple non-default subtitle window positions.
* <p>
* Do not enable 'pass thru mode' until this many non default subtitle settings are observed for a single video.
*/
private static final int NUMBER_OF_NON_DEFAULT_SUBTITLES_BEFORE_ENABLING_PASSTHRU = 2;
/**
* The number of non default subtitle settings encountered for the current video.
*/
private static int numberOfNonDefaultSettingsObserved;
@GuardedBy("itself")
private static final Map<String, Boolean> lastVideoIds = new LinkedHashMap<>() {
private static final int NUMBER_OF_LAST_VIDEO_IDS_TO_TRACK = 5;
@Override
protected boolean removeEldestEntry(Entry eldest) {
return size() > NUMBER_OF_LAST_VIDEO_IDS_TO_TRACK;
}
};
/**
* Injection point.
*/
public static String spoofParameter(@NonNull String videoId, @Nullable String parameter) {
if (SPOOF_PLAYER_PARAMETER) {
synchronized (lastVideoIds) {
Boolean isSamples = parameterIsSample(parameter);
if (lastVideoIds.put(videoId, isSamples) == null) {
Logger.printDebug(() -> "New video loaded (videoId: " + videoId + ", isSamples: " + isSamples + ")");
}
}
return parameterIsAgeRestricted(parameter)
? PLAYER_PARAMETER_SHORTS
: PLAYER_PARAMETER_SAMPLES;
}
return parameter;
}
/**
* Injection point. Overrides values passed into SubtitleWindowSettings constructor.
*
* @param ap anchor position. A bitmask with 6 bit fields, that appears to indicate the layout position on screen
* @param ah anchor horizontal. A percentage [0, 100], that appears to be a horizontal text anchor point
* @param av anchor vertical. A percentage [0, 100], that appears to be a vertical text anchor point
* @param vs appears to indicate if subtitles exist, and the value is always true.
* @param sd function is not entirely clear
*/
public static int[] fixSubtitleWindowPosition(int ap, int ah, int av, boolean vs, boolean sd) {
// Videos with custom captions that specify screen positions appear to always have correct screen positions (even with spoofing).
// But for auto generated and most other captions, the spoof incorrectly gives various default Samples caption settings.
// Check for these known default Samples captions parameters, and replace with the known correct values.
//
// If a regular video uses a custom subtitle setting that match a default Samples setting,
// then this will incorrectly replace the setting.
// But, if the video uses multiple subtitles in different screen locations, then detect the non-default values
// and do not replace any window settings for the video (regardless if they match a Samples default).
if (SPOOF_PLAYER_PARAMETER &&
numberOfNonDefaultSettingsObserved < NUMBER_OF_NON_DEFAULT_SUBTITLES_BEFORE_ENABLING_PASSTHRU) {
synchronized (lastVideoIds) {
String videoId = VideoInformation.getVideoId();
Boolean isSample = lastVideoIds.get(videoId);
if (BooleanUtils.isFalse(isSample)) {
for (SubtitleWindowReplacementSettings setting : SubtitleWindowReplacementSettings.values()) {
if (setting.match(ap, ah, av, vs, sd)) {
return setting.replacementSetting();
}
}
numberOfNonDefaultSettingsObserved++;
}
}
}
return new int[]{ap, ah, av};
}
/**
* Injection point.
* <p>
* Return false to force disable age restricted playback feature flag.
*/
public static boolean forceDisableAgeRestrictedPlaybackFeatureFlag(boolean original) {
if (SPOOF_PLAYER_PARAMETER) {
return false;
}
return original;
}
/**
* Known incorrect default Samples subtitle parameters, and the corresponding correct (non-Samples) values.
*/
private enum SubtitleWindowReplacementSettings {
DEFAULT_SAMPLES_PARAMETERS_1(10, 50, 0, true, false,
34, 50, 95),
DEFAULT_SAMPLES_PARAMETERS_2(9, 20, 0, true, false,
34, 50, 90),
DEFAULT_SAMPLES_PARAMETERS_3(9, 20, 0, true, true,
33, 20, 100);
// original values
final int ap, ah, av;
final boolean vs, sd;
// replacement int values
final int[] replacement;
SubtitleWindowReplacementSettings(int ap, int ah, int av, boolean vs, boolean sd,
int replacementAp, int replacementAh, int replacementAv) {
this.ap = ap;
this.ah = ah;
this.av = av;
this.vs = vs;
this.sd = sd;
this.replacement = new int[]{replacementAp, replacementAh, replacementAv};
}
boolean match(int ap, int ah, int av, boolean vs, boolean sd) {
return this.ap == ap && this.ah == ah && this.av == av && this.vs == vs && this.sd == sd;
}
int[] replacementSetting() {
return replacement;
}
}
}

View File

@ -5,6 +5,7 @@ import androidx.annotation.GuardedBy
import app.revanced.extension.shared.patches.client.YouTubeAppClient
import app.revanced.extension.shared.patches.spoof.requests.PlayerRoutes
import app.revanced.extension.shared.requests.Requester
import app.revanced.extension.shared.settings.AppLanguage
import app.revanced.extension.shared.utils.Logger
import app.revanced.extension.shared.utils.Utils
import org.json.JSONException
@ -139,11 +140,24 @@ class PlaylistRequest private constructor(
PlayerRoutes.GET_PLAYLIST_PAGE,
clientType
)
/**
* For some reason, the tracks in Top Songs have the playlistId of the album:
* [ReVanced_Extended#2835](https://github.com/inotia00/ReVanced_Extended/issues/2835)
*
* We can work around this issue by checking the playlist title in the response.
* Tracks played from an album have a playlist title that starts with 'Album',
* Tracks played from Top Songs have a playlist title that starts with 'Song'.
*
* By default, the playlist title follows the app language,
* So we can work around this by setting the language to English when sending the request.
*/
val requestBody =
PlayerRoutes.createApplicationRequestBody(
clientType = clientType,
videoId = videoId,
playlistId = playlistId
playlistId = playlistId,
setLocale = true,
language = AppLanguage.EN.language,
)
connection.setFixedLengthStreamingMode(requestBody.size)
@ -183,6 +197,10 @@ class PlaylistRequest private constructor(
.getJSONObject("playlist")
.getJSONObject("playlist")
val playlistTitle = playlistJsonObject
?.getString("title") + ""
if (playlistTitle.startsWith("Album")) {
val currentStreamJsonObject = playlistJsonObject
?.getJSONArray("contents")
?.get(playlistIndex)
@ -197,6 +215,7 @@ class PlaylistRequest private constructor(
return watchEndpointJsonObject?.getString("videoId") + ""
}
}
}
} catch (e: JSONException) {
Logger.printException(
{ "Fetch failed while processing response data for response: $playlistJson" },

View File

@ -4,13 +4,19 @@ import static app.revanced.extension.shared.utils.StringRef.str;
import static app.revanced.extension.shared.utils.Utils.hideViewUnderCondition;
import android.graphics.Color;
import android.text.Spanned;
import android.view.View;
import android.widget.TextView;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import java.lang.reflect.Field;
import app.revanced.extension.music.patches.utils.PatchStatus;
import app.revanced.extension.music.settings.Settings;
import app.revanced.extension.music.utils.ExtendedUtils;
import app.revanced.extension.shared.utils.Logger;
import app.revanced.extension.shared.utils.ResourceUtils;
import app.revanced.extension.shared.utils.Utils;
@ -20,7 +26,14 @@ public class NavigationPatch {
? ResourceUtils.getColor("ytm_color_grey_12")
: ResourceUtils.getColor("revanced_color_grey_12");
public static Enum<?> lastPivotTab;
@Nullable
private static String lastYTNavigationEnumName;
public static void setLastAppNavigationEnum(@Nullable Enum<?> ytNavigationEnumName) {
if (ytNavigationEnumName != null) {
lastYTNavigationEnumName = ytNavigationEnumName.name();
}
}
public static int enableCustomNavigationBarColor() {
try {
@ -46,24 +59,138 @@ public class NavigationPatch {
return;
}
for (NavigationButton button : NavigationButton.values())
if (lastPivotTab.name().equals(button.name))
hideViewUnderCondition(button.enabled, view);
for (NavigationButton button : NavigationButton.values()) {
if (button.ytEnumNames.equals(lastYTNavigationEnumName)) {
if (button.replace) {
Runnable onClickAction = button.onClickAction;
if (onClickAction != null) {
view.setOnClickListener(v -> onClickAction.run());
Utils.runOnMainThreadDelayed(() -> view.setOnClickListener(v -> onClickAction.run()), 500);
}
}
hideViewUnderCondition(button.hidden, view);
}
}
}
public static String replaceBrowseId(Object component, String browseId, String fieldName) {
for (NavigationButton button : NavigationButton.values()) {
if (button.replace && button.browseId.equals(browseId)) {
String replaceBrowseId = Settings.CHANGE_START_PAGE.get().getBrowseId();
if (replaceBrowseId.isEmpty()) {
replaceBrowseId = NavigationButton.HOME.browseId;
}
try {
Field browseIdField = component.getClass().getField(fieldName);
browseIdField.setAccessible(true);
browseIdField.set(component, replaceBrowseId);
return replaceBrowseId;
} catch (Exception ignored) {
Logger.printException(() -> "replaceBrowseId failed");
}
}
}
return browseId;
}
public static int replaceNavigationIcon(int drawableId) {
for (NavigationButton button : NavigationButton.values()) {
if (button.replace &&
(drawableId == button.unSelectedDrawableId || drawableId == button.selectedDrawableId)) {
int replaceDrawableId = button.replaceDrawableId;
if (replaceDrawableId != 0) {
return replaceDrawableId;
}
}
}
return drawableId;
}
public static Spanned replaceNavigationLabel(@NonNull Spanned sourceStyle) {
for (NavigationButton button : NavigationButton.values()) {
if (button.ytEnumNames.equals(lastYTNavigationEnumName) && button.replace) {
String label = button.label;
if (!label.isEmpty()) {
return Utils.newSpanUsingStylingOfAnotherSpan(sourceStyle, label);
}
}
}
return sourceStyle;
}
private enum NavigationButton {
HOME("TAB_HOME", Settings.HIDE_NAVIGATION_HOME_BUTTON.get()),
SAMPLES("TAB_SAMPLES", Settings.HIDE_NAVIGATION_SAMPLES_BUTTON.get()),
EXPLORE("TAB_EXPLORE", Settings.HIDE_NAVIGATION_EXPLORE_BUTTON.get()),
LIBRARY("LIBRARY_MUSIC", Settings.HIDE_NAVIGATION_LIBRARY_BUTTON.get()),
UPGRADE("TAB_MUSIC_PREMIUM", Settings.HIDE_NAVIGATION_UPGRADE_BUTTON.get());
HOME(
"TAB_HOME",
Settings.HIDE_NAVIGATION_HOME_BUTTON.get(),
"FEmusic_home"
),
SAMPLES(
"TAB_SAMPLES",
Settings.HIDE_NAVIGATION_SAMPLES_BUTTON.get(),
Settings.REPLACE_NAVIGATION_SAMPLES_BUTTON.get(),
"FEmusic_immersive",
"search",
"yt_fill_samples_vd_theme_24",
"yt_outline_samples_vd_theme_24",
"yt_outline_search_vd_theme_24",
ExtendedUtils::openSearch
),
EXPLORE(
"TAB_EXPLORE",
Settings.HIDE_NAVIGATION_EXPLORE_BUTTON.get(),
"FEmusic_explore"
),
LIBRARY(
"LIBRARY_MUSIC",
Settings.HIDE_NAVIGATION_LIBRARY_BUTTON.get(),
"FEmusic_library_landing"
),
UPGRADE(
"TAB_MUSIC_PREMIUM",
Settings.HIDE_NAVIGATION_UPGRADE_BUTTON.get(),
Settings.REPLACE_NAVIGATION_UPGRADE_BUTTON.get(),
"SPunlimited",
"settings",
"yt_fill_youtube_music_vd_theme_24",
"yt_outline_youtube_music_vd_theme_24",
"yt_outline_gear_vd_theme_24",
ExtendedUtils::openSetting
);
private final boolean enabled;
private final String name;
private final String ytEnumNames;
private final boolean hidden;
private final boolean replace;
@NonNull
private final String browseId;
@NonNull
private final String label;
private final int selectedDrawableId;
private final int unSelectedDrawableId;
private final int replaceDrawableId;
@Nullable
private final Runnable onClickAction;
NavigationButton(String name, boolean enabled) {
this.enabled = enabled;
this.name = name;
NavigationButton(@NonNull String ytEnumNames, boolean hidden,
@NonNull String browseId) {
this(ytEnumNames, hidden, false, browseId, null, null, null, null, null);
}
NavigationButton(@NonNull String ytEnumNames, boolean hidden, boolean replace,
@NonNull String browseId, @Nullable String label,
@Nullable String selectedIcon, @Nullable String unSelectedIcon,
@Nullable String replaceIcon, @Nullable Runnable onClickAction) {
this.ytEnumNames = ytEnumNames;
this.hidden = hidden;
this.replace = replace;
this.browseId = browseId;
this.label = label != null ? ResourceUtils.getString(label) : "";
this.selectedDrawableId = selectedIcon != null ? ResourceUtils.getDrawableIdentifier(selectedIcon) : 0;
this.unSelectedDrawableId = unSelectedIcon != null ? ResourceUtils.getDrawableIdentifier(unSelectedIcon) : 0;
this.replaceDrawableId = replaceIcon != null ? ResourceUtils.getDrawableIdentifier(replaceIcon) : 0;
this.onClickAction = onClickAction;
}
}
}

View File

@ -3,6 +3,7 @@ package app.revanced.extension.music.returnyoutubedislike;
import static app.revanced.extension.shared.returnyoutubedislike.ReturnYouTubeDislike.Vote;
import static app.revanced.extension.shared.utils.StringRef.str;
import static app.revanced.extension.shared.utils.Utils.isSDKAbove;
import static app.revanced.extension.shared.utils.Utils.newSpanUsingStylingOfAnotherSpan;
import android.graphics.Canvas;
import android.graphics.Paint;
@ -283,15 +284,6 @@ public class ReturnYouTubeDislike {
: formatDislikeCount(voteData.getDislikeCount()));
}
private static SpannableString newSpanUsingStylingOfAnotherSpan(@NonNull Spanned sourceStyle, @NonNull CharSequence newSpanText) {
SpannableString destination = new SpannableString(newSpanText);
Object[] spans = sourceStyle.getSpans(0, sourceStyle.length(), Object.class);
for (Object span : spans) {
destination.setSpan(span, 0, destination.length(), sourceStyle.getSpanFlags(span));
}
return destination;
}
private static String formatDislikeCount(long dislikeCount) {
if (isSDKAbove(24)) {
if (dislikeCountFormatter == null) {

View File

@ -130,6 +130,9 @@ public class Settings extends BaseSettings {
public static final BooleanSetting HIDE_NAVIGATION_UPGRADE_BUTTON = new BooleanSetting("revanced_hide_navigation_upgrade_button", TRUE, true);
public static final BooleanSetting HIDE_NAVIGATION_BAR = new BooleanSetting("revanced_hide_navigation_bar", FALSE, true);
public static final BooleanSetting HIDE_NAVIGATION_LABEL = new BooleanSetting("revanced_hide_navigation_label", FALSE, true);
public static final BooleanSetting REPLACE_NAVIGATION_SAMPLES_BUTTON = new BooleanSetting("revanced_replace_navigation_samples_button", FALSE, true);
public static final BooleanSetting REPLACE_NAVIGATION_UPGRADE_BUTTON = new BooleanSetting("revanced_replace_navigation_upgrade_button", FALSE, true);
public static final BooleanSetting REPLACE_NAVIGATION_BUTTON_ABOUT = new BooleanSetting("revanced_replace_navigation_button_about", FALSE, false);
// PreferenceScreen: Player
@ -190,6 +193,7 @@ public class Settings extends BaseSettings {
public static final BooleanSetting DISABLE_DRC_AUDIO = new BooleanSetting("revanced_disable_drc_audio", FALSE, true);
public static final BooleanSetting DISABLE_MUSIC_VIDEO_IN_ALBUM = new BooleanSetting("revanced_disable_music_video_in_album", FALSE, true);
public static final EnumSetting<RedirectType> DISABLE_MUSIC_VIDEO_IN_ALBUM_REDIRECT_TYPE = new EnumSetting<>("revanced_disable_music_video_in_album_redirect_type", RedirectType.REDIRECT, true);
public static final BooleanSetting SPOOF_PLAYER_PARAMETER = new BooleanSetting("revanced_spoof_player_parameter", TRUE, true);
public static final BooleanSetting SETTINGS_IMPORT_EXPORT = new BooleanSetting("revanced_extended_settings_import_export", FALSE, false);
// PreferenceScreen: Return YouTube Dislike
@ -276,6 +280,7 @@ public class Settings extends BaseSettings {
RETURN_YOUTUBE_USERNAME_ABOUT.key,
RETURN_YOUTUBE_USERNAME_DISPLAY_FORMAT.key,
RETURN_YOUTUBE_USERNAME_YOUTUBE_DATA_API_V3_DEVELOPER_KEY.key,
REPLACE_NAVIGATION_BUTTON_ABOUT.key,
SB_API_URL.key,
SETTINGS_IMPORT_EXPORT.key,
SPOOF_APP_VERSION_TARGET.key,

View File

@ -12,6 +12,7 @@ import static app.revanced.extension.music.settings.Settings.EXTERNAL_DOWNLOADER
import static app.revanced.extension.music.settings.Settings.HIDE_ACCOUNT_MENU_FILTER_STRINGS;
import static app.revanced.extension.music.settings.Settings.OPEN_DEFAULT_APP_SETTINGS;
import static app.revanced.extension.music.settings.Settings.OPTIONAL_SPONSOR_BLOCK_SETTINGS_PREFIX;
import static app.revanced.extension.music.settings.Settings.REPLACE_NAVIGATION_BUTTON_ABOUT;
import static app.revanced.extension.music.settings.Settings.RETURN_YOUTUBE_USERNAME_ABOUT;
import static app.revanced.extension.music.settings.Settings.SB_API_URL;
import static app.revanced.extension.music.settings.Settings.SETTINGS_IMPORT_EXPORT;
@ -161,6 +162,8 @@ public class ReVancedPreferenceFragment extends PreferenceFragment {
importExportListDialogBuilder();
} else if (settings.equals(RETURN_YOUTUBE_USERNAME_ABOUT)) {
YouTubeDataAPIDialogBuilder.showDialog(mActivity);
} else if (settings.equals(REPLACE_NAVIGATION_BUTTON_ABOUT)) {
ResettableListPreference.showDialog(mActivity, CHANGE_START_PAGE, 0);
} else {
Logger.printDebug(() -> "Failed to find the right value: " + dataString);
}

View File

@ -22,12 +22,25 @@ public final class VideoInformation {
private static final float DEFAULT_YOUTUBE_MUSIC_PLAYBACK_SPEED = 1.0f;
private static final int DEFAULT_YOUTUBE_MUSIC_VIDEO_QUALITY = -2;
private static final String DEFAULT_YOUTUBE_MUSIC_VIDEO_QUALITY_STRING = getString("quality_auto");
/**
* Prefix present in all Age-restricted music player parameters signature.
*/
private static final String AGE_RESTRICTED_PLAYER_PARAMETER = "ygYQ";
/**
* Prefix present in all Sample player parameters signature.
*/
private static final String SAMPLES_PLAYER_PARAMETERS = "8AEB";
@NonNull
private static String videoId = "";
private static long videoLength = 0;
private static long videoTime = -1;
@NonNull
private static volatile String playerResponseVideoId = "";
private static volatile boolean playerResponseVideoIdIsSample;
/**
* The current playback speed
*/
@ -85,6 +98,65 @@ public final class VideoInformation {
videoId = newlyLoadedVideoId;
}
/**
* Differs from {@link #videoId} as this is the video id for the
* last player response received, which may not be the last video opened.
* <p>
* If Shorts are loading the background, this commonly will be
* different from the Short that is currently on screen.
* <p>
* For most use cases, you should instead use {@link #getVideoId()}.
*
* @return The id of the last video loaded, or an empty string if no videos have been loaded yet.
*/
@NonNull
public static String getPlayerResponseVideoId() {
return playerResponseVideoId;
}
/**
* @return If the last player response video id was a Sample.
*/
public static boolean lastPlayerResponseIsSample() {
return playerResponseVideoIdIsSample;
}
/**
* Injection point. Called off the main thread.
*
* @param videoId The id of the last video loaded.
*/
public static void setPlayerResponseVideoId(@NonNull String videoId) {
if (!playerResponseVideoId.equals(videoId)) {
playerResponseVideoId = videoId;
}
}
/**
* @return If the player parameter is for a Age-restricted video.
*/
public static boolean parameterIsAgeRestricted(@Nullable String parameter) {
return parameter != null && parameter.startsWith(AGE_RESTRICTED_PLAYER_PARAMETER);
}
/**
* @return If the player parameter is for a Sample.
*/
public static boolean parameterIsSample(@Nullable String parameter) {
return parameter != null && parameter.startsWith(SAMPLES_PLAYER_PARAMETERS);
}
/**
* Injection point.
*/
@Nullable
public static String newPlayerResponseParameter(@NonNull String videoId, @Nullable String playerParameter) {
playerResponseVideoIdIsSample = parameterIsSample(playerParameter);
Logger.printDebug(() -> "videoId: " + videoId + ", playerParameter: " + playerParameter);
return playerParameter; // Return the original value since we are observing and not modifying.
}
/**
* Seek on the current video.
* Does not function for playback of Shorts.

View File

@ -1,7 +1,9 @@
package app.revanced.extension.music.utils;
import android.app.Activity;
import android.app.AlertDialog;
import android.content.Context;
import android.content.Intent;
import android.view.ViewGroup;
import android.widget.FrameLayout;
@ -9,8 +11,20 @@ import androidx.annotation.NonNull;
import app.revanced.extension.music.settings.Settings;
import app.revanced.extension.shared.utils.PackageUtils;
import app.revanced.extension.shared.utils.ResourceUtils;
public class ExtendedUtils extends PackageUtils {
private static final String SETTINGS_CLASS_DESCRIPTOR = "com.google.android.apps.youtube.music.settings.SettingsCompatActivity";
private static final String SETTINGS_ATTRIBUTION_FRAGMENT_KEY = ":android:show_fragment";
private static final String SETTINGS_ATTRIBUTION_FRAGMENT_VALUE = "com.google.android.apps.youtube.music.settings.fragment.SettingsHeadersFragment";
private static final String SETTINGS_ATTRIBUTION_HEADER_KEY = ":android:no_headers";
private static final int SETTINGS_ATTRIBUTION_HEADER_VALUE = 1;
private static final String SHORTCUT_ACTION = "com.google.android.youtube.music.action.shortcut";
private static final String SHORTCUT_CLASS_DESCRIPTOR = "com.google.android.apps.youtube.music.activities.InternalMusicActivity";
private static final String SHORTCUT_TYPE = "com.google.android.youtube.music.action.shortcut_type";
private static final String SHORTCUT_ID_SEARCH = "Eh4IBRDTnQEYmgMiEwiZn+H0r5WLAxVV5OcDHcHRBmPqpd25AQA=";
private static final int SHORTCUT_TYPE_SEARCH = 1;
@SuppressWarnings("unused")
public static boolean isSpoofingToLessThan(@NonNull String versionName) {
@ -39,4 +53,35 @@ public class ExtendedUtils extends PackageUtils {
return params;
}
public static void openSearch() {
Activity mActivity = ResourceUtils.getActivity();
if (mActivity == null) {
return;
}
Intent intent = new Intent();
setSearchIntent(mActivity, intent);
mActivity.startActivity(intent);
}
public static void openSetting() {
Activity mActivity = ResourceUtils.getActivity();
if (mActivity == null) {
return;
}
Intent intent = new Intent();
intent.setPackage(mActivity.getPackageName());
intent.setClassName(mActivity, SETTINGS_CLASS_DESCRIPTOR);
intent.putExtra(SETTINGS_ATTRIBUTION_FRAGMENT_KEY, SETTINGS_ATTRIBUTION_FRAGMENT_VALUE);
intent.putExtra(SETTINGS_ATTRIBUTION_HEADER_KEY, SETTINGS_ATTRIBUTION_HEADER_VALUE);
mActivity.startActivity(intent);
}
public static void setSearchIntent(Activity mActivity, Intent intent) {
intent.setAction(SHORTCUT_ACTION);
intent.setClassName(mActivity, SHORTCUT_CLASS_DESCRIPTOR);
intent.setPackage(mActivity.getPackageName());
intent.putExtra(SHORTCUT_TYPE, SHORTCUT_TYPE_SEARCH);
intent.putExtra(SHORTCUT_ACTION, SHORTCUT_ID_SEARCH);
}
}

View File

@ -3,8 +3,7 @@ package app.revanced.extension.shared.patches;
import static java.lang.Boolean.FALSE;
import static java.lang.Boolean.TRUE;
import android.text.SpannableString;
import android.text.Spanned;
import static app.revanced.extension.shared.utils.Utils.newSpanUsingStylingOfAnotherSpan;
import androidx.annotation.NonNull;
@ -73,26 +72,13 @@ public class ReturnYouTubeUsernamePatch {
Logger.printDebug(() -> "ChannelRequest Stream is null, handle:" + handle);
return original;
}
return copySpannableString(original, userName);
return newSpanUsingStylingOfAnotherSpan(original, userName);
} catch (Exception ex) {
Logger.printException(() -> "onLithoTextLoaded failure", ex);
}
return original;
}
private static CharSequence copySpannableString(CharSequence original, String userName) {
if (original instanceof Spanned spanned) {
SpannableString newString = new SpannableString(userName);
Object[] spans = spanned.getSpans(0, spanned.length(), Object.class);
for (Object span : spans) {
int flags = spanned.getSpanFlags(span);
newString.setSpan(span, 0, newString.length(), flags);
}
return newString;
}
return original;
}
public enum DisplayFormat {
USERNAME_ONLY(null),
USERNAME_HANDLE(TRUE),

View File

@ -27,28 +27,28 @@ object YouTubeAppClient {
private val CLIENT_VERSION_IOS = if (forceAVC())
"17.40.5"
else
"19.29.1"
"20.10.4"
private const val DEVICE_MAKE_IOS = "Apple"
private const val OS_NAME_IOS = "iOS"
/**
* The device machine id for the iPhone 15 Pro Max (iPhone16,2),
* The device machine id for the iPhone 16 Pro Max (iPhone17,2),
* used to get HDR with AV1 hardware decoding.
* See [this GitHub Gist](https://gist.github.com/adamawolf/3048717) for more information.
*/
private val DEVICE_MODEL_IOS = if (forceAVC())
"iPhone12,5" // 11 Pro Max. (last device with iOS 13)
else
"iPhone16,2" // 15 Pro Max.
"iPhone17,2" // 16 Pro Max.
private val OS_VERSION_IOS = if (forceAVC())
"13.7.17H35" // Last release of iOS 13.
else
"17.7.2.21H221"
"18.3.2.22D82"
private val USER_AGENT_VERSION_IOS = if (forceAVC())
"13_7"
else
"17_7_2"
"18_3_2"
private val USER_AGENT_IOS = iOSUserAgent(PACKAGE_NAME_IOS, CLIENT_VERSION_IOS)
@ -69,7 +69,7 @@ object YouTubeAppClient {
private val CLIENT_VERSION_IOS_UNPLUGGED = if (forceAVC())
"6.45"
else
"8.33"
"9.10"
private val USER_AGENT_IOS_UNPLUGGED =
iOSUserAgent(PACKAGE_NAME_IOS_UNPLUGGED, CLIENT_VERSION_IOS_UNPLUGGED)
@ -92,35 +92,31 @@ object YouTubeAppClient {
* Package name for YouTube VR (Meta Quests): com.google.android.apps.youtube.vr.oculus
* Package name for YouTube VR (ByteDance Pico): com.google.android.apps.youtube.vr.pico
*/
private const val PACKAGE_NAME_ANDROID_VR = "com.google.android.apps.youtube.vr.pico"
private const val PACKAGE_NAME_ANDROID_VR = "com.google.android.apps.youtube.vr.oculus"
/**
* The hardcoded client version of the Android VR app used for InnerTube requests with this client.
*
* It can be extracted by getting the latest release version of the app on
* [the App Store page of the YouTube VR app](https://store-global.picoxr.com/en/detail/1/7270207384512020485/),
* in the `Information` section.
* [the App Store page of the YouTube app](https://www.meta.com/en-us/experiences/2002317119880945/),
* in the `Additional details` section.
*/
private const val CLIENT_VERSION_ANDROID_VR = "1.62.27"
/**
* The device machine id for the Pico 4 Ultra.
*
* For Pico 4 Ultra, there is no public firmware archive yet.
* The device machine id is taken from [this repository](https://github.com/Genymobile/scrcpy/issues/5659).
* The OS version and build ID are taken from [the signature key of OTA firmware](https://pico.crx.moe/docs/picoos-research/version-table#pico-4-ultra-series).
* The device machine id for the Meta Quest 3, used to get opus codec with the Android VR client.
* See [this GitLab](https://dumps.tadiphone.dev/dumps/oculus/eureka) for more information.
*/
private const val DEVICE_MODEL_ANDROID_VR = "A9210"
private const val DEVICE_MODEL_ANDROID_VR = "Quest 3"
private const val DEVICE_MAKE_ANDROID_VR = "Oculus"
private const val OS_VERSION_ANDROID_VR = "12"
/**
* The manufacturer is 'ByteDance', but the build prop is marked as 'Pico'.
* The SDK version for Android 12 is 31,
* but for some reason the build.props for the `Quest 3` state that the SDK version is 32.
*/
private const val DEVICE_MAKE_ANDROID_VR = "Pico"
private const val OS_VERSION_ANDROID_VR = "14"
private const val ANDROID_SDK_VERSION_ANDROID_VR = "34"
/**
* PICO OS 5.12.6 (Android 14)
*/
private const val BUILD_ID_ANDROID_VR = "UKQ1.240321.001"
private const val ANDROID_SDK_VERSION_ANDROID_VR = "32"
private const val BUILD_ID_ANDROID_VR = "SQ3A.220605.009.A1"
private val USER_AGENT_ANDROID_VR = androidUserAgent(
packageName = PACKAGE_NAME_ANDROID_VR,
@ -137,7 +133,7 @@ object YouTubeAppClient {
* Note: Audio track is not available
*/
private const val PACKAGE_NAME_ANDROID_UNPLUGGED = "com.google.android.apps.youtube.unplugged"
private const val CLIENT_VERSION_ANDROID_UNPLUGGED = "8.16.0"
private const val CLIENT_VERSION_ANDROID_UNPLUGGED = "9.09.1"
/**
* The device machine id for the Chromecast with Google TV 4K.
@ -147,8 +143,8 @@ object YouTubeAppClient {
private const val DEVICE_MAKE_ANDROID_UNPLUGGED = "Google"
private const val OS_VERSION_ANDROID_UNPLUGGED = "14"
private const val ANDROID_SDK_VERSION_ANDROID_UNPLUGGED = "34"
private const val BUILD_ID_ANDROID_UNPLUGGED = "UTT3.240625.001.K5"
private const val GMS_CORE_VERSION_CODE_ANDROID_UNPLUGGED = "244336107"
private const val BUILD_ID_ANDROID_UNPLUGGED = "UTTK.241210.003"
private const val GMS_CORE_VERSION_CODE_ANDROID_UNPLUGGED = "244738119"
private val USER_AGENT_ANDROID_UNPLUGGED = androidUserAgent(
packageName = PACKAGE_NAME_ANDROID_UNPLUGGED,
@ -165,7 +161,7 @@ object YouTubeAppClient {
* Note: Audio track is not available
*/
private const val PACKAGE_NAME_ANDROID_CREATOR = "com.google.android.apps.youtube.creator"
private const val CLIENT_VERSION_ANDROID_CREATOR = "23.47.101"
private const val CLIENT_VERSION_ANDROID_CREATOR = "25.10.100"
/**
* The device machine id for the Google Pixel 9 Pro Fold.
@ -176,7 +172,7 @@ object YouTubeAppClient {
private const val OS_VERSION_ANDROID_CREATOR = "15"
private const val ANDROID_SDK_VERSION_ANDROID_CREATOR = "35"
private const val BUILD_ID_ANDROID_CREATOR = "AP3A.241005.015.A2"
private const val GMS_CORE_VERSION_CODE_ANDROID_CREATOR = "244738035"
private const val GMS_CORE_VERSION_CODE_ANDROID_CREATOR = "250932035"
private val USER_AGENT_ANDROID_CREATOR = androidUserAgent(
packageName = PACKAGE_NAME_ANDROID_CREATOR,

View File

@ -23,6 +23,8 @@ public class SpoofStreamingDataPatch extends BlockRequestPatch {
BaseSettings.SPOOF_STREAMING_DATA_PO_TOKEN.get();
private static final String VISITOR_DATA =
BaseSettings.SPOOF_STREAMING_DATA_VISITOR_DATA.get();
private static final boolean SPOOF_STREAMING_DATA_SKIP_RESPONSE_ENCRYPTION =
SPOOF_STREAMING_DATA && BaseSettings.SPOOF_STREAMING_DATA_SKIP_RESPONSE_ENCRYPTION.get();
/**
* Any unreachable ip address. Used to intentionally fail requests.
@ -67,11 +69,11 @@ public class SpoofStreamingDataPatch extends BlockRequestPatch {
* Skip response encryption in OnesiePlayerRequest.
*/
public static boolean skipResponseEncryption(boolean original) {
if (!SPOOF_STREAMING_DATA) {
return original;
if (SPOOF_STREAMING_DATA_SKIP_RESPONSE_ENCRYPTION) {
return false;
}
return false;
return original;
}
/**

View File

@ -88,6 +88,7 @@ object PlayerRoutes {
botGuardPoToken: String = "",
visitorId: String = "",
setLocale: Boolean = false,
language: String = BaseSettings.SPOOF_STREAMING_DATA_LANGUAGE.get().language,
): ByteArray {
val innerTubeBody = JSONObject()
@ -108,7 +109,7 @@ object PlayerRoutes {
client.put(
"hl",
if (setLocale) {
BaseSettings.SPOOF_STREAMING_DATA_LANGUAGE.get().language
language
} else {
LOCALE_LANGUAGE
}

View File

@ -30,7 +30,7 @@ public class BaseSettings {
* These settings are used by YouTube Music.
* Some patches are in a shared path, so they are declared here.
*/
public static final BooleanSetting SPOOF_CLIENT = new BooleanSetting("revanced_spoof_client", TRUE, true);
public static final BooleanSetting SPOOF_CLIENT = new BooleanSetting("revanced_spoof_client", FALSE, true);
public static final EnumSetting<MusicAppClient.ClientType> SPOOF_CLIENT_TYPE = new EnumSetting<>("revanced_spoof_client_type", MusicAppClient.ClientType.IOS_MUSIC_6_21, true);
/**
@ -41,6 +41,7 @@ public class BaseSettings {
public static final EnumSetting<AppLanguage> SPOOF_STREAMING_DATA_LANGUAGE = new EnumSetting<>("revanced_spoof_streaming_data_language", AppLanguage.DEFAULT, new AudioStreamLanguageOverrideAvailability());
public static final BooleanSetting SPOOF_STREAMING_DATA_IOS_FORCE_AVC = new BooleanSetting("revanced_spoof_streaming_data_ios_force_avc", FALSE, true,
"revanced_spoof_streaming_data_ios_force_avc_user_dialog_message");
public static final BooleanSetting SPOOF_STREAMING_DATA_SKIP_RESPONSE_ENCRYPTION = new BooleanSetting("revanced_spoof_streaming_data_skip_response_encryption", TRUE, true);
public static final BooleanSetting SPOOF_STREAMING_DATA_STATS_FOR_NERDS = new BooleanSetting("revanced_spoof_streaming_data_stats_for_nerds", TRUE);
// Client type must be last spoof setting due to cyclic references.
public static final EnumSetting<YouTubeAppClient.ClientType> SPOOF_STREAMING_DATA_TYPE = new EnumSetting<>("revanced_spoof_streaming_data_type", YouTubeAppClient.ClientType.ANDROID_UNPLUGGED, true);

View File

@ -24,9 +24,6 @@ import app.revanced.extension.shared.utils.Logger;
import app.revanced.extension.shared.utils.StringRef;
import app.revanced.extension.shared.utils.Utils;
/**
* @noinspection rawtypes
*/
@SuppressWarnings("unused")
public abstract class Setting<T> {
@ -128,6 +125,7 @@ public abstract class Setting<T> {
/**
* @return All settings that have been created, sorted by keys.
* @noinspection Java8ListSort
*/
@NonNull
private static List<Setting<?>> allLoadedSettingsSorted() {
@ -171,7 +169,6 @@ public abstract class Setting<T> {
/**
* Confirmation message to display, if the user tries to change the setting from the default value.
* Currently this works only for Boolean setting types.
*/
@Nullable
public final StringRef userDialogMessage;
@ -271,6 +268,7 @@ public abstract class Setting<T> {
* <p>
* This method will be deleted in the future.
*/
@SuppressWarnings("rawtypes")
public static void migrateFromOldPreferences(@NonNull SharedPrefCategory oldPrefs, @NonNull Setting setting, String settingKey) {
if (!oldPrefs.preferences.contains(settingKey)) {
return; // Nothing to do.
@ -452,6 +450,7 @@ public abstract class Setting<T> {
boolean rebootSettingChanged = false;
int numberOfSettingsImported = 0;
//noinspection rawtypes
for (Setting setting : SETTINGS) {
String key = setting.getImportExportKey();
if (json.has(key)) {

View File

@ -12,6 +12,7 @@ import android.preference.EditTextPreference;
import android.preference.ListPreference;
import android.preference.Preference;
import android.preference.PreferenceFragment;
import android.preference.PreferenceGroup;
import android.preference.PreferenceManager;
import android.preference.PreferenceScreen;
import android.preference.SwitchPreference;
@ -21,6 +22,9 @@ import android.widget.ListView;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import java.util.Objects;
import app.revanced.extension.shared.settings.BaseSettings;
import app.revanced.extension.shared.settings.BooleanSetting;
import app.revanced.extension.shared.settings.Setting;
import app.revanced.extension.shared.utils.Logger;
@ -48,10 +52,7 @@ public abstract class AbstractPreferenceFragment extends PreferenceFragment {
private final SharedPreferences.OnSharedPreferenceChangeListener listener = (sharedPreferences, str) -> {
try {
if (str == null) {
return;
}
Setting<?> setting = Setting.getSettingFromPath(str);
Setting<?> setting = Setting.getSettingFromPath(Objects.requireNonNull(str));
if (setting == null) {
return;
}
@ -59,24 +60,23 @@ public abstract class AbstractPreferenceFragment extends PreferenceFragment {
if (pref == null) {
return;
}
Logger.printDebug(() -> "Preference changed: " + setting.key);
if (!settingImportInProgress && !showingUserDialogMessage) {
if (setting.userDialogMessage != null && !prefIsSetToDefault(pref, setting)) {
// Do not change the setting yet, to allow preserving whatever
// list/text value was previously set if it needs to be reverted.
showSettingUserDialogConfirmation(pref, setting);
return;
} else if (setting.rebootApp) {
showRestartDialog(getContext());
}
}
// Apply 'Setting <- Preference', unless during importing when it needs to be 'Setting -> Preference'.
updatePreference(pref, setting, true, settingImportInProgress);
// Update any other preference availability that may now be different.
updateUIAvailability();
if (settingImportInProgress) {
return;
}
if (!showingUserDialogMessage) {
if (setting.userDialogMessage != null && ((SwitchPreference) pref).isChecked() != (Boolean) setting.defaultValue) {
showSettingUserDialogConfirmation((SwitchPreference) pref, (BooleanSetting) setting);
} else if (setting.rebootApp) {
showRestartDialog(getActivity());
}
}
} catch (Exception ex) {
Logger.printException(() -> "OnSharedPreferenceChangeListener failure", ex);
}
@ -90,14 +90,16 @@ public abstract class AbstractPreferenceFragment extends PreferenceFragment {
* so all app specific {@link Setting} instances are loaded before this method returns.
*/
protected void initialize() {
final int id = getXmlIdentifier("revanced_prefs");
final int identifier = getXmlIdentifier("revanced_prefs");
if (identifier == 0) return;
addPreferencesFromResource(identifier);
if (id == 0) return;
addPreferencesFromResource(id);
Utils.sortPreferenceGroups(getPreferenceScreen());
PreferenceScreen screen = getPreferenceScreen();
Utils.sortPreferenceGroups(screen);
Utils.setPreferenceTitlesToMultiLineIfNeeded(screen);
}
private void showSettingUserDialogConfirmation(SwitchPreference switchPref, BooleanSetting setting) {
private void showSettingUserDialogConfirmation(Preference pref, Setting<?> setting) {
Utils.verifyOnMainThread();
final var context = getActivity();
@ -107,12 +109,19 @@ public abstract class AbstractPreferenceFragment extends PreferenceFragment {
.setTitle(android.R.string.dialog_alert_title)
.setMessage(setting.userDialogMessage.toString())
.setPositiveButton(android.R.string.ok, (dialog, id) -> {
// User confirmed, save to the Setting.
updatePreference(pref, setting, true, false);
// Update availability of other preferences that may be changed.
updateUIAvailability();
if (setting.rebootApp) {
showRestartDialog(context);
}
})
.setNegativeButton(android.R.string.cancel, (dialog, id) -> {
switchPref.setChecked(setting.defaultValue); // Recursive call that resets the Setting value.
// Restore whatever the setting was before the change.
updatePreference(pref, setting, true, true);
})
.setOnDismissListener(dialog -> showingUserDialogMessage = false)
.setCancelable(false)
@ -123,7 +132,7 @@ public abstract class AbstractPreferenceFragment extends PreferenceFragment {
* Updates all Preferences values and their availability using the current values in {@link Setting}.
*/
protected void updateUIToSettingValues() {
updatePreferenceScreen(getPreferenceScreen(), true, true);
updatePreferenceScreen(getPreferenceScreen(), true,true);
}
/**
@ -133,24 +142,48 @@ public abstract class AbstractPreferenceFragment extends PreferenceFragment {
updatePreferenceScreen(getPreferenceScreen(), false, false);
}
/**
* @return If the preference is currently set to the default value of the Setting.
*/
protected boolean prefIsSetToDefault(Preference pref, Setting<?> setting) {
if (pref instanceof SwitchPreference switchPref) {
return switchPref.isChecked() == (Boolean) setting.defaultValue;
}
if (pref instanceof EditTextPreference editPreference) {
return editPreference.getText().equals(setting.defaultValue.toString());
}
if (pref instanceof ListPreference listPref) {
return listPref.getValue().equals(setting.defaultValue.toString());
}
throw new IllegalStateException("Must override method to handle "
+ "preference type: " + pref.getClass());
}
/**
* Syncs all UI Preferences to any {@link Setting} they represent.
*/
private void updatePreferenceScreen(@NonNull PreferenceScreen screen,
private void updatePreferenceScreen(@NonNull PreferenceGroup group,
boolean syncSettingValue,
boolean applySettingToPreference) {
// Alternatively this could iterate thru all Settings and check for any matching Preferences,
// but there are many more Settings than UI preferences so it's more efficient to only check
// the Preferences.
for (int i = 0, prefCount = screen.getPreferenceCount(); i < prefCount; i++) {
Preference pref = screen.getPreference(i);
if (pref instanceof PreferenceScreen preferenceScreen) {
updatePreferenceScreen(preferenceScreen, syncSettingValue, applySettingToPreference);
for (int i = 0, prefCount = group.getPreferenceCount(); i < prefCount; i++) {
Preference pref = group.getPreference(i);
if (pref instanceof PreferenceGroup subGroup) {
updatePreferenceScreen(subGroup, syncSettingValue, applySettingToPreference);
} else if (pref.hasKey()) {
String key = pref.getKey();
Setting<?> setting = Setting.getSettingFromPath(key);
if (setting != null) {
updatePreference(pref, setting, syncSettingValue, applySettingToPreference);
} else if (BaseSettings.ENABLE_DEBUG_LOGGING.get() && (pref instanceof SwitchPreference
|| pref instanceof EditTextPreference || pref instanceof ListPreference)) {
// Probably a typo in the patches preference declaration.
Logger.printException(() -> "Preference key has no setting: " + key);
}
}
}
@ -166,26 +199,26 @@ public abstract class AbstractPreferenceFragment extends PreferenceFragment {
protected void syncSettingWithPreference(@NonNull Preference pref,
@NonNull Setting<?> setting,
boolean applySettingToPreference) {
if (pref instanceof SwitchPreference switchPreference) {
if (pref instanceof SwitchPreference switchPref) {
BooleanSetting boolSetting = (BooleanSetting) setting;
if (applySettingToPreference) {
switchPreference.setChecked(boolSetting.get());
switchPref.setChecked(boolSetting.get());
} else {
BooleanSetting.privateSetValue(boolSetting, switchPreference.isChecked());
BooleanSetting.privateSetValue(boolSetting, switchPref.isChecked());
}
} else if (pref instanceof EditTextPreference editTextPreference) {
} else if (pref instanceof EditTextPreference editPreference) {
if (applySettingToPreference) {
editTextPreference.setText(setting.get().toString());
editPreference.setText(setting.get().toString());
} else {
Setting.privateSetValueFromString(setting, editTextPreference.getText());
Setting.privateSetValueFromString(setting, editPreference.getText());
}
} else if (pref instanceof ListPreference listPreference) {
} else if (pref instanceof ListPreference listPref) {
if (applySettingToPreference) {
listPreference.setValue(setting.get().toString());
listPref.setValue(setting.get().toString());
} else {
Setting.privateSetValueFromString(setting, listPreference.getValue());
Setting.privateSetValueFromString(setting, listPref.getValue());
}
updateListPreferenceSummary(listPreference, setting);
updateListPreferenceSummary(listPref, setting);
} else {
Logger.printException(() -> "Setting cannot be handled: " + pref.getClass() + ": " + pref);
}

View File

@ -7,6 +7,7 @@ import android.view.animation.Animation;
import android.view.animation.AnimationUtils;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
@SuppressWarnings({"unused", "deprecation", "DiscouragedApi"})
public class ResourceUtils extends Utils {
@ -100,13 +101,19 @@ public class ResourceUtils extends Utils {
return getIdentifier(str, ResourceType.XML);
}
@Nullable
public static Animation getAnimation(@NonNull String str) {
try {
int identifier = getAnimIdentifier(str);
if (identifier == 0) {
handleException(str, ResourceType.ANIM);
identifier = android.R.anim.fade_in;
}
return AnimationUtils.loadAnimation(getContext(), identifier);
} catch (Exception ex) {
handleException(str, ResourceType.ANIM);
}
return null;
}
public static int getColor(@NonNull String str) {

View File

@ -3,6 +3,8 @@ package app.revanced.extension.shared.utils;
import android.annotation.SuppressLint;
import android.app.Activity;
import android.app.AlertDialog;
import android.app.Dialog;
import android.app.DialogFragment;
import android.app.Fragment;
import android.content.ClipboardManager;
import android.content.Context;
@ -13,11 +15,14 @@ import android.net.ConnectivityManager;
import android.net.NetworkInfo;
import android.net.Uri;
import android.os.Build;
import android.os.Bundle;
import android.os.Handler;
import android.os.Looper;
import android.preference.Preference;
import android.preference.PreferenceGroup;
import android.preference.PreferenceScreen;
import android.text.SpannableString;
import android.text.Spanned;
import android.util.TypedValue;
import android.view.View;
import android.view.ViewGroup;
@ -43,17 +48,18 @@ import java.util.concurrent.Future;
import java.util.concurrent.SynchronousQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import java.util.regex.Pattern;
import app.revanced.extension.shared.settings.AppLanguage;
import app.revanced.extension.shared.settings.BaseSettings;
import app.revanced.extension.shared.settings.BooleanSetting;
import kotlin.text.Regex;
@SuppressWarnings("deprecation")
public class Utils {
private static WeakReference<Activity> activityRef = new WeakReference<>(null);
private static WeakReference<Context> contextRef = new WeakReference<>(null);
@SuppressLint("StaticFieldLeak")
private static volatile Context context;
protected Utils() {
} // utility class
@ -274,17 +280,15 @@ public class Utils {
}
public static Context getContext() {
Context mContext = contextRef.get();
if (mContext == null) {
if (context == null) {
Logger.initializationException(Utils.class, "Context is null, returning null!", null);
}
return mContext;
return context;
}
public static Resources getResources() {
Context mContext = contextRef.get();
if (mContext != null) {
return mContext.getResources();
if (context != null) {
return context.getResources();
}
Activity mActivity = activityRef.get();
if (mActivity != null) {
@ -347,7 +351,7 @@ public class Utils {
}
// Must initially set context to check the app language.
contextRef = new WeakReference<>(appContext);
context = appContext;
Logger.initializationInfo(Utils.class, "Set context: " + appContext);
AppLanguage language = BaseSettings.REVANCED_LANGUAGE.get();
@ -355,7 +359,7 @@ public class Utils {
// Create a new context with the desired language.
Configuration config = appContext.getResources().getConfiguration();
config.setLocale(language.getLocale());
contextRef = new WeakReference<>(appContext.createConfigurationContext(config));
context = appContext.createConfigurationContext(config);
}
}
@ -364,8 +368,7 @@ public class Utils {
}
public static void setClipboard(@NonNull String text, @Nullable String toastMessage) {
Context mContext = contextRef.get();
if (mContext != null && mContext.getSystemService(Context.CLIPBOARD_SERVICE) instanceof ClipboardManager clipboardManager) {
if (context != null && context.getSystemService(Context.CLIPBOARD_SERVICE) instanceof ClipboardManager clipboardManager) {
android.content.ClipData clip = android.content.ClipData.newPlainText("ReVanced", text);
clipboardManager.setPrimaryClip(clip);
@ -508,6 +511,26 @@ public class Utils {
return false;
}
public static CharSequence newSpanUsingStylingOfAnotherSpan(@Nullable CharSequence sourceStyle, @NonNull CharSequence newSpanText) {
if (sourceStyle instanceof Spanned spanned) {
return newSpanUsingStylingOfAnotherSpan(spanned, newSpanText);
}
return sourceStyle;
}
public static SpannableString newSpanUsingStylingOfAnotherSpan(@NonNull Spanned sourceStyle, @NonNull CharSequence newSpanText) {
if (sourceStyle == newSpanText && sourceStyle instanceof SpannableString spannableString) {
return spannableString; // Nothing to do.
}
SpannableString destination = new SpannableString(newSpanText);
Object[] spans = sourceStyle.getSpans(0, sourceStyle.length(), Object.class);
for (Object span : spans) {
destination.setSpan(span, 0, destination.length(), sourceStyle.getSpanFlags(span));
}
return destination;
}
/**
* @return whether the device's API level is higher than a specific SDK version.
*/
@ -516,23 +539,96 @@ public class Utils {
}
public static int dpToPx(float dp) {
Context mContext = contextRef.get();
if (mContext == null) {
if (context == null) {
return (int) dp;
} else {
return (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, dp, mContext.getResources().getDisplayMetrics());
return (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, dp, context.getResources().getDisplayMetrics());
}
}
public static int dpToPx(int dp) {
Context mContext = contextRef.get();
if (mContext == null) {
if (context == null) {
return dp;
} else {
return (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, dp, mContext.getResources().getDisplayMetrics());
return (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, dp, context.getResources().getDisplayMetrics());
}
}
/**
* Ignore this class. It must be public to satisfy Android requirements.
*/
public static final class DialogFragmentWrapper extends DialogFragment {
private Dialog dialog;
@Nullable
private DialogFragmentOnStartAction onStartAction;
@Override
public void onSaveInstanceState(Bundle outState) {
// Do not call super method to prevent state saving.
}
@NonNull
@Override
public Dialog onCreateDialog(Bundle savedInstanceState) {
return dialog;
}
@Override
public void onStart() {
try {
super.onStart();
if (onStartAction != null) {
onStartAction.onStart((AlertDialog) getDialog());
}
} catch (Exception ex) {
Logger.printException(() -> "onStart failure: " + dialog.getClass().getSimpleName(), ex);
}
}
}
/**
* Interface for {@link #showDialog(Activity, AlertDialog, boolean, DialogFragmentOnStartAction)}.
*/
@FunctionalInterface
public interface DialogFragmentOnStartAction {
void onStart(AlertDialog dialog);
}
public static void showDialog(Activity activity, AlertDialog dialog) {
showDialog(activity, dialog, true, null);
}
/**
* Utility method to allow showing an AlertDialog on top of other alert dialogs.
* Calling this will always display the dialog on top of all other dialogs
* previously called using this method.
* <br>
* Be aware the on start action can be called multiple times for some situations,
* such as the user switching apps without dismissing the dialog then switching back to this app.
*<br>
* This method is only useful during app startup and multiple patches may show their own dialog,
* and the most important dialog can be called last (using a delay) so it's always on top.
*<br>
* For all other situations it's better to not use this method and
* call {@link AlertDialog#show()} on the dialog.
*/
public static void showDialog(Activity activity,
AlertDialog dialog,
boolean isCancelable,
@Nullable DialogFragmentOnStartAction onStartAction) {
verifyOnMainThread();
DialogFragmentWrapper fragment = new DialogFragmentWrapper();
fragment.dialog = dialog;
fragment.onStartAction = onStartAction;
fragment.setCancelable(isCancelable);
fragment.show(activity.getFragmentManager(), null);
}
/**
* Safe to call from any thread
*/
@ -550,20 +646,18 @@ public class Utils {
private static void showToast(@NonNull String messageToToast, int toastDuration) {
Objects.requireNonNull(messageToToast);
runOnMainThreadNowOrLater(() -> {
Context mContext = contextRef.get();
if (mContext == null) {
if (context == null) {
Logger.initializationException(Utils.class, "Cannot show toast (context is null): " + messageToToast, null);
} else {
Logger.printDebug(() -> "Showing toast: " + messageToToast);
Toast.makeText(mContext, messageToToast, toastDuration).show();
Toast.makeText(context, messageToToast, toastDuration).show();
}
});
}
public static boolean isLandscapeOrientation() {
Context mContext = contextRef.get();
if (mContext == null) return false;
final int orientation = mContext.getResources().getConfiguration().orientation;
if (context == null) return false;
final int orientation = context.getResources().getConfiguration().orientation;
return orientation == Configuration.ORIENTATION_LANDSCAPE;
}
@ -654,8 +748,7 @@ public class Utils {
@SuppressLint("MissingPermission") // permission already included in YouTube
public static NetworkType getNetworkType() {
Context mContext = contextRef.get();
if (mContext == null || !(mContext.getSystemService(Context.CONNECTIVITY_SERVICE) instanceof ConnectivityManager cm))
if (context == null || !(context.getSystemService(Context.CONNECTIVITY_SERVICE) instanceof ConnectivityManager cm))
return NetworkType.NONE;
final NetworkInfo networkInfo = cm.getActiveNetworkInfo();
@ -744,14 +837,14 @@ public class Utils {
}
}
private static final Regex punctuationRegex = new Regex("\\p{P}+");
private static final Pattern punctuationPattern = Pattern.compile("\\p{P}+");
/**
* Strips all punctuation and converts to lower case. A null parameter returns an empty string.
*/
public static String removePunctuationConvertToLowercase(@Nullable CharSequence original) {
if (original == null) return "";
return punctuationRegex.replace(original, "").toLowerCase();
return punctuationPattern.matcher(original).replaceAll("").toLowerCase();
}
/**
@ -762,7 +855,6 @@ public class Utils {
* If a preference has no key or no {@link Sort} suffix,
* then the preferences are left unsorted.
*/
@SuppressWarnings("deprecation")
public static void sortPreferenceGroups(@NonNull PreferenceGroup group) {
Sort groupSort = Sort.fromKey(group.getKey(), Sort.UNSORTED);
SortedMap<String, Preference> preferences = new TreeMap<>();
@ -771,8 +863,8 @@ public class Utils {
Preference preference = group.getPreference(i);
final Sort preferenceSort;
if (preference instanceof PreferenceGroup preferenceGroup) {
sortPreferenceGroups(preferenceGroup);
if (preference instanceof PreferenceGroup subGroup) {
sortPreferenceGroups(subGroup);
preferenceSort = groupSort; // Sort value for groups is for it's content, not itself.
} else {
// Allow individual preferences to set a key sorting.
@ -782,13 +874,16 @@ public class Utils {
final String sortValue;
switch (preferenceSort) {
case BY_TITLE ->
case BY_TITLE:
sortValue = removePunctuationConvertToLowercase(preference.getTitle());
case BY_KEY -> sortValue = preference.getKey();
case UNSORTED -> {
break;
case BY_KEY:
sortValue = preference.getKey();
break;
case UNSORTED:
continue; // Keep original sorting.
}
default -> throw new IllegalStateException();
default:
throw new IllegalStateException();
}
preferences.put(sortValue, preference);
@ -798,7 +893,7 @@ public class Utils {
for (Preference pref : preferences.values()) {
int order = index++;
// If the preference is a PreferenceScreen or is an intent preference, move to the top.
// Move any screens, intents, and the one off About preference to the top.
if (pref instanceof PreferenceScreen || pref.getIntent() != null) {
// Arbitrary high number.
order -= 1000;
@ -807,4 +902,32 @@ public class Utils {
pref.setOrder(order);
}
}
/**
* Set all preferences to multiline titles if the device is not using an English variant.
* The English strings are heavily scrutinized and all titles fit on screen
* except 2 or 3 preference strings and those do not affect readability.
* <p>
* Allowing multiline for those 2 or 3 English preferences looks weird and out of place,
* and visually it looks better to clip the text and keep all titles 1 line.
*/
public static void setPreferenceTitlesToMultiLineIfNeeded(PreferenceGroup group) {
if (!isSDKAbove(26)) {
return;
}
String revancedLocale = Utils.getContext().getResources().getConfiguration().locale.getLanguage();
if (revancedLocale.equals(Locale.ENGLISH.getLanguage())) {
return;
}
for (int i = 0, prefCount = group.getPreferenceCount(); i < prefCount; i++) {
Preference pref = group.getPreference(i);
pref.setSingleLineTitle(false);
if (pref instanceof PreferenceGroup subGroup) {
setPreferenceTitlesToMultiLineIfNeeded(subGroup);
}
}
}
}

View File

@ -189,14 +189,13 @@ public final class AlternativeThumbnailsPatch {
// Unknown tab, treat as the home tab;
return homeOption;
}
if (selectedNavButton == NavigationButton.HOME) {
return homeOption;
}
if (selectedNavButton == NavigationButton.SUBSCRIPTIONS || selectedNavButton == NavigationButton.NOTIFICATIONS) {
return subscriptionsOption;
}
// A library tab variant is active.
return libraryOption;
return switch (selectedNavButton) {
case SUBSCRIPTIONS, NOTIFICATIONS -> subscriptionsOption;
case LIBRARY -> libraryOption;
// Home or explore tab.
default -> homeOption;
};
}
/**

View File

@ -7,12 +7,15 @@ import app.revanced.extension.shared.patches.components.ByteArrayFilterGroupList
import app.revanced.extension.shared.patches.components.Filter;
import app.revanced.extension.shared.patches.components.StringFilterGroup;
import app.revanced.extension.youtube.settings.Settings;
import app.revanced.extension.youtube.shared.EngagementPanel;
import app.revanced.extension.youtube.shared.RootView;
@SuppressWarnings("unused")
public final class DescriptionsFilter extends Filter {
private final ByteArrayFilterGroupList macroMarkerShelfGroupList = new ByteArrayFilterGroupList();
private final StringFilterGroup howThisWasMadeSection;
private final StringFilterGroup horizontalShelf;
private final StringFilterGroup infoCardsSection;
private final StringFilterGroup macroMarkerShelf;
private final StringFilterGroup shoppingLinks;
@ -54,6 +57,13 @@ public final class DescriptionsFilter extends Filter {
"how_this_was_made_section.eml"
);
// In the latest YouTube, the Attribute section has the same path as the Carousel shelf.
// To hide only the Attribute section, check if the Description panel is open.
horizontalShelf = new StringFilterGroup(
Settings.HIDE_ATTRIBUTES_SECTION,
"horizontal_shelf.eml"
);
infoCardsSection = new StringFilterGroup(
Settings.HIDE_INFO_CARDS_SECTION,
"infocards_section.eml"
@ -72,6 +82,7 @@ public final class DescriptionsFilter extends Filter {
addPathCallbacks(
howThisWasMadeSection,
horizontalShelf,
infoCardsSection,
macroMarkerShelf,
shoppingLinks
@ -104,6 +115,16 @@ public final class DescriptionsFilter extends Filter {
if (!macroMarkerShelfGroupList.check(protobufBufferArray).isFiltered()) {
return false;
}
} else if (matchedGroup == horizontalShelf) {
if (contentIndex != 0) {
return false;
}
if (!RootView.isPlayerActive()) {
return false;
}
if (!EngagementPanel.isDescription()) {
return false;
}
}
return super.isFiltered(path, identifier, allValue, protobufBufferArray, matchedGroup, contentType, contentIndex);

View File

@ -556,14 +556,13 @@ public final class KeywordContentFilter extends Filter {
if (selectedNavButton == null) {
return hideHome; // Unknown tab, treat the same as home.
}
if (selectedNavButton == NavigationButton.HOME) {
return hideHome;
}
if (selectedNavButton == NavigationButton.SUBSCRIPTIONS) {
return hideSubscriptions;
}
// User is in the Library or Notifications tab.
return false;
return switch (selectedNavButton) {
case HOME, EXPLORE -> hideHome;
case SUBSCRIPTIONS -> hideSubscriptions;
// User is in the Library or notifications.
default -> false;
};
}
private void updateStats(boolean videoWasHidden, @Nullable String keyword) {

View File

@ -0,0 +1,151 @@
package app.revanced.extension.youtube.patches.general;
import static java.lang.Boolean.FALSE;
import static java.lang.Boolean.TRUE;
import static app.revanced.extension.youtube.shared.NavigationBar.NavigationButton;
import android.view.View;
import androidx.annotation.Nullable;
import org.apache.commons.lang3.BooleanUtils;
import java.util.Objects;
import app.revanced.extension.shared.utils.Logger;
import app.revanced.extension.shared.utils.PackageUtils;
import app.revanced.extension.youtube.settings.Settings;
import app.revanced.extension.youtube.shared.PlayerType;
import app.revanced.extension.youtube.shared.RootView;
@SuppressWarnings("unused")
public class ChangeFormFactorPatch {
public enum FormFactor {
/**
* Unmodified, and same as un-patched.
*/
DEFAULT(null, null, null),
/**
* <pre>
* Some changes include:
* - Explore tab is present.
* - watch history is missing.
* - feed thumbnails fade in.
*/
UNKNOWN(0, null, null),
SMALL(1, null, TRUE),
SMALL_WIDTH_DP(1, 480, TRUE),
LARGE(2, null, FALSE),
LARGE_WIDTH_DP(2, 600, FALSE),
/**
* Cars with 'Google built-in'.
* Layout seems identical to {@link #UNKNOWN}
* even when using an Android Automotive device.
*/
AUTOMOTIVE(3, null, null),
WEARABLE(4, null, null);
@Nullable
final Integer formFactorType;
@Nullable
final Integer widthDp;
@Nullable
final Boolean setMinimumDp;
FormFactor(@Nullable Integer formFactorType, @Nullable Integer widthDp, @Nullable Boolean setMinimumDp) {
this.formFactorType = formFactorType;
this.widthDp = widthDp;
this.setMinimumDp = setMinimumDp;
}
private boolean setMinimumDp() {
return BooleanUtils.isTrue(setMinimumDp);
}
}
private static final FormFactor FORM_FACTOR = Settings.CHANGE_FORM_FACTOR.get();
@Nullable
private static final Integer FORM_FACTOR_TYPE = FORM_FACTOR.formFactorType;
private static final boolean USING_AUTOMOTIVE_TYPE = Objects.requireNonNull(
FormFactor.AUTOMOTIVE.formFactorType).equals(FORM_FACTOR_TYPE);
/**
* Injection point.
*/
public static int getFormFactor(int original) {
if (FORM_FACTOR_TYPE == null) return original;
if (USING_AUTOMOTIVE_TYPE) {
// Do not change if the player is opening or is opened,
// otherwise the video description cannot be opened.
PlayerType current = PlayerType.getCurrent();
if (current.isMaximizedOrFullscreen() || current == PlayerType.WATCH_WHILE_SLIDING_MINIMIZED_MAXIMIZED) {
Logger.printDebug(() -> "Using original form factor for player");
return original;
}
if (!RootView.isSearchBarActive()) {
// Automotive type shows error 400 when opening a channel page and using some explore tab.
// This is a bug in unpatched YouTube that occurs on actual Android Automotive devices.
// Work around the issue by using the original form factor if not in search and the
// navigation back button is present.
if (RootView.isBackButtonVisible()) {
Logger.printDebug(() -> "Using original form factor, as back button is visible without search present");
return original;
}
// Do not change library tab otherwise watch history is hidden.
// Do this check last since the current navigation button is required.
if (NavigationButton.getSelectedNavigationButton() == NavigationButton.LIBRARY) {
return original;
}
}
}
return FORM_FACTOR_TYPE;
}
/**
* Injection point.
*/
public static int getWidthDp(int original) {
if (FORM_FACTOR_TYPE == null) return original;
Integer widthDp = FORM_FACTOR.widthDp;
if (widthDp == null) {
return original;
}
final int smallestScreenWidthDp = PackageUtils.getSmallestScreenWidthDp();
if (smallestScreenWidthDp == 0) {
return original;
}
return FORM_FACTOR.setMinimumDp()
? Math.min(smallestScreenWidthDp, widthDp)
: Math.max(smallestScreenWidthDp, widthDp);
}
public static boolean phoneLayoutEnabled() {
return Objects.equals(FORM_FACTOR.formFactorType, 1);
}
public static boolean tabletLayoutEnabled() {
return Objects.equals(FORM_FACTOR.formFactorType, 2);
}
/**
* Injection point.
*/
public static void navigationTabCreated(NavigationButton button, View tabView) {
// On first startup of the app the navigation buttons are fetched and updated.
// If the user immediately opens the 'You' or opens a video, then the call to
// update the navigtation buttons will use the non automotive form factor
// and the explore tab is missing.
// Fixing this is not so simple because of the concurrent calls for the player and You tab.
// For now, always hide the explore tab.
if (USING_AUTOMOTIVE_TYPE && button == NavigationButton.EXPLORE) {
tabView.setVisibility(View.GONE);
}
}
}

View File

@ -478,6 +478,10 @@ public class GeneralPatch {
return Settings.HIDE_SEARCH_TERM_THUMBNAIL.get();
}
public static boolean hideSearchTermThumbnail(boolean original) {
return !hideSearchTermThumbnail() && original;
}
private static final boolean hideImageSearchButton = Settings.HIDE_IMAGE_SEARCH_BUTTON.get();
private static final boolean hideVoiceSearchButton = Settings.HIDE_VOICE_SEARCH_BUTTON.get();

View File

@ -1,79 +0,0 @@
package app.revanced.extension.youtube.patches.general;
import static java.lang.Boolean.FALSE;
import static java.lang.Boolean.TRUE;
import androidx.annotation.Nullable;
import org.apache.commons.lang3.BooleanUtils;
import java.util.Objects;
import app.revanced.extension.shared.utils.PackageUtils;
import app.revanced.extension.youtube.settings.Settings;
@SuppressWarnings("unused")
public final class LayoutSwitchPatch {
public enum FormFactor {
/**
* Unmodified type, and same as un-patched.
*/
ORIGINAL(null, null, null),
SMALL_FORM_FACTOR(1, null, TRUE),
SMALL_FORM_FACTOR_WIDTH_DP(1, 480, TRUE),
LARGE_FORM_FACTOR(2, null, FALSE),
LARGE_FORM_FACTOR_WIDTH_DP(2, 600, FALSE);
@Nullable
final Integer formFactorType;
@Nullable
final Integer widthDp;
@Nullable
final Boolean setMinimumDp;
FormFactor(@Nullable Integer formFactorType, @Nullable Integer widthDp, @Nullable Boolean setMinimumDp) {
this.formFactorType = formFactorType;
this.widthDp = widthDp;
this.setMinimumDp = setMinimumDp;
}
private boolean setMinimumDp() {
return BooleanUtils.isTrue(setMinimumDp);
}
}
private static final FormFactor FORM_FACTOR = Settings.CHANGE_LAYOUT.get();
public static int getFormFactor(int original) {
Integer formFactorType = FORM_FACTOR.formFactorType;
return formFactorType == null
? original
: formFactorType;
}
public static int getWidthDp(int original) {
Integer widthDp = FORM_FACTOR.widthDp;
if (widthDp == null) {
return original;
}
final int smallestScreenWidthDp = PackageUtils.getSmallestScreenWidthDp();
if (smallestScreenWidthDp == 0) {
return original;
}
return FORM_FACTOR.setMinimumDp()
? Math.min(smallestScreenWidthDp, widthDp)
: Math.max(smallestScreenWidthDp, widthDp);
}
public static boolean phoneLayoutEnabled() {
return Objects.equals(FORM_FACTOR.formFactorType, 1);
}
public static boolean tabletLayoutEnabled() {
return Objects.equals(FORM_FACTOR.formFactorType, 2);
}
}

View File

@ -25,8 +25,11 @@ import app.revanced.extension.shared.utils.Logger;
import app.revanced.extension.shared.utils.Utils;
public abstract class BottomControlButton {
@Nullable
private static final Animation fadeIn;
@Nullable
private static final Animation fadeOut;
@Nullable
private static final Animation fadeOutImmediate;
private final ColorFilter cf =
@ -40,29 +43,35 @@ public abstract class BottomControlButton {
static {
fadeIn = getAnimation("fade_in");
if (fadeIn != null) {
// android.R.integer.config_shortAnimTime, 200
fadeIn.setDuration(getInteger("fade_duration_fast"));
}
fadeOut = getAnimation("fade_out");
if (fadeOut != null) {
// android.R.integer.config_mediumAnimTime, 400
fadeOut.setDuration(getInteger("fade_overlay_fade_duration"));
}
fadeOutImmediate = getAnimation("abc_fade_out");
if (fadeOutImmediate != null) {
// android.R.integer.config_shortAnimTime, 200
fadeOutImmediate.setDuration(getInteger("fade_duration_fast"));
}
}
@NonNull
@Nullable
public static Animation getButtonFadeIn() {
return fadeIn;
}
@NonNull
@Nullable
public static Animation getButtonFadeOut() {
return fadeOut;
}
@NonNull
@Nullable
public static Animation getButtonFadeOutImmediate() {
return fadeOutImmediate;
}
@ -153,11 +162,15 @@ public abstract class BottomControlButton {
imageView.clearAnimation();
if (visible && setting.get()) {
imageView.setVisibility(View.VISIBLE);
if (animation) imageView.startAnimation(fadeIn);
if (animation && fadeIn != null) {
imageView.startAnimation(fadeIn);
}
return;
}
if (imageView.getVisibility() == View.VISIBLE) {
if (animation) imageView.startAnimation(fadeOut);
if (animation && fadeOut != null) {
imageView.startAnimation(fadeOut);
}
imageView.setVisibility(View.GONE);
}
}
@ -168,7 +181,9 @@ public abstract class BottomControlButton {
if (!setting.get()) return;
imageView.clearAnimation();
if (fadeOutImmediate != null) {
imageView.startAnimation(fadeOutImmediate);
}
imageView.setVisibility(View.GONE);
}
}

View File

@ -147,7 +147,7 @@ class ActionButtonRequest private constructor(
} catch (ex: IOException) {
handleConnectionError("Network error", ex)
} catch (ex: Exception) {
Logger.printException({ "sendApplicationRequest failed" }, ex)
Logger.printException({ "sendRequest failed" }, ex)
} finally {
Logger.printDebug { "video: " + videoId + " took: " + (System.currentTimeMillis() - startTime) + "ms" }
}

View File

@ -1,5 +1,7 @@
package app.revanced.extension.youtube.patches.utils;
import app.revanced.extension.shared.utils.Utils;
public class PatchStatus {
public static boolean ImageSearchButton() {
@ -7,6 +9,11 @@ public class PatchStatus {
return false;
}
public static boolean OldSplashAnimation() {
// Replace this with true if the Restore old splash animation (Custom branding icon) succeeds
return false;
}
public static boolean PlayerButtons() {
// Replace this with true if the Hide player buttons patch succeeds
return false;
@ -22,6 +29,12 @@ public class PatchStatus {
return false;
}
public static boolean SplashAnimation() {
// If 'Restore old splash animation' is included and device is running Android 12+,
// YouTube TV splash animations will be disabled by default.
return OldSplashAnimation() && Utils.isSDKAbove(31);
}
public static boolean SponsorBlock() {
// Replace this with true if the SponsorBlock patch succeeds
return false;

View File

@ -3,6 +3,7 @@ package app.revanced.extension.youtube.returnyoutubedislike;
import static app.revanced.extension.shared.returnyoutubedislike.ReturnYouTubeDislike.Vote;
import static app.revanced.extension.shared.utils.StringRef.str;
import static app.revanced.extension.shared.utils.Utils.isSDKAbove;
import static app.revanced.extension.shared.utils.Utils.newSpanUsingStylingOfAnotherSpan;
import static app.revanced.extension.youtube.utils.ExtendedUtils.isSpoofingToLessThan;
import android.content.res.Resources;
@ -336,20 +337,6 @@ public class ReturnYouTubeDislike {
: formatDislikeCount(voteData.getDislikeCount()));
}
private static SpannableString newSpanUsingStylingOfAnotherSpan(@NonNull Spanned sourceStyle, @NonNull CharSequence newSpanText) {
if (sourceStyle == newSpanText && sourceStyle instanceof SpannableString spannableString) {
return spannableString; // Nothing to do.
}
SpannableString destination = new SpannableString(newSpanText);
Object[] spans = sourceStyle.getSpans(0, sourceStyle.length(), Object.class);
for (Object span : spans) {
destination.setSpan(span, 0, destination.length(), sourceStyle.getSpanFlags(span));
}
return destination;
}
private static String formatDislikeCount(long dislikeCount) {
if (isSDKAbove(24)) {
synchronized (ReturnYouTubeDislike.class) { // Number formatter is not thread safe.

View File

@ -32,9 +32,9 @@ import app.revanced.extension.youtube.patches.alternativethumbnails.AlternativeT
import app.revanced.extension.youtube.patches.alternativethumbnails.AlternativeThumbnailsPatch.StillImagesAvailability;
import app.revanced.extension.youtube.patches.alternativethumbnails.AlternativeThumbnailsPatch.ThumbnailOption;
import app.revanced.extension.youtube.patches.alternativethumbnails.AlternativeThumbnailsPatch.ThumbnailStillTime;
import app.revanced.extension.youtube.patches.general.ChangeFormFactorPatch.FormFactor;
import app.revanced.extension.youtube.patches.general.ChangeStartPagePatch;
import app.revanced.extension.youtube.patches.general.ChangeStartPagePatch.StartPage;
import app.revanced.extension.youtube.patches.general.LayoutSwitchPatch.FormFactor;
import app.revanced.extension.youtube.patches.general.YouTubeMusicActionsPatch;
import app.revanced.extension.youtube.patches.player.ExitFullscreenPatch.FullscreenMode;
import app.revanced.extension.youtube.patches.player.MiniplayerPatch;
@ -147,14 +147,14 @@ public class Settings extends BaseSettings {
public static final BooleanSetting CHANGE_START_PAGE_TYPE = new BooleanSetting("revanced_change_start_page_type", FALSE, true,
new ChangeStartPagePatch.ChangeStartPageTypeAvailability());
public static final BooleanSetting DISABLE_AUTO_AUDIO_TRACKS = new BooleanSetting("revanced_disable_auto_audio_tracks", FALSE);
public static final BooleanSetting DISABLE_SPLASH_ANIMATION = new BooleanSetting("revanced_disable_splash_animation", FALSE, true);
public static final BooleanSetting DISABLE_SPLASH_ANIMATION = new BooleanSetting("revanced_disable_splash_animation", PatchStatus.SplashAnimation(), true);
public static final BooleanSetting DISABLE_TRANSLUCENT_STATUS_BAR = new BooleanSetting("revanced_disable_translucent_status_bar", TRUE, true);
public static final BooleanSetting ENABLE_GRADIENT_LOADING_SCREEN = new BooleanSetting("revanced_enable_gradient_loading_screen", FALSE, true);
public static final BooleanSetting HIDE_FLOATING_MICROPHONE = new BooleanSetting("revanced_hide_floating_microphone", TRUE, true);
public static final BooleanSetting HIDE_GRAY_SEPARATOR = new BooleanSetting("revanced_hide_gray_separator", TRUE);
public static final BooleanSetting REMOVE_VIEWER_DISCRETION_DIALOG = new BooleanSetting("revanced_remove_viewer_discretion_dialog", FALSE);
public static final EnumSetting<FormFactor> CHANGE_LAYOUT = new EnumSetting<>("revanced_change_layout", FormFactor.ORIGINAL, true);
public static final EnumSetting<FormFactor> CHANGE_FORM_FACTOR = new EnumSetting<>("revanced_change_form_factor", FormFactor.DEFAULT, true, "revanced_change_form_factor_user_dialog_message");
public static final BooleanSetting CHANGE_LIVE_RING_CLICK_ACTION = new BooleanSetting("revanced_change_live_ring_click_action", FALSE, true);
public static final BooleanSetting SPOOF_APP_VERSION = new BooleanSetting("revanced_spoof_app_version", false, true, "revanced_spoof_app_version_user_dialog_message");
public static final StringSetting SPOOF_APP_VERSION_TARGET = new StringSetting("revanced_spoof_app_version_target", PatchStatus.SpoofAppVersionDefaultString(), true, parent(SPOOF_APP_VERSION));

View File

@ -75,7 +75,7 @@ public class ReVancedPreferenceFragment extends PreferenceFragment {
private final SharedPreferences.OnSharedPreferenceChangeListener listener = (sharedPreferences, str) -> {
try {
if (str == null) return;
Setting<?> setting = Setting.getSettingFromPath(str);
Setting<?> setting = Setting.getSettingFromPath(Objects.requireNonNull(str));
if (setting == null) return;

View File

@ -8,7 +8,7 @@ import android.preference.Preference;
import android.preference.SwitchPreference;
import app.revanced.extension.shared.settings.Setting;
import app.revanced.extension.youtube.patches.general.LayoutSwitchPatch;
import app.revanced.extension.youtube.patches.general.ChangeFormFactorPatch;
import app.revanced.extension.youtube.patches.utils.PatchStatus;
import app.revanced.extension.youtube.patches.utils.ReturnYouTubeDislikePatch;
import app.revanced.extension.youtube.returnyoutubedislike.ReturnYouTubeDislike;
@ -82,7 +82,7 @@ public class ReVancedSettingsPreference extends ReVancedPreferenceFragment {
*/
private static void TabletLayoutLinks() {
final boolean isTablet = ExtendedUtils.isTablet() &&
!LayoutSwitchPatch.phoneLayoutEnabled();
!ChangeFormFactorPatch.phoneLayoutEnabled();
enableDisablePreferences(
isTablet,

View File

@ -6,7 +6,7 @@ import java.util.concurrent.atomic.AtomicReference;
import app.revanced.extension.shared.utils.Logger;
@SuppressWarnings("unused")
@SuppressWarnings({"unused", "BooleanMethodIsAlwaysInverted"})
public final class EngagementPanel {
private static final AtomicReference<String> engagementPanelId = new AtomicReference<>("");

View File

@ -223,6 +223,10 @@ public final class NavigationBar {
* This tab will never be in a selected state, even if the create video UI is on screen.
*/
CREATE("CREATION_TAB_LARGE", "CREATION_TAB_LARGE_CAIRO"),
/**
* Only shown to automotive layout.
*/
EXPLORE("TAB_EXPLORE"),
SUBSCRIPTIONS("PIVOT_SUBSCRIPTIONS", "TAB_SUBSCRIPTIONS_CAIRO"),
/**
* Notifications tab. Only present when

View File

@ -2,9 +2,48 @@ package app.revanced.extension.youtube.shared;
import static app.revanced.extension.youtube.patches.components.RelatedVideoFilter.isActionBarVisible;
import android.graphics.drawable.Drawable;
import android.widget.FrameLayout;
import java.lang.ref.WeakReference;
import app.revanced.extension.shared.utils.Logger;
import app.revanced.extension.shared.utils.Utils;
@SuppressWarnings("unused")
public final class RootView {
/**
* Interface to call obfuscated methods in AppCompat Toolbar class.
*/
public interface AppCompatToolbarPatchInterface {
Drawable patch_getToolbarIcon();
}
private static volatile WeakReference<AppCompatToolbarPatchInterface> toolbarResultsRef
= new WeakReference<>(null);
/**
* Injection point.
*/
public static void setToolbar(FrameLayout layout) {
AppCompatToolbarPatchInterface toolbar = Utils.getChildView(layout, false, (view) ->
view instanceof AppCompatToolbarPatchInterface
);
if (toolbar == null) {
Logger.printException(() -> "Could not find toolbar");
return;
}
toolbarResultsRef = new WeakReference<>(toolbar);
}
public static boolean isBackButtonVisible() {
AppCompatToolbarPatchInterface toolbar = toolbarResultsRef.get();
return toolbar != null && toolbar.patch_getToolbarIcon() != null;
}
/**
* @return If the search bar is on screen. This includes if the player
* is on screen and the search results are behind the player (and not visible).

View File

@ -3,6 +3,7 @@ package app.revanced.extension.youtube.sponsorblock.ui;
import static app.revanced.extension.shared.utils.Utils.getChildView;
import android.view.View;
import android.view.animation.Animation;
import android.widget.ImageView;
import java.lang.ref.WeakReference;
@ -45,7 +46,10 @@ public class CreateSegmentButtonController {
return;
}
if (animation) {
imageView.startAnimation(BottomControlButton.getButtonFadeIn());
Animation fadeIn = BottomControlButton.getButtonFadeIn();
if (fadeIn != null) {
imageView.startAnimation(fadeIn);
}
}
imageView.setVisibility(View.VISIBLE);
return;
@ -53,7 +57,10 @@ public class CreateSegmentButtonController {
if (imageView.getVisibility() == View.VISIBLE) {
imageView.clearAnimation();
if (animation) {
imageView.startAnimation(BottomControlButton.getButtonFadeOut());
Animation fadeOut = BottomControlButton.getButtonFadeOut();
if (fadeOut != null) {
imageView.startAnimation(fadeOut);
}
}
imageView.setVisibility(View.GONE);
}
@ -65,7 +72,10 @@ public class CreateSegmentButtonController {
if (!shouldBeShown()) return;
imageView.clearAnimation();
imageView.startAnimation(BottomControlButton.getButtonFadeOutImmediate());
Animation fadeOutImmediate = BottomControlButton.getButtonFadeOutImmediate();
if (fadeOutImmediate != null) {
imageView.startAnimation(fadeOutImmediate);
}
imageView.setVisibility(View.GONE);
}

View File

@ -4,6 +4,7 @@ import static app.revanced.extension.shared.utils.Utils.getChildView;
import static app.revanced.extension.youtube.sponsorblock.SegmentPlaybackController.videoHasSegments;
import android.view.View;
import android.view.animation.Animation;
import android.widget.ImageView;
import java.lang.ref.WeakReference;
@ -47,7 +48,10 @@ public class VotingButtonController {
return;
}
if (animation) {
imageView.startAnimation(BottomControlButton.getButtonFadeIn());
Animation fadeIn = BottomControlButton.getButtonFadeIn();
if (fadeIn != null) {
imageView.startAnimation(fadeIn);
}
}
imageView.setVisibility(View.VISIBLE);
return;
@ -55,7 +59,10 @@ public class VotingButtonController {
if (imageView.getVisibility() == View.VISIBLE) {
imageView.clearAnimation();
if (animation) {
imageView.startAnimation(BottomControlButton.getButtonFadeOut());
Animation fadeOut = BottomControlButton.getButtonFadeOut();
if (fadeOut != null) {
imageView.startAnimation(fadeOut);
}
}
imageView.setVisibility(View.GONE);
}
@ -68,7 +75,10 @@ public class VotingButtonController {
imageView.clearAnimation();
imageView.startAnimation(BottomControlButton.getButtonFadeOutImmediate());
Animation fadeOutImmediate = BottomControlButton.getButtonFadeOutImmediate();
if (fadeOutImmediate != null) {
imageView.startAnimation(fadeOutImmediate);
}
imageView.setVisibility(View.GONE);
}

View File

@ -4,5 +4,5 @@ org.gradle.parallel = true
android.useAndroidX = true
kotlin.code.style = official
kotlin.jvm.target.validation.mode = IGNORE
version = 5.4.2
version = 5.5.1

View File

@ -3,7 +3,7 @@ revanced-patcher = "21.0.0"
# Tracking https://github.com/google/smali/issues/64.
#noinspection GradleDependency
smali = "3.0.5"
gson = "2.11.0"
gson = "2.12.1"
agp = "8.2.2"
annotation = "1.9.1"
lang3 = "3.17.0"

View File

@ -58,7 +58,8 @@
"6.51.53",
"7.16.53",
"7.25.53",
"8.05.51"
"8.05.51",
"8.10.51"
]
},
"options": []
@ -99,7 +100,8 @@
"6.51.53",
"7.16.53",
"7.25.53",
"8.05.51"
"8.05.51",
"8.10.51"
]
},
"options": []
@ -138,17 +140,20 @@
"6.51.53",
"7.16.53",
"7.25.53",
"8.05.51"
"8.05.51",
"8.10.51"
]
},
"options": []
},
{
"name": "Change layout",
"description": "Adds an option to change the dp in order to use a tablet or phone layout.",
"name": "Change form factor",
"description": "Adds an option to change the UI appearance to a phone, tablet, or automotive device.",
"use": true,
"dependencies": [
"Settings for YouTube"
"Settings for YouTube",
"BytecodePatch",
"BytecodePatch"
],
"compatiblePackages": {
"com.google.android.youtube": [
@ -250,7 +255,8 @@
"6.51.53",
"7.16.53",
"7.25.53",
"8.05.51"
"8.05.51",
"8.10.51"
]
},
"options": []
@ -282,6 +288,7 @@
"description": "Adds an option to set which page the app opens in instead of the homepage.",
"use": true,
"dependencies": [
"ResourcePatch",
"Settings for YouTube Music"
],
"compatiblePackages": {
@ -292,7 +299,8 @@
"6.51.53",
"7.16.53",
"7.25.53",
"8.05.51"
"8.05.51",
"8.10.51"
]
},
"options": []
@ -448,7 +456,8 @@
"6.51.53",
"7.16.53",
"7.25.53",
"8.05.51"
"8.05.51",
"8.10.51"
]
},
"options": [
@ -564,7 +573,8 @@
"6.51.53",
"7.16.53",
"7.25.53",
"8.05.51"
"8.05.51",
"8.10.51"
]
},
"options": [
@ -676,7 +686,8 @@
"6.51.53",
"7.16.53",
"7.25.53",
"8.05.51"
"8.05.51",
"8.10.51"
]
},
"options": [
@ -708,7 +719,8 @@
"6.51.53",
"7.16.53",
"7.25.53",
"8.05.51"
"8.05.51",
"8.10.51"
]
},
"options": [
@ -781,7 +793,8 @@
"7.06.54",
"7.16.53",
"7.25.53",
"8.05.51"
"8.05.51",
"8.10.51"
]
},
"options": []
@ -802,7 +815,8 @@
"6.51.53",
"7.16.53",
"7.25.53",
"8.05.51"
"8.05.51",
"8.10.51"
]
},
"options": []
@ -823,7 +837,8 @@
"6.51.53",
"7.16.53",
"7.25.53",
"8.05.51"
"8.05.51",
"8.10.51"
]
},
"options": []
@ -865,7 +880,8 @@
"6.51.53",
"7.16.53",
"7.25.53",
"8.05.51"
"8.05.51",
"8.10.51"
]
},
"options": []
@ -906,7 +922,8 @@
"6.51.53",
"7.16.53",
"7.25.53",
"8.05.51"
"8.05.51",
"8.10.51"
]
},
"options": []
@ -969,7 +986,8 @@
"6.51.53",
"7.16.53",
"7.25.53",
"8.05.51"
"8.05.51",
"8.10.51"
]
},
"options": []
@ -1020,7 +1038,8 @@
"description": "Adds an option to disable the popup that appears when taking a screenshot.",
"use": true,
"dependencies": [
"Settings for Reddit"
"Settings for Reddit",
"ResourcePatch"
],
"compatiblePackages": {
"com.reddit.frontpage": [
@ -1067,7 +1086,8 @@
"6.51.53",
"7.16.53",
"7.25.53",
"8.05.51"
"8.05.51",
"8.10.51"
]
},
"options": []
@ -1108,7 +1128,8 @@
"6.51.53",
"7.16.53",
"7.25.53",
"8.05.51"
"8.05.51",
"8.10.51"
]
},
"options": []
@ -1169,7 +1190,8 @@
"6.51.53",
"7.16.53",
"7.25.53",
"8.05.51"
"8.05.51",
"8.10.51"
]
},
"options": []
@ -1196,7 +1218,8 @@
"6.51.53",
"7.16.53",
"7.25.53",
"8.05.51"
"8.05.51",
"8.10.51"
]
},
"options": []
@ -1264,7 +1287,8 @@
"6.51.53",
"7.16.53",
"7.25.53",
"8.05.51"
"8.05.51",
"8.10.51"
]
},
"options": [
@ -1451,7 +1475,8 @@
"6.51.53",
"7.16.53",
"7.25.53",
"8.05.51"
"8.05.51",
"8.10.51"
]
},
"options": []
@ -1476,7 +1501,8 @@
"6.51.53",
"7.16.53",
"7.25.53",
"8.05.51"
"8.05.51",
"8.10.51"
]
},
"options": []
@ -1526,7 +1552,8 @@
"6.51.53",
"7.16.53",
"7.25.53",
"8.05.51"
"8.05.51",
"8.10.51"
]
},
"options": []
@ -1661,7 +1688,8 @@
"6.51.53",
"7.16.53",
"7.25.53",
"8.05.51"
"8.05.51",
"8.10.51"
]
},
"options": []
@ -1722,7 +1750,8 @@
"6.51.53",
"7.16.53",
"7.25.53",
"8.05.51"
"8.05.51",
"8.10.51"
]
},
"options": []
@ -1788,7 +1817,8 @@
"6.51.53",
"7.16.53",
"7.25.53",
"8.05.51"
"8.05.51",
"8.10.51"
]
},
"options": []
@ -1956,9 +1986,11 @@
"description": "Adds options to hide or change components related to the navigation bar.",
"use": true,
"dependencies": [
"Change start page",
"ResourcePatch",
"ResourcePatch",
"Settings for YouTube Music"
"Settings for YouTube Music",
"ResourcePatch"
],
"compatiblePackages": {
"com.google.android.apps.youtube.music": [
@ -1968,7 +2000,8 @@
"6.51.53",
"7.16.53",
"7.25.53",
"8.05.51"
"8.05.51",
"8.10.51"
]
},
"options": []
@ -2140,7 +2173,8 @@
"6.51.53",
"7.16.53",
"7.25.53",
"8.05.51"
"8.05.51",
"8.10.51"
]
},
"options": []
@ -2203,7 +2237,8 @@
"6.51.53",
"7.16.53",
"7.25.53",
"8.05.51"
"8.05.51",
"8.10.51"
]
},
"options": []
@ -2261,7 +2296,8 @@
"6.51.53",
"7.16.53",
"7.25.53",
"8.05.51"
"8.05.51",
"8.10.51"
]
},
"options": []
@ -2302,7 +2338,8 @@
"6.51.53",
"7.16.53",
"7.25.53",
"8.05.51"
"8.05.51",
"8.10.51"
]
},
"options": []
@ -2323,7 +2360,8 @@
"6.51.53",
"7.16.53",
"7.25.53",
"8.05.51"
"8.05.51",
"8.10.51"
]
},
"options": []
@ -2369,7 +2407,8 @@
"6.51.53",
"7.16.53",
"7.25.53",
"8.05.51"
"8.05.51",
"8.10.51"
]
},
"options": []
@ -2411,7 +2450,8 @@
"6.51.53",
"7.16.53",
"7.25.53",
"8.05.51"
"8.05.51",
"8.10.51"
]
},
"options": []
@ -2591,7 +2631,8 @@
"6.51.53",
"7.16.53",
"7.25.53",
"8.05.51"
"8.05.51",
"8.10.51"
]
},
"options": [
@ -2750,7 +2791,8 @@
"6.51.53",
"7.16.53",
"7.25.53",
"8.05.51"
"8.05.51",
"8.10.51"
]
},
"options": []
@ -2840,7 +2882,7 @@
{
"name": "Spoof client",
"description": "Adds options to spoof the client to allow playback.",
"use": true,
"use": false,
"dependencies": [
"Settings for YouTube Music",
"ResourcePatch",
@ -2856,7 +2898,31 @@
"6.51.53",
"7.16.53",
"7.25.53",
"8.05.51"
"8.05.51",
"8.10.51"
]
},
"options": []
},
{
"name": "Spoof player parameter",
"description": "Adds options to spoof player parameter to allow playback.",
"use": true,
"dependencies": [
"Settings for YouTube Music",
"BytecodePatch",
"BytecodePatch"
],
"compatiblePackages": {
"com.google.android.apps.youtube.music": [
"6.20.51",
"6.29.59",
"6.42.55",
"6.51.53",
"7.16.53",
"7.25.53",
"8.05.51",
"8.10.51"
]
},
"options": []
@ -3056,7 +3122,8 @@
"6.51.53",
"7.16.53",
"7.25.53",
"8.05.51"
"8.05.51",
"8.10.51"
]
},
"options": [
@ -3106,7 +3173,8 @@
"6.51.53",
"7.16.53",
"7.25.53",
"8.05.51"
"8.05.51",
"8.10.51"
]
},
"options": []
@ -3201,7 +3269,8 @@
"6.51.53",
"7.16.53",
"7.25.53",
"8.05.51"
"8.05.51",
"8.10.51"
]
},
"options": [
@ -3238,7 +3307,8 @@
"6.51.53",
"7.16.53",
"7.25.53",
"8.05.51"
"8.05.51",
"8.10.51"
]
},
"options": []

View File

@ -198,6 +198,10 @@ public final class app/revanced/patches/music/utils/fix/fileprovider/FileProvide
public static final fun fileProviderPatch (Ljava/lang/String;Ljava/lang/String;)Lapp/revanced/patcher/patch/BytecodePatch;
}
public final class app/revanced/patches/music/utils/fix/parameter/SpoofPlayerParameterPatchKt {
public static final fun getSpoofPlayerParameterPatch ()Lapp/revanced/patcher/patch/BytecodePatch;
}
public final class app/revanced/patches/music/utils/flyoutmenu/FlyoutMenuHookPatchKt {
public static final fun getFlyoutMenuHookPatch ()Lapp/revanced/patcher/patch/BytecodePatch;
}
@ -288,6 +292,10 @@ public final class app/revanced/patches/music/utils/resourceid/SharedResourceIdP
public static final fun getTouchOutside ()J
public static final fun getTrimSilenceSwitch ()J
public static final fun getVarispeedUnavailableTitle ()J
public static final fun getYtFillSamples ()J
public static final fun getYtFillYouTubeMusic ()J
public static final fun getYtOutlineSamples ()J
public static final fun getYtOutlineYouTubeMusic ()J
public static final fun getYtmLogo ()J
public static final fun getYtmLogoRingo2 ()J
public static final fun isTablet ()J
@ -331,10 +339,30 @@ public final class app/revanced/patches/music/video/playback/VideoPlaybackPatchK
public static final fun getVideoPlaybackPatch ()Lapp/revanced/patcher/patch/BytecodePatch;
}
public abstract class app/revanced/patches/music/video/playerresponse/Hook {
public synthetic fun <init> (Ljava/lang/String;Lkotlin/jvm/internal/DefaultConstructorMarker;)V
public fun toString ()Ljava/lang/String;
}
public final class app/revanced/patches/music/video/playerresponse/Hook$PlayerParameter : app/revanced/patches/music/video/playerresponse/Hook {
public fun <init> (Ljava/lang/String;)V
}
public final class app/revanced/patches/music/video/playerresponse/Hook$PlayerParameterBeforeVideoId : app/revanced/patches/music/video/playerresponse/Hook {
public fun <init> (Ljava/lang/String;)V
}
public final class app/revanced/patches/music/video/playerresponse/Hook$VideoId : app/revanced/patches/music/video/playerresponse/Hook {
public fun <init> (Ljava/lang/String;)V
}
public final class app/revanced/patches/music/video/playerresponse/Hook$VideoIdAndPlaylistId : app/revanced/patches/music/video/playerresponse/Hook {
public fun <init> (Ljava/lang/String;)V
}
public final class app/revanced/patches/music/video/playerresponse/PlayerResponseMethodHookPatchKt {
public static final fun addPlayerResponseMethodHook (Lapp/revanced/patches/music/video/playerresponse/Hook;)V
public static final fun getPlayerResponseMethodHookPatch ()Lapp/revanced/patcher/patch/BytecodePatch;
public static final fun hookPlayerResponse (Ljava/lang/String;Z)V
public static synthetic fun hookPlayerResponse$default (Ljava/lang/String;ZILjava/lang/Object;)V
}
public final class app/revanced/patches/reddit/ad/AdsPatchKt {
@ -424,6 +452,10 @@ public final class app/revanced/patches/reddit/utils/extension/SharedExtensionPa
public static final fun getSharedExtensionPatch ()Lapp/revanced/patcher/patch/BytecodePatch;
}
public final class app/revanced/patches/reddit/utils/resourceid/SharedResourceIdPatchKt {
public static final fun getScreenShotShareBanner ()J
}
public final class app/revanced/patches/reddit/utils/settings/SettingsPatchKt {
public static final fun getSettingsPatch ()Lapp/revanced/patcher/patch/ResourcePatch;
public static final fun is_2024_26_or_greater ()Z
@ -643,8 +675,8 @@ public final class app/revanced/patches/youtube/general/downloads/DownloadAction
public static final fun getDownloadActionsPatch ()Lapp/revanced/patcher/patch/BytecodePatch;
}
public final class app/revanced/patches/youtube/general/layoutswitch/LayoutSwitchPatchKt {
public static final fun getLayoutSwitchPatch ()Lapp/revanced/patcher/patch/BytecodePatch;
public final class app/revanced/patches/youtube/general/formfactor/ChangeFormFactorPatchKt {
public static final fun getChangeFormFactorPatch ()Lapp/revanced/patcher/patch/BytecodePatch;
}
public final class app/revanced/patches/youtube/general/livering/OpenChannelOfLiveAvatarPatchKt {
@ -992,6 +1024,7 @@ public final class app/revanced/patches/youtube/utils/playservice/VersionCheckPa
public static final fun is_19_04_or_greater ()Z
public static final fun is_19_09_or_greater ()Z
public static final fun is_19_15_or_greater ()Z
public static final fun is_19_16_or_greater ()Z
public static final fun is_19_17_or_greater ()Z
public static final fun is_19_23_or_greater ()Z
public static final fun is_19_25_or_greater ()Z
@ -1010,6 +1043,7 @@ public final class app/revanced/patches/youtube/utils/playservice/VersionCheckPa
public static final fun is_20_02_or_greater ()Z
public static final fun is_20_03_or_greater ()Z
public static final fun is_20_05_or_greater ()Z
public static final fun is_20_10_or_greater ()Z
}
public final class app/revanced/patches/youtube/utils/recyclerview/RecyclerViewTreeObserverPatchKt {
@ -1122,6 +1156,7 @@ public final class app/revanced/patches/youtube/utils/resourceid/SharedResourceI
public static final fun getTapBloomView ()J
public static final fun getTitleAnchor ()J
public static final fun getToolTipContentView ()J
public static final fun getToolbarContainerId ()J
public static final fun getTotalTime ()J
public static final fun getTouchArea ()J
public static final fun getVarispeedUnavailableTitle ()J

View File

@ -4,13 +4,17 @@ import app.revanced.patcher.extensions.InstructionExtensions.addInstruction
import app.revanced.patcher.extensions.InstructionExtensions.addInstructions
import app.revanced.patcher.extensions.InstructionExtensions.getInstruction
import app.revanced.patcher.patch.bytecodePatch
import app.revanced.patcher.patch.resourcePatch
import app.revanced.patches.music.utils.compatibility.Constants.COMPATIBLE_PACKAGE
import app.revanced.patches.music.utils.extension.Constants.GENERAL_PATH
import app.revanced.patches.music.utils.patch.PatchList.CHANGE_START_PAGE
import app.revanced.patches.music.utils.playservice.is_6_27_or_greater
import app.revanced.patches.music.utils.playservice.versionCheckPatch
import app.revanced.patches.music.utils.settings.CategoryType
import app.revanced.patches.music.utils.settings.ResourceUtils.updatePatchStatus
import app.revanced.patches.music.utils.settings.addPreferenceWithIntent
import app.revanced.patches.music.utils.settings.settingsPatch
import app.revanced.util.addEntryValues
import app.revanced.util.fingerprint.methodOrThrow
import app.revanced.util.getReference
import app.revanced.util.indexOfFirstInstructionReversedOrThrow
@ -22,6 +26,33 @@ import com.android.tools.smali.dexlib2.iface.reference.FieldReference
private const val EXTENSION_CLASS_DESCRIPTOR =
"$GENERAL_PATH/ChangeStartPagePatch;"
private val changeStartPageResourcePatch = resourcePatch(
description = "changeStartPageResourcePatch"
) {
dependsOn(
settingsPatch,
versionCheckPatch,
)
execute {
fun appendStartPage(startPage: String) {
addEntryValues(
"revanced_change_start_page_entries",
"@string/revanced_change_start_page_entry_$startPage",
)
addEntryValues(
"revanced_change_start_page_entry_values",
startPage.uppercase(),
)
}
if (is_6_27_or_greater) {
appendStartPage("search")
}
appendStartPage("subscriptions")
}
}
@Suppress("unused")
val changeStartPagePatch = bytecodePatch(
CHANGE_START_PAGE.title,
@ -29,7 +60,10 @@ val changeStartPagePatch = bytecodePatch(
) {
compatibleWith(COMPATIBLE_PACKAGE)
dependsOn(settingsPatch)
dependsOn(
changeStartPageResourcePatch,
settingsPatch,
)
execute {

View File

@ -14,7 +14,8 @@ import app.revanced.patches.music.utils.settings.addSwitchPreference
import app.revanced.patches.music.utils.settings.settingsPatch
import app.revanced.patches.music.video.information.videoIdHook
import app.revanced.patches.music.video.information.videoInformationPatch
import app.revanced.patches.music.video.playerresponse.hookPlayerResponse
import app.revanced.patches.music.video.playerresponse.Hook
import app.revanced.patches.music.video.playerresponse.addPlayerResponseMethodHook
import app.revanced.patches.music.video.playerresponse.playerResponseMethodHookPatch
import app.revanced.util.findMethodOrThrow
import app.revanced.util.fingerprint.methodOrThrow
@ -46,7 +47,11 @@ val albumMusicVideoPatch = bytecodePatch(
// region hook player response
hookPlayerResponse("$EXTENSION_CLASS_DESCRIPTOR->newPlayerResponse(Ljava/lang/String;Ljava/lang/String;I)V")
addPlayerResponseMethodHook(
Hook.VideoIdAndPlaylistId(
"$EXTENSION_CLASS_DESCRIPTOR->newPlayerResponse(Ljava/lang/String;Ljava/lang/String;I)V"
),
)
// endregion

View File

@ -41,6 +41,7 @@ val cairoSplashAnimationPatch = bytecodePatch(
"7.16.53",
"7.25.53",
"8.05.51",
"8.10.51",
),
)

View File

@ -3,9 +3,13 @@ package app.revanced.patches.music.navigation.components
import app.revanced.patches.music.utils.resourceid.colorGrey
import app.revanced.patches.music.utils.resourceid.text1
import app.revanced.util.fingerprint.legacyFingerprint
import app.revanced.util.getReference
import app.revanced.util.indexOfFirstInstruction
import app.revanced.util.or
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.MethodReference
internal val tabLayoutFingerprint = legacyFingerprint(
name = "tabLayoutFingerprint",
@ -30,5 +34,22 @@ internal val tabLayoutTextFingerprint = legacyFingerprint(
Opcode.INVOKE_INTERFACE,
Opcode.MOVE_RESULT
),
literals = listOf(text1)
strings = listOf("FEmusic_search"),
literals = listOf(text1),
customFingerprint = { method, _ ->
indexOfGetVisibilityInstruction(method) >= 0 &&
indexOfSetTextInstruction(method) >= 0
}
)
internal fun indexOfGetVisibilityInstruction(method: Method) =
method.indexOfFirstInstruction {
opcode == Opcode.INVOKE_VIRTUAL &&
getReference<MethodReference>()?.name == "getVisibility"
}
internal fun indexOfSetTextInstruction(method: Method) =
method.indexOfFirstInstruction {
opcode == Opcode.INVOKE_VIRTUAL &&
getReference<MethodReference>()?.name == "setText"
}

View File

@ -6,26 +6,38 @@ import app.revanced.patcher.extensions.InstructionExtensions.getInstruction
import app.revanced.patcher.patch.PatchException
import app.revanced.patcher.patch.bytecodePatch
import app.revanced.patcher.patch.resourcePatch
import app.revanced.patches.music.general.startpage.changeStartPagePatch
import app.revanced.patches.music.utils.compatibility.Constants.COMPATIBLE_PACKAGE
import app.revanced.patches.music.utils.extension.Constants.NAVIGATION_CLASS_DESCRIPTOR
import app.revanced.patches.music.utils.patch.PatchList.NAVIGATION_BAR_COMPONENTS
import app.revanced.patches.music.utils.playservice.is_6_27_or_greater
import app.revanced.patches.music.utils.playservice.versionCheckPatch
import app.revanced.patches.music.utils.resourceid.colorGrey
import app.revanced.patches.music.utils.resourceid.sharedResourceIdPatch
import app.revanced.patches.music.utils.resourceid.text1
import app.revanced.patches.music.utils.resourceid.ytFillSamples
import app.revanced.patches.music.utils.resourceid.ytFillYouTubeMusic
import app.revanced.patches.music.utils.resourceid.ytOutlineSamples
import app.revanced.patches.music.utils.resourceid.ytOutlineYouTubeMusic
import app.revanced.patches.music.utils.settings.CategoryType
import app.revanced.patches.music.utils.settings.ResourceUtils.updatePatchStatus
import app.revanced.patches.music.utils.settings.addPreferenceWithIntent
import app.revanced.patches.music.utils.settings.addSwitchPreference
import app.revanced.patches.music.utils.settings.settingsPatch
import app.revanced.util.REGISTER_TEMPLATE_REPLACEMENT
import app.revanced.util.fingerprint.matchOrThrow
import app.revanced.util.fingerprint.methodOrThrow
import app.revanced.util.getReference
import app.revanced.util.indexOfFirstInstructionOrThrow
import app.revanced.util.indexOfFirstInstructionReversedOrThrow
import app.revanced.util.indexOfFirstLiteralInstructionOrThrow
import app.revanced.util.replaceLiteralInstructionCall
import com.android.tools.smali.dexlib2.Opcode
import com.android.tools.smali.dexlib2.iface.instruction.FiveRegisterInstruction
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.instruction.TwoRegisterInstruction
import com.android.tools.smali.dexlib2.iface.reference.FieldReference
import com.android.tools.smali.dexlib2.iface.reference.MethodReference
private const val FLAG = "android:layout_weight"
@ -56,9 +68,11 @@ val navigationBarComponentsPatch = bytecodePatch(
compatibleWith(COMPATIBLE_PACKAGE)
dependsOn(
changeStartPagePatch,
navigationBarComponentsResourcePatch,
sharedResourceIdPatch,
settingsPatch,
versionCheckPatch,
)
execute {
@ -105,29 +119,73 @@ val navigationBarComponentsPatch = bytecodePatch(
*/
tabLayoutTextFingerprint.matchOrThrow().let {
it.method.apply {
val stringIndex = it.stringMatches!!.first().index
val browseIdIndex = indexOfFirstInstructionReversedOrThrow(stringIndex) {
opcode == Opcode.IGET_OBJECT &&
getReference<FieldReference>()?.type == "Ljava/lang/String;"
}
val browseIdReference = getInstruction<ReferenceInstruction>(browseIdIndex).reference as FieldReference
val fieldName = browseIdReference.name
val componentIndex = indexOfFirstInstructionOrThrow(stringIndex) {
opcode == Opcode.IGET_OBJECT &&
getReference<FieldReference>()?.toString() == browseIdReference.toString()
}
val browseIdRegister = getInstruction<TwoRegisterInstruction>(componentIndex).registerA
val componentRegister = getInstruction<TwoRegisterInstruction>(componentIndex).registerB
val enumIndex = it.patternMatch!!.startIndex + 3
val enumRegister = getInstruction<OneRegisterInstruction>(enumIndex).registerA
val insertEnumIndex = indexOfFirstInstructionOrThrow(Opcode.AND_INT_LIT8) - 2
val pivotTabIndex = indexOfFirstInstructionOrThrow {
opcode == Opcode.INVOKE_VIRTUAL &&
getReference<MethodReference>()?.name == "getVisibility"
}
val pivotTabIndex = indexOfGetVisibilityInstruction(this)
val pivotTabRegister =
getInstruction<FiveRegisterInstruction>(pivotTabIndex).registerC
val spannedIndex = indexOfSetTextInstruction(this)
val spannedRegister =
getInstruction<FiveRegisterInstruction>(spannedIndex).registerD
addInstruction(
pivotTabIndex,
"invoke-static {v$pivotTabRegister}, $NAVIGATION_CLASS_DESCRIPTOR->hideNavigationButton(Landroid/view/View;)V"
)
addInstructions(
componentIndex + 1, """
const-string v$enumRegister, "$fieldName"
invoke-static {v$componentRegister, v$browseIdRegister, v$enumRegister}, $NAVIGATION_CLASS_DESCRIPTOR->replaceBrowseId(Ljava/lang/Object;Ljava/lang/String;Ljava/lang/String;)Ljava/lang/String;
move-result-object v$browseIdRegister
"""
)
addInstructions(
spannedIndex, """
invoke-static {v$spannedRegister}, $NAVIGATION_CLASS_DESCRIPTOR->replaceNavigationLabel(Landroid/text/Spanned;)Landroid/text/Spanned;
move-result-object v$spannedRegister
"""
)
addInstruction(
insertEnumIndex,
"sput-object v$enumRegister, $NAVIGATION_CLASS_DESCRIPTOR->lastPivotTab:Ljava/lang/Enum;"
"invoke-static {v$enumRegister}, $NAVIGATION_CLASS_DESCRIPTOR->setLastAppNavigationEnum(Ljava/lang/Enum;)V"
)
}
}
val smaliInstruction = """
invoke-static {v$REGISTER_TEMPLATE_REPLACEMENT}, $NAVIGATION_CLASS_DESCRIPTOR->replaceNavigationIcon(I)I
move-result v$REGISTER_TEMPLATE_REPLACEMENT
"""
arrayOf(
ytFillSamples,
ytFillYouTubeMusic,
ytOutlineSamples,
ytOutlineYouTubeMusic,
).forEach { literal ->
replaceLiteralInstructionCall(literal, smaliInstruction)
}
addSwitchPreference(
CategoryType.NAVIGATION,
"revanced_enable_custom_navigation_bar_color",
@ -173,6 +231,22 @@ val navigationBarComponentsPatch = bytecodePatch(
"revanced_hide_navigation_label",
"false"
)
if (is_6_27_or_greater) {
addSwitchPreference(
CategoryType.NAVIGATION,
"revanced_replace_navigation_samples_button",
"false"
)
}
addSwitchPreference(
CategoryType.NAVIGATION,
"revanced_replace_navigation_upgrade_button",
"false"
)
addPreferenceWithIntent(
CategoryType.NAVIGATION,
"revanced_replace_navigation_button_about"
)
updatePatchStatus(NAVIGATION_BAR_COMPONENTS)

View File

@ -20,8 +20,8 @@ import app.revanced.util.or
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.instruction.ReferenceInstruction
import com.android.tools.smali.dexlib2.iface.reference.MethodReference
import com.android.tools.smali.dexlib2.iface.reference.TypeReference
const val AUDIO_VIDEO_SWITCH_TOGGLE_VISIBILITY =
"/AudioVideoSwitcherToggleView;->setVisibility(I)V"
@ -44,7 +44,6 @@ internal val audioVideoSwitchToggleFingerprint = legacyFingerprint(
internal val engagementPanelHeightFingerprint = legacyFingerprint(
name = "engagementPanelHeightFingerprint",
returnType = "L",
accessFlags = AccessFlags.PUBLIC or AccessFlags.FINAL,
// In YouTube Music 7.21.50+, there are two methods with similar structure, so this Opcode pattern must be used.
opcodes = listOf(
Opcode.IGET_OBJECT,
@ -54,6 +53,7 @@ internal val engagementPanelHeightFingerprint = legacyFingerprint(
),
parameters = emptyList(),
customFingerprint = { method, _ ->
AccessFlags.FINAL.isSet(method.accessFlags) &&
method.containsLiteralInstruction(1) &&
method.indexOfFirstInstruction {
opcode == Opcode.INVOKE_VIRTUAL &&
@ -65,7 +65,6 @@ internal val engagementPanelHeightFingerprint = legacyFingerprint(
internal val engagementPanelHeightParentFingerprint = legacyFingerprint(
name = "engagementPanelHeightParentFingerprint",
returnType = "L",
accessFlags = AccessFlags.PUBLIC or AccessFlags.FINAL,
opcodes = listOf(Opcode.NEW_ARRAY),
parameters = emptyList(),
customFingerprint = custom@{ method, _ ->
@ -75,9 +74,12 @@ internal val engagementPanelHeightParentFingerprint = legacyFingerprint(
if (method.returnType == "Ljava/lang/Object;") {
return@custom false
}
if (!AccessFlags.FINAL.isSet(method.accessFlags)) {
return@custom false
}
method.indexOfFirstInstruction {
opcode == Opcode.CHECK_CAST &&
(this as? ReferenceInstruction)?.reference?.toString() == "Lcom/google/android/libraries/youtube/engagementpanel/size/EngagementPanelSizeBehavior;"
getReference<TypeReference>()?.type == "Lcom/google/android/libraries/youtube/engagementpanel/size/EngagementPanelSizeBehavior;"
} >= 0
}
)

View File

@ -15,7 +15,8 @@ internal object Constants {
"6.51.53", // This is the latest version of YouTube Music 6.xx.xx
"7.16.53", // This is the latest version that supports the 'Spoof app version' patch.
"7.25.53", // This is the last supported version for 2024.
"8.05.51", // This is the latest version supported by the RVX patch.
"8.05.51", // This was the latest version supported by the previous RVX patch.
"8.10.51", // This is the latest version supported by the RVX patch.
)
)
}

View File

@ -64,7 +64,8 @@ private const val CLIENT_INFO_CLASS_DESCRIPTOR =
@Suppress("unused")
val spoofClientPatch = bytecodePatch(
SPOOF_CLIENT.title,
SPOOF_CLIENT.summary
SPOOF_CLIENT.summary,
false,
) {
compatibleWith(COMPATIBLE_PACKAGE)
@ -359,7 +360,7 @@ val spoofClientPatch = bytecodePatch(
addSwitchPreference(
CategoryType.MISC,
"revanced_spoof_client",
"true"
"false"
)
addPreferenceWithIntent(
CategoryType.MISC,

View File

@ -0,0 +1,26 @@
package app.revanced.patches.music.utils.fix.parameter
import app.revanced.util.fingerprint.legacyFingerprint
import app.revanced.util.or
import com.android.tools.smali.dexlib2.AccessFlags
internal val subtitleWindowFingerprint = legacyFingerprint(
name = "subtitleWindowFingerprint",
returnType = "V",
accessFlags = AccessFlags.PUBLIC or AccessFlags.CONSTRUCTOR,
parameters = listOf("I", "I", "I", "Z", "Z"),
strings = listOf("invalid anchorHorizontalPos: %s"),
)
/**
* If this flag is activated, a playback issue occurs in age-restricted videos.
*/
internal const val AGE_RESTRICTED_PLAYBACK_FEATURE_FLAG = 45651506L
internal val ageRestrictedPlaybackFeatureFlagFingerprint = legacyFingerprint(
name = "ageRestrictedPlaybackFeatureFlagFingerprint",
returnType = "Z",
accessFlags = AccessFlags.PUBLIC or AccessFlags.FINAL,
parameters = emptyList(),
literals = listOf(AGE_RESTRICTED_PLAYBACK_FEATURE_FLAG),
)

View File

@ -0,0 +1,82 @@
package app.revanced.patches.music.utils.fix.parameter
import app.revanced.patcher.extensions.InstructionExtensions.addInstructions
import app.revanced.patcher.patch.bytecodePatch
import app.revanced.patches.music.utils.compatibility.Constants.COMPATIBLE_PACKAGE
import app.revanced.patches.music.utils.extension.Constants.MISC_PATH
import app.revanced.patches.music.utils.patch.PatchList.SPOOF_PLAYER_PARAMETER
import app.revanced.patches.music.utils.settings.CategoryType
import app.revanced.patches.music.utils.settings.ResourceUtils.updatePatchStatus
import app.revanced.patches.music.utils.settings.addSwitchPreference
import app.revanced.patches.music.utils.settings.settingsPatch
import app.revanced.patches.music.video.information.videoInformationPatch
import app.revanced.patches.music.video.playerresponse.Hook
import app.revanced.patches.music.video.playerresponse.addPlayerResponseMethodHook
import app.revanced.patches.music.video.playerresponse.playerResponseMethodHookPatch
import app.revanced.util.fingerprint.injectLiteralInstructionBooleanCall
import app.revanced.util.fingerprint.methodOrThrow
import app.revanced.util.fingerprint.resolvable
private const val EXTENSION_CLASS_DESCRIPTOR =
"$MISC_PATH/SpoofPlayerParameterPatch;"
@Suppress("unused")
val spoofPlayerParameterPatch = bytecodePatch(
SPOOF_PLAYER_PARAMETER.title,
SPOOF_PLAYER_PARAMETER.summary
) {
compatibleWith(COMPATIBLE_PACKAGE)
dependsOn(
settingsPatch,
videoInformationPatch,
playerResponseMethodHookPatch,
)
execute {
addPlayerResponseMethodHook(
Hook.PlayerParameter(
"$EXTENSION_CLASS_DESCRIPTOR->spoofParameter(Ljava/lang/String;Ljava/lang/String;)Ljava/lang/String;"
),
)
// region fix for subtitles position
subtitleWindowFingerprint.methodOrThrow().addInstructions(
0,
"""
invoke-static {p1, p2, p3, p4, p5}, $EXTENSION_CLASS_DESCRIPTOR->fixSubtitleWindowPosition(IIIZZ)[I
move-result-object v0
const/4 v1, 0x0
aget p1, v0, v1 # ap, anchor position
const/4 v1, 0x1
aget p2, v0, v1 # ah, horizontal anchor
const/4 v1, 0x2
aget p3, v0, v1 # av, vertical anchor
"""
)
// endregion
// region fix for feature flags
if (ageRestrictedPlaybackFeatureFlagFingerprint.resolvable()) {
ageRestrictedPlaybackFeatureFlagFingerprint.injectLiteralInstructionBooleanCall(
AGE_RESTRICTED_PLAYBACK_FEATURE_FLAG,
"$EXTENSION_CLASS_DESCRIPTOR->forceDisableAgeRestrictedPlaybackFeatureFlag(Z)Z"
)
}
// endregion
addSwitchPreference(
CategoryType.MISC,
"revanced_spoof_player_parameter",
"true"
)
updatePatchStatus(SPOOF_PLAYER_PARAMETER)
}
}

View File

@ -157,6 +157,10 @@ internal enum class PatchList(
"Spoof client",
"Adds options to spoof the client to allow playback."
),
SPOOF_PLAYER_PARAMETER(
"Spoof player parameter",
"Adds options to spoof player parameter to allow playback."
),
TRANSLATIONS_FOR_YOUTUBE_MUSIC(
"Translations for YouTube Music",
"Add translations or remove string resources."

View File

@ -105,6 +105,14 @@ var trimSilenceSwitch = -1L
private set
var varispeedUnavailableTitle = -1L
private set
var ytFillSamples = -1L
private set
var ytFillYouTubeMusic = -1L
private set
var ytOutlineSamples = -1L
private set
var ytOutlineYouTubeMusic = -1L
private set
var ytmLogo = -1L
private set
var ytmLogoRingo2 = -1L
@ -300,6 +308,22 @@ internal val sharedResourceIdPatch = resourcePatch(
STRING,
"varispeed_unavailable_title"
]
ytFillSamples = resourceMappings[
DRAWABLE,
"yt_fill_samples_vd_theme_24",
]
ytFillYouTubeMusic = resourceMappings[
DRAWABLE,
"yt_fill_youtube_music_vd_theme_24",
]
ytOutlineSamples = resourceMappings[
DRAWABLE,
"yt_outline_samples_vd_theme_24",
]
ytOutlineYouTubeMusic = resourceMappings[
DRAWABLE,
"yt_outline_youtube_music_vd_theme_24",
]
ytmLogo = resourceMappings[
DRAWABLE,
"ytm_logo",

View File

@ -13,6 +13,9 @@ import app.revanced.patches.music.utils.extension.Constants.SHARED_PATH
import app.revanced.patches.music.utils.playbackSpeedFingerprint
import app.revanced.patches.music.utils.playbackSpeedParentFingerprint
import app.revanced.patches.music.utils.resourceid.sharedResourceIdPatch
import app.revanced.patches.music.video.playerresponse.Hook
import app.revanced.patches.music.video.playerresponse.addPlayerResponseMethodHook
import app.revanced.patches.music.video.playerresponse.playerResponseMethodHookPatch
import app.revanced.patches.shared.mdxPlayerDirectorSetVideoStageFingerprint
import app.revanced.patches.shared.videoLengthFingerprint
import app.revanced.util.addStaticFieldToExtension
@ -71,7 +74,10 @@ private var videoTimeConstructorInsertIndex = 2
val videoInformationPatch = bytecodePatch(
description = "videoInformationPatch",
) {
dependsOn(sharedResourceIdPatch)
dependsOn(
playerResponseMethodHookPatch,
sharedResourceIdPatch
)
execute {
fun addSeekInterfaceMethods(
@ -241,7 +247,18 @@ val videoInformationPatch = bytecodePatch(
* Set current video id
*/
videoIdHook("$EXTENSION_CLASS_DESCRIPTOR->setVideoId(Ljava/lang/String;)V")
addPlayerResponseMethodHook(
Hook.VideoId(
"$EXTENSION_CLASS_DESCRIPTOR->setPlayerResponseVideoId(Ljava/lang/String;)V"
),
)
// Call before any other video id hooks,
// so they can use VideoInformation and check if the video id is for a Short.
addPlayerResponseMethodHook(
Hook.PlayerParameterBeforeVideoId(
"$EXTENSION_CLASS_DESCRIPTOR->newPlayerResponseParameter(Ljava/lang/String;Ljava/lang/String;)Ljava/lang/String;"
)
)
/**
* Hook current playback speed
*/

View File

@ -11,8 +11,7 @@ private val PLAYER_PARAMETER_STARTS_WITH_PARAMETER_LIST = listOf(
"[B",
"Ljava/lang/String;", // Player parameters proto buffer.
"Ljava/lang/String;", // PlaylistId.
"I", // PlaylistIndex.
"I"
"I" // PlaylistIndex.
)
/**
@ -30,7 +29,7 @@ internal val playerParameterBuilderFingerprint = legacyFingerprint(
return@custom false
}
val startsWithMethodParameterList = parameterTypes.slice(0..5)
val startsWithMethodParameterList = parameterTypes.slice(0..4)
parametersEqual(
PLAYER_PARAMETER_STARTS_WITH_PARAMETER_LIST,

View File

@ -1,22 +1,35 @@
package app.revanced.patches.music.video.playerresponse
import app.revanced.patcher.extensions.InstructionExtensions.addInstruction
import app.revanced.patcher.extensions.InstructionExtensions.addInstructions
import app.revanced.patcher.patch.bytecodePatch
import app.revanced.patcher.util.proxy.mutableTypes.MutableMethod
import app.revanced.patches.music.utils.extension.sharedExtensionPatch
import app.revanced.patches.music.utils.playservice.is_7_03_or_greater
import app.revanced.patches.music.utils.playservice.versionCheckPatch
import app.revanced.util.fingerprint.methodOrThrow
private val hooks = mutableSetOf<Hook>()
fun addPlayerResponseMethodHook(hook: Hook) {
hooks += hook
}
private const val REGISTER_VIDEO_ID = "p1"
private const val REGISTER_PLAYER_PARAMETER = "p3"
private const val REGISTER_PLAYLIST_ID = "p4"
private const val REGISTER_PLAYLIST_INDEX = "p5"
private lateinit var playerResponseMethod: MutableMethod
private var numberOfInstructionsAdded = 0
val playerResponseMethodHookPatch = bytecodePatch(
description = "playerResponseMethodHookPatch"
) {
dependsOn(versionCheckPatch)
dependsOn(
sharedExtensionPatch,
versionCheckPatch,
)
execute {
playerResponseMethod = if (is_7_03_or_greater) {
@ -25,16 +38,70 @@ val playerResponseMethodHookPatch = bytecodePatch(
playerParameterBuilderLegacyFingerprint
}.methodOrThrow()
}
finalize {
fun hookVideoId(hook: Hook) {
playerResponseMethod.addInstruction(
0,
"invoke-static {$REGISTER_VIDEO_ID}, $hook",
)
numberOfInstructionsAdded++
}
fun hookVideoIdAndPlaylistId(hook: Hook) {
playerResponseMethod.addInstruction(
0,
"invoke-static {$REGISTER_VIDEO_ID, $REGISTER_PLAYLIST_ID, $REGISTER_PLAYLIST_INDEX}, $hook",
)
numberOfInstructionsAdded++
}
fun hookPlayerParameter(hook: Hook) {
playerResponseMethod.addInstructions(
0,
"""
invoke-static {$REGISTER_VIDEO_ID, v0}, $hook
move-result-object v0
""",
)
numberOfInstructionsAdded += 2
}
// Reverse the order in order to preserve insertion order of the hooks.
val beforeVideoIdHooks =
hooks.filterIsInstance<Hook.PlayerParameterBeforeVideoId>().asReversed()
val videoIdHooks = hooks.filterIsInstance<Hook.VideoId>().asReversed()
val videoIdAndPlaylistIdHooks = hooks.filterIsInstance<Hook.VideoIdAndPlaylistId>().asReversed()
val afterVideoIdHooks = hooks.filterIsInstance<Hook.PlayerParameter>().asReversed()
// Add the hooks in this specific order as they insert instructions at the beginning of the method.
afterVideoIdHooks.forEach(::hookPlayerParameter)
videoIdAndPlaylistIdHooks.forEach(::hookVideoIdAndPlaylistId)
videoIdHooks.forEach(::hookVideoId)
beforeVideoIdHooks.forEach(::hookPlayerParameter)
playerResponseMethod.apply {
addInstruction(
0,
"move-object/from16 v0, $REGISTER_PLAYER_PARAMETER"
)
numberOfInstructionsAdded++
// Move the modified register back.
addInstruction(
numberOfInstructionsAdded,
"move-object/from16 $REGISTER_PLAYER_PARAMETER, v0"
)
}
}
}
fun hookPlayerResponse(
descriptor: String,
onlyVideoId: Boolean = false
) {
val smaliInstruction = if (onlyVideoId)
"invoke-static {$REGISTER_VIDEO_ID}, $descriptor"
else
"invoke-static {$REGISTER_VIDEO_ID, $REGISTER_PLAYLIST_ID, $REGISTER_PLAYLIST_INDEX}, $descriptor"
sealed class Hook(private val methodDescriptor: String) {
class VideoId(methodDescriptor: String) : Hook(methodDescriptor)
class VideoIdAndPlaylistId(methodDescriptor: String) : Hook(methodDescriptor)
playerResponseMethod.addInstruction(0, smaliInstruction)
class PlayerParameter(methodDescriptor: String) : Hook(methodDescriptor)
class PlayerParameterBeforeVideoId(methodDescriptor: String) : Hook(methodDescriptor)
override fun toString() = methodDescriptor
}

View File

@ -5,9 +5,11 @@ import app.revanced.patcher.extensions.InstructionExtensions.addInstructionsWith
import app.revanced.patcher.extensions.InstructionExtensions.getInstruction
import app.revanced.patcher.extensions.InstructionExtensions.replaceInstruction
import app.revanced.patcher.patch.bytecodePatch
import app.revanced.patcher.util.proxy.mutableTypes.MutableMethod
import app.revanced.patches.reddit.utils.compatibility.Constants.COMPATIBLE_PACKAGE
import app.revanced.patches.reddit.utils.extension.Constants.PATCHES_PATH
import app.revanced.patches.reddit.utils.patch.PatchList.HIDE_ADS
import app.revanced.patches.reddit.utils.settings.is_2025_06_or_greater
import app.revanced.patches.reddit.utils.settings.settingsPatch
import app.revanced.patches.reddit.utils.settings.updatePatchStatus
import app.revanced.util.findMutableMethodOf
@ -78,13 +80,8 @@ val adsPatch = bytecodePatch(
}
// region Filter comment ads
classes.forEach { classDef ->
classDef.methods.forEach { method ->
if (method.isCommentAdsMethod()) {
proxy(classDef)
.mutableClass
.findMutableMethodOf(method)
.addInstructionsWithLabels(
fun MutableMethod.hook() =
addInstructionsWithLabels(
0, """
invoke-static {}, $EXTENSION_CLASS_DESCRIPTOR->hideCommentAds()Z
move-result v0
@ -94,6 +91,22 @@ val adsPatch = bytecodePatch(
nop
"""
)
if (is_2025_06_or_greater) {
listOf(
commentAdCommentScreenAdViewFingerprint,
commentAdDetailListHeaderViewFingerprint
).forEach { fingerprint ->
fingerprint.methodOrThrow().hook()
}
} else {
classes.forEach { classDef ->
classDef.methods.forEach { method ->
if (method.isCommentAdsMethod()) {
proxy(classDef)
.mutableClass
.findMutableMethodOf(method)
.hook()
}
}
}
}

View File

@ -27,6 +27,28 @@ internal val adPostFingerprint = legacyFingerprint(
},
)
internal val commentAdCommentScreenAdViewFingerprint = legacyFingerprint(
name = "commentAdCommentScreenAdViewFingerprint",
returnType = "V",
accessFlags = AccessFlags.PUBLIC or AccessFlags.FINAL,
parameters = listOf("L"),
strings = listOf("ad"),
customFingerprint = { _, classDef ->
classDef.type.endsWith("/CommentScreenAdView;")
},
)
internal val commentAdDetailListHeaderViewFingerprint = legacyFingerprint(
name = "commentAdDetailListHeaderViewFingerprint",
returnType = "V",
accessFlags = AccessFlags.PUBLIC or AccessFlags.FINAL,
parameters = listOf("L"),
strings = listOf("ad"),
customFingerprint = { _, classDef ->
classDef.type.endsWith("/DetailListHeaderView;")
},
)
internal val newAdPostFingerprint = legacyFingerprint(
name = "newAdPostFingerprint",
returnType = "L",

View File

@ -1,5 +1,6 @@
package app.revanced.patches.reddit.layout.navigation
import app.revanced.util.containsLiteralInstruction
import app.revanced.util.fingerprint.legacyFingerprint
import app.revanced.util.getReference
import app.revanced.util.indexOfFirstInstruction
@ -8,6 +9,7 @@ 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.MethodReference
import com.android.tools.smali.dexlib2.iface.reference.TypeReference
internal val bottomNavScreenFingerprint = legacyFingerprint(
name = "bottomNavScreenFingerprint",
@ -62,13 +64,32 @@ internal val bottomNavScreenOnGlobalLayoutFingerprint = legacyFingerprint(
}
)
private const val CHAT_BUTTON_MAGIC_NUMBER = 1906671695L
internal val bottomNavScreenSetupBottomNavigationFingerprint = legacyFingerprint(
name = "bottomNavScreenSetupBottomNavigationFingerprint",
returnType = "V",
accessFlags = AccessFlags.PUBLIC or AccessFlags.FINAL,
opcodes = listOf(Opcode.FILLED_NEW_ARRAY),
customFingerprint = { method, classDef ->
classDef.type.startsWith("Lcom/reddit/launch/bottomnav/BottomNavScreen${'$'}setupBottomNavigation${'$'}") &&
method.name == "invoke"
method.containsLiteralInstruction(CHAT_BUTTON_MAGIC_NUMBER) &&
method.name == "invoke" &&
indexOfButtonsArrayInstruction(method) >= 0
}
)
internal val composeBottomNavScreenFingerprint = legacyFingerprint(
name = "composeBottomNavScreenFingerprint",
accessFlags = AccessFlags.PUBLIC or AccessFlags.FINAL,
parameters = listOf("Landroid/content/res/Resources;"),
opcodes = listOf(Opcode.FILLED_NEW_ARRAY),
customFingerprint = { method, classDef ->
classDef.type == "Lcom/reddit/launch/bottomnav/ComposeBottomNavScreen;" &&
indexOfButtonsArrayInstruction(method) >= 0
}
)
internal fun indexOfButtonsArrayInstruction(method: Method) =
method.indexOfFirstInstruction {
opcode == Opcode.FILLED_NEW_ARRAY &&
getReference<TypeReference>()?.type?.startsWith("[Lcom/reddit/widget/bottomnav/") == true
}

View File

@ -8,12 +8,12 @@ import app.revanced.patches.reddit.utils.compatibility.Constants.COMPATIBLE_PACK
import app.revanced.patches.reddit.utils.extension.Constants.PATCHES_PATH
import app.revanced.patches.reddit.utils.patch.PatchList.HIDE_NAVIGATION_BUTTONS
import app.revanced.patches.reddit.utils.settings.is_2024_26_or_greater
import app.revanced.patches.reddit.utils.settings.is_2025_06_or_greater
import app.revanced.patches.reddit.utils.settings.settingsPatch
import app.revanced.patches.reddit.utils.settings.updatePatchStatus
import app.revanced.util.fingerprint.methodOrThrow
import app.revanced.util.fingerprint.resolvable
import app.revanced.util.indexOfFirstInstructionOrThrow
import app.revanced.util.indexOfFirstInstructionReversedOrThrow
import com.android.tools.smali.dexlib2.Opcode
import com.android.tools.smali.dexlib2.iface.instruction.FiveRegisterInstruction
import com.android.tools.smali.dexlib2.iface.instruction.OneRegisterInstruction
@ -34,8 +34,13 @@ val navigationButtonsPatch = bytecodePatch(
execute {
if (is_2024_26_or_greater) {
bottomNavScreenSetupBottomNavigationFingerprint.methodOrThrow().apply {
val arrayIndex = indexOfFirstInstructionReversedOrThrow(Opcode.FILLED_NEW_ARRAY)
val fingerprints = mutableListOf(bottomNavScreenSetupBottomNavigationFingerprint)
if (is_2025_06_or_greater) fingerprints += composeBottomNavScreenFingerprint
fingerprints.forEach { fingerprint ->
fingerprint.methodOrThrow().apply {
val arrayIndex = indexOfButtonsArrayInstruction(this)
val arrayRegister =
getInstruction<OneRegisterInstruction>(arrayIndex + 1).registerA
@ -46,6 +51,7 @@ val navigationButtonsPatch = bytecodePatch(
"""
)
}
}
} else {
if (bottomNavScreenFingerprint.resolvable()) {
val bottomNavScreenMutableClass = with(bottomNavScreenFingerprint.methodOrThrow()) {

View File

@ -1,9 +1,32 @@
package app.revanced.patches.reddit.layout.screenshotpopup
import app.revanced.patches.reddit.utils.resourceid.screenShotShareBanner
import app.revanced.util.containsLiteralInstruction
import app.revanced.util.fingerprint.legacyFingerprint
import com.android.tools.smali.dexlib2.Opcode
/**
* Reddit 2025.06.0 ~
*/
internal val screenshotTakenBannerFingerprint = legacyFingerprint(
name = "screenshotTakenBannerFingerprint",
returnType = "L",
opcodes = listOf(
Opcode.CONST_4,
Opcode.IF_NE,
),
customFingerprint = { method, classDef ->
method.containsLiteralInstruction(screenShotShareBanner) &&
classDef.type.startsWith("Lcom/reddit/sharing/screenshot/composables/") &&
method.name == "invoke"
}
)
/**
* ~ Reddit 2025.05.1
*/
internal val screenshotTakenBannerLegacyFingerprint = legacyFingerprint(
name = "screenshotTakenBannerLegacyFingerprint",
returnType = "V",
parameters = listOf("Landroidx/compose/runtime/", "I"),
customFingerprint = { method, classDef ->

View File

@ -7,9 +7,17 @@ import app.revanced.patcher.util.smali.ExternalLabel
import app.revanced.patches.reddit.utils.compatibility.Constants.COMPATIBLE_PACKAGE
import app.revanced.patches.reddit.utils.extension.Constants.PATCHES_PATH
import app.revanced.patches.reddit.utils.patch.PatchList.DISABLE_SCREENSHOT_POPUP
import app.revanced.patches.reddit.utils.resourceid.screenShotShareBanner
import app.revanced.patches.reddit.utils.resourceid.sharedResourceIdPatch
import app.revanced.patches.reddit.utils.settings.is_2025_06_or_greater
import app.revanced.patches.reddit.utils.settings.settingsPatch
import app.revanced.patches.reddit.utils.settings.updatePatchStatus
import app.revanced.util.fingerprint.methodOrThrow
import app.revanced.util.indexOfFirstInstructionOrThrow
import app.revanced.util.indexOfFirstInstructionReversedOrThrow
import app.revanced.util.indexOfFirstLiteralInstructionOrThrow
import com.android.tools.smali.dexlib2.Opcode
import com.android.tools.smali.dexlib2.iface.instruction.OneRegisterInstruction
private const val EXTENSION_METHOD_DESCRIPTOR =
"$PATCHES_PATH/ScreenshotPopupPatch;->disableScreenshotPopup()Z"
@ -21,10 +29,30 @@ val screenshotPopupPatch = bytecodePatch(
) {
compatibleWith(COMPATIBLE_PACKAGE)
dependsOn(settingsPatch)
dependsOn(
settingsPatch,
sharedResourceIdPatch,
)
execute {
if (is_2025_06_or_greater) {
screenshotTakenBannerFingerprint.methodOrThrow().apply {
val literalIndex = indexOfFirstLiteralInstructionOrThrow(screenShotShareBanner)
val insertIndex = indexOfFirstInstructionReversedOrThrow(literalIndex, Opcode.CONST_4)
val insertRegister = getInstruction<OneRegisterInstruction>(insertIndex).registerA
val jumpIndex = indexOfFirstInstructionOrThrow(literalIndex, Opcode.SGET_OBJECT)
addInstructionsWithLabels(
insertIndex, """
invoke-static {}, $EXTENSION_METHOD_DESCRIPTOR
move-result v$insertRegister
if-nez v$insertRegister, :hidden
""", ExternalLabel("hidden", getInstruction(jumpIndex))
)
}
} else {
screenshotTakenBannerLegacyFingerprint.methodOrThrow().apply {
addInstructionsWithLabels(
0, """
invoke-static {}, $EXTENSION_METHOD_DESCRIPTOR
@ -34,6 +62,7 @@ val screenshotPopupPatch = bytecodePatch(
""", ExternalLabel("dismiss", getInstruction(0))
)
}
}
updatePatchStatus(
"enableScreenshotPopup",

View File

@ -0,0 +1,23 @@
package app.revanced.patches.reddit.utils.resourceid
import app.revanced.patcher.patch.resourcePatch
import app.revanced.patches.shared.mapping.ResourceType.STRING
import app.revanced.patches.shared.mapping.get
import app.revanced.patches.shared.mapping.resourceMappingPatch
import app.revanced.patches.shared.mapping.resourceMappings
var screenShotShareBanner = -1L
private set
internal val sharedResourceIdPatch = resourcePatch(
description = "sharedResourceIdPatch"
) {
dependsOn(resourceMappingPatch)
execute {
screenShotShareBanner = resourceMappings[
STRING,
"screenshot_share_banner_title",
]
}
}

View File

@ -259,13 +259,7 @@ val feedComponentsPatch = bytecodePatch(
elementParserFingerprint.matchOrThrow(elementParserParentFingerprint).let {
it.method.apply {
val freeRegister = implementation!!.registerCount - parameters.size - 2
val insertIndex = indexOfFirstInstructionOrThrow {
val reference = getReference<MethodReference>()
reference?.parameterTypes?.size == 1 &&
reference.parameterTypes.first() == "[B" &&
reference.returnType.startsWith("L")
}
val insertIndex = indexOfBufferParserInstruction(this)
if (is_19_46_or_greater) {
val objectIndex = indexOfFirstInstructionReversedOrThrow(insertIndex, Opcode.IGET_OBJECT)

View File

@ -11,9 +11,13 @@ import app.revanced.patches.youtube.utils.resourceid.filterBarHeight
import app.revanced.patches.youtube.utils.resourceid.horizontalCardList
import app.revanced.patches.youtube.utils.resourceid.relatedChipCloudMargin
import app.revanced.util.fingerprint.legacyFingerprint
import app.revanced.util.getReference
import app.revanced.util.indexOfFirstInstruction
import app.revanced.util.or
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.MethodReference
internal val breakingNewsFingerprint = legacyFingerprint(
name = "breakingNewsFingerprint",
@ -90,9 +94,19 @@ internal val elementParserFingerprint = legacyFingerprint(
Opcode.MOVE_RESULT_OBJECT,
Opcode.IGET_OBJECT,
Opcode.RETURN_OBJECT
)
),
customFingerprint = { method, _ ->
indexOfBufferParserInstruction(method) >= 0
}
)
internal fun indexOfBufferParserInstruction(method: Method) =
method.indexOfFirstInstruction {
val reference = getReference<MethodReference>()
reference?.parameterTypes?.firstOrNull() == "[B" &&
reference.returnType.startsWith("L")
}
internal val elementParserParentFingerprint = legacyFingerprint(
name = "elementParserParentFingerprint",
returnType = "L",

View File

@ -1,4 +1,4 @@
package app.revanced.patches.youtube.general.layoutswitch
package app.revanced.patches.youtube.general.formfactor
import app.revanced.patcher.extensions.InstructionExtensions.addInstructions
import app.revanced.patcher.extensions.InstructionExtensions.getInstruction
@ -6,30 +6,37 @@ import app.revanced.patcher.patch.bytecodePatch
import app.revanced.patches.shared.createPlayerRequestBodyWithModelFingerprint
import app.revanced.patches.youtube.utils.compatibility.Constants.COMPATIBLE_PACKAGE
import app.revanced.patches.youtube.utils.extension.Constants.GENERAL_PATH
import app.revanced.patches.youtube.utils.patch.PatchList.CHANGE_LAYOUT
import app.revanced.patches.youtube.utils.navigation.hookNavigationButtonCreated
import app.revanced.patches.youtube.utils.navigation.navigationBarHookPatch
import app.revanced.patches.youtube.utils.patch.PatchList.CHANGE_FORM_FACTOR
import app.revanced.patches.youtube.utils.playertype.playerTypeHookPatch
import app.revanced.patches.youtube.utils.settings.ResourceUtils.addPreference
import app.revanced.patches.youtube.utils.settings.settingsPatch
import app.revanced.util.fingerprint.definingClassOrThrow
import app.revanced.util.fingerprint.matchOrThrow
import app.revanced.util.fingerprint.methodOrThrow
import app.revanced.util.getReference
import app.revanced.util.indexOfFirstInstructionOrThrow
import app.revanced.util.indexOfFirstInstructionReversedOrThrow
import com.android.tools.smali.dexlib2.Opcode
import com.android.tools.smali.dexlib2.iface.instruction.OneRegisterInstruction
import com.android.tools.smali.dexlib2.iface.instruction.TwoRegisterInstruction
import com.android.tools.smali.dexlib2.iface.reference.FieldReference
private const val EXTENSION_CLASS_DESCRIPTOR =
"$GENERAL_PATH/LayoutSwitchPatch;"
"$GENERAL_PATH/ChangeFormFactorPatch;"
@Suppress("unused")
val layoutSwitchPatch = bytecodePatch(
CHANGE_LAYOUT.title,
CHANGE_LAYOUT.summary,
val changeFormFactorPatch = bytecodePatch(
CHANGE_FORM_FACTOR.title,
CHANGE_FORM_FACTOR.summary,
) {
compatibleWith(COMPATIBLE_PACKAGE)
dependsOn(settingsPatch)
dependsOn(
settingsPatch,
playerTypeHookPatch,
navigationBarHookPatch,
)
execute {
@ -53,8 +60,9 @@ val layoutSwitchPatch = bytecodePatch(
)
}
layoutSwitchFingerprint.methodOrThrow().apply {
val index = indexOfFirstInstructionReversedOrThrow(Opcode.IF_NEZ)
widthDpUIFingerprint.matchOrThrow().let {
it.method.apply {
val index = it.patternMatch!!.startIndex
val register = getInstruction<OneRegisterInstruction>(index).registerA
addInstructions(
@ -64,6 +72,9 @@ val layoutSwitchPatch = bytecodePatch(
"""
)
}
}
hookNavigationButtonCreated(EXTENSION_CLASS_DESCRIPTOR)
// region add settings
@ -71,9 +82,9 @@ val layoutSwitchPatch = bytecodePatch(
arrayOf(
"PREFERENCE_SCREEN: GENERAL",
"PREFERENCE_CATEGORY: GENERAL_EXPERIMENTAL_FLAGS",
"SETTINGS: CHANGE_LAYOUT"
"SETTINGS: CHANGE_FORM_FACTOR"
),
CHANGE_LAYOUT
CHANGE_FORM_FACTOR
)
// endregion

View File

@ -1,4 +1,4 @@
package app.revanced.patches.youtube.general.layoutswitch
package app.revanced.patches.youtube.general.formfactor
import app.revanced.util.fingerprint.legacyFingerprint
import app.revanced.util.or
@ -11,20 +11,16 @@ internal val formFactorEnumConstructorFingerprint = legacyFingerprint(
strings = listOf(
"UNKNOWN_FORM_FACTOR",
"SMALL_FORM_FACTOR",
"LARGE_FORM_FACTOR"
"LARGE_FORM_FACTOR",
"AUTOMOTIVE_FORM_FACTOR",
)
)
internal val layoutSwitchFingerprint = legacyFingerprint(
name = "layoutSwitchFingerprint",
internal val widthDpUIFingerprint = legacyFingerprint(
name = "widthDpUIFingerprint",
returnType = "I",
accessFlags = AccessFlags.PUBLIC or AccessFlags.STATIC,
parameters = listOf("L"),
opcodes = listOf(
Opcode.INVOKE_VIRTUAL,
Opcode.MOVE_RESULT_OBJECT,
Opcode.INVOKE_STATIC,
Opcode.MOVE_RESULT,
Opcode.IF_NEZ,
Opcode.CONST_4,
Opcode.RETURN,
@ -41,6 +37,11 @@ internal val layoutSwitchFingerprint = legacyFingerprint(
Opcode.CONST_4,
Opcode.RETURN,
Opcode.CONST_4,
Opcode.RETURN
Opcode.RETURN,
),
literals = listOf(
480L,
600L,
720L
)
)

View File

@ -91,7 +91,25 @@ internal val createSearchSuggestionsFingerprint = legacyFingerprint(
returnType = "Landroid/view/View;",
accessFlags = AccessFlags.PUBLIC or AccessFlags.FINAL,
parameters = listOf("I", "Landroid/view/View;", "Landroid/view/ViewGroup;"),
strings = listOf("ss_rds")
strings = listOf("ss_rds"),
customFingerprint = { method, _ ->
indexOfIteratorInstruction(method) >= 0
}
)
internal fun indexOfIteratorInstruction(method: Method) =
method.indexOfFirstInstructionReversed {
opcode == Opcode.INVOKE_INTERFACE &&
getReference<MethodReference>()?.toString() == "Ljava/util/Iterator;->next()Ljava/lang/Object;"
}
// Flag is present in YouTube 19.16, but was not used until YouTube 19.43.
// Related issue: https://github.com/inotia00/ReVanced_Extended/issues/2784
internal const val SEARCH_FRAGMENT_FEATURE_FLAG = 45353159L
internal val searchFragmentFeatureFlagFingerprint = legacyFingerprint(
name = "searchFragmentFeatureFlagFingerprint",
literals = listOf(SEARCH_FRAGMENT_FEATURE_FLAG),
)
internal val drawerContentViewConstructorFingerprint = legacyFingerprint(

View File

@ -15,7 +15,7 @@ import app.revanced.patches.youtube.utils.castbutton.hookToolBarCastButton
import app.revanced.patches.youtube.utils.compatibility.Constants.COMPATIBLE_PACKAGE
import app.revanced.patches.youtube.utils.extension.Constants.GENERAL_CLASS_DESCRIPTOR
import app.revanced.patches.youtube.utils.patch.PatchList.TOOLBAR_COMPONENTS
import app.revanced.patches.youtube.utils.playservice.is_19_46_or_greater
import app.revanced.patches.youtube.utils.playservice.is_19_16_or_greater
import app.revanced.patches.youtube.utils.playservice.versionCheckPatch
import app.revanced.patches.youtube.utils.resourceid.actionBarRingoBackground
import app.revanced.patches.youtube.utils.resourceid.sharedResourceIdPatch
@ -31,6 +31,7 @@ import app.revanced.util.REGISTER_TEMPLATE_REPLACEMENT
import app.revanced.util.doRecursively
import app.revanced.util.findInstructionIndicesReversedOrThrow
import app.revanced.util.findMethodOrThrow
import app.revanced.util.fingerprint.injectLiteralInstructionBooleanCall
import app.revanced.util.fingerprint.matchOrThrow
import app.revanced.util.fingerprint.methodCall
import app.revanced.util.fingerprint.methodOrThrow
@ -38,7 +39,6 @@ import app.revanced.util.fingerprint.mutableClassOrThrow
import app.revanced.util.getReference
import app.revanced.util.getWalkerMethod
import app.revanced.util.indexOfFirstInstructionOrThrow
import app.revanced.util.indexOfFirstInstructionReversedOrThrow
import app.revanced.util.indexOfFirstLiteralInstructionOrThrow
import app.revanced.util.replaceLiteralInstructionCall
import com.android.tools.smali.dexlib2.Opcode
@ -46,6 +46,7 @@ import com.android.tools.smali.dexlib2.iface.instruction.FiveRegisterInstruction
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.instruction.TwoRegisterInstruction
import com.android.tools.smali.dexlib2.iface.reference.FieldReference
import com.android.tools.smali.dexlib2.iface.reference.MethodReference
import com.android.tools.smali.dexlib2.util.MethodUtil
import org.w3c.dom.Element
@ -266,36 +267,39 @@ val toolBarComponentsPatch = bytecodePatch(
// region patch for hide search term thumbnail
createSearchSuggestionsFingerprint.methodOrThrow().apply {
val literal = if (is_19_46_or_greater)
32L
else
40L
val relativeIndex = indexOfFirstLiteralInstructionOrThrow(literal)
val replaceIndex = indexOfFirstInstructionReversedOrThrow(relativeIndex) {
opcode == Opcode.INVOKE_VIRTUAL &&
getReference<MethodReference>()?.toString() == "Landroid/widget/ImageView;->setVisibility(I)V"
} - 1
val jumpIndex = indexOfFirstInstructionOrThrow(relativeIndex) {
val iteratorIndex = indexOfIteratorInstruction(this)
val replaceIndex = indexOfFirstInstructionOrThrow(iteratorIndex) {
opcode == Opcode.IGET_OBJECT &&
getReference<FieldReference>()?.type == "Landroid/widget/ImageView;"
}
val jumpIndex = indexOfFirstInstructionOrThrow(replaceIndex) {
opcode == Opcode.INVOKE_STATIC &&
getReference<MethodReference>()?.toString() == "Landroid/net/Uri;->parse(Ljava/lang/String;)Landroid/net/Uri;"
} + 4
val replaceIndexInstruction = getInstruction<TwoRegisterInstruction>(replaceIndex)
val freeRegister = replaceIndexInstruction.registerA
val classRegister = replaceIndexInstruction.registerB
val replaceIndexReference =
getInstruction<ReferenceInstruction>(replaceIndex).reference
addInstructionsWithLabels(
replaceIndex + 1, """
invoke-static { }, $GENERAL_CLASS_DESCRIPTOR->hideSearchTermThumbnail()Z
move-result v${replaceIndexInstruction.registerA}
if-nez v${replaceIndexInstruction.registerA}, :hidden
iget-object v${replaceIndexInstruction.registerA}, v${replaceIndexInstruction.registerB}, $replaceIndexReference
move-result v$freeRegister
if-nez v$freeRegister, :hidden
iget-object v$freeRegister, v$classRegister, $replaceIndexReference
""", ExternalLabel("hidden", getInstruction(jumpIndex))
)
removeInstruction(replaceIndex)
}
if (is_19_16_or_greater) {
searchFragmentFeatureFlagFingerprint.injectLiteralInstructionBooleanCall(
SEARCH_FRAGMENT_FEATURE_FLAG,
"$GENERAL_CLASS_DESCRIPTOR->hideSearchTermThumbnail(Z)Z"
)
}
// endregion
/*

View File

@ -4,12 +4,14 @@ import app.revanced.patcher.patch.booleanOption
import app.revanced.patcher.patch.resourcePatch
import app.revanced.patcher.patch.stringOption
import app.revanced.patches.youtube.utils.compatibility.Constants.COMPATIBLE_PACKAGE
import app.revanced.patches.youtube.utils.extension.Constants.PATCH_STATUS_CLASS_DESCRIPTOR
import app.revanced.patches.youtube.utils.patch.PatchList.CUSTOM_BRANDING_ICON_FOR_YOUTUBE
import app.revanced.patches.youtube.utils.playservice.is_19_17_or_greater
import app.revanced.patches.youtube.utils.playservice.is_19_32_or_greater
import app.revanced.patches.youtube.utils.playservice.is_19_34_or_greater
import app.revanced.patches.youtube.utils.playservice.versionCheckPatch
import app.revanced.patches.youtube.utils.settings.ResourceUtils.updatePatchStatusIcon
import app.revanced.patches.youtube.utils.settings.getBytecodeContext
import app.revanced.patches.youtube.utils.settings.settingsPatch
import app.revanced.util.ResourceGroup
import app.revanced.util.Utils.printWarn
@ -19,6 +21,7 @@ import app.revanced.util.copyFile
import app.revanced.util.copyResources
import app.revanced.util.getResourceGroup
import app.revanced.util.underBarOrThrow
import app.revanced.util.updatePatchStatus
import app.revanced.util.valueOrThrow
import org.w3c.dom.Element
@ -247,11 +250,17 @@ val customBrandingIconPatch = resourcePatch(
resourcesNode.appendChild(style)
}
}
getBytecodeContext().apply {
updatePatchStatus(PATCH_STATUS_CLASS_DESCRIPTOR, "OldSplashAnimation")
}
}
updatePatchStatusIcon(appIcon)
}
CUSTOM_BRANDING_ICON_FOR_YOUTUBE.included = true
// region fix app icon
if (!is_19_34_or_greater) {

View File

@ -123,7 +123,7 @@ val seekbarComponentsPatch = bytecodePatch(
execute {
val restoreOldSplashAnimationIncluded = CUSTOM_BRANDING_ICON_FOR_YOUTUBE.included == true &&
customBrandingIconPatch.getBooleanOptionValue("restoreOldSplashAnimationOption").value == true
customBrandingIconPatch.getBooleanOptionValue("restoreOldSplashAnimation").value == true
var settingArray = arrayOf(
"PREFERENCE_SCREEN: PLAYER",
@ -388,7 +388,7 @@ val seekbarComponentsPatch = bytecodePatch(
scaleNode.replaceChild(replacementNode, shapeNode)
}
if (is_19_25_or_greater && !restoreOldSplashAnimationIncluded) {
if (is_19_25_or_greater) {
// Add attribute and styles for splash screen custom color.
// Using a style is the only way to selectively change just the seekbar fill color.
//

View File

@ -234,7 +234,8 @@ internal val youtubeControlsOverlayFingerprint = legacyFingerprint(
accessFlags = AccessFlags.PRIVATE or AccessFlags.FINAL,
parameters = emptyList(),
literals = listOf(
fadeDurationFast,
// Removed in YouTube 20.09.40+
// fadeDurationFast,
insetOverlayViewLayout,
scrimOverlay,
// Removed in YouTube 20.02.38+

View File

@ -1,8 +1,11 @@
package app.revanced.patches.youtube.utils.fix.splash
import app.revanced.patcher.patch.resourcePatch
import app.revanced.patches.youtube.layout.branding.icon.customBrandingIconPatch
import app.revanced.patches.youtube.utils.patch.PatchList.CUSTOM_BRANDING_ICON_FOR_YOUTUBE
import app.revanced.patches.youtube.utils.playservice.is_19_32_or_greater
import app.revanced.patches.youtube.utils.playservice.versionCheckPatch
import app.revanced.util.getBooleanOptionValue
import org.w3c.dom.Element
val darkModeSplashScreenPatch = resourcePatch(
@ -10,10 +13,10 @@ val darkModeSplashScreenPatch = resourcePatch(
) {
dependsOn(versionCheckPatch)
execute {
if (!is_19_32_or_greater) {
return@execute
}
finalize {
val restoreOldSplashAnimationIncluded = is_19_32_or_greater &&
CUSTOM_BRANDING_ICON_FOR_YOUTUBE.included == true &&
customBrandingIconPatch.getBooleanOptionValue("restoreOldSplashAnimation").value == true
/**
* Fix the splash screen dark mode background color.
@ -24,6 +27,7 @@ val darkModeSplashScreenPatch = resourcePatch(
* This is a bug in unpatched YouTube.
* Should always be applied even if the `Theme` patch is excluded.
*/
if (restoreOldSplashAnimationIncluded) {
document("res/values-night/styles.xml").use { document ->
val resourcesNode = document.getElementsByTagName("resources").item(0) as Element
val childNodes = resourcesNode.childNodes
@ -48,5 +52,32 @@ val darkModeSplashScreenPatch = resourcePatch(
}
}
}
} else {
document("res/values-night-v27/styles.xml").use { document ->
// Create a night mode specific override for the splash screen background.
val style = document.createElement("style")
style.setAttribute("name", "Theme.YouTube.Home")
style.setAttribute("parent", "@style/Base.V27.Theme.YouTube.Home")
// Fix status and navigation bar showing white on some Android devices,
// such as SDK 28 Android 10 medium tablet.
val colorSplashBackgroundColor = "@color/yt_black1"
arrayOf(
"android:navigationBarColor" to colorSplashBackgroundColor,
"android:windowBackground" to colorSplashBackgroundColor,
"android:colorBackground" to colorSplashBackgroundColor,
"colorPrimaryDark" to colorSplashBackgroundColor,
"android:windowLightStatusBar" to "false",
).forEach { (name, value) ->
val styleItem = document.createElement("item")
styleItem.setAttribute("name", name)
styleItem.textContent = value
style.appendChild(styleItem)
}
val resourcesNode = document.getElementsByTagName("resources").item(0) as Element
resourcesNode.appendChild(style)
}
}
}
}

View File

@ -146,4 +146,10 @@ internal val onesieEncryptionFeatureFlagFingerprint = legacyFingerprint(
literals = listOf(ONESIE_ENCRYPTION_FEATURE_FLAG),
)
// YouTube 20.10 ~
internal const val ONESIE_ENCRYPTION_ALTERNATIVE_FEATURE_FLAG = 45683169L
internal val onesieEncryptionAlternativeFeatureFlagFingerprint = legacyFingerprint(
name = "onesieEncryptionAlternativeFeatureFlagFingerprint",
literals = listOf(ONESIE_ENCRYPTION_ALTERNATIVE_FEATURE_FLAG),
)

View File

@ -19,6 +19,7 @@ import app.revanced.patches.youtube.utils.compatibility.Constants.COMPATIBLE_PAC
import app.revanced.patches.youtube.utils.compatibility.Constants.YOUTUBE_PACKAGE_NAME
import app.revanced.patches.youtube.utils.patch.PatchList.SPOOF_STREAMING_DATA
import app.revanced.patches.youtube.utils.playservice.is_19_34_or_greater
import app.revanced.patches.youtube.utils.playservice.is_20_10_or_greater
import app.revanced.patches.youtube.utils.playservice.versionCheckPatch
import app.revanced.patches.youtube.utils.request.buildRequestPatch
import app.revanced.patches.youtube.utils.request.hookBuildRequest
@ -61,6 +62,10 @@ val spoofStreamingDataPatch = bytecodePatch(
execute {
var settingArray = arrayOf(
"SETTINGS: SPOOF_STREAMING_DATA"
)
// region Get replacement streams at player requests.
hookBuildRequest("$EXTENSION_CLASS_DESCRIPTOR->fetchStreams(Ljava/lang/String;Ljava/util/Map;)V")
@ -327,6 +332,15 @@ val spoofStreamingDataPatch = bytecodePatch(
ONESIE_ENCRYPTION_FEATURE_FLAG,
"$EXTENSION_CLASS_DESCRIPTOR->skipResponseEncryption(Z)Z"
)
if (is_20_10_or_greater) {
onesieEncryptionAlternativeFeatureFlagFingerprint.injectLiteralInstructionBooleanCall(
ONESIE_ENCRYPTION_ALTERNATIVE_FEATURE_FLAG,
"$EXTENSION_CLASS_DESCRIPTOR->skipResponseEncryption(Z)Z"
)
}
settingArray += "SETTINGS: SKIP_RESPONSE_ENCRYPTION"
}
// endregion
@ -339,9 +353,7 @@ val spoofStreamingDataPatch = bytecodePatch(
)
addPreference(
arrayOf(
"SETTINGS: SPOOF_STREAMING_DATA"
),
settingArray,
SPOOF_STREAMING_DATA
)
}

View File

@ -109,8 +109,7 @@ val navigationBarHookPatch = bytecodePatch(
hookNavigationButtonCreated = { extensionClassDescriptor ->
navigationBarHookCallbackFingerprint.methodOrThrow().addInstruction(
0,
"invoke-static { p0, p1 }, " +
"$extensionClassDescriptor->navigationTabCreated" +
"invoke-static { p0, p1 }, $extensionClassDescriptor->navigationTabCreated" +
"(${EXTENSION_NAVIGATION_BUTTON_DESCRIPTOR}Landroid/view/View;)V",
)
}

View File

@ -21,9 +21,9 @@ internal enum class PatchList(
"Bypass URL redirects",
"Adds an option to bypass URL redirects and open the original URL directly."
),
CHANGE_LAYOUT(
"Change layout",
"Adds an option to change the dp in order to use a tablet or phone layout."
CHANGE_FORM_FACTOR(
"Change form factor",
"Adds an option to change the UI appearance to a phone, tablet, or automotive device."
),
CHANGE_LIVE_RING_CLICK_ACTION(
"Change live ring click action",

View File

@ -1,6 +1,7 @@
package app.revanced.patches.youtube.utils.playertype
import app.revanced.patches.youtube.utils.resourceid.reelWatchPlayer
import app.revanced.patches.youtube.utils.resourceid.toolbarContainerId
import app.revanced.util.fingerprint.legacyFingerprint
import app.revanced.util.getReference
import app.revanced.util.indexOfFirstInstruction
@ -9,6 +10,7 @@ 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.MethodReference
import com.android.tools.smali.dexlib2.iface.reference.TypeReference
internal val browseIdClassFingerprint = legacyFingerprint(
name = "browseIdClassFingerprint",
@ -61,6 +63,34 @@ internal val searchQueryClassFingerprint = legacyFingerprint(
}
)
internal val toolbarLayoutFingerprint = legacyFingerprint(
name = "toolbarLayoutFingerprint",
literals = listOf(toolbarContainerId),
customFingerprint = { method, _ ->
method.name == "<init>" &&
indexOfMainCollapsingToolbarLayoutInstruction(method) >= 0
}
)
internal fun indexOfMainCollapsingToolbarLayoutInstruction(method: Method) =
method.indexOfFirstInstruction {
opcode == Opcode.CHECK_CAST &&
getReference<TypeReference>()?.type == "Lcom/google/android/apps/youtube/app/ui/actionbar/MainCollapsingToolbarLayout;"
}
/**
* Matches to https://android.googlesource.com/platform/frameworks/support/+/9eee6ba/v7/appcompat/src/android/support/v7/widget/Toolbar.java#963
*/
internal val appCompatToolbarBackButtonFingerprint = legacyFingerprint(
name = "appCompatToolbarBackButtonFingerprint",
accessFlags = AccessFlags.PUBLIC or AccessFlags.FINAL,
returnType = "Landroid/graphics/drawable/Drawable;",
parameters = emptyList(),
customFingerprint = { _, classDef ->
classDef.type == "Landroid/support/v7/widget/Toolbar;"
},
)
internal val videoStateFingerprint = legacyFingerprint(
name = "videoStateFingerprint",
returnType = "V",

View File

@ -5,6 +5,7 @@ import app.revanced.patcher.extensions.InstructionExtensions.addInstructions
import app.revanced.patcher.extensions.InstructionExtensions.getInstruction
import app.revanced.patcher.patch.PatchException
import app.revanced.patcher.patch.bytecodePatch
import app.revanced.patcher.util.proxy.mutableTypes.MutableMethod.Companion.toMutable
import app.revanced.patches.shared.litho.addLithoFilter
import app.revanced.patches.shared.litho.lithoFilterPatch
import app.revanced.patches.youtube.utils.extension.Constants.COMPONENTS_PATH
@ -21,10 +22,13 @@ import app.revanced.util.getReference
import app.revanced.util.indexOfFirstInstructionOrThrow
import app.revanced.util.indexOfFirstLiteralInstructionOrThrow
import app.revanced.util.indexOfFirstStringInstructionOrThrow
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.immutable.ImmutableMethod
private const val EXTENSION_PLAYER_TYPE_HOOK_CLASS_DESCRIPTOR =
"$UTILS_PATH/PlayerTypeHookPatch;"
@ -32,6 +36,9 @@ private const val EXTENSION_PLAYER_TYPE_HOOK_CLASS_DESCRIPTOR =
private const val EXTENSION_ROOT_VIEW_HOOK_CLASS_DESCRIPTOR =
"$SHARED_PATH/RootView;"
private const val EXTENSION_ROOT_VIEW_TOOLBAR_INTERFACE =
"$SHARED_PATH/RootView${'$'}AppCompatToolbarPatchInterface;"
private const val FILTER_CLASS_DESCRIPTOR =
"$COMPONENTS_PATH/RelatedVideoFilter;"
@ -165,6 +172,53 @@ val playerTypeHookPatch = bytecodePatch(
// endregion
// region patch for hook back button visibility
toolbarLayoutFingerprint.methodOrThrow().apply {
val index = indexOfMainCollapsingToolbarLayoutInstruction(this)
val register = getInstruction<OneRegisterInstruction>(index).registerA
addInstruction(
index + 1,
"invoke-static { v$register }, $EXTENSION_ROOT_VIEW_HOOK_CLASS_DESCRIPTOR->setToolbar(Landroid/widget/FrameLayout;)V"
)
}
// Add interface for extensions code to call obfuscated methods.
appCompatToolbarBackButtonFingerprint.matchOrThrow().let {
it.classDef.apply {
interfaces.add(EXTENSION_ROOT_VIEW_TOOLBAR_INTERFACE)
val definingClass = type
val obfuscatedMethodName = it.originalMethod.name
val returnType = "Landroid/graphics/drawable/Drawable;"
methods.add(
ImmutableMethod(
definingClass,
"patch_getToolbarIcon",
listOf(),
returnType,
AccessFlags.PUBLIC.value or AccessFlags.FINAL.value,
null,
null,
MutableMethodImplementation(2),
).toMutable().apply {
addInstructions(
0,
"""
invoke-virtual { p0 }, $definingClass->$obfuscatedMethodName()$returnType
move-result-object v0
return-object v0
"""
)
}
)
}
}
// endregion
addLithoFilter(FILTER_CLASS_DESCRIPTOR)
}
}

View File

@ -23,6 +23,8 @@ var is_19_09_or_greater = false
private set
var is_19_15_or_greater = false
private set
var is_19_16_or_greater = false
private set
var is_19_17_or_greater = false
private set
var is_19_23_or_greater = false
@ -59,6 +61,8 @@ var is_20_03_or_greater = false
private set
var is_20_05_or_greater = false
private set
var is_20_10_or_greater = false
private set
val versionCheckPatch = resourcePatch(
description = "versionCheckPatch",
@ -83,6 +87,7 @@ val versionCheckPatch = resourcePatch(
is_19_04_or_greater = 240502000 <= playStoreServicesVersion
is_19_09_or_greater = 241002000 <= playStoreServicesVersion
is_19_15_or_greater = 241602000 <= playStoreServicesVersion
is_19_16_or_greater = 241702000 <= playStoreServicesVersion
is_19_17_or_greater = 241802000 <= playStoreServicesVersion
is_19_23_or_greater = 242402000 <= playStoreServicesVersion
is_19_25_or_greater = 242599000 <= playStoreServicesVersion
@ -101,5 +106,6 @@ val versionCheckPatch = resourcePatch(
is_20_02_or_greater = 250299000 <= playStoreServicesVersion
is_20_03_or_greater = 250405000 <= playStoreServicesVersion
is_20_05_or_greater = 250605000 <= playStoreServicesVersion
is_20_10_or_greater = 251105000 <= playStoreServicesVersion
}
}

View File

@ -213,6 +213,8 @@ var tapBloomView = -1L
private set
var titleAnchor = -1L
private set
var toolbarContainerId = -1L
private set
var toolTipContentView = -1L
private set
var totalTime = -1L
@ -656,6 +658,10 @@ internal val sharedResourceIdPatch = resourcePatch(
ID,
"title_anchor"
]
toolbarContainerId = resourceMappings[
ID,
"toolbar_container"
]
toolTipContentView = resourceMappings[
LAYOUT,
"tooltip_content_view"

View File

@ -1,6 +1,7 @@
package app.revanced.patches.youtube.utils.settings
import app.revanced.patcher.extensions.InstructionExtensions.getInstruction
import app.revanced.patcher.patch.BytecodePatchContext
import app.revanced.patcher.patch.bytecodePatch
import app.revanced.patcher.patch.resourcePatch
import app.revanced.patcher.patch.stringOption
@ -39,6 +40,10 @@ private const val EXTENSION_INITIALIZATION_CLASS_DESCRIPTOR =
private const val EXTENSION_THEME_METHOD_DESCRIPTOR =
"$EXTENSION_UTILS_PATH/BaseThemeUtils;->setTheme(Ljava/lang/Enum;)V"
private lateinit var bytecodeContext: BytecodePatchContext
internal fun getBytecodeContext() = bytecodeContext
private val settingsBytecodePatch = bytecodePatch(
description = "settingsBytecodePatch"
) {
@ -50,6 +55,7 @@ private val settingsBytecodePatch = bytecodePatch(
)
execute {
bytecodeContext = this
// apply the current theme of the settings page
themeSetterSystemFingerprint.methodOrThrow().apply {

View File

@ -10,7 +10,6 @@ private val PLAYER_PARAMETER_STARTS_WITH_PARAMETER_LIST = listOf(
"[B",
"Ljava/lang/String;", // Player parameters proto buffer.
"Ljava/lang/String;", // PlaylistId.
"I",
"I"
)
@ -19,7 +18,7 @@ internal val playerParameterBuilderFingerprint = legacyFingerprint(
accessFlags = AccessFlags.PUBLIC or AccessFlags.FINAL,
returnType = "L",
strings = listOf("psps"),
// 19.22 and earlier parameters are:
// parameters in 18.29 ~ 19.22 :
// "Ljava/lang/String;", // VideoId.
// "[B",
// "Ljava/lang/String;", // Player parameters proto buffer.
@ -34,20 +33,39 @@ internal val playerParameterBuilderFingerprint = legacyFingerprint(
// "Z",
// "Z"
// 19.23+ parameters are:
// parameters in 19.23 ~ 20.09 :
// "Ljava/lang/String;", // VideoId.
// "[B",
// "Ljava/lang/String;", // Player parameters proto buffer.
// "Ljava/lang/String;", // PlaylistId.
// "I",
// "I",
// "L",
// "L", // New parameters added in 19.25.
// "Ljava/util/Set;",
// "Ljava/lang/String;",
// "Ljava/lang/String;",
// "L",
// "Z", // Appears to indicate if the video id is being opened or is currently playing.
// "Z",
// "Z",
// "Z"
// parameters in 20.10 ~ :
// "Ljava/lang/String;", // VideoId.
// "[B",
// "Ljava/lang/String;", // Player parameters proto buffer.
// "Ljava/lang/String;", // PlaylistId.
// "I",
// "Z", // New parameters added in 20.10.
// "I",
// "L", // New parameters added in 19.25.
// "Ljava/util/Set;",
// "Ljava/lang/String;",
// "Ljava/lang/String;",
// "L",
// "Z", // Appears to indicate if the video id is being opened or is currently playing.
// "Z",
// "Z",
// "Z"
customFingerprint = custom@{ method, _ ->
val parameterTypes = method.parameterTypes
@ -56,7 +74,7 @@ internal val playerParameterBuilderFingerprint = legacyFingerprint(
return@custom false
}
val startsWithMethodParameterList = parameterTypes.slice(0..5)
val startsWithMethodParameterList = parameterTypes.slice(0..4)
parametersEqual(
PLAYER_PARAMETER_STARTS_WITH_PARAMETER_LIST,

View File

@ -37,11 +37,18 @@ val playerResponseMethodHookPatch = bytecodePatch(
?: playerParameterBuilderLegacyFingerprint.methodOrThrow()
playerResponseMethod.apply {
parameterIsShortAndOpeningOrPlaying = parameterTypes.indexOfFirst { it == "Z" } + 1
val setIndex = parameterTypes.indexOfFirst { it == "Ljava/util/Set;" }
val parameterSize = parameterTypes.size
val relativeIndex = parameterTypes.subList(setIndex, parameterSize - 1).indexOfFirst { it == "Z" }
// YouTube 18.29 ~ 19.22 : p11
// YouTube 19.23 ~ 20.09 : p12
// YouTube 20.10 ~ : p13
parameterIsShortAndOpeningOrPlaying = setIndex + relativeIndex + 1
// On some app targets the method has too many registers pushing the parameters past v15.
// If needed, move the parameters to 4-bit registers so they can be passed to extension.
playerResponseMethodCopyRegisters = implementation!!.registerCount -
parameterTypes.size + parameterIsShortAndOpeningOrPlaying > 15
parameterSize + parameterIsShortAndOpeningOrPlaying > 15
}
if (playerResponseMethodCopyRegisters) {

View File

@ -100,24 +100,24 @@ fun Pair<String, Fingerprint>.injectLiteralInstructionBooleanCall(
) {
methodOrThrow().apply {
val literalIndex = indexOfFirstLiteralInstruction(literal)
val targetIndex = indexOfFirstInstructionOrThrow(literalIndex, Opcode.MOVE_RESULT)
val targetRegister = getInstruction<OneRegisterInstruction>(targetIndex).registerA
val index = indexOfFirstInstructionOrThrow(literalIndex, Opcode.MOVE_RESULT)
val register = getInstruction<OneRegisterInstruction>(index).registerA
val smaliInstruction =
if (descriptor.startsWith("0x")) """
const/16 v$targetRegister, $descriptor
const/16 v$register, $descriptor
"""
else if (descriptor.endsWith("(Z)Z")) """
invoke-static {v$targetRegister}, $descriptor
move-result v$targetRegister
invoke-static/range { v$register .. v$register }, $descriptor
move-result v$register
"""
else """
invoke-static {}, $descriptor
move-result v$targetRegister
move-result v$register
"""
addInstructions(
targetIndex + 1,
index + 1,
smaliInstruction
)
}

View File

@ -10,8 +10,6 @@
<item>@string/revanced_change_start_page_entry_liked_music</item>
<item>@string/revanced_change_start_page_entry_podcasts</item>
<item>@string/revanced_change_start_page_entry_samples</item>
<item>@string/revanced_change_start_page_entry_search</item>
<item>@string/revanced_change_start_page_entry_subscriptions</item>
</string-array>
<string-array name="revanced_change_start_page_entry_values">
<item>ORIGINAL</item>
@ -23,8 +21,6 @@
<item>LIKED_MUSIC</item>
<item>PODCASTS</item>
<item>SAMPLES</item>
<item>SEARCH</item>
<item>SUBSCRIPTIONS</item>
</string-array>
<string-array name="revanced_disable_music_video_in_album_redirect_type_entries">
<item>@string/revanced_disable_music_video_in_album_redirect_type_entry_redirect</item>

View File

@ -231,6 +231,18 @@ This does not bypass the age restriction. It just accepts it automatically."</st
<string name="revanced_hide_navigation_bar_summary">Hides the navigation bar.</string>
<string name="revanced_hide_navigation_label_title">Hide navigation labels</string>
<string name="revanced_hide_navigation_label_summary">Hides the label below each navigation button.</string>
<string name="revanced_replace_navigation_samples_button_title">Replace Samples button</string>
<string name="revanced_replace_navigation_samples_button_summary">Replaces the Samples button with the Search button.</string>
<string name="revanced_replace_navigation_upgrade_button_title">Replace Upgrade button</string>
<string name="revanced_replace_navigation_upgrade_button_summary">Replaces the Upgrade button with the Settings button.</string>
<string name="revanced_replace_navigation_button_about_title">About replace button</string>
<string name="revanced_replace_navigation_button_about_summary">"This feature is experimental.
There are structural limitations of the patch, as Activities such as Search and Settings in YouTube Music are not public.
Known issues:
• When a replaced Activity such as Search and Settings is closed, the start page opens.
Click to open the 'Change start page' settings."</string>
<!-- PreferenceScreen: Player -->
@ -501,6 +513,11 @@ Info:
<string name="revanced_spoof_client_type_entry_android_music_5_29">Android Music 5.29.53</string>
<string name="revanced_spoof_client_type_entry_ios_music_6_21">iOS Music 6.21</string>
<string name="revanced_spoof_client_type_entry_ios_music_7_04">iOS Music 7.04</string>
<string name="revanced_spoof_player_parameter_title">Spoof player parameter</string>
<string name="revanced_spoof_player_parameter_summary">"Spoof the player parameter to prevent playback issues.
Side effect:
• Sometimes the subtitles are located at the top of the player instead of the bottom."</string>
<string name="revanced_watch_history_type_title">Watch history type</string>
<string name="revanced_watch_history_type_summary">"• Original: Follows the watch history settings of Google account, but watch history may not work due to DNS or VPN.
• Replace domain: Follows the watch history settings of Google account.

View File

@ -451,6 +451,7 @@
<string name="revanced_spoof_client_type_entry_android_music_5_29">Android Music 5.29.53</string>
<string name="revanced_spoof_client_type_entry_ios_music_6_21">iOS Music 6.21</string>
<string name="revanced_spoof_client_type_entry_ios_music_7_04">iOS Music 7.04</string>
<string name="revanced_spoof_player_parameter_title">Παραποίηση παραμέτρου προγράμματος αναπαραγωγής</string>
<string name="revanced_watch_history_type_title">Τύπος ιστορικού παρακολούθησης</string>
<string name="revanced_watch_history_type_summary">"• Αρχικός: Ακολουθεί τις ρυθμίσεις ιστορικού παρακολούθησης του λογαριασμού Google σας, αλλά το ιστορικό παρακολούθησης μπορεί να μη λειτουργεί λόγω χρήσης VPN ή εναλλακτικού DNS.
• Αντικατάσταση του domain: Ακολουθεί τις ρυθμίσεις ιστορικού παρακολούθησης του λογαριασμού Google σας.

View File

@ -1,6 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<!-- Shared -->
<string name="revanced_extended_settings_title">RVX</string>
<string name="revanced_extended_reset_to_default_toast">Restablecer a valores por defecto.</string>
<!-- Shared Category -->
<string name="revanced_extended_restart_first_run">Reiniciar para cargar el diseño normalmente</string>
@ -19,6 +20,8 @@
<string name="revanced_hide_terms_container_summary">Oculta los términos del contenedor de servicio.</string>
<!-- PreferenceScreen: Action bar -->
<string name="revanced_preference_screen_action_bar_title">Barra de Acción</string>
<string name="revanced_change_action_bar_position_title">Cambiar posición de la barra de acción</string>
<string name="revanced_change_action_bar_position_summary">Mueve la barra de acción debajo del botón de reproducción.</string>
<string name="revanced_hide_action_button_like_dislike_title">Ocultar botones Me gusta y No me gusta</string>
<string name="revanced_hide_action_button_like_dislike_summary">Oculta los botones \"Me gusta\" y \"no me gusta\". No funciona en el diseño del reproductor antiguo.</string>
<string name="revanced_hide_action_button_comment_title">Ocultar botón de comentarios</string>
@ -31,6 +34,9 @@
<string name="revanced_hide_action_button_radio_summary">Oculta el botón Radio.</string>
<string name="revanced_hide_action_button_share_title">Ocultar botón de compartir</string>
<string name="revanced_hide_action_button_share_summary">Oculta el botón Compartir.</string>
<string name="revanced_hide_action_button_song_video_title">Ocultar el botón de Canción / Video</string>
<string name="revanced_hide_action_button_song_video_summary">"Oculta el botón de Canción / Video.
(Este botón está disponible para algunos usuarios)"</string>
<string name="revanced_hide_action_button_label_title">Ocultar etiquetas de botón de acción</string>
<string name="revanced_hide_action_button_label_summary">Oculta las etiquetas de los botones de acción.</string>
<string name="revanced_external_downloader_action_title">Reemplazar botón de acción de Descarga</string>
@ -48,6 +54,9 @@ Descarga %2$s desde el sitio web."</string>
<!-- PreferenceScreen: Ads -->
<string name="revanced_preference_screen_ads_title">Anuncios</string>
<string name="revanced_hide_fullscreen_ads_title">Ocultar anuncios en pantalla completa</string>
<string name="revanced_hide_fullscreen_ads_summary">"Oculta los anuncios en pantalla completa.
• Algunas veces puede que se vea una pantalla negra vacía en lugar del inicio."</string>
<string name="revanced_fullscreen_ads_closed_toast">Los anuncios en pantalla completa son cerrados.</string>
<string name="revanced_hide_general_ads_title">Ocultar anuncios generales</string>
<string name="revanced_hide_general_ads_summary">Oculta anuncios generales.</string>
<string name="revanced_hide_music_ads_title">Ocultar anuncios de música</string>
@ -56,6 +65,7 @@ Descarga %2$s desde el sitio web."</string>
<string name="revanced_hide_paid_promotion_label_summary">Oculta etiqueta de promoción pagada.</string>
<string name="revanced_hide_premium_promotion_title">Ocultar popups de promoción premium</string>
<string name="revanced_hide_premium_promotion_summary">Oculta popups de promoción premium.</string>
<string name="revanced_hide_premium_promotion_closed_toast">Los mensajes emergentes promocionando premium son cerrados.</string>
<string name="revanced_hide_premium_renewal_title">Ocultar banner de renovación premium</string>
<string name="revanced_hide_premium_renewal_summary">Oculta banner de renovación premium.</string>
<string name="revanced_hide_promotion_alert_banner_title">Ocultar banner de alerta de promoción</string>
@ -87,6 +97,7 @@ Problemas conocidos:
<string name="revanced_hide_flyout_menu_go_to_episode_title">Ocultar menú de ir a episodios</string>
<string name="revanced_hide_flyout_menu_go_to_podcast_title">Ocultar menú de ir al podcast</string>
<string name="revanced_hide_flyout_menu_help_title">Ocultar menú Ayuda &amp; Comentarios</string>
<string name="revanced_hide_flyout_menu_not_interested_title">Ocultar el menú de \"No interesado\"</string>
<string name="revanced_hide_flyout_menu_pin_to_speed_dial_title">Ocultar el pin al menú de marcación rápida</string>
<string name="revanced_hide_flyout_menu_play_next_title">Ocultar menú de reproducción siguiente</string>
<string name="revanced_hide_flyout_menu_quality_title">Ocultar menú de calidad</string>
@ -182,6 +193,10 @@ Esto no evita la restricción de edad. Solo la acepta automáticamente."</string
<!-- PreferenceScreen: Navigation bar -->
<string name="revanced_preference_screen_navigation_title">Barra de navegación</string>
<string name="revanced_enable_custom_navigation_bar_color_title">Activar color personalizado de la barra de navegación</string>
<string name="revanced_enable_custom_navigation_bar_color_summary">Aplica un color en la barra de navegación.</string>
<string name="revanced_custom_navigation_bar_color_value_title">Valor de color personalizado para la barra de navegación</string>
<string name="revanced_custom_navigation_bar_color_value_summary">Escribe un valor hexadecimal para el color de la barra de navegación.</string>
<string name="revanced_custom_navigation_bar_color_value_invalid_invalid_toast">Valor de color hexadecimal Inválido para la barra de navegación.</string>
<string name="revanced_hide_navigation_home_button_title">Ocultar botón de Inicio</string>
<string name="revanced_hide_navigation_home_button_summary">Oculta el botón de Inicio.</string>
<string name="revanced_hide_navigation_samples_button_title">Ocultar botón de Samples</string>
@ -198,8 +213,37 @@ Esto no evita la restricción de edad. Solo la acepta automáticamente."</string
<string name="revanced_hide_navigation_label_summary">Oculta las etiquetas en la barra de navegación.</string>
<!-- PreferenceScreen: Player -->
<string name="revanced_preference_screen_player_title">Reproductor</string>
<string name="revanced_add_miniplayer_next_button_title">Añade un botón de siguiente al minireproductor</string>
<string name="revanced_add_miniplayer_next_button_summary">Añade un botón de siguiente pista al minireproductor.</string>
<string name="revanced_add_miniplayer_previous_button_title">Añade un botón de anterior al minireproductor</string>
<string name="revanced_add_miniplayer_previous_button_summary">Añade un botón de pista anterior al minireproductor.</string>
<string name="revanced_change_miniplayer_color_title">Cambia el color del minireproductor</string>
<string name="revanced_change_miniplayer_color_summary">Cambia el color del minireproductor para que concuerde con el color del reproductor de la pantalla completa.</string>
<string name="revanced_change_player_background_color_title">Cambia el color de fondo del reproductor</string>
<string name="revanced_change_player_background_color_summary">Cambia el color de fondo del reproductor a un color personalizado.</string>
<string name="revanced_custom_player_background_color_primary_title">Color principal del fondo del reproductor</string>
<string name="revanced_custom_player_background_color_primary_summary">"Escribe el valor hexadecimal del color primario del fondo del reproductor.
Preferiblemente que sean colores oscuros, la aplicación no soporta temas claros."</string>
<string name="revanced_custom_player_background_color_secondary_title">Color secundario del fondo del reproductor</string>
<string name="revanced_custom_player_background_color_secondary_summary">"Escriba el valor hexadecimal del color secundario del reproductor.
Preferiblemente colores oscuros, la aplicación no soporta temas claros."</string>
<string name="revanced_custom_player_background_invalid_toast">Color de fondo del reproductor inválido.</string>
<string name="revanced_change_seekbar_position_title">Cambiar posición de la barra de acción</string>
<string name="revanced_change_seekbar_position_summary">Mueve la barra de acción por debajo del botón de reproducción.</string>
<string name="revanced_disable_miniplayer_gesture_title">Deshabilitar el gesto del minireproductor</string>
<string name="revanced_disable_miniplayer_gesture_summary">Deshabilita el gesto de deslizamiento para cambiar de pista en el minireproductor.</string>
<string name="revanced_disable_player_gesture_title">Desactivar gesto del reproductor</string>
<string name="revanced_disable_player_gesture_summary">Desactivar el gesto de deslizar para cambiar de pista en el reproductor.</string>
<string name="revanced_enable_forced_miniplayer_title">Activar el minireproductor forzado</string>
<string name="revanced_enable_forced_miniplayer_summary">Habilita que se fuerce el minireproductor cuando se cambia a una nueva pista.</string>
<string name="revanced_enable_swipe_to_dismiss_miniplayer_title">Habilitar el gesto de descarte del minireproductor</string>
<string name="revanced_enable_swipe_to_dismiss_miniplayer_summary">Habilita que se pueda deslizar hacia abajo en el minireproductor para descartarlo.</string>
<string name="revanced_enable_thick_seekbar_title">Habilita una barra de acción más gruesa</string>
<string name="revanced_enable_thick_seekbar_summary">"Habilita una barra de acción más gruesa.
Limitantes: Los segmentos de SponsorBlock no se mostrarán en la barra de acción."</string>
<string name="revanced_enable_zen_mode_title">Activar modo zen</string>
<string name="revanced_enable_zen_mode_summary">Añade un tinte gris al reproductor de vídeo para reducir la fatiga visual.</string>
<string name="revanced_enable_zen_mode_podcast_title">Activar el modo Zen en podcasts</string>
@ -212,6 +256,8 @@ Esto no evita la restricción de edad. Solo la acepta automáticamente."</string
<string name="revanced_hide_comment_timestamp_and_emoji_buttons_summary">Oculta los botones marca de tiempo y emoji al escribir comentarios.</string>
<string name="revanced_hide_fullscreen_share_button_title">Ocultar el botón Compartir en pantalla completa</string>
<string name="revanced_hide_fullscreen_share_button_summary">Oculta el botón Compartir en el reproductor de pantalla completa.</string>
<string name="revanced_hide_song_video_toggle_title">Ocultar el interruptor de Canción / Video</string>
<string name="revanced_hide_song_video_toggle_summary">Oculta el interruptor de Canción / Video en el reproductor.</string>
<string name="revanced_remember_repeat_state_title">Recordar estado de repetición</string>
<string name="revanced_remember_repeat_state_summary">Recuerda el estado de la repetición.</string>
<string name="revanced_remember_shuffle_state_title">Recordar estado aleatorio</string>
@ -373,11 +419,18 @@ Toca para ver cómo crear una clave de API."</string>
<string name="revanced_disable_drc_audio_title">Desactivar audio DRC</string>
<string name="revanced_disable_drc_audio_summary">Deshabilita DRC (Dynamic Range Compression) aplicado al audio.</string>
<string name="revanced_disable_music_video_in_album_title">Desactivar vídeo de música en el álbum</string>
<string name="revanced_disable_music_video_in_album_summary">"Cuando un usuario no-premium reproduce una canción incluida en un álbum, el video musical a veces es reproducido en lugar de la canción oficial en sí.
Encuentra y redirige la canción oficial, sí se detecta un video musical al reproducirse desde un álbum.
Limitantes: Vídeos para niños puede que no se redirijan."</string>
<string name="revanced_disable_music_video_in_album_redirect_type_title">Tipo de redirección</string>
<string name="revanced_disable_music_video_in_album_redirect_type_summary">Especifica cómo redirigir a la canción oficial.</string>
<string name="revanced_disable_music_video_in_album_redirect_type_entry_redirect">Redirigir</string>
<string name="revanced_disable_music_video_in_album_redirect_type_entry_on_click">Interruptor de Audio / Video</string>
<string name="revanced_disable_music_video_in_album_redirect_type_entry_on_long_click">Mantén pulsado el interruptor de Audio / Video</string>
<string name="revanced_disable_quic_protocol_title">Desactiva el protocolo QUIC</string>
<string name="revanced_disable_quic_protocol_summary">"Deshabilita el protocolo QUIC de CronetEngine."</string>
<string name="revanced_enable_debug_logging_title">Activar registro de depuración</string>
<string name="revanced_enable_debug_logging_summary">Imprime el registro de depuración.</string>
<string name="revanced_enable_debug_buffer_logging_title">Incluir búfer en registro de depuración</string>
@ -387,10 +440,20 @@ Toca para ver cómo crear una clave de API."</string>
<string name="revanced_sanitize_sharing_links_title">Desinfectar enlaces compartidos</string>
<string name="revanced_sanitize_sharing_links_summary">Elimina los parámetros de consulta de seguimiento de las URL al compartir enlaces.</string>
<string name="revanced_spoof_client_title">Falsificar cliente</string>
<string name="revanced_spoof_client_summary">Falsifica el cliente para prevenir problemas en la reproducción.</string>
<string name="revanced_spoof_client_type_title">Cliente por defecto</string>
<string name="revanced_spoof_client_type_summary">Define un cliente de por defecto para el falsificado.</string>
<string name="revanced_spoof_client_type_entry_android_music_4_27">Android Music 4.27.53</string>
<string name="revanced_spoof_client_type_entry_android_music_5_29">Android Music 5.29.53</string>
<string name="revanced_spoof_client_type_entry_ios_music_6_21">iOS Music 6.21</string>
<string name="revanced_spoof_client_type_entry_ios_music_7_04">iOS Music 7.04</string>
<string name="revanced_watch_history_type_title">Tipo de vista del historial</string>
<string name="revanced_watch_history_type_summary">"• Original: Sigue la configuración del registro del historial de su cuenta de Google, pero la opción del registro del historial pueda no funcionar al tener un DNS o VPN.
• Reemplazar dominio: Sigue la configuración del registro del historial de su cuenta de Google.
• Bloquear el registro del historial: El registro de historial está bloqueado."</string>
<string name="revanced_watch_history_type_entry_1">Original</string>
<string name="revanced_watch_history_type_entry_2">Reemplazar dominio</string>
<string name="revanced_watch_history_type_entry_3">Bloquear el registro del historial</string>
<string name="revanced_default_app_settings_title">Abrir ajustes predeterminados de la app</string>
<string name="revanced_default_app_settings_summary">Para abrir los enlaces de YouTube Music en RVX Music, activa \'Abrir enlaces soportados\' y activa las direcciones web soportadas.</string>
<string name="gms_core_settings_title">Abrir GmsCore</string>

View File

@ -4,14 +4,14 @@
<string name="revanced_extended_settings_title">RVX</string>
<string name="revanced_extended_reset_to_default_toast">Atur ulang ke nilai default.</string>
<!-- Shared Category -->
<string name="revanced_extended_restart_first_run">Mulai ulang untuk memuat layout secara normal</string>
<string name="revanced_extended_restart_message">Refresh dan mulai ulang</string>
<string name="revanced_extended_restart_first_run">Mulai ulang untuk memuat tata letak secara normal</string>
<string name="revanced_extended_restart_message">Segarkan dan mulai ulang</string>
<!-- PreferenceScreen: Account -->
<string name="revanced_preference_screen_account_title">Akun</string>
<string name="revanced_hide_account_menu_title">Sembunyikan menu akun</string>
<string name="revanced_hide_account_menu_summary">Menyembunyikan elemen menu akun menggunakan filter custom.</string>
<string name="revanced_hide_account_menu_filter_strings_title">Filter menu Akun</string>
<string name="revanced_hide_account_menu_filter_strings_summary">Daftar dari nama-nama menu akun ke filter, terpisah oleh garis baru.</string>
<string name="revanced_hide_account_menu_summary">Menyembunyikan elemen menu akun menggunakan penyaring khusus.</string>
<string name="revanced_hide_account_menu_filter_strings_title">Penyaring menu Akun</string>
<string name="revanced_hide_account_menu_filter_strings_summary">Daftar nama menu akun yang akan disaring, dipisahkan dengan baris baru.</string>
<string name="revanced_hide_account_menu_empty_component_title">Sembunyikan komponen kosong</string>
<string name="revanced_hide_account_menu_empty_component_summary">Menyembunyikan komponen kosong di menu akun.</string>
<string name="revanced_hide_handle_title">Sembunyikan handle</string>
@ -20,13 +20,13 @@
<string name="revanced_hide_terms_container_summary">Menyembunyikan kontainer ketentuan layanan.</string>
<!-- PreferenceScreen: Action bar -->
<string name="revanced_preference_screen_action_bar_title">Bilah Tindakan</string>
<string name="revanced_change_action_bar_position_title">Ubah posisi bilah aksi</string>
<string name="revanced_change_action_bar_position_summary">Pindahkan bilah aksi di bawah tombol putar.</string>
<string name="revanced_hide_action_button_like_dislike_title">Sembunyikan tombol Like dan Dislike</string>
<string name="revanced_hide_action_button_like_dislike_summary">Menyembunyikan tombol Like dan Dislike. Itu tidak akan bekerja di layout player lama.</string>
<string name="revanced_change_action_bar_position_title">Ubah posisi bilah tindakan</string>
<string name="revanced_change_action_bar_position_summary">Pindahkan bilah tindakan di bawah tombol putar.</string>
<string name="revanced_hide_action_button_like_dislike_title">Sembunyikan tombol Suka dan Tidak suka</string>
<string name="revanced_hide_action_button_like_dislike_summary">Menyembunyikan tombol Suka dan Tidak suka. Itu tidak akan bekerja di tata letak pemutar lama.</string>
<string name="revanced_hide_action_button_comment_title">Sembunyikan tombol Komentar</string>
<string name="revanced_hide_action_button_comment_summary">Menyembunyikan tombol Komentar.</string>
<string name="revanced_hide_action_button_add_to_playlist_title">Sembunyikan tombol Save</string>
<string name="revanced_hide_action_button_add_to_playlist_title">Sembunyikan tombol Simpan</string>
<string name="revanced_hide_action_button_add_to_playlist_summary">Menyembunyikan tombol Simpan.</string>
<string name="revanced_hide_action_button_download_title">Sembunyikan tombol Unduh</string>
<string name="revanced_hide_action_button_download_summary">Menyembunyikan tombol Unduh.</string>
@ -37,27 +37,31 @@
<string name="revanced_hide_action_button_song_video_title">Sembunyikan tombol Lagu / Video</string>
<string name="revanced_hide_action_button_song_video_summary">"Sembunyikan tombol Lagu / Video.
(Tombol ini hanya tersedia untuk sebagian pengguna)"</string>
<string name="revanced_hide_action_button_label_title">Sembunyikan tombol bilah tindakan</string>
<string name="revanced_hide_action_button_label_summary">Menyembunyikan bilah dari tombol tindakan.</string>
<string name="revanced_hide_action_button_label_title">Sembunyikan label tombol tindakan</string>
<string name="revanced_hide_action_button_label_summary">Menyembunyikan label tombol tindakan.</string>
<string name="revanced_external_downloader_action_title">Ganti tombol tindakan Unduh</string>
<string name="revanced_external_downloader_action_summary">"Tombol Unduh membuka Downloader eksternal kamu.
<string name="revanced_external_downloader_action_summary">"Tombol Unduh membuka pengunduh eksternal Anda
• Hanya menggantikan tombol Unduh di player.
• Tidak bisa menggantikan tombol Unduh di menu flyout atau tab Library."</string>
<string name="revanced_external_downloader_package_name_title">Nama paket downloader eksternal</string>
<string name="revanced_external_downloader_package_name_summary">Nama paket aplikasi downloader eksternal yang terinstal, seperti NewPipe atau YTDLnis.</string>
<string name="revanced_external_downloader_dialog_title">Downloader eksternal</string>
• Hanya mengganti tombol tindakan Unduh di pemutar.
• Tidak mengganti tombol Unduh di menu flyout atau tab Pustaka."</string>
<string name="revanced_external_downloader_package_name_title">Nama paket pengunduh eksternal</string>
<string name="revanced_external_downloader_package_name_summary">Nama paket aplikasi pengunduh eksternal yang terpasang, seperti NewPipe atau YTDLnis.</string>
<string name="revanced_external_downloader_dialog_title">Pengunduh eksternal</string>
<string name="revanced_external_downloader_not_installed_dialog_title">Peringatan</string>
<string name="revanced_external_downloader_not_installed_dialog_message">"%1$s belum terinstall.
Download %2$s dari website."</string>
<string name="revanced_external_downloader_not_installed_warning">%s tidak diinstal. Silakan instal.</string>
<string name="revanced_external_downloader_not_installed_dialog_message">"%1$s belum terpasang.
Harap unduh %2$s dari website."</string>
<string name="revanced_external_downloader_not_installed_warning">%s tidak dipasang. Silakan pasang terlebih dahulu.</string>
<!-- PreferenceScreen: Ads -->
<string name="revanced_preference_screen_ads_title">Iklan</string>
<string name="revanced_hide_fullscreen_ads_title">Sembunyikan iklan fullscreen</string>
<string name="revanced_hide_fullscreen_ads_title">Sembunyikan iklan layar penuh</string>
<string name="revanced_hide_fullscreen_ads_summary">"Menyembunyikan iklan layar penuh.
Keterbatasan:
• Terkadang Anda mungkin melihat layar hitam kosong, bukan feed beranda."</string>
<string name="revanced_fullscreen_ads_closed_toast">Iklan layar penuh ditutup.</string>
<string name="revanced_hide_general_ads_title">Sembunyikan Iklan Umum</string>
<string name="revanced_hide_general_ads_summary">Menyembunyikan Iklan Umum.</string>
<string name="revanced_hide_music_ads_title">Sembunyikan iklan musik</string>
<string name="revanced_hide_music_ads_title">Sembunyikan iklan media</string>
<string name="revanced_hide_music_ads_summary">Menyembunyikan iklan sebelum memutar musik.</string>
<string name="revanced_hide_paid_promotion_label_title">Sembunyikan label promosi berbayar</string>
<string name="revanced_hide_paid_promotion_label_summary">Menyembunyikan label promosi berbayar.</string>
@ -67,289 +71,411 @@ Download %2$s dari website."</string>
<string name="revanced_hide_premium_renewal_title">Sembunyikan banner pembaruan premium</string>
<string name="revanced_hide_premium_renewal_summary">Menyembunyikan banner pembaruan premium.</string>
<string name="revanced_hide_promotion_alert_banner_title">Sembunyikan banner peringatan promosi</string>
<string name="revanced_hide_promotion_alert_banner_summary">Sembunyikan banner peringatan promosi.</string>
<!-- PreferenceScreen: Flyout menu -->
<string name="revanced_preference_screen_flyout_title">Menu flyout</string>
<string name="revanced_enable_trim_silence_title">Tambah switch Trim silence</string>
<string name="revanced_enable_trim_silence_summary">"Menambahkan tombol Trim silence ke menu flyout playback speed.
<string name="revanced_enable_trim_silence_title">Tambah tombol Pangkas keheningan</string>
<string name="revanced_enable_trim_silence_summary">"Menambahkan tombol Pangkas keheningan ke menu flyout kecepatan pemutaran.
Info:
• Fitur ini hanya untuk podcast.
• Fitur ini masih dalam pengembangan, jadi ini tidak akan stabil."</string>
• Fitur ini masih dalam pengembangan, jadi mungkin tidak stabil."</string>
<string name="revanced_enable_compact_dialog_title">Aktifkan dialog ringkas</string>
<string name="revanced_enable_compact_dialog_summary">"Aktifkan dialog ringkas di ponsel.
<string name="revanced_enable_compact_dialog_summary">"Mengaktifkan dialog ringkas di ponsel.
Masalah yang diketahui:
• Gambar album di Tab library juga menjadi lebih kecil.
Keterbatasan:
• Gambar album di tab Pustaka juga menjadi lebih kecil ketika diatur dalam model kotak.
• Tata letak pengatur waktu tidur mungkin terlihat tidak biasa."</string>
<string name="revanced_hide_flyout_menu_like_dislike_title">Sembunyikan tombol Like dan Dislike</string>
<string name="revanced_hide_flyout_menu_like_dislike_title">Sembunyikan tombol Suka dan Tidak suka</string>
<string name="revanced_hide_flyout_menu_3_column_component_title">Sembunyikan komponen 3-kolom</string>
<string name="revanced_hide_flyout_menu_add_to_queue_title">Sembunyikan menu tambahkan ke antrean</string>
<string name="revanced_hide_flyout_menu_captions_title">Sembunyikan menu teks</string>
<string name="revanced_hide_flyout_menu_delete_playlist_title">Sembunyikan menu hapus playlist</string>
<string name="revanced_hide_flyout_menu_dismiss_queue_title">Sembunyikan menu abaikan antrean</string>
<string name="revanced_hide_flyout_menu_captions_title">Sembunyikan menu Teks</string>
<string name="revanced_hide_flyout_menu_delete_playlist_title">Sembunyikan menu Hapus playlist</string>
<string name="revanced_hide_flyout_menu_dismiss_queue_title">Sembunyikan menu Abaikan antrean</string>
<string name="revanced_hide_flyout_menu_download_title">Sembunyikan menu Unduh</string>
<string name="revanced_hide_flyout_menu_edit_playlist_title">Sembunyikan menu edit playlist</string>
<string name="revanced_hide_flyout_menu_edit_playlist_title">Sembunyikan menu Edit playlist</string>
<string name="revanced_hide_flyout_menu_go_to_album_title">Sembunyikan menu Pergi ke album</string>
<string name="revanced_hide_flyout_menu_go_to_artist_title">Sembunyikan menu Pergi ke artis</string>
<string name="revanced_hide_flyout_menu_go_to_episode_title">Sembunyikan menu Pergi ke episode</string>
<string name="revanced_hide_flyout_menu_go_to_podcast_title">Sembunyikan menu Pergi ke podcast</string>
<string name="revanced_hide_flyout_menu_help_title">Sembunyikan menu bantuan &amp; saran</string>
<string name="revanced_hide_flyout_menu_play_next_title">Sembunyikan menu putar berikutnya</string>
<string name="revanced_hide_flyout_menu_quality_title">Hide menu Kualitas</string>
<string name="revanced_hide_flyout_menu_remove_from_library_title">Sembunyikan menu hapus dari koleksi</string>
<string name="revanced_hide_flyout_menu_remove_from_playlist_title">Sembunyikan hapus dari menu playlist</string>
<string name="revanced_hide_flyout_menu_report_title">Sembunyikan menu laporkan</string>
<string name="revanced_hide_flyout_menu_save_episode_for_later_title">Sembunyikan menu simpan episode untuk ditonton nanti</string>
<string name="revanced_hide_flyout_menu_save_to_library_title">Sembunyikan menu simpan ke koleksi</string>
<string name="revanced_hide_flyout_menu_save_to_playlist_title">Sembunyikan menu simpan ke playlist</string>
<string name="revanced_hide_flyout_menu_share_title">Sembunyikan menu bagikan</string>
<string name="revanced_hide_flyout_menu_shuffle_play_title">Sembunyikan menu putar acak</string>
<string name="revanced_hide_flyout_menu_sleep_timer_title">Sembunyikan menu waktu tidur</string>
<string name="revanced_hide_flyout_menu_start_radio_title">Sembunyikan menu mulai radio</string>
<string name="revanced_hide_flyout_menu_stats_for_nerds_title">Sembunyikan menu statistik untuk nerds</string>
<string name="revanced_hide_flyout_menu_subscribe_title">Sembunyikan menu Subscribe / Unsubscribe</string>
<string name="revanced_hide_flyout_menu_view_song_credit_title">Sembunyikan menu kredit lagu</string>
<string name="revanced_hide_flyout_menu_help_title">Sembunyikan menu Bantuan &amp; saran</string>
<string name="revanced_hide_flyout_menu_not_interested_title">Sembunyikan menu Tidak tertarik</string>
<string name="revanced_hide_flyout_menu_pin_to_speed_dial_title">Sembunyikan Pin ke menu Panggil cepat</string>
<string name="revanced_hide_flyout_menu_play_next_title">Sembunyikan menu Putar berikutnya</string>
<string name="revanced_hide_flyout_menu_quality_title">Sembunyikan menu Kualitas</string>
<string name="revanced_hide_flyout_menu_remove_from_library_title">Sembunyikan menu Hapus dari koleksi</string>
<string name="revanced_hide_flyout_menu_remove_from_playlist_title">Sembunyikan menu Hapus dari playlist</string>
<string name="revanced_hide_flyout_menu_report_title">Sembunyikan menu Laporkan</string>
<string name="revanced_hide_flyout_menu_save_episode_for_later_title">Sembunyikan menu Simpan episode untuk nanti</string>
<string name="revanced_hide_flyout_menu_save_to_library_title">Sembunyikan menu Simpan ke pustaka</string>
<string name="revanced_hide_flyout_menu_save_to_playlist_title">Sembunyikan menu Simpan ke playlist</string>
<string name="revanced_hide_flyout_menu_share_title">Sembunyikan menu Bagikan</string>
<string name="revanced_hide_flyout_menu_shuffle_play_title">Sembunyikan menu Putar acak</string>
<string name="revanced_hide_flyout_menu_sleep_timer_title">Sembunyikan menu Waktu tidur</string>
<string name="revanced_hide_flyout_menu_start_radio_title">Sembunyikan menu Mulai radio</string>
<string name="revanced_hide_flyout_menu_stats_for_nerds_title">Sembunyikan menu Statistik untuk nerds</string>
<string name="revanced_hide_flyout_menu_subscribe_title">Sembunyikan menu Berlangganan / Berhenti Berlangganan</string>
<string name="revanced_hide_flyout_menu_unpin_from_speed_dial_title">Sembunyikan Buka pin dari menu panggilan cepat</string>
<string name="revanced_hide_flyout_menu_view_song_credit_title">Sembunyikan menu Lihat kredit lagu</string>
<string name="revanced_replace_flyout_menu_dismiss_queue_continue_watch_title">Lanjutkan menonton</string>
<string name="revanced_replace_flyout_menu_dismiss_queue_continue_watch_summary">Melanjutkan video dari waktu saat ini ketika berlaih ke YouTube.</string>
<string name="revanced_replace_flyout_menu_dismiss_queue_continue_watch_summary">Melanjutkan video dari waktu saat ini ketika beralih ke YouTube.</string>
<string name="revanced_replace_flyout_menu_dismiss_queue_watch_on_youtube_label">Tonton di YouTube</string>
<string name="revanced_replace_flyout_menu_dismiss_queue_watch_on_youtube_warning">Url video tidak valid.</string>
<string name="revanced_replace_flyout_menu_dismiss_queue_title">Ganti menu hapus antrean</string>
<string name="revanced_replace_flyout_menu_dismiss_queue_summary">Menggantikan menu hapus antrean menjadi tonton di YouTube.</string>
<string name="revanced_replace_flyout_menu_report_title">Ganti menu laporkan</string>
<string name="revanced_replace_flyout_menu_report_summary">Menggantikan menu laporkan dengan menu Kecepatan pemutaran.</string>
<string name="revanced_replace_flyout_menu_report_only_player_title">Simpan laporkan di komentar</string>
<string name="revanced_replace_flyout_menu_report_only_player_summary">Mempertahankan menu laporkan di bagian komentar.</string>
<string name="revanced_replace_flyout_menu_dismiss_queue_watch_on_youtube_warning">Url video tidak sah.</string>
<string name="revanced_replace_flyout_menu_dismiss_queue_title">Ganti menu Hapus antrean</string>
<string name="revanced_replace_flyout_menu_dismiss_queue_summary">Menggantikan menu Hapus antrean dengan menu Tonton di YouTube.</string>
<string name="revanced_replace_flyout_menu_report_title">Ganti menu Laporkan</string>
<string name="revanced_replace_flyout_menu_report_summary">Menggantikan menu Laporkan dengan menu Kecepatan pemutaran.</string>
<string name="revanced_replace_flyout_menu_report_only_player_title">Simpan Laporkan di komentar</string>
<string name="revanced_replace_flyout_menu_report_only_player_summary">Menyimpan menu Laporkan di bagian komentar tetap utuh.</string>
<!-- PreferenceScreen: General -->
<string name="revanced_preference_screen_general_title">Umum</string>
<string name="revanced_change_start_page_title">Ganti Halaman Awal</string>
<string name="revanced_change_start_page_summary">Select which page the app opens in.</string>
<string name="revanced_change_start_page_summary">Pilih di halaman mana aplikasi dibuka.</string>
<string name="revanced_change_start_page_entry_default">Bawaan</string>
<string name="revanced_change_start_page_entry_charts">Tangga lagu</string>
<string name="revanced_change_start_page_entry_episodes_for_later">Episode untuk Nanti</string>
<string name="revanced_change_start_page_entry_explore">Jelajahi</string>
<string name="revanced_change_start_page_entry_library">Koleksi</string>
<string name="revanced_disable_dislike_redirection_title">Disable dislike redirection</string>
<string name="revanced_disable_dislike_redirection_summary">Disables redirection to the next track when clicking the Dislike button.</string>
<string name="revanced_change_start_page_entry_history">Riwayat</string>
<string name="revanced_change_start_page_entry_library">Pustaka</string>
<string name="revanced_change_start_page_entry_liked_music">Musik yang Disukai</string>
<string name="revanced_change_start_page_entry_podcasts">Podcast</string>
<string name="revanced_change_start_page_entry_samples">Sampel</string>
<string name="revanced_change_start_page_entry_search">Cari</string>
<string name="revanced_change_start_page_entry_subscriptions">Berlangganan</string>
<string name="revanced_disable_dislike_redirection_title">Nonaktifkan pengalihan tidak suka</string>
<string name="revanced_disable_dislike_redirection_summary">Menonaktifkan pengalihan ke trek berikutnya ketika mengklik tombol Tidak Suka.</string>
<string name="revanced_disable_auto_captions_title">Nonaktifkan teks otomatis paksa</string>
<string name="revanced_disable_auto_captions_summary">Teks otomatis paksa yang dinonaktifkan.</string>
<string name="revanced_disable_auto_captions_summary">Menonaktifkan teks otomatis agar tidak aktif.</string>
<string name="revanced_enable_landscape_mode_title">Aktifkan mode lanskap</string>
<string name="revanced_enable_landscape_mode_summary">Mengaktifkan masuk ke mode lanskap dengan rotasi layar di ponsel.</string>
<string name="revanced_custom_filter_title">Aktifkan filter kustom</string>
<string name="revanced_enable_landscape_mode_summary">Mengaktifkan mode lanskap saat memutar layar ponsel.</string>
<string name="revanced_custom_filter_title">Aktifkan filter khusus</string>
<string name="revanced_custom_filter_summary">Mengaktifkan filter kustom untuk menyembunyikan komponen tata letak.</string>
<string name="revanced_custom_filter_strings_title">Edit filter kustom</string>
<string name="revanced_custom_filter_strings_title">Edit filter khusus</string>
<!-- 'Component path builder strings' is the technical name for identifying the Litho UI layout items to hide. This is an advanced feature and most users will never use this. -->
<string name="revanced_custom_filter_strings_summary">Memfilter nama komponen dengan baris yang dipisahkan.</string>
<string name="revanced_custom_filter_toast_invalid_syntax">Invalid custom filter: %s.</string>
<string name="revanced_custom_filter_strings_summary">Daftar string pembangun jalur komponen yang akan disaring, dipisahkan dengan baris baru.</string>
<string name="revanced_custom_filter_toast_invalid_syntax">Filter khusus tidak sah: %s.</string>
<string name="revanced_hide_button_shelf_title">Sembunyikan rak tombol</string>
<string name="revanced_hide_button_shelf_summary">Menyembunyikan rak tombol dari beranda dan eksplorasi.</string>
<string name="revanced_hide_carousel_shelf_title">Sembunyikan rak korsel</string>
<string name="revanced_hide_carousel_shelf_summary">Menyembunyikan rak korsel dari beranda dan eksplorasi.</string>
<string name="revanced_hide_cast_button_title">Sembunyikan tombol cast</string>
<string name="revanced_hide_cast_button_summary">Menyembunyikan tombol cast.</string>
<string name="revanced_hide_button_shelf_summary">Menyembunyikan rak tombol di feed.</string>
<string name="revanced_hide_carousel_shelf_title">Menyembunyikan rak carousel</string>
<string name="revanced_hide_carousel_shelf_summary">Menyembunyikan rak carousel di feed.</string>
<string name="revanced_hide_cast_button_title">Sembunyikan tombol Cast</string>
<string name="revanced_hide_cast_button_summary">Menyembunyikan tombol Cast.</string>
<string name="revanced_hide_category_bar_title">Sembunyikan bilah kategori</string>
<string name="revanced_hide_category_bar_summary">Menyembunyikan bilah kategori musik di bagian atas beranda.</string>
<string name="revanced_hide_floating_button_title">Hide floating button</string>
<string name="revanced_hide_floating_button_summary">Hides the floating button in the Library tab.</string>
<string name="revanced_hide_history_button_title">Sembunyikan tombol riwayat</string>
<string name="revanced_hide_history_button_summary">Menyembunyikan tombol riwayat di toolbar.</string>
<string name="revanced_hide_notification_button_title">Hide Notifications button</string>
<string name="revanced_hide_notification_button_summary">Hides the Notifications button in the toolbar.</string>
<string name="revanced_hide_playlist_card_shelf_title">Hide playlist card shelf</string>
<string name="revanced_hide_playlist_card_shelf_summary">Hides the playlist card shelf in the feed.</string>
<string name="revanced_hide_samples_shelf_title">Hide Samples shelf</string>
<string name="revanced_hide_samples_shelf_summary">Hides the Samples shelf in the feed.</string>
<string name="revanced_hide_sound_search_button_title">Hide sound search button</string>
<string name="revanced_hide_sound_search_button_summary">Hides the sound search button in the search bar.</string>
<string name="revanced_hide_tap_to_update_button_title">Hide \'Tap to update\' button</string>
<string name="revanced_hide_tap_to_update_button_summary">Hides the \'Tap to update\' button.</string>
<string name="revanced_hide_voice_search_button_title">Hide voice search button</string>
<string name="revanced_hide_voice_search_button_summary">Hides the voice search button in the search bar.</string>
<string name="revanced_restore_old_style_library_shelf_title">Restore old style library shelf</string>
<string name="revanced_restore_old_style_library_shelf_summary">Returns the Library tab to the old style. (Experimental)</string>
<string name="revanced_remove_viewer_discretion_dialog_title">Remove viewer discretion dialog</string>
<string name="revanced_remove_viewer_discretion_dialog_summary">"Removes the viewer discretion dialog.
This does not bypass the age restriction. It just accepts it automatically."</string>
<string name="revanced_hide_category_bar_summary">Menyembunyikan bilah kategori.</string>
<string name="revanced_hide_floating_button_title">Sembunyikan tombol mengambang</string>
<string name="revanced_hide_floating_button_summary">Menyembunyikan tombol mengambang di tab Pustaka.</string>
<string name="revanced_hide_history_button_title">Sembunyikan tombol Riwayat</string>
<string name="revanced_hide_history_button_summary">Menyembunyikan Rombol riwayat di toolbar.</string>
<string name="revanced_hide_notification_button_title">Sembunyikan tombol Pemberitahuan</string>
<string name="revanced_hide_notification_button_summary">Menyembunyikan tombol Pemberitahuan di toolbar.</string>
<string name="revanced_hide_playlist_card_shelf_title">Sembunyikan rak kartu playlist</string>
<string name="revanced_hide_playlist_card_shelf_summary">Menyembunyikan rak kartu playlist di feed.</string>
<string name="revanced_hide_samples_shelf_title">Sembunyikan rak Sampel</string>
<string name="revanced_hide_samples_shelf_summary">Menyembunyikan rak Sampel di feed.</string>
<string name="revanced_hide_sound_search_button_title">Sembunyikan tombol pencarian suara</string>
<string name="revanced_hide_sound_search_button_summary">Menyembunyikan tombol pencarian suara di bilah pencarian.</string>
<string name="revanced_hide_tap_to_update_button_title">Sembunyikan tombol Ketuk untuk memperbarui</string>
<string name="revanced_hide_tap_to_update_button_summary">Menyembunyikan tombol Ketuk untuk memperbarui.</string>
<string name="revanced_hide_voice_search_button_title">Sembunyikan tombol pencarian suara</string>
<string name="revanced_hide_voice_search_button_summary">Menyembunyikan tombol pencarian suara di bilah pencarian.</string>
<string name="revanced_restore_old_style_library_shelf_title">Pulihkan tab Pustaka lama</string>
<string name="revanced_restore_old_style_library_shelf_summary">Memulihkan tab Pustaka ke gaya lama. (Eksperimental)</string>
<string name="revanced_remove_viewer_discretion_dialog_title">Hapus dialog pembatasan penonton</string>
<string name="revanced_remove_viewer_discretion_dialog_summary">"Menghapus dialog pembatasan penonton.
Ini tidak mengabaikan pembatasan usia. Ini hanya menerimanya secara otomatis."</string>
<string name="revanced_spoof_app_version_title">Palsukan versi aplikasi</string>
<string name="revanced_spoof_app_version_summary">"Memalsukan versi klien ke versi lama
<string name="revanced_spoof_app_version_summary">"Memalsukan versi klien ke versi lama.
• Ini akan mengubah tampilan aplikasi, namun efek samping yang tidak diketahui mungkin terjadi.
• Jika nanti dinonaktifkan, UI lama mungkin tetap ada hingga aplikasi dihapus data."</string>
• Jika nanti dinonaktifkan, UI lama mungkin tetap ada hingga data aplikasi dihapus."</string>
<string name="revanced_spoof_app_version_target_title">Target pemalsuan versi aplikasi</string>
<string name="revanced_spoof_app_version_target_summary">Pilih target pemalsuan versi aplikasi.</string>
<string name="revanced_spoof_app_version_target_entry_6_42_55"></string>
<string name="revanced_spoof_app_version_target_entry_6_42_55">6.42.55 - Nonaktifkan lirik real-time</string>
<string name="revanced_spoof_app_version_target_entry_7_16_53">7.16.53 - Pulihkan bilah tindakan lama</string>
<!-- PreferenceScreen: Navigation bar -->
<string name="revanced_preference_screen_navigation_title">Bilah Navigasi</string>
<string name="revanced_hide_navigation_home_button_title">Hide Home button</string>
<string name="revanced_hide_navigation_home_button_summary">Hides the Home button.</string>
<string name="revanced_hide_navigation_samples_button_title">Hide Samples button</string>
<string name="revanced_hide_navigation_samples_button_summary">Hides the Samples button.</string>
<string name="revanced_hide_navigation_explore_button_title">Hide Explore button</string>
<string name="revanced_hide_navigation_explore_button_summary">Hides the Explore button.</string>
<string name="revanced_hide_navigation_library_button_title">Hide Library button</string>
<string name="revanced_hide_navigation_library_button_summary">Hides the Library button.</string>
<string name="revanced_hide_navigation_upgrade_button_title">Hide Upgrade button</string>
<string name="revanced_hide_navigation_upgrade_button_summary">Hides the Upgrade button.</string>
<string name="revanced_hide_navigation_bar_title">Menyembunyikan bilah navigasi</string>
<string name="revanced_hide_navigation_bar_summary">Sembunyikan bilah navigasi.</string>
<string name="revanced_enable_custom_navigation_bar_color_title">Aktifkan warna bilah navigasi khusus</string>
<string name="revanced_enable_custom_navigation_bar_color_summary">Atur warna bilah navigasi.</string>
<string name="revanced_custom_navigation_bar_color_value_title">Nilai warna bilah navigasi khusus</string>
<string name="revanced_custom_navigation_bar_color_value_summary">Masukkan kode hex dari warna bilah navigasi.</string>
<string name="revanced_custom_navigation_bar_color_value_invalid_invalid_toast">Nilai warna bilah navigasi tidak sah.</string>
<string name="revanced_hide_navigation_home_button_title">Sembunyikan tombol Beranda</string>
<string name="revanced_hide_navigation_home_button_summary">Menyembunyikan tombol Beranda.</string>
<string name="revanced_hide_navigation_samples_button_title">Sembunyikan tombol Sampel</string>
<string name="revanced_hide_navigation_samples_button_summary">Menyembunyikan tombol Sampel.</string>
<string name="revanced_hide_navigation_explore_button_title">Sembunyikan tombol Jelajahi</string>
<string name="revanced_hide_navigation_explore_button_summary">Menyembunyikan tombol Jelajahi.</string>
<string name="revanced_hide_navigation_library_button_title">Sembunyikan tombol Pustaka</string>
<string name="revanced_hide_navigation_library_button_summary">Menyembunyikan tombol Pustaka.</string>
<string name="revanced_hide_navigation_upgrade_button_title">Sembunyikan tombol Upgrade</string>
<string name="revanced_hide_navigation_upgrade_button_summary">Menyembunyikan tombol Upgrade.</string>
<string name="revanced_hide_navigation_bar_title">Sembunyikan bilah navigasi</string>
<string name="revanced_hide_navigation_bar_summary">Menyembunyikan bilah navigasi.</string>
<string name="revanced_hide_navigation_label_title">Sembunyikan label navigasi</string>
<string name="revanced_hide_navigation_label_summary">Menyembunyikan label di bilah navigasi.</string>
<string name="revanced_hide_navigation_label_summary">Menyembunyikan label di bawah setiap tombol navigasi.</string>
<!-- PreferenceScreen: Player -->
<string name="revanced_preference_screen_player_title">Player</string>
<string name="revanced_disable_player_gesture_title">Menonaktifkan gerakan pemutar</string>
<string name="revanced_disable_player_gesture_summary">Nonaktifkan usap untuk mengubah trek di pemutar.</string>
<string name="revanced_enable_zen_mode_title">Aktifkan mode zen</string>
<string name="revanced_enable_zen_mode_summary">Menambahkan rona abu-abu ke pemutar video untuk mengurangi ketegangan mata.</string>
<string name="revanced_enable_zen_mode_podcast_title">Enable Zen mode in podcasts</string>
<string name="revanced_enable_zen_mode_podcast_summary">Also enables Zen mode for podcasts.</string>
<string name="revanced_hide_comment_channel_guidelines_title">Hide channel guidelines</string>
<string name="revanced_hide_comment_channel_guidelines_summary">Hides the channel guidelines at the top of the comments section.</string>
<string name="revanced_hide_double_tap_overlay_filter_title">Sembunyikan filter overlay double-tap</string>
<string name="revanced_hide_double_tap_overlay_filter_summary">Menyembunyikan overlay gelap yang muncul ketika double-tap to seek.</string>
<string name="revanced_hide_comment_timestamp_and_emoji_buttons_title">Hide timestamp and emoji buttons</string>
<string name="revanced_preference_screen_player_title">Pemutar</string>
<string name="revanced_add_miniplayer_next_button_title">Menambahkan tombol berikutnya pemutar mini</string>
<string name="revanced_add_miniplayer_next_button_summary">Menambahkan tombol trek berikutnya ke pemutar mini.</string>
<string name="revanced_add_miniplayer_previous_button_title">Tambahkan tombol sebelumnya miniplayer</string>
<string name="revanced_add_miniplayer_previous_button_summary">Menambahkan tombol trek sebelumnya ke pemutar mini.</string>
<string name="revanced_change_miniplayer_color_title">Ubah warna pemutar mini</string>
<string name="revanced_change_miniplayer_color_summary">Mengubah warna pemutar mini supaya sesuai dengan pemutar layar penuh.</string>
<string name="revanced_change_player_background_color_title">Ubah warna latar belakang pemutar</string>
<string name="revanced_change_player_background_color_summary">Mengubah warna latar belakang pemutar ke warna khusus.</string>
<string name="revanced_custom_player_background_color_primary_title">Warna primer latar belakang pemutar</string>
<string name="revanced_custom_player_background_color_primary_summary">"Masukkan kode hex dari warna primer latar belakang pemutar.
Gunakan warna gelap jika memungkinkan, karena aplikasi ini tidak mendukung tema terang."</string>
<string name="revanced_custom_player_background_color_secondary_title">Warna sekunder latar belakang pemutar</string>
<string name="revanced_custom_player_background_color_secondary_summary">"Masukkan kode hex warna sekunder latar belakang pemutar.
Gunakan warna gelap jika memungkinkan, karena aplikasi ini tidak mendukung tema terang."</string>
<string name="revanced_custom_player_background_invalid_toast">Warna latar belakang pemutar tidak sah.</string>
<string name="revanced_change_seekbar_position_title">Ubah posisi seekbar</string>
<string name="revanced_change_seekbar_position_summary">Memindahkan seekbar di bawah tombol putar.</string>
<string name="revanced_disable_miniplayer_gesture_title">Nonaktifkan gerakan pemutar mini</string>
<string name="revanced_disable_miniplayer_gesture_summary">Menonaktifkan usapan untuk mengubah trek di pemutar mini.</string>
<string name="revanced_disable_player_gesture_title">Nonaktifkan gerakan pemutar</string>
<string name="revanced_disable_player_gesture_summary">Menonaktifkan usap untuk mengubah trek di pemutar.</string>
<string name="revanced_enable_forced_miniplayer_title">Aktifkan pemutar mini paksa</string>
<string name="revanced_enable_forced_miniplayer_summary">Mengaktifkan pemutar mini paksa saat beralih ke trek baru.</string>
<string name="revanced_enable_swipe_to_dismiss_miniplayer_title">Aktifkan usapan untuk menutup pemutar mini</string>
<string name="revanced_enable_swipe_to_dismiss_miniplayer_summary">Mengaktifkan gesek ke bawah untuk menutup pemutar mini.</string>
<string name="revanced_enable_thick_seekbar_title">Aktifkan seekbar tebal</string>
<string name="revanced_enable_thick_seekbar_summary">"Mengaktifkan seekbar yang tebal.
Keterbatasan: Segmen SponsorBlock tidak ditampilkan di seekbar."</string>
<string name="revanced_enable_zen_mode_title">Aktifkan mode Zen</string>
<string name="revanced_enable_zen_mode_summary">Mengaktifkan warna abu-abu muda untuk latar belakang pemutar untuk mengurangi kelelahan mata.</string>
<string name="revanced_enable_zen_mode_podcast_title">Aktifkan mode Zen di podcast</string>
<string name="revanced_enable_zen_mode_podcast_summary">Mengaktifkan mode Zen di podcast.</string>
<string name="revanced_hide_comment_channel_guidelines_title">Sembunyikan pedoman saluran</string>
<string name="revanced_hide_comment_channel_guidelines_summary">Menyembunyikan panduan saluran di bagian atas komentar.</string>
<string name="revanced_hide_double_tap_overlay_filter_title">Sembunyikan penyaring hamparan ketuk dua kali</string>
<string name="revanced_hide_double_tap_overlay_filter_summary">Menyembunyikan hamparan gelap yang muncul ketika mengetuk dua kali untuk menggeser.</string>
<string name="revanced_hide_comment_timestamp_and_emoji_buttons_title">Sembunyikan tombol emoji dan stempel waktu</string>
<string name="revanced_hide_comment_timestamp_and_emoji_buttons_summary">Hides the timestamp and emoji buttons when typing comments.</string>
<string name="revanced_hide_fullscreen_share_button_title">Hide fullscreen Share button</string>
<string name="revanced_hide_fullscreen_share_button_summary">Hides the Share button in the fullscreen player.</string>
<string name="revanced_hide_fullscreen_share_button_title">Sembunyikan tombol Bagikan layar penuh</string>
<string name="revanced_hide_fullscreen_share_button_summary">Menyembunyikan tombol Bagikan di pemutar layar penuh.</string>
<string name="revanced_hide_song_video_toggle_title">Sembunyikan tombol Lagu / Video</string>
<string name="revanced_hide_song_video_toggle_summary">Menyembunyikan tombol Lagu / Video di pemutar.</string>
<string name="revanced_remember_repeat_state_title">Ingat keadaan pengulangan</string>
<string name="revanced_remember_repeat_state_summary">Mengingat keadaan pengulangan.</string>
<string name="revanced_remember_repeat_state_summary">Mengingat keadaan tombol pengulangan.</string>
<string name="revanced_remember_shuffle_state_title">Ingat keadaan pengacakan</string>
<string name="revanced_remember_shuffle_state_summary">Mengingat keadaan pengacakan.</string>
<string name="revanced_restore_old_comments_popup_panels_title">Restore old comments popup panels</string>
<string name="revanced_restore_old_comments_popup_panels_summary">Returns the comments popup panels to the old style.</string>
<string name="revanced_restore_old_player_background_title">Restore old player background</string>
<string name="revanced_restore_old_player_background_summary">Returns the player background to the old style.</string>
<string name="revanced_restore_old_player_layout_title">Restore old player layout</string>
<string name="revanced_restore_old_player_layout_summary">"Returns the player layout to the old style.
Some features may not work properly in the old player layout."</string>
<string name="revanced_remember_shuffle_state_summary">Mengingat status tombol pengacakan.</string>
<string name="revanced_restore_old_comments_popup_panels_title">Pulihkan panel sembulan komentar lama</string>
<string name="revanced_restore_old_comments_popup_panels_summary">Memulihkan panel sembulan komentar ke gaya lama.</string>
<string name="revanced_restore_old_player_background_title">Pulihkan latar belakang pemutar lama</string>
<string name="revanced_restore_old_player_background_summary">Mengembalikan latar belakang pemutar ke gaya lama.</string>
<string name="revanced_restore_old_player_layout_title">Pulihkan tata letak pemutar lama</string>
<string name="revanced_restore_old_player_layout_summary">"Memulihkan tata letak pemutar ke gaya lama.
Beberapa fitur mungkin tidak berfungsi dengan baik dalam tata letak pemutar lama."</string>
<!-- PreferenceScreen: Settings menu -->
<string name="revanced_preference_screen_settings_title">Menu pengaturan</string>
<string name="revanced_hide_settings_menu_parent_tools_title">Sembunyikan menu Pusat Keluarga</string>
<string name="revanced_hide_settings_menu_general_title">Sembunyikan menu Umum</string>
<string name="revanced_hide_settings_menu_playback_title">Sembunyikan menu Pemutaran</string>
<string name="revanced_hide_settings_menu_data_saving_title">Sembunyikan menu penghematan Data</string>
<string name="revanced_hide_settings_menu_downloads_and_storage_title">Sembunyikan menu Unduhan &amp; penyimpanan</string>
<string name="revanced_hide_settings_menu_notification_title">Sembunyikan menu Pemberitahuan</string>
<string name="revanced_hide_settings_menu_privacy_and_location_title">Sembunyikan menu Privasi &amp; data</string>
<string name="revanced_hide_settings_menu_recommendations_title">Sembunyikan menu Rekomendasi</string>
<string name="revanced_hide_settings_menu_paid_memberships_title">Sembunyikan menu Dapatkan Music premium</string>
<string name="revanced_hide_settings_menu_about_title">Sembunyikan menu Tentang</string>
<!-- PreferenceScreen: Video -->
<string name="revanced_preference_screen_video_title">Video</string>
<string name="revanced_custom_playback_speeds_title">Edit kecepatan pemutaran kustom</string>
<string name="revanced_custom_playback_speeds_title">Edit kecepatan pemutaran khusus</string>
<string name="revanced_custom_playback_speeds_summary">Menambah atau mengubah kecepatan pemutaran yang tersedia.</string>
<string name="revanced_remember_playback_speed_last_selected_title">Remember playback speed changes</string>
<string name="revanced_remember_playback_speed_last_selected_title">Ingat perubahan kecepatan pemutaran</string>
<string name="revanced_remember_playback_speed_last_selected_summary">Remembers the last playback speed selected.</string>
<string name="revanced_remember_playback_speed_last_selected_toast_title">Tampilkan toast</string>
<string name="revanced_remember_playback_speed_last_selected_toast_summary">Menunjukkan toast ketika mengubah playback speed semula.</string>
<string name="revanced_remember_video_quality_last_selected_title">Remember video quality changes</string>
<string name="revanced_remember_video_quality_last_selected_summary">Remembers the last video quality selected.</string>
<string name="revanced_remember_playback_speed_last_selected_toast_summary">Menampilkan toast saat mengubah kecepatan pemutaran bawaan.</string>
<string name="revanced_remember_video_quality_last_selected_title">Ingat perubahan kualitas video</string>
<string name="revanced_remember_video_quality_last_selected_summary">Mengingat kualitas video terakhir yang dipilih.</string>
<string name="revanced_remember_video_quality_last_selected_toast_title">Tampilkan toast</string>
<string name="revanced_remember_video_quality_last_selected_toast_summary">Menunjukkan toast ketika mengubah playback speed semula.</string>
<string name="revanced_custom_playback_speeds_invalid">Kecepatan pemutaran kustom tidak valid. Atur ulang ke nilai default.</string>
<string name="revanced_custom_playback_speeds_parse_exception">Invalid custom playback speeds. Using default values.</string>
<string name="revanced_remember_playback_speed_toast">Changing default speed to %s.</string>
<string name="revanced_remember_video_quality_mobile">Changing default mobile data quality to %s.</string>
<string name="revanced_remember_video_quality_none">Failed to set quality.</string>
<string name="revanced_remember_video_quality_wifi">Changing default Wi-Fi quality to %s.</string>
<string name="revanced_remember_video_quality_last_selected_toast_summary">Menampilkan toast ketika mengubah kualitas video bawaan.</string>
<string name="revanced_custom_playback_speeds_invalid">Kecepatan khusus harus kurang dari %sx.</string>
<string name="revanced_custom_playback_speeds_parse_exception">Kecepatan pemutaran kustom tidak sah.</string>
<string name="revanced_remember_playback_speed_toast">Mengubah kecepatan bawaan ke %s.</string>
<string name="revanced_remember_video_quality_mobile">Mengubah kualitas data seluler bawaan ke %s.</string>
<string name="revanced_remember_video_quality_none">Gagal mengatur kualitas.</string>
<string name="revanced_remember_video_quality_wifi">Mengubah kualitas Wi-Fi bawaan ke %s.</string>
<!-- PreferenceScreen: Return YouTube Dislike -->
<string name="revanced_preference_screen_ryd_title">Return YouTube Dislike</string>
<string name="revanced_ryd_enabled_title">Enable Return YouTube Dislike</string>
<string name="revanced_ryd_enabled_summary">Menunjukkan jumlah dislike pada video.</string>
<string name="revanced_ryd_enabled_title">Aktifkan Return YouTube Dislike</string>
<string name="revanced_ryd_enabled_summary">Menampilkan jumlah tidak suka pada video.</string>
<string name="revanced_ryd_dislike_percentage_title">Dislike sebagai persentase</string>
<string name="revanced_ryd_dislike_percentage_summary">Alih-alih jumlah dislike, yang ditampilkan adalah persentase dislike.</string>
<string name="revanced_ryd_compact_layout_title">Tombol like ringkas</string>
<string name="revanced_ryd_compact_layout_summary">Menyembunyikan pemisah tombol like.</string>
<string name="revanced_ryd_toast_on_connection_error_title">Show a toast if API is unavailable</string>
<string name="revanced_ryd_toast_on_connection_error_summary">Shows a toast if the Return YouTube Dislike API is unavailable.</string>
<string name="revanced_ryd_dislike_percentage_summary">Menampilkan persentase dislike, bukan jumlah dislike.</string>
<string name="revanced_ryd_compact_layout_title">Tombol suka ringkas</string>
<string name="revanced_ryd_compact_layout_summary">Menyembunyikan pemisah tombol suka.</string>
<string name="revanced_ryd_estimated_like_title">Tampilkan perkiraan suka</string>
<string name="revanced_ryd_estimated_like_summary">Menampilkan perkiraan jumlah suka video.</string>
<string name="revanced_ryd_toast_on_connection_error_title">Tampilkan toast jika API tidak tersedia</string>
<string name="revanced_ryd_toast_on_connection_error_summary">Menampilkan toast jika API Return YouTube Dislike tidak tersedia.</string>
<string name="revanced_ryd_about">Tentang</string>
<string name="revanced_ryd_attribution_title">ReturnYouTubeDislike.com</string>
<string name="revanced_ryd_attribution_summary">Data disediakan oleh API Return YouTube Dislike. Tekan di sini untuk mempelajari lebih lanjut.</string>
<string name="revanced_ryd_failure_connection_timeout">Dislikes are temporarily unavailable (API timed out).</string>
<string name="revanced_ryd_failure_connection_status_code">Dislikes are unavailable (status %d).</string>
<string name="revanced_ryd_failure_connection_timeout">Dislike untuk sementara tidak tersedia (API kehabisan waktu).</string>
<string name="revanced_ryd_failure_connection_status_code">Dislike tidak tersedia (status %d).</string>
<string name="revanced_ryd_failure_client_rate_limit_requested">Dislike tidak tersedia (batas API client tercapai).</string>
<string name="revanced_ryd_failure_generic">Dislikes are unavailable (%s).</string>
<string name="revanced_ryd_failure_generic">Dislikes tidak tersedia (%s).</string>
<string name="revanced_ryd_video_likes_hidden_by_video_owner">Disembunyikan</string>
<!-- PreferenceScreen: Return YouTube Username -->
<string name="revanced_preference_screen_return_youtube_username_title">Return YouTube Username</string>
<string name="revanced_return_youtube_username_enabled_title">Aktifkan Return YouTube Username</string>
<string name="revanced_return_youtube_username_enabled_summary">Mengganti penanganan dengan nama pengguna di komentar.</string>
<string name="revanced_return_youtube_username_display_format_title">Format tampilan</string>
<string name="revanced_return_youtube_username_display_format_summary">Pilih format tampilan nama pengguna.</string>
<string name="revanced_return_youtube_username_display_format_username_only">Nama pengguna</string>
<string name="revanced_return_youtube_username_display_format_username_handle">Nama pengguna (@penganganan)</string>
<string name="revanced_return_youtube_username_display_format_handle_username">\@penanganan (Nama pengguna)</string>
<string name="revanced_return_youtube_username_youtube_data_api_v3_developer_key_title">Kunci API Data YouTube</string>
<string name="revanced_return_youtube_username_youtube_data_api_v3_developer_key_summary">Kunci pengembang untuk menggunakan API Data YouTube v3.</string>
<string name="revanced_return_youtube_username_youtube_data_api_v3_about_title">Tentang kunci API Data YouTube</string>
<string name="revanced_return_youtube_username_youtube_data_api_v3_about_summary">"Kunci Pengembang API Data YouTube v3 diperlukan untuk mengganti penanganan dengan nama pengguna.
Kuota harian untuk kunci API pada paket gratis adalah 10.000, dan 1 kuota digunakan untuk mengganti penanganan dengan nama pengguna untuk 1 komentar.
Klik untuk melihat cara menerbitkan kunci API."</string>
<string name="revanced_return_youtube_username_youtube_data_api_v3_dialog_title">Terbitkan kunci pengembang API Data YouTube v3</string>
<string name="revanced_return_youtube_username_youtube_data_api_v3_dialog_message">1. Pergi ke &lt;a href=%1$s&gt;Buat proyek baru&lt;/a&gt;.&lt;br&gt;2. Klik &lt;b&gt;tombol;&lt;/b&gt; BUAT.&lt;br&gt;3. Pergi ke &lt;a href=%2$s&gt;API Data YouTube v3&lt;/a&gt;.&lt;br&gt;4. Klik &lt;b&gt;tombol&lt;/b&gt; NYALAKAN.&lt;br&gt;5. Klik &lt;b&gt;tombol&lt;/b&gt; BUAT KREDENSIAL.&lt;br&gt;6. Pilih &lt;b&gt;pilihan&lt;/b&gt; data Publik.&lt;br&gt;7. Klik &lt;b&gt;tombol&lt;/b&gt; SELANJUTNYA.&lt;br&gt;8. Salin kunci API.&lt;br&gt;&lt;br&gt;※ Kunci API tidak boleh dibagikan dengan orang lain, sehingga tidak disertakan dalam pengaturan Impor / Ekspor.</string>
<!-- PreferenceScreen: SponsorBlock -->
<string name="revanced_preference_screen_sb_title">SponsorBlock</string>
<string name="revanced_sb_enabled">Enable SponsorBlock</string>
<string name="revanced_sb_enabled_sum">SponsorBlock is a crowd-sourced system for skipping annoying parts of YouTube videos.</string>
<string name="revanced_sb_toast_on_connection_error">Show a toast if API is unavailable</string>
<string name="revanced_sb_toast_on_connection_error_sum">Shows a toast if the SponsorBlock API is unavailable.</string>
<string name="revanced_sb_toast_on_skip">Show a toast when skipping automatically</string>
<string name="revanced_sb_toast_on_skip_sum">Shows a toast when a segment is automatically skipped.</string>
<string name="revanced_sb_api_url">Change API URL</string>
<string name="revanced_sb_api_url_sum">The address SponsorBlock uses to make calls to the server. Do not change this unless you know what you\'re doing.</string>
<string name="revanced_sb_api_url_reset">API URL reset.</string>
<string name="revanced_sb_api_url_invalid">API URL is invalid.</string>
<string name="revanced_sb_api_url_changed">API URL changed.</string>
<string name="revanced_sb_diff_segments">Change segment behavior</string>
<string name="revanced_sb_enabled">Aktifkan SponsorBlock</string>
<string name="revanced_sb_enabled_sum">SponsorBlock adalah sistem yang bersumber dari banyak orang untuk melewatkan bagian video YouTube yang mengganggu.</string>
<string name="revanced_sb_toast_on_connection_error">Tampilkan toast jika API tidak tersedia</string>
<string name="revanced_sb_toast_on_connection_error_sum">Menampilkan toast jika API SponsorBlock tidak tersedia.</string>
<string name="revanced_sb_toast_on_skip">Menampilkan toast saat melewatkan secara otomatis</string>
<string name="revanced_sb_toast_on_skip_sum">Menampilkan toast ketika segmen dilewati secara otomatis.</string>
<string name="revanced_sb_api_url">Ubah URL API</string>
<string name="revanced_sb_api_url_sum">Alamat yang digunakan SponsorBlock untuk melakukan panggilan ke server. Jangan ubah ini kecuali Anda tahu apa yang Anda lakukan.</string>
<string name="revanced_sb_api_url_reset">URL API diatur ulang.</string>
<string name="revanced_sb_api_url_invalid">URL API tidak sah.</string>
<string name="revanced_sb_api_url_changed">URL API diubah.</string>
<string name="revanced_sb_diff_segments">Ubah perilaku segmen</string>
<string name="revanced_sb_segments_sponsor">Sponsor</string>
<string name="revanced_sb_segments_sponsor_sum">Paid promotion, paid referrals, and direct advertisements. Not for self-promotion or free shout-outs to causes / creators / websites / products they like.</string>
<string name="revanced_sb_segments_selfpromo">Unpaid/Self Promotion</string>
<string name="revanced_sb_segments_selfpromo_sum">Similar to \'Sponsor\' except for unpaid or self promotion. Includes sections about merchandise, donations, or information about who they collaborated with.</string>
<string name="revanced_sb_segments_interaction">Interaction Reminder (Subscribe)</string>
<string name="revanced_sb_segments_interaction_sum">A short reminder to like, subscribe, or follow them in the middle of content. If it is long or about something specific, it should instead be under self promotion.</string>
<string name="revanced_sb_segments_intro">Intermission/Intro Animation</string>
<string name="revanced_sb_segments_intro_sum">An interval without actual content. Could be a pause, static frame, or repeating animation. Does not include transitions containing information.</string>
<string name="revanced_sb_segments_outro">Endcards/Credits</string>
<string name="revanced_sb_segments_outro_sum">Credits or when the YouTube endcards appear. Not for conclusions with information.</string>
<string name="revanced_sb_segments_preview">Preview/Recap / Hook</string>
<string name="revanced_sb_segments_preview_sum">Collection of clips that show what is coming up or what happened in the video or in other videos of a series, where all information is repeated elsewhere.</string>
<string name="revanced_sb_segments_filler">Filler Tangent/Jokes</string>
<string name="revanced_sb_segments_filler_sum">Tangential scenes added only for filler or humor that are not required to understand the main content of the video. Does not include segments providing context or background details.</string>
<string name="revanced_sb_segments_nomusic">Music: Non-Music Section</string>
<string name="revanced_sb_segments_nomusic_sum">Only for use in music videos. Sections of music videos without music, that aren\'t already covered by another category.</string>
<string name="revanced_sb_skipped_sponsor">Skipped sponsor.</string>
<string name="revanced_sb_skipped_selfpromo">Skipped self promotion.</string>
<string name="revanced_sb_skipped_interaction">Skipped annoying reminder.</string>
<string name="revanced_sb_skipped_intro_beginning">Skipped intro.</string>
<string name="revanced_sb_skipped_intro_middle">Skipped intermission.</string>
<string name="revanced_sb_skipped_intro_end">Skipped intermission.</string>
<string name="revanced_sb_skipped_outro">Skipped outro.</string>
<string name="revanced_sb_skipped_preview_beginning">Skipped preview.</string>
<string name="revanced_sb_skipped_preview_middle">Skipped preview.</string>
<string name="revanced_sb_skipped_preview_end">Skipped recap.</string>
<string name="revanced_sb_skipped_filler">Skipped filler.</string>
<string name="revanced_sb_skipped_nomusic">Skipped a non-music section.</string>
<string name="revanced_sb_skipped_multiple_segments">Skipped multiple segments.</string>
<string name="revanced_sb_skip_automatically">Skip automatically</string>
<string name="revanced_sb_skip_ignore">Disable</string>
<string name="revanced_sb_sponsorblock_connection_failure_generic">SponsorBlock is temporarily unavailable.</string>
<string name="revanced_sb_sponsorblock_connection_failure_status">SponsorBlock is temporarily unavailable (status %d).</string>
<string name="revanced_sb_sponsorblock_connection_failure_timeout">SponsorBlock is temporarily unavailable (API timed out).</string>
<string name="revanced_sb_color_dot_label">Color:</string>
<string name="revanced_sb_color_changed">Color changed.</string>
<string name="revanced_sb_color_reset">Color reset.</string>
<string name="revanced_sb_color_invalid">Invalid color code. Color reset to default.</string>
<string name="revanced_sb_reset_color">Reset color</string>
<string name="revanced_sb_about_api_sum">Data is provided by the SponsorBlock API. Tap here to learn more and see downloads for other platforms.</string>
<string name="revanced_sb_about">About</string>
<string name="revanced_sb_segments_sponsor_sum">Promosi berbayar, rujukan berbayar, dan iklan langsung. Bukan untuk promosi diri atau promosi gratis untuk tujuan / kreator / situs web / produk yang mereka sukai.</string>
<string name="revanced_sb_segments_selfpromo">Tidak Berbayar / Promosi Sendiri</string>
<string name="revanced_sb_segments_selfpromo_sum">Mirip dengan Sponsor, kecuali untuk promosi yang tidak berbayar atau promosi sendiri. Termasuk bagian tentang barang dagangan, donasi, atau informasi tentang dengan siapa mereka berkolaborasi.</string>
<string name="revanced_sb_segments_interaction">Pengingat Interaksi (Berlangganan)</string>
<string name="revanced_sb_segments_interaction_sum">Pengingat singkat untuk menyukai, berlangganan, atau mengikuti mereka di tengah-tengah konten. Jika panjang atau tentang sesuatu yang spesifik, sebaiknya berada di bawah promosi diri.</string>
<string name="revanced_sb_segments_intro">Animasi Jeda / Intro</string>
<string name="revanced_sb_segments_intro_sum">Selang waktu tanpa konten yang sebenarnya. Bisa berupa jeda, bingkai statis, atau animasi berulang. Tidak termasuk transisi yang berisi informasi.</string>
<string name="revanced_sb_segments_outro">Kartu Akhir / Kredit</string>
<string name="revanced_sb_segments_outro_sum">Kredit atau saat kartu akhir YouTube muncul. Bukan untuk menyimpulkan informasi.</string>
<string name="revanced_sb_segments_preview">Pratinjau / Rekap / Pengait</string>
<string name="revanced_sb_segments_preview_sum">Kumpulan klip yang menunjukkan apa yang akan datang atau apa yang terjadi dalam video atau dalam video lain dari suatu seri, di mana semua informasi diulang di tempat lain.</string>
<string name="revanced_sb_segments_filler">Pengisi Tidak Relevan / Lelucon</string>
<string name="revanced_sb_segments_filler_sum">Adegan berbelit-belit yang ditambahkan hanya sebagai filler atau candaan yang tidak diperlukan untuk memahami isi utama video. Tidak termasuk bagian yang mengandung konteks atau detail latar belakang.</string>
<string name="revanced_sb_segments_nomusic">Musik: Bagian Non-Musik</string>
<string name="revanced_sb_segments_nomusic_sum">Hanya untuk digunakan dalam video musik. Bagian video musik tanpa musik, yang belum tercakup dalam kategori lain.</string>
<string name="revanced_sb_skipped_sponsor">Melewatkan sponsor.</string>
<string name="revanced_sb_skipped_selfpromo">Melewatkan promosi diri.</string>
<string name="revanced_sb_skipped_interaction">Melewatkan pengingat mengganggu.</string>
<string name="revanced_sb_skipped_intro_beginning">Melewatkan intro.</string>
<string name="revanced_sb_skipped_intro_middle">Melewatkan jeda.</string>
<string name="revanced_sb_skipped_intro_end">Melewatkan jeda.</string>
<string name="revanced_sb_skipped_outro">Melewatkan outro.</string>
<string name="revanced_sb_skipped_preview_beginning">Melewatkan pratinjau.</string>
<string name="revanced_sb_skipped_preview_middle">Melewatkan pratinjau.</string>
<string name="revanced_sb_skipped_preview_end">Melewatkan rekap.</string>
<string name="revanced_sb_skipped_filler">Melewatkan pengisi.</string>
<string name="revanced_sb_skipped_nomusic">Melewatkan bagian non musik.</string>
<string name="revanced_sb_skipped_multiple_segments">Melewatkan beberapa segmen.</string>
<string name="revanced_sb_skip_automatically">Lewati otomatis</string>
<string name="revanced_sb_skip_ignore">Nonaktifkan</string>
<string name="revanced_sb_sponsorblock_connection_failure_generic">SponsorBlock untuk sementara tidak tersedia.</string>
<string name="revanced_sb_sponsorblock_connection_failure_status">SponsorBlock untuk sementara tidak tersedia (status %d).</string>
<string name="revanced_sb_sponsorblock_connection_failure_timeout">SponsorBlock untuk sementara tidak tersedia (API habis batas waktunya).</string>
<string name="revanced_sb_color_dot_label">Warna:</string>
<string name="revanced_sb_color_changed">Warna diubah.</string>
<string name="revanced_sb_color_reset">Warna diatur ulang.</string>
<string name="revanced_sb_color_invalid">Kode warna tidak sah.</string>
<string name="revanced_sb_reset_color">Atur ulang warna</string>
<string name="revanced_sb_about_api_sum">Data disediakan oleh API SponsorBlock. Tekan di sini untuk mempelajari lebih lanjut dan melihat unduhan untuk platform lain.</string>
<string name="revanced_sb_about">Tentang</string>
<string name="revanced_sb_about_api">sponsor.ajay.app</string>
<!-- PreferenceScreen: Miscellaneous -->
<string name="revanced_preference_screen_misc_title">Miscellaneous</string>
<string name="revanced_preference_screen_misc_title">Lain-lain</string>
<string name="revanced_extended_settings_import_export_title">Ekspor / Impor</string>
<string name="revanced_extended_settings_import_export_summary">Impor atau ekspor setelan sebagai teks.</string>
<string name="revanced_extended_settings_export_as_file">Export settings to file</string>
<string name="revanced_extended_settings_import_as_file">Import settings from file</string>
<string name="revanced_extended_settings_import_export_as_text">Import / Export settings as text</string>
<string name="revanced_extended_settings_export_failed">Failed to export settings.</string>
<string name="revanced_extended_settings_export_success">Settings were successfully exported.</string>
<string name="revanced_extended_settings_import_export_summary">Impor / Ekspor pengaturan Musik RVX.</string>
<string name="revanced_extended_settings_export_as_file">Ekspor pengaturan ke berkas</string>
<string name="revanced_extended_settings_import_as_file">Impor pengaturan dari berkas</string>
<string name="revanced_extended_settings_import_export_as_text">Impor / Ekspor pengaturan sebagai teks</string>
<string name="revanced_extended_settings_export_failed">Gagal mengekspor pengaturan.</string>
<string name="revanced_extended_settings_export_success">Pengaturan berhasil diekspor.</string>
<string name="revanced_extended_settings_import">Impor</string>
<string name="revanced_extended_settings_import_copy">Salin</string>
<string name="revanced_extended_settings_import_failed">Import failed: %s.</string>
<string name="revanced_extended_settings_import_reset">Reset setelan ke default.</string>
<string name="revanced_extended_settings_import_failed">Impor gagal: %s.</string>
<string name="revanced_extended_settings_import_reset">Pengaturan diatur ulang ke bawaan.</string>
<string name="revanced_extended_settings_import_success">Setelan %d diimpor.</string>
<string name="revanced_extended_settings_reset">Reset</string>
<string name="revanced_extended_settings_reset">Atur ulang</string>
<string name="revanced_share_copy_settings_success">Setelan disalin ke papan klip.</string>
<string name="revanced_bypass_image_region_restrictions_title">Bypass gambar larangan wilayah</string>
<string name="revanced_bypass_image_region_restrictions_summary">Mengganti domain yang ke blokir di negara tertentu sehingga playlist thumbnail, channel avatar, dll bisa di terima.</string>
<string name="revanced_bypass_image_region_restrictions_title">Abaikan pembatasan wilayah gambar</string>
<string name="revanced_bypass_image_region_restrictions_summary">Mengabaikan domain yang diblokir di beberapa wilayah sehingga thumbnail playlist, avatar saluran, dll. dapat diterima.</string>
<string name="revanced_change_share_sheet_title">Ubah lembar berbagi</string>
<string name="revanced_change_share_sheet_summary">Mengubah dari lembar berbagi dalam aplikasi ke lembar berbagi sistem.</string>
<string name="revanced_change_share_sheet_summary">Mengubah lembar berbagi dalam aplikasi ke lembar berbagi sistem.</string>
<string name="revanced_disable_cairo_splash_animation_title">Menonaktifkan animasi percikan Cairo</string>
<string name="revanced_disable_cairo_splash_animation_summary">Menonaktifkan animasi percikan Cairo saat aplikasi dimulai.</string>
<string name="revanced_disable_drc_audio_title">Nonaktifkan audio DRC</string>
<string name="revanced_disable_drc_audio_summary">Menonaktifkan DRC (Dynamic Range Compression) yang diterapkan ke audio.</string>
<string name="revanced_disable_music_video_in_album_title">Nonaktifkan video musik dalam album</string>
<string name="revanced_disable_music_video_in_album_summary">"Ketika pengguna non-premium memutar lagu yang termasuk dalam album, video musik terkadang diputar dan bukannya lagu resminya.
Temukan lagu resmi jika video musik terdeteksi diputar dari album.
Keterbatasan: Video anak-anak mungkin tidak dapat dialihkan."</string>
<string name="revanced_disable_music_video_in_album_redirect_type_title">Jenis pengalihan</string>
<string name="revanced_disable_music_video_in_album_redirect_type_summary">Menentukan cara mengalihkan ke lagu resmi.</string>
<string name="revanced_disable_music_video_in_album_redirect_type_entry_redirect">Alihkan</string>
<string name="revanced_disable_music_video_in_album_redirect_type_entry_on_click">Ketuk tombol Lagu / Video</string>
<string name="revanced_disable_music_video_in_album_redirect_type_entry_on_long_click">Ketuk dan tahan tombol Lagu / Video</string>
<string name="revanced_disable_quic_protocol_title">Nonaktifkan protokol QUIC</string>
<string name="revanced_disable_quic_protocol_summary">"Menonaktifkan protokol QUIC CronetEngine."</string>
<string name="revanced_enable_debug_logging_title">Aktifkan pencatatan debug</string>
<string name="revanced_enable_debug_logging_summary">Mencetak catatan debug.</string>
<string name="revanced_enable_debug_buffer_logging_title">Enable debug buffer logging</string>
<string name="revanced_enable_debug_buffer_logging_summary">Includes the buffer in the debug log.</string>
<string name="revanced_enable_debug_buffer_logging_title">Mengaktifkan pencatatan buffer debug</string>
<string name="revanced_enable_debug_buffer_logging_summary">Menyertakan buffer dalam log debug.</string>
<string name="revanced_enable_opus_codec_title">Aktifkan codec opus</string>
<string name="revanced_enable_opus_codec_summary">"Mengaktifkan codec audio opus alih-alih codec audio mp4a."</string>
<string name="revanced_sanitize_sharing_links_title">Sanitasi tautan berbagi</string>
<string name="revanced_sanitize_sharing_links_summary">Menghapus parameter kueri pelacakan dari URL saat membagikan tautan.</string>
<string name="gms_core_settings_title">Open GmsCore</string>
<string name="gms_core_settings_summary">Enable cloud messaging to receive notifications.</string>
<string name="gms_core_toast_not_installed_message">GmsCore is not installed. Install it.</string>
<string name="gms_core_dialog_title">Action needed</string>
<string name="gms_core_dialog_not_whitelisted_not_allowed_in_background_message">"GmsCore does not have permission to run in the background.
<string name="revanced_enable_opus_codec_summary">"Mengaktifkan codec OPUS jika respon pemutar menyertakannya.
Follow the 'Don't kill my app!' guide for your device, and apply the instructions to your GmsCore installation.
Info:
• Klien YouTube Music terbaru menggunakan codec audio OPUS secara default.
• Ini hanya berlaku untuk pengguna yang memalsukan dengan klien yang sangat lama."</string>
<string name="revanced_sanitize_sharing_links_title">Bersihkan tautan berbagi</string>
<string name="revanced_sanitize_sharing_links_summary">Membersihkan tautan berbagi dengan menghapus parameter kueri pelacakan.</string>
<string name="revanced_spoof_client_title">Palsukan klien</string>
<string name="revanced_spoof_client_summary">Memalsukan klien untuk mencegah masalah pemutaran.</string>
<string name="revanced_spoof_client_type_title">Klien bawaan</string>
<string name="revanced_spoof_client_type_summary">Menentukan klien bawaan untuk pemalsuan.</string>
<string name="revanced_spoof_client_type_entry_android_music_4_27">Musik Android 4.27.53</string>
<string name="revanced_spoof_client_type_entry_android_music_5_29">Musik Android 5.29.53</string>
<string name="revanced_spoof_client_type_entry_ios_music_6_21">Musik iOS 6.21</string>
<string name="revanced_spoof_client_type_entry_ios_music_7_04">Musik iOS 7.04</string>
<string name="revanced_watch_history_type_title">Jenis riwayat tontonan</string>
<string name="revanced_watch_history_type_summary">"• Asli: Mengikuti pengaturan riwayat tontonan akun Google, tetapi riwayat tontonan mungkin tidak berfungsi karena DNS atau VPN.
• Ganti domain: Mengikuti pengaturan riwayat tontonan akun Google.
• Blokir riwayat tontonan: Riwayat tontonan diblokir."</string>
<string name="revanced_watch_history_type_entry_1">Asli</string>
<string name="revanced_watch_history_type_entry_2">Ganti domain</string>
<string name="revanced_watch_history_type_entry_3">Blokir riwayat tontonan</string>
<string name="revanced_default_app_settings_title">Buka pengaturan aplikasi bawaan</string>
<string name="revanced_default_app_settings_summary">Untuk membuka tautan YouTube Music di RVX Music, aktifkan Buka tautan yang didukung dan aktifkan semua alamat web yang Didukung.</string>
<string name="gms_core_settings_title">Buka pengaturan GmsCore</string>
<string name="gms_core_settings_summary">Untuk menerima pemberitahuan di RVX Music, aktifkan Cloud Messaging.</string>
<string name="gms_core_toast_not_installed_message">GmsCore tidak terpasang. Pasang dulu.</string>
<string name="gms_core_dialog_title">Diperlukan tindakan</string>
<string name="gms_core_dialog_not_whitelisted_not_allowed_in_background_message">"GmsCore tidak memiliki izin untuk berjalan di latar belakang.
This is required for the app to work."</string>
<string name="gms_core_dialog_open_website_text">Open website</string>
<string name="gms_core_dialog_not_whitelisted_using_battery_optimizations_message">"GmsCore battery optimizations must be disabled to prevent issues.
Ikuti panduan 'Don't kill my app!' untuk perangkat Anda, dan terapkan petunjuk pada pemasangan GmsCore.
Tap on the continue button and disable battery optimizations."</string>
<string name="gms_core_dialog_continue_text">Continue</string>
Hal ini diperlukan agar aplikasi dapat berfungsi."</string>
<string name="gms_core_dialog_open_website_text">Buka website</string>
<string name="gms_core_dialog_not_whitelisted_using_battery_optimizations_message">"Pengoptimalan baterai GmsCore harus dinonaktifkan untuk mencegah masalah.
Menonaktifkan pengoptimalan baterai untuk GmsCore tidak akan berdampak negatif pada penggunaan baterai.
Ketuk tombol lanjutkan dan izinkan perubahan pengoptimalan."</string>
<string name="gms_core_dialog_continue_text">Lanjutkan</string>
</resources>

View File

@ -4,14 +4,14 @@
<string name="revanced_extended_settings_title">RVX</string>
<string name="revanced_extended_reset_to_default_toast">Atur ulang ke nilai default.</string>
<!-- Shared Category -->
<string name="revanced_extended_restart_first_run">Mulai ulang untuk memuat layout secara normal</string>
<string name="revanced_extended_restart_message">Refresh dan mulai ulang</string>
<string name="revanced_extended_restart_first_run">Mulai ulang untuk memuat tata letak secara normal</string>
<string name="revanced_extended_restart_message">Segarkan dan mulai ulang</string>
<!-- PreferenceScreen: Account -->
<string name="revanced_preference_screen_account_title">Akun</string>
<string name="revanced_hide_account_menu_title">Sembunyikan menu akun</string>
<string name="revanced_hide_account_menu_summary">Menyembunyikan elemen menu akun menggunakan filter custom.</string>
<string name="revanced_hide_account_menu_filter_strings_title">Filter menu Akun</string>
<string name="revanced_hide_account_menu_filter_strings_summary">Daftar dari nama-nama menu akun ke filter, terpisah oleh garis baru.</string>
<string name="revanced_hide_account_menu_summary">Menyembunyikan elemen menu akun menggunakan penyaring khusus.</string>
<string name="revanced_hide_account_menu_filter_strings_title">Penyaring menu Akun</string>
<string name="revanced_hide_account_menu_filter_strings_summary">Daftar nama menu akun yang akan disaring, dipisahkan dengan baris baru.</string>
<string name="revanced_hide_account_menu_empty_component_title">Sembunyikan komponen kosong</string>
<string name="revanced_hide_account_menu_empty_component_summary">Menyembunyikan komponen kosong di menu akun.</string>
<string name="revanced_hide_handle_title">Sembunyikan handle</string>
@ -20,13 +20,13 @@
<string name="revanced_hide_terms_container_summary">Menyembunyikan kontainer ketentuan layanan.</string>
<!-- PreferenceScreen: Action bar -->
<string name="revanced_preference_screen_action_bar_title">Bilah Tindakan</string>
<string name="revanced_change_action_bar_position_title">Ubah posisi bilah aksi</string>
<string name="revanced_change_action_bar_position_summary">Pindahkan bilah aksi di bawah tombol putar.</string>
<string name="revanced_hide_action_button_like_dislike_title">Sembunyikan tombol Like dan Dislike</string>
<string name="revanced_hide_action_button_like_dislike_summary">Menyembunyikan tombol Like dan Dislike. Itu tidak akan bekerja di layout player lama.</string>
<string name="revanced_change_action_bar_position_title">Ubah posisi bilah tindakan</string>
<string name="revanced_change_action_bar_position_summary">Pindahkan bilah tindakan di bawah tombol putar.</string>
<string name="revanced_hide_action_button_like_dislike_title">Sembunyikan tombol Suka dan Tidak suka</string>
<string name="revanced_hide_action_button_like_dislike_summary">Menyembunyikan tombol Suka dan Tidak suka. Itu tidak akan bekerja di tata letak pemutar lama.</string>
<string name="revanced_hide_action_button_comment_title">Sembunyikan tombol Komentar</string>
<string name="revanced_hide_action_button_comment_summary">Menyembunyikan tombol Komentar.</string>
<string name="revanced_hide_action_button_add_to_playlist_title">Sembunyikan tombol Save</string>
<string name="revanced_hide_action_button_add_to_playlist_title">Sembunyikan tombol Simpan</string>
<string name="revanced_hide_action_button_add_to_playlist_summary">Menyembunyikan tombol Simpan.</string>
<string name="revanced_hide_action_button_download_title">Sembunyikan tombol Unduh</string>
<string name="revanced_hide_action_button_download_summary">Menyembunyikan tombol Unduh.</string>
@ -37,27 +37,31 @@
<string name="revanced_hide_action_button_song_video_title">Sembunyikan tombol Lagu / Video</string>
<string name="revanced_hide_action_button_song_video_summary">"Sembunyikan tombol Lagu / Video.
(Tombol ini hanya tersedia untuk sebagian pengguna)"</string>
<string name="revanced_hide_action_button_label_title">Sembunyikan tombol bilah tindakan</string>
<string name="revanced_hide_action_button_label_summary">Menyembunyikan bilah dari tombol tindakan.</string>
<string name="revanced_hide_action_button_label_title">Sembunyikan label tombol tindakan</string>
<string name="revanced_hide_action_button_label_summary">Menyembunyikan label tombol tindakan.</string>
<string name="revanced_external_downloader_action_title">Ganti tombol tindakan Unduh</string>
<string name="revanced_external_downloader_action_summary">"Tombol Unduh membuka Downloader eksternal kamu.
<string name="revanced_external_downloader_action_summary">"Tombol Unduh membuka pengunduh eksternal Anda
• Hanya menggantikan tombol Unduh di player.
• Tidak bisa menggantikan tombol Unduh di menu flyout atau tab Library."</string>
<string name="revanced_external_downloader_package_name_title">Nama paket downloader eksternal</string>
<string name="revanced_external_downloader_package_name_summary">Nama paket aplikasi downloader eksternal yang terinstal, seperti NewPipe atau YTDLnis.</string>
<string name="revanced_external_downloader_dialog_title">Downloader eksternal</string>
• Hanya mengganti tombol tindakan Unduh di pemutar.
• Tidak mengganti tombol Unduh di menu flyout atau tab Pustaka."</string>
<string name="revanced_external_downloader_package_name_title">Nama paket pengunduh eksternal</string>
<string name="revanced_external_downloader_package_name_summary">Nama paket aplikasi pengunduh eksternal yang terpasang, seperti NewPipe atau YTDLnis.</string>
<string name="revanced_external_downloader_dialog_title">Pengunduh eksternal</string>
<string name="revanced_external_downloader_not_installed_dialog_title">Peringatan</string>
<string name="revanced_external_downloader_not_installed_dialog_message">"%1$s belum terinstall.
Download %2$s dari website."</string>
<string name="revanced_external_downloader_not_installed_warning">%s tidak diinstal. Silakan instal.</string>
<string name="revanced_external_downloader_not_installed_dialog_message">"%1$s belum terpasang.
Harap unduh %2$s dari website."</string>
<string name="revanced_external_downloader_not_installed_warning">%s tidak dipasang. Silakan pasang terlebih dahulu.</string>
<!-- PreferenceScreen: Ads -->
<string name="revanced_preference_screen_ads_title">Iklan</string>
<string name="revanced_hide_fullscreen_ads_title">Sembunyikan iklan fullscreen</string>
<string name="revanced_hide_fullscreen_ads_title">Sembunyikan iklan layar penuh</string>
<string name="revanced_hide_fullscreen_ads_summary">"Menyembunyikan iklan layar penuh.
Keterbatasan:
• Terkadang Anda mungkin melihat layar hitam kosong, bukan feed beranda."</string>
<string name="revanced_fullscreen_ads_closed_toast">Iklan layar penuh ditutup.</string>
<string name="revanced_hide_general_ads_title">Sembunyikan Iklan Umum</string>
<string name="revanced_hide_general_ads_summary">Menyembunyikan Iklan Umum.</string>
<string name="revanced_hide_music_ads_title">Sembunyikan iklan musik</string>
<string name="revanced_hide_music_ads_title">Sembunyikan iklan media</string>
<string name="revanced_hide_music_ads_summary">Menyembunyikan iklan sebelum memutar musik.</string>
<string name="revanced_hide_paid_promotion_label_title">Sembunyikan label promosi berbayar</string>
<string name="revanced_hide_paid_promotion_label_summary">Menyembunyikan label promosi berbayar.</string>
@ -67,289 +71,411 @@ Download %2$s dari website."</string>
<string name="revanced_hide_premium_renewal_title">Sembunyikan banner pembaruan premium</string>
<string name="revanced_hide_premium_renewal_summary">Menyembunyikan banner pembaruan premium.</string>
<string name="revanced_hide_promotion_alert_banner_title">Sembunyikan banner peringatan promosi</string>
<string name="revanced_hide_promotion_alert_banner_summary">Sembunyikan banner peringatan promosi.</string>
<!-- PreferenceScreen: Flyout menu -->
<string name="revanced_preference_screen_flyout_title">Menu flyout</string>
<string name="revanced_enable_trim_silence_title">Tambah switch Trim silence</string>
<string name="revanced_enable_trim_silence_summary">"Menambahkan tombol Trim silence ke menu flyout playback speed.
<string name="revanced_enable_trim_silence_title">Tambah tombol Pangkas keheningan</string>
<string name="revanced_enable_trim_silence_summary">"Menambahkan tombol Pangkas keheningan ke menu flyout kecepatan pemutaran.
Info:
• Fitur ini hanya untuk podcast.
• Fitur ini masih dalam pengembangan, jadi ini tidak akan stabil."</string>
• Fitur ini masih dalam pengembangan, jadi mungkin tidak stabil."</string>
<string name="revanced_enable_compact_dialog_title">Aktifkan dialog ringkas</string>
<string name="revanced_enable_compact_dialog_summary">"Aktifkan dialog ringkas di ponsel.
<string name="revanced_enable_compact_dialog_summary">"Mengaktifkan dialog ringkas di ponsel.
Masalah yang diketahui:
• Gambar album di Tab library juga menjadi lebih kecil.
Keterbatasan:
• Gambar album di tab Pustaka juga menjadi lebih kecil ketika diatur dalam model kotak.
• Tata letak pengatur waktu tidur mungkin terlihat tidak biasa."</string>
<string name="revanced_hide_flyout_menu_like_dislike_title">Sembunyikan tombol Like dan Dislike</string>
<string name="revanced_hide_flyout_menu_like_dislike_title">Sembunyikan tombol Suka dan Tidak suka</string>
<string name="revanced_hide_flyout_menu_3_column_component_title">Sembunyikan komponen 3-kolom</string>
<string name="revanced_hide_flyout_menu_add_to_queue_title">Sembunyikan menu tambahkan ke antrean</string>
<string name="revanced_hide_flyout_menu_captions_title">Sembunyikan menu teks</string>
<string name="revanced_hide_flyout_menu_delete_playlist_title">Sembunyikan menu hapus playlist</string>
<string name="revanced_hide_flyout_menu_dismiss_queue_title">Sembunyikan menu abaikan antrean</string>
<string name="revanced_hide_flyout_menu_captions_title">Sembunyikan menu Teks</string>
<string name="revanced_hide_flyout_menu_delete_playlist_title">Sembunyikan menu Hapus playlist</string>
<string name="revanced_hide_flyout_menu_dismiss_queue_title">Sembunyikan menu Abaikan antrean</string>
<string name="revanced_hide_flyout_menu_download_title">Sembunyikan menu Unduh</string>
<string name="revanced_hide_flyout_menu_edit_playlist_title">Sembunyikan menu edit playlist</string>
<string name="revanced_hide_flyout_menu_edit_playlist_title">Sembunyikan menu Edit playlist</string>
<string name="revanced_hide_flyout_menu_go_to_album_title">Sembunyikan menu Pergi ke album</string>
<string name="revanced_hide_flyout_menu_go_to_artist_title">Sembunyikan menu Pergi ke artis</string>
<string name="revanced_hide_flyout_menu_go_to_episode_title">Sembunyikan menu Pergi ke episode</string>
<string name="revanced_hide_flyout_menu_go_to_podcast_title">Sembunyikan menu Pergi ke podcast</string>
<string name="revanced_hide_flyout_menu_help_title">Sembunyikan menu bantuan &amp; saran</string>
<string name="revanced_hide_flyout_menu_play_next_title">Sembunyikan menu putar berikutnya</string>
<string name="revanced_hide_flyout_menu_quality_title">Hide menu Kualitas</string>
<string name="revanced_hide_flyout_menu_remove_from_library_title">Sembunyikan menu hapus dari koleksi</string>
<string name="revanced_hide_flyout_menu_remove_from_playlist_title">Sembunyikan hapus dari menu playlist</string>
<string name="revanced_hide_flyout_menu_report_title">Sembunyikan menu laporkan</string>
<string name="revanced_hide_flyout_menu_save_episode_for_later_title">Sembunyikan menu simpan episode untuk ditonton nanti</string>
<string name="revanced_hide_flyout_menu_save_to_library_title">Sembunyikan menu simpan ke koleksi</string>
<string name="revanced_hide_flyout_menu_save_to_playlist_title">Sembunyikan menu simpan ke playlist</string>
<string name="revanced_hide_flyout_menu_share_title">Sembunyikan menu bagikan</string>
<string name="revanced_hide_flyout_menu_shuffle_play_title">Sembunyikan menu putar acak</string>
<string name="revanced_hide_flyout_menu_sleep_timer_title">Sembunyikan menu waktu tidur</string>
<string name="revanced_hide_flyout_menu_start_radio_title">Sembunyikan menu mulai radio</string>
<string name="revanced_hide_flyout_menu_stats_for_nerds_title">Sembunyikan menu statistik untuk nerds</string>
<string name="revanced_hide_flyout_menu_subscribe_title">Sembunyikan menu Subscribe / Unsubscribe</string>
<string name="revanced_hide_flyout_menu_view_song_credit_title">Sembunyikan menu kredit lagu</string>
<string name="revanced_hide_flyout_menu_help_title">Sembunyikan menu Bantuan &amp; saran</string>
<string name="revanced_hide_flyout_menu_not_interested_title">Sembunyikan menu Tidak tertarik</string>
<string name="revanced_hide_flyout_menu_pin_to_speed_dial_title">Sembunyikan Pin ke menu Panggil cepat</string>
<string name="revanced_hide_flyout_menu_play_next_title">Sembunyikan menu Putar berikutnya</string>
<string name="revanced_hide_flyout_menu_quality_title">Sembunyikan menu Kualitas</string>
<string name="revanced_hide_flyout_menu_remove_from_library_title">Sembunyikan menu Hapus dari koleksi</string>
<string name="revanced_hide_flyout_menu_remove_from_playlist_title">Sembunyikan menu Hapus dari playlist</string>
<string name="revanced_hide_flyout_menu_report_title">Sembunyikan menu Laporkan</string>
<string name="revanced_hide_flyout_menu_save_episode_for_later_title">Sembunyikan menu Simpan episode untuk nanti</string>
<string name="revanced_hide_flyout_menu_save_to_library_title">Sembunyikan menu Simpan ke pustaka</string>
<string name="revanced_hide_flyout_menu_save_to_playlist_title">Sembunyikan menu Simpan ke playlist</string>
<string name="revanced_hide_flyout_menu_share_title">Sembunyikan menu Bagikan</string>
<string name="revanced_hide_flyout_menu_shuffle_play_title">Sembunyikan menu Putar acak</string>
<string name="revanced_hide_flyout_menu_sleep_timer_title">Sembunyikan menu Waktu tidur</string>
<string name="revanced_hide_flyout_menu_start_radio_title">Sembunyikan menu Mulai radio</string>
<string name="revanced_hide_flyout_menu_stats_for_nerds_title">Sembunyikan menu Statistik untuk nerds</string>
<string name="revanced_hide_flyout_menu_subscribe_title">Sembunyikan menu Berlangganan / Berhenti Berlangganan</string>
<string name="revanced_hide_flyout_menu_unpin_from_speed_dial_title">Sembunyikan Buka pin dari menu panggilan cepat</string>
<string name="revanced_hide_flyout_menu_view_song_credit_title">Sembunyikan menu Lihat kredit lagu</string>
<string name="revanced_replace_flyout_menu_dismiss_queue_continue_watch_title">Lanjutkan menonton</string>
<string name="revanced_replace_flyout_menu_dismiss_queue_continue_watch_summary">Melanjutkan video dari waktu saat ini ketika berlaih ke YouTube.</string>
<string name="revanced_replace_flyout_menu_dismiss_queue_continue_watch_summary">Melanjutkan video dari waktu saat ini ketika beralih ke YouTube.</string>
<string name="revanced_replace_flyout_menu_dismiss_queue_watch_on_youtube_label">Tonton di YouTube</string>
<string name="revanced_replace_flyout_menu_dismiss_queue_watch_on_youtube_warning">Url video tidak valid.</string>
<string name="revanced_replace_flyout_menu_dismiss_queue_title">Ganti menu hapus antrean</string>
<string name="revanced_replace_flyout_menu_dismiss_queue_summary">Menggantikan menu hapus antrean menjadi tonton di YouTube.</string>
<string name="revanced_replace_flyout_menu_report_title">Ganti menu laporkan</string>
<string name="revanced_replace_flyout_menu_report_summary">Menggantikan menu laporkan dengan menu Kecepatan pemutaran.</string>
<string name="revanced_replace_flyout_menu_report_only_player_title">Simpan laporkan di komentar</string>
<string name="revanced_replace_flyout_menu_report_only_player_summary">Mempertahankan menu laporkan di bagian komentar.</string>
<string name="revanced_replace_flyout_menu_dismiss_queue_watch_on_youtube_warning">Url video tidak sah.</string>
<string name="revanced_replace_flyout_menu_dismiss_queue_title">Ganti menu Hapus antrean</string>
<string name="revanced_replace_flyout_menu_dismiss_queue_summary">Menggantikan menu Hapus antrean dengan menu Tonton di YouTube.</string>
<string name="revanced_replace_flyout_menu_report_title">Ganti menu Laporkan</string>
<string name="revanced_replace_flyout_menu_report_summary">Menggantikan menu Laporkan dengan menu Kecepatan pemutaran.</string>
<string name="revanced_replace_flyout_menu_report_only_player_title">Simpan Laporkan di komentar</string>
<string name="revanced_replace_flyout_menu_report_only_player_summary">Menyimpan menu Laporkan di bagian komentar tetap utuh.</string>
<!-- PreferenceScreen: General -->
<string name="revanced_preference_screen_general_title">Umum</string>
<string name="revanced_change_start_page_title">Ganti Halaman Awal</string>
<string name="revanced_change_start_page_summary">Select which page the app opens in.</string>
<string name="revanced_change_start_page_summary">Pilih di halaman mana aplikasi dibuka.</string>
<string name="revanced_change_start_page_entry_default">Bawaan</string>
<string name="revanced_change_start_page_entry_charts">Tangga lagu</string>
<string name="revanced_change_start_page_entry_episodes_for_later">Episode untuk Nanti</string>
<string name="revanced_change_start_page_entry_explore">Jelajahi</string>
<string name="revanced_change_start_page_entry_library">Koleksi</string>
<string name="revanced_disable_dislike_redirection_title">Disable dislike redirection</string>
<string name="revanced_disable_dislike_redirection_summary">Disables redirection to the next track when clicking the Dislike button.</string>
<string name="revanced_change_start_page_entry_history">Riwayat</string>
<string name="revanced_change_start_page_entry_library">Pustaka</string>
<string name="revanced_change_start_page_entry_liked_music">Musik yang Disukai</string>
<string name="revanced_change_start_page_entry_podcasts">Podcast</string>
<string name="revanced_change_start_page_entry_samples">Sampel</string>
<string name="revanced_change_start_page_entry_search">Cari</string>
<string name="revanced_change_start_page_entry_subscriptions">Berlangganan</string>
<string name="revanced_disable_dislike_redirection_title">Nonaktifkan pengalihan tidak suka</string>
<string name="revanced_disable_dislike_redirection_summary">Menonaktifkan pengalihan ke trek berikutnya ketika mengklik tombol Tidak Suka.</string>
<string name="revanced_disable_auto_captions_title">Nonaktifkan teks otomatis paksa</string>
<string name="revanced_disable_auto_captions_summary">Teks otomatis paksa yang dinonaktifkan.</string>
<string name="revanced_disable_auto_captions_summary">Menonaktifkan teks otomatis agar tidak aktif.</string>
<string name="revanced_enable_landscape_mode_title">Aktifkan mode lanskap</string>
<string name="revanced_enable_landscape_mode_summary">Mengaktifkan masuk ke mode lanskap dengan rotasi layar di ponsel.</string>
<string name="revanced_custom_filter_title">Aktifkan filter kustom</string>
<string name="revanced_enable_landscape_mode_summary">Mengaktifkan mode lanskap saat memutar layar ponsel.</string>
<string name="revanced_custom_filter_title">Aktifkan filter khusus</string>
<string name="revanced_custom_filter_summary">Mengaktifkan filter kustom untuk menyembunyikan komponen tata letak.</string>
<string name="revanced_custom_filter_strings_title">Edit filter kustom</string>
<string name="revanced_custom_filter_strings_title">Edit filter khusus</string>
<!-- 'Component path builder strings' is the technical name for identifying the Litho UI layout items to hide. This is an advanced feature and most users will never use this. -->
<string name="revanced_custom_filter_strings_summary">Memfilter nama komponen dengan baris yang dipisahkan.</string>
<string name="revanced_custom_filter_toast_invalid_syntax">Invalid custom filter: %s.</string>
<string name="revanced_custom_filter_strings_summary">Daftar string pembangun jalur komponen yang akan disaring, dipisahkan dengan baris baru.</string>
<string name="revanced_custom_filter_toast_invalid_syntax">Filter khusus tidak sah: %s.</string>
<string name="revanced_hide_button_shelf_title">Sembunyikan rak tombol</string>
<string name="revanced_hide_button_shelf_summary">Menyembunyikan rak tombol dari beranda dan eksplorasi.</string>
<string name="revanced_hide_carousel_shelf_title">Sembunyikan rak korsel</string>
<string name="revanced_hide_carousel_shelf_summary">Menyembunyikan rak korsel dari beranda dan eksplorasi.</string>
<string name="revanced_hide_cast_button_title">Sembunyikan tombol cast</string>
<string name="revanced_hide_cast_button_summary">Menyembunyikan tombol cast.</string>
<string name="revanced_hide_button_shelf_summary">Menyembunyikan rak tombol di feed.</string>
<string name="revanced_hide_carousel_shelf_title">Menyembunyikan rak carousel</string>
<string name="revanced_hide_carousel_shelf_summary">Menyembunyikan rak carousel di feed.</string>
<string name="revanced_hide_cast_button_title">Sembunyikan tombol Cast</string>
<string name="revanced_hide_cast_button_summary">Menyembunyikan tombol Cast.</string>
<string name="revanced_hide_category_bar_title">Sembunyikan bilah kategori</string>
<string name="revanced_hide_category_bar_summary">Menyembunyikan bilah kategori musik di bagian atas beranda.</string>
<string name="revanced_hide_floating_button_title">Hide floating button</string>
<string name="revanced_hide_floating_button_summary">Hides the floating button in the Library tab.</string>
<string name="revanced_hide_history_button_title">Sembunyikan tombol riwayat</string>
<string name="revanced_hide_history_button_summary">Menyembunyikan tombol riwayat di toolbar.</string>
<string name="revanced_hide_notification_button_title">Hide Notifications button</string>
<string name="revanced_hide_notification_button_summary">Hides the Notifications button in the toolbar.</string>
<string name="revanced_hide_playlist_card_shelf_title">Hide playlist card shelf</string>
<string name="revanced_hide_playlist_card_shelf_summary">Hides the playlist card shelf in the feed.</string>
<string name="revanced_hide_samples_shelf_title">Hide Samples shelf</string>
<string name="revanced_hide_samples_shelf_summary">Hides the Samples shelf in the feed.</string>
<string name="revanced_hide_sound_search_button_title">Hide sound search button</string>
<string name="revanced_hide_sound_search_button_summary">Hides the sound search button in the search bar.</string>
<string name="revanced_hide_tap_to_update_button_title">Hide \'Tap to update\' button</string>
<string name="revanced_hide_tap_to_update_button_summary">Hides the \'Tap to update\' button.</string>
<string name="revanced_hide_voice_search_button_title">Hide voice search button</string>
<string name="revanced_hide_voice_search_button_summary">Hides the voice search button in the search bar.</string>
<string name="revanced_restore_old_style_library_shelf_title">Restore old style library shelf</string>
<string name="revanced_restore_old_style_library_shelf_summary">Returns the Library tab to the old style. (Experimental)</string>
<string name="revanced_remove_viewer_discretion_dialog_title">Remove viewer discretion dialog</string>
<string name="revanced_remove_viewer_discretion_dialog_summary">"Removes the viewer discretion dialog.
This does not bypass the age restriction. It just accepts it automatically."</string>
<string name="revanced_hide_category_bar_summary">Menyembunyikan bilah kategori.</string>
<string name="revanced_hide_floating_button_title">Sembunyikan tombol mengambang</string>
<string name="revanced_hide_floating_button_summary">Menyembunyikan tombol mengambang di tab Pustaka.</string>
<string name="revanced_hide_history_button_title">Sembunyikan tombol Riwayat</string>
<string name="revanced_hide_history_button_summary">Menyembunyikan Rombol riwayat di toolbar.</string>
<string name="revanced_hide_notification_button_title">Sembunyikan tombol Pemberitahuan</string>
<string name="revanced_hide_notification_button_summary">Menyembunyikan tombol Pemberitahuan di toolbar.</string>
<string name="revanced_hide_playlist_card_shelf_title">Sembunyikan rak kartu playlist</string>
<string name="revanced_hide_playlist_card_shelf_summary">Menyembunyikan rak kartu playlist di feed.</string>
<string name="revanced_hide_samples_shelf_title">Sembunyikan rak Sampel</string>
<string name="revanced_hide_samples_shelf_summary">Menyembunyikan rak Sampel di feed.</string>
<string name="revanced_hide_sound_search_button_title">Sembunyikan tombol pencarian suara</string>
<string name="revanced_hide_sound_search_button_summary">Menyembunyikan tombol pencarian suara di bilah pencarian.</string>
<string name="revanced_hide_tap_to_update_button_title">Sembunyikan tombol Ketuk untuk memperbarui</string>
<string name="revanced_hide_tap_to_update_button_summary">Menyembunyikan tombol Ketuk untuk memperbarui.</string>
<string name="revanced_hide_voice_search_button_title">Sembunyikan tombol pencarian suara</string>
<string name="revanced_hide_voice_search_button_summary">Menyembunyikan tombol pencarian suara di bilah pencarian.</string>
<string name="revanced_restore_old_style_library_shelf_title">Pulihkan tab Pustaka lama</string>
<string name="revanced_restore_old_style_library_shelf_summary">Memulihkan tab Pustaka ke gaya lama. (Eksperimental)</string>
<string name="revanced_remove_viewer_discretion_dialog_title">Hapus dialog pembatasan penonton</string>
<string name="revanced_remove_viewer_discretion_dialog_summary">"Menghapus dialog pembatasan penonton.
Ini tidak mengabaikan pembatasan usia. Ini hanya menerimanya secara otomatis."</string>
<string name="revanced_spoof_app_version_title">Palsukan versi aplikasi</string>
<string name="revanced_spoof_app_version_summary">"Memalsukan versi klien ke versi lama
<string name="revanced_spoof_app_version_summary">"Memalsukan versi klien ke versi lama.
• Ini akan mengubah tampilan aplikasi, namun efek samping yang tidak diketahui mungkin terjadi.
• Jika nanti dinonaktifkan, UI lama mungkin tetap ada hingga aplikasi dihapus data."</string>
• Jika nanti dinonaktifkan, UI lama mungkin tetap ada hingga data aplikasi dihapus."</string>
<string name="revanced_spoof_app_version_target_title">Target pemalsuan versi aplikasi</string>
<string name="revanced_spoof_app_version_target_summary">Pilih target pemalsuan versi aplikasi.</string>
<string name="revanced_spoof_app_version_target_entry_6_42_55"></string>
<string name="revanced_spoof_app_version_target_entry_6_42_55">6.42.55 - Nonaktifkan lirik real-time</string>
<string name="revanced_spoof_app_version_target_entry_7_16_53">7.16.53 - Pulihkan bilah tindakan lama</string>
<!-- PreferenceScreen: Navigation bar -->
<string name="revanced_preference_screen_navigation_title">Bilah Navigasi</string>
<string name="revanced_hide_navigation_home_button_title">Hide Home button</string>
<string name="revanced_hide_navigation_home_button_summary">Hides the Home button.</string>
<string name="revanced_hide_navigation_samples_button_title">Hide Samples button</string>
<string name="revanced_hide_navigation_samples_button_summary">Hides the Samples button.</string>
<string name="revanced_hide_navigation_explore_button_title">Hide Explore button</string>
<string name="revanced_hide_navigation_explore_button_summary">Hides the Explore button.</string>
<string name="revanced_hide_navigation_library_button_title">Hide Library button</string>
<string name="revanced_hide_navigation_library_button_summary">Hides the Library button.</string>
<string name="revanced_hide_navigation_upgrade_button_title">Hide Upgrade button</string>
<string name="revanced_hide_navigation_upgrade_button_summary">Hides the Upgrade button.</string>
<string name="revanced_hide_navigation_bar_title">Menyembunyikan bilah navigasi</string>
<string name="revanced_hide_navigation_bar_summary">Sembunyikan bilah navigasi.</string>
<string name="revanced_enable_custom_navigation_bar_color_title">Aktifkan warna bilah navigasi khusus</string>
<string name="revanced_enable_custom_navigation_bar_color_summary">Atur warna bilah navigasi.</string>
<string name="revanced_custom_navigation_bar_color_value_title">Nilai warna bilah navigasi khusus</string>
<string name="revanced_custom_navigation_bar_color_value_summary">Masukkan kode hex dari warna bilah navigasi.</string>
<string name="revanced_custom_navigation_bar_color_value_invalid_invalid_toast">Nilai warna bilah navigasi tidak sah.</string>
<string name="revanced_hide_navigation_home_button_title">Sembunyikan tombol Beranda</string>
<string name="revanced_hide_navigation_home_button_summary">Menyembunyikan tombol Beranda.</string>
<string name="revanced_hide_navigation_samples_button_title">Sembunyikan tombol Sampel</string>
<string name="revanced_hide_navigation_samples_button_summary">Menyembunyikan tombol Sampel.</string>
<string name="revanced_hide_navigation_explore_button_title">Sembunyikan tombol Jelajahi</string>
<string name="revanced_hide_navigation_explore_button_summary">Menyembunyikan tombol Jelajahi.</string>
<string name="revanced_hide_navigation_library_button_title">Sembunyikan tombol Pustaka</string>
<string name="revanced_hide_navigation_library_button_summary">Menyembunyikan tombol Pustaka.</string>
<string name="revanced_hide_navigation_upgrade_button_title">Sembunyikan tombol Upgrade</string>
<string name="revanced_hide_navigation_upgrade_button_summary">Menyembunyikan tombol Upgrade.</string>
<string name="revanced_hide_navigation_bar_title">Sembunyikan bilah navigasi</string>
<string name="revanced_hide_navigation_bar_summary">Menyembunyikan bilah navigasi.</string>
<string name="revanced_hide_navigation_label_title">Sembunyikan label navigasi</string>
<string name="revanced_hide_navigation_label_summary">Menyembunyikan label di bilah navigasi.</string>
<string name="revanced_hide_navigation_label_summary">Menyembunyikan label di bawah setiap tombol navigasi.</string>
<!-- PreferenceScreen: Player -->
<string name="revanced_preference_screen_player_title">Player</string>
<string name="revanced_disable_player_gesture_title">Menonaktifkan gerakan pemutar</string>
<string name="revanced_disable_player_gesture_summary">Nonaktifkan usap untuk mengubah trek di pemutar.</string>
<string name="revanced_enable_zen_mode_title">Aktifkan mode zen</string>
<string name="revanced_enable_zen_mode_summary">Menambahkan rona abu-abu ke pemutar video untuk mengurangi ketegangan mata.</string>
<string name="revanced_enable_zen_mode_podcast_title">Enable Zen mode in podcasts</string>
<string name="revanced_enable_zen_mode_podcast_summary">Also enables Zen mode for podcasts.</string>
<string name="revanced_hide_comment_channel_guidelines_title">Hide channel guidelines</string>
<string name="revanced_hide_comment_channel_guidelines_summary">Hides the channel guidelines at the top of the comments section.</string>
<string name="revanced_hide_double_tap_overlay_filter_title">Sembunyikan filter overlay double-tap</string>
<string name="revanced_hide_double_tap_overlay_filter_summary">Menyembunyikan overlay gelap yang muncul ketika double-tap to seek.</string>
<string name="revanced_hide_comment_timestamp_and_emoji_buttons_title">Hide timestamp and emoji buttons</string>
<string name="revanced_preference_screen_player_title">Pemutar</string>
<string name="revanced_add_miniplayer_next_button_title">Menambahkan tombol berikutnya pemutar mini</string>
<string name="revanced_add_miniplayer_next_button_summary">Menambahkan tombol trek berikutnya ke pemutar mini.</string>
<string name="revanced_add_miniplayer_previous_button_title">Tambahkan tombol sebelumnya miniplayer</string>
<string name="revanced_add_miniplayer_previous_button_summary">Menambahkan tombol trek sebelumnya ke pemutar mini.</string>
<string name="revanced_change_miniplayer_color_title">Ubah warna pemutar mini</string>
<string name="revanced_change_miniplayer_color_summary">Mengubah warna pemutar mini supaya sesuai dengan pemutar layar penuh.</string>
<string name="revanced_change_player_background_color_title">Ubah warna latar belakang pemutar</string>
<string name="revanced_change_player_background_color_summary">Mengubah warna latar belakang pemutar ke warna khusus.</string>
<string name="revanced_custom_player_background_color_primary_title">Warna primer latar belakang pemutar</string>
<string name="revanced_custom_player_background_color_primary_summary">"Masukkan kode hex dari warna primer latar belakang pemutar.
Gunakan warna gelap jika memungkinkan, karena aplikasi ini tidak mendukung tema terang."</string>
<string name="revanced_custom_player_background_color_secondary_title">Warna sekunder latar belakang pemutar</string>
<string name="revanced_custom_player_background_color_secondary_summary">"Masukkan kode hex warna sekunder latar belakang pemutar.
Gunakan warna gelap jika memungkinkan, karena aplikasi ini tidak mendukung tema terang."</string>
<string name="revanced_custom_player_background_invalid_toast">Warna latar belakang pemutar tidak sah.</string>
<string name="revanced_change_seekbar_position_title">Ubah posisi seekbar</string>
<string name="revanced_change_seekbar_position_summary">Memindahkan seekbar di bawah tombol putar.</string>
<string name="revanced_disable_miniplayer_gesture_title">Nonaktifkan gerakan pemutar mini</string>
<string name="revanced_disable_miniplayer_gesture_summary">Menonaktifkan usapan untuk mengubah trek di pemutar mini.</string>
<string name="revanced_disable_player_gesture_title">Nonaktifkan gerakan pemutar</string>
<string name="revanced_disable_player_gesture_summary">Menonaktifkan usap untuk mengubah trek di pemutar.</string>
<string name="revanced_enable_forced_miniplayer_title">Aktifkan pemutar mini paksa</string>
<string name="revanced_enable_forced_miniplayer_summary">Mengaktifkan pemutar mini paksa saat beralih ke trek baru.</string>
<string name="revanced_enable_swipe_to_dismiss_miniplayer_title">Aktifkan usapan untuk menutup pemutar mini</string>
<string name="revanced_enable_swipe_to_dismiss_miniplayer_summary">Mengaktifkan gesek ke bawah untuk menutup pemutar mini.</string>
<string name="revanced_enable_thick_seekbar_title">Aktifkan seekbar tebal</string>
<string name="revanced_enable_thick_seekbar_summary">"Mengaktifkan seekbar yang tebal.
Keterbatasan: Segmen SponsorBlock tidak ditampilkan di seekbar."</string>
<string name="revanced_enable_zen_mode_title">Aktifkan mode Zen</string>
<string name="revanced_enable_zen_mode_summary">Mengaktifkan warna abu-abu muda untuk latar belakang pemutar untuk mengurangi kelelahan mata.</string>
<string name="revanced_enable_zen_mode_podcast_title">Aktifkan mode Zen di podcast</string>
<string name="revanced_enable_zen_mode_podcast_summary">Mengaktifkan mode Zen di podcast.</string>
<string name="revanced_hide_comment_channel_guidelines_title">Sembunyikan pedoman saluran</string>
<string name="revanced_hide_comment_channel_guidelines_summary">Menyembunyikan panduan saluran di bagian atas komentar.</string>
<string name="revanced_hide_double_tap_overlay_filter_title">Sembunyikan penyaring hamparan ketuk dua kali</string>
<string name="revanced_hide_double_tap_overlay_filter_summary">Menyembunyikan hamparan gelap yang muncul ketika mengetuk dua kali untuk menggeser.</string>
<string name="revanced_hide_comment_timestamp_and_emoji_buttons_title">Sembunyikan tombol emoji dan stempel waktu</string>
<string name="revanced_hide_comment_timestamp_and_emoji_buttons_summary">Hides the timestamp and emoji buttons when typing comments.</string>
<string name="revanced_hide_fullscreen_share_button_title">Hide fullscreen Share button</string>
<string name="revanced_hide_fullscreen_share_button_summary">Hides the Share button in the fullscreen player.</string>
<string name="revanced_hide_fullscreen_share_button_title">Sembunyikan tombol Bagikan layar penuh</string>
<string name="revanced_hide_fullscreen_share_button_summary">Menyembunyikan tombol Bagikan di pemutar layar penuh.</string>
<string name="revanced_hide_song_video_toggle_title">Sembunyikan tombol Lagu / Video</string>
<string name="revanced_hide_song_video_toggle_summary">Menyembunyikan tombol Lagu / Video di pemutar.</string>
<string name="revanced_remember_repeat_state_title">Ingat keadaan pengulangan</string>
<string name="revanced_remember_repeat_state_summary">Mengingat keadaan pengulangan.</string>
<string name="revanced_remember_repeat_state_summary">Mengingat keadaan tombol pengulangan.</string>
<string name="revanced_remember_shuffle_state_title">Ingat keadaan pengacakan</string>
<string name="revanced_remember_shuffle_state_summary">Mengingat keadaan pengacakan.</string>
<string name="revanced_restore_old_comments_popup_panels_title">Restore old comments popup panels</string>
<string name="revanced_restore_old_comments_popup_panels_summary">Returns the comments popup panels to the old style.</string>
<string name="revanced_restore_old_player_background_title">Restore old player background</string>
<string name="revanced_restore_old_player_background_summary">Returns the player background to the old style.</string>
<string name="revanced_restore_old_player_layout_title">Restore old player layout</string>
<string name="revanced_restore_old_player_layout_summary">"Returns the player layout to the old style.
Some features may not work properly in the old player layout."</string>
<string name="revanced_remember_shuffle_state_summary">Mengingat status tombol pengacakan.</string>
<string name="revanced_restore_old_comments_popup_panels_title">Pulihkan panel sembulan komentar lama</string>
<string name="revanced_restore_old_comments_popup_panels_summary">Memulihkan panel sembulan komentar ke gaya lama.</string>
<string name="revanced_restore_old_player_background_title">Pulihkan latar belakang pemutar lama</string>
<string name="revanced_restore_old_player_background_summary">Mengembalikan latar belakang pemutar ke gaya lama.</string>
<string name="revanced_restore_old_player_layout_title">Pulihkan tata letak pemutar lama</string>
<string name="revanced_restore_old_player_layout_summary">"Memulihkan tata letak pemutar ke gaya lama.
Beberapa fitur mungkin tidak berfungsi dengan baik dalam tata letak pemutar lama."</string>
<!-- PreferenceScreen: Settings menu -->
<string name="revanced_preference_screen_settings_title">Menu pengaturan</string>
<string name="revanced_hide_settings_menu_parent_tools_title">Sembunyikan menu Pusat Keluarga</string>
<string name="revanced_hide_settings_menu_general_title">Sembunyikan menu Umum</string>
<string name="revanced_hide_settings_menu_playback_title">Sembunyikan menu Pemutaran</string>
<string name="revanced_hide_settings_menu_data_saving_title">Sembunyikan menu penghematan Data</string>
<string name="revanced_hide_settings_menu_downloads_and_storage_title">Sembunyikan menu Unduhan &amp; penyimpanan</string>
<string name="revanced_hide_settings_menu_notification_title">Sembunyikan menu Pemberitahuan</string>
<string name="revanced_hide_settings_menu_privacy_and_location_title">Sembunyikan menu Privasi &amp; data</string>
<string name="revanced_hide_settings_menu_recommendations_title">Sembunyikan menu Rekomendasi</string>
<string name="revanced_hide_settings_menu_paid_memberships_title">Sembunyikan menu Dapatkan Music premium</string>
<string name="revanced_hide_settings_menu_about_title">Sembunyikan menu Tentang</string>
<!-- PreferenceScreen: Video -->
<string name="revanced_preference_screen_video_title">Video</string>
<string name="revanced_custom_playback_speeds_title">Edit kecepatan pemutaran kustom</string>
<string name="revanced_custom_playback_speeds_title">Edit kecepatan pemutaran khusus</string>
<string name="revanced_custom_playback_speeds_summary">Menambah atau mengubah kecepatan pemutaran yang tersedia.</string>
<string name="revanced_remember_playback_speed_last_selected_title">Remember playback speed changes</string>
<string name="revanced_remember_playback_speed_last_selected_title">Ingat perubahan kecepatan pemutaran</string>
<string name="revanced_remember_playback_speed_last_selected_summary">Remembers the last playback speed selected.</string>
<string name="revanced_remember_playback_speed_last_selected_toast_title">Tampilkan toast</string>
<string name="revanced_remember_playback_speed_last_selected_toast_summary">Menunjukkan toast ketika mengubah playback speed semula.</string>
<string name="revanced_remember_video_quality_last_selected_title">Remember video quality changes</string>
<string name="revanced_remember_video_quality_last_selected_summary">Remembers the last video quality selected.</string>
<string name="revanced_remember_playback_speed_last_selected_toast_summary">Menampilkan toast saat mengubah kecepatan pemutaran bawaan.</string>
<string name="revanced_remember_video_quality_last_selected_title">Ingat perubahan kualitas video</string>
<string name="revanced_remember_video_quality_last_selected_summary">Mengingat kualitas video terakhir yang dipilih.</string>
<string name="revanced_remember_video_quality_last_selected_toast_title">Tampilkan toast</string>
<string name="revanced_remember_video_quality_last_selected_toast_summary">Menunjukkan toast ketika mengubah playback speed semula.</string>
<string name="revanced_custom_playback_speeds_invalid">Kecepatan pemutaran kustom tidak valid. Atur ulang ke nilai default.</string>
<string name="revanced_custom_playback_speeds_parse_exception">Invalid custom playback speeds. Using default values.</string>
<string name="revanced_remember_playback_speed_toast">Changing default speed to %s.</string>
<string name="revanced_remember_video_quality_mobile">Changing default mobile data quality to %s.</string>
<string name="revanced_remember_video_quality_none">Failed to set quality.</string>
<string name="revanced_remember_video_quality_wifi">Changing default Wi-Fi quality to %s.</string>
<string name="revanced_remember_video_quality_last_selected_toast_summary">Menampilkan toast ketika mengubah kualitas video bawaan.</string>
<string name="revanced_custom_playback_speeds_invalid">Kecepatan khusus harus kurang dari %sx.</string>
<string name="revanced_custom_playback_speeds_parse_exception">Kecepatan pemutaran kustom tidak sah.</string>
<string name="revanced_remember_playback_speed_toast">Mengubah kecepatan bawaan ke %s.</string>
<string name="revanced_remember_video_quality_mobile">Mengubah kualitas data seluler bawaan ke %s.</string>
<string name="revanced_remember_video_quality_none">Gagal mengatur kualitas.</string>
<string name="revanced_remember_video_quality_wifi">Mengubah kualitas Wi-Fi bawaan ke %s.</string>
<!-- PreferenceScreen: Return YouTube Dislike -->
<string name="revanced_preference_screen_ryd_title">Return YouTube Dislike</string>
<string name="revanced_ryd_enabled_title">Enable Return YouTube Dislike</string>
<string name="revanced_ryd_enabled_summary">Menunjukkan jumlah dislike pada video.</string>
<string name="revanced_ryd_enabled_title">Aktifkan Return YouTube Dislike</string>
<string name="revanced_ryd_enabled_summary">Menampilkan jumlah tidak suka pada video.</string>
<string name="revanced_ryd_dislike_percentage_title">Dislike sebagai persentase</string>
<string name="revanced_ryd_dislike_percentage_summary">Alih-alih jumlah dislike, yang ditampilkan adalah persentase dislike.</string>
<string name="revanced_ryd_compact_layout_title">Tombol like ringkas</string>
<string name="revanced_ryd_compact_layout_summary">Menyembunyikan pemisah tombol like.</string>
<string name="revanced_ryd_toast_on_connection_error_title">Show a toast if API is unavailable</string>
<string name="revanced_ryd_toast_on_connection_error_summary">Shows a toast if the Return YouTube Dislike API is unavailable.</string>
<string name="revanced_ryd_dislike_percentage_summary">Menampilkan persentase dislike, bukan jumlah dislike.</string>
<string name="revanced_ryd_compact_layout_title">Tombol suka ringkas</string>
<string name="revanced_ryd_compact_layout_summary">Menyembunyikan pemisah tombol suka.</string>
<string name="revanced_ryd_estimated_like_title">Tampilkan perkiraan suka</string>
<string name="revanced_ryd_estimated_like_summary">Menampilkan perkiraan jumlah suka video.</string>
<string name="revanced_ryd_toast_on_connection_error_title">Tampilkan toast jika API tidak tersedia</string>
<string name="revanced_ryd_toast_on_connection_error_summary">Menampilkan toast jika API Return YouTube Dislike tidak tersedia.</string>
<string name="revanced_ryd_about">Tentang</string>
<string name="revanced_ryd_attribution_title">ReturnYouTubeDislike.com</string>
<string name="revanced_ryd_attribution_summary">Data disediakan oleh API Return YouTube Dislike. Tekan di sini untuk mempelajari lebih lanjut.</string>
<string name="revanced_ryd_failure_connection_timeout">Dislikes are temporarily unavailable (API timed out).</string>
<string name="revanced_ryd_failure_connection_status_code">Dislikes are unavailable (status %d).</string>
<string name="revanced_ryd_failure_connection_timeout">Dislike untuk sementara tidak tersedia (API kehabisan waktu).</string>
<string name="revanced_ryd_failure_connection_status_code">Dislike tidak tersedia (status %d).</string>
<string name="revanced_ryd_failure_client_rate_limit_requested">Dislike tidak tersedia (batas API client tercapai).</string>
<string name="revanced_ryd_failure_generic">Dislikes are unavailable (%s).</string>
<string name="revanced_ryd_failure_generic">Dislikes tidak tersedia (%s).</string>
<string name="revanced_ryd_video_likes_hidden_by_video_owner">Disembunyikan</string>
<!-- PreferenceScreen: Return YouTube Username -->
<string name="revanced_preference_screen_return_youtube_username_title">Return YouTube Username</string>
<string name="revanced_return_youtube_username_enabled_title">Aktifkan Return YouTube Username</string>
<string name="revanced_return_youtube_username_enabled_summary">Mengganti penanganan dengan nama pengguna di komentar.</string>
<string name="revanced_return_youtube_username_display_format_title">Format tampilan</string>
<string name="revanced_return_youtube_username_display_format_summary">Pilih format tampilan nama pengguna.</string>
<string name="revanced_return_youtube_username_display_format_username_only">Nama pengguna</string>
<string name="revanced_return_youtube_username_display_format_username_handle">Nama pengguna (@penganganan)</string>
<string name="revanced_return_youtube_username_display_format_handle_username">\@penanganan (Nama pengguna)</string>
<string name="revanced_return_youtube_username_youtube_data_api_v3_developer_key_title">Kunci API Data YouTube</string>
<string name="revanced_return_youtube_username_youtube_data_api_v3_developer_key_summary">Kunci pengembang untuk menggunakan API Data YouTube v3.</string>
<string name="revanced_return_youtube_username_youtube_data_api_v3_about_title">Tentang kunci API Data YouTube</string>
<string name="revanced_return_youtube_username_youtube_data_api_v3_about_summary">"Kunci Pengembang API Data YouTube v3 diperlukan untuk mengganti penanganan dengan nama pengguna.
Kuota harian untuk kunci API pada paket gratis adalah 10.000, dan 1 kuota digunakan untuk mengganti penanganan dengan nama pengguna untuk 1 komentar.
Klik untuk melihat cara menerbitkan kunci API."</string>
<string name="revanced_return_youtube_username_youtube_data_api_v3_dialog_title">Terbitkan kunci pengembang API Data YouTube v3</string>
<string name="revanced_return_youtube_username_youtube_data_api_v3_dialog_message">1. Pergi ke &lt;a href=%1$s&gt;Buat proyek baru&lt;/a&gt;.&lt;br&gt;2. Klik &lt;b&gt;tombol;&lt;/b&gt; BUAT.&lt;br&gt;3. Pergi ke &lt;a href=%2$s&gt;API Data YouTube v3&lt;/a&gt;.&lt;br&gt;4. Klik &lt;b&gt;tombol&lt;/b&gt; NYALAKAN.&lt;br&gt;5. Klik &lt;b&gt;tombol&lt;/b&gt; BUAT KREDENSIAL.&lt;br&gt;6. Pilih &lt;b&gt;pilihan&lt;/b&gt; data Publik.&lt;br&gt;7. Klik &lt;b&gt;tombol&lt;/b&gt; SELANJUTNYA.&lt;br&gt;8. Salin kunci API.&lt;br&gt;&lt;br&gt;※ Kunci API tidak boleh dibagikan dengan orang lain, sehingga tidak disertakan dalam pengaturan Impor / Ekspor.</string>
<!-- PreferenceScreen: SponsorBlock -->
<string name="revanced_preference_screen_sb_title">SponsorBlock</string>
<string name="revanced_sb_enabled">Enable SponsorBlock</string>
<string name="revanced_sb_enabled_sum">SponsorBlock is a crowd-sourced system for skipping annoying parts of YouTube videos.</string>
<string name="revanced_sb_toast_on_connection_error">Show a toast if API is unavailable</string>
<string name="revanced_sb_toast_on_connection_error_sum">Shows a toast if the SponsorBlock API is unavailable.</string>
<string name="revanced_sb_toast_on_skip">Show a toast when skipping automatically</string>
<string name="revanced_sb_toast_on_skip_sum">Shows a toast when a segment is automatically skipped.</string>
<string name="revanced_sb_api_url">Change API URL</string>
<string name="revanced_sb_api_url_sum">The address SponsorBlock uses to make calls to the server. Do not change this unless you know what you\'re doing.</string>
<string name="revanced_sb_api_url_reset">API URL reset.</string>
<string name="revanced_sb_api_url_invalid">API URL is invalid.</string>
<string name="revanced_sb_api_url_changed">API URL changed.</string>
<string name="revanced_sb_diff_segments">Change segment behavior</string>
<string name="revanced_sb_enabled">Aktifkan SponsorBlock</string>
<string name="revanced_sb_enabled_sum">SponsorBlock adalah sistem yang bersumber dari banyak orang untuk melewatkan bagian video YouTube yang mengganggu.</string>
<string name="revanced_sb_toast_on_connection_error">Tampilkan toast jika API tidak tersedia</string>
<string name="revanced_sb_toast_on_connection_error_sum">Menampilkan toast jika API SponsorBlock tidak tersedia.</string>
<string name="revanced_sb_toast_on_skip">Menampilkan toast saat melewatkan secara otomatis</string>
<string name="revanced_sb_toast_on_skip_sum">Menampilkan toast ketika segmen dilewati secara otomatis.</string>
<string name="revanced_sb_api_url">Ubah URL API</string>
<string name="revanced_sb_api_url_sum">Alamat yang digunakan SponsorBlock untuk melakukan panggilan ke server. Jangan ubah ini kecuali Anda tahu apa yang Anda lakukan.</string>
<string name="revanced_sb_api_url_reset">URL API diatur ulang.</string>
<string name="revanced_sb_api_url_invalid">URL API tidak sah.</string>
<string name="revanced_sb_api_url_changed">URL API diubah.</string>
<string name="revanced_sb_diff_segments">Ubah perilaku segmen</string>
<string name="revanced_sb_segments_sponsor">Sponsor</string>
<string name="revanced_sb_segments_sponsor_sum">Paid promotion, paid referrals, and direct advertisements. Not for self-promotion or free shout-outs to causes / creators / websites / products they like.</string>
<string name="revanced_sb_segments_selfpromo">Unpaid/Self Promotion</string>
<string name="revanced_sb_segments_selfpromo_sum">Similar to \'Sponsor\' except for unpaid or self promotion. Includes sections about merchandise, donations, or information about who they collaborated with.</string>
<string name="revanced_sb_segments_interaction">Interaction Reminder (Subscribe)</string>
<string name="revanced_sb_segments_interaction_sum">A short reminder to like, subscribe, or follow them in the middle of content. If it is long or about something specific, it should instead be under self promotion.</string>
<string name="revanced_sb_segments_intro">Intermission/Intro Animation</string>
<string name="revanced_sb_segments_intro_sum">An interval without actual content. Could be a pause, static frame, or repeating animation. Does not include transitions containing information.</string>
<string name="revanced_sb_segments_outro">Endcards/Credits</string>
<string name="revanced_sb_segments_outro_sum">Credits or when the YouTube endcards appear. Not for conclusions with information.</string>
<string name="revanced_sb_segments_preview">Preview/Recap / Hook</string>
<string name="revanced_sb_segments_preview_sum">Collection of clips that show what is coming up or what happened in the video or in other videos of a series, where all information is repeated elsewhere.</string>
<string name="revanced_sb_segments_filler">Filler Tangent/Jokes</string>
<string name="revanced_sb_segments_filler_sum">Tangential scenes added only for filler or humor that are not required to understand the main content of the video. Does not include segments providing context or background details.</string>
<string name="revanced_sb_segments_nomusic">Music: Non-Music Section</string>
<string name="revanced_sb_segments_nomusic_sum">Only for use in music videos. Sections of music videos without music, that aren\'t already covered by another category.</string>
<string name="revanced_sb_skipped_sponsor">Skipped sponsor.</string>
<string name="revanced_sb_skipped_selfpromo">Skipped self promotion.</string>
<string name="revanced_sb_skipped_interaction">Skipped annoying reminder.</string>
<string name="revanced_sb_skipped_intro_beginning">Skipped intro.</string>
<string name="revanced_sb_skipped_intro_middle">Skipped intermission.</string>
<string name="revanced_sb_skipped_intro_end">Skipped intermission.</string>
<string name="revanced_sb_skipped_outro">Skipped outro.</string>
<string name="revanced_sb_skipped_preview_beginning">Skipped preview.</string>
<string name="revanced_sb_skipped_preview_middle">Skipped preview.</string>
<string name="revanced_sb_skipped_preview_end">Skipped recap.</string>
<string name="revanced_sb_skipped_filler">Skipped filler.</string>
<string name="revanced_sb_skipped_nomusic">Skipped a non-music section.</string>
<string name="revanced_sb_skipped_multiple_segments">Skipped multiple segments.</string>
<string name="revanced_sb_skip_automatically">Skip automatically</string>
<string name="revanced_sb_skip_ignore">Disable</string>
<string name="revanced_sb_sponsorblock_connection_failure_generic">SponsorBlock is temporarily unavailable.</string>
<string name="revanced_sb_sponsorblock_connection_failure_status">SponsorBlock is temporarily unavailable (status %d).</string>
<string name="revanced_sb_sponsorblock_connection_failure_timeout">SponsorBlock is temporarily unavailable (API timed out).</string>
<string name="revanced_sb_color_dot_label">Color:</string>
<string name="revanced_sb_color_changed">Color changed.</string>
<string name="revanced_sb_color_reset">Color reset.</string>
<string name="revanced_sb_color_invalid">Invalid color code. Color reset to default.</string>
<string name="revanced_sb_reset_color">Reset color</string>
<string name="revanced_sb_about_api_sum">Data is provided by the SponsorBlock API. Tap here to learn more and see downloads for other platforms.</string>
<string name="revanced_sb_about">About</string>
<string name="revanced_sb_segments_sponsor_sum">Promosi berbayar, rujukan berbayar, dan iklan langsung. Bukan untuk promosi diri atau promosi gratis untuk tujuan / kreator / situs web / produk yang mereka sukai.</string>
<string name="revanced_sb_segments_selfpromo">Tidak Berbayar / Promosi Sendiri</string>
<string name="revanced_sb_segments_selfpromo_sum">Mirip dengan Sponsor, kecuali untuk promosi yang tidak berbayar atau promosi sendiri. Termasuk bagian tentang barang dagangan, donasi, atau informasi tentang dengan siapa mereka berkolaborasi.</string>
<string name="revanced_sb_segments_interaction">Pengingat Interaksi (Berlangganan)</string>
<string name="revanced_sb_segments_interaction_sum">Pengingat singkat untuk menyukai, berlangganan, atau mengikuti mereka di tengah-tengah konten. Jika panjang atau tentang sesuatu yang spesifik, sebaiknya berada di bawah promosi diri.</string>
<string name="revanced_sb_segments_intro">Animasi Jeda / Intro</string>
<string name="revanced_sb_segments_intro_sum">Selang waktu tanpa konten yang sebenarnya. Bisa berupa jeda, bingkai statis, atau animasi berulang. Tidak termasuk transisi yang berisi informasi.</string>
<string name="revanced_sb_segments_outro">Kartu Akhir / Kredit</string>
<string name="revanced_sb_segments_outro_sum">Kredit atau saat kartu akhir YouTube muncul. Bukan untuk menyimpulkan informasi.</string>
<string name="revanced_sb_segments_preview">Pratinjau / Rekap / Pengait</string>
<string name="revanced_sb_segments_preview_sum">Kumpulan klip yang menunjukkan apa yang akan datang atau apa yang terjadi dalam video atau dalam video lain dari suatu seri, di mana semua informasi diulang di tempat lain.</string>
<string name="revanced_sb_segments_filler">Pengisi Tidak Relevan / Lelucon</string>
<string name="revanced_sb_segments_filler_sum">Adegan berbelit-belit yang ditambahkan hanya sebagai filler atau candaan yang tidak diperlukan untuk memahami isi utama video. Tidak termasuk bagian yang mengandung konteks atau detail latar belakang.</string>
<string name="revanced_sb_segments_nomusic">Musik: Bagian Non-Musik</string>
<string name="revanced_sb_segments_nomusic_sum">Hanya untuk digunakan dalam video musik. Bagian video musik tanpa musik, yang belum tercakup dalam kategori lain.</string>
<string name="revanced_sb_skipped_sponsor">Melewatkan sponsor.</string>
<string name="revanced_sb_skipped_selfpromo">Melewatkan promosi diri.</string>
<string name="revanced_sb_skipped_interaction">Melewatkan pengingat mengganggu.</string>
<string name="revanced_sb_skipped_intro_beginning">Melewatkan intro.</string>
<string name="revanced_sb_skipped_intro_middle">Melewatkan jeda.</string>
<string name="revanced_sb_skipped_intro_end">Melewatkan jeda.</string>
<string name="revanced_sb_skipped_outro">Melewatkan outro.</string>
<string name="revanced_sb_skipped_preview_beginning">Melewatkan pratinjau.</string>
<string name="revanced_sb_skipped_preview_middle">Melewatkan pratinjau.</string>
<string name="revanced_sb_skipped_preview_end">Melewatkan rekap.</string>
<string name="revanced_sb_skipped_filler">Melewatkan pengisi.</string>
<string name="revanced_sb_skipped_nomusic">Melewatkan bagian non musik.</string>
<string name="revanced_sb_skipped_multiple_segments">Melewatkan beberapa segmen.</string>
<string name="revanced_sb_skip_automatically">Lewati otomatis</string>
<string name="revanced_sb_skip_ignore">Nonaktifkan</string>
<string name="revanced_sb_sponsorblock_connection_failure_generic">SponsorBlock untuk sementara tidak tersedia.</string>
<string name="revanced_sb_sponsorblock_connection_failure_status">SponsorBlock untuk sementara tidak tersedia (status %d).</string>
<string name="revanced_sb_sponsorblock_connection_failure_timeout">SponsorBlock untuk sementara tidak tersedia (API habis batas waktunya).</string>
<string name="revanced_sb_color_dot_label">Warna:</string>
<string name="revanced_sb_color_changed">Warna diubah.</string>
<string name="revanced_sb_color_reset">Warna diatur ulang.</string>
<string name="revanced_sb_color_invalid">Kode warna tidak sah.</string>
<string name="revanced_sb_reset_color">Atur ulang warna</string>
<string name="revanced_sb_about_api_sum">Data disediakan oleh API SponsorBlock. Tekan di sini untuk mempelajari lebih lanjut dan melihat unduhan untuk platform lain.</string>
<string name="revanced_sb_about">Tentang</string>
<string name="revanced_sb_about_api">sponsor.ajay.app</string>
<!-- PreferenceScreen: Miscellaneous -->
<string name="revanced_preference_screen_misc_title">Miscellaneous</string>
<string name="revanced_preference_screen_misc_title">Lain-lain</string>
<string name="revanced_extended_settings_import_export_title">Ekspor / Impor</string>
<string name="revanced_extended_settings_import_export_summary">Impor atau ekspor setelan sebagai teks.</string>
<string name="revanced_extended_settings_export_as_file">Export settings to file</string>
<string name="revanced_extended_settings_import_as_file">Import settings from file</string>
<string name="revanced_extended_settings_import_export_as_text">Import / Export settings as text</string>
<string name="revanced_extended_settings_export_failed">Failed to export settings.</string>
<string name="revanced_extended_settings_export_success">Settings were successfully exported.</string>
<string name="revanced_extended_settings_import_export_summary">Impor / Ekspor pengaturan Musik RVX.</string>
<string name="revanced_extended_settings_export_as_file">Ekspor pengaturan ke berkas</string>
<string name="revanced_extended_settings_import_as_file">Impor pengaturan dari berkas</string>
<string name="revanced_extended_settings_import_export_as_text">Impor / Ekspor pengaturan sebagai teks</string>
<string name="revanced_extended_settings_export_failed">Gagal mengekspor pengaturan.</string>
<string name="revanced_extended_settings_export_success">Pengaturan berhasil diekspor.</string>
<string name="revanced_extended_settings_import">Impor</string>
<string name="revanced_extended_settings_import_copy">Salin</string>
<string name="revanced_extended_settings_import_failed">Import failed: %s.</string>
<string name="revanced_extended_settings_import_reset">Reset setelan ke default.</string>
<string name="revanced_extended_settings_import_failed">Impor gagal: %s.</string>
<string name="revanced_extended_settings_import_reset">Pengaturan diatur ulang ke bawaan.</string>
<string name="revanced_extended_settings_import_success">Setelan %d diimpor.</string>
<string name="revanced_extended_settings_reset">Reset</string>
<string name="revanced_extended_settings_reset">Atur ulang</string>
<string name="revanced_share_copy_settings_success">Setelan disalin ke papan klip.</string>
<string name="revanced_bypass_image_region_restrictions_title">Bypass gambar larangan wilayah</string>
<string name="revanced_bypass_image_region_restrictions_summary">Mengganti domain yang ke blokir di negara tertentu sehingga playlist thumbnail, channel avatar, dll bisa di terima.</string>
<string name="revanced_bypass_image_region_restrictions_title">Abaikan pembatasan wilayah gambar</string>
<string name="revanced_bypass_image_region_restrictions_summary">Mengabaikan domain yang diblokir di beberapa wilayah sehingga thumbnail playlist, avatar saluran, dll. dapat diterima.</string>
<string name="revanced_change_share_sheet_title">Ubah lembar berbagi</string>
<string name="revanced_change_share_sheet_summary">Mengubah dari lembar berbagi dalam aplikasi ke lembar berbagi sistem.</string>
<string name="revanced_change_share_sheet_summary">Mengubah lembar berbagi dalam aplikasi ke lembar berbagi sistem.</string>
<string name="revanced_disable_cairo_splash_animation_title">Menonaktifkan animasi percikan Cairo</string>
<string name="revanced_disable_cairo_splash_animation_summary">Menonaktifkan animasi percikan Cairo saat aplikasi dimulai.</string>
<string name="revanced_disable_drc_audio_title">Nonaktifkan audio DRC</string>
<string name="revanced_disable_drc_audio_summary">Menonaktifkan DRC (Dynamic Range Compression) yang diterapkan ke audio.</string>
<string name="revanced_disable_music_video_in_album_title">Nonaktifkan video musik dalam album</string>
<string name="revanced_disable_music_video_in_album_summary">"Ketika pengguna non-premium memutar lagu yang termasuk dalam album, video musik terkadang diputar dan bukannya lagu resminya.
Temukan lagu resmi jika video musik terdeteksi diputar dari album.
Keterbatasan: Video anak-anak mungkin tidak dapat dialihkan."</string>
<string name="revanced_disable_music_video_in_album_redirect_type_title">Jenis pengalihan</string>
<string name="revanced_disable_music_video_in_album_redirect_type_summary">Menentukan cara mengalihkan ke lagu resmi.</string>
<string name="revanced_disable_music_video_in_album_redirect_type_entry_redirect">Alihkan</string>
<string name="revanced_disable_music_video_in_album_redirect_type_entry_on_click">Ketuk tombol Lagu / Video</string>
<string name="revanced_disable_music_video_in_album_redirect_type_entry_on_long_click">Ketuk dan tahan tombol Lagu / Video</string>
<string name="revanced_disable_quic_protocol_title">Nonaktifkan protokol QUIC</string>
<string name="revanced_disable_quic_protocol_summary">"Menonaktifkan protokol QUIC CronetEngine."</string>
<string name="revanced_enable_debug_logging_title">Aktifkan pencatatan debug</string>
<string name="revanced_enable_debug_logging_summary">Mencetak catatan debug.</string>
<string name="revanced_enable_debug_buffer_logging_title">Enable debug buffer logging</string>
<string name="revanced_enable_debug_buffer_logging_summary">Includes the buffer in the debug log.</string>
<string name="revanced_enable_debug_buffer_logging_title">Mengaktifkan pencatatan buffer debug</string>
<string name="revanced_enable_debug_buffer_logging_summary">Menyertakan buffer dalam log debug.</string>
<string name="revanced_enable_opus_codec_title">Aktifkan codec opus</string>
<string name="revanced_enable_opus_codec_summary">"Mengaktifkan codec audio opus alih-alih codec audio mp4a."</string>
<string name="revanced_sanitize_sharing_links_title">Sanitasi tautan berbagi</string>
<string name="revanced_sanitize_sharing_links_summary">Menghapus parameter kueri pelacakan dari URL saat membagikan tautan.</string>
<string name="gms_core_settings_title">Open GmsCore</string>
<string name="gms_core_settings_summary">Enable cloud messaging to receive notifications.</string>
<string name="gms_core_toast_not_installed_message">GmsCore is not installed. Install it.</string>
<string name="gms_core_dialog_title">Action needed</string>
<string name="gms_core_dialog_not_whitelisted_not_allowed_in_background_message">"GmsCore does not have permission to run in the background.
<string name="revanced_enable_opus_codec_summary">"Mengaktifkan codec OPUS jika respon pemutar menyertakannya.
Follow the 'Don't kill my app!' guide for your device, and apply the instructions to your GmsCore installation.
Info:
• Klien YouTube Music terbaru menggunakan codec audio OPUS secara default.
• Ini hanya berlaku untuk pengguna yang memalsukan dengan klien yang sangat lama."</string>
<string name="revanced_sanitize_sharing_links_title">Bersihkan tautan berbagi</string>
<string name="revanced_sanitize_sharing_links_summary">Membersihkan tautan berbagi dengan menghapus parameter kueri pelacakan.</string>
<string name="revanced_spoof_client_title">Palsukan klien</string>
<string name="revanced_spoof_client_summary">Memalsukan klien untuk mencegah masalah pemutaran.</string>
<string name="revanced_spoof_client_type_title">Klien bawaan</string>
<string name="revanced_spoof_client_type_summary">Menentukan klien bawaan untuk pemalsuan.</string>
<string name="revanced_spoof_client_type_entry_android_music_4_27">Musik Android 4.27.53</string>
<string name="revanced_spoof_client_type_entry_android_music_5_29">Musik Android 5.29.53</string>
<string name="revanced_spoof_client_type_entry_ios_music_6_21">Musik iOS 6.21</string>
<string name="revanced_spoof_client_type_entry_ios_music_7_04">Musik iOS 7.04</string>
<string name="revanced_watch_history_type_title">Jenis riwayat tontonan</string>
<string name="revanced_watch_history_type_summary">"• Asli: Mengikuti pengaturan riwayat tontonan akun Google, tetapi riwayat tontonan mungkin tidak berfungsi karena DNS atau VPN.
• Ganti domain: Mengikuti pengaturan riwayat tontonan akun Google.
• Blokir riwayat tontonan: Riwayat tontonan diblokir."</string>
<string name="revanced_watch_history_type_entry_1">Asli</string>
<string name="revanced_watch_history_type_entry_2">Ganti domain</string>
<string name="revanced_watch_history_type_entry_3">Blokir riwayat tontonan</string>
<string name="revanced_default_app_settings_title">Buka pengaturan aplikasi bawaan</string>
<string name="revanced_default_app_settings_summary">Untuk membuka tautan YouTube Music di RVX Music, aktifkan Buka tautan yang didukung dan aktifkan semua alamat web yang Didukung.</string>
<string name="gms_core_settings_title">Buka pengaturan GmsCore</string>
<string name="gms_core_settings_summary">Untuk menerima pemberitahuan di RVX Music, aktifkan Cloud Messaging.</string>
<string name="gms_core_toast_not_installed_message">GmsCore tidak terpasang. Pasang dulu.</string>
<string name="gms_core_dialog_title">Diperlukan tindakan</string>
<string name="gms_core_dialog_not_whitelisted_not_allowed_in_background_message">"GmsCore tidak memiliki izin untuk berjalan di latar belakang.
This is required for the app to work."</string>
<string name="gms_core_dialog_open_website_text">Open website</string>
<string name="gms_core_dialog_not_whitelisted_using_battery_optimizations_message">"GmsCore battery optimizations must be disabled to prevent issues.
Ikuti panduan 'Don't kill my app!' untuk perangkat Anda, dan terapkan petunjuk pada pemasangan GmsCore.
Tap on the continue button and disable battery optimizations."</string>
<string name="gms_core_dialog_continue_text">Continue</string>
Hal ini diperlukan agar aplikasi dapat berfungsi."</string>
<string name="gms_core_dialog_open_website_text">Buka website</string>
<string name="gms_core_dialog_not_whitelisted_using_battery_optimizations_message">"Pengoptimalan baterai GmsCore harus dinonaktifkan untuk mencegah masalah.
Menonaktifkan pengoptimalan baterai untuk GmsCore tidak akan berdampak negatif pada penggunaan baterai.
Ketuk tombol lanjutkan dan izinkan perubahan pengoptimalan."</string>
<string name="gms_core_dialog_continue_text">Lanjutkan</string>
</resources>

View File

@ -54,6 +54,11 @@
<!-- PreferenceScreen: Ads -->
<string name="revanced_preference_screen_ads_title">広告</string>
<string name="revanced_hide_fullscreen_ads_title">全画面広告を非表示</string>
<string name="revanced_hide_fullscreen_ads_summary">"全画面広告を隠す
注意:
・ホームフィードの代わりに真っ黒な画面が表示されることがあります。"</string>
<string name="revanced_fullscreen_ads_closed_toast">全画面広告を閉じました。</string>
<string name="revanced_hide_general_ads_title">一般広告を非表示</string>
<string name="revanced_hide_general_ads_summary">一般広告を非表示にします。</string>
<string name="revanced_hide_music_ads_title">音楽の広告を非表示</string>
@ -62,6 +67,7 @@
<string name="revanced_hide_paid_promotion_label_summary">有料プロモーションラベルを非表示にします。</string>
<string name="revanced_hide_premium_promotion_title">プレミアムプロモーションポップアップを非表示</string>
<string name="revanced_hide_premium_promotion_summary">プレミアムプロモーションポップアップを非表示にします。</string>
<string name="revanced_hide_premium_promotion_closed_toast">プレミアムプロモーションのポップアップは閉じられました。</string>
<string name="revanced_hide_premium_renewal_title">プレミアム更新バナーを非表示</string>
<string name="revanced_hide_premium_renewal_summary">プレミアム更新バナーを非表示にします。</string>
<string name="revanced_hide_promotion_alert_banner_title">プロモーションバナーを非表示</string>
@ -93,6 +99,7 @@
<string name="revanced_hide_flyout_menu_go_to_episode_title">「エピソードに移動」を非表示</string>
<string name="revanced_hide_flyout_menu_go_to_podcast_title">「ポッドキャストに移動」を非表示</string>
<string name="revanced_hide_flyout_menu_help_title">「ヘルプとフィードバック」を非表示</string>
<string name="revanced_hide_flyout_menu_not_interested_title">「興味なし」を非表示</string>
<string name="revanced_hide_flyout_menu_pin_to_speed_dial_title">「[クイック アクセス] に固定」を非表示</string>
<string name="revanced_hide_flyout_menu_play_next_title">「次に再生」を非表示</string>
<string name="revanced_hide_flyout_menu_quality_title">画質メニューを非表示</string>
@ -124,8 +131,17 @@
<string name="revanced_preference_screen_general_title">全般</string>
<string name="revanced_change_start_page_title">スタートページを変更</string>
<string name="revanced_change_start_page_summary">アプリのスタートページを変更します。</string>
<string name="revanced_change_start_page_entry_default">デフォルト</string>
<string name="revanced_change_start_page_entry_charts">チャート</string>
<string name="revanced_change_start_page_entry_episodes_for_later">次に再生</string>
<string name="revanced_change_start_page_entry_explore">探索</string>
<string name="revanced_change_start_page_entry_history">履歴</string>
<string name="revanced_change_start_page_entry_library">ライブラリ</string>
<string name="revanced_change_start_page_entry_liked_music">高く評価した音楽</string>
<string name="revanced_change_start_page_entry_podcasts">ポッドキャスト</string>
<string name="revanced_change_start_page_entry_samples">サンプル</string>
<string name="revanced_change_start_page_entry_search">検索</string>
<string name="revanced_change_start_page_entry_subscriptions">登録チャンネル</string>
<string name="revanced_disable_dislike_redirection_title">低評価リダイレクトを無効化</string>
<string name="revanced_disable_dislike_redirection_summary">低評価ボタンを押したとき、次の曲へのリダイレクトするのを無効にする。</string>
<string name="revanced_disable_auto_captions_title">字幕の強制を無効化</string>
@ -197,6 +213,18 @@
<string name="revanced_hide_navigation_bar_summary">ナビゲーションバーを非表示にします。</string>
<string name="revanced_hide_navigation_label_title">ナビゲーションバーのラベルを非表示</string>
<string name="revanced_hide_navigation_label_summary">ナビゲーションバーのラベルを非表示にします。</string>
<string name="revanced_replace_navigation_samples_button_title">サンプルボタンを置換</string>
<string name="revanced_replace_navigation_samples_button_summary">「サンプル」を「検索」に置き換えます。</string>
<string name="revanced_replace_navigation_upgrade_button_title">アップグレードボタンを置換</string>
<string name="revanced_replace_navigation_upgrade_button_summary">「アップグレード」を「設定」に置き換えます。</string>
<string name="revanced_replace_navigation_button_about_title">ボタンの置き換えについて</string>
<string name="revanced_replace_navigation_button_about_summary">"この機能は実験的なものです。
YouTube Music の検索や設定などのアクティビティは公開されていないため、このパッチには構造的な制限があります。
既知の問題:
・検索や設定などの置換されたアクティビティを閉じると、スタートページが開きます。
クリックすると「スタートページの変更」設定が開きます。"</string>
<!-- PreferenceScreen: Player -->
<string name="revanced_preference_screen_player_title">プレーヤー</string>
<string name="revanced_add_miniplayer_next_button_title">「次の曲に進むボタン」を表示</string>
@ -222,8 +250,14 @@
<string name="revanced_disable_miniplayer_gesture_summary">ミニプレイヤーでスワイプによる曲の変更を無効にします。</string>
<string name="revanced_disable_player_gesture_title">プレイヤージェスチャーを無効にする</string>
<string name="revanced_disable_player_gesture_summary">プレイヤーでスワイプによる曲の変更を無効にします。</string>
<string name="revanced_enable_forced_miniplayer_title">ミニプレイヤーの強制</string>
<string name="revanced_enable_forced_miniplayer_summary">新しいトラックに切り替えたときに強制的にミニプレーヤーを有効にします。</string>
<string name="revanced_enable_swipe_to_dismiss_miniplayer_title">スワイプしてミニプレーヤーを閉じる</string>
<string name="revanced_enable_swipe_to_dismiss_miniplayer_summary">下にスワイプしてミニプレーヤーを閉じられるようにします。</string>
<string name="revanced_enable_thick_seekbar_title">太いシークバーを有効化</string>
<string name="revanced_enable_thick_seekbar_summary">"太いシークバーを有効にします。
注意: SponsorBlock セグメントはシークバーに表示されません。"</string>
<string name="revanced_enable_zen_mode_title">Zen モードを有効化</string>
<string name="revanced_enable_zen_mode_summary">動画プレーヤーに灰色の色合いを追加し、目の疲れを軽減します。</string>
<string name="revanced_enable_zen_mode_podcast_title">ポッドキャストでZenモードを有効化</string>
@ -399,11 +433,18 @@ API キーの発行方法については、ここをタップしてください
<string name="revanced_disable_drc_audio_title">DRCオーディオを無効にする</string>
<string name="revanced_disable_drc_audio_summary">音声に適用されるDRC (Dynamic Range Compression) を無効にします。</string>
<string name="revanced_disable_music_video_in_album_title">アルバム内のミュージックビデオを無効にする</string>
<string name="revanced_disable_music_video_in_album_summary">"非プレミアムユーザーがアルバムに含まれる曲を再生すると、公式の曲ではなくMVが再生されることがあります。
アルバムからMVの再生が検出された場合は、公式楽曲を検索します。
注意: キッズビデオはリダイレクトされない場合があります。"</string>
<string name="revanced_disable_music_video_in_album_redirect_type_title">リダイレクトのタイプ</string>
<string name="revanced_disable_music_video_in_album_redirect_type_summary">公式楽曲にリダイレクトする方法を指定します。</string>
<string name="revanced_disable_music_video_in_album_redirect_type_entry_redirect">リダイレクト</string>
<string name="revanced_disable_music_video_in_album_redirect_type_entry_on_click">曲 / 動画 の切り替えをタップ</string>
<string name="revanced_disable_music_video_in_album_redirect_type_entry_on_long_click">曲 / 動画 の切り替えをタップし長押し</string>
<string name="revanced_disable_quic_protocol_title">QUIC プロトコルを無効化</string>
<string name="revanced_disable_quic_protocol_summary">"CronetEngine の QUIC プロトコルを無効化します。"</string>
<string name="revanced_enable_debug_logging_title">デバッグログ</string>
<string name="revanced_enable_debug_logging_summary">デバッグログを出力します。</string>
<string name="revanced_enable_debug_buffer_logging_title">デバッグバッファログを有効化</string>
@ -415,10 +456,23 @@ API キーの発行方法については、ここをタップしてください
<string name="revanced_spoof_client_title">クライアントを偽装</string>
<string name="revanced_spoof_client_summary">再生の問題を防ぐためにクライアントを偽装します。</string>
<string name="revanced_spoof_client_type_title">既定のクライアント</string>
<string name="revanced_spoof_client_type_summary">偽装するデフォルトのクライアントを定義します。</string>
<string name="revanced_spoof_client_type_entry_android_music_4_27">Android Music 4.27.53</string>
<string name="revanced_spoof_client_type_entry_android_music_5_29">Android Music 5.29.53</string>
<string name="revanced_spoof_client_type_entry_ios_music_6_21">iOS Music 6.21</string>
<string name="revanced_spoof_client_type_entry_ios_music_7_04">iOS Music 7.04</string>
<string name="revanced_spoof_player_parameter_title">プレイヤーパラメータを偽装</string>
<string name="revanced_spoof_player_parameter_summary">"再生の問題を防ぐためにプレイヤーパラメーターを偽装します。
副作用:
• 字幕が下部ではなくプレーヤーの上部に配置されることがあります。"</string>
<string name="revanced_watch_history_type_title">再生履歴の種類</string>
<string name="revanced_watch_history_type_summary">"・オリジナル Google アカウントの視聴履歴設定に従いますが、DNSやVPNにより視聴履歴が機能しない場合があります。
・ドメインを置き換えます:  Google アカウントの視聴履歴設定に従います。
・視聴履歴をブロックします:  視聴履歴をブロックします。"</string>
<string name="revanced_watch_history_type_entry_1">オリジナル</string>
<string name="revanced_watch_history_type_entry_2">ドメインを置換</string>
<string name="revanced_watch_history_type_entry_3">再生履歴をブロック</string>
<string name="revanced_default_app_settings_title">「デフォルトで開く」の設定</string>
<string name="revanced_default_app_settings_summary">RVX Music でYouTube Music のURLを開くには、「対応リンクを開く」を有効にし、サポートされているURLを有効にします。</string>
<string name="gms_core_settings_title">GmsCoreを開く</string>

View File

@ -212,6 +212,18 @@
<string name="revanced_hide_navigation_bar_summary">하단바를 숨깁니다.</string>
<string name="revanced_hide_navigation_label_title">하단바 버튼 라벨 제거</string>
<string name="revanced_hide_navigation_label_summary">하단바 버튼에서 라벨을 숨깁니다.</string>
<string name="revanced_replace_navigation_samples_button_title">샘플 버튼 변경</string>
<string name="revanced_replace_navigation_samples_button_summary">샘플 버튼을 검색 버튼으로 변경합니다.</string>
<string name="revanced_replace_navigation_upgrade_button_title">업그레이드 버튼 변경</string>
<string name="revanced_replace_navigation_upgrade_button_summary">업그레이드 버튼을 설정 버튼으로 변경합니다.</string>
<string name="revanced_replace_navigation_button_about_title">버튼 변경에 대한 정보</string>
<string name="revanced_replace_navigation_button_about_summary">"이 기능은 실험 기능입니다.
YouTube Music의 검색 및 설정과 같은 활동은 공개되지 않으므로 패치에는 구조적인 제한이 있습니다.
알려진 문제:
• 검색 및 설정과 같은 교체된 활동이 닫히면 앱 시작 페이지가 열립니다.
앱 시작 페이지 설정을 열려면 여기를 누르세요."</string>
<!-- PreferenceScreen: Player -->
<string name="revanced_preference_screen_player_title">플레이어</string>
<string name="revanced_add_miniplayer_next_button_title">미니 플레이어 다음 재생 버튼 추가</string>
@ -426,7 +438,7 @@ API Key를 발급받는 방법을 보려면 여기를 누르세요."</string>
앨범에서 뮤직 비디오가 재생되는 것이 감지되면 정식 음원을 찾아줍니다.
알려진 문제점:
• Kids 동영상은 리렉션되지 않을 수 있습니다."</string>
• Kids 동영상은 리다이렉션되지 않을 수 있습니다."</string>
<string name="revanced_disable_music_video_in_album_redirect_type_title">리다이렉션 유형</string>
<string name="revanced_disable_music_video_in_album_redirect_type_summary">정식 음원으로 리다이렉션하는 방법을 지정할 수 있습니다.</string>
<string name="revanced_disable_music_video_in_album_redirect_type_entry_redirect">리다이렉션</string>
@ -454,6 +466,11 @@ API Key를 발급받는 방법을 보려면 여기를 누르세요."</string>
<string name="revanced_spoof_client_type_entry_android_music_5_29">Android Music 5.29.53</string>
<string name="revanced_spoof_client_type_entry_ios_music_6_21">iOS Music 6.21</string>
<string name="revanced_spoof_client_type_entry_ios_music_7_04">iOS Music 7.04</string>
<string name="revanced_spoof_player_parameter_title">플레이어 매개변수 변경하기</string>
<string name="revanced_spoof_player_parameter_summary">"플레이어 매개변수를 변경하여 재생 문제를 방지할 수 있습니다.
알려진 문제점:
• 자막이 플레이어 하단이 아닌 상단에 위치하는 경우가 있습니다."</string>
<string name="revanced_watch_history_type_title">시청 기록 유형</string>
<string name="revanced_watch_history_type_summary">"• 기본값: Google 계정의 시청 기록 설정을 따르지만 DNS 또는 VPN으로 인하여 시청 기록이 작동되지 않을 수 있습니다.
• 도메인 변경: Google 계정의 시청 기록 설정을 따릅니다.

View File

@ -213,6 +213,18 @@ Nie pomija to ograniczeń wiekowych, lecz akceptuje je automatycznie."</string>
<string name="revanced_hide_navigation_bar_summary">Ukrywa pasek nawigacji.</string>
<string name="revanced_hide_navigation_label_title">Ukryj nazwy w pasku nawigacji</string>
<string name="revanced_hide_navigation_label_summary">Ukrywa nazwy każdego przycisku w pasku nawigacji.</string>
<string name="revanced_replace_navigation_samples_button_title">Zastąp przycisk sampli</string>
<string name="revanced_replace_navigation_samples_button_summary">Zastępuje przycisk sampli przyciskiem wyszukiwania.</string>
<string name="revanced_replace_navigation_upgrade_button_title">Zastąp przycisk przejścia na YouTube Premium</string>
<string name="revanced_replace_navigation_upgrade_button_summary">Zastępuje przycisk przejścia na YouTube Premium przyciskiem ustawień.</string>
<string name="revanced_replace_navigation_button_about_title">O zastępowaniu przycisków</string>
<string name="revanced_replace_navigation_button_about_summary">"Ta funkcja jest eksperymentalna.
Istnieją strukturalne ograniczenia łatki, ponieważ działania takie jak wyszukiwanie i ustawienia w YouTube Music nie są publiczne.
Znane problemy:
• Po zamknięciu zastąpionego działania, takiego jak wyszukiwanie i ustawienia, otwierana jest strona startowa.
Kliknij, by otworzyć ustawienia 'Zmień stronę startową'."</string>
<!-- PreferenceScreen: Player -->
<string name="revanced_preference_screen_player_title">Odtwarzacz</string>
<string name="revanced_add_miniplayer_next_button_title">Dodaj przycisk do następnego utworu w miniodtwarzaczu</string>
@ -453,6 +465,11 @@ Informacje:
<string name="revanced_spoof_client_type_entry_android_music_5_29">Android Music 5.29.53</string>
<string name="revanced_spoof_client_type_entry_ios_music_6_21">iOS Music 6.21</string>
<string name="revanced_spoof_client_type_entry_ios_music_7_04">iOS Music 7.04</string>
<string name="revanced_spoof_player_parameter_title">Oszukuj parametry odtwarzacza</string>
<string name="revanced_spoof_player_parameter_summary">"Oszukuje parametry odtwarzacza, by zapobiec problemom z odtwarzaniem.
Efekt uboczny:
• Czasem napisy są u góry odtwarzacza, zamiast na dole"</string>
<string name="revanced_watch_history_type_title">Typ historii oglądania</string>
<string name="revanced_watch_history_type_summary">"• Oryginalny: Stosuje się do ustawień historii oglądania konta Google, lecz historia oglądania może nie działać przy używaniu DNS lub VPN
• Zastąpienie domeny: Stosuje się do ustawień historii oglądania konta Google

View File

@ -54,6 +54,10 @@ Por favor, baixe %2$s do site."</string>
<!-- PreferenceScreen: Ads -->
<string name="revanced_preference_screen_ads_title">Anúncios</string>
<string name="revanced_hide_fullscreen_ads_title">Ocultar anúncios em tela cheia</string>
<string name="revanced_hide_fullscreen_ads_summary">"Oculta anúncios de tela cheia.
Limitações:
• Às vezes você pode ver uma tela preta em branco ao invés do feed inicial."</string>
<string name="revanced_fullscreen_ads_closed_toast">Os anúncios em tela cheia são fechados.</string>
<string name="revanced_hide_general_ads_title">Ocultar anúncios gerais</string>
<string name="revanced_hide_general_ads_summary">Oculta anúncios gerais.</string>
@ -239,7 +243,9 @@ Use cores escuras se possível, pois o aplicativo não suporta temas claros."</s
<string name="revanced_enable_swipe_to_dismiss_miniplayer_title">Ativar deslizar para dispensar o mini reprodutor</string>
<string name="revanced_enable_swipe_to_dismiss_miniplayer_summary">Ativa deslizar para baixo para fechar o mini reprodutor.</string>
<string name="revanced_enable_thick_seekbar_title">Ativar barra de busca espessa</string>
<string name="revanced_enable_thick_seekbar_summary">"Ativa a barra de busca espessa."</string>
<string name="revanced_enable_thick_seekbar_summary">"Ativar a barra de busca espessa.
Limitações: segmentos de patrocinadores não são exibidos na barra de busca."</string>
<string name="revanced_enable_zen_mode_title">Ativar modo Calmo</string>
<string name="revanced_enable_zen_mode_summary">Ativa uma cor cinza claro para o fundo do reprodutor para reduzir o cansaço visual.</string>
<string name="revanced_enable_zen_mode_podcast_title">Ativar o modo Calmo em podcasts</string>
@ -415,11 +421,18 @@ Clique para ver como emitir uma chave de API."</string>
<string name="revanced_disable_drc_audio_title">Desativar áudio DRC</string>
<string name="revanced_disable_drc_audio_summary">Desativa o DRC (Compressão de faixa dinâmica) aplicada ao áudio.</string>
<string name="revanced_disable_music_video_in_album_title">Desativar vídeo da música no álbum</string>
<string name="revanced_disable_music_video_in_album_summary">"Quando um usuário não premium reproduz uma música incluída em um álbum, o vídeo da música é tocado às vezes em vez da música oficial.
Encontre a música oficial se um vídeo de música for detectado tocando de um álbum.
Limitações: Vídeos infantis não podem ser redirecionados."</string>
<string name="revanced_disable_music_video_in_album_redirect_type_title">Tipo de redirecionamento</string>
<string name="revanced_disable_music_video_in_album_redirect_type_summary">Especifica como redirecionar para a música oficial.</string>
<string name="revanced_disable_music_video_in_album_redirect_type_entry_redirect">Redirecionar</string>
<string name="revanced_disable_music_video_in_album_redirect_type_entry_on_click">Tocar alternador de Música / Vídeo</string>
<string name="revanced_disable_music_video_in_album_redirect_type_entry_on_long_click">Tocar e segurar alternador de Música / Vídeo</string>
<string name="revanced_disable_quic_protocol_title">Desativar protocolo QUIC</string>
<string name="revanced_disable_quic_protocol_summary">"Desativa o protocolo QUIC da CronetEngine."</string>
<string name="revanced_enable_debug_logging_title">Ativar o relatório de depuração</string>
<string name="revanced_enable_debug_logging_summary">Imprime o relatório de depuração</string>
<string name="revanced_enable_debug_buffer_logging_title">Ativar o registro de depuração do buffer</string>

View File

@ -213,6 +213,18 @@
<string name="revanced_hide_navigation_bar_summary">Скрывает панель навигации.</string>
<string name="revanced_hide_navigation_label_title">Скрыть подписи кнопок навигации</string>
<string name="revanced_hide_navigation_label_summary">Скрывает подписи под кнопками навигации.</string>
<string name="revanced_replace_navigation_samples_button_title">Замена кнопки \"Сэмплы\"</string>
<string name="revanced_replace_navigation_samples_button_summary">Заменяет кнопку \"Сэмпли\" на кнопку \"Поиск\".</string>
<string name="revanced_replace_navigation_upgrade_button_title">Замена кнопки \"Обновить\"</string>
<string name="revanced_replace_navigation_upgrade_button_summary">Заменяет кнопку \"Обновить\" на кнопку \"Настройки\".</string>
<string name="revanced_replace_navigation_button_about_title">О замене кнопки</string>
<string name="revanced_replace_navigation_button_about_summary">"Эта функция является экспериментальной.
Такие действия, как Поиск и Настройки в YouTube Music, не являются общедоступными, поэтому патч имеет структурные ограничения.
Известные проблемы:
• При закрытии замененных действий, таких как Поиск и Настройки, открывается начальная страница.
Нажмите, чтобы открыть \"Изменить начальную страницу\"."</string>
<!-- PreferenceScreen: Player -->
<string name="revanced_preference_screen_player_title">Плеер</string>
<string name="revanced_add_miniplayer_next_button_title">Добавить кнопку следующее в миниплеер</string>
@ -451,6 +463,11 @@
<string name="revanced_spoof_client_type_entry_android_music_5_29">Android Music 5.29.53</string>
<string name="revanced_spoof_client_type_entry_ios_music_6_21">iOS Music 6.21</string>
<string name="revanced_spoof_client_type_entry_ios_music_7_04">iOS Music 7.04</string>
<string name="revanced_spoof_player_parameter_title">Подменить параметр плеера</string>
<string name="revanced_spoof_player_parameter_summary">"Подменяет параметр плеера, чтобы предотвратить проблемы с воспроизведением.
Побочный эффект:
• Иногда субтитры располагаются в верхней части плеера, а не в нижней."</string>
<string name="revanced_watch_history_type_title">Тип истории просмотра</string>
<string name="revanced_watch_history_type_summary">"• Оригинальный: Соответствует настройкам истории просмотров аккаунта Google, но история просмотров может не работать через DNS или VPN.
• Заменить домен: Настройка истории просмотров аккаунта Google.

View File

@ -130,7 +130,7 @@
<!-- PreferenceScreen: General -->
<string name="revanced_preference_screen_general_title">Загальні</string>
<string name="revanced_change_start_page_title">Змінити початкову сторінку</string>
<string name="revanced_change_start_page_summary">Виберіть сторінку з якої буде стартувати додаток.</string>
<string name="revanced_change_start_page_summary">Виберіть сторінку з якої буде стартувати застосунок.</string>
<string name="revanced_change_start_page_entry_default">За замовчуванням</string>
<string name="revanced_change_start_page_entry_charts">Хіт-паради</string>
<string name="revanced_change_start_page_entry_episodes_for_later">Послухати пізніше</string>
@ -183,13 +183,13 @@
<string name="revanced_remove_viewer_discretion_dialog_title">Вилучити діалог про небажаний контент</string>
<string name="revanced_remove_viewer_discretion_dialog_summary">"Вилучає діалог про небажаний контент.
Це не обходить вікові обмеження. Просто приймає їх автоматично."</string>
<string name="revanced_spoof_app_version_title">Підробити версію програми</string>
<string name="revanced_spoof_app_version_summary">"Підміна версії клієнта на старішу версію.
<string name="revanced_spoof_app_version_title">Підмінити версію програми</string>
<string name="revanced_spoof_app_version_summary">"Підміняє версію клієнта на старішу версію.
• Це змінить зовнішній вигляд програми, але можуть виникнути невідомі побічні ефекти.
• Якщо пізніше вимкнути, старий інтерфейс може залишитися, доки не буде очищено дані програми."</string>
<string name="revanced_spoof_app_version_target_title">Підробити цільову версію програми</string>
<string name="revanced_spoof_app_version_target_summary">Виберіть зі списку цільову версію підробки програми.</string>
• Це змінить зовнішній вигляд застосунку, але можуть виникнути невідомі побічні ефекти.
• Якщо пізніше вимкнути, старий інтерфейс може залишитися, доки не буде очищено дані застосунку."</string>
<string name="revanced_spoof_app_version_target_title">Підмінити цільову версію застосунку</string>
<string name="revanced_spoof_app_version_target_summary">Виберіть зі списку цільову версію для підміни.</string>
<string name="revanced_spoof_app_version_target_entry_6_42_55">6.42.55 - Вимкнення динамічних текстів</string>
<string name="revanced_spoof_app_version_target_entry_7_16_53">7.16.53 - Відновлення старої панелі дій</string>
<!-- PreferenceScreen: Navigation bar -->
@ -213,6 +213,17 @@
<string name="revanced_hide_navigation_bar_summary">Приховує панель навігації.</string>
<string name="revanced_hide_navigation_label_title">Приховати підписи кнопок навігації</string>
<string name="revanced_hide_navigation_label_summary">Приховує підпис під кожною навігаційною кнопкою.</string>
<string name="revanced_replace_navigation_samples_button_title">Заміна кнопки \"Семпли\"</string>
<string name="revanced_replace_navigation_samples_button_summary">Замінює кнопку \"Семпли\" на кнопку \"Пошук\".</string>
<string name="revanced_replace_navigation_upgrade_button_title">Заміна кнопки \"Оновити\"</string>
<string name="revanced_replace_navigation_upgrade_button_summary">Замінює кнопку \"Оновити\" на кнопку \"Налаштування\".</string>
<string name="revanced_replace_navigation_button_about_title">Про заміну кнопки</string>
<string name="revanced_replace_navigation_button_about_summary">"Ця функція є експериментальною.
Існують структурні обмеження патчу, оскільки такі Activities, як Пошук і Налаштування в YouTube Music, не є загальнодоступними.
Відомі проблеми:
• Коли замінені Activity як Пошук і Налаштування закрито, відкривається початкова сторінка.
Натисніть, щоб відкрити \"Змінити початкову сторінку\"."</string>
<!-- PreferenceScreen: Player -->
<string name="revanced_preference_screen_player_title">Плеєр</string>
<string name="revanced_add_miniplayer_next_button_title">Додати кнопку наступне у мініплеєр</string>
@ -226,11 +237,11 @@
<string name="revanced_custom_player_background_color_primary_title">Основний колір фону плеєра</string>
<string name="revanced_custom_player_background_color_primary_summary">"Введіть hex код основного кольору фону плеєра.
По можливості використовуйте темні кольори, оскільки програма не підтримує світлі теми."</string>
По можливості використовуйте темні кольори, оскільки застосунок не підтримує світлі теми."</string>
<string name="revanced_custom_player_background_color_secondary_title">Вторинний колір фону плеєра</string>
<string name="revanced_custom_player_background_color_secondary_summary">"Введіть hex код вторинного кольору фону плеєра.
По можливості використовуйте темні кольори, оскільки програма не підтримує світлі теми."</string>
По можливості використовуйте темні кольори, оскільки застосунок не підтримує світлі теми."</string>
<string name="revanced_custom_player_background_invalid_toast">Недійсний колір фону плеєра.</string>
<string name="revanced_change_seekbar_position_title">Змінити положення панелі прогресу</string>
<string name="revanced_change_seekbar_position_summary">Переміщує панель прогресу під кнопку відтворення.</string>
@ -445,14 +456,19 @@
• Це буде корисно лише для користувачів, які користуються дуже старими клієнтами."</string>
<string name="revanced_sanitize_sharing_links_title">Обробляти поширення посилань</string>
<string name="revanced_sanitize_sharing_links_summary">Видаляє параметри запиту відстеження з посилання перед тим, як поділитися ним.</string>
<string name="revanced_spoof_client_title">Підміна клієнта</string>
<string name="revanced_spoof_client_summary">Підміна клієнта щоб запобігти проблемам відтворення.</string>
<string name="revanced_spoof_client_title">Підмінити клієнт</string>
<string name="revanced_spoof_client_summary">Підмінює клієнт, щоб запобігти проблемам із відтворенням.</string>
<string name="revanced_spoof_client_type_title">Клієнт за замовчуванням</string>
<string name="revanced_spoof_client_type_summary">Визначає клієнт за замовчуванням для підробки.</string>
<string name="revanced_spoof_client_type_entry_android_music_4_27">Android Music 4.27.53</string>
<string name="revanced_spoof_client_type_entry_android_music_5_29">Android Music 5.29.53</string>
<string name="revanced_spoof_client_type_entry_ios_music_6_21">iOS Music 6.21</string>
<string name="revanced_spoof_client_type_entry_ios_music_7_04">iOS Music 7.04</string>
<string name="revanced_spoof_player_parameter_title">Підмінити параметр плеєра</string>
<string name="revanced_spoof_player_parameter_summary">"Підмінює параметр плеєра, щоб запобігти проблемам із відтворенням.
Побічний ефект:
• Іноді субтитри розташовані у верхній частині плеєра, а не внизу."</string>
<string name="revanced_watch_history_type_title">Тип історії перегляду</string>
<string name="revanced_watch_history_type_summary">"• Оригінальний: Відповідає налаштуванням історії переглядів облікового запису Google, але історія переглядів може не працювати через DNS або VPN.
• Замінити домен: Дотримується налаштувань історії переглядів облікового запису Google.
@ -470,7 +486,7 @@
Дотримуйтесь посібника \"Don't kill my app\" для вашого пристрою і застосуйте інструкції для встановлення GmsCore.
Це необхідно для того, щоб програма працювала."</string>
Це необхідно для того, щоб застосунок працював."</string>
<string name="gms_core_dialog_open_website_text">Відкрити сайт</string>
<string name="gms_core_dialog_not_whitelisted_using_battery_optimizations_message">"Необхідно вимкнути оптимізацію енергії для MicroG GmsCore, щоб запобігти проблемам.

View File

@ -212,6 +212,18 @@ Hạn chế:
<string name="revanced_hide_navigation_bar_summary">Ẩn thanh điều hướng.</string>
<string name="revanced_hide_navigation_label_title">Ẩn tên bên dưới nút</string>
<string name="revanced_hide_navigation_label_summary">Ẩn tên bên dưới các nút trên thanh điều hướng.</string>
<string name="revanced_replace_navigation_samples_button_title">Thay thế nút Đoạn nhạc</string>
<string name="revanced_replace_navigation_samples_button_summary">Thay thế nút Đoạn nhạc bằng nút Tìm kiếm.</string>
<string name="revanced_replace_navigation_upgrade_button_title">Thay thế nút Nâng cấp</string>
<string name="revanced_replace_navigation_upgrade_button_summary">Thay thế nút Nâng cấp bằng nút Cài đặt.</string>
<string name="revanced_replace_navigation_button_about_title">Về tính năng thay thế nút</string>
<string name="revanced_replace_navigation_button_about_summary">"Hiện tính năng này đang trong giai đoạn thử nghiệm.
Bản vá có một số hạn chế về mặt cấu trúc vì các hoạt động như Tìm kiếm và Cài đặt trong YouTube Music không được công khai.
Các sự cố đã biết:
• Khi một hoạt động thay thế như Tìm kiếm và Cài đặt bị đóng, trang khởi động sẽ được mở.
Bạn có thể mở Thay đổi cài đặt trang khởi động bằng cách nhấp vào đây."</string>
<!-- PreferenceScreen: Player -->
<string name="revanced_preference_screen_player_title">Trình phát</string>
<string name="revanced_add_miniplayer_next_button_title">Nút tiếp theo trong trình phát thu nhỏ</string>
@ -452,6 +464,11 @@ Chi tiết:
<string name="revanced_spoof_client_type_entry_android_music_5_29">Android Music 5.29.53</string>
<string name="revanced_spoof_client_type_entry_ios_music_6_21">iOS Music 6.21</string>
<string name="revanced_spoof_client_type_entry_ios_music_7_04">iOS Music 7.04</string>
<string name="revanced_spoof_player_parameter_title">Giả mạo thông số trình phát</string>
<string name="revanced_spoof_player_parameter_summary">"Giả mạo thông số trình phát để khắc phục sự cố phát.
Hạn chế:
• Đôi khi phụ đề hiển thị ở phía trên của trình phát thay vì ở phía dưới như thường lệ."</string>
<string name="revanced_watch_history_type_title">Kiểu nhật ký</string>
<string name="revanced_watch_history_type_summary">"• Gốc: Tuân theo cài đặt nhật ký xem của tài khoản Google, nhưng có thể không hoạt động do DNS hoặc VPN.
• Thay thế miền: Vẫn tuân theo cài đặt nhật ký xem của tài khoản Google.

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