Repository: sigma67/ytmusicapi
Pull request #667 (2024-10-28 02:23:32)

get_playlist: join multiple title runs
* Return complete titles when split on time-like strings:

```diff
>>> yt.get_playlist("PLaZPMsuQNCsWn0iVMtGbaUXO6z-EdZaZm")["title"]
-03 Jan
+03 Jan 12:09
```

* `test_get_playlist`: assert more props have the expected value/shape

codecov[bot]: ## [Codecov](https://app.codecov.io/gh/sigma67/ytmusicapi/pull/667?dropdown=coverage&src=pr&el=h1&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=sigma67) Report
All modified and coverable lines are covered by tests :white_check_mark:
> Project coverage is 94.88%. Comparing base [(`fe95f59`)](https://app.codecov.io/gh/sigma67/ytmusicapi/commit/fe95f5974efd7ba8b87ba030a1f528afe41a5a31?dropdown=coverage&el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=sigma67) to head [(`a05abe8`)](https://app.codecov.io/gh/sigma67/ytmusicapi/commit/a05abe81330725b3353267fb4a4a95e5c8b0d3e0?dropdown=coverage&el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=sigma67).

<details><summary>Additional details and impacted files</summary>


```diff
@@            Coverage Diff             @@
##             main     #667      +/-   ##
==========================================
- Coverage   94.93%   94.88%   -0.05%     
==========================================
  Files          38       38              
  Lines        2288     2288              
==========================================
- Hits         2172     2171       -1     
- Misses        116      117       +1     
```

| [Flag](https://app.codecov.io/gh/sigma67/ytmusicapi/pull/667/flags?src=pr&el=flags&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=sigma67) | Coverage Δ | |
|---|---|---|
| [unittests](https://app.codecov.io/gh/sigma67/ytmusicapi/pull/667/flags?src=pr&el=flag&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=sigma67) | `94.88% <ø> (-0.05%)` | :arrow_down: |

Flags with carried forward coverage won't be shown. [Click here](https://docs.codecov.io/docs/carryforward-flags?utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=sigma67#carryforward-flags-in-the-pull-request-comment) to find out more.

</details>

[:umbrella: View full report in Codecov by Sentry](https://app.codecov.io/gh/sigma67/ytmusicapi/pull/667?dropdown=coverage&src=pr&el=continue&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=sigma67).   
:loudspeaker: Have feedback on the report? [Share it here](https://about.codecov.io/codecov-pr-comment-feedback/?utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=sigma67).

sigma67: @sgvictorino you may have forgotten to `git add` the files :)

Patch:
diff --git a/ytmusicapi/parsers/playlists.py b/ytmusicapi/parsers/playlists.py
index 35f7ed3..ebaf04f 100644
--- a/ytmusicapi/parsers/playlists.py
+++ b/ytmusicapi/parsers/playlists.py
@@ -40,7 +40,7 @@ def parse_playlist_header_meta(header: dict[str, Any]) -> dict[str, Any]:
         "views": None,
         "duration": None,
         "trackCount": None,
-        "title": nav(header, TITLE_TEXT, none_if_absent=True),
+        "title": "".join([run["text"] for run in header.get("title", {}).get("runs", [])]),
         "thumbnails": nav(header, THUMBNAILS),
     }
     if "runs" in header["secondSubtitle"]:


Test patch:
diff --git a/tests/data/expected_output/2024_03_get_playlist.json b/tests/data/expected_output/2024_03_get_playlist.json
new file mode 100644
index 0000000..982985a
--- /dev/null
+++ b/tests/data/expected_output/2024_03_get_playlist.json
@@ -0,0 +1,9 @@
+{
+    "id": "PLaZPMsuQNCsWn0iVMtGbaUXO6z-EdZaZm",
+    "title": "03 Jan 12:09",
+    "owned": true,
+    "trackCount": 245,
+    "year": "2024",
+    "description": "Created by Playlist Generator Bot, @spotify_youtube_playlist_bot",
+    "duration": "5+ hours"
+}
diff --git a/tests/data/expected_output/2024_03_get_playlist_public.json b/tests/data/expected_output/2024_03_get_playlist_public.json
new file mode 100644
index 0000000..a4eb762
--- /dev/null
+++ b/tests/data/expected_output/2024_03_get_playlist_public.json
@@ -0,0 +1,9 @@
+{
+    "id": "RDCLAK5uy_lWy02cQBnTVTlwuRauaGKeUDH3L6PXNxI",
+    "title": "Feel-Good Classic Rock",
+    "owned": false,
+    "trackCount": 101,
+    "year": "2024",
+    "description": "Hold on to the feeling.\n#essentials #rock #happy",
+    "duration": "6+ hours"
+}
diff --git a/tests/data/expected_output/2024_07_get_playlist_collaborative.json b/tests/data/expected_output/2024_07_get_playlist_collaborative.json
new file mode 100644
index 0000000..624a164
--- /dev/null
+++ b/tests/data/expected_output/2024_07_get_playlist_collaborative.json
@@ -0,0 +1,9 @@
+{
+    "id": "PLEUijtLfpCOgI8LNOwiwvq0EJ8HAGj7dT",
+    "title": "Example_collaborative_playlist",
+    "owned": true,
+    "trackCount": 4,
+    "year": "2024",
+    "description": "Example playlist with collaboration",
+    "duration": "11 minutes, 15 seconds"
+}
diff --git a/tests/mixins/test_playlists.py b/tests/mixins/test_playlists.py
index d4a85c5..bc1134f 100644
--- a/tests/mixins/test_playlists.py
+++ b/tests/mixins/test_playlists.py
@@ -13,25 +13,28 @@ from ytmusicapi.exceptions import YTMusicUserError
 
 class TestPlaylists:
     @pytest.mark.parametrize(
-        ("test_file", "owned"),
+        "test_file",
         [
-            ("2024_03_get_playlist.json", True),
-            ("2024_03_get_playlist_public.json", False),
-            ("2024_07_get_playlist_collaborative.json", True),
+            "2024_03_get_playlist.json",
+            "2024_03_get_playlist_public.json",
+            "2024_07_get_playlist_collaborative.json",
         ],
     )
-    def test_get_playlist_2024(self, yt, test_file, owned):
-        with open(Path(__file__).parent.parent / "data" / test_file, encoding="utf8") as f:
+    def test_get_playlist(self, yt, test_file):
+        data_dir = Path(__file__).parent.parent / "data"
+        with open(data_dir / test_file, encoding="utf8") as f:
             mock_response = json.load(f)
+        with open(data_dir / "expected_output" / test_file, encoding="utf8") as f:
+            expected_output = json.load(f)
+
         with mock.patch("ytmusicapi.YTMusic._send_request", return_value=mock_response):
             playlist = yt.get_playlist("MPREabc")
-            assert playlist["year"] == "2024"
-            assert playlist["owned"] == owned
-            assert "hours" in playlist["duration"] or "minutes" in playlist["duration"]
-            assert playlist["id"]
-            assert isinstance(playlist["description"], str) and playlist["description"]
-            assert len(playlist["tracks"]) > 0
+            assert playlist == playlist | expected_output
 
+            for thumbnail in playlist.get("thumbnails", []):
+                assert thumbnail["url"] and thumbnail["width"] and thumbnail["height"]
+
+            assert len(playlist["tracks"]) > 0
             for track in playlist["tracks"]:
                 assert isinstance(track["title"], str) and track["title"]