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
11 from enigma import eServiceReference
13 from Screens.Screen import Screen
14 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
60 class DummyWebScreen(WebScreen):
61 #use it, if you dont need any source, just to can do a static file with an xml-file
62 def __init__(self, session,request):
63 WebScreen.__init__(self, session,request)
65 class UpdateWebScreen(InfoBarServiceName, InfoBarEvent,InfoBarTuner,WebScreen):
66 def __init__(self, session,request):
67 WebScreen.__init__(self, session,request)
68 InfoBarServiceName.__init__(self)
69 InfoBarEvent.__init__(self)
70 InfoBarTuner.__init__(self)
71 self["CurrentTime"] = Clock()
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')
78 class MessageWebScreen(WebScreen):
79 def __init__(self, session,request):
80 WebScreen.__init__(self, session,request)
81 self["Message"] = Message(session)
83 class AudioWebScreen(WebScreen):
84 def __init__(self, session,request):
85 WebScreen.__init__(self, session,request)
86 self["AudioTracks"] = AudioTracks(session)
88 class AboutWebScreen(WebScreen):
89 def __init__(self, session,request):
90 WebScreen.__init__(self, session,request)
91 self["About"] = About(session)
93 class VolumeWebScreen(WebScreen):
94 def __init__(self, session,request):
95 WebScreen.__init__(self, session,request)
96 self["Volume"] = Volume(session)
98 class SettingsWebScreen(WebScreen):
99 def __init__(self, session,request):
100 WebScreen.__init__(self, session,request)
101 self["Settings"] = Settings(session)
103 class SubServiceWebScreen(WebScreen):
104 def __init__(self, session,request):
105 WebScreen.__init__(self, session,request)
106 self["SubServices"] = SubServices(session)
108 class ServiceWebScreen(WebScreen):
109 def __init__(self, session,request):
110 WebScreen.__init__(self, session,request)
111 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')
112 self["SwitchService"] = ServiceList(fav, command_func = self.zapTo, validate_commands=False)
113 self["ServiceList"] = ServiceList(fav, command_func = self.getServiceList, validate_commands=False)
114 self["ServiceListRecursive"] = ServiceListRecursive(session, func=ServiceListRecursive.FETCH)
116 def getServiceList(self, sRef):
117 self["ServiceList"].root = sRef
119 def zapTo(self, reftozap):
120 from Components.config import config
121 pc = config.ParentalControl.configured.value
123 config.ParentalControl.configured.value = False
124 self.session.nav.playService(reftozap)
126 config.ParentalControl.configured.value = pc
128 switching config.ParentalControl.configured.value
129 ugly, but necessary :(
132 class EPGWebScreen(WebScreen):
133 def __init__(self, session,request):
134 WebScreen.__init__(self, session,request)
135 self["EPGTITLE"] = EPG(session,func=EPG.TITLE)
136 self["EPGSERVICE"] = EPG(session,func=EPG.SERVICE)
137 self["EPGNOW"] = EPG(session,func=EPG.NOW)
139 class MovieWebScreen(WebScreen):
140 def __init__(self, session,request):
141 WebScreen.__init__(self, session,request)
142 from Components.MovieList import MovieList
143 from Tools.Directories import resolveFilename,SCOPE_HDD
144 movielist = MovieList(eServiceReference("2:0:1:0:0:0:0:0:0:0:" + resolveFilename(SCOPE_HDD)))
145 self["MovieList"] = Movie(session,movielist,func = Movie.LIST)
146 self["MovieFileDel"] = Movie(session,movielist,func = Movie.DEL)
147 self["MovieTags"] = Movie(session,movielist,func = Movie.TAGS)
150 class TimerWebScreen(WebScreen):
151 def __init__(self, session,request):
152 WebScreen.__init__(self, session,request)
153 self["TimerList"] = Timer(session,func = Timer.LIST)
154 self["TimerAddEventID"] = Timer(session,func = Timer.ADDBYID)
155 self["TimerAdd"] = Timer(session,func = Timer.ADD)
156 self["TimerDel"] = Timer(session,func = Timer.DEL)
157 self["TimerChange"] = Timer(session,func = Timer.CHANGE)
158 self["TimerListWrite"] = Timer(session,func = Timer.WRITE)
159 self["TVBrowser"] = Timer(session,func = Timer.TVBROWSER)
160 self["RecordNow"] = Timer(session,func = Timer.RECNOW)
162 class RemoteWebScreen(WebScreen):
163 def __init__(self, session,request):
164 WebScreen.__init__(self, session,request)
165 self["RemoteControl"] = RemoteControl(session)
167 class PowerWebScreen(WebScreen):
168 def __init__(self, session,request):
169 WebScreen.__init__(self, session,request)
170 self["PowerState"] = PowerState(session)
172 class ParentControlWebScreen(WebScreen):
173 def __init__(self, session,request):
174 WebScreen.__init__(self, session,request)
175 self["ParentControlList"] = ParentControl(session)
177 class WAPWebScreen(WebScreen):
178 def __init__(self, session,request):
179 WebScreen.__init__(self, session,request)
180 self["WAPFillOptionListSyear"] = WAPfunctions(session,func = WAPfunctions.LISTTIME)
181 self["WAPFillOptionListSday"] = WAPfunctions(session,func = WAPfunctions.LISTTIME)
182 self["WAPFillOptionListSmonth"] = WAPfunctions(session,func = WAPfunctions.LISTTIME)
183 self["WAPFillOptionListShour"] = WAPfunctions(session,func = WAPfunctions.LISTTIME)
184 self["WAPFillOptionListSmin"] = WAPfunctions(session,func = WAPfunctions.LISTTIME)
186 self["WAPFillOptionListEyear"] = WAPfunctions(session,func = WAPfunctions.LISTTIME)
187 self["WAPFillOptionListEday"] = WAPfunctions(session,func = WAPfunctions.LISTTIME)
188 self["WAPFillOptionListEmonth"] = WAPfunctions(session,func = WAPfunctions.LISTTIME)
189 self["WAPFillOptionListEhour"] = WAPfunctions(session,func = WAPfunctions.LISTTIME)
190 self["WAPFillOptionListEmin"] = WAPfunctions(session,func = WAPfunctions.LISTTIME)
192 self["WAPFillOptionListRecord"] = WAPfunctions(session,func = WAPfunctions.OPTIONLIST)
193 self["WAPFillOptionListAfterEvent"] = WAPfunctions(session,func = WAPfunctions.OPTIONLIST)
195 self["WAPFillValueName"] = WAPfunctions(session,func = WAPfunctions.FILLVALUE)
196 self["WAPFillValueDescr"] = WAPfunctions(session,func = WAPfunctions.FILLVALUE)
198 self["WAPFillOptionListRepeated"] = WAPfunctions(session,func = WAPfunctions.REPEATED)
199 self["WAPServiceList"] = WAPfunctions(session, func = WAPfunctions.SERVICELIST)
201 self["WAPdeleteOldOnSave"] = WAPfunctions(session,func = WAPfunctions.DELETEOLD)
203 class StreamingWebScreen(WebScreen):
204 def __init__(self, session,request):
205 WebScreen.__init__(self, session,request)
206 from Components.Sources.StreamService import StreamService
207 self["StreamService"] = StreamService(self.session.nav)
209 class M3UStreamingWebScreen(WebScreen):
210 def __init__(self, session,request):
211 WebScreen.__init__(self, session,request)
212 from Components.Sources.StaticText import StaticText
213 from Components.Sources.Config import Config
214 from Components.config import config
215 self["ref"] = StaticText()
216 self["localip"] = RequestData(request,what=RequestData.HOST)
218 class TsM3U(WebScreen):
219 def __init__(self, session,request):
220 WebScreen.__init__(self, session,request)
221 from Components.Sources.StaticText import StaticText
222 from Components.Sources.Config import Config
223 from Components.config import config
224 self["file"] = StaticText()
225 self["localip"] = RequestData(request,what=RequestData.HOST)
227 class RestartWebScreen(WebScreen):
228 def __init__(self, session,request):
229 WebScreen.__init__(self, session,request)
231 plugin.restartWebserver()
233 class GetPid(WebScreen):
234 def __init__(self, session,request):
235 WebScreen.__init__(self, session,request)
236 from Components.Sources.StaticText import StaticText
237 from enigma import iServiceInformation
238 pids = self.session.nav.getCurrentService()
240 pidinfo = pids.info()
241 VPID = hex(pidinfo.getInfo(iServiceInformation.sVideoPID))
242 APID = hex(pidinfo.getInfo(iServiceInformation.sAudioPID))
243 PPID = hex(pidinfo.getInfo(iServiceInformation.sPMTPID))
244 self["pids"] = StaticText("%s,%s,%s"%(PPID.lstrip("0x"),VPID.lstrip("0x"),APID.lstrip("0x")))
245 self["localip"] = RequestData(request,what=RequestData.HOST)
248 # implements the 'render'-call.
249 # this will act as a downstream_element, like a renderer.
250 class OneTimeElement(Element):
251 def __init__(self, id):
252 Element.__init__(self)
255 # CHECKME: is this ok performance-wise?
256 def handleCommand(self, args):
257 if self.source_id.find(",") >=0:
258 paramlist = self.source_id.split(",")
260 for key in paramlist:
261 arg = args.get(key, [])
265 list[key] = "".join(arg)
268 self.source.handleCommand(list)
270 for c in args.get(self.source_id, []):
271 self.source.handleCommand(c)
273 def render(self, stream):
274 t = self.source.getHTML(self.source_id)
292 class StreamingElement(OneTimeElement):
293 def __init__(self, id):
294 OneTimeElement.__init__(self, id)
297 def changed(self, what):
299 self.render(self.stream)
301 def setStream(self, stream):
304 # a to-be-filled list item
306 def __init__(self, name, filternum):
308 self.filternum = filternum
310 class TextToHTML(Converter):
311 def __init__(self, arg):
312 Converter.__init__(self, arg)
314 def getHTML(self, id):
315 return self.source.text # encode & etc. here!
317 class TextToURL(Converter):
318 def __init__(self, arg):
319 Converter.__init__(self, arg)
321 def getHTML(self, id):
322 return self.source.text.replace(" ","%20")
324 class ReturnEmptyXML(Converter):
325 def __init__(self, arg):
326 Converter.__init__(self, arg)
328 def getHTML(self, id):
329 return "<rootElement></rootElement>"
331 # a null-output. Useful if you only want to issue a command.
332 class Null(Converter):
333 def __init__(self, arg):
334 Converter.__init__(self, arg)
336 def getHTML(self, id):
339 class JavascriptUpdate(Converter):
340 def __init__(self, arg):
341 Converter.__init__(self, arg)
343 def getHTML(self, id):
344 # 3c5x9, added parent. , this is because the ie loads this in a iframe. an the set is in index.html.xml
345 # all other will replace this in JS
346 return '<script>parent.set("%s", "%s");</script>\n'%(id, self.source.text.replace("\\", "\\\\").replace("\n", "\\n").replace('"', '\\"').replace('\xb0', '°'))
348 # the performant 'listfiller'-engine (plfe)
349 class ListFiller(Converter):
350 def __init__(self, arg):
351 Converter.__init__(self, arg)
352 # print "ListFiller-arg: ",arg
356 lut = self.source.lut
357 conv_args = self.converter_arguments
359 # now build a ["string", 1, "string", 2]-styled list, with indices into the
360 # list to avoid lookup of item name for each entry
361 lutlist = [ isinstance(element, basestring) and (element, None) or (lut[element.name], element.filternum) for element in conv_args ]
363 # now, for the huge list, do:
365 append = strlist.append
367 for (element, filternum) in lutlist:
371 append(str(item[element]).replace("\\", "\\\\").replace("\n", "\\n").replace('"', '\\"'))
373 append(str(item[element]).replace("&", "&").replace("<", "<").replace('"', '"').replace(">", ">"))
375 append(str(item[element]).replace("%", "%25").replace("+", "%2B").replace('&', '%26').replace('?', '%3f').replace(' ', '+'))
377 append(str(item[element]))
378 # (this will be done in c++ later!)
379 return ''.join(strlist)
381 text = property(getText)
383 class webifHandler(ContentHandler):
384 def __init__(self, session,request):
388 self.session = session
390 self.request = request
392 def startElement(self, name, attrs):
393 if name == "e2:screen":
394 self.screen = eval(attrs["name"])(self.session,self.request) # fixme
395 self.screens.append(self.screen)
398 if name[:3] == "e2:":
401 tag = [' %s="%s"' %(key,val) for (key, val) in attrs.items()]
405 tag = ''.join(tag)#.encode('utf-8')
409 elif self.mode == 1: # expect "<e2:element>"
410 assert name == "e2:element", "found %s instead of e2:element" % name
411 source = attrs["source"]
412 self.source_id = str(attrs.get("id", source))
413 self.source = self.screen[source]
414 self.is_streaming = "streaming" in attrs
415 elif self.mode == 2: # expect "<e2:convert>"
416 if name[:3] == "e2:":
417 assert name == "e2:convert"
419 ctype = attrs["type"]
421 # TODO: we need something better here
422 if ctype[:4] == "web:": # for now
423 self.converter = eval(ctype[4:])
426 self.converter = my_import('.'.join(["Components", "Converter", ctype])).__dict__.get(ctype)
428 self.converter = my_import('.'.join(["Plugins", "Extensions", "WebInterface", "WebComponents", "Converter", ctype])).__dict__.get(ctype)
433 assert name == "e2:item", "found %s instead of e2:item!" % name
434 assert "name" in attrs, "e2:item must have a name= attribute!"
435 filter = {"": 1, "javascript_escape": 2, "xml": 3, "uri": 4}[attrs.get("filter", "")]
436 self.sub.append(ListItem(attrs["name"], filter))
438 def endElement(self, name):
439 if name == "e2:screen":
443 tag = "</" + name + ">"
446 elif self.mode == 2 and name[:3] != "e2:":
448 elif self.mode == 2: # closed 'convert' -> sub
449 if len(self.sub) == 1:
450 self.sub = self.sub[0]
451 c = self.converter(self.sub)
452 c.connect(self.source)
455 elif self.mode == 1: # closed 'element'
456 # instatiate either a StreamingElement or a OneTimeElement, depending on what's required.
457 if not self.is_streaming:
458 c = OneTimeElement(self.source_id)
460 c = StreamingElement(self.source_id)
462 c.connect(self.source)
464 self.screen.renderer.append(c)
467 if name[:3] == "e2:":
470 def processingInstruction(self, target, data):
471 self.res.append('<?' + target + ' ' + data + '>')
473 def characters(self, ch):
474 ch = ch.encode('utf-8')
480 def startEntity(self, name):
481 self.res.append('&' + name + ';');
484 for screen in self.screens:
488 print "screen cleanup!"
489 for screen in self.screens:
494 def renderPage(stream, path, req, session):
496 # read in the template, create required screens
497 # we don't have persistense yet.
498 # if we had, this first part would only be done once.
499 handler = webifHandler(session,req)
500 parser = make_parser()
501 parser.setFeature(feature_namespaces, 0)
502 parser.setContentHandler(handler)
503 parser.parse(open(util.sibpath(__file__, path)))
505 # by default, we have non-streaming pages
508 # first, apply "commands" (aka. URL argument)
509 for x in handler.res:
510 if isinstance(x, Element):
511 x.handleCommand(req.args)
515 # now, we have a list with static texts mixed
516 # with non-static Elements.
517 # flatten this list, write into the stream.
518 for x in handler.res:
519 if isinstance(x, Element):
520 if isinstance(x, StreamingElement):
528 from twisted.internet import reactor
530 reactor.callLater(3, ping, s)
532 # if we met a "StreamingElement", there is at least one
533 # element which wants to output data more than once,
534 # i.e. on host-originated changes.
535 # in this case, don't finish yet, don't cleanup yet,
536 # but instead do that when the client disconnects.
542 # you *need* something which constantly sends something in a regular interval,
543 # in order to detect disconnected clients.
544 # i agree that this "ping" sucks terrible, so better be sure to have something
545 # similar. A "CurrentTime" is fine. Or anything that creates *some* output.
547 stream.closed_callback = lambda: handler.cleanup()