3 # OK, this is more than a proof of concept
6 # - screens need to be defined somehow else.
7 # I don't know how, yet. Probably each in an own file.
8 # - more components, like the channellist
9 # - better error handling
10 # - use namespace parser
11 from enigma import eServiceReference
13 from Screens.Screen import Screen
14 from Tools.Import import my_import
16 from Screens.InfoBarGenerics import InfoBarServiceName, InfoBarEvent, InfoBarTuner
18 from Components.Sources.Clock import Clock
19 from Components.Sources.ServiceList import ServiceList
21 from WebComponents.Sources.ServiceListRecursive import ServiceListRecursive
22 from WebComponents.Sources.Volume import Volume
23 from WebComponents.Sources.EPG import EPG
24 from WebComponents.Sources.Timer import Timer
25 from WebComponents.Sources.Movie import Movie
26 from WebComponents.Sources.Message import Message
27 from WebComponents.Sources.PowerState import PowerState
28 from WebComponents.Sources.RemoteControl import RemoteControl
29 from WebComponents.Sources.Settings import Settings
30 from WebComponents.Sources.SubServices import SubServices
31 from WebComponents.Sources.ParentControl import ParentControl
32 from WebComponents.Sources.About import About
33 from WebComponents.Sources.RequestData import RequestData
34 from WebComponents.Sources.AudioTracks import AudioTracks
35 from WebComponents.Sources.WAPfunctions import WAPfunctions
36 from WebComponents.Sources.MP import MP
38 from Components.Sources.FrontendStatus import FrontendStatus
40 from Components.Converter.Converter import Converter
42 from Components.Element import Element
44 from xml.sax import make_parser
45 from xml.sax.handler import ContentHandler, feature_namespaces
47 from twisted.python import util
52 # prototype of the new web frontend template system.
54 class WebScreen(Screen):
55 def __init__(self, session, request):
56 Screen.__init__(self, session)
57 self.stand_alone = True
58 self.request = request
61 class DummyWebScreen(WebScreen):
62 #use it, if you dont need any source, just to can do a static file with an xml-file
63 def __init__(self, session,request):
64 WebScreen.__init__(self, session,request)
66 class UpdateWebScreen(InfoBarServiceName, InfoBarEvent,InfoBarTuner,WebScreen):
67 def __init__(self, session,request):
68 WebScreen.__init__(self, session,request)
69 InfoBarServiceName.__init__(self)
70 InfoBarEvent.__init__(self)
71 InfoBarTuner.__init__(self)
72 self["CurrentTime"] = Clock()
73 fav = eServiceReference('1:7:1:0:0:0:0:0:0:0:(type == 1) || (type == 17) || (type == 195) || (type == 25) FROM BOUQUET "bouquets.tv" ORDER BY bouquet')
79 class MessageWebScreen(WebScreen):
80 def __init__(self, session,request):
81 WebScreen.__init__(self, session,request)
82 self["Message"] = Message(session,func = Message.PRINT)
83 self["GetAnswer"] = Message(session,func = Message.ANSWER)
85 class AudioWebScreen(WebScreen):
86 def __init__(self, session,request):
87 WebScreen.__init__(self, session,request)
88 self["AudioTracks"] = AudioTracks(session)
90 class AboutWebScreen(WebScreen):
91 def __init__(self, session,request):
92 WebScreen.__init__(self, session,request)
93 self["About"] = About(session)
95 class VolumeWebScreen(WebScreen):
96 def __init__(self, session,request):
97 WebScreen.__init__(self, session,request)
98 self["Volume"] = Volume(session)
100 class SettingsWebScreen(WebScreen):
101 def __init__(self, session,request):
102 WebScreen.__init__(self, session,request)
103 self["Settings"] = Settings(session)
105 class SubServiceWebScreen(WebScreen):
106 def __init__(self, session,request):
107 WebScreen.__init__(self, session,request)
108 self["SubServices"] = SubServices(session)
110 class ServiceWebScreen(WebScreen):
111 def __init__(self, session,request):
112 WebScreen.__init__(self, session,request)
113 fav = eServiceReference('1:7:1:0:0:0:0:0:0:0:(type == 1) || (type == 17) || (type == 195) || (type == 25) FROM BOUQUET "bouquets.tv" ORDER BY bouquet')
114 self["SwitchService"] = ServiceList(fav, command_func = self.zapTo, validate_commands=False)
115 self["ServiceList"] = ServiceList(fav, command_func = self.getServiceList, validate_commands=False)
116 self["ServiceListRecursive"] = ServiceListRecursive(session, func=ServiceListRecursive.FETCH)
118 def getServiceList(self, sRef):
119 self["ServiceList"].root = sRef
121 def zapTo(self, reftozap):
122 from Components.config import config
123 pc = config.ParentalControl.configured.value
125 config.ParentalControl.configured.value = False
126 self.session.nav.playService(reftozap)
128 config.ParentalControl.configured.value = pc
130 switching config.ParentalControl.configured.value
131 ugly, but necessary :(
134 class EPGWebScreen(WebScreen):
135 def __init__(self, session,request):
136 WebScreen.__init__(self, session,request)
137 self["EPGTITLE"] = EPG(session,func=EPG.TITLE)
138 self["EPGSERVICE"] = EPG(session,func=EPG.SERVICE)
139 self["EPGNOW"] = EPG(session,func=EPG.NOW)
141 class MovieWebScreen(WebScreen):
142 def __init__(self, session,request):
143 WebScreen.__init__(self, session,request)
144 from Components.MovieList import MovieList
145 from Tools.Directories import resolveFilename,SCOPE_HDD
146 movielist = MovieList(eServiceReference("2:0:1:0:0:0:0:0:0:0:" + resolveFilename(SCOPE_HDD)))
147 self["MovieList"] = Movie(session,movielist,func = Movie.LIST)
148 self["MovieFileDel"] = Movie(session,movielist,func = Movie.DEL)
149 self["MovieTags"] = Movie(session,movielist,func = Movie.TAGS)
151 class MediaPlayerWebScreen(WebScreen):
152 def __init__(self, session,request):
153 WebScreen.__init__(self, session,request)
154 self["FileList"] = MP(session,func = MP.LIST)
155 self["PlayFile"] = MP(session,func = MP.PLAY)
156 self["Command"] = MP(session,func = MP.COMMAND)
160 class TimerWebScreen(WebScreen):
161 def __init__(self, session,request):
162 WebScreen.__init__(self, session,request)
163 self["TimerList"] = Timer(session,func = Timer.LIST)
164 self["TimerAddEventID"] = Timer(session,func = Timer.ADDBYID)
165 self["TimerAdd"] = Timer(session,func = Timer.ADD)
166 self["TimerDel"] = Timer(session,func = Timer.DEL)
167 self["TimerChange"] = Timer(session,func = Timer.CHANGE)
168 self["TimerListWrite"] = Timer(session,func = Timer.WRITE)
169 self["TVBrowser"] = Timer(session,func = Timer.TVBROWSER)
170 self["RecordNow"] = Timer(session,func = Timer.RECNOW)
172 class RemoteWebScreen(WebScreen):
173 def __init__(self, session,request):
174 WebScreen.__init__(self, session,request)
175 self["RemoteControl"] = RemoteControl(session)
177 class PowerWebScreen(WebScreen):
178 def __init__(self, session,request):
179 WebScreen.__init__(self, session,request)
180 self["PowerState"] = PowerState(session)
182 class ParentControlWebScreen(WebScreen):
183 def __init__(self, session,request):
184 WebScreen.__init__(self, session,request)
185 self["ParentControlList"] = ParentControl(session)
187 class WAPWebScreen(WebScreen):
188 def __init__(self, session,request):
189 WebScreen.__init__(self, session,request)
190 self["WAPFillOptionListSyear"] = WAPfunctions(session,func = WAPfunctions.LISTTIME)
191 self["WAPFillOptionListSday"] = WAPfunctions(session,func = WAPfunctions.LISTTIME)
192 self["WAPFillOptionListSmonth"] = WAPfunctions(session,func = WAPfunctions.LISTTIME)
193 self["WAPFillOptionListShour"] = WAPfunctions(session,func = WAPfunctions.LISTTIME)
194 self["WAPFillOptionListSmin"] = WAPfunctions(session,func = WAPfunctions.LISTTIME)
196 self["WAPFillOptionListEyear"] = WAPfunctions(session,func = WAPfunctions.LISTTIME)
197 self["WAPFillOptionListEday"] = WAPfunctions(session,func = WAPfunctions.LISTTIME)
198 self["WAPFillOptionListEmonth"] = WAPfunctions(session,func = WAPfunctions.LISTTIME)
199 self["WAPFillOptionListEhour"] = WAPfunctions(session,func = WAPfunctions.LISTTIME)
200 self["WAPFillOptionListEmin"] = WAPfunctions(session,func = WAPfunctions.LISTTIME)
202 self["WAPFillOptionListRecord"] = WAPfunctions(session,func = WAPfunctions.OPTIONLIST)
203 self["WAPFillOptionListAfterEvent"] = WAPfunctions(session,func = WAPfunctions.OPTIONLIST)
205 self["WAPFillValueName"] = WAPfunctions(session,func = WAPfunctions.FILLVALUE)
206 self["WAPFillValueDescr"] = WAPfunctions(session,func = WAPfunctions.FILLVALUE)
208 self["WAPFillOptionListRepeated"] = WAPfunctions(session,func = WAPfunctions.REPEATED)
209 self["WAPServiceList"] = WAPfunctions(session, func = WAPfunctions.SERVICELIST)
211 self["WAPdeleteOldOnSave"] = WAPfunctions(session,func = WAPfunctions.DELETEOLD)
213 class StreamingWebScreen(WebScreen):
214 def __init__(self, session,request):
215 WebScreen.__init__(self, session,request)
216 from Components.Sources.StreamService import StreamService
217 self["StreamService"] = StreamService(self.session.nav)
219 class M3UStreamingWebScreen(WebScreen):
220 def __init__(self, session,request):
221 WebScreen.__init__(self, session,request)
222 from Components.Sources.StaticText import StaticText
223 from Components.Sources.Config import Config
224 from Components.config import config
225 self["ref"] = StaticText()
226 self["localip"] = RequestData(request,what=RequestData.HOST)
228 class TsM3U(WebScreen):
229 def __init__(self, session,request):
230 WebScreen.__init__(self, session,request)
231 from Components.Sources.StaticText import StaticText
232 from Components.Sources.Config import Config
233 from Components.config import config
234 self["file"] = StaticText()
235 self["localip"] = RequestData(request,what=RequestData.HOST)
237 class RestartWebScreen(WebScreen):
238 def __init__(self, session,request):
239 WebScreen.__init__(self, session,request)
241 plugin.restartWebserver()
243 class GetPid(WebScreen):
244 def __init__(self, session,request):
245 WebScreen.__init__(self, session,request)
246 from Components.Sources.StaticText import StaticText
247 from enigma import iServiceInformation
248 pids = self.session.nav.getCurrentService()
250 pidinfo = pids.info()
251 VPID = hex(pidinfo.getInfo(iServiceInformation.sVideoPID))
252 APID = hex(pidinfo.getInfo(iServiceInformation.sAudioPID))
253 PPID = hex(pidinfo.getInfo(iServiceInformation.sPMTPID))
254 self["pids"] = StaticText("%s,%s,%s"%(PPID.lstrip("0x"),VPID.lstrip("0x"),APID.lstrip("0x")))
255 self["localip"] = RequestData(request,what=RequestData.HOST)
258 # implements the 'render'-call.
259 # this will act as a downstream_element, like a renderer.
260 class OneTimeElement(Element):
261 def __init__(self, id):
262 Element.__init__(self)
265 # CHECKME: is this ok performance-wise?
266 def handleCommand(self, args):
267 if self.source_id.find(",") >=0:
268 paramlist = self.source_id.split(",")
270 for key in paramlist:
271 arg = args.get(key, [])
275 list[key] = "".join(arg)
278 self.source.handleCommand(list)
280 for c in args.get(self.source_id, []):
281 self.source.handleCommand(c)
283 def render(self, stream):
284 t = self.source.getHTML(self.source_id)
302 class StreamingElement(OneTimeElement):
303 def __init__(self, id):
304 OneTimeElement.__init__(self, id)
307 def changed(self, what):
309 self.render(self.stream)
311 def setStream(self, stream):
314 # a to-be-filled list item
316 def __init__(self, name, filternum):
318 self.filternum = filternum
320 class TextToHTML(Converter):
321 def __init__(self, arg):
322 Converter.__init__(self, arg)
324 def getHTML(self, id):
325 return self.source.text # encode & etc. here!
327 class TextToURL(Converter):
328 def __init__(self, arg):
329 Converter.__init__(self, arg)
331 def getHTML(self, id):
332 return self.source.text.replace(" ","%20")
334 class ReturnEmptyXML(Converter):
335 def __init__(self, arg):
336 Converter.__init__(self, arg)
338 def getHTML(self, id):
339 return "<rootElement></rootElement>"
341 # a null-output. Useful if you only want to issue a command.
342 class Null(Converter):
343 def __init__(self, arg):
344 Converter.__init__(self, arg)
346 def getHTML(self, id):
349 class JavascriptUpdate(Converter):
350 def __init__(self, arg):
351 Converter.__init__(self, arg)
353 def getHTML(self, id):
354 # 3c5x9, added parent. , this is because the ie loads this in a iframe. an the set is in index.html.xml
355 # all other will replace this in JS
356 return '<script>parent.set("%s", "%s");</script>\n'%(id, self.source.text.replace("\\", "\\\\").replace("\n", "\\n").replace('"', '\\"').replace('\xb0', '°'))
358 # the performant 'listfiller'-engine (plfe)
359 class ListFiller(Converter):
360 def __init__(self, arg):
361 Converter.__init__(self, arg)
362 # print "ListFiller-arg: ",arg
366 lut = self.source.lut
367 conv_args = self.converter_arguments
369 # now build a ["string", 1, "string", 2]-styled list, with indices into the
370 # list to avoid lookup of item name for each entry
371 lutlist = [ isinstance(element, basestring) and (element, None) or (lut[element.name], element.filternum) for element in conv_args ]
373 # now, for the huge list, do:
375 append = strlist.append
377 for (element, filternum) in lutlist:
381 append(str(item[element]).replace("\\", "\\\\").replace("\n", "\\n").replace('"', '\\"'))
383 append(str(item[element]).replace("&", "&").replace("<", "<").replace('"', '"').replace(">", ">"))
385 append(str(item[element]).replace("%", "%25").replace("+", "%2B").replace('&', '%26').replace('?', '%3f').replace(' ', '+'))
387 append(str(item[element]))
388 # (this will be done in c++ later!)
389 return ''.join(strlist)
391 text = property(getText)
393 class webifHandler(ContentHandler):
394 def __init__(self, session,request):
398 self.session = session
400 self.request = request
402 def startElement(self, name, attrs):
403 if name == "e2:screen":
404 self.screen = eval(attrs["name"])(self.session,self.request) # fixme
405 self.screens.append(self.screen)
408 if name[:3] == "e2:":
411 tag = [' %s="%s"' %(key,val) for (key, val) in attrs.items()]
415 tag = ''.join(tag)#.encode('utf-8')
419 elif self.mode == 1: # expect "<e2:element>"
420 assert name == "e2:element", "found %s instead of e2:element" % name
421 source = attrs["source"]
422 self.source_id = str(attrs.get("id", source))
423 self.source = self.screen[source]
424 self.is_streaming = "streaming" in attrs
425 elif self.mode == 2: # expect "<e2:convert>"
426 if name[:3] == "e2:":
427 assert name == "e2:convert"
429 ctype = attrs["type"]
431 # TODO: we need something better here
432 if ctype[:4] == "web:": # for now
433 self.converter = eval(ctype[4:])
436 self.converter = my_import('.'.join(["Components", "Converter", ctype])).__dict__.get(ctype)
438 self.converter = my_import('.'.join(["Plugins", "Extensions", "WebInterface", "WebComponents", "Converter", ctype])).__dict__.get(ctype)
443 assert name == "e2:item", "found %s instead of e2:item!" % name
444 assert "name" in attrs, "e2:item must have a name= attribute!"
445 filter = {"": 1, "javascript_escape": 2, "xml": 3, "uri": 4}[attrs.get("filter", "")]
446 self.sub.append(ListItem(attrs["name"], filter))
448 def endElement(self, name):
449 if name == "e2:screen":
453 tag = "</" + name + ">"
456 elif self.mode == 2 and name[:3] != "e2:":
458 elif self.mode == 2: # closed 'convert' -> sub
459 if len(self.sub) == 1:
460 self.sub = self.sub[0]
461 c = self.converter(self.sub)
462 c.connect(self.source)
465 elif self.mode == 1: # closed 'element'
466 # instatiate either a StreamingElement or a OneTimeElement, depending on what's required.
467 if not self.is_streaming:
468 c = OneTimeElement(self.source_id)
470 c = StreamingElement(self.source_id)
472 c.connect(self.source)
474 self.screen.renderer.append(c)
477 if name[:3] == "e2:":
480 def processingInstruction(self, target, data):
481 self.res.append('<?' + target + ' ' + data + '>')
483 def characters(self, ch):
484 ch = ch.encode('utf-8')
490 def startEntity(self, name):
491 self.res.append('&' + name + ';');
494 for screen in self.screens:
498 print "screen cleanup!"
499 for screen in self.screens:
504 def renderPage(stream, path, req, session):
506 # read in the template, create required screens
507 # we don't have persistense yet.
508 # if we had, this first part would only be done once.
509 handler = webifHandler(session,req)
510 parser = make_parser()
511 parser.setFeature(feature_namespaces, 0)
512 parser.setContentHandler(handler)
513 parser.parse(open(util.sibpath(__file__, path)))
515 # by default, we have non-streaming pages
518 # first, apply "commands" (aka. URL argument)
519 for x in handler.res:
520 if isinstance(x, Element):
521 x.handleCommand(req.args)
525 # now, we have a list with static texts mixed
526 # with non-static Elements.
527 # flatten this list, write into the stream.
528 for x in handler.res:
529 if isinstance(x, Element):
530 if isinstance(x, StreamingElement):
538 from twisted.internet import reactor
540 reactor.callLater(3, ping, s)
542 # if we met a "StreamingElement", there is at least one
543 # element which wants to output data more than once,
544 # i.e. on host-originated changes.
545 # in this case, don't finish yet, don't cleanup yet,
546 # but instead do that when the client disconnects.
552 # you *need* something which constantly sends something in a regular interval,
553 # in order to detect disconnected clients.
554 # i agree that this "ping" sucks terrible, so better be sure to have something
555 # similar. A "CurrentTime" is fine. Or anything that creates *some* output.
557 stream.closed_callback = lambda: handler.cleanup()