~mterry/+junk/sonata-app

« back to all changes in this revision

Viewing changes to app/soco/plugins/spotify.py

  • Committer: Michael Terry
  • Date: 2015-08-13 04:57:47 UTC
  • Revision ID: mike@mterry.name-20150813045747-cprj0rbleluyzs0r
Rough first pass

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
# -*- coding: utf-8 -*-
 
2
# pylint: disable=R0913,W0142
 
3
 
 
4
""" Spotify Plugin """
 
5
 
 
6
import requests
 
7
 
 
8
from ..xml import XML
 
9
from ..compat import quote_plus
 
10
from . import SoCoPlugin
 
11
 
 
12
 
 
13
__all__ = ['Spotify']
 
14
 
 
15
 
 
16
class SpotifyTrack(object):
 
17
 
 
18
    """ Class that represents a Spotify track
 
19
 
 
20
    usage example: SpotifyTrack('spotify:track:20DfkHC5grnKNJCzZQB6KC') """
 
21
 
 
22
    def __init__(self, spotify_uri):
 
23
        self.data = {}
 
24
        self.data['spotify_uri'] = spotify_uri
 
25
 
 
26
    @property
 
27
    def spotify_uri(self):
 
28
        """ The track's Spotify URI """
 
29
        return self.data['spotify_uri']
 
30
 
 
31
    @spotify_uri.setter
 
32
    def spotify_uri(self, uri):
 
33
        """ Set the track's Spotify URI """
 
34
        self.data['spotify_uri'] = uri
 
35
 
 
36
    @property
 
37
    def album_uri(self):
 
38
        """ The album's URI """
 
39
        return self.data['album_uri']
 
40
 
 
41
    @album_uri.setter
 
42
    def album_uri(self, uri):
 
43
        """ Set the album's URI """
 
44
        self.data['album_uri'] = uri
 
45
 
 
46
    @property
 
47
    def title(self):
 
48
        """ The track's title """
 
49
        return self.data['title']
 
50
 
 
51
    @title.setter
 
52
    def title(self, title):
 
53
        """ Set the track's title """
 
54
        self.data['title'] = title.encode('utf-8')
 
55
 
 
56
    @property
 
57
    def didl_metadata(self):
 
58
        """ DIDL Metadata """
 
59
        if ('spotify_uri' in self.data and 'title' in self.data and
 
60
                'album_uri' in self.data):
 
61
 
 
62
            didl_metadata = """\
 
63
<DIDL-Lite xmlns:dc="http://purl.org/dc/elements/1.1/"
 
64
           xmlns:upnp="urn:schemas-upnp-org:metadata-1-0/upnp/"
 
65
           xmlns:r="urn:schemas-rinconnetworks-com:metadata-1-0/"
 
66
           xmlns="urn:schemas-upnp-org:metadata-1-0/DIDL-Lite/">
 
67
    <item id="{0}" parentID="{1}" restricted="true">
 
68
        <dc:title>{2}</dc:title>
 
69
        <upnp:class>object.item.audioItem.musicTrack</upnp:class>
 
70
        <desc id="cdudn"
 
71
            nameSpace="urn:schemas-rinconnetworks-com:metadata-1-0/">
 
72
            SA_RINCON2311_X_#Svc2311-0-Token
 
73
        </desc>
 
74
    </item>
 
75
</DIDL-Lite>""".format(quote_plus(self.data['spotify_uri']),
 
76
                       quote_plus(self.data['album_uri']),
 
77
                       quote_plus(self.data['title']))
 
78
            didl_metadata = didl_metadata.encode('utf-8')
 
79
            return XML.fromstring(didl_metadata)
 
80
        else:
 
81
            return None
 
82
 
 
83
    @property
 
84
    def uri(self):
 
85
        """ Sonos-Spotify URI """
 
86
        if 'spotify_uri' in self.data:
 
87
            track = self.data['spotify_uri']
 
88
            track = track.encode('utf-8')
 
89
            track = quote_plus(track)
 
90
            return 'x-sonos-spotify:' + track
 
91
        else:
 
92
            return ''
 
93
 
 
94
    def satisfied(self):
 
95
        """ Checks if necessary track data is available """
 
96
        return 'title' in self.data and 'didl_metadata' in self.data
 
97
 
 
98
 
 
99
class SpotifyAlbum(object):
 
100
 
 
101
    """ Class that represents a Spotifyalbum
 
102
 
 
103
    usage example: SpotifyAlbum('spotify:album:6a50SaJpvdWDp13t0wUcPU') """
 
104
 
 
105
    def __init__(self, spotify_uri):
 
106
        self.data = {}
 
107
        self.data['spotify_uri'] = spotify_uri
 
108
 
 
109
    @property
 
110
    def spotify_uri(self):
 
111
        """ The album's Spotify URI """
 
112
        return self.data['spotify_uri']
 
113
 
 
114
    @spotify_uri.setter
 
115
    def spotify_uri(self, uri):
 
116
        """ Set the album's Spotify URI """
 
117
        self.data['spotify_uri'] = uri
 
118
 
 
119
    @property
 
120
    def artist_uri(self):
 
121
        """ The artist's URI """
 
122
        return self.data['artist_uri']
 
123
 
 
124
    @artist_uri.setter
 
125
    def artist_uri(self, artist_uri):
 
126
        """ Set the artist's URI """
 
127
        self.data['artist_uri'] = artist_uri
 
128
 
 
129
    @property
 
130
    def title(self):
 
131
        """ The album's title """
 
132
        return self.data['title']
 
133
 
 
134
    @title.setter
 
135
    def title(self, title):
 
136
        """ Set the album's title """
 
137
        self.data['title'] = title.encode('utf-8')
 
138
 
 
139
    @property
 
140
    def uri(self):
 
141
        """ Sonos-Spotify URI """
 
142
        if 'spotify_uri' in self.data:
 
143
            album = self.data['spotify_uri']
 
144
            album = album.encode('utf-8')
 
145
            album = quote_plus(album)
 
146
            return "x-rincon-cpcontainer:" + album
 
147
        else:
 
148
            return ""
 
149
 
 
150
    @property
 
151
    def didl_metadata(self):
 
152
        """ DIDL Metadata """
 
153
        if self.satisfied:
 
154
            didl_metadata = """\
 
155
<DIDL-Lite xmlns:dc="http://purl.org/dc/elements/1.1/"
 
156
           xmlns:upnp="urn:schemas-upnp-org:metadata-1-0/upnp/"
 
157
           xmlns:r="urn:schemas-rinconnetworks-com:metadata-1-0/"
 
158
           xmlns="urn:schemas-upnp-org:metadata-1-0/DIDL-Lite/">
 
159
    <item id="{0}" parentID="{1}" restricted="true">
 
160
        <dc:title>{2}</dc:title>
 
161
        <upnp:class>object.container.album.musicAlbum</upnp:class>
 
162
        <desc id="cdudn"
 
163
              nameSpace="urn:schemas-rinconnetworks-com:metadata-1-0/">
 
164
            SA_RINCON2311_X_#Svc2311-0-Token
 
165
        </desc>
 
166
    </item>
 
167
</DIDL-Lite>""".format(quote_plus(self.data['spotify_uri']),
 
168
                       quote_plus(self.data['artist_uri']),
 
169
                       quote_plus(self.data['title']))
 
170
            didl_metadata = didl_metadata.encode('utf-8')
 
171
            return XML.fromstring(didl_metadata)
 
172
        else:
 
173
            return None
 
174
 
 
175
    def satisfied(self):
 
176
        """ Checks if necessary album data is available """
 
177
        return ('spotify_uri' in self.data and
 
178
                'artist' in self.data and
 
179
                'title' in self.data)
 
180
 
 
181
 
 
182
class Spotify(SoCoPlugin):
 
183
 
 
184
    """ Class that implements spotify plugin"""
 
185
 
 
186
    sid = '9'
 
187
    api_lookup_url = 'http://ws.spotify.com/lookup/1/.json'
 
188
 
 
189
    def __init__(self, soco):
 
190
        """ Initialize the plugin"""
 
191
        super(Spotify, self).__init__(soco)
 
192
 
 
193
    @property
 
194
    def name(self):
 
195
        return 'Spotify plugin'
 
196
 
 
197
    def _add_track_metadata(self, spotify_track):
 
198
        """ Adds metadata by using the spotify public API """
 
199
        track = SpotifyTrack(spotify_track.spotify_uri)
 
200
        params = {'uri': spotify_track.spotify_uri}
 
201
        res = requests.get(self.api_lookup_url, params=params)
 
202
        data = res.json()
 
203
 
 
204
        if 'track' in data:
 
205
            track.title = data['track']['name']
 
206
            track.album_uri = data['track']['album']['href']
 
207
 
 
208
        return track
 
209
 
 
210
    def _add_album_metadata(self, spotify_album):
 
211
        """ Adds metadata by using the spotify public API """
 
212
        album = SpotifyAlbum(spotify_album.spotify_uri)
 
213
        params = {'uri': spotify_album.spotify_uri}
 
214
        res = requests.get(self.api_lookup_url, params=params)
 
215
        data = res.json()
 
216
 
 
217
        if 'album' in data:
 
218
            album.title = data['album']['name']
 
219
            album.artist_uri = data['album']['artist-id']
 
220
 
 
221
        return album
 
222
 
 
223
    def add_track_to_queue(self, spotify_track):
 
224
        """ Add a spotify track to the queue using the SpotifyTrack class"""
 
225
        if not spotify_track.satisfied():
 
226
            spotify_track = self._add_track_metadata(spotify_track)
 
227
 
 
228
        return self.soco.add_to_queue(spotify_track)
 
229
 
 
230
    def add_album_to_queue(self, spotify_album):
 
231
        """ Add a spotify album to the queue using the SpotifyAlbum class """
 
232
        if not spotify_album.satisfied():
 
233
            spotify_album = self._add_album_metadata(spotify_album)
 
234
 
 
235
        return self.soco.add_to_queue(spotify_album)