fix Bug. now clears the streamplayerCB on Screen.close()
[enigma2-plugins.git] / lastfm / src / plugin.py
1 from enigma import eTimer
2 from enigma import eConsoleAppContainer
3 from enigma import loadPic
4
5 from Screens.Screen import Screen
6 from Screens.MessageBox import MessageBox
7 from Screens.HelpMenu import HelpableScreen , HelpMenu              
8
9 from Components.Pixmap import Pixmap,MovingPixmap
10 from Components.Label import Label
11 from Components.MenuList import MenuList
12 from Components.ActionMap import ActionMap, NumberActionMap
13 from Components.config import config, ConfigSubsection, ConfigSlider,ConfigInteger,ConfigYesNo, ConfigText
14 from Components.HelpMenuList import HelpMenuList
15
16
17 from Tools import Notifications
18
19 from Plugins.Plugin import PluginDescriptor
20
21 from StreamPlayer import StreamPlayer
22 from LastFMConfig import LastFMConfigScreen
23 from LastFM import LastFM, lastfm_event_register
24 import httpclient
25 import os
26 import urllib
27
28 import random
29
30 ###############################################################################        
31 plugin_path = ""
32
33 ###############################################################################        
34
35 config.plugins.LastFM = ConfigSubsection()
36 config.plugins.LastFM.showcoverart = ConfigYesNo(default = True)
37 config.plugins.LastFM.username = ConfigText("user",fixed_size=False)
38 config.plugins.LastFM.password = ConfigText("passwd",fixed_size=False)
39 config.plugins.LastFM.timeoutstatustext = ConfigInteger(3,limits = (0, 10))
40 config.plugins.LastFM.timeouttabselect = ConfigInteger(2,limits = (0, 10))
41 config.plugins.LastFM.metadatarefreshinterval = ConfigInteger(5,limits = (0, 100))
42 config.plugins.LastFM.recommendedlevel = ConfigInteger(3,limits = (0, 100))
43 config.plugins.LastFM.sendSubmissions = ConfigYesNo(default = False)
44
45 config.plugins.LastFM.sreensaver = ConfigSubsection()
46 config.plugins.LastFM.sreensaver.use = ConfigYesNo(default = True)
47 config.plugins.LastFM.sreensaver.wait = ConfigInteger(30,limits = (0, 1000))
48 config.plugins.LastFM.sreensaver.showcoverart = ConfigYesNo(default = True)
49 config.plugins.LastFM.sreensaver.coverartanimation = ConfigYesNo(default = True)
50 config.plugins.LastFM.sreensaver.coverartspeed = ConfigInteger(10,limits = (0, 100))
51 config.plugins.LastFM.sreensaver.coverartinterval = ConfigInteger(10,limits = (0, 100))
52
53 ###############################################################################        
54 def main(session,**kwargs):
55     session.open(LastFMScreenMain)    
56         
57 def startScrobbler(reason, **kwargs):
58     if "session" in kwargs and config.plugins.LastFM.sendSubmissions.value:
59         from scrobbler import EventListener
60         evl = EventListener(kwargs["session"])
61         evl.startListenToEvents()
62         
63 def Plugins(path,**kwargs):
64     global plugin_path
65     plugin_path = path
66     return [PluginDescriptor(
67         name="Last.FM", 
68         description="the social music revolution", 
69         where = PluginDescriptor.WHERE_PLUGINMENU,
70         fnc = main
71         ),
72         PluginDescriptor(where = [PluginDescriptor.WHERE_SESSIONSTART, PluginDescriptor.WHERE_AUTOSTART], fnc = startScrobbler)
73         ]
74 ############################################################################### 
75 class LastFMScreenMain(Screen,HelpableScreen,LastFM):
76     skin = """
77         <screen position="110,83" size="530,430" title="Last.FM" >
78             
79             <widget name="artist" position="0,0" size="70,30" valign=\"center\" halign=\"left\" zPosition=\"2\"  foregroundColor=\"white\" font=\"Regular;18\" />          
80             <widget name="album" position="0,40" size="70,30" valign=\"center\" halign=\"left\" zPosition=\"2\"  foregroundColor=\"white\" font=\"Regular;18\" />          
81             <widget name="track" position="0,80" size="70,30" valign=\"center\" halign=\"left\" zPosition=\"2\"  foregroundColor=\"white\" font=\"Regular;18\" />          
82             
83             <widget name="info_artist" position="70,0" size="344,30" valign=\"center\" halign=\"left\" zPosition=\"2\"  foregroundColor=\"white\" font=\"Regular;18\" />          
84             <widget name="info_album" position="70,40" size="344,30" valign=\"center\" halign=\"left\" zPosition=\"2\"  foregroundColor=\"white\" font=\"Regular;18\" />          
85             <widget name="info_track" position="70,80" size="344,30" valign=\"center\" halign=\"left\" zPosition=\"2\"  foregroundColor=\"white\" font=\"Regular;18\" />          
86             <widget name="info_cover" position="414,0" size="116,116" />          
87             
88             <widget name="tablist" position="0,120" size="150,260" scrollbarMode="showOnDemand" />            
89             <widget name="streamlist" position="150,120" size="380,260" scrollbarMode="showOnDemand" />            
90             
91             <widget name="button_red" position="10,400" size="60,30" backgroundColor=\"red\" valign=\"center\" halign=\"center\" zPosition=\"2\"  foregroundColor=\"white\" font=\"Regular;18\" />          
92             <widget name="button_green" position="80,400" size="60,30" backgroundColor=\"green\" valign=\"center\" halign=\"center\" zPosition=\"2\"  foregroundColor=\"white\" font=\"Regular;18\"/>            
93             <widget name="button_yellow" position="150,400" size="60,30" backgroundColor=\"yellow\" valign=\"center\" halign=\"center\" zPosition=\"2\"  foregroundColor=\"white\" font=\"Regular;18\" />            
94             <widget name="button_blue" position="220,400" size="60,30" backgroundColor=\"blue\" valign=\"center\" halign=\"center\" zPosition=\"2\"  foregroundColor=\"white\" font=\"Regular;18\" />            
95             <widget name="infolabel" position="290,400" size="290,30" valign=\"center\" halign=\"center\" zPosition=\"2\"  foregroundColor=\"white\" font=\"Regular;18\" />           
96         </screen>"""
97          
98     noCoverArtPNG = "/usr/share/enigma2/no_coverArt.png"
99     
100     def __init__(self, session, args = 0):
101         self.skin = LastFMScreenMain.skin
102         Screen.__init__(self, session)
103         HelpableScreen.__init__(self)
104         LastFM.__init__(self)
105         self.session = session
106         self.streamplayer = StreamPlayer(session)
107         self.streamplayer.onStateChanged.append(self.updateGUI)
108         self.imageconverter = ImageConverter(116,116,self.setCoverArt)
109         Screen.__init__(self, session)
110         
111         self.tabs=[("personal Stations",self.loadPersonalStations)
112                    ,("Global Tags",self.loadGlobalTags)
113                    ,("Top Tracks",self.loadTopTracks)
114                    ,("Recent Tracks",self.loadRecentTracks)
115                    ,("Loved Tracks",self.loadLovedTracks)
116                    ,("Banned Tracks",self.loadBannedTracks)
117                    ,("Friends",self.loadFriends)
118                    ,("Neighbours",self.loadNeighbours)
119                    ]
120         tablist =[]
121         for tab in self.tabs:
122             tablist.append((tab[0],tab))
123         self.tablist = MenuList(tablist)
124         self.tablist.onSelectionChanged.append(self.action_TabChanged)
125         
126         self["artist"] = Label(_("Artist")+":")
127         self["album"] = Label(_("Album")+":")
128         self["track"] = Label(_("Track")+":")
129         
130         self["info_artist"] = Label("N/A")
131         self["info_album"] = Label("N/A")
132         self["info_track"] = Label("N/A")
133         self["info_cover"] = Pixmap()
134         
135         self["tablist"] = self.tablist
136         self["streamlist"] = MenuList([])
137         
138         self["button_red"] = Label(_("play"))
139         self["button_green"] = Label(_("skip"))
140         self["button_yellow"] = Label(_("love"))
141         self["button_blue"] = Label(_("ban"))
142         self["infolabel"] = Label("")
143         
144         self["actions"] = ActionMap(["InfobarChannelSelection","WizardActions", "DirectionActions","MenuActions","ShortcutActions","GlobalActions","HelpActions","NumberActions"], 
145             {
146              "ok": self.action_ok,
147              "back": self.action_exit,
148              "red": self.action_startstop,
149              "green": self.skip,
150              "yellow": self.love,
151              "blue": self.ban ,
152              "historyNext": self.action_nextTab,
153              "historyBack": self.action_prevTab,
154              
155              "menu": self.action_menu,
156              }, -1)
157         
158         self.helpList.append((self["actions"], "WizardActions", [("ok", _("switch to selected Station"))]))
159         self.helpList.append((self["actions"], "WizardActions", [("back", _("quit Last.FM"))]))
160
161         self.helpList.append((self["actions"], "MenuActions", [("menu", _("open Configuration"))]))
162
163         self.helpList.append((self["actions"], "ShortcutActions", [("red", _("start/stop streaming"))]))
164         self.helpList.append((self["actions"], "ShortcutActions", [("green", _("skip current Track"))]))
165         self.helpList.append((self["actions"], "ShortcutActions", [("yellow", _("mark current Track as loved"))]))
166         self.helpList.append((self["actions"], "ShortcutActions", [("blue", _("ban Track, never play again"))]))
167         self.helpList.append((self["actions"], "InfobarChannelSelection", [("historyNext", _("select next Tab"))]))
168         self.helpList.append((self["actions"], "InfobarChannelSelection", [("historyBack", _("select prev Tab"))]))
169
170         self.onLayoutFinish.append(self.initLastFM)
171         self.onLayoutFinish.append(self.tabchangedtimerFired)
172         self.onLayoutFinish.append(self.setCoverArt)
173         
174         self.guiupdatetimer = eTimer()
175         self.guiupdatetimer.timeout.get().append(self.guiupdatetimerFired)
176         self.guiupdatetimer.start(config.plugins.LastFM.metadatarefreshinterval.value*1000)
177         
178         self.tabchangetimer = eTimer()
179         self.tabchangetimer.timeout.get().append(self.tabchangedtimerFired)
180         
181         self.infolabelcleartimer = eTimer()
182         self.infolabelcleartimer.timeout.get().append(self.clearInfoLabel)
183
184         self.screensavertimer = eTimer()
185         self.screensavertimer.timeout.get().append(self.startScreensaver)
186         self.onShown.append(self.startScreensaverTimer)
187
188     def initLastFM(self):
189         self.setInfoLabel("loggin into last.fm")
190         self.connect(config.plugins.LastFM.username.value,config.plugins.LastFM.password.value)
191     
192     def onConnectSuccessful(self,text):
193         self.setInfoLabel("login successful")      
194     
195     def onConnectFailed(self,text):
196         self.setInfoLabel("login failed! "+text,timeout=False)
197
198     def onTrackSkiped(self,reason):
199         self.setInfoLabel("Track skiped")
200
201     def onTrackLoved(self,reason):
202         self.setInfoLabel("Track loved")
203     
204     def onTrackBaned(self,reason):
205         self.setInfoLabel("Track baned")
206     
207     def onCommandFailed(self,reason):
208         self.setInfoLabel(reason)
209
210     def onGlobalTagsLoaded(self,tags):
211         self.setInfoLabel("Global Tags loaded")
212         self.buildMenuList(tags)
213
214     def onTopTracksLoaded(self,tracks):
215         self.setInfoLabel("Top Tracks loaded")
216         self.buildMenuList(tracks)
217
218     def onRecentTracksLoaded(self,tracks):
219         self.setInfoLabel("recent Tracks loaded")
220         self.buildMenuList(tracks)
221         
222     def onRecentBannedTracksLoaded(self,tracks):
223         self.setInfoLabel("banned Tracks loaded")
224         self.buildMenuList(tracks)
225
226     def onRecentLovedTracksLoaded(self,tracks):
227         self.setInfoLabel("loved Tracks loaded")
228         self.buildMenuList(tracks)
229
230     def onNeighboursLoaded(self,user):
231         self.setInfoLabel("Neighbours loaded")
232         self.buildMenuList(user)
233
234     def onFriendsLoaded(self,user):
235         self.setInfoLabel("Friends loaded")
236         self.buildMenuList(user)
237     
238     def onStationChanged(self,reason):
239         self.setInfoLabel(reason)    
240         
241     def onMetadataLoaded(self,metadata):
242         self.updateGUI()
243         self.guiupdatetimer.start(config.plugins.LastFM.metadatarefreshinterval.value*1000)
244
245     def action_TabChanged(self):
246         self.tabchangetimer.stop()
247         self.tabchangetimer.start(config.plugins.LastFM.timeouttabselect.value*1000)
248         
249     def guiupdatetimerFired(self):
250         if self.streamplayer.is_playing:
251             self.getMetadata()
252         self.guiupdatetimer.start(config.plugins.LastFM.metadatarefreshinterval.value*1000)
253         
254     def tabchangedtimerFired(self):
255         self.tablist.getCurrent()[1][1]()
256         self.tabchangetimer.stop()
257
258     def startScreensaverTimer(self):
259         if config.plugins.LastFM.sreensaver.use.value:
260             self.screensavertimer.start(config.plugins.LastFM.sreensaver.wait.value*1000)
261
262     def resetScreensaverTimer(self):
263         self.screensavertimer.stop()
264         self.screensavertimer.start(config.plugins.LastFM.sreensaver.wait.value*1000)
265         
266     def startScreensaver(self):
267         self.screensavertimer.stop()
268         self.session.openWithCallback(self.updateGUI, LastFMSaveScreen,self.metadata)
269            
270     def action_nextTab(self):
271         self.tablist.down()
272         self.resetScreensaverTimer()
273         
274     def action_prevTab(self):
275         self.tablist.up()
276         self.resetScreensaverTimer()
277
278     def action_menu(self):
279         self.session.open(LastFMConfigScreen)
280         self.resetScreensaverTimer()
281
282     def action_exit(self):
283         self.screensavertimer.stop()
284         self.guiupdatetimer.stop()
285         self.streamplayer.stop()
286         self.streamplayer.onStateChanged=[]
287         
288         self.close()
289
290     def action_ok(self):
291         x = self["streamlist"].l.getCurrentSelection()
292         if x is None:
293             pass
294         elif len(x) >1:
295             self.changeStation(x[1])
296             self.resetScreensaverTimer()
297
298     def action_startstop(self):
299         self.resetScreensaverTimer()
300         if self.streamplayer.is_playing:
301             self.streamplayer.stop()
302             self.metadata = {}
303             self.setInfoLabel("stream stopped")
304         
305         else:
306             self.setInfoLabel("starting stream",timeout=True)
307             if self.info.has_key("stream_url"):
308                 self.streamplayer.play(self.info["stream_url"])
309                 self.guiupdatetimer.start(config.plugins.LastFM.metadatarefreshinterval.value*1000)
310
311     def setInfoLabel(self,text,timeout=True):
312         self.infolabelcleartimer.stop() 
313         self["infolabel"].setText(text)
314         if timeout is True:
315             self.infolabelcleartimer.start(config.plugins.LastFM.timeoutstatustext.value*1000)
316             
317     def clearInfoLabel(self):
318         self["infolabel"].setText("")
319         
320     def updateGUI(self):
321         print "updateGUI"
322         if self.streamplayer.is_playing is True:
323             self["button_red"].setText(_("stop"))
324         else:
325             self["button_red"].setText(_("play"))            
326         
327         if self.streamplayer.is_playing is not True or self.shown is not True:
328             return None
329             
330         if self.metadata.has_key("station"):
331             self.setTitle("Last.FM: "+self.metadata["station"])
332         else:
333             self.setTitle("Last.FM")
334
335         if self.metadata.has_key("artist"):
336             self["info_artist"].setText(self.metadata["artist"])
337         else:
338             self["info_artist"].setText("N/A")
339
340         if self.metadata.has_key("album"):
341             self["info_album"].setText(self.metadata["album"])
342         else:
343             self["info_album"].setText("N/A")
344
345         if self.metadata.has_key("track"):
346             self["info_track"].setText(self.metadata["track"])
347         else:
348             self["info_track"].setText("N/A")
349         
350         if self.metadata.has_key("albumcover_large") and config.plugins.LastFM.showcoverart.value:
351             self.imageconverter.convert(self.metadata["albumcover_large"])
352         elif self.metadata.has_key("albumcover_medium") and config.plugins.LastFM.showcoverart.value:
353             self.imageconverter.convert(self.metadata["albumcover_medium"])
354         elif self.metadata.has_key("albumcover_small") and config.plugins.LastFM.showcoverart.value:
355             self.imageconverter.convert(self.metadata["albumcover_small"],self.setCoverArt)
356         else:
357             self.setCoverArt()
358         
359         if self.streamplayer.is_playing is not True:
360             self.setTitle(myname)
361             self.setCoverArt()
362             self["info_artist"].setText("N/A")
363             self["info_album"].setText("N/A")
364             self["info_track"].setText("N/A")
365         
366     def setCoverArt(self,pixmap=None):
367         if pixmap is None:
368             self["info_cover"].instance.setPixmapFromFile(self.noCoverArtPNG)            
369         else:
370             self["info_cover"].instance.setPixmap(pixmap.__deref__())
371     
372
373     def loadPersonalStations(self):
374         tags = []
375         x= {}
376         x["_display"] = "Personal Radio"
377         x["stationurl"] = self.getPersonalURL(config.plugins.LastFM.username.value,level=config.plugins.LastFM.recommendedlevel.value)
378         tags.append(x)
379         
380         x= {}
381         x["_display"] = "Neighbours Tracks"
382         x["stationurl"] = self.getNeighboursURL(config.plugins.LastFM.username.value)
383         tags.append(x)
384         
385         x= {}
386         x["_display"] = "Loved Tracks"
387         x["stationurl"] = self.getLovedURL(config.plugins.LastFM.username.value)
388         tags.append(x)
389         
390         if self.metadata.has_key("artist"):
391             x= {}
392             x["_display"] = "similar Tracks of current Artist"
393             x["stationurl"] = self.getSimilarArtistsURL()
394             tags.append(x)
395             
396             x= {}
397             x["_display"] = "Tracks liked by Fans of current Track"
398             x["stationurl"] = self.getArtistsLikedByFans()
399             tags.append(x)
400
401             x= {}
402             x["_display"] = "Group of Artist of current Track"
403             x["stationurl"] = self.getArtistGroup()
404             tags.append(x)
405         
406         self.buildMenuList(tags)
407         
408     def loadGlobalTags(self):
409         self.setInfoLabel("loading Global Tags")
410         tags = self.getGlobalTags()
411
412     def loadTopTracks(self):
413         self.setInfoLabel("loading Top Tacks")
414         tracks = self.getTopTracks(config.plugins.LastFM.username.value)
415
416     def loadRecentTracks(self):
417         self.setInfoLabel("loading recent Tracks")
418         tracks = self.getRecentTracks(config.plugins.LastFM.username.value)
419
420     def loadLovedTracks(self):
421         self.setInfoLabel("loading loved Tracks")
422         tracks = self.getRecentLovedTracks(config.plugins.LastFM.username.value)
423
424     def loadBannedTracks(self):
425         self.setInfoLabel("loading loved Tracks")
426         tracks = self.getRecentBannedTracks(config.plugins.LastFM.username.value)
427         
428     def loadNeighbours(self):
429         self.setInfoLabel("loading Neighbours")
430         tracks = self.getNeighbours(config.plugins.LastFM.username.value)
431
432     def loadFriends(self):
433         self.setInfoLabel("loading Friends")
434         tracks = self.getFriends(config.plugins.LastFM.username.value)
435
436     def buildMenuList(self,items):
437         menuliste = []
438         for i in items:
439             menuliste.append((i['_display'],i['stationurl']))
440         self["streamlist"].l.setList(menuliste) 
441
442 class LastFMSaveScreen(Screen):
443     skin = """<screen position="0,0" size="720,576" flags="wfNoBorder" title="LastFMSaveScreen" >
444                 <widget name="cover" position="50,50" size="200,200" />          
445               </screen>"""
446     noCoverArtPNG = "/usr/share/enigma2/no_coverArt.png"
447     coverartsize= [200,200]
448     def __init__(self,session,initialMetadata):
449         self.skin = """<screen position="0,0" size="720,576" flags="wfNoBorder" title="LastFMSaveScreen" >
450                 <widget name="cover" position="50,50" size="%i,%i" />          
451               </screen>"""%(self.coverartsize[0],self.coverartsize[1])
452     
453         Screen.__init__(self,session)
454         self.imageconverter = ImageConverter(self.coverartsize[0],self.coverartsize[1],self.setCoverArt)
455         self.session = session
456         self.initialMetadata = initialMetadata
457         self["cover"] = MovingPixmap()
458         self["actions"] = ActionMap(["InfobarChannelSelection","WizardActions", "DirectionActions","MenuActions","ShortcutActions","GlobalActions","HelpActions"], 
459             {
460              "ok": self.action_exit,
461              "back": self.action_exit,
462              }, -1)
463         
464         self.onLayoutFinish.append(self.update)
465         self.onLayoutFinish.append(self.registerToMetadataUpdates)
466         
467         if config.plugins.LastFM.sreensaver.coverartanimation.value:
468             self.startmovingtimer = eTimer()
469             self.startmovingtimer.timeout.get().append(self.movePixmap)
470             self.startmovingtimer.start(config.plugins.LastFM.sreensaver.coverartinterval.value*1000)
471         
472     def action_ok(self):
473         pass
474     
475     def action_exit(self):
476         lastfm_event_register.removeOnMetadataChanged(self.update)
477         self.close()
478         
479     def setCoverArt(self,pixmap=None):
480         if pixmap is None:
481             self["cover"].instance.setPixmapFromFile(self.noCoverArtPNG)            
482         else:
483             self["cover"].instance.setPixmap(pixmap.__deref__())
484             
485     def registerToMetadataUpdates(self):
486         lastfm_event_register.addOnMetadataChanged(self.update)#added here, to make shure that is called after onLayoutFinished
487         
488     
489     def update(self,metadata=None):
490         
491         if metadata is None:
492             metadata = self.initialMetadata
493             
494         if config.plugins.LastFM.sreensaver.showcoverart.value is not True:
495             pass#do nothing
496         elif metadata.has_key("albumcover_large") and config.plugins.LastFM.showcoverart.value:
497             self.imageconverter.convert(metadata["albumcover_large"])
498         elif metadata.has_key("albumcover_medium") and config.plugins.LastFM.showcoverart.value:
499             self.imageconverter.convert(metadata["albumcover_medium"])
500         elif metadata.has_key("albumcover_small") and config.plugins.LastFM.showcoverart.value:
501             self.imageconverter.convert(metadata["albumcover_small"],self.setCoverArt)
502         else:
503             self.setCoverArt()
504
505     def movePixmap(self):
506         self.startmovingtimer.stop() 
507         newX = random.randrange(720-self.coverartsize[0]-1)
508         newY = random.randrange(576-self.coverartsize[1]-1)
509         self["cover"].moveTo(newX, newY, time = config.plugins.LastFM.sreensaver.coverartspeed.value)
510         self["cover"].startMoving()
511         self.startmovingtimer.start(config.plugins.LastFM.sreensaver.coverartinterval.value*1000)
512
513 class ImageConverter:
514     
515     lastURL = ""
516
517     def __init__(self,width,height,callBack):
518         self.callBack = callBack
519         self.width = width
520         self.height = height
521         self.targetfile= "/tmp/coverart"+str(random.randrange(5000))
522     
523         
524     def convert(self,sourceURL):
525         if self.lastURL != sourceURL:
526             extension = sourceURL.split(".")[-1]
527             self.tmpfile = self.targetfile+"."+extension
528             httpclient.getFile(self.tmpfile,sourceURL,callback=self.onImageLoaded)
529             self.lastURL = sourceURL
530
531     def onImageLoaded(self,dummy):
532             self.currPic = loadPic(self.tmpfile, self.width, self.height, 0,1, 0,1)
533             os.remove(self.tmpfile)
534             self.callBack(pixmap=self.currPic)
535