Skip to content

Commit 2e3da44

Browse files
committed
[YouTube] Add documentation about parameters added and clients versions and key
Also move the iPhone device machine id to a constant, explain how it is used and move the licence in the header of the file, and fix missing imports in YoutubeStreamExtractor (due to a rebase issue).
1 parent 1dad3bf commit 2e3da44

File tree

2 files changed

+69
-4
lines changed

2 files changed

+69
-4
lines changed

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

Lines changed: 66 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,9 @@ public final class YoutubeParsingHelper {
8080
private YoutubeParsingHelper() {
8181
}
8282

83+
/**
84+
* The base URL of requests of the {@code WEB} client to the InnerTube internal API
85+
*/
8386
public static final String YOUTUBEI_V1_URL = "https://www.youtube.com/youtubei/v1/";
8487

8588
/**
@@ -91,14 +94,60 @@ private YoutubeParsingHelper() {
9194
* </p>
9295
**/
9396
public static final String DISABLE_PRETTY_PRINT_PARAMETER = "&prettyPrint=false";
97+
98+
/**
99+
* A parameter sent by official clients named {@code contentPlaybackNonce}.
100+
*
101+
* <p>
102+
* It is sent by official clients on videoplayback requests, and by all clients (except the
103+
* {@code WEB} one to the player requests.
104+
* </p>
105+
*
106+
* <p>
107+
* It is composed of 16 characters which are generated from
108+
* {@link #CONTENT_PLAYBACK_NONCE_ALPHABET this alphabet}, with the use of strong random
109+
* values.
110+
* </p>
111+
*
112+
* @see #generateContentPlaybackNonce()
113+
*/
94114
public static final String CPN = "cpn";
95115
public static final String VIDEO_ID = "videoId";
96116

117+
/**
118+
* The client version for InnerTube requests with the {@code WEB} client, used as the last
119+
* fallback if the extraction of the real one failed.
120+
*
121+
* You can get it directly either into YouTube pages or the service worker JavaScript file
122+
* ({@code https://www.youtube.com/sw.js}) (also applies for YouTube Music).
123+
*/
97124
private static final String HARDCODED_CLIENT_VERSION = "2.20220315.01.00";
98125
private static final String HARDCODED_KEY = "AIzaSyAO_FJ2SlqU8Q4STEHLGCilw_Y9_11qcW8";
99126

127+
/**
128+
* The InnerTube API key used by the {@code ANDROID} client. Found with the help of
129+
* reverse-engineering app network requests.
130+
*/
100131
private static final String ANDROID_YOUTUBE_KEY = "AIzaSyA8eiZmM1FaDVjRy-df2KTyQ_vz_yYM39w";
132+
133+
/**
134+
* The InnerTube API key used by the {@code iOS} client. Found with the help of
135+
* reverse-engineering app network requests.
136+
*/
101137
private static final String IOS_YOUTUBE_KEY = "AIzaSyB-63vPrdThhKuerbB2N_l7Kwwcxj6yUAc";
138+
139+
/**
140+
* The hardcoded client version of the Android app used for InnerTube requests with this
141+
* client.
142+
*
143+
* <p>
144+
* It can be extracted by getting the latest release version of the app in an APK repository
145+
* such as APKMirror.
146+
* </p>
147+
*
148+
* @implNote This version is also used for the {@code iOS} client, as getting the app version
149+
* without an iPhone device is not so easily.
150+
*/
102151
private static final String MOBILE_YOUTUBE_CLIENT_VERSION = "17.10.35";
103152

104153
private static String clientVersion;
@@ -128,6 +177,16 @@ private YoutubeParsingHelper() {
128177
private static final String CONTENT_PLAYBACK_NONCE_ALPHABET =
129178
"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_";
130179

180+
/**
181+
* The device machine id for the iPhone 13, used to get 60fps with the {@code iOS} client.
182+
*
183+
* <p>
184+
* See <a href="https://gist.github.com/adamawolf/3048717">this GitHub Gist</a> for more
185+
* information.
186+
* </p>
187+
*/
188+
private static final String IOS_DEVICE_MODEL = "iPhone14,5";
189+
131190
private static Random numberGenerator = new SecureRandom();
132191

133192
/**
@@ -1071,7 +1130,7 @@ public static JsonBuilder<JsonObject> prepareIosMobileJsonBuilder(
10711130
.value("clientName", "IOS")
10721131
.value("clientVersion", MOBILE_YOUTUBE_CLIENT_VERSION)
10731132
// Device model is required to get 60fps streams
1074-
.value("deviceModel", "iPhone14,5")
1133+
.value("deviceModel", IOS_DEVICE_MODEL)
10751134
.value("platform", "MOBILE")
10761135
.value("hl", localization.getLocalizationCode())
10771136
.value("gl", contentCountry.getCountryCode())
@@ -1169,7 +1228,7 @@ public static JsonBuilder<JsonObject> prepareIosMobileEmbedVideoJsonBuilder(
11691228
.value("clientVersion", MOBILE_YOUTUBE_CLIENT_VERSION)
11701229
.value("clientScreen", "EMBED")
11711230
// Device model is required to get 60fps streams
1172-
.value("deviceModel", "iPhone14,5")
1231+
.value("deviceModel", IOS_DEVICE_MODEL)
11731232
.value("platform", "MOBILE")
11741233
.value("hl", localization.getLocalizationCode())
11751234
.value("gl", contentCountry.getCountryCode())
@@ -1208,6 +1267,8 @@ public static byte[] createDesktopPlayerBody(
12081267
: prepareDesktopJsonBuilder(localization, contentCountry))
12091268
.object("playbackContext")
12101269
.object("contentPlaybackContext")
1270+
// Some parameters which are sent by the official WEB client (probably some
1271+
// of them are not useful)
12111272
.value("currentUrl", "/watch?v=" + videoId)
12121273
.value("vis", 0)
12131274
.value("splay", false)
@@ -1260,9 +1321,10 @@ public static String getAndroidUserAgent(@Nullable final Localization localizati
12601321
*/
12611322
@Nonnull
12621323
public static String getIosUserAgent(@Nullable final Localization localization) {
1263-
// Spoofing an iPhone 13 running iOS 15.4 with the hardcoded mobile client version
1324+
// Spoofing an iPhone running iOS 15.4 with the hardcoded mobile client version
12641325
return "com.google.ios.youtube/" + MOBILE_YOUTUBE_CLIENT_VERSION
1265-
+ "(iPhone14,5; U; CPU iOS 15_4 like Mac OS X; "
1326+
+ "(" + IOS_DEVICE_MODEL
1327+
+ "; U; CPU iOS 15_4 like Mac OS X; "
12661328
+ (localization != null ? localization.getCountryCode()
12671329
: Localization.DEFAULT.getCountryCode())
12681330
+ ")";

extractor/src/main/java/org/schabi/newpipe/extractor/services/youtube/extractors/YoutubeStreamExtractor.java

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,12 +7,15 @@
77
import static org.schabi.newpipe.extractor.services.youtube.YoutubeParsingHelper.generateContentPlaybackNonce;
88
import static org.schabi.newpipe.extractor.services.youtube.YoutubeParsingHelper.generateTParameter;
99
import static org.schabi.newpipe.extractor.services.youtube.YoutubeParsingHelper.getJsonAndroidPostResponse;
10+
import static org.schabi.newpipe.extractor.services.youtube.YoutubeParsingHelper.getJsonIosPostResponse;
1011
import static org.schabi.newpipe.extractor.services.youtube.YoutubeParsingHelper.getJsonPostResponse;
1112
import static org.schabi.newpipe.extractor.services.youtube.YoutubeParsingHelper.getTextFromObject;
1213
import static org.schabi.newpipe.extractor.services.youtube.YoutubeParsingHelper.prepareAndroidMobileEmbedVideoJsonBuilder;
1314
import static org.schabi.newpipe.extractor.services.youtube.YoutubeParsingHelper.prepareAndroidMobileJsonBuilder;
1415
import static org.schabi.newpipe.extractor.services.youtube.YoutubeParsingHelper.prepareDesktopEmbedVideoJsonBuilder;
1516
import static org.schabi.newpipe.extractor.services.youtube.YoutubeParsingHelper.prepareDesktopJsonBuilder;
17+
import static org.schabi.newpipe.extractor.services.youtube.YoutubeParsingHelper.prepareIosMobileEmbedVideoJsonBuilder;
18+
import static org.schabi.newpipe.extractor.services.youtube.YoutubeParsingHelper.prepareIosMobileJsonBuilder;
1619
import static org.schabi.newpipe.extractor.utils.Utils.EMPTY_STRING;
1720
import static org.schabi.newpipe.extractor.utils.Utils.UTF_8;
1821
import static org.schabi.newpipe.extractor.utils.Utils.isNullOrEmpty;

0 commit comments

Comments
 (0)