Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
package org.schabi.newpipe.extractor.exceptions;

/**
* Exception for contents not supported in a country.
*
* <p>
* Unsupported content means content is not intentionally geographically restricted such as for
* distribution rights, for which {@link GeographicRestrictionException} should be used instead.
* </p>
*/
public class UnsupportedContentInCountryException extends ContentNotAvailableException {

public UnsupportedContentInCountryException(final String message) {
super(message);
}

public UnsupportedContentInCountryException(final String message, final Throwable cause) {
super(message, cause);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,12 @@ private ClientsConstants() {
static final String WEB_EMBEDDED_CLIENT_NAME = "WEB_EMBEDDED_PLAYER";
static final String WEB_EMBEDDED_CLIENT_VERSION = "1.20250121.00.00";

// WEB_MUSIC_ANALYTICS (YouTube charts)

static final String WEB_MUSIC_ANALYTICS_CLIENT_ID = "31";
static final String WEB_MUSIC_ANALYTICS_CLIENT_NAME = "WEB_MUSIC_ANALYTICS";
static final String WEB_MUSIC_ANALYTICS_CLIENT_VERSION = "2.0";

// IOS (iOS YouTube app) client fields

static final String IOS_CLIENT_ID = "5";
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
package org.schabi.newpipe.extractor.services.youtube;

import javax.annotation.Nonnull;
import javax.annotation.Nullable;

import static org.schabi.newpipe.extractor.services.youtube.ClientsConstants.ANDROID_CLIENT_ID;
import static org.schabi.newpipe.extractor.services.youtube.ClientsConstants.ANDROID_CLIENT_NAME;
import static org.schabi.newpipe.extractor.services.youtube.ClientsConstants.ANDROID_CLIENT_VERSION;
Expand All @@ -16,11 +19,11 @@
import static org.schabi.newpipe.extractor.services.youtube.ClientsConstants.WEB_CLIENT_NAME;
import static org.schabi.newpipe.extractor.services.youtube.ClientsConstants.WEB_EMBEDDED_CLIENT_ID;
import static org.schabi.newpipe.extractor.services.youtube.ClientsConstants.WEB_EMBEDDED_CLIENT_NAME;
import static org.schabi.newpipe.extractor.services.youtube.ClientsConstants.WEB_EMBEDDED_CLIENT_VERSION;
import static org.schabi.newpipe.extractor.services.youtube.ClientsConstants.WEB_HARDCODED_CLIENT_VERSION;
import static org.schabi.newpipe.extractor.services.youtube.ClientsConstants.WEB_REMIX_HARDCODED_CLIENT_VERSION;

import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import static org.schabi.newpipe.extractor.services.youtube.ClientsConstants.WEB_MUSIC_ANALYTICS_CLIENT_ID;
import static org.schabi.newpipe.extractor.services.youtube.ClientsConstants.WEB_MUSIC_ANALYTICS_CLIENT_NAME;
import static org.schabi.newpipe.extractor.services.youtube.ClientsConstants.WEB_MUSIC_ANALYTICS_CLIENT_VERSION;

// TODO: add docs

Expand All @@ -38,28 +41,28 @@ public static final class ClientInfo {
@Nonnull
public String clientVersion;
@Nonnull
public String clientScreen;
@Nullable
public String clientId;
@Nullable
public String clientScreen;
@Nullable
public String visitorData;

private ClientInfo(@Nonnull final String clientName,
@Nonnull final String clientVersion,
@Nonnull final String clientScreen,
@Nullable final String clientId,
@Nonnull final String clientId,
@Nullable final String clientScreen,
@Nullable final String visitorData) {
this.clientName = clientName;
this.clientVersion = clientVersion;
this.clientScreen = clientScreen;
this.clientId = clientId;
this.clientScreen = clientScreen;
this.visitorData = visitorData;
}
}

public static final class DeviceInfo {

@Nonnull
@Nullable
public String platform;
@Nullable
public String deviceMake;
Expand All @@ -71,7 +74,7 @@ public static final class DeviceInfo {
public String osVersion;
public int androidSdkVersion;

private DeviceInfo(@Nonnull final String platform,
private DeviceInfo(@Nullable final String platform,
@Nullable final String deviceMake,
@Nullable final String deviceModel,
@Nullable final String osName,
Expand All @@ -96,8 +99,8 @@ private InnertubeClientRequestInfo(@Nonnull final ClientInfo clientInfo,
public static InnertubeClientRequestInfo ofWebClient() {
return new InnertubeClientRequestInfo(
new InnertubeClientRequestInfo.ClientInfo(
WEB_CLIENT_NAME, WEB_HARDCODED_CLIENT_VERSION, WATCH_CLIENT_SCREEN,
WEB_CLIENT_ID, null),
WEB_CLIENT_NAME, WEB_HARDCODED_CLIENT_VERSION, WEB_CLIENT_ID,
WATCH_CLIENT_SCREEN, null),
new InnertubeClientRequestInfo.DeviceInfo(DESKTOP_CLIENT_PLATFORM, null, null,
null, null, -1));
}
Expand All @@ -106,17 +109,27 @@ public static InnertubeClientRequestInfo ofWebClient() {
public static InnertubeClientRequestInfo ofWebEmbeddedPlayerClient() {
return new InnertubeClientRequestInfo(
new InnertubeClientRequestInfo.ClientInfo(WEB_EMBEDDED_CLIENT_NAME,
WEB_REMIX_HARDCODED_CLIENT_VERSION, EMBED_CLIENT_SCREEN,
WEB_EMBEDDED_CLIENT_ID, null),
WEB_EMBEDDED_CLIENT_VERSION, WEB_EMBEDDED_CLIENT_ID, EMBED_CLIENT_SCREEN,
null),
new InnertubeClientRequestInfo.DeviceInfo(DESKTOP_CLIENT_PLATFORM, null, null,
null, null, -1));
}

@Nonnull
public static InnertubeClientRequestInfo ofWebMusicAnalyticsChartsClient() {
return new InnertubeClientRequestInfo(
new InnertubeClientRequestInfo.ClientInfo(WEB_MUSIC_ANALYTICS_CLIENT_NAME,
WEB_MUSIC_ANALYTICS_CLIENT_VERSION, WEB_MUSIC_ANALYTICS_CLIENT_ID, null,
null),
new InnertubeClientRequestInfo.DeviceInfo(null, null, null,
null, null, -1));
}

@Nonnull
public static InnertubeClientRequestInfo ofAndroidClient() {
return new InnertubeClientRequestInfo(
new InnertubeClientRequestInfo.ClientInfo(ANDROID_CLIENT_NAME,
ANDROID_CLIENT_VERSION, WATCH_CLIENT_SCREEN, ANDROID_CLIENT_ID, null),
ANDROID_CLIENT_VERSION, ANDROID_CLIENT_ID, WATCH_CLIENT_SCREEN, null),
new InnertubeClientRequestInfo.DeviceInfo(MOBILE_CLIENT_PLATFORM, null, null,
"Android", "15", 35));
}
Expand All @@ -125,7 +138,7 @@ public static InnertubeClientRequestInfo ofAndroidClient() {
public static InnertubeClientRequestInfo ofIosClient() {
return new InnertubeClientRequestInfo(
new InnertubeClientRequestInfo.ClientInfo(IOS_CLIENT_NAME, IOS_CLIENT_VERSION,
WATCH_CLIENT_SCREEN, IOS_CLIENT_ID, null),
IOS_CLIENT_ID, WATCH_CLIENT_SCREEN, null),
new InnertubeClientRequestInfo.DeviceInfo(MOBILE_CLIENT_PLATFORM, "Apple",
IOS_DEVICE_MODEL, "iOS", IOS_OS_VERSION, -1));
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1181,8 +1181,8 @@ public static Map<String, List<String>> getOriginReferrerHeaders(@Nonnull final
* @param name The X-YouTube-Client-Name value.
* @param version X-YouTube-Client-Version value.
*/
static Map<String, List<String>> getClientHeaders(@Nonnull final String name,
@Nonnull final String version) {
public static Map<String, List<String>> getClientHeaders(@Nonnull final String name,
@Nonnull final String version) {
return Map.of("X-YouTube-Client-Name", List.of(name),
"X-YouTube-Client-Version", List.of(version));
}
Expand Down Expand Up @@ -1525,7 +1525,7 @@ public static String getVisitorDataFromInnertube(
}

@Nonnull
static JsonBuilder<JsonObject> prepareJsonBuilder(
public static JsonBuilder<JsonObject> prepareJsonBuilder(
@Nonnull final Localization localization,
@Nonnull final ContentCountry contentCountry,
@Nonnull final InnertubeClientRequestInfo innertubeClientRequestInfo,
Expand All @@ -1534,9 +1534,15 @@ static JsonBuilder<JsonObject> prepareJsonBuilder(
.object("context")
.object("client")
.value("clientName", innertubeClientRequestInfo.clientInfo.clientName)
.value("clientVersion", innertubeClientRequestInfo.clientInfo.clientVersion)
.value("clientScreen", innertubeClientRequestInfo.clientInfo.clientScreen)
.value("platform", innertubeClientRequestInfo.deviceInfo.platform);
.value("clientVersion", innertubeClientRequestInfo.clientInfo.clientVersion);

if (innertubeClientRequestInfo.clientInfo.clientScreen != null) {
builder.value("clientScreen", innertubeClientRequestInfo.clientInfo.clientScreen);
}

if (innertubeClientRequestInfo.deviceInfo.platform != null) {
builder.value("platform", innertubeClientRequestInfo.deviceInfo.platform);
}

if (innertubeClientRequestInfo.clientInfo.visitorData != null) {
builder.value("visitorData", innertubeClientRequestInfo.clientInfo.visitorData);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,14 +35,24 @@
import org.schabi.newpipe.extractor.services.youtube.extractors.YoutubeStreamExtractor;
import org.schabi.newpipe.extractor.services.youtube.extractors.YoutubeSubscriptionExtractor;
import org.schabi.newpipe.extractor.services.youtube.extractors.YoutubeSuggestionExtractor;
import org.schabi.newpipe.extractor.services.youtube.extractors.YoutubeTrendingExtractor;
import org.schabi.newpipe.extractor.services.youtube.extractors.kiosk.YoutubeTrendingExtractor;
import org.schabi.newpipe.extractor.services.youtube.extractors.kiosk.YoutubeLiveExtractor;
import org.schabi.newpipe.extractor.services.youtube.extractors.kiosk.YoutubeTrendingGamingVideosExtractor;
import org.schabi.newpipe.extractor.services.youtube.extractors.kiosk.YoutubeTrendingMoviesAndShowsTrailersExtractor;
import org.schabi.newpipe.extractor.services.youtube.extractors.kiosk.YoutubeTrendingMusicExtractor;
import org.schabi.newpipe.extractor.services.youtube.extractors.kiosk.YoutubeTrendingPodcastsEpisodesExtractor;
import org.schabi.newpipe.extractor.services.youtube.linkHandler.YoutubeChannelLinkHandlerFactory;
import org.schabi.newpipe.extractor.services.youtube.linkHandler.YoutubeChannelTabLinkHandlerFactory;
import org.schabi.newpipe.extractor.services.youtube.linkHandler.YoutubeCommentsLinkHandlerFactory;
import org.schabi.newpipe.extractor.services.youtube.linkHandler.YoutubeLiveLinkHandlerFactory;
import org.schabi.newpipe.extractor.services.youtube.linkHandler.YoutubePlaylistLinkHandlerFactory;
import org.schabi.newpipe.extractor.services.youtube.linkHandler.YoutubeSearchQueryHandlerFactory;
import org.schabi.newpipe.extractor.services.youtube.linkHandler.YoutubeStreamLinkHandlerFactory;
import org.schabi.newpipe.extractor.services.youtube.linkHandler.YoutubeTrendingGamingVideosLinkHandlerFactory;
import org.schabi.newpipe.extractor.services.youtube.linkHandler.YoutubeTrendingLinkHandlerFactory;
import org.schabi.newpipe.extractor.services.youtube.linkHandler.YoutubeTrendingMoviesAndShowsTrailersLinkHandlerFactory;
import org.schabi.newpipe.extractor.services.youtube.linkHandler.YoutubeTrendingMusicLinkHandlerFactory;
import org.schabi.newpipe.extractor.services.youtube.linkHandler.YoutubeTrendingPodcastsEpisodesLinkHandlerFactory;
import org.schabi.newpipe.extractor.stream.StreamExtractor;
import org.schabi.newpipe.extractor.subscription.SubscriptionExtractor;
import org.schabi.newpipe.extractor.suggestion.SuggestionExtractor;
Expand Down Expand Up @@ -154,20 +164,71 @@ public SuggestionExtractor getSuggestionExtractor() {
@Override
public KioskList getKioskList() throws ExtractionException {
final KioskList list = new KioskList(this);
final ListLinkHandlerFactory h = YoutubeTrendingLinkHandlerFactory.getInstance();
final ListLinkHandlerFactory trendingLHF = YoutubeTrendingLinkHandlerFactory.INSTANCE;
final ListLinkHandlerFactory runningLivesLHF =
YoutubeLiveLinkHandlerFactory.INSTANCE;
final ListLinkHandlerFactory trendingPodcastsEpisodesLHF =
YoutubeTrendingPodcastsEpisodesLinkHandlerFactory.INSTANCE;
final ListLinkHandlerFactory trendingGamingVideosLHF =
YoutubeTrendingGamingVideosLinkHandlerFactory.INSTANCE;
final ListLinkHandlerFactory trendingMoviesAndShowsLHF =
YoutubeTrendingMoviesAndShowsTrailersLinkHandlerFactory.INSTANCE;
final ListLinkHandlerFactory trendingMusicLHF =
YoutubeTrendingMusicLinkHandlerFactory.INSTANCE;

// add kiosks here e.g.:
try {
list.addKioskEntry(
(streamingService, url, id) -> new YoutubeLiveExtractor(
YoutubeService.this,
runningLivesLHF.fromUrl(url),
id),
runningLivesLHF,
YoutubeLiveLinkHandlerFactory.KIOSK_ID
);
list.addKioskEntry(
(streamingService, url, id) -> new YoutubeTrendingPodcastsEpisodesExtractor(
YoutubeService.this,
trendingPodcastsEpisodesLHF.fromUrl(url),
id),
trendingPodcastsEpisodesLHF,
YoutubeTrendingPodcastsEpisodesLinkHandlerFactory.KIOSK_ID
);
list.addKioskEntry(
(streamingService, url, id) -> new YoutubeTrendingGamingVideosExtractor(
YoutubeService.this,
trendingGamingVideosLHF.fromUrl(url),
id),
trendingGamingVideosLHF,
YoutubeTrendingGamingVideosLinkHandlerFactory.KIOSK_ID
);
list.addKioskEntry(
(streamingService, url, id) ->
new YoutubeTrendingMoviesAndShowsTrailersExtractor(
YoutubeService.this,
trendingMoviesAndShowsLHF.fromUrl(url),
id),
trendingMoviesAndShowsLHF,
YoutubeTrendingMoviesAndShowsTrailersLinkHandlerFactory.KIOSK_ID
);
list.addKioskEntry(
(streamingService, url, id) -> new YoutubeTrendingMusicExtractor(
YoutubeService.this,
trendingMusicLHF.fromUrl(url),
id),
trendingMusicLHF,
YoutubeTrendingMusicLinkHandlerFactory.KIOSK_ID
);
// Deprecated (i.e. removed from the interface of YouTube) since July 21, 2025
list.addKioskEntry(
(streamingService, url, id) -> new YoutubeTrendingExtractor(
YoutubeService.this,
h.fromUrl(url),
trendingLHF.fromUrl(url),
id
),
h,
trendingLHF,
YoutubeTrendingExtractor.KIOSK_ID
);
list.setDefaultKiosk(YoutubeTrendingExtractor.KIOSK_ID);
list.setDefaultKiosk(YoutubeLiveLinkHandlerFactory.KIOSK_ID);
} catch (final Exception e) {
throw new ExtractionException(e);
}
Expand Down
Loading