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
37 from Components.Sources.FrontendStatus import FrontendStatus
39 from Components.Converter.Converter import Converter
41 from Components.Element import Element
43 from xml.sax import make_parser
44 from xml.sax.handler import ContentHandler, feature_namespaces
46 from twisted.python import util
51 # prototype of the new web frontend template system.
53 class WebScreen(Screen):
54 def __init__(self, session, request):
55 Screen.__init__(self, session)
56 self.stand_alone = True
57 self.request = request
60 class DummyWebScreen(WebScreen):
61 #use it, if you dont need any source, just to can do a static file with an xml-file
62 def __init__(self, session,request):
63 WebScreen.__init__(self, session,request)
65 class UpdateWebScreen(InfoBarServiceName, InfoBarEvent,InfoBarTuner,WebScreen):
66 def __init__(self, session,request):
67 WebScreen.__init__(self, session,request)
68 InfoBarServiceName.__init__(self)
69 InfoBarEvent.__init__(self)
70 InfoBarTuner.__init__(self)
71 self["CurrentTime"] = Clock()
72 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')
78 class MessageWebScreen(WebScreen):
79 def __init__(self, session,request):
80 WebScreen.__init__(self, session,request)
81 self["Message"] = Message(session)
83 class AudioWebScreen(WebScreen):
84 def __init__(self, session,request):
85 WebScreen.__init__(self, session,request)
86 self["AudioTracks"] = AudioTracks(session)
88 class AboutWebScreen(WebScreen):
89 def __init__(self, session,request):
90 WebScreen.__init__(self, session,request)
91 self["About"] = About(session)
93 class VolumeWebScreen(WebScreen):
94 def __init__(self, session,request):
95 WebScreen.__init__(self, session,request)
96 self["Volume"] = Volume(session)
98 class SettingsWebScreen(WebScreen):
99 def __init__(self, session,request):
100 WebScreen.__init__(self, session,request)
101 self["Settings"] = Settings(session)
103 class SubServiceWebScreen(WebScreen):
104 def __init__(self, session,request):
105 WebScreen.__init__(self, session,request)
106 self["SubServices"] = SubServices(session)
108 class ServiceWebScreen(WebScreen):
109 def __init__(self, session,request):
110 WebScreen.__init__(self, session,request)
111 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')
112 self["SwitchService"] = ServiceList(fav, command_func = self.zapTo, validate_commands=False)
113 self["ServiceList"] = ServiceList(fav, command_func = self.getServiceList, validate_commands=False)
114 self["ServiceListRecursive"] = ServiceListRecursive(session, func=ServiceListRecursive.FETCH)
116 def getServiceList(self, sRef):
117 self["ServiceList"].root = sRef
119 def zapTo(self, reftozap):
120 from Components.config import config
121 pc = config.ParentalControl.configured.value
123 config.ParentalControl.configured.value = False
124 self.session.nav.playService(reftozap)
126 config.ParentalControl.configured.value = pc
128 switching config.ParentalControl.configured.value
129 ugly, but necessary :(
132 class EPGWebScreen(WebScreen):
133 def __init__(self, session,request):
134 WebScreen.__init__(self, session,request)
135 self["EPGTITLE"] = EPG(session,func=EPG.TITLE)
136 self["EPGSERVICE"] = EPG(session,func=EPG.SERVICE)
137 self["EPGNOW"] = EPG(session,func=EPG.NOW)
139 class MovieWebScreen(WebScreen):
140 def __init__(self, session,request):
141 WebScreen.__init__(self, session,request)
142 from Components.MovieList import MovieList
143 from Tools.Directories import resolveFilename,SCOPE_HDD
144 movielist = MovieList(eServiceReference("2:0:1:0:0:0:0:0:0:0:" + resolveFilename(SCOPE_HDD)))
145 self["MovieList"] = Movie(session,movielist,func = Movie.LIST)
146 self["MovieFileDel"] = Movie(session,movielist,func = Movie.DEL)
147 self["MovieTags"] = Movie(session,movielist,func = Movie.TAGS)
150 class TimerWebScreen(WebScreen):
151 def __init__(self, session,request):
152 WebScreen.__init__(self, session,request)
153 self["TimerList"] = Timer(session,func = Timer.LIST)
154 self["TimerAddEventID"] = Timer(session,func = Timer.ADDBYID)
155 self["TimerAdd"] = Timer(session,func = Timer.ADD)
156 self["TimerDel"] = Timer(session,func = Timer.DEL)
157 self["TimerChange"] = Timer(session,func = Timer.CHANGE)
158 self["TimerListWrite"] = Timer(session,func = Timer.WRITE)
159 self["TVBrowser"] = Timer(session,func = Timer.TVBROWSER)
160 self["RecordNow"] = Timer(session,func = Timer.RECNOW)
162 class RemoteWebScreen(WebScreen):
163 def __init__(self, session,request):
164 WebScreen.__init__(self, session,request)
165 self["RemoteControl"] = RemoteControl(session)
167 class PowerWebScreen(WebScreen):
168 def __init__(self, session,request):
169 WebScreen.__init__(self, session,request)
170 self["PowerState"] = PowerState(session)
172 class ParentControlWebScreen(WebScreen):
173 def __init__(self, session,request):
174 WebScreen.__init__(self, session,request)
175 self["ParentControlList"] = ParentControl(session)
177 class WAPWebScreen(WebScreen):
178 def __init__(self, session,request):
179 WebScreen.__init__(self, session,request)
180 self["WAPFillOptionListSyear"] = WAPfunctions(session,func = WAPfunctions.LISTTIME)
181 self["WAPFillOptionListSday"] = WAPfunctions(session,func = WAPfunctions.LISTTIME)
182 self["WAPFillOptionListSmonth"] = WAPfunctions(session,func = WAPfunctions.LISTTIME)
183 self["WAPFillOptionListShour"] = WAPfunctions(session,func = WAPfunctions.LISTTIME)
184 self["WAPFillOptionListSmin"] = WAPfunctions(session,func = WAPfunctions.LISTTIME)
186 self["WAPFillOptionListEyear"] = WAPfunctions(session,func = WAPfunctions.LISTTIME)
187 self["WAPFillOptionListEday"] = WAPfunctions(session,func = WAPfunctions.LISTTIME)
188 self["WAPFillOptionListEmonth"] = WAPfunctions(session,func = WAPfunctions.LISTTIME)
189 self["WAPFillOptionListEhour"] = WAPfunctions(session,func = WAPfunctions.LISTTIME)
190 self["WAPFillOptionListEmin"] = WAPfunctions(session,func = WAPfunctions.LISTTIME)
192 self["WAPFillOptionListRecord"] = WAPfunctions(session,func = WAPfunctions.OPTIONLIST)
193 self["WAPFillOptionListAfterEvent"] = WAPfunctions(session,func = WAPfunctions.OPTIONLIST)
195 self["WAPFillValueName"] = WAPfunctions(session,func = WAPfunctions.FILLVALUE)
196 self["WAPFillValueDescr"] = WAPfunctions(session,func = WAPfunctions.FILLVALUE)
198 self["WAPFillOptionListRepeated"] = WAPfunctions(session,func = WAPfunctions.REPEATED)
199 self["WAPServiceList"] = WAPfunctions(session, func = WAPfunctions.SERVICELIST)
201 class StreamingWebScreen(WebScreen):
202 def __init__(self, session,request):
203 WebScreen.__init__(self, session,request)
204 from Components.Sources.StreamService import StreamService
205 self["StreamService"] = StreamService(self.session.nav)
207 class M3UStreamingWebScreen(WebScreen):
208 def __init__(self, session,request):
209 WebScreen.__init__(self, session,request)
210 from Components.Sources.StaticText import StaticText
211 from Components.Sources.Config import Config
212 from Components.config import config
213 self["ref"] = StaticText()
214 self["localip"] = RequestData(request,what=RequestData.HOST)
216 class TsM3U(WebScreen):
217 def __init__(self, session,request):
218 WebScreen.__init__(self, session,request)
219 from Components.Sources.StaticText import StaticText
220 from Components.Sources.Config import Config
221 from Components.config import config
222 self["file"] = StaticText()
223 self["localip"] = RequestData(request,what=RequestData.HOST)
225 class RestartWebScreen(WebScreen):
226 def __init__(self, session,request):
227 WebScreen.__init__(self, session,request)
229 plugin.restartWebserver()
231 class GetPid(WebScreen):
232 def __init__(self, session,request):
233 WebScreen.__init__(self, session,request)
234 from Components.Sources.StaticText import StaticText
235 from enigma import iServiceInformation
236 pids = self.session.nav.getCurrentService()
238 pidinfo = pids.info()
239 VPID = hex(pidinfo.getInfo(iServiceInformation.sVideoPID))
240 APID = hex(pidinfo.getInfo(iServiceInformation.sAudioPID))
241 PPID = hex(pidinfo.getInfo(iServiceInformation.sPMTPID))
242 self["pids"] = StaticText("%s,%s,%s"%(PPID.lstrip("0x"),VPID.lstrip("0x"),APID.lstrip("0x")))
243 self["localip"] = RequestData(request,what=RequestData.HOST)
246 # implements the 'render'-call.
247 # this will act as a downstream_element, like a renderer.
248 class OneTimeElement(Element):
249 def __init__(self, id):
250 Element.__init__(self)
253 # CHECKME: is this ok performance-wise?
254 def handleCommand(self, args):
255 if self.source_id.find(",") >=0:
256 paramlist = self.source_id.split(",")
258 for key in paramlist:
259 arg = args.get(key, [])
263 list[key] = "".join(arg)
266 self.source.handleCommand(list)
268 for c in args.get(self.source_id, []):
269 self.source.handleCommand(c)
271 def render(self, stream):
272 t = self.source.getHTML(self.source_id)
290 class StreamingElement(OneTimeElement):
291 def __init__(self, id):
292 OneTimeElement.__init__(self, id)
295 def changed(self, what):
297 self.render(self.stream)
299 def setStream(self, stream):
302 # a to-be-filled list item
304 def __init__(self, name, filternum):
306 self.filternum = filternum
308 class TextToHTML(Converter):
309 def __init__(self, arg):
310 Converter.__init__(self, arg)
312 def getHTML(self, id):
313 return self.source.text # encode & etc. here!
315 class TextToURL(Converter):
316 def __init__(self, arg):
317 Converter.__init__(self, arg)
319 def getHTML(self, id):
320 return self.source.text.replace(" ","%20")
322 class ReturnEmptyXML(Converter):
323 def __init__(self, arg):
324 Converter.__init__(self, arg)
326 def getHTML(self, id):
327 return "<rootElement></rootElement>"
329 # a null-output. Useful if you only want to issue a command.
330 class Null(Converter):
331 def __init__(self, arg):
332 Converter.__init__(self, arg)
334 def getHTML(self, id):
337 class JavascriptUpdate(Converter):
338 def __init__(self, arg):
339 Converter.__init__(self, arg)
341 def getHTML(self, id):
342 # 3c5x9, added parent. , this is because the ie loads this in a iframe. an the set is in index.html.xml
343 # all other will replace this in JS
344 return '<script>parent.set("%s", "%s");</script>\n'%(id, self.source.text.replace("\\", "\\\\").replace("\n", "\\n").replace('"', '\\"').replace('\xb0', '°'))
346 # the performant 'listfiller'-engine (plfe)
347 class ListFiller(Converter):
348 def __init__(self, arg):
349 Converter.__init__(self, arg)
350 # print "ListFiller-arg: ",arg
354 lut = self.source.lut
355 conv_args = self.converter_arguments
357 # now build a ["string", 1, "string", 2]-styled list, with indices into the
358 # list to avoid lookup of item name for each entry
359 lutlist = [ isinstance(element, basestring) and (element, None) or (lut[element.name], element.filternum) for element in conv_args ]
361 # now, for the huge list, do:
363 append = strlist.append
365 for (element, filternum) in lutlist:
369 append(str(item[element]).replace("\\", "\\\\").replace("\n", "\\n").replace('"', '\\"'))
371 append(str(item[element]).replace("&", "&").replace("<", "<").replace('"', '"').replace(">", ">"))
373 append(str(item[element]).replace("%", "%25").replace("+", "%2B").replace('&', '%26').replace('?', '%3f').replace(' ', '+'))
375 append(str(item[element]))
376 # (this will be done in c++ later!)
377 return ''.join(strlist)
379 text = property(getText)
381 class webifHandler(ContentHandler):
382 def __init__(self, session,request):
386 self.session = session
388 self.request = request
390 def startElement(self, name, attrs):
391 if name == "e2:screen":
392 self.screen = eval(attrs["name"])(self.session,self.request) # fixme
393 self.screens.append(self.screen)
396 if name[:3] == "e2:":
399 tag = [' %s="%s"' %(key,val) for (key, val) in attrs.items()]
403 tag = ''.join(tag)#.encode('utf-8')
407 elif self.mode == 1: # expect "<e2:element>"
408 assert name == "e2:element", "found %s instead of e2:element" % name
409 source = attrs["source"]
410 self.source_id = str(attrs.get("id", source))
411 self.source = self.screen[source]
412 self.is_streaming = "streaming" in attrs
413 elif self.mode == 2: # expect "<e2:convert>"
414 if name[:3] == "e2:":
415 assert name == "e2:convert"
417 ctype = attrs["type"]
419 # TODO: we need something better here
420 if ctype[:4] == "web:": # for now
421 self.converter = eval(ctype[4:])
424 self.converter = my_import('.'.join(["Components", "Converter", ctype])).__dict__.get(ctype)
426 self.converter = my_import('.'.join(["Plugins", "Extensions", "WebInterface", "WebComponents", "Converter", ctype])).__dict__.get(ctype)
431 assert name == "e2:item", "found %s instead of e2:item!" % name
432 assert "name" in attrs, "e2:item must have a name= attribute!"
433 filter = {"": 1, "javascript_escape": 2, "xml": 3, "uri": 4}[attrs.get("filter", "")]
434 self.sub.append(ListItem(attrs["name"], filter))
436 def endElement(self, name):
437 if name == "e2:screen":
441 tag = "</" + name + ">"
444 elif self.mode == 2 and name[:3] != "e2:":
446 elif self.mode == 2: # closed 'convert' -> sub
447 if len(self.sub) == 1:
448 self.sub = self.sub[0]
449 c = self.converter(self.sub)
450 c.connect(self.source)
453 elif self.mode == 1: # closed 'element'
454 # instatiate either a StreamingElement or a OneTimeElement, depending on what's required.
455 if not self.is_streaming:
456 c = OneTimeElement(self.source_id)
458 c = StreamingElement(self.source_id)
460 c.connect(self.source)
462 self.screen.renderer.append(c)
465 if name[:3] == "e2:":
468 def processingInstruction(self, target, data):
469 self.res.append('<?' + target + ' ' + data + '>')
471 def characters(self, ch):
472 ch = ch.encode('utf-8')
478 def startEntity(self, name):
479 self.res.append('&' + name + ';');
482 for screen in self.screens:
486 print "screen cleanup!"
487 for screen in self.screens:
492 def renderPage(stream, path, req, session):
494 # read in the template, create required screens
495 # we don't have persistense yet.
496 # if we had, this first part would only be done once.
497 handler = webifHandler(session,req)
498 parser = make_parser()
499 parser.setFeature(feature_namespaces, 0)
500 parser.setContentHandler(handler)
501 parser.parse(open(util.sibpath(__file__, path)))
503 # by default, we have non-streaming pages
506 # first, apply "commands" (aka. URL argument)
507 for x in handler.res:
508 if isinstance(x, Element):
509 x.handleCommand(req.args)
513 # now, we have a list with static texts mixed
514 # with non-static Elements.
515 # flatten this list, write into the stream.
516 for x in handler.res:
517 if isinstance(x, Element):
518 if isinstance(x, StreamingElement):
526 from twisted.internet import reactor
528 reactor.callLater(3, ping, s)
530 # if we met a "StreamingElement", there is at least one
531 # element which wants to output data more than once,
532 # i.e. on host-originated changes.
533 # in this case, don't finish yet, don't cleanup yet,
534 # but instead do that when the client disconnects.
540 # you *need* something which constantly sends something in a regular interval,
541 # in order to detect disconnected clients.
542 # i agree that this "ping" sucks terrible, so better be sure to have something
543 # similar. A "CurrentTime" is fine. Or anything that creates *some* output.
545 stream.closed_callback = lambda: handler.cleanup()