Skip to content

Commit

Permalink
✨ Implement WEB_REMIX search endpoint
Browse files Browse the repository at this point in the history
Merge pull request #5 from tombulled/feature/search-albums
  • Loading branch information
tombulled authored May 27, 2024
2 parents 28c1e1d + fb85a07 commit 219aed3
Show file tree
Hide file tree
Showing 24 changed files with 386 additions and 170 deletions.
299 changes: 150 additions & 149 deletions poetry.lock

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ readme = "README.md"

[tool.poetry.dependencies]
python = "^3.10"
innertube = "^2.1.13"
innertube = "^2.1.16"
pyhumps = "^3.8.0"
pydantic = "^2.5.3"
typing-extensions = "^4.9.0"
Expand Down
4 changes: 4 additions & 0 deletions youtubei/clients/web_remix/parser.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
WebRemixGetBrowseAlbumDetailPageResponse,
WebRemixGetBrowsePlaylistDetailPageResponse,
WebRemixGuideResponse,
WebRemixGetSearchResponse,
)

__all__ = ("WebRemixParser",)
Expand All @@ -24,3 +25,6 @@ def config(self, response, /) -> WebRemixConfigResponse:

def guide(self, response, /) -> WebRemixGuideResponse:
return self._parse(response, WebRemixGuideResponse)

def search(self, response, /) -> WebRemixGetSearchResponse:
return self._parse(response, WebRemixGetSearchResponse)
6 changes: 5 additions & 1 deletion youtubei/clients/web_remix/responses.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,8 @@
MicroformatDataRenderer,
MusicDetailHeaderRenderer,
SingleColumnBrowseResultsRenderer,
TabbedSearchResultsRenderer,
)

from .types import GuideItem

__all__ = (
Expand Down Expand Up @@ -48,3 +48,7 @@ class WebRemixGetBrowseAlbumDetailPageResponse(WebRemixResponse):
class WebRemixGetBrowsePlaylistDetailPageResponse(WebRemixResponse):
contents: Dynamic[SingleColumnBrowseResultsRenderer]
header: Optional[Dynamic[MusicDetailHeaderRenderer]] = None


class WebRemixGetSearchResponse(WebRemixResponse):
contents: Dynamic[TabbedSearchResultsRenderer]
24 changes: 23 additions & 1 deletion youtubei/clients/web_remix/wrapper.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
from dataclasses import dataclass, field
from typing import Optional

from innertube import InnerTube
from innertube import InnerTube, utils
from innertube.enums import Endpoint

from .constants import WEB_REMIX_CLIENT, WEB_REMIX_PARSER
from .parser import WebRemixParser
Expand Down Expand Up @@ -53,3 +55,23 @@ def guide(self) -> WebRemixGuideResponse:
response: dict = self.client.adaptor.dispatch("guide")

return self.parser.guide(response)

def search(
self,
query: Optional[str] = None,
*,
params: Optional[str] = None,
continuation: Optional[str] = None,
):
response: dict = self.client.adaptor.dispatch(
Endpoint.SEARCH,
body=utils.filter(
dict(
query=query or "",
params=params,
continuation=continuation,
)
),
)

return self.parser.search(response)
8 changes: 8 additions & 0 deletions youtubei/enums/music.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
"MusicResponsiveListItemColumnDisplayPriority",
"MusicResponsiveListItemFixedColumnSize",
"MusicResponsiveListItemHeight",
"MusicResponsiveListItemFlexColumnDisplayStyle",
"MusicThumbnailCrop",
"MusicThumbnailScale",
"MusicVideoType",
Expand Down Expand Up @@ -41,17 +42,24 @@ class MusicResponsiveListItemFixedColumnSize(StrEnum):

class MusicResponsiveListItemHeight(StrEnum):
MEDIUM: str = "MUSIC_RESPONSIVE_LIST_ITEM_HEIGHT_MEDIUM"
TALL: str = "MUSIC_RESPONSIVE_LIST_ITEM_HEIGHT_TALL"

class MusicResponsiveListItemFlexColumnDisplayStyle(StrEnum):
TWO_LINE_STACK: str = "MUSIC_RESPONSIVE_LIST_ITEM_FLEX_COLUMN_DISPLAY_STYLE_TWO_LINE_STACK"

class MusicThumbnailCrop(StrEnum):
UNSPECIFIED: str = "MUSIC_THUMBNAIL_CROP_UNSPECIFIED"
CIRCLE: str = "MUSIC_THUMBNAIL_CROP_CIRCLE"


class MusicThumbnailScale(StrEnum):
ASPECT_FIT: str = "MUSIC_THUMBNAIL_SCALE_ASPECT_FIT"
ASPECT_FILL: str = "MUSIC_THUMBNAIL_SCALE_ASPECT_FILL"
UNSPECIFIED: str = "MUSIC_THUMBNAIL_SCALE_UNSPECIFIED"


class MusicVideoType(StrEnum):
ATV: str = "MUSIC_VIDEO_TYPE_ATV"
OMV: str = "MUSIC_VIDEO_TYPE_OMV"
UGC: str = "MUSIC_VIDEO_TYPE_UGC"
PODCAST_EPISODE: str = "MUSIC_VIDEO_TYPE_PODCAST_EPISODE"
6 changes: 6 additions & 0 deletions youtubei/enums/other.py
Original file line number Diff line number Diff line change
Expand Up @@ -497,6 +497,10 @@ class MusicPageType(StrEnum):
METRONOME = "MUSIC_PAGE_TYPE_METRONOME"
ALBUM = "MUSIC_PAGE_TYPE_ALBUM"
ARTIST = "MUSIC_PAGE_TYPE_ARTIST"
USER_CHANNEL = "MUSIC_PAGE_TYPE_USER_CHANNEL"
PODCAST_SHOW_DETAIL_PAGE = "MUSIC_PAGE_TYPE_PODCAST_SHOW_DETAIL_PAGE"
NON_MUSIC_AUDIO_TRACK_PAGE = "MUSIC_PAGE_TYPE_NON_MUSIC_AUDIO_TRACK_PAGE"
PLAYLIST = "MUSIC_PAGE_TYPE_PLAYLIST"


class PlaybackMode(StrEnum):
Expand Down Expand Up @@ -598,6 +602,8 @@ class Style(StrEnum):
MONO_TONAL_OVERLAY: str = "STYLE_MONO_TONAL_OVERLAY"
MONO_FILLED_OVERLAY: str = "STYLE_MONO_FILLED_OVERLAY"
DARK_ON_WHITE: str = "STYLE_DARK_ON_WHITE"
WHITE_TRANSLUCENT: str = "STYLE_WHITE_TRANSLUCENT"
SECONDARY: str = "STYLE_SECONDARY"


class Target(StrEnum):
Expand Down
9 changes: 7 additions & 2 deletions youtubei/models/endpoints.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,14 +15,15 @@
Signal,
Target,
)
from youtubei.enums.other import LikeStatus
from youtubei.models.actions import AddToToastAction
from youtubei.models.config import (
BrowseEndpointContextMusicConfig,
Html5PlaybackOnesieConfig,
WatchEndpointMusicConfig,
)
from youtubei.models.contexts import LoggingContext
from youtubei.models.other import PlaylistEditAction, QueueTarget
from youtubei.models.other import LikeTarget, PlaylistEditAction, QueueTarget
from youtubei.models.params import SkAdParameters
from youtubei.parse.validated_types import Dynamic
from youtubei.types import BrowseId
Expand Down Expand Up @@ -342,8 +343,10 @@ class IosApplicationEndpoint(BaseModel):
fallback_endpoint: DynamicCommand[Any] # Observed: AppStoreEndpoint


@WEB_REMIX_REGISTRY
class LikeEndpoint(BaseEndpoint):
pass
status: LikeStatus
target: LikeTarget


class LiveChatActionEndpoint(BaseEndpoint):
Expand Down Expand Up @@ -451,11 +454,13 @@ class ScrollToSectionEndpoint(BaseEndpoint):
pass


@WEB_REMIX_REGISTRY
@WEB_REGISTRY
@ANDROID_REGISTRY
@IOS_REGISTRY
class SearchEndpoint(BaseEndpoint):
query: str
params: Optional[str] = None


class SelectActiveIdentityEndpoint(BaseEndpoint):
Expand Down
4 changes: 2 additions & 2 deletions youtubei/models/music.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
from typing import Sequence
from typing import Optional, Sequence

from ._base import BaseModel

Expand All @@ -18,5 +18,5 @@ class MusicItemThumbnailOverlayBackground(BaseModel):


class PlaylistItemData(BaseModel):
playlist_set_video_id: str
video_id: str
playlist_set_video_id: Optional[str] = None
5 changes: 5 additions & 0 deletions youtubei/models/other.py
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@
"TranslationLanguage",
"VideoDetails",
"QueueTarget",
"LikeTarget",
"LikeButtonTarget",
)

Expand Down Expand Up @@ -256,5 +257,9 @@ class QueueTarget(BaseModel):
on_empty_queue: DynamicCommand[Any] # Observed: WatchEndpoint


class LikeTarget(BaseModel):
playlist_id: str


class LikeButtonTarget(BaseModel):
video_id: str
16 changes: 12 additions & 4 deletions youtubei/renderers/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,9 @@
from .button import ButtonRenderer
from .card import CardRenderer
from .card_collection import CardCollectionRenderer
from .checkbox import CheckboxRenderer
from .chip_cloud import ChipCloudRenderer
from .chip_cloud_chip import ChipCloudChipRenderer
from .cinematic_container import CinematicContainerRenderer
from .client_forecasting_ad import ClientForecastingAdRenderer
from .compact_link import CompactLinkRenderer
Expand Down Expand Up @@ -45,16 +48,18 @@
from .modal_with_title_and_button import ModalWithTitleAndButtonRenderer
from .multi_page_menu import MultiPageMenuRenderer
from .multi_page_menu_section import MultiPageMenuSectionRenderer
from .music_card_shelf import MusicCardShelfRenderer
from .music_card_shelf_header_basic import MusicCardShelfHeaderBasicRenderer
from .music_carousel_shelf import MusicCarouselShelfRenderer
from .music_data_bound_menu import MusicDataBoundMenuRenderer
from .music_detail_header import MusicDetailHeaderRenderer
from .music_inline_badge import MusicInlineBadgeRenderer
from .music_item_thumbnail_overlay import MusicItemThumbnailOverlayRenderer
from .music_play_button import MusicPlayButtonRenderer
from .music_playlist_shelf import MusicPlaylistShelfRenderer
from .music_responsive_list_item import MusicResponsiveListItemRenderer
from .music_responsive_list_item_flex_column import (
MusicResponsiveListItemFlexColumnRenderer,
)
from .music_responsive_list_item_flex_column import \
MusicResponsiveListItemFlexColumnRenderer
from .music_shelf import MusicShelfRenderer
from .music_shelf_divider import MusicShelfDividerRenderer
from .music_thumbnail import MusicThumbnailRenderer
Expand All @@ -67,7 +72,8 @@
from .player_bytes_sequential_layout import PlayerBytesSequentialLayoutRenderer
from .player_captions_tracklist import PlayerCaptionsTracklistRenderer
from .player_error_message import PlayerErrorMessageRenderer
from .player_legacy_desktop_watch_ads import PlayerLegacyDesktopWatchAdsRenderer
from .player_legacy_desktop_watch_ads import \
PlayerLegacyDesktopWatchAdsRenderer
from .player_microformat import PlayerMicroformatRenderer
from .player_storyboard_spec import PlayerStoryboardSpecRenderer
from .playlist_byline import PlaylistBylineRenderer
Expand All @@ -88,11 +94,13 @@
from .skip_button import SkipButtonRenderer
from .subscribe_button import SubscribeButtonRenderer
from .tab import TabRenderer
from .tabbed_search_results import TabbedSearchResultsRenderer
from .thumbnail_overlay_hover_text import ThumbnailOverlayHoverTextRenderer
from .thumbnail_overlay_now_playing import ThumbnailOverlayNowPlayingRenderer
from .thumbnail_overlay_side_panel import ThumbnailOverlaySidePanelRenderer
from .thumbnail_overlay_time_status import ThumbnailOverlayTimeStatusRenderer
from .toggle_button import ToggleButtonRenderer
from .toggle_menu_service_item import ToggleMenuServiceItemRenderer
from .topbar_button import TopbarButtonRenderer
from .topbar_logo import TopbarLogoRenderer
from .topbar_menu_button import TopbarMenuButtonRenderer
Expand Down
15 changes: 15 additions & 0 deletions youtubei/renderers/chip_cloud.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
from typing import Sequence

from youtubei.parse.validated_types import Dynamic
from youtubei.renderers.chip_cloud_chip import ChipCloudChipRenderer
from .._registries import WEB_REMIX_REGISTRY
from ._base import BaseRenderer

__all__ = ("ChipCloudRenderer",)


@WEB_REMIX_REGISTRY
class ChipCloudRenderer(BaseRenderer):
chips: Sequence[Dynamic[ChipCloudChipRenderer]]
collapsed_row_count: int
horizontal_scrollable: bool
23 changes: 23 additions & 0 deletions youtubei/renderers/chip_cloud_chip.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
from typing import Optional

from youtubei.models.accessibility import Accessibility
from youtubei.models.endpoints import SearchEndpoint
from youtubei.models.other import Icon, Style
from youtubei.models.text import ComplexText
from youtubei.validated_types import DynamicCommand

from .._registries import WEB_REMIX_REGISTRY
from ._base import BaseRenderer

__all__ = ("ChipCloudChipRenderer",)


@WEB_REMIX_REGISTRY
class ChipCloudChipRenderer(BaseRenderer):
style: Style
text: Optional[ComplexText] = None
icon: Optional[Icon] = None
navigation_endpoint: DynamicCommand[SearchEndpoint]
accessibility_data: Accessibility
is_selected: bool
unique_id: Optional[str] = None
11 changes: 10 additions & 1 deletion youtubei/renderers/menu.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
from youtubei.renderers.menu_navigation_item import MenuNavigationItemRenderer
from youtubei.renderers.menu_service_item import MenuServiceItemRenderer
from youtubei.renderers.toggle_button import ToggleButtonRenderer
from youtubei.renderers.toggle_menu_service_item import ToggleMenuServiceItemRenderer

from ._base import BaseRenderer

Expand All @@ -18,7 +19,15 @@
@WEB_REMIX_REGISTRY
class MenuRenderer(BaseRenderer):
items: Optional[
Sequence[Dynamic[Union[MenuNavigationItemRenderer, MenuServiceItemRenderer]]]
Sequence[
Dynamic[
Union[
MenuNavigationItemRenderer,
MenuServiceItemRenderer,
ToggleMenuServiceItemRenderer,
]
]
]
] = None
open_immediately: Optional[bool] = None
top_level_buttons: Optional[
Expand Down
28 changes: 28 additions & 0 deletions youtubei/renderers/music_card_shelf.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
from typing import Sequence
from youtubei.models.endpoints import BrowseEndpoint
from youtubei.models.other import Icon
from youtubei.models.text import ComplexText
from youtubei.parse.validated_types import Dynamic
from youtubei.renderers.button import ButtonRenderer
from youtubei.renderers.menu import MenuRenderer
from youtubei.renderers.music_card_shelf_header_basic import MusicCardShelfHeaderBasicRenderer
from youtubei.renderers.music_responsive_list_item import MusicResponsiveListItemRenderer
from youtubei.renderers.music_thumbnail import MusicThumbnailRenderer
from youtubei.validated_types import DynamicCommand
from .._registries import WEB_REMIX_REGISTRY
from ._base import BaseRenderer

__all__ = ("MusicCardShelfRenderer",)


@WEB_REMIX_REGISTRY
class MusicCardShelfRenderer(BaseRenderer):
thumbnail: Dynamic[MusicThumbnailRenderer]
title: ComplexText
subtitle: ComplexText
contents: Sequence[Dynamic[MusicResponsiveListItemRenderer]]
buttons: Sequence[Dynamic[ButtonRenderer]]
menu: Dynamic[MenuRenderer]
on_tap: DynamicCommand[BrowseEndpoint]
header: Dynamic[MusicCardShelfHeaderBasicRenderer]
end_icon: Icon
10 changes: 10 additions & 0 deletions youtubei/renderers/music_card_shelf_header_basic.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
from youtubei.models.text import ComplexText
from .._registries import WEB_REMIX_REGISTRY
from ._base import BaseRenderer

__all__ = ("MusicCardShelfHeaderBasicRenderer",)


@WEB_REMIX_REGISTRY
class MusicCardShelfHeaderBasicRenderer(BaseRenderer):
title: ComplexText
12 changes: 12 additions & 0 deletions youtubei/renderers/music_inline_badge.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
from youtubei.models.accessibility import Accessibility
from youtubei.models.other import Icon
from .._registries import WEB_REMIX_REGISTRY
from ._base import BaseRenderer

__all__ = ("MusicInlineBadgeRenderer",)


@WEB_REMIX_REGISTRY
class MusicInlineBadgeRenderer(BaseRenderer):
icon: Icon
accessibility_data: Accessibility
5 changes: 3 additions & 2 deletions youtubei/renderers/music_play_button.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
from typing import Union
from youtubei.enums.music import MusicPlayButtonRippleTarget, MusicPlayButtonSize
from youtubei.models.accessibility import Accessibility
from youtubei.models.endpoints import WatchEndpoint
from youtubei.models.endpoints import WatchEndpoint, WatchPlaylistEndpoint
from youtubei.models.other import Icon
from youtubei.validated_types import DynamicCommand

Expand All @@ -12,7 +13,7 @@

@WEB_REMIX_REGISTRY
class MusicPlayButtonRenderer(BaseRenderer):
play_navigation_endpoint: DynamicCommand[WatchEndpoint]
play_navigation_endpoint: DynamicCommand[Union[WatchEndpoint, WatchPlaylistEndpoint]]
play_icon: Icon
pause_icon: Icon
icon_color: int
Expand Down
Loading

0 comments on commit 219aed3

Please sign in to comment.