Skip to content

Commit d4352f9

Browse files
committed
support comments for SoundCloud
1 parent 636c430 commit d4352f9

9 files changed

Lines changed: 250 additions & 13 deletions

extractor/src/main/java/org/schabi/newpipe/extractor/comments/CommentsInfoItem.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,8 @@ public class CommentsInfoItem extends InfoItem {
1313
private String authorThumbnail;
1414
private String authorEndpoint;
1515
private String textualPublishedTime;
16-
@Nullable private DateWrapper publishedTime;
16+
@Nullable
17+
private DateWrapper publishedTime;
1718
private int likeCount;
1819

1920
public CommentsInfoItem(int serviceId, String url, String name) {

extractor/src/main/java/org/schabi/newpipe/extractor/comments/CommentsInfoItemExtractor.java

Lines changed: 32 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -3,17 +3,44 @@
33
import org.schabi.newpipe.extractor.InfoItemExtractor;
44
import org.schabi.newpipe.extractor.exceptions.ParsingException;
55
import org.schabi.newpipe.extractor.localization.DateWrapper;
6+
import org.schabi.newpipe.extractor.stream.StreamExtractor;
67

78
import javax.annotation.Nullable;
89

910
public interface CommentsInfoItemExtractor extends InfoItemExtractor {
10-
String getCommentId() throws ParsingException;
11-
String getCommentText() throws ParsingException;
12-
String getAuthorName() throws ParsingException;
13-
String getAuthorThumbnail() throws ParsingException;
11+
12+
/**
13+
* AuthorEndpoint, in other words, link to authors' channel page
14+
*/
1415
String getAuthorEndpoint() throws ParsingException;
16+
17+
/**
18+
* Return the like count of the comment, or -1 if it's unavailable
19+
* see {@link StreamExtractor#getLikeCount()}
20+
*/
21+
int getLikeCount() throws ParsingException;
22+
23+
/**
24+
* The text of the comment
25+
*/
26+
String getCommentText() throws ParsingException;
27+
28+
/**
29+
* The upload date given by the service, unmodified
30+
* see {@link StreamExtractor#getTextualUploadDate()}
31+
*/
1532
String getTextualPublishedTime() throws ParsingException;
33+
34+
/**
35+
* The upload date wrapped with DateWrapper class
36+
* see {@link StreamExtractor#getUploadDate()}
37+
*/
1638
@Nullable
1739
DateWrapper getPublishedTime() throws ParsingException;
18-
int getLikeCount() throws ParsingException;
40+
41+
String getCommentId() throws ParsingException;
42+
43+
String getAuthorName() throws ParsingException;
44+
45+
String getAuthorThumbnail() throws ParsingException;
1946
}

extractor/src/main/java/org/schabi/newpipe/extractor/services/soundcloud/SoundcloudParsingHelper.java

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -88,10 +88,12 @@ static boolean checkIfHardcodedClientIdIsValid() {
8888
}
8989
}
9090

91-
public static Calendar parseDate(String textualUploadDate) throws ParsingException {
91+
public static Calendar parseDateFrom(String textualUploadDate) throws ParsingException {
9292
Date date;
9393
try {
94-
date = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss'Z'").parse(textualUploadDate);
94+
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss'Z'");
95+
sdf.setTimeZone(TimeZone.getTimeZone("GMT"));
96+
date = sdf.parse(textualUploadDate);
9597
} catch (ParseException e1) {
9698
try {
9799
date = new SimpleDateFormat("yyyy/MM/dd HH:mm:ss +0000").parse(textualUploadDate);

extractor/src/main/java/org/schabi/newpipe/extractor/services/soundcloud/SoundcloudService.java

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -17,13 +17,15 @@
1717

1818
import java.util.List;
1919

20+
import static java.util.Arrays.asList;
2021
import static java.util.Collections.singletonList;
2122
import static org.schabi.newpipe.extractor.StreamingService.ServiceInfo.MediaCapability.AUDIO;
23+
import static org.schabi.newpipe.extractor.StreamingService.ServiceInfo.MediaCapability.COMMENTS;
2224

2325
public class SoundcloudService extends StreamingService {
2426

2527
public SoundcloudService(int id) {
26-
super(id, "SoundCloud", singletonList(AUDIO));
28+
super(id, "SoundCloud", asList(AUDIO, COMMENTS));
2729
}
2830

2931
@Override
@@ -119,13 +121,13 @@ public SubscriptionExtractor getSubscriptionExtractor() {
119121

120122
@Override
121123
public ListLinkHandlerFactory getCommentsLHFactory() {
122-
return null;
124+
return SoundcloudCommentsLinkHandlerFactory.getInstance();
123125
}
124126

125127
@Override
126128
public CommentsExtractor getCommentsExtractor(ListLinkHandler linkHandler)
127129
throws ExtractionException {
128-
return null;
130+
return new SoundcloudCommentsExtractor(this, linkHandler);
129131
}
130132

131133
}
Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,82 @@
1+
package org.schabi.newpipe.extractor.services.soundcloud.extractors;
2+
3+
import com.grack.nanojson.JsonArray;
4+
import com.grack.nanojson.JsonObject;
5+
import com.grack.nanojson.JsonParser;
6+
import com.grack.nanojson.JsonParserException;
7+
import org.schabi.newpipe.extractor.NewPipe;
8+
import org.schabi.newpipe.extractor.StreamingService;
9+
import org.schabi.newpipe.extractor.comments.CommentsExtractor;
10+
import org.schabi.newpipe.extractor.comments.CommentsInfoItem;
11+
import org.schabi.newpipe.extractor.comments.CommentsInfoItemsCollector;
12+
import org.schabi.newpipe.extractor.downloader.Downloader;
13+
import org.schabi.newpipe.extractor.downloader.Response;
14+
import org.schabi.newpipe.extractor.exceptions.ExtractionException;
15+
import org.schabi.newpipe.extractor.exceptions.ParsingException;
16+
import org.schabi.newpipe.extractor.linkhandler.ListLinkHandler;
17+
18+
import javax.annotation.Nonnull;
19+
import java.io.IOException;
20+
21+
public class SoundcloudCommentsExtractor extends CommentsExtractor {
22+
23+
private JsonObject json;
24+
25+
public SoundcloudCommentsExtractor(StreamingService service, ListLinkHandler uiHandler) {
26+
super(service, uiHandler);
27+
}
28+
29+
@Nonnull
30+
@Override
31+
public InfoItemsPage<CommentsInfoItem> getInitialPage() throws IOException, ExtractionException {
32+
final CommentsInfoItemsCollector collector = new CommentsInfoItemsCollector(getServiceId());
33+
34+
collectStreamsFrom(collector, json.getArray("collection"));
35+
36+
return new InfoItemsPage<>(collector, getNextPageUrl());
37+
}
38+
39+
@Override
40+
public String getNextPageUrl() throws IOException, ExtractionException {
41+
return json.getString("next_href");
42+
}
43+
44+
@Override
45+
public InfoItemsPage<CommentsInfoItem> getPage(String pageUrl) throws IOException, ExtractionException {
46+
Downloader dl = NewPipe.getDownloader();
47+
Response rp = dl.get(pageUrl);
48+
try {
49+
json = JsonParser.object().from(rp.responseBody());
50+
} catch (JsonParserException e) {
51+
throw new ParsingException("Could not parse json", e);
52+
}
53+
54+
final CommentsInfoItemsCollector collector = new CommentsInfoItemsCollector(getServiceId());
55+
collectStreamsFrom(collector, json.getArray("collection"));
56+
57+
return new InfoItemsPage<>(collector, getNextPageUrl());
58+
}
59+
60+
@Override
61+
public void onFetchPage(@Nonnull Downloader downloader) throws IOException, ExtractionException {
62+
Response response = downloader.get(getUrl());
63+
try {
64+
json = JsonParser.object().from(response.responseBody());
65+
} catch (JsonParserException e) {
66+
throw new ParsingException("Could not parse json", e);
67+
}
68+
}
69+
70+
@Nonnull
71+
@Override
72+
public String getName() throws ParsingException {
73+
return "SoundCloud comments of track " + getId();
74+
}
75+
76+
private void collectStreamsFrom(final CommentsInfoItemsCollector collector, final JsonArray entries) throws ParsingException {
77+
String url = getUrl();
78+
for (Object comment : entries) {
79+
collector.commit(new SoundcloudCommentsInfoItemExtractor((JsonObject) comment, url));
80+
}
81+
}
82+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
package org.schabi.newpipe.extractor.services.soundcloud.extractors;
2+
3+
import com.grack.nanojson.JsonObject;
4+
import org.schabi.newpipe.extractor.comments.CommentsInfoItemExtractor;
5+
import org.schabi.newpipe.extractor.exceptions.ParsingException;
6+
import org.schabi.newpipe.extractor.localization.DateWrapper;
7+
import org.schabi.newpipe.extractor.services.soundcloud.SoundcloudParsingHelper;
8+
9+
import javax.annotation.Nullable;
10+
11+
public class SoundcloudCommentsInfoItemExtractor implements CommentsInfoItemExtractor {
12+
13+
private JsonObject json;
14+
private String url;
15+
16+
public SoundcloudCommentsInfoItemExtractor(JsonObject json, String url) {
17+
this.json = json;
18+
this.url = url;
19+
}
20+
21+
@Override
22+
public String getCommentId() throws ParsingException {
23+
return json.getNumber("id").toString();
24+
}
25+
26+
@Override
27+
public String getCommentText() throws ParsingException {
28+
return json.getString("body");
29+
}
30+
31+
@Override
32+
public String getAuthorName() throws ParsingException {
33+
return json.getObject("user").getString("username");
34+
}
35+
36+
@Override
37+
public String getAuthorThumbnail() throws ParsingException {
38+
return json.getObject("user").getString("avatar_url");
39+
}
40+
41+
@Override
42+
public String getAuthorEndpoint() throws ParsingException {
43+
return json.getObject("user").getString("permalink_url");
44+
}
45+
46+
@Override
47+
public String getTextualPublishedTime() throws ParsingException {
48+
return json.getString("created_at");
49+
}
50+
51+
@Nullable
52+
@Override
53+
public DateWrapper getPublishedTime() throws ParsingException {
54+
return new DateWrapper(SoundcloudParsingHelper.parseDateFrom(getTextualPublishedTime()));
55+
}
56+
57+
@Override
58+
public int getLikeCount() throws ParsingException {
59+
return -1;
60+
}
61+
62+
@Override
63+
public String getName() throws ParsingException {
64+
return json.getObject("user").getString("permalink");
65+
}
66+
67+
@Override
68+
public String getUrl() throws ParsingException {
69+
return url;
70+
}
71+
72+
@Override
73+
public String getThumbnailUrl() throws ParsingException {
74+
return json.getObject("user").getString("avatar_url");
75+
}
76+
}

extractor/src/main/java/org/schabi/newpipe/extractor/services/soundcloud/extractors/SoundcloudStreamExtractor.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -73,7 +73,7 @@ public String getTextualUploadDate() throws ParsingException {
7373
@Nonnull
7474
@Override
7575
public DateWrapper getUploadDate() throws ParsingException {
76-
return new DateWrapper(SoundcloudParsingHelper.parseDate(track.getString("created_at")));
76+
return new DateWrapper(SoundcloudParsingHelper.parseDateFrom(track.getString("created_at")));
7777
}
7878

7979
@Nonnull

extractor/src/main/java/org/schabi/newpipe/extractor/services/soundcloud/extractors/SoundcloudStreamInfoItemExtractor.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,7 @@ public String getTextualUploadDate() {
4949

5050
@Override
5151
public DateWrapper getUploadDate() throws ParsingException {
52-
return new DateWrapper(SoundcloudParsingHelper.parseDate(getTextualUploadDate()));
52+
return new DateWrapper(SoundcloudParsingHelper.parseDateFrom(getTextualUploadDate()));
5353
}
5454

5555
private String getCreatedAt() {
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
package org.schabi.newpipe.extractor.services.soundcloud.linkHandler;
2+
3+
import org.schabi.newpipe.extractor.exceptions.ExtractionException;
4+
import org.schabi.newpipe.extractor.exceptions.ParsingException;
5+
import org.schabi.newpipe.extractor.linkhandler.ListLinkHandlerFactory;
6+
7+
import java.io.IOException;
8+
import java.util.List;
9+
10+
import static org.schabi.newpipe.extractor.services.soundcloud.SoundcloudParsingHelper.clientId;
11+
12+
public class SoundcloudCommentsLinkHandlerFactory extends ListLinkHandlerFactory {
13+
14+
private static final SoundcloudCommentsLinkHandlerFactory instance = new SoundcloudCommentsLinkHandlerFactory();
15+
16+
public static SoundcloudCommentsLinkHandlerFactory getInstance() {
17+
return instance;
18+
}
19+
20+
@Override
21+
public String getUrl(String id, List<String> contentFilter, String sortFilter) throws ParsingException {
22+
try {
23+
return "https://api-v2.soundcloud.com/tracks/" + id + "/comments" + "?client_id=" + clientId() +
24+
"&threaded=0" + "&filter_replies=1"; // anything but 1 = sort by new
25+
// + "&limit=NUMBER_OF_ITEMS_PER_REQUEST". We let the API control (default = 10)
26+
// + "&offset=OFFSET". We let the API control (default = 0, then we use nextPageUrl)
27+
} catch (ExtractionException | IOException e) {
28+
throw new ParsingException("Could not get comments");
29+
}
30+
}
31+
32+
@Override
33+
public String getId(String url) throws ParsingException {
34+
// delagation to avoid duplicate code, as we need the same id
35+
return SoundcloudStreamLinkHandlerFactory.getInstance().getId(url);
36+
}
37+
38+
@Override
39+
public boolean onAcceptUrl(String url) {
40+
try {
41+
getId(url);
42+
return true;
43+
} catch (ParsingException e) {
44+
return false;
45+
}
46+
}
47+
}

0 commit comments

Comments
 (0)