initial version
[enigma2-plugins.git] / lastfm / src / plugin.py
1 #from enigma import *
2 from enigma import eTimer
3 from enigma import eConsoleAppContainer
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
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 from Tools import Notifications
17
18 from Plugins.Plugin import PluginDescriptor
19
20
21 from StreamPlayer import StreamPlayer
22 from LastFMConfig import LastFMConfigScreen
23 from LastFM import LastFM
24
25 import os
26 ###############################################################################        
27 myname = "Last.FM"     
28 myversion = "0.1"
29
30 plugin_path = ""
31
32 ###############################################################################        
33
34 config.plugins.LastFM = ConfigSubsection()
35 config.plugins.LastFM.showcoverart = ConfigYesNo(default = False)
36 config.plugins.LastFM.username = ConfigText("user",fixed_size=False)
37 config.plugins.LastFM.password = ConfigText("passwd",fixed_size=False)
38 config.plugins.LastFM.timeoutstatustext = ConfigInteger(3,limits = (0, 10))
39 config.plugins.LastFM.timeouttabselect = ConfigInteger(2,limits = (0, 10))
40 config.plugins.LastFM.metadatarefreshinterval = ConfigInteger(5,limits = (0, 100))
41 config.plugins.LastFM.recommendedlevel = ConfigInteger(3,limits = (0, 100))
42
43 ###############################################################################        
44 def main(session,**kwargs):
45     session.open(LastFMScreenMain)    
46         
47 def Plugins(path,**kwargs):
48     global plugin_path
49     plugin_path = path
50     return PluginDescriptor(
51         name=myname, 
52         description="the social music revolution", 
53         where = PluginDescriptor.WHERE_PLUGINMENU,
54         fnc = main
55         )
56 ############################################################################### 
57 class LastFMScreenMain(Screen,HelpableScreen):
58     skin = """
59         <screen position="110,83" size="530,430" title="Last.FM" >
60             
61             <widget name="artist" position="0,0" size="70,30" valign=\"center\" halign=\"left\" zPosition=\"2\"  foregroundColor=\"white\" font=\"Regular;18\" />          
62             <widget name="album" position="0,40" size="70,30" valign=\"center\" halign=\"left\" zPosition=\"2\"  foregroundColor=\"white\" font=\"Regular;18\" />          
63             <widget name="track" position="0,80" size="70,30" valign=\"center\" halign=\"left\" zPosition=\"2\"  foregroundColor=\"white\" font=\"Regular;18\" />          
64             
65             <widget name="info_artist" position="70,0" size="344,30" valign=\"center\" halign=\"left\" zPosition=\"2\"  foregroundColor=\"white\" font=\"Regular;18\" />          
66             <widget name="info_album" position="70,40" size="344,30" valign=\"center\" halign=\"left\" zPosition=\"2\"  foregroundColor=\"white\" font=\"Regular;18\" />          
67             <widget name="info_track" position="70,80" size="344,30" valign=\"center\" halign=\"left\" zPosition=\"2\"  foregroundColor=\"white\" font=\"Regular;18\" />          
68             <widget name="info_cover" position="414,0" size="116,116" />          
69             
70             <widget name="tablist" position="0,120" size="150,260" scrollbarMode="showOnDemand" />            
71             <widget name="streamlist" position="150,120" size="380,260" scrollbarMode="showOnDemand" />            
72             
73             <widget name="button_red" position="10,400" size="60,30" backgroundColor=\"red\" valign=\"center\" halign=\"center\" zPosition=\"2\"  foregroundColor=\"white\" font=\"Regular;18\" />          
74             <widget name="button_green" position="80,400" size="60,30" backgroundColor=\"green\" valign=\"center\" halign=\"center\" zPosition=\"2\"  foregroundColor=\"white\" font=\"Regular;18\"/>            
75             <widget name="button_yellow" position="150,400" size="60,30" backgroundColor=\"yellow\" valign=\"center\" halign=\"center\" zPosition=\"2\"  foregroundColor=\"white\" font=\"Regular;18\" />            
76             <widget name="button_blue" position="220,400" size="60,30" backgroundColor=\"blue\" valign=\"center\" halign=\"center\" zPosition=\"2\"  foregroundColor=\"white\" font=\"Regular;18\" />            
77             <widget name="infolabel" position="290,400" size="290,30" valign=\"center\" halign=\"center\" zPosition=\"2\"  foregroundColor=\"white\" font=\"Regular;18\" />           
78         </screen>"""
79          
80     noCoverArtPNG = "/usr/share/enigma2/no_coverArt.png"
81     
82     def __init__(self, session, args = 0):
83         Screen.__init__(self, session)
84         HelpableScreen.__init__(self)
85         self.skin = LastFMScreenMain.skin
86         self.session = session
87         self.lastfm = LastFM()
88         self.streamplayer = StreamPlayer(session)
89         self.imageconverter = ImageConverter(self.setCoverArt)
90         Screen.__init__(self, session)
91         self.tabs=[("personal Stations",self.loadPersonalStations)
92                    ,("Global Tags",self.loadGlobalTags)
93                    ,("Top Tracks",self.loadTopTracks)
94                    ,("Recent Tracks",self.loadRecentTracks)
95                    ,("Loved Tracks",self.loadLovedTracks)
96                    ,("Banned Tracks",self.loadBannedTracks)
97                    ,("Friends",self.loadFriends)
98                    ,("Neighbours",self.loadNeighbours)
99                    ]
100         tablist =[]
101         for tab in self.tabs:
102             tablist.append((tab[0],tab))
103         self.tablist = MenuList(tablist)
104         self.tablist.onSelectionChanged.append(self.action_TabChanged)
105         
106         self["artist"] = Label(_("Artist")+":")
107         self["album"] = Label(_("Album")+":")
108         self["track"] = Label(_("Track")+":")
109         
110         self["info_artist"] = Label("N/A")
111         self["info_album"] = Label("N/A")
112         self["info_track"] = Label("N/A")
113         self["info_cover"] = Pixmap()
114         
115         self["tablist"] = self.tablist
116         self["streamlist"] = MenuList([])
117         
118         self["button_red"] = Label(_("play"))
119         self["button_green"] = Label(_("skip"))
120         self["button_yellow"] = Label(_("love"))
121         self["button_blue"] = Label(_("ban"))
122         self["infolabel"] = Label("")
123         
124         self["actions"] = ActionMap(["InfobarChannelSelection","WizardActions", "DirectionActions","MenuActions","ShortcutActions","GlobalActions","HelpActions"], 
125             {
126              "ok": self.action_ok,
127              "back": self.action_exit,
128              "red": self.action_startstop,
129              "green": self.action_green,
130              "yellow": self.action_yellow,
131              "blue": self.action_blue ,
132              "historyNext": self.action_nextTab,
133              "historyBack": self.action_prevTab,
134              
135              "menu": self.action_menu,
136              }, -1)
137         self.helpList.append((self["actions"], "WizardActions", [("ok", _("switch to selected Station"))]))
138         self.helpList.append((self["actions"], "WizardActions", [("back", _("quit Last.FM"))]))
139
140         self.helpList.append((self["actions"], "MenuActions", [("menu", _("open Configuration"))]))
141
142         self.helpList.append((self["actions"], "ShortcutActions", [("red", _("start/stop streaming"))]))
143         self.helpList.append((self["actions"], "ShortcutActions", [("green", _("skip current Track"))]))
144         self.helpList.append((self["actions"], "ShortcutActions", [("yellow", _("mark current Track as loved"))]))
145         self.helpList.append((self["actions"], "ShortcutActions", [("blue", _("ban Track, never play again"))]))
146         self.helpList.append((self["actions"], "InfobarChannelSelection", [("historyNext", _("select next Tab"))]))
147         self.helpList.append((self["actions"], "InfobarChannelSelection", [("historyBack", _("select prev Tab"))]))
148
149         self.onLayoutFinish.append(self.initLastFM)
150         self.onLayoutFinish.append(self.tabchangedtimerFired)
151         self.onLayoutFinish.append(self.setCoverArt)
152         
153         self.guiupdatetimer = eTimer()
154         self.guiupdatetimer.timeout.get().append(self.updateGUI)
155         
156         self.tabchangetimer = eTimer()
157         self.tabchangetimer.timeout.get().append(self.tabchangedtimerFired)
158         
159         self.infolabelcleartimer = eTimer()
160         self.infolabelcleartimer.timeout.get().append(self.clearInfoLabel)
161         
162         
163     def action_TabChanged(self):
164         self.tabchangetimer.stop()
165         self.tabchangetimer.start(config.plugins.LastFM.timeouttabselect.value*1000)
166         
167     def tabchangedtimerFired(self):
168         self.tablist.getCurrent()[1][1]()
169         self.tabchangetimer.stop()
170         
171     def action_nextTab(self):
172         self.tablist.down()
173             
174     def action_prevTab(self):
175         self.tablist.up()
176         
177     def showTab(self,tabnumber):
178         self.currenttab=tabnumber
179         print "showing tab",tabnumber
180         print self.tabs[tabnumber]
181                 
182     def action_menu(self):
183         self.session.open(LastFMConfigScreen)
184
185     def action_exit(self):
186         print "action_exit"
187         self.guiupdatetimer.stop()
188         self.streamplayer.stop()
189         self.close()
190
191     def action_ok(self):
192         print "action_ok"
193         selectedTag = self["streamlist"].l.getCurrentSelection()[1]
194         self.lastfm.changestation(selectedTag)
195         
196     def action_startstop(self):
197         if self.streamplayer.is_playing:
198             self.streamplayer.stop()
199             self.lastfm.metadata = {}
200             self.setInfoLabel("stream stopped")
201         
202         else:
203             self.setInfoLabel("starting stream",timeout=True)
204             if self.lastfm.info.has_key("stream_url"):
205                 self.streamplayer.play(self.lastfm.info["stream_url"])
206                 self.guiupdatetimer.start(config.plugins.LastFM.metadatarefreshinterval.value*1000)
207
208     def action_green(self):
209         self.lastfm.skip()
210         self.setInfoLabel("Track skipped",timeout=True)
211         
212     def action_yellow(self):
213         self.lastfm.love()
214         self.setInfoLabel("Track loved",timeout=True)
215         
216     def action_blue(self):
217         self.lastfm.ban()
218         self.setInfoLabel("Track banned",timeout=True)
219            
220     def setInfoLabel(self,text,timeout=True):
221         self.infolabelcleartimer.stop() 
222         self["infolabel"].setText(text)
223         if timeout is True:
224             self.infolabelcleartimer.start(config.plugins.LastFM.timeoutstatustext.value*1000)
225             
226     def clearInfoLabel(self):
227         self["infolabel"].setText("")
228         
229     def updateGUI(self):
230         if self.streamplayer.is_playing is not True:
231             return None
232         print "updateGUI"
233         
234         if self.lastfm.state:
235             self.lastfm.getmetadata()
236         
237         if self.streamplayer.is_playing:
238             self["button_red"].setText(_("stop"))
239         else:
240             self["button_red"].setText(_("play"))            
241             
242         if self.lastfm.metadata.has_key("station"):
243             self.setTitle(myname+": "+self.lastfm.metadata["station"])
244         else:
245             self.setTitle(myname)
246
247         if self.lastfm.metadata.has_key("artist"):
248             self["info_artist"].setText(self.lastfm.metadata["artist"])
249         else:
250             self["info_artist"].setText("N/A")
251
252         if self.lastfm.metadata.has_key("album"):
253             self["info_album"].setText(self.lastfm.metadata["album"])
254         else:
255             self["info_album"].setText("N/A")
256
257         if self.lastfm.metadata.has_key("track"):
258             self["info_track"].setText(self.lastfm.metadata["track"])
259         else:
260             self["info_track"].setText("N/A")
261         
262         if self.lastfm.metadata.has_key("albumcover_large") and config.plugins.LastFM.showcoverart.value:
263             self.imageconverter.convert(self.lastfm.metadata["albumcover_large"])
264         elif self.lastfm.metadata.has_key("albumcover_medium") and config.plugins.LastFM.showcoverart.value:
265             self.imageconverter.convert(self.lastfm.metadata["albumcover_medium"])
266         elif self.lastfm.metadata.has_key("albumcover_small") and config.plugins.LastFM.showcoverart.value:
267             self.imageconverter.convert(self.lastfm.metadata["albumcover_small"],self.setCoverArt)
268         else:
269             self.setCoverArt()
270         
271         if self.streamplayer.is_playing:
272             self.guiupdatetimer.start(config.plugins.LastFM.metadatarefreshinterval.value*1000)
273         else:
274             self.setTitle(myname)
275             self.setCoverArt()
276             self["info_artist"].setText("N/A")
277             self["info_album"].setText("N/A")
278             self["info_track"].setText("N/A")
279             
280     def setCoverArt(self,filename=None):
281         #print "coverart from file",filename
282         if filename is None or os.path.isfile(filename) is False:
283             self["info_cover"].instance.setPixmapFromFile(self.noCoverArtPNG)            
284         else:
285             self["info_cover"].instance.setPixmapFromFile(filename)
286     
287     def initLastFM(self):
288         self.setInfoLabel("loggin into last.fm")
289         (result,resulttext) = self.lastfm.connect(config.plugins.LastFM.username.value,config.plugins.LastFM.password.value)
290         if result is False:
291             self.setInfoLabel("login failed")
292             Notifications.AddPopup("Login to Last.FM failed!", resulttext,MessageBox.TYPE_INFO, 5)        
293 #            if self.shown:
294 #                self.session.open(MessageBox, resulttext, MessageBox.TYPE_ERROR)
295         else:
296             self.setInfoLabel("login successful",timeout=True)      
297
298     def loadPersonalStations(self):
299         tags = []
300         x= {}
301         x["_display"] = "Personal Radio"
302         x["stationurl"] = self.lastfm.getPersonalURL(config.plugins.LastFM.username.value,level=config.plugins.LastFM.recommendedlevel.value)
303         tags.append(x)
304         
305         x= {}
306         x["_display"] = "Neighbours Tracks"
307         x["stationurl"] = self.lastfm.getNeighboursURL(config.plugins.LastFM.username.value)
308         tags.append(x)
309         
310         x= {}
311         x["_display"] = "Loved Tracks"
312         x["stationurl"] = self.lastfm.getLovedURL(config.plugins.LastFM.username.value)
313         tags.append(x)
314         
315         if self.lastfm.metadata.has_key("artist"):
316             x= {}
317             x["_display"] = "similar Tracks of current Artist"
318             x["stationurl"] = self.lastfm.getSimilarArtistsURL()
319             tags.append(x)
320             
321             x= {}
322             x["_display"] = "Tracks liked by Fans of current Track"
323             x["stationurl"] = self.lastfm.getArtistsLikedByFans()
324             tags.append(x)
325
326             x= {}
327             x["_display"] = "Group of Artist of current Track"
328             x["stationurl"] = self.lastfm.getArtistGroup()
329             tags.append(x)
330         
331
332         self.buildMenuList(tags)
333         
334     def loadGlobalTags(self):
335         self.setInfoLabel("loading Global Tags")
336         tags = self.lastfm.getGlobalTags()
337         self.buildMenuList(tags)
338
339     def loadTopTracks(self):
340         self.setInfoLabel("loading Top Tacks")
341         tracks = self.lastfm.getTopTracks(config.plugins.LastFM.username.value)
342         self.buildMenuList(tracks)
343
344     def loadRecentTracks(self):
345         self.setInfoLabel("loading recent Tracks")
346         tracks = self.lastfm.getRecentTracks(config.plugins.LastFM.username.value)
347         self.buildMenuList(tracks)
348
349     def loadLovedTracks(self):
350         self.setInfoLabel("loading loved Tracks")
351         tracks = self.lastfm.getRecentLovedTracks(config.plugins.LastFM.username.value)
352         self.buildMenuList(tracks)
353
354     def loadBannedTracks(self):
355         self.setInfoLabel("loading loved Tracks")
356         tracks = self.lastfm.getRecentBannedTracks(config.plugins.LastFM.username.value)
357         self.buildMenuList(tracks)
358         
359     def loadNeighbours(self):
360         self.setInfoLabel("loading Neighbours")
361         tracks = self.lastfm.getNeighbours(config.plugins.LastFM.username.value)
362         self.buildMenuList(tracks)
363
364     def loadFriends(self):
365         self.setInfoLabel("loading Friends")
366         tracks = self.lastfm.getFriends(config.plugins.LastFM.username.value)
367         self.buildMenuList(tracks)
368
369     def buildMenuList(self,items):
370         menuliste = []
371         for i in items:
372             menuliste.append((i['_display'],i['stationurl']))
373         self["streamlist"].l.setList(menuliste) 
374
375
376 class ImageConverter:
377     
378     targetfile= "/tmp/coverart.png"
379     lastURL =""
380
381     def __init__(self,callBack):
382         self.callBack = callBack
383         self.container = eConsoleAppContainer()
384         self.container.appClosed.get().append(self.appClosed)
385         self.container.dataAvail.get().append(self.dataAvail)
386     
387     def dataAvail(self,text):
388         print "imageconverter:",text
389
390     def convert(self,sourceURL):
391         #print "start converting coverart",sourceURL
392         try: # this is not nice... i have to implement a function to check it while activating CoverArts
393             import Image
394         except:
395             pass
396         
397         if self.lastURL == sourceURL:
398             pass #self.callBack(filename=self.targetfile)
399         else:
400             cmd = "/usr/bin/python "+plugin_path+"/imageconverter.py '"+sourceURL+"' '"+self.targetfile+"' "
401             self.container.execute(cmd)
402             self.lastURL = sourceURL
403         
404     def appClosed(self,text):
405         #print "appClosed",text
406         self.callBack(filename=self.targetfile)