@@ -417,28 +417,88 @@ public long getViewCount() throws ParsingException {
417417 @ Override
418418 public long getLikeCount () throws ParsingException {
419419 assertPageFetched ();
420+
421+ // If ratings are not allowed, there is no like count available
422+ if (!playerResponse .getObject ("videoDetails" ).getBoolean ("allowRatings" )) {
423+ return -1 ;
424+ }
425+
420426 String likesString = "" ;
427+
421428 try {
422- likesString = getVideoPrimaryInfoRenderer ()
429+ final JsonArray topLevelButtons = getVideoPrimaryInfoRenderer ()
423430 .getObject ("videoActions" )
424431 .getObject ("menuRenderer" )
425- .getArray ("topLevelButtons" )
426- .getObject (0 )
427- .getObject ("toggleButtonRenderer" )
428- .getObject ("defaultText" )
429- .getObject ("accessibility" )
432+ .getArray ("topLevelButtons" );
433+
434+ // Try first with the new video actions buttons data structure
435+ JsonObject likeToggleButtonRenderer = topLevelButtons .stream ()
436+ .filter (JsonObject .class ::isInstance )
437+ .map (JsonObject .class ::cast )
438+ .map (button -> button .getObject ("segmentedLikeDislikeButtonRenderer" )
439+ .getObject ("likeButton" )
440+ .getObject ("toggleButtonRenderer" ))
441+ .filter (toggleButtonRenderer -> !isNullOrEmpty (toggleButtonRenderer ))
442+ .findFirst ()
443+ .orElse (null );
444+
445+ // Use the old video actions buttons data structure if the new one isn't returned
446+ if (likeToggleButtonRenderer == null ) {
447+ /*
448+ In the old video actions buttons data structure, there are 3 ways to detect whether
449+ a button is the like button, using its toggleButtonRenderer:
450+ - checking whether toggleButtonRenderer.targetId is equal to watch-like;
451+ - checking whether toggleButtonRenderer.defaultIcon.iconType is equal to LIKE;
452+ - checking whether
453+ toggleButtonRenderer.toggleButtonSupportedData.toggleButtonIdData.id
454+ is equal to TOGGLE_BUTTON_ID_TYPE_LIKE.
455+ */
456+ likeToggleButtonRenderer = topLevelButtons .stream ()
457+ .filter (JsonObject .class ::isInstance )
458+ .map (JsonObject .class ::cast )
459+ .map (topLevelButton -> topLevelButton .getObject ("toggleButtonRenderer" ))
460+ .filter (toggleButtonRenderer -> toggleButtonRenderer .getString ("targetId" )
461+ .equalsIgnoreCase ("watch-like" )
462+ || toggleButtonRenderer .getObject ("defaultIcon" )
463+ .getString ("iconType" )
464+ .equalsIgnoreCase ("LIKE" )
465+ || toggleButtonRenderer .getObject ("toggleButtonSupportedData" )
466+ .getObject ("toggleButtonIdData" )
467+ .getString ("id" )
468+ .equalsIgnoreCase ("TOGGLE_BUTTON_ID_TYPE_LIKE" ))
469+ .findFirst ()
470+ .orElseThrow (() -> new ParsingException (
471+ "The like button is missing even though ratings are enabled" ));
472+ }
473+
474+ // Use one of the accessibility strings available (this one has the same path as the
475+ // one used for comments' like count extraction)
476+ likesString = likeToggleButtonRenderer .getObject ("accessibilityData" )
430477 .getObject ("accessibilityData" )
431478 .getString ("label" );
432479
480+ // Use the other accessibility string available which contains the exact like count
433481 if (likesString == null ) {
434- // If this kicks in our button has no content and therefore ratings must be disabled
435- if (playerResponse .getObject ("videoDetails" ).getBoolean ("allowRatings" )) {
436- throw new ParsingException (
437- "Ratings are enabled even though the like button is missing" );
438- }
439- return -1 ;
482+ likesString = likeToggleButtonRenderer .getObject ("accessibility" )
483+ .getString ("label" );
484+ }
485+
486+ // Last method: use the defaultText's accessibility data, which contains the exact like
487+ // count too, except when it is equal to 0, where a localized string is returned instead
488+ if (likesString == null ) {
489+ likesString = likeToggleButtonRenderer .getObject ("defaultText" )
490+ .getObject ("accessibility" )
491+ .getObject ("accessibilityData" )
492+ .getString ("label" );
440493 }
441494
495+ // If ratings are allowed and the likes string is null, it means that we couldn't
496+ // extract the (real) like count from accessibility data
497+ if (likesString == null ) {
498+ throw new ParsingException ("Could not get like count from accessibility data" );
499+ }
500+
501+ // This check only works with English localizations!
442502 if (likesString .toLowerCase ().contains ("no likes" )) {
443503 return 0 ;
444504 }
@@ -448,10 +508,7 @@ public long getLikeCount() throws ParsingException {
448508 throw new ParsingException ("Could not parse \" " + likesString + "\" as an Integer" ,
449509 nfe );
450510 } catch (final Exception e ) {
451- if (getAgeLimit () == NO_AGE_LIMIT ) {
452- throw new ParsingException ("Could not get like count" , e );
453- }
454- return -1 ;
511+ throw new ParsingException ("Could not get like count" , e );
455512 }
456513 }
457514
0 commit comments