diff --git a/tests/parsers/test_search.py b/tests/parsers/test_search.py new file mode 100644 index 00000000..df8226d8 --- /dev/null +++ b/tests/parsers/test_search.py @@ -0,0 +1,39 @@ +from ytmusicapi.parsers.search import parse_search_result + + +def _flex_result(**extra: str) -> dict: + result = { + "flexColumns": [ + {"musicResponsiveListItemFlexColumnRenderer": {"text": {"runs": [{"text": "Song Title"}]}}}, + {"musicResponsiveListItemFlexColumnRenderer": {"text": {"runs": [{"text": "Artist"}]}}}, + ], + } + result.update(extra) + return result + + +class TestParseSearchResultIsAvailable: + def test_defaults_to_available(self): + parsed = parse_search_result(_flex_result(), "song", "Songs") + assert parsed["isAvailable"] is True + + def test_grey_out_policy_marks_unavailable(self): + parsed = parse_search_result( + _flex_result(musicItemRendererDisplayPolicy="MUSIC_ITEM_RENDERER_DISPLAY_POLICY_GREY_OUT"), + "video", + "Videos", + ) + assert parsed["isAvailable"] is False + + def test_other_policy_values_remain_available(self): + parsed = parse_search_result( + _flex_result(musicItemRendererDisplayPolicy="MUSIC_ITEM_RENDERER_DISPLAY_POLICY_DEFAULT"), + "song", + "Songs", + ) + assert parsed["isAvailable"] is True + + def test_other_result_types_are_unaffected(self): + # isAvailable is only meaningful for playable song/video results + parsed = parse_search_result(_flex_result(), "album", "Albums") + assert "isAvailable" not in parsed diff --git a/ytmusicapi/mixins/search.py b/ytmusicapi/mixins/search.py index bd6ee3c3..657e2eb2 100644 --- a/ytmusicapi/mixins/search.py +++ b/ytmusicapi/mixins/search.py @@ -91,7 +91,8 @@ def search( "id": "MPREb_9nqEki4ZDpp" }, "duration": "4:19", - "duration_seconds": 259 + "duration_seconds": 259, + "isAvailable": true, "isExplicit": false, "inLibrary": false, "feedbackTokens": { @@ -138,7 +139,8 @@ def search( ], "views": "386M", "duration": "4:38", - "duration_seconds": 278 + "duration_seconds": 278, + "isAvailable": true }, { "category": "Artists", diff --git a/ytmusicapi/parsers/search.py b/ytmusicapi/parsers/search.py index d31cb9f2..beeae5f5 100644 --- a/ytmusicapi/parsers/search.py +++ b/ytmusicapi/parsers/search.py @@ -182,6 +182,14 @@ def parse_search_result(data: JsonDict, result_type: str | None, category: str | ) search_result["videoType"] = video_type + if result_type in ["song", "video"]: + isAvailable = True + if "musicItemRendererDisplayPolicy" in data: + isAvailable = ( + data["musicItemRendererDisplayPolicy"] != "MUSIC_ITEM_RENDERER_DISPLAY_POLICY_GREY_OUT" + ) + search_result["isAvailable"] = isAvailable + if result_type in ["song", "video", "album"]: search_result["duration"] = None search_result["year"] = None