spotifywebapiPython 1.0.115__py3-none-any.whl → 1.0.117__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.
- {spotifywebapiPython-1.0.115.dist-info → spotifywebapiPython-1.0.117.dist-info}/METADATA +1 -1
- {spotifywebapiPython-1.0.115.dist-info → spotifywebapiPython-1.0.117.dist-info}/RECORD +22 -22
- spotifywebapipython/const.py +1 -1
- spotifywebapipython/docs/__init__.html +15 -2
- spotifywebapipython/docs/index.html +2 -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 +6 -4
- spotifywebapipython/docs/spotifywebapipython/spotifydiscovery.html +2 -2
- spotifywebapipython/docs/spotifywebapipython/spotifywebapiauthenticationerror.html +2 -2
- spotifywebapipython/docs/spotifywebapipython/spotifywebapierror.html +2 -2
- spotifywebapipython/models/artistinfo.py +20 -1
- spotifywebapipython/models/artistinfotourevent.py +19 -0
- spotifywebapipython/spotifyclient.py +200 -141
- {spotifywebapiPython-1.0.115.dist-info → spotifywebapiPython-1.0.117.dist-info}/LICENSE +0 -0
- {spotifywebapiPython-1.0.115.dist-info → spotifywebapiPython-1.0.117.dist-info}/WHEEL +0 -0
- {spotifywebapiPython-1.0.115.dist-info → spotifywebapiPython-1.0.117.dist-info}/top_level.txt +0 -0
|
@@ -50,6 +50,8 @@ CACHE_SOURCE_CURRENT:str = "current"
|
|
|
50
50
|
|
|
51
51
|
SPOTIFY_DJ_PLAYLIST_ID = "37i9dqzf1eykqdzj48dyyq"
|
|
52
52
|
|
|
53
|
+
SPOTIFY_ONLINE_LINK_PREFIX = "https://open.spotify.com"
|
|
54
|
+
|
|
53
55
|
# get smartinspect logger reference; create a new session for this module name.
|
|
54
56
|
from smartinspectpython.siauto import SIAuto, SILevel, SISession, SIMethodParmListContext, SISourceId
|
|
55
57
|
import logging
|
|
@@ -423,10 +425,16 @@ class SpotifyClient:
|
|
|
423
425
|
False if there are no more pages to process;
|
|
424
426
|
otherwise, True to safely process the next page of results.
|
|
425
427
|
"""
|
|
426
|
-
# anymore page results?
|
|
428
|
+
# anymore page results for non-cursor paging types?
|
|
427
429
|
if (pageObj.Next is None) or (resultItemsCount >= limitTotal):
|
|
428
430
|
# no - all pages were processed, or limit total reached.
|
|
429
431
|
return False
|
|
432
|
+
|
|
433
|
+
# anymore page results for cursor paging types?
|
|
434
|
+
if (pageObj.IsCursor and pageObj.CursorAfter is None and pageObj.CursorBefore is None) \
|
|
435
|
+
or (resultItemsCount >= limitTotal):
|
|
436
|
+
# no - all pages were processed, or limit total reached.
|
|
437
|
+
return False
|
|
430
438
|
|
|
431
439
|
# modify paging-related request parameters for next page of items.
|
|
432
440
|
urlParms['limit'] = pageObj.Limit
|
|
@@ -434,7 +442,7 @@ class SpotifyClient:
|
|
|
434
442
|
urlParms['after'] = pageObj.CursorAfter
|
|
435
443
|
else:
|
|
436
444
|
urlParms['before'] = pageObj.CursorBefore
|
|
437
|
-
|
|
445
|
+
|
|
438
446
|
# just in case spotify doesn't return what is expected, and to prevent
|
|
439
447
|
# an endless loop
|
|
440
448
|
if pageObj.ItemsCount == 0:
|
|
@@ -3658,162 +3666,213 @@ class SpotifyClient:
|
|
|
3658
3666
|
|
|
3659
3667
|
# was a Spotify external url specified for the artist? if not then we are done.
|
|
3660
3668
|
if artist.ExternalUrls is None or artist.ExternalUrls.Spotify is None:
|
|
3661
|
-
|
|
3669
|
+
_logsi.LogVerbose("Spotify ExternalUrl not present for artist details page")
|
|
3670
|
+
return result
|
|
3662
3671
|
|
|
3663
|
-
|
|
3664
|
-
msg:SpotifyApiMessage = SpotifyApiMessage(apiMethodName, artist.ExternalUrls.Spotify)
|
|
3665
|
-
msg.RequestHeaders[self.AuthToken.HeaderKey] = self.AuthToken.HeaderValue
|
|
3666
|
-
self.MakeRequest('GET', msg)
|
|
3672
|
+
try:
|
|
3667
3673
|
|
|
3668
|
-
|
|
3669
|
-
|
|
3670
|
-
|
|
3674
|
+
# retrieve artist details page.
|
|
3675
|
+
msg:SpotifyApiMessage = SpotifyApiMessage(apiMethodName, artist.ExternalUrls.Spotify)
|
|
3676
|
+
msg.RequestHeaders[self.AuthToken.HeaderKey] = self.AuthToken.HeaderValue
|
|
3677
|
+
self.MakeRequest('GET', msg)
|
|
3671
3678
|
|
|
3672
|
-
|
|
3673
|
-
|
|
3674
|
-
_logsi.
|
|
3675
|
-
return result
|
|
3679
|
+
# log html response.
|
|
3680
|
+
#_logsi.LogHtml(SILevel.Verbose, "GetArtistInfo response HTML", msg.ResponseData)
|
|
3681
|
+
_logsi.LogSource(SILevel.Verbose, "GetArtistInfo response Text", msg.ResponseData, SISourceId.Html)
|
|
3676
3682
|
|
|
3677
|
-
|
|
3678
|
-
|
|
3679
|
-
|
|
3680
|
-
if html is None:
|
|
3681
|
-
return result
|
|
3682
|
-
if not html.startswith('<html'):
|
|
3683
|
-
idx:int = html.find('<html')
|
|
3684
|
-
if idx == -1:
|
|
3685
|
-
_logsi.LogVerbose("<html> tag could not be found for artist details page: '%s'" % (artist.ExternalUrls.Spotify))
|
|
3683
|
+
# if no response data then we are done.
|
|
3684
|
+
if not msg.HasResponseData:
|
|
3685
|
+
_logsi.LogVerbose("No html returned for artist details page: '%s'" % (artist.ExternalUrls.Spotify))
|
|
3686
3686
|
return result
|
|
3687
|
-
|
|
3688
|
-
|
|
3687
|
+
|
|
3688
|
+
# find the starting <html> tag.
|
|
3689
|
+
# ElementTree fromstring method will fail if initial tag is "<!doctype html>".
|
|
3690
|
+
html:str = msg.ResponseData
|
|
3691
|
+
if html is None:
|
|
3692
|
+
return result
|
|
3693
|
+
if not html.startswith('<html'):
|
|
3694
|
+
idx:int = html.find('<html')
|
|
3695
|
+
if idx == -1:
|
|
3696
|
+
_logsi.LogVerbose("<html> tag could not be found for artist details page: '%s'" % (artist.ExternalUrls.Spotify))
|
|
3697
|
+
return result
|
|
3698
|
+
html = html[idx:]
|
|
3699
|
+
_logsi.LogSource(SILevel.Verbose, "GetArtistInfo response Text (starting from '<html')", html, SISourceId.Html)
|
|
3689
3700
|
|
|
3690
|
-
|
|
3691
|
-
|
|
3701
|
+
# log formatted html response.
|
|
3702
|
+
_logsi.LogXml(SILevel.Verbose, "GetArtistInfo response Text (formatted)", html, prettyPrint=True)
|
|
3692
3703
|
|
|
3693
|
-
|
|
3694
|
-
|
|
3695
|
-
|
|
3704
|
+
# load html element tree so we can parse it.
|
|
3705
|
+
doc:Element = fromstring(html)
|
|
3706
|
+
_logsi.LogObject(SILevel.Verbose, "doc Element object", doc)
|
|
3696
3707
|
|
|
3697
|
-
|
|
3698
|
-
|
|
3699
|
-
|
|
3700
|
-
|
|
3701
|
-
|
|
3702
|
-
|
|
3703
|
-
|
|
3704
|
-
|
|
3705
|
-
|
|
3706
|
-
|
|
3707
|
-
|
|
3708
|
-
|
|
3709
|
-
|
|
3710
|
-
|
|
3711
|
-
|
|
3708
|
+
attrXPath:str = None
|
|
3709
|
+
elm:Element = None
|
|
3710
|
+
elmChild:Element = None
|
|
3711
|
+
innerText:str = None
|
|
3712
|
+
|
|
3713
|
+
# about information - image (button / bio page):
|
|
3714
|
+
attrXPath = './/button[@type="button"]/descendant-or-self::img'
|
|
3715
|
+
_logsi.LogVerbose("Element search 'image button': '%s'" % attrXPath)
|
|
3716
|
+
attrElements:list[Element] = doc.xpath(attrXPath)
|
|
3717
|
+
for elm in attrElements:
|
|
3718
|
+
_logsi.LogObject(SILevel.Verbose, "Element match: '%s'" % attrXPath, elm)
|
|
3719
|
+
attrValue:str = elm.get('src', None)
|
|
3720
|
+
if attrValue is not None:
|
|
3721
|
+
result.ImageUrl = attrValue
|
|
3722
|
+
break
|
|
3712
3723
|
|
|
3713
|
-
|
|
3714
|
-
|
|
3715
|
-
|
|
3716
|
-
|
|
3717
|
-
|
|
3718
|
-
|
|
3719
|
-
|
|
3724
|
+
# about information - image (entity-id):
|
|
3725
|
+
# use the internal `_ImageUrl` attribute value to check, as the `ImageUrl` property
|
|
3726
|
+
# will return the `ImageUrlDefault` if `_ImageUrl` attribute was not set.
|
|
3727
|
+
# <img data-testid="artist-entity-image" src="https://i.scdn.co/image/ab6761610000517446196125b56397cd4e0d9c4b" />
|
|
3728
|
+
if result._ImageUrl is None:
|
|
3729
|
+
attrXPath = './/attribute::data-testid[contains(., "artist-entity-image")]/../@src'
|
|
3730
|
+
_logsi.LogVerbose("Element search 'image entity': '%s'" % attrXPath)
|
|
3731
|
+
elmChild = doc.xpath(attrXPath)
|
|
3732
|
+
if elmChild is not None:
|
|
3733
|
+
_logsi.LogVerbose("Element match: '%s'" % elmChild[0])
|
|
3734
|
+
result.ImageUrl = elmChild[0]
|
|
3735
|
+
|
|
3736
|
+
# about information - monthly listeners:
|
|
3737
|
+
# <div data-testid="monthly-listeners-label">2,707,252 monthly listeners</div>
|
|
3738
|
+
attrXPath = './/attribute::data-testid[contains(., "monthly-listeners-label")]/../text()'
|
|
3739
|
+
_logsi.LogVerbose("Element search 'monthly listeners': '%s'" % attrXPath)
|
|
3720
3740
|
elmChild = doc.xpath(attrXPath)
|
|
3721
3741
|
if elmChild is not None:
|
|
3722
3742
|
_logsi.LogVerbose("Element match: '%s'" % elmChild[0])
|
|
3723
|
-
|
|
3724
|
-
|
|
3725
|
-
|
|
3726
|
-
|
|
3727
|
-
|
|
3728
|
-
|
|
3729
|
-
|
|
3730
|
-
|
|
3731
|
-
|
|
3732
|
-
|
|
3733
|
-
|
|
3734
|
-
|
|
3735
|
-
|
|
3736
|
-
|
|
3737
|
-
|
|
3738
|
-
|
|
3739
|
-
|
|
3740
|
-
|
|
3741
|
-
|
|
3742
|
-
|
|
3743
|
-
|
|
3744
|
-
|
|
3745
|
-
|
|
3746
|
-
|
|
3747
|
-
|
|
3748
|
-
|
|
3749
|
-
|
|
3750
|
-
|
|
3751
|
-
|
|
3752
|
-
|
|
3753
|
-
|
|
3754
|
-
|
|
3755
|
-
|
|
3756
|
-
|
|
3757
|
-
|
|
3758
|
-
|
|
3759
|
-
|
|
3760
|
-
|
|
3761
|
-
|
|
3762
|
-
|
|
3763
|
-
|
|
3764
|
-
|
|
3765
|
-
|
|
3766
|
-
|
|
3767
|
-
|
|
3768
|
-
|
|
3769
|
-
|
|
3770
|
-
|
|
3771
|
-
|
|
3772
|
-
|
|
3773
|
-
|
|
3774
|
-
|
|
3775
|
-
|
|
3776
|
-
|
|
3777
|
-
|
|
3778
|
-
|
|
3779
|
-
|
|
3743
|
+
attrValue = elmChild[0].replace(ABOUT_MONTHLY_LISTENERS,'')
|
|
3744
|
+
attrValue = attrValue.replace(',','')
|
|
3745
|
+
attrValue = attrValue.strip()
|
|
3746
|
+
if attrValue.isnumeric:
|
|
3747
|
+
result.MonthlyListeners = int(attrValue)
|
|
3748
|
+
|
|
3749
|
+
# about information - bio:
|
|
3750
|
+
# <div data-testid="expandable-description">
|
|
3751
|
+
# <div>
|
|
3752
|
+
# <div>
|
|
3753
|
+
# <span>xxxxx is a contemporary music band ...</span>
|
|
3754
|
+
# </div>
|
|
3755
|
+
# </div>
|
|
3756
|
+
# </div>
|
|
3757
|
+
attrXPath = './/attribute::data-testid[contains(., "expandable-description")]/..'
|
|
3758
|
+
_logsi.LogVerbose("Element search 'bio': '%s'" % attrXPath)
|
|
3759
|
+
attrElements:list[Element] = doc.xpath(attrXPath)
|
|
3760
|
+
for elm in attrElements:
|
|
3761
|
+
innerText = _xmlGetInnerText(elm)
|
|
3762
|
+
_logsi.LogObject(SILevel.Verbose, "Element match: '%s' (innerText=%s)" % (attrXPath, innerText), elm)
|
|
3763
|
+
|
|
3764
|
+
# replace "see more" suffix with spotify link.
|
|
3765
|
+
SEE_MORE:str = "see more"
|
|
3766
|
+
if (innerText) and (innerText.endswith(SEE_MORE)):
|
|
3767
|
+
innerText = innerText.removesuffix(SEE_MORE)
|
|
3768
|
+
innerText += "<a href=\"%s\" target=\"_blank\">%s</a>" % (artist.ExternalUrls.Spotify, SEE_MORE)
|
|
3769
|
+
|
|
3770
|
+
result.Bio = innerText
|
|
3771
|
+
|
|
3772
|
+
# remove various html attributes from sub-elements.
|
|
3773
|
+
for elmDIV in elm.findall(".//*[@class]"):
|
|
3774
|
+
del elmDIV.attrib["class"]
|
|
3775
|
+
for elmDIV in elm.findall(".//*[@data-testid]"):
|
|
3776
|
+
del elmDIV.attrib["data-testid"]
|
|
3777
|
+
for elmDIV in elm.findall(".//*[@data-encore-id]"):
|
|
3778
|
+
del elmDIV.attrib["data-encore-id"]
|
|
3779
|
+
for elmDIV in elm.findall(".//*[@draggable]"):
|
|
3780
|
+
del elmDIV.attrib["draggable"]
|
|
3781
|
+
|
|
3782
|
+
# modify anchor link relative references in sub-elements.
|
|
3783
|
+
# also force all links to open in a new window / tab.
|
|
3784
|
+
for elmDIV in elm.findall(".//a[@href]"):
|
|
3785
|
+
value:str = elmDIV.attrib["href"]
|
|
3786
|
+
if (value.startswith("/")):
|
|
3787
|
+
value = SPOTIFY_ONLINE_LINK_PREFIX + value
|
|
3788
|
+
elmDIV.attrib["href"] = value
|
|
3789
|
+
elmDIV.attrib["target"] = "_blank"
|
|
3790
|
+
|
|
3791
|
+
# drop the parent DIV element(s), convert to html, and update result property.
|
|
3792
|
+
xml_string = etree.tostring(elm[0], pretty_print=True, encoding="utf-8")
|
|
3793
|
+
xml_decoded:str = xml_string.decode("utf-8")
|
|
3794
|
+
xml_decoded = xml_decoded.replace("\n","<br/>") # add <br> tags in place of newlines
|
|
3795
|
+
if xml_decoded.startswith("<div><br/> <div><br/> "): # trim leading and trailing line breaks
|
|
3796
|
+
xml_decoded = xml_decoded.removeprefix("<div><br/> <div><br/> ")
|
|
3797
|
+
xml_decoded = xml_decoded.removesuffix(" </div><br/></div><br/>")
|
|
3798
|
+
xml_decoded = xml_decoded.removesuffix("<br/>")
|
|
3799
|
+
# xml_decoded = xml_decoded.replace("<div><br/> ","<div>") # trim leading line breaks
|
|
3800
|
+
# if xml_decoded.endswith(" </div><br/></div><br/>"): # trim trailing line breaks
|
|
3801
|
+
# xml_decoded = xml_decoded.removesuffix(" </div><br/></div><br/>")
|
|
3802
|
+
# xml_decoded += "</div></div>"
|
|
3803
|
+
result.BioHtml = xml_decoded
|
|
3804
|
+
_logsi.LogHtml(SILevel.Verbose, "GetArtistInfo result BioHtml", result.BioHtml)
|
|
3805
|
+
break
|
|
3780
3806
|
|
|
3781
|
-
|
|
3782
|
-
|
|
3783
|
-
|
|
3784
|
-
|
|
3785
|
-
|
|
3786
|
-
|
|
3787
|
-
|
|
3788
|
-
|
|
3789
|
-
|
|
3790
|
-
|
|
3791
|
-
|
|
3807
|
+
# about information - on tour dates:
|
|
3808
|
+
# <h2>On tour</h2>
|
|
3809
|
+
# <div><ul><li>
|
|
3810
|
+
# <a draggable="false" class="kZAjDLOBaUEUgIh0u_ar" href="/concert/35nHWo11uitgmm5XSkd8CU">
|
|
3811
|
+
# <time class="jqymLdIlkUVKl8shJfKZ" dateTime="2024-11-09T19:00-05:00">
|
|
3812
|
+
# <div class="encore-text encore-text-marginal-bold diaCgR2BgDJgHboSPS1S" data-encore-id="text">Nov</div>
|
|
3813
|
+
# <div class="encore-text encore-text-title-small RjfBbWVLuXMzwFtTXDL0" data-encore-id="text">9</div>
|
|
3814
|
+
# </time>
|
|
3815
|
+
# <div class="ymKJ1kNm06pnfmgmATrg">
|
|
3816
|
+
# <div class="encore-text encore-text-body-medium-bold encore-internal-color-text-base UmCMpZhcC2lll241N1NU" data-encore-id="text">Danny Gokey with Mac Powell and Seph Schlueter</div>
|
|
3817
|
+
# <p class="encore-text encore-text-body-small bk3jsOor2S1AwKPihqwM" data-encore-id="text">
|
|
3818
|
+
# <time class="cR3tL5CgXmgwfJDDKQ2A">Sat, Nov 9</time>Hobart Arena, Troy</p>
|
|
3819
|
+
# </div>
|
|
3820
|
+
# </a>
|
|
3821
|
+
# </li></ul></div>
|
|
3822
|
+
|
|
3823
|
+
attrXPath = './/h2[text()="On tour"]/following-sibling::*/descendant-or-self::a'
|
|
3824
|
+
_logsi.LogVerbose("Element search 'On tour': '%s'" % attrXPath)
|
|
3825
|
+
attrElements:list[Element] = doc.xpath(attrXPath)
|
|
3826
|
+
for elm in attrElements:
|
|
3827
|
+
event:ArtistInfoTourEvent = ArtistInfoTourEvent()
|
|
3828
|
+
|
|
3829
|
+
# process event more info link.
|
|
3830
|
+
attrValue = elm.attrib["href"]
|
|
3831
|
+
if (attrValue):
|
|
3832
|
+
event.Href = SPOTIFY_ONLINE_LINK_PREFIX + attrValue
|
|
3833
|
+
|
|
3834
|
+
# process event datetime.
|
|
3835
|
+
for elmDT in elm.findall(".//*[@dateTime]"):
|
|
3836
|
+
attrValue = elmDT.attrib["dateTime"]
|
|
3792
3837
|
idx:int = attrValue.rfind('-')
|
|
3793
3838
|
if idx > 15: # drop timezone portion of the datetime.
|
|
3794
|
-
|
|
3795
|
-
|
|
3796
|
-
|
|
3797
|
-
|
|
3798
|
-
|
|
3799
|
-
|
|
3800
|
-
|
|
3801
|
-
|
|
3802
|
-
|
|
3839
|
+
try:
|
|
3840
|
+
event.EventDateTime = datetime.fromisoformat(attrValue[:idx])
|
|
3841
|
+
except Exception as ex:
|
|
3842
|
+
_logsi.LogException("Element value '%s' (%s) could not be parsed to a datetime object" % (elmDT, attrValue), ex, logToSystemLogger=False)
|
|
3843
|
+
event.EventDateTime = None
|
|
3844
|
+
break
|
|
3845
|
+
|
|
3846
|
+
# process event venue.
|
|
3847
|
+
# we will remove the <time> element, leaving only the innerText of the <p> element.
|
|
3848
|
+
for elmPT in elm.findall(".//p/time"):
|
|
3849
|
+
#xml_string_before = etree.tostring(elm, pretty_print=True, encoding="utf-8")
|
|
3850
|
+
elmPT.text = "" # remove <time> text value
|
|
3851
|
+
#xml_string_after = etree.tostring(elm, pretty_print=True, encoding="utf-8")
|
|
3852
|
+
break
|
|
3853
|
+
for elmP in elm.findall(".//p"):
|
|
3854
|
+
event.VenueName = _xmlGetInnerText(elmP)
|
|
3855
|
+
break
|
|
3803
3856
|
|
|
3804
|
-
|
|
3805
|
-
|
|
3806
|
-
|
|
3807
|
-
|
|
3857
|
+
# process event title.
|
|
3858
|
+
for elmDIV in elm.findall(".//div/div"):
|
|
3859
|
+
event.Title = _xmlGetInnerText(elmDIV)
|
|
3860
|
+
break
|
|
3808
3861
|
|
|
3809
|
-
|
|
3810
|
-
|
|
3862
|
+
# append event info to events collection.
|
|
3863
|
+
result.TourEvents.append(event)
|
|
3864
|
+
_logsi.LogObject(SILevel.Verbose, '%s: "%s" (%s)' % (type(event).__name__, event.Title, event.EventDateTime), event, excludeNonPublic=True)
|
|
3811
3865
|
|
|
3812
|
-
|
|
3813
|
-
|
|
3814
|
-
|
|
3815
|
-
|
|
3816
|
-
|
|
3866
|
+
# about information - links.
|
|
3867
|
+
result.AboutUrlFacebook = self._GetArtistInfoAboutLink(doc,"Facebook")
|
|
3868
|
+
result.AboutUrlInstagram = self._GetArtistInfoAboutLink(doc,"Instagram")
|
|
3869
|
+
result.AboutUrlTwitter = self._GetArtistInfoAboutLink(doc,"Twitter")
|
|
3870
|
+
result.AboutUrlWikipedia = self._GetArtistInfoAboutLink(doc,"Wikipedia")
|
|
3871
|
+
|
|
3872
|
+
except Exception as ex:
|
|
3873
|
+
|
|
3874
|
+
# trace parsing exceptions.
|
|
3875
|
+
_logsi.LogException(str(ex), ex, logToSystemLogger=False)
|
|
3817
3876
|
|
|
3818
3877
|
# trace.
|
|
3819
3878
|
_logsi.LogObject(SILevel.Verbose, TRACE_METHOD_RESULT_TYPE % (apiMethodName, type(result).__name__), result, excludeNonPublic=True)
|
|
@@ -7681,7 +7740,7 @@ class SpotifyClient:
|
|
|
7681
7740
|
result = Playlist()
|
|
7682
7741
|
result._Collaborative = False
|
|
7683
7742
|
result._Description = 'Spotify DJ Playlist'
|
|
7684
|
-
result._ExternalUrls._Spotify =
|
|
7743
|
+
result._ExternalUrls._Spotify = SPOTIFY_ONLINE_LINK_PREFIX + "/playlist/" + playlistId
|
|
7685
7744
|
result._Id = playlistId
|
|
7686
7745
|
result._Name = 'DJ'
|
|
7687
7746
|
result._Public = False
|
|
File without changes
|
|
File without changes
|
{spotifywebapiPython-1.0.115.dist-info → spotifywebapiPython-1.0.117.dist-info}/top_level.txt
RENAMED
|
File without changes
|