Skip to content

Commit 7f42e8d

Browse files
authored
get_album: distinguish between "other versions" and "releases for you" (#791)
* parse_album: return audioPlaylistId for Premium users `audioPlaylistId` is no longer `None` when `parse_album` is called with data from a Premium account * get_album: distinguish between "other versions" and "releases for you" adds `related_recommendations`, a list of albums in "Releases for you" `other_versions` no longer contains those recommendations when its carousel is positioned after "Releases for you"
1 parent 59b3ca5 commit 7f42e8d

File tree

5 files changed

+24
-12
lines changed

5 files changed

+24
-12
lines changed

tests/mixins/test_browsing.py

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -109,6 +109,7 @@ def test_get_album_2024(self, yt):
109109
def test_get_album(self, yt, yt_auth, sample_album):
110110
album = yt_auth.get_album(sample_album)
111111
assert len(album) >= 9
112+
assert album["related_recommendations"]
112113
assert "isExplicit" in album
113114
assert album["tracks"][0]["isExplicit"]
114115
assert all(item["views"] is not None for item in album["tracks"])
@@ -141,13 +142,14 @@ def test_get_album_without_artist(self, yt):
141142
assert album["audioPlaylistId"] is not None
142143
assert len(album["tracks"]) == 11
143144

144-
def test_get_album_other_versions(self, yt):
145+
def test_get_album_other_versions(self, yt, yt_oauth):
145146
# Eminem - Curtain Call: The Hits (Explicit Variant)
146-
album = yt.get_album("MPREb_LQCAymzbaKJ")
147+
album = yt_oauth.get_album("MPREb_LQCAymzbaKJ")
147148
variants = album["other_versions"]
148149
assert len(variants) >= 1 # appears to be regional
149150
variant = variants[0]
150151
assert variant["type"] == "Album"
152+
assert variant["title"] == album["title"]
151153
assert len(variant["artists"]) == 1
152154
assert variant["artists"][0] == {"name": "Eminem", "id": "UCedvOgsKFzcK3hA5taf3KoQ"}
153155
assert variant["audioPlaylistId"] is not None
@@ -158,11 +160,13 @@ def test_get_album_other_versions(self, yt):
158160
assert not album["isExplicit"]
159161
variant = album["other_versions"][0]
160162
assert variant["type"] == "Single"
163+
assert variant["title"] == "Prada"
161164
assert variant["isExplicit"]
162165
assert len(variant["artists"]) == 3
163166
assert variant["artists"][0]["id"] == "UCGWMNnI1Ky5bMcRlr73Cj2Q"
164167
assert variant["artists"][1]["name"] == "RAYE"
165168
assert variant["artists"][2] == {"id": "UCb7jnkQW94hzOoWkG14zs4w", "name": "D-Block Europe"}
169+
assert variant["audioPlaylistId"] is not None
166170

167171
def test_get_song(self, config, yt, yt_oauth, sample_video):
168172
song = yt_oauth.get_song(config["uploads"]["private_upload_id"]) # private upload

ytmusicapi/mixins/browsing.py

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -564,11 +564,17 @@ def get_album(self, browseId: str) -> JsonDict:
564564
results = nav(response, [*TWO_COLUMN_RENDERER, "secondaryContents", *SECTION_LIST_ITEM, *MUSIC_SHELF])
565565
album["tracks"] = parse_playlist_items(results["contents"], is_album=True)
566566

567-
other_versions = nav(
568-
response, [*TWO_COLUMN_RENDERER, "secondaryContents", *SECTION_LIST, 1, *CAROUSEL], True
567+
secondary_carousels = (
568+
nav(response, [*TWO_COLUMN_RENDERER, "secondaryContents", *SECTION_LIST], True) or []
569569
)
570-
if other_versions is not None:
571-
album["other_versions"] = parse_content_list(other_versions["contents"], parse_album)
570+
for section in secondary_carousels[1:]:
571+
carousel = nav(section, CAROUSEL)
572+
key = {
573+
"COLLECTION_STYLE_ITEM_SIZE_SMALL": "related_recommendations",
574+
"COLLECTION_STYLE_ITEM_SIZE_MEDIUM": "other_versions",
575+
}[carousel["itemSize"]]
576+
album[key] = parse_content_list(carousel["contents"], parse_album)
577+
572578
album["duration_seconds"] = sum_total_duration(album)
573579
for i, track in enumerate(album["tracks"]):
574580
album["tracks"][i]["album"] = album["title"]

ytmusicapi/parsers/albums.py

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -85,3 +85,8 @@ def parse_album_header_2024(response: JsonDict) -> JsonDict:
8585
album["likeStatus"] = parse_like_status(service)
8686

8787
return album
88+
89+
90+
def parse_album_playlistid_if_exists(data: JsonDict | None) -> str | None:
91+
"""the content of the data changes based on whether the user is authenticated or not"""
92+
return nav(data, WATCH_PID, True) or nav(data, WATCH_PLAYLIST_ID, True) if data else None

ytmusicapi/parsers/browsing.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
from ytmusicapi.type_alias import JsonDict, JsonList, ParseFuncDictType
44

5+
from .albums import parse_album_playlistid_if_exists
56
from .podcasts import parse_episode, parse_podcast
67
from .songs import *
78

@@ -64,7 +65,7 @@ def parse_album(result: JsonDict) -> JsonDict:
6465
"type": nav(result, SUBTITLE),
6566
"artists": [parse_id_name(x) for x in nav(result, ["subtitle", "runs"]) if "navigationEndpoint" in x],
6667
"browseId": nav(result, TITLE + NAVIGATION_BROWSE_ID),
67-
"audioPlaylistId": nav(result, THUMBNAIL_OVERLAY, True),
68+
"audioPlaylistId": parse_album_playlistid_if_exists(nav(result, THUMBNAIL_OVERLAY_NAVIGATION, True)),
6869
"thumbnails": nav(result, THUMBNAIL_RENDERER),
6970
"isExplicit": nav(result, SUBTITLE_BADGE_LABEL, True) is not None,
7071
}

ytmusicapi/parsers/search.py

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
from ..helpers import to_int
44
from ._utils import *
5+
from .albums import parse_album_playlistid_if_exists
56
from .songs import *
67

78
ALL_RESULT_TYPES = [
@@ -214,11 +215,6 @@ def parse_search_result(
214215
return search_result
215216

216217

217-
def parse_album_playlistid_if_exists(data: JsonDict | None) -> str | None:
218-
"""the content of the data changes based on whether the user is authenticated or not"""
219-
return nav(data, WATCH_PID, True) or nav(data, WATCH_PLAYLIST_ID, True) if data else None
220-
221-
222218
def parse_search_results(
223219
results: JsonList,
224220
api_search_result_types: list[str],

0 commit comments

Comments
 (0)