diff --git a/extensions/shared/library/src/main/java/app/revanced/extension/shared/settings/BaseSettings.java b/extensions/shared/library/src/main/java/app/revanced/extension/shared/settings/BaseSettings.java index 9a62db4332..7072ea07af 100644 --- a/extensions/shared/library/src/main/java/app/revanced/extension/shared/settings/BaseSettings.java +++ b/extensions/shared/library/src/main/java/app/revanced/extension/shared/settings/BaseSettings.java @@ -5,7 +5,6 @@ import static app.revanced.extension.shared.settings.Setting.parent; import static app.revanced.extension.shared.spoof.SpoofVideoStreamsPatch.ForceiOSAVCAvailability; -import app.revanced.extension.shared.spoof.AudioStreamLanguage; import app.revanced.extension.shared.spoof.ClientType; /** @@ -22,7 +21,6 @@ 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 SPOOF_VIDEO_STREAMS_LANGUAGE = new EnumSetting<>("revanced_spoof_video_streams_language", AudioStreamLanguage.DEFAULT, parent(SPOOF_VIDEO_STREAMS)); 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()); public static final EnumSetting SPOOF_VIDEO_STREAMS_CLIENT_TYPE = new EnumSetting<>("revanced_spoof_video_streams_client", ClientType.ANDROID_VR, true, parent(SPOOF_VIDEO_STREAMS)); diff --git a/extensions/shared/library/src/main/java/app/revanced/extension/shared/spoof/AudioStreamLanguage.java b/extensions/shared/library/src/main/java/app/revanced/extension/shared/spoof/AudioStreamLanguage.java index 0d9070e2fd..c5455ae4c5 100644 --- a/extensions/shared/library/src/main/java/app/revanced/extension/shared/spoof/AudioStreamLanguage.java +++ b/extensions/shared/library/src/main/java/app/revanced/extension/shared/spoof/AudioStreamLanguage.java @@ -3,8 +3,21 @@ import java.util.Locale; public enum AudioStreamLanguage { + /** + * Change nothing, same as unpatched. + */ DEFAULT, + /** + * Original language of the video. + */ + ORIGINAL, + + /** + * Current language the app is set to. + */ + APP_LANGUAGE, + // Language codes found in locale_config.xml // Region specific variants of Chinese/English/Spanish/French have been removed. AF, @@ -86,15 +99,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; diff --git a/extensions/shared/library/src/main/java/app/revanced/extension/shared/spoof/ClientType.java b/extensions/shared/library/src/main/java/app/revanced/extension/shared/spoof/ClientType.java index f51779a15a..032d420ec4 100644 --- a/extensions/shared/library/src/main/java/app/revanced/extension/shared/spoof/ClientType.java +++ b/extensions/shared/library/src/main/java/app/revanced/extension/shared/spoof/ClientType.java @@ -16,8 +16,8 @@ public enum ClientType { "com.google.android.apps.youtube.vr.oculus/1.56.21 (Linux; U; Android 12; GB) gzip", "32", // Android 12.1 "1.56.21", - true, - true), + true + ), // Specific for kids videos. IOS(5, "IOS", @@ -39,22 +39,8 @@ public enum ClientType { // but 17.40 is the last version that supports iOS 13. ? "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); + false + ); private static boolean forceAVC() { return BaseSettings.SPOOF_VIDEO_STREAMS_IOS_FORCE_AVC.get(); @@ -100,11 +86,6 @@ private static boolean forceAVC() { */ public final boolean canLogin; - /** - * If a language code should be used. - */ - public final boolean useLanguageCode; - ClientType(int id, String clientName, String deviceModel, @@ -112,8 +93,7 @@ private static boolean forceAVC() { String userAgent, @Nullable String androidSdkVersion, String clientVersion, - boolean canLogin, - boolean useLanguageCode) { + boolean canLogin) { this.id = id; this.clientName = clientName; this.deviceModel = deviceModel; @@ -122,6 +102,5 @@ private static boolean forceAVC() { this.androidSdkVersion = androidSdkVersion; this.clientVersion = clientVersion; this.canLogin = canLogin; - this.useLanguageCode = useLanguageCode; } } diff --git a/extensions/shared/library/src/main/java/app/revanced/extension/shared/spoof/SpoofVideoStreamsPatch.java b/extensions/shared/library/src/main/java/app/revanced/extension/shared/spoof/SpoofVideoStreamsPatch.java index 5ad672f126..4016c8ef99 100644 --- a/extensions/shared/library/src/main/java/app/revanced/extension/shared/spoof/SpoofVideoStreamsPatch.java +++ b/extensions/shared/library/src/main/java/app/revanced/extension/shared/spoof/SpoofVideoStreamsPatch.java @@ -22,14 +22,6 @@ public class SpoofVideoStreamsPatch { 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. diff --git a/extensions/shared/library/src/main/java/app/revanced/extension/shared/spoof/requests/PlayerRoutes.java b/extensions/shared/library/src/main/java/app/revanced/extension/shared/spoof/requests/PlayerRoutes.java index ca1a0eb897..80c4632e0e 100644 --- a/extensions/shared/library/src/main/java/app/revanced/extension/shared/spoof/requests/PlayerRoutes.java +++ b/extensions/shared/library/src/main/java/app/revanced/extension/shared/spoof/requests/PlayerRoutes.java @@ -5,11 +5,11 @@ import java.io.IOException; import java.net.HttpURLConnection; +import java.util.Locale; import app.revanced.extension.shared.Logger; import app.revanced.extension.shared.requests.Requester; import app.revanced.extension.shared.requests.Route; -import app.revanced.extension.shared.settings.BaseSettings; import app.revanced.extension.shared.spoof.ClientType; final class PlayerRoutes { @@ -35,9 +35,9 @@ static String createInnertubeBody(ClientType clientType) { JSONObject context = new JSONObject(); JSONObject client = new JSONObject(); - if (clientType.useLanguageCode) { - client.put("hl", BaseSettings.SPOOF_VIDEO_STREAMS_LANGUAGE.get().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. + client.put("hl", Locale.getDefault().toLanguageTag()); client.put("clientName", clientType.clientName); client.put("clientVersion", clientType.clientVersion); client.put("deviceModel", clientType.deviceModel); diff --git a/extensions/youtube/src/main/java/app/revanced/extension/youtube/patches/ChangeDefaultAudioLanguagePatch.java b/extensions/youtube/src/main/java/app/revanced/extension/youtube/patches/ChangeDefaultAudioLanguagePatch.java new file mode 100644 index 0000000000..eec276618e --- /dev/null +++ b/extensions/youtube/src/main/java/app/revanced/extension/youtube/patches/ChangeDefaultAudioLanguagePatch.java @@ -0,0 +1,75 @@ +package app.revanced.extension.youtube.patches; + +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +import app.revanced.extension.shared.Logger; +import app.revanced.extension.shared.spoof.AudioStreamLanguage; +import app.revanced.extension.youtube.settings.Settings; + +@SuppressWarnings("unused") +public class ChangeDefaultAudioLanguagePatch { + + private static final String DEFAULT_AUDIO_TRACKS_IDENTIFIER = "original"; + + /** + * Audio track identifier. + * + * Examples: + * fr-FR.10 + * it.10 + */ + private static final Pattern AUDIO_TRACK_ID_PATTERN = + Pattern.compile("^([a-z]{2})(-[A-Z]{2})?(\\.\\d+)"); + + private static void printDebug(Logger.LogMessage message) { + // Do not log by default as it's spammy. + final boolean logAudioStreams = false; + + //noinspection ConstantConditions + if (logAudioStreams) { + Logger.printDebug(message); + } + } + + /** + * Injection point. + */ + public static boolean setAudioStreamAsDefault(boolean isDefault, String audioTrackId, String audioTrackDisplayName) { + try { + AudioStreamLanguage defaultLanguage = Settings.AUDIO_DEFAULT_LANGUAGE.get(); + if (defaultLanguage == AudioStreamLanguage.DEFAULT) { + return isDefault; // Do nothing. + } + + printDebug(() -> "isDefault: " + isDefault + " audioTrackId: " + audioTrackId + + " audioTrackDisplayName:" + audioTrackDisplayName); + + if (defaultLanguage == AudioStreamLanguage.ORIGINAL) { + final boolean isOriginal = audioTrackDisplayName.contains(DEFAULT_AUDIO_TRACKS_IDENTIFIER); + if (isOriginal) { + printDebug(() -> "Using original audio language: " + audioTrackId); + } + + return isOriginal; + } + + Matcher matcher = AUDIO_TRACK_ID_PATTERN.matcher(audioTrackId); + if (!matcher.matches()) { + Logger.printException(() -> "Cannot set default audio, unknown track: " + audioTrackId); + return isDefault; + } + + String desiredIso639 = defaultLanguage.getIso639_1(); + if (desiredIso639.equals(matcher.group(1)) + || desiredIso639.equals(matcher.group(2))) { + printDebug(() -> "Using preferred audio language: " + audioTrackId); + return true; + } + } catch (Exception ex) { + Logger.printException(() -> "setAudioStreamAsDefault failure", ex); + } + + return isDefault; + } +} diff --git a/extensions/youtube/src/main/java/app/revanced/extension/youtube/settings/Settings.java b/extensions/youtube/src/main/java/app/revanced/extension/youtube/settings/Settings.java index 950f34be4d..1bc1ddea04 100644 --- a/extensions/youtube/src/main/java/app/revanced/extension/youtube/settings/Settings.java +++ b/extensions/youtube/src/main/java/app/revanced/extension/youtube/settings/Settings.java @@ -34,6 +34,7 @@ import app.revanced.extension.shared.settings.Setting; import app.revanced.extension.shared.settings.StringSetting; import app.revanced.extension.shared.settings.preference.SharedPrefCategory; +import app.revanced.extension.shared.spoof.AudioStreamLanguage; import app.revanced.extension.youtube.patches.AlternativeThumbnailsPatch.DeArrowAvailability; import app.revanced.extension.youtube.patches.AlternativeThumbnailsPatch.StillImagesAvailability; import app.revanced.extension.youtube.patches.AlternativeThumbnailsPatch.ThumbnailOption; @@ -52,6 +53,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 EnumSetting AUDIO_DEFAULT_LANGUAGE = new EnumSetting<>("revanced_audio_default_language", AudioStreamLanguage.DEFAULT); // Ads public static final BooleanSetting HIDE_BUTTONED_ADS = new BooleanSetting("revanced_hide_buttoned_ads", TRUE); diff --git a/extensions/youtube/src/main/java/app/revanced/extension/youtube/settings/preference/ReVancedPreferenceFragment.java b/extensions/youtube/src/main/java/app/revanced/extension/youtube/settings/preference/ReVancedPreferenceFragment.java index ed4502aef4..24bb9d1315 100644 --- a/extensions/youtube/src/main/java/app/revanced/extension/youtube/settings/preference/ReVancedPreferenceFragment.java +++ b/extensions/youtube/src/main/java/app/revanced/extension/youtube/settings/preference/ReVancedPreferenceFragment.java @@ -48,35 +48,44 @@ public static Drawable getBackButtonDrawable() { /** * 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> firstPairs = new ArrayList<>(firstEntriesToPreserve); + List> pairsToSort = new ArrayList<>(entrySize); - List> 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 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 pair : firstPairs) { + sortedEntries[i] = pair.first; + sortedEntryValues[i] = pair.second; + i++; + } - int i = 1; - for (Pair pair : entryPairs) { + for (Pair pair : pairsToSort) { sortedEntries[i] = pair.first; sortedEntryValues[i] = pair.second; i++; @@ -100,9 +109,9 @@ protected void initialize() { CustomPlaybackSpeedPatch.initializeListPreference(playbackPreference); } - preference = findPreference(Settings.SPOOF_VIDEO_STREAMS_LANGUAGE.key); + preference = findPreference(Settings.AUDIO_DEFAULT_LANGUAGE.key); if (preference instanceof ListPreference languagePreference) { - sortListPreferenceByValues(languagePreference); + sortListPreferenceByValues(languagePreference, 3); } } catch (Exception ex) { Logger.printException(() -> "initialize failure", ex); diff --git a/patches/api/patches.api b/patches/api/patches.api index 5f7861ff2f..21c40715d5 100644 --- a/patches/api/patches.api +++ b/patches/api/patches.api @@ -1392,6 +1392,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/ChangeDefaultAudioLanguagePatchKt { + public static final fun getChangeDefaultAudioLanguagePatch ()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 diff --git a/patches/src/main/kotlin/app/revanced/patches/music/misc/spoof/SpoofVideoStreamsPatch.kt b/patches/src/main/kotlin/app/revanced/patches/music/misc/spoof/SpoofVideoStreamsPatch.kt index 573ac0c154..21eb321569 100644 --- a/patches/src/main/kotlin/app/revanced/patches/music/misc/spoof/SpoofVideoStreamsPatch.kt +++ b/patches/src/main/kotlin/app/revanced/patches/music/misc/spoof/SpoofVideoStreamsPatch.kt @@ -1,15 +1,7 @@ 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" - ) }) \ No newline at end of file diff --git a/patches/src/main/kotlin/app/revanced/patches/youtube/misc/spoof/SpoofVideoStreamsPatch.kt b/patches/src/main/kotlin/app/revanced/patches/youtube/misc/spoof/SpoofVideoStreamsPatch.kt index 45bdfc0c89..a9c945f754 100644 --- a/patches/src/main/kotlin/app/revanced/patches/youtube/misc/spoof/SpoofVideoStreamsPatch.kt +++ b/patches/src/main/kotlin/app/revanced/patches/youtube/misc/spoof/SpoofVideoStreamsPatch.kt @@ -40,10 +40,6 @@ val spoofVideoStreamsPatch = spoofVideoStreamsPatch({ "revanced_spoof_video_streams_client", summaryKey = null, ), - ListPreference( - "revanced_spoof_video_streams_language", - summaryKey = null - ), SwitchPreference("revanced_spoof_video_streams_ios_force_avc"), // Preference requires a title but the actual text is chosen at runtime. NonInteractivePreference( diff --git a/patches/src/main/kotlin/app/revanced/patches/youtube/video/audio/ChangeDefaultAudioLanguagePatch.kt b/patches/src/main/kotlin/app/revanced/patches/youtube/video/audio/ChangeDefaultAudioLanguagePatch.kt new file mode 100644 index 0000000000..82e5391a1a --- /dev/null +++ b/patches/src/main/kotlin/app/revanced/patches/youtube/video/audio/ChangeDefaultAudioLanguagePatch.kt @@ -0,0 +1,135 @@ +package app.revanced.patches.youtube.video.audio + +import app.revanced.patcher.extensions.InstructionExtensions.addInstructions +import app.revanced.patcher.extensions.InstructionExtensions.getInstruction +import app.revanced.patcher.patch.bytecodePatch +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.ListPreference +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.ImmutableMethod +import com.android.tools.smali.dexlib2.immutable.ImmutableMethodParameter + +private const val EXTENSION_CLASS_DESCRIPTOR = + "Lapp/revanced/extension/youtube/patches/ChangeDefaultAudioLanguagePatch;" + +@Suppress("unused") +val changeDefaultAudioLanguagePatch = bytecodePatch( + name = "Change default audio track", + description = "Adds an option to set a video default audio language .", +) { + 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.changeDefaultAudioTrackPatch") + + PreferenceScreen.VIDEO.addPreferences( + ListPreference( + "revanced_audio_default_language", + summaryKey = null + ) + ) + + fun Method.firstFormatStreamingModelCall( + returnType: String = "Ljava/lang/String;" + ): MutableMethod { + val audioTrackIdIndex = indexOfFirstInstructionOrThrow { + val reference = getReference() + 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 helper method because the isDefaultAudioTrack() has only 2 registers and 3 are needed. + val helperMethodClass = type + val helperMethodName = "extensions_isDefaultAudioTrack" + val helperMethod = ImmutableMethod( + helperMethodClass, + helperMethodName, + listOf(ImmutableMethodParameter("Z", null, null)), + "Z", + AccessFlags.PRIVATE.value, + null, + null, + MutableMethodImplementation(4), + ).toMutable().apply { + // This is the equivalent of + // String featureName = feature.toString() + // + // return null + addInstructions( + 0, + """ + invoke-virtual { p0 }, $audioTrackIdMethod + move-result-object v0 + + invoke-virtual { p0 }, $audioTrackDisplayNameMethod + move-result-object v1 + + invoke-static { p1, v0, v1 }, $EXTENSION_CLASS_DESCRIPTOR->setAudioStreamAsDefault(ZLjava/lang/String;Ljava/lang/String;)Z + move-result v0 + + return v0 + """ + ) + } + methods.add(helperMethod) + + // Modify isDefaultAudioTrack() to call extension helper method. + isDefaultMethod.apply { + val index = indexOfFirstInstructionOrThrow(Opcode.RETURN) + val register = getInstruction(index).registerA + + addInstructions( + index, + """ + invoke-direct { p0, v$register }, $helperMethodClass->$helperMethodName(Z)Z + move-result v$register + """ + ) + } + } + } +} diff --git a/patches/src/main/kotlin/app/revanced/patches/youtube/video/audio/Fingerprints.kt b/patches/src/main/kotlin/app/revanced/patches/youtube/video/audio/Fingerprints.kt new file mode 100644 index 0000000000..a338ea6b7d --- /dev/null +++ b/patches/src/main/kotlin/app/revanced/patches/youtube/video/audio/Fingerprints.kt @@ -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 +} \ No newline at end of file diff --git a/patches/src/main/resources/addresources/values/arrays.xml b/patches/src/main/resources/addresources/values/arrays.xml index c600e86b7e..ef86eae874 100644 --- a/patches/src/main/resources/addresources/values/arrays.xml +++ b/patches/src/main/resources/addresources/values/arrays.xml @@ -11,116 +11,6 @@ ANDROID_VR IOS - - @string/revanced_spoof_video_streams_language_DEFAULT - @string/revanced_spoof_video_streams_language_AR - @string/revanced_spoof_video_streams_language_AZ - @string/revanced_spoof_video_streams_language_BG - @string/revanced_spoof_video_streams_language_BN - @string/revanced_spoof_video_streams_language_CA - @string/revanced_spoof_video_streams_language_CS - @string/revanced_spoof_video_streams_language_DA - @string/revanced_spoof_video_streams_language_DE - @string/revanced_spoof_video_streams_language_EL - @string/revanced_spoof_video_streams_language_EN - @string/revanced_spoof_video_streams_language_ES - @string/revanced_spoof_video_streams_language_ET - @string/revanced_spoof_video_streams_language_FA - @string/revanced_spoof_video_streams_language_FI - @string/revanced_spoof_video_streams_language_FR - @string/revanced_spoof_video_streams_language_GU - @string/revanced_spoof_video_streams_language_HI - @string/revanced_spoof_video_streams_language_HR - @string/revanced_spoof_video_streams_language_HU - @string/revanced_spoof_video_streams_language_ID - @string/revanced_spoof_video_streams_language_IT - @string/revanced_spoof_video_streams_language_JA - @string/revanced_spoof_video_streams_language_KK - @string/revanced_spoof_video_streams_language_KO - @string/revanced_spoof_video_streams_language_LT - @string/revanced_spoof_video_streams_language_LV - @string/revanced_spoof_video_streams_language_MK - @string/revanced_spoof_video_streams_language_MN - @string/revanced_spoof_video_streams_language_MR - @string/revanced_spoof_video_streams_language_MS - @string/revanced_spoof_video_streams_language_MY - @string/revanced_spoof_video_streams_language_NL - @string/revanced_spoof_video_streams_language_OR - @string/revanced_spoof_video_streams_language_PA - @string/revanced_spoof_video_streams_language_PL - @string/revanced_spoof_video_streams_language_PT_BR - @string/revanced_spoof_video_streams_language_PT_PT - @string/revanced_spoof_video_streams_language_RO - @string/revanced_spoof_video_streams_language_RU - @string/revanced_spoof_video_streams_language_SK - @string/revanced_spoof_video_streams_language_SL - @string/revanced_spoof_video_streams_language_SR - @string/revanced_spoof_video_streams_language_SV - @string/revanced_spoof_video_streams_language_SW - @string/revanced_spoof_video_streams_language_TA - @string/revanced_spoof_video_streams_language_TE - @string/revanced_spoof_video_streams_language_TH - @string/revanced_spoof_video_streams_language_TR - @string/revanced_spoof_video_streams_language_UK - @string/revanced_spoof_video_streams_language_UR - @string/revanced_spoof_video_streams_language_VI - @string/revanced_spoof_video_streams_language_ZH - - - DEFAULT - AR - AZ - BG - BN - CA - CS - DA - DE - EL - EN - ES - ET - FA - FI - FR - GU - HI - HR - HU - ID - IT - JA - KK - KO - LT - LV - MK - MN - MR - MS - MY - NL - OR - PA - PL - PT_BR - PT_PT - RO - RU - SK - SL - SR - SV - SW - TA - TE - TH - TR - UK - UR - VI - ZH - @@ -270,6 +160,122 @@ 144 + + + @string/revanced_audio_default_language_DEFAULT + @string/revanced_audio_default_language_ORIGINAL + @string/revanced_audio_default_language_APP_LANGUAGE + @string/revanced_audio_default_language_AR + @string/revanced_audio_default_language_AZ + @string/revanced_audio_default_language_BG + @string/revanced_audio_default_language_BN + @string/revanced_audio_default_language_CA + @string/revanced_audio_default_language_CS + @string/revanced_audio_default_language_DA + @string/revanced_audio_default_language_DE + @string/revanced_audio_default_language_EL + @string/revanced_audio_default_language_EN + @string/revanced_audio_default_language_ES + @string/revanced_audio_default_language_ET + @string/revanced_audio_default_language_FA + @string/revanced_audio_default_language_FI + @string/revanced_audio_default_language_FR + @string/revanced_audio_default_language_GU + @string/revanced_audio_default_language_HI + @string/revanced_audio_default_language_HR + @string/revanced_audio_default_language_HU + @string/revanced_audio_default_language_ID + @string/revanced_audio_default_language_IT + @string/revanced_audio_default_language_JA + @string/revanced_audio_default_language_KK + @string/revanced_audio_default_language_KO + @string/revanced_audio_default_language_LT + @string/revanced_audio_default_language_LV + @string/revanced_audio_default_language_MK + @string/revanced_audio_default_language_MN + @string/revanced_audio_default_language_MR + @string/revanced_audio_default_language_MS + @string/revanced_audio_default_language_MY + @string/revanced_audio_default_language_NL + @string/revanced_audio_default_language_OR + @string/revanced_audio_default_language_PA + @string/revanced_audio_default_language_PL + @string/revanced_audio_default_language_PT_BR + @string/revanced_audio_default_language_PT_PT + @string/revanced_audio_default_language_RO + @string/revanced_audio_default_language_RU + @string/revanced_audio_default_language_SK + @string/revanced_audio_default_language_SL + @string/revanced_audio_default_language_SR + @string/revanced_audio_default_language_SV + @string/revanced_audio_default_language_SW + @string/revanced_audio_default_language_TA + @string/revanced_audio_default_language_TE + @string/revanced_audio_default_language_TH + @string/revanced_audio_default_language_TR + @string/revanced_audio_default_language_UK + @string/revanced_audio_default_language_UR + @string/revanced_audio_default_language_VI + @string/revanced_audio_default_language_ZH + + + DEFAULT + ORIGINAL + APP_LANGUAGE + AR + AZ + BG + BN + CA + CS + DA + DE + EL + EN + ES + ET + FA + FI + FR + GU + HI + HR + HU + ID + IT + JA + KK + KO + LT + LV + MK + MN + MR + MS + MY + NL + OR + PA + PL + PT_BR + PT_PT + RO + RU + SK + SL + SR + SV + SW + TA + TE + TH + TR + UK + UR + VI + ZH + + diff --git a/patches/src/main/resources/addresources/values/strings.xml b/patches/src/main/resources/addresources/values/strings.xml index 56db255896..2ce8e0c4ed 100644 --- a/patches/src/main/resources/addresources/values/strings.xml +++ b/patches/src/main/resources/addresources/values/strings.xml @@ -1226,6 +1226,64 @@ Enabling this can unlock higher video qualities" Haptics are disabled Haptics are enabled + + Default audio stream language + Default + Original video language + App language + Arabic + Azerbaijani + Bulgarian + Bengali + Catalan + Czech + Danish + German + Greek + English + Spanish + Estonian + Persian + Finnish + French + Gujarati + Hindi + Croatian + Hungarian + Indonesian + Italian + Japanese + Kazakh + Korean + Lithuanian + Latvian + Macedonian + Mongolian + Marathi + Malay + Burmese + Dutch + Odia + Punjabi + Polish + Portuguese (Brazil) + Portuguese (Portugal) + Romanian + Russian + Slovak + Slovene + Serbian + Swedish + Swahili + Tamil + Telugu + Thai + Turkish + Ukrainian + Urdu + Vietnamese + Chinese + Auto @@ -1292,62 +1350,8 @@ AVC has a maximum resolution of 1080p, Opus audio codec is not available, and vi • Videos end 1 second early" Android VR spoofing side effects "• Kids videos may not play -• Livestreams start from the beginning -• Videos end 1 second early" - Default audio stream language - App language - Arabic - Azerbaijani - Bulgarian - Bengali - Catalan - Czech - Danish - German - Greek - English - Spanish - Estonian - Persian - Finnish - French - Gujarati - Hindi - Croatian - Hungarian - Indonesian - Italian - Japanese - Kazakh - Korean - Lithuanian - Latvian - Macedonian - Mongolian - Marathi - Malay - Burmese - Dutch - Odia - Punjabi - Polish - Portuguese (Brazil) - Portuguese (Portugal) - Romanian - Russian - Slovak - Slovene - Serbian - Swedish - Swahili - Tamil - Telugu - Thai - Turkish - Ukrainian - Urdu - Vietnamese - Chinese +• Audio track menu is missing +• Stable volume is not available"