Skip to content

Commit f926fbc

Browse files
committed
[YouTube] Add support for shortsLockupViewModels
This new UI data type is replacing the reelItemRenderer one.
1 parent 6e3a4a6 commit f926fbc

3 files changed

Lines changed: 185 additions & 2 deletions

File tree

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

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -299,6 +299,9 @@ private Optional<JsonObject> collectItem(@Nonnull final MultiInfoItemsCollector
299299
} else if (richItem.has("reelItemRenderer")) {
300300
commitReel(collector, richItem.getObject("reelItemRenderer"),
301301
channelVerifiedStatus, channelName, channelUrl);
302+
} else if (richItem.has("shortsLockupViewModel")) {
303+
commitShortsLockup(collector, richItem.getObject("shortsLockupViewModel"),
304+
channelVerifiedStatus, channelName, channelUrl);
302305
} else if (richItem.has("playlistRenderer")) {
303306
commitPlaylist(collector, richItem.getObject("playlistRenderer"),
304307
channelVerifiedStatus, channelName, channelUrl);
@@ -356,6 +359,30 @@ public boolean isUploaderVerified() {
356359
});
357360
}
358361

362+
private static void commitShortsLockup(@Nonnull final MultiInfoItemsCollector collector,
363+
@Nonnull final JsonObject shortsLockupViewModel,
364+
@Nonnull final VerifiedStatus channelVerifiedStatus,
365+
@Nullable final String channelName,
366+
@Nullable final String channelUrl) {
367+
collector.commit(
368+
new YoutubeShortsLockupInfoItemExtractor(shortsLockupViewModel) {
369+
@Override
370+
public String getUploaderName() throws ParsingException {
371+
return isNullOrEmpty(channelName) ? super.getUploaderName() : channelName;
372+
}
373+
374+
@Override
375+
public String getUploaderUrl() throws ParsingException {
376+
return isNullOrEmpty(channelUrl) ? super.getUploaderName() : channelUrl;
377+
}
378+
379+
@Override
380+
public boolean isUploaderVerified() {
381+
return channelVerifiedStatus == VerifiedStatus.VERIFIED;
382+
}
383+
});
384+
}
385+
359386
private void commitVideo(@Nonnull final MultiInfoItemsCollector collector,
360387
@Nonnull final TimeAgoParser timeAgoParser,
361388
@Nonnull final JsonObject jsonObject,

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

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,13 +20,19 @@
2020
import javax.annotation.Nullable;
2121

2222
/**
23-
* A {@link StreamInfoItemExtractor} for YouTube's {@code reelItemRenderers}.
23+
* A {@link StreamInfoItemExtractor} for YouTube's {@code reelItemRenderer}s.
2424
*
2525
* <p>
26-
* {@code reelItemRenderers} are returned on YouTube for their short-form contents on almost every
26+
* {@code reelItemRenderer}s were returned on YouTube for their short-form contents on almost every
2727
* place and every major client. They provide a limited amount of information and do not provide
2828
* the exact view count, any uploader info (name, URL, avatar, verified status) and the upload date.
2929
* </p>
30+
*
31+
* <p>
32+
* At the time this documentation has been updated, they are being replaced by
33+
* {@code shortsLockupViewModel}s. See {@link YoutubeShortsLockupInfoItemExtractor} for an
34+
* extractor for this new UI data type.
35+
* </p>
3036
*/
3137
public class YoutubeReelInfoItemExtractor implements StreamInfoItemExtractor {
3238

Lines changed: 150 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,150 @@
1+
package org.schabi.newpipe.extractor.services.youtube.extractors;
2+
3+
import com.grack.nanojson.JsonObject;
4+
import org.schabi.newpipe.extractor.Image;
5+
import org.schabi.newpipe.extractor.exceptions.ParsingException;
6+
import org.schabi.newpipe.extractor.localization.DateWrapper;
7+
import org.schabi.newpipe.extractor.services.youtube.linkHandler.YoutubeStreamLinkHandlerFactory;
8+
import org.schabi.newpipe.extractor.stream.StreamInfoItemExtractor;
9+
import org.schabi.newpipe.extractor.stream.StreamType;
10+
import org.schabi.newpipe.extractor.utils.Utils;
11+
12+
import javax.annotation.Nonnull;
13+
import javax.annotation.Nullable;
14+
15+
import java.util.List;
16+
17+
import static org.schabi.newpipe.extractor.services.youtube.YoutubeParsingHelper.getThumbnailsFromInfoItem;
18+
import static org.schabi.newpipe.extractor.utils.Utils.isNullOrEmpty;
19+
20+
/**
21+
* A {@link StreamInfoItemExtractor} for YouTube's {@code shortsLockupViewModel}s.
22+
*
23+
* <p>
24+
* {@code shortsLockupViewModel}s are returned on YouTube for their short-form contents on almost
25+
* every place and every major client. They provide a limited amount of information and do not
26+
* provide the exact view count, any uploader info (name, URL, avatar, verified status) and the
27+
* upload date.
28+
* </p>
29+
*
30+
* <p>
31+
* At the time this documentation has been written, this data UI type is not fully used (rolled
32+
* out), so {@code reelItemRenderer}s are also returned. See {@link YoutubeReelInfoItemExtractor}
33+
* for an extractor for this UI data type.
34+
* </p>
35+
*/
36+
public class YoutubeShortsLockupInfoItemExtractor implements StreamInfoItemExtractor {
37+
38+
@Nonnull
39+
private final JsonObject shortsLockupViewModel;
40+
41+
public YoutubeShortsLockupInfoItemExtractor(@Nonnull final JsonObject shortsLockupViewModel) {
42+
this.shortsLockupViewModel = shortsLockupViewModel;
43+
}
44+
45+
@Override
46+
public String getName() throws ParsingException {
47+
return shortsLockupViewModel.getObject("overlayMetadata")
48+
.getObject("primaryText")
49+
.getString("content");
50+
}
51+
52+
@Override
53+
public String getUrl() throws ParsingException {
54+
String videoId = shortsLockupViewModel.getObject("onTap")
55+
.getObject("innertubeCommand")
56+
.getObject("reelWatchEndpoint")
57+
.getString("videoId");
58+
59+
if (isNullOrEmpty(videoId)) {
60+
videoId = shortsLockupViewModel.getObject("inlinePlayerData")
61+
.getObject("onVisible")
62+
.getObject("innertubeCommand")
63+
.getObject("watchEndpoint")
64+
.getString("videoId");
65+
}
66+
67+
if (isNullOrEmpty(videoId)) {
68+
throw new ParsingException("Could not get video ID");
69+
}
70+
71+
try {
72+
return YoutubeStreamLinkHandlerFactory.getInstance().getUrl(videoId);
73+
} catch (final Exception e) {
74+
throw new ParsingException("Could not get URL", e);
75+
}
76+
}
77+
78+
@Nonnull
79+
@Override
80+
public List<Image> getThumbnails() throws ParsingException {
81+
return getThumbnailsFromInfoItem(shortsLockupViewModel.getObject("thumbnail")
82+
.getObject("sources"));
83+
}
84+
85+
@Override
86+
public StreamType getStreamType() throws ParsingException {
87+
return StreamType.VIDEO_STREAM;
88+
}
89+
90+
@Override
91+
public long getViewCount() throws ParsingException {
92+
final String viewCountText = shortsLockupViewModel.getObject("overlayMetadata")
93+
.getObject("secondaryText")
94+
.getString("content");
95+
if (!isNullOrEmpty(viewCountText)) {
96+
// This approach is language dependent
97+
if (viewCountText.toLowerCase().contains("no views")) {
98+
return 0;
99+
}
100+
101+
return Utils.mixedNumberWordToLong(viewCountText);
102+
}
103+
104+
throw new ParsingException("Could not get short view count");
105+
}
106+
107+
@Override
108+
public boolean isShortFormContent() {
109+
return true;
110+
}
111+
112+
// All the following properties cannot be obtained from shortsLockupViewModels
113+
114+
@Override
115+
public boolean isAd() throws ParsingException {
116+
return false;
117+
}
118+
119+
@Override
120+
public long getDuration() throws ParsingException {
121+
return -1;
122+
}
123+
124+
@Override
125+
public String getUploaderName() throws ParsingException {
126+
return null;
127+
}
128+
129+
@Override
130+
public String getUploaderUrl() throws ParsingException {
131+
return null;
132+
}
133+
134+
@Override
135+
public boolean isUploaderVerified() throws ParsingException {
136+
return false;
137+
}
138+
139+
@Nullable
140+
@Override
141+
public String getTextualUploadDate() throws ParsingException {
142+
return null;
143+
}
144+
145+
@Nullable
146+
@Override
147+
public DateWrapper getUploadDate() throws ParsingException {
148+
return null;
149+
}
150+
}

0 commit comments

Comments
 (0)