feat: add tab support for Peertube

This commit is contained in:
ThetaDev 2022-10-23 23:09:40 +02:00
parent 57865e2195
commit aed685e58b
8 changed files with 370 additions and 24 deletions

View File

@ -16,7 +16,9 @@ import org.schabi.newpipe.extractor.linkhandler.SearchQueryHandlerFactory;
import org.schabi.newpipe.extractor.playlist.PlaylistExtractor;
import org.schabi.newpipe.extractor.search.SearchExtractor;
import org.schabi.newpipe.extractor.services.peertube.extractors.PeertubeAccountExtractor;
import org.schabi.newpipe.extractor.services.peertube.extractors.PeertubeAccountTabExtractor;
import org.schabi.newpipe.extractor.services.peertube.extractors.PeertubeChannelExtractor;
import org.schabi.newpipe.extractor.services.peertube.extractors.PeertubeChannelTabExtractor;
import org.schabi.newpipe.extractor.services.peertube.extractors.PeertubeCommentsExtractor;
import org.schabi.newpipe.extractor.services.peertube.extractors.PeertubePlaylistExtractor;
import org.schabi.newpipe.extractor.services.peertube.extractors.PeertubeSearchExtractor;
@ -108,7 +110,11 @@ public class PeertubeService extends StreamingService {
@Override
public ChannelTabExtractor getChannelTabExtractor(final ChannelTabHandler linkHandler)
throws ExtractionException {
return null;
if (linkHandler.getUrl().contains("/video-channels/")) {
return new PeertubeChannelTabExtractor(this, linkHandler);
} else {
return new PeertubeAccountTabExtractor(this, linkHandler);
}
}
@Override

View File

@ -26,10 +26,7 @@ import java.io.IOException;
import java.util.Collections;
import java.util.List;
import static org.schabi.newpipe.extractor.services.peertube.PeertubeParsingHelper.COUNT_KEY;
import static org.schabi.newpipe.extractor.services.peertube.PeertubeParsingHelper.ITEMS_PER_PAGE;
import static org.schabi.newpipe.extractor.services.peertube.PeertubeParsingHelper.START_KEY;
import static org.schabi.newpipe.extractor.services.peertube.PeertubeParsingHelper.collectStreamsFrom;
import static org.schabi.newpipe.extractor.services.peertube.PeertubeParsingHelper.*;
import static org.schabi.newpipe.extractor.utils.Utils.isNullOrEmpty;
public class PeertubeAccountExtractor extends ChannelExtractor {
@ -125,8 +122,7 @@ public class PeertubeAccountExtractor extends ChannelExtractor {
@Nonnull
@Override
public List<ChannelTabHandler> getTabs() throws ParsingException {
// TODO: implement Peertube account channels tab
return Collections.emptyList();
return Collections.singletonList(new ChannelTabHandler(getLinkHandler(), ChannelTabHandler.Tab.Channels));
}
@Nonnull

View File

@ -1,4 +1,129 @@
package org.schabi.newpipe.extractor.services.peertube.extractors;
public class PeertubeAccountTabExtractor {
import com.grack.nanojson.JsonArray;
import com.grack.nanojson.JsonObject;
import com.grack.nanojson.JsonParser;
import com.grack.nanojson.JsonParserException;
import org.schabi.newpipe.extractor.InfoItem;
import org.schabi.newpipe.extractor.MultiInfoItemsCollector;
import org.schabi.newpipe.extractor.Page;
import org.schabi.newpipe.extractor.StreamingService;
import org.schabi.newpipe.extractor.channel.ChannelTabExtractor;
import org.schabi.newpipe.extractor.downloader.Downloader;
import org.schabi.newpipe.extractor.downloader.Response;
import org.schabi.newpipe.extractor.exceptions.ExtractionException;
import org.schabi.newpipe.extractor.exceptions.ParsingException;
import org.schabi.newpipe.extractor.linkhandler.ChannelTabHandler;
import org.schabi.newpipe.extractor.services.peertube.PeertubeParsingHelper;
import org.schabi.newpipe.extractor.services.peertube.linkHandler.PeertubeChannelLinkHandlerFactory;
import org.schabi.newpipe.extractor.utils.Utils;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import java.io.IOException;
import static org.schabi.newpipe.extractor.services.peertube.PeertubeParsingHelper.*;
import static org.schabi.newpipe.extractor.utils.Utils.isNullOrEmpty;
public class PeertubeAccountTabExtractor extends ChannelTabExtractor {
private JsonObject initialJson;
private final String baseUrl;
private String initialUrl;
private static final String ACCOUNTS = "accounts/";
public PeertubeAccountTabExtractor(final StreamingService service,
final ChannelTabHandler linkHandler) throws ParsingException {
super(service, linkHandler);
baseUrl = getBaseUrl();
initialUrl = baseUrl + PeertubeChannelLinkHandlerFactory.API_ENDPOINT;
if (getId().contains(ACCOUNTS)) {
initialUrl += getId();
} else {
initialUrl += ACCOUNTS + getId();
}
initialUrl += "/video-channels?" + START_KEY + "=0&" + COUNT_KEY + "=" + ITEMS_PER_PAGE;
}
@Override
public void onFetchPage(@Nonnull Downloader downloader) throws IOException, ExtractionException {
if (getLinkHandler().getTab() != ChannelTabHandler.Tab.Channels) {
throw new ExtractionException("tab " + getLinkHandler().getTab().name() + " not supported");
}
final Response response = downloader.get(initialUrl);
if (response != null) {
setInitialData(response.responseBody());
} else {
throw new ExtractionException("Unable to extract PeerTube account channel data");
}
}
private void setInitialData(final String responseBody) throws ExtractionException {
try {
initialJson = JsonParser.object().from(responseBody);
} catch (final JsonParserException e) {
throw new ExtractionException("Unable to extract PeerTube account channel data", e);
}
if (initialJson == null) {
throw new ExtractionException("Unable to extract PeerTube account channel data");
}
}
@Nonnull
@Override
public String getName() throws ParsingException {
return initialJson.getArray("data").getObject(0)
.getObject("ownerAccount").getString("displayName", "");
}
@Nonnull
@Override
public InfoItemsPage<InfoItem> getInitialPage() throws IOException, ExtractionException {
return parsePage(initialJson, initialUrl);
}
@Override
public InfoItemsPage<InfoItem> getPage(Page page) throws IOException, ExtractionException {
if (page == null || isNullOrEmpty(page.getUrl())) {
throw new IllegalArgumentException("Page doesn't contain an URL");
}
final Response response = getDownloader().get(page.getUrl());
JsonObject pageJson = null;
if (response != null && !Utils.isBlank(response.responseBody())) {
try {
pageJson = JsonParser.object().from(response.responseBody());
} catch (final Exception e) {
throw new ParsingException("Could not parse json data for account info", e);
}
}
return parsePage(pageJson, page.getUrl());
}
private InfoItemsPage<InfoItem> parsePage(@Nullable JsonObject json, String pageUrl)
throws ExtractionException {
if (json == null) {
throw new ExtractionException("Unable to get account channel list");
}
PeertubeParsingHelper.validate(json);
final MultiInfoItemsCollector collector = new MultiInfoItemsCollector(getServiceId());
JsonArray contents = json.getArray("data");
if (contents == null) {
throw new ParsingException("Unable to extract account channel list");
}
for (final Object c : contents) {
if (c instanceof JsonObject) {
collector.commit(new PeertubeChannelInfoItemExtractor((JsonObject) c, baseUrl));
}
}
return new InfoItemsPage<>(
collector, PeertubeParsingHelper.getNextPage(pageUrl, json.getLong("total")));
}
}

View File

@ -24,10 +24,7 @@ import java.io.IOException;
import java.util.Collections;
import java.util.List;
import static org.schabi.newpipe.extractor.services.peertube.PeertubeParsingHelper.COUNT_KEY;
import static org.schabi.newpipe.extractor.services.peertube.PeertubeParsingHelper.ITEMS_PER_PAGE;
import static org.schabi.newpipe.extractor.services.peertube.PeertubeParsingHelper.START_KEY;
import static org.schabi.newpipe.extractor.services.peertube.PeertubeParsingHelper.collectStreamsFrom;
import static org.schabi.newpipe.extractor.services.peertube.PeertubeParsingHelper.*;
import static org.schabi.newpipe.extractor.utils.Utils.isNullOrEmpty;
public class PeertubeChannelExtractor extends ChannelExtractor {
@ -104,8 +101,8 @@ public class PeertubeChannelExtractor extends ChannelExtractor {
@Nonnull
@Override
public List<ChannelTabHandler> getTabs() throws ParsingException {
// TODO: implement Peertube channel playlists tab
return Collections.emptyList();
return Collections.singletonList(
new ChannelTabHandler(getLinkHandler(), ChannelTabHandler.Tab.Playlists));
}
@Nonnull

View File

@ -0,0 +1,54 @@
package org.schabi.newpipe.extractor.services.peertube.extractors;
import com.grack.nanojson.JsonObject;
import org.schabi.newpipe.extractor.ListExtractor;
import org.schabi.newpipe.extractor.channel.ChannelInfoItemExtractor;
import org.schabi.newpipe.extractor.exceptions.ParsingException;
import org.schabi.newpipe.extractor.utils.JsonUtils;
public class PeertubeChannelInfoItemExtractor implements ChannelInfoItemExtractor {
protected final JsonObject item;
private final String baseUrl;
public PeertubeChannelInfoItemExtractor(final JsonObject item, final String baseUrl) {
this.item = item;
this.baseUrl = baseUrl;
}
@Override
public String getUrl() throws ParsingException {
return JsonUtils.getString(item, "url");
}
@Override
public String getThumbnailUrl() throws ParsingException {
final JsonObject avatar = JsonUtils.getObject(item, "avatar");
return baseUrl + JsonUtils.getString(avatar, "path");
}
@Override
public String getName() throws ParsingException {
return JsonUtils.getString(item, "displayName");
}
@Override
public String getDescription() throws ParsingException {
return item.getString("description", "");
}
@Override
public long getSubscriberCount() throws ParsingException {
return JsonUtils.getNumber(item, "followersCount").longValue();
}
@Override
public long getStreamCount() throws ParsingException {
return ListExtractor.ITEM_COUNT_UNKNOWN;
}
@Override
public boolean isVerified() throws ParsingException {
return false;
}
}

View File

@ -0,0 +1,122 @@
package org.schabi.newpipe.extractor.services.peertube.extractors;
import com.grack.nanojson.JsonArray;
import com.grack.nanojson.JsonObject;
import com.grack.nanojson.JsonParser;
import com.grack.nanojson.JsonParserException;
import org.schabi.newpipe.extractor.InfoItem;
import org.schabi.newpipe.extractor.MultiInfoItemsCollector;
import org.schabi.newpipe.extractor.Page;
import org.schabi.newpipe.extractor.StreamingService;
import org.schabi.newpipe.extractor.channel.ChannelTabExtractor;
import org.schabi.newpipe.extractor.downloader.Downloader;
import org.schabi.newpipe.extractor.downloader.Response;
import org.schabi.newpipe.extractor.exceptions.ExtractionException;
import org.schabi.newpipe.extractor.exceptions.ParsingException;
import org.schabi.newpipe.extractor.linkhandler.ChannelTabHandler;
import org.schabi.newpipe.extractor.services.peertube.PeertubeParsingHelper;
import org.schabi.newpipe.extractor.services.peertube.linkHandler.PeertubeChannelLinkHandlerFactory;
import org.schabi.newpipe.extractor.utils.Utils;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import java.io.IOException;
import static org.schabi.newpipe.extractor.services.peertube.PeertubeParsingHelper.*;
import static org.schabi.newpipe.extractor.utils.Utils.isNullOrEmpty;
public class PeertubeChannelTabExtractor extends ChannelTabExtractor {
private JsonObject initialJson;
private final String baseUrl;
private String initialUrl;
public PeertubeChannelTabExtractor(final StreamingService service,
final ChannelTabHandler linkHandler) throws ParsingException {
super(service, linkHandler);
baseUrl = getBaseUrl();
initialUrl = this.baseUrl + PeertubeChannelLinkHandlerFactory.API_ENDPOINT + getId() + "/video-playlists?"
+ START_KEY + "=0&" + COUNT_KEY + "=" + ITEMS_PER_PAGE;
}
@Override
public void onFetchPage(@Nonnull Downloader downloader) throws IOException, ExtractionException {
if (getLinkHandler().getTab() != ChannelTabHandler.Tab.Playlists) {
throw new ExtractionException("tab " + getLinkHandler().getTab().name() + " not supported");
}
final Response response = downloader.get(initialUrl);
if (response != null) {
setInitialData(response.responseBody());
} else {
throw new ExtractionException("Unable to extract PeerTube channel playlist data");
}
}
private void setInitialData(final String responseBody) throws ExtractionException {
try {
initialJson = JsonParser.object().from(responseBody);
} catch (final JsonParserException e) {
throw new ExtractionException("Unable to extract PeerTube channel playlist data", e);
}
if (initialJson == null) {
throw new ExtractionException("Unable to extract PeerTube channel playlist data");
}
}
@Nonnull
@Override
public String getName() throws ParsingException {
return initialJson.getArray("data").getObject(0)
.getObject("ownerAccount").getString("displayName", "");
}
@Nonnull
@Override
public InfoItemsPage<InfoItem> getInitialPage() throws IOException, ExtractionException {
return parsePage(initialJson, initialUrl);
}
@Override
public InfoItemsPage<InfoItem> getPage(Page page) throws IOException, ExtractionException {
if (page == null || isNullOrEmpty(page.getUrl())) {
throw new IllegalArgumentException("Page doesn't contain an URL");
}
final Response response = getDownloader().get(page.getUrl());
JsonObject pageJson = null;
if (response != null && !Utils.isBlank(response.responseBody())) {
try {
pageJson = JsonParser.object().from(response.responseBody());
} catch (final Exception e) {
throw new ParsingException("Could not parse json data for account info", e);
}
}
return parsePage(pageJson, page.getUrl());
}
private InfoItemsPage<InfoItem> parsePage(@Nullable JsonObject json, String pageUrl)
throws ExtractionException {
if (json == null) {
throw new ExtractionException("Unable to get channel playlist list");
}
PeertubeParsingHelper.validate(json);
final MultiInfoItemsCollector collector = new MultiInfoItemsCollector(getServiceId());
JsonArray contents = json.getArray("data");
if (contents == null) {
throw new ParsingException("Unable to extract channel playlist list");
}
for (final Object c : contents) {
if (c instanceof JsonObject) {
collector.commit(new PeertubePlaylistInfoItemExtractor((JsonObject) c, baseUrl));
}
}
return new InfoItemsPage<>(
collector, PeertubeParsingHelper.getNextPage(pageUrl, json.getLong("total")));
}
}

View File

@ -0,0 +1,54 @@
package org.schabi.newpipe.extractor.services.peertube.extractors;
import com.grack.nanojson.JsonObject;
import org.schabi.newpipe.extractor.ServiceList;
import org.schabi.newpipe.extractor.exceptions.ParsingException;
import org.schabi.newpipe.extractor.playlist.PlaylistInfo;
import org.schabi.newpipe.extractor.playlist.PlaylistInfoItemExtractor;
import org.schabi.newpipe.extractor.utils.JsonUtils;
import javax.annotation.Nonnull;
public class PeertubePlaylistInfoItemExtractor implements PlaylistInfoItemExtractor {
protected final JsonObject item;
private final String baseUrl;
public PeertubePlaylistInfoItemExtractor(final JsonObject item, final String baseUrl) {
this.item = item;
this.baseUrl = baseUrl;
}
@Override
public String getUrl() throws ParsingException {
final String uuid = JsonUtils.getString(item, "uuid");
return ServiceList.PeerTube.getPlaylistLHFactory().fromId(uuid, baseUrl).getUrl();
}
@Override
public String getThumbnailUrl() throws ParsingException {
return baseUrl + JsonUtils.getString(item, "thumbnailPath");
}
@Override
public String getName() throws ParsingException {
return JsonUtils.getString(item, "displayName");
}
@Override
public String getUploaderName() throws ParsingException {
JsonObject owner = JsonUtils.getObject(item, "ownerAccount");
return JsonUtils.getString(owner, "displayName");
}
@Override
public long getStreamCount() throws ParsingException {
return JsonUtils.getNumber(item, "videosLength").longValue();
}
@Nonnull
@Override
public PlaylistInfo.PlaylistType getPlaylistType() throws ParsingException {
return PlaylistInfoItemExtractor.super.getPlaylistType();
}
}

View File

@ -26,15 +26,7 @@ import java.util.List;
import java.util.Map;
import java.util.function.Consumer;
import static org.schabi.newpipe.extractor.services.youtube.YoutubeParsingHelper.ChannelResponseData;
import static org.schabi.newpipe.extractor.services.youtube.YoutubeParsingHelper.DISABLE_PRETTY_PRINT_PARAMETER;
import static org.schabi.newpipe.extractor.services.youtube.YoutubeParsingHelper.YOUTUBEI_V1_URL;
import static org.schabi.newpipe.extractor.services.youtube.YoutubeParsingHelper.addClientInfoHeaders;
import static org.schabi.newpipe.extractor.services.youtube.YoutubeParsingHelper.getChannelResponse;
import static org.schabi.newpipe.extractor.services.youtube.YoutubeParsingHelper.getKey;
import static org.schabi.newpipe.extractor.services.youtube.YoutubeParsingHelper.getValidJsonResponseBody;
import static org.schabi.newpipe.extractor.services.youtube.YoutubeParsingHelper.prepareDesktopJsonBuilder;
import static org.schabi.newpipe.extractor.services.youtube.YoutubeParsingHelper.resolveChannelId;
import static org.schabi.newpipe.extractor.services.youtube.YoutubeParsingHelper.*;
import static org.schabi.newpipe.extractor.utils.Utils.isNullOrEmpty;
public class YoutubeChannelTabExtractor extends ChannelTabExtractor {
@ -82,7 +74,7 @@ public class YoutubeChannelTabExtractor extends ChannelTabExtractor {
ExtractionException {
final String params = getParams();
if (params == null) {
throw new ExtractionException("tab not supported");
throw new ExtractionException("tab " + getLinkHandler().getTab().name() + " not supported");
}
final String id = resolveChannelId(super.getId());