11package org .schabi .newpipe .extractor .services .soundcloud .extractors ;
22
3+ import static org .schabi .newpipe .extractor .utils .Utils .isNullOrEmpty ;
4+
35import com .grack .nanojson .JsonArray ;
46import com .grack .nanojson .JsonObject ;
7+
58import org .schabi .newpipe .extractor .Page ;
69import org .schabi .newpipe .extractor .ServiceList ;
710import org .schabi .newpipe .extractor .comments .CommentsInfoItem ;
1215import org .schabi .newpipe .extractor .services .soundcloud .SoundcloudParsingHelper ;
1316import org .schabi .newpipe .extractor .stream .Description ;
1417
15- import javax .annotation .Nullable ;
16- import java .util .ArrayList ;
17- import java .util .List ;
1818import java .util .Objects ;
1919
20+ import javax .annotation .Nullable ;
21+
2022public class SoundcloudCommentsInfoItemExtractor implements CommentsInfoItemExtractor {
21- public static final String USER = "user" ;
2223 public static final String BODY = "body" ;
2324 public static final String USER_PERMALINK = "permalink" ;
25+ public static final String USER_FULL_NAME = "full_name" ;
26+ public static final String USER_USERNAME = "username" ;
2427
2528 private final JsonObject json ;
2629 private final int index ;
2730 private final JsonObject item ;
2831 private final String url ;
32+ private final JsonObject user ;
2933 private final JsonObject superComment ;
3034
3135 private int replyCount = CommentsInfoItem .UNKNOWN_REPLY_COUNT ;
@@ -39,6 +43,7 @@ public SoundcloudCommentsInfoItemExtractor(final JsonObject json, final int inde
3943 this .item = item ;
4044 this .url = url ;
4145 this .superComment = superComment ;
46+ this .user = item .getObject ("user" );
4247 }
4348
4449 public SoundcloudCommentsInfoItemExtractor (final JsonObject json , final int index ,
@@ -50,7 +55,6 @@ public SoundcloudCommentsInfoItemExtractor(final JsonObject json, final int inde
5055 public String getCommentId () {
5156 return Objects .toString (item .getLong ("id" ), null );
5257 }
53-
5458 @ Override
5559 public Description getCommentText () {
5660 String commentContent = item .getString (BODY );
@@ -61,32 +65,49 @@ public Description getCommentText() {
6165 // Therefore, the comment starts with the mention of the original comment's author.
6266 // The account is automatically linked by the SoundCloud web UI.
6367 // We need to do this manually.
64- final JsonObject user = superComment .getObject ("user" );
65- final String link = "<a href=\" " + user .getString ("permalink_url" ) + "\" >"
66- + "@" + user .getString ("full_name" ) + "</a>" ;
67- commentContent = commentContent
68- .replace ("@" + user .getString (USER_PERMALINK ), link )
69- .replace ("@" + superComment .getInt ("user_id" ), link );
68+ if (commentContent .startsWith ("@" )) {
69+ final String authorName = commentContent .split (" " , 2 )[0 ].replace ("@" , "" );
70+ final JsonArray comments = json .getArray (SoundcloudCommentsExtractor .COLLECTION );
71+ JsonObject author = null ;
72+ for (int i = index - 1 ; i >= 0 && author == null ; i --) {
73+ final JsonObject commentsAuthor = comments .getObject (i ).getObject ("user" );
74+ // use startsWith because sometimes the mention of the user
75+ // is followed by a punctuation character.
76+ if (authorName .startsWith (commentsAuthor .getString (USER_PERMALINK ))) {
77+ author = commentsAuthor ;
78+ }
79+ }
80+ if (author == null ) {
81+ author = superComment .getObject ("user" );
82+ }
83+ final String name = isNullOrEmpty (author .getString (USER_FULL_NAME ))
84+ ? author .getString (USER_USERNAME ) : author .getString (USER_FULL_NAME );
85+ final String link = "<a href=\" " + author .getString ("permalink_url" ) + "\" >"
86+ + "@" + name + "</a>" ;
87+ commentContent = commentContent
88+ .replace ("@" + author .getString (USER_PERMALINK ), link )
89+ .replace ("@" + author .getInt ("user_id" ), link );
90+ }
7091
7192 return new Description (commentContent , Description .HTML );
7293 }
7394
7495 @ Override
7596 public String getUploaderName () {
76- if (isNullOrEmpty (user .getString ("full_name" ))) {
77- return user .getString ("username" );
97+ if (isNullOrEmpty (user .getString (USER_FULL_NAME ))) {
98+ return user .getString (USER_USERNAME );
7899 }
79- return user .getString ("full_name" );
100+ return user .getString (USER_FULL_NAME );
80101 }
81102
82103 @ Override
83104 public String getUploaderAvatarUrl () {
84- return item . getObject ( USER ) .getString ("avatar_url" );
105+ return user .getString ("avatar_url" );
85106 }
86107
87108 @ Override
88109 public boolean isUploaderVerified () throws ParsingException {
89- return item . getObject ( USER ) .getBoolean ("verified" );
110+ return user .getBoolean ("verified" );
90111 }
91112
92113 @ Override
@@ -96,7 +117,7 @@ public int getStreamPosition() throws ParsingException {
96117
97118 @ Override
98119 public String getUploaderUrl () {
99- return item . getObject ( USER ) .getString ("permalink_url" );
120+ return user .getString ("permalink_url" );
100121 }
101122
102123 @ Override
@@ -112,7 +133,7 @@ public DateWrapper getUploadDate() throws ParsingException {
112133
113134 @ Override
114135 public String getName () throws ParsingException {
115- return item . getObject ( USER ). getString ("permalink" );
136+ return user . getString (USER_PERMALINK );
116137 }
117138
118139 @ Override
@@ -122,38 +143,39 @@ public String getUrl() {
122143
123144 @ Override
124145 public String getThumbnailUrl () {
125- return item . getObject ( USER ) .getString ("avatar_url" );
146+ return user .getString ("avatar_url" );
126147 }
127148
128149 @ Override
129150 public Page getReplies () {
130151 if (replyCount == CommentsInfoItem .UNKNOWN_REPLY_COUNT ) {
131- final List < JsonObject > replies = new ArrayList <> ();
152+ final JsonArray replies = new JsonArray ();
132153 final CommentsInfoItemsCollector collector = new CommentsInfoItemsCollector (
133154 ServiceList .SoundCloud .getServiceId ());
134- final JsonArray jsonArray = new JsonArray ();
135- // Replies start with the mention of the user who created the original comment.
136- final String mention = "@" + item .getObject (USER ).getString (USER_PERMALINK );
137- // Loop through all comments which come after the original comment to find its replies.
138- final JsonArray allItems = json .getArray (SoundcloudCommentsExtractor .COLLECTION );
139- for (int i = index + 1 ; i < allItems .size (); i ++) {
140- final JsonObject comment = allItems .getObject (i );
141- final String commentContent = comment .getString ("body" );
142- if (commentContent .startsWith (mention )) {
143- replies .add (comment );
144- jsonArray .add (comment );
145- collector .commit (new SoundcloudCommentsInfoItemExtractor (
146- json , i , comment , url , item ));
147- } else if (!commentContent .startsWith ("@" ) || replies .isEmpty ()) {
148- // Only the comments directly after the original comment
149- // starting with the mention of the comment's creator
150- // are replies to the original comment.
151- // The first comment not starting with these letters
152- // is the next top-level comment.
153- break ;
155+ // SoundCloud has only comments and top level replies, but not nested replies.
156+ // Therefore, replies cannot have further replies.
157+ if (superComment == null ) {
158+ // Loop through all comments which come after the original comment
159+ // to find its replies.
160+ final JsonArray allItems = json .getArray (SoundcloudCommentsExtractor .COLLECTION );
161+ boolean foundReply = false ;
162+ for (int i = index + 1 ; i < allItems .size (); i ++) {
163+ final JsonObject comment = allItems .getObject (i );
164+ if (SoundcloudParsingHelper .isReplyTo (item , comment )) {
165+ replies .add (comment );
166+ collector .commit (new SoundcloudCommentsInfoItemExtractor (
167+ json , i , comment , url , item ));
168+ foundReply = true ;
169+ } else if (foundReply ) {
170+ // Only the comments directly after the original comment
171+ // having the same timestamp are replies to the original comment.
172+ // The first comment not having the same timestamp
173+ // is the next top-level comment.
174+ break ;
175+ }
154176 }
155177 }
156- replyCount = jsonArray .size ();
178+ replyCount = replies .size ();
157179 if (collector .getItems ().isEmpty ()) {
158180 return null ;
159181 }
@@ -165,7 +187,7 @@ public Page getReplies() {
165187 }
166188
167189 @ Override
168- public int getReplyCount () throws ParsingException {
190+ public int getReplyCount () {
169191 if (replyCount == CommentsInfoItem .UNKNOWN_REPLY_COUNT ) {
170192 getReplies ();
171193 }
0 commit comments