From 1b6907ec0366225044101ee9f92a61fbf4f3d43d Mon Sep 17 00:00:00 2001 From: FineFindus Date: Thu, 13 Feb 2025 18:14:55 +0100 Subject: [PATCH] [Youtube] Mark members-only videos YouTube inserts members-only videos (i.e., videos that require channel membership to watch) into the Videos tab. Because the extractor unable to distinguish between them and "normal" videos, they may appear in subscriptions feeds. This enables the extractor to check if videos require membership, allowing clients to filter them. Ref: https://github.com/TeamNewPipe/NewPipe/issues/12040 Ref: https://github.com/TeamNewPipe/NewPipe/issues/12011 --- .../extractors/YoutubeStreamInfoItemExtractor.java | 14 ++++++++++++++ .../newpipe/extractor/stream/StreamExtractor.java | 10 ++++++++++ .../newpipe/extractor/stream/StreamInfo.java | 14 ++++++++++++++ .../newpipe/extractor/stream/StreamInfoItem.java | 9 +++++++++ .../extractor/stream/StreamInfoItemExtractor.java | 10 ++++++++++ .../extractor/stream/StreamInfoItemsCollector.java | 5 +++++ 6 files changed, 62 insertions(+) diff --git a/extractor/src/main/java/org/schabi/newpipe/extractor/services/youtube/extractors/YoutubeStreamInfoItemExtractor.java b/extractor/src/main/java/org/schabi/newpipe/extractor/services/youtube/extractors/YoutubeStreamInfoItemExtractor.java index d00cee987..1192cf54c 100644 --- a/extractor/src/main/java/org/schabi/newpipe/extractor/services/youtube/extractors/YoutubeStreamInfoItemExtractor.java +++ b/extractor/src/main/java/org/schabi/newpipe/extractor/services/youtube/extractors/YoutubeStreamInfoItemExtractor.java @@ -470,4 +470,18 @@ public class YoutubeStreamInfoItemExtractor implements StreamInfoItemExtractor { throw new ParsingException("Could not determine if this is short-form content", e); } } + + @Nonnull + @Override + public boolean requiresMembership() throws ParsingException { + final JsonArray badges = videoInfo.getArray("badges"); + for (final Object badge : badges) { + if (((JsonObject) badge).getObject("metadataBadgeRenderer") + .getString("style", "").equals("BADGE_STYLE_TYPE_MEMBERS_ONLY")) { + return true; + } + } + return false; + } + } diff --git a/extractor/src/main/java/org/schabi/newpipe/extractor/stream/StreamExtractor.java b/extractor/src/main/java/org/schabi/newpipe/extractor/stream/StreamExtractor.java index 170291740..e0c2a97f2 100644 --- a/extractor/src/main/java/org/schabi/newpipe/extractor/stream/StreamExtractor.java +++ b/extractor/src/main/java/org/schabi/newpipe/extractor/stream/StreamExtractor.java @@ -581,6 +581,16 @@ public abstract class StreamExtractor extends Extractor { return false; } + /** + * Whether the stream is only available to channel members. + * + * @return whether the stream is only available to channel members. + * @throws ParsingException if there is an error in the extraction + */ + public boolean requiresMembership() throws ParsingException { + return false; + } + public enum Privacy { PUBLIC, UNLISTED, diff --git a/extractor/src/main/java/org/schabi/newpipe/extractor/stream/StreamInfo.java b/extractor/src/main/java/org/schabi/newpipe/extractor/stream/StreamInfo.java index b54c69afc..4292bce49 100644 --- a/extractor/src/main/java/org/schabi/newpipe/extractor/stream/StreamInfo.java +++ b/extractor/src/main/java/org/schabi/newpipe/extractor/stream/StreamInfo.java @@ -330,6 +330,11 @@ public class StreamInfo extends Info { } catch (final Exception e) { streamInfo.addError(e); } + try { + streamInfo.setRequiresMembership(extractor.requiresMembership()); + } catch (final Exception e) { + streamInfo.addError(e); + } streamInfo.setRelatedItems(ExtractorHelper.getRelatedItemsOrLogError(streamInfo, extractor)); @@ -381,6 +386,7 @@ public class StreamInfo extends Info { private List streamSegments = List.of(); private List metaInfo = List.of(); private boolean shortFormContent = false; + private boolean membersOnly = false; /** * Preview frames, e.g. for the storyboard / seekbar thumbnail preview @@ -727,4 +733,12 @@ public class StreamInfo extends Info { public void setShortFormContent(final boolean isShortFormContent) { this.shortFormContent = isShortFormContent; } + + public boolean requiresMembership() { + return membersOnly; + } + + public void setRequiresMembership(final boolean requiresMembership) { + this.membersOnly = requiresMembership; + } } diff --git a/extractor/src/main/java/org/schabi/newpipe/extractor/stream/StreamInfoItem.java b/extractor/src/main/java/org/schabi/newpipe/extractor/stream/StreamInfoItem.java index 6996111ff..c613af3f4 100644 --- a/extractor/src/main/java/org/schabi/newpipe/extractor/stream/StreamInfoItem.java +++ b/extractor/src/main/java/org/schabi/newpipe/extractor/stream/StreamInfoItem.java @@ -47,6 +47,7 @@ public class StreamInfoItem extends InfoItem { private List uploaderAvatars = List.of(); private boolean uploaderVerified = false; private boolean shortFormContent = false; + private boolean requiresMembership = false; public StreamInfoItem(final int serviceId, final String url, @@ -143,6 +144,14 @@ public class StreamInfoItem extends InfoItem { this.shortFormContent = shortFormContent; } + public boolean requiresMembership() { + return requiresMembership; + } + + public void setRequiresMembership(final boolean requiresMembership) { + this.requiresMembership = requiresMembership; + } + @Override public String toString() { return "StreamInfoItem{" diff --git a/extractor/src/main/java/org/schabi/newpipe/extractor/stream/StreamInfoItemExtractor.java b/extractor/src/main/java/org/schabi/newpipe/extractor/stream/StreamInfoItemExtractor.java index c59b0aab3..7573dbdea 100644 --- a/extractor/src/main/java/org/schabi/newpipe/extractor/stream/StreamInfoItemExtractor.java +++ b/extractor/src/main/java/org/schabi/newpipe/extractor/stream/StreamInfoItemExtractor.java @@ -147,4 +147,14 @@ public interface StreamInfoItemExtractor extends InfoItemExtractor { default boolean isShortFormContent() throws ParsingException { return false; } + + /** + * Whether the stream is only available to channel members. + * + * @return whether the stream is only available to channel members. + * @throws ParsingException if there is an error in the extraction + */ + default boolean requiresMembership() throws ParsingException { + return false; + } } diff --git a/extractor/src/main/java/org/schabi/newpipe/extractor/stream/StreamInfoItemsCollector.java b/extractor/src/main/java/org/schabi/newpipe/extractor/stream/StreamInfoItemsCollector.java index 19cd2baa8..9af3cb03a 100644 --- a/extractor/src/main/java/org/schabi/newpipe/extractor/stream/StreamInfoItemsCollector.java +++ b/extractor/src/main/java/org/schabi/newpipe/extractor/stream/StreamInfoItemsCollector.java @@ -103,6 +103,11 @@ public class StreamInfoItemsCollector } catch (final Exception e) { addError(e); } + try { + resultItem.setRequiresMembership(extractor.requiresMembership()); + } catch (final Exception e) { + addError(e); + } return resultItem; }