From f618c09e01540e731adfd72e74e6eb4becd584f6 Mon Sep 17 00:00:00 2001 From: Priveetee Date: Thu, 25 Jun 2026 16:34:16 +0200 Subject: [PATCH] fix(youtube): tolerate missing fields in channel-search playlist items --- ...YoutubeMixOrPlaylistInfoItemExtractor.java | 36 ++++++++++++++++--- 1 file changed, 31 insertions(+), 5 deletions(-) diff --git a/extractor/src/main/java/org/schabi/newpipe/extractor/services/youtube/extractors/YoutubeMixOrPlaylistInfoItemExtractor.java b/extractor/src/main/java/org/schabi/newpipe/extractor/services/youtube/extractors/YoutubeMixOrPlaylistInfoItemExtractor.java index 4ed428872..fc0daf72b 100644 --- a/extractor/src/main/java/org/schabi/newpipe/extractor/services/youtube/extractors/YoutubeMixOrPlaylistInfoItemExtractor.java +++ b/extractor/src/main/java/org/schabi/newpipe/extractor/services/youtube/extractors/YoutubeMixOrPlaylistInfoItemExtractor.java @@ -12,6 +12,7 @@ import org.schabi.newpipe.extractor.playlist.PlaylistInfo; import org.schabi.newpipe.extractor.playlist.PlaylistInfoItemExtractor; import org.schabi.newpipe.extractor.services.youtube.YoutubeParsingHelper; +import org.schabi.newpipe.extractor.services.youtube.linkHandler.YoutubePlaylistLinkHandlerFactory; import javax.annotation.Nonnull; @@ -33,16 +34,38 @@ public String getName() throws ParsingException { @Override public String getUrl() throws ParsingException { + // Mixes and radio renderers expose a ready-to-share "shareUrl"; use it when present. final String url = mixInfoItem.getString("shareUrl"); - if (isNullOrEmpty(url)) { - throw new ParsingException("Could not get url"); + if (!isNullOrEmpty(url)) { + return url; } - return url; + // Regular channel playlists surfaced in channel tabs (e.g. channel-search results) + // carry no "shareUrl" but have a "playlistId". Rebuild the canonical playlist URL + // from it instead of throwing "Could not get url", which used to abort the whole + // item and surface a spurious error snackbar to the user while the other results + // kept loading fine (#2546). + final String playlistId = mixInfoItem.getString("playlistId"); + if (!isNullOrEmpty(playlistId)) { + try { + return YoutubePlaylistLinkHandlerFactory.getInstance().getUrl(playlistId); + } catch (final Exception e) { + throw new ParsingException("Could not get url", e); + } + } + throw new ParsingException("Could not get url"); } @Override public String getThumbnailUrl() throws ParsingException { - return getThumbnailUrlFromInfoItem(mixInfoItem); + // A thumbnail is optional: some incomplete playlist/mix items surfaced in channel + // tabs (e.g. channel-search results) carry no usable thumbnail. Return null rather + // than throwing, so the item still shows up (with a placeholder) instead of being + // turned into a user-facing error snackbar (#2546). + try { + return getThumbnailUrlFromInfoItem(mixInfoItem); + } catch (final ParsingException e) { + return null; + } } @Override @@ -56,7 +79,10 @@ public long getStreamCount() throws ParsingException { final String countString = YoutubeParsingHelper.getTextFromObject( mixInfoItem.getObject("videoCountShortText")); if (countString == null) { - throw new ParsingException("Could not extract item count for playlist/mix info item"); + // The item count is optional too: some incomplete playlist/mix items have no + // "videoCountShortText". Report "unknown" rather than throwing, so the item is + // still committed instead of bubbling up a spurious error (#2546). + return ListExtractor.ITEM_COUNT_UNKNOWN; } try {