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