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
27 from WebComponents.Sources.SubServices import SubServices
29 from WebComponents.Sources.RequestData import RequestData
30 from Components.Sources.FrontendStatus import FrontendStatus
32 from Components.Converter.Converter import Converter
34 from Components.Element import Element
36 from xml.sax import make_parser
37 from xml.sax.handler import ContentHandler, feature_namespaces
38 from twisted.python import util
42 # prototype of the new web frontend template system.
44 class WebScreen(Screen):
45 def __init__(self, session,request):
46 Screen.__init__(self, session)
47 self.stand_alone = True
48 self.request = request
50 class TestScreen(InfoBarServiceName, InfoBarEvent,InfoBarTuner, WebScreen):
51 def __init__(self, session,request):
52 WebScreen.__init__(self, session,request)
53 InfoBarServiceName.__init__(self)
54 InfoBarEvent.__init__(self)
55 InfoBarTuner.__init__(self)
56 self["CurrentTime"] = Clock()
57 # self["TVSystem"] = Config(config.av.tvsystem)
58 # self["OSDLanguage"] = Config(config.osd.language)
59 # self["FirstRun"] = Config(config.misc.firstrun)
60 from enigma import eServiceReference
61 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')
62 self["ServiceList"] = ServiceList(fav, command_func = self.zapTo, validate_commands=False)
63 self["ServiceListBrowse"] = ServiceList(fav, command_func = self.browseTo, validate_commands=False)
64 self["SubServices"] = SubServices(session)
65 self["Volume"] = Volume(session)
66 self["EPGTITLE"] = EPG(session,func=EPG.TITLE)
67 self["EPGSERVICE"] = EPG(session,func=EPG.SERVICE)
68 self["EPGNOW"] = EPG(session,func=EPG.NOW)
69 self["TimerList"] = Timer(session,func = Timer.LIST)
70 self["TimerAddEventID"] = Timer(session,func = Timer.ADDBYID)
71 self["TimerAdd"] = Timer(session,func = Timer.ADD)
72 self["TimerDel"] = Timer(session,func = Timer.DEL)
73 self["TimerChange"] = Timer(session,func = Timer.CHANGE)
74 self["TVBrowser"] = Timer(session,func = Timer.TVBROWSER)
75 self["MovieList"] = Movie(session)
76 self["Volume"] = Volume(session)
77 self["Message"] = Message(session)
78 self["PowerState"] = PowerState(session)
79 self["RemoteControl"] = RemoteControl(session)
80 self["Settings"] = Settings(session)
83 def browseTo(self, reftobrowse):
84 self["ServiceListBrowse"].root = reftobrowse
86 def zapTo(self, reftozap):
87 self.session.nav.playService(reftozap)
89 # TODO: (really.) put screens into own files.
90 class Streaming(WebScreen):
91 def __init__(self, session,request):
92 WebScreen.__init__(self, session,request)
93 from Components.Sources.StreamService import StreamService
94 self["StreamService"] = StreamService(self.session.nav)
96 class StreamingM3U(WebScreen):
97 def __init__(self, session,request):
98 WebScreen.__init__(self, session,request)
99 from Components.Sources.StaticText import StaticText
100 from Components.Sources.Config import Config
101 from Components.config import config
102 self["ref"] = StaticText()
103 self["localip"] = RequestData(request,what=RequestData.HOST)
105 class GetPid(WebScreen):
106 def __init__(self, session,request):
107 WebScreen.__init__(self, session,request)
108 from Components.Sources.StaticText import StaticText
109 from enigma import iServiceInformation
110 pids = self.session.nav.getCurrentService()
112 pidinfo = pids.info()
113 VPID = hex(pidinfo.getInfo(iServiceInformation.sVideoPID))
114 APID = hex(pidinfo.getInfo(iServiceInformation.sAudioPID))
115 PPID = hex(pidinfo.getInfo(iServiceInformation.sPMTPID))
116 self["pids"] = StaticText("%s,%s,%s"%(PPID.lstrip("0x"),VPID.lstrip("0x"),APID.lstrip("0x")))
117 self["localip"] = RequestData(request,what=RequestData.HOST)
120 # implements the 'render'-call.
121 # this will act as a downstream_element, like a renderer.
122 class OneTimeElement(Element):
123 def __init__(self, id):
124 Element.__init__(self)
127 # CHECKME: is this ok performance-wise?
128 def handleCommand(self, args):
129 if self.source_id.find(",") >=0:
130 paramlist = self.source_id.split(",")
132 for key in paramlist:
133 arg = args.get(key, [])
137 list[key] = "".join(arg)
140 self.source.handleCommand(list)
142 for c in args.get(self.source_id, []):
143 self.source.handleCommand(c)
145 def render(self, stream):
146 t = self.source.getHTML(self.source_id)
164 class StreamingElement(OneTimeElement):
165 def __init__(self, id):
166 OneTimeElement.__init__(self, id)
169 def changed(self, what):
171 self.render(self.stream)
173 def setStream(self, stream):
176 # a to-be-filled list item
178 def __init__(self, name, filternum):
180 self.filternum = filternum
182 class TextToHTML(Converter):
183 def __init__(self, arg):
184 Converter.__init__(self, arg)
186 def getHTML(self, id):
187 return self.source.text # encode & etc. here!
189 class ReturnEmptyXML(Converter):
190 def __init__(self, arg):
191 Converter.__init__(self, arg)
193 def getHTML(self, id):
194 return "<rootElement></rootElement>"
196 # a null-output. Useful if you only want to issue a command.
197 class Null(Converter):
198 def __init__(self, arg):
199 Converter.__init__(self, arg)
201 def getHTML(self, id):
204 class JavascriptUpdate(Converter):
205 def __init__(self, arg):
206 Converter.__init__(self, arg)
208 def getHTML(self, id):
209 # 3c5x9, added parent. , this is because the ie loads this in a iframe. an the set is in index.html.xml
210 # all other will replace this in JS
211 return '<script>parent.set("%s", "%s");</script>\n'%(id, self.source.text.replace("\\", "\\\\").replace("\n", "\\n").replace('"', '\\"'))
213 # the performant 'listfiller'-engine (plfe)
214 class ListFiller(Converter):
215 def __init__(self, arg):
216 Converter.__init__(self, arg)
220 lut = self.source.lut
221 conv_args = self.converter_arguments
223 # now build a ["string", 1, "string", 2]-styled list, with indices into the
224 # list to avoid lookup of item name for each entry
225 lutlist = [ isinstance(element, basestring) and (element, None) or (lut[element.name], element.filternum) for element in conv_args ]
227 # now, for the huge list, do:
229 append = strlist.append
231 for (element, filternum) in lutlist:
235 append(str(item[element]).replace("\\", "\\\\").replace("\n", "\\n").replace('"', '\\"'))
237 append(str(item[element]).replace("&", "&").replace("<", "<").replace('"', '"').replace(">", ">"))
239 append(str(item[element]).replace("%", "%25").replace("+", "%2B").replace('&', '%26').replace('?', '%3f').replace(' ', '+'))
241 append(str(item[element]))
242 # (this will be done in c++ later!)
243 return ''.join(strlist)
245 text = property(getText)
247 class webifHandler(ContentHandler):
248 def __init__(self, session,request):
252 self.session = session
254 self.request = request
256 def startElement(self, name, attrs):
257 if name == "e2:screen":
258 self.screen = eval(attrs["name"])(self.session,self.request) # fixme
259 self.screens.append(self.screen)
262 if name[:3] == "e2:":
265 tag = [' %s="%s"' %(key,val) for (key, val) in attrs.items()]
269 tag = ''.join(tag)#.encode('utf-8')
273 elif self.mode == 1: # expect "<e2:element>"
274 assert name == "e2:element", "found %s instead of e2:element" % name
275 source = attrs["source"]
276 self.source_id = str(attrs.get("id", source))
277 self.source = self.screen[source]
278 self.is_streaming = "streaming" in attrs
279 elif self.mode == 2: # expect "<e2:convert>"
280 if name[:3] == "e2:":
281 assert name == "e2:convert"
283 ctype = attrs["type"]
285 # TODO: we need something better here
286 if ctype[:4] == "web:": # for now
287 self.converter = eval(ctype[4:])
290 self.converter = my_import('.'.join(["Components", "Converter", ctype])).__dict__.get(ctype)
292 self.converter = my_import('.'.join(["Plugins", "Extensions", "WebInterface", "WebComponents", "Converter", ctype])).__dict__.get(ctype)
297 assert name == "e2:item", "found %s instead of e2:item!" % name
298 assert "name" in attrs, "e2:item must have a name= attribute!"
299 filter = {"": 1, "javascript_escape": 2, "xml": 3, "uri": 4}[attrs.get("filter", "")]
300 self.sub.append(ListItem(attrs["name"], filter))
302 def endElement(self, name):
303 if name == "e2:screen":
307 tag = "</" + name + ">"
310 elif self.mode == 2 and name[:3] != "e2:":
312 elif self.mode == 2: # closed 'convert' -> sub
313 if len(self.sub) == 1:
314 self.sub = self.sub[0]
315 c = self.converter(self.sub)
316 c.connect(self.source)
319 elif self.mode == 1: # closed 'element'
320 # instatiate either a StreamingElement or a OneTimeElement, depending on what's required.
321 if not self.is_streaming:
322 c = OneTimeElement(self.source_id)
324 c = StreamingElement(self.source_id)
326 c.connect(self.source)
328 self.screen.renderer.append(c)
331 if name[:3] == "e2:":
334 def processingInstruction(self, target, data):
335 self.res.append('<?' + target + ' ' + data + '>')
337 def characters(self, ch):
338 ch = ch.encode('utf-8')
344 def startEntity(self, name):
345 self.res.append('&' + name + ';');
348 for screen in self.screens:
352 print "screen cleanup!"
353 for screen in self.screens:
358 def renderPage(stream, path, req, session):
360 # read in the template, create required screens
361 # we don't have persistense yet.
362 # if we had, this first part would only be done once.
363 handler = webifHandler(session,req)
364 parser = make_parser()
365 parser.setFeature(feature_namespaces, 0)
366 parser.setContentHandler(handler)
367 parser.parse(open(util.sibpath(__file__, path)))
369 # by default, we have non-streaming pages
372 # first, apply "commands" (aka. URL argument)
373 for x in handler.res:
374 if isinstance(x, Element):
375 x.handleCommand(req.args)
379 # now, we have a list with static texts mixed
380 # with non-static Elements.
381 # flatten this list, write into the stream.
382 for x in handler.res:
383 if isinstance(x, Element):
384 if isinstance(x, StreamingElement):
392 from twisted.internet import reactor
394 reactor.callLater(3, ping, s)
396 # if we met a "StreamingElement", there is at least one
397 # element which wants to output data more than once,
398 # i.e. on host-originated changes.
399 # in this case, don't finish yet, don't cleanup yet,
400 # but instead do that when the client disconnects.
406 # you *need* something which constantly sends something in a regular interval,
407 # in order to detect disconnected clients.
408 # i agree that this "ping" sucks terrible, so better be sure to have something
409 # similar. A "CurrentTime" is fine. Or anything that creates *some* output.
411 stream.closed_callback = lambda: handler.cleanup()