Skip to content

Commit

Permalink
fix #544 (#560)
Browse files Browse the repository at this point in the history
* fix #544

* fix ignores
  • Loading branch information
sigma67 authored Apr 5, 2024
1 parent d63eb07 commit 2217f3d
Show file tree
Hide file tree
Showing 7 changed files with 64 additions and 8 deletions.
3 changes: 2 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
.idea
.vscode
**.json
tests/*.json
tests/setup/*.json
docs/build
test.cfg
**.mp3
Expand Down
1 change: 1 addition & 0 deletions tests/data/2024_03_get_album.json

Large diffs are not rendered by default.

12 changes: 12 additions & 0 deletions tests/mixins/test_browsing.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,7 @@
import json
import warnings
from pathlib import Path
from unittest import mock

import pytest

Expand Down Expand Up @@ -74,6 +77,15 @@ def test_get_album_browse_id_issue_470(self, yt):
escaped_browse_id = yt.get_album_browse_id("OLAK5uy_nbMYyrfeg5ZgknoOsOGBL268hGxtcbnDM")
assert escaped_browse_id == "MPREb_scJdtUCpPE2"

def test_get_album_2024(self, yt):
with open(Path(__file__).parent.parent / "data" / "2024_03_get_album.json", encoding="utf8") as f:
mock_response = json.load(f)
with mock.patch("ytmusicapi.YTMusic._send_request", return_value=mock_response):
album = yt.get_album("MPREabc")
assert len(album["tracks"]) == 19
assert len(album["artists"]) == 1
assert len(album) == 13

def test_get_album(self, yt, yt_auth, sample_album):
album = yt_auth.get_album(sample_album)
assert len(album) >= 9
Expand Down
13 changes: 10 additions & 3 deletions ytmusicapi/mixins/browsing.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
get_reloadable_continuation_params,
)
from ytmusicapi.helpers import YTM_DOMAIN, sum_total_duration
from ytmusicapi.parsers.albums import parse_album_header
from ytmusicapi.parsers.albums import parse_album_header, parse_album_header_2024
from ytmusicapi.parsers.browsing import parse_album, parse_content_list, parse_mixed_content, parse_playlist
from ytmusicapi.parsers.library import parse_albums
from ytmusicapi.parsers.playlists import parse_playlist_items
Expand Down Expand Up @@ -516,8 +516,15 @@ def get_album(self, browseId: str) -> Dict:
body = {"browseId": browseId}
endpoint = "browse"
response = self._send_request(endpoint, body)
album = parse_album_header(response)
results = nav(response, SINGLE_COLUMN_TAB + SECTION_LIST_ITEM + MUSIC_SHELF)
if "header" in response:
album = parse_album_header(response)
else:
album = parse_album_header_2024(response)
results = nav(response, SINGLE_COLUMN_TAB + SECTION_LIST_ITEM + MUSIC_SHELF, True) or nav(
# fallback for 2024 format
response,
[*TWO_COLUMN_RENDERER, "secondaryContents", *SECTION_LIST_ITEM, *MUSIC_SHELF],
)
album["tracks"] = parse_playlist_items(results["contents"], is_album=True)
results = nav(response, SINGLE_COLUMN_TAB + SECTION_LIST + [1] + CAROUSEL, True)
if results is not None:
Expand Down
3 changes: 2 additions & 1 deletion ytmusicapi/navigation.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,9 +28,10 @@
NAVIGATION_BROWSE_ID = [*NAVIGATION_BROWSE, "browseId"]
PAGE_TYPE = ["browseEndpointContextSupportedConfigs", "browseEndpointContextMusicConfig", "pageType"]
WATCH_VIDEO_ID = ["watchEndpoint", "videoId"]
WATCH_PLAYLIST_ID = ["watchEndpoint", "playlistId"]
NAVIGATION_VIDEO_ID = ["navigationEndpoint", *WATCH_VIDEO_ID]
QUEUE_VIDEO_ID = ["queueAddEndpoint", "queueTarget", "videoId"]
NAVIGATION_PLAYLIST_ID = ["navigationEndpoint", "watchEndpoint", "playlistId"]
NAVIGATION_PLAYLIST_ID = ["navigationEndpoint", *WATCH_PLAYLIST_ID]
WATCH_PID = ["watchPlaylistEndpoint", "playlistId"]
NAVIGATION_WATCH_PLAYLIST_ID = ["navigationEndpoint", *WATCH_PID]
NAVIGATION_VIDEO_TYPE = [
Expand Down
34 changes: 34 additions & 0 deletions ytmusicapi/parsers/albums.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
from ytmusicapi.helpers import to_int

from ._utils import *
from .podcasts import parse_base_header
from .songs import parse_like_status, parse_song_runs


Expand Down Expand Up @@ -36,3 +37,36 @@ def parse_album_header(response):
album["likeStatus"] = parse_like_status(service)

return album


def parse_album_header_2024(response):
header = nav(response, [*TWO_COLUMN_RENDERER, *TAB_CONTENT, *SECTION_LIST_ITEM, *RESPONSIVE_HEADER])
album = {
"title": nav(header, TITLE_TEXT),
"type": nav(header, SUBTITLE),
"thumbnails": nav(header, THUMBNAILS),
"isExplicit": nav(header, SUBTITLE_BADGE_LABEL, True) is not None,
}

album["description"] = nav(header, ["description", *DESCRIPTION_SHELF, *DESCRIPTION], True)

album_info = parse_song_runs(header["subtitle"]["runs"][2:])
album_info["artists"] = [parse_base_header(header)["author"]]
album.update(album_info)

if len(header["secondSubtitle"]["runs"]) > 1:
album["trackCount"] = to_int(header["secondSubtitle"]["runs"][0]["text"])
album["duration"] = header["secondSubtitle"]["runs"][2]["text"]
else:
album["duration"] = header["secondSubtitle"]["runs"][0]["text"]

# add to library/uploaded
buttons = header["buttons"]
album["audioPlaylistId"] = nav(
buttons, [1, "musicPlayButtonRenderer", "playNavigationEndpoint", *WATCH_PLAYLIST_ID], True
)
service = nav(buttons, [0, "toggleButtonRenderer", "defaultServiceEndpoint"], True)
if service:
album["likeStatus"] = parse_like_status(service)

return album
6 changes: 3 additions & 3 deletions ytmusicapi/parsers/podcasts.py
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ def from_runs(cls, description_runs: List[Dict]) -> "Description":
return cls(elements)


def _parse_base_header(header: Dict) -> Dict:
def parse_base_header(header: Dict) -> Dict:
"""parse common left hand side (header) items of an episode or podcast page"""
strapline = nav(header, ["straplineTextOne"])
return {
Expand All @@ -71,15 +71,15 @@ def _parse_base_header(header: Dict) -> Dict:


def parse_podcast_header(header: Dict) -> Dict:
metadata = _parse_base_header(header)
metadata = parse_base_header(header)
metadata["description"] = nav(header, ["description", *DESCRIPTION_SHELF, *DESCRIPTION], True)
metadata["saved"] = nav(header, ["buttons", 1, *TOGGLED_BUTTON])

return metadata


def parse_episode_header(header: Dict) -> Dict:
metadata = _parse_base_header(header)
metadata = parse_base_header(header)
metadata["date"] = nav(header, [*SUBTITLE2])
metadata["duration"] = nav(header, [*SUBTITLE3], True)
if not metadata["duration"]: # progress started
Expand Down

0 comments on commit 2217f3d

Please sign in to comment.