mirror of
https://github.com/home-assistant/core.git
synced 2026-09-24 07:25:52 -05:00
Bump spotifyaio to 2.0.2 (#164114)
Co-authored-by: Robert Resch <robert@resch.dev>
This commit is contained in:
co-authored by
Robert Resch
parent
ffca43027f
commit
4bcea27151
@@ -118,7 +118,6 @@ class BrowsableMedia(StrEnum):
|
|||||||
CURRENT_USER_RECENTLY_PLAYED = "current_user_recently_played"
|
CURRENT_USER_RECENTLY_PLAYED = "current_user_recently_played"
|
||||||
CURRENT_USER_TOP_ARTISTS = "current_user_top_artists"
|
CURRENT_USER_TOP_ARTISTS = "current_user_top_artists"
|
||||||
CURRENT_USER_TOP_TRACKS = "current_user_top_tracks"
|
CURRENT_USER_TOP_TRACKS = "current_user_top_tracks"
|
||||||
NEW_RELEASES = "new_releases"
|
|
||||||
|
|
||||||
|
|
||||||
LIBRARY_MAP = {
|
LIBRARY_MAP = {
|
||||||
@@ -130,7 +129,6 @@ LIBRARY_MAP = {
|
|||||||
BrowsableMedia.CURRENT_USER_RECENTLY_PLAYED.value: "Recently played",
|
BrowsableMedia.CURRENT_USER_RECENTLY_PLAYED.value: "Recently played",
|
||||||
BrowsableMedia.CURRENT_USER_TOP_ARTISTS.value: "Top Artists",
|
BrowsableMedia.CURRENT_USER_TOP_ARTISTS.value: "Top Artists",
|
||||||
BrowsableMedia.CURRENT_USER_TOP_TRACKS.value: "Top Tracks",
|
BrowsableMedia.CURRENT_USER_TOP_TRACKS.value: "Top Tracks",
|
||||||
BrowsableMedia.NEW_RELEASES.value: "New Releases",
|
|
||||||
}
|
}
|
||||||
|
|
||||||
CONTENT_TYPE_MEDIA_CLASS: dict[str, Any] = {
|
CONTENT_TYPE_MEDIA_CLASS: dict[str, Any] = {
|
||||||
@@ -166,10 +164,6 @@ CONTENT_TYPE_MEDIA_CLASS: dict[str, Any] = {
|
|||||||
"parent": MediaClass.DIRECTORY,
|
"parent": MediaClass.DIRECTORY,
|
||||||
"children": MediaClass.TRACK,
|
"children": MediaClass.TRACK,
|
||||||
},
|
},
|
||||||
BrowsableMedia.NEW_RELEASES.value: {
|
|
||||||
"parent": MediaClass.DIRECTORY,
|
|
||||||
"children": MediaClass.ALBUM,
|
|
||||||
},
|
|
||||||
MediaType.PLAYLIST: {
|
MediaType.PLAYLIST: {
|
||||||
"parent": MediaClass.PLAYLIST,
|
"parent": MediaClass.PLAYLIST,
|
||||||
"children": MediaClass.TRACK,
|
"children": MediaClass.TRACK,
|
||||||
@@ -356,14 +350,11 @@ async def build_item_response( # noqa: C901
|
|||||||
elif media_content_type == BrowsableMedia.CURRENT_USER_TOP_TRACKS:
|
elif media_content_type == BrowsableMedia.CURRENT_USER_TOP_TRACKS:
|
||||||
if top_tracks := await spotify.get_top_tracks():
|
if top_tracks := await spotify.get_top_tracks():
|
||||||
items = [_get_track_item_payload(track) for track in 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:
|
elif media_content_type == MediaType.PLAYLIST:
|
||||||
if playlist := await spotify.get_playlist(media_content_id):
|
if playlist := await spotify.get_playlist(media_content_id):
|
||||||
title = playlist.name
|
title = playlist.name
|
||||||
image = playlist.images[0].url if playlist.images else None
|
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 playlist_item.track.type is ItemType.TRACK:
|
||||||
if TYPE_CHECKING:
|
if TYPE_CHECKING:
|
||||||
assert isinstance(playlist_item.track, Track)
|
assert isinstance(playlist_item.track, Track)
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ from collections.abc import Mapping
|
|||||||
import logging
|
import logging
|
||||||
from typing import Any
|
from typing import Any
|
||||||
|
|
||||||
from spotifyaio import SpotifyClient
|
from spotifyaio import SpotifyClient, SpotifyForbiddenError
|
||||||
|
|
||||||
from homeassistant.config_entries import SOURCE_REAUTH, ConfigFlowResult
|
from homeassistant.config_entries import SOURCE_REAUTH, ConfigFlowResult
|
||||||
from homeassistant.const import CONF_ACCESS_TOKEN, CONF_NAME, CONF_TOKEN
|
from homeassistant.const import CONF_ACCESS_TOKEN, CONF_NAME, CONF_TOKEN
|
||||||
@@ -41,6 +41,9 @@ class SpotifyFlowHandler(
|
|||||||
|
|
||||||
try:
|
try:
|
||||||
current_user = await spotify.get_current_user()
|
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:
|
except Exception:
|
||||||
self.logger.exception("Error while connecting to Spotify")
|
self.logger.exception("Error while connecting to Spotify")
|
||||||
return self.async_abort(reason="connection_error")
|
return self.async_abort(reason="connection_error")
|
||||||
|
|||||||
@@ -11,12 +11,15 @@ from spotifyaio import (
|
|||||||
Playlist,
|
Playlist,
|
||||||
SpotifyClient,
|
SpotifyClient,
|
||||||
SpotifyConnectionError,
|
SpotifyConnectionError,
|
||||||
|
SpotifyForbiddenError,
|
||||||
SpotifyNotFoundError,
|
SpotifyNotFoundError,
|
||||||
UserProfile,
|
UserProfile,
|
||||||
)
|
)
|
||||||
|
|
||||||
from homeassistant.config_entries import ConfigEntry
|
from homeassistant.config_entries import ConfigEntry
|
||||||
from homeassistant.core import HomeAssistant
|
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.helpers.update_coordinator import DataUpdateCoordinator, UpdateFailed
|
||||||
from homeassistant.util import dt as dt_util
|
from homeassistant.util import dt as dt_util
|
||||||
|
|
||||||
@@ -33,6 +36,11 @@ type SpotifyConfigEntry = ConfigEntry[SpotifyData]
|
|||||||
|
|
||||||
UPDATE_INTERVAL = timedelta(seconds=30)
|
UPDATE_INTERVAL = timedelta(seconds=30)
|
||||||
|
|
||||||
|
FREE_API_BLOGPOST = (
|
||||||
|
"https://developer.spotify.com/blog/"
|
||||||
|
"2026-02-06-update-on-developer-access-and-platform-security"
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
@dataclass
|
@dataclass
|
||||||
class SpotifyCoordinatorData:
|
class SpotifyCoordinatorData:
|
||||||
@@ -78,6 +86,19 @@ class SpotifyCoordinator(DataUpdateCoordinator[SpotifyCoordinatorData]):
|
|||||||
"""Set up the coordinator."""
|
"""Set up the coordinator."""
|
||||||
try:
|
try:
|
||||||
self.current_user = await self.client.get_current_user()
|
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:
|
except SpotifyConnectionError as err:
|
||||||
raise UpdateFailed("Error communicating with Spotify API") from err
|
raise UpdateFailed("Error communicating with Spotify API") from err
|
||||||
|
|
||||||
|
|||||||
@@ -8,5 +8,5 @@
|
|||||||
"integration_type": "service",
|
"integration_type": "service",
|
||||||
"iot_class": "cloud_polling",
|
"iot_class": "cloud_polling",
|
||||||
"loggers": ["spotifyaio"],
|
"loggers": ["spotifyaio"],
|
||||||
"requirements": ["spotifyaio==1.0.0"]
|
"requirements": ["spotifyaio==2.0.2"]
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -14,10 +14,10 @@ from spotifyaio import (
|
|||||||
Item,
|
Item,
|
||||||
ItemType,
|
ItemType,
|
||||||
PlaybackState,
|
PlaybackState,
|
||||||
ProductType,
|
|
||||||
RepeatMode as SpotifyRepeatMode,
|
RepeatMode as SpotifyRepeatMode,
|
||||||
Track,
|
Track,
|
||||||
)
|
)
|
||||||
|
from spotifyaio.models import ProductType
|
||||||
from yarl import URL
|
from yarl import URL
|
||||||
|
|
||||||
from homeassistant.components.media_player import (
|
from homeassistant.components.media_player import (
|
||||||
@@ -222,7 +222,7 @@ class SpotifyMediaPlayer(SpotifyEntity, MediaPlayerEntity):
|
|||||||
if item.type == ItemType.EPISODE:
|
if item.type == ItemType.EPISODE:
|
||||||
if TYPE_CHECKING:
|
if TYPE_CHECKING:
|
||||||
assert isinstance(item, Episode)
|
assert isinstance(item, Episode)
|
||||||
return item.show.publisher
|
return item.show.name
|
||||||
|
|
||||||
if TYPE_CHECKING:
|
if TYPE_CHECKING:
|
||||||
assert isinstance(item, Track)
|
assert isinstance(item, Track)
|
||||||
@@ -230,12 +230,10 @@ class SpotifyMediaPlayer(SpotifyEntity, MediaPlayerEntity):
|
|||||||
|
|
||||||
@property
|
@property
|
||||||
@ensure_item
|
@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."""
|
"""Return the media album."""
|
||||||
if item.type == ItemType.EPISODE:
|
if item.type == ItemType.EPISODE:
|
||||||
if TYPE_CHECKING:
|
return None
|
||||||
assert isinstance(item, Episode)
|
|
||||||
return item.show.name
|
|
||||||
|
|
||||||
if TYPE_CHECKING:
|
if TYPE_CHECKING:
|
||||||
assert isinstance(item, Track)
|
assert isinstance(item, Track)
|
||||||
|
|||||||
@@ -12,7 +12,8 @@
|
|||||||
"oauth_timeout": "[%key:common::config_flow::abort::oauth2_timeout%]",
|
"oauth_timeout": "[%key:common::config_flow::abort::oauth2_timeout%]",
|
||||||
"oauth_unauthorized": "[%key:common::config_flow::abort::oauth2_unauthorized%]",
|
"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_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": {
|
"create_entry": {
|
||||||
"default": "Successfully authenticated with Spotify."
|
"default": "Successfully authenticated with Spotify."
|
||||||
@@ -41,6 +42,12 @@
|
|||||||
"message": "[%key:common::exceptions::oauth2_implementation_unavailable::message%]"
|
"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": {
|
"system_health": {
|
||||||
"info": {
|
"info": {
|
||||||
"api_endpoint_reachable": "Spotify API endpoint reachable"
|
"api_endpoint_reachable": "Spotify API endpoint reachable"
|
||||||
|
|||||||
Generated
+1
-1
@@ -2978,7 +2978,7 @@ speak2mary==1.4.0
|
|||||||
speedtest-cli==2.1.3
|
speedtest-cli==2.1.3
|
||||||
|
|
||||||
# homeassistant.components.spotify
|
# homeassistant.components.spotify
|
||||||
spotifyaio==1.0.0
|
spotifyaio==2.0.2
|
||||||
|
|
||||||
# homeassistant.components.sql
|
# homeassistant.components.sql
|
||||||
sqlparse==0.5.5
|
sqlparse==0.5.5
|
||||||
|
|||||||
Generated
+1
-1
@@ -2514,7 +2514,7 @@ speak2mary==1.4.0
|
|||||||
speedtest-cli==2.1.3
|
speedtest-cli==2.1.3
|
||||||
|
|
||||||
# homeassistant.components.spotify
|
# homeassistant.components.spotify
|
||||||
spotifyaio==1.0.0
|
spotifyaio==2.0.2
|
||||||
|
|
||||||
# homeassistant.components.sql
|
# homeassistant.components.sql
|
||||||
sqlparse==0.5.5
|
sqlparse==0.5.5
|
||||||
|
|||||||
@@ -10,7 +10,6 @@ from spotifyaio.models import (
|
|||||||
Artist,
|
Artist,
|
||||||
Devices,
|
Devices,
|
||||||
FollowedArtistResponse,
|
FollowedArtistResponse,
|
||||||
NewReleasesResponse,
|
|
||||||
NewReleasesResponseInner,
|
NewReleasesResponseInner,
|
||||||
PlaybackState,
|
PlaybackState,
|
||||||
PlayedTrackResponse,
|
PlayedTrackResponse,
|
||||||
@@ -142,9 +141,6 @@ def mock_spotify() -> Generator[AsyncMock]:
|
|||||||
client.get_followed_artists.return_value = FollowedArtistResponse.from_json(
|
client.get_followed_artists.return_value = FollowedArtistResponse.from_json(
|
||||||
load_fixture("followed_artists.json", DOMAIN)
|
load_fixture("followed_artists.json", DOMAIN)
|
||||||
).artists.items
|
).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(
|
client.get_devices.return_value = Devices.from_json(
|
||||||
load_fixture("devices.json", DOMAIN)
|
load_fixture("devices.json", DOMAIN)
|
||||||
).devices
|
).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,
|
'width': None,
|
||||||
}),
|
}),
|
||||||
]),
|
]),
|
||||||
'name': 'Spotify Web API Testing playlist',
|
'items': dict({
|
||||||
'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': list([
|
'items': list([
|
||||||
dict({
|
dict({
|
||||||
'added_at': '2015-01-15T12:39:22+00:00',
|
'added_at': '2015-01-15T12:39:22+00:00',
|
||||||
@@ -517,7 +503,6 @@
|
|||||||
}),
|
}),
|
||||||
]),
|
]),
|
||||||
'name': 'Safety Third',
|
'name': 'Safety Third',
|
||||||
'publisher': 'Safety Third ',
|
|
||||||
'show_id': '1Y9ExMgMxoBVrgrfU7u0nD',
|
'show_id': '1Y9ExMgMxoBVrgrfU7u0nD',
|
||||||
'total_episodes': 120,
|
'total_episodes': 120,
|
||||||
'uri': 'spotify:show:1Y9ExMgMxoBVrgrfU7u0nD',
|
'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',
|
'uri': 'spotify:playlist:3cEYpjA9oz9GiPac4AsH4n',
|
||||||
}),
|
}),
|
||||||
}),
|
}),
|
||||||
|
|||||||
@@ -93,17 +93,6 @@
|
|||||||
'thumbnail': None,
|
'thumbnail': None,
|
||||||
'title': 'Top Tracks',
|
'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'>,
|
'children_media_class': <MediaClass.DIRECTORY: 'directory'>,
|
||||||
'media_class': <MediaClass.DIRECTORY: 'directory'>,
|
'media_class': <MediaClass.DIRECTORY: 'directory'>,
|
||||||
@@ -608,44 +597,6 @@
|
|||||||
'title': 'Top Tracks',
|
'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]
|
# name: test_browsing[playlist-spotify:playlist:3cEYpjA9oz9GiPac4AsH4n]
|
||||||
dict({
|
dict({
|
||||||
'can_expand': True,
|
'can_expand': True,
|
||||||
|
|||||||
@@ -116,8 +116,7 @@
|
|||||||
'attributes': ReadOnlyDict({
|
'attributes': ReadOnlyDict({
|
||||||
'entity_picture': '/api/media_player_proxy/media_player.spotify_spotify_1?token=mock-token&cache=cf1e6e1e830f08d3',
|
'entity_picture': '/api/media_player_proxy/media_player.spotify_spotify_1?token=mock-token&cache=cf1e6e1e830f08d3',
|
||||||
'friendly_name': 'Spotify spotify_1',
|
'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_id': 'spotify:episode:3o0RYoo5iOMKSmEbunsbvW',
|
||||||
'media_content_type': <MediaType.PODCAST: 'podcast'>,
|
'media_content_type': <MediaType.PODCAST: 'podcast'>,
|
||||||
'media_duration': 3690,
|
'media_duration': 3690,
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ from http import HTTPStatus
|
|||||||
from unittest.mock import MagicMock, patch
|
from unittest.mock import MagicMock, patch
|
||||||
|
|
||||||
import pytest
|
import pytest
|
||||||
from spotifyaio import SpotifyConnectionError
|
from spotifyaio import SpotifyConnectionError, SpotifyForbiddenError
|
||||||
|
|
||||||
from homeassistant.components.spotify.const import DOMAIN
|
from homeassistant.components.spotify.const import DOMAIN
|
||||||
from homeassistant.config_entries import SOURCE_USER
|
from homeassistant.config_entries import SOURCE_USER
|
||||||
@@ -95,6 +95,13 @@ async def test_full_flow(
|
|||||||
assert result["result"].unique_id == "1112264111"
|
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("current_request_with_host")
|
||||||
@pytest.mark.usefixtures("setup_credentials")
|
@pytest.mark.usefixtures("setup_credentials")
|
||||||
async def test_abort_if_spotify_error(
|
async def test_abort_if_spotify_error(
|
||||||
@@ -102,6 +109,8 @@ async def test_abort_if_spotify_error(
|
|||||||
hass_client_no_auth: ClientSessionGenerator,
|
hass_client_no_auth: ClientSessionGenerator,
|
||||||
aioclient_mock: AiohttpClientMocker,
|
aioclient_mock: AiohttpClientMocker,
|
||||||
mock_spotify: MagicMock,
|
mock_spotify: MagicMock,
|
||||||
|
exception: Exception,
|
||||||
|
reason: str,
|
||||||
) -> None:
|
) -> None:
|
||||||
"""Check Spotify errors causes flow to abort."""
|
"""Check Spotify errors causes flow to abort."""
|
||||||
result = await hass.config_entries.flow.async_init(
|
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"])
|
result = await hass.config_entries.flow.async_configure(result["flow_id"])
|
||||||
|
|
||||||
assert result["type"] is FlowResultType.ABORT
|
assert result["type"] is FlowResultType.ABORT
|
||||||
assert result["reason"] == "connection_error"
|
assert result["reason"] == reason
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.usefixtures("current_request_with_host")
|
@pytest.mark.usefixtures("current_request_with_host")
|
||||||
|
|||||||
@@ -3,10 +3,12 @@
|
|||||||
from unittest.mock import MagicMock, patch
|
from unittest.mock import MagicMock, patch
|
||||||
|
|
||||||
import pytest
|
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.config_entries import ConfigEntryState
|
||||||
from homeassistant.core import HomeAssistant
|
from homeassistant.core import HomeAssistant
|
||||||
|
from homeassistant.helpers import issue_registry as ir
|
||||||
from homeassistant.helpers.config_entry_oauth2_flow import (
|
from homeassistant.helpers.config_entry_oauth2_flow import (
|
||||||
ImplementationUnavailableError,
|
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)
|
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")
|
@pytest.mark.usefixtures("setup_credentials")
|
||||||
async def test_oauth_implementation_not_available(
|
async def test_oauth_implementation_not_available(
|
||||||
hass: HomeAssistant,
|
hass: HomeAssistant,
|
||||||
|
|||||||
@@ -66,7 +66,7 @@ async def test_browse_media_categories(
|
|||||||
|
|
||||||
|
|
||||||
@pytest.mark.parametrize(
|
@pytest.mark.parametrize(
|
||||||
("config_entry_id"), [("01J5TX5A0FF6G5V0QJX6HBC94T"), ("32oesphrnacjcf7vw5bf6odx3")]
|
"config_entry_id", ["01J5TX5A0FF6G5V0QJX6HBC94T", "32oesphrnacjcf7vw5bf6odx3"]
|
||||||
)
|
)
|
||||||
@pytest.mark.usefixtures("setup_credentials")
|
@pytest.mark.usefixtures("setup_credentials")
|
||||||
async def test_browse_media_playlists(
|
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_recently_played", "current_user_recently_played"),
|
||||||
("current_user_top_artists", "current_user_top_artists"),
|
("current_user_top_artists", "current_user_top_artists"),
|
||||||
("current_user_top_tracks", "current_user_top_tracks"),
|
("current_user_top_tracks", "current_user_top_tracks"),
|
||||||
("new_releases", "new_releases"),
|
|
||||||
("playlist", "spotify:playlist:3cEYpjA9oz9GiPac4AsH4n"),
|
("playlist", "spotify:playlist:3cEYpjA9oz9GiPac4AsH4n"),
|
||||||
("album", "spotify:album:3IqzqH6ShrRtie9Yd2ODyG"),
|
("album", "spotify:album:3IqzqH6ShrRtie9Yd2ODyG"),
|
||||||
("artist", "spotify:artist:0TnOYISbd1XYRBk9myaseg"),
|
("artist", "spotify:artist:0TnOYISbd1XYRBk9myaseg"),
|
||||||
@@ -138,13 +137,7 @@ async def test_browsing(
|
|||||||
assert response.as_dict() == snapshot
|
assert response.as_dict() == snapshot
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.parametrize(
|
@pytest.mark.parametrize("media_content_id", ["artist", None])
|
||||||
("media_content_id"),
|
|
||||||
[
|
|
||||||
"artist",
|
|
||||||
None,
|
|
||||||
],
|
|
||||||
)
|
|
||||||
@pytest.mark.usefixtures("setup_credentials")
|
@pytest.mark.usefixtures("setup_credentials")
|
||||||
async def test_invalid_spotify_url(
|
async def test_invalid_spotify_url(
|
||||||
hass: HomeAssistant,
|
hass: HomeAssistant,
|
||||||
|
|||||||
@@ -7,7 +7,6 @@ from freezegun.api import FrozenDateTimeFactory
|
|||||||
import pytest
|
import pytest
|
||||||
from spotifyaio import (
|
from spotifyaio import (
|
||||||
PlaybackState,
|
PlaybackState,
|
||||||
ProductType,
|
|
||||||
RepeatMode as SpotifyRepeatMode,
|
RepeatMode as SpotifyRepeatMode,
|
||||||
SpotifyConnectionError,
|
SpotifyConnectionError,
|
||||||
SpotifyNotFoundError,
|
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")
|
@pytest.mark.usefixtures("setup_credentials")
|
||||||
async def test_restricted_device(
|
async def test_restricted_device(
|
||||||
hass: HomeAssistant,
|
hass: HomeAssistant,
|
||||||
|
|||||||
Reference in New Issue
Block a user