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, InfoBarTuner
17 from Components.Sources.Clock import Clock
18 from Components.Sources.ServiceList import ServiceList
19 from WebComponents.Sources.Volume import Volume
20 from WebComponents.Sources.EPG import EPG
21 from WebComponents.Sources.Timer import Timer
22 from WebComponents.Sources.Movie import Movie
23 from WebComponents.Sources.Message import Message
24 from WebComponents.Sources.PowerState import PowerState
25 from WebComponents.Sources.RemoteControl import RemoteControl
26 from WebComponents.Sources.Settings import Settings
28 from WebComponents.Sources.RequestData import RequestData
29 from Components.Sources.FrontendStatus import FrontendStatus
31 from Components.Converter.Converter import Converter
33 from Components.Element import Element
35 from xml.sax import make_parser
36 from xml.sax.handler import ContentHandler, feature_namespaces
37 from twisted.python import util
41 # prototype of the new web frontend template system.
43 class WebScreen(Screen):
44 def __init__(self, session,request):
45 Screen.__init__(self, session)
46 self.stand_alone = True
47 self.request = request
49 class TestScreen(InfoBarServiceName, InfoBarEvent,InfoBarTuner, WebScreen):
50 def __init__(self, session,request):
51 WebScreen.__init__(self, session,request)
52 InfoBarServiceName.__init__(self)
53 InfoBarEvent.__init__(self)
54 InfoBarTuner.__init__(self)
55 self["CurrentTime"] = Clock()
56 # self["TVSystem"] = Config(config.av.tvsystem)
57 # self["OSDLanguage"] = Config(config.osd.language)
58 # self["FirstRun"] = Config(config.misc.firstrun)
59 from enigma import eServiceReference
60 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')
61 self["ServiceList"] = ServiceList(fav, command_func = self.zapTo, validate_commands=False)
62 self["ServiceListBrowse"] = ServiceList(fav, command_func = self.browseTo, validate_commands=False)
63 self["Volume"] = Volume(session)
64 self["EPGTITLE"] = EPG(session,func=EPG.TITLE)
65 self["EPGSERVICE"] = EPG(session,func=EPG.SERVICE)
66 self["EPGNOW"] = EPG(session,func=EPG.NOW)
67 self["TimerList"] = Timer(session,func = Timer.LIST)
68 self["TimerAddEventID"] = Timer(session,func = Timer.ADDBYID)
69 self["TimerAdd"] = Timer(session,func = Timer.ADD)
70 self["TimerDel"] = Timer(session,func = Timer.DEL)
71 self["TVBrowser"] = Timer(session,func = Timer.TVBROWSER)
72 self["MovieList"] = Movie(session)
73 self["Volume"] = Volume(session)
74 self["Message"] = Message(session)
75 self["PowerState"] = PowerState(session)
76 self["RemoteControl"] = RemoteControl(session)
77 self["Settings"] = Settings(session)
80 def browseTo(self, reftobrowse):
81 self["ServiceListBrowse"].root = reftobrowse
83 def zapTo(self, reftozap):
84 self.session.nav.playService(reftozap)
86 # TODO: (really.) put screens into own files.
87 class Streaming(WebScreen):
88 def __init__(self, session,request):
89 WebScreen.__init__(self, session,request)
90 from Components.Sources.StreamService import StreamService
91 self["StreamService"] = StreamService(self.session.nav)
93 class StreamingM3U(WebScreen):
94 def __init__(self, session,request):
95 WebScreen.__init__(self, session,request)
96 from Components.Sources.StaticText import StaticText
97 from Components.Sources.Config import Config
98 from Components.config import config
99 self["ref"] = StaticText()
100 self["localip"] = RequestData(request,what=RequestData.HOST)
102 class GetPid(WebScreen):
103 def __init__(self, session,request):
104 WebScreen.__init__(self, session,request)
105 from Components.Sources.StaticText import StaticText
106 from enigma import iServiceInformation
107 pids = self.session.nav.getCurrentService()
109 pidinfo = pids.info()
110 VPID = hex(pidinfo.getInfo(iServiceInformation.sVideoPID))
111 APID = hex(pidinfo.getInfo(iServiceInformation.sAudioPID))
112 PPID = hex(pidinfo.getInfo(iServiceInformation.sPMTPID))
113 self["pids"] = StaticText("%s,%s,%s"%(PPID.lstrip("0x"),VPID.lstrip("0x"),APID.lstrip("0x")))
114 self["localip"] = RequestData(request,what=RequestData.HOST)
117 # implements the 'render'-call.
118 # this will act as a downstream_element, like a renderer.
119 class OneTimeElement(Element):
120 def __init__(self, id):
121 Element.__init__(self)
124 # CHECKME: is this ok performance-wise?
125 def handleCommand(self, args):
126 if self.source_id.find(",") >=0:
127 paramlist = self.source_id.split(",")
129 for key in paramlist:
130 arg = args.get(key, [])
134 list[key] = "".join(arg)
137 self.source.handleCommand(list)
139 for c in args.get(self.source_id, []):
140 self.source.handleCommand(c)
142 def render(self, stream):
143 t = self.source.getHTML(self.source_id)
161 class StreamingElement(OneTimeElement):
162 def __init__(self, id):
163 OneTimeElement.__init__(self, id)
166 def changed(self, what):
168 self.render(self.stream)
170 def setStream(self, stream):
173 # a to-be-filled list item
175 def __init__(self, name, filternum):
177 self.filternum = filternum
179 class TextToHTML(Converter):
180 def __init__(self, arg):
181 Converter.__init__(self, arg)
183 def getHTML(self, id):
184 return self.source.text # encode & etc. here!
186 # a null-output. Useful if you only want to issue a command.
187 class Null(Converter):
188 def __init__(self, arg):
189 Converter.__init__(self, arg)
191 def getHTML(self, id):
194 class JavascriptUpdate(Converter):
195 def __init__(self, arg):
196 Converter.__init__(self, arg)
198 def getHTML(self, id):
199 # 3c5x9, added parent. , this is because the ie loads this in a iframe. an the set is in index.html.xml
200 # all other will replace this in JS
201 return '<script>parent.set("%s", "%s");</script>\n'%(id, self.source.text.replace("\\", "\\\\").replace("\n", "\\n").replace('"', '\\"'))
203 # the performant 'listfiller'-engine (plfe)
204 class ListFiller(Converter):
205 def __init__(self, arg):
206 Converter.__init__(self, arg)
210 lut = self.source.lut
211 conv_args = self.converter_arguments
213 # now build a ["string", 1, "string", 2]-styled list, with indices into the
214 # list to avoid lookup of item name for each entry
215 lutlist = [ isinstance(element, basestring) and (element, None) or (lut[element.name], element.filternum) for element in conv_args ]
217 # now, for the huge list, do:
219 append = strlist.append
221 for (element, filternum) in lutlist:
225 append(str(item[element]).replace("\\", "\\\\").replace("\n", "\\n").replace('"', '\\"'))
227 append(str(item[element]).replace("&", "&").replace("<", "<").replace('"', '"').replace(">", ">"))
229 append(str(item[element]).replace("%", "%25").replace("+", "%2B").replace('&', '%26').replace('?', '%3f').replace(' ', '+'))
231 append(str(item[element]))
232 # (this will be done in c++ later!)
233 return ''.join(strlist)
235 text = property(getText)
237 class webifHandler(ContentHandler):
238 def __init__(self, session,request):
242 self.session = session
244 self.request = request
246 def startElement(self, name, attrs):
247 if name == "e2:screen":
248 self.screen = eval(attrs["name"])(self.session,self.request) # fixme
249 self.screens.append(self.screen)
252 if name[:3] == "e2:":
255 tag = [' %s="%s"' %(key,val) for (key, val) in attrs.items()]
259 tag = ''.join(tag)#.encode('utf-8')
263 elif self.mode == 1: # expect "<e2:element>"
264 assert name == "e2:element", "found %s instead of e2:element" % name
265 source = attrs["source"]
266 self.source_id = str(attrs.get("id", source))
267 self.source = self.screen[source]
268 self.is_streaming = "streaming" in attrs
269 elif self.mode == 2: # expect "<e2:convert>"
270 if name[:3] == "e2:":
271 assert name == "e2:convert"
273 ctype = attrs["type"]
275 # TODO: we need something better here
276 if ctype[:4] == "web:": # for now
277 self.converter = eval(ctype[4:])
280 self.converter = my_import('.'.join(["Components", "Converter", ctype])).__dict__.get(ctype)
282 self.converter = my_import('.'.join(["Plugins", "Extensions", "WebInterface", "WebComponents", "Converter", ctype])).__dict__.get(ctype)
287 assert name == "e2:item", "found %s instead of e2:item!" % name
288 assert "name" in attrs, "e2:item must have a name= attribute!"
289 filter = {"": 1, "javascript_escape": 2, "xml": 3, "uri": 4}[attrs.get("filter", "")]
290 self.sub.append(ListItem(attrs["name"], filter))
292 def endElement(self, name):
293 if name == "e2:screen":
297 tag = "</" + name + ">"
300 elif self.mode == 2 and name[:3] != "e2:":
302 elif self.mode == 2: # closed 'convert' -> sub
303 if len(self.sub) == 1:
304 self.sub = self.sub[0]
305 c = self.converter(self.sub)
306 c.connect(self.source)
309 elif self.mode == 1: # closed 'element'
310 # instatiate either a StreamingElement or a OneTimeElement, depending on what's required.
311 if not self.is_streaming:
312 c = OneTimeElement(self.source_id)
314 c = StreamingElement(self.source_id)
316 c.connect(self.source)
318 self.screen.renderer.append(c)
321 if name[:3] == "e2:":
324 def processingInstruction(self, target, data):
325 self.res.append('<?' + target + ' ' + data + '>')
327 def characters(self, ch):
328 ch = ch.encode('utf-8')
334 def startEntity(self, name):
335 self.res.append('&' + name + ';');
338 for screen in self.screens:
342 print "screen cleanup!"
343 for screen in self.screens:
348 def renderPage(stream, path, req, session):
350 # read in the template, create required screens
351 # we don't have persistense yet.
352 # if we had, this first part would only be done once.
353 handler = webifHandler(session,req)
354 parser = make_parser()
355 parser.setFeature(feature_namespaces, 0)
356 parser.setContentHandler(handler)
357 parser.parse(open(util.sibpath(__file__, path)))
359 # by default, we have non-streaming pages
362 # first, apply "commands" (aka. URL argument)
363 for x in handler.res:
364 if isinstance(x, Element):
365 x.handleCommand(req.args)
369 # now, we have a list with static texts mixed
370 # with non-static Elements.
371 # flatten this list, write into the stream.
372 for x in handler.res:
373 if isinstance(x, Element):
374 if isinstance(x, StreamingElement):
382 from twisted.internet import reactor
384 reactor.callLater(3, ping, s)
386 # if we met a "StreamingElement", there is at least one
387 # element which wants to output data more than once,
388 # i.e. on host-originated changes.
389 # in this case, don't finish yet, don't cleanup yet,
390 # but instead do that when the client disconnects.
396 # you *need* something which constantly sends something in a regular interval,
397 # in order to detect disconnected clients.
398 # i agree that this "ping" sucks terrible, so better be sure to have something
399 # similar. A "CurrentTime" is fine. Or anything that creates *some* output.
401 stream.closed_callback = lambda: handler.cleanup()