From 2726231404384d87f101d825e10a17c944e8f1bd Mon Sep 17 00:00:00 2001 From: LisoUseInAIKyrios <118716522+LisoUseInAIKyrios@users.noreply.github.com> Date: Tue, 16 Sep 2025 15:33:55 +0400 Subject: [PATCH] feat(YouTube - Spoof video streams): Add iPadOS client --- .../shared/settings/BaseSettings.java | 8 ++++ .../extension/shared/spoof/ClientType.java | 44 ++++++++++++++++++- .../patches/ForceOriginalAudioPatch.java | 13 +++--- .../patches/spoof/SpoofVideoStreamsPatch.java | 2 + ...oofStreamingDataSideEffectsPreference.java | 21 ++++++--- .../resources/addresources/values/arrays.xml | 2 + .../resources/addresources/values/strings.xml | 3 +- 7 files changed, 78 insertions(+), 15 deletions(-) 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 2cb08ff90..43ea8e226 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,6 +5,7 @@ import static java.lang.Boolean.TRUE; import static app.revanced.extension.shared.settings.Setting.parent; import static app.revanced.extension.shared.spoof.SpoofVideoStreamsPatch.AudioStreamLanguageOverrideAvailability; +import app.revanced.extension.shared.Logger; import app.revanced.extension.shared.spoof.ClientType; /** @@ -32,4 +33,11 @@ public class BaseSettings { public static final BooleanSetting SPOOF_STREAMING_DATA_STATS_FOR_NERDS = new BooleanSetting("revanced_spoof_streaming_data_stats_for_nerds", TRUE, parent(SPOOF_VIDEO_STREAMS)); // 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_1_61_48, true, parent(SPOOF_VIDEO_STREAMS)); + + static { + if (SPOOF_VIDEO_STREAMS_CLIENT_TYPE.get() == ClientType.IOS_UNPLUGGED) { + Logger.printInfo(() -> "Migrating from iOS Unplugged to iPadOS"); + SPOOF_VIDEO_STREAMS_CLIENT_TYPE.save(ClientType.IPADOS); + } + } } 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 d29b293ff..4226b415c 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 @@ -10,6 +10,7 @@ import java.util.Objects; import app.revanced.extension.shared.Logger; +@SuppressWarnings("ConstantLocale") public enum ClientType { /** * Video not playable: Kids / Paid / Movie / Private / Age-restricted. @@ -87,6 +88,48 @@ public enum ClientType { false, false, "visionOS" + ), + /** + * The device machine id for the iPad 6th Gen (iPad7,6). + * AV1 hardware decoding is not supported. + * See [this GitHub Gist](https://gist.github.com/adamawolf/3048717) for more information. + * + * Based on Google's actions to date, PoToken may not be required on devices with very low specs. + * For example, suppose the User-Agent for a PlayStation 3 (with 256MB of RAM) is used. + * Accessing 'Web' (https://www.youtube.com) will redirect to 'TV' (https://www.youtube.com/tv). + * 'TV' target devices with very low specs, such as embedded devices, game consoles, and blu-ray players, so PoToken is not required. + * + * For this reason, the device machine id for the iPad 6th Gen (with 2GB of RAM), + * the lowest spec device capable of running iPadOS 17, was used. + */ + IPADOS(5, + "IOS", + "Apple", + "iPad7,6", + "iPadOS", + "17.7.10.21H450", + "19.22.3", + "com.google.ios.youtube/19.22.3 (iPad7,6; U; CPU iPadOS 17_7_10 like Mac OS X; " + Locale.getDefault() + ")", + false, + false, + "iPadOS" + ), + /** + * Obsolete and broken client. Here only to migrate data. + */ + @Deprecated + IOS_UNPLUGGED( + 33, + "IOS_UNPLUGGED", + "Apple", + "iPhone16,2", + "iOS", + "18.2.22C152", + "8.49", + "dummy user-agent", + true, + true, + "iOS TV" ); /** @@ -173,7 +216,6 @@ public enum ClientType { /** * Android constructor. */ - @SuppressWarnings("ConstantLocale") ClientType(int id, String clientName, @NonNull String packageName, diff --git a/extensions/youtube/src/main/java/app/revanced/extension/youtube/patches/ForceOriginalAudioPatch.java b/extensions/youtube/src/main/java/app/revanced/extension/youtube/patches/ForceOriginalAudioPatch.java index 02149115c..290928f98 100644 --- a/extensions/youtube/src/main/java/app/revanced/extension/youtube/patches/ForceOriginalAudioPatch.java +++ b/extensions/youtube/src/main/java/app/revanced/extension/youtube/patches/ForceOriginalAudioPatch.java @@ -14,12 +14,13 @@ public class ForceOriginalAudioPatch { * Injection point. */ public static void setPreferredLanguage() { - if (Settings.FORCE_ORIGINAL_AUDIO.get()) { - // None of the current spoof clients support audio track menu, - // And all are un-authenticated and can request any language code - // (authenticated requests ignore the language code and always use the account language). - // To still support force original audio, if it's enabled then pick a language - // that is not auto-dubbed by YouTube: https://support.google.com/youtube/answer/15569972 + if (Settings.FORCE_ORIGINAL_AUDIO.get() + && SpoofVideoStreamsPatch.spoofingToClientWithNoMultiAudioStreams()) { + // If client spoofing does not use authentication and lacks multi-audio streams, + // then can use any language code for the request and if that requested language is + // not available YT uses the original audio language. Authenticated requests ignore + // the language code and always use the account language. Use a language that is + // not auto-dubbed by YouTube: https://support.google.com/youtube/answer/15569972 // but the language is also supported natively by the Meta Quest device that // Android VR is spoofing. AppLanguage override = AppLanguage.SV; 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 index df228e288..5e786afea 100644 --- 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 @@ -3,6 +3,7 @@ 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_VR_1_43_32; import static app.revanced.extension.shared.spoof.ClientType.ANDROID_VR_1_61_48; +import static app.revanced.extension.shared.spoof.ClientType.IPADOS; import static app.revanced.extension.shared.spoof.ClientType.VISIONOS; import java.util.List; @@ -22,6 +23,7 @@ public class SpoofVideoStreamsPatch { ANDROID_VR_1_61_48, ANDROID_CREATOR, VISIONOS, + IPADOS, // VR 1.43 must be last as spoof streaming data handles it slightly differently. ANDROID_VR_1_43_32 ); 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 07d5c9d6b..04f366a2f 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 @@ -78,18 +78,25 @@ public class SpoofStreamingDataSideEffectsPreference extends Preference { Logger.printDebug(() -> "Updating spoof stream side effects preference"); setEnabled(BaseSettings.SPOOF_VIDEO_STREAMS.get()); - String title = str("revanced_spoof_video_streams_about_title"); - // Currently only Android VR and VisionOS are supported, and both have the same base side effects. - String summary = str("revanced_spoof_video_streams_about_android_summary"); - summary += '\n' + str("revanced_spoof_video_streams_about_kids_videos"); + setTitle(str("revanced_spoof_video_streams_about_title")); - if (clientType == ClientType.VISIONOS) { + String summary = str(clientType == ClientType.IPADOS + ? "revanced_spoof_video_streams_about_ipados_summary" + // visionOS has same base side effects as Android VR. + : "revanced_spoof_video_streams_about_android_summary"); + + if (clientType == ClientType.IPADOS) { + summary = str("revanced_spoof_video_streams_about_no_av1") + + '\n' + summary; + } else if (clientType == ClientType.VISIONOS) { summary = str("revanced_spoof_video_streams_about_experimental") + '\n' + summary - + '\n' + str("revanced_spoof_video_streams_about_no_av1"); + + '\n' + str("revanced_spoof_video_streams_about_no_av1") + + '\n' + str("revanced_spoof_video_streams_about_kids_videos"); + } else { + summary += '\n' + str("revanced_spoof_video_streams_about_kids_videos"); } - setTitle(title); setSummary(summary); } } diff --git a/patches/src/main/resources/addresources/values/arrays.xml b/patches/src/main/resources/addresources/values/arrays.xml index c8796f57d..1c174cad0 100644 --- a/patches/src/main/resources/addresources/values/arrays.xml +++ b/patches/src/main/resources/addresources/values/arrays.xml @@ -125,10 +125,12 @@ Android VR visionOS + iPadOS ANDROID_VR_1_61_48 VISIONOS + IPADOS diff --git a/patches/src/main/resources/addresources/values/strings.xml b/patches/src/main/resources/addresources/values/strings.xml index 2f4840a5f..c4d072888 100644 --- a/patches/src/main/resources/addresources/values/strings.xml +++ b/patches/src/main/resources/addresources/values/strings.xml @@ -784,7 +784,7 @@ If changing this setting does not take effect, try switching to Incognito mode." "Audio track menu is hidden -Audio track menu is not available when \'Spoof video streams\' is enabled" +To show the Audio track menu, change \'Spoof video streams\' to iPadOS" Hide Watch in VR Watch in VR menu is hidden @@ -1629,6 +1629,7 @@ Enabling this can unlock higher video qualities" Android spoofing side effects "• Audio track menu is missing • Stable volume is not available" + • Video may stop at 1:00, or may not be available in some regions • Experimental client and may stop working anytime • No AV1 video codec • Kids videos may not play when logged out or in incognito mode