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