diff --git a/extractor/src/main/java/org/schabi/newpipe/extractor/services/youtube/extractors/YoutubePlaylistExtractor.java b/extractor/src/main/java/org/schabi/newpipe/extractor/services/youtube/extractors/YoutubePlaylistExtractor.java index 10ae6bc4..d564c89b 100644 --- a/extractor/src/main/java/org/schabi/newpipe/extractor/services/youtube/extractors/YoutubePlaylistExtractor.java +++ b/extractor/src/main/java/org/schabi/newpipe/extractor/services/youtube/extractors/YoutubePlaylistExtractor.java @@ -272,6 +272,19 @@ public InfoItemsPage getInitialPage() throws IOException, Extrac collectStreamsFrom(collector, videosArray); nextPage = getNextPageFrom(videosArray); + } else { + for (final Object content : contents) { + if (!(content instanceof JsonObject)) { + continue; + } + final JsonArray itemContents = ((JsonObject) content) + .getObject("itemSectionRenderer") + .getArray("contents"); + collectStreamsFrom(collector, itemContents); + if (nextPage == null) { + nextPage = getNextPageFrom(itemContents); + } + } } // Handle richGridRenderer for Shorts playlists @@ -416,7 +429,15 @@ private void collectStreamsFrom(@Nonnull final StreamInfoItemsCollector collecto return extractor; }) .forEachOrdered(collector::commit); - + + videos.stream() + .filter(JsonObject.class::isInstance) + .map(JsonObject.class::cast) + .filter(video -> video.has("lockupViewModel")) + .map(video -> video.getObject("lockupViewModel")) + .forEachOrdered(lockup -> commitLockupStreamIfSupported( + collector, lockup, timeAgoParser, fallbackName, fallbackUrl)); + // Handle Shorts in richItemRenderer format (for continuation responses) videos.stream() .filter(JsonObject.class::isInstance) @@ -428,6 +449,46 @@ private void collectStreamsFrom(@Nonnull final StreamInfoItemsCollector collecto collector.commit(new YoutubeShortsInfoItemExtractor( content.getObject("shortsLockupViewModel"))); }); + + videos.stream() + .filter(JsonObject.class::isInstance) + .map(JsonObject.class::cast) + .filter(video -> video.has("richItemRenderer")) + .map(video -> video.getObject("richItemRenderer").getObject("content")) + .filter(content -> content.has("lockupViewModel")) + .map(content -> content.getObject("lockupViewModel")) + .forEachOrdered(lockup -> commitLockupStreamIfSupported( + collector, lockup, timeAgoParser, fallbackName, fallbackUrl)); + } + + private void commitLockupStreamIfSupported(@Nonnull final StreamInfoItemsCollector collector, + @Nonnull final JsonObject lockupViewModel, + @Nonnull final TimeAgoParser timeAgoParser, + @Nullable final String fallbackName, + @Nullable final String fallbackUrl) { + final String contentType = lockupViewModel.getString("contentType"); + if (!"LOCKUP_CONTENT_TYPE_VIDEO".equals(contentType) + && !"LOCKUP_CONTENT_TYPE_EPISODE".equals(contentType)) { + return; + } + if (isNullOrEmpty(lockupViewModel.getObject("metadata") + .getObject("lockupMetadataViewModel") + .getObject("title") + .getString("content"))) { + return; + } + + collector.commit(new YoutubeLockupStreamInfoItemExtractor(lockupViewModel, timeAgoParser) { + @Override + public String getUploaderName() throws ParsingException { + return fallbackName == null ? super.getUploaderName() : fallbackName; + } + + @Override + public String getUploaderUrl() throws ParsingException { + return fallbackUrl == null ? super.getUploaderUrl() : fallbackUrl; + } + }); } @Nonnull