spotifywebapiPython 1.0.244__py3-none-any.whl → 1.0.251__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.
- spotifywebapipython/const.py +1 -1
- spotifywebapipython/docs/__init__.html +49 -2
- spotifywebapipython/docs/index.html +5 -2
- spotifywebapipython/docs/search.js +1 -1
- spotifywebapipython/docs/spotifywebapipython/const.html +3 -3
- spotifywebapipython/docs/spotifywebapipython/saappmessages.html +2 -2
- spotifywebapipython/docs/spotifywebapipython/sautils.html +2 -2
- spotifywebapipython/docs/spotifywebapipython/spotifyapierror.html +2 -2
- spotifywebapipython/docs/spotifywebapipython/spotifyapimessage.html +2 -2
- spotifywebapipython/docs/spotifywebapipython/spotifyauthtoken.html +2 -2
- spotifywebapipython/docs/spotifywebapipython/spotifyclient.html +235 -4
- spotifywebapipython/docs/spotifywebapipython/spotifydiscovery.html +2 -2
- spotifywebapipython/docs/spotifywebapipython/spotifywebapiauthenticationerror.html +2 -2
- spotifywebapipython/docs/spotifywebapipython/spotifywebapierror.html +2 -2
- spotifywebapipython/docs/spotifywebapipython/spotifywebplayertoken.html +2 -2
- spotifywebapipython/models/__init__.py +2 -0
- spotifywebapipython/models/episodesimplified.py +41 -0
- spotifywebapipython/models/imagepalettecolors.py +111 -0
- spotifywebapipython/models/imagevibrantcolors.py +2 -0
- spotifywebapipython/models/pageobject.py +1 -0
- spotifywebapipython/spotifyclient.py +219 -6
- spotifywebapipython/spotifyconnect/spotifyconnectdirectorytask.py +4 -5
- spotifywebapipython/vibrant/__init__.py +2 -0
- spotifywebapipython/vibrant/colorthieffast.py +626 -0
- {spotifywebapipython-1.0.244.dist-info → spotifywebapipython-1.0.251.dist-info}/METADATA +3 -2
- {spotifywebapipython-1.0.244.dist-info → spotifywebapipython-1.0.251.dist-info}/RECORD +29 -27
- {spotifywebapipython-1.0.244.dist-info → spotifywebapipython-1.0.251.dist-info}/LICENSE +0 -0
- {spotifywebapipython-1.0.244.dist-info → spotifywebapipython-1.0.251.dist-info}/WHEEL +0 -0
- {spotifywebapipython-1.0.244.dist-info → spotifywebapipython-1.0.251.dist-info}/top_level.txt +0 -0
|
@@ -186,6 +186,18 @@ class EpisodeSimplified:
|
|
|
186
186
|
return self._Id
|
|
187
187
|
|
|
188
188
|
|
|
189
|
+
@property
|
|
190
|
+
def IdOrigin(self) -> str:
|
|
191
|
+
"""
|
|
192
|
+
The origin Spotify ID for the episode.
|
|
193
|
+
|
|
194
|
+
The `Id` value is always returned, as linkfrom details do not exist for episodes.
|
|
195
|
+
|
|
196
|
+
This is a helper property, and is not part of the Spotify Web API specification.
|
|
197
|
+
"""
|
|
198
|
+
return self._Id
|
|
199
|
+
|
|
200
|
+
|
|
189
201
|
@property
|
|
190
202
|
def Images(self) -> list[ImageObject]:
|
|
191
203
|
"""
|
|
@@ -211,6 +223,18 @@ class EpisodeSimplified:
|
|
|
211
223
|
return self._IsExternallyHosted
|
|
212
224
|
|
|
213
225
|
|
|
226
|
+
@property
|
|
227
|
+
def IsLinkedFrom(self) -> bool:
|
|
228
|
+
"""
|
|
229
|
+
Whether or not the episode is linked from another episode.
|
|
230
|
+
|
|
231
|
+
False is always returned, as linkfrom details do not exist for episodes.
|
|
232
|
+
|
|
233
|
+
This is a helper property, and is not part of the Spotify Web API specification.
|
|
234
|
+
"""
|
|
235
|
+
return False
|
|
236
|
+
|
|
237
|
+
|
|
214
238
|
@property
|
|
215
239
|
def IsPlayable(self) -> bool:
|
|
216
240
|
"""
|
|
@@ -294,6 +318,18 @@ class EpisodeSimplified:
|
|
|
294
318
|
return self._Uri
|
|
295
319
|
|
|
296
320
|
|
|
321
|
+
@property
|
|
322
|
+
def UriOrigin(self) -> str:
|
|
323
|
+
"""
|
|
324
|
+
The origin Spotify URI for the episode.
|
|
325
|
+
|
|
326
|
+
The `Uri` value is always returned, as linkfrom details do not exist for episodes.
|
|
327
|
+
|
|
328
|
+
This is a helper property, and is not part of the Spotify Web API specification.
|
|
329
|
+
"""
|
|
330
|
+
return self._Uri
|
|
331
|
+
|
|
332
|
+
|
|
297
333
|
def ToDictionary(self) -> dict:
|
|
298
334
|
"""
|
|
299
335
|
Returns a dictionary representation of the class.
|
|
@@ -332,6 +368,9 @@ class EpisodeSimplified:
|
|
|
332
368
|
'resume_point': resumePoint,
|
|
333
369
|
'type': self._Type,
|
|
334
370
|
'uri': self._Uri,
|
|
371
|
+
'is_linked_from': self.IsLinkedFrom,
|
|
372
|
+
'id_origin': self.IdOrigin,
|
|
373
|
+
'uri_origin': self.UriOrigin,
|
|
335
374
|
}
|
|
336
375
|
return result
|
|
337
376
|
|
|
@@ -367,4 +406,6 @@ class EpisodeSimplified:
|
|
|
367
406
|
if self._Restrictions is not None: msg = '%s\n %s' % (msg, str(self._Restrictions))
|
|
368
407
|
if self._ResumePoint is not None: msg = '%s\n %s' % (msg, str(self._ResumePoint))
|
|
369
408
|
if self._Type is not None: msg = '%s\n Type="%s"' % (msg, str(self._Type))
|
|
409
|
+
if self.IdOrigin is not None: msg = '%s\n IdOrigin="%s"' % (msg, str(self.IdOrigin))
|
|
410
|
+
if self.UriOrigin is not None: msg = '%s\n UriOrigin="%s"' % (msg, str(self.UriOrigin))
|
|
370
411
|
return msg
|
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
# external package imports.
|
|
2
|
+
from typing import List, Tuple
|
|
3
|
+
|
|
4
|
+
# our package imports.
|
|
5
|
+
from ..vibrant import Swatch
|
|
6
|
+
from ..sautils import export
|
|
7
|
+
|
|
8
|
+
@export
|
|
9
|
+
class ImagePaletteColors:
|
|
10
|
+
"""
|
|
11
|
+
Image Palette Colors object.
|
|
12
|
+
|
|
13
|
+
This is a helper class, and is not part of the Spotify Web API specification.
|
|
14
|
+
"""
|
|
15
|
+
|
|
16
|
+
def __init__(self, root:list[Tuple[int, int, int]]=None) -> None:
|
|
17
|
+
"""
|
|
18
|
+
Initializes a new instance of the class.
|
|
19
|
+
|
|
20
|
+
Args:
|
|
21
|
+
root (list[Tuple[int, int, int]]):
|
|
22
|
+
A list of color palette Tuple in the form (r, g, b) that contains extracted
|
|
23
|
+
color palette information; otherwise, None.
|
|
24
|
+
"""
|
|
25
|
+
# initialize storage.
|
|
26
|
+
self._ImageSource:str = None
|
|
27
|
+
self._Items:list[Swatch] = []
|
|
28
|
+
|
|
29
|
+
if (not isinstance(root, list)):
|
|
30
|
+
|
|
31
|
+
pass
|
|
32
|
+
|
|
33
|
+
else:
|
|
34
|
+
|
|
35
|
+
# store color palette information.
|
|
36
|
+
if root is not None:
|
|
37
|
+
for item in root:
|
|
38
|
+
if item is not None:
|
|
39
|
+
self._Items.append(Swatch(rgb=item))
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
def __repr__(self) -> str:
|
|
43
|
+
return self.ToString()
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
def __str__(self) -> str:
|
|
47
|
+
return self.ToString()
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
@property
|
|
51
|
+
def ImageSource(self) -> str:
|
|
52
|
+
"""
|
|
53
|
+
Image that was processed to obtain the palette.
|
|
54
|
+
"""
|
|
55
|
+
return self._ImageSource
|
|
56
|
+
|
|
57
|
+
@ImageSource.setter
|
|
58
|
+
def ImageSource(self, value:str):
|
|
59
|
+
"""
|
|
60
|
+
Sets the ImageSource property value.
|
|
61
|
+
"""
|
|
62
|
+
self._ImageSource = value
|
|
63
|
+
|
|
64
|
+
|
|
65
|
+
@property
|
|
66
|
+
def Items(self) -> list[Swatch]:
|
|
67
|
+
"""
|
|
68
|
+
Array of `Swatch` objects.
|
|
69
|
+
"""
|
|
70
|
+
return self._Items
|
|
71
|
+
|
|
72
|
+
|
|
73
|
+
@property
|
|
74
|
+
def ItemsCount(self) -> int:
|
|
75
|
+
"""
|
|
76
|
+
Length of `Items` array.
|
|
77
|
+
"""
|
|
78
|
+
return len(self._Items)
|
|
79
|
+
|
|
80
|
+
|
|
81
|
+
def ToDictionary(self) -> dict:
|
|
82
|
+
"""
|
|
83
|
+
Returns a dictionary representation of the class.
|
|
84
|
+
"""
|
|
85
|
+
result:dict = \
|
|
86
|
+
{
|
|
87
|
+
'image_source': self._ImageSource,
|
|
88
|
+
'items_count': self.ItemsCount,
|
|
89
|
+
'items': [ item.ToDictionary() for item in self._Items ],
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
return result
|
|
93
|
+
|
|
94
|
+
|
|
95
|
+
def ToString(self, includeItems:bool=False) -> str:
|
|
96
|
+
"""
|
|
97
|
+
Returns a displayable string representation of the class.
|
|
98
|
+
|
|
99
|
+
Args:
|
|
100
|
+
includeItems (bool):
|
|
101
|
+
True to include the Items collection of objects; otherwise, False
|
|
102
|
+
to only return base properties.
|
|
103
|
+
"""
|
|
104
|
+
msg:str = 'Swatch:'
|
|
105
|
+
msg = '%s\n ImageSource="%s"' % (msg, str(self._ImageSource))
|
|
106
|
+
msg = '%s\n Items Count="%s"' % (msg, str(self.ItemsCount))
|
|
107
|
+
|
|
108
|
+
if (includeItems):
|
|
109
|
+
if self.Items is not None: msg = '%s\n\n %s' % (msg, str(self.Items))
|
|
110
|
+
|
|
111
|
+
return msg
|
|
@@ -3,8 +3,6 @@ import base64
|
|
|
3
3
|
from datetime import datetime
|
|
4
4
|
import json
|
|
5
5
|
from io import BytesIO
|
|
6
|
-
from platform import architecture
|
|
7
|
-
from ssl import ALERT_DESCRIPTION_ACCESS_DENIED
|
|
8
6
|
from oauthlib.oauth2 import BackendApplicationClient, WebApplicationClient
|
|
9
7
|
import random
|
|
10
8
|
from soco import SoCo
|
|
@@ -24,7 +22,7 @@ from zeroconf import Zeroconf
|
|
|
24
22
|
|
|
25
23
|
# our package imports.
|
|
26
24
|
from .oauthcli import AuthClient
|
|
27
|
-
from .vibrant import Vibrant, Palette
|
|
25
|
+
from .vibrant import Vibrant, Palette, ColorThiefFast
|
|
28
26
|
from .models import *
|
|
29
27
|
from .models import UserProfile as UserProfileCurrentUser
|
|
30
28
|
from .spotifyconnect import SpotifyConnectDirectoryTask, SpotifyConnectDeviceNotFound
|
|
@@ -7288,6 +7286,152 @@ class SpotifyClient:
|
|
|
7288
7286
|
return None
|
|
7289
7287
|
|
|
7290
7288
|
|
|
7289
|
+
def GetImagePaletteColors(
|
|
7290
|
+
self,
|
|
7291
|
+
imageSource:str=None,
|
|
7292
|
+
colorCount:int=10,
|
|
7293
|
+
colorQuality:int=1,
|
|
7294
|
+
brightnessFilterLow:int=None,
|
|
7295
|
+
brightnessFilterHigh:int=None,
|
|
7296
|
+
hueDistanceFilter:int=None,
|
|
7297
|
+
) -> ImagePaletteColors:
|
|
7298
|
+
"""
|
|
7299
|
+
Extracts color palette RGB values from the specified image source.
|
|
7300
|
+
|
|
7301
|
+
Args:
|
|
7302
|
+
imageSource (str):
|
|
7303
|
+
The image source to extract color palette information from. If the prefix of the
|
|
7304
|
+
value is `http:` or `https:`, then the image is downloaded from the url.
|
|
7305
|
+
This can also point to a filename on the local file system.
|
|
7306
|
+
If null, the currently playing Spotify track image url is used; if no track
|
|
7307
|
+
is playing, then an exception is raised.
|
|
7308
|
+
Example: `http://mydomain/image1.jpg`
|
|
7309
|
+
Example: `c:/image1.jpg`
|
|
7310
|
+
Default is None.
|
|
7311
|
+
colorCount (int):
|
|
7312
|
+
The size of the palette (max number of colors).
|
|
7313
|
+
Range is 1 to 256.
|
|
7314
|
+
Default is 10.
|
|
7315
|
+
colorQuality (int):
|
|
7316
|
+
Controls the processing time and quality of the palette generation.
|
|
7317
|
+
A lower value (e.g. 1) results in higher quality but takes more processing time,
|
|
7318
|
+
while a higher value (e.g. 5) is faster but may result in a lower-quality palette.
|
|
7319
|
+
Default is 1.
|
|
7320
|
+
brightnessFilterLow (int):
|
|
7321
|
+
Removes colors that are too dark based on their brightness value.
|
|
7322
|
+
Range is 0 to 765.
|
|
7323
|
+
Default is None.
|
|
7324
|
+
brightnessFilterHigh (int):
|
|
7325
|
+
Remove colors that are too light based on their brightness value.
|
|
7326
|
+
Range is 0 to 765.
|
|
7327
|
+
Default is None.
|
|
7328
|
+
hueDistanceFilter (int):
|
|
7329
|
+
Remove colors that are too close to each other for the specified hue.
|
|
7330
|
+
This keeps the colors looking fairly distinct.
|
|
7331
|
+
Range is 0 to 360.
|
|
7332
|
+
Default is None.
|
|
7333
|
+
|
|
7334
|
+
Returns:
|
|
7335
|
+
A `ImagePaletteColors` object that contains extracted color information.
|
|
7336
|
+
|
|
7337
|
+
Raises:
|
|
7338
|
+
SpotifyWebApiError:
|
|
7339
|
+
If the Spotify Web API request was for a non-authorization service
|
|
7340
|
+
and the response contains error information.
|
|
7341
|
+
SpotifyApiError:
|
|
7342
|
+
If the method fails for any other reason.
|
|
7343
|
+
|
|
7344
|
+
The median cut algorithm is used to cluster similar colors.
|
|
7345
|
+
|
|
7346
|
+
The returned list of palette entries will always have a length value that
|
|
7347
|
+
matches the `colorCount` argument. If colors are filtered, then the
|
|
7348
|
+
list is padded with black (e.g. 0,0,0) entries. This ensures that
|
|
7349
|
+
the list length always matches the requested color count.
|
|
7350
|
+
|
|
7351
|
+
If the `brightnessFilterX` values are specified, each color in the
|
|
7352
|
+
returned array is converted to a brightness value (R+G+B=brightness, 0-765)
|
|
7353
|
+
and will be filtered out based on the high / low filter arguments.
|
|
7354
|
+
|
|
7355
|
+
If the `hueDistanceFilter` is specified, each color in the returned array
|
|
7356
|
+
is converted to a hue value and will be filtered out based on the distance
|
|
7357
|
+
between it and the next color.
|
|
7358
|
+
|
|
7359
|
+
<details>
|
|
7360
|
+
<summary>Sample Code</summary>
|
|
7361
|
+
```python
|
|
7362
|
+
.. include:: ../docs/include/samplecode/SpotifyClient/GetImagePaletteColors.py
|
|
7363
|
+
```
|
|
7364
|
+
</details>
|
|
7365
|
+
"""
|
|
7366
|
+
apiMethodName:str = 'GetImagePaletteColors'
|
|
7367
|
+
apiMethodParms:SIMethodParmListContext = None
|
|
7368
|
+
result:ImagePaletteColors = {}
|
|
7369
|
+
|
|
7370
|
+
try:
|
|
7371
|
+
|
|
7372
|
+
# trace.
|
|
7373
|
+
apiMethodParms = _logsi.EnterMethodParmList(SILevel.Debug, apiMethodName)
|
|
7374
|
+
apiMethodParms.AppendKeyValue("imageSource", imageSource)
|
|
7375
|
+
apiMethodParms.AppendKeyValue("colorCount", colorCount)
|
|
7376
|
+
apiMethodParms.AppendKeyValue("colorQuality", colorQuality)
|
|
7377
|
+
apiMethodParms.AppendKeyValue("brightnessFilterLow", brightnessFilterLow)
|
|
7378
|
+
apiMethodParms.AppendKeyValue("brightnessFilterHigh", brightnessFilterHigh)
|
|
7379
|
+
apiMethodParms.AppendKeyValue("hueDistanceFilter", hueDistanceFilter)
|
|
7380
|
+
_logsi.LogMethodParmList(SILevel.Verbose, "Get color palette from an image url", apiMethodParms)
|
|
7381
|
+
|
|
7382
|
+
# validations.
|
|
7383
|
+
if (not isinstance(colorCount,int)):
|
|
7384
|
+
colorCount = 10
|
|
7385
|
+
if (not isinstance(colorQuality,int)):
|
|
7386
|
+
colorQuality = 1
|
|
7387
|
+
|
|
7388
|
+
# if image source not specified, then return currently playing track image url value.
|
|
7389
|
+
if (imageSource is None):
|
|
7390
|
+
nowPlaying:PlayerPlayState = self.GetPlayerNowPlaying(additionalTypes=SpotifyMediaTypes.EPISODE.value)
|
|
7391
|
+
if nowPlaying is not None:
|
|
7392
|
+
if nowPlaying.CurrentlyPlayingType in [SpotifyMediaTypes.TRACK.value,SpotifyMediaTypes.EPISODE.value]:
|
|
7393
|
+
trackItem:Track = nowPlaying.Item
|
|
7394
|
+
if (trackItem is not None):
|
|
7395
|
+
imageSource = trackItem.ImageUrl
|
|
7396
|
+
if (imageSource is None):
|
|
7397
|
+
raise SpotifyApiError(SAAppMessages.ARGUMENT_REQUIRED_ERROR % (apiMethodName, 'imageSource'), logsi=_logsi)
|
|
7398
|
+
|
|
7399
|
+
# prepare to extract color palette from the image source.
|
|
7400
|
+
colorThiefFast = ColorThiefFast(imageSource)
|
|
7401
|
+
|
|
7402
|
+
# extract the color palette, based on filter criteria specified.
|
|
7403
|
+
palette = colorThiefFast.get_palette(
|
|
7404
|
+
color_count=colorCount,
|
|
7405
|
+
quality=colorQuality,
|
|
7406
|
+
brightness_filter_low=brightnessFilterLow,
|
|
7407
|
+
brightness_filter_high=brightnessFilterHigh,
|
|
7408
|
+
hue_distance_filter=hueDistanceFilter,
|
|
7409
|
+
)
|
|
7410
|
+
|
|
7411
|
+
_logsi.LogArray(SILevel.Verbose, TRACE_METHOD_RESULT_TYPE % (apiMethodName, type(palette).__name__), palette)
|
|
7412
|
+
|
|
7413
|
+
# process results.
|
|
7414
|
+
result = ImagePaletteColors(root=palette)
|
|
7415
|
+
result.ImageSource = imageSource
|
|
7416
|
+
|
|
7417
|
+
# trace.
|
|
7418
|
+
_logsi.LogObject(SILevel.Verbose, TRACE_METHOD_RESULT_TYPE % (apiMethodName, type(result).__name__), result, excludeNonPublic=True)
|
|
7419
|
+
return result
|
|
7420
|
+
|
|
7421
|
+
except SpotifyApiError: raise # pass handled exceptions on thru
|
|
7422
|
+
except SpotifyWebApiError: raise # pass handled exceptions on thru
|
|
7423
|
+
except SpotifyWebApiAuthenticationError: raise # pass handled exceptions on thru
|
|
7424
|
+
except Exception as ex:
|
|
7425
|
+
|
|
7426
|
+
# format unhandled exception.
|
|
7427
|
+
raise SpotifyApiError(SAAppMessages.UNHANDLED_EXCEPTION.format(apiMethodName, str(ex)), ex, logsi=_logsi)
|
|
7428
|
+
|
|
7429
|
+
finally:
|
|
7430
|
+
|
|
7431
|
+
# trace.
|
|
7432
|
+
_logsi.LeaveMethod(SILevel.Debug, apiMethodName)
|
|
7433
|
+
|
|
7434
|
+
|
|
7291
7435
|
def GetImageVibrantColors(
|
|
7292
7436
|
self,
|
|
7293
7437
|
imageSource:str=None,
|
|
@@ -10730,6 +10874,8 @@ class SpotifyClient:
|
|
|
10730
10874
|
market:str=None,
|
|
10731
10875
|
limitTotal:int=None,
|
|
10732
10876
|
sortResult:bool=True,
|
|
10877
|
+
filterArtist:str=None,
|
|
10878
|
+
filterAlbum:str=None,
|
|
10733
10879
|
) -> TrackPageSaved:
|
|
10734
10880
|
"""
|
|
10735
10881
|
Get a list of the tracks saved in the current Spotify user's 'Your Library'.
|
|
@@ -10763,6 +10909,12 @@ class SpotifyClient:
|
|
|
10763
10909
|
True to sort the items by name; otherwise, False to leave the items in the same order they
|
|
10764
10910
|
were returned in by the Spotify Web API.
|
|
10765
10911
|
Default: True
|
|
10912
|
+
filterArtist (str):
|
|
10913
|
+
Filter returned entries by an artist name or uri value.
|
|
10914
|
+
Value can be the full name of the artist (e.g. "Jeremy Camp"), or a partial name (e.g. "Camp").
|
|
10915
|
+
filterAlbum (str):
|
|
10916
|
+
Filter returned entries by an album name or uri value.
|
|
10917
|
+
Value can be the full name of the album (e.g. "Carried Me"), or a partial name (e.g. "Carried").
|
|
10766
10918
|
|
|
10767
10919
|
Returns:
|
|
10768
10920
|
An `TrackPageSaved` object that contains saved track information.
|
|
@@ -10800,6 +10952,8 @@ class SpotifyClient:
|
|
|
10800
10952
|
apiMethodParms.AppendKeyValue("market", market)
|
|
10801
10953
|
apiMethodParms.AppendKeyValue("limitTotal", limitTotal)
|
|
10802
10954
|
apiMethodParms.AppendKeyValue("sortResult", sortResult)
|
|
10955
|
+
apiMethodParms.AppendKeyValue("filterArtist", filterArtist)
|
|
10956
|
+
apiMethodParms.AppendKeyValue("filterAlbum", filterAlbum)
|
|
10803
10957
|
_logsi.LogMethodParmList(SILevel.Verbose, "Get a list of the users track favorites", apiMethodParms)
|
|
10804
10958
|
|
|
10805
10959
|
# validations.
|
|
@@ -10873,13 +11027,55 @@ class SpotifyClient:
|
|
|
10873
11027
|
result.Total = pageObj.Total
|
|
10874
11028
|
result.DateLastRefreshed = datetime.utcnow().timestamp()
|
|
10875
11029
|
|
|
10876
|
-
#
|
|
11030
|
+
# any results to process?
|
|
10877
11031
|
if (len(result.Items) > 0):
|
|
11032
|
+
|
|
11033
|
+
# sort result items.
|
|
10878
11034
|
if (sortResult is True):
|
|
10879
11035
|
result.Items.sort(key=lambda x: (x.Track.Name or "").lower(), reverse=False)
|
|
10880
11036
|
else:
|
|
10881
11037
|
result.Items.sort(key=lambda x: (x.AddedAt or "").lower(), reverse=True)
|
|
10882
11038
|
|
|
11039
|
+
# apply artist filter criteria.
|
|
11040
|
+
if (filterArtist is not None):
|
|
11041
|
+
_logsi.LogVerbose("Applying filter criteria to results list: %s=\"%s\"" % ("artist", filterArtist))
|
|
11042
|
+
filterArtistCompare:str = (filterArtist or "").lower()
|
|
11043
|
+
isFilterUri:bool = SpotifyClient.IsSpotifyUri(filterArtistCompare)
|
|
11044
|
+
# process list in reverse order since we are removing items.
|
|
11045
|
+
for idx in reversed(range(len(result.Items))):
|
|
11046
|
+
trackSaved:TrackSaved = result.Items[idx]
|
|
11047
|
+
artistFound:bool = False
|
|
11048
|
+
artist:ArtistSimplified = None
|
|
11049
|
+
for artist in trackSaved.Track.Artists:
|
|
11050
|
+
if (isFilterUri):
|
|
11051
|
+
if ((artist.Uri or "").lower() == filterArtistCompare):
|
|
11052
|
+
artistFound = True
|
|
11053
|
+
break
|
|
11054
|
+
elif ((artist.Name or "").lower().find(filterArtistCompare) > -1):
|
|
11055
|
+
artistFound = True
|
|
11056
|
+
break
|
|
11057
|
+
if (not artistFound):
|
|
11058
|
+
result.Items.pop(idx)
|
|
11059
|
+
|
|
11060
|
+
# apply album filter criteria.
|
|
11061
|
+
if (filterAlbum is not None):
|
|
11062
|
+
_logsi.LogVerbose("Applying filter criteria to results list: %s=\"%s\"" % ("album", filterAlbum))
|
|
11063
|
+
filterAlbumCompare:str = (filterAlbum or "").lower()
|
|
11064
|
+
isFilterUri:bool = SpotifyClient.IsSpotifyUri(filterAlbumCompare)
|
|
11065
|
+
# process list in reverse order since we are removing items.
|
|
11066
|
+
for idx in reversed(range(len(result.Items))):
|
|
11067
|
+
trackSaved:TrackSaved = result.Items[idx]
|
|
11068
|
+
albumFound:bool = False
|
|
11069
|
+
album:Album = trackSaved.Track.Album
|
|
11070
|
+
if (album is not None):
|
|
11071
|
+
if (isFilterUri):
|
|
11072
|
+
if ((album.Uri or "").lower() == filterAlbumCompare):
|
|
11073
|
+
albumFound = True
|
|
11074
|
+
elif ((album.Name or "").lower().find(filterAlbumCompare) > -1):
|
|
11075
|
+
albumFound = True
|
|
11076
|
+
if (not albumFound):
|
|
11077
|
+
result.Items.pop(idx)
|
|
11078
|
+
|
|
10883
11079
|
# trace.
|
|
10884
11080
|
_logsi.LogObject(SILevel.Verbose, (TRACE_METHOD_RESULT_TYPE + result.PagingInfo) % (apiMethodName, type(result).__name__), result, excludeNonPublic=True)
|
|
10885
11081
|
return result
|
|
@@ -12683,6 +12879,8 @@ class SpotifyClient:
|
|
|
12683
12879
|
delay:float=0.50,
|
|
12684
12880
|
resolveDeviceId:bool=True,
|
|
12685
12881
|
limitTotal:int=200,
|
|
12882
|
+
filterArtist:str=None,
|
|
12883
|
+
filterAlbum:str=None,
|
|
12686
12884
|
) -> None:
|
|
12687
12885
|
"""
|
|
12688
12886
|
Get a list of the tracks saved in the current Spotify user's 'Your Library'
|
|
@@ -12711,6 +12909,12 @@ class SpotifyClient:
|
|
|
12711
12909
|
limitTotal (int):
|
|
12712
12910
|
The maximum number of items to retrieve from favorites.
|
|
12713
12911
|
Default is 200; value range is 1 - 750.
|
|
12912
|
+
filterArtist (str):
|
|
12913
|
+
Filter returned entries by an artist name or uri value.
|
|
12914
|
+
Value can be the full name of the artist (e.g. "Jeremy Camp"), or a partial name (e.g. "Camp").
|
|
12915
|
+
filterAlbum (str):
|
|
12916
|
+
Filter returned entries by an album name or uri value.
|
|
12917
|
+
Value can be the full name of the album (e.g. "Carried Me"), or a partial name (e.g. "Carried").
|
|
12714
12918
|
|
|
12715
12919
|
Raises:
|
|
12716
12920
|
SpotifyWebApiError:
|
|
@@ -12735,6 +12939,13 @@ class SpotifyClient:
|
|
|
12735
12939
|
.. include:: ../docs/include/samplecode/SpotifyClient/PlayerMediaPlayTrackFavorites.py
|
|
12736
12940
|
```
|
|
12737
12941
|
</details>
|
|
12942
|
+
|
|
12943
|
+
<details>
|
|
12944
|
+
<summary>Sample Code - Filter by Artist</summary>
|
|
12945
|
+
```python
|
|
12946
|
+
.. include:: ../docs/include/samplecode/SpotifyClient/PlayerMediaPlayTrackFavorites_FilterArtist.py
|
|
12947
|
+
```
|
|
12948
|
+
</details>
|
|
12738
12949
|
"""
|
|
12739
12950
|
apiMethodName:str = 'PlayerMediaPlayTrackFavorites'
|
|
12740
12951
|
apiMethodParms:SIMethodParmListContext = None
|
|
@@ -12749,6 +12960,8 @@ class SpotifyClient:
|
|
|
12749
12960
|
apiMethodParms.AppendKeyValue("delay", delay)
|
|
12750
12961
|
apiMethodParms.AppendKeyValue("resolveDeviceId (DEPRECATED)", resolveDeviceId)
|
|
12751
12962
|
apiMethodParms.AppendKeyValue("limitTotal", limitTotal)
|
|
12963
|
+
apiMethodParms.AppendKeyValue("filterArtist", filterArtist)
|
|
12964
|
+
apiMethodParms.AppendKeyValue("filterAlbum", filterAlbum)
|
|
12752
12965
|
_logsi.LogMethodParmList(SILevel.Verbose, "Spotify Connect device play track favorites", apiMethodParms)
|
|
12753
12966
|
|
|
12754
12967
|
# validations.
|
|
@@ -12759,7 +12972,7 @@ class SpotifyClient:
|
|
|
12759
12972
|
limitTotal = 750
|
|
12760
12973
|
|
|
12761
12974
|
# get current users favorite tracks.
|
|
12762
|
-
tracks:TrackPageSaved = self.GetTrackFavorites(limitTotal=limitTotal, sortResult=False)
|
|
12975
|
+
tracks:TrackPageSaved = self.GetTrackFavorites(limitTotal=limitTotal, sortResult=False, filterArtist=filterArtist, filterAlbum=filterAlbum)
|
|
12763
12976
|
if (tracks.ItemsCount == 0):
|
|
12764
12977
|
_logsi.LogVerbose("Current user has no favorite tracks; nothing to do")
|
|
12765
12978
|
return
|
|
@@ -12768,7 +12981,7 @@ class SpotifyClient:
|
|
|
12768
12981
|
arrUris:list[str] = []
|
|
12769
12982
|
trackSaved:TrackSaved
|
|
12770
12983
|
for trackSaved in tracks.Items:
|
|
12771
|
-
arrUris.append(trackSaved.Track.
|
|
12984
|
+
arrUris.append(trackSaved.Track.UriOrigin)
|
|
12772
12985
|
|
|
12773
12986
|
# resolve the device object from the device id.
|
|
12774
12987
|
scDevice = self._ResolveDeviceObject(deviceId, True)
|
|
@@ -4,8 +4,7 @@ import copy
|
|
|
4
4
|
from datetime import datetime
|
|
5
5
|
import hashlib
|
|
6
6
|
from pychromecast import CastBrowser, CastInfo, Chromecast, get_chromecast_from_cast_info, get_chromecast_from_host
|
|
7
|
-
import
|
|
8
|
-
from pychromecast.dial import MultizoneInfo, MultizoneStatus, get_multizone_status, get_device_info
|
|
7
|
+
from pychromecast.dial import MultizoneInfo, MultizoneStatus, get_multizone_status
|
|
9
8
|
from pychromecast.controllers.multizone import MultiZoneManagerListener, MultizoneController, MultizoneManager
|
|
10
9
|
from pychromecast.const import CAST_TYPE_GROUP
|
|
11
10
|
import socket
|
|
@@ -637,7 +636,7 @@ class SpotifyConnectDirectoryTask(threading.Thread):
|
|
|
637
636
|
scDevice.ZeroconfResponseInfo = ZeroconfResponse()
|
|
638
637
|
|
|
639
638
|
castDevice:Chromecast = None
|
|
640
|
-
deviceWaitTimeoutSecs:float =
|
|
639
|
+
deviceWaitTimeoutSecs:float = 20.0
|
|
641
640
|
|
|
642
641
|
# is this a cast group?
|
|
643
642
|
if (castInfo.cast_type == CAST_TYPE_GROUP):
|
|
@@ -702,7 +701,7 @@ class SpotifyConnectDirectoryTask(threading.Thread):
|
|
|
702
701
|
host=(groupHost, groupPort, castInfo.uuid, castInfo.model_name, castInfo.friendly_name),
|
|
703
702
|
tries=2,
|
|
704
703
|
retry_wait=0.5,
|
|
705
|
-
timeout=
|
|
704
|
+
timeout=10)
|
|
706
705
|
|
|
707
706
|
# calculate wait timeout for devices to become active, based on number of devices in group.
|
|
708
707
|
if (castMultiZoneStatus.groups is not None):
|
|
@@ -723,7 +722,7 @@ class SpotifyConnectDirectoryTask(threading.Thread):
|
|
|
723
722
|
zconf=self._ZeroconfInstance,
|
|
724
723
|
tries=2,
|
|
725
724
|
retry_wait=0.5,
|
|
726
|
-
timeout=
|
|
725
|
+
timeout=5)
|
|
727
726
|
|
|
728
727
|
# trace.
|
|
729
728
|
_logsi.LogVerbose("%s - Waiting %d seconds max for Chromecast device to activate: %s [ip=%s:%s]" % (self.name, deviceWaitTimeoutSecs, scDevice.Title, castInfo.host, castInfo.port))
|
|
@@ -7,6 +7,7 @@ from .image import VibrantImage
|
|
|
7
7
|
from .main import Vibrant
|
|
8
8
|
from .models import Palette, Props, Swatch
|
|
9
9
|
from .utils import hsl_to_rgb, rgb_to_hsl
|
|
10
|
+
from .colorthieffast import ColorThiefFast
|
|
10
11
|
|
|
11
12
|
# all classes to import when "import *" is specified.
|
|
12
13
|
__all__ = [
|
|
@@ -14,4 +15,5 @@ __all__ = [
|
|
|
14
15
|
'Vibrant',
|
|
15
16
|
'Palette',
|
|
16
17
|
'Swatch',
|
|
18
|
+
'ColorThiefFast',
|
|
17
19
|
]
|