ytmusicapi package
******************


Subpackages
===========

* ytmusicapi.auth package

  * Subpackages

    * ytmusicapi.auth.oauth package

      * Submodules

      * ytmusicapi.auth.oauth.credentials module

      * ytmusicapi.auth.oauth.exceptions module

      * ytmusicapi.auth.oauth.models module

      * ytmusicapi.auth.oauth.token module

      * Module contents

  * Submodules

  * ytmusicapi.auth.auth_parse module

    * "determine_auth_type()"

    * "parse_auth_str()"

  * ytmusicapi.auth.browser module

    * "is_browser()"

    * "setup_browser()"

  * ytmusicapi.auth.types module

    * "AuthType"

      * "AuthType.BROWSER"

      * "AuthType.OAUTH_CUSTOM_CLIENT"

      * "AuthType.OAUTH_CUSTOM_FULL"

      * "AuthType.UNAUTHORIZED"

  * Module contents

* ytmusicapi.mixins package

  * Submodules

  * ytmusicapi.mixins.browsing module

    * "BrowsingMixin"

      * "BrowsingMixin.ArtistOrderType"

      * "BrowsingMixin.get_album()"

      * "BrowsingMixin.get_album_browse_id()"

      * "BrowsingMixin.get_artist()"

      * "BrowsingMixin.get_artist_albums()"

      * "BrowsingMixin.get_basejs_url()"

      * "BrowsingMixin.get_home()"

      * "BrowsingMixin.get_lyrics()"

      * "BrowsingMixin.get_signatureTimestamp()"

      * "BrowsingMixin.get_song()"

      * "BrowsingMixin.get_song_credits()"

      * "BrowsingMixin.get_song_related()"

      * "BrowsingMixin.get_tasteprofile()"

      * "BrowsingMixin.get_user()"

      * "BrowsingMixin.get_user_playlists()"

      * "BrowsingMixin.get_user_videos()"

      * "BrowsingMixin.set_tasteprofile()"

  * ytmusicapi.mixins.charts module

    * "ChartsMixin"

      * "ChartsMixin.get_charts()"

  * ytmusicapi.mixins.explore module

    * "ExploreMixin"

      * "ExploreMixin.get_explore()"

      * "ExploreMixin.get_mood_categories()"

      * "ExploreMixin.get_mood_playlists()"

  * ytmusicapi.mixins.library module

    * "LibraryMixin"

      * "LibraryMixin.add_history_item()"

      * "LibraryMixin.edit_song_library_status()"

      * "LibraryMixin.get_account_info()"

      * "LibraryMixin.get_history()"

      * "LibraryMixin.get_library_albums()"

      * "LibraryMixin.get_library_artists()"

      * "LibraryMixin.get_library_channels()"

      * "LibraryMixin.get_library_playlists()"

      * "LibraryMixin.get_library_podcasts()"

      * "LibraryMixin.get_library_songs()"

      * "LibraryMixin.get_library_subscriptions()"

      * "LibraryMixin.rate_playlist()"

      * "LibraryMixin.rate_song()"

      * "LibraryMixin.remove_history_items()"

      * "LibraryMixin.subscribe_artists()"

      * "LibraryMixin.unsubscribe_artists()"

  * ytmusicapi.mixins.playlists module

    * "PlaylistsMixin"

      * "PlaylistsMixin.add_playlist_items()"

      * "PlaylistsMixin.create_playlist()"

      * "PlaylistsMixin.delete_playlist()"

      * "PlaylistsMixin.edit_playlist()"

      * "PlaylistsMixin.get_liked_songs()"

      * "PlaylistsMixin.get_playlist()"

      * "PlaylistsMixin.get_saved_episodes()"

      * "PlaylistsMixin.join_collaborative_playlist()"

      * "PlaylistsMixin.remove_playlist_items()"

  * ytmusicapi.mixins.podcasts module

    * "PodcastsMixin"

      * "PodcastsMixin.get_channel()"

      * "PodcastsMixin.get_channel_episodes()"

      * "PodcastsMixin.get_episode()"

      * "PodcastsMixin.get_episodes_playlist()"

      * "PodcastsMixin.get_podcast()"

  * ytmusicapi.mixins.search module

    * "SearchMixin"

      * "SearchMixin.get_search_suggestions()"

      * "SearchMixin.remove_search_suggestions()"

      * "SearchMixin.search()"

  * ytmusicapi.mixins.uploads module

    * "UploadsMixin"

      * "UploadsMixin.delete_upload_entity()"

      * "UploadsMixin.get_library_upload_album()"

      * "UploadsMixin.get_library_upload_albums()"

      * "UploadsMixin.get_library_upload_artist()"

      * "UploadsMixin.get_library_upload_artists()"

      * "UploadsMixin.get_library_upload_songs()"

      * "UploadsMixin.upload_song()"

  * ytmusicapi.mixins.watch module

    * "WatchMixin"

      * "WatchMixin.get_watch_playlist()"

  * Module contents

* ytmusicapi.models package

  * Subpackages

    * ytmusicapi.models.content package

      * Submodules

      * ytmusicapi.models.content.TextRun module

      * ytmusicapi.models.content.enums module

      * Module contents

  * Submodules

  * ytmusicapi.models.lyrics module

    * "LyricLine"

      * "LyricLine.end_time"

      * "LyricLine.from_raw()"

      * "LyricLine.id"

      * "LyricLine.start_time"

      * "LyricLine.text"

    * "Lyrics"

      * "Lyrics.hasTimestamps"

      * "Lyrics.lyrics"

      * "Lyrics.source"

    * "TimedLyrics"

      * "TimedLyrics.hasTimestamps"

      * "TimedLyrics.lyrics"

      * "TimedLyrics.source"

  * Module contents

    * "LyricLine"

      * "LyricLine.end_time"

      * "LyricLine.from_raw()"

      * "LyricLine.id"

      * "LyricLine.start_time"

      * "LyricLine.text"

    * "Lyrics"

      * "Lyrics.hasTimestamps"

      * "Lyrics.lyrics"

      * "Lyrics.source"

    * "TimedLyrics"

      * "TimedLyrics.hasTimestamps"

      * "TimedLyrics.lyrics"

      * "TimedLyrics.source"

* ytmusicapi.parsers package

  * Submodules

  * ytmusicapi.parsers.albums module

    * "parse_album_header()"

    * "parse_album_header_2024()"

    * "parse_album_playlistid_if_exists()"

  * ytmusicapi.parsers.artists module

    * "parse_artists_runs()"

  * ytmusicapi.parsers.browsing module

    * "parse_album()"

    * "parse_content_list()"

    * "parse_mixed_content()"

    * "parse_playlist()"

    * "parse_related_artist()"

    * "parse_single()"

    * "parse_song()"

    * "parse_song_flat()"

    * "parse_video()"

    * "parse_watch_playlist()"

  * ytmusicapi.parsers.constants module

  * ytmusicapi.parsers.explore module

    * "parse_chart_artist()"

    * "parse_chart_episode()"

    * "parse_chart_playlist()"

    * "parse_chart_song()"

    * "parse_ranking()"

    * "parse_trending_item()"

  * ytmusicapi.parsers.i18n module

    * "Parser"

      * "Parser.get_api_result_types()"

      * "Parser.get_search_result_types()"

      * "Parser.get_song_credit_section_map()"

      * "Parser.parse_channel_contents()"

  * ytmusicapi.parsers.library module

    * "get_library_contents()"

    * "parse_albums()"

    * "parse_artists()"

    * "parse_library_albums()"

    * "parse_library_artists()"

    * "parse_library_podcasts()"

    * "parse_library_songs()"

    * "pop_songs_random_mix()"

  * ytmusicapi.parsers.playlists module

    * "parse_audio_playlist()"

    * "parse_playlist_header()"

    * "parse_playlist_header_meta()"

    * "parse_playlist_item()"

    * "parse_playlist_items()"

    * "validate_playlist_id()"

  * ytmusicapi.parsers.podcasts module

    * "Description"

      * "Description.from_runs()"

      * "Description.text"

    * "DescriptionElement"

      * "DescriptionElement.text"

    * "Link"

      * "Link.url"

    * "Timestamp"

      * "Timestamp.seconds"

    * "parse_base_header()"

    * "parse_episode()"

    * "parse_episode_flat()"

    * "parse_episode_header()"

    * "parse_podcast()"

    * "parse_podcast_header()"

  * ytmusicapi.parsers.search module

    * "get_search_params()"

    * "get_search_result_type()"

    * "parse_search_result()"

    * "parse_search_results()"

    * "parse_search_suggestions()"

    * "parse_top_result()"

  * ytmusicapi.parsers.songs module

    * "parse_like_status()"

    * "parse_song_album()"

    * "parse_song_artists()"

    * "parse_song_menu_data()"

    * "parse_song_run()"

    * "parse_song_runs()"

  * ytmusicapi.parsers.uploads module

    * "parse_uploaded_items()"

  * ytmusicapi.parsers.watch module

    * "get_tab_browse_ids()"

    * "parse_watch_playlist()"

    * "parse_watch_track()"

  * Module contents


Submodules
==========


ytmusicapi.constants module
===========================


ytmusicapi.continuations module
===============================

ytmusicapi.continuations.get_continuation_contents(continuation: dict[str, Any], parse_func: Callable[[list[dict[str, Any]]], list[dict[str, Any]]]) -> list[dict[str, Any]]

   Return type:
      "list"["dict"["str", "Any"]]

ytmusicapi.continuations.get_continuation_params(results: dict[str, Any], ctoken_path: str = '') -> str

   Return type:
      "str"

ytmusicapi.continuations.get_continuation_string(ctoken: str) -> str

   Returns the continuation string used in the continuation request

   Parameters:
      **ctoken** ("str") -- the unique continuation token

   Return type:
      "str"

ytmusicapi.continuations.get_continuation_token(results: list[dict[str, Any]]) -> str | None

   Return type:
      "str" | "None"

ytmusicapi.continuations.get_continuations(results: dict[str, Any], continuation_type: str, limit: int | None, request_func: Callable[[str], dict[str, Any]], parse_func: Callable[[list[dict[str, Any]]], list[dict[str, Any]]], ctoken_path: str = '', additionalParams: str | None = None) -> list[dict[str, Any]]

   Parameters:
      * **results** ("dict"["str", "Any"]) -- result list from request
        data

      * **continuation_type** ("str") -- type of continuation,
        determines which subkey will be used to navigate the
        continuation return data

      * **limit** ("int" | "None") -- determines minimum of how many
        items to retrieve in total. None to retrieve all items until
        no more continuations are returned

      * **request_func** ("Callable"[["str"], "dict"["str", "Any"]])
        -- the request func to use to get the continuations

      * **parse_func** ("Callable"[["list"["dict"["str", "Any"]]],
        "list"["dict"["str", "Any"]]]) -- the parse func to apply on
        the returned continuations

      * **ctoken_path** ("str") -- rarely used specifier applied to
        retrieve the ctoken ("next<ctoken_path>ContinuationData").
        Default empty string

      * **additionalParams** ("str" | "None") -- Optional additional
        params to pass to the request func. Default: use
        get_continuation_params

   Return type:
      "list"["dict"["str", "Any"]]

   Returns:
      list of parsed continuation results

ytmusicapi.continuations.get_continuations_2025(results: dict[str, Any], limit: int | None, request_func: Callable[[dict[str, Any]], dict[str, Any]], parse_func: Callable[[list[dict[str, Any]]], list[dict[str, Any]]]) -> list[dict[str, Any]]

   Return type:
      "list"["dict"["str", "Any"]]

ytmusicapi.continuations.get_parsed_continuation_items(response: dict[str, Any], parse_func: Callable[[list[dict[str, Any]]], list[dict[str, Any]]], continuation_type: str) -> dict[str, Any]

   Return type:
      "dict"["str", "Any"]

ytmusicapi.continuations.get_reloadable_continuation_params(results: dict[str, Any]) -> str

   Return type:
      "str"

ytmusicapi.continuations.get_reloadable_continuations(results: dict[str, Any], continuation_type: str, limit: int | None, request_func: Callable[[str], dict[str, Any]], parse_func: Callable[[list[dict[str, Any]]], list[dict[str, Any]]]) -> list[dict[str, Any]]

   Reloadable continuations are a special case that only exists on the
   playlists page (suggestions).

   Return type:
      "list"["dict"["str", "Any"]]

ytmusicapi.continuations.get_validated_continuations(results: dict[str, Any], continuation_type: str, limit: int, per_page: int, request_func: Callable[[str], dict[str, Any]], parse_func: Callable[[list[dict[str, Any]]], list[dict[str, Any]]], ctoken_path: str = '') -> list[dict[str, Any]]

   Return type:
      "list"["dict"["str", "Any"]]

ytmusicapi.continuations.resend_request_until_parsed_response_is_valid(request_func: Callable[[str], dict[str, Any]], request_additional_params: str, parse_func: Callable[[dict[str, Any]], dict[str, Any]], validate_func: Callable[[dict[str, Any]], bool], max_retries: int) -> dict[str, Any]

   Return type:
      "dict"["str", "Any"]

ytmusicapi.continuations.validate_response(response: dict[str, Any], per_page: int, limit: int, current_count: int) -> bool

   Return type:
      "bool"


ytmusicapi.enums module
=======================

class ytmusicapi.enums.ResponseStatus(*values)

   Bases: "str", "Enum"

   SUCCEEDED = 'STATUS_SUCCEEDED'


ytmusicapi.exceptions module
============================

custom exception classes for ytmusicapi

exception ytmusicapi.exceptions.YTMusicError

   Bases: "Exception"

   base error class

   shall only be raised if none of the subclasses below are fitting

exception ytmusicapi.exceptions.YTMusicServerError

   Bases: "YTMusicError"

   error caused by the YouTube Music backend

exception ytmusicapi.exceptions.YTMusicUserError

   Bases: "YTMusicError"

   error caused by invalid usage of ytmusicapi


ytmusicapi.helpers module
=========================

ytmusicapi.helpers.get_authorization(auth: str) -> str

   Returns SAPISIDHASH value based on headers and current time

   Parameters:
      **auth** ("str") -- SAPISID and Origin value from headers
      concatenated with space

   Return type:
      "str"

ytmusicapi.helpers.get_visitor_id(request_func: Callable[[str], Response]) -> dict[str, str]

   Return type:
      "dict"["str", "str"]

ytmusicapi.helpers.initialize_context() -> dict[str, Any]

   Return type:
      "dict"["str", "Any"]

ytmusicapi.helpers.initialize_headers() -> CaseInsensitiveDict[str]

   Return type:
      "CaseInsensitiveDict"["str"]

ytmusicapi.helpers.parse_description_runs(descriptionRunsList: Any | None) -> tuple[str, list[HyperLink | PlainText]]

   Return type:
      "tuple"["str", "list"["HyperLink" | "PlainText"]]

ytmusicapi.helpers.sapisid_from_cookie(raw_cookie: str) -> str

   Return type:
      "str"

ytmusicapi.helpers.sum_total_duration(item: dict[str, Any]) -> int

   Return type:
      "int"

ytmusicapi.helpers.to_int(string: str) -> int

   Attempts to cast a string to an integer using locale or Python int
   cast

   Parameters:
      **string** ("str") -- string that can be cast to an integer

   Return type:
      "int"

   :return Integer if string is a valid integer

   :raise ValueError if string is not a valid integer


ytmusicapi.navigation module
============================

commonly used navigation paths

ytmusicapi.navigation.find_object_by_key(object_list: list[dict[str, Any]], key: str, nested: str | None = None, is_key: bool = False) -> dict[str, Any] | None

   Return type:
      "dict"["str", "Any"] | "None"

ytmusicapi.navigation.find_objects_by_key(object_list: list[dict[str, Any]], key: str, nested: str | None = None) -> list[dict[str, Any]]

   Return type:
      "list"["dict"["str", "Any"]]

ytmusicapi.navigation.nav(root: dict[str, Any] | None, items: list[Any], none_if_absent: bool = False) -> Any | None

   Overloads:
      * **root** (JsonDict | None), **items** (list[Any]),
        **none_if_absent** (Literal[False]) → Any

      * **root** (JsonDict | None), **items** (list[Any]),
        **none_if_absent** (Literal[True]) → Any | None

   Access a nested object in root by item sequence.


ytmusicapi.setup module
=======================

ytmusicapi.setup.main() -> RefreshingToken | str

   Return type:
      "RefreshingToken" | "str"

ytmusicapi.setup.parse_args(args: list[str]) -> Namespace

   Return type:
      "Namespace"

ytmusicapi.setup.setup(filepath: str | None = None, headers_raw: str | None = None) -> str

   Requests browser headers from the user via command line and returns
   a string that can be passed to YTMusic()

   Parameters:
      * **filepath** ("str" | "None") -- Optional filepath to store
        headers to.

      * **headers_raw** ("str" | "None") -- Optional request headers
        copied from browser. Otherwise requested from terminal

   Return type:
      "str"

   Returns:
      configuration headers string

ytmusicapi.setup.setup_oauth(client_id: str, client_secret: str, filepath: str | None = None, session: Session | None = None, proxies: dict[str, str] | None = None, open_browser: bool = False) -> RefreshingToken

   Starts oauth flow from the terminal and returns a string that can
   be passed to YTMusic()

   Parameters:
      * **client_id** ("str") -- Optional. Used to specify the
        client_id oauth should use for authentication flow. If
        provided, client_secret MUST also be passed or both will be
        ignored.

      * **client_secret** ("str") -- Optional. Same as client_id but
        for the oauth client secret.

      * **session** ("Session" | "None") -- Session to use for
        authentication

      * **proxies** ("dict"["str", "str"] | "None") -- Proxies to use
        for authentication

      * **filepath** ("str" | "None") -- Optional filepath to store
        headers to.

      * **open_browser** ("bool") -- If True, open the default browser
        with the setup link

   Return type:
      "RefreshingToken"

   Returns:
      configuration headers string


ytmusicapi.type_alias module
============================


ytmusicapi.ytmusic module
=========================

class ytmusicapi.ytmusic.YTMusic(auth: str | dict[str, Any] | None = None, user: str | None = None, requests_session: Session | None = None, proxies: dict[str, str] | None = None, language: str = 'en', location: str = '', oauth_credentials: OAuthCredentials | None = None)

   Bases: "YTMusicBase", "BrowsingMixin", "SearchMixin", "WatchMixin",
   "ChartsMixin", "ExploreMixin", "LibraryMixin", "PlaylistsMixin",
   "PodcastsMixin", "UploadsMixin"

   Allows automated interactions with YouTube Music by emulating the
   YouTube web client's requests. Permits both authenticated and non-
   authenticated requests. Authentication header data must be provided
   on initialization.

class ytmusicapi.ytmusic.YTMusicBase(auth: str | dict[str, Any] | None = None, user: str | None = None, requests_session: Session | None = None, proxies: dict[str, str] | None = None, language: str = 'en', location: str = '', oauth_credentials: OAuthCredentials | None = None)

   Bases: "object"

   as_mobile() -> Iterator[None]

      Return type:
         "Iterator"["None"]


      Not thread-safe!
      ----------------

      Temporarily changes the *context* to enable different results
      from the API, meant for the Android mobile-app. All calls inside
      the *with*-statement with emulate mobile behavior.

      This context-manager has no *enter_result*, as it operates in-
      place and only temporarily alters the underlying
      *YTMusic*-object.

      Example:

         with yt.as_mobile():
             yt._send_request(...)  # results as mobile-app

         yt._send_request(...)  # back to normal, like web-app

   property base_headers: CaseInsensitiveDict[str]

   property headers: CaseInsensitiveDict[str]

   proxies: dict[str, str] | None

      params for session modification


Module contents
===============

class ytmusicapi.LikeStatus(*values)

   Bases: "str", "Enum"

   DISLIKE = 'DISLIKE'

   INDIFFERENT = 'INDIFFERENT'

   LIKE = 'LIKE'

class ytmusicapi.OAuthCredentials(client_id: str, client_secret: str, session: Session | None = None, proxies: dict[str, str] | None = None)

   Bases: "Credentials"

   Class for handling OAuth credential retrieval and refreshing.

   client_id: str

   client_secret: str

   get_code() -> AuthCodeDict

      Method for obtaining a new user auth code. First step of token
      creation.

      Return type:
         "AuthCodeDict"

   refresh_token(refresh_token: str) -> BaseTokenDict

      Method for requesting a new access token for a given
      "refresh_token". Token must have been created by the same OAuth
      client.

      Parameters:
         **refresh_token** ("str") -- Corresponding "refresh_token"
         for a matching "access_token". Obtained via

      Return type:
         "BaseTokenDict"

   token_from_code(device_code: str) -> RefreshableTokenDict

      Method for verifying user auth code and conversion into a
      FullTokenDict.

      Return type:
         "RefreshableTokenDict"

class ytmusicapi.YTMusic(auth: str | dict[str, Any] | None = None, user: str | None = None, requests_session: Session | None = None, proxies: dict[str, str] | None = None, language: str = 'en', location: str = '', oauth_credentials: OAuthCredentials | None = None)

   Bases: "YTMusicBase", "BrowsingMixin", "SearchMixin", "WatchMixin",
   "ChartsMixin", "ExploreMixin", "LibraryMixin", "PlaylistsMixin",
   "PodcastsMixin", "UploadsMixin"

   Allows automated interactions with YouTube Music by emulating the
   YouTube web client's requests. Permits both authenticated and non-
   authenticated requests. Authentication header data must be provided
   on initialization.

ytmusicapi.setup(filepath: str | None = None, headers_raw: str | None = None) -> str

   Requests browser headers from the user via command line and returns
   a string that can be passed to YTMusic()

   Parameters:
      * **filepath** ("str" | "None") -- Optional filepath to store
        headers to.

      * **headers_raw** ("str" | "None") -- Optional request headers
        copied from browser. Otherwise requested from terminal

   Return type:
      "str"

   Returns:
      configuration headers string

ytmusicapi.setup_oauth(client_id: str, client_secret: str, filepath: str | None = None, session: Session | None = None, proxies: dict[str, str] | None = None, open_browser: bool = False) -> RefreshingToken

   Starts oauth flow from the terminal and returns a string that can
   be passed to YTMusic()

   Parameters:
      * **client_id** ("str") -- Optional. Used to specify the
        client_id oauth should use for authentication flow. If
        provided, client_secret MUST also be passed or both will be
        ignored.

      * **client_secret** ("str") -- Optional. Same as client_id but
        for the oauth client secret.

      * **session** ("Session" | "None") -- Session to use for
        authentication

      * **proxies** ("dict"["str", "str"] | "None") -- Proxies to use
        for authentication

      * **filepath** ("str" | "None") -- Optional filepath to store
        headers to.

      * **open_browser** ("bool") -- If True, open the default browser
        with the setup link

   Return type:
      "RefreshingToken"

   Returns:
      configuration headers string
