SHOUTcast plugin - initial checkin
[enigma2-plugins.git] / shoutcast / src / plugin.py
1 #
2 #  SHOUTcast E2
3 #
4 #  $Id$
5 #
6 #  Coded by Dr.Best (c) 2010
7 #  Support: www.dreambox-tools.info
8 #
9 #  This plugin is licensed under the Creative Commons 
10 #  Attribution-NonCommercial-ShareAlike 3.0 Unported 
11 #  License. To view a copy of this license, visit
12 #  http://creativecommons.org/licenses/by-nc-sa/3.0/ or send a letter to Creative
13 #  Commons, 559 Nathan Abbott Way, Stanford, California 94305, USA.
14 #
15 #  Alternatively, this plugin may be distributed and executed on hardware which
16 #  is licensed by Dream Multimedia GmbH.
17
18 #  This plugin is NOT free software. It is open source, you are allowed to
19 #  modify it (if you keep the license), but it may not be commercially 
20 #  distributed other than under the conditions noted above.
21 #
22
23
24 from Plugins.Plugin import PluginDescriptor
25 from Screens.Screen import Screen
26 from Components.ActionMap import ActionMap
27 from Components.Label import Label
28 from enigma import eServiceReference
29 from enigma import eListboxPythonMultiContent, eListbox, gFont, \
30         RT_HALIGN_LEFT, RT_HALIGN_RIGHT, RT_VALIGN_CENTER
31 from Tools.LoadPixmap import LoadPixmap
32 import xml.etree.cElementTree
33 from Screens.InfoBarGenerics import InfoBarAudioSelection, InfoBarSeek
34 from Components.ServiceEventTracker import ServiceEventTracker
35 from enigma import iPlayableService, iServiceInformation
36
37 from twisted.internet import reactor, defer
38 from twisted.web import client
39 from twisted.web.client import HTTPClientFactory
40 from Components.Pixmap import Pixmap
41 from enigma import ePicLoad
42 from Components.ScrollLabel import ScrollLabel
43 import string
44 import os
45 from enigma import getDesktop
46 from Components.config import config, ConfigSubsection, ConfigSelection, ConfigDirectory, ConfigYesNo, Config, ConfigInteger, ConfigSubList, ConfigText, getConfigListEntry, configfile
47 from Components.ConfigList import ConfigListScreen
48 from Screens.MessageBox import MessageBox
49 from Components.GUIComponent import GUIComponent
50 from Components.Sources.StaticText import StaticText
51 from urllib import quote
52 from twisted.web.client import downloadPage
53 from Screens.ChoiceBox import ChoiceBox
54 from Screens.VirtualKeyBoard import VirtualKeyBoard
55 from enigma import eTimer
56 from enigma import eConsoleAppContainer
57 from Components.Input import Input
58 from Screens.InputBox import InputBox
59 from Components.FileList import FileList
60
61
62 containerStreamripper = None
63
64 config.plugins.shoutcast = ConfigSubsection()
65 config.plugins.shoutcast.streamingrate = ConfigSelection(default="0", choices = [("0",_("All")), ("64",_(">= 64 kbps")), ("128",_(">= 128 kbps")), ("192",_(">= 192 kbps")), ("256",_(">= 256 kbps"))])
66 config.plugins.shoutcast.reloadstationlist = ConfigSelection(default="0", choices = [("0",_("Off")), ("1",_("every minute")), ("3",_("every three minutes")), ("5",_("every five minutes"))])
67 config.plugins.shoutcast.dirname = ConfigDirectory(default = "/hdd/streamripper/")
68 config.plugins.shoutcast.riptosinglefile = ConfigYesNo(default = False)
69 config.plugins.shoutcast.createdirforeachstream = ConfigYesNo(default = True)
70 config.plugins.shoutcast.addsequenceoutputfile = ConfigYesNo(default = False)
71
72
73 class SHOUTcastGenre:
74         def __init__(self, name = ""):
75                 self.name = name
76
77 class SHOUTcastStation:
78         def __init__(self, name = "", mt = "", id = "", br = "", genre = "", ct = "", lc = ""):
79                 self.name = name
80                 self.mt = mt
81                 self.id = id
82                 self.br = br
83                 self.genre = genre
84                 self.ct = ct
85                 self.lc = lc
86
87 class Favorite:
88         def __init__(self, configItem = None):
89                 self.configItem = configItem
90
91 class myHTTPClientFactory(HTTPClientFactory):
92         def __init__(self, url, method='GET', postdata=None, headers=None,
93         agent="SHOUTcast", timeout=0, cookies=None,
94         followRedirect=1, lastModified=None, etag=None):
95                 HTTPClientFactory.__init__(self, url, method=method, postdata=postdata,
96                 headers=headers, agent=agent, timeout=timeout, cookies=cookies,followRedirect=followRedirect)
97
98 def sendUrlCommand(url, contextFactory=None, timeout=60, *args, **kwargs):
99         scheme, host, port, path = client._parse(url)
100         factory = myHTTPClientFactory(url, *args, **kwargs)
101         reactor.connectTCP(host, port, factory, timeout=timeout)
102         return factory.deferred
103
104
105 def main(session,**kwargs):
106         session.open(SHOUTcastWidget)
107
108 def Plugins(**kwargs):
109         list = [PluginDescriptor(name="SHOUTcast", description=_("listen to shoutcast internet-radio"), where = [PluginDescriptor.WHERE_EXTENSIONSMENU], fnc=main)]
110         return list
111
112 class SHOUTcastWidget(Screen, InfoBarSeek):
113
114         GENRELIST = 0
115         STATIONLIST = 1
116         FAVORITELIST = 2
117         SEARCHLIST = 3
118
119         STREAMRIPPER_BIN = '/usr/bin/streamripper'
120
121         sz_w = getDesktop(0).size().width()
122         if sz_w == 1280:
123                 skin = """
124                         <screen name="SHOUTcastWidget" position="0,0" size="1280,720" flags="wfNoBorder" backgroundColor="#00000000" title="SHOUTcast">
125                                 <ePixmap position="50,30" zPosition="4" size="140,40" pixmap="skin_default/buttons/red.png" transparent="1" alphatest="on" />
126                                 <ePixmap position="200,30" zPosition="4" size="140,40" pixmap="skin_default/buttons/green.png" transparent="1" alphatest="on" />
127                                 <ePixmap position="350,30" zPosition="4" size="140,40" pixmap="skin_default/buttons/yellow.png" transparent="1" alphatest="on" />
128                                 <ePixmap position="500,30" zPosition="4" size="140,40" pixmap="skin_default/buttons/blue.png" transparent="1" alphatest="on" />
129                                 <widget render="Label" source="key_red" position="50,30" size="140,40" zPosition="5" valign="center" halign="center" backgroundColor="red" font="Regular;21" transparent="1" foregroundColor="white" shadowColor="black" shadowOffset="-1,-1" />
130                                 <widget render="Label" source="key_green" position="200,30" size="140,40" zPosition="5" valign="center" halign="center" backgroundColor="red" font="Regular;21" transparent="1" foregroundColor="white" shadowColor="black" shadowOffset="-1,-1" />
131                                 <widget render="Label" source="key_yellow" position="350,30" size="140,40" zPosition="5" valign="center" halign="center" backgroundColor="red" font="Regular;21" transparent="1" foregroundColor="white" shadowColor="black" shadowOffset="-1,-1" />
132                                 <widget render="Label" source="key_blue" position="500,30" size="140,40" zPosition="5" valign="center" halign="center" backgroundColor="red" font="Regular;21" transparent="1" foregroundColor="white" shadowColor="black" shadowOffset="-1,-1" />
133                                 <widget name="headertext" position="50,77" zPosition="1" size="1180,23" font="Regular;20" transparent="1"  backgroundColor="#00000000"/>
134                                 <widget name="statustext" position="20,270" zPosition="1" size="1240,90" font="Regular;20" halign="center" valign="center" transparent="0"  backgroundColor="#00000000"/>
135                                 <widget name="list" position="50,110" zPosition="2" size="1180,445" scrollbarMode="showOnDemand" transparent="0"  backgroundColor="#00000000"/>
136                                 <widget name="titel" position="160,580" zPosition="1" size="900,20" font="Regular;18" transparent="1"  backgroundColor="#00000000"/>
137                                 <widget name="station" position="160,600" zPosition="1" size="900,40" font="Regular;18" transparent="1"  backgroundColor="#00000000"/>
138                                 <widget name="console" position="160,650" zPosition="1" size="900,50" font="Regular;18" transparent="1"  backgroundColor="#00000000"/>
139                                 <widget name="cover" zPosition="2" position="50,580" size="102,110" alphatest="blend" />
140                                 <ePixmap position="1100,35" zPosition="4" size="120,35" pixmap="/usr/lib/enigma2/python/Plugins/Extensions/SHOUTcast/shoutcast-logo1-fs8.png" transparent="1" alphatest="on" />
141                         </screen>"""
142
143         elif sz_w == 1024:
144                 skin = """
145                         <screen name="SHOUTcastWidget" position="0,0" size="1024,576" flags="wfNoBorder" backgroundColor="#00000000" title="SHOUTcast">
146                                 <ePixmap position="50,30" zPosition="4" size="140,40" pixmap="skin_default/buttons/red.png" transparent="1" alphatest="on" />
147                                 <ePixmap position="200,30" zPosition="4" size="140,40" pixmap="skin_default/buttons/green.png" transparent="1" alphatest="on" />
148                                 <ePixmap position="350,30" zPosition="4" size="140,40" pixmap="skin_default/buttons/yellow.png" transparent="1" alphatest="on" />
149                                 <ePixmap position="500,30" zPosition="4" size="140,40" pixmap="skin_default/buttons/blue.png" transparent="1" alphatest="on" />
150                                 <widget render="Label" source="key_red" position="50,30" size="140,40" zPosition="5" valign="center" halign="center" backgroundColor="red" font="Regular;21" transparent="1" foregroundColor="white" shadowColor="black" shadowOffset="-1,-1" />
151                                 <widget render="Label" source="key_green" position="200,30" size="140,40" zPosition="5" valign="center" halign="center" backgroundColor="red" font="Regular;21" transparent="1" foregroundColor="white" shadowColor="black" shadowOffset="-1,-1" />
152                                 <widget render="Label" source="key_yellow" position="350,30" size="140,40" zPosition="5" valign="center" halign="center" backgroundColor="red" font="Regular;21" transparent="1" foregroundColor="white" shadowColor="black" shadowOffset="-1,-1" />
153                                 <widget render="Label" source="key_blue" position="500,30" size="140,40" zPosition="5" valign="center" halign="center" backgroundColor="red" font="Regular;21" transparent="1" foregroundColor="white" shadowColor="black" shadowOffset="-1,-1" />
154                                 <widget name="headertext" position="50,77" zPosition="1" size="900,23" font="Regular;20" transparent="1"  backgroundColor="#00000000"/>
155                                 <widget name="statustext" position="20,270" zPosition="1" size="1004,90" font="Regular;20" halign="center" valign="center" transparent="0"  backgroundColor="#00000000"/>
156                                 <widget name="list" position="50,110" zPosition="2" size="940,313" scrollbarMode="showOnDemand" transparent="0"  backgroundColor="#00000000"/>
157                                 <widget name="titel" position="160,450" zPosition="1" size="800,20" font="Regular;18" transparent="1"  backgroundColor="#00000000"/>
158                                 <widget name="station" position="160,470" zPosition="1" size="800,40" font="Regular;18" transparent="1"  backgroundColor="#00000000"/>
159                                 <widget name="console" position="160,520" zPosition="1" size="800,50" font="Regular;18" transparent="1"  backgroundColor="#00000000"/>
160                                 <widget name="cover" zPosition="2" position="50,450" size="102,110" alphatest="blend" />
161                                 <ePixmap position="870,35" zPosition="4" size="120,35" pixmap="/usr/lib/enigma2/python/Plugins/Extensions/SHOUTcast/shoutcast-logo1-fs8.png" transparent="1" alphatest="on" />
162                         </screen>"""
163         else:
164
165                 skin = """
166                         <screen name="SHOUTcastWidget" position="0,0" size="720,576" flags="wfNoBorder" backgroundColor="#00000000" title="SHOUTcast">
167                                 <ePixmap position="20,30" zPosition="4" size="140,40" pixmap="skin_default/buttons/red.png" transparent="1" alphatest="on" />
168                                 <ePixmap position="160,30" zPosition="4" size="140,40" pixmap="skin_default/buttons/green.png" transparent="1" alphatest="on" />
169                                 <ePixmap position="300,30" zPosition="4" size="140,40" pixmap="skin_default/buttons/yellow.png" transparent="1" alphatest="on" />
170                                 <ePixmap position="440,30" zPosition="4" size="140,40" pixmap="skin_default/buttons/blue.png" transparent="1" alphatest="on" />
171                                 <widget render="Label" source="key_red" position="20,30" size="140,40" zPosition="5" valign="center" halign="center" backgroundColor="red" font="Regular;21" transparent="1" foregroundColor="white" shadowColor="black" shadowOffset="-1,-1" />
172                                 <widget render="Label" source="key_green" position="160,30" size="140,40" zPosition="5" valign="center" halign="center" backgroundColor="red" font="Regular;21" transparent="1" foregroundColor="white" shadowColor="black" shadowOffset="-1,-1" />
173                                 <widget render="Label" source="key_yellow" position="300,30" size="140,40" zPosition="5" valign="center" halign="center" backgroundColor="red" font="Regular;21" transparent="1" foregroundColor="white" shadowColor="black" shadowOffset="-1,-1" />
174                                 <widget render="Label" source="key_blue" position="440,30" size="140,40" zPosition="5" valign="center" halign="center" backgroundColor="red" font="Regular;21" transparent="1" foregroundColor="white" shadowColor="black" shadowOffset="-1,-1" />
175                                 <widget name="headertext" position="20,77" zPosition="1" size="680,23" font="Regular;20" transparent="1"  backgroundColor="#00000000"/>
176                                 <widget name="statustext" position="20,270" zPosition="1" size="680,90" font="Regular;20" halign="center" valign="center" transparent="0"  backgroundColor="#00000000"/>
177                                 <widget name="list" position="20,110" zPosition="2" size="680,313" scrollbarMode="showOnDemand" transparent="0"  backgroundColor="#00000000"/>
178                                 <widget name="titel" position="130,450" zPosition="1" size="550,20" font="Regular;18" transparent="1"  backgroundColor="#00000000"/>
179                                 <widget name="station" position="130,470" zPosition="1" size="550,40" font="Regular;18" transparent="1"  backgroundColor="#00000000"/>
180                                 <widget name="console" position="130,520" zPosition="1" size="550,50" font="Regular;18" transparent="1"  backgroundColor="#00000000"/>
181                                 <widget name="cover" zPosition="2" position="20,450" size="102,110" alphatest="blend" />
182                                 <ePixmap position="590,35" zPosition="4" size="120,35" pixmap="/usr/lib/enigma2/python/Plugins/Extensions/SHOUTcast/shoutcast-logo1-fs8.png" transparent="1" alphatest="on" />
183                         </screen>"""
184
185         
186         def __init__(self, session):
187                 self.session = session
188                 Screen.__init__(self, session)
189                 self.CurrentService = self.session.nav.getCurrentlyPlayingServiceReference()
190                 self.session.nav.stopService()
191                 self["cover"] = Cover()
192                 self["key_red"] = StaticText(_("Record"))
193                 self["key_green"] = StaticText(_("Genres"))
194                 self["key_yellow"] = StaticText(_("Stations"))
195                 self["key_blue"] = StaticText(_("Favorites"))
196                 self.__event_tracker = ServiceEventTracker(screen=self, eventmap=
197                         {
198                                 iPlayableService.evUpdatedInfo: self.__evUpdatedInfo,
199                                 iPlayableService.evUser+10: self.__evAudioDecodeError,
200                                 iPlayableService.evUser+12: self.__evPluginError
201                         })
202                 InfoBarSeek.__init__(self, actionmap = "MediaPlayerSeekActions")
203                 self.mode = self.FAVORITELIST
204                 self["list"] = SHOUTcastList()
205                 self["list"].connectSelChanged(self.onSelectionChanged)
206                 self["statustext"] = Label(_("Getting SHOUTcast genre list..."))
207                 self["actions"] = ActionMap(["WizardActions", "DirectionActions", "ColorActions", "EPGSelectActions"],
208                 {
209                         "ok": self.ok_pressed,
210                         "back": self.close,
211                         "input_date_time": self.menu_pressed,
212                         "red": self.red_pressed,
213                         "green": self.green_pressed,
214                         "yellow": self.yellow_pressed,
215                         "blue": self.blue_pressed,
216                         
217                 }, -1)
218                 self.stationList = []
219                 self.stationListIndex = 0
220                 self.genreList = []
221                 self.genreListIndex = 0
222                 self.favoriteList = []
223                 self.favoriteListIndex = 0
224
225                 self.favoriteConfigFile = "/usr/lib/enigma2/python/Plugins/Extensions/SHOUTcast/favorites"
226                 self.favoriteConfig = Config()
227                 if os.path.exists(self.favoriteConfigFile):
228                         self.favoriteConfig.loadFromFile(self.favoriteConfigFile)
229                 self.favoriteConfig.entriescount =  ConfigInteger(0)
230                 self.favoriteConfig.Entries = ConfigSubList()
231                 self.initFavouriteConfig()
232
233                 self.stationListXML = ""
234                 self["titel"] = Label()
235                 self["station"] = Label()
236                 self["headertext"] = Label()
237                 self["console"] = Label()
238                 self.headerTextString = ""
239                 self.stationListHeader = ""
240                 self.tunein = ""
241                 self.searchSHOUTcastString = ""
242                 self.currentStreamingURL = ""
243                 self.currentStreamingStation = ""
244                 self.stationListURL = ""
245                 self.onClose.append(self.__onClose)
246                 self.onLayoutFinish.append(self.getFavoriteList)
247
248                 self.reloadStationListTimer = eTimer()
249                 self.reloadStationListTimer.timeout.get().append(self.reloadStationListTimerTimeout)
250                 self.reloadStationListTimerVar = int(config.plugins.shoutcast.reloadstationlist.value)
251
252                 self.visible = True
253
254                 global containerStreamripper
255                 if containerStreamripper is None:
256                         containerStreamripper = eConsoleAppContainer()
257
258                 containerStreamripper.dataAvail.append(self.streamripperDataAvail)
259                 containerStreamripper.appClosed.append(self.streamripperClosed)
260
261                 if containerStreamripper.running():
262                         # just to hear to recording music when starting the plugin...
263                         self.currentStreamingStation = _("Recording stream station")
264                         self.playServiceStream("http://localhost:9191")
265
266         def streamripperClosed(self, retval):
267                 if retval == 0:
268                         self["console"].setText("")
269
270         def streamripperDataAvail(self, data):
271                 sData = data.replace('\n','')
272                 self["console"].setText(sData)
273
274         def stopReloadStationListTimer(self):
275                 if self.reloadStationListTimer.isActive():
276                         self.reloadStationListTimer.stop()
277
278         def reloadStationListTimerTimeout(self):
279                 self.stopReloadStationListTimer()
280                 if self.mode == self.STATIONLIST:
281                         print "[SHOUTcast] reloadStationList: %s " % self.stationListURL
282                         sendUrlCommand(self.stationListURL, None,10).addCallback(self.callbackStationList).addErrback(self.callbackStationListError)
283
284         def InputBoxStartRecordingCallback(self, returnValue = None):
285                 recordingLength = 0
286                 if returnValue:
287                         recordingLength =  int(returnValue) * 60
288                 if not os.path.exists(config.plugins.shoutcast.dirname.value):
289                         os.mkdir(config.plugins.shoutcast.dirname.value)
290                 args = []
291                 args.append(self.currentStreamingURL)
292                 args.append('-d')
293                 args.append(config.plugins.shoutcast.dirname.value)
294                 args.append('-r')
295                 args.append('9191')
296                 if recordingLength != 0:
297                         args.append('-l')
298                         args.append("%d" % int(recordingLength))
299                 if config.plugins.shoutcast.riptosinglefile.value:
300                         args.append('-a')
301                         args.append('-A')
302                 if not config.plugins.shoutcast.createdirforeachstream.value:
303                         args.append('-s')
304                 if config.plugins.shoutcast.addsequenceoutputfile.value:
305                         args.append('-q')
306                 cmd = [self.STREAMRIPPER_BIN, self.STREAMRIPPER_BIN] + args
307                 containerStreamripper.execute(*cmd)
308
309         def red_pressed(self):
310                 if containerStreamripper.running():
311                         containerStreamripper.sendCtrlC()
312                 else:
313                         if len(self.currentStreamingURL) != 0:
314                                 self.session.openWithCallback(self.InputBoxStartRecordingCallback, InputBox, windowTitle = _("Recording length"),  title=_("Enter in minutes (0 means unlimited)"), text="0", type=Input.NUMBER)
315                         else:
316                                 self.session.open(MessageBox, _("Only running streamings can be recorded!"), type = MessageBox.TYPE_INFO,timeout = 20 )
317
318         def green_pressed(self):
319                 if self.mode != self.GENRELIST:
320                         self.stopReloadStationListTimer()
321                         self.mode = self.GENRELIST
322                         if len(self.genreList):
323                                 self["headertext"].setText("SHOUTcast genre-list")
324                                 self["list"].setMode(self.mode)
325                                 self["list"].setList([ (x,) for x in self.genreList])
326                                 self["list"].moveToIndex(self.genreListIndex)
327                         else:
328                                 self.getGenreList()
329                 else:
330                         self.getGenreList()
331
332         def yellow_pressed(self):
333                 if self.mode != self.STATIONLIST:
334                         if len(self.stationList):
335                                 self.mode = self.STATIONLIST
336                                 self.headerTextString = _("SHOUTcast station list for %s") % self.stationListHeader
337                                 self["headertext"].setText(self.headerTextString)
338                                 self["list"].setMode(self.mode)
339                                 self["list"].setList([ (x,) for x in self.stationList])
340                                 self["list"].moveToIndex(self.stationListIndex)
341                                 if self.reloadStationListTimerVar != 0:
342                                         self.reloadStationListTimer.start(60000 * self.reloadStationListTimerVar)
343
344         def blue_pressed(self):
345                 if self.mode != self.FAVORITELIST:
346                         self.stopReloadStationListTimer()
347                         self.getFavoriteList(self.favoriteListIndex)
348
349         def getFavoriteList(self, favoriteListIndex = 0):
350                 self["statustext"].setText("")
351                 self.headerTextString = _("Favorite list")
352                 self["headertext"].setText(self.headerTextString)
353                 self.mode = self.FAVORITELIST
354                 self["list"].setMode(self.mode)
355                 favoriteList = []
356                 for item in self.favoriteConfig.Entries:
357                         favoriteList.append(Favorite(configItem=item))
358                 self["list"].setList([ (x,) for x in favoriteList])
359                 if len(favoriteList):
360                         self["list"].moveToIndex(favoriteListIndex)
361                 self["list"].show()
362
363         def getGenreList(self):
364                 self["headertext"].setText("")
365                 self["statustext"].setText(_("Getting SHOUTcast genre list..."))
366                 self["list"].hide()
367                 url = "http://yp.shoutcast.com/sbin/newxml.phtml"
368                 sendUrlCommand(url, None,10).addCallback(self.callbackGenreList).addErrback(self.callbackGenreListError)
369
370         def callbackGenreList(self, xmlstring):
371                 self["headertext"].setText("SHOUTcast genre list")
372                 self.genreListIndex = 0
373                 self.mode = self.GENRELIST
374                 self["list"].setMode(self.mode)
375                 self.genreList = self.fillGenreList(xmlstring)
376                 self["statustext"].setText("")
377                 self["list"].setList([ (x,) for x in self.genreList])
378                 if len(self.genreList):
379                         self["list"].moveToIndex(self.genreListIndex)
380                 self["list"].show()
381
382         def callbackGenreListError(self, error = None):
383                 if error is not None:
384                         try:
385                                 self["list"].hide()
386                                 self["statustext"].setText(_("%s\nPress green-button to try again...") % str(error.getErrorMessage()))
387                         except: pass
388         
389                 
390         def fillGenreList(self,xmlstring):
391                 genreList = []
392                 try:
393                         root = xml.etree.cElementTree.fromstring(xmlstring)
394                 except: return []
395                 for childs in root.findall("genre"):
396                         genreList.append(SHOUTcastGenre(name = childs.get("name")))
397                 return genreList
398
399         
400         def onSelectionChanged(self):
401                 pass
402                 # till I find a better solution
403 #               if self.mode == self.STATIONLIST:
404 #                       self.stationListIndex = self["list"].getCurrentIndex()
405 #               elif self.mode == self.FAVORITELIST:
406 #                       self.favoriteListIndex = self["list"].getCurrentIndex()
407 #               elif self.mode == self.GENRELIST:
408 #                       self.genreListIndex = self["list"].getCurrentIndex()
409
410         def ok_pressed(self):
411                 if self.visible:
412                         sel = None
413                         try:
414                                 sel = self["list"].l.getCurrentSelection()[0]
415                         except:return
416                         if sel is None:
417                                 return
418                         else:
419                                 if self.mode == self.GENRELIST:
420                                         self.genreListIndex = self["list"].getCurrentIndex()
421                                         self.getStationList(sel.name)
422                                 elif self.mode == self.STATIONLIST:
423                                         self.stationListIndex = self["list"].getCurrentIndex()
424                                         self.stopPlaying()
425                                         url = "http://yp.shoutcast.com%s?id=%s" % (self.tunein, sel.id)
426                                         self["list"].hide()
427                                         self["statustext"].setText("Getting streaming data from\n%s" % sel.name)
428                                         self.currentStreamingStation = sel.name
429                                         sendUrlCommand(url, None,10).addCallback(self.callbackPLS).addErrback(self.callbackStationListError)
430                                 elif self.mode == self.FAVORITELIST:
431                                         self.favoriteListIndex = self["list"].getCurrentIndex()
432                                         if sel.configItem.type.value == "url":
433                                                 self.stopPlaying()
434                                                 self["headertext"].setText(self.headerTextString)
435                                                 self.currentStreamingStation = sel.configItem.name.value
436                                                 self.playServiceStream(sel.configItem.text.value)
437                                         elif sel.configItem.type.value == "pls":
438                                                 self.stopPlaying()
439                                                 url = sel.configItem.text.value
440                                                 self["list"].hide()
441                                                 self["statustext"].setText(_("Getting streaming data from\n%s") % sel.configItem.name.value)
442                                                 self.currentStreamingStation = sel.configItem.name.value
443                                                 sendUrlCommand(url, None,10).addCallback(self.callbackPLS).addErrback(self.callbackStationListError)
444                                         elif sel.configItem.type.value == "genre":
445                                                 self.getStationList(sel.configItem.name.value)
446                                 elif self.mode == self.SEARCHLIST and self.searchSHOUTcastString != "":
447                                         self.searchSHOUTcast(self.searchSHOUTcastString)
448                 else:
449                         self.showWindow()
450
451         def stopPlaying(self):
452                 self.currentStreamingURL = ""
453                 self.currentStreamingStation = ""
454                 self["headertext"].setText("")
455                 self["titel"].setText("")
456                 self["station"].setText("")
457                 self.summaries.setText("")
458                 self["cover"].hide()
459                 self.session.nav.stopService()
460
461         def callbackPLS(self, result):
462                 self["headertext"].setText(self.headerTextString)
463                 found = False
464                 parts = string.split(result,"\n")
465                 for lines in parts:
466                         if lines.find("File1=") != -1:
467                                 line = string.split(lines,"File1=")
468                                 found = True
469                                 self.playServiceStream(line[-1])
470                                 
471                 if found:
472                         self["statustext"].setText("")
473                         self["list"].show()
474                 else:
475                         self.currentStreamingStation = ""
476                         self["statustext"].setText(_("No streaming data found..."))
477
478         def getStationList(self,genre):
479                 self.stationListHeader = "genre %s" % genre
480                 self.headerTextString = _("SHOUTcast station list for %s") % self.stationListHeader
481                 self["headertext"].setText("")
482                 self["statustext"].setText(_("Getting %s") %  self.headerTextString)
483                 self["list"].hide()
484                 self.stationListURL = "http://yp.shoutcast.com/sbin/newxml.phtml?genre=%s" % genre
485                 self.stationListIndex = 0
486                 sendUrlCommand(self.stationListURL, None,10).addCallback(self.callbackStationList).addErrback(self.callbackStationListError)
487
488         def callbackStationList(self, xmlstring):
489                 self.searchSHOUTcastString = ""
490                 self.stationListXML = xmlstring
491                 self["headertext"].setText(self.headerTextString)
492                 self.mode = self.STATIONLIST
493                 self["list"].setMode(self.mode)
494                 self.stationList = self.fillStationList(xmlstring)
495                 self["statustext"].setText("")
496                 self["list"].setList([ (x,) for x in self.stationList])
497                 if len(self.stationList):
498                         self["list"].moveToIndex(self.stationListIndex)
499                 self["list"].show()
500                 if self.reloadStationListTimerVar != 0:
501                         self.reloadStationListTimer.start(1000 * 60)
502
503         def fillStationList(self,xmlstring):
504                 stationList = []
505                 try:
506                         root = xml.etree.cElementTree.fromstring(xmlstring)
507                 except: return []
508                 config_bitrate = int(config.plugins.shoutcast.streamingrate.value)
509                 for childs in root.findall("tunein"):
510                         self.tunein = childs.get("base")
511                 for childs in root.findall("station"):
512                         try: bitrate = int(childs.get("br"))
513                         except: bitrate = 0
514                         if bitrate >= config_bitrate:
515                                 stationList.append(SHOUTcastStation(name = childs.get("name"), 
516                                                                         mt = childs.get("mt"), id = childs.get("id"), br = childs.get("br"), 
517                                                                         genre = childs.get("genre"), ct = childs.get("ct"), lc = childs.get("lc")))
518                 return stationList
519
520         def menu_pressed(self):
521                 if not self.visible:
522                         self.showWindow()
523                 options = [(_("Config"), self.config),(_("Search"), self.search),]
524                 if self.mode == self.FAVORITELIST and self.getSelectedItem() is not None:
525                         options.extend(((_("rename current selected favorite"), self.renameFavorite),))
526                         options.extend(((_("remove current selected favorite"), self.removeFavorite),))
527                 elif self.mode == self.GENRELIST and self.getSelectedItem() is not None:
528                         options.extend(((_("Add current selected genre to favorite"), self.addGenreToFavorite),))
529                 elif self.mode == self.STATIONLIST and self.getSelectedItem() is not None:
530                         options.extend(((_("Add current selected station to favorite"), self.addStationToFavorite),))
531                 if len(self.currentStreamingURL) != 0:
532                         options.extend(((_("Add current playing stream to favorite"), self.addCurrentStreamToFavorite),))
533                 options.extend(((_("Hide"), self.hideWindow),))
534                 self.session.openWithCallback(self.menuCallback, ChoiceBox,list = options)
535
536         def menuCallback(self, ret):
537                 ret and ret[1]()
538
539         def hideWindow(self):
540                 self.visible = False
541                 self.hide()
542
543         def showWindow(self):
544                 self.visible = True
545                 self.show()
546
547         def addGenreToFavorite(self):
548                 sel = self.getSelectedItem()
549                 if sel is not None:
550                         self.addFavorite(name = sel.name, text = sel.name, favoritetype = "genre")                      
551
552         def addStationToFavorite(self):
553                 sel = self.getSelectedItem()
554                 if sel is not None:
555                         self.addFavorite(name = sel.name, text = "http://yp.shoutcast.com%s?id=%s" % (self.tunein, sel.id), favoritetype = "pls", audio = sel.mt, bitrate = sel.br)                     
556
557         def addCurrentStreamToFavorite(self):
558                 self.addFavorite(name = self.currentStreamingStation, text = self.currentStreamingURL, favoritetype = "url")
559
560         def addFavorite(self, name = "", text = "", favoritetype = "", audio = "", bitrate = ""):
561                 self.favoriteConfig.entriescount.value = self.favoriteConfig.entriescount.value + 1
562                 self.favoriteConfig.entriescount.save()
563                 newFavorite = self.initFavouriteEntryConfig()
564                 newFavorite.name.value = name
565                 newFavorite.text.value = text
566                 newFavorite.type.value = favoritetype
567                 newFavorite.audio.value = audio
568                 newFavorite.bitrate.value = bitrate
569                 newFavorite.save()
570                 self.favoriteConfig.saveToFile(self.favoriteConfigFile)
571
572         def renameFavorite(self):
573                 sel = self.getSelectedItem()
574                 if sel is not None:
575                         self.session.openWithCallback(self.renameFavoriteFinished, VirtualKeyBoard, title = _("Enter new name for favorite item"), text = sel.configItem.name.value)
576
577         def renameFavoriteFinished(self, text = None):
578                 if text:
579                         sel = self.getSelectedItem()
580                         sel.configItem.name.value = text
581                         sel.configItem.save()
582                         self.favoriteConfig.saveToFile(self.favoriteConfigFile)
583                         self.favoriteListIndex = 0
584                         self.getFavoriteList()
585
586
587         def removeFavorite(self):
588                 sel = self.getSelectedItem()
589                 if sel is not None:
590                         self.favoriteConfig.entriescount.value = self.favoriteConfig.entriescount.value - 1
591                         self.favoriteConfig.entriescount.save()
592                         self.favoriteConfig.Entries.remove(sel.configItem)
593                         self.favoriteConfig.Entries.save()
594                         self.favoriteConfig.saveToFile(self.favoriteConfigFile)
595                         self.favoriteListIndex = 0
596                         self.getFavoriteList()
597
598         def search(self):
599                 self.session.openWithCallback(self.searchSHOUTcast, VirtualKeyBoard, title = _("Enter text to search for"))
600
601         def searchSHOUTcast(self, searchstring = None):
602                 if searchstring:
603                         self.stopReloadStationListTimer()
604                         self.stationListHeader = _("search-cirteria %s") % searchstring
605                         self.headerTextString = _("(SHOUTcast station list for %s") % self.stationListHeader
606                         self["headertext"].setText("")
607                         self["statustext"].setText(_("Searching SHOUTcast for %s...") % searchstring)
608                         self["list"].hide()
609                         self.stationListURL = "http://yp.shoutcast.com/sbin/newxml.phtml?search=%s" % searchstring
610                         self.mode = self.SEARCHLIST
611                         self.searchSHOUTcastString = searchstring
612                         self.stationListIndex = 0
613                         sendUrlCommand(self.stationListURL, None,10).addCallback(self.callbackStationList).addErrback(self.callbackStationListError)
614
615         def config(self):
616                 self.stopReloadStationListTimer()
617                 self.session.openWithCallback(self.setupFinished, SHOUTcastSetup)
618
619         def setupFinished(self, result):
620                 if result:
621                         if self.mode == self.STATIONLIST:
622                                 self.reloadStationListTimerVar = int(config.plugins.shoutcast.reloadstationlist.value)
623                                 self.stationListIndex = 0
624                                 self.callbackStationList(self.stationListXML)
625
626         def callbackStationListError(self, error = None):
627                 if error is not None:
628                         try:
629                                 self["list"].hide()
630                                 self["statustext"].setText(_("%s\nPress OK to try again...") % str(error.getErrorMessage()))
631                         except: pass
632
633         def Error(self, error = None):
634                 if error is not None:
635                         try:
636                                 self["list"].hide()
637                                 self["statustext"].setText(str(error.getErrorMessage()))
638                         except: pass
639         
640         def __onClose(self):
641                 self.stopReloadStationListTimer()
642                 self.session.nav.playService(self.CurrentService)
643                 containerStreamripper.dataAvail.remove(self.streamripperDataAvail)
644                 containerStreamripper.appClosed.remove(self.streamripperClosed)
645
646         def GoogleImageCallback(self, result):
647                 foundPos = result.find("imgres?imgurl=")
648                 foundPos2 = result.find("&imgrefurl=")
649                 if foundPos != -1 and foundPos2 != -1:
650                         print "[SHOUTcast] downloading cover from %s " % result[foundPos+14:foundPos2]
651                         downloadPage(result[foundPos+14:foundPos2] ,"/tmp/.cover").addCallback(self.coverDownloadFinished).addErrback(self.coverDownloadFailed)
652
653         def coverDownloadFailed(self,result):
654                 print "[SHOUTcast] cover download failed: %s " % result
655                 self["cover"].hide()
656
657         def coverDownloadFinished(self,result):
658                 print "[SHOUTcast] cover download finished"
659                 self["cover"].updateIcon("/tmp/.cover")
660                 self["cover"].show()
661                 
662         def __evUpdatedInfo(self):
663                 sTitle = ""
664                 currPlay = self.session.nav.getCurrentService()
665                 if currPlay is not None:
666                         sTitle = currPlay.info().getInfoString(iServiceInformation.sTagTitle)
667                         if (len(sTitle) !=0):
668                                 url = "http://images.google.de/images?q=%s&btnG=Bilder-Suche" % quote(sTitle)
669                                 sendUrlCommand(url, None,10).addCallback(self.GoogleImageCallback).addErrback(self.Error)
670                 if len(sTitle) == 0:
671                         sTitle = "n/a"
672                 title = _("Title: %s") % sTitle
673                 self["titel"].setText(title)
674                 self.summaries.setText(title)
675
676
677         def __evAudioDecodeError(self):
678                 currPlay = self.session.nav.getCurrentService()
679                 sAudioType = currPlay.info().getInfoString(iServiceInformation.sUser+10)
680                 print "[SHOUTcast __evAudioDecodeError] audio-codec %s can't be decoded by hardware" % (sAudioType)
681                 self.session.open(MessageBox, _("This Dreambox can't decode %s streams!") % sAudioType, type = MessageBox.TYPE_INFO,timeout = 20 )
682
683         def __evPluginError(self):
684                 currPlay = self.session.nav.getCurrentService()
685                 message = currPlay.info().getInfoString(iServiceInformation.sUser+12)
686                 print "[SHOUTcast __evPluginError]" , message
687                 self.session.open(MessageBox, message, type = MessageBox.TYPE_INFO,timeout = 20 )
688
689         def doEofInternal(self, playing):
690                 self.stopPlaying()
691
692         def checkSkipShowHideLock(self):
693                 # nothing to do here
694                 pass
695         
696         def playServiceStream(self, url):
697                 self.session.nav.stopService()
698                 sref = eServiceReference(4097, 0, url)
699                 self.session.nav.playService(sref)
700                 self.currentStreamingURL = url
701                 self["titel"].setText(_("Title: n/a"))
702                 self["station"].setText(_("Station: %s") % self.currentStreamingStation)
703
704         def createSummary(self):
705                 return SHOUTcastLCDScreen
706
707         def initFavouriteEntryConfig(self):
708                 self.favoriteConfig.Entries.append(ConfigSubsection())
709                 i = len(self.favoriteConfig.Entries) -1
710                 self.favoriteConfig.Entries[i].name = ConfigText(default = "")
711                 self.favoriteConfig.Entries[i].text = ConfigText(default = "")
712                 self.favoriteConfig.Entries[i].type = ConfigText(default = "")
713                 self.favoriteConfig.Entries[i].audio = ConfigText(default = "")
714                 self.favoriteConfig.Entries[i].bitrate = ConfigText(default = "")
715                 return self.favoriteConfig.Entries[i]
716
717         def initFavouriteConfig(self):
718                 count = self.favoriteConfig.entriescount.value
719                 if count != 0:
720                         i = 0
721                         while i < count:
722                                 self.initFavouriteEntryConfig()
723                                 i += 1
724
725         def getSelectedItem(self):
726                 sel = None
727                 try:
728                         sel = self["list"].l.getCurrentSelection()[0]
729                 except:return None
730                 return sel
731
732 class Cover(Pixmap):
733         def __init__(self):
734                 Pixmap.__init__(self)
735                 self.picload = ePicLoad()
736                 self.picload.PictureData.get().append(self.paintIconPixmapCB)
737
738         def onShow(self):
739                 Pixmap.onShow(self)
740                 self.picload.setPara((self.instance.size().width(), self.instance.size().height(), 1, 1, False, 1, "#00000000"))
741
742         def paintIconPixmapCB(self, picInfo=None):
743                 ptr = self.picload.getData()
744                 if ptr != None:
745                         self.instance.setPixmap(ptr.__deref__())
746
747         def updateIcon(self, filename):
748                 self.picload.startDecode(filename)
749
750 class SHOUTcastList(GUIComponent, object):
751         def buildEntry(self, item):
752                 width = self.l.getItemSize().width()
753                 res = [ None ]
754                 if self.mode == 0: # GENRELIST
755                         res.append((eListboxPythonMultiContent.TYPE_TEXT, 0, 3, width, 20, 0, RT_HALIGN_LEFT|RT_VALIGN_CENTER, item.name))
756                 elif self.mode == 1: # STATIONLIST
757                         res.append((eListboxPythonMultiContent.TYPE_TEXT, 0, 3, width, 20, 1, RT_HALIGN_LEFT|RT_VALIGN_CENTER, item.name))
758                         res.append((eListboxPythonMultiContent.TYPE_TEXT, 0, 23, width, 20, 1, RT_HALIGN_LEFT|RT_VALIGN_CENTER, item.ct))
759                         res.append((eListboxPythonMultiContent.TYPE_TEXT, 0, 43, width / 2, 20, 1, RT_HALIGN_LEFT|RT_VALIGN_CENTER, _("Audio: %s") % item.mt))
760                         res.append((eListboxPythonMultiContent.TYPE_TEXT,  width / 2, 43, width / 2, 20, 1, RT_HALIGN_RIGHT|RT_VALIGN_CENTER, _("Bit rate: %s kbps") % item.br))
761                 elif self.mode == 2: # FAVORITELIST
762                         res.append((eListboxPythonMultiContent.TYPE_TEXT, 0, 3, width, 20, 1, RT_HALIGN_LEFT|RT_VALIGN_CENTER, item.configItem.name.value))
763                         res.append((eListboxPythonMultiContent.TYPE_TEXT, 0, 23, width, 20, 1, RT_HALIGN_LEFT|RT_VALIGN_CENTER, "%s (%s)" % (item.configItem.text.value, item.configItem.type.value)))
764                         if len(item.configItem.audio.value) != 0:
765                                 res.append((eListboxPythonMultiContent.TYPE_TEXT, 0, 43, width / 2, 20, 1, RT_HALIGN_LEFT|RT_VALIGN_CENTER, _("Audio: %s") % item.configItem.audio.value))
766                         if len(item.configItem.bitrate.value) != 0:
767                                 res.append((eListboxPythonMultiContent.TYPE_TEXT,  width / 2, 43, width / 2, 20, 1, RT_HALIGN_RIGHT|RT_VALIGN_CENTER, _("Bit rate: %s kbps") % item.configItem.bitrate.value))
768                 return res
769
770         def __init__(self):
771                 GUIComponent.__init__(self)
772                 self.l = eListboxPythonMultiContent()
773                 self.l.setFont(0, gFont("Regular", 20))
774                 self.l.setFont(1, gFont("Regular", 18))
775                 self.l.setBuildFunc(self.buildEntry)
776                 self.l.setItemHeight(22)
777                 self.onSelectionChanged = [ ]
778                 self.mode = 0
779
780         def setMode(self, mode):
781                 self.mode = mode
782                 if mode == 0: # GENRELIST
783                         self.l.setItemHeight(22)
784                 elif mode == 1 or mode == 2: # STATIONLIST OR FAVORITELIST
785                         self.l.setItemHeight(63)
786
787         def connectSelChanged(self, fnc):
788                 if not fnc in self.onSelectionChanged:
789                         self.onSelectionChanged.append(fnc)
790
791         def disconnectSelChanged(self, fnc):
792                 if fnc in self.onSelectionChanged:
793                         self.onSelectionChanged.remove(fnc)
794
795         def selectionChanged(self):
796                 for x in self.onSelectionChanged:
797                         x()
798         
799         def getCurrent(self):
800                 cur = self.l.getCurrentSelection()
801                 return cur and cur[0]
802         
803         GUI_WIDGET = eListbox
804         
805         def postWidgetCreate(self, instance):
806                 instance.setContent(self.l)
807                 instance.selectionChanged.get().append(self.selectionChanged)
808
809         def preWidgetRemove(self, instance):
810                 instance.setContent(None)
811                 instance.selectionChanged.get().remove(self.selectionChanged)
812
813         def moveToIndex(self, index):
814                 self.instance.moveSelectionTo(index)
815
816         def getCurrentIndex(self):
817                 return self.instance.getCurrentIndex()
818
819         currentIndex = property(getCurrentIndex, moveToIndex)
820         currentSelection = property(getCurrent)
821
822         def setList(self, list):
823                 self.l.setList(list)
824
825 class SHOUTcastLCDScreen(Screen):
826         skin = """
827         <screen position="0,0" size="132,64" title="SHOUTcast">
828                 <widget name="text1" position="4,0" size="132,14" font="Regular;12" halign="center" valign="center"/>
829                 <widget name="text2" position="4,14" size="132,49" font="Regular;10" halign="center" valign="center"/>
830         </screen>"""
831
832         def __init__(self, session, parent):
833                 Screen.__init__(self, session)
834                 self["text1"] = Label("SHOUTcast")
835                 self["text2"] = Label("")
836
837         def setText(self, text):
838                 self["text2"].setText(text)
839
840
841 class SHOUTcastSetup(Screen, ConfigListScreen):
842
843         skin = """
844                 <screen position="center,center" size="560,400" title="SHOUTcast Setup" >
845                         <ePixmap pixmap="skin_default/buttons/red.png" position="0,0" zPosition="0" size="140,40" transparent="1" alphatest="on" />
846                         <ePixmap pixmap="skin_default/buttons/green.png" position="140,0" zPosition="0" size="140,40" transparent="1" alphatest="on" />
847                         <ePixmap pixmap="skin_default/buttons/yellow.png" position="280,0" zPosition="0" size="140,40" transparent="1" alphatest="on" />
848                         <ePixmap pixmap="skin_default/buttons/blue.png" position="420,0" zPosition="0" size="140,40" transparent="1" alphatest="on" />
849                         <widget render="Label" source="key_red" position="0,0" size="140,40" zPosition="5" valign="center" halign="center" backgroundColor="red" font="Regular;21" transparent="1" foregroundColor="white" shadowColor="black" shadowOffset="-1,-1" />
850                         <widget render="Label" source="key_green" position="140,0" size="140,40" zPosition="5" valign="center" halign="center" backgroundColor="red" font="Regular;21" transparent="1" foregroundColor="white" shadowColor="black" shadowOffset="-1,-1" />
851                         <widget name="config" position="20,50" size="520,330" scrollbarMode="showOnDemand" />
852                 </screen>"""
853
854         def __init__(self, session):
855                 Screen.__init__(self, session)
856
857                 self["key_red"] = StaticText(_("Cancel"))
858                 self["key_green"] = StaticText(_("OK"))
859
860
861                 self.list = [ ]
862                 self.list.append(getConfigListEntry(_("Streaming rate:"), config.plugins.shoutcast.streamingrate))
863                 self.list.append(getConfigListEntry(_("Reload station list:"), config.plugins.shoutcast.reloadstationlist))
864                 self.list.append(getConfigListEntry(_("Rip to single file, name is timestamped"), config.plugins.shoutcast.riptosinglefile))
865                 self.list.append(getConfigListEntry(_("Create a directory for each stream"), config.plugins.shoutcast.createdirforeachstream))
866                 self.list.append(getConfigListEntry(_("Add sequence number to output file"), config.plugins.shoutcast.addsequenceoutputfile))
867                 self.dirname = getConfigListEntry(_("Recording location:"), config.plugins.shoutcast.dirname)
868                 self.list.append(self.dirname)
869                 ConfigListScreen.__init__(self, self.list, session)
870                 self["setupActions"] = ActionMap(["SetupActions", "ColorActions"],
871                 {
872                         "green": self.keySave,
873                         "cancel": self.keyClose,
874                         "ok": self.keySelect,
875                 }, -2)
876
877         def keySelect(self):
878                 cur = self["config"].getCurrent()
879                 if cur == self.dirname:
880                         self.session.openWithCallback(self.pathSelected,SHOUTcastStreamripperRecordingPath,config.plugins.shoutcast.dirname.value)
881
882         def pathSelected(self, res):
883                 if res is not None:
884                         config.plugins.shoutcast.dirname.value = res
885
886         def keySave(self):
887                 for x in self["config"].list:
888                         x[1].save()
889                 configfile.save()
890                 self.close(True)
891
892         def keyClose(self):
893                 for x in self["config"].list:
894                         x[1].cancel()
895                 self.close(False)
896
897
898 class SHOUTcastStreamripperRecordingPath(Screen):
899         skin = """<screen name="SHOUTcastStreamripperRecordingPath" position="center,center" size="560,320" title="Select record path for streamripper">
900                         <ePixmap pixmap="skin_default/buttons/red.png" position="0,0" zPosition="0" size="140,40" transparent="1" alphatest="on" />
901                         <ePixmap pixmap="skin_default/buttons/green.png" position="140,0" zPosition="0" size="140,40" transparent="1" alphatest="on" />
902                         <ePixmap pixmap="skin_default/buttons/yellow.png" position="280,0" zPosition="0" size="140,40" transparent="1" alphatest="on" />
903                         <ePixmap pixmap="skin_default/buttons/blue.png" position="420,0" zPosition="0" size="140,40" transparent="1" alphatest="on" />
904                         <widget name="target" position="0,60" size="540,22" valign="center" font="Regular;22" />
905                         <widget name="filelist" position="0,100" zPosition="1" size="560,220" scrollbarMode="showOnDemand"/>
906                         <widget render="Label" source="key_red" position="0,0" size="140,40" zPosition="5" valign="center" halign="center" backgroundColor="red" font="Regular;21" transparent="1" foregroundColor="white" shadowColor="black" shadowOffset="-1,-1" />
907                         <widget render="Label" source="key_green" position="140,0" size="140,40" zPosition="5" valign="center" halign="center" backgroundColor="red" font="Regular;21" transparent="1" foregroundColor="white" shadowColor="black" shadowOffset="-1,-1" />
908                 </screen>"""
909         def __init__(self, session, initDir):
910                 Screen.__init__(self, session)
911                 inhibitDirs = ["/bin", "/boot", "/dev", "/etc", "/lib", "/proc", "/sbin", "/sys", "/usr", "/var"]
912                 inhibitMounts = []
913                 self["filelist"] = FileList(initDir, showDirectories = True, showFiles = False, inhibitMounts = inhibitMounts, inhibitDirs = inhibitDirs)
914                 self["target"] = Label()
915                 self["actions"] = ActionMap(["WizardActions", "DirectionActions", "ColorActions", "EPGSelectActions"],
916                 {
917                         "back": self.cancel,
918                         "left": self.left,
919                         "right": self.right,
920                         "up": self.up,
921                         "down": self.down,
922                         "ok": self.ok,
923                         "green": self.green,
924                         "red": self.cancel
925                         
926                 }, -1)
927                 self["key_red"] = StaticText(_("Cancel"))
928                 self["key_green"] = StaticText(_("OK"))
929
930         def cancel(self):
931                 self.close(None)
932
933         def green(self):
934                 self.close(self["filelist"].getSelection()[0])
935
936         def up(self):
937                 self["filelist"].up()
938                 self.updateTarget()
939
940         def down(self):
941                 self["filelist"].down()
942                 self.updateTarget()
943
944         def left(self):
945                 self["filelist"].pageUp()
946                 self.updateTarget()
947
948         def right(self):
949                 self["filelist"].pageDown()
950                 self.updateTarget()
951
952         def ok(self):
953                 if self["filelist"].canDescent():
954                         self["filelist"].descent()
955                         self.updateTarget()
956
957         def updateTarget(self):
958                 currFolder = self["filelist"].getSelection()[0]
959                 if currFolder is not None:
960                         self["target"].setText(currFolder)
961                 else:
962                         self["target"].setText(_("Invalid Location"))
963