Skip to content

Commit e425207

Browse files
AudricVStypox
authored andcommitted
[YouTube] Add ClientsConstants for client versions and names
1 parent 96c2884 commit e425207

2 files changed

Lines changed: 147 additions & 110 deletions

File tree

Lines changed: 114 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,114 @@
1+
package org.schabi.newpipe.extractor.services.youtube;
2+
3+
final class ClientsConstants {
4+
private ClientsConstants() {
5+
}
6+
7+
// Common client fields
8+
9+
static final String DESKTOP_CLIENT_PLATFORM = "DESKTOP";
10+
static final String MOBILE_CLIENT_PLATFORM = "MOBILE";
11+
static final String WATCH_CLIENT_SCREEN = "WATCH";
12+
static final String EMBED_CLIENT_SCREEN = "EMBED";
13+
14+
// WEB (YouTube desktop) client fields
15+
16+
static final String WEB_CLIENT_ID = "1";
17+
static final String WEB_CLIENT_NAME = "WEB";
18+
/**
19+
* The client version for InnerTube requests with the {@code WEB} client, used as the last
20+
* fallback if the extraction of the real one failed.
21+
*/
22+
static final String WEB_HARDCODED_CLIENT_VERSION = "2.20250122.04.00";
23+
24+
// WEB_REMIX (YouTube Music) client fields
25+
26+
static final String WEB_REMIX_CLIENT_ID = "67";
27+
static final String WEB_REMIX_CLIENT_NAME = "WEB_REMIX";
28+
static final String WEB_REMIX_HARDCODED_CLIENT_VERSION = "1.20250122.01.00";
29+
30+
// TVHTML5 (YouTube web on TV and consoles) client fields
31+
static final String TVHTML5_CLIENT_ID = "7";
32+
static final String TVHTML5_CLIENT_NAME = "TVHTML5";
33+
static final String TVHTML5_CLIENT_VERSION = "7.20250122.15.00";
34+
static final String TVHTML5_CLIENT_PLATFORM = "CONSOLE";
35+
// CHECKSTYLE:OFF
36+
static final String TVHTML5_USER_AGENT =
37+
"Mozilla/5.0 (PlayStation; PlayStation 4/8.50) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/12.0 Safari/605.1.15";
38+
// CHECKSTYLE:ON
39+
40+
// WEB_EMBEDDED_PLAYER (YouTube embeds)
41+
42+
static final String WEB_EMBEDDED_CLIENT_ID = "56";
43+
static final String WEB_EMBEDDED_CLIENT_NAME = "WEB_EMBEDDED_PLAYER";
44+
static final String WEB_EMBEDDED_CLIENT_VERSION = "1.20250121.00.00";
45+
46+
// IOS (iOS YouTube app) client fields
47+
48+
static final String IOS_CLIENT_NAME = "IOS";
49+
50+
/**
51+
* The hardcoded client version of the iOS app used for InnerTube requests with this client.
52+
*
53+
* <p>
54+
* It can be extracted by getting the latest release version of the app on
55+
* <a href="https://apps.apple.com/us/app/youtube-watch-listen-stream/id544007664/">the App
56+
* Store page of the YouTube app</a>, in the {@code What’s New} section.
57+
* </p>
58+
*/
59+
static final String IOS_CLIENT_VERSION = "19.28.1";
60+
61+
/**
62+
* The device machine id for the iPhone 15 Pro Max, used to get 60fps with the {@code iOS}
63+
* client.
64+
*
65+
* <p>
66+
* See <a href="https://gist.github.com/adamawolf/3048717">this GitHub Gist</a> for more
67+
* information.
68+
* </p>
69+
*/
70+
static final String IOS_DEVICE_MODEL = "iPhone16,2";
71+
72+
/**
73+
* The iOS version to be used in JSON POST requests, the one of an iPhone 15 Pro Max running
74+
* iOS 18.2.1 with the hardcoded version of the iOS app (for the {@code "osVersion"} field).
75+
*
76+
* <p>
77+
* The value of this field seems to use the following structure:
78+
* "iOS major version.minor version.patch version.build version", where
79+
* "patch version" is equal to 0 if it isn't set
80+
* The build version corresponding to the iOS version used can be found on
81+
* <a href="https://theapplewiki.com/wiki/Firmware/iPhone/18.x#iPhone_15_Pro_Max">
82+
* https://theapplewiki.com/wiki/Firmware/iPhone/18.x#iPhone_15_Pro_Max</a>
83+
* </p>
84+
*
85+
* @see #IOS_USER_AGENT_VERSION
86+
*/
87+
static final String IOS_OS_VERSION = "18.2.1.22C161";
88+
89+
/**
90+
* The iOS version to be used in the HTTP user agent for requests.
91+
*
92+
* <p>
93+
* This should be the same of as {@link #IOS_OS_VERSION}.
94+
* </p>
95+
*
96+
* @see #IOS_OS_VERSION
97+
*/
98+
static final String IOS_USER_AGENT_VERSION = "18_2_1";
99+
100+
// ANDROID (Android YouTube app) client fields
101+
102+
static final String ANDROID_CLIENT_NAME = "ANDROID";
103+
104+
/**
105+
* The hardcoded client version of the Android app used for InnerTube requests with this
106+
* client.
107+
*
108+
* <p>
109+
* It can be extracted by getting the latest release version of the app in an APK repository
110+
* such as <a href="https://www.apkmirror.com/apk/google-inc/youtube/">APKMirror</a>.
111+
* </p>
112+
*/
113+
static final String ANDROID_CLIENT_VERSION = "19.28.35";
114+
}

extractor/src/main/java/org/schabi/newpipe/extractor/services/youtube/YoutubeParsingHelper.java

Lines changed: 33 additions & 110 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,19 @@
2121
package org.schabi.newpipe.extractor.services.youtube;
2222

2323
import 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;
2437
import static org.schabi.newpipe.extractor.utils.Utils.HTTP;
2538
import static org.schabi.newpipe.extractor.utils.Utils.HTTPS;
2639
import 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

Comments
 (0)