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