11package org .schabi .newpipe .extractor .services .youtube .extractors ;
22
3+ import static org .schabi .newpipe .extractor .services .youtube .linkHandler .YoutubeParsingHelper .fixThumbnailUrl ;
34import static org .schabi .newpipe .extractor .services .youtube .linkHandler .YoutubeParsingHelper .getJsonResponse ;
45
56import com .grack .nanojson .JsonArray ;
2930public class YoutubeMixPlaylistExtractor extends PlaylistExtractor {
3031
3132 private JsonObject initialData ;
32- private Document doc ;
33+ private JsonObject playlistData ;
3334
3435 public YoutubeMixPlaylistExtractor (StreamingService service , ListLinkHandler linkHandler ) {
3536 super (service , linkHandler );
@@ -40,18 +41,17 @@ public void onFetchPage(@Nonnull Downloader downloader) throws IOException, Extr
4041 final String url = getUrl () + "&pbj=1" ;
4142 final JsonArray ajaxJson = getJsonResponse (url , getExtractorLocalization ());
4243 initialData = ajaxJson .getObject (3 ).getObject ("response" );
43- JsonObject a = initialData .getObject ("contents" );
44- JsonObject b = initialData .getObject ("contents" ).getObject ("twoColumnWatchNextResults" );
45- JsonObject c = initialData .getObject ("contents" );
46- JsonObject playlist = initialData .getObject ("contents" ).getObject ("twoColumnWatchNextResults" ).getObject ("playlist" ).getObject ("playlist" );
44+ playlistData = initialData .getObject ("contents" ).getObject ("twoColumnWatchNextResults" ).getObject ("playlist" ).getObject ("playlist" );
4745 System .out .println ();
4846 }
4947
5048 @ Nonnull
5149 @ Override
5250 public String getName () throws ParsingException {
5351 try {
54- return doc .select ("div[class=\" playlist-info\" ] h3[class=\" playlist-title\" ]" ).first ().text ();
52+ final String name = playlistData .getString ("title" );
53+ if (name != null ) return name ;
54+ else return "" ;
5555 } catch (Exception e ) {
5656 throw new ParsingException ("Could not get playlist name" , e );
5757 }
@@ -60,16 +60,8 @@ public String getName() throws ParsingException {
6060 @ Override
6161 public String getThumbnailUrl () throws ParsingException {
6262 try {
63- Element li = doc .select ("ol[class*=\" playlist-videos-list\" ] li" ).first ();
64- String videoId = li .attr ("data-video-id" );
65- if (videoId != null && !videoId .isEmpty ()) {
66- //higher quality
67- return getThumbnailUrlFromId (videoId );
68- } else {
69- //lower quality
70- return doc .select ("ol[class*=\" playlist-videos-list\" ] li" ).first ()
71- .attr ("data-thumbnail-url" );
72- }
63+ final String videoId = playlistData .getArray ("contents" ).getObject (0 ).getString ("videoId" );
64+ return getThumbnailUrlFromId (videoId );
7365 } catch (Exception e ) {
7466 throw new ParsingException ("Could not get playlist thumbnail" , e );
7567 }
@@ -101,112 +93,66 @@ public String getUploaderAvatarUrl() {
10193 @ Override
10294 public long getStreamCount () {
10395 // Auto-generated playlist always start with 25 videos and are endless
104- // But the html doesn't have a continuation url
105- return doc .select ("ol[class*=\" playlist-videos-list\" ] li" ).size ();
96+ return 25 ;
10697 }
10798
10899 @ Nonnull
109100 @ Override
110101 public InfoItemsPage <StreamInfoItem > getInitialPage () {
111102 StreamInfoItemsCollector collector = new StreamInfoItemsCollector (getServiceId ());
112- Element ol = doc .select ("ol[class*=\" playlist-videos-list\" ]" ).first ();
113- collectStreamsFrom (collector , ol );
103+ collectStreamsFrom (collector , playlistData .getArray ("contents" ));
114104 return new InfoItemsPage <>(collector , getNextPageUrl ());
115105 }
116106
117107 @ Override
118108 public String getNextPageUrl () {
119- return "" ;
109+ final JsonObject lastStream = ((JsonObject ) playlistData .getArray ("contents" )
110+ .get (playlistData .getArray ("contents" ).size () - 1 ));
111+ final String lastStreamId = lastStream .getObject ("playlistPanelVideoRenderer" )
112+ .getString ("videoId" );
113+ return "https://youtube.com" + lastStream .getObject ("playlistPanelVideoRenderer" ).getObject ("navigationEndpoint" )
114+ .getObject ("commandMetadata" ).getObject ("webCommandMetadata" ).getString ("url" ) + "&pbj=1" ;
120115 }
121116
122117 @ Override
123- public InfoItemsPage <StreamInfoItem > getPage (final String pageUrl ) {
124- //Continuations are not implemented
125- return null ;
118+ public InfoItemsPage <StreamInfoItem > getPage (final String pageUrl )
119+ throws ExtractionException , IOException {
120+ if (pageUrl == null || pageUrl .isEmpty ()) {
121+ throw new ExtractionException (new IllegalArgumentException ("Page url is empty or null" ));
122+ }
123+
124+ StreamInfoItemsCollector collector = new StreamInfoItemsCollector (getServiceId ());
125+ final JsonArray ajaxJson = getJsonResponse (pageUrl , getExtractorLocalization ());
126+ playlistData =
127+ ajaxJson .getObject (3 ).getObject ("response" ).getObject ("contents" )
128+ .getObject ("twoColumnWatchNextResults" ).getObject ("playlist" )
129+ .getObject ("playlist" );
130+ final JsonArray streams = playlistData .getArray ("contents" );
131+ //Because continuation requests are created with the last video of previous request as start
132+ streams .remove (0 );
133+ collectStreamsFrom (collector , streams );
134+ return new InfoItemsPage <>(collector , getNextPageUrl ());
126135 }
127136
128137 private void collectStreamsFrom (
129138 @ Nonnull StreamInfoItemsCollector collector ,
130- @ Nullable Element element ) {
139+ @ Nullable JsonArray streams ) {
131140 collector .reset ();
132141
133- if (element == null ) {
142+ if (streams == null ) {
134143 return ;
135144 }
136145
137- final LinkHandlerFactory streamLinkHandlerFactory = getService ().getStreamLHFactory ();
138146 final TimeAgoParser timeAgoParser = getTimeAgoParser ();
139147
140- // for (final Element li : element.children()) {
141- //
142- // collector.commit(new YoutubeStreamInfoItemExtractor(li, timeAgoParser) {
143- //
144- // @Override
145- // public boolean isAd() {
146- // return false;
147- // }
148- //
149- // @Override
150- // public String getUrl() throws ParsingException {
151- // try {
152- // return streamLinkHandlerFactory.fromId(li.attr("data-video-id")).getUrl();
153- // } catch (Exception e) {
154- // throw new ParsingException("Could not get web page url for the video", e);
155- // }
156- // }
157- //
158- // @Override
159- // public String getName() throws ParsingException {
160- // try {
161- // return li.attr("data-video-title");
162- // } catch (Exception e) {
163- // throw new ParsingException("Could not get name", e);
164- // }
165- // }
166- //
167- // @Override
168- // public long getDuration() {
169- // //Not present in doc
170- // return 0;
171- // }
172- //
173- // @Override
174- // public String getUploaderName() throws ParsingException {
175- // String uploaderName = li.attr("data-video-username");
176- // if (uploaderName == null || uploaderName.isEmpty()) {
177- // throw new ParsingException("Could not get uploader name");
178- // } else {
179- // return uploaderName;
180- // }
181- // }
182- //
183- // @Override
184- // public String getUploaderUrl() {
185- // //Not present in doc
186- // return "";
187- // }
188- //
189- // @Override
190- // public String getTextualUploadDate() {
191- // //Not present in doc
192- // return "";
193- // }
194- //
195- // @Override
196- // public long getViewCount() {
197- // return -1;
198- // }
199- //
200- // @Override
201- // public String getThumbnailUrl() throws ParsingException {
202- // try {
203- // return getThumbnailUrlFromId(streamLinkHandlerFactory.fromUrl(getUrl()).getId());
204- // } catch (Exception e) {
205- // throw new ParsingException("Could not get thumbnail url", e);
206- // }
207- // }
208- // });
209- // }
148+ for (Object stream : streams ) {
149+ if (stream instanceof JsonObject ) {
150+ JsonObject streamInfo = ((JsonObject ) stream ).getObject ("playlistPanelVideoRenderer" );
151+ if (streamInfo != null ) {
152+ collector .commit (new YoutubeStreamInfoItemExtractor (streamInfo , timeAgoParser ));
153+ }
154+ }
155+ }
210156 }
211157
212158 private String getThumbnailUrlFromId (String videoId ) {
0 commit comments