Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
62 commits
Select commit Hold shift + click to select a range
ce1711c
Bump gradle-wrapper from 9.3.1 to 9.4.1
dependabot[bot] Mar 20, 2026
af765c9
Merge pull request #1469 from TeamNewPipe/dependabot/gradle/gradle-wr…
TobiGr Mar 20, 2026
8b082d1
Bump com.google.protobuf:protobuf-javalite from 4.34.0 to 4.34.1
dependabot[bot] Mar 23, 2026
912a989
Merge pull request #1470 from TeamNewPipe/dependabot/gradle/com.googl…
TobiGr Mar 24, 2026
cc2a0ee
Add logging functionality to extractor and add some logging
absurdlylongusername Nov 11, 2025
3407ef9
Fix null exception in tests
absurdlylongusername Nov 18, 2025
0ebaaf5
Final edits
absurdlylongusername Nov 18, 2025
9ad7782
Fix typo
absurdlylongusername Apr 3, 2026
9895e81
Add JUnit LoggerExtension documentation
absurdlylongusername Apr 3, 2026
b89a8ba
A double brace escape to log formatting
absurdlylongusername Apr 3, 2026
c17a580
More javadocs to ExtractorLogger
absurdlylongusername Apr 3, 2026
30416f4
More ExtractorLoggerTest tests
absurdlylongusername Apr 3, 2026
3a252d0
Merge pull request #1403 from absurdlylongusername/add-extractor-logging
TobiGr Apr 3, 2026
95d60f5
Fix fetching YouTube item duration
G-flat Mar 1, 2026
c0cb0c6
Update mocks for new YT stream info item duration extraction
litetex Apr 7, 2026
5106444
Merge pull request #1464 from G-flat/fix_fetching_yt_stream_info_item…
litetex Apr 7, 2026
7e05387
Fix gradle build
litetex Apr 7, 2026
c46af33
Merge pull request #1474 from litetex/fix-build
Stypox Apr 8, 2026
1875788
NewPipe Extractor v0.26.1
TobiGr Apr 10, 2026
81da2b2
Fix SoundcloudCommentsExtractor to handle null page URLs and improve …
Ecomont Apr 13, 2026
b4c493d
Add tests for SoundcloudCommentsExtractor to handle null next_href an…
Ecomont Apr 14, 2026
9de7617
Add mock JSON files for SoundCloud comments extractor with no comments
Ecomont Apr 14, 2026
6797137
Update SoundCloud API client IDs in mock JSON files
Ecomont Apr 17, 2026
003dca6
Merge pull request #1476 from Ecomont/fix/soundcloud-comments-no-url
TobiGr Apr 18, 2026
8e6e0ed
Enhance duration parsing in YouTube extractors to handle invalid inpu…
Ecomont Apr 19, 2026
29bafb7
Fix duration and live stream detection in lockup extractor
Ecomont Apr 19, 2026
4bebf3d
Add unit tests for lockup extractor edge cases
Ecomont Apr 19, 2026
796d81b
Bump com.google.protobuf from 0.9.6 to 0.10.0
dependabot[bot] Apr 20, 2026
6ba91b2
Bump org.jsoup:jsoup from 1.22.1 to 1.22.2
dependabot[bot] Apr 20, 2026
fe91f88
Merge pull request #1480 from TeamNewPipe/dependabot/gradle/org.jsoup…
TobiGr Apr 20, 2026
1512cf3
Merge pull request #1479 from TeamNewPipe/dependabot/gradle/com.googl…
TobiGr Apr 20, 2026
827d206
Merge pull request #1478 from Ecomont/fix/youtube-lockup-duration-liv…
TobiGr Apr 23, 2026
835b839
Bump com.google.code.gson:gson from 2.13.2 to 2.14.0
dependabot[bot] Apr 24, 2026
0fa6d4f
Merge pull request #1482 from TeamNewPipe/dependabot/gradle/com.googl…
TobiGr Apr 24, 2026
a02db33
Bump org.junit:junit-bom from 5.14.3 to 5.14.4
dependabot[bot] Apr 27, 2026
eb98890
Merge pull request #1483 from TeamNewPipe/dependabot/gradle/org.junit…
TobiGr Apr 27, 2026
ae33863
Bump gradle-wrapper from 9.4.1 to 9.5.0
dependabot[bot] Apr 29, 2026
e5eb463
Merge pull request #1484 from TeamNewPipe/dependabot/gradle/gradle-wr…
TobiGr May 4, 2026
5f251e9
Use streamAsJsonObjects
Isira-Seneviratne May 10, 2026
e12567e
Restore map call
Isira-Seneviratne May 10, 2026
c21cd68
Merge pull request #1485 from Isira-Seneviratne/streamAsJsonObjects
TobiGr May 10, 2026
43be475
Bump gradle-wrapper from 9.5.0 to 9.5.1
dependabot[bot] May 12, 2026
b33c151
Merge pull request #1487 from TeamNewPipe/dependabot/gradle/gradle-wr…
TobiGr May 12, 2026
45e21ba
Bump com.google.protobuf:protobuf-javalite from 4.34.1 to 4.35.0
dependabot[bot] May 20, 2026
f56d001
Merge pull request #1490 from TeamNewPipe/dependabot/gradle/com.googl…
TobiGr May 20, 2026
de599d9
[YouTube] Extract lockupViewModels in richItemRenderers of channel tabs
AudricV May 21, 2026
3ec2dc9
[YouTube] Fix YoutubeChannelExtractorTest.Gronkh test
AudricV May 21, 2026
924b433
[YouTube] Update mocks of Gronk and modified channels tabs tests
AudricV May 21, 2026
c0dddfa
[YouTube] Fix upload date extraction for lockupViewModel in channel tabs
Ecomont May 22, 2026
3f40cdb
Improve metadata extraction for views and dates in YoutubeStreamInfoI…
Ecomont May 22, 2026
e56e2d3
Add tests for lockupViewModel date and view count extraction
Ecomont May 22, 2026
99b542a
Fix view count predicate to use word boundaries
Ecomont May 22, 2026
ed1e1e9
[YouTube] Fallback to playlist uploader on course playlists
FineFindus Jan 5, 2026
5621f5d
[YouTube] only apply course-playlists fallback for courses
FineFindus Jan 6, 2026
6be2d10
Merge pull request #1431 from FineFindus/fix/course-playlists
TobiGr May 23, 2026
f8c6390
Merge branch 'dev' into fix/lockup-channel-tab-upload-dates
ShareASmile May 23, 2026
e667b75
Address review: search only info row for date/views, not all rows
Ecomont May 23, 2026
5f57172
Add all rows fallback for lockup view count and date extraction
Ecomont May 23, 2026
585ccde
Merge pull request #1492 from Ecomont/fix/lockup-channel-tab-upload-d…
TobiGr May 23, 2026
df389f5
NewPipe Extractor v0.26.2
TobiGr May 23, 2026
c4eb49f
Merge upstream/master into master and resolve conflicts in StreamInfo…
feuerswut May 26, 2026
3a25736
i forgor to save accepted changes 💀
feuerswut May 26, 2026
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
2 changes: 1 addition & 1 deletion build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ plugins {
allprojects {
apply(plugin = "java-library")

version = "v0.26.0"
version = "v0.26.2"

tasks.withType<JavaCompile> {
options.encoding = Charsets.UTF_8.toString()
Expand Down
5 changes: 1 addition & 4 deletions extractor/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -191,10 +191,7 @@ publishing {
}

signing {
setRequired(shouldSignCIRelease)
useInMemoryPgpKeys(ciSigningKey, ciSigningPassword)
sign(publishing.publications["snapshot"])
}

tasks.withType<Sign> {
onlyIf("Signing credentials are present (only used for maven central)") { shouldSignCIRelease }
}
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
import org.schabi.newpipe.extractor.localization.ContentCountry;
import org.schabi.newpipe.extractor.localization.Localization;
import org.schabi.newpipe.extractor.localization.TimeAgoParser;
import org.schabi.newpipe.extractor.utils.ExtractorLogger;

import javax.annotation.Nonnull;
import javax.annotation.Nullable;
Expand All @@ -15,6 +16,8 @@
import java.util.Objects;

public abstract class Extractor {
private final String TAG = getClass().getSimpleName() + "@" + hashCode();

/**
* {@link StreamingService} currently related to this extractor.<br>
* Useful for getting other things from a service (like the url handlers for
Expand Down Expand Up @@ -54,7 +57,9 @@ public LinkHandler getLinkHandler() {
* @throws ExtractionException if the pages content is not understood
*/
public void fetchPage() throws IOException, ExtractionException {
ExtractorLogger.d(TAG, "base fetchPage called");
if (pageFetched) {
ExtractorLogger.d(TAG, "Page already fetched; returning");
return;
}
onFetchPage(downloader);
Expand Down Expand Up @@ -151,4 +156,9 @@ public ContentCountry getExtractorContentCountry() {
public TimeAgoParser getTimeAgoParser() {
return getService().getTimeAgoParser(getExtractorLocalization());
}

@Override
public String toString() {
return getClass().getSimpleName();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

import org.schabi.newpipe.extractor.exceptions.ExtractionException;
import org.schabi.newpipe.extractor.linkhandler.LinkHandler;
import org.schabi.newpipe.extractor.utils.ExtractorLogger;

import java.io.Serializable;
import java.util.ArrayList;
Expand All @@ -10,6 +11,7 @@

public abstract class Info implements Serializable {

private static final String TAG = "Info";
private final int serviceId;
/**
* Id of this Info object <br>
Expand Down Expand Up @@ -52,6 +54,7 @@ public Info(final int serviceId,
this.url = url;
this.originalUrl = originalUrl;
this.name = name;
ExtractorLogger.d(TAG, "Base Created {}", this);
}

public Info(final int serviceId, final LinkHandler linkHandler, final String name) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
import org.schabi.newpipe.extractor.exceptions.ExtractionException;
import org.schabi.newpipe.extractor.localization.ContentCountry;
import org.schabi.newpipe.extractor.localization.Localization;
import org.schabi.newpipe.extractor.utils.ExtractorLogger;

import java.util.List;

Expand All @@ -34,6 +35,7 @@
* Provides access to streaming services supported by NewPipe.
*/
public final class NewPipe {
private static final String TAG = NewPipe.class.getSimpleName();
private static Downloader downloader;
private static Localization preferredLocalization;
private static ContentCountry preferredContentCountry;
Expand All @@ -42,15 +44,19 @@ private NewPipe() {
}

public static void init(final Downloader d) {
ExtractorLogger.d(TAG, "Default init called");
init(d, Localization.DEFAULT);
}

public static void init(final Downloader d, final Localization l) {
ExtractorLogger.d(TAG, "Default init called with localization={}");
init(d, l, l.getCountryCode().isEmpty()
? ContentCountry.DEFAULT : new ContentCountry(l.getCountryCode()));
}

public static void init(final Downloader d, final Localization l, final ContentCountry c) {
ExtractorLogger.d(TAG, "Initializing with downloader={}, localization={}, country={}",
d, l, c);
downloader = d;
preferredLocalization = l;
preferredContentCountry = c;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -243,4 +243,9 @@ public Response postWithContentTypeJson(final String url,
*/
public abstract Response execute(@Nonnull Request request)
throws IOException, ReCaptchaException;

@Override
public String toString() {
return getClass().getSimpleName();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -114,9 +114,7 @@ private JsonObject fetchReviewsData(final String trackId, final String token)
}

private String getNextPageToken(final JsonArray reviews) throws ParsingException {
return reviews.stream()
.filter(JsonObject.class::isInstance)
.map(JsonObject.class::cast)
return reviews.streamAsJsonObjects()
.map(review -> review.getString("token"))
.reduce((a, b) -> b) // keep only the last element
.orElseThrow(() -> new ParsingException("Could not get token"));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -48,9 +48,8 @@ public List<String> suggestionList(final String query) throws IOException, Extra
.done()
.getBytes(StandardCharsets.UTF_8)).responseBody());

return fuzzyResults.getObject("auto").getArray("results").stream()
.filter(JsonObject.class::isInstance)
.map(JsonObject.class::cast)
return fuzzyResults.getObject("auto").getArray("results")
.streamAsJsonObjects()
.map(jsonObject -> jsonObject.getString("name"))
.distinct()
.collect(Collectors.toList());
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -55,9 +55,7 @@ public ListExtractor.InfoItemsPage<InfoItem> getInitialPage() {
new MultiInfoItemsCollector(getServiceId());
Objects.requireNonNull(conferenceData) // will surely be != null after onFetchPage
.getArray("events")
.stream()
.filter(JsonObject.class::isInstance)
.map(JsonObject.class::cast)
.streamAsJsonObjects()
.forEach(event -> collector.commit(new MediaCCCStreamInfoItemExtractor(event)));
return new ListExtractor.InfoItemsPage<>(collector, null);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -145,9 +145,7 @@ public String getHlsUrl() {

@Nonnull
private String getManifestOfDeliveryMethodWanted(@Nonnull final String deliveryMethod) {
return room.getArray(STREAMS).stream()
.filter(JsonObject.class::isInstance)
.map(JsonObject.class::cast)
return room.getArray(STREAMS).streamAsJsonObjects()
.map(streamObject -> streamObject.getObject(URLS))
.filter(urls -> urls.has(deliveryMethod))
.map(urls -> urls.getObject(deliveryMethod).getString(URL, ""))
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -57,9 +57,7 @@ public InfoItemsPage<StreamInfoItem> getInitialPage() throws IOException, Extrac
.reversed();
final var collector = new StreamInfoItemsCollector(getServiceId(), comparator);

events.stream()
.filter(JsonObject.class::isInstance)
.map(JsonObject.class::cast)
events.streamAsJsonObjects()
.map(MediaCCCRecentKioskExtractor::new)
// #813 / voc/voctoweb#609 -> returns faulty data -> filter it out
.filter(extractor -> extractor.getDuration() > 0)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -293,9 +293,7 @@ private static List<Image> getImagesFromAvatarsOrBanners(
private static List<Image> getImagesFromAvatarOrBannerArray(
@Nonnull final String baseUrl,
@Nonnull final JsonArray avatarsOrBannersArray) {
return avatarsOrBannersArray.stream()
.filter(JsonObject.class::isInstance)
.map(JsonObject.class::cast)
return avatarsOrBannersArray.streamAsJsonObjects()
.filter(image -> !isNullOrEmpty(image.getString("path")))
.map(image -> new Image(baseUrl + image.getString("path"), HEIGHT_UNKNOWN,
image.getInt("width", WIDTH_UNKNOWN), ResolutionLevel.UNKNOWN))
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -480,10 +480,7 @@ private void loadSubtitles() {

private void extractLiveVideoStreams() throws ParsingException {
try {
final JsonArray streamingPlaylists = json.getArray(STREAMING_PLAYLISTS);
streamingPlaylists.stream()
.filter(JsonObject.class::isInstance)
.map(JsonObject.class::cast)
json.getArray(STREAMING_PLAYLISTS).streamAsJsonObjects()
.map(stream -> new VideoStream.Builder()
.setId(String.valueOf(stream.getInt("id", -1)))
.setContent(stream.getString(PLAYLIST_URL, ""), true)
Expand All @@ -507,10 +504,10 @@ private void getStreams() throws ParsingException {

// HLS streams
try {
for (final JsonObject playlist : json.getArray(STREAMING_PLAYLISTS).stream()
.filter(JsonObject.class::isInstance)
.map(JsonObject.class::cast)
.collect(Collectors.toList())) {
final var playlistStream = json.getArray(STREAMING_PLAYLISTS).streamAsJsonObjects();
final var it = playlistStream.iterator();
while (it.hasNext()) {
final var playlist = it.next();
getStreamsFromArray(playlist.getArray(FILES), playlist.getString(PLAYLIST_URL));
}
} catch (final Exception e) {
Expand All @@ -530,11 +527,10 @@ private void getStreamsFromArray(@Nonnull final JsonArray streams,
*/
final boolean isInstanceUsingRandomUuidsForHlsStreams = !isNullOrEmpty(playlistUrl)
&& playlistUrl.endsWith("-master.m3u8");

for (final JsonObject stream : streams.stream()
.filter(JsonObject.class::isInstance)
.map(JsonObject.class::cast)
.collect(Collectors.toList())) {
final var streamOfStreams = streams.streamAsJsonObjects();
final var it = streamOfStreams.iterator();
while (it.hasNext()) {
final var stream = it.next();

// Extract stream version of streams first
final String url = JsonUtils.getString(stream,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -383,9 +383,7 @@ public static String getInfoItemsFromApi(final MultiInfoItemsCollector collector
}

responseObject.getArray("collection")
.stream()
.filter(JsonObject.class::isInstance)
.map(JsonObject.class::cast)
.streamAsJsonObjects()
.forEach(searchResult -> {
final String kind = searchResult.getString("kind", "");
switch (kind) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ public InfoItemsPage<CommentsInfoItem> getInitialPage() throws ExtractionExcepti
public InfoItemsPage<CommentsInfoItem> getPage(final Page page) throws ExtractionException,
IOException {
if (page == null || isNullOrEmpty(page.getUrl())) {
throw new IllegalArgumentException("Page doesn't contain an URL");
return InfoItemsPage.emptyPage();
}
return getPage(page.getUrl());
}
Expand All @@ -63,7 +63,8 @@ private InfoItemsPage<CommentsInfoItem> getPage(@Nonnull final String url)
getServiceId());

collectStreamsFrom(collector, json.getArray("collection"));
return new InfoItemsPage<>(collector, new Page(json.getString("next_href", null)));
final String nextHref = json.getString("next_href");
return new InfoItemsPage<>(collector, isNullOrEmpty(nextHref) ? null : new Page(nextHref));
}

@Override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -140,9 +140,7 @@ public InfoItemsPage<StreamInfoItem> getInitialPage() {
final List<String> ids = new ArrayList<>();

playlist.getArray("tracks")
.stream()
.filter(JsonObject.class::isInstance)
.map(JsonObject.class::cast)
.streamAsJsonObjects()
.forEachOrdered(track -> {
// i.e. if full info is available
if (track.has("title")) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -204,9 +204,7 @@ private String getTranscodingUrl(final String endpointUrl)

private void extractAudioStreams(@Nonnull final JsonArray transcodings,
final List<AudioStream> audioStreams) {
transcodings.stream()
.filter(JsonObject.class::isInstance)
.map(JsonObject.class::cast)
transcodings.streamAsJsonObjects()
.forEachOrdered(transcoding -> {
final String url = transcoding.getString("url");
if (isNullOrEmpty(url)) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -344,11 +344,8 @@ public static ChannelHeader getChannelHeader(
.map(json -> new ChannelHeader(json, ChannelHeader.HeaderType.C4_TABBED))
.orElse(null);
} else if (header.has(CAROUSEL_HEADER_RENDERER)) {
return header.getObject(CAROUSEL_HEADER_RENDERER)
.getArray(CONTENTS)
.stream()
.filter(JsonObject.class::isInstance)
.map(JsonObject.class::cast)
return header.getObject(CAROUSEL_HEADER_RENDERER).getArray(CONTENTS)
.streamAsJsonObjects()
.filter(item -> item.has(TOPIC_CHANNEL_DETAILS_RENDERER))
.findFirst()
.map(item -> item.getObject(TOPIC_CHANNEL_DETAILS_RENDERER))
Expand Down Expand Up @@ -465,9 +462,7 @@ public static String getChannelId(
final String navigationCarouselChannelId = channelHeader.json.getObject(HEADER)
.getObject(CAROUSEL_HEADER_RENDERER)
.getArray(CONTENTS)
.stream()
.filter(JsonObject.class::isInstance)
.map(JsonObject.class::cast)
.streamAsJsonObjects()
.filter(item -> item.has(TOPIC_CHANNEL_DETAILS_RENDERER))
.findFirst()
.orElse(new JsonObject())
Expand Down Expand Up @@ -551,16 +546,12 @@ public static JsonObject getChannelAgeGateRenderer(@Nonnull final JsonObject jso
return jsonResponse.getObject(CONTENTS)
.getObject("twoColumnBrowseResultsRenderer")
.getArray("tabs")
.stream()
.filter(JsonObject.class::isInstance)
.map(JsonObject.class::cast)
.streamAsJsonObjects()
.flatMap(tab -> tab.getObject(TAB_RENDERER)
.getObject(CONTENT)
.getObject("sectionListRenderer")
.getArray(CONTENTS)
.stream()
.filter(JsonObject.class::isInstance)
.map(JsonObject.class::cast))
.streamAsJsonObjects())
.filter(content -> content.has("channelAgeGateRenderer"))
.map(content -> content.getObject("channelAgeGateRenderer"))
.findFirst()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -228,9 +228,7 @@ private static void addAllCommandRuns(
@Nonnull final List<Run> closers
) {
attributedDescription.getArray("commandRuns")
.stream()
.filter(JsonObject.class::isInstance)
.map(JsonObject.class::cast)
.streamAsJsonObjects()
.forEach(run -> {
final JsonObject navigationEndpoint = run.getObject("onTap")
.getObject("innertubeCommand");
Expand Down Expand Up @@ -301,9 +299,7 @@ private static void addAllStyleRuns(
@Nonnull final List<Run> closers
) {
attributedDescription.getArray("styleRuns")
.stream()
.filter(JsonObject.class::isInstance)
.map(JsonObject.class::cast)
.streamAsJsonObjects()
.forEach(run -> {
final int start = run.getInt("startIndex", -1);
final int length = run.getInt("length", 0);
Expand Down
Loading