Skip to content

Commit 7adbc48

Browse files
litetexTobiGr
authored andcommitted
Merge pull request TeamNewPipe#1332 from litetex/fix-all-tests
Fix all tests and make everything work offline/with mocks. Remove old mocks and generate new ones with new structure. Remove SoundCloud "Top 50" kiosk.
1 parent f3df599 commit 7adbc48

1,400 files changed

Lines changed: 165388 additions & 165852 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

extractor/src/main/java/org/schabi/newpipe/extractor/services/soundcloud/SoundcloudParsingHelper.java

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -402,10 +402,9 @@ public static String getInfoItemsFromApi(final MultiInfoItemsCollector collector
402402
// `Likes` feed, so they should be handled by the correct extractor.
403403
final JsonObject likedPlaylist =
404404
searchResult.getObject("playlist", null);
405-
collector.commit(
406-
null == likedPlaylist
407-
? new SoundcloudLikesInfoItemExtractor(searchResult)
408-
: new SoundcloudPlaylistInfoItemExtractor(likedPlaylist)
405+
collector.commit(likedPlaylist == null
406+
? new SoundcloudLikesInfoItemExtractor(searchResult)
407+
: new SoundcloudPlaylistInfoItemExtractor(likedPlaylist)
409408
);
410409
break;
411410
}

extractor/src/main/java/org/schabi/newpipe/extractor/services/soundcloud/SoundcloudService.java

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -126,7 +126,6 @@ public KioskList getKioskList() throws ExtractionException {
126126

127127
// add kiosks here e.g.:
128128
try {
129-
list.addKioskEntry(chartsFactory, h, "Top 50");
130129
list.addKioskEntry(chartsFactory, h, "New & hot");
131130
list.setDefaultKiosk("New & hot");
132131
} catch (final Exception e) {
Lines changed: 42 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,9 @@
11
package org.schabi.newpipe.extractor.services.soundcloud.extractors;
22

3+
import static org.schabi.newpipe.extractor.ServiceList.SoundCloud;
4+
import static org.schabi.newpipe.extractor.services.soundcloud.SoundcloudParsingHelper.SOUNDCLOUD_API_V2_URL;
5+
import static org.schabi.newpipe.extractor.utils.Utils.isNullOrEmpty;
6+
37
import org.schabi.newpipe.extractor.Page;
48
import org.schabi.newpipe.extractor.StreamingService;
59
import org.schabi.newpipe.extractor.downloader.Downloader;
@@ -11,22 +15,54 @@
1115
import org.schabi.newpipe.extractor.stream.StreamInfoItem;
1216
import org.schabi.newpipe.extractor.stream.StreamInfoItemsCollector;
1317

14-
import javax.annotation.Nonnull;
1518
import java.io.IOException;
1619

17-
import static org.schabi.newpipe.extractor.ServiceList.SoundCloud;
18-
import static org.schabi.newpipe.extractor.services.soundcloud.SoundcloudParsingHelper.SOUNDCLOUD_API_V2_URL;
19-
import static org.schabi.newpipe.extractor.utils.Utils.isNullOrEmpty;
20+
import javax.annotation.Nonnull;
2021

2122
public class SoundcloudChartsExtractor extends KioskExtractor<StreamInfoItem> {
23+
24+
private String initialFetchNextPageUrl;
25+
private StreamInfoItemsCollector initialFetchCollector;
26+
2227
public SoundcloudChartsExtractor(final StreamingService service,
2328
final ListLinkHandler linkHandler,
2429
final String kioskId) {
2530
super(service, linkHandler, kioskId);
2631
}
2732

2833
@Override
29-
public void onFetchPage(@Nonnull final Downloader downloader) {
34+
public void onFetchPage(@Nonnull final Downloader downloader)
35+
throws ExtractionException, IOException {
36+
// Check if already run
37+
if (initialFetchNextPageUrl != null) {
38+
return;
39+
}
40+
41+
initialFetchCollector = new StreamInfoItemsCollector(getServiceId());
42+
43+
final String apiUrl = SOUNDCLOUD_API_V2_URL + "charts"
44+
+ "?genre=soundcloud:genres:all-music"
45+
+ "&client_id=" + SoundcloudParsingHelper.clientId()
46+
+ "&kind=trending";
47+
48+
final ContentCountry contentCountry = SoundCloud.getContentCountry();
49+
String apiUrlWithRegion = null;
50+
if (getService().getSupportedCountries().contains(contentCountry)) {
51+
apiUrlWithRegion = apiUrl + "&region=soundcloud:regions:"
52+
+ contentCountry.getCountryCode();
53+
}
54+
55+
try {
56+
initialFetchNextPageUrl = SoundcloudParsingHelper.getStreamsFromApi(
57+
initialFetchCollector,
58+
apiUrlWithRegion == null ? apiUrl : apiUrlWithRegion, true);
59+
} catch (final IOException e) {
60+
// Request to other region may be geo-restricted.
61+
// See https://github.com/TeamNewPipe/NewPipeExtractor/issues/537.
62+
// We retry without the specified region.
63+
initialFetchNextPageUrl = SoundcloudParsingHelper.getStreamsFromApi(
64+
initialFetchCollector, apiUrl, true);
65+
}
3066
}
3167

3268
@Nonnull
@@ -52,35 +88,6 @@ public InfoItemsPage<StreamInfoItem> getPage(final Page page) throws IOException
5288
@Nonnull
5389
@Override
5490
public InfoItemsPage<StreamInfoItem> getInitialPage() throws IOException, ExtractionException {
55-
final StreamInfoItemsCollector collector = new StreamInfoItemsCollector(getServiceId());
56-
57-
String apiUrl = SOUNDCLOUD_API_V2_URL + "charts" + "?genre=soundcloud:genres:all-music"
58-
+ "&client_id=" + SoundcloudParsingHelper.clientId();
59-
60-
if (getId().equals("Top 50")) {
61-
apiUrl += "&kind=top";
62-
} else {
63-
apiUrl += "&kind=trending";
64-
}
65-
66-
final ContentCountry contentCountry = SoundCloud.getContentCountry();
67-
String apiUrlWithRegion = null;
68-
if (getService().getSupportedCountries().contains(contentCountry)) {
69-
apiUrlWithRegion = apiUrl + "&region=soundcloud:regions:"
70-
+ contentCountry.getCountryCode();
71-
}
72-
73-
String nextPageUrl;
74-
try {
75-
nextPageUrl = SoundcloudParsingHelper.getStreamsFromApi(collector,
76-
apiUrlWithRegion == null ? apiUrl : apiUrlWithRegion, true);
77-
} catch (final IOException e) {
78-
// Request to other region may be geo-restricted.
79-
// See https://github.com/TeamNewPipe/NewPipeExtractor/issues/537.
80-
// We retry without the specified region.
81-
nextPageUrl = SoundcloudParsingHelper.getStreamsFromApi(collector, apiUrl, true);
82-
}
83-
84-
return new InfoItemsPage<>(collector, new Page(nextPageUrl));
91+
return new InfoItemsPage<>(initialFetchCollector, new Page(initialFetchNextPageUrl));
8592
}
8693
}

extractor/src/main/java/org/schabi/newpipe/extractor/services/soundcloud/linkHandler/SoundcloudChartsLinkHandlerFactory.java

Lines changed: 2 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -11,8 +11,6 @@ public final class SoundcloudChartsLinkHandlerFactory extends ListLinkHandlerFac
1111
private static final SoundcloudChartsLinkHandlerFactory INSTANCE =
1212
new SoundcloudChartsLinkHandlerFactory();
1313

14-
private static final String TOP_URL_PATTERN =
15-
"^https?://(www\\.|m\\.)?soundcloud.com/charts(/top)?/?([#?].*)?$";
1614
private static final String URL_PATTERN =
1715
"^https?://(www\\.|m\\.)?soundcloud.com/charts(/top|/new)?/?([#?].*)?$";
1816

@@ -25,23 +23,15 @@ public static SoundcloudChartsLinkHandlerFactory getInstance() {
2523

2624
@Override
2725
public String getId(final String url) throws ParsingException, UnsupportedOperationException {
28-
if (Parser.isMatch(TOP_URL_PATTERN, url.toLowerCase())) {
29-
return "Top 50";
30-
} else {
31-
return "New & hot";
32-
}
26+
return "New & hot";
3327
}
3428

3529
@Override
3630
public String getUrl(final String id,
3731
final List<String> contentFilter,
3832
final String sortFilter)
3933
throws ParsingException, UnsupportedOperationException {
40-
if (id.equals("Top 50")) {
41-
return "https://soundcloud.com/charts/top";
42-
} else {
43-
return "https://soundcloud.com/charts/new";
44-
}
34+
return "https://soundcloud.com/charts/new";
4535
}
4636

4737
@Override

extractor/src/main/java/org/schabi/newpipe/extractor/services/youtube/YoutubeJavaScriptPlayerManager.java

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
package org.schabi.newpipe.extractor.services.youtube;
22

3+
import static org.schabi.newpipe.extractor.utils.Utils.isNullOrEmpty;
4+
35
import org.schabi.newpipe.extractor.exceptions.ParsingException;
46
import org.schabi.newpipe.extractor.utils.JavaScript;
57

@@ -264,6 +266,10 @@ public static String getUrlWithThrottlingParameterDeobfuscated(
264266
cachedThrottlingDeobfuscationFunctionName,
265267
obfuscatedThrottlingParameter);
266268

269+
if (isNullOrEmpty(deobfuscatedThrottlingParameter)) {
270+
throw new IllegalStateException("Extracted n-parameter is empty");
271+
}
272+
267273
CACHED_THROTTLING_PARAMETERS.put(
268274
obfuscatedThrottlingParameter, deobfuscatedThrottlingParameter);
269275

extractor/src/main/java/org/schabi/newpipe/extractor/services/youtube/extractors/YoutubeMusicSearchExtractor.java

Lines changed: 16 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -34,15 +34,17 @@
3434
import java.nio.charset.StandardCharsets;
3535
import java.util.Collections;
3636
import java.util.List;
37-
import java.util.stream.Collectors;
3837
import java.util.Objects;
38+
import java.util.stream.Collectors;
3939

4040
import javax.annotation.Nonnull;
4141
import javax.annotation.Nullable;
4242

4343
public class YoutubeMusicSearchExtractor extends SearchExtractor {
4444
private JsonObject initialData;
4545

46+
private List<JsonObject> cachedItemSectionRendererContents;
47+
4648
public YoutubeMusicSearchExtractor(final StreamingService service,
4749
final SearchQueryHandler linkHandler) {
4850
super(service, linkHandler);
@@ -116,7 +118,11 @@ public void onFetchPage(@Nonnull final Downloader downloader)
116118
}
117119

118120
private List<JsonObject> getItemSectionRendererContents() {
119-
return initialData
121+
if (cachedItemSectionRendererContents != null) {
122+
return cachedItemSectionRendererContents;
123+
}
124+
125+
cachedItemSectionRendererContents = initialData
120126
.getObject("contents")
121127
.getObject("tabbedSearchResultsRenderer")
122128
.getArray("tabs")
@@ -134,6 +140,7 @@ private List<JsonObject> getItemSectionRendererContents() {
134140
.getArray("contents")
135141
.getObject(0))
136142
.collect(Collectors.toList());
143+
return cachedItemSectionRendererContents;
137144
}
138145

139146
@Nonnull
@@ -142,12 +149,16 @@ public String getSearchSuggestion() throws ParsingException {
142149
for (final JsonObject obj : getItemSectionRendererContents()) {
143150
final JsonObject didYouMeanRenderer = obj
144151
.getObject("didYouMeanRenderer");
145-
final JsonObject showingResultsForRenderer = obj
146-
.getObject("showingResultsForRenderer");
147152

148153
if (!didYouMeanRenderer.isEmpty()) {
149154
return getTextFromObject(didYouMeanRenderer.getObject("correctedQuery"));
150-
} else if (!showingResultsForRenderer.isEmpty()) {
155+
}
156+
157+
// NOTE: As of 2025-07 "showing results for ..." doesn't seem to be returned by
158+
// the backend anymore, however the code is still present in the JS frontend.
159+
final JsonObject showingResultsForRenderer = obj
160+
.getObject("showingResultsForRenderer");
161+
if (!showingResultsForRenderer.isEmpty()) {
151162
return JsonUtils.getString(showingResultsForRenderer,
152163
"correctedQueryEndpoint.searchEndpoint.query");
153164
}

extractor/src/main/java/org/schabi/newpipe/extractor/utils/Parser.java

Lines changed: 10 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -91,22 +91,19 @@ public static Matcher matchMultiplePatterns(final Pattern[] patterns, final Stri
9191
if (matcher.find()) {
9292
return matcher;
9393
} else if (exception == null) {
94-
// only pass input to exception message when it is not too long
95-
if (input.length() > 1024) {
96-
exception = new RegexException("Failed to find pattern \"" + pattern.pattern()
97-
+ "\"");
98-
} else {
99-
exception = new RegexException("Failed to find pattern \"" + pattern.pattern()
100-
+ "\" inside of \"" + input + "\"");
101-
}
94+
exception = new RegexException("Failed to find pattern \"" + pattern.pattern()
95+
+ "\""
96+
// only pass input to exception message when it is not too long
97+
+ (input.length() <= 1000
98+
? "inside of \"" + input + "\""
99+
: "")
100+
);
102101
}
103102
}
104103

105-
if (exception == null) {
106-
throw new RegexException("Empty patterns array passed to matchMultiplePatterns");
107-
} else {
108-
throw exception;
109-
}
104+
throw exception != null
105+
? exception
106+
: new RegexException("Empty patterns array passed to matchMultiplePatterns");
110107
}
111108

112109
public static boolean isMatch(final String pattern, final String input) {

extractor/src/test/java/org/schabi/newpipe/downloader/DownloaderFactory.java

Lines changed: 40 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -2,22 +2,56 @@
22

33
import org.schabi.newpipe.extractor.downloader.Downloader;
44

5-
import java.io.IOException;
5+
import java.util.Locale;
66

77
public class DownloaderFactory {
88

9-
public static final String RESOURCE_PATH = "src/test/resources/org/schabi/newpipe/extractor/";
10-
119
private static final DownloaderType DEFAULT_DOWNLOADER = DownloaderType.REAL;
1210

13-
public static DownloaderType getDownloaderType() {
11+
private static DownloaderType cachedDownloaderType;
12+
13+
private static DownloaderType getDownloaderType() {
14+
if (cachedDownloaderType == null) {
15+
cachedDownloaderType = determineDownloaderType();
16+
}
17+
18+
return cachedDownloaderType;
19+
}
20+
21+
private static DownloaderType determineDownloaderType() {
22+
String propValue = System.getProperty("downloader");
23+
if (propValue == null) {
24+
return DEFAULT_DOWNLOADER;
25+
}
26+
propValue = propValue.toUpperCase();
27+
// Use shortcut because RECORDING is quite long
28+
if (propValue.equals("REC")) {
29+
return DownloaderType.RECORDING;
30+
}
1431
try {
15-
return DownloaderType.valueOf(System.getProperty("downloader"));
32+
return DownloaderType.valueOf(propValue);
1633
} catch (final Exception e) {
1734
return DEFAULT_DOWNLOADER;
1835
}
1936
}
2037

38+
public static Downloader getDownloader(final Class<?> clazz) {
39+
return getDownloader(clazz, null);
40+
}
41+
42+
public static Downloader getDownloader(final Class<?> clazz, final String specificUseCase) {
43+
String baseName = clazz.getName();
44+
if (specificUseCase != null) {
45+
baseName += "." + specificUseCase;
46+
}
47+
return getDownloader("src/test/resources/mocks/v1/"
48+
+ baseName
49+
.toLowerCase(Locale.ENGLISH)
50+
.replace('$', '.')
51+
.replace("test", "")
52+
.replace('.', '/'));
53+
}
54+
2155
/**
2256
* <p>
2357
* Returns a implementation of a {@link Downloader}.
@@ -34,9 +68,8 @@ public static DownloaderType getDownloaderType() {
3468
* </p>
3569
*
3670
* @param path The path to the folder where mocks are saved/retrieved.
37-
* Preferably starting with {@link DownloaderFactory#RESOURCE_PATH}
3871
*/
39-
public static Downloader getDownloader(final String path) {
72+
protected static Downloader getDownloader(final String path) {
4073
final DownloaderType type = getDownloaderType();
4174
switch (type) {
4275
case REAL:

extractor/src/test/java/org/schabi/newpipe/downloader/MockOnly.java

Lines changed: 0 additions & 25 deletions
This file was deleted.

extractor/src/test/java/org/schabi/newpipe/downloader/MockOnlyCondition.java

Lines changed: 0 additions & 21 deletions
This file was deleted.

0 commit comments

Comments
 (0)