From 5e7818cd84f04d6c84c8965b16d73c91eea011d8 Mon Sep 17 00:00:00 2001 From: David Asunmo <22662897+davidasunmo@users.noreply.github.com.> Date: Thu, 22 May 2025 04:46:38 +0100 Subject: [PATCH 01/13] Fix typo --- .../java/org/schabi/newpipe/extractor/utils/ImageSuffix.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/extractor/src/main/java/org/schabi/newpipe/extractor/utils/ImageSuffix.java b/extractor/src/main/java/org/schabi/newpipe/extractor/utils/ImageSuffix.java index 4d8a141917..7332c75c08 100644 --- a/extractor/src/main/java/org/schabi/newpipe/extractor/utils/ImageSuffix.java +++ b/extractor/src/main/java/org/schabi/newpipe/extractor/utils/ImageSuffix.java @@ -14,7 +14,7 @@ *

* This class is used to construct {@link org.schabi.newpipe.extractor.Image Image} * instances from a single base URL/path, in order to get all or most image resolutions provided, - * depending of the service and the resolutions provided. + * depending on the service and the resolutions provided. *

* *

From d9af459ecfff8802a0e6ec7061202123e36175d4 Mon Sep 17 00:00:00 2001 From: David Asunmo <22662897+davidasunmo@users.noreply.github.com.> Date: Mon, 26 May 2025 04:39:10 +0100 Subject: [PATCH 02/13] Remove redundant override --- .../services/soundcloud/SoundcloudStreamExtractorTest.java | 1 - 1 file changed, 1 deletion(-) diff --git a/extractor/src/test/java/org/schabi/newpipe/extractor/services/soundcloud/SoundcloudStreamExtractorTest.java b/extractor/src/test/java/org/schabi/newpipe/extractor/services/soundcloud/SoundcloudStreamExtractorTest.java index d33ea37443..d7fab32d1f 100644 --- a/extractor/src/test/java/org/schabi/newpipe/extractor/services/soundcloud/SoundcloudStreamExtractorTest.java +++ b/extractor/src/test/java/org/schabi/newpipe/extractor/services/soundcloud/SoundcloudStreamExtractorTest.java @@ -132,7 +132,6 @@ public void testRelatedItems() throws Exception { @Override public long expectedDislikeCountAtLeast() { return -1; } @Override public boolean expectedHasAudioStreams() { return false; } @Override public boolean expectedHasVideoStreams() { return false; } - @Override public boolean expectedHasRelatedItems() { return true; } @Override public boolean expectedHasSubtitles() { return false; } @Override public boolean expectedHasFrames() { return false; } @Override public int expectedStreamSegmentsCount() { return 0; } From 5fcd97104c5116d56a578688ddfb082d7c92405f Mon Sep 17 00:00:00 2001 From: David Asunmo <22662897+davidasunmo@users.noreply.github.com.> Date: Wed, 4 Jun 2025 05:06:13 +0100 Subject: [PATCH 03/13] [SoundCloud] SoundCloudStreamExtractor.getTimeStamp return 0 if no timestamp in url --- .../soundcloud/extractors/SoundcloudStreamExtractor.java | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/extractor/src/main/java/org/schabi/newpipe/extractor/services/soundcloud/extractors/SoundcloudStreamExtractor.java b/extractor/src/main/java/org/schabi/newpipe/extractor/services/soundcloud/extractors/SoundcloudStreamExtractor.java index 67cd533e9c..595862bde7 100644 --- a/extractor/src/main/java/org/schabi/newpipe/extractor/services/soundcloud/extractors/SoundcloudStreamExtractor.java +++ b/extractor/src/main/java/org/schabi/newpipe/extractor/services/soundcloud/extractors/SoundcloudStreamExtractor.java @@ -121,7 +121,8 @@ public long getLength() { @Override public long getTimeStamp() throws ParsingException { - return getTimestampSeconds("(#t=\\d{0,3}h?\\d{0,3}m?\\d{1,3}s?)"); + final var timestamp = getTimestampSeconds("(#t=\\d{0,3}h?\\d{0,3}m?\\d{1,3}s?)"); + return timestamp == -2 ? 0 : timestamp; } @Override @@ -170,7 +171,7 @@ public List getAudioStreams() throws ExtractionException { try { final JsonArray transcodings = track.getObject("media") - .getArray("transcodings"); + .getArray("transcodings"); if (!isNullOrEmpty(transcodings)) { // Get information about what stream formats are available extractAudioStreams(transcodings, audioStreams); From 41b77ef2ac7f30274eae6800d66c33432aee4b9d Mon Sep 17 00:00:00 2001 From: David Asunmo <22662897+davidasunmo@users.noreply.github.com.> Date: Wed, 4 Jun 2025 05:18:19 +0100 Subject: [PATCH 04/13] Fix typo --- .../src/main/java/org/schabi/newpipe/extractor/utils/Utils.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/extractor/src/main/java/org/schabi/newpipe/extractor/utils/Utils.java b/extractor/src/main/java/org/schabi/newpipe/extractor/utils/Utils.java index c061ce30fa..12e0069f31 100644 --- a/extractor/src/main/java/org/schabi/newpipe/extractor/utils/Utils.java +++ b/extractor/src/main/java/org/schabi/newpipe/extractor/utils/Utils.java @@ -115,7 +115,7 @@ public static void checkUrl(final String pattern, final String url) throws Parsi } if (!Parser.isMatch(pattern, url.toLowerCase())) { - throw new ParsingException("Url don't match the pattern"); + throw new ParsingException("Url doesn't match the pattern"); } } From 8114af4c01e1e76ceca3f88cac829f69cd4699d3 Mon Sep 17 00:00:00 2001 From: David Asunmo <22662897+davidasunmo@users.noreply.github.com.> Date: Sat, 7 Jun 2025 08:22:55 +0100 Subject: [PATCH 05/13] [Localization] Add toString to DateWrapper for better debugging --- .../newpipe/extractor/localization/DateWrapper.java | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/extractor/src/main/java/org/schabi/newpipe/extractor/localization/DateWrapper.java b/extractor/src/main/java/org/schabi/newpipe/extractor/localization/DateWrapper.java index ffc29a61ce..2f0a2bdd2a 100644 --- a/extractor/src/main/java/org/schabi/newpipe/extractor/localization/DateWrapper.java +++ b/extractor/src/main/java/org/schabi/newpipe/extractor/localization/DateWrapper.java @@ -69,4 +69,12 @@ public OffsetDateTime offsetDateTime() { public boolean isApproximation() { return isApproximation; } + + @Override + public String toString() { + return "DateWrapper{" + + "offsetDateTime=" + offsetDateTime + + ", isApproximation=" + isApproximation + + '}'; + } } From 89dab90aa70094db3f250af00cd06fd9b68e0e06 Mon Sep 17 00:00:00 2001 From: David Asunmo <22662897+davidasunmo@users.noreply.github.com.> Date: Sun, 8 Jun 2025 11:49:10 +0100 Subject: [PATCH 06/13] [SoundCloud] Use Pattern instead of string for regex in SoundcloudStreamLinkHandlerFactory Use non-capturing groups in regex Refactor Parser.java Add more utility methods (will be used in later commits) --- .../SoundcloudStreamLinkHandlerFactory.java | 19 +++- .../newpipe/extractor/utils/Parser.java | 105 +++++++++++++----- .../schabi/newpipe/extractor/utils/Utils.java | 10 ++ 3 files changed, 102 insertions(+), 32 deletions(-) diff --git a/extractor/src/main/java/org/schabi/newpipe/extractor/services/soundcloud/linkHandler/SoundcloudStreamLinkHandlerFactory.java b/extractor/src/main/java/org/schabi/newpipe/extractor/services/soundcloud/linkHandler/SoundcloudStreamLinkHandlerFactory.java index e7809c52a1..421022ef0a 100644 --- a/extractor/src/main/java/org/schabi/newpipe/extractor/services/soundcloud/linkHandler/SoundcloudStreamLinkHandlerFactory.java +++ b/extractor/src/main/java/org/schabi/newpipe/extractor/services/soundcloud/linkHandler/SoundcloudStreamLinkHandlerFactory.java @@ -1,5 +1,7 @@ package org.schabi.newpipe.extractor.services.soundcloud.linkHandler; +import java.util.regex.Pattern; + import org.schabi.newpipe.extractor.exceptions.ParsingException; import org.schabi.newpipe.extractor.linkhandler.LinkHandlerFactory; import org.schabi.newpipe.extractor.services.soundcloud.SoundcloudParsingHelper; @@ -9,11 +11,18 @@ public final class SoundcloudStreamLinkHandlerFactory extends LinkHandlerFactory { private static final SoundcloudStreamLinkHandlerFactory INSTANCE = new SoundcloudStreamLinkHandlerFactory(); - private static final String URL_PATTERN = "^https?://(www\\.|m\\.|on\\.)?" - + "soundcloud.com/[0-9a-z_-]+" - + "/(?!(tracks|albums|sets|reposts|followers|following)/?$)[0-9a-z_-]+/?([#?].*)?$"; - private static final String API_URL_PATTERN = "^https?://api-v2\\.soundcloud.com" - + "/(tracks|albums|sets|reposts|followers|following)/([0-9a-z_-]+)/"; + + private static final Pattern URL_PATTERN = Pattern.compile( + "^https?://(?:www\\.|m\\.|on\\.)?" + + "soundcloud.com/[0-9a-z_-]+" + + "/(?!(?:tracks|albums|sets|reposts|followers|following)/?$)[0-9a-z_-]+/?(?:[#?].*)?$" + ); + + private static final Pattern API_URL_PATTERN = Pattern.compile( + "^https?://api-v2\\.soundcloud.com" + + "/(tracks|albums|sets|reposts|followers|following)/([0-9a-z_-]+)/" + ); + private SoundcloudStreamLinkHandlerFactory() { } diff --git a/extractor/src/main/java/org/schabi/newpipe/extractor/utils/Parser.java b/extractor/src/main/java/org/schabi/newpipe/extractor/utils/Parser.java index 325867e75d..ddda97bee6 100644 --- a/extractor/src/main/java/org/schabi/newpipe/extractor/utils/Parser.java +++ b/extractor/src/main/java/org/schabi/newpipe/extractor/utils/Parser.java @@ -44,38 +44,83 @@ public RegexException(final String message) { } } + @Nonnull + public static Matcher matchOrThrow(@Nonnull final Pattern pattern, + final String input) throws RegexException { + final Matcher matcher = pattern.matcher(input); + if (matcher.find()) { + return matcher; + } else { + String errorMessage = "Failed to find pattern \"" + pattern.pattern() + "\""; + if (input.length() <= 1024) { + errorMessage += " inside of \"" + input + "\""; + } + throw new RegexException(errorMessage); + } + } + + @Nonnull + public static String matchNamedGroup(final String pattern, + final String input, + final String groupName) throws RegexException { + return matchNamedGroup(Pattern.compile(pattern), input, groupName); + } + + @Nonnull + public static String matchNamedGroup(@Nonnull final Pattern pattern, + final String input, + @Nonnull final String groupName) throws RegexException { + return matchOrThrow(pattern, input).group(groupName); + } + + public static int getStartIndexOfNamedGroup(@Nonnull final Pattern pattern, + final String input, + @Nonnull final String groupName) + throws RegexException { + return matchOrThrow(pattern, input).start(groupName); + } + + public static int getEndIndexOfNamedGroup(@Nonnull final Pattern pattern, + final String input, + @Nonnull final String groupName) + throws RegexException { + return matchOrThrow(pattern, input).end(groupName); + } + + @Nonnull public static String matchGroup1(final String pattern, final String input) throws RegexException { return matchGroup(pattern, input, 1); } + + @Nonnull public static String matchGroup1(final Pattern pattern, - final String input) throws RegexException { + final String input) throws RegexException { return matchGroup(pattern, input, 1); } - + + /** + * Matches the specified group of the given pattern against the input. + * + * @param pattern The regex pattern to match. + * @param input The input string to match against. + * @param group The group number to retrieve (1-based index). + * @return The matching group as a string. + * @throws RegexException If the pattern does not match the input or if the group is not found. + */ + @Nonnull public static String matchGroup(final String pattern, - final String input, - final int group) throws RegexException { + final String input, + final int group) throws RegexException { return matchGroup(Pattern.compile(pattern), input, group); } - - public static String matchGroup(@Nonnull final Pattern pat, + + @Nonnull + public static String matchGroup(@Nonnull final Pattern pattern, final String input, final int group) throws RegexException { - final Matcher matcher = pat.matcher(input); - final boolean foundMatch = matcher.find(); - if (foundMatch) { - return matcher.group(group); - } else { - // only pass input to exception message when it is not too long - if (input.length() > 1024) { - throw new RegexException("Failed to find pattern \"" + pat.pattern() + "\""); - } else { - throw new RegexException("Failed to find pattern \"" + pat.pattern() - + "\" inside of \"" + input + "\""); - } - } + return matchOrThrow(pattern, input).group(group); } public static String matchGroup1MultiplePatterns(final Pattern[] patterns, final String input) @@ -83,11 +128,20 @@ public static String matchGroup1MultiplePatterns(final Pattern[] patterns, final return matchMultiplePatterns(patterns, input).group(1); } + /** + * Matches multiple patterns against the input string and + * returns the first successful matcher + * + * @param patterns The array of regex patterns to match. + * @param input The input string to match against. + * @return A {@code Matcher} for the first successful match. + * @throws RegexException If no patterns match the input or if {@code patterns} is empty. + */ public static Matcher matchMultiplePatterns(final Pattern[] patterns, final String input) throws RegexException { - Parser.RegexException exception = null; - for (final Pattern pattern : patterns) { - final Matcher matcher = pattern.matcher(input); + RegexException exception = null; + for (final var pattern : patterns) { + final var matcher = pattern.matcher(input); if (matcher.find()) { return matcher; } else if (exception == null) { @@ -107,14 +161,11 @@ public static Matcher matchMultiplePatterns(final Pattern[] patterns, final Stri } public static boolean isMatch(final String pattern, final String input) { - final Pattern pat = Pattern.compile(pattern); - final Matcher mat = pat.matcher(input); - return mat.find(); + return isMatch(Pattern.compile(pattern), input); } public static boolean isMatch(@Nonnull final Pattern pattern, final String input) { - final Matcher mat = pattern.matcher(input); - return mat.find(); + return pattern.matcher(input).find(); } @Nonnull diff --git a/extractor/src/main/java/org/schabi/newpipe/extractor/utils/Utils.java b/extractor/src/main/java/org/schabi/newpipe/extractor/utils/Utils.java index 12e0069f31..bdf6bf20a2 100644 --- a/extractor/src/main/java/org/schabi/newpipe/extractor/utils/Utils.java +++ b/extractor/src/main/java/org/schabi/newpipe/extractor/utils/Utils.java @@ -110,6 +110,16 @@ public static long mixedNumberWordToLong(final String numberWord) * @param url the url to be tested */ public static void checkUrl(final String pattern, final String url) throws ParsingException { + checkUrl(Pattern.compile(pattern), url); + } + + /** + * Check if the url matches the pattern. + * + * @param pattern the pattern that will be used to check the url + * @param url the url to be tested + */ + public static void checkUrl(final Pattern pattern, final String url) throws ParsingException { if (isNullOrEmpty(url)) { throw new IllegalArgumentException("Url can't be null or empty"); } From ffb7c48e8af55752bc232100dfea79ac82634649 Mon Sep 17 00:00:00 2001 From: David Asunmo <22662897+davidasunmo@users.noreply.github.com.> Date: Sun, 8 Jun 2025 15:47:24 +0100 Subject: [PATCH 07/13] Refactor Description constructor --- .../org/schabi/newpipe/extractor/stream/Description.java | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/extractor/src/main/java/org/schabi/newpipe/extractor/stream/Description.java b/extractor/src/main/java/org/schabi/newpipe/extractor/stream/Description.java index 2641815b12..439609a2c1 100644 --- a/extractor/src/main/java/org/schabi/newpipe/extractor/stream/Description.java +++ b/extractor/src/main/java/org/schabi/newpipe/extractor/stream/Description.java @@ -17,11 +17,7 @@ public class Description implements Serializable { public Description(@Nullable final String content, final int type) { this.type = type; - if (content == null) { - this.content = ""; - } else { - this.content = content; - } + this.content = Objects.requireNonNullElse(content, ""); } public String getContent() { From ea00238df699903b118f2a953f18017da979fa7e Mon Sep 17 00:00:00 2001 From: David Asunmo <22662897+davidasunmo@users.noreply.github.com.> Date: Tue, 8 Jul 2025 07:13:11 +0100 Subject: [PATCH 08/13] Remove redundant initialization --- .../org/schabi/newpipe/extractor/stream/StreamInfo.java | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) 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 b54c69afc2..21d07cd94a 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 @@ -352,7 +352,7 @@ private static void extractOptionalData(final StreamInfo streamInfo, private String uploaderUrl = ""; @Nonnull private List uploaderAvatars = List.of(); - private boolean uploaderVerified = false; + private boolean uploaderVerified; private long uploaderSubscriberCount = -1; private String subChannelName = ""; @@ -368,7 +368,7 @@ private static void extractOptionalData(final StreamInfo streamInfo, private String hlsUrl = ""; private List relatedItems = List.of(); - private long startPosition = 0; + private long startPosition; private List subtitles = List.of(); private String host = ""; @@ -376,11 +376,11 @@ private static void extractOptionalData(final StreamInfo streamInfo, private String category = ""; private String licence = ""; private String supportInfo = ""; - private Locale language = null; + private Locale language; private List tags = List.of(); private List streamSegments = List.of(); private List metaInfo = List.of(); - private boolean shortFormContent = false; + private boolean shortFormContent; /** * Preview frames, e.g. for the storyboard / seekbar thumbnail preview From 05d951b2fce570d313e8a4311882efb3f40cfb3b Mon Sep 17 00:00:00 2001 From: David Asunmo <22662897+davidasunmo@users.noreply.github.com.> Date: Tue, 8 Jul 2025 07:19:20 +0100 Subject: [PATCH 09/13] Remove matchNamedGroup methods from Parser.java as not available on Android API < 26 --- .../newpipe/extractor/utils/Parser.java | 87 ++++++++++--------- 1 file changed, 48 insertions(+), 39 deletions(-) diff --git a/extractor/src/main/java/org/schabi/newpipe/extractor/utils/Parser.java b/extractor/src/main/java/org/schabi/newpipe/extractor/utils/Parser.java index ddda97bee6..962d3f9478 100644 --- a/extractor/src/main/java/org/schabi/newpipe/extractor/utils/Parser.java +++ b/extractor/src/main/java/org/schabi/newpipe/extractor/utils/Parser.java @@ -59,49 +59,39 @@ public static Matcher matchOrThrow(@Nonnull final Pattern pattern, } } - @Nonnull - public static String matchNamedGroup(final String pattern, - final String input, - final String groupName) throws RegexException { - return matchNamedGroup(Pattern.compile(pattern), input, groupName); - } - - @Nonnull - public static String matchNamedGroup(@Nonnull final Pattern pattern, - final String input, - @Nonnull final String groupName) throws RegexException { - return matchOrThrow(pattern, input).group(groupName); - } - - public static int getStartIndexOfNamedGroup(@Nonnull final Pattern pattern, - final String input, - @Nonnull final String groupName) - throws RegexException { - return matchOrThrow(pattern, input).start(groupName); - } - - public static int getEndIndexOfNamedGroup(@Nonnull final Pattern pattern, - final String input, - @Nonnull final String groupName) - throws RegexException { - return matchOrThrow(pattern, input).end(groupName); - } - + /** + * Matches group 1 of the given pattern against the input + * and returns the matched group + * + * @param pattern The regex pattern to match. + * @param input The input string to match against. + * @return The matching group as a string. + * @throws RegexException If the pattern does not match the input or if the group is not found. + */ @Nonnull public static String matchGroup1(final String pattern, final String input) throws RegexException { return matchGroup(pattern, input, 1); } - + /** + * Matches group 1 of the given pattern against the input + * and returns the matched group + * + * @param pattern The regex pattern to match. + * @param input The input string to match against. + * @return The matching group as a string. + * @throws RegexException If the pattern does not match the input or if the group is not found. + */ @Nonnull - public static String matchGroup1(final Pattern pattern, - final String input) throws RegexException { + public static String matchGroup1(final Pattern pattern, final String input) + throws RegexException { return matchGroup(pattern, input, 1); } - + /** - * Matches the specified group of the given pattern against the input. + * Matches the specified group of the given pattern against the input, + * and returns the matched group * * @param pattern The regex pattern to match. * @param input The input string to match against. @@ -110,26 +100,45 @@ public static String matchGroup1(final Pattern pattern, * @throws RegexException If the pattern does not match the input or if the group is not found. */ @Nonnull - public static String matchGroup(final String pattern, - final String input, - final int group) throws RegexException { + public static String matchGroup(final String pattern, final String input, final int group) + throws RegexException { return matchGroup(Pattern.compile(pattern), input, group); } - + + /** + * Matches the specified group of the given pattern against the input, + * and returns the matched group + * + * @param pattern The regex pattern to match. + * @param input The input string to match against. + * @param group The group number to retrieve (1-based index). + * @return The matching group as a string. + * @throws RegexException If the pattern does not match the input or if the group is not found. + */ @Nonnull public static String matchGroup(@Nonnull final Pattern pattern, final String input, - final int group) throws RegexException { + final int group) + throws RegexException { return matchOrThrow(pattern, input).group(group); } + /** + * Matches multiple patterns against the input string and + * returns the first successful matcher + * + * @param patterns The array of regex patterns to match. + * @param input The input string to match against. + * @return A {@code Matcher} for the first successful match. + * @throws RegexException If no patterns match the input or if {@code patterns} is empty. + */ public static String matchGroup1MultiplePatterns(final Pattern[] patterns, final String input) throws RegexException { return matchMultiplePatterns(patterns, input).group(1); } /** - * Matches multiple patterns against the input string and + * Matches multiple patterns against the input string and * returns the first successful matcher * * @param patterns The array of regex patterns to match. From 93328a38e6d46ff27107c65b47ae87ed251f910a Mon Sep 17 00:00:00 2001 From: David Asunmo <22662897+davidasunmo@users.noreply.github.com.> Date: Thu, 3 Jul 2025 05:25:25 +0100 Subject: [PATCH 10/13] Refactor AudioStream constructor to use builder instead of explicit parameters. Refactor documentation also --- .../newpipe/extractor/stream/AudioStream.java | 94 ++++++++----------- 1 file changed, 38 insertions(+), 56 deletions(-) diff --git a/extractor/src/main/java/org/schabi/newpipe/extractor/stream/AudioStream.java b/extractor/src/main/java/org/schabi/newpipe/extractor/stream/AudioStream.java index e31e1aff35..410a20592f 100644 --- a/extractor/src/main/java/org/schabi/newpipe/extractor/stream/AudioStream.java +++ b/extractor/src/main/java/org/schabi/newpipe/extractor/stream/AudioStream.java @@ -88,7 +88,8 @@ public Builder() { } /** - * Set the identifier of the {@link AudioStream}. + * Set the identifier of the {@link AudioStream} which uniquely identifies the stream, + * e.g. for YouTube this would be the itag * *

* It must not be null and should be non empty. @@ -108,14 +109,14 @@ public Builder setId(@Nonnull final String id) { } /** - * Set the content of the {@link AudioStream}. - * + * Set the content or the URL of the {@link AudioStream}, depending on whether isUrl is + * true *

* It must not be null, and should be non empty. *

* * @param content the content of the {@link AudioStream} - * @param isUrl whether the content is a URL + * @param isUrl whether content is the URL or the actual content of e.g. a DASH manifest * @return this {@link Builder} instance */ public Builder setContent(@Nonnull final String content, @@ -126,7 +127,7 @@ public Builder setContent(@Nonnull final String content, } /** - * Set the {@link MediaFormat} used by the {@link AudioStream}. + * Set the {@link MediaFormat} used by the {@link AudioStream}, which can be null * *

* It should be one of the audio {@link MediaFormat}s ({@link MediaFormat#M4A M4A}, @@ -278,16 +279,22 @@ public Builder setItagItem(@Nullable final ItagItem itagItem) { * Build an {@link AudioStream} using the builder's current values. * *

- * The identifier and the content (and so the {@code isUrl} boolean) properties must have + * The identifier and the content (and thus {@code isUrl}) properties must have * been set. *

* * @return a new {@link AudioStream} using the builder's current values - * @throws IllegalStateException if {@code id}, {@code content} (and so {@code isUrl}) or + * @throws IllegalStateException if {@code id}, {@code content} (and thus {@code isUrl}) or * {@code deliveryMethod} have been not set, or have been set as {@code null} */ @Nonnull public AudioStream build() { + validateBuild(); + + return new AudioStream(this); + } + + void validateBuild() { if (id == null) { throw new IllegalStateException( "The identifier of the audio stream has been not set or is null. If you " @@ -305,64 +312,39 @@ public AudioStream build() { "The delivery method of the audio stream has been set as null, which is " + "not allowed. Pass a valid one instead with setDeliveryMethod."); } - - return new AudioStream(id, content, isUrl, mediaFormat, deliveryMethod, averageBitrate, - manifestUrl, audioTrackId, audioTrackName, audioLocale, audioTrackType, - itagItem); } } /** - * Create a new audio stream. + * Create a new audio stream using the given {@link Builder}. * - * @param id the identifier which uniquely identifies the stream, e.g. for YouTube - * this would be the itag - * @param content the content or the URL of the stream, depending on whether isUrl is - * true - * @param isUrl whether content is the URL or the actual content of e.g. a DASH - * manifest - * @param format the {@link MediaFormat} used by the stream, which can be null - * @param deliveryMethod the {@link DeliveryMethod} of the stream - * @param averageBitrate the average bitrate of the stream (which can be unknown, see - * {@link #UNKNOWN_BITRATE}) - * @param audioTrackId the id of the audio track - * @param audioTrackName the name of the audio track - * @param audioLocale the {@link Locale} of the audio stream, representing its language - * @param itagItem the {@link ItagItem} corresponding to the stream, which cannot be null - * @param manifestUrl the URL of the manifest this stream comes from (if applicable, - * otherwise null) + * @param builder The {@link Builder} to use to create the audio stream */ @SuppressWarnings("checkstyle:ParameterNumber") - private AudioStream(@Nonnull final String id, - @Nonnull final String content, - final boolean isUrl, - @Nullable final MediaFormat format, - @Nonnull final DeliveryMethod deliveryMethod, - final int averageBitrate, - @Nullable final String manifestUrl, - @Nullable final String audioTrackId, - @Nullable final String audioTrackName, - @Nullable final Locale audioLocale, - @Nullable final AudioTrackType audioTrackType, - @Nullable final ItagItem itagItem) { - super(id, content, isUrl, format, deliveryMethod, manifestUrl); - if (itagItem != null) { - this.itagItem = itagItem; - this.itag = itagItem.id; - this.quality = itagItem.getQuality(); - this.bitrate = itagItem.getBitrate(); - this.initStart = itagItem.getInitStart(); - this.initEnd = itagItem.getInitEnd(); - this.indexStart = itagItem.getIndexStart(); - this.indexEnd = itagItem.getIndexEnd(); - this.codec = itagItem.getCodec(); + AudioStream(final Builder builder) { + super(builder.id, + builder.content, + builder.isUrl, + builder.mediaFormat, + builder.deliveryMethod, + builder.manifestUrl); + if (builder.itagItem != null) { + this.itagItem = builder.itagItem; + this.itag = builder.itagItem.id; + this.quality = builder.itagItem.getQuality(); + this.bitrate = builder.itagItem.getBitrate(); + this.initStart = builder.itagItem.getInitStart(); + this.initEnd = builder.itagItem.getInitEnd(); + this.indexStart = builder.itagItem.getIndexStart(); + this.indexEnd = builder.itagItem.getIndexEnd(); + this.codec = builder.itagItem.getCodec(); } - this.averageBitrate = averageBitrate; - this.audioTrackId = audioTrackId; - this.audioTrackName = audioTrackName; - this.audioLocale = audioLocale; - this.audioTrackType = audioTrackType; + this.averageBitrate = builder.averageBitrate; + this.audioTrackId = builder.audioTrackId; + this.audioTrackName = builder.audioTrackName; + this.audioLocale = builder.audioLocale; + this.audioTrackType = builder.audioTrackType; } /** From 73d93f83152538e29fcf73a809f807ed404bccae Mon Sep 17 00:00:00 2001 From: David Asunmo <22662897+davidasunmo@users.noreply.github.com.> Date: Tue, 8 Jul 2025 07:23:58 +0100 Subject: [PATCH 11/13] List.of IDE suggestions refactoring --- .../java/org/schabi/newpipe/downloader/DownloaderTestImpl.java | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/extractor/src/test/java/org/schabi/newpipe/downloader/DownloaderTestImpl.java b/extractor/src/test/java/org/schabi/newpipe/downloader/DownloaderTestImpl.java index 091db071d0..6fd0c4c7cc 100644 --- a/extractor/src/test/java/org/schabi/newpipe/downloader/DownloaderTestImpl.java +++ b/extractor/src/test/java/org/schabi/newpipe/downloader/DownloaderTestImpl.java @@ -7,7 +7,6 @@ import org.schabi.newpipe.extractor.exceptions.ReCaptchaException; import java.io.IOException; -import java.util.Arrays; import java.util.List; import java.util.Map; import java.util.concurrent.TimeUnit; @@ -35,7 +34,7 @@ private DownloaderTestImpl(final OkHttpClient.Builder builder) { // Required for certain services // For example Bandcamp otherwise fails on Windows with Java 17+ // as their Fastly-CDN returns 403 - .connectionSpecs(Arrays.asList(ConnectionSpec.RESTRICTED_TLS)) + .connectionSpecs(List.of(ConnectionSpec.RESTRICTED_TLS)) .build()); } From 5066141be008ed0e3c5d6fc39ff62786531350e0 Mon Sep 17 00:00:00 2001 From: David Asunmo <22662897+davidasunmo@users.noreply.github.com.> Date: Tue, 8 Jul 2025 08:17:08 +0100 Subject: [PATCH 12/13] CamelCase typo --- .../extractor/services/youtube/search/YoutubeSearchQHTest.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/extractor/src/test/java/org/schabi/newpipe/extractor/services/youtube/search/YoutubeSearchQHTest.java b/extractor/src/test/java/org/schabi/newpipe/extractor/services/youtube/search/YoutubeSearchQHTest.java index b007f6ddc0..f7f1d95d1d 100644 --- a/extractor/src/test/java/org/schabi/newpipe/extractor/services/youtube/search/YoutubeSearchQHTest.java +++ b/extractor/src/test/java/org/schabi/newpipe/extractor/services/youtube/search/YoutubeSearchQHTest.java @@ -43,7 +43,7 @@ public void testGetContentFilter() throws Exception { } @Test - public void testWithContentfilter() throws Exception { + public void testWithContentFilter() throws Exception { assertEquals("https://www.youtube.com/results?search_query=asdf&sp=EgIQAfABAQ%253D%253D", YouTube.getSearchQHFactory() .fromQuery("asdf", List.of(VIDEOS), "").getUrl()); assertEquals("https://www.youtube.com/results?search_query=asdf&sp=EgIQAvABAQ%253D%253D", YouTube.getSearchQHFactory() From 25113b2453f9d584272c9e522dc66411ed5d0a2e Mon Sep 17 00:00:00 2001 From: David Asunmo <22662897+davidasunmo@users.noreply.github.com.> Date: Tue, 8 Jul 2025 12:48:05 +0100 Subject: [PATCH 13/13] Minor refactoring in Stream.java --- .../org/schabi/newpipe/extractor/stream/Stream.java | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/extractor/src/main/java/org/schabi/newpipe/extractor/stream/Stream.java b/extractor/src/main/java/org/schabi/newpipe/extractor/stream/Stream.java index 04d2b3facb..57e824560d 100644 --- a/extractor/src/main/java/org/schabi/newpipe/extractor/stream/Stream.java +++ b/extractor/src/main/java/org/schabi/newpipe/extractor/stream/Stream.java @@ -68,7 +68,7 @@ public Stream(final String id, * @param streamList the list of {@link Stream}s which will be compared * @return whether the list already contains one stream with equals stats */ - public static boolean containSimilarStream(final Stream stream, + public static boolean containSimilarStream(@Nonnull final Stream stream, final List streamList) { if (isNullOrEmpty(streamList)) { return false; @@ -98,11 +98,9 @@ public static boolean containSimilarStream(final Stream stream, * @return whether the stream have the same stats or not, based on the criteria above */ public boolean equalStats(@Nullable final Stream other) { - if (other == null || mediaFormat == null || other.mediaFormat == null) { - return false; - } - return mediaFormat.id == other.mediaFormat.id && deliveryMethod == other.deliveryMethod - && isUrl == other.isUrl; + return other != null && mediaFormat != null && other.mediaFormat != null + && mediaFormat.id == other.mediaFormat.id && deliveryMethod == other.deliveryMethod + && isUrl == other.isUrl; } /** @@ -137,6 +135,7 @@ public String getUrl() { * * @return the content or URL */ + @Nonnull public String getContent() { return content; }