Skip to content
Merged
Show file tree
Hide file tree
Changes from 33 commits
Commits
Show all changes
35 commits
Select commit Hold shift + click to select a range
f0ed76c
Implement rate-limiting
litetex Feb 9, 2025
6e82a80
Disabled age restricted test as it's currently not working
litetex Feb 9, 2025
ddc7f3e
Fix unexpected error due to malformed url
litetex Feb 9, 2025
ee7d8b0
Channel has a new name
litetex Feb 9, 2025
451bb13
YouTube channel mixes no longer exist
litetex Feb 9, 2025
51586c0
Disable YT Shorts UI
litetex Feb 9, 2025
271a465
Add missing override annotations
litetex Feb 9, 2025
d746d1b
Disable irrelevant test
litetex Feb 9, 2025
61ef169
Fix code style
litetex Feb 9, 2025
1a9df85
MetaInfo Search is was removed or there is none active anymore
litetex Feb 9, 2025
f1b9098
Remove outdated comment
litetex Feb 9, 2025
e1b94c4
Move into dedicated package
litetex Feb 9, 2025
c892cfa
Fix PeerTube tests
litetex Feb 9, 2025
ad5ca4e
Backport/Sync code from NewPipe implementation
litetex Feb 9, 2025
12e2145
Update gradle
litetex Feb 9, 2025
351174a
Enforce modern TLS1.2+
litetex Feb 11, 2025
460e0f7
Update test dependencies
litetex Feb 11, 2025
9f94a29
Remove recording retry/throttle as this is now handled in a more gene…
litetex Feb 11, 2025
59a2f1a
No random errors
litetex Feb 11, 2025
1966ad1
Use new Bandcamp autocomplete api
litetex Feb 11, 2025
fa99003
Fix duplicated ;
litetex Feb 11, 2025
ee6bc84
Re-Enable test as it's no longer broken
litetex Feb 11, 2025
25e4a6f
YT: Suggestion test is working again
litetex Feb 11, 2025
4d7df14
Fix YT artist getSubscriberCount extraction
litetex Feb 11, 2025
287123b
Add rate limiter with default cold factor
litetex Feb 11, 2025
cf5df3f
Improve rate limiting
litetex Feb 11, 2025
1772624
Make all YT tests in MOCK mode use mock data
litetex Feb 11, 2025
3b34b82
Downloader: Don't force IOException
litetex Feb 12, 2025
69d5bf2
YouTube 100% Mock coverage
litetex Feb 12, 2025
daac243
Use camelCase
litetex Feb 12, 2025
2f2e2df
Add recorded mock data
litetex Feb 12, 2025
4c3918a
Removed unused code
litetex Feb 12, 2025
d9caa90
Fix okhttp deprecation
litetex Feb 12, 2025
3ad9f17
Revert potentially breaking change
litetex Feb 13, 2025
e408124
Improve documentation
litetex Feb 15, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 2 additions & 4 deletions extractor/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -29,8 +29,6 @@ dependencies {
implementation 'org.jsoup:jsoup:1.17.2'
implementation "com.github.spotbugs:spotbugs-annotations:$spotbugsVersion"

// do not upgrade to 1.7.14, since in 1.7.14 Rhino uses the `SourceVersion` class, which is not
// available on Android (even when using desugaring), and `NoClassDefFoundError` is thrown
implementation 'org.mozilla:rhino:1.7.15'

checkstyle "com.puppycrawl.tools:checkstyle:$checkstyleVersion"
Expand All @@ -40,6 +38,6 @@ dependencies {
testRuntimeOnly 'org.junit.jupiter:junit-jupiter-engine'
testImplementation 'org.junit.jupiter:junit-jupiter-params'

testImplementation "com.squareup.okhttp3:okhttp:3.12.13"
testImplementation 'com.google.code.gson:gson:2.11.0'
testImplementation "com.squareup.okhttp3:okhttp:4.12.0"
testImplementation 'com.google.code.gson:gson:2.12.1'
}
Original file line number Diff line number Diff line change
Expand Up @@ -42,12 +42,6 @@ public enum PlaylistType {
*/
MIX_MUSIC,

/**
* A mix made only of streams from (or related to) the same channel, for example YouTube
* channel mixes
*/
MIX_CHANNEL,

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Are you really sure that channel mixes are gone? Even if they are gone from tests, maybe they aren't on the whole platform. This concept may be applicable to other services too.

This is a breaking change, this should precised in the PR description, so you're also required to add if you tested the changes in NewPipe and if you want to make a PR in the app repo to fix breaking changes, if applicable (from the PR template).

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah it might make sense to keep the code, you never know if YouTube might readd them

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I just now noticed that this is inside org.schabi.newpipe.extractor.playlist and not in inside youtube. Thought it was in youtube (because these mix type only exist for YT and nothing else).

I will reintroduce the enum but deprecate it.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Are you really sure that channel mixes are gone?

Yes for around 7-8 months.
See description of 451bb13

Yeah it might make sense to keep the code, you never know if YouTube might readd them

I think it has no sense to keep unused code around as it only creates a maintenance burden.
One can also still get it from the git history if really needed ;)

/**
* A mix made only of streams related to a particular (musical) genre, for example YouTube
* genre mixes
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@ public List<MetaInfo> getMetaInfo() throws ParsingException {
return Collections.emptyList();
}

@Override
public InfoItemsPage<InfoItem> getPage(final Page page)
throws IOException, ExtractionException {
final MultiInfoItemsCollector collector = new MultiInfoItemsCollector(getServiceId());
Expand Down Expand Up @@ -97,16 +98,12 @@ public InfoItemsPage<InfoItem> getPage(final Page page)
}
}

// Search results appear to be capped at six pages
assert pages.size() < 10;

String nextUrl = null;
if (currentPage < pages.size()) {
nextUrl = page.getUrl().substring(0, page.getUrl().length() - 1) + (currentPage + 1);
}

return new InfoItemsPage<>(collector, new Page(nextUrl));

}

@Nonnull
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,22 +7,24 @@
import com.grack.nanojson.JsonObject;
import com.grack.nanojson.JsonParser;
import com.grack.nanojson.JsonParserException;
import com.grack.nanojson.JsonWriter;

import org.schabi.newpipe.extractor.NewPipe;
import org.schabi.newpipe.extractor.StreamingService;
import org.schabi.newpipe.extractor.downloader.Downloader;
import org.schabi.newpipe.extractor.exceptions.ExtractionException;
import org.schabi.newpipe.extractor.suggestion.SuggestionExtractor;
import org.schabi.newpipe.extractor.utils.Utils;

import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.util.Collections;
import java.util.List;
import java.util.stream.Collectors;

public class BandcampSuggestionExtractor extends SuggestionExtractor {

private static final String AUTOCOMPLETE_URL = BASE_API_URL + "/fuzzysearch/1/autocomplete?q=";
private static final String AUTOCOMPLETE_URL = BASE_API_URL
+ "/bcsearch_public_api/1/autocomplete_elastic";
public BandcampSuggestionExtractor(final StreamingService service) {
super(service);
}
Expand All @@ -33,7 +35,18 @@ public List<String> suggestionList(final String query) throws IOException, Extra

try {
final JsonObject fuzzyResults = JsonParser.object().from(downloader
.get(AUTOCOMPLETE_URL + Utils.encodeUrlUtf8(query)).responseBody());
.postWithContentTypeJson(
AUTOCOMPLETE_URL,
Collections.emptyMap(),
JsonWriter.string()
.object()
.value("fan_id", (String) null)
.value("full_page", false)
.value("search_filter", "")
.value("search_text", query)
.end()
.done()
.getBytes(StandardCharsets.UTF_8)).responseBody());

return fuzzyResults.getObject("auto").getArray("results").stream()
.filter(JsonObject.class::isInstance)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,6 @@
import org.schabi.newpipe.extractor.stream.AudioTrackType;
import org.schabi.newpipe.extractor.utils.JsonUtils;
import org.schabi.newpipe.extractor.utils.Parser;
import org.schabi.newpipe.extractor.utils.ProtoBuilder;
import org.schabi.newpipe.extractor.utils.RandomStringFromAlphabetGenerator;
import org.schabi.newpipe.extractor.utils.Utils;

Expand Down Expand Up @@ -235,23 +234,6 @@ public static boolean isY2ubeURL(@Nonnull final URL url) {
return url.getHost().equalsIgnoreCase("y2u.be");
}

public static String randomVisitorData(final ContentCountry country) {
final ProtoBuilder pbE2 = new ProtoBuilder();
pbE2.string(2, "");
pbE2.varint(4, numberGenerator.nextInt(255) + 1);

final ProtoBuilder pbE = new ProtoBuilder();
pbE.string(1, country.getCountryCode());
pbE.bytes(2, pbE2.toBytes());

final ProtoBuilder pb = new ProtoBuilder();
pb.string(1, RandomStringFromAlphabetGenerator.generate(
CONTENT_PLAYBACK_NONCE_ALPHABET, 11, numberGenerator));
pb.varint(5, System.currentTimeMillis() / 1000 - numberGenerator.nextInt(600000));
pb.bytes(6, pbE.toBytes());
return pb.toUrlencodedBase64();
}

/**
* Parses the duration string of the video expecting ":" or "." as separators
*
Expand Down Expand Up @@ -359,16 +341,6 @@ public static boolean isYoutubeMusicMixId(@Nonnull final String playlistId) {
return playlistId.startsWith("RDAMVM") || playlistId.startsWith("RDCLAK");
}

/**
* Checks if the given playlist id is a YouTube Channel Mix (auto-generated playlist)
* Ids from a YouTube channel Mix start with "RDCM"
*
* @return Whether given id belongs to a YouTube Channel Mix
*/
public static boolean isYoutubeChannelMixId(@Nonnull final String playlistId) {
return playlistId.startsWith("RDCM");
}

/**
* Checks if the given playlist id is a YouTube Genre Mix (auto-generated playlist)
* Ids from a YouTube Genre Mix start with "RDGMEM"
Expand Down Expand Up @@ -399,11 +371,6 @@ public static String extractVideoIdFromMixId(final String playlistId)
} else if (isYoutubeMusicMixId(playlistId)) {
return playlistId.substring(6);

} else if (isYoutubeChannelMixId(playlistId)) {
// Channel mixes are of the form RMCM{channelId}, so videoId can't be determined
throw new ParsingException("Video id could not be determined from channel mix id: "
+ playlistId);

} else if (isYoutubeGenreMixId(playlistId)) {
// Genre mixes are of the form RDGMEM{garbage}, so videoId can't be determined
throw new ParsingException("Video id could not be determined from genre mix id: "
Expand Down Expand Up @@ -438,8 +405,6 @@ public static PlaylistInfo.PlaylistType extractPlaylistTypeFromPlaylistId(
throw new ParsingException("Could not extract playlist type from empty playlist id");
} else if (isYoutubeMusicMixId(playlistId)) {
return PlaylistInfo.PlaylistType.MIX_MUSIC;
} else if (isYoutubeChannelMixId(playlistId)) {
return PlaylistInfo.PlaylistType.MIX_CHANNEL;
} else if (isYoutubeGenreMixId(playlistId)) {
return PlaylistInfo.PlaylistType.MIX_GENRE;
} else if (isYoutubeMixId(playlistId)) { // normal mix
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package org.schabi.newpipe.extractor.services.youtube.extractors;

import com.grack.nanojson.JsonArray;
import com.grack.nanojson.JsonObject;
import org.schabi.newpipe.extractor.Image;
import org.schabi.newpipe.extractor.channel.ChannelInfoItemExtractor;
Expand Down Expand Up @@ -61,10 +62,14 @@ public String getUrl() throws ParsingException {

@Override
public long getSubscriberCount() throws ParsingException {
final String subscriberCount = getTextFromObject(artistInfoItem.getArray("flexColumns")
.getObject(2)
final JsonArray flexColumns = artistInfoItem.getArray("flexColumns");
final JsonArray runs = flexColumns
.getObject(flexColumns.size() - 1)
.getObject("musicResponsiveListItemFlexColumnRenderer")
.getObject("text"));
.getObject("text")
.getArray("runs");
final String subscriberCount = runs.getObject(runs.size() - 1)
.getString("text");
if (!isNullOrEmpty(subscriberCount)) {
try {
return Utils.mixedNumberWordToLong(subscriberCount);
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
package org.schabi.newpipe.extractor.services.youtube.linkHandler;

import org.schabi.newpipe.extractor.exceptions.ContentNotSupportedException;
import org.schabi.newpipe.extractor.exceptions.ParsingException;
import org.schabi.newpipe.extractor.linkhandler.LinkHandler;
import org.schabi.newpipe.extractor.linkhandler.ListLinkHandler;
Expand Down Expand Up @@ -57,14 +56,6 @@ public String getId(final String url) throws ParsingException, UnsupportedOperat
"the list-ID given in the URL does not match the list pattern");
}

if (YoutubeParsingHelper.isYoutubeChannelMixId(listID)
&& Utils.getQueryValue(urlObj, "v") == null) {
// Video id can't be determined from the channel mix id.
// See YoutubeParsingHelper#extractVideoIdFromMixId
throw new ContentNotSupportedException(
"Channel Mix without a video id are not supported");
}

return listID;
} catch (final Exception exception) {
throw new ParsingException("Error could not parse URL: " + exception.getMessage(),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -114,7 +114,7 @@ public String getId(final String theUrlString)
try {
url = Utils.stringToURL(urlString);
} catch (final MalformedURLException e) {
throw new IllegalArgumentException("The given URL is not valid");
throw new ParsingException("The given URL is not valid", e);
}

final String host = url.getHost();
Expand Down

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ public static DownloaderType getDownloaderType() {
* @param path The path to the folder where mocks are saved/retrieved.
* Preferably starting with {@link DownloaderFactory#RESOURCE_PATH}
*/
public static Downloader getDownloader(final String path) throws IOException {
public static Downloader getDownloader(final String path) {
final DownloaderType type = getDownloaderType();
switch (type) {
case REAL:
Expand Down
Loading