support comments for SoundCloud

This commit is contained in:
bopol 2020-04-10 14:48:11 +02:00
parent 636c430743
commit d4352f9b84
9 changed files with 250 additions and 13 deletions

View File

@ -13,7 +13,8 @@ public class CommentsInfoItem extends InfoItem {
private String authorThumbnail; private String authorThumbnail;
private String authorEndpoint; private String authorEndpoint;
private String textualPublishedTime; private String textualPublishedTime;
@Nullable private DateWrapper publishedTime; @Nullable
private DateWrapper publishedTime;
private int likeCount; private int likeCount;
public CommentsInfoItem(int serviceId, String url, String name) { public CommentsInfoItem(int serviceId, String url, String name) {

View File

@ -3,17 +3,44 @@ package org.schabi.newpipe.extractor.comments;
import org.schabi.newpipe.extractor.InfoItemExtractor; import org.schabi.newpipe.extractor.InfoItemExtractor;
import org.schabi.newpipe.extractor.exceptions.ParsingException; import org.schabi.newpipe.extractor.exceptions.ParsingException;
import org.schabi.newpipe.extractor.localization.DateWrapper; import org.schabi.newpipe.extractor.localization.DateWrapper;
import org.schabi.newpipe.extractor.stream.StreamExtractor;
import javax.annotation.Nullable; import javax.annotation.Nullable;
public interface CommentsInfoItemExtractor extends InfoItemExtractor { public interface CommentsInfoItemExtractor extends InfoItemExtractor {
String getCommentId() throws ParsingException;
String getCommentText() throws ParsingException; /**
String getAuthorName() throws ParsingException; * AuthorEndpoint, in other words, link to authors' channel page
String getAuthorThumbnail() throws ParsingException; */
String getAuthorEndpoint() throws ParsingException; String getAuthorEndpoint() throws ParsingException;
/**
* Return the like count of the comment, or -1 if it's unavailable
* see {@link StreamExtractor#getLikeCount()}
*/
int getLikeCount() throws ParsingException;
/**
* The text of the comment
*/
String getCommentText() throws ParsingException;
/**
* The upload date given by the service, unmodified
* see {@link StreamExtractor#getTextualUploadDate()}
*/
String getTextualPublishedTime() throws ParsingException; String getTextualPublishedTime() throws ParsingException;
/**
* The upload date wrapped with DateWrapper class
* see {@link StreamExtractor#getUploadDate()}
*/
@Nullable @Nullable
DateWrapper getPublishedTime() throws ParsingException; DateWrapper getPublishedTime() throws ParsingException;
int getLikeCount() throws ParsingException;
String getCommentId() throws ParsingException;
String getAuthorName() throws ParsingException;
String getAuthorThumbnail() throws ParsingException;
} }

View File

@ -88,10 +88,12 @@ public class SoundcloudParsingHelper {
} }
} }
public static Calendar parseDate(String textualUploadDate) throws ParsingException { public static Calendar parseDateFrom(String textualUploadDate) throws ParsingException {
Date date; Date date;
try { try {
date = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss'Z'").parse(textualUploadDate); SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss'Z'");
sdf.setTimeZone(TimeZone.getTimeZone("GMT"));
date = sdf.parse(textualUploadDate);
} catch (ParseException e1) { } catch (ParseException e1) {
try { try {
date = new SimpleDateFormat("yyyy/MM/dd HH:mm:ss +0000").parse(textualUploadDate); date = new SimpleDateFormat("yyyy/MM/dd HH:mm:ss +0000").parse(textualUploadDate);

View File

@ -17,13 +17,15 @@ import org.schabi.newpipe.extractor.subscription.SubscriptionExtractor;
import java.util.List; import java.util.List;
import static java.util.Arrays.asList;
import static java.util.Collections.singletonList; import static java.util.Collections.singletonList;
import static org.schabi.newpipe.extractor.StreamingService.ServiceInfo.MediaCapability.AUDIO; import static org.schabi.newpipe.extractor.StreamingService.ServiceInfo.MediaCapability.AUDIO;
import static org.schabi.newpipe.extractor.StreamingService.ServiceInfo.MediaCapability.COMMENTS;
public class SoundcloudService extends StreamingService { public class SoundcloudService extends StreamingService {
public SoundcloudService(int id) { public SoundcloudService(int id) {
super(id, "SoundCloud", singletonList(AUDIO)); super(id, "SoundCloud", asList(AUDIO, COMMENTS));
} }
@Override @Override
@ -119,13 +121,13 @@ public class SoundcloudService extends StreamingService {
@Override @Override
public ListLinkHandlerFactory getCommentsLHFactory() { public ListLinkHandlerFactory getCommentsLHFactory() {
return null; return SoundcloudCommentsLinkHandlerFactory.getInstance();
} }
@Override @Override
public CommentsExtractor getCommentsExtractor(ListLinkHandler linkHandler) public CommentsExtractor getCommentsExtractor(ListLinkHandler linkHandler)
throws ExtractionException { throws ExtractionException {
return null; return new SoundcloudCommentsExtractor(this, linkHandler);
} }
} }

View File

@ -0,0 +1,82 @@
package org.schabi.newpipe.extractor.services.soundcloud.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.NewPipe;
import org.schabi.newpipe.extractor.StreamingService;
import org.schabi.newpipe.extractor.comments.CommentsExtractor;
import org.schabi.newpipe.extractor.comments.CommentsInfoItem;
import org.schabi.newpipe.extractor.comments.CommentsInfoItemsCollector;
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.ListLinkHandler;
import javax.annotation.Nonnull;
import java.io.IOException;
public class SoundcloudCommentsExtractor extends CommentsExtractor {
private JsonObject json;
public SoundcloudCommentsExtractor(StreamingService service, ListLinkHandler uiHandler) {
super(service, uiHandler);
}
@Nonnull
@Override
public InfoItemsPage<CommentsInfoItem> getInitialPage() throws IOException, ExtractionException {
final CommentsInfoItemsCollector collector = new CommentsInfoItemsCollector(getServiceId());
collectStreamsFrom(collector, json.getArray("collection"));
return new InfoItemsPage<>(collector, getNextPageUrl());
}
@Override
public String getNextPageUrl() throws IOException, ExtractionException {
return json.getString("next_href");
}
@Override
public InfoItemsPage<CommentsInfoItem> getPage(String pageUrl) throws IOException, ExtractionException {
Downloader dl = NewPipe.getDownloader();
Response rp = dl.get(pageUrl);
try {
json = JsonParser.object().from(rp.responseBody());
} catch (JsonParserException e) {
throw new ParsingException("Could not parse json", e);
}
final CommentsInfoItemsCollector collector = new CommentsInfoItemsCollector(getServiceId());
collectStreamsFrom(collector, json.getArray("collection"));
return new InfoItemsPage<>(collector, getNextPageUrl());
}
@Override
public void onFetchPage(@Nonnull Downloader downloader) throws IOException, ExtractionException {
Response response = downloader.get(getUrl());
try {
json = JsonParser.object().from(response.responseBody());
} catch (JsonParserException e) {
throw new ParsingException("Could not parse json", e);
}
}
@Nonnull
@Override
public String getName() throws ParsingException {
return "SoundCloud comments of track " + getId();
}
private void collectStreamsFrom(final CommentsInfoItemsCollector collector, final JsonArray entries) throws ParsingException {
String url = getUrl();
for (Object comment : entries) {
collector.commit(new SoundcloudCommentsInfoItemExtractor((JsonObject) comment, url));
}
}
}

View File

@ -0,0 +1,76 @@
package org.schabi.newpipe.extractor.services.soundcloud.extractors;
import com.grack.nanojson.JsonObject;
import org.schabi.newpipe.extractor.comments.CommentsInfoItemExtractor;
import org.schabi.newpipe.extractor.exceptions.ParsingException;
import org.schabi.newpipe.extractor.localization.DateWrapper;
import org.schabi.newpipe.extractor.services.soundcloud.SoundcloudParsingHelper;
import javax.annotation.Nullable;
public class SoundcloudCommentsInfoItemExtractor implements CommentsInfoItemExtractor {
private JsonObject json;
private String url;
public SoundcloudCommentsInfoItemExtractor(JsonObject json, String url) {
this.json = json;
this.url = url;
}
@Override
public String getCommentId() throws ParsingException {
return json.getNumber("id").toString();
}
@Override
public String getCommentText() throws ParsingException {
return json.getString("body");
}
@Override
public String getAuthorName() throws ParsingException {
return json.getObject("user").getString("username");
}
@Override
public String getAuthorThumbnail() throws ParsingException {
return json.getObject("user").getString("avatar_url");
}
@Override
public String getAuthorEndpoint() throws ParsingException {
return json.getObject("user").getString("permalink_url");
}
@Override
public String getTextualPublishedTime() throws ParsingException {
return json.getString("created_at");
}
@Nullable
@Override
public DateWrapper getPublishedTime() throws ParsingException {
return new DateWrapper(SoundcloudParsingHelper.parseDateFrom(getTextualPublishedTime()));
}
@Override
public int getLikeCount() throws ParsingException {
return -1;
}
@Override
public String getName() throws ParsingException {
return json.getObject("user").getString("permalink");
}
@Override
public String getUrl() throws ParsingException {
return url;
}
@Override
public String getThumbnailUrl() throws ParsingException {
return json.getObject("user").getString("avatar_url");
}
}

View File

@ -73,7 +73,7 @@ public class SoundcloudStreamExtractor extends StreamExtractor {
@Nonnull @Nonnull
@Override @Override
public DateWrapper getUploadDate() throws ParsingException { public DateWrapper getUploadDate() throws ParsingException {
return new DateWrapper(SoundcloudParsingHelper.parseDate(track.getString("created_at"))); return new DateWrapper(SoundcloudParsingHelper.parseDateFrom(track.getString("created_at")));
} }
@Nonnull @Nonnull

View File

@ -49,7 +49,7 @@ public class SoundcloudStreamInfoItemExtractor implements StreamInfoItemExtracto
@Override @Override
public DateWrapper getUploadDate() throws ParsingException { public DateWrapper getUploadDate() throws ParsingException {
return new DateWrapper(SoundcloudParsingHelper.parseDate(getTextualUploadDate())); return new DateWrapper(SoundcloudParsingHelper.parseDateFrom(getTextualUploadDate()));
} }
private String getCreatedAt() { private String getCreatedAt() {

View File

@ -0,0 +1,47 @@
package org.schabi.newpipe.extractor.services.soundcloud.linkHandler;
import org.schabi.newpipe.extractor.exceptions.ExtractionException;
import org.schabi.newpipe.extractor.exceptions.ParsingException;
import org.schabi.newpipe.extractor.linkhandler.ListLinkHandlerFactory;
import java.io.IOException;
import java.util.List;
import static org.schabi.newpipe.extractor.services.soundcloud.SoundcloudParsingHelper.clientId;
public class SoundcloudCommentsLinkHandlerFactory extends ListLinkHandlerFactory {
private static final SoundcloudCommentsLinkHandlerFactory instance = new SoundcloudCommentsLinkHandlerFactory();
public static SoundcloudCommentsLinkHandlerFactory getInstance() {
return instance;
}
@Override
public String getUrl(String id, List<String> contentFilter, String sortFilter) throws ParsingException {
try {
return "https://api-v2.soundcloud.com/tracks/" + id + "/comments" + "?client_id=" + clientId() +
"&threaded=0" + "&filter_replies=1"; // anything but 1 = sort by new
// + "&limit=NUMBER_OF_ITEMS_PER_REQUEST". We let the API control (default = 10)
// + "&offset=OFFSET". We let the API control (default = 0, then we use nextPageUrl)
} catch (ExtractionException | IOException e) {
throw new ParsingException("Could not get comments");
}
}
@Override
public String getId(String url) throws ParsingException {
// delagation to avoid duplicate code, as we need the same id
return SoundcloudStreamLinkHandlerFactory.getInstance().getId(url);
}
@Override
public boolean onAcceptUrl(String url) {
try {
getId(url);
return true;
} catch (ParsingException e) {
return false;
}
}
}