1
0
mirror of https://github.com/TeamNewPipe/NewPipeExtractor synced 2025-10-06 00:23:15 +02:00

Merge pull request #1323 from absurdlylongusername/minor-code-cleanup

This commit is contained in:
Stypox
2025-10-02 14:52:36 +02:00
committed by GitHub
13 changed files with 175 additions and 112 deletions

View File

@@ -69,4 +69,12 @@ public class DateWrapper implements Serializable {
public boolean isApproximation() {
return isApproximation;
}
@Override
public String toString() {
return "DateWrapper{"
+ "offsetDateTime=" + offsetDateTime
+ ", isApproximation=" + isApproximation
+ '}';
}
}

View File

@@ -121,7 +121,8 @@ public class SoundcloudStreamExtractor extends StreamExtractor {
@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 class SoundcloudStreamExtractor extends StreamExtractor {
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);

View File

@@ -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 @@ import org.schabi.newpipe.extractor.utils.Utils;
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() {
}

View File

@@ -88,7 +88,8 @@ public final class AudioStream extends Stream {
}
/**
* 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
*
* <p>
* It <b>must not be null</b> and should be non empty.
@@ -108,14 +109,14 @@ public final class AudioStream extends Stream {
}
/**
* Set the content of the {@link AudioStream}.
*
* Set the content or the URL of the {@link AudioStream}, depending on whether isUrl is
* true
* <p>
* It must not be null, and should be non empty.
* </p>
*
* @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 final class AudioStream extends Stream {
}
/**
* Set the {@link MediaFormat} used by the {@link AudioStream}.
* Set the {@link MediaFormat} used by the {@link AudioStream}, which can be null
*
* <p>
* It should be one of the audio {@link MediaFormat}s ({@link MediaFormat#M4A M4A},
@@ -278,16 +279,22 @@ public final class AudioStream extends Stream {
* Build an {@link AudioStream} using the builder's current values.
*
* <p>
* 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.
* </p>
*
* @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 final class AudioStream extends Stream {
"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;
}
/**

View File

@@ -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() {

View File

@@ -68,7 +68,7 @@ public abstract class Stream implements Serializable {
* @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<? extends Stream> streamList) {
if (isNullOrEmpty(streamList)) {
return false;
@@ -98,11 +98,9 @@ public abstract class Stream implements Serializable {
* @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 abstract class Stream implements Serializable {
*
* @return the content or URL
*/
@Nonnull
public String getContent() {
return content;
}

View File

@@ -352,7 +352,7 @@ public class StreamInfo extends Info {
private String uploaderUrl = "";
@Nonnull
private List<Image> uploaderAvatars = List.of();
private boolean uploaderVerified = false;
private boolean uploaderVerified;
private long uploaderSubscriberCount = -1;
private String subChannelName = "";
@@ -368,7 +368,7 @@ public class StreamInfo extends Info {
private String hlsUrl = "";
private List<InfoItem> relatedItems = List.of();
private long startPosition = 0;
private long startPosition;
private List<SubtitlesStream> subtitles = List.of();
private String host = "";
@@ -376,11 +376,11 @@ public class StreamInfo extends Info {
private String category = "";
private String licence = "";
private String supportInfo = "";
private Locale language = null;
private Locale language;
private List<String> tags = List.of();
private List<StreamSegment> streamSegments = List.of();
private List<MetaInfo> metaInfo = List.of();
private boolean shortFormContent = false;
private boolean shortFormContent;
/**
* Preview frames, e.g. for the storyboard / seekbar thumbnail preview

View File

@@ -14,7 +14,7 @@ import java.util.Objects;
* <p>
* 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.
* </p>
*
* <p>

View File

@@ -44,50 +44,113 @@ public final class Parser {
}
}
@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);
}
}
/**
* 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);
}
public static String matchGroup1(final Pattern pattern,
final String input) throws RegexException {
/**
* 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 {
return matchGroup(pattern, input, 1);
}
public static String matchGroup(final String pattern,
final String input,
final int group) throws RegexException {
/**
* 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(final String pattern, final String input, final int group)
throws RegexException {
return matchGroup(Pattern.compile(pattern), input, group);
}
public static String matchGroup(@Nonnull final Pattern pat,
/**
* 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 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 + "\"");
}
}
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
* 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 +170,11 @@ public final class Parser {
}
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

View File

@@ -110,12 +110,22 @@ public final class Utils {
* @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");
}
if (!Parser.isMatch(pattern, url.toLowerCase())) {
throw new ParsingException("Url don't match the pattern");
throw new ParsingException("Url doesn't match the pattern");
}
}

View File

@@ -7,7 +7,6 @@ import org.schabi.newpipe.extractor.downloader.Response;
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 @@ public final class DownloaderTestImpl extends Downloader {
// 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());
}

View File

@@ -132,7 +132,6 @@ public class SoundcloudStreamExtractorTest {
@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; }

View File

@@ -43,7 +43,7 @@ public class YoutubeSearchQHTest {
}
@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()