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