2 # OK, this is more than a proof of concept
5 # - screens need to be defined somehow else.
6 # I don't know how, yet. Probably each in an own file.
7 # - more components, like the channellist
8 # - better error handling
9 # - use namespace parser
11 from Screens.Screen import Screen
12 from Tools.Import import my_import
15 from Screens.InfoBarGenerics import InfoBarServiceName, InfoBarEvent
16 from Components.Sources.Clock import Clock
17 #from Components.Sources.Config import Config
18 from Components.Sources.ServiceList import ServiceList
19 from Components.Converter.Converter import Converter
20 #from Components.config import config
21 from Components.Element import Element
23 from xml.sax import make_parser
24 from xml.sax.handler import ContentHandler, feature_namespaces
25 from twisted.python import util
29 # prototype of the new web frontend template system.
31 class WebScreen(Screen):
32 def __init__(self, session):
33 Screen.__init__(self, session)
34 self.stand_alone = True
37 class TestScreen(InfoBarServiceName, InfoBarEvent, WebScreen):
38 def __init__(self, session):
39 WebScreen.__init__(self, session)
40 InfoBarServiceName.__init__(self)
41 InfoBarEvent.__init__(self)
42 self["CurrentTime"] = Clock()
43 # self["TVSystem"] = Config(config.av.tvsystem)
44 # self["OSDLanguage"] = Config(config.osd.language)
45 # self["FirstRun"] = Config(config.misc.firstrun)
46 from enigma import eServiceReference
47 fav = eServiceReference('1:7:1:0:0:0:0:0:0:0:(type == 1) || (type == 17) || (type == 195) || (type == 25) FROM BOUQUET "userbouquet.favourites.tv" ORDER BY bouquet')
48 self["ServiceList"] = ServiceList(fav, command_func = self.zapTo)
49 self["ServiceListBrowse"] = ServiceList(fav, command_func = self.browseTo)
51 def browseTo(self, reftobrowse):
52 self["ServiceListBrowse"].root = reftobrowse
54 def zapTo(self, reftozap):
55 self.session.nav.playService(reftozap)
57 class Streaming(WebScreen):
58 def __init__(self, session):
59 WebScreen.__init__(self, session)
60 from Components.Sources.StreamService import StreamService
61 self["StreamService"] = StreamService(self.session.nav)
63 # implements the 'render'-call.
64 # this will act as a downstream_element, like a renderer.
65 class OneTimeElement(Element):
66 def __init__(self, id):
67 Element.__init__(self)
70 # CHECKME: is this ok performance-wise?
71 def handleCommand(self, args):
72 for c in args.get(self.source_id, []):
73 self.source.handleCommand(c)
75 def render(self, stream):
76 t = self.source.getHTML(self.source_id)
77 if isinstance(t, unicode):
96 class StreamingElement(OneTimeElement):
97 def __init__(self, id):
98 OneTimeElement.__init__(self, id)
101 def changed(self, what):
103 self.render(self.stream)
105 def setStream(self, stream):
108 # a to-be-filled list item
110 def __init__(self, name):
113 class TextToHTML(Converter):
114 def __init__(self, arg):
115 Converter.__init__(self, arg)
117 def getHTML(self, id):
118 return self.source.text # encode & etc. here!
120 # a null-output. Useful if you only want to issue a command.
121 class Null(Converter):
122 def __init__(self, arg):
123 Converter.__init__(self, arg)
125 def getHTML(self, id):
129 return s.replace("\\", "\\\\").replace("\n", "\\n").replace('"', '\\"')
131 class JavascriptUpdate(Converter):
132 def __init__(self, arg):
133 Converter.__init__(self, arg)
135 def getHTML(self, id):
136 return '<script>set("' + id + '", "' + escape(self.source.text) + '");</script>\n'
138 # the performant 'listfiller'-engine (plfe)
139 class ListFiller(Converter):
140 def __init__(self, arg):
141 Converter.__init__(self, arg)
145 lut = self.source.lut
147 # now build a ["string", 1, "string", 2]-styled list, with indices into the
148 # list to avoid lookup of item name for each entry
150 for element in self.converter_arguments:
151 if isinstance(element, str):
152 lutlist.append(element)
153 elif isinstance(element, ListItem):
154 lutlist.append(lut[element.name])
156 # now, for the huge list, do:
159 for element in lutlist:
160 if isinstance(element, str):
163 res += str(item[element])
164 # (this will be done in c++ later!)
167 text = property(getText)
169 class webifHandler(ContentHandler):
170 def __init__(self, session):
174 self.session = session
177 def startElement(self, name, attrs):
178 if name == "e2:screen":
179 self.screen = eval(attrs["name"])(self.session) # fixme
180 self.screens.append(self.screen)
183 if name[:3] == "e2:":
186 tag = "<" + name + ''.join([' ' + key + '="' + val + '"' for (key, val) in attrs.items()]) + ">"
187 tag = tag.encode("UTF-8")
191 elif self.mode == 1: # expect "<e2:element>"
192 assert name == "e2:element", "found %s instead of e2:element" % name
193 source = attrs["source"]
194 self.source_id = str(attrs.get("id", source))
195 self.source = self.screen[source]
196 self.is_streaming = "streaming" in attrs
197 elif self.mode == 2: # expect "<e2:convert>"
198 if name[:3] == "e2:":
199 assert name == "e2:convert"
201 ctype = attrs["type"]
202 if ctype[:4] == "web:": # for now
203 self.converter = eval(ctype[4:])
205 self.converter = my_import('.'.join(["Components", "Converter", ctype])).__dict__.get(ctype)
210 assert name == "e2:item", "found %s instead of e2:item!" % name
211 assert "name" in attrs, "e2:item must have a name= attribute!"
212 self.sub.append(ListItem(attrs["name"]))
214 def endElement(self, name):
215 if name == "e2:screen":
219 tag = "</" + name + ">"
222 elif self.mode == 2 and name[:3] != "e2:":
224 elif self.mode == 2: # closed 'convert' -> sub
225 self.sub = lreduce(self.sub)
226 if len(self.sub) == 1:
227 self.sub = self.sub[0]
228 c = self.converter(self.sub)
229 c.connect(self.source)
233 elif self.mode == 1: # closed 'element'
234 # instatiate either a StreamingElement or a OneTimeElement, depending on what's required.
235 if not self.is_streaming:
236 c = OneTimeElement(self.source_id)
238 c = StreamingElement(self.source_id)
240 c.connect(self.source)
242 self.screen.renderer.append(c)
245 if name[:3] == "e2:":
248 def processingInstruction(self, target, data):
249 self.res.append('<?' + target + ' ' + data + '>')
251 def characters(self, ch):
252 ch = ch.encode("UTF-8")
258 def startEntity(self, name):
259 self.res.append('&' + name + ';');
262 for screen in self.screens:
266 print "screen cleanup!"
267 for screen in self.screens:
273 # ouch, can be made better
277 if isinstance(x, str) or isinstance(x, unicode):
278 if isinstance(x, unicode):
279 x = x.encode("UTF-8")
285 if string is not None:
289 if string is not None:
294 def renderPage(stream, path, req, session):
296 # read in the template, create required screens
297 # we don't have persistense yet.
298 # if we had, this first part would only be done once.
299 handler = webifHandler(session)
300 parser = make_parser()
301 parser.setFeature(feature_namespaces, 0)
302 parser.setContentHandler(handler)
303 parser.parse(open(util.sibpath(__file__, path)))
305 # by default, we have non-streaming pages
308 # first, apply "commands" (aka. URL argument)
309 for x in handler.res:
310 if isinstance(x, Element):
311 x.handleCommand(req.args)
315 # now, we have a list with static texts mixed
316 # with non-static Elements.
317 # flatten this list, write into the stream.
318 for x in lreduce(handler.res):
319 if isinstance(x, Element):
320 if isinstance(x, StreamingElement):
328 from twisted.internet import reactor
330 reactor.callLater(3, ping, s)
332 # if we met a "StreamingElement", there is at least one
333 # element which wants to output data more than once,
334 # i.e. on host-originated changes.
335 # in this case, don't finish yet, don't cleanup yet,
336 # but instead do that when the client disconnects.
342 # you *need* something which constantly sends something in a regular interval,
343 # in order to detect disconnected clients.
344 # i agree that this "ping" sucks terrible, so better be sure to have something
345 # similar. A "CurrentTime" is fine. Or anything that creates *some* output.
347 stream.closed_callback = lambda: handler.cleanup()