removed obsolet import
[enigma2-plugins.git] / lastfm / src / LastFM.py
1 import httpclient
2
3 import os
4 import md5 # to encode password
5 import string
6 import time
7 import urllib
8 import xml.dom.minidom
9
10
11
12 class LastFM:
13     DEFAULT_NAMESPACES = (
14         None, # RSS 0.91, 0.92, 0.93, 0.94, 2.0
15         'http://purl.org/rss/1.0/', # RSS 1.0
16         'http://my.netscape.com/rdf/simple/0.9/' # RSS 0.90
17     )
18     DUBLIN_CORE = ('http://purl.org/dc/elements/1.1/',)
19     
20     version = "1.0.1"
21     platform = "linux"
22     host = "ws.audioscrobbler.com"
23     port = 80
24     metadata = {}
25     info={}
26     cache_toptags= "/tmp/toptags"
27     
28     def __init__(self):
29         self.state = False # if logged in
30     
31     def _loadURL(self,url):
32         s = httpclient.httpclient(self.host, self.port)
33         s.req(url)
34         return s.response
35     
36     def connect(self,username,password):
37         self.info = self.parselines(self._loadURL("/radio/handshake.php?version=" + self.version + "&platform=" + self.platform + "&username=" + username + "&passwordmd5=" + self.hexify(md5.md5(password).digest())))
38         if self.info.has_key("session"):
39             self.session = self.info["session"]
40             if self.session.startswith("FAILED"):
41                 return False,self.info["msg"]
42             else:
43                 self.streamurl = self.info["stream_url"]
44                 self.baseurl = self.info["base_url"]
45                 self.basepath = self.info["base_path"]
46                 self.subscriber = self.info["subscriber"]
47                 self.framehack = self.info["base_path"]
48                 self.state = True
49                 return True,"loggedin"
50         else:
51             return False,"Could not login, wrong username or password?"
52         
53     def parselines(self, str):
54         res = {}
55         vars = string.split(str, "\n")
56         for v in vars:
57             x = string.split(string.rstrip(v), "=", 1)
58             if len(x) == 2:
59                 res[x[0]] = x[1]
60             elif x != [""]:
61                 print "(urk?", x, ")"
62         return res
63     
64     def getPersonalURL(self,username,level=50):
65         return "lastfm://user/%s/recommended/32"%username
66     
67     def getNeighboursURL(self,username):
68         return "lastfm://user/%s/neighbours"%username
69
70     def getLovedURL(self,username):
71         return "lastfm://user/%s/loved"%username
72     
73     def getSimilarArtistsURL(self,artist=None):
74         if artist is None and self.metadata.has_key('artist'):
75             return "lastfm://artist/%s/similarartists"%self.metadata['artist'].replace(" ","%20")
76         else:
77             return "lastfm://artist/%s/similarartists"%artist.replace(" ","%20")
78
79     def getArtistsLikedByFans(self,artist=None):
80         if artist is None and self.metadata.has_key('artist'):
81             return "lastfm://artist/%s/fans"%self.metadata['artist'].replace(" ","%20")
82         else:
83             return "lastfm://artist/%s/fans"%artist.replace(" ","%20")
84     
85     def getArtistGroup(self,artist=None):
86         if artist is None and self.metadata.has_key('artist'):
87             return "lastfm://group/%s"%self.metadata['artist'].replace(" ","%20")
88         else:
89             return "lastfm://group/%s"%artist.replace(" ","%20")
90     
91     def getmetadata(self):
92         if self.state is not True:
93             return False
94         else:
95             s = httpclient.httpclient(self.info["base_url"], 80)
96             s.req(self.info["base_path"] + "/np.php?session=" + self.info["session"])
97             tmp = self.parselines(s.response)
98             if tmp.has_key('\xef\xbb\xbfstreaming'):
99                 tmp["streaming"] = tmp['\xef\xbb\xbfstreaming']
100     
101             if tmp.has_key("streaming"):
102                 if tmp["streaming"] == "false" or (tmp["streaming"] == "true" and tmp.has_key("artist") and tmp.has_key("track") and tmp.has_key("trackduration")):
103                     if not tmp.has_key("album"):
104                         tmp["album"] = ""
105                         tmp["album_url"] = ""
106                     self.metadata = tmp
107                     self.metadatatime = time.time()
108                     self.metadataage = str(int(time.time() - self.metadatatime))
109                     #print self.metadata
110                     #print self.metadatatime
111                     #print "age",self.metadataage
112                     return True
113             return False
114
115     def command(self, cmd):
116         if self.state is not True:
117             return False
118         else:
119             try:
120                 # commands = skip, love, ban, rtp, nortp
121                 s = httpclient.httpclient(self.info["base_url"], 80)
122                 s.req(self.info["base_path"] + "/control.php?command=" + cmd + "&session=" + self.info["session"])
123                 res = self.parselines(s.response)
124                 if res["response"] != "OK":
125                     return True
126                 else:
127                     return False
128             except Exception,e:
129                 print "Error",e
130                 return False
131     
132     def hexify(self,s):
133         result = ""
134         for c in s:
135             result = result + ("%02x" % ord(c))
136         return result
137     
138     def love(self):
139         return self.command("love")
140
141     def ban(self):
142         return self.command("ban")
143
144     def skip(self):
145         return self.command("skip")
146     
147
148     def XMLgetElementsByTagName( self, node, tagName, possibleNamespaces=DEFAULT_NAMESPACES ):
149         for namespace in possibleNamespaces:
150             children = node.getElementsByTagNameNS(namespace, tagName)
151             if len(children): return children
152         return []
153
154     def XMLnode_data( self, node, tagName, possibleNamespaces=DEFAULT_NAMESPACES):
155         children = self.XMLgetElementsByTagName(node, tagName, possibleNamespaces)
156         node = len(children) and children[0] or None
157         return node and "".join([child.data.encode("utf-8") for child in node.childNodes]) or None
158
159     def XMLget_txt( self, node, tagName, default_txt="" ):
160         return self.XMLnode_data( node, tagName ) or self.XMLnode_data( node, tagName, self.DUBLIN_CORE ) or default_txt
161
162     def getGlobalTags( self ,force_reload=False):
163         if self.state is not True:
164             return []
165         else:
166             #TODO IOError
167             try: 
168                 if os.path.isfile(self.cache_toptags) is False or force_reload is True :
169                     s = httpclient.httpclient(self.info["base_url"], 80)
170                     s.req("/1.0/tag/toptags.xml")
171                     xmlsrc = s.response
172                     fp = open(self.cache_toptags,"w")
173                     fp.write(xmlsrc)
174                     fp.close()
175                 else:
176                     fp = open(self.cache_toptags)
177                     xmlsrc = fp.read()
178                     fp.close()
179                 rssDocument = xml.dom.minidom.parseString(xmlsrc)
180                 data =[]
181                 for node in self.XMLgetElementsByTagName(rssDocument, 'tag'):
182                     nodex={}
183                     nodex['_display'] = nodex['name'] = node.getAttribute("name").encode("utf-8")
184                     nodex['count'] =  node.getAttribute("count").encode("utf-8")
185                     nodex['stationurl'] = "lastfm://globaltags/"+node.getAttribute("name").encode("utf-8").replace(" ","%20")
186                     nodex['url'] =  node.getAttribute("url").encode("utf-8")
187                     data.append(nodex)
188                 return data
189             except xml.parsers.expat.ExpatError,e:
190                 print e
191                 return []
192
193     def getTopTracks(self,username):
194         if self.state is not True:
195             return []
196         else:
197             s = httpclient.httpclient(self.info["base_url"], 80)
198             s.req("/1.0/user/%s/toptracks.xml"%username)
199             return self._parseTracks(s.response)
200
201     def getRecentTracks(self,username):
202         if self.state is not True:
203             return []
204         else:
205             s = httpclient.httpclient(self.info["base_url"], 80)
206             s.req("/1.0/user/%s/recenttracks.xml"%username)
207             return self._parseTracks(s.response)
208     def getRecentLovedTracks(self,username):
209         if self.state is not True:
210             return []
211         else:
212             s = httpclient.httpclient(self.info["base_url"], 80)
213             s.req("/1.0/user/%s/recentlovedtracks.xml"%username)
214             return self._parseTracks(s.response)
215
216     def getRecentBannedTracks(self,username):
217         if self.state is not True:
218             return []
219         else:
220             s = httpclient.httpclient(self.info["base_url"], 80)
221             s.req("/1.0/user/%s/recentbannedtracks.xml"%username)
222             return self._parseTracks(s.response)
223
224     def _parseTracks(self,xmlrawdata):
225         #print xmlrawdata
226         try:
227             rssDocument = xml.dom.minidom.parseString(xmlrawdata)
228             data =[]
229             for node in self.XMLgetElementsByTagName(rssDocument, 'track'):
230                 nodex={}
231                 nodex['name'] = self.XMLget_txt(node, "name", "N/A" )
232                 nodex['artist'] =  self.XMLget_txt(node, "artist", "N/A" )
233                 nodex['playcount'] = self.XMLget_txt(node, "playcount", "N/A" )
234                 nodex['stationurl'] =  "lastfm://artist/"+nodex['artist'].replace(" ","%20")+"/"+nodex['name'].replace(" ","%20")
235                 nodex['url'] =  self.XMLget_txt(node, "url", "N/A" )
236                 nodex['_display'] = nodex['artist']+" - "+nodex['name']
237                 data.append(nodex)
238             return data
239         except xml.parsers.expat.ExpatError,e:
240             print e
241             return []
242
243     def getNeighbours(self,username):
244         if self.state is not True:
245             return []
246         else:
247             s = httpclient.httpclient(self.info["base_url"], 80)
248             s.req("/1.0/user/%s/neighbours.xml"%username)
249             return self._parseUser(s.response)
250
251     def getFriends(self,username):
252         if self.state is not True:
253             return []
254         else:
255             s = httpclient.httpclient(self.info["base_url"], 80)
256             s.req("/1.0/user/%s/friends.xml"%username)
257             return self._parseUser(s.response)
258
259     def _parseUser(self,xmlrawdata):
260         print xmlrawdata
261         try:
262             rssDocument = xml.dom.minidom.parseString(xmlrawdata)
263             data =[]
264             for node in self.XMLgetElementsByTagName(rssDocument, 'user'):
265                 nodex={}
266                 nodex['name'] = node.getAttribute("username").encode("utf-8")
267                 nodex['url'] =  self.XMLget_txt(node, "url", "N/A" )
268                 nodex['stationurl'] =  "lastfm://user/"+nodex['name']+"/personal"
269                 nodex['_display'] = nodex['name']
270                 data.append(nodex)
271             return data
272         except xml.parsers.expat.ExpatError,e:
273             print e
274             return []
275
276     def changestation(self, url):
277         print "#"*20,self.state
278         if self.state is not True:
279             return False
280         else:
281             s = httpclient.httpclient(self.info["base_url"], 80)
282             s.req(self.info["base_path"] + "/adjust.php?session=" + self.info["session"] + "&url=" + url)
283             res = self.parselines(s.response)
284             if res["response"] != "OK":
285                 print "station " + url + " returned:", res
286             return res