Skip to content

Commit

Permalink
[ 1.0.35 ] * Updated underlying spotifywebapiPython package require…
Browse files Browse the repository at this point in the history
…ment to version 1.0.68.
  • Loading branch information
thlucas1 committed Jun 26, 2024
1 parent 1a9e607 commit 720f4ac
Show file tree
Hide file tree
Showing 4 changed files with 151 additions and 88 deletions.
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,10 @@ Change are listed in reverse chronological order (newest to oldest).

<span class="changelog">

###### [ 1.0.35 ] - 2024/06/26

* Updated underlying `spotifywebapiPython` package requirement to version 1.0.68.

###### [ 1.0.34 ] - 2024/06/25

* Updated underlying `spotifywebapiPython` package requirement to version 1.0.67.
Expand Down
4 changes: 2 additions & 2 deletions custom_components/spotifyplus/manifest.json
Original file line number Diff line number Diff line change
Expand Up @@ -17,10 +17,10 @@
"requests>=2.31.0",
"requests_oauthlib>=1.3.1",
"smartinspectPython>=3.0.33",
"spotifywebapiPython>=1.0.67",
"spotifywebapiPython>=1.0.68",
"urllib3>=1.21.1,<1.27",
"zeroconf>=0.132.2"
],
"version": "1.0.34",
"version": "1.0.35",
"zeroconf": [ "_spotify-connect._tcp.local." ]
}
229 changes: 144 additions & 85 deletions custom_components/spotifyplus/media_player.py
Original file line number Diff line number Diff line change
Expand Up @@ -550,11 +550,8 @@ def media_play(self) -> None:
self._attr_state = MediaPlayerState.PLAYING
self.schedule_update_ha_state(force_refresh=False)

# verify that a spotify connect player device is active.
deviceId:str = self._VerifyDeviceActive()

# resume playback.
self.data.spotifyClient.PlayerMediaResume(deviceId)
# resume playback (use currently active player).
self.data.spotifyClient.PlayerMediaResume()


@spotify_exception_handler
Expand All @@ -566,35 +563,26 @@ def media_pause(self) -> None:
self._attr_state = MediaPlayerState.PAUSED
self.schedule_update_ha_state(force_refresh=False)

# verify that a spotify connect player device is active.
deviceId:str = self._VerifyDeviceActive()

# pause playback.
self.data.spotifyClient.PlayerMediaPause(deviceId)
# pause playback (use currently active player).
self.data.spotifyClient.PlayerMediaPause()


@spotify_exception_handler
def media_previous_track(self) -> None:
""" Skip to previous track. """
_logsi.LogVerbose(STAppMessages.MSG_MEDIAPLAYER_SERVICE, self.name, "media_previous_track")

# verify that a spotify connect player device is active.
deviceId:str = self._VerifyDeviceActive()

# skip to previous track.
self.data.spotifyClient.PlayerMediaSkipPrevious(deviceId)
# skip to previous track (use currently active player).
self.data.spotifyClient.PlayerMediaSkipPrevious()


@spotify_exception_handler
def media_next_track(self) -> None:
""" Skip to next track. """
_logsi.LogVerbose(STAppMessages.MSG_MEDIAPLAYER_SERVICE, self.name, "media_next_track")

# verify that a spotify connect player device is active.
deviceId:str = self._VerifyDeviceActive()

# skip to next track.
self.data.spotifyClient.PlayerMediaSkipNext(deviceId)
# skip to next track (use currently active player).
self.data.spotifyClient.PlayerMediaSkipNext()


@spotify_exception_handler
Expand All @@ -607,11 +595,8 @@ def media_seek(self, position: float) -> None:
self._attr_media_position_updated_at = utcnow()
self.schedule_update_ha_state(force_refresh=False)

# verify that a spotify connect player device is active.
deviceId:str = self._VerifyDeviceActive()

# seek to track position.
self.data.spotifyClient.PlayerMediaSeek(int(position * 1000), deviceId)
# seek to track position (use currently active player).
self.data.spotifyClient.PlayerMediaSeek(int(position * 1000))


@spotify_exception_handler
Expand Down Expand Up @@ -659,28 +644,25 @@ def play_media(self, media_type: MediaType | str, media_id: str, **kwargs: Any)
self.turn_on()
self._isInCommandEvent = True # turn "in a command event" indicator back on.

# verify that a spotify connect player device is active.
deviceId:str = self._VerifyDeviceActive()

# spotify can't handle URI's with query strings or anchors
# yet, they do generate those types of URI in their official clients.
media_id:str = str(URL(media_id).with_query(None).with_fragment(None))

# is this an enqueue request?
if enqueue is not None:

_logsi.LogVerbose("Enqueue command received for this play media request: '%s', deviceId='%s'" % (enqueue, deviceId))
_logsi.LogVerbose("Enqueue command received for this play media request: '%s'" % (enqueue))

if enqueue == MediaPlayerEnqueue.ADD:
# add to queue request.
_logsi.LogVerbose("Adding uri to Spotify Player Queue: '%s', deviceId='%s'" % (media_id, deviceId))
self.data.spotifyClient.AddPlayerQueueItem(media_id, deviceId)
_logsi.LogVerbose("Adding uri to Spotify Player Queue: '%s'" % (media_id))
self.data.spotifyClient.AddPlayerQueueItem(media_id)
return

elif enqueue == MediaPlayerEnqueue.NEXT:
# play next request.
_logsi.LogVerbose("Playing next item in the player queue: deviceId='%s'" % (deviceId))
self.data.spotifyClient.PlayerMediaSkipNext(deviceId)
_logsi.LogVerbose("Playing next item in the player queue")
self.data.spotifyClient.PlayerMediaSkipNext()
return

elif enqueue == MediaPlayerEnqueue.PLAY:
Expand All @@ -694,6 +676,12 @@ def play_media(self, media_type: MediaType | str, media_id: str, **kwargs: Any)
else:
_logsi.LogWarning("Spotify Connect Player does not support '%s' enqueue requests; request ignored" % enqueue)
return

# if no source selected, then use the default source.
deviceId:str = self._attr_source
if deviceId is None:
deviceId = "*"
deviceId = self._GetDefaultDeviceOption(deviceId)

# start play based upon the media type.
if media_type in {MediaType.TRACK, MediaType.EPISODE, MediaType.MUSIC}:
Expand Down Expand Up @@ -732,15 +720,17 @@ def select_source(self, source: str) -> None:
self.turn_on()
wasTurnedOn = True
# immediately pause (if not already) to ensure media starts playing on the correct device after transfer.
self.media_pause()
#self.media_pause()
self._isInCommandEvent = True # turn "in a command event" indicator back on.

if source is not None:

# transfer playback to the specified device.
self.data.spotifyClient.PlayerTransferPlayback(source, (self.state == MediaPlayerState.PLAYING))

# if player was turned on, then resume play on the new device.
# transfer playback to the specified device if the source was changed.
if (source != self._attr_source):
self.data.spotifyClient.PlayerTransferPlayback(source, (self.state == MediaPlayerState.PLAYING))
self._attr_source = source

# if player was turned on, then start play.
if wasTurnedOn:
self.media_play()

Expand All @@ -754,11 +744,8 @@ def set_shuffle(self, shuffle: bool) -> None:
self._attr_shuffle = shuffle
self.schedule_update_ha_state(force_refresh=False)

# verify that a spotify connect player device is active.
deviceId:str = self._VerifyDeviceActive()

# set shuffle mode.
self.data.spotifyClient.PlayerSetShuffleMode(shuffle, deviceId)
# set shuffle mode (use currently active player).
self.data.spotifyClient.PlayerSetShuffleMode(shuffle)


@spotify_exception_handler
Expand All @@ -772,11 +759,8 @@ def set_repeat(self, repeat: RepeatMode) -> None:
self._attr_repeat = repeat
self.schedule_update_ha_state(force_refresh=False)

# verify that a spotify connect player device is active.
deviceId:str = self._VerifyDeviceActive()

# set repeat mode.
self.data.spotifyClient.PlayerSetRepeatMode(REPEAT_MODE_MAPPING_TO_SPOTIFY[repeat], deviceId)
# set repeat mode (use currently active player).
self.data.spotifyClient.PlayerSetRepeatMode(REPEAT_MODE_MAPPING_TO_SPOTIFY[repeat])


@spotify_exception_handler
Expand All @@ -792,11 +776,8 @@ def set_volume_level(self, volume: float) -> None:
self._attr_volume_level = volume
self.schedule_update_ha_state(force_refresh=False)

# verify that a spotify connect player device is active.
deviceId:str = self._VerifyDeviceActive()

# set volume.
self.data.spotifyClient.PlayerSetVolume(int(volume * 100), deviceId)
# set volume (use currently active player).
self.data.spotifyClient.PlayerSetVolume(int(volume * 100))


@spotify_exception_handler
Expand All @@ -819,7 +800,7 @@ def turn_off(self) -> None:
_logsi.LogObject(SILevel.Verbose, "'%s': Spotify player state at power off" % self.name, self._playerState, excludeNonPublic=True)

# if playing, then pause playback.
if (self._playerState.IsPlaying) and (self.data.spotifyClient.UserProfile.Product == 'premium'):
if (self._playerState.IsPlaying) and (self.data.spotifyClient.UserProfile.IsProductPremium):
_logsi.LogVerbose("'%s': Pausing Spotify playback on deviceId: %s" % (self.name, self._playerState.Device.Id))
self.data.spotifyClient.PlayerMediaPause(self._playerState.Device.Id)

Expand Down Expand Up @@ -1252,33 +1233,111 @@ def _GetUserProfilePartialDictionary(self, userProfile:UserProfile) -> dict:
}


def _VerifyDeviceActive(self, deviceId:str=None) -> str:
# def _VerifySourceDeviceActive(self, sourceName:str=None) -> str:
# """
# Verifies that the Spotify Player is active for the selected source.

# Args:
# sourceName (str):
# The media player source name to verify.

# Returns:
# A device ID if one could be resolved; otherwise, the device name.
# """
# apiMethodName:str = '_VerifySourceDeviceActive'
# apiMethodParms:SIMethodParmListContext = None

# try:

# # trace.
# apiMethodParms = _logsi.EnterMethodParmList(SILevel.Debug, apiMethodName)
# apiMethodParms.AppendKeyValue("sourceName", sourceName)
# _logsi.LogMethodParmList(SILevel.Verbose, "Verifying sourceName '%s' is the active Spotify Player" % sourceName, apiMethodParms)

# deviceId:str = sourceName
# playerState:PlayerPlayState = None

# # if source name was not specified, then try to resolve a default to use.
# if (sourceName is None):

# # get currently active Spotify Player device.
# _logsi.LogVerbose("'%s': Retrieving Spotify Player playback state and device info" % (self.name))
# playerState = self.data.spotifyClient.GetPlayerPlaybackState()

# if (playerState.Device.IsActive):
# # if an active Spotify Player device was found, then use it.
# sourceName = playerState.Device.Name
# deviceId = playerState.Device.Id
# _logsi.LogVerbose("'%s': Device was verified: '%s' - active Spotify Player device" % (self.name, sourceName))
# return deviceId

# elif self._attr_source is not None:
# # otherwise, use the media_player source if one was selected.
# sourceName = self._attr_source
# _logsi.LogVerbose("'%s': Default device was selected: '%s' - media_player source device" % (self.name, sourceName))

# elif self._attr_source_list is not None:
# # otherwise, use the first entry in the media_player source_list if one was specified.
# sourceName = self._attr_source_list[0]
# _logsi.LogVerbose("'%s': Default device was selected: '%s' - first media_player source_list device" % (self.name, sourceName))

# else:
# # otherwise, the operation will probably fail.
# _logsi.LogVerbose("'%s': Default device could not be resolved; operation will probably fail" % (self.name))

# # get currently active Spotify Player device.
# # if it matches the specified sourceName then we will consider it verified.
# if playerState is None:
# _logsi.LogVerbose("'%s': Retrieving Spotify Player playback state and device info" % (self.name))
# playerState = self.data.spotifyClient.GetPlayerPlaybackState()

# # if an active Spotify Player device was found, then use it.
# if (playerState.Device.IsActive):
# if (playerState.Device.Name.lower() == sourceName.lower()):
# _logsi.LogVerbose("'%s': Active Spotify Player device '%s' matched the selected source '%s' - verified" % (self.name, playerState.Device.Id, sourceName))
# return playerState.Device.Id

# # at this point, the active Spotify Player device does not match the specified device,
# # which means that someone else took control of the device OR the device went to sleep
# # and dropped out of the Spotify Connect device list.
# # transfer playback to the specified device to make it the active player.
# _logsi.LogVerbose("'%s': Active Spotify Player device '%s' does not match sourceName '%s'; transferring playback to selected source" % (self.name, playerState.Device.Name or '', sourceName))
# self.data.spotifyClient.PlayerTransferPlayback(sourceName, (self.state == MediaPlayerState.PLAYING))

# # return the sourceName.
# return deviceId

# finally:

# # trace.
# _logsi.LeaveMethod(SILevel.Debug, apiMethodName)


def _GetDefaultDeviceOption(
self,
deviceId:str=None,
) -> str:
"""
Verifies that a device id was specified. If not supplied, the user's currently
active device is the target. If no device is active (or an "*" is specified), then
the SpotifyPlus default device is activated.
Returns the SpotifyPlus defult device option if an "*" was specified for the `deviceId` argument.
Args:
deviceId (str):
The Spotify Player device id to verify.
Returns:
One of the following values:
- if the `deviceId` argument is not an "*", then the argument value is returned as-is.
- otherwise, if a SpotifyPlus default device option was configured then it's deviceId value is returned.
- otherwise, None is returned to indicate that the active player will be used.
"""
# if a device was not specified, then we will try to use the currently active
# spotify connect player device.
if deviceId is None:

_logsi.LogVerbose("'%s': Verifying active Spotify Connect device" % self.name)
result:PlayerPlayState = self.data.spotifyClient.PlayerVerifyDeviceDefault(PlayerDevice.GetIdFromSelectItem(self.data.OptionDeviceDefault), False)
if result.Device.Id is not None:
_logsi.LogVerbose("'%s': Using SpotifyPlus active device: '%s'(%s)" % (self.name, result.Device.Id, result.Device.Name))
deviceId = result.Device.Id

# if there was no active device, or the default device was specified (e.g. "*"),
# then we will force the configuration option default device to be used.
if deviceId is None or deviceId == "*":

_logsi.LogVerbose("'%s': Using SpotifyPlus default device: '%s'" % (self.name, self.data.OptionDeviceDefault))
deviceId = PlayerDevice.GetIdFromSelectItem(self.data.OptionDeviceDefault)
self._attr_source = PlayerDevice.GetNameFromSelectItem(self.data.OptionDeviceDefault)

# transfer playback to the device to ensure it's active.
_logsi.LogVerbose("'%s': Transferring playback to SpotifyPlus default device '%s' (%s)" % (self.name, self._attr_source, deviceId))
self.data.spotifyClient.PlayerTransferPlayback(deviceId, (self.state == MediaPlayerState.PLAYING))
if (deviceId == "*"):

if (self.data.OptionDeviceDefault is not None):
deviceId = PlayerDevice.GetIdFromSelectItem(self.data.OptionDeviceDefault)
deviceName = PlayerDevice.GetNameFromSelectItem(self.data.OptionDeviceDefault)
_logsi.LogVerbose("'%s': SpotifyPlus default device will be used: '%s' (%s)" % (self.name, deviceName, deviceId))
else:
_logsi.LogVerbose("'%s': SpotifyPlus default device was not configured; will use currently active Spotify Player device" % (self.name))
deviceId = None

return deviceId

Expand Down Expand Up @@ -3088,8 +3147,8 @@ def service_spotify_player_media_play_context(self,
apiMethodParms.AppendKeyValue("deviceId", deviceId)
_logsi.LogMethodParmList(SILevel.Verbose, "Spotify Player Media Play Context Service", apiMethodParms)

# verify that a spotify connect player device is active.
deviceId = self._VerifyDeviceActive(deviceId)
# see if default device option was specified (e.g. deviceId="*").
deviceId = self._GetDefaultDeviceOption(deviceId)

# start playing one or more tracks of the specified context on a Spotify Connect device.
_logsi.LogVerbose("Playing Media Context on device")
Expand Down Expand Up @@ -3146,8 +3205,8 @@ def service_spotify_player_media_play_track_favorites(self,
apiMethodParms.AppendKeyValue("delay", delay)
_logsi.LogMethodParmList(SILevel.Verbose, "Spotify Player Media Play Favorite Tracks Service", apiMethodParms)

# verify that a spotify connect player device is active.
deviceId = self._VerifyDeviceActive(deviceId)
# see if default device option was specified (e.g. deviceId="*").
deviceId = self._GetDefaultDeviceOption(deviceId)

# start playing track favorites on the specified Spotify Connect device.
_logsi.LogVerbose("Playing Media Favorite Tracks on device")
Expand Down Expand Up @@ -3208,8 +3267,8 @@ def service_spotify_player_media_play_tracks(self,
apiMethodParms.AppendKeyValue("deviceId", deviceId)
_logsi.LogMethodParmList(SILevel.Verbose, "Spotify Player Media Play Tracks Service", apiMethodParms)

# verify that a spotify connect player device is active.
deviceId = self._VerifyDeviceActive(deviceId)
# see if default device option was specified (e.g. deviceId="*").
deviceId = self._GetDefaultDeviceOption(deviceId)

# start playing one or more tracks on the specified Spotify Connect device.
_logsi.LogVerbose("Playing Media Tracks on device")
Expand Down
2 changes: 1 addition & 1 deletion requirements.txt
Original file line number Diff line number Diff line change
Expand Up @@ -3,4 +3,4 @@ colorlog==6.7.0
homeassistant==2024.5.0
ruff==0.1.3
smartinspectPython>=3.0.33
spotifywebapiPython>=1.0.67
spotifywebapiPython>=1.0.68

0 comments on commit 720f4ac

Please sign in to comment.