httpclient removed, no using better twisted.web.client
[enigma2-plugins.git] / lastfm / src / scrobbler.py
1 from re import sub
2 from datetime import datetime
3 from md5 import md5
4 from twisted.internet import reactor
5 from enigma import iServiceInformation, iPlayableService
6 from Components.config import config
7 from twisted.web.client import getPage
8
9 from urllib import  urlencode as urllib_urlencode
10
11 class LastFMScrobbler(object):
12     client     = "tst" # this must be changed to a own ID
13     version    = "1.0"
14     host        = "post.audioscrobbler.com"
15     port       = 80
16     loggedin  = False # indicates, if we are logged in
17
18     def __init__(self,user,password):
19         self.user = user
20         self.password = password
21         self.tracks2Submit = []
22     
23     def addTrack2Submit(self,track):
24         self.tracks2Submit.append(track)
25     
26     def removeTrack2Submit(self,track):
27         self.tracks2Submit.remove(track)
28         
29     def handshake(self):
30         print "[LastFMScrobbler] try logging into lastfm-submission-server"
31         url = "http://"+self.host+":"+str(self.port)+"?"+urllib_urlencode({
32             "hs":"true",
33             "p":"1.1",
34             "c":self.client,
35             "v":self.version,
36             "u":self.user
37             })
38         getPage(url).addCallback(self.handshakeCB).addErrback(self.handshakeCBError)
39
40     def handshakeCBError(self,data): 
41         self.failed(data.split("\n"))
42
43     def handshakeCB(self,data): 
44         result = data.split("\n")   
45         if result[0].startswith("BADUSER"):
46             return self.baduser(result[1:])
47         if result[0].startswith("UPTODATE"):
48             return self.uptodate(result[1:])
49         if result[0].startswith("FAILED"):
50             return self.failed(result)
51
52     def uptodate(self,lines):
53         self.md5 = sub("\n$","",lines[0])
54         self.submiturl = sub("\n$","",lines[1])
55         self.loggedin = True
56         print "[LastFMScrobbler] logged in"
57         
58     def baduser(self,lines):
59         print "[LastFMScrobbler] Bad user"
60         
61     def failed(self,lines):
62         print "[LastFMScrobbler] FAILED",lines[0]
63            
64     def submit(self):
65         if self.loggedin is False:
66             print "[LastFMScrobbler] Submitting cancled, because not logged in"
67             return False
68         tracks = self.tracks2Submit
69         print "[LastFMScrobbler] Submitting ",len(tracks)," tracks"
70         md5response = md5(md5(self.password).hexdigest()+self.md5).hexdigest()
71         post = {}
72         post["u"]=self.user
73         post["s"]=md5response
74         count = 0
75         for track in tracks:
76             track.urlencoded(post,count)
77             count += 1
78         (host,port) = self.submiturl.split("/")[2].split(":")
79         url = "http://"+host+":"+port+"/"+"/".join(self.submiturl.split("/")[3:])
80         data = self.encode(post)
81         getPage(url,method="POST",headers = {'Content-Type': "application/x-www-form-urlencoded",'Content-Length': str(len(data))},postdata=data).addCallback(self.submitCB).addErrback(self.submitCBError)
82     
83     def encode(self,postdict):
84         result=[]
85         for key,value in postdict.items():
86             result.append(key+"="+value)
87         return "&".join(result)
88
89     def submitCBError(self,data):
90         self.failed(data.split("\n"))
91         
92     def submitCB(self,data):
93         results = data.split("\n")
94         if results[0].startswith("OK"):
95             print "[LastFMScrobbler] Submitting successful"
96             self.tracks2Submit = []
97         if results[0].startswith("FAILED"):
98             print "[LastFMScrobbler] Submitting failed,",results[0]
99             self.failed([results[0],"INTERVAL 0"])
100
101 ############
102
103 class Track(object):
104     def __init__(self,artist,name,album,length=-1,mbid=None,tracktime=None):
105         self.params = {}
106         self.artist = artist
107         self.name = name
108         self.album = album
109         self.length = length
110         self.mbid = mbid
111         self.tracktime = tracktime
112         self.date = datetime.utcnow().strftime("%Y-%m-%d %H:%M:%S")
113         
114     def __repr__(self):
115         return "'"+self.name+"' by '"+self.artist+"' from '"+self.album+"'"
116
117     def urlencoded(self,encodedict,num):
118         if self.length is not None:
119             encodedict["l["+str(num)+"]"]=str(self.length)
120         else:
121             encodedict["l["+str(num)+"]"]=''
122         
123         if self.mbid is not None:
124             encodedic["m["+str(num)+"]"]=self.mbid
125         else:
126             encodedict["m["+str(num)+"]"]=''
127         
128         encodedict["i["+str(num)+"]"]=self.date
129         encodedict["a["+str(num)+"]"]=self.artist
130         encodedict["t["+str(num)+"]"]=self.name
131         encodedict["b["+str(num)+"]"]=self.album
132 ##########
133 class EventListener:
134     time2wait4submit = 30
135     
136     def __init__(self,session):
137         self.session = session
138         self.tracks_checking_for = []
139         self.scrobbler = LastFMScrobbler(config.plugins.LastFM.username.value,config.plugins.LastFM.password.value)
140         self.scrobbler.handshake()
141         
142     def onEvent(self,event):
143         if event == iPlayableService.evUpdatedInfo:
144             track = self.getCurrentServiceType()
145             try:
146                 self.tracks_checking_for.index(str(track))
147             except ValueError,e:
148                 if track is not False:
149                     self.tracks_checking_for.append(str(track))
150                     if track.length < self.time2wait4submit:
151                         waittime = self.time2wait4submit
152                     else:
153                         waittime = track.length/2
154                     print "[LastFMScrobbler] waiting",waittime,"sec. until checking if the track '"+str(track)+"' is still playing"
155                     reactor.callLater(waittime, self.checkTrack, track)
156
157     def startListenToEvents(self):
158         self.session.nav.event.append(self.onEvent)
159
160     def stopListentoEvents(self):
161         self.session.nav.event.remove(self.onEvent)
162     
163     def getCurrentServiceType(self):
164         currPlay = self.session.nav.getCurrentService()
165         sref=self.session.nav.getCurrentlyPlayingServiceReference()
166         if sref is None:
167             print "[LastFMScrobbler] CurrentlyPlayingServiceReference is None, not submitting to LastFM"
168             return False
169         elif sref.toString().startswith("4097:0:0:0:0:0:0:0:0:0:") is not True:
170             print "[LastFMScrobbler] CurrentlyPlayingServiceReference is not a File, not submitting to LastFM"
171             return False
172         elif sref.toString().endswith("lastfm.mp3") is True:
173             print "[LastFMScrobbler] LastFm-Plugin is playing, not submitting"
174             return False
175         elif currPlay is not None:
176             tracklength = -1
177             seek = currPlay and currPlay.seek()
178             if seek != None:
179                 r= seek.getLength()
180                 if not r[0]:
181                     tracklength = r[1] / 90000
182             return self.getTrack( artist = currPlay.info().getInfoString(iServiceInformation.sArtist),
183                                   title = currPlay.info().getInfoString(iServiceInformation.sTitle),
184                                   album = currPlay.info().getInfoString(iServiceInformation.sAlbum),
185                                   length = tracklength,
186                                  )
187              
188              
189     def getTrack(self , artist = None, title = None, album = None,length=-1):
190         if artist == "" or artist is None:
191             print "[LastFMScrobbler] CurrentlyPlayingServiceReference has no Artist, not submitting to LastFM"
192             return False
193         elif title == "" or title is None:
194             print "[LastFMScrobbler] CurrentlyPlayingServiceReference has no Tracktitle, not submitting to LastFM"
195             return False
196         else:
197             return Track(artist,title,album,length=length)
198             
199     
200     def checkTrack(self,track):
201         trackcurrent = self.getCurrentServiceType()
202         if str(track) == str(trackcurrent):
203             print "[LastFMScrobbler] sending track to lastfm as now playing... "+str(track)
204             self.scrobbler.addTrack2Submit(track)
205             self.scrobbler.submit()
206             self.tracks_checking_for.remove(str(track))
207         else:
208             print "[LastFMScrobbler] track is not playing, skipping sending "+str(track)
209