[YouTube] Move to their own file and update clients' constants

Also update client version and device info and add TVHTML5 client and
WEB_EMBEDDED_PLAYER constants, these will be used in the future.

TVHTML5_SIMPLY_EMBED_CLIENT_VERSION has been kept in its place as
the corresponding client does not work anonymously anymore, so it will
be removed soon.
This commit is contained in:
AudricV 2025-01-28 20:16:13 +01:00 committed by Stypox
parent 186e32c9db
commit fd82ec585b
No known key found for this signature in database
GPG Key ID: 4BDF1B40A49FDD23
2 changed files with 144 additions and 102 deletions

View File

@ -0,0 +1,116 @@
package org.schabi.newpipe.extractor.services.youtube;
final class ClientsConstants {
private ClientsConstants() {
}
// Common client fields
static final String DESKTOP_CLIENT_PLATFORM = "DESKTOP";
static final String MOBILE_CLIENT_PLATFORM = "MOBILE";
static final String WATCH_CLIENT_SCREEN = "WATCH";
static final String EMBED_CLIENT_SCREEN = "EMBED";
// WEB (YouTube desktop) client fields
static final String WEB_CLIENT_ID = "1";
static final String WEB_CLIENT_NAME = "WEB";
/**
* The client version for InnerTube requests with the {@code WEB} client, used as the last
* fallback if the extraction of the real one failed.
*/
static final String WEB_HARDCODED_CLIENT_VERSION = "2.20250122.04.00";
// WEB_REMIX (YouTube Music) client fields
static final String WEB_REMIX_CLIENT_ID = "67";
static final String WEB_REMIX_CLIENT_NAME = "WEB_REMIX";
static final String WEB_REMIX_HARDCODED_CLIENT_VERSION = "1.20250122.01.00";
// TVHTML5 (YouTube on TVs and consoles using HTML5) client fields
static final String TVHTML5_CLIENT_ID = "7";
static final String TVHTML5_CLIENT_NAME = "TVHTML5";
static final String TVHTML5_CLIENT_VERSION = "7.20250122.15.00";
static final String TVHTML5_CLIENT_PLATFORM = "GAME_CONSOLE";
static final String TVHTML5_DEVICE_MAKE = "Sony";
static final String TVHTML5_DEVICE_MODEL_AND_OS_NAME = "PlayStation 4";
// CHECKSTYLE:OFF
static final String TVHTML5_USER_AGENT =
"Mozilla/5.0 (PlayStation; PlayStation 4/12.00) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/15.4 Safari/605.1.15";
// CHECKSTYLE:ON
// WEB_EMBEDDED_PLAYER (YouTube embeds)
static final String WEB_EMBEDDED_CLIENT_ID = "56";
static final String WEB_EMBEDDED_CLIENT_NAME = "WEB_EMBEDDED_PLAYER";
static final String WEB_EMBEDDED_CLIENT_VERSION = "1.20250121.00.00";
// IOS (iOS YouTube app) client fields
static final String IOS_CLIENT_NAME = "IOS";
/**
* The hardcoded client version of the iOS app used for InnerTube requests with this client.
*
* <p>
* It can be extracted by getting the latest release version of the app on
* <a href="https://apps.apple.com/us/app/youtube-watch-listen-stream/id544007664/">the App
* Store page of the YouTube app</a>, in the {@code Whats New} section.
* </p>
*/
static final String IOS_CLIENT_VERSION = "20.03.02";
/**
* The device machine id for the iPhone 15 Pro Max, used to get 60fps with the {@code iOS}
* client.
*
* <p>
* See <a href="https://gist.github.com/adamawolf/3048717">this GitHub Gist</a> for more
* information.
* </p>
*/
static final String IOS_DEVICE_MODEL = "iPhone16,2";
/**
* The iOS version to be used in JSON POST requests, the one of an iPhone 15 Pro Max running
* iOS 18.2.1 with the hardcoded version of the iOS app (for the {@code "osVersion"} field).
*
* <p>
* The value of this field seems to use the following structure:
* "iOS major version.minor version.patch version.build version", where
* "patch version" is equal to 0 if it isn't set
* The build version corresponding to the iOS version used can be found on
* <a href="https://theapplewiki.com/wiki/Firmware/iPhone/18.x#iPhone_15_Pro_Max">
* https://theapplewiki.com/wiki/Firmware/iPhone/18.x#iPhone_15_Pro_Max</a>
* </p>
*
* @see #IOS_USER_AGENT_VERSION
*/
static final String IOS_OS_VERSION = "18.2.1.22C161";
/**
* The iOS version to be used in the HTTP user agent for requests.
*
* <p>
* This should be the same of as {@link #IOS_OS_VERSION}.
* </p>
*
* @see #IOS_OS_VERSION
*/
static final String IOS_USER_AGENT_VERSION = "18_2_1";
// ANDROID (Android YouTube app) client fields
static final String ANDROID_CLIENT_NAME = "ANDROID";
/**
* The hardcoded client version of the Android app used for InnerTube requests with this
* client.
*
* <p>
* It can be extracted by getting the latest release version of the app in an APK repository
* such as <a href="https://www.apkmirror.com/apk/google-inc/youtube/">APKMirror</a>.
* </p>
*/
static final String ANDROID_CLIENT_VERSION = "19.28.35";
}

View File

@ -21,6 +21,17 @@
package org.schabi.newpipe.extractor.services.youtube;
import static org.schabi.newpipe.extractor.NewPipe.getDownloader;
import static org.schabi.newpipe.extractor.services.youtube.ClientsConstants.ANDROID_CLIENT_VERSION;
import static org.schabi.newpipe.extractor.services.youtube.ClientsConstants.DESKTOP_CLIENT_PLATFORM;
import static org.schabi.newpipe.extractor.services.youtube.ClientsConstants.IOS_CLIENT_VERSION;
import static org.schabi.newpipe.extractor.services.youtube.ClientsConstants.IOS_DEVICE_MODEL;
import static org.schabi.newpipe.extractor.services.youtube.ClientsConstants.IOS_USER_AGENT_VERSION;
import static org.schabi.newpipe.extractor.services.youtube.ClientsConstants.WEB_CLIENT_ID;
import static org.schabi.newpipe.extractor.services.youtube.ClientsConstants.WEB_CLIENT_NAME;
import static org.schabi.newpipe.extractor.services.youtube.ClientsConstants.WEB_HARDCODED_CLIENT_VERSION;
import static org.schabi.newpipe.extractor.services.youtube.ClientsConstants.WEB_REMIX_CLIENT_ID;
import static org.schabi.newpipe.extractor.services.youtube.ClientsConstants.WEB_REMIX_CLIENT_NAME;
import static org.schabi.newpipe.extractor.services.youtube.ClientsConstants.WEB_REMIX_HARDCODED_CLIENT_VERSION;
import static org.schabi.newpipe.extractor.utils.Utils.HTTP;
import static org.schabi.newpipe.extractor.utils.Utils.HTTPS;
import static org.schabi.newpipe.extractor.utils.Utils.getStringResultFromRegexArray;
@ -144,55 +155,11 @@ public final class YoutubeParsingHelper {
*/
public static final String RACY_CHECK_OK = "racyCheckOk";
/**
* The hardcoded client ID used for InnerTube requests with the {@code WEB} client.
*/
private static final String WEB_CLIENT_ID = "1";
/**
* The client version for InnerTube requests with the {@code WEB} client, used as the last
* fallback if the extraction of the real one failed.
*/
private static final String HARDCODED_CLIENT_VERSION = "2.20240718.01.00";
/**
* The hardcoded client version of the Android app used for InnerTube requests with this
* client.
*
* <p>
* It can be extracted by getting the latest release version of the app in an APK repository
* such as <a href="https://www.apkmirror.com/apk/google-inc/youtube/">APKMirror</a>.
* </p>
*/
private static final String ANDROID_YOUTUBE_CLIENT_VERSION = "19.28.35";
/**
* The hardcoded client version of the iOS app used for InnerTube requests with this client.
*
* <p>
* It can be extracted by getting the latest release version of the app on
* <a href="https://apps.apple.com/us/app/youtube-watch-listen-stream/id544007664/">the App
* Store page of the YouTube app</a>, in the {@code Whats New} section.
* </p>
*/
private static final String IOS_YOUTUBE_CLIENT_VERSION = "20.03.02";
/**
* The hardcoded client version used for InnerTube requests with the TV HTML5 embed client.
*/
private static final String TVHTML5_SIMPLY_EMBED_CLIENT_VERSION = "2.0";
/**
* The hardcoded client ID used for InnerTube requests with the YouTube Music desktop client.
*/
private static final String YOUTUBE_MUSIC_CLIENT_ID = "67";
/**
* The hardcoded client version used for InnerTube requests with the YouTube Music desktop
* client.
*/
private static final String HARDCODED_YOUTUBE_MUSIC_CLIENT_VERSION = "1.20240715.01.00";
private static String clientVersion;
private static String youtubeMusicClientVersion;
@ -212,41 +179,6 @@ public final class YoutubeParsingHelper {
private static final String CONTENT_PLAYBACK_NONCE_ALPHABET =
"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_";
/**
* The device machine id for the iPhone 15 Pro Max,
* used to get 60fps with the {@code iOS} client.
*
* <p>
* See <a href="https://gist.github.com/adamawolf/3048717">this GitHub Gist</a> for more
* information.
* </p>
*/
private static final String IOS_DEVICE_MODEL = "iPhone16,2";
/**
* Spoofing an iPhone 15 Pro Max running iOS 18.2.1 with the hardcoded version of the iOS app.
* To be used for the {@code "osVersion"} field in JSON POST requests.
* <p>
* The value of this field seems to use the following structure:
* "iOS major version.minor version.patch version.build version", where
* "patch version" is equal to 0 if it isn't set
* The build version corresponding to the iOS version used can be found on
* <a href="https://theapplewiki.com/wiki/Firmware/iPhone/18.x#iPhone_15_Pro_Max">
* https://theapplewiki.com/wiki/Firmware/iPhone/18.x#iPhone_15_Pro_Max</a>
* </p>
*
* @see #IOS_USER_AGENT_VERSION
*/
private static final String IOS_OS_VERSION = "18.2.1.22C161";
/**
* Spoofing an iPhone 15 Pro Max running iOS 18.2.1 with the hardcoded version of the iOS app.
* To be used in the user agent for requests.
*
* @see #IOS_OS_VERSION
*/
private static final String IOS_USER_AGENT_VERSION = "18_2_1";
private static Random numberGenerator = new Random();
private static final String FEED_BASE_CHANNEL_ID =
@ -561,9 +493,9 @@ public final class YoutubeParsingHelper {
.object("client")
.value("hl", "en-GB")
.value("gl", "GB")
.value("clientName", "WEB")
.value("clientVersion", HARDCODED_CLIENT_VERSION)
.value("platform", "DESKTOP")
.value("clientName", WEB_CLIENT_NAME)
.value("clientVersion", WEB_HARDCODED_CLIENT_VERSION)
.value("platform", DESKTOP_CLIENT_PLATFORM)
.value("utcOffsetMinutes", 0)
.end()
.object("request")
@ -581,7 +513,7 @@ public final class YoutubeParsingHelper {
.end().done().getBytes(StandardCharsets.UTF_8);
// @formatter:on
final var headers = getClientHeaders(WEB_CLIENT_ID, HARDCODED_CLIENT_VERSION);
final var headers = getClientHeaders(WEB_CLIENT_ID, WEB_HARDCODED_CLIENT_VERSION);
// This endpoint is fetched by the YouTube website to get the items of its main menu and is
// pretty lightweight (around 30kB)
@ -705,7 +637,7 @@ public final class YoutubeParsingHelper {
// Fallback to the hardcoded one if it is valid
if (isHardcodedClientVersionValid()) {
clientVersion = HARDCODED_CLIENT_VERSION;
clientVersion = WEB_HARDCODED_CLIENT_VERSION;
return clientVersion;
}
@ -752,11 +684,11 @@ public final class YoutubeParsingHelper {
.object()
.object("context")
.object("client")
.value("clientName", "WEB_REMIX")
.value("clientVersion", HARDCODED_YOUTUBE_MUSIC_CLIENT_VERSION)
.value("clientName", WEB_REMIX_CLIENT_NAME)
.value("clientVersion", WEB_REMIX_HARDCODED_CLIENT_VERSION)
.value("hl", "en-GB")
.value("gl", "GB")
.value("platform", "DESKTOP")
.value("platform", DESKTOP_CLIENT_PLATFORM)
.value("utcOffsetMinutes", 0)
.end()
.object("request")
@ -775,8 +707,7 @@ public final class YoutubeParsingHelper {
// @formatter:on
final var headers = new HashMap<>(getOriginReferrerHeaders(YOUTUBE_MUSIC_URL));
headers.putAll(getClientHeaders(YOUTUBE_MUSIC_CLIENT_ID,
HARDCODED_YOUTUBE_MUSIC_CLIENT_VERSION));
headers.putAll(getClientHeaders(WEB_REMIX_CLIENT_ID, WEB_HARDCODED_CLIENT_VERSION));
final Response response = getDownloader().postWithContentTypeJson(url, headers, json);
// Ensure to have a valid response
@ -789,7 +720,7 @@ public final class YoutubeParsingHelper {
return youtubeMusicClientVersion;
}
if (isHardcodedYoutubeMusicClientVersionValid()) {
youtubeMusicClientVersion = HARDCODED_YOUTUBE_MUSIC_CLIENT_VERSION;
youtubeMusicClientVersion = WEB_REMIX_HARDCODED_CLIENT_VERSION;
return youtubeMusicClientVersion;
}
@ -1196,10 +1127,10 @@ public final class YoutubeParsingHelper {
.object("client")
.value("hl", localization.getLocalizationCode())
.value("gl", contentCountry.getCountryCode())
.value("clientName", "WEB")
.value("clientName", WEB_CLIENT_NAME)
.value("clientVersion", getClientVersion())
.value("originalUrl", "https://www.youtube.com")
.value("platform", "DESKTOP")
.value("platform", DESKTOP_CLIENT_PLATFORM)
.value("utcOffsetMinutes", 0)
.value("visitorData", vData)
.end()
@ -1391,9 +1322,8 @@ public final class YoutubeParsingHelper {
*/
@Nonnull
public static String getAndroidUserAgent(@Nullable final Localization localization) {
// Spoofing an Android 14 device with the hardcoded version of the Android app
return "com.google.android.youtube/" + ANDROID_YOUTUBE_CLIENT_VERSION
+ " (Linux; U; Android 14; "
return "com.google.android.youtube/" + ANDROID_CLIENT_VERSION
+ " (Linux; U; Android 15; "
+ (localization != null ? localization : Localization.DEFAULT).getCountryCode()
+ ") gzip";
}
@ -1413,11 +1343,8 @@ public final class YoutubeParsingHelper {
*/
@Nonnull
public static String getIosUserAgent(@Nullable final Localization localization) {
// Spoofing an iPhone 15 Pro Max running iOS 18.1.0
// with the hardcoded version of the iOS app
return "com.google.ios.youtube/" + IOS_YOUTUBE_CLIENT_VERSION
+ "(" + IOS_DEVICE_MODEL + "; U; CPU iOS "
+ IOS_USER_AGENT_VERSION + " like Mac OS X; "
return "com.google.ios.youtube/" + IOS_CLIENT_VERSION + "(" + IOS_DEVICE_MODEL
+ "; U; CPU iOS " + IOS_USER_AGENT_VERSION + " like Mac OS X; "
+ (localization != null ? localization : Localization.DEFAULT).getCountryCode()
+ ")";
}
@ -1428,8 +1355,7 @@ public final class YoutubeParsingHelper {
@Nonnull
public static Map<String, List<String>> getYoutubeMusicHeaders() {
final var headers = new HashMap<>(getOriginReferrerHeaders(YOUTUBE_MUSIC_URL));
headers.putAll(getClientHeaders(YOUTUBE_MUSIC_CLIENT_ID,
youtubeMusicClientVersion));
headers.putAll(getClientHeaders(WEB_REMIX_CLIENT_ID, youtubeMusicClientVersion));
return headers;
}