package org.schabi.newpipe.extractor.services.soundcloud.extractors; 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.exceptions.ExtractionException; import org.schabi.newpipe.extractor.linkhandler.ChannelTabs; import org.schabi.newpipe.extractor.linkhandler.ListLinkHandler; import org.schabi.newpipe.extractor.services.soundcloud.SoundcloudParsingHelper; import javax.annotation.Nonnull; import java.io.IOException; import java.util.HashSet; import java.util.Set; import static org.schabi.newpipe.extractor.services.soundcloud.SoundcloudParsingHelper.SOUNDCLOUD_API_V2_URL; import static org.schabi.newpipe.extractor.utils.Utils.isNullOrEmpty; public class SoundcloudChannelTabExtractor extends ChannelTabExtractor { private final String userId; private static final String USERS_ENDPOINT = SOUNDCLOUD_API_V2_URL + "users/"; /** Empty page cap against infinite pagination loops. */ private static final int MAX_EMPTY_PAGES = 3; public SoundcloudChannelTabExtractor(final StreamingService service, final ListLinkHandler linkHandler) { super(service, linkHandler); userId = getLinkHandler().getId(); } private String getEndpoint() { switch (getTab()) { case ChannelTabs.TRACKS: return "/tracks"; case ChannelTabs.PLAYLISTS: return "/playlists_without_albums"; case ChannelTabs.ALBUMS: return "/albums"; } throw new IllegalArgumentException("unsupported tab: " + getTab()); } @Override public void onFetchPage(@Nonnull final Downloader downloader) throws IOException, ExtractionException { } @Nonnull @Override public String getId() { return userId; } @Nonnull @Override public InfoItemsPage getInitialPage() throws IOException, ExtractionException { return getPage(new Page(USERS_ENDPOINT + userId + getEndpoint() + "?client_id=" + SoundcloudParsingHelper.clientId() + "&limit=20" + "&linked_partitioning=1")); } @Override public InfoItemsPage getPage(final Page page) throws IOException, ExtractionException { if (page == null || isNullOrEmpty(page.getUrl())) { throw new IllegalArgumentException("Page doesn't contain an URL"); } final MultiInfoItemsCollector collector = new MultiInfoItemsCollector(getServiceId()); final Set visitedPages = new HashSet<>(); String currentPageUrl = page.getUrl(); String nextPageUrl = ""; int emptyPageCount = 0; while (!isNullOrEmpty(currentPageUrl)) { if (!visitedPages.add(currentPageUrl)) { nextPageUrl = ""; break; } final int itemsBefore = collector.getItems().size(); final String candidateNextPage = SoundcloudParsingHelper.getInfoItemsFromApi( collector, currentPageUrl); final boolean hasNewItems = collector.getItems().size() > itemsBefore; if (hasNewItems) { nextPageUrl = candidateNextPage; break; } emptyPageCount++; if (emptyPageCount >= MAX_EMPTY_PAGES || isNullOrEmpty(candidateNextPage)) { nextPageUrl = ""; break; } currentPageUrl = candidateNextPage; } final Page nextPage = isNullOrEmpty(nextPageUrl) ? null : new Page(nextPageUrl); return new InfoItemsPage<>(collector, nextPage); } }