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