2121package org .schabi .newpipe .extractor .services .youtube ;
2222
2323import static org .schabi .newpipe .extractor .NewPipe .getDownloader ;
24+ import static org .schabi .newpipe .extractor .services .youtube .ClientsConstants .ANDROID_CLIENT_VERSION ;
25+ import static org .schabi .newpipe .extractor .services .youtube .ClientsConstants .DESKTOP_CLIENT_PLATFORM ;
26+ import static org .schabi .newpipe .extractor .services .youtube .ClientsConstants .IOS_CLIENT_VERSION ;
27+ import static org .schabi .newpipe .extractor .services .youtube .ClientsConstants .IOS_DEVICE_MODEL ;
28+ import static org .schabi .newpipe .extractor .services .youtube .ClientsConstants .IOS_OS_VERSION ;
29+ import static org .schabi .newpipe .extractor .services .youtube .ClientsConstants .IOS_USER_AGENT_VERSION ;
30+ import static org .schabi .newpipe .extractor .services .youtube .ClientsConstants .TVHTML5_CLIENT_VERSION ;
31+ import static org .schabi .newpipe .extractor .services .youtube .ClientsConstants .WEB_CLIENT_ID ;
32+ import static org .schabi .newpipe .extractor .services .youtube .ClientsConstants .WEB_CLIENT_NAME ;
33+ import static org .schabi .newpipe .extractor .services .youtube .ClientsConstants .WEB_HARDCODED_CLIENT_VERSION ;
34+ import static org .schabi .newpipe .extractor .services .youtube .ClientsConstants .WEB_REMIX_CLIENT_ID ;
35+ import static org .schabi .newpipe .extractor .services .youtube .ClientsConstants .WEB_REMIX_CLIENT_NAME ;
36+ import static org .schabi .newpipe .extractor .services .youtube .ClientsConstants .WEB_REMIX_HARDCODED_CLIENT_VERSION ;
2437import static org .schabi .newpipe .extractor .utils .Utils .HTTP ;
2538import static org .schabi .newpipe .extractor .utils .Utils .HTTPS ;
2639import static org .schabi .newpipe .extractor .utils .Utils .getStringResultFromRegexArray ;
@@ -144,55 +157,6 @@ private YoutubeParsingHelper() {
144157 */
145158 public static final String RACY_CHECK_OK = "racyCheckOk" ;
146159
147- /**
148- * The hardcoded client ID used for InnerTube requests with the {@code WEB} client.
149- */
150- private static final String WEB_CLIENT_ID = "1" ;
151-
152- /**
153- * The client version for InnerTube requests with the {@code WEB} client, used as the last
154- * fallback if the extraction of the real one failed.
155- */
156- private static final String HARDCODED_CLIENT_VERSION = "2.20240718.01.00" ;
157-
158- /**
159- * The hardcoded client version of the Android app used for InnerTube requests with this
160- * client.
161- *
162- * <p>
163- * It can be extracted by getting the latest release version of the app in an APK repository
164- * such as <a href="https://www.apkmirror.com/apk/google-inc/youtube/">APKMirror</a>.
165- * </p>
166- */
167- private static final String ANDROID_YOUTUBE_CLIENT_VERSION = "19.28.35" ;
168-
169- /**
170- * The hardcoded client version of the iOS app used for InnerTube requests with this client.
171- *
172- * <p>
173- * It can be extracted by getting the latest release version of the app on
174- * <a href="https://apps.apple.com/us/app/youtube-watch-listen-stream/id544007664/">the App
175- * Store page of the YouTube app</a>, in the {@code What’s New} section.
176- * </p>
177- */
178- private static final String IOS_YOUTUBE_CLIENT_VERSION = "19.45.4" ;
179-
180- /**
181- * The hardcoded client version used for InnerTube requests with the TV HTML5 embed client.
182- */
183- private static final String TVHTML5_SIMPLY_EMBED_CLIENT_VERSION = "2.0" ;
184-
185- /**
186- * The hardcoded client ID used for InnerTube requests with the YouTube Music desktop client.
187- */
188- private static final String YOUTUBE_MUSIC_CLIENT_ID = "67" ;
189-
190- /**
191- * The hardcoded client version used for InnerTube requests with the YouTube Music desktop
192- * client.
193- */
194- private static final String HARDCODED_YOUTUBE_MUSIC_CLIENT_VERSION = "1.20240715.01.00" ;
195-
196160 private static String clientVersion ;
197161
198162 private static String youtubeMusicClientVersion ;
@@ -212,41 +176,6 @@ private YoutubeParsingHelper() {
212176 private static final String CONTENT_PLAYBACK_NONCE_ALPHABET =
213177 "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_" ;
214178
215- /**
216- * The device machine id for the iPhone 15 Pro Max,
217- * used to get 60fps with the {@code iOS} client.
218- *
219- * <p>
220- * See <a href="https://gist.github.com/adamawolf/3048717">this GitHub Gist</a> for more
221- * information.
222- * </p>
223- */
224- private static final String IOS_DEVICE_MODEL = "iPhone16,2" ;
225-
226- /**
227- * Spoofing an iPhone 15 Pro Max running iOS 18.1.0 with the hardcoded version of the iOS app.
228- * To be used for the {@code "osVersion"} field in JSON POST requests.
229- * <p>
230- * The value of this field seems to use the following structure:
231- * "iOS major version.minor version.patch version.build version", where
232- * "patch version" is equal to 0 if it isn't set
233- * The build version corresponding to the iOS version used can be found on
234- * <a href="https://theapplewiki.com/wiki/Firmware/iPhone/18.x#iPhone_15_Pro_Max">
235- * https://theapplewiki.com/wiki/Firmware/iPhone/18.x#iPhone_15_Pro_Max</a>
236- * </p>
237- *
238- * @see #IOS_USER_AGENT_VERSION
239- */
240- private static final String IOS_OS_VERSION = "18.1.0.22B83" ;
241-
242- /**
243- * Spoofing an iPhone 15 Pro Max running iOS 18.1.0 with the hardcoded version of the iOS app.
244- * To be used in the user agent for requests.
245- *
246- * @see #IOS_OS_VERSION
247- */
248- private static final String IOS_USER_AGENT_VERSION = "18_1_0" ;
249-
250179 private static Random numberGenerator = new Random ();
251180
252181 private static final String FEED_BASE_CHANNEL_ID =
@@ -561,9 +490,9 @@ public static boolean isHardcodedClientVersionValid()
561490 .object ("client" )
562491 .value ("hl" , "en-GB" )
563492 .value ("gl" , "GB" )
564- .value ("clientName" , "WEB" )
565- .value ("clientVersion" , HARDCODED_CLIENT_VERSION )
566- .value ("platform" , "DESKTOP" )
493+ .value ("clientName" , WEB_CLIENT_NAME )
494+ .value ("clientVersion" , WEB_HARDCODED_CLIENT_VERSION )
495+ .value ("platform" , DESKTOP_CLIENT_PLATFORM )
567496 .value ("utcOffsetMinutes" , 0 )
568497 .end ()
569498 .object ("request" )
@@ -581,7 +510,7 @@ public static boolean isHardcodedClientVersionValid()
581510 .end ().done ().getBytes (StandardCharsets .UTF_8 );
582511 // @formatter:on
583512
584- final var headers = getClientHeaders (WEB_CLIENT_ID , HARDCODED_CLIENT_VERSION );
513+ final var headers = getClientHeaders (WEB_CLIENT_ID , WEB_HARDCODED_CLIENT_VERSION );
585514
586515 // This endpoint is fetched by the YouTube website to get the items of its main menu and is
587516 // pretty lightweight (around 30kB)
@@ -705,7 +634,7 @@ public static String getClientVersion() throws IOException, ExtractionException
705634
706635 // Fallback to the hardcoded one if it is valid
707636 if (isHardcodedClientVersionValid ()) {
708- clientVersion = HARDCODED_CLIENT_VERSION ;
637+ clientVersion = WEB_HARDCODED_CLIENT_VERSION ;
709638 return clientVersion ;
710639 }
711640
@@ -752,11 +681,11 @@ public static boolean isHardcodedYoutubeMusicClientVersionValid() throws IOExcep
752681 .object ()
753682 .object ("context" )
754683 .object ("client" )
755- .value ("clientName" , "WEB_REMIX" )
756- .value ("clientVersion" , HARDCODED_YOUTUBE_MUSIC_CLIENT_VERSION )
684+ .value ("clientName" , WEB_REMIX_CLIENT_NAME )
685+ .value ("clientVersion" , WEB_REMIX_HARDCODED_CLIENT_VERSION )
757686 .value ("hl" , "en-GB" )
758687 .value ("gl" , "GB" )
759- .value ("platform" , "DESKTOP" )
688+ .value ("platform" , DESKTOP_CLIENT_PLATFORM )
760689 .value ("utcOffsetMinutes" , 0 )
761690 .end ()
762691 .object ("request" )
@@ -775,8 +704,7 @@ public static boolean isHardcodedYoutubeMusicClientVersionValid() throws IOExcep
775704 // @formatter:on
776705
777706 final var headers = new HashMap <>(getOriginReferrerHeaders (YOUTUBE_MUSIC_URL ));
778- headers .putAll (getClientHeaders (YOUTUBE_MUSIC_CLIENT_ID ,
779- HARDCODED_YOUTUBE_MUSIC_CLIENT_VERSION ));
707+ headers .putAll (getClientHeaders (WEB_REMIX_CLIENT_ID , WEB_HARDCODED_CLIENT_VERSION ));
780708
781709 final Response response = getDownloader ().postWithContentTypeJson (url , headers , json );
782710 // Ensure to have a valid response
@@ -789,7 +717,7 @@ public static String getYoutubeMusicClientVersion()
789717 return youtubeMusicClientVersion ;
790718 }
791719 if (isHardcodedYoutubeMusicClientVersionValid ()) {
792- youtubeMusicClientVersion = HARDCODED_YOUTUBE_MUSIC_CLIENT_VERSION ;
720+ youtubeMusicClientVersion = WEB_REMIX_HARDCODED_CLIENT_VERSION ;
793721 return youtubeMusicClientVersion ;
794722 }
795723
@@ -1196,10 +1124,10 @@ public static JsonBuilder<JsonObject> prepareDesktopJsonBuilder(
11961124 .object ("client" )
11971125 .value ("hl" , localization .getLocalizationCode ())
11981126 .value ("gl" , contentCountry .getCountryCode ())
1199- .value ("clientName" , "WEB" )
1127+ .value ("clientName" , WEB_CLIENT_NAME )
12001128 .value ("clientVersion" , getClientVersion ())
12011129 .value ("originalUrl" , "https://www.youtube.com" )
1202- .value ("platform" , "DESKTOP" )
1130+ .value ("platform" , DESKTOP_CLIENT_PLATFORM )
12031131 .value ("utcOffsetMinutes" , 0 )
12041132 .value ("visitorData" , vData )
12051133 .end ()
@@ -1227,7 +1155,7 @@ public static JsonBuilder<JsonObject> prepareAndroidMobileJsonBuilder(
12271155 .object ("context" )
12281156 .object ("client" )
12291157 .value ("clientName" , "ANDROID" )
1230- .value ("clientVersion" , ANDROID_YOUTUBE_CLIENT_VERSION )
1158+ .value ("clientVersion" , ANDROID_CLIENT_VERSION )
12311159 .value ("platform" , "MOBILE" )
12321160 .value ("osName" , "Android" )
12331161 .value ("osVersion" , "14" )
@@ -1276,7 +1204,7 @@ public static JsonBuilder<JsonObject> prepareIosMobileJsonBuilder(
12761204 .object ("context" )
12771205 .object ("client" )
12781206 .value ("clientName" , "IOS" )
1279- .value ("clientVersion" , IOS_YOUTUBE_CLIENT_VERSION )
1207+ .value ("clientVersion" , IOS_CLIENT_VERSION )
12801208 .value ("deviceMake" , "Apple" )
12811209 // Device model is required to get 60fps streams
12821210 .value ("deviceModel" , IOS_DEVICE_MODEL )
@@ -1312,7 +1240,7 @@ public static JsonBuilder<JsonObject> prepareTvHtml5EmbedJsonBuilder(
13121240 .object ("context" )
13131241 .object ("client" )
13141242 .value ("clientName" , "TVHTML5_SIMPLY_EMBEDDED_PLAYER" )
1315- .value ("clientVersion" , TVHTML5_SIMPLY_EMBED_CLIENT_VERSION )
1243+ .value ("clientVersion" , TVHTML5_CLIENT_VERSION )
13161244 .value ("clientScreen" , "EMBED" )
13171245 .value ("platform" , "TV" )
13181246 .value ("hl" , localization .getLocalizationCode ())
@@ -1378,9 +1306,8 @@ public static byte[] createTvHtml5EmbedPlayerBody(
13781306 */
13791307 @ Nonnull
13801308 public static String getAndroidUserAgent (@ Nullable final Localization localization ) {
1381- // Spoofing an Android 14 device with the hardcoded version of the Android app
1382- return "com.google.android.youtube/" + ANDROID_YOUTUBE_CLIENT_VERSION
1383- + " (Linux; U; Android 14; "
1309+ return "com.google.android.youtube/" + ANDROID_CLIENT_VERSION
1310+ + " (Linux; U; Android 15; "
13841311 + (localization != null ? localization : Localization .DEFAULT ).getCountryCode ()
13851312 + ") gzip" ;
13861313 }
@@ -1400,11 +1327,8 @@ public static String getAndroidUserAgent(@Nullable final Localization localizati
14001327 */
14011328 @ Nonnull
14021329 public static String getIosUserAgent (@ Nullable final Localization localization ) {
1403- // Spoofing an iPhone 15 Pro Max running iOS 18.1.0
1404- // with the hardcoded version of the iOS app
1405- return "com.google.ios.youtube/" + IOS_YOUTUBE_CLIENT_VERSION
1406- + "(" + IOS_DEVICE_MODEL + "; U; CPU iOS "
1407- + IOS_USER_AGENT_VERSION + " like Mac OS X; "
1330+ return "com.google.ios.youtube/" + IOS_CLIENT_VERSION + "(" + IOS_DEVICE_MODEL
1331+ + "; U; CPU iOS " + IOS_USER_AGENT_VERSION + " like Mac OS X; "
14081332 + (localization != null ? localization : Localization .DEFAULT ).getCountryCode ()
14091333 + ")" ;
14101334 }
@@ -1415,8 +1339,7 @@ public static String getIosUserAgent(@Nullable final Localization localization)
14151339 @ Nonnull
14161340 public static Map <String , List <String >> getYoutubeMusicHeaders () {
14171341 final var headers = new HashMap <>(getOriginReferrerHeaders (YOUTUBE_MUSIC_URL ));
1418- headers .putAll (getClientHeaders (YOUTUBE_MUSIC_CLIENT_ID ,
1419- youtubeMusicClientVersion ));
1342+ headers .putAll (getClientHeaders (WEB_REMIX_CLIENT_ID , youtubeMusicClientVersion ));
14201343 return headers ;
14211344 }
14221345
0 commit comments