File: navigation.py

package info (click to toggle)
python-ytmusicapi 1.10.2-1
  • links: PTS, VCS
  • area: main
  • in suites: sid
  • size: 3,412 kB
  • sloc: python: 4,324; sh: 14; makefile: 12
file content (141 lines) | stat: -rw-r--r-- 5,829 bytes parent folder | download | duplicates (2)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
"""commonly used navigation paths"""

from typing import Any, Literal, Optional, overload

CONTENT = ["contents", 0]
RUN_TEXT = ["runs", 0, "text"]
TAB_CONTENT = ["tabs", 0, "tabRenderer", "content"]
TAB_1_CONTENT = ["tabs", 1, "tabRenderer", "content"]
TAB_2_CONTENT = ["tabs", 2, "tabRenderer", "content"]
TWO_COLUMN_RENDERER = ["contents", "twoColumnBrowseResultsRenderer"]
SINGLE_COLUMN = ["contents", "singleColumnBrowseResultsRenderer"]
SINGLE_COLUMN_TAB = SINGLE_COLUMN + TAB_CONTENT
SECTION = ["sectionListRenderer"]
SECTION_LIST = [*SECTION, "contents"]
SECTION_LIST_ITEM = SECTION + CONTENT
RESPONSIVE_HEADER = ["musicResponsiveHeaderRenderer"]
ITEM_SECTION = ["itemSectionRenderer", *CONTENT]
MUSIC_SHELF = ["musicShelfRenderer"]
GRID = ["gridRenderer"]
GRID_ITEMS = [*GRID, "items"]
MENU = ["menu", "menuRenderer"]
MENU_ITEMS = [*MENU, "items"]
MENU_LIKE_STATUS = [*MENU, "topLevelButtons", 0, "likeButtonRenderer", "likeStatus"]
MENU_SERVICE = ["menuServiceItemRenderer", "serviceEndpoint"]
TOGGLE_MENU = "toggleMenuServiceItemRenderer"
OVERLAY_RENDERER = ["musicItemThumbnailOverlayRenderer", "content", "musicPlayButtonRenderer"]
PLAY_BUTTON = ["overlay", *OVERLAY_RENDERER]
NAVIGATION_BROWSE = ["navigationEndpoint", "browseEndpoint"]
NAVIGATION_BROWSE_ID = [*NAVIGATION_BROWSE, "browseId"]
PAGE_TYPE = ["browseEndpointContextSupportedConfigs", "browseEndpointContextMusicConfig", "pageType"]
WATCH_VIDEO_ID = ["watchEndpoint", "videoId"]
PLAYLIST_ID = ["playlistId"]
WATCH_PLAYLIST_ID = ["watchEndpoint", *PLAYLIST_ID]
NAVIGATION_VIDEO_ID = ["navigationEndpoint", *WATCH_VIDEO_ID]
QUEUE_VIDEO_ID = ["queueAddEndpoint", "queueTarget", "videoId"]
NAVIGATION_PLAYLIST_ID = ["navigationEndpoint", *WATCH_PLAYLIST_ID]
WATCH_PID = ["watchPlaylistEndpoint", *PLAYLIST_ID]
NAVIGATION_WATCH_PLAYLIST_ID = ["navigationEndpoint", *WATCH_PID]
NAVIGATION_VIDEO_TYPE = [
    "watchEndpoint",
    "watchEndpointMusicSupportedConfigs",
    "watchEndpointMusicConfig",
    "musicVideoType",
]
ICON_TYPE = ["icon", "iconType"]
TOGGLED_BUTTON = ["toggleButtonRenderer", "isToggled"]
TITLE = ["title", "runs", 0]
TITLE_TEXT = ["title", *RUN_TEXT]
TEXT_RUNS = ["text", "runs"]
TEXT_RUN = [*TEXT_RUNS, 0]
TEXT_RUN_TEXT = [*TEXT_RUN, "text"]
SUBTITLE = ["subtitle", *RUN_TEXT]
SUBTITLE_RUNS = ["subtitle", "runs"]
SUBTITLE_RUN = [*SUBTITLE_RUNS, 0]
SUBTITLE2 = [*SUBTITLE_RUNS, 2, "text"]
SUBTITLE3 = [*SUBTITLE_RUNS, 4, "text"]
THUMBNAIL = ["thumbnail", "thumbnails"]
THUMBNAILS = ["thumbnail", "musicThumbnailRenderer", *THUMBNAIL]
THUMBNAIL_RENDERER = ["thumbnailRenderer", "musicThumbnailRenderer", *THUMBNAIL]
THUMBNAIL_OVERLAY = ["thumbnailOverlay", *OVERLAY_RENDERER, "playNavigationEndpoint", *WATCH_PID]
THUMBNAIL_CROPPED = ["thumbnail", "croppedSquareThumbnailRenderer", *THUMBNAIL]
FEEDBACK_TOKEN = ["feedbackEndpoint", "feedbackToken"]
BADGE_PATH = [0, "musicInlineBadgeRenderer", "accessibilityData", "accessibilityData", "label"]
BADGE_LABEL = ["badges", *BADGE_PATH]
SUBTITLE_BADGE_LABEL = ["subtitleBadges", *BADGE_PATH]
CATEGORY_TITLE = ["musicNavigationButtonRenderer", "buttonText", *RUN_TEXT]
CATEGORY_PARAMS = ["musicNavigationButtonRenderer", "clickCommand", "browseEndpoint", "params"]
MMRIR = "musicMultiRowListItemRenderer"
MRLIR = "musicResponsiveListItemRenderer"
MTRIR = "musicTwoRowItemRenderer"
MNIR = "menuNavigationItemRenderer"
TASTE_PROFILE_ITEMS = ["contents", "tastebuilderRenderer", "contents"]
TASTE_PROFILE_ARTIST = ["title", "runs"]
SECTION_LIST_CONTINUATION = ["continuationContents", "sectionListContinuation"]
MENU_PLAYLIST_ID = [*MENU_ITEMS, 0, MNIR, *NAVIGATION_WATCH_PLAYLIST_ID]
MULTI_SELECT = ["musicMultiSelectMenuItemRenderer"]
HEADER = ["header"]
HEADER_DETAIL = [*HEADER, "musicDetailHeaderRenderer"]
EDITABLE_PLAYLIST_DETAIL_HEADER = ["musicEditablePlaylistDetailHeaderRenderer"]
HEADER_EDITABLE_DETAIL = [*HEADER, *EDITABLE_PLAYLIST_DETAIL_HEADER]
HEADER_SIDE = [*HEADER, "musicSideAlignedItemRenderer"]
HEADER_MUSIC_VISUAL = [*HEADER, "musicVisualHeaderRenderer"]
DESCRIPTION_SHELF = ["musicDescriptionShelfRenderer"]
DESCRIPTION = ["description", *RUN_TEXT]
CAROUSEL = ["musicCarouselShelfRenderer"]
IMMERSIVE_CAROUSEL = ["musicImmersiveCarouselShelfRenderer"]
CAROUSEL_CONTENTS = [*CAROUSEL, "contents"]
CAROUSEL_TITLE = [*HEADER, "musicCarouselShelfBasicHeaderRenderer", *TITLE]
CARD_SHELF_TITLE = [*HEADER, "musicCardShelfHeaderBasicRenderer", *TITLE_TEXT]
FRAMEWORK_MUTATIONS = ["frameworkUpdates", "entityBatchUpdate", "mutations"]
TIMESTAMPED_LYRICS = [
    "contents",
    "elementRenderer",
    "newElement",
    "type",
    "componentType",
    "model",
    "timedLyricsModel",
    "lyricsData",
]


@overload
def nav(root: dict[str, Any], items: list[Any], none_if_absent: Literal[False] = False) -> Any:
    """overload for mypy only"""


@overload
def nav(root: dict, items: list[Any], none_if_absent: Literal[True] = True) -> Optional[Any]:
    """overload for mypy only"""


def nav(root: dict, items: list[Any], none_if_absent: bool = False) -> Optional[Any]:
    """Access a nested object in root by item sequence."""
    try:
        for k in items:
            root = root[k]
    except (KeyError, IndexError, TypeError) as e:
        if none_if_absent:
            return None
        raise type(e)(f"Unable to find '{k}' using path {items!r} on {root!r}, exception: {e}")
    return root


def find_object_by_key(object_list, key, nested=None, is_key=False):
    for item in object_list:
        if nested:
            item = item[nested]
        if key in item:
            return item[key] if is_key else item
    return None


def find_objects_by_key(object_list, key, nested=None):
    objects = []
    for item in object_list:
        if nested:
            item = item[nested]
        if key in item:
            objects.append(item)
    return objects