Skip to content

Commit ce15f7c

Browse files
authored
Merge pull request #996 from TeamNewPipe/feat/peeertube-playlists
[PeerTube] Support searching for playlists and channels
2 parents 95cc6ae + 2a8729a commit ce15f7c

7 files changed

Lines changed: 175 additions & 7 deletions

File tree

extractor/src/main/java/org/schabi/newpipe/extractor/services/peertube/PeertubeParsingHelper.java

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,14 @@
22

33
import com.grack.nanojson.JsonArray;
44
import com.grack.nanojson.JsonObject;
5+
6+
import org.schabi.newpipe.extractor.InfoItemExtractor;
57
import org.schabi.newpipe.extractor.InfoItemsCollector;
68
import org.schabi.newpipe.extractor.Page;
79
import org.schabi.newpipe.extractor.exceptions.ContentNotAvailableException;
810
import org.schabi.newpipe.extractor.exceptions.ParsingException;
11+
import org.schabi.newpipe.extractor.services.peertube.extractors.PeertubeChannelInfoItemExtractor;
12+
import org.schabi.newpipe.extractor.services.peertube.extractors.PeertubePlaylistInfoItemExtractor;
913
import org.schabi.newpipe.extractor.services.peertube.extractors.PeertubeSepiaStreamInfoItemExtractor;
1014
import org.schabi.newpipe.extractor.services.peertube.extractors.PeertubeStreamInfoItemExtractor;
1115
import org.schabi.newpipe.extractor.utils.JsonUtils;
@@ -101,10 +105,16 @@ public static void collectStreamsFrom(final InfoItemsCollector collector,
101105
if (item.has("video")) {
102106
item = item.getObject("video");
103107
}
108+
final boolean isPlaylistInfoItem = item.has("videosLength");
109+
final boolean isChannelInfoItem = item.has("followersCount");
104110

105-
final PeertubeStreamInfoItemExtractor extractor;
111+
final InfoItemExtractor extractor;
106112
if (sepia) {
107113
extractor = new PeertubeSepiaStreamInfoItemExtractor(item, baseUrl);
114+
} else if (isPlaylistInfoItem) {
115+
extractor = new PeertubePlaylistInfoItemExtractor(item, baseUrl);
116+
} else if (isChannelInfoItem) {
117+
extractor = new PeertubeChannelInfoItemExtractor(item, baseUrl);
108118
} else {
109119
extractor = new PeertubeStreamInfoItemExtractor(item, baseUrl);
110120
}
Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
package org.schabi.newpipe.extractor.services.peertube.extractors;
2+
3+
import com.grack.nanojson.JsonObject;
4+
import org.schabi.newpipe.extractor.channel.ChannelExtractor;
5+
import org.schabi.newpipe.extractor.channel.ChannelInfoItemExtractor;
6+
import org.schabi.newpipe.extractor.exceptions.ParsingException;
7+
8+
import javax.annotation.Nonnull;
9+
import java.util.Comparator;
10+
11+
public class PeertubeChannelInfoItemExtractor implements ChannelInfoItemExtractor {
12+
13+
final JsonObject item;
14+
final JsonObject uploader;
15+
final String baseUrl;
16+
public PeertubeChannelInfoItemExtractor(@Nonnull final JsonObject item,
17+
@Nonnull final String baseUrl) {
18+
this.item = item;
19+
this.uploader = item.getObject("uploader");
20+
this.baseUrl = baseUrl;
21+
}
22+
23+
@Override
24+
public String getName() throws ParsingException {
25+
return item.getString("displayName");
26+
}
27+
28+
@Override
29+
public String getUrl() throws ParsingException {
30+
return item.getString("url");
31+
}
32+
33+
@Override
34+
public String getThumbnailUrl() throws ParsingException {
35+
return item.getArray("avatars").stream()
36+
.filter(JsonObject.class::isInstance)
37+
.map(JsonObject.class::cast)
38+
.max(Comparator.comparingInt(avatar -> avatar.getInt("width")))
39+
.map(avatar -> baseUrl + avatar.getString("path"))
40+
.orElse(null);
41+
}
42+
43+
@Override
44+
public String getDescription() throws ParsingException {
45+
return item.getString("description");
46+
}
47+
48+
@Override
49+
public long getSubscriberCount() throws ParsingException {
50+
return item.getInt("followersCount");
51+
}
52+
53+
@Override
54+
public long getStreamCount() throws ParsingException {
55+
return ChannelExtractor.ITEM_COUNT_UNKNOWN;
56+
}
57+
58+
@Override
59+
public boolean isVerified() throws ParsingException {
60+
return false;
61+
}
62+
}
Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
package org.schabi.newpipe.extractor.services.peertube.extractors;
2+
3+
import com.grack.nanojson.JsonObject;
4+
5+
import org.schabi.newpipe.extractor.exceptions.ParsingException;
6+
import org.schabi.newpipe.extractor.playlist.PlaylistInfoItemExtractor;
7+
8+
import javax.annotation.Nonnull;
9+
10+
public class PeertubePlaylistInfoItemExtractor implements PlaylistInfoItemExtractor {
11+
12+
final JsonObject item;
13+
final JsonObject uploader;
14+
final String baseUrl;
15+
16+
public PeertubePlaylistInfoItemExtractor(@Nonnull final JsonObject item,
17+
@Nonnull final String baseUrl) {
18+
this.item = item;
19+
this.uploader = item.getObject("uploader");
20+
this.baseUrl = baseUrl;
21+
}
22+
23+
@Override
24+
public String getName() throws ParsingException {
25+
return item.getString("displayName");
26+
}
27+
28+
@Override
29+
public String getUrl() throws ParsingException {
30+
return item.getString("url");
31+
}
32+
33+
@Override
34+
public String getThumbnailUrl() throws ParsingException {
35+
return baseUrl + item.getString("thumbnailPath");
36+
}
37+
38+
@Override
39+
public String getUploaderName() throws ParsingException {
40+
return uploader.getString("displayName");
41+
}
42+
43+
@Override
44+
public String getUploaderUrl() throws ParsingException {
45+
return uploader.getString("url");
46+
}
47+
48+
@Override
49+
public boolean isUploaderVerified() throws ParsingException {
50+
return false;
51+
}
52+
53+
@Override
54+
public long getStreamCount() throws ParsingException {
55+
return item.getInt("videosLength");
56+
}
57+
}

extractor/src/main/java/org/schabi/newpipe/extractor/services/peertube/extractors/PeertubeStreamExtractor.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -329,7 +329,7 @@ public String getSupportInfo() {
329329
@Nonnull
330330
private String getRelatedItemsUrl(@Nonnull final List<String> tags)
331331
throws UnsupportedEncodingException {
332-
final String url = baseUrl + PeertubeSearchQueryHandlerFactory.SEARCH_ENDPOINT;
332+
final String url = baseUrl + PeertubeSearchQueryHandlerFactory.SEARCH_ENDPOINT_VIDEOS;
333333
final StringBuilder params = new StringBuilder();
334334
params.append("start=0&count=8&sort=-createdAt");
335335
for (final String tag : tags) {

extractor/src/main/java/org/schabi/newpipe/extractor/services/peertube/linkHandler/PeertubePlaylistLinkHandlerFactory.java

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ public final class PeertubePlaylistLinkHandlerFactory extends ListLinkHandlerFac
1313
private static final PeertubePlaylistLinkHandlerFactory INSTANCE
1414
= new PeertubePlaylistLinkHandlerFactory();
1515
private static final String ID_PATTERN = "(/videos/watch/playlist/|/w/p/)([^/?&#]*)";
16+
private static final String API_ID_PATTERN = "/video-playlists/([^/?&#]*)";
1617

1718
private PeertubePlaylistLinkHandlerFactory() {
1819
}
@@ -38,7 +39,12 @@ public String getUrl(final String id,
3839

3940
@Override
4041
public String getId(final String url) throws ParsingException {
41-
return Parser.matchGroup(ID_PATTERN, url, 2);
42+
try {
43+
return Parser.matchGroup(ID_PATTERN, url, 2);
44+
} catch (final ParsingException ignored) {
45+
// might also be an API url, no reason to throw an exception here
46+
}
47+
return Parser.matchGroup1(API_ID_PATTERN, url);
4248
}
4349

4450
@Override

extractor/src/main/java/org/schabi/newpipe/extractor/services/peertube/linkHandler/PeertubeSearchQueryHandlerFactory.java

Lines changed: 19 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -12,8 +12,12 @@ public final class PeertubeSearchQueryHandlerFactory extends SearchQueryHandlerF
1212

1313
public static final String VIDEOS = "videos";
1414
public static final String SEPIA_VIDEOS = "sepia_videos"; // sepia is the global index
15+
public static final String PLAYLISTS = "playlists";
16+
public static final String CHANNELS = "channels";
1517
public static final String SEPIA_BASE_URL = "https://sepiasearch.org";
16-
public static final String SEARCH_ENDPOINT = "/api/v1/search/videos";
18+
public static final String SEARCH_ENDPOINT_PLAYLISTS = "/api/v1/search/video-playlists";
19+
public static final String SEARCH_ENDPOINT_VIDEOS = "/api/v1/search/videos";
20+
public static final String SEARCH_ENDPOINT_CHANNELS = "/api/v1/search/video-channels";
1721

1822
private PeertubeSearchQueryHandlerFactory() {
1923
}
@@ -41,7 +45,17 @@ public String getUrl(final String searchString,
4145
final String sortFilter,
4246
final String baseUrl) throws ParsingException {
4347
try {
44-
return baseUrl + SEARCH_ENDPOINT + "?search=" + Utils.encodeUrlUtf8(searchString);
48+
final String endpoint;
49+
if (contentFilters.isEmpty()
50+
|| contentFilters.get(0).equals(VIDEOS)
51+
|| contentFilters.get(0).equals(SEPIA_VIDEOS)) {
52+
endpoint = SEARCH_ENDPOINT_VIDEOS;
53+
} else if (contentFilters.get(0).equals(CHANNELS)) {
54+
endpoint = SEARCH_ENDPOINT_CHANNELS;
55+
} else {
56+
endpoint = SEARCH_ENDPOINT_PLAYLISTS;
57+
}
58+
return baseUrl + endpoint + "?search=" + Utils.encodeUrlUtf8(searchString);
4559
} catch (final UnsupportedEncodingException e) {
4660
throw new ParsingException("Could not encode query", e);
4761
}
@@ -51,7 +65,9 @@ public String getUrl(final String searchString,
5165
public String[] getAvailableContentFilter() {
5266
return new String[]{
5367
VIDEOS,
54-
SEPIA_VIDEOS
68+
PLAYLISTS,
69+
CHANNELS,
70+
SEPIA_VIDEOS,
5571
};
5672
}
5773
}

extractor/src/test/java/org/schabi/newpipe/extractor/services/peertube/search/PeertubeSearchQHTest.java

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,13 +18,30 @@ public static void setUpClass() throws Exception {
1818
}
1919

2020
@Test
21-
public void testRegularValues() throws Exception {
21+
void testVideoSearch() throws Exception {
2222
assertEquals("https://peertube.mastodon.host/api/v1/search/videos?search=asdf", PeerTube.getSearchQHFactory().fromQuery("asdf").getUrl());
2323
assertEquals("https://peertube.mastodon.host/api/v1/search/videos?search=hans", PeerTube.getSearchQHFactory().fromQuery("hans").getUrl());
2424
assertEquals("https://peertube.mastodon.host/api/v1/search/videos?search=Poifj%26jaijf", PeerTube.getSearchQHFactory().fromQuery("Poifj&jaijf").getUrl());
2525
assertEquals("https://peertube.mastodon.host/api/v1/search/videos?search=G%C3%BCl%C3%BCm", PeerTube.getSearchQHFactory().fromQuery("Gülüm").getUrl());
2626
assertEquals("https://peertube.mastodon.host/api/v1/search/videos?search=%3Fj%24%29H%C2%A7B", PeerTube.getSearchQHFactory().fromQuery("?j$)H§B").getUrl());
27+
}
28+
29+
@Test
30+
void testSepiaVideoSearch() throws Exception {
2731
assertEquals("https://sepiasearch.org/api/v1/search/videos?search=%3Fj%24%29H%C2%A7B", PeerTube.getSearchQHFactory().fromQuery("?j$)H§B", singletonList(PeertubeSearchQueryHandlerFactory.SEPIA_VIDEOS), "").getUrl());
2832
assertEquals("https://anotherpeertubeindex.com/api/v1/search/videos?search=%3Fj%24%29H%C2%A7B", PeerTube.getSearchQHFactory().fromQuery("?j$)H§B", singletonList(PeertubeSearchQueryHandlerFactory.SEPIA_VIDEOS), "", "https://anotherpeertubeindex.com").getUrl());
2933
}
34+
35+
@Test
36+
void testPlaylistSearch() throws Exception {
37+
assertEquals("https://peertube.mastodon.host/api/v1/search/video-playlists?search=asdf", PeerTube.getSearchQHFactory().fromQuery("asdf", singletonList(PeertubeSearchQueryHandlerFactory.PLAYLISTS), "").getUrl());
38+
assertEquals("https://peertube.mastodon.host/api/v1/search/video-playlists?search=hans", PeerTube.getSearchQHFactory().fromQuery("hans", singletonList(PeertubeSearchQueryHandlerFactory.PLAYLISTS), "").getUrl());
39+
}
40+
41+
@Test
42+
void testChannelSearch() throws Exception {
43+
assertEquals("https://peertube.mastodon.host/api/v1/search/video-channels?search=asdf", PeerTube.getSearchQHFactory().fromQuery("asdf", singletonList(PeertubeSearchQueryHandlerFactory.CHANNELS), "").getUrl());
44+
assertEquals("https://peertube.mastodon.host/api/v1/search/video-channels?search=hans", PeerTube.getSearchQHFactory().fromQuery("hans", singletonList(PeertubeSearchQueryHandlerFactory.CHANNELS), "").getUrl());
45+
46+
}
3047
}

0 commit comments

Comments
 (0)