4848import org .schabi .newpipe .extractor .stream .AudioTrackType ;
4949import org .schabi .newpipe .extractor .utils .JsonUtils ;
5050import org .schabi .newpipe .extractor .utils .Parser ;
51+ import org .schabi .newpipe .extractor .utils .ProtoBuilder ;
5152import org .schabi .newpipe .extractor .utils .RandomStringFromAlphabetGenerator ;
5253import org .schabi .newpipe .extractor .utils .Utils ;
5354
@@ -174,7 +175,7 @@ private YoutubeParsingHelper() {
174175 * Store page of the YouTube app</a>, in the {@code What’s New} section.
175176 * </p>
176177 */
177- private static final String IOS_YOUTUBE_CLIENT_VERSION = "19.28.1 " ;
178+ private static final String IOS_YOUTUBE_CLIENT_VERSION = "19.45.4 " ;
178179
179180 /**
180181 * The hardcoded client version used for InnerTube requests with the TV HTML5 embed client.
@@ -222,28 +223,28 @@ private YoutubeParsingHelper() {
222223 private static final String IOS_DEVICE_MODEL = "iPhone16,2" ;
223224
224225 /**
225- * Spoofing an iPhone 15 Pro Max running iOS 17.5.1 with the hardcoded version of the iOS app.
226+ * Spoofing an iPhone 15 Pro Max running iOS 18.1.0 with the hardcoded version of the iOS app.
226227 * To be used for the {@code "osVersion"} field in JSON POST requests.
227228 * <p>
228229 * The value of this field seems to use the following structure:
229230 * "iOS major version.minor version.patch version.build version", where
230231 * "patch version" is equal to 0 if it isn't set
231232 * The build version corresponding to the iOS version used can be found on
232- * <a href="https://theapplewiki.com/wiki/Firmware/iPhone/17 .x#iPhone_15_Pro_Max">
233- * https://theapplewiki.com/wiki/Firmware/iPhone/17 .x#iPhone_15_Pro_Max</a>
233+ * <a href="https://theapplewiki.com/wiki/Firmware/iPhone/18 .x#iPhone_15_Pro_Max">
234+ * https://theapplewiki.com/wiki/Firmware/iPhone/18 .x#iPhone_15_Pro_Max</a>
234235 * </p>
235236 *
236237 * @see #IOS_USER_AGENT_VERSION
237238 */
238- private static final String IOS_OS_VERSION = "17.5.1.21F90 " ;
239+ private static final String IOS_OS_VERSION = "18.1.0.22B83 " ;
239240
240241 /**
241- * Spoofing an iPhone 15 running iOS 17.5.1 with the hardcoded version of the iOS app. To be
242+ * Spoofing an iPhone 15 Pro Max running iOS 18.1.0 with the hardcoded version of the iOS app. To be
242243 * used in the user agent for requests.
243244 *
244245 * @see #IOS_OS_VERSION
245246 */
246- private static final String IOS_USER_AGENT_VERSION = "17_5_1 " ;
247+ private static final String IOS_USER_AGENT_VERSION = "18_1_0 " ;
247248
248249 private static Random numberGenerator = new Random ();
249250
@@ -303,6 +304,23 @@ public static boolean isY2ubeURL(@Nonnull final URL url) {
303304 return url .getHost ().equalsIgnoreCase ("y2u.be" );
304305 }
305306
307+ public static String randomVisitorData (final ContentCountry country ) {
308+ final ProtoBuilder pbE2 = new ProtoBuilder ();
309+ pbE2 .string (2 , "" );
310+ pbE2 .varint (4 , numberGenerator .nextInt (255 ) + 1 );
311+
312+ final ProtoBuilder pbE = new ProtoBuilder ();
313+ pbE .string (1 , country .getCountryCode ());
314+ pbE .bytes (2 , pbE2 .toBytes ());
315+
316+ final ProtoBuilder pb = new ProtoBuilder ();
317+ pb .string (1 , RandomStringFromAlphabetGenerator .generate (
318+ CONTENT_PLAYBACK_NONCE_ALPHABET , 11 , numberGenerator ));
319+ pb .varint (5 , System .currentTimeMillis () / 1000 - numberGenerator .nextInt (600000 ));
320+ pb .bytes (6 , pbE .toBytes ());
321+ return pb .toUrlencodedBase64 ();
322+ }
323+
306324 /**
307325 * Parses the duration string of the video expecting ":" or "." as separators
308326 *
@@ -1166,8 +1184,13 @@ public static JsonBuilder<JsonObject> prepareDesktopJsonBuilder(
11661184 @ Nonnull final ContentCountry contentCountry ,
11671185 @ Nullable final String visitorData )
11681186 throws IOException , ExtractionException {
1187+ String vData = visitorData ;
1188+ if (vData == null ) {
1189+ vData = randomVisitorData (contentCountry );
1190+ }
1191+
11691192 // @formatter:off
1170- final JsonBuilder < JsonObject > builder = JsonObject .builder ()
1193+ return JsonObject .builder ()
11711194 .object ("context" )
11721195 .object ("client" )
11731196 .value ("hl" , localization .getLocalizationCode ())
@@ -1176,13 +1199,9 @@ public static JsonBuilder<JsonObject> prepareDesktopJsonBuilder(
11761199 .value ("clientVersion" , getClientVersion ())
11771200 .value ("originalUrl" , "https://www.youtube.com" )
11781201 .value ("platform" , "DESKTOP" )
1179- .value ("utcOffsetMinutes" , 0 );
1180-
1181- if (visitorData != null ) {
1182- builder .value ("visitorData" , visitorData );
1183- }
1184-
1185- return builder .end ()
1202+ .value ("utcOffsetMinutes" , 0 )
1203+ .value ("visitorData" , vData )
1204+ .end ()
11861205 .object ("request" )
11871206 .array ("internalExperimentFlags" )
11881207 .end ()
@@ -1256,6 +1275,7 @@ public static JsonBuilder<JsonObject> prepareIosMobileJsonBuilder(
12561275 .value ("platform" , "MOBILE" )
12571276 .value ("osName" , "iOS" )
12581277 .value ("osVersion" , IOS_OS_VERSION )
1278+ .value ("visitorData" , randomVisitorData (contentCountry ))
12591279 .value ("hl" , localization .getLocalizationCode ())
12601280 .value ("gl" , contentCountry .getCountryCode ())
12611281 .value ("utcOffsetMinutes" , 0 )
@@ -1392,7 +1412,7 @@ public static String getAndroidUserAgent(@Nullable final Localization localizati
13921412 */
13931413 @ Nonnull
13941414 public static String getIosUserAgent (@ Nullable final Localization localization ) {
1395- // Spoofing an iPhone 15 running iOS 17.5.1 with the hardcoded version of the iOS app
1415+ // Spoofing an iPhone 15 Pro Max running iOS 18.1.0 with the hardcoded version of the iOS app
13961416 return "com.google.ios.youtube/" + IOS_YOUTUBE_CLIENT_VERSION
13971417 + "(" + IOS_DEVICE_MODEL + "; U; CPU iOS "
13981418 + IOS_USER_AGENT_VERSION + " like Mac OS X; "
0 commit comments