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