package YTMusicAPI::Navigation; use strict; use warnings; use Exporter 'import'; our $CONTENT = [ "contents", 0 ]; our $RUN_TEXT = [ "runs", 0, "text" ]; our $TAB_CONTENT = [ "tabs", 0, "tabRenderer", "content" ]; our $TAB_1_CONTENT = [ "tabs", 1, "tabRenderer", "content" ]; our $TWO_COLUMN_RENDERER = [ "contents", "twoColumnBrowseResultsRenderer" ]; our $SINGLE_COLUMN = [ "contents", "singleColumnBrowseResultsRenderer" ]; our $SINGLE_COLUMN_TAB = [ @$SINGLE_COLUMN, @$TAB_CONTENT ]; our $SECTION = ["sectionListRenderer"]; our $SECTION_LIST = [ @$SECTION, "contents" ]; our $SECTION_LIST_ITEM = [ @$SECTION, @$CONTENT ]; our $RESPONSIVE_HEADER = ["musicResponsiveHeaderRenderer"]; our $ITEM_SECTION = [ "itemSectionRenderer", @$CONTENT ]; our $MUSIC_SHELF = ["musicShelfRenderer"]; our $GRID = ["gridRenderer"]; our $GRID_ITEMS = [ @$GRID, "items" ]; our $MENU = [ "menu", "menuRenderer" ]; our $MENU_ITEMS = [ @$MENU, "items" ]; our $MENU_LIKE_STATUS = [ @$MENU, "topLevelButtons", 0, "likeButtonRenderer", "likeStatus" ]; our $MENU_SERVICE = [ "menuServiceItemRenderer", "serviceEndpoint" ]; our $TOGGLE_MENU = "toggleMenuServiceItemRenderer"; our $OVERLAY_RENDERER = [ "musicItemThumbnailOverlayRenderer", "content", "musicPlayButtonRenderer" ]; our $PLAY_BUTTON = [ "overlay", @$OVERLAY_RENDERER ]; our $NAVIGATION_BROWSE = [ "navigationEndpoint", "browseEndpoint" ]; our $NAVIGATION_BROWSE_ID = [ @$NAVIGATION_BROWSE, "browseId" ]; our $PAGE_TYPE = [ "browseEndpointContextSupportedConfigs", "browseEndpointContextMusicConfig", "pageType" ]; our $WATCH_VIDEO_ID = [ "watchEndpoint", "videoId" ]; our $NAVIGATION_VIDEO_ID = [ "navigationEndpoint", @$WATCH_VIDEO_ID ]; our $QUEUE_VIDEO_ID = [ "queueAddEndpoint", "queueTarget", "videoId" ]; our $NAVIGATION_PLAYLIST_ID = [ "navigationEndpoint", "watchEndpoint", "playlistId" ]; our $WATCH_PID = [ "watchPlaylistEndpoint", "playlistId" ]; our $NAVIGATION_WATCH_PLAYLIST_ID = [ "navigationEndpoint", @$WATCH_PID ]; our $NAVIGATION_VIDEO_TYPE = [ "watchEndpoint", "watchEndpointMusicSupportedConfigs", "watchEndpointMusicConfig", "musicVideoType", ]; our $ICON_TYPE = [ "icon", "iconType" ]; our $TOGGLED_BUTTON = [ "toggleButtonRenderer", "isToggled" ]; our $TITLE = [ "title", "runs", 0 ]; our $TITLE_TEXT = [ "title", @$RUN_TEXT ]; our $TEXT_RUNS = [ "text", "runs" ]; our $TEXT_RUN = [ @$TEXT_RUNS, 0 ]; our $TEXT_RUN_TEXT = [ @$TEXT_RUN, "text" ]; our $SUBTITLE = [ "subtitle", @$RUN_TEXT ]; our $SUBTITLE_RUNS = [ "subtitle", "runs" ]; our $SUBTITLE_RUN = [ @$SUBTITLE_RUNS, 0 ]; our $SUBTITLE2 = [ @$SUBTITLE_RUNS, 2, "text" ]; our $SUBTITLE3 = [ @$SUBTITLE_RUNS, 4, "text" ]; our $THUMBNAIL = [ "thumbnail", "thumbnails" ]; our $THUMBNAILS = [ "thumbnail", "musicThumbnailRenderer", @$THUMBNAIL ]; our $THUMBNAIL_RENDERER = [ "thumbnailRenderer", "musicThumbnailRenderer", @$THUMBNAIL ]; our $THUMBNAIL_OVERLAY = [ "thumbnailOverlay", @$OVERLAY_RENDERER, "playNavigationEndpoint", @$WATCH_PID ]; our $THUMBNAIL_CROPPED = [ "thumbnail", "croppedSquareThumbnailRenderer", @$THUMBNAIL ]; our $FEEDBACK_TOKEN = [ "feedbackEndpoint", "feedbackToken" ]; our $BADGE_PATH = [ 0, "musicInlineBadgeRenderer", "accessibilityData", "accessibilityData", "label" ]; our $BADGE_LABEL = [ "badges", @$BADGE_PATH ]; our $SUBTITLE_BADGE_LABEL = [ "subtitleBadges", @$BADGE_PATH ]; our $CATEGORY_TITLE = [ "musicNavigationButtonRenderer", "buttonText", @$RUN_TEXT ]; our $CATEGORY_PARAMS = [ "musicNavigationButtonRenderer", "clickCommand", "browseEndpoint", "params" ]; our $MMRIR = "musicMultiRowListItemRenderer"; our $MRLIR = "musicResponsiveListItemRenderer"; our $MTRIR = "musicTwoRowItemRenderer"; our $MNIR = "menuNavigationItemRenderer"; our $TASTE_PROFILE_ITEMS = [ "contents", "tastebuilderRenderer", "contents" ]; our $TASTE_PROFILE_ARTIST = [ "title", "runs" ]; our $SECTION_LIST_CONTINUATION = [ "continuationContents", "sectionListContinuation" ]; our $MENU_PLAYLIST_ID = [ @$MENU_ITEMS, 0, $MNIR, @$NAVIGATION_WATCH_PLAYLIST_ID ]; our $MULTI_SELECT = ["musicMultiSelectMenuItemRenderer"]; our $HEADER_DETAIL = [ "header", "musicDetailHeaderRenderer" ]; our $HEADER_SIDE = [ "header", "musicSideAlignedItemRenderer" ]; our $HEADER_MUSIC_VISUAL = [ "header", "musicVisualHeaderRenderer" ]; our $DESCRIPTION_SHELF = ["musicDescriptionShelfRenderer"]; our $DESCRIPTION = [ "description", @$RUN_TEXT ]; our $CAROUSEL = ["musicCarouselShelfRenderer"]; our $IMMERSIVE_CAROUSEL = ["musicImmersiveCarouselShelfRenderer"]; our $CAROUSEL_CONTENTS = [ @$CAROUSEL, "contents" ]; our $CAROUSEL_TITLE = [ "header", "musicCarouselShelfBasicHeaderRenderer", @$TITLE ]; our $CARD_SHELF_TITLE = [ "header", "musicCardShelfHeaderBasicRenderer", @$TITLE_TEXT ]; our $FRAMEWORK_MUTATIONS = [ "frameworkUpdates", "entityBatchUpdate", "mutations" ]; sub nav { my ( $root, $items, $none_if_absent ) = @_; $none_if_absent //= 0; if (!ref($items)) { $root = $root->{$items}; return $root; } foreach my $k (@$items) { if ( ref($root) eq 'HASH' && exists $root->{$k} ) { $root = $root->{$k}; } elsif ( ref($root) eq 'ARRAY' && $k =~ /^\d+$/ && $k < @$root ) { $root = $root->[$k]; } else { if ($none_if_absent) { return undef; } else { die "Unable to find '$k' using path @$items on $root"; } } } return $root; } sub find_object_by_key { my ( $object_list, $key, $nested, $is_key ) = @_; foreach my $item (@$object_list) { if ($nested) { $item = $item->{$nested}; } if ( exists $item->{$key} ) { return $is_key ? $item->{$key} : $item; } } return undef; } sub find_objects_by_key { my ( $object_list, $key, $nested ) = @_; my @objects; foreach my $item (@$object_list) { if ($nested) { $item = $item->{$nested}; } if ( exists $item->{$key} ) { push @objects, $item; } } return \@objects; } our @EXPORT = qw( $CONTENT $RUN_TEXT $TAB_CONTENT $TAB_1_CONTENT $TWO_COLUMN_RENDERER $SINGLE_COLUMN $SINGLE_COLUMN_TAB $SECTION $SECTION_LIST $SECTION_LIST_ITEM $RESPONSIVE_HEADER $ITEM_SECTION $MUSIC_SHELF $GRID $GRID_ITEMS $MENU $MENU_ITEMS $MENU_LIKE_STATUS $MENU_SERVICE $TOGGLE_MENU $OVERLAY_RENDERER $PLAY_BUTTON $NAVIGATION_BROWSE $NAVIGATION_BROWSE_ID $PAGE_TYPE $WATCH_VIDEO_ID $NAVIGATION_VIDEO_ID $QUEUE_VIDEO_ID $NAVIGATION_PLAYLIST_ID $WATCH_PID $NAVIGATION_WATCH_PLAYLIST_ID $NAVIGATION_VIDEO_TYPE $ICON_TYPE $TOGGLED_BUTTON $TITLE $TITLE_TEXT $TEXT_RUNS $TEXT_RUN $TEXT_RUN_TEXT $SUBTITLE $SUBTITLE_RUNS $SUBTITLE_RUN $SUBTITLE2 $SUBTITLE3 $THUMBNAIL $THUMBNAILS $THUMBNAIL_RENDERER $THUMBNAIL_OVERLAY $THUMBNAIL_CROPPED $FEEDBACK_TOKEN $BADGE_PATH $BADGE_LABEL $SUBTITLE_BADGE_LABEL $CATEGORY_TITLE $CATEGORY_PARAMS $MMRIR $MRLIR $MTRIR $MNIR $TASTE_PROFILE_ITEMS $TASTE_PROFILE_ARTIST $SECTION_LIST_CONTINUATION $MENU_PLAYLIST_ID $MULTI_SELECT $HEADER_DETAIL $HEADER_SIDE $HEADER_MUSIC_VISUAL $DESCRIPTION_SHELF $DESCRIPTION $CAROUSEL $IMMERSIVE_CAROUSEL $CAROUSEL_CONTENTS $CAROUSEL_TITLE $CARD_SHELF_TITLE $FRAMEWORK_MUTATIONS nav find_object_by_key find_objects_by_key ); 1;