mirror of
https://github.com/home-assistant/core.git
synced 2026-04-02 08:26:41 +01:00
Bump spotifyaio to 2.0.2 (#164114)
Co-authored-by: Robert Resch <robert@resch.dev>
This commit is contained in:
committed by
GitHub
parent
ffca43027f
commit
4bcea27151
@@ -118,7 +118,6 @@ class BrowsableMedia(StrEnum):
|
||||
CURRENT_USER_RECENTLY_PLAYED = "current_user_recently_played"
|
||||
CURRENT_USER_TOP_ARTISTS = "current_user_top_artists"
|
||||
CURRENT_USER_TOP_TRACKS = "current_user_top_tracks"
|
||||
NEW_RELEASES = "new_releases"
|
||||
|
||||
|
||||
LIBRARY_MAP = {
|
||||
@@ -130,7 +129,6 @@ LIBRARY_MAP = {
|
||||
BrowsableMedia.CURRENT_USER_RECENTLY_PLAYED.value: "Recently played",
|
||||
BrowsableMedia.CURRENT_USER_TOP_ARTISTS.value: "Top Artists",
|
||||
BrowsableMedia.CURRENT_USER_TOP_TRACKS.value: "Top Tracks",
|
||||
BrowsableMedia.NEW_RELEASES.value: "New Releases",
|
||||
}
|
||||
|
||||
CONTENT_TYPE_MEDIA_CLASS: dict[str, Any] = {
|
||||
@@ -166,10 +164,6 @@ CONTENT_TYPE_MEDIA_CLASS: dict[str, Any] = {
|
||||
"parent": MediaClass.DIRECTORY,
|
||||
"children": MediaClass.TRACK,
|
||||
},
|
||||
BrowsableMedia.NEW_RELEASES.value: {
|
||||
"parent": MediaClass.DIRECTORY,
|
||||
"children": MediaClass.ALBUM,
|
||||
},
|
||||
MediaType.PLAYLIST: {
|
||||
"parent": MediaClass.PLAYLIST,
|
||||
"children": MediaClass.TRACK,
|
||||
@@ -356,14 +350,11 @@ async def build_item_response( # noqa: C901
|
||||
elif media_content_type == BrowsableMedia.CURRENT_USER_TOP_TRACKS:
|
||||
if top_tracks := await spotify.get_top_tracks():
|
||||
items = [_get_track_item_payload(track) for track in top_tracks]
|
||||
elif media_content_type == BrowsableMedia.NEW_RELEASES:
|
||||
if new_releases := await spotify.get_new_releases():
|
||||
items = [_get_album_item_payload(album) for album in new_releases]
|
||||
elif media_content_type == MediaType.PLAYLIST:
|
||||
if playlist := await spotify.get_playlist(media_content_id):
|
||||
title = playlist.name
|
||||
image = playlist.images[0].url if playlist.images else None
|
||||
for playlist_item in playlist.tracks.items:
|
||||
for playlist_item in playlist.items.items:
|
||||
if playlist_item.track.type is ItemType.TRACK:
|
||||
if TYPE_CHECKING:
|
||||
assert isinstance(playlist_item.track, Track)
|
||||
|
||||
@@ -6,7 +6,7 @@ from collections.abc import Mapping
|
||||
import logging
|
||||
from typing import Any
|
||||
|
||||
from spotifyaio import SpotifyClient
|
||||
from spotifyaio import SpotifyClient, SpotifyForbiddenError
|
||||
|
||||
from homeassistant.config_entries import SOURCE_REAUTH, ConfigFlowResult
|
||||
from homeassistant.const import CONF_ACCESS_TOKEN, CONF_NAME, CONF_TOKEN
|
||||
@@ -41,6 +41,9 @@ class SpotifyFlowHandler(
|
||||
|
||||
try:
|
||||
current_user = await spotify.get_current_user()
|
||||
except SpotifyForbiddenError:
|
||||
self.logger.exception("User is not subscribed to Spotify")
|
||||
return self.async_abort(reason="user_not_premium")
|
||||
except Exception:
|
||||
self.logger.exception("Error while connecting to Spotify")
|
||||
return self.async_abort(reason="connection_error")
|
||||
|
||||
@@ -11,12 +11,15 @@ from spotifyaio import (
|
||||
Playlist,
|
||||
SpotifyClient,
|
||||
SpotifyConnectionError,
|
||||
SpotifyForbiddenError,
|
||||
SpotifyNotFoundError,
|
||||
UserProfile,
|
||||
)
|
||||
|
||||
from homeassistant.config_entries import ConfigEntry
|
||||
from homeassistant.core import HomeAssistant
|
||||
from homeassistant.exceptions import ConfigEntryError
|
||||
from homeassistant.helpers.issue_registry import IssueSeverity, async_create_issue
|
||||
from homeassistant.helpers.update_coordinator import DataUpdateCoordinator, UpdateFailed
|
||||
from homeassistant.util import dt as dt_util
|
||||
|
||||
@@ -33,6 +36,11 @@ type SpotifyConfigEntry = ConfigEntry[SpotifyData]
|
||||
|
||||
UPDATE_INTERVAL = timedelta(seconds=30)
|
||||
|
||||
FREE_API_BLOGPOST = (
|
||||
"https://developer.spotify.com/blog/"
|
||||
"2026-02-06-update-on-developer-access-and-platform-security"
|
||||
)
|
||||
|
||||
|
||||
@dataclass
|
||||
class SpotifyCoordinatorData:
|
||||
@@ -78,6 +86,19 @@ class SpotifyCoordinator(DataUpdateCoordinator[SpotifyCoordinatorData]):
|
||||
"""Set up the coordinator."""
|
||||
try:
|
||||
self.current_user = await self.client.get_current_user()
|
||||
except SpotifyForbiddenError as err:
|
||||
async_create_issue(
|
||||
self.hass,
|
||||
DOMAIN,
|
||||
f"user_not_premium_{self.config_entry.unique_id}",
|
||||
is_fixable=False,
|
||||
issue_domain=DOMAIN,
|
||||
severity=IssueSeverity.ERROR,
|
||||
translation_key="user_not_premium",
|
||||
translation_placeholders={"entry_title": self.config_entry.title},
|
||||
learn_more_url=FREE_API_BLOGPOST,
|
||||
)
|
||||
raise ConfigEntryError("User is not subscribed to Spotify") from err
|
||||
except SpotifyConnectionError as err:
|
||||
raise UpdateFailed("Error communicating with Spotify API") from err
|
||||
|
||||
|
||||
@@ -8,5 +8,5 @@
|
||||
"integration_type": "service",
|
||||
"iot_class": "cloud_polling",
|
||||
"loggers": ["spotifyaio"],
|
||||
"requirements": ["spotifyaio==1.0.0"]
|
||||
"requirements": ["spotifyaio==2.0.2"]
|
||||
}
|
||||
|
||||
@@ -14,10 +14,10 @@ from spotifyaio import (
|
||||
Item,
|
||||
ItemType,
|
||||
PlaybackState,
|
||||
ProductType,
|
||||
RepeatMode as SpotifyRepeatMode,
|
||||
Track,
|
||||
)
|
||||
from spotifyaio.models import ProductType
|
||||
from yarl import URL
|
||||
|
||||
from homeassistant.components.media_player import (
|
||||
@@ -222,7 +222,7 @@ class SpotifyMediaPlayer(SpotifyEntity, MediaPlayerEntity):
|
||||
if item.type == ItemType.EPISODE:
|
||||
if TYPE_CHECKING:
|
||||
assert isinstance(item, Episode)
|
||||
return item.show.publisher
|
||||
return item.show.name
|
||||
|
||||
if TYPE_CHECKING:
|
||||
assert isinstance(item, Track)
|
||||
@@ -230,12 +230,10 @@ class SpotifyMediaPlayer(SpotifyEntity, MediaPlayerEntity):
|
||||
|
||||
@property
|
||||
@ensure_item
|
||||
def media_album_name(self, item: Item) -> str: # noqa: PLR0206
|
||||
def media_album_name(self, item: Item) -> str | None: # noqa: PLR0206
|
||||
"""Return the media album."""
|
||||
if item.type == ItemType.EPISODE:
|
||||
if TYPE_CHECKING:
|
||||
assert isinstance(item, Episode)
|
||||
return item.show.name
|
||||
return None
|
||||
|
||||
if TYPE_CHECKING:
|
||||
assert isinstance(item, Track)
|
||||
|
||||
@@ -12,7 +12,8 @@
|
||||
"oauth_timeout": "[%key:common::config_flow::abort::oauth2_timeout%]",
|
||||
"oauth_unauthorized": "[%key:common::config_flow::abort::oauth2_unauthorized%]",
|
||||
"reauth_account_mismatch": "The Spotify account authenticated with does not match the account that needed re-authentication.",
|
||||
"reauth_successful": "[%key:common::config_flow::abort::reauth_successful%]"
|
||||
"reauth_successful": "[%key:common::config_flow::abort::reauth_successful%]",
|
||||
"user_not_premium": "The Spotify API has been changed and Developer applications created with a free account can no longer access the API. To continue using the Spotify integration, you should use an Spotify Developer application created with a Spotify Premium account, or upgrade to Spotify Premium."
|
||||
},
|
||||
"create_entry": {
|
||||
"default": "Successfully authenticated with Spotify."
|
||||
@@ -41,6 +42,12 @@
|
||||
"message": "[%key:common::exceptions::oauth2_implementation_unavailable::message%]"
|
||||
}
|
||||
},
|
||||
"issues": {
|
||||
"user_not_premium": {
|
||||
"description": "[%key:component::spotify::config::abort::user_not_premium%]",
|
||||
"title": "Spotify integration requires a Spotify Premium account"
|
||||
}
|
||||
},
|
||||
"system_health": {
|
||||
"info": {
|
||||
"api_endpoint_reachable": "Spotify API endpoint reachable"
|
||||
|
||||
2
requirements_all.txt
generated
2
requirements_all.txt
generated
@@ -2978,7 +2978,7 @@ speak2mary==1.4.0
|
||||
speedtest-cli==2.1.3
|
||||
|
||||
# homeassistant.components.spotify
|
||||
spotifyaio==1.0.0
|
||||
spotifyaio==2.0.2
|
||||
|
||||
# homeassistant.components.sql
|
||||
sqlparse==0.5.5
|
||||
|
||||
2
requirements_test_all.txt
generated
2
requirements_test_all.txt
generated
@@ -2514,7 +2514,7 @@ speak2mary==1.4.0
|
||||
speedtest-cli==2.1.3
|
||||
|
||||
# homeassistant.components.spotify
|
||||
spotifyaio==1.0.0
|
||||
spotifyaio==2.0.2
|
||||
|
||||
# homeassistant.components.sql
|
||||
sqlparse==0.5.5
|
||||
|
||||
@@ -10,7 +10,6 @@ from spotifyaio.models import (
|
||||
Artist,
|
||||
Devices,
|
||||
FollowedArtistResponse,
|
||||
NewReleasesResponse,
|
||||
NewReleasesResponseInner,
|
||||
PlaybackState,
|
||||
PlayedTrackResponse,
|
||||
@@ -142,9 +141,6 @@ def mock_spotify() -> Generator[AsyncMock]:
|
||||
client.get_followed_artists.return_value = FollowedArtistResponse.from_json(
|
||||
load_fixture("followed_artists.json", DOMAIN)
|
||||
).artists.items
|
||||
client.get_new_releases.return_value = NewReleasesResponse.from_json(
|
||||
load_fixture("new_releases.json", DOMAIN)
|
||||
).albums.items
|
||||
client.get_devices.return_value = Devices.from_json(
|
||||
load_fixture("devices.json", DOMAIN)
|
||||
).devices
|
||||
|
||||
@@ -1,469 +0,0 @@
|
||||
{
|
||||
"albums": {
|
||||
"href": "https://api.spotify.com/v1/browse/new-releases?offset=0&limit=20&locale=en-US,en;q%3D0.5",
|
||||
"items": [
|
||||
{
|
||||
"album_type": "album",
|
||||
"artists": [
|
||||
{
|
||||
"external_urls": {
|
||||
"spotify": "https://open.spotify.com/artist/4gzpq5DPGxSnKTe4SA8HAU"
|
||||
},
|
||||
"href": "https://api.spotify.com/v1/artists/4gzpq5DPGxSnKTe4SA8HAU",
|
||||
"id": "4gzpq5DPGxSnKTe4SA8HAU",
|
||||
"name": "Coldplay",
|
||||
"type": "artist",
|
||||
"uri": "spotify:artist:4gzpq5DPGxSnKTe4SA8HAU"
|
||||
}
|
||||
],
|
||||
"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",
|
||||
"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/5SGtrmYbIo0Dsg4kJ4qjM6"
|
||||
},
|
||||
"href": "https://api.spotify.com/v1/albums/5SGtrmYbIo0Dsg4kJ4qjM6",
|
||||
"id": "5SGtrmYbIo0Dsg4kJ4qjM6",
|
||||
"images": [
|
||||
{
|
||||
"height": 300,
|
||||
"url": "https://i.scdn.co/image/ab67616d00001e0209ba52a5116e0c3e8461f58b",
|
||||
"width": 300
|
||||
},
|
||||
{
|
||||
"height": 64,
|
||||
"url": "https://i.scdn.co/image/ab67616d0000485109ba52a5116e0c3e8461f58b",
|
||||
"width": 64
|
||||
},
|
||||
{
|
||||
"height": 640,
|
||||
"url": "https://i.scdn.co/image/ab67616d0000b27309ba52a5116e0c3e8461f58b",
|
||||
"width": 640
|
||||
}
|
||||
],
|
||||
"name": "Moon Music",
|
||||
"release_date": "2024-10-04",
|
||||
"release_date_precision": "day",
|
||||
"total_tracks": 10,
|
||||
"type": "album",
|
||||
"uri": "spotify:album:5SGtrmYbIo0Dsg4kJ4qjM6"
|
||||
},
|
||||
{
|
||||
"album_type": "album",
|
||||
"artists": [
|
||||
{
|
||||
"external_urls": {
|
||||
"spotify": "https://open.spotify.com/artist/4U9nsRTH2mr9L4UXEWqG5e"
|
||||
},
|
||||
"href": "https://api.spotify.com/v1/artists/4U9nsRTH2mr9L4UXEWqG5e",
|
||||
"id": "4U9nsRTH2mr9L4UXEWqG5e",
|
||||
"name": "Bente",
|
||||
"type": "artist",
|
||||
"uri": "spotify:artist:4U9nsRTH2mr9L4UXEWqG5e"
|
||||
}
|
||||
],
|
||||
"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",
|
||||
"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",
|
||||
"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/713lZ7AF55fEFSQgcttj9y"
|
||||
},
|
||||
"href": "https://api.spotify.com/v1/albums/713lZ7AF55fEFSQgcttj9y",
|
||||
"id": "713lZ7AF55fEFSQgcttj9y",
|
||||
"images": [
|
||||
{
|
||||
"height": 300,
|
||||
"url": "https://i.scdn.co/image/ab67616d00001e02ab9953b1d18f8233f6b26027",
|
||||
"width": 300
|
||||
},
|
||||
{
|
||||
"height": 64,
|
||||
"url": "https://i.scdn.co/image/ab67616d00004851ab9953b1d18f8233f6b26027",
|
||||
"width": 64
|
||||
},
|
||||
{
|
||||
"height": 640,
|
||||
"url": "https://i.scdn.co/image/ab67616d0000b273ab9953b1d18f8233f6b26027",
|
||||
"width": 640
|
||||
}
|
||||
],
|
||||
"name": "drift",
|
||||
"release_date": "2024-10-03",
|
||||
"release_date_precision": "day",
|
||||
"total_tracks": 14,
|
||||
"type": "album",
|
||||
"uri": "spotify:album:713lZ7AF55fEFSQgcttj9y"
|
||||
}
|
||||
],
|
||||
"limit": 20,
|
||||
"next": "https://api.spotify.com/v1/browse/new-releases?offset=20&limit=20&locale=en-US,en;q%3D0.5",
|
||||
"offset": 0,
|
||||
"previous": null,
|
||||
"total": 100
|
||||
}
|
||||
}
|
||||
@@ -108,21 +108,7 @@
|
||||
'width': None,
|
||||
}),
|
||||
]),
|
||||
'name': 'Spotify Web API Testing playlist',
|
||||
'object_type': 'playlist',
|
||||
'owner': dict({
|
||||
'display_name': 'JMPerez²',
|
||||
'external_urls': dict({
|
||||
'spotify': 'https://open.spotify.com/user/jmperezperez',
|
||||
}),
|
||||
'href': 'https://api.spotify.com/v1/users/jmperezperez',
|
||||
'object_type': 'user',
|
||||
'owner_id': 'jmperezperez',
|
||||
'uri': 'spotify:user:jmperezperez',
|
||||
}),
|
||||
'playlist_id': '3cEYpjA9oz9GiPac4AsH4n',
|
||||
'public': True,
|
||||
'tracks': dict({
|
||||
'items': dict({
|
||||
'items': list([
|
||||
dict({
|
||||
'added_at': '2015-01-15T12:39:22+00:00',
|
||||
@@ -517,7 +503,6 @@
|
||||
}),
|
||||
]),
|
||||
'name': 'Safety Third',
|
||||
'publisher': 'Safety Third ',
|
||||
'show_id': '1Y9ExMgMxoBVrgrfU7u0nD',
|
||||
'total_episodes': 120,
|
||||
'uri': 'spotify:show:1Y9ExMgMxoBVrgrfU7u0nD',
|
||||
@@ -528,6 +513,20 @@
|
||||
}),
|
||||
]),
|
||||
}),
|
||||
'name': 'Spotify Web API Testing playlist',
|
||||
'object_type': 'playlist',
|
||||
'owner': dict({
|
||||
'display_name': 'JMPerez²',
|
||||
'external_urls': dict({
|
||||
'spotify': 'https://open.spotify.com/user/jmperezperez',
|
||||
}),
|
||||
'href': 'https://api.spotify.com/v1/users/jmperezperez',
|
||||
'object_type': 'user',
|
||||
'owner_id': 'jmperezperez',
|
||||
'uri': 'spotify:user:jmperezperez',
|
||||
}),
|
||||
'playlist_id': '3cEYpjA9oz9GiPac4AsH4n',
|
||||
'public': True,
|
||||
'uri': 'spotify:playlist:3cEYpjA9oz9GiPac4AsH4n',
|
||||
}),
|
||||
}),
|
||||
|
||||
@@ -93,17 +93,6 @@
|
||||
'thumbnail': None,
|
||||
'title': 'Top Tracks',
|
||||
}),
|
||||
dict({
|
||||
'can_expand': True,
|
||||
'can_play': False,
|
||||
'can_search': False,
|
||||
'children_media_class': <MediaClass.ALBUM: 'album'>,
|
||||
'media_class': <MediaClass.DIRECTORY: 'directory'>,
|
||||
'media_content_id': 'spotify://01j5tx5a0ff6g5v0qjx6hbc94t/new_releases',
|
||||
'media_content_type': 'spotify://new_releases',
|
||||
'thumbnail': None,
|
||||
'title': 'New Releases',
|
||||
}),
|
||||
]),
|
||||
'children_media_class': <MediaClass.DIRECTORY: 'directory'>,
|
||||
'media_class': <MediaClass.DIRECTORY: 'directory'>,
|
||||
@@ -608,44 +597,6 @@
|
||||
'title': 'Top Tracks',
|
||||
})
|
||||
# ---
|
||||
# name: test_browsing[new_releases-new_releases]
|
||||
dict({
|
||||
'can_expand': True,
|
||||
'can_play': False,
|
||||
'can_search': False,
|
||||
'children': list([
|
||||
dict({
|
||||
'can_expand': True,
|
||||
'can_play': True,
|
||||
'can_search': False,
|
||||
'children_media_class': <MediaClass.TRACK: 'track'>,
|
||||
'media_class': <MediaClass.ALBUM: 'album'>,
|
||||
'media_content_id': 'spotify://01j5tx5a0ff6g5v0qjx6hbc94t/spotify:album:5SGtrmYbIo0Dsg4kJ4qjM6',
|
||||
'media_content_type': 'spotify://album',
|
||||
'thumbnail': 'https://i.scdn.co/image/ab67616d00001e0209ba52a5116e0c3e8461f58b',
|
||||
'title': 'Moon Music',
|
||||
}),
|
||||
dict({
|
||||
'can_expand': True,
|
||||
'can_play': True,
|
||||
'can_search': False,
|
||||
'children_media_class': <MediaClass.TRACK: 'track'>,
|
||||
'media_class': <MediaClass.ALBUM: 'album'>,
|
||||
'media_content_id': 'spotify://01j5tx5a0ff6g5v0qjx6hbc94t/spotify:album:713lZ7AF55fEFSQgcttj9y',
|
||||
'media_content_type': 'spotify://album',
|
||||
'thumbnail': 'https://i.scdn.co/image/ab67616d00001e02ab9953b1d18f8233f6b26027',
|
||||
'title': 'drift',
|
||||
}),
|
||||
]),
|
||||
'children_media_class': <MediaClass.ALBUM: 'album'>,
|
||||
'media_class': <MediaClass.DIRECTORY: 'directory'>,
|
||||
'media_content_id': 'spotify://01j5tx5a0ff6g5v0qjx6hbc94t/new_releases',
|
||||
'media_content_type': 'spotify://new_releases',
|
||||
'not_shown': 0,
|
||||
'thumbnail': None,
|
||||
'title': 'New Releases',
|
||||
})
|
||||
# ---
|
||||
# name: test_browsing[playlist-spotify:playlist:3cEYpjA9oz9GiPac4AsH4n]
|
||||
dict({
|
||||
'can_expand': True,
|
||||
|
||||
@@ -116,8 +116,7 @@
|
||||
'attributes': ReadOnlyDict({
|
||||
'entity_picture': '/api/media_player_proxy/media_player.spotify_spotify_1?token=mock-token&cache=cf1e6e1e830f08d3',
|
||||
'friendly_name': 'Spotify spotify_1',
|
||||
'media_album_name': 'Safety Third',
|
||||
'media_artist': 'Safety Third ',
|
||||
'media_artist': 'Safety Third',
|
||||
'media_content_id': 'spotify:episode:3o0RYoo5iOMKSmEbunsbvW',
|
||||
'media_content_type': <MediaType.PODCAST: 'podcast'>,
|
||||
'media_duration': 3690,
|
||||
|
||||
@@ -4,7 +4,7 @@ from http import HTTPStatus
|
||||
from unittest.mock import MagicMock, patch
|
||||
|
||||
import pytest
|
||||
from spotifyaio import SpotifyConnectionError
|
||||
from spotifyaio import SpotifyConnectionError, SpotifyForbiddenError
|
||||
|
||||
from homeassistant.components.spotify.const import DOMAIN
|
||||
from homeassistant.config_entries import SOURCE_USER
|
||||
@@ -95,6 +95,13 @@ async def test_full_flow(
|
||||
assert result["result"].unique_id == "1112264111"
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
("exception", "reason"),
|
||||
[
|
||||
(SpotifyConnectionError, "connection_error"),
|
||||
(SpotifyForbiddenError, "user_not_premium"),
|
||||
],
|
||||
)
|
||||
@pytest.mark.usefixtures("current_request_with_host")
|
||||
@pytest.mark.usefixtures("setup_credentials")
|
||||
async def test_abort_if_spotify_error(
|
||||
@@ -102,6 +109,8 @@ async def test_abort_if_spotify_error(
|
||||
hass_client_no_auth: ClientSessionGenerator,
|
||||
aioclient_mock: AiohttpClientMocker,
|
||||
mock_spotify: MagicMock,
|
||||
exception: Exception,
|
||||
reason: str,
|
||||
) -> None:
|
||||
"""Check Spotify errors causes flow to abort."""
|
||||
result = await hass.config_entries.flow.async_init(
|
||||
@@ -128,12 +137,12 @@ async def test_abort_if_spotify_error(
|
||||
},
|
||||
)
|
||||
|
||||
mock_spotify.return_value.get_current_user.side_effect = SpotifyConnectionError
|
||||
mock_spotify.return_value.get_current_user.side_effect = exception
|
||||
|
||||
result = await hass.config_entries.flow.async_configure(result["flow_id"])
|
||||
|
||||
assert result["type"] is FlowResultType.ABORT
|
||||
assert result["reason"] == "connection_error"
|
||||
assert result["reason"] == reason
|
||||
|
||||
|
||||
@pytest.mark.usefixtures("current_request_with_host")
|
||||
|
||||
@@ -3,10 +3,12 @@
|
||||
from unittest.mock import MagicMock, patch
|
||||
|
||||
import pytest
|
||||
from spotifyaio import SpotifyConnectionError
|
||||
from spotifyaio import SpotifyConnectionError, SpotifyForbiddenError
|
||||
|
||||
from homeassistant.components.spotify.const import DOMAIN
|
||||
from homeassistant.config_entries import ConfigEntryState
|
||||
from homeassistant.core import HomeAssistant
|
||||
from homeassistant.helpers import issue_registry as ir
|
||||
from homeassistant.helpers.config_entry_oauth2_flow import (
|
||||
ImplementationUnavailableError,
|
||||
)
|
||||
@@ -53,6 +55,26 @@ async def test_setup_with_required_calls_failing(
|
||||
assert not await hass.config_entries.async_setup(mock_config_entry.entry_id)
|
||||
|
||||
|
||||
@pytest.mark.usefixtures("setup_credentials")
|
||||
async def test_setup_free_account_is_failing(
|
||||
hass: HomeAssistant,
|
||||
mock_spotify: MagicMock,
|
||||
mock_config_entry: MockConfigEntry,
|
||||
issue_registry: ir.IssueRegistry,
|
||||
) -> None:
|
||||
"""Test the Spotify setup with a free account is failing."""
|
||||
mock_spotify.return_value.get_current_user.side_effect = SpotifyForbiddenError(
|
||||
"Check settings on developer.spotify.com/dashboard, the user may not be registered."
|
||||
)
|
||||
mock_config_entry.add_to_hass(hass)
|
||||
|
||||
assert not await hass.config_entries.async_setup(mock_config_entry.entry_id)
|
||||
issue = issue_registry.issues.get(
|
||||
(DOMAIN, f"user_not_premium_{mock_config_entry.unique_id}")
|
||||
)
|
||||
assert issue, "Repair issue not created"
|
||||
|
||||
|
||||
@pytest.mark.usefixtures("setup_credentials")
|
||||
async def test_oauth_implementation_not_available(
|
||||
hass: HomeAssistant,
|
||||
|
||||
@@ -66,7 +66,7 @@ async def test_browse_media_categories(
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
("config_entry_id"), [("01J5TX5A0FF6G5V0QJX6HBC94T"), ("32oesphrnacjcf7vw5bf6odx3")]
|
||||
"config_entry_id", ["01J5TX5A0FF6G5V0QJX6HBC94T", "32oesphrnacjcf7vw5bf6odx3"]
|
||||
)
|
||||
@pytest.mark.usefixtures("setup_credentials")
|
||||
async def test_browse_media_playlists(
|
||||
@@ -112,7 +112,6 @@ async def test_browse_media_playlists(
|
||||
("current_user_recently_played", "current_user_recently_played"),
|
||||
("current_user_top_artists", "current_user_top_artists"),
|
||||
("current_user_top_tracks", "current_user_top_tracks"),
|
||||
("new_releases", "new_releases"),
|
||||
("playlist", "spotify:playlist:3cEYpjA9oz9GiPac4AsH4n"),
|
||||
("album", "spotify:album:3IqzqH6ShrRtie9Yd2ODyG"),
|
||||
("artist", "spotify:artist:0TnOYISbd1XYRBk9myaseg"),
|
||||
@@ -138,13 +137,7 @@ async def test_browsing(
|
||||
assert response.as_dict() == snapshot
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
("media_content_id"),
|
||||
[
|
||||
"artist",
|
||||
None,
|
||||
],
|
||||
)
|
||||
@pytest.mark.parametrize("media_content_id", ["artist", None])
|
||||
@pytest.mark.usefixtures("setup_credentials")
|
||||
async def test_invalid_spotify_url(
|
||||
hass: HomeAssistant,
|
||||
|
||||
@@ -7,7 +7,6 @@ from freezegun.api import FrozenDateTimeFactory
|
||||
import pytest
|
||||
from spotifyaio import (
|
||||
PlaybackState,
|
||||
ProductType,
|
||||
RepeatMode as SpotifyRepeatMode,
|
||||
SpotifyConnectionError,
|
||||
SpotifyNotFoundError,
|
||||
@@ -108,20 +107,6 @@ async def test_podcast(
|
||||
)
|
||||
|
||||
|
||||
@pytest.mark.usefixtures("setup_credentials")
|
||||
async def test_free_account(
|
||||
hass: HomeAssistant,
|
||||
mock_spotify: MagicMock,
|
||||
mock_config_entry: MockConfigEntry,
|
||||
) -> None:
|
||||
"""Test the Spotify entities with a free account."""
|
||||
mock_spotify.return_value.get_current_user.return_value.product = ProductType.FREE
|
||||
await setup_integration(hass, mock_config_entry)
|
||||
state = hass.states.get("media_player.spotify_spotify_1")
|
||||
assert state
|
||||
assert state.attributes["supported_features"] == 0
|
||||
|
||||
|
||||
@pytest.mark.usefixtures("setup_credentials")
|
||||
async def test_restricted_device(
|
||||
hass: HomeAssistant,
|
||||
|
||||
Reference in New Issue
Block a user