[YouTube] Extract mixes from streams related items
This commit is contained in:
parent
638da1756c
commit
50db871d89
|
@ -249,6 +249,17 @@ public class YoutubeParsingHelper {
|
|||
return playlistId.startsWith("RD") && !isYoutubeMusicMixId(playlistId);
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if the given playlist id is a YouTube My Mix (auto-generated playlist)
|
||||
* Ids from a YouTube My Mix start with "RDMM"
|
||||
*
|
||||
* @param playlistId the playlist id
|
||||
* @return Whether given id belongs to a YouTube My Mix
|
||||
*/
|
||||
public static boolean isYoutubeMyMixId(@Nonnull final String playlistId) {
|
||||
return playlistId.startsWith("RDMM");
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if the given playlist id is a YouTube Music Mix (auto-generated playlist)
|
||||
* Ids from a YouTube Music Mix start with "RDAMVM" or "RDCLAK"
|
||||
|
@ -278,7 +289,7 @@ public class YoutubeParsingHelper {
|
|||
@Nonnull
|
||||
public static String extractVideoIdFromMixId(@Nonnull final String playlistId)
|
||||
throws ParsingException {
|
||||
if (playlistId.startsWith("RDMM")) { // My Mix
|
||||
if (isYoutubeMyMixId(playlistId)) { // My Mix
|
||||
return playlistId.substring(4);
|
||||
|
||||
} else if (isYoutubeMusicMixId(playlistId)) { // starts with "RDAMVM" or "RDCLAK"
|
||||
|
@ -705,6 +716,17 @@ public class YoutubeParsingHelper {
|
|||
return thumbnailUrl;
|
||||
}
|
||||
|
||||
public static String getThumbnailUrlFromInfoItem(final JsonObject infoItem)
|
||||
throws ParsingException {
|
||||
// TODO: Don't simply get the first item, but look at all thumbnails and their resolution
|
||||
try {
|
||||
return fixThumbnailUrl(infoItem.getObject("thumbnail").getArray("thumbnails")
|
||||
.getObject(0).getString("url"));
|
||||
} catch (final Exception e) {
|
||||
throw new ParsingException("Could not get thumbnail url", e);
|
||||
}
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
public static String getValidJsonResponseBody(@Nonnull final Response response)
|
||||
throws ParsingException, MalformedURLException {
|
||||
|
|
|
@ -234,9 +234,9 @@ public class YoutubeMixPlaylistExtractor extends PlaylistExtractor {
|
|||
@Nonnull
|
||||
private String getThumbnailUrlFromPlaylistId(@Nonnull final String playlistId) throws ParsingException {
|
||||
final String videoId;
|
||||
if (playlistId.startsWith("RDMM")) {
|
||||
if (isYoutubeMyMixId(playlistId)) {
|
||||
videoId = playlistId.substring(4);
|
||||
} else if (playlistId.startsWith("RDCMUC")) {
|
||||
} else if (isYoutubeChannelMixId(playlistId)) {
|
||||
throw new ParsingException("This playlist is a channel mix");
|
||||
} else {
|
||||
videoId = playlistId.substring(2);
|
||||
|
|
|
@ -0,0 +1,85 @@
|
|||
package org.schabi.newpipe.extractor.services.youtube.extractors;
|
||||
|
||||
import static org.schabi.newpipe.extractor.services.youtube.YoutubeParsingHelper.getTextFromObject;
|
||||
import static org.schabi.newpipe.extractor.services.youtube.YoutubeParsingHelper.getThumbnailUrlFromInfoItem;
|
||||
import static org.schabi.newpipe.extractor.services.youtube.YoutubeParsingHelper.isYoutubeChannelMixId;
|
||||
import static org.schabi.newpipe.extractor.services.youtube.YoutubeParsingHelper.isYoutubeMusicMixId;
|
||||
import static org.schabi.newpipe.extractor.utils.Utils.isNullOrEmpty;
|
||||
|
||||
import com.grack.nanojson.JsonObject;
|
||||
|
||||
import org.schabi.newpipe.extractor.ListExtractor;
|
||||
import org.schabi.newpipe.extractor.exceptions.ParsingException;
|
||||
import org.schabi.newpipe.extractor.playlist.PlaylistInfo;
|
||||
import org.schabi.newpipe.extractor.playlist.PlaylistInfoItemExtractor;
|
||||
import org.schabi.newpipe.extractor.utils.Utils;
|
||||
|
||||
import java.net.MalformedURLException;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
|
||||
public class YoutubeMixPlaylistInfoItemExtractor implements PlaylistInfoItemExtractor {
|
||||
private final JsonObject mixInfoItem;
|
||||
|
||||
public YoutubeMixPlaylistInfoItemExtractor(final JsonObject mixInfoItem) {
|
||||
this.mixInfoItem = mixInfoItem;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getName() throws ParsingException {
|
||||
final String name = getTextFromObject(mixInfoItem.getObject("title"));
|
||||
if (isNullOrEmpty(name)) {
|
||||
throw new ParsingException("Could not get name");
|
||||
}
|
||||
return name;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getUrl() throws ParsingException {
|
||||
final String url = mixInfoItem.getString("shareUrl");
|
||||
if (isNullOrEmpty(url)) {
|
||||
throw new ParsingException("Could not get url");
|
||||
}
|
||||
return url;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getThumbnailUrl() throws ParsingException {
|
||||
return getThumbnailUrlFromInfoItem(mixInfoItem);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getUploaderName() throws ParsingException {
|
||||
// YouTube mixes are auto-generated by YouTube
|
||||
return "YouTube";
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getStreamCount() throws ParsingException {
|
||||
// Auto-generated playlists always start with 25 videos and are endless
|
||||
return ListExtractor.ITEM_COUNT_INFINITE;
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
@Override
|
||||
public PlaylistInfo.PlaylistType getPlaylistType() throws ParsingException {
|
||||
try {
|
||||
final String url = getUrl();
|
||||
final String mixPlaylistId = Utils.getQueryValue(Utils.stringToURL(url), "list");
|
||||
if (isNullOrEmpty(mixPlaylistId)) {
|
||||
throw new ParsingException("Mix playlist id was null or empty for url " + url);
|
||||
}
|
||||
|
||||
if (isYoutubeMusicMixId(mixPlaylistId)) {
|
||||
return PlaylistInfo.PlaylistType.MIX_MUSIC;
|
||||
} else if (isYoutubeChannelMixId(mixPlaylistId)) {
|
||||
return PlaylistInfo.PlaylistType.MIX_CHANNEL;
|
||||
} else {
|
||||
// either a normal mix based on a stream, or a "my mix" (still based on a stream)
|
||||
return PlaylistInfo.PlaylistType.MIX_STREAM;
|
||||
}
|
||||
} catch (final MalformedURLException e) {
|
||||
throw new ParsingException("Could not obtain mix playlist id", e);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -10,6 +10,7 @@ import org.mozilla.javascript.ScriptableObject;
|
|||
|
||||
import org.schabi.newpipe.extractor.MediaFormat;
|
||||
import org.schabi.newpipe.extractor.MetaInfo;
|
||||
import org.schabi.newpipe.extractor.MultiInfoItemsCollector;
|
||||
import org.schabi.newpipe.extractor.StreamingService;
|
||||
import org.schabi.newpipe.extractor.downloader.Downloader;
|
||||
import org.schabi.newpipe.extractor.exceptions.AgeRestrictedContentException;
|
||||
|
@ -618,7 +619,7 @@ public class YoutubeStreamExtractor extends StreamExtractor {
|
|||
|
||||
@Nullable
|
||||
@Override
|
||||
public StreamInfoItemsCollector getRelatedItems() throws ExtractionException {
|
||||
public MultiInfoItemsCollector getRelatedItems() throws ExtractionException {
|
||||
assertPageFetched();
|
||||
|
||||
if (getAgeLimit() != NO_AGE_LIMIT) {
|
||||
|
@ -626,8 +627,7 @@ public class YoutubeStreamExtractor extends StreamExtractor {
|
|||
}
|
||||
|
||||
try {
|
||||
final StreamInfoItemsCollector collector = new StreamInfoItemsCollector(
|
||||
getServiceId());
|
||||
final MultiInfoItemsCollector collector = new MultiInfoItemsCollector(getServiceId());
|
||||
|
||||
final JsonArray results = nextResponse.getObject("contents")
|
||||
.getObject("twoColumnWatchNextResults").getObject("secondaryResults")
|
||||
|
@ -635,10 +635,14 @@ public class YoutubeStreamExtractor extends StreamExtractor {
|
|||
|
||||
final TimeAgoParser timeAgoParser = getTimeAgoParser();
|
||||
|
||||
for (final Object ul : results) {
|
||||
if (((JsonObject) ul).has("compactVideoRenderer")) {
|
||||
collector.commit(new YoutubeStreamInfoItemExtractor(((JsonObject) ul)
|
||||
.getObject("compactVideoRenderer"), timeAgoParser));
|
||||
for (final Object resultObject : results) {
|
||||
final JsonObject result = (JsonObject) resultObject;
|
||||
if (result.has("compactVideoRenderer")) {
|
||||
collector.commit(new YoutubeStreamInfoItemExtractor(
|
||||
result.getObject("compactVideoRenderer"), timeAgoParser));
|
||||
} else if (result.has("compactRadioRenderer")) {
|
||||
collector.commit(new YoutubeMixPlaylistInfoItemExtractor(
|
||||
result.getObject("compactRadioRenderer")));
|
||||
}
|
||||
}
|
||||
return collector;
|
||||
|
|
|
@ -252,15 +252,7 @@ public class YoutubeStreamInfoItemExtractor implements StreamInfoItemExtractor {
|
|||
|
||||
@Override
|
||||
public String getThumbnailUrl() throws ParsingException {
|
||||
try {
|
||||
// TODO: Don't simply get the first item, but look at all thumbnails and their resolution
|
||||
String url = videoInfo.getObject("thumbnail").getArray("thumbnails")
|
||||
.getObject(0).getString("url");
|
||||
|
||||
return fixThumbnailUrl(url);
|
||||
} catch (Exception e) {
|
||||
throw new ParsingException("Could not get thumbnail url", e);
|
||||
}
|
||||
return getThumbnailUrlFromInfoItem(videoInfo);
|
||||
}
|
||||
|
||||
private boolean isPremium() {
|
||||
|
|
Loading…
Reference in New Issue