diff --git a/extensions/music/build.gradle.kts b/extensions/music/build.gradle.kts index 1e7be309c..f84a54a0d 100644 --- a/extensions/music/build.gradle.kts +++ b/extensions/music/build.gradle.kts @@ -1,3 +1,9 @@ +dependencies { + compileOnly(project(":extensions:shared:library")) + compileOnly(project(":extensions:youtube:stub")) + compileOnly(libs.annotation) +} + android { defaultConfig { minSdk = 26 diff --git a/extensions/music/src/main/java/app/revanced/extension/music/patches/spoof/SpoofVideoStreamsPatch.java b/extensions/music/src/main/java/app/revanced/extension/music/patches/spoof/SpoofVideoStreamsPatch.java new file mode 100644 index 000000000..5c60437b5 --- /dev/null +++ b/extensions/music/src/main/java/app/revanced/extension/music/patches/spoof/SpoofVideoStreamsPatch.java @@ -0,0 +1,23 @@ +package app.revanced.extension.music.patches.spoof; + +import static app.revanced.extension.shared.spoof.ClientType.ANDROID_VR_1_43_32; +import static app.revanced.extension.shared.spoof.ClientType.ANDROID_VR_1_61_48; + +import app.revanced.extension.shared.spoof.ClientType; +import app.revanced.extension.shared.spoof.requests.StreamingDataRequest; + +@SuppressWarnings("unused") +public class SpoofVideoStreamsPatch { + + /** + * Injection point. + */ + public static void setClientOrderToUse() { + ClientType[] availableClients = { + ANDROID_VR_1_43_32, + ANDROID_VR_1_61_48, + }; + + StreamingDataRequest.setClientOrderToUse(availableClients, ANDROID_VR_1_43_32); + } +} 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 1fafec71c..e2c70f616 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 @@ -6,8 +6,6 @@ import static app.revanced.extension.shared.settings.Setting.parent; import static app.revanced.extension.shared.spoof.SpoofVideoStreamsPatch.AudioStreamLanguageOverrideAvailability; import static app.revanced.extension.shared.spoof.SpoofVideoStreamsPatch.SpoofiOSAvailability; -import app.revanced.extension.shared.Logger; -import app.revanced.extension.shared.Utils; import app.revanced.extension.shared.spoof.ClientType; /** @@ -36,18 +34,5 @@ public class BaseSettings { 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 SpoofiOSAvailability()); // Client type must be last spoof setting due to cyclic references. - public static final EnumSetting SPOOF_VIDEO_STREAMS_CLIENT_TYPE = new EnumSetting<>("revanced_spoof_video_streams_client_type", ClientType.ANDROID_VR_NO_AUTH, true, parent(SPOOF_VIDEO_STREAMS)); - - static { - // Data migration fix for YT Music users updating from very old patches that always - // stored default values in preference object, which requires manually updating - // the setting if the default changes. Package name may not contain "youtube.music" - // if the user has used change package name patch, but this will detect users - // with default installations. - if (!SPOOF_VIDEO_STREAMS_CLIENT_TYPE.isSetToDefault() - && Utils.getContext().getPackageName().contains("youtube.music")) { - Logger.printInfo(() -> "Resetting spoof client from: " + SPOOF_VIDEO_STREAMS_CLIENT_TYPE.get()); - SPOOF_VIDEO_STREAMS_CLIENT_TYPE.resetToDefault(); - } - } + public static final EnumSetting SPOOF_VIDEO_STREAMS_CLIENT_TYPE = new EnumSetting<>("revanced_spoof_video_streams_client_type", ClientType.ANDROID_VR_1_61_48, true, parent(SPOOF_VIDEO_STREAMS)); } 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 39d6ad823..cc80cb924 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 @@ -12,7 +12,7 @@ import app.revanced.extension.shared.settings.BaseSettings; public enum ClientType { // https://dumps.tadiphone.dev/dumps/oculus/eureka - ANDROID_VR_NO_AUTH( + ANDROID_VR_1_61_48( 28, "ANDROID_VR", "com.google.android.apps.youtube.vr.oculus", @@ -27,7 +27,7 @@ public enum ClientType { "1.61.48", false, false, - "Android VR No auth" + "Android VR 1.61" ), // Chromecast with Google TV 4K. // https://dumps.tadiphone.dev/dumps/google/kirkwood @@ -96,6 +96,26 @@ public enum ClientType { forceAVC() ? "iOS TV Force AVC" : "iOS TV" + ), + /** + * Uses non adaptive bitrate, which fixes audio stuttering with YT Music. + * Uses VP9 and not AV1. + */ + ANDROID_VR_1_43_32( + ANDROID_VR_1_61_48.id, + ANDROID_VR_1_61_48.clientName, + ANDROID_VR_1_61_48.packageName, + ANDROID_VR_1_61_48.deviceMake, + ANDROID_VR_1_61_48.deviceModel, + ANDROID_VR_1_61_48.osName, + ANDROID_VR_1_61_48.osVersion, + ANDROID_VR_1_61_48.androidSdkVersion, + ANDROID_VR_1_61_48.buildId, + "107.0.5284.2", + "1.43.32", + ANDROID_VR_1_61_48.requiresAuth, + ANDROID_VR_1_61_48.useAuth, + "Android VR 1.43" ); private static boolean forceAVC() { 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 d999b9678..0980f816f 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 @@ -252,8 +252,9 @@ public class SpoofVideoStreamsPatch { public static final class AudioStreamLanguageOverrideAvailability implements Setting.Availability { @Override public boolean isAvailable() { + ClientType clientType = BaseSettings.SPOOF_VIDEO_STREAMS_CLIENT_TYPE.get(); return BaseSettings.SPOOF_VIDEO_STREAMS.get() - && BaseSettings.SPOOF_VIDEO_STREAMS_CLIENT_TYPE.get() == ClientType.ANDROID_VR_NO_AUTH; + && (clientType == ClientType.ANDROID_VR_1_61_48 || clientType == ClientType.ANDROID_VR_1_43_32); } } 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 5179b3e5f..6cc3ec1cd 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 @@ -42,7 +42,8 @@ final class PlayerRoutes { // but if this is a fall over client it will set the language even though // the audio language is not selectable in the UI. ClientType userSelectedClient = BaseSettings.SPOOF_VIDEO_STREAMS_CLIENT_TYPE.get(); - Locale streamLocale = userSelectedClient == ClientType.ANDROID_VR_NO_AUTH + Locale streamLocale = (userSelectedClient == ClientType.ANDROID_VR_1_61_48 + || userSelectedClient == ClientType.ANDROID_VR_1_43_32) ? BaseSettings.SPOOF_VIDEO_STREAMS_LANGUAGE.get().getLocale() : Locale.getDefault(); diff --git a/extensions/shared/library/src/main/java/app/revanced/extension/shared/spoof/requests/StreamingDataRequest.java b/extensions/shared/library/src/main/java/app/revanced/extension/shared/spoof/requests/StreamingDataRequest.java index eeab17d1f..d814ced88 100644 --- a/extensions/shared/library/src/main/java/app/revanced/extension/shared/spoof/requests/StreamingDataRequest.java +++ b/extensions/shared/library/src/main/java/app/revanced/extension/shared/spoof/requests/StreamingDataRequest.java @@ -35,21 +35,22 @@ import app.revanced.extension.shared.spoof.ClientType; */ public class StreamingDataRequest { - private static final ClientType[] CLIENT_ORDER_TO_USE; + private static volatile ClientType[] clientOrderToUse = ClientType.values(); - static { - ClientType[] allClientTypes = ClientType.values(); - ClientType preferredClient = BaseSettings.SPOOF_VIDEO_STREAMS_CLIENT_TYPE.get(); + public static void setClientOrderToUse(ClientType[] availableClients, ClientType preferredClient) { + Objects.requireNonNull(availableClients); - CLIENT_ORDER_TO_USE = new ClientType[allClientTypes.length]; - CLIENT_ORDER_TO_USE[0] = preferredClient; + clientOrderToUse = new ClientType[availableClients.length]; + clientOrderToUse[0] = preferredClient; int i = 1; - for (ClientType c : allClientTypes) { + for (ClientType c : availableClients) { if (c != preferredClient) { - CLIENT_ORDER_TO_USE[i++] = c; + clientOrderToUse[i++] = c; } } + + Logger.printDebug(() -> "Available spoof clients: " + Arrays.toString(clientOrderToUse)); } private static final String AUTHORIZATION_HEADER = "Authorization"; @@ -193,9 +194,9 @@ public class StreamingDataRequest { // Retry with different client if empty response body is received. int i = 0; - for (ClientType clientType : CLIENT_ORDER_TO_USE) { + for (ClientType clientType : clientOrderToUse) { // Show an error if the last client type fails, or if debug is enabled then show for all attempts. - final boolean showErrorToast = (++i == CLIENT_ORDER_TO_USE.length) || debugEnabled; + final boolean showErrorToast = (++i == clientOrderToUse.length) || debugEnabled; HttpURLConnection connection = send(clientType, videoId, playerHeaders, showErrorToast); if (connection != null) { diff --git a/extensions/youtube/src/main/java/app/revanced/extension/youtube/patches/spoof/SpoofVideoStreamsPatch.java b/extensions/youtube/src/main/java/app/revanced/extension/youtube/patches/spoof/SpoofVideoStreamsPatch.java new file mode 100644 index 000000000..23a415708 --- /dev/null +++ b/extensions/youtube/src/main/java/app/revanced/extension/youtube/patches/spoof/SpoofVideoStreamsPatch.java @@ -0,0 +1,29 @@ +package app.revanced.extension.youtube.patches.spoof; + +import static app.revanced.extension.shared.spoof.ClientType.ANDROID_CREATOR; +import static app.revanced.extension.shared.spoof.ClientType.ANDROID_UNPLUGGED; +import static app.revanced.extension.shared.spoof.ClientType.ANDROID_VR_1_61_48; +import static app.revanced.extension.shared.spoof.ClientType.IOS_UNPLUGGED; + +import app.revanced.extension.shared.settings.BaseSettings; +import app.revanced.extension.shared.spoof.ClientType; +import app.revanced.extension.shared.spoof.requests.StreamingDataRequest; + +@SuppressWarnings("unused") +public class SpoofVideoStreamsPatch { + + /** + * Injection point. + */ + public static void setClientOrderToUse() { + ClientType[] availableClients = { + ANDROID_VR_1_61_48, + ANDROID_UNPLUGGED, + ANDROID_CREATOR, + IOS_UNPLUGGED + }; + + StreamingDataRequest.setClientOrderToUse(availableClients, + BaseSettings.SPOOF_VIDEO_STREAMS_CLIENT_TYPE.get()); + } +} \ No newline at end of file diff --git a/extensions/youtube/src/main/java/app/revanced/extension/youtube/settings/preference/SpoofStreamingDataSideEffectsPreference.java b/extensions/youtube/src/main/java/app/revanced/extension/youtube/settings/preference/SpoofStreamingDataSideEffectsPreference.java index 6ea249a4f..33a69b6b0 100644 --- a/extensions/youtube/src/main/java/app/revanced/extension/youtube/settings/preference/SpoofStreamingDataSideEffectsPreference.java +++ b/extensions/youtube/src/main/java/app/revanced/extension/youtube/settings/preference/SpoofStreamingDataSideEffectsPreference.java @@ -86,7 +86,8 @@ public class SpoofStreamingDataSideEffectsPreference extends Preference { String summary = str(key + "_summary"); // Android VR supports AV1 but all other clients do not. - if (clientType != ClientType.ANDROID_VR_NO_AUTH) { + if (clientType != ClientType.ANDROID_VR_1_61_48 + && clientType != ClientType.ANDROID_VR_1_43_32) { summary += '\n' + str("revanced_spoof_video_streams_about_no_av1"); } diff --git a/patches/src/main/kotlin/app/revanced/patches/music/misc/spoof/SpoofVideoStreams.kt b/patches/src/main/kotlin/app/revanced/patches/music/misc/spoof/SpoofVideoStreams.kt index 58940f061..e50a70a49 100644 --- a/patches/src/main/kotlin/app/revanced/patches/music/misc/spoof/SpoofVideoStreams.kt +++ b/patches/src/main/kotlin/app/revanced/patches/music/misc/spoof/SpoofVideoStreams.kt @@ -1,13 +1,20 @@ package app.revanced.patches.music.misc.spoof +import app.revanced.patcher.extensions.InstructionExtensions.addInstruction import app.revanced.patches.music.misc.extension.sharedExtensionPatch +import app.revanced.patches.music.misc.gms.musicActivityOnCreateFingerprint import app.revanced.patches.music.playservice.is_7_33_or_greater import app.revanced.patches.music.playservice.is_8_11_or_greater import app.revanced.patches.music.playservice.is_8_15_or_greater import app.revanced.patches.music.playservice.versionCheckPatch import app.revanced.patches.shared.misc.spoof.spoofVideoStreamsPatch +private const val EXTENSION_CLASS_DESCRIPTOR = "Lapp/revanced/extension/music/patches/spoof/SpoofVideoStreamsPatch;" + val spoofVideoStreamsPatch = spoofVideoStreamsPatch( + fixMediaFetchHotConfigChanges = { true }, + fixMediaFetchHotConfigAlternativeChanges = { is_8_11_or_greater && !is_8_15_or_greater }, + fixParsePlaybackResponseFeatureFlag = { is_7_33_or_greater }, block = { compatibleWith( "com.google.android.apps.youtube.music"( @@ -17,7 +24,10 @@ val spoofVideoStreamsPatch = spoofVideoStreamsPatch( dependsOn(sharedExtensionPatch, versionCheckPatch, userAgentClientSpoofPatch) }, - fixMediaFetchHotConfigChanges = { true }, - fixMediaFetchHotConfigAlternativeChanges = { is_8_11_or_greater && !is_8_15_or_greater }, - fixParsePlaybackResponseFeatureFlag = { is_7_33_or_greater } + executeBlock = { + musicActivityOnCreateFingerprint.method.addInstruction( + 1, // Must use 1 index so context is set by extension patch. + "invoke-static { }, $EXTENSION_CLASS_DESCRIPTOR->setClientOrderToUse()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 aa03de71a..dbf055299 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 @@ -1,5 +1,6 @@ package app.revanced.patches.youtube.misc.spoof +import app.revanced.patcher.extensions.InstructionExtensions.addInstruction import app.revanced.patches.all.misc.resources.addResources import app.revanced.patches.shared.misc.settings.preference.ListPreference import app.revanced.patches.shared.misc.settings.preference.NonInteractivePreference @@ -13,56 +14,70 @@ import app.revanced.patches.youtube.misc.playservice.is_20_14_or_greater import app.revanced.patches.youtube.misc.playservice.versionCheckPatch import app.revanced.patches.youtube.misc.settings.PreferenceScreen import app.revanced.patches.youtube.misc.settings.settingsPatch +import app.revanced.patches.youtube.shared.mainActivityOnCreateFingerprint -val spoofVideoStreamsPatch = spoofVideoStreamsPatch({ - compatibleWith( - "com.google.android.youtube"( - "19.34.42", - "19.43.41", - "19.47.53", - "20.07.39", - "20.12.46", - "20.13.41", +private const val EXTENSION_CLASS_DESCRIPTOR = "Lapp/revanced/extension/youtube/patches/spoof/SpoofVideoStreamsPatch;" + +val spoofVideoStreamsPatch = spoofVideoStreamsPatch( + block = { + compatibleWith( + "com.google.android.youtube"( + "19.34.42", + "19.43.41", + "19.47.53", + "20.07.39", + "20.12.46", + "20.13.41", + ) ) - ) - dependsOn( - userAgentClientSpoofPatch, - settingsPatch, - versionCheckPatch - ) -}, { - is_19_34_or_greater -}, { - // In 20.14 the flag was merged with 20.03 start playback flag. - is_20_10_or_greater && !is_20_14_or_greater -}, { - is_20_03_or_greater -}, { - addResources("youtube", "misc.fix.playback.spoofVideoStreamsPatch") + dependsOn( + userAgentClientSpoofPatch, + settingsPatch, + versionCheckPatch + ) + }, + fixMediaFetchHotConfigChanges = { + is_19_34_or_greater + }, + fixMediaFetchHotConfigAlternativeChanges = { + // In 20.14 the flag was merged with 20.03 start playback flag. + is_20_10_or_greater && !is_20_14_or_greater + }, + fixParsePlaybackResponseFeatureFlag = { + is_20_03_or_greater + }, + executeBlock = { + addResources("youtube", "misc.fix.playback.spoofVideoStreamsPatch") - PreferenceScreen.MISC.addPreferences( - PreferenceScreenPreference( - key = "revanced_spoof_video_streams_screen", - sorting = PreferenceScreenPreference.Sorting.UNSORTED, - preferences = setOf( - SwitchPreference("revanced_spoof_video_streams"), - ListPreference("revanced_spoof_video_streams_client_type"), - NonInteractivePreference( - // Requires a key and title but the actual text is chosen at runtime. - key = "revanced_spoof_video_streams_about_android", - tag = "app.revanced.extension.youtube.settings.preference.SpoofStreamingDataSideEffectsPreference" + PreferenceScreen.MISC.addPreferences( + PreferenceScreenPreference( + key = "revanced_spoof_video_streams_screen", + sorting = PreferenceScreenPreference.Sorting.UNSORTED, + preferences = setOf( + SwitchPreference("revanced_spoof_video_streams"), + ListPreference("revanced_spoof_video_streams_client_type"), + NonInteractivePreference( + // Requires a key and title but the actual text is chosen at runtime. + key = "revanced_spoof_video_streams_about_android", + tag = "app.revanced.extension.youtube.settings.preference.SpoofStreamingDataSideEffectsPreference" + ), + ListPreference( + key = "revanced_spoof_video_streams_language", + // Language strings are declared in Setting patch. + entriesKey = "revanced_language_entries", + entryValuesKey = "revanced_language_entry_values", + tag = "app.revanced.extension.shared.settings.preference.SortedListPreference" + ), + SwitchPreference("revanced_spoof_video_streams_ios_force_avc"), + SwitchPreference("revanced_spoof_streaming_data_stats_for_nerds"), ), - ListPreference( - key = "revanced_spoof_video_streams_language", - // Language strings are declared in Setting patch. - entriesKey = "revanced_language_entries", - entryValuesKey = "revanced_language_entry_values", - tag = "app.revanced.extension.shared.settings.preference.SortedListPreference" - ), - SwitchPreference("revanced_spoof_video_streams_ios_force_avc"), - SwitchPreference("revanced_spoof_streaming_data_stats_for_nerds"), ), - ), - ) -}) + ) + + mainActivityOnCreateFingerprint.method.addInstruction( + 1, // Must use 1 index so context is set by extension patch., + "invoke-static { }, $EXTENSION_CLASS_DESCRIPTOR->setClientOrderToUse()V" + ) + } +) diff --git a/patches/src/main/resources/addresources/values/arrays.xml b/patches/src/main/resources/addresources/values/arrays.xml index 454ed0c1e..197680376 100644 --- a/patches/src/main/resources/addresources/values/arrays.xml +++ b/patches/src/main/resources/addresources/values/arrays.xml @@ -131,7 +131,7 @@ ANDROID_UNPLUGGED - ANDROID_VR_NO_AUTH + ANDROID_VR_1_61_48 IOS_UNPLUGGED