mirror of
https://github.com/TeamNewPipe/NewPipeExtractor.git
synced 2025-04-29 00:10:35 +05:30
[YouTube] Apply changes in YoutubeMusicSearchExtractor and split its InfoItemExtractors into separate classes
Splitting YoutubeMusicSearchExtractor's InfoItemExtractors into separate classes (YoutubeMusicSongOrVideoInfoItemExtractor, YoutubeMusicAlbumOrPlaylistInfoItemExtractor and YoutubeMusicArtistInfoItemExtractor) allows to simplify YoutubeMusicSearchExtractor,improves reading and applying changes to InfoItems (no more losing at least quarter of a line due to indentations). These InfoItems, in which the image changes have been applied, don't extend the YouTube ones anymore, as most methods were overridden and the few ones that are not don't apply in YouTube Music items responses, so it was useless to extend them. The code of YoutubeMusicSearchExtractor have been also improved a bit.
This commit is contained in:
parent
c1981ed54f
commit
266cd1f76b
@ -0,0 +1,157 @@
|
||||
package org.schabi.newpipe.extractor.services.youtube.extractors;
|
||||
|
||||
import com.grack.nanojson.JsonArray;
|
||||
import com.grack.nanojson.JsonObject;
|
||||
import org.schabi.newpipe.extractor.Image;
|
||||
import org.schabi.newpipe.extractor.exceptions.ParsingException;
|
||||
import org.schabi.newpipe.extractor.playlist.PlaylistInfoItemExtractor;
|
||||
import org.schabi.newpipe.extractor.utils.Utils;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
import javax.annotation.Nullable;
|
||||
import java.util.List;
|
||||
|
||||
import static org.schabi.newpipe.extractor.ListExtractor.ITEM_COUNT_MORE_THAN_100;
|
||||
import static org.schabi.newpipe.extractor.ListExtractor.ITEM_COUNT_UNKNOWN;
|
||||
import static org.schabi.newpipe.extractor.services.youtube.YoutubeParsingHelper.getTextFromObject;
|
||||
import static org.schabi.newpipe.extractor.services.youtube.YoutubeParsingHelper.getImagesFromThumbnailsArray;
|
||||
import static org.schabi.newpipe.extractor.services.youtube.YoutubeParsingHelper.getUrlFromNavigationEndpoint;
|
||||
import static org.schabi.newpipe.extractor.services.youtube.linkHandler.YoutubeSearchQueryHandlerFactory.MUSIC_ALBUMS;
|
||||
import static org.schabi.newpipe.extractor.services.youtube.linkHandler.YoutubeSearchQueryHandlerFactory.MUSIC_PLAYLISTS;
|
||||
import static org.schabi.newpipe.extractor.utils.Utils.isNullOrEmpty;
|
||||
|
||||
public class YoutubeMusicAlbumOrPlaylistInfoItemExtractor implements PlaylistInfoItemExtractor {
|
||||
private final JsonObject albumOrPlaylistInfoItem;
|
||||
private final JsonArray descriptionElements;
|
||||
private final String searchType;
|
||||
|
||||
public YoutubeMusicAlbumOrPlaylistInfoItemExtractor(final JsonObject albumOrPlaylistInfoItem,
|
||||
final JsonArray descriptionElements,
|
||||
final String searchType) {
|
||||
this.albumOrPlaylistInfoItem = albumOrPlaylistInfoItem;
|
||||
this.descriptionElements = descriptionElements;
|
||||
this.searchType = searchType;
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
@Override
|
||||
public List<Image> getThumbnails() throws ParsingException {
|
||||
try {
|
||||
return getImagesFromThumbnailsArray(
|
||||
albumOrPlaylistInfoItem.getObject("thumbnail")
|
||||
.getObject("musicThumbnailRenderer")
|
||||
.getObject("thumbnail")
|
||||
.getArray("thumbnails"));
|
||||
} catch (final Exception e) {
|
||||
throw new ParsingException("Could not get thumbnails", e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getName() throws ParsingException {
|
||||
final String name = getTextFromObject(albumOrPlaylistInfoItem.getArray("flexColumns")
|
||||
.getObject(0)
|
||||
.getObject("musicResponsiveListItemFlexColumnRenderer")
|
||||
.getObject("text"));
|
||||
|
||||
if (!isNullOrEmpty(name)) {
|
||||
return name;
|
||||
}
|
||||
|
||||
throw new ParsingException("Could not get name");
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getUrl() throws ParsingException {
|
||||
String playlistId = albumOrPlaylistInfoItem.getObject("menu")
|
||||
.getObject("menuRenderer")
|
||||
.getArray("items")
|
||||
.getObject(4)
|
||||
.getObject("toggleMenuServiceItemRenderer")
|
||||
.getObject("toggledServiceEndpoint")
|
||||
.getObject("likeEndpoint")
|
||||
.getObject("target")
|
||||
.getString("playlistId");
|
||||
|
||||
if (isNullOrEmpty(playlistId)) {
|
||||
playlistId = albumOrPlaylistInfoItem.getObject("overlay")
|
||||
.getObject("musicItemThumbnailOverlayRenderer")
|
||||
.getObject("content")
|
||||
.getObject("musicPlayButtonRenderer")
|
||||
.getObject("playNavigationEndpoint")
|
||||
.getObject("watchPlaylistEndpoint")
|
||||
.getString("playlistId");
|
||||
}
|
||||
|
||||
if (!isNullOrEmpty(playlistId)) {
|
||||
return "https://music.youtube.com/playlist?list=" + playlistId;
|
||||
}
|
||||
|
||||
throw new ParsingException("Could not get URL");
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getUploaderName() throws ParsingException {
|
||||
final String name;
|
||||
if (searchType.equals(MUSIC_ALBUMS)) {
|
||||
name = descriptionElements.getObject(2).getString("text");
|
||||
} else {
|
||||
name = descriptionElements.getObject(0).getString("text");
|
||||
}
|
||||
|
||||
if (!isNullOrEmpty(name)) {
|
||||
return name;
|
||||
}
|
||||
|
||||
throw new ParsingException("Could not get uploader name");
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public String getUploaderUrl() throws ParsingException {
|
||||
if (searchType.equals(MUSIC_PLAYLISTS)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
final JsonArray items = albumOrPlaylistInfoItem.getObject("menu")
|
||||
.getObject("menuRenderer")
|
||||
.getArray("items");
|
||||
for (final Object item : items) {
|
||||
final JsonObject menuNavigationItemRenderer =
|
||||
((JsonObject) item).getObject("menuNavigationItemRenderer");
|
||||
if (menuNavigationItemRenderer.getObject("icon")
|
||||
.getString("iconType", "")
|
||||
.equals("ARTIST")) {
|
||||
return getUrlFromNavigationEndpoint(
|
||||
menuNavigationItemRenderer.getObject("navigationEndpoint"));
|
||||
}
|
||||
}
|
||||
|
||||
throw new ParsingException("Could not get uploader URL");
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isUploaderVerified() throws ParsingException {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getStreamCount() throws ParsingException {
|
||||
if (searchType.equals(MUSIC_ALBUMS)) {
|
||||
return ITEM_COUNT_UNKNOWN;
|
||||
}
|
||||
|
||||
final String count = descriptionElements.getObject(2)
|
||||
.getString("text");
|
||||
|
||||
if (!isNullOrEmpty(count)) {
|
||||
if (count.contains("100+")) {
|
||||
return ITEM_COUNT_MORE_THAN_100;
|
||||
} else {
|
||||
return Long.parseLong(Utils.removeNonDigitCharacters(count));
|
||||
}
|
||||
}
|
||||
|
||||
throw new ParsingException("Could not get stream count");
|
||||
}
|
||||
}
|
@ -0,0 +1,95 @@
|
||||
package org.schabi.newpipe.extractor.services.youtube.extractors;
|
||||
|
||||
import com.grack.nanojson.JsonObject;
|
||||
import org.schabi.newpipe.extractor.Image;
|
||||
import org.schabi.newpipe.extractor.channel.ChannelInfoItemExtractor;
|
||||
import org.schabi.newpipe.extractor.exceptions.ParsingException;
|
||||
import org.schabi.newpipe.extractor.utils.Parser;
|
||||
import org.schabi.newpipe.extractor.utils.Utils;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
import javax.annotation.Nullable;
|
||||
import java.util.List;
|
||||
|
||||
import static org.schabi.newpipe.extractor.services.youtube.YoutubeParsingHelper.getTextFromObject;
|
||||
import static org.schabi.newpipe.extractor.services.youtube.YoutubeParsingHelper.getImagesFromThumbnailsArray;
|
||||
import static org.schabi.newpipe.extractor.services.youtube.YoutubeParsingHelper.getUrlFromNavigationEndpoint;
|
||||
import static org.schabi.newpipe.extractor.utils.Utils.isNullOrEmpty;
|
||||
|
||||
public class YoutubeMusicArtistInfoItemExtractor implements ChannelInfoItemExtractor {
|
||||
private final JsonObject artistInfoItem;
|
||||
|
||||
public YoutubeMusicArtistInfoItemExtractor(final JsonObject artistInfoItem) {
|
||||
this.artistInfoItem = artistInfoItem;
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
@Override
|
||||
public List<Image> getThumbnails() throws ParsingException {
|
||||
try {
|
||||
return getImagesFromThumbnailsArray(
|
||||
artistInfoItem.getObject("thumbnail")
|
||||
.getObject("musicThumbnailRenderer")
|
||||
.getObject("thumbnail")
|
||||
.getArray("thumbnails"));
|
||||
} catch (final Exception e) {
|
||||
throw new ParsingException("Could not get thumbnails", e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getName() throws ParsingException {
|
||||
final String name = getTextFromObject(artistInfoItem.getArray("flexColumns")
|
||||
.getObject(0)
|
||||
.getObject("musicResponsiveListItemFlexColumnRenderer")
|
||||
.getObject("text"));
|
||||
if (!isNullOrEmpty(name)) {
|
||||
return name;
|
||||
}
|
||||
throw new ParsingException("Could not get name");
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getUrl() throws ParsingException {
|
||||
final String url = getUrlFromNavigationEndpoint(
|
||||
artistInfoItem.getObject("navigationEndpoint"));
|
||||
if (!isNullOrEmpty(url)) {
|
||||
return url;
|
||||
}
|
||||
throw new ParsingException("Could not get URL");
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getSubscriberCount() throws ParsingException {
|
||||
final String subscriberCount = getTextFromObject(artistInfoItem.getArray("flexColumns")
|
||||
.getObject(2)
|
||||
.getObject("musicResponsiveListItemFlexColumnRenderer")
|
||||
.getObject("text"));
|
||||
if (!isNullOrEmpty(subscriberCount)) {
|
||||
try {
|
||||
return Utils.mixedNumberWordToLong(subscriberCount);
|
||||
} catch (final Parser.RegexException ignored) {
|
||||
// probably subscriberCount == "No subscribers" or similar
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
throw new ParsingException("Could not get subscriber count");
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getStreamCount() {
|
||||
return -1;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isVerified() {
|
||||
// An artist on YouTube Music is always verified
|
||||
return true;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public String getDescription() {
|
||||
return null;
|
||||
}
|
||||
}
|
@ -1,9 +1,7 @@
|
||||
package org.schabi.newpipe.extractor.services.youtube.extractors;
|
||||
|
||||
import static org.schabi.newpipe.extractor.services.youtube.YoutubeParsingHelper.DISABLE_PRETTY_PRINT_PARAMETER;
|
||||
import static org.schabi.newpipe.extractor.services.youtube.YoutubeParsingHelper.fixThumbnailUrl;
|
||||
import static org.schabi.newpipe.extractor.services.youtube.YoutubeParsingHelper.getTextFromObject;
|
||||
import static org.schabi.newpipe.extractor.services.youtube.YoutubeParsingHelper.getUrlFromNavigationEndpoint;
|
||||
import static org.schabi.newpipe.extractor.services.youtube.YoutubeParsingHelper.getValidJsonResponseBody;
|
||||
import static org.schabi.newpipe.extractor.services.youtube.YoutubeParsingHelper.getYoutubeMusicHeaders;
|
||||
import static org.schabi.newpipe.extractor.services.youtube.linkHandler.YoutubeSearchQueryHandlerFactory.MUSIC_ALBUMS;
|
||||
@ -29,19 +27,16 @@ import org.schabi.newpipe.extractor.exceptions.ExtractionException;
|
||||
import org.schabi.newpipe.extractor.exceptions.ParsingException;
|
||||
import org.schabi.newpipe.extractor.exceptions.ReCaptchaException;
|
||||
import org.schabi.newpipe.extractor.linkhandler.SearchQueryHandler;
|
||||
import org.schabi.newpipe.extractor.localization.DateWrapper;
|
||||
import org.schabi.newpipe.extractor.localization.TimeAgoParser;
|
||||
import org.schabi.newpipe.extractor.search.SearchExtractor;
|
||||
import org.schabi.newpipe.extractor.services.youtube.YoutubeParsingHelper;
|
||||
import org.schabi.newpipe.extractor.utils.JsonUtils;
|
||||
import org.schabi.newpipe.extractor.utils.Parser;
|
||||
import org.schabi.newpipe.extractor.utils.Utils;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.Objects;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
import javax.annotation.Nullable;
|
||||
@ -222,7 +217,7 @@ public class YoutubeMusicSearchExtractor extends SearchExtractor {
|
||||
.object("client")
|
||||
.value("clientName", "WEB_REMIX")
|
||||
.value("clientVersion", youtubeMusicKeys[2])
|
||||
.value("hl", "en")
|
||||
.value("hl", "en-GB")
|
||||
.value("gl", getExtractorContentCountry().getCountryCode())
|
||||
.array("experimentIds").end()
|
||||
.value("experimentsToken", "")
|
||||
@ -263,316 +258,44 @@ public class YoutubeMusicSearchExtractor extends SearchExtractor {
|
||||
return new InfoItemsPage<>(collector, getNextPageFrom(continuations));
|
||||
}
|
||||
|
||||
@SuppressWarnings("MethodLength")
|
||||
private void collectMusicStreamsFrom(final MultiInfoItemsCollector collector,
|
||||
@Nonnull final JsonArray videos) {
|
||||
final TimeAgoParser timeAgoParser = getTimeAgoParser();
|
||||
final String searchType = getLinkHandler().getContentFilters().get(0);
|
||||
videos.stream()
|
||||
.filter(JsonObject.class::isInstance)
|
||||
.map(JsonObject.class::cast)
|
||||
.map(item -> item.getObject("musicResponsiveListItemRenderer", null))
|
||||
.filter(Objects::nonNull)
|
||||
.forEachOrdered(infoItem -> {
|
||||
final String displayPolicy = infoItem.getString(
|
||||
"musicItemRendererDisplayPolicy", "");
|
||||
if (displayPolicy.equals("MUSIC_ITEM_RENDERER_DISPLAY_POLICY_GREY_OUT")) {
|
||||
// No info about URL available
|
||||
return;
|
||||
}
|
||||
|
||||
for (final Object item : videos) {
|
||||
final JsonObject info = ((JsonObject) item)
|
||||
.getObject("musicResponsiveListItemRenderer", null);
|
||||
if (info != null) {
|
||||
final String displayPolicy = info.getString("musicItemRendererDisplayPolicy",
|
||||
"");
|
||||
if (displayPolicy.equals("MUSIC_ITEM_RENDERER_DISPLAY_POLICY_GREY_OUT")) {
|
||||
continue; // No info about video URL available
|
||||
}
|
||||
final JsonArray descriptionElements = infoItem.getArray("flexColumns")
|
||||
.getObject(1)
|
||||
.getObject("musicResponsiveListItemFlexColumnRenderer")
|
||||
.getObject("text")
|
||||
.getArray("runs");
|
||||
|
||||
final JsonObject flexColumnRenderer = info.getArray("flexColumns")
|
||||
.getObject(1)
|
||||
.getObject("musicResponsiveListItemFlexColumnRenderer");
|
||||
final JsonArray descriptionElements = flexColumnRenderer.getObject("text")
|
||||
.getArray("runs");
|
||||
final String searchType = getLinkHandler().getContentFilters().get(0);
|
||||
if (searchType.equals(MUSIC_SONGS) || searchType.equals(MUSIC_VIDEOS)) {
|
||||
collector.commit(new YoutubeStreamInfoItemExtractor(info, timeAgoParser) {
|
||||
@Override
|
||||
public String getUrl() throws ParsingException {
|
||||
final String id = info.getObject("playlistItemData")
|
||||
.getString("videoId");
|
||||
if (!isNullOrEmpty(id)) {
|
||||
return "https://music.youtube.com/watch?v=" + id;
|
||||
}
|
||||
throw new ParsingException("Could not get url");
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getName() throws ParsingException {
|
||||
final String name = getTextFromObject(info.getArray("flexColumns")
|
||||
.getObject(0)
|
||||
.getObject("musicResponsiveListItemFlexColumnRenderer")
|
||||
.getObject("text"));
|
||||
if (!isNullOrEmpty(name)) {
|
||||
return name;
|
||||
}
|
||||
throw new ParsingException("Could not get name");
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getDuration() throws ParsingException {
|
||||
final String duration = descriptionElements
|
||||
.getObject(descriptionElements.size() - 1)
|
||||
.getString("text");
|
||||
if (!isNullOrEmpty(duration)) {
|
||||
return YoutubeParsingHelper.parseDurationString(duration);
|
||||
}
|
||||
throw new ParsingException("Could not get duration");
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getUploaderName() throws ParsingException {
|
||||
final String name = descriptionElements.getObject(0).getString("text");
|
||||
if (!isNullOrEmpty(name)) {
|
||||
return name;
|
||||
}
|
||||
throw new ParsingException("Could not get uploader name");
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getUploaderUrl() throws ParsingException {
|
||||
if (searchType.equals(MUSIC_VIDEOS)) {
|
||||
final JsonArray items = info.getObject("menu")
|
||||
.getObject("menuRenderer")
|
||||
.getArray("items");
|
||||
for (final Object item : items) {
|
||||
final JsonObject menuNavigationItemRenderer =
|
||||
((JsonObject) item).getObject(
|
||||
"menuNavigationItemRenderer");
|
||||
if (menuNavigationItemRenderer.getObject("icon")
|
||||
.getString("iconType", "")
|
||||
.equals("ARTIST")) {
|
||||
return getUrlFromNavigationEndpoint(
|
||||
menuNavigationItemRenderer
|
||||
.getObject("navigationEndpoint"));
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
} else {
|
||||
final JsonObject navigationEndpointHolder = info
|
||||
.getArray("flexColumns")
|
||||
.getObject(1)
|
||||
.getObject("musicResponsiveListItemFlexColumnRenderer")
|
||||
.getObject("text").getArray("runs").getObject(0);
|
||||
|
||||
if (!navigationEndpointHolder.has("navigationEndpoint")) {
|
||||
return null;
|
||||
}
|
||||
|
||||
final String url = getUrlFromNavigationEndpoint(
|
||||
navigationEndpointHolder.getObject("navigationEndpoint"));
|
||||
|
||||
if (!isNullOrEmpty(url)) {
|
||||
return url;
|
||||
}
|
||||
|
||||
throw new ParsingException("Could not get uploader URL");
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getTextualUploadDate() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public DateWrapper getUploadDate() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getViewCount() throws ParsingException {
|
||||
if (searchType.equals(MUSIC_SONGS)) {
|
||||
return -1;
|
||||
}
|
||||
final String viewCount = descriptionElements
|
||||
.getObject(descriptionElements.size() - 3)
|
||||
.getString("text");
|
||||
if (!isNullOrEmpty(viewCount)) {
|
||||
try {
|
||||
return Utils.mixedNumberWordToLong(viewCount);
|
||||
} catch (final Parser.RegexException e) {
|
||||
// probably viewCount == "No views" or similar
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
throw new ParsingException("Could not get view count");
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getThumbnailUrl() throws ParsingException {
|
||||
try {
|
||||
final JsonArray thumbnails = info.getObject("thumbnail")
|
||||
.getObject("musicThumbnailRenderer")
|
||||
.getObject("thumbnail").getArray("thumbnails");
|
||||
// the last thumbnail is the one with the highest resolution
|
||||
final String url = thumbnails.getObject(thumbnails.size() - 1)
|
||||
.getString("url");
|
||||
|
||||
return fixThumbnailUrl(url);
|
||||
} catch (final Exception e) {
|
||||
throw new ParsingException("Could not get thumbnail url", e);
|
||||
}
|
||||
}
|
||||
});
|
||||
} else if (searchType.equals(MUSIC_ARTISTS)) {
|
||||
collector.commit(new YoutubeChannelInfoItemExtractor(info) {
|
||||
@Override
|
||||
public String getThumbnailUrl() throws ParsingException {
|
||||
try {
|
||||
final JsonArray thumbnails = info.getObject("thumbnail")
|
||||
.getObject("musicThumbnailRenderer")
|
||||
.getObject("thumbnail").getArray("thumbnails");
|
||||
// the last thumbnail is the one with the highest resolution
|
||||
final String url = thumbnails.getObject(thumbnails.size() - 1)
|
||||
.getString("url");
|
||||
|
||||
return fixThumbnailUrl(url);
|
||||
} catch (final Exception e) {
|
||||
throw new ParsingException("Could not get thumbnail url", e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getName() throws ParsingException {
|
||||
final String name = getTextFromObject(info.getArray("flexColumns")
|
||||
.getObject(0)
|
||||
.getObject("musicResponsiveListItemFlexColumnRenderer")
|
||||
.getObject("text"));
|
||||
if (!isNullOrEmpty(name)) {
|
||||
return name;
|
||||
}
|
||||
throw new ParsingException("Could not get name");
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getUrl() throws ParsingException {
|
||||
final String url = getUrlFromNavigationEndpoint(info
|
||||
.getObject("navigationEndpoint"));
|
||||
if (!isNullOrEmpty(url)) {
|
||||
return url;
|
||||
}
|
||||
throw new ParsingException("Could not get url");
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getSubscriberCount() throws ParsingException {
|
||||
final String subscriberCount = getTextFromObject(info
|
||||
.getArray("flexColumns").getObject(2)
|
||||
.getObject("musicResponsiveListItemFlexColumnRenderer")
|
||||
.getObject("text"));
|
||||
if (!isNullOrEmpty(subscriberCount)) {
|
||||
try {
|
||||
return Utils.mixedNumberWordToLong(subscriberCount);
|
||||
} catch (final Parser.RegexException ignored) {
|
||||
// probably subscriberCount == "No subscribers" or similar
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
throw new ParsingException("Could not get subscriber count");
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getStreamCount() {
|
||||
return -1;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getDescription() {
|
||||
return null;
|
||||
}
|
||||
});
|
||||
} else if (searchType.equals(MUSIC_ALBUMS) || searchType.equals(MUSIC_PLAYLISTS)) {
|
||||
collector.commit(new YoutubePlaylistInfoItemExtractor(info) {
|
||||
@Override
|
||||
public String getThumbnailUrl() throws ParsingException {
|
||||
try {
|
||||
final JsonArray thumbnails = info.getObject("thumbnail")
|
||||
.getObject("musicThumbnailRenderer")
|
||||
.getObject("thumbnail").getArray("thumbnails");
|
||||
// the last thumbnail is the one with the highest resolution
|
||||
final String url = thumbnails.getObject(thumbnails.size() - 1)
|
||||
.getString("url");
|
||||
|
||||
return fixThumbnailUrl(url);
|
||||
} catch (final Exception e) {
|
||||
throw new ParsingException("Could not get thumbnail url", e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getName() throws ParsingException {
|
||||
final String name = getTextFromObject(info.getArray("flexColumns")
|
||||
.getObject(0)
|
||||
.getObject("musicResponsiveListItemFlexColumnRenderer")
|
||||
.getObject("text"));
|
||||
if (!isNullOrEmpty(name)) {
|
||||
return name;
|
||||
}
|
||||
throw new ParsingException("Could not get name");
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getUrl() throws ParsingException {
|
||||
String playlistId = info.getObject("menu")
|
||||
.getObject("menuRenderer")
|
||||
.getArray("items")
|
||||
.getObject(4)
|
||||
.getObject("toggleMenuServiceItemRenderer")
|
||||
.getObject("toggledServiceEndpoint")
|
||||
.getObject("likeEndpoint")
|
||||
.getObject("target")
|
||||
.getString("playlistId");
|
||||
|
||||
if (isNullOrEmpty(playlistId)) {
|
||||
playlistId = info.getObject("overlay")
|
||||
.getObject("musicItemThumbnailOverlayRenderer")
|
||||
.getObject("content")
|
||||
.getObject("musicPlayButtonRenderer")
|
||||
.getObject("playNavigationEndpoint")
|
||||
.getObject("watchPlaylistEndpoint")
|
||||
.getString("playlistId");
|
||||
}
|
||||
if (!isNullOrEmpty(playlistId)) {
|
||||
return "https://music.youtube.com/playlist?list=" + playlistId;
|
||||
}
|
||||
throw new ParsingException("Could not get url");
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getUploaderName() throws ParsingException {
|
||||
final String name;
|
||||
if (searchType.equals(MUSIC_ALBUMS)) {
|
||||
name = descriptionElements.getObject(2).getString("text");
|
||||
} else {
|
||||
name = descriptionElements.getObject(0).getString("text");
|
||||
}
|
||||
if (!isNullOrEmpty(name)) {
|
||||
return name;
|
||||
}
|
||||
throw new ParsingException("Could not get uploader name");
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getStreamCount() throws ParsingException {
|
||||
if (searchType.equals(MUSIC_ALBUMS)) {
|
||||
return ITEM_COUNT_UNKNOWN;
|
||||
}
|
||||
final String count = descriptionElements.getObject(2)
|
||||
.getString("text");
|
||||
if (!isNullOrEmpty(count)) {
|
||||
if (count.contains("100+")) {
|
||||
return ITEM_COUNT_MORE_THAN_100;
|
||||
} else {
|
||||
return Long.parseLong(Utils.removeNonDigitCharacters(count));
|
||||
}
|
||||
}
|
||||
throw new ParsingException("Could not get count");
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
switch (searchType) {
|
||||
case MUSIC_SONGS:
|
||||
case MUSIC_VIDEOS:
|
||||
collector.commit(new YoutubeMusicSongOrVideoInfoItemExtractor(
|
||||
infoItem, descriptionElements, searchType));
|
||||
break;
|
||||
case MUSIC_ARTISTS:
|
||||
collector.commit(new YoutubeMusicArtistInfoItemExtractor(infoItem));
|
||||
break;
|
||||
case MUSIC_ALBUMS:
|
||||
case MUSIC_PLAYLISTS:
|
||||
collector.commit(new YoutubeMusicAlbumOrPlaylistInfoItemExtractor(
|
||||
infoItem, descriptionElements, searchType));
|
||||
break;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Nullable
|
||||
|
@ -0,0 +1,177 @@
|
||||
package org.schabi.newpipe.extractor.services.youtube.extractors;
|
||||
|
||||
import com.grack.nanojson.JsonArray;
|
||||
import com.grack.nanojson.JsonObject;
|
||||
import org.schabi.newpipe.extractor.Image;
|
||||
import org.schabi.newpipe.extractor.exceptions.ParsingException;
|
||||
import org.schabi.newpipe.extractor.localization.DateWrapper;
|
||||
import org.schabi.newpipe.extractor.services.youtube.YoutubeParsingHelper;
|
||||
import org.schabi.newpipe.extractor.stream.StreamInfoItemExtractor;
|
||||
import org.schabi.newpipe.extractor.stream.StreamType;
|
||||
import org.schabi.newpipe.extractor.utils.Parser;
|
||||
import org.schabi.newpipe.extractor.utils.Utils;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
import java.util.List;
|
||||
|
||||
import static org.schabi.newpipe.extractor.services.youtube.YoutubeParsingHelper.getTextFromObject;
|
||||
import static org.schabi.newpipe.extractor.services.youtube.YoutubeParsingHelper.getImagesFromThumbnailsArray;
|
||||
import static org.schabi.newpipe.extractor.services.youtube.YoutubeParsingHelper.getUrlFromNavigationEndpoint;
|
||||
import static org.schabi.newpipe.extractor.services.youtube.linkHandler.YoutubeSearchQueryHandlerFactory.MUSIC_SONGS;
|
||||
import static org.schabi.newpipe.extractor.services.youtube.linkHandler.YoutubeSearchQueryHandlerFactory.MUSIC_VIDEOS;
|
||||
import static org.schabi.newpipe.extractor.utils.Utils.isNullOrEmpty;
|
||||
|
||||
public class YoutubeMusicSongOrVideoInfoItemExtractor implements StreamInfoItemExtractor {
|
||||
private final JsonObject songOrVideoInfoItem;
|
||||
private final JsonArray descriptionElements;
|
||||
private final String searchType;
|
||||
|
||||
public YoutubeMusicSongOrVideoInfoItemExtractor(final JsonObject songOrVideoInfoItem,
|
||||
final JsonArray descriptionElements,
|
||||
final String searchType) {
|
||||
this.songOrVideoInfoItem = songOrVideoInfoItem;
|
||||
this.descriptionElements = descriptionElements;
|
||||
this.searchType = searchType;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public String getUrl() throws ParsingException {
|
||||
final String id = songOrVideoInfoItem.getObject("playlistItemData").getString("videoId");
|
||||
if (!isNullOrEmpty(id)) {
|
||||
return "https://music.youtube.com/watch?v=" + id;
|
||||
}
|
||||
throw new ParsingException("Could not get URL");
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getName() throws ParsingException {
|
||||
final String name = getTextFromObject(songOrVideoInfoItem.getArray("flexColumns")
|
||||
.getObject(0)
|
||||
.getObject("musicResponsiveListItemFlexColumnRenderer")
|
||||
.getObject("text"));
|
||||
if (!isNullOrEmpty(name)) {
|
||||
return name;
|
||||
}
|
||||
throw new ParsingException("Could not get name");
|
||||
}
|
||||
|
||||
@Override
|
||||
public StreamType getStreamType() {
|
||||
return StreamType.VIDEO_STREAM;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isAd() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getDuration() throws ParsingException {
|
||||
final String duration = descriptionElements.getObject(descriptionElements.size() - 1)
|
||||
.getString("text");
|
||||
if (!isNullOrEmpty(duration)) {
|
||||
return YoutubeParsingHelper.parseDurationString(duration);
|
||||
}
|
||||
throw new ParsingException("Could not get duration");
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getUploaderName() throws ParsingException {
|
||||
final String name = descriptionElements.getObject(0).getString("text");
|
||||
if (!isNullOrEmpty(name)) {
|
||||
return name;
|
||||
}
|
||||
throw new ParsingException("Could not get uploader name");
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getUploaderUrl() throws ParsingException {
|
||||
if (searchType.equals(MUSIC_VIDEOS)) {
|
||||
final JsonArray items = songOrVideoInfoItem.getObject("menu")
|
||||
.getObject("menuRenderer")
|
||||
.getArray("items");
|
||||
for (final Object item : items) {
|
||||
final JsonObject menuNavigationItemRenderer =
|
||||
((JsonObject) item).getObject("menuNavigationItemRenderer");
|
||||
if (menuNavigationItemRenderer.getObject("icon")
|
||||
.getString("iconType", "")
|
||||
.equals("ARTIST")) {
|
||||
return getUrlFromNavigationEndpoint(
|
||||
menuNavigationItemRenderer.getObject("navigationEndpoint"));
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
} else {
|
||||
final JsonObject navigationEndpointHolder = songOrVideoInfoItem.getArray("flexColumns")
|
||||
.getObject(1)
|
||||
.getObject("musicResponsiveListItemFlexColumnRenderer")
|
||||
.getObject("text")
|
||||
.getArray("runs")
|
||||
.getObject(0);
|
||||
|
||||
if (!navigationEndpointHolder.has("navigationEndpoint")) {
|
||||
return null;
|
||||
}
|
||||
|
||||
final String url = getUrlFromNavigationEndpoint(
|
||||
navigationEndpointHolder.getObject("navigationEndpoint"));
|
||||
|
||||
if (!isNullOrEmpty(url)) {
|
||||
return url;
|
||||
}
|
||||
|
||||
throw new ParsingException("Could not get uploader URL");
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isUploaderVerified() {
|
||||
// We don't have the ability to know this information on YouTube Music
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getTextualUploadDate() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public DateWrapper getUploadDate() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getViewCount() throws ParsingException {
|
||||
if (searchType.equals(MUSIC_SONGS)) {
|
||||
return -1;
|
||||
}
|
||||
final String viewCount = descriptionElements
|
||||
.getObject(descriptionElements.size() - 3)
|
||||
.getString("text");
|
||||
if (!isNullOrEmpty(viewCount)) {
|
||||
try {
|
||||
return Utils.mixedNumberWordToLong(viewCount);
|
||||
} catch (final Parser.RegexException e) {
|
||||
// probably viewCount == "No views" or similar
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
throw new ParsingException("Could not get view count");
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
@Override
|
||||
public List<Image> getThumbnails() throws ParsingException {
|
||||
try {
|
||||
return getImagesFromThumbnailsArray(
|
||||
songOrVideoInfoItem.getObject("thumbnail")
|
||||
.getObject("musicThumbnailRenderer")
|
||||
.getObject("thumbnail")
|
||||
.getArray("thumbnails"));
|
||||
} catch (final Exception e) {
|
||||
throw new ParsingException("Could not get thumbnails", e);
|
||||
}
|
||||
}
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user