Skip to content

Commit c1981ed

Browse files
committed
[YouTube] Apply changes in Extractors except YoutubeMusicSearchExtractor
Also improve a bit some code related to the changes.
1 parent 4cc99f9 commit c1981ed

4 files changed

Lines changed: 110 additions & 130 deletions

File tree

extractor/src/main/java/org/schabi/newpipe/extractor/services/youtube/extractors/YoutubeChannelExtractor.java

Lines changed: 43 additions & 61 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,23 @@
1+
/*
2+
* Created by Christian Schabesberger on 25.07.16.
3+
*
4+
* Copyright (C) Christian Schabesberger 2018 <chris.schabesberger@mailbox.org>
5+
* YoutubeChannelExtractor.java is part of NewPipe Extractor.
6+
*
7+
* NewPipe Extractor is free software: you can redistribute it and/or modify
8+
* it under the terms of the GNU General Public License as published by
9+
* the Free Software Foundation, either version 3 of the License, or
10+
* (at your option) any later version.
11+
*
12+
* NewPipe Extractor is distributed in the hope that it will be useful,
13+
* but WITHOUT ANY WARRANTY; without even the implied warranty of
14+
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15+
* GNU General Public License for more details.
16+
*
17+
* You should have received a copy of the GNU General Public License
18+
* along with NewPipe Extractor. If not, see <https://www.gnu.org/licenses/>.
19+
*/
20+
121
package org.schabi.newpipe.extractor.services.youtube.extractors;
222

323
import static org.schabi.newpipe.extractor.services.youtube.YoutubeChannelHelper.getChannelResponse;
@@ -8,6 +28,7 @@
828
import com.grack.nanojson.JsonArray;
929
import com.grack.nanojson.JsonObject;
1030

31+
import org.schabi.newpipe.extractor.Image;
1132
import org.schabi.newpipe.extractor.StreamingService;
1233
import org.schabi.newpipe.extractor.channel.ChannelExtractor;
1334
import org.schabi.newpipe.extractor.channel.tabs.ChannelTabs;
@@ -36,26 +57,6 @@
3657
import javax.annotation.Nonnull;
3758
import javax.annotation.Nullable;
3859

39-
/*
40-
* Created by Christian Schabesberger on 25.07.16.
41-
*
42-
* Copyright (C) Christian Schabesberger 2018 <chris.schabesberger@mailbox.org>
43-
* YoutubeChannelExtractor.java is part of NewPipe.
44-
*
45-
* NewPipe is free software: you can redistribute it and/or modify
46-
* it under the terms of the GNU General Public License as published by
47-
* the Free Software Foundation, either version 3 of the License, or
48-
* (at your option) any later version.
49-
*
50-
* NewPipe is distributed in the hope that it will be useful,
51-
* but WITHOUT ANY WARRANTY; without even the implied warranty of
52-
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
53-
* GNU General Public License for more details.
54-
*
55-
* You should have received a copy of the GNU General Public License
56-
* along with NewPipe. If not, see <http://www.gnu.org/licenses/>.
57-
*/
58-
5960
public class YoutubeChannelExtractor extends ChannelExtractor {
6061

6162
private JsonObject jsonResponse;
@@ -190,16 +191,15 @@ public String getName() throws ParsingException {
190191
.orElseThrow(() -> new ParsingException("Could not get channel name"));
191192
}
192193

194+
@Nonnull
193195
@Override
194-
public String getAvatarUrl() throws ParsingException {
196+
public List<Image> getAvatars() throws ParsingException {
195197
assertPageFetched();
196198
if (channelAgeGateRenderer != null) {
197199
return Optional.ofNullable(channelAgeGateRenderer.getObject("avatar")
198-
.getArray("thumbnails")
199-
.getObject(0)
200-
.getString("url"))
201-
.map(YoutubeParsingHelper::fixThumbnailUrl)
202-
.orElseThrow(() -> new ParsingException("Could not get avatar URL"));
200+
.getArray("thumbnails"))
201+
.map(YoutubeParsingHelper::getImagesFromThumbnailsArray)
202+
.orElseThrow(() -> new ParsingException("Could not get avatars"));
203203
}
204204

205205
return channelHeader.map(header -> {
@@ -210,56 +210,37 @@ public String getAvatarUrl() throws ParsingException {
210210
.getObject("image")
211211
.getObject("contentPreviewImageViewModel")
212212
.getObject("image")
213-
.getArray("sources")
214-
.getObject(0)
215-
.getString("url");
213+
.getArray("sources");
216214

217215
case INTERACTIVE_TABBED:
218216
return header.json.getObject("boxArt")
219-
.getArray("thumbnails")
220-
.getObject(0)
221-
.getString("url");
217+
.getArray("thumbnails");
222218

223219
case C4_TABBED:
224220
case CAROUSEL:
225221
default:
226222
return header.json.getObject("avatar")
227-
.getArray("thumbnails")
228-
.getObject(0)
229-
.getString("url");
223+
.getArray("thumbnails");
230224
}
231225
})
232-
.map(YoutubeParsingHelper::fixThumbnailUrl)
233-
.orElseThrow(() -> new ParsingException("Could not get avatar URL"));
226+
.map(YoutubeParsingHelper::getImagesFromThumbnailsArray)
227+
.orElseThrow(() -> new ParsingException("Could not get avatars"));
234228
}
235229

230+
@Nonnull
236231
@Override
237-
public String getBannerUrl() throws ParsingException {
232+
public List<Image> getBanners() {
238233
assertPageFetched();
239234
if (channelAgeGateRenderer != null) {
240-
return null;
241-
}
242-
243-
if (channelHeader.isPresent()) {
244-
final ChannelHeader header = channelHeader.get();
245-
if (header.headerType == HeaderType.PAGE) {
246-
// No banner is available on pageHeaderRenderer headers
247-
return null;
248-
}
249-
250-
return Optional.ofNullable(header.json.getObject("banner")
251-
.getArray("thumbnails")
252-
.getObject(0)
253-
.getString("url"))
254-
.filter(url -> !url.contains("s.ytimg.com") && !url.contains("default_banner"))
255-
.map(YoutubeParsingHelper::fixThumbnailUrl)
256-
// Channels may not have a banner, so no exception should be thrown if no
257-
// banner is found
258-
// Return null in this case
259-
.orElse(null);
235+
return List.of();
260236
}
261237

262-
return null;
238+
// No banner is available on pageHeaderRenderer headers
239+
return channelHeader.filter(header -> header.headerType != HeaderType.PAGE)
240+
.map(header -> header.json.getObject("banner")
241+
.getArray("thumbnails"))
242+
.map(YoutubeParsingHelper::getImagesFromThumbnailsArray)
243+
.orElse(List.of());
263244
}
264245

265246
@Override
@@ -359,9 +340,10 @@ public String getParentChannelUrl() {
359340
return "";
360341
}
361342

343+
@Nonnull
362344
@Override
363-
public String getParentChannelAvatarUrl() {
364-
return "";
345+
public List<Image> getParentChannelAvatars() {
346+
return List.of();
365347
}
366348

367349
@Override

extractor/src/main/java/org/schabi/newpipe/extractor/services/youtube/extractors/YoutubeMixPlaylistExtractor.java

Lines changed: 27 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,8 @@
1717
import com.grack.nanojson.JsonObject;
1818
import com.grack.nanojson.JsonWriter;
1919

20+
import org.schabi.newpipe.extractor.Image;
21+
import org.schabi.newpipe.extractor.Image.ResolutionLevel;
2022
import org.schabi.newpipe.extractor.ListExtractor;
2123
import org.schabi.newpipe.extractor.Page;
2224
import org.schabi.newpipe.extractor.StreamingService;
@@ -34,6 +36,7 @@
3436
import org.schabi.newpipe.extractor.stream.Description;
3537
import org.schabi.newpipe.extractor.stream.StreamInfoItem;
3638
import org.schabi.newpipe.extractor.stream.StreamInfoItemsCollector;
39+
import org.schabi.newpipe.extractor.utils.ImageSuffix;
3740
import org.schabi.newpipe.extractor.utils.JsonUtils;
3841

3942
import java.io.IOException;
@@ -43,6 +46,7 @@
4346
import java.util.List;
4447
import java.util.Map;
4548
import java.util.Objects;
49+
import java.util.stream.Collectors;
4650

4751
import javax.annotation.Nonnull;
4852
import javax.annotation.Nullable;
@@ -53,6 +57,12 @@
5357
* {@code youtube.com/watch?v=videoId&list=playlistId}
5458
*/
5559
public class YoutubeMixPlaylistExtractor extends PlaylistExtractor {
60+
private static final List<ImageSuffix> IMAGE_URL_SUFFIXES_AND_RESOLUTIONS = List.of(
61+
// sqdefault and maxresdefault image resolutions are not available on all
62+
// videos, so don't add them in the list of available resolutions
63+
new ImageSuffix("default.jpg", 90, 120, ResolutionLevel.LOW),
64+
new ImageSuffix("mqdefault.jpg", 180, 320, ResolutionLevel.MEDIUM),
65+
new ImageSuffix("hqdefault.jpg", 360, 480, ResolutionLevel.MEDIUM));
5666

5767
/**
5868
* YouTube identifies mixes based on this cookie. With this information it can generate
@@ -126,18 +136,18 @@ public String getName() throws ParsingException {
126136

127137
@Nonnull
128138
@Override
129-
public String getThumbnailUrl() throws ParsingException {
139+
public List<Image> getThumbnails() throws ParsingException {
130140
try {
131-
return getThumbnailUrlFromPlaylistId(playlistData.getString("playlistId"));
141+
return getThumbnailsFromPlaylistId(playlistData.getString("playlistId"));
132142
} catch (final Exception e) {
133143
try {
134-
// Fallback to thumbnail of current video. Always the case for channel mix
135-
return getThumbnailUrlFromVideoId(initialData.getObject("currentVideoEndpoint")
144+
// Fallback to thumbnail of current video. Always the case for channel mixes
145+
return getThumbnailsFromVideoId(initialData.getObject("currentVideoEndpoint")
136146
.getObject("watchEndpoint").getString("videoId"));
137147
} catch (final Exception ignored) {
138148
}
139149

140-
throw new ParsingException("Could not get playlist thumbnail", e);
150+
throw new ParsingException("Could not get playlist thumbnails", e);
141151
}
142152
}
143153

@@ -153,10 +163,11 @@ public String getUploaderName() {
153163
return "YouTube";
154164
}
155165

166+
@Nonnull
156167
@Override
157-
public String getUploaderAvatarUrl() {
168+
public List<Image> getUploaderAvatars() {
158169
// YouTube mixes are auto-generated by YouTube
159-
return "";
170+
return List.of();
160171
}
161172

162173
@Override
@@ -264,14 +275,19 @@ private void collectStreamsFrom(@Nonnull final StreamInfoItemsCollector collecto
264275
}
265276

266277
@Nonnull
267-
private String getThumbnailUrlFromPlaylistId(@Nonnull final String playlistId)
278+
private List<Image> getThumbnailsFromPlaylistId(@Nonnull final String playlistId)
268279
throws ParsingException {
269-
return getThumbnailUrlFromVideoId(YoutubeParsingHelper.extractVideoIdFromMixId(playlistId));
280+
return getThumbnailsFromVideoId(YoutubeParsingHelper.extractVideoIdFromMixId(playlistId));
270281
}
271282

272283
@Nonnull
273-
private String getThumbnailUrlFromVideoId(final String videoId) {
274-
return "https://i.ytimg.com/vi/" + videoId + "/hqdefault.jpg";
284+
private List<Image> getThumbnailsFromVideoId(@Nonnull final String videoId) {
285+
final String baseUrl = "https://i.ytimg.com/vi/" + videoId + "/";
286+
return IMAGE_URL_SUFFIXES_AND_RESOLUTIONS.stream()
287+
.map(imageSuffix -> new Image(baseUrl + imageSuffix.getSuffix(),
288+
imageSuffix.getHeight(), imageSuffix.getWidth(),
289+
imageSuffix.getResolutionLevel()))
290+
.collect(Collectors.toUnmodifiableList());
275291
}
276292

277293
@Nonnull

extractor/src/main/java/org/schabi/newpipe/extractor/services/youtube/extractors/YoutubePlaylistExtractor.java

Lines changed: 24 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -3,10 +3,10 @@
33
import static org.schabi.newpipe.extractor.services.youtube.YoutubeParsingHelper.DISABLE_PRETTY_PRINT_PARAMETER;
44
import static org.schabi.newpipe.extractor.services.youtube.YoutubeParsingHelper.YOUTUBEI_V1_URL;
55
import static org.schabi.newpipe.extractor.services.youtube.YoutubeParsingHelper.extractPlaylistTypeFromPlaylistUrl;
6-
import static org.schabi.newpipe.extractor.services.youtube.YoutubeParsingHelper.fixThumbnailUrl;
76
import static org.schabi.newpipe.extractor.services.youtube.YoutubeParsingHelper.getJsonPostResponse;
87
import static org.schabi.newpipe.extractor.services.youtube.YoutubeParsingHelper.getKey;
98
import static org.schabi.newpipe.extractor.services.youtube.YoutubeParsingHelper.getTextFromObject;
9+
import static org.schabi.newpipe.extractor.services.youtube.YoutubeParsingHelper.getImagesFromThumbnailsArray;
1010
import static org.schabi.newpipe.extractor.services.youtube.YoutubeParsingHelper.getUrlFromNavigationEndpoint;
1111
import static org.schabi.newpipe.extractor.services.youtube.YoutubeParsingHelper.prepareDesktopJsonBuilder;
1212
import static org.schabi.newpipe.extractor.utils.Utils.isNullOrEmpty;
@@ -15,6 +15,7 @@
1515
import com.grack.nanojson.JsonObject;
1616
import com.grack.nanojson.JsonWriter;
1717

18+
import org.schabi.newpipe.extractor.Image;
1819
import org.schabi.newpipe.extractor.Page;
1920
import org.schabi.newpipe.extractor.StreamingService;
2021
import org.schabi.newpipe.extractor.downloader.Downloader;
@@ -33,6 +34,7 @@
3334

3435
import java.io.IOException;
3536
import java.nio.charset.StandardCharsets;
37+
import java.util.List;
3638

3739
import javax.annotation.Nonnull;
3840
import javax.annotation.Nullable;
@@ -160,39 +162,35 @@ public String getName() throws ParsingException {
160162

161163
@Nonnull
162164
@Override
163-
public String getThumbnailUrl() throws ParsingException {
164-
String url;
165+
public List<Image> getThumbnails() throws ParsingException {
166+
final JsonArray playlistMetadataThumbnailsArray;
165167
if (isNewPlaylistInterface) {
166-
url = getPlaylistHeader().getObject("playlistHeaderBanner")
168+
playlistMetadataThumbnailsArray = getPlaylistHeader().getObject("playlistHeaderBanner")
167169
.getObject("heroPlaylistThumbnailRenderer")
168170
.getObject("thumbnail")
169-
.getArray("thumbnails")
170-
.getObject(0)
171-
.getString("url");
171+
.getArray("thumbnails");
172172
} else {
173-
url = getPlaylistInfo().getObject("thumbnailRenderer")
173+
playlistMetadataThumbnailsArray = playlistInfo.getObject("thumbnailRenderer")
174174
.getObject("playlistVideoThumbnailRenderer")
175175
.getObject("thumbnail")
176-
.getArray("thumbnails")
177-
.getObject(0)
178-
.getString("url");
176+
.getArray("thumbnails");
177+
}
178+
179+
if (!isNullOrEmpty(playlistMetadataThumbnailsArray)) {
180+
return getImagesFromThumbnailsArray(playlistMetadataThumbnailsArray);
179181
}
180182

181183
// This data structure is returned in both layouts
182-
if (isNullOrEmpty(url)) {
183-
url = browseResponse.getObject("microformat")
184+
final JsonArray microFormatThumbnailsArray = browseResponse.getObject("microformat")
184185
.getObject("microformatDataRenderer")
185186
.getObject("thumbnail")
186-
.getArray("thumbnails")
187-
.getObject(0)
188-
.getString("url");
187+
.getArray("thumbnails");
189188

190-
if (isNullOrEmpty(url)) {
191-
throw new ParsingException("Could not get playlist thumbnail");
192-
}
189+
if (!isNullOrEmpty(microFormatThumbnailsArray)) {
190+
return getImagesFromThumbnailsArray(microFormatThumbnailsArray);
193191
}
194192

195-
return fixThumbnailUrl(url);
193+
throw new ParsingException("Could not get playlist thumbnails");
196194
}
197195

198196
@Override
@@ -220,23 +218,19 @@ public String getUploaderName() throws ParsingException {
220218
}
221219
}
222220

221+
@Nonnull
223222
@Override
224-
public String getUploaderAvatarUrl() throws ParsingException {
223+
public List<Image> getUploaderAvatars() throws ParsingException {
225224
if (isNewPlaylistInterface) {
226225
// The new playlist interface doesn't provide an uploader avatar
227-
return "";
226+
return List.of();
228227
}
229228

230229
try {
231-
final String url = getUploaderInfo()
232-
.getObject("thumbnail")
233-
.getArray("thumbnails")
234-
.getObject(0)
235-
.getString("url");
236-
237-
return fixThumbnailUrl(url);
230+
return getImagesFromThumbnailsArray(getUploaderInfo().getObject("thumbnail")
231+
.getArray("thumbnails"));
238232
} catch (final Exception e) {
239-
throw new ParsingException("Could not get playlist uploader avatar", e);
233+
throw new ParsingException("Could not get playlist uploader avatars", e);
240234
}
241235
}
242236

0 commit comments

Comments
 (0)