@@ -388,57 +388,43 @@ public long getLikeCount() throws ParsingException {
388388
389389 // If ratings are not allowed, there is no like count available
390390 if (!playerResponse .getObject ("videoDetails" ).getBoolean ("allowRatings" )) {
391- return -1 ;
391+ return -1L ;
392392 }
393393
394- String likesString = "" ;
394+ final JsonArray topLevelButtons = getVideoPrimaryInfoRenderer ()
395+ .getObject ("videoActions" )
396+ .getObject ("menuRenderer" )
397+ .getArray ("topLevelButtons" );
395398
396399 try {
397- final JsonArray topLevelButtons = getVideoPrimaryInfoRenderer ()
398- .getObject ("videoActions" )
399- .getObject ("menuRenderer" )
400- .getArray ("topLevelButtons" );
400+ return parseLikeCountFromLikeButtonViewModel (topLevelButtons );
401+ } catch (final ParsingException ignored ) {
402+ // A segmentedLikeDislikeButtonRenderer could be returned instead of a
403+ // segmentedLikeDislikeButtonViewModel, so ignore extraction errors relative to
404+ // segmentedLikeDislikeButtonViewModel object
405+ }
401406
402- // Try first with the new video actions buttons data structure
403- JsonObject likeToggleButtonRenderer = topLevelButtons .stream ()
404- .filter (JsonObject .class ::isInstance )
405- .map (JsonObject .class ::cast )
406- .map (button -> button .getObject ("segmentedLikeDislikeButtonRenderer" )
407- .getObject ("likeButton" )
408- .getObject ("toggleButtonRenderer" ))
409- .filter (toggleButtonRenderer -> !isNullOrEmpty (toggleButtonRenderer ))
410- .findFirst ()
411- .orElse (null );
412-
413- // Use the old video actions buttons data structure if the new one isn't returned
414- if (likeToggleButtonRenderer == null ) {
415- /*
416- In the old video actions buttons data structure, there are 3 ways to detect whether
417- a button is the like button, using its toggleButtonRenderer:
418- - checking whether toggleButtonRenderer.targetId is equal to watch-like;
419- - checking whether toggleButtonRenderer.defaultIcon.iconType is equal to LIKE;
420- - checking whether
421- toggleButtonRenderer.toggleButtonSupportedData.toggleButtonIdData.id
422- is equal to TOGGLE_BUTTON_ID_TYPE_LIKE.
423- */
424- likeToggleButtonRenderer = topLevelButtons .stream ()
425- .filter (JsonObject .class ::isInstance )
426- .map (JsonObject .class ::cast )
427- .map (topLevelButton -> topLevelButton .getObject ("toggleButtonRenderer" ))
428- .filter (toggleButtonRenderer -> toggleButtonRenderer .getString ("targetId" )
429- .equalsIgnoreCase ("watch-like" )
430- || toggleButtonRenderer .getObject ("defaultIcon" )
431- .getString ("iconType" )
432- .equalsIgnoreCase ("LIKE" )
433- || toggleButtonRenderer .getObject ("toggleButtonSupportedData" )
434- .getObject ("toggleButtonIdData" )
435- .getString ("id" )
436- .equalsIgnoreCase ("TOGGLE_BUTTON_ID_TYPE_LIKE" ))
437- .findFirst ()
438- .orElseThrow (() -> new ParsingException (
439- "The like button is missing even though ratings are enabled" ));
440- }
407+ try {
408+ return parseLikeCountFromLikeButtonRenderer (topLevelButtons );
409+ } catch (final ParsingException e ) {
410+ throw new ParsingException ("Could not get like count" , e );
411+ }
412+ }
441413
414+ private static long parseLikeCountFromLikeButtonRenderer (
415+ @ Nonnull final JsonArray topLevelButtons ) throws ParsingException {
416+ String likesString = null ;
417+ final JsonObject likeToggleButtonRenderer = topLevelButtons .stream ()
418+ .filter (JsonObject .class ::isInstance )
419+ .map (JsonObject .class ::cast )
420+ .map (button -> button .getObject ("segmentedLikeDislikeButtonRenderer" )
421+ .getObject ("likeButton" )
422+ .getObject ("toggleButtonRenderer" ))
423+ .filter (toggleButtonRenderer -> !isNullOrEmpty (toggleButtonRenderer ))
424+ .findFirst ()
425+ .orElse (null );
426+
427+ if (likeToggleButtonRenderer != null ) {
442428 // Use one of the accessibility strings available (this one has the same path as the
443429 // one used for comments' like count extraction)
444430 likesString = likeToggleButtonRenderer .getObject ("accessibilityData" )
@@ -460,23 +446,58 @@ public long getLikeCount() throws ParsingException {
460446 .getString ("label" );
461447 }
462448
463- // If ratings are allowed and the likes string is null, it means that we couldn't
464- // extract the (real) like count from accessibility data
465- if (likesString == null ) {
466- throw new ParsingException ("Could not get like count from accessibility data" );
467- }
468-
469449 // This check only works with English localizations!
470- if (likesString .toLowerCase ().contains ("no likes" )) {
450+ if (likesString != null && likesString .toLowerCase ().contains ("no likes" )) {
471451 return 0 ;
472452 }
453+ }
473454
474- return Integer .parseInt (Utils .removeNonDigitCharacters (likesString ));
475- } catch (final NumberFormatException nfe ) {
476- throw new ParsingException ("Could not parse \" " + likesString + "\" as an Integer" ,
477- nfe );
478- } catch (final Exception e ) {
479- throw new ParsingException ("Could not get like count" , e );
455+ // If ratings are allowed and the likes string is null, it means that we couldn't extract
456+ // the full like count from accessibility data
457+ if (likesString == null ) {
458+ throw new ParsingException ("Could not get like count from accessibility data" );
459+ }
460+
461+ try {
462+ return Long .parseLong (Utils .removeNonDigitCharacters (likesString ));
463+ } catch (final NumberFormatException e ) {
464+ throw new ParsingException ("Could not parse \" " + likesString + "\" as a long" , e );
465+ }
466+ }
467+
468+ private static long parseLikeCountFromLikeButtonViewModel (
469+ @ Nonnull final JsonArray topLevelButtons ) throws ParsingException {
470+ // Try first with the current video actions buttons data structure
471+ final JsonObject likeToggleButtonViewModel = topLevelButtons .stream ()
472+ .filter (JsonObject .class ::isInstance )
473+ .map (JsonObject .class ::cast )
474+ .map (button -> button .getObject ("segmentedLikeDislikeButtonViewModel" )
475+ .getObject ("likeButtonViewModel" )
476+ .getObject ("likeButtonViewModel" )
477+ .getObject ("toggleButtonViewModel" )
478+ .getObject ("toggleButtonViewModel" )
479+ .getObject ("defaultButtonViewModel" )
480+ .getObject ("buttonViewModel" ))
481+ .filter (buttonViewModel -> !isNullOrEmpty (buttonViewModel ))
482+ .findFirst ()
483+ .orElse (null );
484+
485+ if (likeToggleButtonViewModel == null ) {
486+ throw new ParsingException ("Could not find buttonViewModel object" );
487+ }
488+
489+ final String accessibilityText = likeToggleButtonViewModel .getString ("accessibilityText" );
490+ if (accessibilityText == null ) {
491+ throw new ParsingException ("Could not find buttonViewModel's accessibilityText string" );
492+ }
493+
494+ // The like count is always returned as a number in this element, even for videos with no
495+ // likes
496+ try {
497+ return Long .parseLong (Utils .removeNonDigitCharacters (accessibilityText ));
498+ } catch (final NumberFormatException e ) {
499+ throw new ParsingException (
500+ "Could not parse \" " + accessibilityText + "\" as a long" , e );
480501 }
481502 }
482503
0 commit comments