added tests for multi-artist spotify and lastgenre changes

This commit is contained in:
Arden Rasmussen 2025-12-17 11:54:12 -08:00
parent 62256adf4e
commit 963a9692cc
5 changed files with 231 additions and 10 deletions

View file

@ -336,7 +336,9 @@ class SpotifyPlugin(
if album_data["name"] == "":
self._log.debug("Album removed from Spotify: {}", album_id)
return None
artists_names, artists_ids = self._multi_artist_credit(album_data["artists"])
artists_names, artists_ids = self._multi_artist_credit(
album_data["artists"]
)
artist = ", ".join(artists_names)
date_parts = [
@ -406,7 +408,9 @@ class SpotifyPlugin(
:returns: TrackInfo object for track
"""
artists_names, artists_ids = self._multi_artist_credit(track_data["artists"])
artists_names, artists_ids = self._multi_artist_credit(
track_data["artists"]
)
artist = ", ".join(artists_names)
# Get album information for spotify tracks

View file

@ -31,6 +31,11 @@ New features:
no_convert, never_convert_lossy_files, same format, and max_bitrate
- :doc:`plugins/titlecase`: Add the `titlecase` plugin to allow users to
resolve differences in metadata source styles.
- :doc:`plugins/spotify`: Added support for multi-artist albums and tracks,
saving all contributing artists to the respective fields.
- :doc:`plugins/lastgenre`: If looking up a multi-artist album or track,
fallback to searching the individual artists for genres when no results
are found for the combined artist string.
Bug fixes:

View file

@ -546,24 +546,25 @@ class LastGenrePluginTest(PluginTestCase):
def test_get_genre(config_values, item_genre, mock_genres, expected_result):
"""Test _get_genre with various configurations."""
def mock_fetch_track_genre(self, obj=None):
def mock_fetch_track_genre(obj=None):
return mock_genres["track"]
def mock_fetch_album_genre(self, obj):
def mock_fetch_album_genre(obj):
return mock_genres["album"]
def mock_fetch_artist_genre(self, obj):
def mock_fetch_artist_genre(obj):
return mock_genres["artist"]
# Initialize plugin instance and item
plugin = lastgenre.LastGenrePlugin()
# Mock the last.fm fetchers. When whitelist enabled, we can assume only
# whitelisted genres get returned, the plugin's _resolve_genre method
# ensures it.
lastgenre.LastGenrePlugin.fetch_track_genre = mock_fetch_track_genre
lastgenre.LastGenrePlugin.fetch_album_genre = mock_fetch_album_genre
lastgenre.LastGenrePlugin.fetch_artist_genre = mock_fetch_artist_genre
plugin.fetch_track_genre = mock_fetch_track_genre
plugin.fetch_album_genre = mock_fetch_album_genre
plugin.fetch_artist_genre = mock_fetch_artist_genre
# Initialize plugin instance and item
plugin = lastgenre.LastGenrePlugin()
# Configure
plugin.config.set(config_values)
plugin.setup() # Loads default whitelist and canonicalization tree
@ -573,3 +574,36 @@ def test_get_genre(config_values, item_genre, mock_genres, expected_result):
# Run
res = plugin._get_genre(item)
assert res == expected_result
def test_multiartist_fallback():
def mock_lookup(entity, method, *args):
# Only response for the first artist, e.g. no results for the joint
# artist
if entity == "album" and args[0] == "Project Skylate":
return ["Electronic"]
return []
plugin = lastgenre.LastGenrePlugin()
plugin._last_lookup = mock_lookup
plugin.config.set(
{
"force": True,
"keep_existing": False,
"source": "album",
"whitelist": True,
"canonical": False,
"count": 5,
}
)
plugin.setup()
res = plugin._get_genre(
_common.item(
albumartist="Project Skylate & Sugar Shrill",
albumartists=["Project Skylate", "Sugar Shrill"],
artist="Project Skylate & Sugar Shrill",
artists=["Project Skylate", "Sugar Shrill"],
)
)
assert res == ("Electronic", "album, whitelist")

View file

@ -249,3 +249,27 @@ class SpotifyPluginTest(PluginTestCase):
query = params["q"][0]
assert query.isascii()
@responses.activate
def test_multi_artist_album(self):
"""Tests if plugin is able to map multiple artists in an album"""
# Mock the Spotify 'Get Album' call
json_file = os.path.join(
_common.RSRC, b"spotify", b"multi_artist_request.json"
)
with open(json_file, "rb") as f:
response_body = f.read()
responses.add(
responses.GET,
f"{spotify.SpotifyPlugin.album_url}0yhKyyjyKXWUieJ4w1IAEa",
body=response_body,
status=200,
content_type="application/json",
)
album_info = self.spotify.album_for_id("0yhKyyjyKXWUieJ4w1IAEa")
assert album_info is not None
assert album_info.artist == "Project Skylate, Sugar Shrill"
assert album_info.artists == ["Project Skylate", "Sugar Shrill"]

View file

@ -0,0 +1,154 @@
{
"album_type": "single",
"total_tracks": 1,
"available_markets": [
"AR", "AU", "AT", "BE", "BO", "BR", "BG", "CA", "CL", "CO", "CR", "CY",
"CZ", "DK", "DO", "DE", "EC", "EE", "SV", "FI", "FR", "GR", "GT", "HN",
"HK", "HU", "IS", "IE", "IT", "LV", "LT", "LU", "MY", "MT", "MX", "NL",
"NZ", "NI", "NO", "PA", "PY", "PE", "PH", "PL", "PT", "SG", "SK", "ES",
"SE", "CH", "TW", "TR", "UY", "US", "GB", "AD", "LI", "MC", "ID", "JP",
"TH", "VN", "RO", "IL", "ZA", "SA", "AE", "BH", "QA", "OM", "KW", "EG",
"MA", "DZ", "TN", "LB", "JO", "PS", "IN", "BY", "KZ", "MD", "UA", "AL",
"BA", "HR", "ME", "MK", "RS", "SI", "KR", "BD", "PK", "LK", "GH", "KE",
"NG", "TZ", "UG", "AG", "AM", "BS", "BB", "BZ", "BT", "BW", "BF", "CV",
"CW", "DM", "FJ", "GM", "GE", "GD", "GW", "GY", "HT", "JM", "KI", "LS",
"LR", "MW", "MV", "ML", "MH", "FM", "NA", "NR", "NE", "PW", "PG", "PR",
"WS", "SM", "ST", "SN", "SC", "SL", "SB", "KN", "LC", "VC", "SR", "TL",
"TO", "TT", "TV", "VU", "AZ", "BN", "BI", "KH", "CM", "TD", "KM", "GQ",
"SZ", "GA", "GN", "KG", "LA", "MO", "MR", "MN", "NP", "RW", "TG", "UZ",
"ZW", "BJ", "MG", "MU", "MZ", "AO", "CI", "DJ", "ZM", "CD", "CG", "IQ",
"LY", "TJ", "VE", "ET", "XK"
],
"external_urls": {
"spotify": "https://open.spotify.com/album/0yhKyyjyKXWUieJ4w1IAEa"
},
"href": "https://api.spotify.com/v1/albums/0yhKyyjyKXWUieJ4w1IAEa",
"id": "0yhKyyjyKXWUieJ4w1IAEa",
"images": [
{
"url": "https://i.scdn.co/image/ab67616d0000b2739a26f5e04909c87cead97c77",
"height": 640,
"width": 640
},
{
"url": "https://i.scdn.co/image/ab67616d00001e029a26f5e04909c87cead97c77",
"height": 300,
"width": 300
},
{
"url": "https://i.scdn.co/image/ab67616d000048519a26f5e04909c87cead97c77",
"height": 64,
"width": 64
}
],
"name": "Akiba Night",
"release_date": "2017-12-22",
"release_date_precision": "day",
"type": "album",
"uri": "spotify:album:0yhKyyjyKXWUieJ4w1IAEa",
"artists": [
{
"external_urls": {
"spotify": "https://open.spotify.com/artist/6m8MRXIVKb6wQaPlBIDMr1"
},
"href": "https://api.spotify.com/v1/artists/6m8MRXIVKb6wQaPlBIDMr1",
"id": "6m8MRXIVKb6wQaPlBIDMr1",
"name": "Project Skylate",
"type": "artist",
"uri": "spotify:artist:6m8MRXIVKb6wQaPlBIDMr1"
},
{
"external_urls": {
"spotify": "https://open.spotify.com/artist/4kkAIoQmNT5xEoNH5BuQLe"
},
"href": "https://api.spotify.com/v1/artists/4kkAIoQmNT5xEoNH5BuQLe",
"id": "4kkAIoQmNT5xEoNH5BuQLe",
"name": "Sugar Shrill",
"type": "artist",
"uri": "spotify:artist:4kkAIoQmNT5xEoNH5BuQLe"
}
],
"tracks": {
"href": "https://api.spotify.com/v1/albums/0yhKyyjyKXWUieJ4w1IAEa/tracks?offset=0&limit=50",
"limit": 50,
"next": null,
"offset": 0,
"previous": null,
"total": 1,
"items": [
{
"artists": [
{
"external_urls": {
"spotify": "https://open.spotify.com/artist/6m8MRXIVKb6wQaPlBIDMr1"
},
"href": "https://api.spotify.com/v1/artists/6m8MRXIVKb6wQaPlBIDMr1",
"id": "6m8MRXIVKb6wQaPlBIDMr1",
"name": "Project Skylate",
"type": "artist",
"uri": "spotify:artist:6m8MRXIVKb6wQaPlBIDMr1"
},
{
"external_urls": {
"spotify": "https://open.spotify.com/artist/4kkAIoQmNT5xEoNH5BuQLe"
},
"href": "https://api.spotify.com/v1/artists/4kkAIoQmNT5xEoNH5BuQLe",
"id": "4kkAIoQmNT5xEoNH5BuQLe",
"name": "Sugar Shrill",
"type": "artist",
"uri": "spotify:artist:4kkAIoQmNT5xEoNH5BuQLe"
}
],
"available_markets": [
"AR", "AU", "AT", "BE", "BO", "BR", "BG", "CA", "CL", "CO", "CR",
"CY", "CZ", "DK", "DO", "DE", "EC", "EE", "SV", "FI", "FR", "GR",
"GT", "HN", "HK", "HU", "IS", "IE", "IT", "LV", "LT", "LU", "MY",
"MT", "MX", "NL", "NZ", "NI", "NO", "PA", "PY", "PE", "PH", "PL",
"PT", "SG", "SK", "ES", "SE", "CH", "TW", "TR", "UY", "US", "GB",
"AD", "LI", "MC", "ID", "JP", "TH", "VN", "RO", "IL", "ZA", "SA",
"AE", "BH", "QA", "OM", "KW", "EG", "MA", "DZ", "TN", "LB", "JO",
"PS", "IN", "BY", "KZ", "MD", "UA", "AL", "BA", "HR", "ME", "MK",
"RS", "SI", "KR", "BD", "PK", "LK", "GH", "KE", "NG", "TZ", "UG",
"AG", "AM", "BS", "BB", "BZ", "BT", "BW", "BF", "CV", "CW", "DM",
"FJ", "GM", "GE", "GD", "GW", "GY", "HT", "JM", "KI", "LS", "LR",
"MW", "MV", "ML", "MH", "FM", "NA", "NR", "NE", "PW", "PG", "PR",
"WS", "SM", "ST", "SN", "SC", "SL", "SB", "KN", "LC", "VC", "SR",
"TL", "TO", "TT", "TV", "VU", "AZ", "BN", "BI", "KH", "CM", "TD",
"KM", "GQ", "SZ", "GA", "GN", "KG", "LA", "MO", "MR", "MN", "NP",
"RW", "TG", "UZ", "ZW", "BJ", "MG", "MU", "MZ", "AO", "CI", "DJ",
"ZM", "CD", "CG", "IQ", "LY", "TJ", "VE", "ET", "XK"
],
"disc_number": 1,
"duration_ms": 225268,
"explicit": false,
"external_urls": {
"spotify": "https://open.spotify.com/track/6sjZfVJworBX6TqyjkxIJ1"
},
"href": "https://api.spotify.com/v1/tracks/6sjZfVJworBX6TqyjkxIJ1",
"id": "6sjZfVJworBX6TqyjkxIJ1",
"name": "Akiba Nights",
"preview_url": "https://p.scdn.co/mp3-preview/a1c6c0c71f42caff0b19d988849602fefbf7754a?cid=4e414367a1d14c75a5c5129a627fcab8",
"track_number": 1,
"type": "track",
"uri": "spotify:track:6sjZfVJworBX6TqyjkxIJ1",
"is_local": false
}
]
},
"copyrights": [
{
"text": "2017 Sugar Shrill",
"type": "C"
},
{
"text": "2017 Project Skylate",
"type": "P"
}
],
"external_ids": {
"upc": "5057728789361"
},
"genres": [],
"label": "Project Skylate",
"popularity": 21
}