[YT] Fix "Could not get like count"
1. YT uses a different data structure for likes (ViewModels instead of Renderers) 2. Fixed NPE in old code due to incorrect usage of string comparison 3. Unified JSONObject filtering and mapping
This commit is contained in:
parent
eac850ca10
commit
234466374c
|
@ -391,73 +391,34 @@ public class YoutubeStreamExtractor extends StreamExtractor {
|
|||
return -1;
|
||||
}
|
||||
|
||||
String likesString = "";
|
||||
String likesString = null;
|
||||
|
||||
try {
|
||||
final JsonArray topLevelButtons = getVideoPrimaryInfoRenderer()
|
||||
final List<JsonObject> topLevelButtons = getVideoPrimaryInfoRenderer()
|
||||
.getObject("videoActions")
|
||||
.getObject("menuRenderer")
|
||||
.getArray("topLevelButtons");
|
||||
|
||||
// Try first with the new video actions buttons data structure
|
||||
JsonObject likeToggleButtonRenderer = topLevelButtons.stream()
|
||||
.getArray("topLevelButtons")
|
||||
.stream()
|
||||
.filter(JsonObject.class::isInstance)
|
||||
.map(JsonObject.class::cast)
|
||||
.map(button -> button.getObject("segmentedLikeDislikeButtonRenderer")
|
||||
.getObject("likeButton")
|
||||
.getObject("toggleButtonRenderer"))
|
||||
.filter(toggleButtonRenderer -> !isNullOrEmpty(toggleButtonRenderer))
|
||||
.collect(Collectors.toList());
|
||||
|
||||
likesString = topLevelButtons.stream()
|
||||
.map(btn -> btn.getObject("segmentedLikeDislikeButtonViewModel")
|
||||
.getObject("likeButtonViewModel")
|
||||
.getObject("likeButtonViewModel")
|
||||
.getObject("toggleButtonViewModel")
|
||||
.getObject("toggleButtonViewModel")
|
||||
.getObject("defaultButtonViewModel")
|
||||
.getObject("buttonViewModel")
|
||||
.getString("accessibilityText"))
|
||||
.filter(Objects::nonNull)
|
||||
.findFirst()
|
||||
.orElse(null);
|
||||
|
||||
// Use the old video actions buttons data structure if the new one isn't returned
|
||||
if (likeToggleButtonRenderer == null) {
|
||||
/*
|
||||
In the old video actions buttons data structure, there are 3 ways to detect whether
|
||||
a button is the like button, using its toggleButtonRenderer:
|
||||
- checking whether toggleButtonRenderer.targetId is equal to watch-like;
|
||||
- checking whether toggleButtonRenderer.defaultIcon.iconType is equal to LIKE;
|
||||
- checking whether
|
||||
toggleButtonRenderer.toggleButtonSupportedData.toggleButtonIdData.id
|
||||
is equal to TOGGLE_BUTTON_ID_TYPE_LIKE.
|
||||
*/
|
||||
likeToggleButtonRenderer = topLevelButtons.stream()
|
||||
.filter(JsonObject.class::isInstance)
|
||||
.map(JsonObject.class::cast)
|
||||
.map(topLevelButton -> topLevelButton.getObject("toggleButtonRenderer"))
|
||||
.filter(toggleButtonRenderer -> toggleButtonRenderer.getString("targetId")
|
||||
.equalsIgnoreCase("watch-like")
|
||||
|| toggleButtonRenderer.getObject("defaultIcon")
|
||||
.getString("iconType")
|
||||
.equalsIgnoreCase("LIKE")
|
||||
|| toggleButtonRenderer.getObject("toggleButtonSupportedData")
|
||||
.getObject("toggleButtonIdData")
|
||||
.getString("id")
|
||||
.equalsIgnoreCase("TOGGLE_BUTTON_ID_TYPE_LIKE"))
|
||||
.findFirst()
|
||||
.orElseThrow(() -> new ParsingException(
|
||||
"The like button is missing even though ratings are enabled"));
|
||||
}
|
||||
|
||||
// Use one of the accessibility strings available (this one has the same path as the
|
||||
// one used for comments' like count extraction)
|
||||
likesString = likeToggleButtonRenderer.getObject("accessibilityData")
|
||||
.getObject("accessibilityData")
|
||||
.getString("label");
|
||||
|
||||
// Use the other accessibility string available which contains the exact like count
|
||||
// Old - pre Dec 2023 way
|
||||
if (likesString == null) {
|
||||
likesString = likeToggleButtonRenderer.getObject("accessibility")
|
||||
.getString("label");
|
||||
}
|
||||
|
||||
// Last method: use the defaultText's accessibility data, which contains the exact like
|
||||
// count too, except when it is equal to 0, where a localized string is returned instead
|
||||
if (likesString == null) {
|
||||
likesString = likeToggleButtonRenderer.getObject("defaultText")
|
||||
.getObject("accessibility")
|
||||
.getObject("accessibilityData")
|
||||
.getString("label");
|
||||
likesString = getPreDec2023LikeString(topLevelButtons);
|
||||
}
|
||||
|
||||
// If ratings are allowed and the likes string is null, it means that we couldn't
|
||||
|
@ -480,6 +441,67 @@ public class YoutubeStreamExtractor extends StreamExtractor {
|
|||
}
|
||||
}
|
||||
|
||||
protected String getPreDec2023LikeString(final List<JsonObject> topLevelButtons)
|
||||
throws ParsingException {
|
||||
// Try first with the new video actions buttons data structure
|
||||
JsonObject likeToggleButtonRenderer = topLevelButtons.stream()
|
||||
.map(button -> button.getObject("segmentedLikeDislikeButtonRenderer")
|
||||
.getObject("likeButton")
|
||||
.getObject("toggleButtonRenderer"))
|
||||
.filter(toggleButtonRenderer -> !isNullOrEmpty(toggleButtonRenderer))
|
||||
.findFirst()
|
||||
.orElse(null);
|
||||
|
||||
// Use the old video actions buttons data structure if the new one isn't returned
|
||||
if (likeToggleButtonRenderer == null) {
|
||||
/*
|
||||
In the old video actions buttons data structure, there are 3 ways to detect whether
|
||||
a button is the like button, using its toggleButtonRenderer:
|
||||
- checking whether toggleButtonRenderer.targetId is equal to watch-like;
|
||||
- checking whether toggleButtonRenderer.defaultIcon.iconType is equal to LIKE;
|
||||
- checking whether
|
||||
toggleButtonRenderer.toggleButtonSupportedData.toggleButtonIdData.id
|
||||
is equal to TOGGLE_BUTTON_ID_TYPE_LIKE.
|
||||
*/
|
||||
likeToggleButtonRenderer = topLevelButtons.stream()
|
||||
.map(topLevelButton -> topLevelButton.getObject("toggleButtonRenderer"))
|
||||
.filter(toggleButtonRenderer -> "watch-like".equalsIgnoreCase(
|
||||
toggleButtonRenderer.getString("targetId"))
|
||||
|| "LIKE".equalsIgnoreCase(
|
||||
toggleButtonRenderer.getObject("defaultIcon")
|
||||
.getString("iconType"))
|
||||
|| "TOGGLE_BUTTON_ID_TYPE_LIKE".equalsIgnoreCase(
|
||||
toggleButtonRenderer.getObject("toggleButtonSupportedData")
|
||||
.getObject("toggleButtonIdData")
|
||||
.getString("id")))
|
||||
.findFirst()
|
||||
.orElseThrow(() -> new ParsingException(
|
||||
"The like button is missing even though ratings are enabled"));
|
||||
}
|
||||
|
||||
// Use one of the accessibility strings available (this one has the same path as the
|
||||
// one used for comments' like count extraction)
|
||||
String likesString = likeToggleButtonRenderer.getObject("accessibilityData")
|
||||
.getObject("accessibilityData")
|
||||
.getString("label");
|
||||
|
||||
// Use the other accessibility string available which contains the exact like count
|
||||
if (likesString == null) {
|
||||
likesString = likeToggleButtonRenderer.getObject("accessibility")
|
||||
.getString("label");
|
||||
}
|
||||
|
||||
// Last method: use the defaultText's accessibility data, which contains the exact like
|
||||
// count too, except when it is equal to 0, where a localized string is returned instead
|
||||
if (likesString == null) {
|
||||
likesString = likeToggleButtonRenderer.getObject("defaultText")
|
||||
.getObject("accessibility")
|
||||
.getObject("accessibilityData")
|
||||
.getString("label");
|
||||
}
|
||||
return likesString;
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
@Override
|
||||
public String getUploaderUrl() throws ParsingException {
|
||||
|
|
Loading…
Reference in New Issue