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