spotifywebapiPython 1.0.152__py3-none-any.whl → 1.0.154__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of spotifywebapiPython might be problematic. Click here for more details.

Files changed (21) hide show
  1. {spotifywebapiPython-1.0.152.dist-info → spotifywebapiPython-1.0.154.dist-info}/METADATA +1 -1
  2. {spotifywebapiPython-1.0.152.dist-info → spotifywebapiPython-1.0.154.dist-info}/RECORD +21 -21
  3. spotifywebapipython/const.py +1 -1
  4. spotifywebapipython/docs/__init__.html +15 -2
  5. spotifywebapipython/docs/index.html +2 -2
  6. spotifywebapipython/docs/search.js +1 -1
  7. spotifywebapipython/docs/spotifywebapipython/const.html +3 -3
  8. spotifywebapipython/docs/spotifywebapipython/saappmessages.html +2 -2
  9. spotifywebapipython/docs/spotifywebapipython/sautils.html +2 -2
  10. spotifywebapipython/docs/spotifywebapipython/spotifyapierror.html +2 -2
  11. spotifywebapipython/docs/spotifywebapipython/spotifyapimessage.html +2 -2
  12. spotifywebapipython/docs/spotifywebapipython/spotifyauthtoken.html +2 -2
  13. spotifywebapipython/docs/spotifywebapipython/spotifyclient.html +68 -9
  14. spotifywebapipython/docs/spotifywebapipython/spotifydiscovery.html +2 -2
  15. spotifywebapipython/docs/spotifywebapipython/spotifywebapiauthenticationerror.html +2 -2
  16. spotifywebapipython/docs/spotifywebapipython/spotifywebapierror.html +2 -2
  17. spotifywebapipython/docs/spotifywebapipython/spotifywebplayertoken.html +2 -2
  18. spotifywebapipython/spotifyclient.py +220 -94
  19. {spotifywebapiPython-1.0.152.dist-info → spotifywebapiPython-1.0.154.dist-info}/LICENSE +0 -0
  20. {spotifywebapiPython-1.0.152.dist-info → spotifywebapiPython-1.0.154.dist-info}/WHEEL +0 -0
  21. {spotifywebapiPython-1.0.152.dist-info → spotifywebapiPython-1.0.154.dist-info}/top_level.txt +0 -0
@@ -10,6 +10,7 @@ from soco.core import (
10
10
  PLAY_MODE_BY_MEANING as SONOS_PLAY_MODE_BY_MEANING,
11
11
  PLAY_MODES as SONOS_PLAY_MODES,
12
12
  )
13
+ from soco.plugins.sharelink import ShareLinkPlugin
13
14
  import time
14
15
  from typing import Tuple, Callable, Union
15
16
  from urllib3 import PoolManager, Timeout, HTTPResponse
@@ -11697,36 +11698,117 @@ class SpotifyClient:
11697
11698
 
11698
11699
  # validations.
11699
11700
  delay = validateDelay(delay, 0.50, 10)
11701
+ if (positionMS is None) or (positionMS < 0):
11702
+ positionMS = 0
11703
+ if (offsetPosition is None) or (offsetPosition < 0):
11704
+ offsetPosition = 0
11700
11705
 
11701
11706
  # resolve / activate the device object if needed.
11702
11707
  scDevice = self._ResolveDeviceObject(deviceId, False)
11703
11708
 
11704
- # build spotify web api request parameters.
11705
- reqData:dict = \
11706
- {
11707
- 'context_uri': contextUri,
11708
- 'position_ms': positionMS
11709
- }
11710
- if offsetUri is not None:
11711
- reqData['offset'] = { 'uri': offsetUri }
11712
- elif offsetPosition is not None:
11713
- reqData['offset'] = { 'position': offsetPosition }
11709
+ # is this a Sonos device?
11710
+ if (scDevice is not None) and (scDevice.IsSonos):
11711
+
11712
+ # trace.
11713
+ _logsi.LogVerbose("Context will be played on Sonos local queue for device: %s" % (scDevice.Title))
11714
+
11715
+ # Sonos cannot handle playing an artist context!
11716
+ # if this is an artist context, then we will get a list of the artist's albums
11717
+ # and add them to the queue.
11718
+ if (contextUri.find(':artist:') > -1):
11719
+ artistId:str = contextUri.replace('spotify:artist:','')
11720
+ #includeGroups:str = 'album,single,appears_on,compilation'
11721
+ includeGroups:str = 'album'
11722
+ _logsi.LogVerbose("Getting ALL albums for artist id: %s" % (artistId))
11723
+ pageObj:AlbumPageSimplified = self.GetArtistAlbums(artistId, includeGroups, limitTotal=75)
11724
+ contextUri:list = []
11725
+ albumSimplified:AlbumSimplified
11726
+ for albumSimplified in pageObj.Items:
11727
+ contextUri.append(albumSimplified.Uri)
11728
+
11729
+ # build a list of all item uri's.
11730
+ # remove any leading / trailing spaces in case user put a space between the items.
11731
+ arrUris:list[str] = None
11732
+ if isinstance(contextUri, list):
11733
+ arrUris = contextUri
11734
+ else:
11735
+ arrUris = contextUri.split(',')
11736
+ for idx in range(0, len(arrUris)):
11737
+ arrUris[idx] = arrUris[idx].strip()
11714
11738
 
11715
- # formulate url parms; will manually add them since we are adding json body as well.
11716
- urlParms:str = ''
11717
- if (scDevice is not None):
11718
- urlParms = '?device_id=%s' % scDevice.Id
11739
+ # get the Sonos Controller player instance.
11740
+ sonosPlayer:SoCo = self.SpotifyConnectDirectory.GetSonosPlayer(scDevice)
11719
11741
 
11720
- # execute spotify web api request.
11721
- msg:SpotifyApiMessage = SpotifyApiMessage(apiMethodName, '/me/player/play{url_parms}'.format(url_parms=urlParms))
11722
- msg.RequestHeaders[self.AuthToken.HeaderKey] = self.AuthToken.HeaderValue
11723
- msg.RequestJson = reqData
11724
- self.MakeRequest('PUT', msg)
11742
+ # clear the Sonos local queue.
11743
+ _logsi.LogVerbose("Issuing command to Sonos device \"%s\": CLEAR_QUEUE" % (scDevice.Name))
11744
+ sonosPlayer.clear_queue()
11745
+
11746
+ # add all context items to the Sonos local queue.
11747
+ sharelink = ShareLinkPlugin(sonosPlayer)
11748
+ for idx in range(0, len(arrUris)):
11749
+
11750
+ # add context to the Sonos local queue.
11751
+ uri = arrUris[idx].strip()
11752
+ _logsi.LogVerbose("Issuing command to Sonos device \"%s\": ADD_SHARE_LINK_TO_QUEUE (uri=%s)" % (scDevice.Name, uri))
11753
+ sharelink.add_share_link_to_queue(uri)
11754
+
11755
+ # if this is the first item added, then start play of the queue as
11756
+ # it may take awhile to load the rest of the items.
11757
+ if (idx == 0):
11758
+
11759
+ # start playing the Sonos local queue.
11760
+ _logsi.LogVerbose("Issuing command to Sonos device \"%s\": PLAY_FROM_QUEUE (index=%s)" % (scDevice.Name, offsetPosition))
11761
+ sonosPlayer.play_from_queue(index=offsetPosition)
11762
+
11763
+ # # give Sonos Controller time to process the change.
11764
+ # if delay > 0:
11765
+ # _logsi.LogVerbose(TRACE_MSG_DELAY_DEVICE_SONOS % delay)
11766
+ # time.sleep(delay)
11767
+
11768
+ # was a track seek position specified?
11769
+ if (positionMS > 0):
11770
+
11771
+ # seek to the position in the track.
11772
+ sonosPosition:str = mediaPositionHMS_fromSeconds(positionMS / 1000) # convert from milliseconds to Sonos H:MM:SS format
11773
+ _logsi.LogVerbose("Issuing command to Sonos device \"%s\": SEEK (position=%s)" % (scDevice.Name, sonosPosition))
11774
+ sonosPlayer.seek(position=sonosPosition)
11775
+
11776
+ # give Sonos Controller time to process the change.
11777
+ if delay > 0:
11778
+ _logsi.LogVerbose(TRACE_MSG_DELAY_DEVICE_SONOS % delay)
11779
+ time.sleep(delay)
11780
+
11781
+ else:
11782
+
11783
+ # trace.
11784
+ _logsi.LogVerbose("Context will be played on the Spotify Connect device via Spotify Web API")
11785
+
11786
+ # build spotify web api request parameters.
11787
+ reqData:dict = \
11788
+ {
11789
+ 'context_uri': contextUri,
11790
+ 'position_ms': positionMS
11791
+ }
11792
+ if offsetUri is not None:
11793
+ reqData['offset'] = { 'uri': offsetUri }
11794
+ else:
11795
+ reqData['offset'] = { 'position': offsetPosition }
11796
+
11797
+ # formulate url parms; will manually add them since we are adding json body as well.
11798
+ urlParms:str = ''
11799
+ if (scDevice is not None):
11800
+ urlParms = '?device_id=%s' % scDevice.Id
11801
+
11802
+ # execute spotify web api request.
11803
+ msg:SpotifyApiMessage = SpotifyApiMessage(apiMethodName, '/me/player/play{url_parms}'.format(url_parms=urlParms))
11804
+ msg.RequestHeaders[self.AuthToken.HeaderKey] = self.AuthToken.HeaderValue
11805
+ msg.RequestJson = reqData
11806
+ self.MakeRequest('PUT', msg)
11725
11807
 
11726
- # give spotify web api time to process the change.
11727
- if delay > 0:
11728
- _logsi.LogVerbose(TRACE_MSG_DELAY_DEVICE % delay)
11729
- time.sleep(delay)
11808
+ # give spotify web api time to process the change.
11809
+ if delay > 0:
11810
+ _logsi.LogVerbose(TRACE_MSG_DELAY_DEVICE % delay)
11811
+ time.sleep(delay)
11730
11812
 
11731
11813
  # process results.
11732
11814
  # no results to process - this is pass or fail.
@@ -11952,6 +12034,9 @@ class SpotifyClient:
11952
12034
 
11953
12035
  # validations.
11954
12036
  delay = validateDelay(delay, 0.50, 10)
12037
+ if (positionMS is None) or (positionMS < 0):
12038
+ positionMS = 0
12039
+ offsetPosition:int = 0
11955
12040
 
11956
12041
  # build a list of all item uri's.
11957
12042
  # remove any leading / trailing spaces in case user put a space between the items.
@@ -11964,50 +12049,84 @@ class SpotifyClient:
11964
12049
  arrUris[idx] = arrUris[idx].strip()
11965
12050
 
11966
12051
  # resolve / activate the device object if needed.
11967
- scDevice = self._ResolveDeviceObject(deviceId, False)
12052
+ scDevice = self._ResolveDeviceObject(deviceId, True)
11968
12053
 
11969
- # left this here in case we want to implement it in the future.
11970
-
11971
- # # are we backfilling the player queue? if so, we can only have 50 uris total.
11972
- # if (backfillUrisPageOffset > -1) and (len(arrUris) < 50):
12054
+ # is this a Sonos device?
12055
+ if (scDevice is not None) and (scDevice.IsSonos):
11973
12056
 
11974
- # # are we playing tracks?
11975
- # if (len(arrUris) > 0) and (SpotifyClient.GetTypeFromUri(arrUris[0]) == 'track'):
11976
-
11977
- # _logsi.LogVerbose("Backfilling player queue items from users top tracks")
12057
+ # trace.
12058
+ _logsi.LogVerbose("Track list will be played on Sonos local queue for device: %s" % (scDevice.Title))
12059
+
12060
+ # get the Sonos Controller player instance.
12061
+ sonosPlayer:SoCo = self.SpotifyConnectDirectory.GetSonosPlayer(scDevice)
12062
+
12063
+ # clear the Sonos local queue.
12064
+ _logsi.LogVerbose("Issuing command to Sonos device \"%s\": CLEAR_QUEUE" % (scDevice.Name))
12065
+ sonosPlayer.clear_queue()
12066
+
12067
+ # add all track items to the Sonos local queue.
12068
+ sharelink = ShareLinkPlugin(sonosPlayer)
12069
+ for idx in range(0, len(arrUris)):
12070
+
12071
+ # add track to the Sonos local queue.
12072
+ uri = arrUris[idx].strip()
12073
+ _logsi.LogVerbose("Issuing command to Sonos device \"%s\": ADD_SHARE_LINK_TO_QUEUE (uri=%s)" % (scDevice.Name, uri))
12074
+ sharelink.add_share_link_to_queue(uri)
12075
+
12076
+ # if this is the first item added, then start play of the queue as
12077
+ # it may take awhile to load the rest of the items.
12078
+ if (idx == 0):
12079
+
12080
+ # start playing the Sonos local queue.
12081
+ _logsi.LogVerbose("Issuing command to Sonos device \"%s\": PLAY_FROM_QUEUE (index=%s)" % (scDevice.Name, offsetPosition))
12082
+ sonosPlayer.play_from_queue(index=offsetPosition)
11978
12083
 
11979
- # # get list of users top tracks (50 max items).
11980
- # pageObj:TrackPage = self.GetUsersTopTracks(timeRange='medium_term', offset=backfillUrisPageOffset, limit=(50 - len(arrUris)), sortResult=False)
12084
+ # # give Sonos Controller time to process the change.
12085
+ # if delay > 0:
12086
+ # _logsi.LogVerbose(TRACE_MSG_DELAY_DEVICE_SONOS % delay)
12087
+ # time.sleep(delay)
12088
+
12089
+ # was a track seek position specified?
12090
+ if (positionMS > 0):
12091
+
12092
+ # seek to the position in the track.
12093
+ sonosPosition:str = mediaPositionHMS_fromSeconds(positionMS / 1000) # convert from milliseconds to Sonos H:MM:SS format
12094
+ _logsi.LogVerbose("Issuing command to Sonos device \"%s\": SEEK (position=%s)" % (scDevice.Name, sonosPosition))
12095
+ sonosPlayer.seek(position=sonosPosition)
11981
12096
 
11982
- # # add them to the player queue; no need to validate the device id, since we
11983
- # # know there is an active device id by this point.
11984
- # item:Track
11985
- # for item in pageObj.Items:
11986
- # arrUris.append(item.Uri)
12097
+ # give Sonos Controller time to process the change.
12098
+ if delay > 0:
12099
+ _logsi.LogVerbose(TRACE_MSG_DELAY_DEVICE_SONOS % delay)
12100
+ time.sleep(delay)
11987
12101
 
11988
- # build spotify web api request parameters.
11989
- reqData:dict = \
11990
- {
11991
- 'position_ms': positionMS
11992
- }
11993
- if uris is not None:
11994
- reqData['uris'] = arrUris
12102
+ else:
11995
12103
 
11996
- # formulate url parms; will manually add them since we are adding json body as well.
11997
- urlParms:str = ''
11998
- if (scDevice is not None):
11999
- urlParms = '?device_id=%s' % scDevice.Id
12104
+ # trace.
12105
+ _logsi.LogVerbose("Track list will be played on the Spotify Connect device via Spotify Web API")
12000
12106
 
12001
- # execute spotify web api request.
12002
- msg:SpotifyApiMessage = SpotifyApiMessage(apiMethodName, '/me/player/play{url_parms}'.format(url_parms=urlParms))
12003
- msg.RequestHeaders[self.AuthToken.HeaderKey] = self.AuthToken.HeaderValue
12004
- msg.RequestJson = reqData
12005
- self.MakeRequest('PUT', msg)
12107
+ # build spotify web api request parameters.
12108
+ reqData:dict = \
12109
+ {
12110
+ 'position_ms': positionMS
12111
+ }
12112
+ if uris is not None:
12113
+ reqData['uris'] = arrUris
12114
+
12115
+ # formulate url parms; will manually add them since we are adding json body as well.
12116
+ urlParms:str = ''
12117
+ if (scDevice is not None):
12118
+ urlParms = '?device_id=%s' % scDevice.Id
12119
+
12120
+ # execute spotify web api request.
12121
+ msg:SpotifyApiMessage = SpotifyApiMessage(apiMethodName, '/me/player/play{url_parms}'.format(url_parms=urlParms))
12122
+ msg.RequestHeaders[self.AuthToken.HeaderKey] = self.AuthToken.HeaderValue
12123
+ msg.RequestJson = reqData
12124
+ self.MakeRequest('PUT', msg)
12006
12125
 
12007
- # give spotify web api time to process the change.
12008
- if delay > 0:
12009
- _logsi.LogVerbose(TRACE_MSG_DELAY_DEVICE % delay)
12010
- time.sleep(delay)
12126
+ # give spotify web api time to process the change.
12127
+ if delay > 0:
12128
+ _logsi.LogVerbose(TRACE_MSG_DELAY_DEVICE % delay)
12129
+ time.sleep(delay)
12011
12130
 
12012
12131
  # process results.
12013
12132
  # no results to process - this is pass or fail.
@@ -12874,7 +12993,7 @@ class SpotifyClient:
12874
12993
  refreshDeviceList:bool=True,
12875
12994
  forceActivateDevice:bool=True,
12876
12995
  deviceIdFrom:str=None,
12877
- ) -> None:
12996
+ ) -> SpotifyConnectDevice:
12878
12997
  """
12879
12998
  Transfer playback to a new Spotify Connect device and optionally begin playback.
12880
12999
 
@@ -12916,6 +13035,9 @@ class SpotifyClient:
12916
13035
  for this argument.
12917
13036
  Examples are `0d1841b0976bae2a3a310dd74c0f3df354899bc8`, `Office`, `*`, None.
12918
13037
 
13038
+ Returns:
13039
+ A `SpotifyConnectDevice` object for the device where playback was transferred to.
13040
+
12919
13041
  Raises:
12920
13042
  SpotifyWebApiError:
12921
13043
  If the Spotify Web API request was for a non-authorization service
@@ -13020,14 +13142,14 @@ class SpotifyClient:
13020
13142
  sonosPlayer.play()
13021
13143
  wasCmdIssued = True
13022
13144
 
13023
- # give SoCo api time to process the change.
13145
+ # give Sonos Controller time to process the change.
13024
13146
  if (wasCmdIssued) and (delay > 0):
13025
13147
  _logsi.LogVerbose(TRACE_MSG_DELAY_DEVICE_SONOS % delay)
13026
13148
  time.sleep(delay)
13027
13149
 
13028
13150
  # trace.
13029
13151
  _logsi.LogVerbose("Sonos device detected (IsRestricted); bypassing call to Spotify Web API Transfer Playback endpoint")
13030
- return
13152
+ return scDevice
13031
13153
 
13032
13154
  # before transferring playback to the device, we first need to check to see if anything
13033
13155
  # is currently playing. if nothing is playing then we have to start something, otherwise
@@ -13039,48 +13161,52 @@ class SpotifyClient:
13039
13161
 
13040
13162
  # if nothing is playing, then start playing track favorites instead of transferring playback.
13041
13163
  _logsi.LogVerbose("Nothing is currently playing on Spotify Connect device %s; playing track favorites on selected device instead of transferring playback" % (scDevice.Title))
13042
- tracks:TrackPageSaved = self.PlayerMediaPlayTrackFavorites(scDevice, False, limitTotal=20)
13043
-
13044
- return
13164
+ self.PlayerMediaPlayTrackFavorites(scDevice, False, limitTotal=20)
13165
+ return scDevice
13045
13166
 
13046
- # if resolved device is already active, then there is nothing to do.
13167
+ # is the resolved device already active?
13047
13168
  if (scActiveDevice.Name == scDevice.Name):
13048
- _logsi.LogVerbose("Spotify Connect device %s is already active; no need to re-activate" % (scDevice.Title))
13049
- return
13050
13169
 
13051
- # build spotify web api request parameters.
13052
- reqData:dict = \
13053
- {
13054
- 'device_ids': [scDevice.Id]
13055
- }
13056
- if (play is not None):
13057
- reqData['play'] = play
13170
+ # trace.
13171
+ _logsi.LogVerbose("Spotify Connect device %s is already active; no need to transfer playback" % (scDevice.Title))
13058
13172
 
13059
- # execute spotify web api request.
13060
- msg:SpotifyApiMessage = SpotifyApiMessage(apiMethodName, '/me/player')
13061
- msg.RequestHeaders[self.AuthToken.HeaderKey] = self.AuthToken.HeaderValue
13062
- msg.RequestJson = reqData
13063
- self.MakeRequest('PUT', msg)
13173
+ else:
13174
+
13175
+ # build spotify web api request parameters.
13176
+ reqData:dict = \
13177
+ {
13178
+ 'device_ids': [scDevice.Id]
13179
+ }
13180
+ if (play is not None):
13181
+ reqData['play'] = play
13182
+
13183
+ # execute spotify web api request.
13184
+ msg:SpotifyApiMessage = SpotifyApiMessage(apiMethodName, '/me/player')
13185
+ msg.RequestHeaders[self.AuthToken.HeaderKey] = self.AuthToken.HeaderValue
13186
+ msg.RequestJson = reqData
13187
+ self.MakeRequest('PUT', msg)
13064
13188
 
13065
- # give spotify web api time to process the change.
13066
- if delay > 0:
13067
- _logsi.LogVerbose(TRACE_MSG_DELAY_DEVICE % delay)
13068
- time.sleep(delay)
13189
+ # give spotify web api time to process the change.
13190
+ if delay > 0:
13191
+ _logsi.LogVerbose(TRACE_MSG_DELAY_DEVICE % delay)
13192
+ time.sleep(delay)
13069
13193
 
13070
- # resume play if play=True was specified.
13194
+ # pause / resume play based on `play` argument specified.
13071
13195
  # sometimes the `play` argument to the Spotify Web API transfer playback command
13072
- # is not honored, so we do a resume here to ensure it's playing.
13073
- if (play):
13074
- try:
13196
+ # is not honored, so we do a pause / resume here to ensure it's correct.
13197
+ try:
13198
+ if (play):
13075
13199
  _logsi.LogVerbose("Resuming play on device, in case it's not already playing")
13076
13200
  self.PlayerMediaResume(scDevice)
13077
- except:
13078
- # sometimes the resume will raise a "Restriction violated" error - just ignore it.
13079
- pass
13201
+ else:
13202
+ _logsi.LogVerbose("Pausing play on device, in case it's not already paused")
13203
+ self.PlayerMediaPause(scDevice)
13204
+ except:
13205
+ # in case a "Restriction violated" error is raised; just ignore it.
13206
+ pass
13080
13207
 
13081
- # process results.
13082
- # no results to process - this is pass or fail.
13083
- return
13208
+ # return target player device instance.
13209
+ return scDevice
13084
13210
 
13085
13211
  except SpotifyApiError: raise # pass handled exceptions on thru
13086
13212
  except SpotifyWebApiError: raise # pass handled exceptions on thru