[YouTube] Add support for shortsLockupViewModels
This new UI data type is replacing the reelItemRenderer one.
This commit is contained in:
parent
6e3a4a6d9d
commit
f926fbcf35
|
@ -299,6 +299,9 @@ public class YoutubeChannelTabExtractor extends ChannelTabExtractor {
|
||||||
} else if (richItem.has("reelItemRenderer")) {
|
} else if (richItem.has("reelItemRenderer")) {
|
||||||
commitReel(collector, richItem.getObject("reelItemRenderer"),
|
commitReel(collector, richItem.getObject("reelItemRenderer"),
|
||||||
channelVerifiedStatus, channelName, channelUrl);
|
channelVerifiedStatus, channelName, channelUrl);
|
||||||
|
} else if (richItem.has("shortsLockupViewModel")) {
|
||||||
|
commitShortsLockup(collector, richItem.getObject("shortsLockupViewModel"),
|
||||||
|
channelVerifiedStatus, channelName, channelUrl);
|
||||||
} else if (richItem.has("playlistRenderer")) {
|
} else if (richItem.has("playlistRenderer")) {
|
||||||
commitPlaylist(collector, richItem.getObject("playlistRenderer"),
|
commitPlaylist(collector, richItem.getObject("playlistRenderer"),
|
||||||
channelVerifiedStatus, channelName, channelUrl);
|
channelVerifiedStatus, channelName, channelUrl);
|
||||||
|
@ -356,6 +359,30 @@ public class YoutubeChannelTabExtractor extends ChannelTabExtractor {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static void commitShortsLockup(@Nonnull final MultiInfoItemsCollector collector,
|
||||||
|
@Nonnull final JsonObject shortsLockupViewModel,
|
||||||
|
@Nonnull final VerifiedStatus channelVerifiedStatus,
|
||||||
|
@Nullable final String channelName,
|
||||||
|
@Nullable final String channelUrl) {
|
||||||
|
collector.commit(
|
||||||
|
new YoutubeShortsLockupInfoItemExtractor(shortsLockupViewModel) {
|
||||||
|
@Override
|
||||||
|
public String getUploaderName() throws ParsingException {
|
||||||
|
return isNullOrEmpty(channelName) ? super.getUploaderName() : channelName;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getUploaderUrl() throws ParsingException {
|
||||||
|
return isNullOrEmpty(channelUrl) ? super.getUploaderName() : channelUrl;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isUploaderVerified() {
|
||||||
|
return channelVerifiedStatus == VerifiedStatus.VERIFIED;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
private void commitVideo(@Nonnull final MultiInfoItemsCollector collector,
|
private void commitVideo(@Nonnull final MultiInfoItemsCollector collector,
|
||||||
@Nonnull final TimeAgoParser timeAgoParser,
|
@Nonnull final TimeAgoParser timeAgoParser,
|
||||||
@Nonnull final JsonObject jsonObject,
|
@Nonnull final JsonObject jsonObject,
|
||||||
|
|
|
@ -20,13 +20,19 @@ import javax.annotation.Nonnull;
|
||||||
import javax.annotation.Nullable;
|
import javax.annotation.Nullable;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A {@link StreamInfoItemExtractor} for YouTube's {@code reelItemRenderers}.
|
* A {@link StreamInfoItemExtractor} for YouTube's {@code reelItemRenderer}s.
|
||||||
*
|
*
|
||||||
* <p>
|
* <p>
|
||||||
* {@code reelItemRenderers} are returned on YouTube for their short-form contents on almost every
|
* {@code reelItemRenderer}s were returned on YouTube for their short-form contents on almost every
|
||||||
* place and every major client. They provide a limited amount of information and do not provide
|
* place and every major client. They provide a limited amount of information and do not provide
|
||||||
* the exact view count, any uploader info (name, URL, avatar, verified status) and the upload date.
|
* the exact view count, any uploader info (name, URL, avatar, verified status) and the upload date.
|
||||||
* </p>
|
* </p>
|
||||||
|
*
|
||||||
|
* <p>
|
||||||
|
* At the time this documentation has been updated, they are being replaced by
|
||||||
|
* {@code shortsLockupViewModel}s. See {@link YoutubeShortsLockupInfoItemExtractor} for an
|
||||||
|
* extractor for this new UI data type.
|
||||||
|
* </p>
|
||||||
*/
|
*/
|
||||||
public class YoutubeReelInfoItemExtractor implements StreamInfoItemExtractor {
|
public class YoutubeReelInfoItemExtractor implements StreamInfoItemExtractor {
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,150 @@
|
||||||
|
package org.schabi.newpipe.extractor.services.youtube.extractors;
|
||||||
|
|
||||||
|
import com.grack.nanojson.JsonObject;
|
||||||
|
import org.schabi.newpipe.extractor.Image;
|
||||||
|
import org.schabi.newpipe.extractor.exceptions.ParsingException;
|
||||||
|
import org.schabi.newpipe.extractor.localization.DateWrapper;
|
||||||
|
import org.schabi.newpipe.extractor.services.youtube.linkHandler.YoutubeStreamLinkHandlerFactory;
|
||||||
|
import org.schabi.newpipe.extractor.stream.StreamInfoItemExtractor;
|
||||||
|
import org.schabi.newpipe.extractor.stream.StreamType;
|
||||||
|
import org.schabi.newpipe.extractor.utils.Utils;
|
||||||
|
|
||||||
|
import javax.annotation.Nonnull;
|
||||||
|
import javax.annotation.Nullable;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import static org.schabi.newpipe.extractor.services.youtube.YoutubeParsingHelper.getThumbnailsFromInfoItem;
|
||||||
|
import static org.schabi.newpipe.extractor.utils.Utils.isNullOrEmpty;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A {@link StreamInfoItemExtractor} for YouTube's {@code shortsLockupViewModel}s.
|
||||||
|
*
|
||||||
|
* <p>
|
||||||
|
* {@code shortsLockupViewModel}s are returned on YouTube for their short-form contents on almost
|
||||||
|
* every place and every major client. They provide a limited amount of information and do not
|
||||||
|
* provide the exact view count, any uploader info (name, URL, avatar, verified status) and the
|
||||||
|
* upload date.
|
||||||
|
* </p>
|
||||||
|
*
|
||||||
|
* <p>
|
||||||
|
* At the time this documentation has been written, this data UI type is not fully used (rolled
|
||||||
|
* out), so {@code reelItemRenderer}s are also returned. See {@link YoutubeReelInfoItemExtractor}
|
||||||
|
* for an extractor for this UI data type.
|
||||||
|
* </p>
|
||||||
|
*/
|
||||||
|
public class YoutubeShortsLockupInfoItemExtractor implements StreamInfoItemExtractor {
|
||||||
|
|
||||||
|
@Nonnull
|
||||||
|
private final JsonObject shortsLockupViewModel;
|
||||||
|
|
||||||
|
public YoutubeShortsLockupInfoItemExtractor(@Nonnull final JsonObject shortsLockupViewModel) {
|
||||||
|
this.shortsLockupViewModel = shortsLockupViewModel;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getName() throws ParsingException {
|
||||||
|
return shortsLockupViewModel.getObject("overlayMetadata")
|
||||||
|
.getObject("primaryText")
|
||||||
|
.getString("content");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getUrl() throws ParsingException {
|
||||||
|
String videoId = shortsLockupViewModel.getObject("onTap")
|
||||||
|
.getObject("innertubeCommand")
|
||||||
|
.getObject("reelWatchEndpoint")
|
||||||
|
.getString("videoId");
|
||||||
|
|
||||||
|
if (isNullOrEmpty(videoId)) {
|
||||||
|
videoId = shortsLockupViewModel.getObject("inlinePlayerData")
|
||||||
|
.getObject("onVisible")
|
||||||
|
.getObject("innertubeCommand")
|
||||||
|
.getObject("watchEndpoint")
|
||||||
|
.getString("videoId");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isNullOrEmpty(videoId)) {
|
||||||
|
throw new ParsingException("Could not get video ID");
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
return YoutubeStreamLinkHandlerFactory.getInstance().getUrl(videoId);
|
||||||
|
} catch (final Exception e) {
|
||||||
|
throw new ParsingException("Could not get URL", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Nonnull
|
||||||
|
@Override
|
||||||
|
public List<Image> getThumbnails() throws ParsingException {
|
||||||
|
return getThumbnailsFromInfoItem(shortsLockupViewModel.getObject("thumbnail")
|
||||||
|
.getObject("sources"));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public StreamType getStreamType() throws ParsingException {
|
||||||
|
return StreamType.VIDEO_STREAM;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public long getViewCount() throws ParsingException {
|
||||||
|
final String viewCountText = shortsLockupViewModel.getObject("overlayMetadata")
|
||||||
|
.getObject("secondaryText")
|
||||||
|
.getString("content");
|
||||||
|
if (!isNullOrEmpty(viewCountText)) {
|
||||||
|
// This approach is language dependent
|
||||||
|
if (viewCountText.toLowerCase().contains("no views")) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
return Utils.mixedNumberWordToLong(viewCountText);
|
||||||
|
}
|
||||||
|
|
||||||
|
throw new ParsingException("Could not get short view count");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isShortFormContent() {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// All the following properties cannot be obtained from shortsLockupViewModels
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isAd() throws ParsingException {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public long getDuration() throws ParsingException {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getUploaderName() throws ParsingException {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getUploaderUrl() throws ParsingException {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isUploaderVerified() throws ParsingException {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
@Override
|
||||||
|
public String getTextualUploadDate() throws ParsingException {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
@Override
|
||||||
|
public DateWrapper getUploadDate() throws ParsingException {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue