Compare commits

..

16 Commits

Author SHA1 Message Date
semantic-release-bot
9c81d01cc8 chore: Release v5.5.0-dev.4 [skip ci]
# [5.5.0-dev.4](https://github.com/ReVanced/revanced-patches/compare/v5.5.0-dev.3...v5.5.0-dev.4) (2024-12-16)

### Bug Fixes

* **YouTube - Spoof video streams:** Make livestreams start at the current time when using iOS client   ([#4137](https://github.com/ReVanced/revanced-patches/issues/4137)) ([5965478](59654788fc))
2024-12-16 18:46:48 +00:00
LisoUseInAIKyrios
59654788fc fix(YouTube - Spoof video streams): Make livestreams start at the current time when using iOS client (#4137) 2024-12-16 22:43:50 +04:00
semantic-release-bot
4c44982cde chore: Release v5.5.0-dev.3 [skip ci]
# [5.5.0-dev.3](https://github.com/ReVanced/revanced-patches/compare/v5.5.0-dev.2...v5.5.0-dev.3) (2024-12-16)

### Features

* **YouTube - Hide feed components:** Remove obsolete `Hide search result shelf header` option ([#4134](https://github.com/ReVanced/revanced-patches/issues/4134)) ([a7aab9a](a7aab9aeca))
2024-12-16 18:18:23 +00:00
ILoveOpenSourceApplications
a7aab9aeca feat(YouTube - Hide feed components): Remove obsolete Hide search result shelf header option (#4134) 2024-12-16 19:15:22 +01:00
semantic-release-bot
7a8486f562 chore: Release v5.5.0-dev.2 [skip ci]
# [5.5.0-dev.2](https://github.com/ReVanced/revanced-patches/compare/v5.5.0-dev.1...v5.5.0-dev.2) (2024-12-16)

### Bug Fixes

* **YouTube Music:** Add `Spoof client patch` to fix playback ([#4132](https://github.com/ReVanced/revanced-patches/issues/4132)) ([ccb6a7f](ccb6a7f161))
2024-12-16 18:10:37 +00:00
oSumAtrIX
ccb6a7f161 fix(YouTube Music): Add Spoof client patch to fix playback (#4132) 2024-12-16 19:07:37 +01:00
LisoUseInAIKyrios
c792edfb77 chore: fix typo 2024-12-15 17:06:36 +04:00
semantic-release-bot
339cd6cc70 chore: Release v5.5.0-dev.1 [skip ci]
# [5.5.0-dev.1](https://github.com/ReVanced/revanced-patches/compare/v5.4.1-dev.1...v5.5.0-dev.1) (2024-12-15)

### Features

* **YouTube:** Add `Force original audio` patch ([#4122](https://github.com/ReVanced/revanced-patches/issues/4122)) ([68304fd](68304fd96a))
2024-12-15 12:55:13 +00:00
LisoUseInAIKyrios
68304fd96a feat(YouTube): Add Force original audio patch (#4122) 2024-12-15 16:51:34 +04:00
semantic-release-bot
4033048c9b chore: Release v5.4.1-dev.1 [skip ci]
## [5.4.1-dev.1](https://github.com/ReVanced/revanced-patches/compare/v5.4.0...v5.4.1-dev.1) (2024-12-14)

### Bug Fixes

* **Twitch:** Change recommended target to the latest app version ([9525137](9525137800))
2024-12-14 20:18:28 +00:00
LisoUseInAIKyrios
9525137800 fix(Twitch): Change recommended target to the latest app version 2024-12-15 00:15:00 +04:00
semantic-release-bot
0cf05fa2b0 chore: Release v5.4.0 [skip ci]
# [5.4.0](https://github.com/ReVanced/revanced-patches/compare/v5.3.0...v5.4.0) (2024-12-14)

### Bug Fixes

* **GmsCore support:** Adjust presentation of battery optimization dialog  ([#4091](https://github.com/ReVanced/revanced-patches/issues/4091)) ([2062660](2062660d60))
* **TikTok - Settings:** Use correct colors for dark mode ([#4087](https://github.com/ReVanced/revanced-patches/issues/4087)) ([18f1884](18f18849f3))
* **TikTok - SIM Spoof:** Change patch to default off to fix login ([#4084](https://github.com/ReVanced/revanced-patches/issues/4084)) ([3c47bff](3c47bfff1a))
* **YouTube - Hide ads:** Hide new type of featured promotions ([#4113](https://github.com/ReVanced/revanced-patches/issues/4113)) ([c348b10](c348b10a35))
* **YouTube - Spoof video streams:** Fix error toast that is sometimes shown ([#4090](https://github.com/ReVanced/revanced-patches/issues/4090)) ([3ec2577](3ec25778eb))
* **YouTube - Spoof video streams:** Resolve playback of age restricted videos ([#4096](https://github.com/ReVanced/revanced-patches/issues/4096)) ([32be03c](32be03c28d))
* **YouTube Music - Bypass certificate checks:** Add a recommended target version ([#4104](https://github.com/ReVanced/revanced-patches/issues/4104)) ([11216cd](11216cd942))
* **YouTube Music - Spoof video streams:** Disable stable volume ([#4097](https://github.com/ReVanced/revanced-patches/issues/4097)) ([78c5118](78c51182f2))

### Features

* Add Internal data documents provider patch ([#3830](https://github.com/ReVanced/revanced-patches/issues/3830)) ([89c45af](89c45afcc6))
* **Change package name:** Add options to change provider and permission package names to handle installation conflicts ([c44a4af](c44a4af406))
* **Twitch:** Make patches compatible with latest versions ([#4099](https://github.com/ReVanced/revanced-patches/issues/4099)) ([b217ca9](b217ca9f9d))
* **YouTube - Comments:** Add `Hide 'Chat summary'` ([#4110](https://github.com/ReVanced/revanced-patches/issues/4110)) ([8d06a4a](8d06a4a8ad))
2024-12-14 07:40:53 +00:00
oSumAtrIX
a9bfaf44e2 chore: Merge branch dev to main (#4078) 2024-12-14 08:37:30 +01:00
semantic-release-bot
7b08051371 chore: Release v5.4.0-dev.11 [skip ci]
# [5.4.0-dev.11](https://github.com/ReVanced/revanced-patches/compare/v5.4.0-dev.10...v5.4.0-dev.11) (2024-12-14)

### Features

* **Twitch:** Make patches compatible with latest versions ([#4099](https://github.com/ReVanced/revanced-patches/issues/4099)) ([b217ca9](b217ca9f9d))
2024-12-14 07:36:35 +00:00
oSumAtrIX
b217ca9f9d feat(Twitch): Make patches compatible with latest versions (#4099) 2024-12-14 08:33:27 +01:00
github-actions[bot]
9482092579 chore: Sync translations (#4116) 2024-12-14 11:18:19 +04:00
41 changed files with 848 additions and 347 deletions

View File

@@ -1,3 +1,67 @@
# [5.5.0-dev.4](https://github.com/ReVanced/revanced-patches/compare/v5.5.0-dev.3...v5.5.0-dev.4) (2024-12-16)
### Bug Fixes
* **YouTube - Spoof video streams:** Make livestreams start at the current time when using iOS client ([#4137](https://github.com/ReVanced/revanced-patches/issues/4137)) ([140f484](https://github.com/ReVanced/revanced-patches/commit/140f484b4b251b0dfa94163a63f61f45f5302052))
# [5.5.0-dev.3](https://github.com/ReVanced/revanced-patches/compare/v5.5.0-dev.2...v5.5.0-dev.3) (2024-12-16)
### Features
* **YouTube - Hide feed components:** Remove obsolete `Hide search result shelf header` option ([#4134](https://github.com/ReVanced/revanced-patches/issues/4134)) ([c71443a](https://github.com/ReVanced/revanced-patches/commit/c71443a08883ab10ef2553213c03b00e7c580a43))
# [5.5.0-dev.2](https://github.com/ReVanced/revanced-patches/compare/v5.5.0-dev.1...v5.5.0-dev.2) (2024-12-16)
### Bug Fixes
* **YouTube Music:** Add `Spoof client patch` to fix playback ([#4132](https://github.com/ReVanced/revanced-patches/issues/4132)) ([b092508](https://github.com/ReVanced/revanced-patches/commit/b0925088e8b41636e285cb234593d545604ce461))
# [5.5.0-dev.1](https://github.com/ReVanced/revanced-patches/compare/v5.4.1-dev.1...v5.5.0-dev.1) (2024-12-15)
### Features
* **YouTube:** Add `Force original audio` patch ([#4122](https://github.com/ReVanced/revanced-patches/issues/4122)) ([f4aa440](https://github.com/ReVanced/revanced-patches/commit/f4aa4406080b91f01d623e54b11b99ea849ddcdf))
## [5.4.1-dev.1](https://github.com/ReVanced/revanced-patches/compare/v5.4.0...v5.4.1-dev.1) (2024-12-14)
### Bug Fixes
* **Twitch:** Change recommended target to the latest app version ([fb32972](https://github.com/ReVanced/revanced-patches/commit/fb32972f4de92dac1fc5d73f56a392a671c4e94b))
# [5.4.0](https://github.com/ReVanced/revanced-patches/compare/v5.3.0...v5.4.0) (2024-12-14)
### Bug Fixes
* **GmsCore support:** Adjust presentation of battery optimization dialog ([#4091](https://github.com/ReVanced/revanced-patches/issues/4091)) ([5d8fc1b](https://github.com/ReVanced/revanced-patches/commit/5d8fc1bcd4e453298cfac086cdbdf279612bfb63))
* **TikTok - Settings:** Use correct colors for dark mode ([#4087](https://github.com/ReVanced/revanced-patches/issues/4087)) ([6bd22ff](https://github.com/ReVanced/revanced-patches/commit/6bd22ffa7e8af4d8f5d2d3b1711bd92c44b4e4aa))
* **TikTok - SIM Spoof:** Change patch to default off to fix login ([#4084](https://github.com/ReVanced/revanced-patches/issues/4084)) ([f4659a3](https://github.com/ReVanced/revanced-patches/commit/f4659a328eaf600e1e5f02a66fa2af4b6d8dc7c1))
* **YouTube - Hide ads:** Hide new type of featured promotions ([#4113](https://github.com/ReVanced/revanced-patches/issues/4113)) ([13c7592](https://github.com/ReVanced/revanced-patches/commit/13c7592b21defd27e3a7aa9b219ffc0247bb5914))
* **YouTube - Spoof video streams:** Fix error toast that is sometimes shown ([#4090](https://github.com/ReVanced/revanced-patches/issues/4090)) ([4c46cb2](https://github.com/ReVanced/revanced-patches/commit/4c46cb27a02c6f29626cd769b6a8e825645d5b16))
* **YouTube - Spoof video streams:** Resolve playback of age restricted videos ([#4096](https://github.com/ReVanced/revanced-patches/issues/4096)) ([839a404](https://github.com/ReVanced/revanced-patches/commit/839a4045f1bb1759d89047834e0b7695781e82a3))
* **YouTube Music - Bypass certificate checks:** Add a recommended target version ([#4104](https://github.com/ReVanced/revanced-patches/issues/4104)) ([17a5a6c](https://github.com/ReVanced/revanced-patches/commit/17a5a6c1691b0c23f601d3355b72f122c2bd5dcb))
* **YouTube Music - Spoof video streams:** Disable stable volume ([#4097](https://github.com/ReVanced/revanced-patches/issues/4097)) ([16bb9df](https://github.com/ReVanced/revanced-patches/commit/16bb9dfc299612f3922724c136878606987ab132))
### Features
* Add Internal data documents provider patch ([#3830](https://github.com/ReVanced/revanced-patches/issues/3830)) ([cb22f65](https://github.com/ReVanced/revanced-patches/commit/cb22f652ed678d81ffda9ece659b3971225d6931))
* **Change package name:** Add options to change provider and permission package names to handle installation conflicts ([75c740c](https://github.com/ReVanced/revanced-patches/commit/75c740c6ba2e0c62e567f7dc90cdad368fc4f372))
* **Twitch:** Make patches compatible with latest versions ([#4099](https://github.com/ReVanced/revanced-patches/issues/4099)) ([eecfbb7](https://github.com/ReVanced/revanced-patches/commit/eecfbb7122a9072e55e687f2c003f63108654888))
* **YouTube - Comments:** Add `Hide 'Chat summary'` ([#4110](https://github.com/ReVanced/revanced-patches/issues/4110)) ([269493c](https://github.com/ReVanced/revanced-patches/commit/269493cd198604f1438ea2850fb68fe900d0e56f))
# [5.4.0-dev.11](https://github.com/ReVanced/revanced-patches/compare/v5.4.0-dev.10...v5.4.0-dev.11) (2024-12-14)
### Features
* **Twitch:** Make patches compatible with latest versions ([#4099](https://github.com/ReVanced/revanced-patches/issues/4099)) ([eecfbb7](https://github.com/ReVanced/revanced-patches/commit/eecfbb7122a9072e55e687f2c003f63108654888))
# [5.4.0-dev.10](https://github.com/ReVanced/revanced-patches/compare/v5.4.0-dev.9...v5.4.0-dev.10) (2024-12-13)

View File

@@ -0,0 +1 @@
// Do not remove. Necessary for the extension plugin to be applied to the project.

View File

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

View File

@@ -0,0 +1,27 @@
package app.revanced.extension.music.spoof;
/**
* @noinspection unused
*/
public class SpoofClientPatch {
private static final int CLIENT_TYPE_ID = 26;
private static final String CLIENT_VERSION = "6.21";
private static final String DEVICE_MODEL = "iPhone16,2";
private static final String OS_VERSION = "17.7.2.21H221";
public static int getClientId() {
return CLIENT_TYPE_ID;
}
public static String getClientVersion() {
return CLIENT_VERSION;
}
public static String getClientModel() {
return DEVICE_MODEL;
}
public static String getOsVersion() {
return OS_VERSION;
}
}

View File

@@ -3,7 +3,7 @@ package app.revanced.extension.shared.settings;
import static java.lang.Boolean.FALSE;
import static java.lang.Boolean.TRUE;
import static app.revanced.extension.shared.settings.Setting.parent;
import static app.revanced.extension.shared.spoof.SpoofVideoStreamsPatch.ForceiOSAVCAvailability;
import static app.revanced.extension.shared.spoof.SpoofVideoStreamsPatch.SpoofiOSAvailability;
import app.revanced.extension.shared.spoof.AudioStreamLanguage;
import app.revanced.extension.shared.spoof.ClientType;
@@ -22,9 +22,9 @@ public class BaseSettings {
public static final IntegerSetting CHECK_ENVIRONMENT_WARNINGS_ISSUED = new IntegerSetting("revanced_check_environment_warnings_issued", 0, true, false);
public static final BooleanSetting SPOOF_VIDEO_STREAMS = new BooleanSetting("revanced_spoof_video_streams", TRUE, true, "revanced_spoof_video_streams_user_dialog_message");
public static final EnumSetting<AudioStreamLanguage> SPOOF_VIDEO_STREAMS_LANGUAGE = new EnumSetting<>("revanced_spoof_video_streams_language", AudioStreamLanguage.DEFAULT, parent(SPOOF_VIDEO_STREAMS));
public static final EnumSetting<AudioStreamLanguage> SPOOF_VIDEO_STREAMS_LANGUAGE = new EnumSetting<>("revanced_spoof_video_streams_language", AudioStreamLanguage.DEFAULT, new SpoofiOSAvailability());
public static final BooleanSetting SPOOF_VIDEO_STREAMS_IOS_FORCE_AVC = new BooleanSetting("revanced_spoof_video_streams_ios_force_avc", FALSE, true,
"revanced_spoof_video_streams_ios_force_avc_user_dialog_message", new ForceiOSAVCAvailability());
"revanced_spoof_video_streams_ios_force_avc_user_dialog_message", new SpoofiOSAvailability());
public static final EnumSetting<ClientType> SPOOF_VIDEO_STREAMS_CLIENT_TYPE = new EnumSetting<>("revanced_spoof_video_streams_client", ClientType.ANDROID_VR, true, parent(SPOOF_VIDEO_STREAMS));
}

View File

@@ -3,6 +3,11 @@ package app.revanced.extension.shared.spoof;
import java.util.Locale;
public enum AudioStreamLanguage {
/**
* YouTube default.
* Can be the original language or can be app language,
* depending on what YouTube decides to pick as the default.
*/
DEFAULT,
// Language codes found in locale_config.xml
@@ -86,15 +91,21 @@ public enum AudioStreamLanguage {
private final String iso639_1;
AudioStreamLanguage() {
iso639_1 = name().replace('_', '-');
String name = name();
final int regionSeparatorIndex = name.indexOf('_');
if (regionSeparatorIndex >= 0) {
iso639_1 = name.substring(0, regionSeparatorIndex).toLowerCase(Locale.US)
+ name.substring(regionSeparatorIndex);
} else {
iso639_1 = name().toLowerCase(Locale.US);
}
}
public String getIso639_1() {
// Changing the app language does not force the app to completely restart,
// so the default needs to be the current language and not a static field.
if (this == DEFAULT) {
// Android VR requires uppercase language code.
return Locale.getDefault().toLanguageTag().toUpperCase(Locale.US);
return Locale.getDefault().toLanguageTag();
}
return iso639_1;

View File

@@ -17,7 +17,7 @@ public enum ClientType {
"32", // Android 12.1
"1.56.21",
true,
true),
false),
// Specific for kids videos.
IOS(5,
"IOS",
@@ -40,21 +40,8 @@ public enum ClientType {
? "17.40.5"
: "19.47.7",
false,
true),
/**
* Android VR with no language code.
* Used for age restricted videos and YouTube Music to disable stable volume.
*/
ANDROID_VR_NO_HL(
ANDROID_VR.id,
ANDROID_VR.clientName,
ANDROID_VR.deviceModel,
ANDROID_VR.osVersion,
ANDROID_VR.userAgent,
ANDROID_VR.androidSdkVersion,
ANDROID_VR.clientVersion,
ANDROID_VR.canLogin,
false);
true
);
private static boolean forceAVC() {
return BaseSettings.SPOOF_VIDEO_STREAMS_IOS_FORCE_AVC.get();

View File

@@ -16,20 +16,16 @@ import app.revanced.extension.shared.spoof.requests.StreamingDataRequest;
@SuppressWarnings("unused")
public class SpoofVideoStreamsPatch {
private static final boolean SPOOF_STREAMING_DATA = BaseSettings.SPOOF_VIDEO_STREAMS.get();
private static final boolean FIX_HLS_CURRENT_TIME = SPOOF_STREAMING_DATA
&& BaseSettings.SPOOF_VIDEO_STREAMS_CLIENT_TYPE.get() == ClientType.IOS;
/**
* Any unreachable ip address. Used to intentionally fail requests.
*/
private static final String UNREACHABLE_HOST_URI_STRING = "https://127.0.0.0";
private static final Uri UNREACHABLE_HOST_URI = Uri.parse(UNREACHABLE_HOST_URI_STRING);
/**
* Injection point. Used by YT Music to disable stable volume.
*/
public static void setClientTypeToAndroidVrNoHl() {
Logger.printDebug(() -> "Setting stream spoofing to: " + ClientType.ANDROID_VR_NO_HL);
BaseSettings.SPOOF_VIDEO_STREAMS_CLIENT_TYPE.save(ClientType.ANDROID_VR_NO_HL);
}
/**
* Injection point.
* Blocks /get_watch requests by returning an unreachable URI.
@@ -173,10 +169,24 @@ public class SpoofVideoStreamsPatch {
return postData;
}
public static final class ForceiOSAVCAvailability implements Setting.Availability {
/**
* Injection point.
*
* Fixes iOS livestreams starting from the beginning.
*/
public static boolean fixHLSCurrentTime(boolean original) {
if (FIX_HLS_CURRENT_TIME) {
return false;
}
return original;
}
public static final class SpoofiOSAvailability implements Setting.Availability {
@Override
public boolean isAvailable() {
return BaseSettings.SPOOF_VIDEO_STREAMS.get() && BaseSettings.SPOOF_VIDEO_STREAMS_CLIENT_TYPE.get() == ClientType.IOS;
return BaseSettings.SPOOF_VIDEO_STREAMS.get()
&& BaseSettings.SPOOF_VIDEO_STREAMS_CLIENT_TYPE.get() == ClientType.IOS;
}
}
}

View File

@@ -19,7 +19,9 @@ final class PlayerRoutes {
"?fields=streamingData" +
"&alt=proto"
).compile();
private static final String YT_API_URL = "https://youtubei.googleapis.com/youtubei/v1/";
/**
* TCP connection and HTTP read timeout
*/

View File

@@ -0,0 +1,41 @@
package app.revanced.extension.youtube.patches;
import app.revanced.extension.shared.Logger;
import app.revanced.extension.youtube.settings.Settings;
@SuppressWarnings("unused")
public class ForceOriginalAudioPatch {
private static final String DEFAULT_AUDIO_TRACKS_IDENTIFIER = "original";
/**
* Injection point.
*/
public static boolean isDefaultAudioStream(boolean isDefault, String audioTrackId, String audioTrackDisplayName) {
try {
if (!Settings.FORCE_ORIGINAL_AUDIO.get()) {
return isDefault;
}
if (audioTrackDisplayName.isEmpty()) {
// Older app targets can have empty audio tracks and these might be placeholders.
// The real audio tracks are called after these.
return isDefault;
}
Logger.printDebug(() -> "default: " + String.format("%-5s", isDefault) + " id: "
+ String.format("%-8s", audioTrackId) + " name:" + audioTrackDisplayName);
final boolean isOriginal = audioTrackDisplayName.contains(DEFAULT_AUDIO_TRACKS_IDENTIFIER);
if (isOriginal) {
Logger.printDebug(() -> "Using audio: " + audioTrackId);
}
return isOriginal;
} catch (Exception ex) {
Logger.printException(() -> "isDefaultAudioStream failure", ex);
}
return isDefault;
}
}

View File

@@ -36,7 +36,6 @@ public final class LayoutComponentsFilter extends Filter {
);
private final StringTrieSearch exceptions = new StringTrieSearch();
private final StringFilterGroup searchResultShelfHeader;
private final StringFilterGroup inFeedSurvey;
private final StringFilterGroup notifyMe;
private final StringFilterGroup expandableMetadata;
@@ -194,11 +193,6 @@ public final class LayoutComponentsFilter extends Filter {
"timed_reaction"
);
searchResultShelfHeader = new StringFilterGroup(
Settings.HIDE_SEARCH_RESULT_SHELF_HEADER,
"shelf_header.eml"
);
notifyMe = new StringFilterGroup(
Settings.HIDE_NOTIFY_ME_BUTTON,
"set_reminder_button"
@@ -324,9 +318,6 @@ public final class LayoutComponentsFilter extends Filter {
return false;
}
// TODO: This also hides the feed Shorts shelf header
if (matchedGroup == searchResultShelfHeader && contentIndex != 0) return false;
if (matchedGroup == horizontalShelves) {
if (contentIndex == 0 && hideShelves()) {
return super.isFiltered(path, identifier, protobufBufferArray, matchedGroup, contentType, contentIndex);

View File

@@ -52,6 +52,8 @@ public class Settings extends BaseSettings {
public static final FloatSetting PLAYBACK_SPEED_DEFAULT = new FloatSetting("revanced_playback_speed_default", -2.0f);
public static final StringSetting CUSTOM_PLAYBACK_SPEEDS = new StringSetting("revanced_custom_playback_speeds",
"0.25\n0.5\n0.75\n0.9\n0.95\n1.0\n1.05\n1.1\n1.25\n1.5\n1.75\n2.0\n3.0\n4.0\n5.0", true);
// Audio
public static final BooleanSetting FORCE_ORIGINAL_AUDIO = new BooleanSetting("revanced_force_original_audio", FALSE);
// Ads
public static final BooleanSetting HIDE_BUTTONED_ADS = new BooleanSetting("revanced_hide_buttoned_ads", TRUE);
@@ -91,7 +93,6 @@ public class Settings extends BaseSettings {
public static final BooleanSetting HIDE_NOTIFY_ME_BUTTON = new BooleanSetting("revanced_hide_notify_me_button", TRUE);
public static final BooleanSetting HIDE_PLAYABLES = new BooleanSetting("revanced_hide_playables", TRUE);
public static final BooleanSetting HIDE_SEARCH_RESULT_RECOMMENDATIONS = new BooleanSetting("revanced_hide_search_result_recommendations", TRUE);
public static final BooleanSetting HIDE_SEARCH_RESULT_SHELF_HEADER = new BooleanSetting("revanced_hide_search_result_shelf_header", FALSE);
public static final BooleanSetting HIDE_SHOW_MORE_BUTTON = new BooleanSetting("revanced_hide_show_more_button", TRUE, true);
// Alternative thumbnails
public static final EnumSetting<ThumbnailOption> ALT_THUMBNAIL_HOME = new EnumSetting<>("revanced_alt_thumbnail_home", ThumbnailOption.ORIGINAL);

View File

@@ -48,35 +48,44 @@ public class ReVancedPreferenceFragment extends AbstractPreferenceFragment {
/**
* Sorts a preference list by menu entries, but preserves the first value as the first entry.
*
* @noinspection SameParameterValue
*/
private static void sortListPreferenceByValues(ListPreference listPreference) {
private static void sortListPreferenceByValues(ListPreference listPreference, int firstEntriesToPreserve) {
CharSequence[] entries = listPreference.getEntries();
CharSequence[] entryValues = listPreference.getEntryValues();
final int entrySize = entries.length;
if (entrySize != entryValues.length) {
// Xml array declaration has a missing/extra entry.
throw new IllegalStateException();
}
// Ensure the first entry remains the first after sorting.
CharSequence firstEntry = entries[0];
CharSequence firstEntryValue = entryValues[0];
List<Pair<String, String>> firstPairs = new ArrayList<>(firstEntriesToPreserve);
List<Pair<String, String>> pairsToSort = new ArrayList<>(entrySize);
List<Pair<String, String>> entryPairs = new ArrayList<>(entrySize);
for (int i = 1; i < entrySize; i++) {
entryPairs.add(new Pair<>(entries[i].toString(), entryValues[i].toString()));
for (int i = 0; i < entrySize; i++) {
Pair<String, String> pair = new Pair<>(entries[i].toString(), entryValues[i].toString());
if (i < firstEntriesToPreserve) {
firstPairs.add(pair);
} else {
pairsToSort.add(pair);
}
}
Collections.sort(entryPairs, (pair1, pair2) -> pair1.first.compareToIgnoreCase(pair2.first));
Collections.sort(pairsToSort, (pair1, pair2) -> pair1.first.compareToIgnoreCase(pair2.first));
CharSequence[] sortedEntries = new CharSequence[entrySize];
CharSequence[] sortedEntryValues = new CharSequence[entrySize];
sortedEntries[0] = firstEntry;
sortedEntryValues[0] = firstEntryValue;
int i = 0;
for (Pair<String, String> pair : firstPairs) {
sortedEntries[i] = pair.first;
sortedEntryValues[i] = pair.second;
i++;
}
int i = 1;
for (Pair<String, String> pair : entryPairs) {
for (Pair<String, String> pair : pairsToSort) {
sortedEntries[i] = pair.first;
sortedEntryValues[i] = pair.second;
i++;
@@ -102,7 +111,7 @@ public class ReVancedPreferenceFragment extends AbstractPreferenceFragment {
preference = findPreference(Settings.SPOOF_VIDEO_STREAMS_LANGUAGE.key);
if (preference instanceof ListPreference languagePreference) {
sortListPreferenceByValues(languagePreference);
sortListPreferenceByValues(languagePreference, 1);
}
} catch (Exception ex) {
Logger.printException(() -> "initialize failure", ex);

View File

@@ -3,4 +3,4 @@ org.gradle.jvmargs = -Xms512M -Xmx2048M
org.gradle.parallel = true
android.useAndroidX = true
kotlin.code.style = official
version = 5.4.0-dev.10
version = 5.5.0-dev.4

View File

@@ -324,8 +324,12 @@ public final class app/revanced/patches/music/misc/gms/GmsCoreSupportPatchKt {
public static final fun getGmsCoreSupportPatch ()Lapp/revanced/patcher/patch/BytecodePatch;
}
public final class app/revanced/patches/music/misc/spoof/SpoofVideoStreamsPatchKt {
public static final fun getSpoofVideoStreamsPatch ()Lapp/revanced/patcher/patch/BytecodePatch;
public final class app/revanced/patches/music/misc/spoof/SpoofClientPatchKt {
public static final fun getSpoofClientPatch ()Lapp/revanced/patcher/patch/BytecodePatch;
}
public final class app/revanced/patches/music/misc/spoof/UserAgentClientSpoofPatchKt {
public static final fun getUserAgentClientSpoofPatch ()Lapp/revanced/patcher/patch/BytecodePatch;
}
public final class app/revanced/patches/myexpenses/misc/pro/UnlockProPatchKt {
@@ -766,6 +770,10 @@ public final class app/revanced/patches/shared/misc/spoof/SpoofVideoStreamsPatch
public static synthetic fun spoofVideoStreamsPatch$default (Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function1;ILjava/lang/Object;)Lapp/revanced/patcher/patch/BytecodePatch;
}
public final class app/revanced/patches/shared/misc/spoof/UserAgentClientSpoofPatchKt {
public static final fun userAgentClientSpoofPatch (Ljava/lang/String;)Lapp/revanced/patcher/patch/BytecodePatch;
}
public final class app/revanced/patches/solidexplorer2/functionality/filesize/RemoveFileSizeLimitPatchKt {
public static final fun getRemoveFileSizeLimitPatch ()Lapp/revanced/patcher/patch/BytecodePatch;
}
@@ -1392,6 +1400,10 @@ public final class app/revanced/patches/youtube/shared/FingerprintsKt {
public static final fun getRollingNumberTextViewAnimationUpdateFingerprint ()Lapp/revanced/patcher/Fingerprint;
}
public final class app/revanced/patches/youtube/video/audio/ForceOriginalAudioPatchKt {
public static final fun getForceOriginalAudioPatch ()Lapp/revanced/patcher/patch/BytecodePatch;
}
public final class app/revanced/patches/youtube/video/information/VideoInformationPatchKt {
public static final fun getVideoInformationPatch ()Lapp/revanced/patcher/patch/BytecodePatch;
public static final fun userSelectedPlaybackSpeedHook (Ljava/lang/String;Ljava/lang/String;)V

View File

@@ -4,5 +4,6 @@ import app.revanced.patches.music.misc.extension.hooks.applicationInitHook
import app.revanced.patches.shared.misc.extension.sharedExtensionPatch
val sharedExtensionPatch = sharedExtensionPatch(
"music",
applicationInitHook,
)

View File

@@ -4,7 +4,7 @@ import app.revanced.patcher.patch.Option
import app.revanced.patches.music.misc.extension.sharedExtensionPatch
import app.revanced.patches.music.misc.gms.Constants.MUSIC_PACKAGE_NAME
import app.revanced.patches.music.misc.gms.Constants.REVANCED_MUSIC_PACKAGE_NAME
import app.revanced.patches.music.misc.spoof.spoofVideoStreamsPatch
import app.revanced.patches.music.misc.spoof.spoofClientPatch
import app.revanced.patches.shared.castContextFetchFingerprint
import app.revanced.patches.shared.misc.gms.gmsCoreSupportPatch
import app.revanced.patches.shared.primeMethodFingerprint
@@ -21,7 +21,7 @@ val gmsCoreSupportPatch = gmsCoreSupportPatch(
extensionPatch = sharedExtensionPatch,
gmsCoreSupportResourcePatchFactory = ::gmsCoreSupportResourcePatch,
) {
dependsOn(spoofVideoStreamsPatch)
dependsOn(spoofClientPatch)
compatibleWith(MUSIC_PACKAGE_NAME)
}

View File

@@ -0,0 +1,39 @@
package app.revanced.patches.music.misc.spoof
import app.revanced.patcher.fingerprint
import com.android.tools.smali.dexlib2.AccessFlags
import com.android.tools.smali.dexlib2.Opcode
internal val playerRequestConstructorFingerprint = fingerprint {
accessFlags(AccessFlags.PUBLIC, AccessFlags.CONSTRUCTOR)
strings("player")
}
/**
* Matches using the class found in [playerRequestConstructorFingerprint].
*/
internal val createPlayerRequestBodyFingerprint = fingerprint {
parameters("L")
returns("V")
opcodes(
Opcode.CHECK_CAST,
Opcode.IGET,
Opcode.AND_INT_LIT16,
)
strings("ms")
}
/**
* Used to get a reference to other clientInfo fields.
*/
internal val setClientInfoFieldsFingerprint = fingerprint {
returns("L")
strings("Google Inc.")
}
/**
* Used to get a reference to the clientInfo and clientInfo.clientVersion field.
*/
internal val setClientInfoClientVersionFingerprint = fingerprint {
strings("10.29")
}

View File

@@ -0,0 +1,105 @@
package app.revanced.patches.music.misc.spoof
import app.revanced.patcher.extensions.InstructionExtensions.addInstruction
import app.revanced.patcher.extensions.InstructionExtensions.addInstructions
import app.revanced.patcher.extensions.InstructionExtensions.getInstruction
import app.revanced.patcher.extensions.InstructionExtensions.instructions
import app.revanced.patcher.patch.bytecodePatch
import app.revanced.patcher.util.proxy.mutableTypes.MutableMethod.Companion.toMutable
import app.revanced.patches.music.misc.extension.sharedExtensionPatch
import app.revanced.util.getReference
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.reference.FieldReference
import com.android.tools.smali.dexlib2.iface.reference.TypeReference
import com.android.tools.smali.dexlib2.immutable.ImmutableMethod
import com.android.tools.smali.dexlib2.immutable.ImmutableMethodParameter
internal const val EXTENSION_CLASS_DESCRIPTOR =
"Lapp/revanced/extension/music/spoof/SpoofClientPatch;"
// TODO: Replace this patch with spoofVideoStreamsPatch once possible.
val spoofClientPatch = bytecodePatch(
name = "Spoof client",
description = "Spoofs the client to fix playback.",
) {
compatibleWith("com.google.android.apps.youtube.music")
dependsOn(
sharedExtensionPatch,
// TODO: Add settingsPatch
userAgentClientSpoofPatch,
)
execute {
val playerRequestClass = playerRequestConstructorFingerprint.classDef
val createPlayerRequestBodyMatch = createPlayerRequestBodyFingerprint.match(playerRequestClass)
val clientInfoContainerClass = createPlayerRequestBodyMatch.method
.getInstruction(createPlayerRequestBodyMatch.patternMatch!!.startIndex)
.getReference<TypeReference>()!!.type
val clientInfoField = setClientInfoClientVersionFingerprint.method.instructions.first {
it.opcode == Opcode.IPUT_OBJECT && it.getReference<FieldReference>()!!.definingClass == clientInfoContainerClass
}.getReference<FieldReference>()!!
val setClientInfoFieldInstructions = setClientInfoFieldsFingerprint.method.instructions.filter {
(it.opcode == Opcode.IPUT_OBJECT || it.opcode == Opcode.IPUT) &&
it.getReference<FieldReference>()!!.definingClass == clientInfoField.type
}.map { it.getReference<FieldReference>()!! }
// Offsets are known for the fields in the clientInfo object.
val clientIdField = setClientInfoFieldInstructions[0]
val clientModelField = setClientInfoFieldInstructions[5]
val osVersionField = setClientInfoFieldInstructions[7]
val clientVersionField = setClientInfoClientVersionFingerprint.method
.getInstruction(setClientInfoClientVersionFingerprint.stringMatches!!.first().index + 1)
.getReference<FieldReference>()
// Helper method to spoof the client info.
val spoofClientInfoMethod = ImmutableMethod(
playerRequestClass.type,
"spoofClientInfo",
listOf(ImmutableMethodParameter(clientInfoContainerClass, null, null)),
"V",
AccessFlags.PRIVATE.value or AccessFlags.STATIC.value,
null,
null,
MutableMethodImplementation(3),
).toMutable().also(playerRequestClass.methods::add).apply {
addInstructions(
"""
iget-object v0, p0, $clientInfoField
invoke-static { }, $EXTENSION_CLASS_DESCRIPTOR->getClientId()I
move-result v1
iput v1, v0, $clientIdField
invoke-static { }, $EXTENSION_CLASS_DESCRIPTOR->getClientModel()Ljava/lang/String;
move-result-object v1
iput-object v1, v0, $clientModelField
invoke-static { }, $EXTENSION_CLASS_DESCRIPTOR->getClientVersion()Ljava/lang/String;
move-result-object v1
iput-object v1, v0, $clientVersionField
invoke-static { }, $EXTENSION_CLASS_DESCRIPTOR->getOsVersion()Ljava/lang/String;
move-result-object v1
iput-object v1, v0, $osVersionField
return-void
""",
)
}
createPlayerRequestBodyMatch.method.apply {
val checkCastIndex = createPlayerRequestBodyMatch.patternMatch!!.startIndex
val clientInfoContainerRegister = getInstruction<OneRegisterInstruction>(checkCastIndex).registerA
addInstruction(checkCastIndex + 1, "invoke-static {v$clientInfoContainerRegister}, $spoofClientInfoMethod")
}
}
}

View File

@@ -1,15 +0,0 @@
package app.revanced.patches.music.misc.spoof
import app.revanced.patcher.extensions.InstructionExtensions.addInstruction
import app.revanced.patches.music.misc.gms.musicActivityOnCreateFingerprint
import app.revanced.patches.shared.misc.spoof.EXTENSION_CLASS_DESCRIPTOR
import app.revanced.patches.shared.misc.spoof.spoofVideoStreamsPatch
val spoofVideoStreamsPatch = spoofVideoStreamsPatch({
compatibleWith("com.google.android.apps.youtube.music")
}, {
musicActivityOnCreateFingerprint.method.addInstruction(
0,
"invoke-static { }, $EXTENSION_CLASS_DESCRIPTOR->setClientTypeToAndroidVrNoHl()V"
)
})

View File

@@ -0,0 +1,5 @@
package app.revanced.patches.music.misc.spoof
import app.revanced.patches.shared.misc.spoof.userAgentClientSpoofPatch
val userAgentClientSpoofPatch = userAgentClientSpoofPatch("com.google.android.apps.youtube.music")

View File

@@ -1,6 +1,7 @@
package app.revanced.patches.shared.misc.spoof
import app.revanced.patcher.fingerprint
import app.revanced.util.literal
import com.android.tools.smali.dexlib2.AccessFlags
import com.android.tools.smali.dexlib2.Opcode
@@ -110,3 +111,13 @@ internal val buildMediaDataSourceFingerprint = fingerprint {
"Ljava/lang/Object;",
)
}
internal const val HLS_CURRENT_TIME_FEATURE_FLAG = 45355374L
internal val hlsCurrentTimeFingerprint = fingerprint {
accessFlags(AccessFlags.PUBLIC, AccessFlags.FINAL)
parameters("Z", "L")
literal {
HLS_CURRENT_TIME_FEATURE_FLAG
}
}

View File

@@ -12,6 +12,7 @@ import app.revanced.patcher.util.proxy.mutableTypes.MutableMethod.Companion.toMu
import app.revanced.patches.all.misc.resources.addResourcesPatch
import app.revanced.util.getReference
import app.revanced.util.indexOfFirstInstructionOrThrow
import app.revanced.util.insertFeatureFlagBooleanOverride
import com.android.tools.smali.dexlib2.AccessFlags
import com.android.tools.smali.dexlib2.Opcode
import com.android.tools.smali.dexlib2.builder.MutableMethodImplementation
@@ -201,6 +202,15 @@ fun spoofVideoStreamsPatch(
}
// endregion
// region Fix iOS livestream current time.
hlsCurrentTimeFingerprint.method.insertFeatureFlagBooleanOverride(
HLS_CURRENT_TIME_FEATURE_FLAG,
"$EXTENSION_CLASS_DESCRIPTOR->fixHLSCurrentTime(Z)Z"
)
// endregion
executeBlock()
}
}

View File

@@ -0,0 +1,81 @@
package app.revanced.patches.shared.misc.spoof
import app.revanced.patcher.extensions.InstructionExtensions.getInstruction
import app.revanced.patcher.extensions.InstructionExtensions.replaceInstruction
import app.revanced.patches.all.misc.transformation.IMethodCall
import app.revanced.patches.all.misc.transformation.filterMapInstruction35c
import app.revanced.patches.all.misc.transformation.transformInstructionsPatch
import app.revanced.util.getReference
import app.revanced.util.indexOfFirstInstruction
import com.android.tools.smali.dexlib2.Opcode
import com.android.tools.smali.dexlib2.iface.instruction.OneRegisterInstruction
import com.android.tools.smali.dexlib2.iface.reference.MethodReference
import com.android.tools.smali.dexlib2.iface.reference.StringReference
private const val USER_AGENT_STRING_BUILDER_APPEND_METHOD_REFERENCE =
"Ljava/lang/StringBuilder;->append(Ljava/lang/String;)Ljava/lang/StringBuilder;"
fun userAgentClientSpoofPatch(originalPackageName: String) = transformInstructionsPatch(
filterMap = { classDef, _, instruction, instructionIndex ->
filterMapInstruction35c<MethodCall>(
"Lapp/revanced/extension",
classDef,
instruction,
instructionIndex,
)
},
transform = transform@{ mutableMethod, entry ->
val (_, _, instructionIndex) = entry
// Replace the result of context.getPackageName(), if it is used in a user agent string.
mutableMethod.apply {
// After context.getPackageName() the result is moved to a register.
val targetRegister = (
getInstruction(instructionIndex + 1)
as? OneRegisterInstruction ?: return@transform
).registerA
// IndexOutOfBoundsException is technically possible here,
// but no such occurrences are present in the app.
val referee = getInstruction(instructionIndex + 2).getReference<MethodReference>()?.toString()
// Only replace string builder usage.
if (referee != USER_AGENT_STRING_BUILDER_APPEND_METHOD_REFERENCE) {
return@transform
}
// Do not change the package name in methods that use resources, or for methods that use GmsCore.
// Changing these package names will result in playback limitations,
// particularly Android VR background audio only playback.
val resourceOrGmsStringInstructionIndex = indexOfFirstInstruction {
val reference = getReference<StringReference>()
opcode == Opcode.CONST_STRING &&
(reference?.string == "android.resource://" || reference?.string == "gcore_")
}
if (resourceOrGmsStringInstructionIndex >= 0) {
return@transform
}
// Overwrite the result of context.getPackageName() with the original package name.
replaceInstruction(
instructionIndex + 1,
"const-string v$targetRegister, \"$originalPackageName\"",
)
}
},
)
@Suppress("unused")
private enum class MethodCall(
override val definedClassName: String,
override val methodName: String,
override val methodParams: Array<String>,
override val returnType: String,
) : IMethodCall {
GetPackageName(
"Landroid/content/Context;",
"getPackageName",
emptyArray(),
"Ljava/lang/String;",
),
}

View File

@@ -21,7 +21,7 @@ val audioAdsPatch = bytecodePatch(
addResourcesPatch,
)
compatibleWith("tv.twitch.android.app"("15.4.1", "16.1.0", "16.9.1"))
compatibleWith("tv.twitch.android.app")
execute {
addResources("twitch", "ad.audio.audioAdsPatch")

View File

@@ -19,7 +19,7 @@ val embeddedAdsPatch = bytecodePatch(
settingsPatch,
)
compatibleWith("tv.twitch.android.app"("15.4.1", "16.1.0", "16.9.1"))
compatibleWith("tv.twitch.android.app")
execute {
addResources("twitch", "ad.embedded.embeddedAdsPatch")

View File

@@ -4,6 +4,6 @@ import app.revanced.patcher.fingerprint
internal val createsUsherClientFingerprint = fingerprint {
custom { method, _ ->
method.definingClass.endsWith("Ltv/twitch/android/network/OkHttpClientFactory;") && method.name == "buildOkHttpClient"
method.name == "buildOkHttpClient" && method.definingClass.endsWith("Ltv/twitch/android/network/OkHttpClientFactory;")
}
}

View File

@@ -141,24 +141,21 @@ val videoAdsPatch = bytecodePatch(
)
// Spoof showAds JSON field.
contentConfigShowAdsFingerprint.method.addInstructions(
// Late versions of the app don't have the method anymore.
contentConfigShowAdsFingerprint.methodOrNull?.addInstructions(
0,
"""
${createConditionInstructions("v0")}
const/4 v0, 0
:$skipLabelName
return v0
""",
${createConditionInstructions("v0")}
const/4 v0, 0
:$skipLabelName
return v0
""",
)
}
},
)
compatibleWith(
"tv.twitch.android.app"(
"15.4.1",
"16.1.0",
"16.9.1",
),
"tv.twitch.android.app",
)
}

View File

@@ -22,7 +22,7 @@ val showDeletedMessagesPatch = bytecodePatch(
addResourcesPatch,
)
compatibleWith("tv.twitch.android.app"("15.4.1", "16.1.0", "16.9.1"))
compatibleWith("tv.twitch.android.app")
fun createSpoilerConditionInstructions(register: String = "v0") = """
invoke-static {}, Lapp/revanced/extension/twitch/patches/ShowDeletedMessagesPatch;->shouldUseSpoiler()Z

View File

@@ -20,7 +20,7 @@ val autoClaimChannelPointsPatch = bytecodePatch(
addResourcesPatch,
)
compatibleWith("tv.twitch.android.app"("15.4.1", "16.1.0", "16.9.1"))
compatibleWith("tv.twitch.android.app")
execute {
addResources("twitch", "chat.autoclaim.autoClaimChannelPointsPatch")

View File

@@ -48,13 +48,7 @@ val settingsPatch = bytecodePatch(
settingsPatch(preferences = preferences),
)
compatibleWith(
"tv.twitch.android.app"(
"15.4.1",
"16.1.0",
"16.9.1",
),
)
compatibleWith("tv.twitch.android.app")
execute {
addResources("twitch", "misc.settings.settingsPatch")

View File

@@ -222,7 +222,6 @@ val hideLayoutComponentsPatch = bytecodePatch(
SwitchPreference("revanced_hide_notify_me_button"),
SwitchPreference("revanced_hide_playables"),
SwitchPreference("revanced_hide_search_result_recommendations"),
SwitchPreference("revanced_hide_search_result_shelf_header"),
SwitchPreference("revanced_hide_show_more_button"),
SwitchPreference("revanced_hide_doodles"),
)

View File

@@ -110,22 +110,22 @@ val enableDebuggingPatch = bytecodePatch(
"""
)
}
}
experimentalStringFeatureFlagFingerprint.match(
experimentalFeatureFlagParentFingerprint.originalClassDef
).method.apply {
val insertIndex = indexOfFirstInstructionReversedOrThrow(Opcode.MOVE_RESULT_OBJECT)
experimentalStringFeatureFlagFingerprint.match(
experimentalFeatureFlagParentFingerprint.originalClassDef
).method.apply {
val insertIndex = indexOfFirstInstructionReversedOrThrow(Opcode.MOVE_RESULT_OBJECT)
addInstructions(
insertIndex,
"""
move-result-object v0
invoke-static { v0, p1, p2, p3 }, $EXTENSION_CLASS_DESCRIPTOR->isStringFeatureFlagEnabled(Ljava/lang/String;JLjava/lang/String;)Ljava/lang/String;
move-result-object v0
return-object v0
"""
)
addInstructions(
insertIndex,
"""
move-result-object v0
invoke-static { v0, p1, p2, p3 }, $EXTENSION_CLASS_DESCRIPTOR->isStringFeatureFlagEnabled(Ljava/lang/String;JLjava/lang/String;)Ljava/lang/String;
move-result-object v0
return-object v0
"""
)
}
}
// There exists other experimental accessor methods for byte[]

View File

@@ -1,82 +1,5 @@
package app.revanced.patches.youtube.misc.spoof
import app.revanced.patcher.extensions.InstructionExtensions.getInstruction
import app.revanced.patcher.extensions.InstructionExtensions.replaceInstruction
import app.revanced.patches.all.misc.transformation.IMethodCall
import app.revanced.patches.all.misc.transformation.filterMapInstruction35c
import app.revanced.patches.all.misc.transformation.transformInstructionsPatch
import app.revanced.util.getReference
import app.revanced.util.indexOfFirstInstruction
import com.android.tools.smali.dexlib2.Opcode
import com.android.tools.smali.dexlib2.iface.instruction.OneRegisterInstruction
import com.android.tools.smali.dexlib2.iface.reference.MethodReference
import com.android.tools.smali.dexlib2.iface.reference.StringReference
import app.revanced.patches.shared.misc.spoof.userAgentClientSpoofPatch
private const val ORIGINAL_PACKAGE_NAME = "com.google.android.youtube"
private const val USER_AGENT_STRING_BUILDER_APPEND_METHOD_REFERENCE =
"Ljava/lang/StringBuilder;->append(Ljava/lang/String;)Ljava/lang/StringBuilder;"
val userAgentClientSpoofPatch = transformInstructionsPatch(
filterMap = { classDef, _, instruction, instructionIndex ->
filterMapInstruction35c<MethodCall>(
"Lapp/revanced/extension",
classDef,
instruction,
instructionIndex,
)
},
transform = transform@{ mutableMethod, entry ->
val (_, _, instructionIndex) = entry
// Replace the result of context.getPackageName(), if it is used in a user agent string.
mutableMethod.apply {
// After context.getPackageName() the result is moved to a register.
val targetRegister = (
getInstruction(instructionIndex + 1)
as? OneRegisterInstruction ?: return@transform
).registerA
// IndexOutOfBoundsException is technically possible here,
// but no such occurrences are present in the app.
val referee = getInstruction(instructionIndex + 2).getReference<MethodReference>()?.toString()
// Only replace string builder usage.
if (referee != USER_AGENT_STRING_BUILDER_APPEND_METHOD_REFERENCE) {
return@transform
}
// Do not change the package name in methods that use resources, or for methods that use GmsCore.
// Changing these package names will result in playback limitations,
// particularly Android VR background audio only playback.
val resourceOrGmsStringInstructionIndex = indexOfFirstInstruction {
val reference = getReference<StringReference>()
opcode == Opcode.CONST_STRING &&
(reference?.string == "android.resource://" || reference?.string == "gcore_")
}
if (resourceOrGmsStringInstructionIndex >= 0) {
return@transform
}
// Overwrite the result of context.getPackageName() with the original package name.
replaceInstruction(
instructionIndex + 1,
"const-string v$targetRegister, \"$ORIGINAL_PACKAGE_NAME\"",
)
}
},
)
@Suppress("unused")
private enum class MethodCall(
override val definedClassName: String,
override val methodName: String,
override val methodParams: Array<String>,
override val returnType: String,
) : IMethodCall {
GetPackageName(
"Landroid/content/Context;",
"getPackageName",
emptyArray(),
"Ljava/lang/String;",
),
}
val userAgentClientSpoofPatch = userAgentClientSpoofPatch("com.google.android.youtube")

View File

@@ -0,0 +1,23 @@
package app.revanced.patches.youtube.video.audio
import app.revanced.patcher.fingerprint
import com.android.tools.smali.dexlib2.AccessFlags
internal val streamingModelBuilderFingerprint = fingerprint {
accessFlags(AccessFlags.PUBLIC, AccessFlags.FINAL)
returns("L")
strings("vprng")
}
internal val menuItemAudioTrackFingerprint = fingerprint {
accessFlags(AccessFlags.PUBLIC, AccessFlags.FINAL)
parameters("L")
returns("V")
strings("menu_item_audio_track")
}
internal val audioStreamingTypeSelector = fingerprint {
accessFlags(AccessFlags.PRIVATE, AccessFlags.FINAL)
returns("L")
strings("raw") // String is not unique
}

View File

@@ -0,0 +1,159 @@
package app.revanced.patches.youtube.video.audio
import app.revanced.patcher.extensions.InstructionExtensions.addInstructions
import app.revanced.patcher.extensions.InstructionExtensions.addInstructionsWithLabels
import app.revanced.patcher.extensions.InstructionExtensions.getInstruction
import app.revanced.patcher.patch.bytecodePatch
import app.revanced.patcher.util.proxy.mutableTypes.MutableField.Companion.toMutable
import app.revanced.patcher.util.proxy.mutableTypes.MutableMethod
import app.revanced.patcher.util.proxy.mutableTypes.MutableMethod.Companion.toMutable
import app.revanced.patches.all.misc.resources.addResources
import app.revanced.patches.all.misc.resources.addResourcesPatch
import app.revanced.patches.shared.misc.settings.preference.SwitchPreference
import app.revanced.patches.youtube.misc.extension.sharedExtensionPatch
import app.revanced.patches.youtube.misc.settings.PreferenceScreen
import app.revanced.patches.youtube.misc.settings.settingsPatch
import app.revanced.util.getReference
import app.revanced.util.indexOfFirstInstructionOrThrow
import com.android.tools.smali.dexlib2.AccessFlags
import com.android.tools.smali.dexlib2.Opcode
import com.android.tools.smali.dexlib2.builder.MutableMethodImplementation
import com.android.tools.smali.dexlib2.iface.Method
import com.android.tools.smali.dexlib2.iface.instruction.OneRegisterInstruction
import com.android.tools.smali.dexlib2.iface.reference.MethodReference
import com.android.tools.smali.dexlib2.immutable.ImmutableField
import com.android.tools.smali.dexlib2.immutable.ImmutableMethod
import com.android.tools.smali.dexlib2.immutable.ImmutableMethodParameter
private const val EXTENSION_CLASS_DESCRIPTOR =
"Lapp/revanced/extension/youtube/patches/ForceOriginalAudioPatch;"
@Suppress("unused")
val forceOriginalAudioPatch = bytecodePatch(
name = "Force original audio",
description = "Adds an option to always use the original audio track.",
) {
dependsOn(
sharedExtensionPatch,
settingsPatch,
addResourcesPatch,
)
compatibleWith(
"com.google.android.youtube"(
"18.38.44",
"18.49.37",
"19.16.39",
"19.25.37",
"19.34.42",
"19.43.41",
"19.45.38",
"19.46.42",
),
)
execute {
addResources("youtube", "video.audio.forceOriginalAudioPatch")
PreferenceScreen.VIDEO.addPreferences(
SwitchPreference("revanced_force_original_audio")
)
fun Method.firstFormatStreamingModelCall(
returnType: String = "Ljava/lang/String;"
): MutableMethod {
val audioTrackIdIndex = indexOfFirstInstructionOrThrow {
val reference = getReference<MethodReference>()
reference?.definingClass == "Lcom/google/android/libraries/youtube/innertube/model/media/FormatStreamModel;"
&& reference.returnType == returnType
}
return navigate(this).to(audioTrackIdIndex).stop()
}
// Accessor methods of FormatStreamModel have no string constants and
// opcodes are identical to other methods in the same class,
// so must walk from another class that use the methods.
val isDefaultMethod = streamingModelBuilderFingerprint.originalMethod.firstFormatStreamingModelCall("Z")
val audioTrackIdMethod = menuItemAudioTrackFingerprint.originalMethod.firstFormatStreamingModelCall()
val audioTrackDisplayNameMethod = audioStreamingTypeSelector.originalMethod.firstFormatStreamingModelCall()
val formatStreamModelClass = proxy(classes.first {
it.type == audioTrackIdMethod.definingClass
}).mutableClass
formatStreamModelClass.apply {
// Add a new field to store the override.
val helperFieldName = "isDefaultAudioTrackOverride"
fields.add(
ImmutableField(
type,
helperFieldName,
"Ljava/lang/Boolean;",
// Boolean is a 100% immutable class (all fields are final)
// and safe to write to a shared field without volatile/synchronization,
// but without volatile the field can show stale data
// and the same field is calculated more than once by different threads.
AccessFlags.PRIVATE.value or AccessFlags.VOLATILE.value,
null,
null,
null
).toMutable()
)
// Add a helper method because the isDefaultAudioTrack() has only 2 registers and 3 are needed.
val helperMethodClass = type
val helperMethodName = "extension_isDefaultAudioTrack"
val helperMethod = ImmutableMethod(
helperMethodClass,
helperMethodName,
listOf(ImmutableMethodParameter("Z", null, null)),
"Z",
AccessFlags.PRIVATE.value,
null,
null,
MutableMethodImplementation(6),
).toMutable().apply {
addInstructionsWithLabels(
0,
"""
iget-object v0, p0, $helperMethodClass->$helperFieldName:Ljava/lang/Boolean;
if-eqz v0, :call_extension
invoke-virtual { v0 }, Ljava/lang/Boolean;->booleanValue()Z
move-result v3
return v3
:call_extension
invoke-virtual { p0 }, $audioTrackIdMethod
move-result-object v1
invoke-virtual { p0 }, $audioTrackDisplayNameMethod
move-result-object v2
invoke-static { p1, v1, v2 }, $EXTENSION_CLASS_DESCRIPTOR->isDefaultAudioStream(ZLjava/lang/String;Ljava/lang/String;)Z
move-result v3
invoke-static { v3 }, Ljava/lang/Boolean;->valueOf(Z)Ljava/lang/Boolean;
move-result-object v0
iput-object v0, p0, $helperMethodClass->$helperFieldName:Ljava/lang/Boolean;
return v3
"""
)
}
methods.add(helperMethod)
// Modify isDefaultAudioTrack() to call extension helper method.
isDefaultMethod.apply {
val index = indexOfFirstInstructionOrThrow(Opcode.RETURN)
val register = getInstruction<OneRegisterInstruction>(index).registerA
addInstructions(
index,
"""
invoke-direct { p0, v$register }, $helperMethodClass->$helperMethodName(Z)Z
move-result v$register
"""
)
}
}
}
}

View File

@@ -796,11 +796,11 @@ Second \"item\" text"</string>
<string name="revanced_wide_searchbar_summary_off">넓은 검색창을 비활성화합니다</string>
</patch>
<patch id="layout.seekbar.seekbarThumbnailsPatch">
<string name="revanced_seekbar_thumbnails_high_quality_title">고화질 썸네일 활성화하기</string>
<string name="revanced_seekbar_thumbnails_high_quality_summary_on">재생바 썸네일이 고화질입니다</string>
<string name="revanced_seekbar_thumbnails_high_quality_summary_off">재생바 썸네일이 일반 화질입니다</string>
<string name="revanced_seekbar_thumbnails_high_quality_legacy_summary_on">전체 화면 재생바 썸네일이 고화질입니다</string>
<string name="revanced_seekbar_thumbnails_high_quality_legacy_summary_off">전체 화면 재생바 썸네일이 일반화질입니다</string>
<string name="revanced_seekbar_thumbnails_high_quality_title">고화질 재생바 썸네일 활성화하기</string>
<string name="revanced_seekbar_thumbnails_high_quality_summary_on">고화질 재생바 썸네일을 활성화합니다</string>
<string name="revanced_seekbar_thumbnails_high_quality_summary_off">일반 화질 재생바 썸네일을 활성화합니다</string>
<string name="revanced_seekbar_thumbnails_high_quality_legacy_summary_on">전체 화면 고화질 재생바 썸네일을 활성화합니다</string>
<string name="revanced_seekbar_thumbnails_high_quality_legacy_summary_off">전체 화면 일반 화질 재생바 썸네일을 활성화합니다</string>
<string name="revanced_seekbar_thumbnails_high_quality_dialog_message">"이 설정을 활성화하면 재생바 썸네일이 없는 실시간 스트림의 썸네일도 복원됩니다
재생바 썸네일에는 현재 동영상과 동일한 화질 값이 사용됩니다

View File

@@ -376,9 +376,9 @@ Ova funkcija je dostupna samo za starije uređaje"</string>
<string name="revanced_hide_get_premium_summary_off">Promocije za YouTube Premium ispod video plejera su prikazane</string>
</patch>
<patch id="ad.video.videoAdsPatch">
<string name="revanced_hide_video_ads_title">Sakrij oglase u videu</string>
<string name="revanced_hide_video_ads_summary_on">Oglasi u videu su skriveni</string>
<string name="revanced_hide_video_ads_summary_off">Oglasi u videu su prikazani</string>
<string name="revanced_hide_video_ads_title">Sakrij video oglase</string>
<string name="revanced_hide_video_ads_summary_on">Video oglasi su skriveni</string>
<string name="revanced_hide_video_ads_summary_off">Video oglasi su prikazani</string>
</patch>
<patch id="interaction.copyvideourl.copyVideoUrlResourcePatch">
<string name="revanced_share_copy_url_success">Link je kopiran u privremenu memoriju</string>
@@ -513,7 +513,7 @@ Ova funkcija je dostupna samo za starije uređaje"</string>
<string name="revanced_switch_create_with_notifications_button_title">Zameni dugme „Napravi” dugmetom „Obaveštenja”</string>
<string name="revanced_switch_create_with_notifications_button_summary_on">"Dugme „Napravi” je zamenjeno dugmetom „Obaveštenja”
Napomena: Omogućavanje ovoga prisilno sakriva oglase u videu"</string>
Napomena: Omogućavanje ovoga prisilno sakriva i video oglase"</string>
<string name="revanced_switch_create_with_notifications_button_summary_off">Dugme „Napravi” nije zamenjeno dugmetom „Obaveštenja”</string>
<string name="revanced_hide_navigation_button_labels_title">Sakrij oznake dugmadi za navigaciju</string>
<string name="revanced_hide_navigation_button_labels_summary_on">Oznake dugmadi za navigaciju su skrivene</string>
@@ -754,13 +754,13 @@ Napomena: Omogućavanje ovoga prisilno sakriva oglase u videu"</string>
<string name="revanced_ryd_enable_summary_off">Nesviđanja nisu prikazana</string>
<string name="revanced_ryd_shorts_title">Prikaži nesviđanja na Shorts videima</string>
<string name="revanced_ryd_shorts_summary_on">Nesviđanja su prikazana na Shorts videima</string>
<string name="revanced_ryd_shorts_summary_on_disclaimer">"Nezadovoljstvo se prikazuje na Kratkim video zapisiima
<string name="revanced_ryd_shorts_summary_on_disclaimer">"Nesviđanja su prikazana na Shorts videima
Ograničenje: Nezadovoljstvo se možda neće pojaviti u inkognito režimu"</string>
Ograničenje: Nesviđanja se možda neće pojaviti u režimu bez arhiviranja"</string>
<string name="revanced_ryd_shorts_summary_off">Nesviđanja su skrivena na Shorts videima</string>
<string name="revanced_ryd_dislike_percentage_title">Nesviđanja u procentima</string>
<string name="revanced_ryd_dislike_percentage_summary_on">Nesviđanja prikazana u procentima</string>
<string name="revanced_ryd_dislike_percentage_summary_off">Nesviđana prikazana u brojevima</string>
<string name="revanced_ryd_dislike_percentage_summary_off">Nesviđanja prikazana u brojevima</string>
<!-- Translations should use language similar to 'revanced_sb_enable_compact_skip_button' -->
<string name="revanced_ryd_compact_layout_title">Kompaktno dugme „Sviđanje”</string>
<string name="revanced_ryd_compact_layout_summary_on">Dugme „Sviđanje” stilizovano za minimalnu širinu</string>
@@ -799,11 +799,11 @@ Ograničenje: Nezadovoljstvo se možda neće pojaviti u inkognito režimu"</stri
<string name="revanced_seekbar_thumbnails_high_quality_summary_off">Sličice na traci za premotavanje su srednjeg kvaliteta</string>
<string name="revanced_seekbar_thumbnails_high_quality_legacy_summary_on">Sličice na traci za premotavanje u režimu celog ekrana su visokog kvaliteta</string>
<string name="revanced_seekbar_thumbnails_high_quality_legacy_summary_off">Sličice na traci za premotavanje u režimu celog ekrana su srednjeg kvaliteta</string>
<string name="revanced_seekbar_thumbnails_high_quality_dialog_message">"Ovo će takođe vratiti minijature na prenosima uživo koji nemaju minijature u traci za pretraživanje.
<string name="revanced_seekbar_thumbnails_high_quality_dialog_message">"Ovo će takođe vratiti sličice na strimovima uživo koji nemaju sličice na traci za premotavanje.
Minijature u traci za pretraživanje će koristiti isti kvalitet kao i trenutni video.
Sličice na traci za premotavanje će koristiti isti kvalitet kao trenutni video.
Ova funkcija najbolje radi sa kvalitetom videa od 720p ili nižim i kada se koristi brza internet veza."</string>
Ova funkcija najbolje radi sa kvalitetom videa od 720p ili nižim i kada koristite veoma brzu internet vezu."</string>
<string name="revanced_restore_old_seekbar_thumbnails_title">Vrati stare sličice na traci za premotavanje</string>
<string name="revanced_restore_old_seekbar_thumbnails_summary_on">Sličice trake za premotavanje će se pojaviti iznad nje</string>
<string name="revanced_restore_old_seekbar_thumbnails_summary_off">Sličice trake za premotavanje će se pojaviti u režimu celog ekrana</string>
@@ -866,9 +866,9 @@ Ova funkcija najbolje radi sa kvalitetom videa od 720p ili nižim i kada se kori
<string name="revanced_sb_settings_import_successful">Podešavanja su uspešno uvezena</string>
<string name="revanced_sb_settings_import_failed">Neuspešan uvoz: %s</string>
<string name="revanced_sb_settings_export_failed">Neuspešan izvoz: %s</string>
<string name="revanced_sb_settings_revanced_export_user_id_warning">"Vaše postavke sadrže privatni ID korisnika SponsorBlock-a.
<string name="revanced_sb_settings_revanced_export_user_id_warning">"Vaša podešavanja sadrže privatni SponsorBlock korisnički ID.
Vaš ID korisnika je kao lozinka i nikada ga ne biste smeli deliti."</string>
Vaš korisnički ID je kao lozinka i nikada ga ne treba deliti."</string>
<string name="revanced_sb_settings_revanced_export_user_id_warning_dismiss">Ne prikazuj ponovo</string>
<string name="revanced_sb_diff_segments">Promena ponašanja segmenta</string>
<string name="revanced_sb_segments_sponsor">Sponzor</string>
@@ -928,9 +928,9 @@ Vaš ID korisnika je kao lozinka i nikada ga ne biste smeli deliti."</string>
<string name="revanced_sb_submit_failed_invalid">Nije moguće podneti segment: %s</string>
<string name="revanced_sb_submit_failed_timeout">SponsorBlock privremeno ne radi</string>
<string name="revanced_sb_submit_failed_unknown_error">Nije mogući podneti segment (status: %1$d %2$s)</string>
<string name="revanced_sb_submit_failed_rate_limit">Segment se ne može poslati. Premašeno ograničenje brzine (previše sa istog korisnika ili IP adrese)</string>
<string name="revanced_sb_submit_failed_rate_limit">Nije moguće podneti segment. Prekoračeno ograničenje stope (previše od istog korisnika ili IP adrese)</string>
<string name="revanced_sb_submit_failed_forbidden">Nije moguće podneti segment: %s</string>
<string name="revanced_sb_submit_failed_duplicate">"Segment se ne može poslati.
<string name="revanced_sb_submit_failed_duplicate">"Nije moguće podneti segment.
Već postoji"</string>
<string name="revanced_sb_submit_succeeded">Segment je uspešno podnet</string>
<!-- Toast shown if network connection times out. Translations of this should not be longer than the original English or the text can be clipped and not entirely shown. -->
@@ -954,15 +954,15 @@ Već postoji"</string>
<string name="revanced_sb_new_segment_time_start">Vreme početka segmenta</string>
<string name="revanced_sb_new_segment_time_end">Vreme kraja segmenta</string>
<string name="revanced_sb_new_segment_confirm_title">Da li su vremena tačna?</string>
<string name="revanced_sb_new_segment_confirm_content">"Сегмент је од
<string name="revanced_sb_new_segment_confirm_content">"Segment je od
%1$s
до
do
%2$s
(%3$s)
Спремни сте за слање?"</string>
Spreman za podnošenje?"</string>
<string name="revanced_sb_new_segment_start_is_before_end">Početak mora biti pre kraja</string>
<string name="revanced_sb_new_segment_mark_locations_first">Prvo označite dva mesta na vremenskoj traci</string>
<string name="revanced_sb_new_segment_preview_segment_first">Pregledajte segment i uverite se da glatko preskače</string>
@@ -1004,9 +1004,11 @@ Već postoji"</string>
<string name="revanced_spoof_app_version_title">Lažirana verzija aplikacije</string>
<string name="revanced_spoof_app_version_summary_on">Verzija je lažirana</string>
<string name="revanced_spoof_app_version_summary_off">Verzija nije lažirana</string>
<string name="revanced_spoof_app_version_user_dialog_message">"Verzija aplikacije će biti predstavljena kao starija verzija YouTube-a.
<string name="revanced_spoof_app_version_user_dialog_message">"Verzija aplikacije će biti lažirana na stariju verziju YouTubea.
To će promeniti izgled i funkcije aplikacije, ali mogu se pojaviti i nepoznati nuspojava."</string>
Ovo će promeniti izgled i funkcije aplikacije, ali se mogu pojaviti i nepoznati neželjeni efekti.
Ako se kasnije isključi, preporučuje se da izbrišete podatke aplikacije da biste sprečili greške u korisničkom interfejsu."</string>
<!-- It is ideal, but not required, if the text here appears is alphabetically after the text used for 'revanced_spoof_app_version_title'.
This is because the 'General layout' menu uses alphabetic sorting, and it functionally works better if the spoof target selector appears below the 'Spoof app version' UI switch -->
<string name="revanced_spoof_app_version_target_title">Ciljna verzija aplikacije za lažiranje</string>
@@ -1058,8 +1060,8 @@ To će promeniti izgled i funkcije aplikacije, ali mogu se pojaviti i nepoznati
<string name="revanced_miniplayer_screen_title">Mini-plejer</string>
<string name="revanced_miniplayer_screen_summary">Promenite stil minimiziranog plejera u aplikaciji</string>
<string name="revanced_miniplayer_type_title">Tip mini-plejera</string>
<string name="revanced_miniplayer_type_entry_0">Onemogućeno</string>
<string name="revanced_miniplayer_type_entry_1">Podrazumevana</string>
<string name="revanced_miniplayer_type_entry_0">Onemogućen</string>
<string name="revanced_miniplayer_type_entry_1">Podrazumevan</string>
<string name="revanced_miniplayer_type_entry_2">Minimalan</string>
<string name="revanced_miniplayer_type_entry_3">Tablet</string>
<string name="revanced_miniplayer_type_entry_4">Moderan 1</string>
@@ -1069,29 +1071,29 @@ To će promeniti izgled i funkcije aplikacije, ali mogu se pojaviti i nepoznati
<string name="revanced_miniplayer_rounded_corners_summary_on">Uglovi su zaobljeni</string>
<string name="revanced_miniplayer_rounded_corners_summary_off">Uglovi nisu zaobljeni</string>
<string name="revanced_miniplayer_double_tap_action_title">Omogući dvostruki dodir i štipanje za promenu veličine</string>
<string name="revanced_miniplayer_double_tap_action_summary_on">"Akcija dvostrukog dodira i štipanje za promenu veličine su omogućeni
<string name="revanced_miniplayer_double_tap_action_summary_on">"Radnja dvostrukog dodira i štipanja za promenu veličine je omogućena
• Dvostruki dodir za povećanje veličine maluškog pregledača
• Dvostruki dodir ponovo da biste povratili originalnu veličinu"</string>
• Dvaput dodirnite da biste povećali veličinu mini-plejera
• Dvaput dodirnite ponovo da biste vratili originalnu veličinu"</string>
<string name="revanced_miniplayer_double_tap_action_summary_off">Radnja dvostrukog dodira i štipanja za promenu veličine je onemogućena</string>
<string name="revanced_miniplayer_drag_and_drop_title">Omogući prevlačenje i otpuštanje</string>
<string name="revanced_miniplayer_drag_and_drop_summary_on">"Povlačenje i puštanje je omogućeno
<string name="revanced_miniplayer_drag_and_drop_summary_on">"Povlačenje i otpuštanje je omogućeno
Malen pregledač se može povući u bilo koji ugao ekrana"</string>
Mini-plejer se može prevući u bilo koji ugao ekrana"</string>
<string name="revanced_miniplayer_drag_and_drop_summary_off">Prevlačenje i otpuštanje je onemogućeno</string>
<string name="revanced_miniplayer_horizontal_drag_title">Omogući pokret horizontalnog prevlačenja</string>
<string name="revanced_miniplayer_horizontal_drag_summary_on">"Horizontalno kretanje prstom omogućeno
<string name="revanced_miniplayer_horizontal_drag_summary_on">"Pokret horizontalnog prevlačenja je omogućen
Malen pregledač se može povući van ekrana ulevo ili udesno"</string>
Mini-plejer se može prevući sa ekrana ulevo ili udesno"</string>
<string name="revanced_miniplayer_horizontal_drag_summary_off">Pokret horizontalnog prevlačenja je onemogućen</string>
<string name="revanced_miniplayer_hide_expand_close_title">Sakrij dugme za zatvaranje</string>
<string name="revanced_miniplayer_hide_expand_close_summary_on">Dugme za zatvaranje je skriveno</string>
<string name="revanced_miniplayer_hide_expand_close_summary_off">Dugme za zatvaranje je prikazano</string>
<string name="revanced_miniplayer_hide_expand_close_legacy_title">Sakrij dugmad za proširivanje i zatvaranje</string>
<string name="revanced_miniplayer_hide_expand_close_legacy_title">Sakrij dugmad za proširenje i zatvaranje</string>
<string name="revanced_miniplayer_hide_expand_close_legacy_summary_on">"Dugmad su skrivena
Provucite za proširenje ili zatvaranje"</string>
<string name="revanced_miniplayer_hide_expand_close_legacy_summary_off">Dugmad za proširivanje i zatvaranje su prikazana</string>
Prevucite za proširenje ili zatvaranje"</string>
<string name="revanced_miniplayer_hide_expand_close_legacy_summary_off">Dugmad za proširenje i zatvaranje su prikazana</string>
<string name="revanced_miniplayer_hide_subtext_title">Sakrij podtekstove</string>
<string name="revanced_miniplayer_hide_subtext_summary_on">Podtekstovi su skriveni</string>
<string name="revanced_miniplayer_hide_subtext_summary_off">Podtekstovi su prikazani</string>
@@ -1121,9 +1123,9 @@ Provucite za proširenje ili zatvaranje"</string>
<patch id="layout.thumbnails.bypassImageRegionRestrictionsPatch">
<string name="revanced_bypass_image_region_restrictions_title">Zaobiđi ograničenja regiona slike</string>
<string name="revanced_bypass_image_region_restrictions_summary_on">Korišćenje hosta slike yt4.ggpht.com</string>
<string name="revanced_bypass_image_region_restrictions_summary_off">"Korišćenje originalnog hosta slika
<string name="revanced_bypass_image_region_restrictions_summary_off">"Korišćenje originalnog hosta slike
Omogućavanjem ovoga mogu se popraviti nedostajuće slike koje su blokirane u nekim regionima"</string>
Ako ovo omogućite, mogu se popraviti nedostajuće slike koje su blokirane u nekim regionima"</string>
</patch>
<patch id="layout.thumbnails.alternativeThumbnailsPatch">
<!-- 'Home' should be translated using the same localized wording YouTube displays for the home tab. -->
@@ -1138,11 +1140,11 @@ Omogućavanjem ovoga mogu se popraviti nedostajuće slike koje su blokirane u ne
<string name="revanced_alt_thumbnail_options_entry_2">DeArrow i originalne sličice</string>
<string name="revanced_alt_thumbnail_options_entry_3">DeArrow i zahvati kadra</string>
<string name="revanced_alt_thumbnail_options_entry_4">Zahvati kadra</string>
<string name="revanced_alt_thumbnail_dearrow_about_summary">"DeArrow pruža kolekciju minijatura za YouTube video zapise koje su kreirali korisnici. Ove minijature su često relevantnije od onih koje pruža YouTube
<string name="revanced_alt_thumbnail_dearrow_about_summary">"DeArrow pruža sličice za YouTube videe prikupljene od zajednice korisnika. Ove sličice su često relevantnije od onih koje pruža YouTube
Ako je omogućeno, URL video zapisa će se slati na API server i neće se slati drugi podaci. Ako video nema DeArrow minijature, onda se prikazuju originalne ili snimljene minijature
Ako je omogućeno, linkovi videa će biti poslati na API server i nikakvi drugi podaci se neće slati. Ako video nema DeArrow sličice, onda se prikazuju originalne ili zahvati kadra
Dodirnite ovde da saznate više o DeArrow-u"</string>
Dodirnite ovde da saznate više o DeArrowu"</string>
<string name="revanced_alt_thumbnail_dearrow_connection_toast_title">Prikaži iskačuće obaveštenje ako API nije dostupan</string>
<string name="revanced_alt_thumbnail_dearrow_connection_toast_summary_on">Iskačuće obaveštenje je prikazano, ako DeArrow nije dostupan</string>
<string name="revanced_alt_thumbnail_dearrow_connection_toast_summary_off">Iskačuće obaveštenje nije prikazano, ako DeArrow nije dostupan</string>
@@ -1181,13 +1183,13 @@ Dodirnite ovde da saznate više o DeArrow-u"</string>
</patch>
<patch id="misc.dimensions.spoof.spoofDeviceDimensionsPatch">
<string name="revanced_spoof_device_dimensions_title">Lažirane dimenzije uređaja</string>
<string name="revanced_spoof_device_dimensions_summary_on">"Dimenzije uređaja su lažne
<string name="revanced_spoof_device_dimensions_summary_on">"Dimenzije uređaja su lažirane
Možda će biti otključani viši kvaliteti videa, ali možete doživeti trzanje videa, lošiji rad baterije i nepoznate nuspojava"</string>
<string name="revanced_spoof_device_dimensions_summary_off">"Dimenzije uređaja nisu lažne
Viši kvaliteti videa mogu biti otključani, ali može doći do zastoja pri reprodukciji videa, kraćeg trajanja baterije i nepoznatih neželjenih efekata"</string>
<string name="revanced_spoof_device_dimensions_summary_off">"Dimenzije uređaja nisu lažirane
Omogućavanjem ovoga može se otključati viši kvalitet videa"</string>
<string name="revanced_spoof_device_dimensions_user_dialog_message">Ako ovo omogućite, možda će doći do zastoja pri reprodukciji videa, kraćeg trajanja baterije i nepoznatih neželjenih efekata.</string>
Ako ovo omogućite, mogu biti otključani viši kvaliteti videa"</string>
<string name="revanced_spoof_device_dimensions_user_dialog_message">Ako ovo omogućite, može doći do zastoja pri reprodukciji videa, kraćeg trajanja baterije i nepoznatih neželjenih efekata.</string>
</patch>
<patch id="misc.gms.gmsCoreSupportResourcePatch">
<string name="microg_settings_title">Podešavanja GmsCorea</string>
@@ -1236,8 +1238,8 @@ Omogućavanjem ovoga može se otključati viši kvalitet videa"</string>
<string name="revanced_custom_speed_menu_summary_off">Meni prilagođene brzine reprodukcije nije prikazan</string>
<string name="revanced_custom_playback_speeds_title">Prilagođene brzine reprodukcije</string>
<string name="revanced_custom_playback_speeds_summary">Dodajte ili promenite prilagođene brzine reprodukcije</string>
<string name="revanced_custom_playback_speeds_invalid">Brzine po meri moraju biti manje od %s</string>
<string name="revanced_custom_playback_speeds_parse_exception">Nevažeće brzine reprodukcije po meri</string>
<string name="revanced_custom_playback_speeds_invalid">Prilagođene brzine moraju biti manje od %s</string>
<string name="revanced_custom_playback_speeds_parse_exception">Nevažeće prilagođene brzine reprodukcije</string>
<string name="revanced_custom_playback_speeds_auto">Automatski</string>
</patch>
<patch id="video.speed.remember.rememberPlaybackSpeedPatch">
@@ -1262,7 +1264,7 @@ Omogućavanjem ovoga može se otključati viši kvalitet videa"</string>
<string name="revanced_spoof_video_streams_screen_summary">Lažiranje klijenta video strimova da bi se sprečili problemi sa reprodukcijom</string>
<string name="revanced_spoof_video_streams_title">Lažirani video strimovi</string>
<string name="revanced_spoof_video_streams_summary_on">Video strimovi su lažirani</string>
<string name="revanced_spoof_video_streams_summary_off">"Tokovi videa nisu lažni
<string name="revanced_spoof_video_streams_summary_off">"Video strimovi nisu lažirani
Reprodukcija videa možda neće raditi"</string>
<string name="revanced_spoof_video_streams_user_dialog_message">Isključivanje ove opcije će možda izazvati probleme sa reprodukcijom videa.</string>
@@ -1270,9 +1272,9 @@ Reprodukcija videa možda neće raditi"</string>
<string name="revanced_spoof_video_streams_ios_force_avc_title">Prisili AVC (H.264)</string>
<string name="revanced_spoof_video_streams_ios_force_avc_summary_on">Video kodek je prisilno podešen na AVC (H.264)</string>
<string name="revanced_spoof_video_streams_ios_force_avc_summary_off">Video kodek je određen automatski</string>
<string name="revanced_spoof_video_streams_ios_force_avc_user_dialog_message">"Omogućavanjem ovoga može se poboljšati rad baterije i popraviti trzanje reprodukcije.
<string name="revanced_spoof_video_streams_ios_force_avc_user_dialog_message">"Ako ovo omogućite, možda će se produžiti trajanje baterije i popraviti zastoj pri reprodukciji.
AVC ima maksimalnu rezoluciju od 1080p, audio kodek Opus nije dostupan i reprodukcija videa će trošiti više internet podataka od VP9 ili AV1."</string>
AVC ima maksimalnu rezoluciju od 1080p, audio kodek Opus nije dostupan, a reprodukcija videa će koristiti više internet podataka nego VP9 ili AV1."</string>
<string name="revanced_spoof_video_streams_about_ios_title">Neželjeni efekti lažiranja na iOS</string>
<string name="revanced_spoof_video_streams_about_ios_summary">"• Privatni dečji videi se možda neće puštati
• Strimovi uživo počinju od početka
@@ -1299,42 +1301,42 @@ AVC ima maksimalnu rezoluciju od 1080p, audio kodek Opus nije dostupan i reprodu
<string name="revanced_spoof_video_streams_language_FI">Finski</string>
<string name="revanced_spoof_video_streams_language_FR">Francuski</string>
<string name="revanced_spoof_video_streams_language_GU">Gudžarati</string>
<string name="revanced_spoof_video_streams_language_HI">hindi</string>
<string name="revanced_spoof_video_streams_language_HI">Hindi</string>
<string name="revanced_spoof_video_streams_language_HR">Hrvatski</string>
<string name="revanced_spoof_video_streams_language_HU">Mađarski</string>
<string name="revanced_spoof_video_streams_language_ID">Indonezijski</string>
<string name="revanced_spoof_video_streams_language_ID">Indonežanski</string>
<string name="revanced_spoof_video_streams_language_IT">Italijanski</string>
<string name="revanced_spoof_video_streams_language_JA">Japanski</string>
<string name="revanced_spoof_video_streams_language_KK">Kazaški</string>
<string name="revanced_spoof_video_streams_language_KO">Koreanski</string>
<string name="revanced_spoof_video_streams_language_KO">Korejski</string>
<string name="revanced_spoof_video_streams_language_LT">Litvanski</string>
<string name="revanced_spoof_video_streams_language_LV">Latvijski</string>
<string name="revanced_spoof_video_streams_language_LV">Letonski</string>
<string name="revanced_spoof_video_streams_language_MK">Makedonski</string>
<string name="revanced_spoof_video_streams_language_MN">mongolski</string>
<string name="revanced_spoof_video_streams_language_MN">Mongolski</string>
<string name="revanced_spoof_video_streams_language_MR">Marati</string>
<string name="revanced_spoof_video_streams_language_MS">Malajski</string>
<string name="revanced_spoof_video_streams_language_MY">burmanski</string>
<string name="revanced_spoof_video_streams_language_NL">holandski</string>
<string name="revanced_spoof_video_streams_language_OR">odija</string>
<string name="revanced_spoof_video_streams_language_PA">pandžapski</string>
<string name="revanced_spoof_video_streams_language_PL">poljski</string>
<string name="revanced_spoof_video_streams_language_PT_BR">portugalski (Brazil)</string>
<string name="revanced_spoof_video_streams_language_PT_PT">portugalski (Portugalija)</string>
<string name="revanced_spoof_video_streams_language_RO">rumunski</string>
<string name="revanced_spoof_video_streams_language_RU">ruski</string>
<string name="revanced_spoof_video_streams_language_SK">slovački</string>
<string name="revanced_spoof_video_streams_language_MY">Burmanski</string>
<string name="revanced_spoof_video_streams_language_NL">Holandski</string>
<string name="revanced_spoof_video_streams_language_OR">Odija</string>
<string name="revanced_spoof_video_streams_language_PA">Pandžapski</string>
<string name="revanced_spoof_video_streams_language_PL">Poljski</string>
<string name="revanced_spoof_video_streams_language_PT_BR">Portugalski (Brazil)</string>
<string name="revanced_spoof_video_streams_language_PT_PT">Portugalski (Portugalija)</string>
<string name="revanced_spoof_video_streams_language_RO">Rumunski</string>
<string name="revanced_spoof_video_streams_language_RU">Ruski</string>
<string name="revanced_spoof_video_streams_language_SK">Slovački</string>
<string name="revanced_spoof_video_streams_language_SL">slovenački</string>
<string name="revanced_spoof_video_streams_language_SR">srpski</string>
<string name="revanced_spoof_video_streams_language_SV">švedski</string>
<string name="revanced_spoof_video_streams_language_SW">svahili</string>
<string name="revanced_spoof_video_streams_language_TA">tamilski</string>
<string name="revanced_spoof_video_streams_language_TE">telugu</string>
<string name="revanced_spoof_video_streams_language_TH">tajski</string>
<string name="revanced_spoof_video_streams_language_TR">turski</string>
<string name="revanced_spoof_video_streams_language_UK">ukrajinski</string>
<string name="revanced_spoof_video_streams_language_UR">urdu</string>
<string name="revanced_spoof_video_streams_language_VI">vijetnamski</string>
<string name="revanced_spoof_video_streams_language_ZH">kineski</string>
<string name="revanced_spoof_video_streams_language_SR">Srpski</string>
<string name="revanced_spoof_video_streams_language_SV">Švedski</string>
<string name="revanced_spoof_video_streams_language_SW">Svahili</string>
<string name="revanced_spoof_video_streams_language_TA">Tamilski</string>
<string name="revanced_spoof_video_streams_language_TE">Telugu</string>
<string name="revanced_spoof_video_streams_language_TH">Tajski</string>
<string name="revanced_spoof_video_streams_language_TR">Turski</string>
<string name="revanced_spoof_video_streams_language_UK">Ukrajinski</string>
<string name="revanced_spoof_video_streams_language_UR">Urdu</string>
<string name="revanced_spoof_video_streams_language_VI">Vijetnamski</string>
<string name="revanced_spoof_video_streams_language_ZH">Kineski</string>
</patch>
</app>
<app id="twitch">
@@ -1344,9 +1346,9 @@ AVC ima maksimalnu rezoluciju od 1080p, audio kodek Opus nije dostupan i reprodu
<string name="revanced_block_audio_ads_summary_off">Audio oglasi su odblokirani</string>
</patch>
<patch id="ad.embedded.embeddedAdsPatch">
<string name="revanced_embedded_ads_service_unavailable">%s nedostupan, oglasi se mogu prikazivati. Pokušajte da promenite uslugu blokiranja oglasa u postavkama.</string>
<string name="revanced_embedded_ads_service_unavailable">%s nije dostupan, oglasi se možda prikazuju. Pokušajte da promenite uslugu blokiranja oglasa u podešavanjima.</string>
<string name="revanced_embedded_ads_service_failed">%s je vratio grešku, oglasi se mogu prikazivati. Pokušajte da promenite uslugu blokiranja oglasa u postavkama.</string>
<string name="revanced_block_embedded_ads_title">Blokiranje ugrađenih video oglasa</string>
<string name="revanced_block_embedded_ads_title">Blokiraj ugrađene video oglase</string>
<string name="revanced_block_embedded_ads_entry_1">Onemogućeno</string>
<string name="revanced_block_embedded_ads_entry_2">Luminous proksi</string>
<string name="revanced_block_embedded_ads_entry_3">PurpleAdBlock proksi</string>

View File

@@ -377,9 +377,9 @@ Second \"item\" text"</string>
<string name="revanced_hide_get_premium_summary_off">Промоције за YouTube Premium испод видео плејера су приказане</string>
</patch>
<patch id="ad.video.videoAdsPatch">
<string name="revanced_hide_video_ads_title">Сакриј огласе у видеу</string>
<string name="revanced_hide_video_ads_summary_on">Огласи у видеу су скривени</string>
<string name="revanced_hide_video_ads_summary_off">Огласи у видеу су приказани</string>
<string name="revanced_hide_video_ads_title">Сакриј видео огласе</string>
<string name="revanced_hide_video_ads_summary_on">Видео огласи су скривени</string>
<string name="revanced_hide_video_ads_summary_off">Видео огласи су приказани</string>
</patch>
<patch id="interaction.copyvideourl.copyVideoUrlResourcePatch">
<string name="revanced_share_copy_url_success">Линк је копиран у привремену меморију</string>
@@ -514,7 +514,7 @@ Second \"item\" text"</string>
<string name="revanced_switch_create_with_notifications_button_title">Замени дугме „Направи” дугметом „Обавештења”</string>
<string name="revanced_switch_create_with_notifications_button_summary_on">"Дугме „Направи” је замењено дугметом „Обавештења”
Напомена: Омогућавање овога присилно сакрива огласе у видеу"</string>
Напомена: Омогућавање овога присилно сакрива и видео огласе"</string>
<string name="revanced_switch_create_with_notifications_button_summary_off">Дугме „Направи” није замењено дугметом „Обавештења”</string>
<string name="revanced_hide_navigation_button_labels_title">Сакриј ознаке дугмади за навигацију</string>
<string name="revanced_hide_navigation_button_labels_summary_on">Ознаке дугмади за навигацију су скривене</string>
@@ -755,9 +755,9 @@ Second \"item\" text"</string>
<string name="revanced_ryd_enable_summary_off">Несвиђања нису приказана</string>
<string name="revanced_ryd_shorts_title">Прикажи несвиђања на Shorts видеима</string>
<string name="revanced_ryd_shorts_summary_on">Несвиђања су приказана на Shorts видеима</string>
<string name="revanced_ryd_shorts_summary_on_disclaimer">"Nepopularni prikazani na Shorts
<string name="revanced_ryd_shorts_summary_on_disclaimer">"Несвиђања су приказана на Shorts видеима
Ograničenje: Nepopularni se možda neće prikazati u inkognito režimu"</string>
Ограничење: Несвиђања се можда неће појавити у режиму без архивирања"</string>
<string name="revanced_ryd_shorts_summary_off">Несвиђања су скривена на Shorts видеима</string>
<string name="revanced_ryd_dislike_percentage_title">Несвиђања у процентима</string>
<string name="revanced_ryd_dislike_percentage_summary_on">Несвиђања приказана у процентима</string>
@@ -800,11 +800,11 @@ Ograničenje: Nepopularni se možda neće prikazati u inkognito režimu"</string
<string name="revanced_seekbar_thumbnails_high_quality_summary_off">Сличице на траци за премотавање су средњег квалитета</string>
<string name="revanced_seekbar_thumbnails_high_quality_legacy_summary_on">Сличице на траци за премотавање у режиму целог екрана су високог квалитета</string>
<string name="revanced_seekbar_thumbnails_high_quality_legacy_summary_off">Сличице на траци за премотавање у режиму целог екрана су средњег квалитета</string>
<string name="revanced_seekbar_thumbnails_high_quality_dialog_message">"Ovo će takođe vratiti minijature na livestreamovima koji nemaju minijature sa trakom za premotavanje.
<string name="revanced_seekbar_thumbnails_high_quality_dialog_message">"Ово ће такође вратити сличице на стримовима уживо који немају сличице на траци за премотавање.
Minijature sa trakom za premotavanje koristiće isti kvalitet kao i trenutni video.
Сличице на траци за премотавање ће користити исти квалитет као тренутни видео.
Ova funkcija najbolje radi sa kvalitetom videa od 720p ili niže i kada se koristi veoma brza internet veza."</string>
Ова функција најбоље ради са квалитетом видеа од 720p или нижим и када користите веома брзу интернет везу."</string>
<string name="revanced_restore_old_seekbar_thumbnails_title">Врати старе сличице на траци за премотавање</string>
<string name="revanced_restore_old_seekbar_thumbnails_summary_on">Сличице траке за премотавање ће се појавити изнад ње</string>
<string name="revanced_restore_old_seekbar_thumbnails_summary_off">Сличице траке за премотавање ће се појавити у режиму целог екрана</string>
@@ -867,9 +867,9 @@ Ova funkcija najbolje radi sa kvalitetom videa od 720p ili niže i kada se koris
<string name="revanced_sb_settings_import_successful">Подешавања су успешно увезена</string>
<string name="revanced_sb_settings_import_failed">Неуспешан увоз: %s</string>
<string name="revanced_sb_settings_export_failed">Неуспешан извоз: %s</string>
<string name="revanced_sb_settings_revanced_export_user_id_warning">"Vaše postavke sadrže privatni ID korisnika SponsorBlocka.
<string name="revanced_sb_settings_revanced_export_user_id_warning">"Ваша подешавања садрже приватни SponsorBlock кориснички ID.
Vaš ID korisnika je kao lozinka i nikada se ne sme deliti."</string>
Ваш кориснички ID је као лозинка и никада га не треба делити."</string>
<string name="revanced_sb_settings_revanced_export_user_id_warning_dismiss">Не приказуј поново</string>
<string name="revanced_sb_diff_segments">Промена понашања сегмента</string>
<string name="revanced_sb_segments_sponsor">Спонзор</string>
@@ -929,10 +929,10 @@ Vaš ID korisnika je kao lozinka i nikada se ne sme deliti."</string>
<string name="revanced_sb_submit_failed_invalid">Није могуће поднети сегмент: %s</string>
<string name="revanced_sb_submit_failed_timeout">SponsorBlock привремено не ради</string>
<string name="revanced_sb_submit_failed_unknown_error">Није могуће поднети сегмент (статус: %1$d %2$s)</string>
<string name="revanced_sb_submit_failed_rate_limit">Segment se ne može poslati. Bilo je previše pokušaja (previše sa istog korisnika ili IP adrese)</string>
<string name="revanced_sb_submit_failed_rate_limit">Није могуће поднети сегмент. Прекорачено ограничење стопе (превише од истог корисника или IP адресе)</string>
<string name="revanced_sb_submit_failed_forbidden">Није могуће поднети сегмент: %s</string>
<string name="revanced_sb_submit_failed_duplicate">"Segment se ne može poslati.
Već postoji"</string>
<string name="revanced_sb_submit_failed_duplicate">"Није могуће поднети сегмент.
Већ постоји"</string>
<string name="revanced_sb_submit_succeeded">Сегмент је успешно поднет</string>
<!-- Toast shown if network connection times out. Translations of this should not be longer than the original English or the text can be clipped and not entirely shown. -->
<string name="revanced_sb_sponsorblock_connection_failure_timeout">SponsorBlock привремено није доступан (API истекао)</string>
@@ -955,15 +955,15 @@ Već postoji"</string>
<string name="revanced_sb_new_segment_time_start">Време почетка сегмента</string>
<string name="revanced_sb_new_segment_time_end">Време краја сегмента</string>
<string name="revanced_sb_new_segment_confirm_title">Да ли су времена тачна?</string>
<string name="revanced_sb_new_segment_confirm_content">"Segment ide od
<string name="revanced_sb_new_segment_confirm_content">"Сегмент је од
%1$s
do
до
%2$s
(%3$s)
Spremni da podnesete zahtev?"</string>
Спреман за подношење?"</string>
<string name="revanced_sb_new_segment_start_is_before_end">Почетак мора бити пре краја</string>
<string name="revanced_sb_new_segment_mark_locations_first">Прво означите два места на временској траци</string>
<string name="revanced_sb_new_segment_preview_segment_first">Прегледајте сегмент и уверите се да глатко прескаче</string>
@@ -1005,11 +1005,11 @@ Spremni da podnesete zahtev?"</string>
<string name="revanced_spoof_app_version_title">Лажирана верзија апликације</string>
<string name="revanced_spoof_app_version_summary_on">Верзија је лажирана</string>
<string name="revanced_spoof_app_version_summary_off">Верзија није лажирана</string>
<string name="revanced_spoof_app_version_user_dialog_message">"Verzija aplikacije će biti lažno prikazana kao starija verzija YouTube-a.
<string name="revanced_spoof_app_version_user_dialog_message">"Верзија апликације ће бити лажирана на старију верзију YouTube-а.
Ovo će promeniti izgled i funkcije aplikacije, ali se mogu pojaviti nepoznate nuspojave.
Ово ће променити изглед и функције апликације, али се могу појавити непознати нежељени ефекти.
Ako se kasnije isključi, preporučuje se da se obrišu podaci aplikacije kako bi se sprečili greške u UI-ju."</string>
Ако се касније искључи, препоручује се да избришете податке апликације да бисте спречили грешке у корисничком интерфејсу."</string>
<!-- It is ideal, but not required, if the text here appears is alphabetically after the text used for 'revanced_spoof_app_version_title'.
This is because the 'General layout' menu uses alphabetic sorting, and it functionally works better if the spoof target selector appears below the 'Spoof app version' UI switch -->
<string name="revanced_spoof_app_version_target_title">Циљна верзија апликације за лажирање</string>
@@ -1061,8 +1061,8 @@ Ako se kasnije isključi, preporučuje se da se obrišu podaci aplikacije kako b
<string name="revanced_miniplayer_screen_title">Мини-плејер</string>
<string name="revanced_miniplayer_screen_summary">Промените стил минимизираног плејера у апликацији</string>
<string name="revanced_miniplayer_type_title">Тип мини-плејера</string>
<string name="revanced_miniplayer_type_entry_0">Онемогућено</string>
<string name="revanced_miniplayer_type_entry_1">Подразумевана</string>
<string name="revanced_miniplayer_type_entry_0">Онемогућен</string>
<string name="revanced_miniplayer_type_entry_1">Подразумеван</string>
<string name="revanced_miniplayer_type_entry_2">Минималан</string>
<string name="revanced_miniplayer_type_entry_3">Таблет</string>
<string name="revanced_miniplayer_type_entry_4">Модеран 1</string>
@@ -1072,29 +1072,29 @@ Ako se kasnije isključi, preporučuje se da se obrišu podaci aplikacije kako b
<string name="revanced_miniplayer_rounded_corners_summary_on">Углови су заобљени</string>
<string name="revanced_miniplayer_rounded_corners_summary_off">Углови нису заобљени</string>
<string name="revanced_miniplayer_double_tap_action_title">Омогући двоструки додир и штипање за промену величине</string>
<string name="revanced_miniplayer_double_tap_action_summary_on">"Dvostruki dodir i potez za promenu veličine su omogućeni
<string name="revanced_miniplayer_double_tap_action_summary_on">"Радња двоструког додира и штипања за промену величине је омогућена
Dvostruko dodirnite da biste povećali veličinu miniprejera
Dvostruko dodirnite opet da biste povratili originalnu veličinu"</string>
<string name="revanced_miniplayer_double_tap_action_summary_off">Радња двоструког додира и штипање за промену величине су онемогућени</string>
Двапут додирните да бисте повећали величину мини-плејера
Двапут додирните поново да бисте вратили оригиналну величину"</string>
<string name="revanced_miniplayer_double_tap_action_summary_off">Радња двоструког додира и штипања за промену величине су онемогућени</string>
<string name="revanced_miniplayer_drag_and_drop_title">Омогући превлачење и отпуштање</string>
<string name="revanced_miniplayer_drag_and_drop_summary_on">"Povlačenje i puštanje je omogućeno
<string name="revanced_miniplayer_drag_and_drop_summary_on">"Превлачење и отпуштање је омогућено
Miniprejer se može povući u bilo koji ugao ekrana"</string>
Мини-плејер се може превући у било који угао екрана"</string>
<string name="revanced_miniplayer_drag_and_drop_summary_off">Превлачење и отпуштање је онемогућено</string>
<string name="revanced_miniplayer_horizontal_drag_title">Омогући покрет хоризонталног превлачења</string>
<string name="revanced_miniplayer_horizontal_drag_summary_on">"Horizontalni potez omogućen
<string name="revanced_miniplayer_horizontal_drag_summary_on">"Покрет хоризонталног превлачења је омогућен
Miniprejer se može povući van ekrana levo ili desno"</string>
Мини-плејер се може превући са екрана улево или удесно"</string>
<string name="revanced_miniplayer_horizontal_drag_summary_off">Покрет хоризонталног превлачења је онемогућен</string>
<string name="revanced_miniplayer_hide_expand_close_title">Сакриј дугме за затварање</string>
<string name="revanced_miniplayer_hide_expand_close_summary_on">Дугме за затварање је скривено</string>
<string name="revanced_miniplayer_hide_expand_close_summary_off">Дугме за затварање је приказано</string>
<string name="revanced_miniplayer_hide_expand_close_legacy_title">Сакриј дугмад за проширивање и затварање</string>
<string name="revanced_miniplayer_hide_expand_close_legacy_summary_on">"Dugmad su skrivena
<string name="revanced_miniplayer_hide_expand_close_legacy_title">Сакриј дугмад за проширење и затварање</string>
<string name="revanced_miniplayer_hide_expand_close_legacy_summary_on">"Дугмад су скривена
Provucite da biste proširili ili zatvorili"</string>
<string name="revanced_miniplayer_hide_expand_close_legacy_summary_off">Дугмад за проширивање и затварање су приказана</string>
Превуците за проширење или затварање"</string>
<string name="revanced_miniplayer_hide_expand_close_legacy_summary_off">Дугмад за проширење и затварање су приказана</string>
<string name="revanced_miniplayer_hide_subtext_title">Сакриј подтекстове</string>
<string name="revanced_miniplayer_hide_subtext_summary_on">Подтекстови су скривени</string>
<string name="revanced_miniplayer_hide_subtext_summary_off">Подтекстови су приказани</string>
@@ -1124,9 +1124,9 @@ Provucite da biste proširili ili zatvorili"</string>
<patch id="layout.thumbnails.bypassImageRegionRestrictionsPatch">
<string name="revanced_bypass_image_region_restrictions_title">Заобиђи ограничења региона слике</string>
<string name="revanced_bypass_image_region_restrictions_summary_on">Коришћење хоста слике yt4.ggpht.com</string>
<string name="revanced_bypass_image_region_restrictions_summary_off">"Korišćenje originalnog hosta slika
<string name="revanced_bypass_image_region_restrictions_summary_off">"Коришћење оригиналног хоста слика
Omogućavanje ovoga može da popravi nedostajuće slike koje su blokirane u nekim regionima"</string>
Ако ово омогућите, могу се поправити недостајуће слике које су блокиране у неким регионима"</string>
</patch>
<patch id="layout.thumbnails.alternativeThumbnailsPatch">
<!-- 'Home' should be translated using the same localized wording YouTube displays for the home tab. -->
@@ -1142,11 +1142,11 @@ Omogućavanje ovoga može da popravi nedostajuće slike koje su blokirane u neki
<string name="revanced_alt_thumbnail_options_entry_3">DeArrow и захвати кадра</string>
<string name="revanced_alt_thumbnail_options_entry_4">Захвати кадра</string>
<string name="revanced_alt_thumbnail_dearrow_about_title">DeArrow</string>
<string name="revanced_alt_thumbnail_dearrow_about_summary">"DeArrow pruža minijature za YouTube video zapise koje je kreirala zajednica. Ove minijature su često relevantnije od onih koje pruža YouTube
<string name="revanced_alt_thumbnail_dearrow_about_summary">"DeArrow пружа сличице за YouTube видее прикупљене од заједнице корисника. Ове сличице су често релевантније од оних које пружа YouTube
Ako je omogućeno, adrese video zapisa biće poslate na server API-ja i neće se slati drugi podaci. Ako video zapis nema DeArrow minijature, onda se prikazuju originalne ili fiksne snimke
Ако је омогућено, линкови видеа ће бити послати на API сервер и никакви други подаци се неће слати. Ако видео нема DeArrow сличице, онда се приказују оригиналне или захвати кадра
Dodirnite ovde da biste saznali više o DeArrow"</string>
Додирните овде да сазнате више о DeArrow-у"</string>
<string name="revanced_alt_thumbnail_dearrow_connection_toast_title">Прикажи искачуће обавештење ако API није доступан</string>
<string name="revanced_alt_thumbnail_dearrow_connection_toast_summary_on">Искачуће обавештење је приказано, ако DeArrow није доступан</string>
<string name="revanced_alt_thumbnail_dearrow_connection_toast_summary_off">Искачуће обавештење није приказано, ако DeArrow није доступан</string>
@@ -1185,13 +1185,13 @@ Dodirnite ovde da biste saznali više o DeArrow"</string>
</patch>
<patch id="misc.dimensions.spoof.spoofDeviceDimensionsPatch">
<string name="revanced_spoof_device_dimensions_title">Лажиране димензије уређаја</string>
<string name="revanced_spoof_device_dimensions_summary_on">"Dimenzije uređaja su lažno prikazane
<string name="revanced_spoof_device_dimensions_summary_on">"Димензије уређаја су лажиране
Možda će biti otključeni viši kvaliteti videa, ali možete doživeti trzanje prilikom reprodukcije videa, lošiji rad baterije i nepoznate nuspojave"</string>
<string name="revanced_spoof_device_dimensions_summary_off">"Dimenzije uređaja nisu lažno prikazane
Виши квалитети видеа могу бити откључани, али може доћи до застоја при репродукцији видеа, краћег трајања батерије и непознатих нежељених ефеката"</string>
<string name="revanced_spoof_device_dimensions_summary_off">"Димензије уређаја нису лажиране
Omogućavanje ovoga može da otključa više kvalitete videa"</string>
<string name="revanced_spoof_device_dimensions_user_dialog_message">Ако ово омогућите, можда ће доћи до застоја при репродукцији видеа, краћег трајања батерије и непознатих нежељених ефеката.</string>
Ако ово омогућите, могу бити откључани виши квалитети видеа"</string>
<string name="revanced_spoof_device_dimensions_user_dialog_message">Ако ово омогућите, може доћи до застоја при репродукцији видеа, краћег трајања батерије и непознатих нежељених ефеката.</string>
</patch>
<patch id="misc.gms.gmsCoreSupportResourcePatch">
<string name="microg_settings_title">Подешавања GmsCore-а</string>
@@ -1239,8 +1239,8 @@ Omogućavanje ovoga može da otključa više kvalitete videa"</string>
<string name="revanced_custom_speed_menu_summary_off">Мени прилагођене брзине репродукције није приказан</string>
<string name="revanced_custom_playback_speeds_title">Прилагођене брзине репродукције</string>
<string name="revanced_custom_playback_speeds_summary">Додајте или промените прилагођене брзине репродукције</string>
<string name="revanced_custom_playback_speeds_invalid">Brzine po meri moraju biti manje od %s</string>
<string name="revanced_custom_playback_speeds_parse_exception">Nevažeće brzine reprodukcije po meri</string>
<string name="revanced_custom_playback_speeds_invalid">Прилагођене брзине морају бити мање од %s</string>
<string name="revanced_custom_playback_speeds_parse_exception">Неважеће прилагођене брзине репродукције</string>
<string name="revanced_custom_playback_speeds_auto">Аутоматски</string>
</patch>
<patch id="video.speed.remember.rememberPlaybackSpeedPatch">
@@ -1261,29 +1261,29 @@ Omogućavanje ovoga može da otključa više kvalitete videa"</string>
<string name="revanced_slide_to_seek_summary_off">Превлачење за премотавање није омогућено</string>
</patch>
<patch id="misc.fix.playback.spoofVideoStreamsPatch">
<string name="revanced_spoof_video_streams_screen_title">Лажиран видео стрим</string>
<string name="revanced_spoof_video_streams_screen_title">Лажирани видео стримови</string>
<string name="revanced_spoof_video_streams_screen_summary">Лажирање клијента видео стримова да би се спречили проблеми са репродукцијом</string>
<string name="revanced_spoof_video_streams_title">Лажирани видео стримови</string>
<string name="revanced_spoof_video_streams_summary_on">Видео стримови су лажирани</string>
<string name="revanced_spoof_video_streams_summary_off">"Tokovi videa nisu lažno prikazani
<string name="revanced_spoof_video_streams_summary_off">"Видео стримови нису лажирани
Reprodukcija videa možda neće raditi"</string>
Репродукција видеа можда неће радити"</string>
<string name="revanced_spoof_video_streams_user_dialog_message">Искључивање ове опције ће можда изазвати проблеме са репродукцијом видеа.</string>
<string name="revanced_spoof_video_streams_client_title">Подразумевани клијент</string>
<string name="revanced_spoof_video_streams_ios_force_avc_title">Присили AVC (H.264)</string>
<string name="revanced_spoof_video_streams_ios_force_avc_summary_on">Видео кодек је присилно подешен на AVC (H.264)</string>
<string name="revanced_spoof_video_streams_ios_force_avc_summary_off">Видео кодек је одређен аутоматски</string>
<string name="revanced_spoof_video_streams_ios_force_avc_user_dialog_message">"Omogućavanje ovoga može da poboljša rad baterije i popravi trzanje prilikom reprodukcije.
<string name="revanced_spoof_video_streams_ios_force_avc_user_dialog_message">"Ако ово омогућите, можда ће се продужити трајање батерије и поправити застој при репродукцији.
AVC ima maksimalnu rezoluciju od 1080p, Opus audio kodek nije dostupan, a reprodukcija videa će koristiti više internet podataka nego VP9 ili AV1."</string>
AVC има максималну резолуцију од 1080p, аудио кодек Opus није доступан, а репродукција видеа ће користити више интернет података него VP9 или AV1."</string>
<string name="revanced_spoof_video_streams_about_ios_title">Нежељени ефекти лажирања на iOS</string>
<string name="revanced_spoof_video_streams_about_ios_summary">"• Приватни дечји видеи се можда неће пуштати
• Стримови уживо почињу од почетка
• Видеи ће се завршити 1 секунду раније"</string>
<string name="revanced_spoof_video_streams_about_android_vr_title">Нежељени ефекти лажирања на Android VR</string>
<string name="revanced_spoof_video_streams_about_android_vr_summary">"• Dečji video zapisi možda se neće prikazati
Livestreamovi počinju od početka
Video zapisi se završavaju 1 sekundu ranije"</string>
<string name="revanced_spoof_video_streams_about_android_vr_summary">"• Дечји видеи се можда неће пуштати
Стримови уживо почињу од почетка
Видеи ће се завршити 1 секунду раније"</string>
<string name="revanced_spoof_video_streams_language_title">Подразумевани језик аудио стрима</string>
<string name="revanced_spoof_video_streams_language_DEFAULT">Језик апликације</string>
<string name="revanced_spoof_video_streams_language_AR">Арапски</string>
@@ -1291,7 +1291,7 @@ AVC ima maksimalnu rezoluciju od 1080p, Opus audio kodek nije dostupan, a reprod
<string name="revanced_spoof_video_streams_language_BG">Бугарски</string>
<string name="revanced_spoof_video_streams_language_BN">Бенгалски</string>
<string name="revanced_spoof_video_streams_language_CA">Каталонски</string>
<string name="revanced_spoof_video_streams_language_CS">Чешка</string>
<string name="revanced_spoof_video_streams_language_CS">Чешки</string>
<string name="revanced_spoof_video_streams_language_DA">Дански</string>
<string name="revanced_spoof_video_streams_language_DE">Немачки</string>
<string name="revanced_spoof_video_streams_language_EL">Грчки</string>
@@ -1314,12 +1314,12 @@ AVC ima maksimalnu rezoluciju od 1080p, Opus audio kodek nije dostupan, a reprod
<string name="revanced_spoof_video_streams_language_LV">Летонски</string>
<string name="revanced_spoof_video_streams_language_MK">Македонски</string>
<string name="revanced_spoof_video_streams_language_MN">Монголски</string>
<string name="revanced_spoof_video_streams_language_MR">Маратхи</string>
<string name="revanced_spoof_video_streams_language_MR">Марати</string>
<string name="revanced_spoof_video_streams_language_MS">Малајски</string>
<string name="revanced_spoof_video_streams_language_MY">Бурмански</string>
<string name="revanced_spoof_video_streams_language_NL">Холандски</string>
<string name="revanced_spoof_video_streams_language_OR">Одиа</string>
<string name="revanced_spoof_video_streams_language_PA">Панџаби</string>
<string name="revanced_spoof_video_streams_language_OR">Одија</string>
<string name="revanced_spoof_video_streams_language_PA">Панџапски</string>
<string name="revanced_spoof_video_streams_language_PL">Пољски</string>
<string name="revanced_spoof_video_streams_language_PT_BR">Португалcки (Бразил)</string>
<string name="revanced_spoof_video_streams_language_PT_PT">Португалски (Португалија)</string>
@@ -1330,9 +1330,9 @@ AVC ima maksimalnu rezoluciju od 1080p, Opus audio kodek nije dostupan, a reprod
<string name="revanced_spoof_video_streams_language_SR">Српски</string>
<string name="revanced_spoof_video_streams_language_SV">Шведски</string>
<string name="revanced_spoof_video_streams_language_SW">Свахили</string>
<string name="revanced_spoof_video_streams_language_TA">Тамил</string>
<string name="revanced_spoof_video_streams_language_TA">Тамилски</string>
<string name="revanced_spoof_video_streams_language_TE">Телугу</string>
<string name="revanced_spoof_video_streams_language_TH">Тајландски</string>
<string name="revanced_spoof_video_streams_language_TH">Тајски</string>
<string name="revanced_spoof_video_streams_language_TR">Турски</string>
<string name="revanced_spoof_video_streams_language_UK">Украјински</string>
<string name="revanced_spoof_video_streams_language_UR">Урду</string>
@@ -1347,9 +1347,9 @@ AVC ima maksimalnu rezoluciju od 1080p, Opus audio kodek nije dostupan, a reprod
<string name="revanced_block_audio_ads_summary_off">Аудио огласи су одблокирани</string>
</patch>
<patch id="ad.embedded.embeddedAdsPatch">
<string name="revanced_embedded_ads_service_unavailable">%s nije dostupan, reklame se možda prikazuju. Pokušajte da promenite servis za blokiranje reklama u postavkama.</string>
<string name="revanced_embedded_ads_service_failed">%s je vratio grešku, reklame se možda prikazuju. Pokušajte da promenite servis za blokiranje reklama u postavkama.</string>
<string name="revanced_block_embedded_ads_title">Блокирање уграђених видео огласа</string>
<string name="revanced_embedded_ads_service_unavailable">%s није доступан, огласи се можда приказују. Покушајте да промените услугу блокирања огласа у подешавањима.</string>
<string name="revanced_embedded_ads_service_failed">%s је вратио грешку, огласи се можда приказују. Покушајте да промените услугу блокирања огласа у подешавањима.</string>
<string name="revanced_block_embedded_ads_title">Блокирај уграђене видео огласе</string>
<string name="revanced_block_embedded_ads_entry_1">Онемогућено</string>
<string name="revanced_block_embedded_ads_entry_2">Luminous прокси</string>
<string name="revanced_block_embedded_ads_entry_3">PurpleAdBlock прокси</string>

View File

@@ -25,12 +25,12 @@ Second \"item\" text"</string>
<string name="revanced_check_environment_failed_title">Kontroller misslyckades</string>
<string name="revanced_check_environment_dialog_open_official_source_button">Öppna officiell hemsida</string>
<string name="revanced_check_environment_dialog_ignore_button">Ignorera</string>
<string name="revanced_check_environment_failed_message">&lt;h5&gt;Denna app verkar inte vara lappad av dig.&lt;/h5&gt;&lt;br&gt;Denna app kanske inte fungerar korrekt, &lt;b&gt;kan vara skadligt eller till och med farligt att använda&lt;/b&gt;.&lt;br&gt;&lt;br&gt;Dessa kontroller innebär att appen är förlagad eller erhållen från någon annan:&lt;br&gt;&lt;br&gt;&lt;small&gt;%1$s&lt;/small&gt;&lt;br&gt;Det rekommenderas starkt att &lt;b&gt;avinstallera denna app och patch den själv&lt;/b&gt; för att säkerställa att du använder en validerad och säker app.&lt;p&gt;&lt;br&gt;Om den ignoreras kommer denna varning endast visas två gånger.</string>
<string name="revanced_check_environment_failed_message">&lt;h5&gt;Appen verkar inte vara patchad av dig.&lt;/h5&gt;&lt;br&gt;Den kanske inte fungerar korrekt, &lt;b&gt;kan vara skadlig eller till och med farlig att använda&lt;/b&gt;.&lt;br&gt;&lt;br&gt;Resultatet av kontrollerna innebär att appen är förlagad eller hämtad från någon annan:&lt;br&gt;&lt;br&gt;&lt;small&gt;%1$s&lt;/small&gt;&lt;br&gt;Det rekommenderas starkt att &lt;b&gt;avinstallera appen och patcha den själv&lt;/b&gt; för att säkerställa att du använder en validerad och säker app.&lt;p&gt;&lt;br&gt;Om varningen ignoreras kommer den endast visas två gånger.</string>
<string name="revanced_check_environment_not_same_patching_device">Patchad på en annan enhet</string>
<string name="revanced_check_environment_manager_not_expected_installer">Inte installerad av ReVanced Manager</string>
<string name="revanced_check_environment_not_near_patch_time">Patchade mer än 10 minuter sedan</string>
<string name="revanced_check_environment_not_near_patch_time_days">Patchade %s dagar sedan</string>
<string name="revanced_check_environment_not_near_patch_time_invalid">APK byggdatum är skadat</string>
<string name="revanced_check_environment_not_near_patch_time">Patchad för mer än 10 minuter sedan</string>
<string name="revanced_check_environment_not_near_patch_time_days">Patchad för %s dagar sedan</string>
<string name="revanced_check_environment_not_near_patch_time_invalid">Byggdatum för APK är skadat</string>
</patch>
<patch id="misc.settings.settingsResourcePatch">
<string name="revanced_settings_confirm_user_dialog_title">Vill du fortsätta?</string>
@@ -54,7 +54,7 @@ Second \"item\" text"</string>
</patch>
<patch id="misc.gms.gmsCoreSupportResourcePatch">
<!-- Translations of this should not be longer than the original English text, otherwise the text can be clipped and not entirely shown. -->
<string name="gms_core_toast_not_installed_message">MicroG GmsCore är inte installerat. Installera.</string>
<string name="gms_core_toast_not_installed_message">MicroG GmsCore är inte installerat. Installera den.</string>
<string name="gms_core_dialog_title">Åtgärd krävs</string>
<string name="gms_core_dialog_not_whitelisted_not_allowed_in_background_message">"MicroG GmsCore saknar behörighet att köras i bakgrunden.
@@ -85,8 +85,8 @@ Tryck på Fortsätt-knappen och tillåt optimeringsändringar."</string>
</patch>
<patch id="misc.backgroundplayback.backgroundPlaybackPatch">
<string name="revanced_shorts_disable_background_playback_title">Inaktivera Shorts bakgrundsuppspelning</string>
<string name="revanced_shorts_disable_background_playback_summary_on">Shorts bakgrundsspel är inaktiverat</string>
<string name="revanced_shorts_disable_background_playback_summary_off">Shorts bakgrundsuppspelning är aktiverad</string>
<string name="revanced_shorts_disable_background_playback_summary_on">Shorts bakgrundsuppspelning är inaktiverat</string>
<string name="revanced_shorts_disable_background_playback_summary_off">Shorts bakgrundsuppspelning är aktiverat</string>
</patch>
<patch id="misc.debugging.enableDebuggingPatch">
<string name="revanced_debug_screen_title">Felsökning</string>
@@ -953,6 +953,15 @@ Redan finns"</string>
<string name="revanced_sb_new_segment_time_start">Tid då segmentet börjar på</string>
<string name="revanced_sb_new_segment_time_end">Tid då segmentet slutar på</string>
<string name="revanced_sb_new_segment_confirm_title">Är tiderna korrekta?</string>
<string name="revanced_sb_new_segment_confirm_content">"Segmentet är från
%1$s
till
%2$s
(%3$s)
Redo att skicka in?"</string>
<string name="revanced_sb_new_segment_start_is_before_end">Start måste vara innan slutet</string>
<string name="revanced_sb_new_segment_mark_locations_first">Markera två platser i tidsfältet först</string>
<string name="revanced_sb_new_segment_preview_segment_first">Förhandsgranska segmentet och se till att det hoppar över smidigt</string>

View File

@@ -158,9 +158,6 @@ You will not be notified of any unexpected events."</string>
<string name="revanced_hide_timed_reactions_title">Hide timed reactions</string>
<string name="revanced_hide_timed_reactions_summary_on">Timed reactions are hidden</string>
<string name="revanced_hide_timed_reactions_summary_off">Timed reactions are shown</string>
<string name="revanced_hide_search_result_shelf_header_title">Hide search result shelf header</string>
<string name="revanced_hide_search_result_shelf_header_summary_on">Shelf header is hidden</string>
<string name="revanced_hide_search_result_shelf_header_summary_off">Shelf header is shown</string>
<string name="revanced_hide_channel_guidelines_title">Hide channel guidelines</string>
<string name="revanced_hide_channel_guidelines_summary_on">Channel guidelines are hidden</string>
<string name="revanced_hide_channel_guidelines_summary_off">Channel guidelines are shown</string>
@@ -1226,6 +1223,11 @@ Enabling this can unlock higher video qualities"</string>
<string name="revanced_disable_zoom_haptics_summary_on">Haptics are disabled</string>
<string name="revanced_disable_zoom_haptics_summary_off">Haptics are enabled</string>
</patch>
<patch id="video.audio.forceOriginalAudioPatch">
<string name="revanced_force_original_audio_title">Force original audio</string>
<string name="revanced_force_original_audio_summary_on">Using original audio</string>
<string name="revanced_force_original_audio_summary_off">Using default audio</string>
</patch>
<patch id="video.quality.rememberVideoQualityPatch">
<!-- Translations should use the same text as revanced_custom_playback_speeds_auto -->
<string name="revanced_video_quality_default_entry_1">Auto</string>
@@ -1288,12 +1290,11 @@ Video playback may not work"</string>
AVC has a maximum resolution of 1080p, Opus audio codec is not available, and video playback will use more internet data than VP9 or AV1."</string>
<string name="revanced_spoof_video_streams_about_ios_title">iOS spoofing side effects</string>
<string name="revanced_spoof_video_streams_about_ios_summary">"• Private kids videos may not play
• Livestreams start from the beginning
• Videos end 1 second early"</string>
<string name="revanced_spoof_video_streams_about_android_vr_title">Android VR spoofing side effects</string>
<string name="revanced_spoof_video_streams_about_android_vr_summary">"• Kids videos may not play
Livestreams start from the beginning
Videos end 1 second early"</string>
Audio track menu is missing
Stable volume is not available"</string>
<string name="revanced_spoof_video_streams_language_title">Default audio stream language</string>
<string name="revanced_spoof_video_streams_language_DEFAULT">App language</string>
<string name="revanced_spoof_video_streams_language_AR">Arabic</string>