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
12 from Screens.Screen import Screen
13 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
61 class TestScreen(InfoBarServiceName, InfoBarEvent,InfoBarTuner, WebScreen):
62 def __init__(self, session,request):
63 WebScreen.__init__(self, session,request)
64 InfoBarServiceName.__init__(self)
65 InfoBarEvent.__init__(self)
66 InfoBarTuner.__init__(self)
67 self["CurrentTime"] = Clock()
68 # self["TVSystem"] = Config(config.av.tvsystem)
69 # self["OSDLanguage"] = Config(config.osd.language)
70 # self["FirstRun"] = Config(config.misc.firstrun)
71 from enigma import eServiceReference
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')
73 self["SwitchService"] = ServiceList(fav, command_func = self.zapTo, validate_commands=False)
74 self["ServiceList"] = ServiceList(fav, command_func = self.getServiceList, validate_commands=False)
75 self["ServiceListRecursive"] = ServiceListRecursive(session, func=ServiceListRecursive.FETCH)
76 self["ParentControlList"] = ParentControl(session)
77 self["SubServices"] = SubServices(session)
78 self["Volume"] = Volume(session)
79 self["EPGTITLE"] = EPG(session,func=EPG.TITLE)
80 self["EPGSERVICE"] = EPG(session,func=EPG.SERVICE)
81 self["EPGNOW"] = EPG(session,func=EPG.NOW)
82 self["TimerList"] = Timer(session,func = Timer.LIST)
83 self["TimerAddEventID"] = Timer(session,func = Timer.ADDBYID)
84 self["TimerAdd"] = Timer(session,func = Timer.ADD)
85 self["TimerDel"] = Timer(session,func = Timer.DEL)
86 self["TimerChange"] = Timer(session,func = Timer.CHANGE)
87 self["TimerListWrite"] = Timer(session,func = Timer.WRITE)
88 self["TVBrowser"] = Timer(session,func = Timer.TVBROWSER)
89 self["RecordNow"] = Timer(session,func = Timer.RECNOW)
90 self["MovieList"] = Movie(session,func = Movie.LIST)
91 self["MovieFileDel"] = Movie(session,func = Movie.DEL)
92 self["MovieTags"] = Movie(session,func = Movie.TAGS)
93 self["Volume"] = Volume(session)
94 self["Message"] = Message(session)
95 self["PowerState"] = PowerState(session)
96 self["RemoteControl"] = RemoteControl(session)
97 self["Settings"] = Settings(session)
98 self["AudioTracks"] = AudioTracks(session)
100 self["About"] = About(session)
101 self["WAPFillOptionList"] = WAPfunctions(session,func = WAPfunctions.FILLOPTIONLIST)
103 def getServiceList(self, sRef):
104 self["ServiceList"].root = sRef
106 def zapTo(self, reftozap):
107 from Components.config import config
108 pc = config.ParentalControl.configured.value
110 config.ParentalControl.configured.value = False
111 self.session.nav.playService(reftozap)
113 config.ParentalControl.configured.value = pc
115 switching config.ParentalControl.configured.value
116 ugly, but necessary :(
119 # TODO: (really.) put screens into own files.
120 class Streaming(WebScreen):
121 def __init__(self, session,request):
122 WebScreen.__init__(self, session,request)
123 from Components.Sources.StreamService import StreamService
124 self["StreamService"] = StreamService(self.session.nav)
126 class StreamingM3U(WebScreen):
127 def __init__(self, session,request):
128 WebScreen.__init__(self, session,request)
129 from Components.Sources.StaticText import StaticText
130 from Components.Sources.Config import Config
131 from Components.config import config
132 self["ref"] = StaticText()
133 self["localip"] = RequestData(request,what=RequestData.HOST)
135 class TsM3U(WebScreen):
136 def __init__(self, session,request):
137 WebScreen.__init__(self, session,request)
138 from Components.Sources.StaticText import StaticText
139 from Components.Sources.Config import Config
140 from Components.config import config
141 self["file"] = StaticText()
142 self["localip"] = RequestData(request,what=RequestData.HOST)
144 class RestartTwisted(WebScreen):
145 def __init__(self, session,request):
146 WebScreen.__init__(self, session,request)
148 plugin.restartWebserver()
150 class GetPid(WebScreen):
151 def __init__(self, session,request):
152 WebScreen.__init__(self, session,request)
153 from Components.Sources.StaticText import StaticText
154 from enigma import iServiceInformation
155 pids = self.session.nav.getCurrentService()
157 pidinfo = pids.info()
158 VPID = hex(pidinfo.getInfo(iServiceInformation.sVideoPID))
159 APID = hex(pidinfo.getInfo(iServiceInformation.sAudioPID))
160 PPID = hex(pidinfo.getInfo(iServiceInformation.sPMTPID))
161 self["pids"] = StaticText("%s,%s,%s"%(PPID.lstrip("0x"),VPID.lstrip("0x"),APID.lstrip("0x")))
162 self["localip"] = RequestData(request,what=RequestData.HOST)
165 # implements the 'render'-call.
166 # this will act as a downstream_element, like a renderer.
167 class OneTimeElement(Element):
168 def __init__(self, id):
169 Element.__init__(self)
172 # CHECKME: is this ok performance-wise?
173 def handleCommand(self, args):
174 if self.source_id.find(",") >=0:
175 paramlist = self.source_id.split(",")
177 for key in paramlist:
178 arg = args.get(key, [])
182 list[key] = "".join(arg)
185 self.source.handleCommand(list)
187 for c in args.get(self.source_id, []):
188 self.source.handleCommand(c)
190 def render(self, stream):
191 t = self.source.getHTML(self.source_id)
209 class StreamingElement(OneTimeElement):
210 def __init__(self, id):
211 OneTimeElement.__init__(self, id)
214 def changed(self, what):
216 self.render(self.stream)
218 def setStream(self, stream):
221 # a to-be-filled list item
223 def __init__(self, name, filternum):
225 self.filternum = filternum
227 class TextToHTML(Converter):
228 def __init__(self, arg):
229 Converter.__init__(self, arg)
231 def getHTML(self, id):
232 return self.source.text # encode & etc. here!
234 class TextToURL(Converter):
235 def __init__(self, arg):
236 Converter.__init__(self, arg)
238 def getHTML(self, id):
239 return self.source.text.replace(" ","%20")
241 class ReturnEmptyXML(Converter):
242 def __init__(self, arg):
243 Converter.__init__(self, arg)
245 def getHTML(self, id):
246 return "<rootElement></rootElement>"
248 # a null-output. Useful if you only want to issue a command.
249 class Null(Converter):
250 def __init__(self, arg):
251 Converter.__init__(self, arg)
253 def getHTML(self, id):
256 class JavascriptUpdate(Converter):
257 def __init__(self, arg):
258 Converter.__init__(self, arg)
260 def getHTML(self, id):
261 # 3c5x9, added parent. , this is because the ie loads this in a iframe. an the set is in index.html.xml
262 # all other will replace this in JS
263 return '<script>parent.set("%s", "%s");</script>\n'%(id, self.source.text.replace("\\", "\\\\").replace("\n", "\\n").replace('"', '\\"').replace('\xb0', '°'))
265 # the performant 'listfiller'-engine (plfe)
266 class ListFiller(Converter):
267 def __init__(self, arg):
268 Converter.__init__(self, arg)
272 lut = self.source.lut
273 conv_args = self.converter_arguments
275 # now build a ["string", 1, "string", 2]-styled list, with indices into the
276 # list to avoid lookup of item name for each entry
277 lutlist = [ isinstance(element, basestring) and (element, None) or (lut[element.name], element.filternum) for element in conv_args ]
279 # now, for the huge list, do:
281 append = strlist.append
283 for (element, filternum) in lutlist:
287 append(str(item[element]).replace("\\", "\\\\").replace("\n", "\\n").replace('"', '\\"'))
289 append(str(item[element]).replace("&", "&").replace("<", "<").replace('"', '"').replace(">", ">"))
291 append(str(item[element]).replace("%", "%25").replace("+", "%2B").replace('&', '%26').replace('?', '%3f').replace(' ', '+'))
293 append(str(item[element]))
294 # (this will be done in c++ later!)
295 return ''.join(strlist)
297 text = property(getText)
299 class webifHandler(ContentHandler):
300 def __init__(self, session,request):
304 self.session = session
306 self.request = request
308 def startElement(self, name, attrs):
309 if name == "e2:screen":
310 self.screen = eval(attrs["name"])(self.session,self.request) # fixme
311 self.screens.append(self.screen)
314 if name[:3] == "e2:":
317 tag = [' %s="%s"' %(key,val) for (key, val) in attrs.items()]
321 tag = ''.join(tag)#.encode('utf-8')
325 elif self.mode == 1: # expect "<e2:element>"
326 assert name == "e2:element", "found %s instead of e2:element" % name
327 source = attrs["source"]
328 self.source_id = str(attrs.get("id", source))
329 self.source = self.screen[source]
330 self.is_streaming = "streaming" in attrs
331 elif self.mode == 2: # expect "<e2:convert>"
332 if name[:3] == "e2:":
333 assert name == "e2:convert"
335 ctype = attrs["type"]
337 # TODO: we need something better here
338 if ctype[:4] == "web:": # for now
339 self.converter = eval(ctype[4:])
342 self.converter = my_import('.'.join(["Components", "Converter", ctype])).__dict__.get(ctype)
344 self.converter = my_import('.'.join(["Plugins", "Extensions", "WebInterface", "WebComponents", "Converter", ctype])).__dict__.get(ctype)
349 assert name == "e2:item", "found %s instead of e2:item!" % name
350 assert "name" in attrs, "e2:item must have a name= attribute!"
351 filter = {"": 1, "javascript_escape": 2, "xml": 3, "uri": 4}[attrs.get("filter", "")]
352 self.sub.append(ListItem(attrs["name"], filter))
354 def endElement(self, name):
355 if name == "e2:screen":
359 tag = "</" + name + ">"
362 elif self.mode == 2 and name[:3] != "e2:":
364 elif self.mode == 2: # closed 'convert' -> sub
365 if len(self.sub) == 1:
366 self.sub = self.sub[0]
367 c = self.converter(self.sub)
368 c.connect(self.source)
371 elif self.mode == 1: # closed 'element'
372 # instatiate either a StreamingElement or a OneTimeElement, depending on what's required.
373 if not self.is_streaming:
374 c = OneTimeElement(self.source_id)
376 c = StreamingElement(self.source_id)
378 c.connect(self.source)
380 self.screen.renderer.append(c)
383 if name[:3] == "e2:":
386 def processingInstruction(self, target, data):
387 self.res.append('<?' + target + ' ' + data + '>')
389 def characters(self, ch):
390 ch = ch.encode('utf-8')
396 def startEntity(self, name):
397 self.res.append('&' + name + ';');
400 for screen in self.screens:
404 print "screen cleanup!"
405 for screen in self.screens:
410 def renderPage(stream, path, req, session):
412 # read in the template, create required screens
413 # we don't have persistense yet.
414 # if we had, this first part would only be done once.
415 handler = webifHandler(session,req)
416 parser = make_parser()
417 parser.setFeature(feature_namespaces, 0)
418 parser.setContentHandler(handler)
419 parser.parse(open(util.sibpath(__file__, path)))
421 # by default, we have non-streaming pages
424 # first, apply "commands" (aka. URL argument)
425 for x in handler.res:
426 if isinstance(x, Element):
427 x.handleCommand(req.args)
431 # now, we have a list with static texts mixed
432 # with non-static Elements.
433 # flatten this list, write into the stream.
434 for x in handler.res:
435 if isinstance(x, Element):
436 if isinstance(x, StreamingElement):
444 from twisted.internet import reactor
446 reactor.callLater(3, ping, s)
448 # if we met a "StreamingElement", there is at least one
449 # element which wants to output data more than once,
450 # i.e. on host-originated changes.
451 # in this case, don't finish yet, don't cleanup yet,
452 # but instead do that when the client disconnects.
458 # you *need* something which constantly sends something in a regular interval,
459 # in order to detect disconnected clients.
460 # i agree that this "ping" sucks terrible, so better be sure to have something
461 # similar. A "CurrentTime" is fine. Or anything that creates *some* output.
463 stream.closed_callback = lambda: handler.cleanup()