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 79afd5542..ff864deab 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 @@ -374,12 +374,37 @@ public class YoutubePlaylistExtractor extends PlaylistExtractor { final JsonObject lastElement = contents.getObject(contents.size() - 1); if (lastElement.has("continuationItemRenderer")) { - final String continuation = lastElement + final JsonObject continuationEndpoint = lastElement .getObject("continuationItemRenderer") - .getObject("continuationEndpoint") - .getObject("continuationCommand") + .getObject("continuationEndpoint"); + + final JsonObject continuationObject; + if (continuationEndpoint.has("commandExecutorCommand")) { + // This structure is only used at the time this code is written in initial playlist + // responses. continuationItemRenderer objects return multiple commands: one + // containing the continuation we need and one a playlistVotingRefreshPopupCommand + continuationObject = continuationEndpoint.getObject("commandExecutorCommand") + .getArray("commands") + .stream() + .filter(JsonObject.class::isInstance) + .map(JsonObject.class::cast) + .filter(command -> command.has("continuationCommand")) + .findFirst() + .orElse(new JsonObject()); + } else { + // At the time this code is written, this "classic" continuation structure is only + // returned in browse responses of continuation requests + continuationObject = continuationEndpoint; + } + + final String continuation = continuationObject.getObject("continuationCommand") .getString("token"); + if (isNullOrEmpty(continuation)) { + // Invalid continuation or no continuation found + return null; + } + final byte[] body = JsonWriter.string(prepareDesktopJsonBuilder( getExtractorLocalization(), getExtractorContentCountry()) .value("continuation", continuation) @@ -387,9 +412,9 @@ public class YoutubePlaylistExtractor extends PlaylistExtractor { .getBytes(StandardCharsets.UTF_8); return new Page(YOUTUBEI_V1_URL + "browse?" + DISABLE_PRETTY_PRINT_PARAMETER, body); - } else { - return null; } + + return null; } private void collectStreamsFrom(@Nonnull final StreamInfoItemsCollector collector,