Bump models to 1.1.55 (#2397)
authorMarcel van der Veldt <m.vanderveldt@outlook.com>
Mon, 15 Sep 2025 13:14:13 +0000 (15:14 +0200)
committerGitHub <noreply@github.com>
Mon, 15 Sep 2025 13:14:13 +0000 (15:14 +0200)
15 files changed:
music_assistant/controllers/player_queues.py
music_assistant/helpers/upnp.py
music_assistant/models/player.py
music_assistant/providers/airplay/player.py
music_assistant/providers/bluesound/player.py
music_assistant/providers/builtin_player/player.py
music_assistant/providers/hass_players/player.py
music_assistant/providers/snapcast/player.py
music_assistant/providers/sonos/player.py
music_assistant/providers/squeezelite/player.py
music_assistant/providers/universal_group/player.py
pyproject.toml
requirements_all.txt
tests/providers/jellyfin/__snapshots__/test_parsers.ambr
tests/providers/opensubsonic/__snapshots__/test_parsers.ambr

index 17d847fed6c711c5f94fdcdec4f04b18524f446f..7356c5684bb95d16500c2e8d78656a78aca72a95 100644 (file)
@@ -1283,7 +1283,7 @@ class PlayerQueuesController(CoreController):
             title="Music Assistant" if flow_mode else queue_item.name,
             image_url=MASS_LOGO_ONLINE,
             duration=duration,
-            queue_id=queue_item.queue_id,
+            source_id=queue_item.queue_id,
             queue_item_id=queue_item.queue_item_id,
         )
         if not flow_mode and queue_item.media_item:
@@ -1940,7 +1940,7 @@ class PlayerQueuesController(CoreController):
         if not player.current_media:
             return None
         # prefer queue_id and queue_item_id within the current media
-        if player.current_media.queue_id == queue_id and player.current_media.queue_item_id:
+        if player.current_media.source_id == queue_id and player.current_media.queue_item_id:
             return player.current_media.queue_item_id
         # special case for sonos players
         if (
index 3a0f13a036a2726f2d539e8e32ffa6fa3283ef3a..c4b21a2610d50c9f506c881c118649c07d0f1b8e 100644 (file)
@@ -149,7 +149,7 @@ def create_didl_metadata(media: PlayerMedia) -> str:
 
     return (
         '<DIDL-Lite xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:upnp="urn:schemas-upnp-org:metadata-1-0/upnp/" xmlns="urn:schemas-upnp-org:metadata-1-0/DIDL-Lite/" xmlns:r="urn:schemas-rinconnetworks-com:metadata-1-0/">'
-        f'<item id="{media.queue_item_id or xmlescape(media.uri)}" restricted="true" parentID="{media.queue_id or ""}">'
+        f'<item id="{media.queue_item_id or xmlescape(media.uri)}" restricted="true" parentID="{media.source_id or ""}">'
         f"<dc:title>{escape_metadata(media.title or media.uri)}</dc:title>"
         f"<dc:creator>{escape_metadata(media.artist or '')}</dc:creator>"
         f"<upnp:album>{escape_metadata(media.album or '')}</upnp:album>"
index a9f448317aaa802c7c60404e7cb86d772c48e5a5..05979cd8d45abc6e6c70fc99e904414a1c4830d8 100644 (file)
@@ -1047,7 +1047,7 @@ class Player(ABC):
         album: str | None = None,
         image_url: str | None = None,
         duration: int | None = None,
-        queue_id: str | None = None,
+        source_id: str | None = None,
         queue_item_id: str | None = None,
         custom_data: dict[str, Any] | None = None,
         clear_all: bool = False,
@@ -1075,8 +1075,8 @@ class Player(ABC):
             self._attr_current_media.image_url = image_url
         if duration:
             self._attr_current_media.duration = duration
-        if queue_id:
-            self._attr_current_media.queue_id = queue_id
+        if source_id:
+            self._attr_current_media.source_id = source_id
         if queue_item_id:
             self._attr_current_media.queue_item_id = queue_item_id
         if custom_data:
@@ -1622,7 +1622,7 @@ class SyncGroupPlayer(GroupPlayer):
         if sync_leader := self.sync_leader:
             await sync_leader.play_media(media)
             self._attr_current_media = media
-            self._attr_active_source = media.queue_id
+            self._attr_active_source = media.source_id
             self.update_state()
         else:
             raise RuntimeError("an empty group cannot play media, consider adding members first")
index be1eeab04e80ed69e1329b48b2be66fc91f87d48..c4c1eeee6d9e5563bf8f9b8c6e147e4b9df6e382 100644 (file)
@@ -211,7 +211,7 @@ class AirPlayPlayer(Player):
 
         # set the active source for the player to the media queue
         # this accounts for syncgroups and linked players (e.g. sonos)
-        self._attr_active_source = media.queue_id
+        self._attr_active_source = media.source_id
         self._attr_current_media = media
 
         # select audio source
@@ -235,19 +235,21 @@ class AirPlayPlayer(Player):
                 # because this could have been a group
                 player_id=media.custom_data["player_id"],
             )
-        elif media.queue_id and media.queue_id.startswith(UGP_PREFIX):
+        elif media.source_id and media.source_id.startswith(UGP_PREFIX):
             # special case: UGP stream
-            ugp_player = cast("UniversalGroupPlayer", self.mass.players.get(media.queue_id))
+            ugp_player = cast("UniversalGroupPlayer", self.mass.players.get(media.source_id))
             ugp_stream = ugp_player.stream
             assert ugp_stream is not None  # for type checker
             input_format = ugp_stream.base_pcm_format
             audio_source = ugp_stream.subscribe_raw()
-        elif media.queue_id and media.queue_item_id:
+        elif media.source_id and media.queue_item_id:
             # regular queue (flow) stream request
             input_format = AIRPLAY_FLOW_PCM_FORMAT
-            queue = self.mass.player_queues.get(media.queue_id)
+            queue = self.mass.player_queues.get(media.source_id)
             assert queue
-            start_queue_item = self.mass.player_queues.get_item(media.queue_id, media.queue_item_id)
+            start_queue_item = self.mass.player_queues.get_item(
+                media.source_id, media.queue_item_id
+            )
             assert start_queue_item
             audio_source = self.mass.streams.get_queue_flow_stream(
                 queue=queue,
index 8e3e70d7be7103526654eadfd3448bb056727633..8c5c992dfe9e8241dc0911224ca4bf3c79e5e97f 100644 (file)
@@ -183,7 +183,7 @@ class BluesoundPlayer(Player):
 
         # Optimistically update state
         self._attr_current_media = media
-        self._attr_active_source = media.queue_id
+        self._attr_active_source = media.source_id
         self._attr_elapsed_time = 0
         self._attr_elapsed_time_last_updated = time.time()
         self.update_state()
index 1082f179947d951d1951a91c0fc08bf234b9b3d3..cb135027ea9ae02b1db55b9f2fe576a125032559 100644 (file)
@@ -175,7 +175,7 @@ class BuiltinPlayer(Player):
         url = f"builtin_player/flow/{self.player_id}.mp3"
         self._attr_current_media = media
         self._attr_playback_state = PlaybackState.PLAYING
-        self._attr_active_source = media.queue_id
+        self._attr_active_source = media.source_id
         self.update_state()
         self.mass.signal_event(
             EventType.BUILTIN_PLAYER,
@@ -254,10 +254,10 @@ class BuiltinPlayer(Player):
         if queue is None or media is None:
             raise web.HTTPNotFound(reason="No active queue or media found!")
 
-        if media.queue_id is None:
+        if media.source_id is None:
             raise web.HTTPError  # TODO: better error
 
-        queue_item = self.mass.player_queues.get_item(media.queue_id, media.queue_item_id)
+        queue_item = self.mass.player_queues.get_item(media.source_id, media.queue_item_id)
 
         if queue_item is None:
             raise web.HTTPError  # TODO: better error
index ab3ce5768b5b27bb8d17ad46f2269e0ed929af91..4b7ccc4e54bf2a9d0d89895d9cf333133f54c935 100644 (file)
@@ -256,7 +256,7 @@ class HomeAssistantPlayer(Player):
 
         # Optimistically update state
         self._attr_current_media = media
-        self._attr_active_source = media.queue_id
+        self._attr_active_source = media.source_id
         self._attr_elapsed_time = 0
         self._attr_elapsed_time_last_updated = time.time()
         self._attr_playback_state = PlaybackState.PLAYING
index 1b21f2f5903e7dd6f028e0737a6890d13601da2c..0cbeabd179e0abe96bd614f95fc07b7d1b4b4a0d 100644 (file)
@@ -175,7 +175,7 @@ class SnapCastPlayer(Player):
 
         # get stream or create new one
         stream_name = self._get_stream_name(SnapCastStreamType.MUSIC)
-        stream = await self._get_or_create_stream(stream_name, media.queue_id or self.player_id)
+        stream = await self._get_or_create_stream(stream_name, media.source_id or self.player_id)
 
         # if no announcement is playing we activate the stream now, otherwise it
         # will be activated by play_announcement when the announcement is over.
@@ -185,7 +185,7 @@ class SnapCastPlayer(Player):
             await snap_group.set_stream(stream.identifier)
 
         self._attr_current_media = media
-        self._attr_active_source = media.queue_id
+        self._attr_active_source = media.source_id
 
         # select audio source
         if media.media_type == MediaType.PLUGIN_SOURCE:
@@ -197,18 +197,20 @@ class SnapCastPlayer(Player):
                 output_format=DEFAULT_SNAPCAST_FORMAT,
                 player_id=self.player_id,
             )
-        elif media.queue_id and media.queue_id.startswith(UGP_PREFIX):
+        elif media.source_id and media.source_id.startswith(UGP_PREFIX):
             # special case: UGP stream
-            ugp_player = cast("UniversalGroupPlayer", self.mass.players.get(media.queue_id))
+            ugp_player = cast("UniversalGroupPlayer", self.mass.players.get(media.source_id))
             ugp_stream = ugp_player.stream
             assert ugp_stream is not None  # for type checker
             input_format = ugp_stream.base_pcm_format
             audio_source = ugp_stream.subscribe_raw()
-        elif media.queue_id and media.queue_item_id:
+        elif media.source_id and media.queue_item_id:
             # regular queue (flow) stream request
             input_format = DEFAULT_SNAPCAST_PCM_FORMAT
-            queue = self.mass.player_queues.get(media.queue_id)
-            start_queue_item = self.mass.player_queues.get_item(media.queue_id, media.queue_item_id)
+            queue = self.mass.player_queues.get(media.source_id)
+            start_queue_item = self.mass.player_queues.get_item(
+                media.source_id, media.queue_item_id
+            )
             assert queue is not None  # for type checking
             assert start_queue_item is not None  # for type checking
             audio_source = self.mass.streams.get_queue_flow_stream(
index 01d809c4c6c4d19dcce3cdcadb6fbdcc07281462..22db88beca50faeca8d7d5b886e0b7d7b5157acf 100644 (file)
@@ -407,25 +407,25 @@ class SonosPlayer(Player):
         if media.media_type in (
             MediaType.PLUGIN_SOURCE,
             MediaType.FLOW_STREAM,
-        ) or media.queue_id.startswith(UGP_PREFIX):
+        ) or media.source_id.startswith(UGP_PREFIX):
             # flow stream or plugin source playback
             # always use the legacy (UPNP) playback method for this
             await self._play_media_legacy(media)
             _update_state()
             return
 
-        if media.queue_id:
+        if media.source_id and media.queue_item_id:
             # Regular Queue item playback
             # create a sonos cloud queue and load it
             cloud_queue_url = f"{self.mass.streams.base_url}/sonos_queue/v2.3/"
-            mass_queue = self.mass.player_queues.get(media.queue_id)
+            mass_queue = self.mass.player_queues.get(media.source_id)
             await self.client.player.group.play_cloud_queue(
                 cloud_queue_url,
-                http_authorization=media.queue_id,
+                http_authorization=media.source_id,
                 item_id=media.queue_item_id,
                 queue_version=str(int(mass_queue.items_last_updated)),
             )
-            self.mass.call_later(5, self.sync_play_modes, media.queue_id)
+            self.mass.call_later(5, self.sync_play_modes, media.source_id)
             _update_state()
             return
 
@@ -673,7 +673,7 @@ class SonosPlayer(Player):
                 image_url=track_image_url,
             )
             if active_service == MusicService.MUSIC_ASSISTANT:
-                current_media.queue_id = self._attr_active_source
+                current_media.source_id = self._attr_active_source
                 current_media.queue_item_id = current_item["id"]
         # radio stream info
         if container and container.get("name") and active_group.playback_metadata.get("streamInfo"):
@@ -831,7 +831,7 @@ class SonosPlayer(Player):
         player_id = self.player_id
         if (
             airplay_player.playback_state == PlaybackState.PLAYING
-            and airplay_player.active_source == media.queue_id
+            and airplay_player.active_source == media.source_id
         ):
             # if the airplay player is already playing,
             # the stream will be reused so no need to do the whole grouping thing below
index f750dc28eda9e1b0154ce9afde954a866fcdc97b..1ce1699d7e27ac6cf61289ef4216e051d29755af 100644 (file)
@@ -233,18 +233,18 @@ class SqueezelitePlayer(Player):
                 # because this could have been a group
                 player_id=media.custom_data["player_id"],
             )
-        elif media.queue_id.startswith("ugp_"):
+        elif media.source_id.startswith("ugp_"):
             # special case: UGP stream
-            ugp_player: UniversalGroupPlayer = self.mass.players.get(media.queue_id)
+            ugp_player: UniversalGroupPlayer = self.mass.players.get(media.source_id)
             ugp_stream = ugp_player.stream
             # Filter is later applied in MultiClientStream
             audio_source = ugp_stream.get_stream(master_audio_format, filter_params=None)
-        elif media.queue_id and media.queue_item_id:
+        elif media.source_id and media.queue_item_id:
             # regular queue stream request
             audio_source = self.mass.streams.get_queue_flow_stream(
-                queue=self.mass.player_queues.get(media.queue_id),
+                queue=self.mass.player_queues.get(media.source_id),
                 start_queue_item=self.mass.player_queues.get_item(
-                    media.queue_id, media.queue_item_id
+                    media.source_id, media.queue_item_id
                 ),
                 pcm_format=master_audio_format,
             )
@@ -412,10 +412,10 @@ class SqueezelitePlayer(Player):
             "artist": media.artist,
             "image_url": media.image_url,
             "duration": media.duration,
-            "queue_id": media.queue_id,
+            "queue_id": media.source_id,
             "queue_item_id": media.queue_item_id,
         }
-        if queue := self.mass.player_queues.get(media.queue_id):
+        if queue := self.mass.player_queues.get(media.source_id):
             self.extra_data["playlist repeat"] = REPEATMODE_MAP[queue.repeat_mode]
             self.extra_data["playlist shuffle"] = int(queue.shuffle_enabled)
         await slimplayer.play_url(
index 3d2ac0191190375eb881ed4edc6c0504b398cd11..e46448476c55949f53e15b140a83acabe5078e1d 100644 (file)
@@ -220,13 +220,13 @@ class UniversalGroupPlayer(GroupPlayer):
                 output_format=UGP_FORMAT,
                 player_id=media.custom_data["player_id"],
             )
-        elif media.queue_id and media.queue_item_id:
+        elif media.source_id and media.queue_item_id:
             # regular queue stream request
-            queue = self.mass.player_queues.get(media.queue_id)
-            queue_item = self.mass.player_queues.get_item(media.queue_id, media.queue_item_id)
+            queue = self.mass.player_queues.get(media.source_id)
+            queue_item = self.mass.player_queues.get_item(media.source_id, media.queue_item_id)
             if not queue or not queue_item:
                 # this should not happen, but guard just in case
-                raise RuntimeError(f"Invalid queue(item): {media.queue_id}, {media.queue_item_id}")
+                raise RuntimeError(f"Invalid queue(item): {media.source_id}, {media.queue_item_id}")
             audio_source = self.mass.streams.get_queue_flow_stream(
                 queue=queue,
                 start_queue_item=queue_item,
@@ -252,7 +252,7 @@ class UniversalGroupPlayer(GroupPlayer):
         self._attr_elapsed_time = 0
         self._attr_elapsed_time_last_updated = time() - 1
         self._attr_playback_state = PlaybackState.PLAYING
-        self._attr_active_source = media.queue_id
+        self._attr_active_source = media.source_id
         self.update_state()
 
         # forward to downstream play_media commands
@@ -266,7 +266,7 @@ class UniversalGroupPlayer(GroupPlayer):
                             uri=f"{base_url}?player_id={member.player_id}",
                             media_type=MediaType.FLOW_STREAM,
                             title=self.display_name,
-                            queue_id=self.player_id,
+                            source_id=self.player_id,
                         )
                     )
                 )
@@ -303,7 +303,7 @@ class UniversalGroupPlayer(GroupPlayer):
                         uri=f"{base_url}?player_id={player_id}",
                         media_type=MediaType.FLOW_STREAM,
                         title=self.display_name,
-                        queue_id=child_player.player_id,
+                        source_id=child_player.player_id,
                     ),
                 )
         # handle removals
index 4c0e0950241ef89f167eed33f63c5d2078b1a61c..07ddc3ceefbd0c68c915a934cad50362db2febcc 100644 (file)
@@ -25,7 +25,7 @@ dependencies = [
   "ifaddr==0.2.0",
   "mashumaro==3.16",
   "music-assistant-frontend==2.15.4",
-  "music-assistant-models==1.1.53",
+  "music-assistant-models==1.1.55",
   "mutagen==1.47.0",
   "orjson==3.11.3",
   "pillow==11.3.0",
index a0d1ab5b37d9c14c9052c790e4ab510167b5fd5a..8f6b306e7287f0efcc85ed6602f448d3cc0a6be2 100644 (file)
@@ -32,7 +32,7 @@ liblistenbrainz==0.6.0
 lyricsgenius==3.6.5
 mashumaro==3.16
 music-assistant-frontend==2.15.4
-music-assistant-models==1.1.53
+music-assistant-models==1.1.55
 mutagen==1.47.0
 orjson==3.11.3
 pillow==11.3.0
index 0bef29cb51f3b57e29432d7ba1eaa49c1716883f..5ca5c0f53fc73a1d92e3dbf1bd1685a247b989e3 100644 (file)
@@ -39,6 +39,7 @@
       'description': None,
       'explicit': None,
       'genres': None,
+      'grouping': None,
       'images': list([
         dict({
           'path': 'http://localhost:1234/Items/70b7288088b42d318f75dbcc41fd0091/Images/Primary?api_key=ACCESS_TOKEN',
       'description': None,
       'explicit': None,
       'genres': None,
+      'grouping': None,
       'images': list([
         dict({
           'path': 'http://localhost:1234/Items/32ed6a0091733dcff57eae67010f3d4b/Images/Primary?api_key=ACCESS_TOKEN',
       'description': None,
       'explicit': None,
       'genres': None,
+      'grouping': None,
       'images': list([
       ]),
       'label': None,
       'description': None,
       'explicit': None,
       'genres': None,
+      'grouping': None,
       'images': list([
         dict({
           'path': 'http://localhost:1234/Items/dd954bbf54398e247d803186d3585b79/Images/Backdrop/0?api_key=ACCESS_TOKEN',
       'description': None,
       'explicit': None,
       'genres': None,
+      'grouping': None,
       'images': list([
         dict({
           'path': 'http://localhost:1234/Items/da9c458e425584680765ddc3a89cbc0c/Images/Primary?api_key=ACCESS_TOKEN',
       'description': None,
       'explicit': None,
       'genres': None,
+      'grouping': None,
       'images': list([
       ]),
       'label': None,
       'description': None,
       'explicit': None,
       'genres': None,
+      'grouping': None,
       'images': list([
         dict({
           'path': 'http://localhost:1234/Items/54918f75ee8f6c8b8dc5efd680644f29/Images/Primary?api_key=ACCESS_TOKEN',
       'description': None,
       'explicit': None,
       'genres': None,
+      'grouping': None,
       'images': list([
         dict({
           'path': 'http://localhost:1234/Items/fb12a77f49616a9fc56a6fab2b94174c/Images/Primary?api_key=ACCESS_TOKEN',
index 8f842d3a51a41ea82c8c039420b0d91fbc23ef4f..bee5131ab5c9be3a1d9ece45f20692e8c940cc11 100644 (file)
@@ -16,6 +16,7 @@
           'description': None,
           'explicit': None,
           'genres': None,
+          'grouping': None,
           'images': None,
           'label': None,
           'languages': None,
@@ -78,6 +79,7 @@
         'East coast',
         'Hip-Hop',
       ]),
+      'grouping': None,
       'images': list([
         dict({
           'path': 'al-ad0f112b6dcf83de5e9cae85d07f0d35_640a93a8',
           'description': None,
           'explicit': None,
           'genres': None,
+          'grouping': None,
           'images': None,
           'label': None,
           'languages': None,
         'East coast',
         'Hip-Hop',
       ]),
+      'grouping': None,
       'images': list([
         dict({
           'path': 'al-ad0f112b6dcf83de5e9cae85d07f0d35_640a93a8',
         'East coast',
         'Hip-Hop',
       ]),
+      'grouping': None,
       'images': list([
         dict({
           'path': 'al-ad0f112b6dcf83de5e9cae85d07f0d35_640a93a8',
         'East coast',
         'Hip-Hop',
       ]),
+      'grouping': None,
       'images': list([
         dict({
           'path': 'al-ad0f112b6dcf83de5e9cae85d07f0d35_640a93a8',
       'description': None,
       'explicit': None,
       'genres': None,
+      'grouping': None,
       'images': list([
         dict({
           'path': 'https://demo.org/image.jpg',
       'description': 'Empty biography',
       'explicit': None,
       'genres': None,
+      'grouping': None,
       'images': list([
         dict({
           'path': 'https://demo.org/image.jpg',
       'description': None,
       'explicit': None,
       'genres': None,
+      'grouping': None,
       'images': list([
         dict({
           'path': 'https://demo.org/image.jpg',
       'description': 'Empty biography',
       'explicit': None,
       'genres': None,
+      'grouping': None,
       'images': list([
         dict({
           'path': 'https://demo.org/image.jpg',
       'description': None,
       'explicit': None,
       'genres': None,
+      'grouping': None,
       'images': list([
         dict({
           'path': 'ar-100000002',
       'description': 'Empty biography',
       'explicit': None,
       'genres': None,
+      'grouping': None,
       'images': list([
         dict({
           'path': 'ar-100000002',
       'description': 'The history of The History of Rome...Why the Western Empire Fell when it did...Some thoughts on the future...Thank you, goodnight.',
       'explicit': None,
       'genres': None,
+      'grouping': None,
       'images': list([
         dict({
           'path': 'pd-5',
         'description': 'A weekly podcast tracing the rise, decline and fall of the Roman Empire. Now complete!',
         'explicit': None,
         'genres': None,
+        'grouping': None,
         'images': list([
           dict({
             'path': 'pd-5',
       'description': 'The history of The History of Rome...Why the Western Empire Fell when it did...Some thoughts on the future...Thank you, goodnight.',
       'explicit': None,
       'genres': None,
+      'grouping': None,
       'images': list([
         dict({
           'path': 'pd-5',
         'description': 'A weekly podcast tracing the rise, decline and fall of the Roman Empire. Now complete!',
         'explicit': None,
         'genres': None,
+        'grouping': None,
         'images': list([
           dict({
             'path': 'pd-5',
       'description': 'The history of The History of Rome...Why the Western Empire Fell when it did...Some thoughts on the future...Thank you, goodnight.',
       'explicit': None,
       'genres': None,
+      'grouping': None,
       'images': list([
         dict({
           'path': 'pd-5',
         'description': 'A weekly podcast tracing the rise, decline and fall of the Roman Empire. Now complete!',
         'explicit': None,
         'genres': None,
+        'grouping': None,
         'images': list([
           dict({
             'path': 'pd-5',
       'description': None,
       'explicit': None,
       'genres': None,
+      'grouping': None,
       'images': list([
         dict({
           'path': 'al-2250',
       'description': 'A weekly podcast tracing the rise, decline and fall of the Roman Empire. Now complete!',
       'explicit': None,
       'genres': None,
+      'grouping': None,
       'images': list([
         dict({
           'path': 'pd-5',
       'description': 'A weekly podcast tracing the rise, decline and fall of the Roman Empire. Now complete!',
       'explicit': None,
       'genres': None,
+      'grouping': None,
       'images': list([
         dict({
           'path': 'pd-5',
           'description': None,
           'explicit': None,
           'genres': None,
+          'grouping': None,
           'images': None,
           'label': None,
           'languages': None,
         'East coast',
         'Hip-Hop',
       ]),
+      'grouping': None,
       'images': None,
       'label': None,
       'languages': None,
           'description': None,
           'explicit': None,
           'genres': None,
+          'grouping': None,
           'images': None,
           'label': None,
           'languages': None,
         'East coast',
         'Hip-Hop',
       ]),
+      'grouping': None,
       'images': None,
       'label': None,
       'languages': None,
           'description': None,
           'explicit': None,
           'genres': None,
+          'grouping': None,
           'images': None,
           'label': None,
           'languages': None,
         'East coast',
         'Hip-Hop',
       ]),
+      'grouping': None,
       'images': None,
       'label': None,
       'languages': None,
           'description': None,
           'explicit': None,
           'genres': None,
+          'grouping': None,
           'images': None,
           'label': None,
           'languages': None,
         'East coast',
         'Hip-Hop',
       ]),
+      'grouping': None,
       'images': None,
       'label': None,
       'languages': None,
         'East coast',
         'Hip-Hop',
       ]),
+      'grouping': None,
       'images': None,
       'label': None,
       'languages': None,
         'East coast',
         'Hip-Hop',
       ]),
+      'grouping': None,
       'images': None,
       'label': None,
       'languages': None,