always set actual session to Streamplayer on LastFMScreenMain start
[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,streamplayer):
137         self.session = session
138         self.streamplayer = streamplayer
139         
140         self.tracks_checking_for = []
141         self.scrobbler = LastFMScrobbler(config.plugins.LastFM.username.value,config.plugins.LastFM.password.value)
142         self.scrobbler.handshake()
143         
144     def onEvent(self,event):
145         if event == iPlayableService.evUpdatedInfo:
146             track = self.getCurrentServiceType()
147             try:
148                 self.tracks_checking_for.index(str(track))
149             except ValueError,e:
150                 if track is not False:
151                     self.tracks_checking_for.append(str(track))
152                     if track.length < self.time2wait4submit:
153                         waittime = self.time2wait4submit
154                     else:
155                         waittime = track.length/2
156                     print "[LastFMScrobbler] waiting",waittime,"sec. until checking if the track "+str(track)+" is still playing"
157                     reactor.callLater(waittime, self.checkTrack, track)
158
159     def startListenToEvents(self):
160         self.session.nav.event.append(self.onEvent)
161
162     def stopListentoEvents(self):
163         self.session.nav.event.remove(self.onEvent)
164     
165     def getCurrentServiceType(self):
166         currPlay = self.session.nav.getCurrentService()
167         sref=self.session.nav.getCurrentlyPlayingServiceReference()
168         if sref is None:
169             print "[LastFMScrobbler] CurrentlyPlayingServiceReference is None, not submitting to LastFM"
170             return False
171         elif sref.toString().startswith("4097:0:0:0:0:0:0:0:0:0:") is not True:
172             print "[LastFMScrobbler] CurrentlyPlayingServiceReference is not a File, not submitting to LastFM"
173             return False
174         elif sref.toString().endswith("lastfm.mp3") is True:
175             print "[LastFMScrobbler] LastFm-Plugin is playing"
176             trdata= self.streamplayer.playlist.getTrack(self.streamplayer.currentplaylistitemnumber)
177             track = self.getTrack(artist=trdata['creator'],title=trdata['title'],album=trdata['album'],length=(trdata["duration"]/1000))
178             return track
179         elif currPlay is not None:
180             tracklength = -1
181             seek = currPlay and currPlay.seek()
182             if seek != None:
183                 r= seek.getLength()
184                 if not r[0]:
185                     tracklength = r[1] / 90000
186             return self.getTrack( artist = currPlay.info().getInfoString(iServiceInformation.sArtist),
187                                   title = currPlay.info().getInfoString(iServiceInformation.sTitle),
188                                   album = currPlay.info().getInfoString(iServiceInformation.sAlbum),
189                                   length = tracklength,
190                                  )
191              
192              
193     def getTrack(self , artist = None, title = None, album = None,length=-1):
194         if artist == "" or artist is None:
195             print "[LastFMScrobbler] CurrentlyPlayingServiceReference has no Artist, not submitting to LastFM"
196             return False
197         elif title == "" or title is None:
198             print "[LastFMScrobbler] CurrentlyPlayingServiceReference has no Tracktitle, not submitting to LastFM"
199             return False
200         else:
201             return Track(artist,title,album,length=length)
202             
203     
204     def checkTrack(self,track):
205         trackcurrent = self.getCurrentServiceType()
206         if str(track) == str(trackcurrent):
207             print "[LastFMScrobbler] sending track to lastfm as now playing... "+str(track)
208             self.scrobbler.addTrack2Submit(track)
209             self.scrobbler.submit()
210             self.tracks_checking_for.remove(str(track))
211         else:
212             print "[LastFMScrobbler] track is not playing, skipping sending "+str(track)
213