OpenSubsonic: Use server provided version tag if present (#3200)
authorEric Munson <eric@munsonfam.org>
Fri, 20 Feb 2026 23:22:20 +0000 (18:22 -0500)
committerGitHub <noreply@github.com>
Fri, 20 Feb 2026 23:22:20 +0000 (00:22 +0100)
music_assistant/providers/opensubsonic/parsers.py
tests/providers/opensubsonic/__snapshots__/test_parsers.ambr
tests/providers/opensubsonic/fixtures/albums/no-version.album.json [new file with mode: 0644]
tests/providers/opensubsonic/fixtures/albums/no-version.info.json [new file with mode: 0644]
tests/providers/opensubsonic/fixtures/albums/spec.album.json
tests/providers/opensubsonic/fixtures/tracks/no-version.album.json [new file with mode: 0644]
tests/providers/opensubsonic/fixtures/tracks/no-version.track.json [new file with mode: 0644]

index 501234064bc36bd92f5f2c86d34526208a1024cf..56ee89afa6cb28fd5d87afe06c219f09ea3969bc 100644 (file)
@@ -109,7 +109,12 @@ def parse_track(
         for c in sonic_song.contributors:
             metadata.performers.add(c.artist.name)
 
-    name, version = parse_title_and_version(sonic_song.title)
+    if isinstance(album, Album) and album.version:
+        name = sonic_song.title
+        version = album.version
+    else:
+        name, version = parse_title_and_version(sonic_song.title)
+
     track = Track(
         item_id=sonic_song.id,
         provider=instance_id,
@@ -316,7 +321,12 @@ def parse_album(
     if sonic_album.moods:
         metadata.mood = sonic_album.moods[0]
 
-    name, version = parse_title_and_version(sonic_album.name)
+    if sonic_album.version:
+        name = sonic_album.name
+        version = sonic_album.version
+    else:
+        name, version = parse_title_and_version(sonic_album.name)
+
     album = Album(
         item_id=sonic_album.id,
         provider=SUBSONIC_DOMAIN,
index a94896c5c8e6077426fffe977eccb62411c822e3..0acfe2af5ca3b0f3148fbfdf11654b5c056fc9a0 100644 (file)
     'year': 2007,
   })
 # ---
-# name: test_parse_albums[spec.album]
+# name: test_parse_albums[no-version.album]
   dict({
     'album_type': 'unknown',
     'artists': list([
     'year': 2007,
   })
 # ---
-# name: test_parse_albums[spec.album].1
+# name: test_parse_albums[no-version.album].1
   dict({
     'album_type': 'unknown',
     'artists': list([
     'year': 2007,
   })
 # ---
-# name: test_parse_artists[no-sort-name.artist]
+# name: test_parse_albums[spec.album]
   dict({
+    'album_type': 'unknown',
+    'artists': list([
+      dict({
+        'available': True,
+        'external_ids': list([
+        ]),
+        'image': None,
+        'is_playable': True,
+        'item_id': '91c3901ac465b9efc439e4be4270c2b6',
+        'media_type': 'artist',
+        'name': 'pornophonique',
+        'provider': 'xx-instance-id-xx',
+        'sort_name': 'pornophonique',
+        'translation_key': None,
+        'uri': 'xx-instance-id-xx://artist/91c3901ac465b9efc439e4be4270c2b6',
+        'version': '',
+        'year': None,
+      }),
+      dict({
+        'available': True,
+        'external_ids': list([
+        ]),
+        'image': None,
+        'is_playable': True,
+        'item_id': 'ar-1',
+        'media_type': 'artist',
+        'name': 'Artist 1',
+        'provider': 'xx-instance-id-xx',
+        'sort_name': 'artist 1',
+        'translation_key': None,
+        'uri': 'xx-instance-id-xx://artist/ar-1',
+        'version': '',
+        'year': None,
+      }),
+      dict({
+        'available': True,
+        'external_ids': list([
+        ]),
+        'image': None,
+        'is_playable': True,
+        'item_id': 'ar-2',
+        'media_type': 'artist',
+        'name': 'Artist 2',
+        'provider': 'xx-instance-id-xx',
+        'sort_name': 'artist 2',
+        'translation_key': None,
+        'uri': 'xx-instance-id-xx://artist/ar-2',
+        'version': '',
+        'year': None,
+      }),
+    ]),
     'date_added': None,
     'external_ids': list([
       list([
-        'musicbrainz_artistid',
+        'musicbrainz_albumid',
         '189002e7-3285-4e2e-92a3-7f6c30d407a2',
       ]),
     ]),
     'favorite': True,
     'is_playable': True,
-    'item_id': '37ec820ca7193e17040c98f7da7c4b51',
-    'media_type': 'artist',
+    'item_id': 'ad0f112b6dcf83de5e9cae85d07f0d35',
+    'media_type': 'album',
     'metadata': dict({
       'chapters': None,
       'copyright': None,
       'description': None,
       'explicit': None,
-      'genres': None,
+      'genres': list([
+        'East coast',
+        'Hip-Hop',
+      ]),
       'grouping': None,
       'images': list([
         dict({
-          'path': 'https://demo.org/image.jpg',
-          'provider': 'xx-instance-id-xx',
-          'remotely_accessible': True,
-          'type': 'thumb',
-        }),
-        dict({
-          'path': 'ar-37ec820ca7193e17040c98f7da7c4b51_0',
+          'path': 'al-ad0f112b6dcf83de5e9cae85d07f0d35_640a93a8',
           'provider': 'xx-instance-id-xx',
           'remotely_accessible': False,
           'type': 'thumb',
       'links': None,
       'lrc_lyrics': None,
       'lyrics': None,
-      'mood': None,
+      'mood': 'slow',
       'performers': None,
       'popularity': None,
       'preview': None,
       'review': None,
       'style': None,
     }),
-    'name': '2 Mello',
+    'name': '8-bit lagerfeuer',
     'position': None,
     'provider': 'opensubsonic',
     'provider_mappings': list([
         'details': None,
         'in_library': None,
         'is_unique': None,
-        'item_id': '37ec820ca7193e17040c98f7da7c4b51',
+        'item_id': 'ad0f112b6dcf83de5e9cae85d07f0d35',
         'provider_domain': 'opensubsonic',
         'provider_instance': 'xx-instance-id-xx',
         'url': None,
       }),
     ]),
-    'sort_name': '2 mello',
+    'sort_name': 'lagerfeuer (8-bit)',
     'translation_key': None,
-    'uri': 'opensubsonic://artist/37ec820ca7193e17040c98f7da7c4b51',
-    'version': '',
+    'uri': 'opensubsonic://album/ad0f112b6dcf83de5e9cae85d07f0d35',
+    'version': 'Deluxe Edition',
+    'year': 2007,
   })
 # ---
-# name: test_parse_artists[no-sort-name.artist].1
+# name: test_parse_albums[spec.album].1
   dict({
+    'album_type': 'unknown',
+    'artists': list([
+      dict({
+        'available': True,
+        'external_ids': list([
+        ]),
+        'image': None,
+        'is_playable': True,
+        'item_id': '91c3901ac465b9efc439e4be4270c2b6',
+        'media_type': 'artist',
+        'name': 'pornophonique',
+        'provider': 'xx-instance-id-xx',
+        'sort_name': 'pornophonique',
+        'translation_key': None,
+        'uri': 'xx-instance-id-xx://artist/91c3901ac465b9efc439e4be4270c2b6',
+        'version': '',
+        'year': None,
+      }),
+      dict({
+        'available': True,
+        'external_ids': list([
+        ]),
+        'image': None,
+        'is_playable': True,
+        'item_id': 'ar-1',
+        'media_type': 'artist',
+        'name': 'Artist 1',
+        'provider': 'xx-instance-id-xx',
+        'sort_name': 'artist 1',
+        'translation_key': None,
+        'uri': 'xx-instance-id-xx://artist/ar-1',
+        'version': '',
+        'year': None,
+      }),
+      dict({
+        'available': True,
+        'external_ids': list([
+        ]),
+        'image': None,
+        'is_playable': True,
+        'item_id': 'ar-2',
+        'media_type': 'artist',
+        'name': 'Artist 2',
+        'provider': 'xx-instance-id-xx',
+        'sort_name': 'artist 2',
+        'translation_key': None,
+        'uri': 'xx-instance-id-xx://artist/ar-2',
+        'version': '',
+        'year': None,
+      }),
+    ]),
     'date_added': None,
     'external_ids': list([
       list([
-        'musicbrainz_artistid',
+        'musicbrainz_albumid',
         '189002e7-3285-4e2e-92a3-7f6c30d407a2',
       ]),
     ]),
     'favorite': True,
     'is_playable': True,
-    'item_id': '37ec820ca7193e17040c98f7da7c4b51',
-    'media_type': 'artist',
+    'item_id': 'ad0f112b6dcf83de5e9cae85d07f0d35',
+    'media_type': 'album',
     'metadata': dict({
       'chapters': None,
       'copyright': None,
-      'description': 'Empty biography',
+      'description': 'Download the full release here (creative commons). These cripsy beats are ripe with thumping funk and techno influences, sample wizardry and daring shuffles. Composed with the help of unique sound plugins which were especially programmed to measure Comfort Fit’s needs and wishes, we think the chances aren’t bad that you’ll fall for the unique sound signature, bounce and elegance of this unusual Hip Hop production. Ltj bukem / Good looking Rec., UK: "Really love this music." Velanche / XLR8R, UK: "Awesome job he\'s done... overall production is dope." Kwesi / BBE Music, UK: "Wooooooowwwww... WHAT THE FUCK! THIS IS WHAT',
       'explicit': None,
-      'genres': None,
+      'genres': list([
+        'East coast',
+        'Hip-Hop',
+      ]),
       'grouping': None,
       'images': list([
         dict({
-          'path': 'https://demo.org/image.jpg',
-          'provider': 'xx-instance-id-xx',
-          'remotely_accessible': True,
-          'type': 'thumb',
-        }),
-        dict({
-          'path': 'ar-37ec820ca7193e17040c98f7da7c4b51_0',
+          'path': 'al-ad0f112b6dcf83de5e9cae85d07f0d35_640a93a8',
           'provider': 'xx-instance-id-xx',
           'remotely_accessible': False,
           'type': 'thumb',
         }),
         dict({
-          'path': 'http://localhost:8989/play/art/f20070e8e11611cc53542a38801d60fa/artist/2/thumb34.jpg',
+          'path': 'http://localhost:8989/play/art/0f8c3cbd6b0b22c3b5402141351ac812/album/21/thumb34.jpg',
           'provider': 'xx-instance-id-xx',
           'remotely_accessible': True,
           'type': 'thumb',
       'links': None,
       'lrc_lyrics': None,
       'lyrics': None,
-      'mood': None,
+      'mood': 'slow',
       'performers': None,
       'popularity': None,
       'preview': None,
       'review': None,
       'style': None,
     }),
-    'name': '2 Mello',
+    'name': '8-bit lagerfeuer',
     'position': None,
     'provider': 'opensubsonic',
     'provider_mappings': list([
         'details': None,
         'in_library': None,
         'is_unique': None,
-        'item_id': '37ec820ca7193e17040c98f7da7c4b51',
+        'item_id': 'ad0f112b6dcf83de5e9cae85d07f0d35',
         'provider_domain': 'opensubsonic',
         'provider_instance': 'xx-instance-id-xx',
         'url': None,
       }),
     ]),
-    'sort_name': '2 mello',
+    'sort_name': 'lagerfeuer (8-bit)',
     'translation_key': None,
-    'uri': 'opensubsonic://artist/37ec820ca7193e17040c98f7da7c4b51',
-    'version': '',
+    'uri': 'opensubsonic://album/ad0f112b6dcf83de5e9cae85d07f0d35',
+    'version': 'Deluxe Edition',
+    'year': 2007,
   })
 # ---
-# name: test_parse_artists[spec-artistid3.artist]
+# name: test_parse_artists[no-sort-name.artist]
   dict({
     'date_added': None,
     'external_ids': list([
         'url': None,
       }),
     ]),
-    'sort_name': 'Mello (2)',
+    'sort_name': '2 mello',
     'translation_key': None,
     'uri': 'opensubsonic://artist/37ec820ca7193e17040c98f7da7c4b51',
     'version': '',
   })
 # ---
-# name: test_parse_artists[spec-artistid3.artist].1
+# name: test_parse_artists[no-sort-name.artist].1
   dict({
     'date_added': None,
     'external_ids': list([
         'url': None,
       }),
     ]),
-    'sort_name': 'Mello (2)',
+    'sort_name': '2 mello',
     'translation_key': None,
     'uri': 'opensubsonic://artist/37ec820ca7193e17040c98f7da7c4b51',
     'version': '',
   })
 # ---
-# name: test_parse_artists[spec-sample.artist]
+# name: test_parse_artists[spec-artistid3.artist]
   dict({
     'date_added': None,
     'external_ids': list([
+      list([
+        'musicbrainz_artistid',
+        '189002e7-3285-4e2e-92a3-7f6c30d407a2',
+      ]),
     ]),
     'favorite': True,
     'is_playable': True,
-    'item_id': '100000002',
+    'item_id': '37ec820ca7193e17040c98f7da7c4b51',
     'media_type': 'artist',
     'metadata': dict({
       'chapters': None,
       'grouping': None,
       'images': list([
         dict({
-          'path': 'ar-100000002',
+          'path': 'https://demo.org/image.jpg',
+          'provider': 'xx-instance-id-xx',
+          'remotely_accessible': True,
+          'type': 'thumb',
+        }),
+        dict({
+          'path': 'ar-37ec820ca7193e17040c98f7da7c4b51_0',
           'provider': 'xx-instance-id-xx',
           'remotely_accessible': False,
           'type': 'thumb',
       'review': None,
       'style': None,
     }),
-    'name': 'Synthetic',
+    'name': '2 Mello',
     'position': None,
     'provider': 'opensubsonic',
     'provider_mappings': list([
         'details': None,
         'in_library': None,
         'is_unique': None,
-        'item_id': '100000002',
+        'item_id': '37ec820ca7193e17040c98f7da7c4b51',
         'provider_domain': 'opensubsonic',
         'provider_instance': 'xx-instance-id-xx',
         'url': None,
       }),
     ]),
-    'sort_name': 'synthetic',
+    'sort_name': 'Mello (2)',
     'translation_key': None,
-    'uri': 'opensubsonic://artist/100000002',
+    'uri': 'opensubsonic://artist/37ec820ca7193e17040c98f7da7c4b51',
     'version': '',
   })
 # ---
-# name: test_parse_artists[spec-sample.artist].1
+# name: test_parse_artists[spec-artistid3.artist].1
   dict({
     'date_added': None,
     'external_ids': list([
+      list([
+        'musicbrainz_artistid',
+        '189002e7-3285-4e2e-92a3-7f6c30d407a2',
+      ]),
     ]),
     'favorite': True,
     'is_playable': True,
-    'item_id': '100000002',
+    'item_id': '37ec820ca7193e17040c98f7da7c4b51',
     'media_type': 'artist',
     'metadata': dict({
       'chapters': None,
       'grouping': None,
       'images': list([
         dict({
-          'path': 'ar-100000002',
+          'path': 'https://demo.org/image.jpg',
+          'provider': 'xx-instance-id-xx',
+          'remotely_accessible': True,
+          'type': 'thumb',
+        }),
+        dict({
+          'path': 'ar-37ec820ca7193e17040c98f7da7c4b51_0',
           'provider': 'xx-instance-id-xx',
           'remotely_accessible': False,
           'type': 'thumb',
       'review': None,
       'style': None,
     }),
-    'name': 'Synthetic',
+    'name': '2 Mello',
     'position': None,
     'provider': 'opensubsonic',
     'provider_mappings': list([
         'details': None,
         'in_library': None,
         'is_unique': None,
-        'item_id': '100000002',
+        'item_id': '37ec820ca7193e17040c98f7da7c4b51',
         'provider_domain': 'opensubsonic',
         'provider_instance': 'xx-instance-id-xx',
         'url': None,
       }),
     ]),
-    'sort_name': 'synthetic',
+    'sort_name': 'Mello (2)',
     'translation_key': None,
-    'uri': 'opensubsonic://artist/100000002',
+    'uri': 'opensubsonic://artist/37ec820ca7193e17040c98f7da7c4b51',
     'version': '',
   })
 # ---
-# name: test_parse_episode[gonic-sample.episode]
+# name: test_parse_artists[spec-sample.artist]
   dict({
     'date_added': None,
-    'duration': 1878,
     'external_ids': list([
     ]),
-    'favorite': False,
-    'fully_played': None,
+    'favorite': True,
     'is_playable': True,
-    'item_id': 'pd-5$!$pe-1860',
-    'media_type': 'podcast_episode',
+    'item_id': '100000002',
+    'media_type': 'artist',
     'metadata': dict({
       'chapters': None,
       'copyright': None,
-      'description': 'The history of The History of Rome...Why the Western Empire Fell when it did...Some thoughts on the future...Thank you, goodnight.',
+      'description': None,
       'explicit': None,
       'genres': None,
       'grouping': None,
       'images': list([
         dict({
-          'path': 'pd-5',
+          'path': 'ar-100000002',
           'provider': 'xx-instance-id-xx',
           'remotely_accessible': False,
           'type': 'thumb',
       'performers': None,
       'popularity': None,
       'preview': None,
-      'release_date': '2012-05-06T18:18:38+00:00',
+      'release_date': None,
       'review': None,
       'style': None,
     }),
-    'name': '179- The End',
-    'podcast': dict({
-      'date_added': None,
-      'external_ids': list([
-      ]),
-      'favorite': False,
-      'is_playable': True,
-      'item_id': 'pd-5',
-      'media_type': 'podcast',
-      'metadata': dict({
+    'name': 'Synthetic',
+    'position': None,
+    'provider': 'opensubsonic',
+    'provider_mappings': list([
+      dict({
+        'audio_format': dict({
+          'bit_depth': 16,
+          'bit_rate': 0,
+          'channels': 2,
+          'codec_type': '?',
+          'content_type': '?',
+          'output_format_str': '?',
+          'sample_rate': 44100,
+        }),
+        'available': True,
+        'details': None,
+        'in_library': None,
+        'is_unique': None,
+        'item_id': '100000002',
+        'provider_domain': 'opensubsonic',
+        'provider_instance': 'xx-instance-id-xx',
+        'url': None,
+      }),
+    ]),
+    'sort_name': 'synthetic',
+    'translation_key': None,
+    'uri': 'opensubsonic://artist/100000002',
+    'version': '',
+  })
+# ---
+# name: test_parse_artists[spec-sample.artist].1
+  dict({
+    'date_added': None,
+    'external_ids': list([
+    ]),
+    'favorite': True,
+    'is_playable': True,
+    'item_id': '100000002',
+    'media_type': 'artist',
+    'metadata': dict({
+      'chapters': None,
+      'copyright': None,
+      'description': 'Empty biography',
+      'explicit': None,
+      'genres': None,
+      'grouping': None,
+      'images': list([
+        dict({
+          'path': 'ar-100000002',
+          'provider': 'xx-instance-id-xx',
+          'remotely_accessible': False,
+          'type': 'thumb',
+        }),
+        dict({
+          'path': 'http://localhost:8989/play/art/f20070e8e11611cc53542a38801d60fa/artist/2/thumb34.jpg',
+          'provider': 'xx-instance-id-xx',
+          'remotely_accessible': True,
+          'type': 'thumb',
+        }),
+      ]),
+      'label': None,
+      'languages': None,
+      'last_refresh': None,
+      'links': None,
+      'lrc_lyrics': None,
+      'lyrics': None,
+      'mood': None,
+      'performers': None,
+      'popularity': None,
+      'preview': None,
+      'release_date': None,
+      'review': None,
+      'style': None,
+    }),
+    'name': 'Synthetic',
+    'position': None,
+    'provider': 'opensubsonic',
+    'provider_mappings': list([
+      dict({
+        'audio_format': dict({
+          'bit_depth': 16,
+          'bit_rate': 0,
+          'channels': 2,
+          'codec_type': '?',
+          'content_type': '?',
+          'output_format_str': '?',
+          'sample_rate': 44100,
+        }),
+        'available': True,
+        'details': None,
+        'in_library': None,
+        'is_unique': None,
+        'item_id': '100000002',
+        'provider_domain': 'opensubsonic',
+        'provider_instance': 'xx-instance-id-xx',
+        'url': None,
+      }),
+    ]),
+    'sort_name': 'synthetic',
+    'translation_key': None,
+    'uri': 'opensubsonic://artist/100000002',
+    'version': '',
+  })
+# ---
+# name: test_parse_episode[gonic-sample.episode]
+  dict({
+    'date_added': None,
+    'duration': 1878,
+    'external_ids': list([
+    ]),
+    'favorite': False,
+    'fully_played': None,
+    'is_playable': True,
+    'item_id': 'pd-5$!$pe-1860',
+    'media_type': 'podcast_episode',
+    'metadata': dict({
+      'chapters': None,
+      'copyright': None,
+      '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',
+          'provider': 'xx-instance-id-xx',
+          'remotely_accessible': False,
+          'type': 'thumb',
+        }),
+      ]),
+      'label': None,
+      'languages': None,
+      'last_refresh': None,
+      'links': None,
+      'lrc_lyrics': None,
+      'lyrics': None,
+      'mood': None,
+      'performers': None,
+      'popularity': None,
+      'preview': None,
+      'release_date': '2012-05-06T18:18:38+00:00',
+      'review': None,
+      'style': None,
+    }),
+    'name': '179- The End',
+    'podcast': dict({
+      'date_added': None,
+      'external_ids': list([
+      ]),
+      'favorite': False,
+      'is_playable': True,
+      'item_id': 'pd-5',
+      'media_type': 'podcast',
+      'metadata': dict({
         'chapters': None,
         'copyright': None,
         'description': 'A weekly podcast tracing the rise, decline and fall of the Roman Empire. Now complete!',
     'version': '',
   })
 # ---
+# name: test_parse_track[no-version.track]
+  dict({
+    'album': dict({
+      'available': True,
+      'external_ids': list([
+      ]),
+      'image': None,
+      'is_playable': True,
+      'item_id': 'e8a0685e3f3ec6f251649af2b58b8617',
+      'media_type': 'album',
+      'name': 'Live at The Casbah - 2005-04-29',
+      'provider': 'xx-instance-id-xx',
+      'sort_name': 'live at the casbah - 2005-04-29',
+      'translation_key': None,
+      'uri': 'xx-instance-id-xx://album/e8a0685e3f3ec6f251649af2b58b8617',
+      'version': '',
+      'year': None,
+    }),
+    'artists': list([
+      dict({
+        'available': True,
+        'external_ids': list([
+        ]),
+        'image': None,
+        'is_playable': True,
+        'item_id': '97e0398acf63f9fb930d7d4ce209a52b',
+        'media_type': 'artist',
+        'name': 'The New Deal',
+        'provider': 'xx-instance-id-xx',
+        'sort_name': 'new deal, the',
+        'translation_key': None,
+        'uri': 'xx-instance-id-xx://artist/97e0398acf63f9fb930d7d4ce209a52b',
+        'version': '',
+        'year': None,
+      }),
+      dict({
+        'available': True,
+        'external_ids': list([
+        ]),
+        'image': None,
+        'is_playable': True,
+        'item_id': 'ar-1',
+        'media_type': 'artist',
+        'name': 'Artist 1',
+        'provider': 'xx-instance-id-xx',
+        'sort_name': 'artist 1',
+        'translation_key': None,
+        'uri': 'xx-instance-id-xx://artist/ar-1',
+        'version': '',
+        'year': None,
+      }),
+      dict({
+        'available': True,
+        'external_ids': list([
+        ]),
+        'image': None,
+        'is_playable': True,
+        'item_id': 'ar-2',
+        'media_type': 'artist',
+        'name': 'Artist 2',
+        'provider': 'xx-instance-id-xx',
+        'sort_name': 'artist 2',
+        'translation_key': None,
+        'uri': 'xx-instance-id-xx://artist/ar-2',
+        'version': '',
+        'year': None,
+      }),
+    ]),
+    'date_added': None,
+    'disc_number': 1,
+    'duration': 178,
+    'external_ids': list([
+      list([
+        'musicbrainz_recordingid',
+        '189002e7-3285-4e2e-92a3-7f6c30d407a2',
+      ]),
+    ]),
+    'favorite': True,
+    'is_playable': True,
+    'item_id': '082f435a363c32c57d5edb6a678a28d4',
+    'last_played': 0,
+    'media_type': 'track',
+    'metadata': dict({
+      'chapters': None,
+      'copyright': None,
+      'description': None,
+      'explicit': True,
+      'genres': list([
+        'East coast',
+        'Hip-Hop',
+      ]),
+      'grouping': None,
+      'images': None,
+      'label': None,
+      'languages': None,
+      'last_refresh': None,
+      'links': None,
+      'lrc_lyrics': None,
+      'lyrics': None,
+      'mood': 'slow',
+      'performers': list([
+        'Artist 3',
+        'Artist 4',
+        'Artist 5',
+      ]),
+      'popularity': None,
+      'preview': None,
+      'release_date': None,
+      'review': None,
+      'style': None,
+    }),
+    'name': '"polar expedition"',
+    'position': None,
+    'provider': 'xx-instance-id-xx',
+    'provider_mappings': list([
+      dict({
+        'audio_format': dict({
+          'bit_depth': 16,
+          'bit_rate': 880,
+          'channels': 2,
+          'codec_type': '?',
+          'content_type': 'flac',
+          'output_format_str': 'flac',
+          'sample_rate': 44100,
+        }),
+        'available': True,
+        'details': None,
+        'in_library': None,
+        'is_unique': None,
+        'item_id': '082f435a363c32c57d5edb6a678a28d4',
+        'provider_domain': 'opensubsonic',
+        'provider_instance': 'xx-instance-id-xx',
+        'url': None,
+      }),
+    ]),
+    'sort_name': 'Polar expedition',
+    'track_number': 4,
+    'translation_key': None,
+    'uri': 'xx-instance-id-xx://track/082f435a363c32c57d5edb6a678a28d4',
+    'version': '',
+  })
+# ---
+# name: test_parse_track[no-version.track].1
+  dict({
+    'album': dict({
+      'artist': 'pornophonique',
+      'artistId': '97e0398acf63f9fb930d7d4ce209a52b',
+      'artists': list([
+        dict({
+          'id': 'ar-1',
+          'name': 'Artist 1',
+        }),
+        dict({
+          'id': 'ar-2',
+          'name': 'Artist 2',
+        }),
+      ]),
+      'coverArt': 'al-ad0f112b6dcf83de5e9cae85d07f0d35_640a93a8',
+      'created': '2023-03-10T02:19:35.784818075Z',
+      'discTitles': list([
+        dict({
+          'disc': 0,
+          'title': 'Disc 0 title',
+        }),
+        dict({
+          'disc': 2,
+          'title': 'Disc 1 title',
+        }),
+      ]),
+      'displayArtist': 'Artist 1 feat. Artist 2',
+      'duration': 1954,
+      'explicitStatus': 'explicit',
+      'genre': 'Hip-Hop',
+      'genres': list([
+        dict({
+          'name': 'Hip-Hop',
+        }),
+        dict({
+          'name': 'East coast',
+        }),
+      ]),
+      'id': 'e8a0685e3f3ec6f251649af2b58b8617',
+      'isCompilation': False,
+      'moods': list([
+        'slow',
+        'cool',
+      ]),
+      'musicBrainzId': '189002e7-3285-4e2e-92a3-7f6c30d407a2',
+      'name': 'Live at The Casbah - 2005-04-29',
+      'originalReleaseDate': dict({
+        'day': 10,
+        'month': 3,
+        'year': 2001,
+      }),
+      'playCount': 97,
+      'played': '2023-03-28T00:45:13Z',
+      'releaseDate': dict({
+        'day': 10,
+        'month': 3,
+        'year': 2001,
+      }),
+      'releaseTypes': list([
+        'Album',
+        'Remixes',
+      ]),
+      'songCount': 8,
+      'sortName': 'lagerfeuer (8-bit)',
+      'starred': '2023-03-22T01:51:06Z',
+      'userRating': 4,
+      'year': 2007,
+    }),
+    'artists': list([
+      dict({
+        'available': True,
+        'external_ids': list([
+        ]),
+        'image': None,
+        'is_playable': True,
+        'item_id': '97e0398acf63f9fb930d7d4ce209a52b',
+        'media_type': 'artist',
+        'name': 'The New Deal',
+        'provider': 'xx-instance-id-xx',
+        'sort_name': 'new deal, the',
+        'translation_key': None,
+        'uri': 'xx-instance-id-xx://artist/97e0398acf63f9fb930d7d4ce209a52b',
+        'version': '',
+        'year': None,
+      }),
+      dict({
+        'available': True,
+        'external_ids': list([
+        ]),
+        'image': None,
+        'is_playable': True,
+        'item_id': 'ar-1',
+        'media_type': 'artist',
+        'name': 'Artist 1',
+        'provider': 'xx-instance-id-xx',
+        'sort_name': 'artist 1',
+        'translation_key': None,
+        'uri': 'xx-instance-id-xx://artist/ar-1',
+        'version': '',
+        'year': None,
+      }),
+      dict({
+        'available': True,
+        'external_ids': list([
+        ]),
+        'image': None,
+        'is_playable': True,
+        'item_id': 'ar-2',
+        'media_type': 'artist',
+        'name': 'Artist 2',
+        'provider': 'xx-instance-id-xx',
+        'sort_name': 'artist 2',
+        'translation_key': None,
+        'uri': 'xx-instance-id-xx://artist/ar-2',
+        'version': '',
+        'year': None,
+      }),
+    ]),
+    'date_added': None,
+    'disc_number': 1,
+    'duration': 178,
+    'external_ids': list([
+      list([
+        'musicbrainz_recordingid',
+        '189002e7-3285-4e2e-92a3-7f6c30d407a2',
+      ]),
+    ]),
+    'favorite': True,
+    'is_playable': True,
+    'item_id': '082f435a363c32c57d5edb6a678a28d4',
+    'last_played': 0,
+    'media_type': 'track',
+    'metadata': dict({
+      'chapters': None,
+      'copyright': None,
+      'description': None,
+      'explicit': True,
+      'genres': list([
+        'East coast',
+        'Hip-Hop',
+      ]),
+      'grouping': None,
+      'images': None,
+      'label': None,
+      'languages': None,
+      'last_refresh': None,
+      'links': None,
+      'lrc_lyrics': None,
+      'lyrics': None,
+      'mood': 'slow',
+      'performers': list([
+        'Artist 3',
+        'Artist 4',
+        'Artist 5',
+      ]),
+      'popularity': None,
+      'preview': None,
+      'release_date': None,
+      'review': None,
+      'style': None,
+    }),
+    'name': '"polar expedition"',
+    'position': None,
+    'provider': 'xx-instance-id-xx',
+    'provider_mappings': list([
+      dict({
+        'audio_format': dict({
+          'bit_depth': 16,
+          'bit_rate': 880,
+          'channels': 2,
+          'codec_type': '?',
+          'content_type': 'flac',
+          'output_format_str': 'flac',
+          'sample_rate': 44100,
+        }),
+        'available': True,
+        'details': None,
+        'in_library': None,
+        'is_unique': None,
+        'item_id': '082f435a363c32c57d5edb6a678a28d4',
+        'provider_domain': 'opensubsonic',
+        'provider_instance': 'xx-instance-id-xx',
+        'url': None,
+      }),
+    ]),
+    'sort_name': 'Polar expedition',
+    'track_number': 4,
+    'translation_key': None,
+    'uri': 'xx-instance-id-xx://track/082f435a363c32c57d5edb6a678a28d4',
+    'version': '',
+  })
+# ---
 # name: test_parse_track[spec-child.track]
   dict({
     'album': dict({
diff --git a/tests/providers/opensubsonic/fixtures/albums/no-version.album.json b/tests/providers/opensubsonic/fixtures/albums/no-version.album.json
new file mode 100644 (file)
index 0000000..986dbb9
--- /dev/null
@@ -0,0 +1,72 @@
+{
+    "id": "ad0f112b6dcf83de5e9cae85d07f0d35",
+    "name": "8-bit lagerfeuer",
+    "artist": "pornophonique",
+    "year": 2007,
+    "coverArt": "al-ad0f112b6dcf83de5e9cae85d07f0d35_640a93a8",
+    "starred": "2023-03-22T01:51:06Z",
+    "duration": 1954,
+    "playCount": 97,
+    "genre": "Hip-Hop",
+    "created": "2023-03-10T02:19:35.784818075Z",
+    "artistId": "91c3901ac465b9efc439e4be4270c2b6",
+    "songCount": 8,
+    "played": "2023-03-28T00:45:13Z",
+    "userRating": 4,
+    "recordLabels": [
+        {
+            "name": "Sony"
+        }
+    ],
+    "musicBrainzId": "189002e7-3285-4e2e-92a3-7f6c30d407a2",
+    "genres": [
+        {
+            "name": "Hip-Hop"
+        },
+        {
+            "name": "East coast"
+        }
+    ],
+    "artists": [
+        {
+            "id": "ar-1",
+            "name": "Artist 1"
+        },
+        {
+            "id": "ar-2",
+            "name": "Artist 2"
+        }
+    ],
+    "displayArtist": "Artist 1 feat. Artist 2",
+    "releaseTypes": [
+        "Album",
+        "Remixes"
+    ],
+    "moods": [
+        "slow",
+        "cool"
+    ],
+    "sortName": "lagerfeuer (8-bit)",
+    "originalReleaseDate": {
+        "year": 2001,
+        "month": 3,
+        "day": 10
+    },
+    "releaseDate": {
+        "year": 2001,
+        "month": 3,
+        "day": 10
+    },
+    "isCompilation": false,
+    "explicitStatus": "explicit",
+    "discTitles": [
+        {
+            "disc": 0,
+            "title": "Disc 0 title"
+        },
+        {
+            "disc": 2,
+            "title": "Disc 1 title"
+        }
+    ]
+}
diff --git a/tests/providers/opensubsonic/fixtures/albums/no-version.info.json b/tests/providers/opensubsonic/fixtures/albums/no-version.info.json
new file mode 100644 (file)
index 0000000..b971e1b
--- /dev/null
@@ -0,0 +1,7 @@
+{
+    "notes": "Download the full release here (creative commons). These cripsy beats are ripe with thumping funk and techno influences, sample wizardry and daring shuffles. Composed with the help of unique sound plugins which were especially programmed to measure Comfort Fit’s needs and wishes, we think the chances aren’t bad that you’ll fall for the unique sound signature, bounce and elegance of this unusual Hip Hop production. Ltj bukem / Good looking Rec., UK: \"Really love this music.\" Velanche / XLR8R, UK: \"Awesome job he's done... overall production is dope.\" Kwesi / BBE Music, UK: \"Wooooooowwwww... WHAT THE FUCK! THIS IS WHAT",
+    "musicBrainzId": "6e1d48f7-717c-416e-af35-5d2454a13af2",
+    "smallImageUrl": "http://localhost:8989/play/art/0f8c3cbd6b0b22c3b5402141351ac812/album/21/thumb34.jpg",
+    "mediumImageUrl": "http://localhost:8989/play/art/41b16680dc1b3aaf5dfba24ddb6a1712/album/21/thumb64.jpg",
+    "largeImageUrl": "http://localhost:8989/play/art/e6fd8d4e0d35c4436e56991892bfb27b/album/21/thumb174.jpg"
+}
index 986dbb9e13a7e8fda6f59b94da8c8572418d6d05..f956280294160b166a80619510ba907bfe37582d 100644 (file)
@@ -1,6 +1,7 @@
 {
     "id": "ad0f112b6dcf83de5e9cae85d07f0d35",
     "name": "8-bit lagerfeuer",
+    "version": "Deluxe Edition",
     "artist": "pornophonique",
     "year": 2007,
     "coverArt": "al-ad0f112b6dcf83de5e9cae85d07f0d35_640a93a8",
diff --git a/tests/providers/opensubsonic/fixtures/tracks/no-version.album.json b/tests/providers/opensubsonic/fixtures/tracks/no-version.album.json
new file mode 100644 (file)
index 0000000..9ce4363
--- /dev/null
@@ -0,0 +1,74 @@
+
+
+{
+    "id": "e8a0685e3f3ec6f251649af2b58b8617",
+    "name": "Live at The Casbah - 2005-04-29",
+    "artist": "pornophonique",
+    "year": 2007,
+    "coverArt": "al-ad0f112b6dcf83de5e9cae85d07f0d35_640a93a8",
+    "starred": "2023-03-22T01:51:06Z",
+    "duration": 1954,
+    "playCount": 97,
+    "genre": "Hip-Hop",
+    "created": "2023-03-10T02:19:35.784818075Z",
+    "artistId": "97e0398acf63f9fb930d7d4ce209a52b",
+    "songCount": 8,
+    "played": "2023-03-28T00:45:13Z",
+    "userRating": 4,
+    "recordLabels": [
+        {
+            "name": "Sony"
+        }
+    ],
+    "musicBrainzId": "189002e7-3285-4e2e-92a3-7f6c30d407a2",
+    "genres": [
+        {
+            "name": "Hip-Hop"
+        },
+        {
+            "name": "East coast"
+        }
+    ],
+    "artists": [
+        {
+            "id": "ar-1",
+            "name": "Artist 1"
+        },
+        {
+            "id": "ar-2",
+            "name": "Artist 2"
+        }
+    ],
+    "displayArtist": "Artist 1 feat. Artist 2",
+    "releaseTypes": [
+        "Album",
+        "Remixes"
+    ],
+    "moods": [
+        "slow",
+        "cool"
+    ],
+    "sortName": "lagerfeuer (8-bit)",
+    "originalReleaseDate": {
+        "year": 2001,
+        "month": 3,
+        "day": 10
+    },
+    "releaseDate": {
+        "year": 2001,
+        "month": 3,
+        "day": 10
+    },
+    "isCompilation": false,
+    "explicitStatus": "explicit",
+    "discTitles": [
+        {
+            "disc": 0,
+            "title": "Disc 0 title"
+        },
+        {
+            "disc": 2,
+            "title": "Disc 1 title"
+        }
+    ]
+}
diff --git a/tests/providers/opensubsonic/fixtures/tracks/no-version.track.json b/tests/providers/opensubsonic/fixtures/tracks/no-version.track.json
new file mode 100644 (file)
index 0000000..acaae27
--- /dev/null
@@ -0,0 +1,108 @@
+{
+  "id": "082f435a363c32c57d5edb6a678a28d4",
+  "parent": "e8a0685e3f3ec6f251649af2b58b8617",
+  "isDir": false,
+  "title": "\"polar expedition\"",
+  "album": "Live at The Casbah - 2005-04-29",
+  "artist": "The New Deal",
+  "track": 4,
+  "year": 2005,
+  "coverArt": "mf-082f435a363c32c57d5edb6a678a28d4_6410b3ce",
+  "size": 19866778,
+  "contentType": "audio/flac",
+  "suffix": "flac",
+  "starred": "2023-03-27T09:45:27Z",
+  "duration": 178,
+  "bitRate": 880,
+  "bitDepth": 16,
+  "samplingRate": 44100,
+  "channelCount": 2,
+  "path": "The New Deal/Live at The Casbah - 2005-04-29/04 - \"polar expedition\".flac",
+  "playCount": 8,
+  "played": "2023-03-26T22:27:46Z",
+  "discNumber": 1,
+  "created": "2023-03-14T17:51:22.112827504Z",
+  "albumId": "e8a0685e3f3ec6f251649af2b58b8617",
+  "artistId": "97e0398acf63f9fb930d7d4ce209a52b",
+  "type": "music",
+  "mediaType": "song",
+  "isVideo": false,
+  "bpm": 134,
+  "comment": "This is a song comment",
+  "sortName": "Polar expedition",
+  "musicBrainzId": "189002e7-3285-4e2e-92a3-7f6c30d407a2",
+  "genres": [
+    {
+      "name": "Hip-Hop"
+    },
+    {
+      "name": "East coast"
+    }
+  ],
+  "artists": [
+    {
+      "id": "ar-1",
+      "name": "Artist 1"
+    },
+    {
+      "id": "ar-2",
+      "name": "Artist 2"
+    }
+  ],
+  "displayArtist": "Artist 1 feat. Artist 2",
+  "albumArtists": [
+    {
+      "id": "ar-6",
+      "name": "Artist 6"
+    },
+    {
+      "id": "ar-7",
+      "name": "Artist 7"
+    }
+  ],
+  "displayAlbumArtist": "Artist 6 & Artist 7",
+  "contributors": [
+    {
+      "role": "composer",
+      "artist": {
+        "id": "ar-3",
+        "name": "Artist 3"
+      }
+    },
+    {
+      "role": "composer",
+      "artist": {
+        "id": "ar-4",
+        "name": "Artist 4"
+      }
+    },
+    {
+      "role": "lyricist",
+      "artist": {
+        "id": "ar-5",
+        "name": "Artist 5"
+      }
+    },
+    {
+      "role": "performer",
+      "subRole": "Bass",
+      "artist": {
+        "id": "ar-5",
+        "name": "Artist 5"
+      }
+    }
+  ],
+  "displayComposer": "Artist 3, Artist 4",
+  "moods": [
+    "slow",
+    "cool"
+  ],
+  "explicitStatus": "explicit",
+  "replayGain": {
+    "trackGain": 0.1,
+    "albumGain": 1.1,
+    "trackPeak": 9.2,
+    "albumPeak": 9,
+    "baseGain": 0
+  }
+}