diff --git a/app/src/main/java/app/revanced/integrations/youtube/patches/misc/SpoofClientPatch.java b/app/src/main/java/app/revanced/integrations/youtube/patches/misc/SpoofClientPatch.java index 862d1a6e5e..f3ba5f8baa 100644 --- a/app/src/main/java/app/revanced/integrations/youtube/patches/misc/SpoofClientPatch.java +++ b/app/src/main/java/app/revanced/integrations/youtube/patches/misc/SpoofClientPatch.java @@ -158,7 +158,7 @@ public static int getClientTypeId(int originalClientTypeId) { */ public static String getClientVersion(String originalClientVersion) { if (SPOOF_CLIENT_ENABLED) { - return getSpoofClientType().version; + return getSpoofClientType().appVersion; } return originalClientVersion; diff --git a/app/src/main/java/app/revanced/integrations/youtube/patches/misc/requests/PlayerRoutes.java b/app/src/main/java/app/revanced/integrations/youtube/patches/misc/requests/PlayerRoutes.java index f6835f2e1b..2f20413c4b 100644 --- a/app/src/main/java/app/revanced/integrations/youtube/patches/misc/requests/PlayerRoutes.java +++ b/app/src/main/java/app/revanced/integrations/youtube/patches/misc/requests/PlayerRoutes.java @@ -14,8 +14,10 @@ import app.revanced.integrations.shared.requests.Requester; import app.revanced.integrations.shared.requests.Route; +import app.revanced.integrations.shared.settings.Setting; import app.revanced.integrations.shared.utils.Logger; import app.revanced.integrations.shared.utils.PackageUtils; +import app.revanced.integrations.youtube.settings.Settings; public final class PlayerRoutes { public static final Route.CompiledRoute GET_STORYBOARD_SPEC_RENDERER = new Route( @@ -115,9 +117,9 @@ public final class PlayerRoutes { * Store page of the YouTube app, in the {@code What’s New} section. *
*/ - private static final String IOS_CLIENT_VERSION = "19.30.2"; + private static final String IOS_CLIENT_VERSION = "19.16.3"; /** - * The device machine id for the iPhone 14 Pro Max (iPhone15,3), used to get 60fps. + * The device machine id for the iPhone XS Max (iPhone11,4), used to get 60fps. * The device machine id for the iPhone 15 Pro Max (iPhone16,2), used to get HDR with AV1 hardware decoding. * *@@ -125,9 +127,20 @@ public final class PlayerRoutes { * information. *
*/ - private static final String IOS_DEVICE_MODEL = deviceHasAV1HardwareDecoding() ? "iPhone16,2" : "iPhone15,3"; - private static final String IOS_OS_VERSION = "17.6.21G80"; - private static final String IOS_USER_AGENT_VERSION = "17_6"; + private static final String IOS_DEVICE_MODEL = DeviceHardwareSupport.allowAV1() + ? "iPhone16,2" + : "iPhone11,4"; + + /** + * The minimum supported OS version for the iOS YouTube client is iOS 14.0. + * Using an invalid OS version will use the AVC codec. + */ + private static final String IOS_OS_VERSION = DeviceHardwareSupport.allowVP9() + ? "17.6.1.21G101" + : "13.7.17H35"; + private static final String IOS_USER_AGENT_VERSION = DeviceHardwareSupport.allowVP9() + ? "17_6_1" + : "13_7"; private static final String IOS_USER_AGENT = "com.google.ios.youtube/" + IOS_CLIENT_VERSION + "(" + @@ -346,17 +359,42 @@ public final class PlayerRoutes { WEB_INNER_TUBE_BODY = webInnerTubeBody.toString(); } - private static boolean deviceHasAV1HardwareDecoding() { - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) { + // Must check for device features in a separate class and cannot place this code inside + // the Patch or ClientType enum due to cyclic Setting references. + static class DeviceHardwareSupport { + private static final boolean DEVICE_HAS_HARDWARE_DECODING_VP9 = deviceHasVP9HardwareDecoding(); + private static final boolean DEVICE_HAS_HARDWARE_DECODING_AV1 = deviceHasAV1HardwareDecoding(); + + private static boolean deviceHasVP9HardwareDecoding() { MediaCodecList codecList = new MediaCodecList(MediaCodecList.ALL_CODECS); for (MediaCodecInfo codecInfo : codecList.getCodecInfos()) { - if (codecInfo.isHardwareAccelerated() && !codecInfo.isEncoder()) { - String[] supportedTypes = codecInfo.getSupportedTypes(); - for (String type : supportedTypes) { - if (type.equalsIgnoreCase("video/av01")) { - MediaCodecInfo.CodecCapabilities capabilities = codecInfo.getCapabilitiesForType(type); - if (capabilities != null) { + final boolean isHardwareAccelerated = (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) + ? codecInfo.isHardwareAccelerated() + : !codecInfo.getName().startsWith("OMX.google"); // Software decoder. + if (isHardwareAccelerated && !codecInfo.isEncoder()) { + for (String type : codecInfo.getSupportedTypes()) { + if (type.equalsIgnoreCase("video/x-vnd.on2.vp9")) { + Logger.printDebug(() -> "Device supports VP9 hardware decoding."); + return true; + } + } + } + } + + Logger.printDebug(() -> "Device does not support VP9 hardware decoding."); + return false; + } + + private static boolean deviceHasAV1HardwareDecoding() { + // It appears all devices with hardware AV1 are also Android 10 or newer. + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) { + MediaCodecList codecList = new MediaCodecList(MediaCodecList.ALL_CODECS); + + for (MediaCodecInfo codecInfo : codecList.getCodecInfos()) { + if (codecInfo.isHardwareAccelerated() && !codecInfo.isEncoder()) { + for (String type : codecInfo.getSupportedTypes()) { + if (type.equalsIgnoreCase("video/av01")) { Logger.printDebug(() -> "Device supports AV1 hardware decoding."); return true; } @@ -364,10 +402,18 @@ private static boolean deviceHasAV1HardwareDecoding() { } } } + + Logger.printDebug(() -> "Device does not support AV1 hardware decoding."); + return false; } - Logger.printDebug(() -> "Device does not support AV1 hardware decoding."); - return false; + static boolean allowVP9() { + return DEVICE_HAS_HARDWARE_DECODING_VP9 && !Settings.SPOOF_CLIENT_IOS_FORCE_AVC.get(); + } + + static boolean allowAV1() { + return allowVP9() && DEVICE_HAS_HARDWARE_DECODING_AV1; + } } private PlayerRoutes() { @@ -391,6 +437,21 @@ public static HttpURLConnection getPlayerResponseConnectionFromRoute(Route.Compi return connection; } + public static final class SpoofingToIOSAvailability implements Setting.Availability { + public static boolean clientTypeIOSEnabled() { + final ClientType clientTypeIOS = ClientType.IOS; + return Settings.SPOOF_CLIENT_GENERAL.get() == clientTypeIOS || + Settings.SPOOF_CLIENT_LIVESTREAM.get() == clientTypeIOS || + Settings.SPOOF_CLIENT_SHORTS.get() == clientTypeIOS || + Settings.SPOOF_CLIENT_FALLBACK.get() == clientTypeIOS; + } + + @Override + public boolean isAvailable() { + return clientTypeIOSEnabled(); + } + } + public enum ClientType { ANDROID(3, ANDROID_DEVICE_MODEL, ANDROID_CLIENT_VERSION, ANDROID_INNER_TUBE_BODY, ANDROID_OS_RELEASE_VERSION, ANDROID_USER_AGENT), ANDROID_EMBEDDED_PLAYER(55, ANDROID_DEVICE_MODEL, ANDROID_CLIENT_VERSION, ANDROID_EMBED_INNER_TUBE_BODY, ANDROID_OS_RELEASE_VERSION, ANDROID_USER_AGENT), @@ -406,17 +467,17 @@ public enum ClientType { public final String friendlyName; public final int id; public final String model; - public final String version; + public final String appVersion; public final String innerTubeBody; public final String osVersion; public final String userAgent; - ClientType(int id, String model, String version, String innerTubeBody, + ClientType(int id, String model, String appVersion, String innerTubeBody, String osVersion, String userAgent) { this.friendlyName = str("revanced_spoof_client_options_entry_" + name().toLowerCase()); this.id = id; this.model = model; - this.version = version; + this.appVersion = appVersion; this.innerTubeBody = innerTubeBody; this.osVersion = osVersion; this.userAgent = userAgent; diff --git a/app/src/main/java/app/revanced/integrations/youtube/settings/Settings.java b/app/src/main/java/app/revanced/integrations/youtube/settings/Settings.java index 75c616fe79..0211ee9635 100644 --- a/app/src/main/java/app/revanced/integrations/youtube/settings/Settings.java +++ b/app/src/main/java/app/revanced/integrations/youtube/settings/Settings.java @@ -322,7 +322,7 @@ public class Settings extends BaseSettings { public static final BooleanSetting OVERLAY_BUTTON_COPY_VIDEO_URL_TIMESTAMP = new BooleanSetting("revanced_overlay_button_copy_video_url_timestamp", FALSE); public static final BooleanSetting OVERLAY_BUTTON_MUTE_VOLUME = new BooleanSetting("revanced_overlay_button_mute_volume", FALSE); public static final BooleanSetting OVERLAY_BUTTON_EXTERNAL_DOWNLOADER = new BooleanSetting("revanced_overlay_button_external_downloader", FALSE); - public static final BooleanSetting OVERLAY_BUTTON_SPEED_DIALOG = new BooleanSetting("revanced_overlay_button_speed_dialog", TRUE); + public static final BooleanSetting OVERLAY_BUTTON_SPEED_DIALOG = new BooleanSetting("revanced_overlay_button_speed_dialog", FALSE); public static final BooleanSetting OVERLAY_BUTTON_TIME_ORDERED_PLAYLIST = new BooleanSetting("revanced_overlay_button_time_ordered_playlist", FALSE); public static final BooleanSetting OVERLAY_BUTTON_WHITELIST = new BooleanSetting("revanced_overlay_button_whitelist", FALSE); @@ -455,7 +455,9 @@ public class Settings extends BaseSettings { public static final BooleanSetting ENABLE_EXTERNAL_BROWSER = new BooleanSetting("revanced_enable_external_browser", TRUE, true); public static final BooleanSetting ENABLE_OPEN_LINKS_DIRECTLY = new BooleanSetting("revanced_enable_open_links_directly", TRUE); public static final BooleanSetting DISABLE_QUIC_PROTOCOL = new BooleanSetting("revanced_disable_quic_protocol", FALSE, true); - public static final BooleanSetting SPOOF_CLIENT = new BooleanSetting("revanced_spoof_client", FALSE, true, "revanced_spoof_client_user_dialog_message"); + public static final BooleanSetting SPOOF_CLIENT = new BooleanSetting("revanced_spoof_client", TRUE, true, "revanced_spoof_client_user_dialog_message"); + public static final BooleanSetting SPOOF_CLIENT_IOS_FORCE_AVC = new BooleanSetting("revanced_spoof_client_ios_force_avc", FALSE, true, "revanced_spoof_client_ios_force_avc_user_dialog_message", new SpoofingToIOSAvailability()); + public static final EnumSetting