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.
32 class TestScreen(InfoBarServiceName, InfoBarEvent, Screen):
33 def __init__(self, session):
34 Screen.__init__(self, session)
35 InfoBarServiceName.__init__(self)
36 InfoBarEvent.__init__(self)
37 self["CurrentTime"] = Clock()
38 # self["TVSystem"] = Config(config.av.tvsystem)
39 # self["OSDLanguage"] = Config(config.osd.language)
40 # self["FirstRun"] = Config(config.misc.firstrun)
41 from enigma import eServiceReference
42 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')
43 self["ServiceList"] = ServiceList(fav, command_func = self.zapTo)
44 self["ServiceListBrowse"] = ServiceList(fav, command_func = self.browseTo)
46 def browseTo(self, reftobrowse):
47 self["ServiceListBrowse"].root = reftobrowse
49 def zapTo(self, reftozap):
50 self.session.nav.playService(reftozap)
52 # implements the 'render'-call.
53 # this will act as a downstream_element, like a renderer.
54 class OneTimeElement(Element):
55 def __init__(self, id):
56 Element.__init__(self)
59 # CHECKME: is this ok performance-wise?
60 def handleCommand(self, args):
61 for c in args.get(self.source_id, []):
62 self.source.handleCommand(c)
64 def render(self, stream):
65 t = self.source.getHTML(self.source_id)
66 if isinstance(t, unicode):
73 class StreamingElement(OneTimeElement):
74 def __init__(self, id):
75 OneTimeElement.__init__(self, id)
78 def changed(self, what):
80 self.render(self.stream)
82 def setStream(self, stream):
85 # a to-be-filled list item
87 def __init__(self, name):
90 class TextToHTML(Converter):
91 def __init__(self, arg):
92 Converter.__init__(self, arg)
94 def getHTML(self, id):
95 return self.source.text # encode & etc. here!
97 # a null-output. Useful if you only want to issue a command.
98 class Null(Converter):
99 def __init__(self, arg):
100 Converter.__init__(self, arg)
102 def getHTML(self, id):
106 return s.replace("\\", "\\\\").replace("\n", "\\n").replace('"', '\\"')
108 class JavascriptUpdate(Converter):
109 def __init__(self, arg):
110 Converter.__init__(self, arg)
112 def getHTML(self, id):
113 return '<script>set("' + id + '", "' + escape(self.source.text) + '");</script>\n'
115 # the performant 'listfiller'-engine (plfe)
116 class ListFiller(Converter):
117 def __init__(self, arg):
118 Converter.__init__(self, arg)
122 lut = self.source.lut
124 # now build a ["string", 1, "string", 2]-styled list, with indices into the
125 # list to avoid lookup of item name for each entry
127 for element in self.converter_arguments:
128 if isinstance(element, str):
129 lutlist.append(element)
130 elif isinstance(element, ListItem):
131 lutlist.append(lut[element.name])
133 # now, for the huge list, do:
136 for element in lutlist:
137 if isinstance(element, str):
140 res += str(item[element])
141 # (this will be done in c++ later!)
144 text = property(getText)
146 class webifHandler(ContentHandler):
147 def __init__(self, session):
151 self.session = session
154 def startElement(self, name, attrs):
155 if name == "e2:screen":
156 self.screen = eval(attrs["name"])(self.session) # fixme
157 self.screens.append(self.screen)
160 if name[:3] == "e2:":
163 tag = "<" + name + ''.join([' ' + key + '="' + val + '"' for (key, val) in attrs.items()]) + ">"
164 tag = tag.encode("UTF-8")
168 elif self.mode == 1: # expect "<e2:element>"
169 assert name == "e2:element", "found %s instead of e2:element" % name
170 source = attrs["source"]
171 self.source_id = str(attrs.get("id", source))
172 self.source = self.screen[source]
173 self.is_streaming = "streaming" in attrs
174 elif self.mode == 2: # expect "<e2:convert>"
175 if name[:3] == "e2:":
176 assert name == "e2:convert"
178 ctype = attrs["type"]
179 if ctype[:4] == "web:": # for now
180 self.converter = eval(ctype[4:])
182 self.converter = my_import('.'.join(["Components", "Converter", ctype])).__dict__.get(ctype)
187 assert name == "e2:item", "found %s instead of e2:item!" % name
188 assert "name" in attrs, "e2:item must have a name= attribute!"
189 self.sub.append(ListItem(attrs["name"]))
191 def endElement(self, name):
192 if name == "e2:screen":
196 tag = "</" + name + ">"
199 elif self.mode == 2 and name[:3] != "e2:":
201 elif self.mode == 2: # closed 'convert' -> sub
202 self.sub = lreduce(self.sub)
203 if len(self.sub) == 1:
204 self.sub = self.sub[0]
205 c = self.converter(self.sub)
206 c.connect(self.source)
210 elif self.mode == 1: # closed 'element'
211 # instatiate either a StreamingElement or a OneTimeElement, depending on what's required.
212 if not self.is_streaming:
213 c = OneTimeElement(self.source_id)
215 c = StreamingElement(self.source_id)
217 c.connect(self.source)
219 self.screen.renderer.append(c)
222 if name[:3] == "e2:":
225 def processingInstruction(self, target, data):
226 self.res.append('<?' + target + ' ' + data + '>')
228 def characters(self, ch):
229 ch = ch.encode("UTF-8")
235 def startEntity(self, name):
236 self.res.append('&' + name + ';');
239 print "screen cleanup!"
240 for screen in self.screens:
245 # ouch, can be made better
249 if isinstance(x, str) or isinstance(x, unicode):
250 if isinstance(x, unicode):
251 x = x.encode("UTF-8")
257 if string is not None:
261 if string is not None:
266 def renderPage(stream, path, req, session):
268 # read in the template, create required screens
269 # we don't have persistense yet.
270 # if we had, this first part would only be done once.
271 handler = webifHandler(session)
272 parser = make_parser()
273 parser.setFeature(feature_namespaces, 0)
274 parser.setContentHandler(handler)
275 parser.parse(open(util.sibpath(__file__, path)))
277 # by default, we have non-streaming pages
280 # first, apply "commands" (aka. URL argument)
281 for x in handler.res:
282 if isinstance(x, Element):
283 x.handleCommand(req.args)
285 # now, we have a list with static texts mixed
286 # with non-static Elements.
287 # flatten this list, write into the stream.
288 for x in lreduce(handler.res):
289 if isinstance(x, Element):
290 if isinstance(x, StreamingElement):
298 from twisted.internet import reactor
299 s.write("PING<br/>\n");
300 reactor.callLater(3, ping, s)
302 # if we met a "StreamingElement", there is at least one
303 # element which wants to output data more than once,
304 # i.e. on host-originated changes.
305 # in this case, don't finish yet, don't cleanup yet,
306 # but instead do that when the client disconnects.
312 # you *need* something which constantly sends something in a regular interval,
313 # in order to detect disconnected clients.
314 # i agree that this "ping" sucks terrible, so better be sure to have something
315 # similar. A "CurrentTime" is fine. Or anything that creates *some* output.
317 stream.closed_callback = lambda: handler.cleanup()