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
36 from WebComponents.Sources.MP import MP
38 from Components.Sources.FrontendStatus import FrontendStatus
40 from Components.Converter.Converter import Converter
42 from Components.Element import Element
44 from xml.sax import make_parser
45 from xml.sax.handler import ContentHandler, feature_namespaces
47 from twisted.python import util
52 # prototype of the new web frontend template system.
54 class WebScreen(Screen):
55 def __init__(self, session, request):
56 Screen.__init__(self, session)
57 self.stand_alone = True
58 self.request = request
61 class DummyWebScreen(WebScreen):
62 #use it, if you dont need any source, just to can do a static file with an xml-file
63 def __init__(self, session,request):
64 WebScreen.__init__(self, session,request)
66 class UpdateWebScreen(InfoBarServiceName, InfoBarEvent,InfoBarTuner,WebScreen):
67 def __init__(self, session,request):
68 WebScreen.__init__(self, session,request)
69 InfoBarServiceName.__init__(self)
70 InfoBarEvent.__init__(self)
71 InfoBarTuner.__init__(self)
72 self["CurrentTime"] = Clock()
73 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')
79 class MessageWebScreen(WebScreen):
80 def __init__(self, session,request):
81 WebScreen.__init__(self, session,request)
82 self["Message"] = Message(session)
84 class AudioWebScreen(WebScreen):
85 def __init__(self, session,request):
86 WebScreen.__init__(self, session,request)
87 self["AudioTracks"] = AudioTracks(session)
89 class AboutWebScreen(WebScreen):
90 def __init__(self, session,request):
91 WebScreen.__init__(self, session,request)
92 self["About"] = About(session)
94 class VolumeWebScreen(WebScreen):
95 def __init__(self, session,request):
96 WebScreen.__init__(self, session,request)
97 self["Volume"] = Volume(session)
99 class SettingsWebScreen(WebScreen):
100 def __init__(self, session,request):
101 WebScreen.__init__(self, session,request)
102 self["Settings"] = Settings(session)
104 class SubServiceWebScreen(WebScreen):
105 def __init__(self, session,request):
106 WebScreen.__init__(self, session,request)
107 self["SubServices"] = SubServices(session)
109 class ServiceWebScreen(WebScreen):
110 def __init__(self, session,request):
111 WebScreen.__init__(self, session,request)
112 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')
113 self["SwitchService"] = ServiceList(fav, command_func = self.zapTo, validate_commands=False)
114 self["ServiceList"] = ServiceList(fav, command_func = self.getServiceList, validate_commands=False)
115 self["ServiceListRecursive"] = ServiceListRecursive(session, func=ServiceListRecursive.FETCH)
117 def getServiceList(self, sRef):
118 self["ServiceList"].root = sRef
120 def zapTo(self, reftozap):
121 from Components.config import config
122 pc = config.ParentalControl.configured.value
124 config.ParentalControl.configured.value = False
125 self.session.nav.playService(reftozap)
127 config.ParentalControl.configured.value = pc
129 switching config.ParentalControl.configured.value
130 ugly, but necessary :(
133 class EPGWebScreen(WebScreen):
134 def __init__(self, session,request):
135 WebScreen.__init__(self, session,request)
136 self["EPGTITLE"] = EPG(session,func=EPG.TITLE)
137 self["EPGSERVICE"] = EPG(session,func=EPG.SERVICE)
138 self["EPGNOW"] = EPG(session,func=EPG.NOW)
140 class MovieWebScreen(WebScreen):
141 def __init__(self, session,request):
142 WebScreen.__init__(self, session,request)
143 from Components.MovieList import MovieList
144 from Tools.Directories import resolveFilename,SCOPE_HDD
145 movielist = MovieList(eServiceReference("2:0:1:0:0:0:0:0:0:0:" + resolveFilename(SCOPE_HDD)))
146 self["MovieList"] = Movie(session,movielist,func = Movie.LIST)
147 self["MovieFileDel"] = Movie(session,movielist,func = Movie.DEL)
148 self["MovieTags"] = Movie(session,movielist,func = Movie.TAGS)
150 class MediaPlayerWebScreen(WebScreen):
151 def __init__(self, session,request):
152 WebScreen.__init__(self, session,request)
153 self["FileList"] = MP(session,func = MP.LIST)
154 self["PlayFile"] = MP(session,func = MP.PLAY)
155 self["Command"] = MP(session,func = MP.COMMAND)
159 class TimerWebScreen(WebScreen):
160 def __init__(self, session,request):
161 WebScreen.__init__(self, session,request)
162 self["TimerList"] = Timer(session,func = Timer.LIST)
163 self["TimerAddEventID"] = Timer(session,func = Timer.ADDBYID)
164 self["TimerAdd"] = Timer(session,func = Timer.ADD)
165 self["TimerDel"] = Timer(session,func = Timer.DEL)
166 self["TimerChange"] = Timer(session,func = Timer.CHANGE)
167 self["TimerListWrite"] = Timer(session,func = Timer.WRITE)
168 self["TVBrowser"] = Timer(session,func = Timer.TVBROWSER)
169 self["RecordNow"] = Timer(session,func = Timer.RECNOW)
171 class RemoteWebScreen(WebScreen):
172 def __init__(self, session,request):
173 WebScreen.__init__(self, session,request)
174 self["RemoteControl"] = RemoteControl(session)
176 class PowerWebScreen(WebScreen):
177 def __init__(self, session,request):
178 WebScreen.__init__(self, session,request)
179 self["PowerState"] = PowerState(session)
181 class ParentControlWebScreen(WebScreen):
182 def __init__(self, session,request):
183 WebScreen.__init__(self, session,request)
184 self["ParentControlList"] = ParentControl(session)
186 class WAPWebScreen(WebScreen):
187 def __init__(self, session,request):
188 WebScreen.__init__(self, session,request)
189 self["WAPFillOptionListSyear"] = WAPfunctions(session,func = WAPfunctions.LISTTIME)
190 self["WAPFillOptionListSday"] = WAPfunctions(session,func = WAPfunctions.LISTTIME)
191 self["WAPFillOptionListSmonth"] = WAPfunctions(session,func = WAPfunctions.LISTTIME)
192 self["WAPFillOptionListShour"] = WAPfunctions(session,func = WAPfunctions.LISTTIME)
193 self["WAPFillOptionListSmin"] = WAPfunctions(session,func = WAPfunctions.LISTTIME)
195 self["WAPFillOptionListEyear"] = WAPfunctions(session,func = WAPfunctions.LISTTIME)
196 self["WAPFillOptionListEday"] = WAPfunctions(session,func = WAPfunctions.LISTTIME)
197 self["WAPFillOptionListEmonth"] = WAPfunctions(session,func = WAPfunctions.LISTTIME)
198 self["WAPFillOptionListEhour"] = WAPfunctions(session,func = WAPfunctions.LISTTIME)
199 self["WAPFillOptionListEmin"] = WAPfunctions(session,func = WAPfunctions.LISTTIME)
201 self["WAPFillOptionListRecord"] = WAPfunctions(session,func = WAPfunctions.OPTIONLIST)
202 self["WAPFillOptionListAfterEvent"] = WAPfunctions(session,func = WAPfunctions.OPTIONLIST)
204 self["WAPFillValueName"] = WAPfunctions(session,func = WAPfunctions.FILLVALUE)
205 self["WAPFillValueDescr"] = WAPfunctions(session,func = WAPfunctions.FILLVALUE)
207 self["WAPFillOptionListRepeated"] = WAPfunctions(session,func = WAPfunctions.REPEATED)
208 self["WAPServiceList"] = WAPfunctions(session, func = WAPfunctions.SERVICELIST)
210 self["WAPdeleteOldOnSave"] = WAPfunctions(session,func = WAPfunctions.DELETEOLD)
212 class StreamingWebScreen(WebScreen):
213 def __init__(self, session,request):
214 WebScreen.__init__(self, session,request)
215 from Components.Sources.StreamService import StreamService
216 self["StreamService"] = StreamService(self.session.nav)
218 class M3UStreamingWebScreen(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["ref"] = StaticText()
225 self["localip"] = RequestData(request,what=RequestData.HOST)
227 class TsM3U(WebScreen):
228 def __init__(self, session,request):
229 WebScreen.__init__(self, session,request)
230 from Components.Sources.StaticText import StaticText
231 from Components.Sources.Config import Config
232 from Components.config import config
233 self["file"] = StaticText()
234 self["localip"] = RequestData(request,what=RequestData.HOST)
236 class RestartWebScreen(WebScreen):
237 def __init__(self, session,request):
238 WebScreen.__init__(self, session,request)
240 plugin.restartWebserver()
242 class GetPid(WebScreen):
243 def __init__(self, session,request):
244 WebScreen.__init__(self, session,request)
245 from Components.Sources.StaticText import StaticText
246 from enigma import iServiceInformation
247 pids = self.session.nav.getCurrentService()
249 pidinfo = pids.info()
250 VPID = hex(pidinfo.getInfo(iServiceInformation.sVideoPID))
251 APID = hex(pidinfo.getInfo(iServiceInformation.sAudioPID))
252 PPID = hex(pidinfo.getInfo(iServiceInformation.sPMTPID))
253 self["pids"] = StaticText("%s,%s,%s"%(PPID.lstrip("0x"),VPID.lstrip("0x"),APID.lstrip("0x")))
254 self["localip"] = RequestData(request,what=RequestData.HOST)
257 # implements the 'render'-call.
258 # this will act as a downstream_element, like a renderer.
259 class OneTimeElement(Element):
260 def __init__(self, id):
261 Element.__init__(self)
264 # CHECKME: is this ok performance-wise?
265 def handleCommand(self, args):
266 if self.source_id.find(",") >=0:
267 paramlist = self.source_id.split(",")
269 for key in paramlist:
270 arg = args.get(key, [])
274 list[key] = "".join(arg)
277 self.source.handleCommand(list)
279 for c in args.get(self.source_id, []):
280 self.source.handleCommand(c)
282 def render(self, stream):
283 t = self.source.getHTML(self.source_id)
301 class StreamingElement(OneTimeElement):
302 def __init__(self, id):
303 OneTimeElement.__init__(self, id)
306 def changed(self, what):
308 self.render(self.stream)
310 def setStream(self, stream):
313 # a to-be-filled list item
315 def __init__(self, name, filternum):
317 self.filternum = filternum
319 class TextToHTML(Converter):
320 def __init__(self, arg):
321 Converter.__init__(self, arg)
323 def getHTML(self, id):
324 return self.source.text # encode & etc. here!
326 class TextToURL(Converter):
327 def __init__(self, arg):
328 Converter.__init__(self, arg)
330 def getHTML(self, id):
331 return self.source.text.replace(" ","%20")
333 class ReturnEmptyXML(Converter):
334 def __init__(self, arg):
335 Converter.__init__(self, arg)
337 def getHTML(self, id):
338 return "<rootElement></rootElement>"
340 # a null-output. Useful if you only want to issue a command.
341 class Null(Converter):
342 def __init__(self, arg):
343 Converter.__init__(self, arg)
345 def getHTML(self, id):
348 class JavascriptUpdate(Converter):
349 def __init__(self, arg):
350 Converter.__init__(self, arg)
352 def getHTML(self, id):
353 # 3c5x9, added parent. , this is because the ie loads this in a iframe. an the set is in index.html.xml
354 # all other will replace this in JS
355 return '<script>parent.set("%s", "%s");</script>\n'%(id, self.source.text.replace("\\", "\\\\").replace("\n", "\\n").replace('"', '\\"').replace('\xb0', '°'))
357 # the performant 'listfiller'-engine (plfe)
358 class ListFiller(Converter):
359 def __init__(self, arg):
360 Converter.__init__(self, arg)
361 # print "ListFiller-arg: ",arg
365 lut = self.source.lut
366 conv_args = self.converter_arguments
368 # now build a ["string", 1, "string", 2]-styled list, with indices into the
369 # list to avoid lookup of item name for each entry
370 lutlist = [ isinstance(element, basestring) and (element, None) or (lut[element.name], element.filternum) for element in conv_args ]
372 # now, for the huge list, do:
374 append = strlist.append
376 for (element, filternum) in lutlist:
380 append(str(item[element]).replace("\\", "\\\\").replace("\n", "\\n").replace('"', '\\"'))
382 append(str(item[element]).replace("&", "&").replace("<", "<").replace('"', '"').replace(">", ">"))
384 append(str(item[element]).replace("%", "%25").replace("+", "%2B").replace('&', '%26').replace('?', '%3f').replace(' ', '+'))
386 append(str(item[element]))
387 # (this will be done in c++ later!)
388 return ''.join(strlist)
390 text = property(getText)
392 class webifHandler(ContentHandler):
393 def __init__(self, session,request):
397 self.session = session
399 self.request = request
401 def startElement(self, name, attrs):
402 if name == "e2:screen":
403 self.screen = eval(attrs["name"])(self.session,self.request) # fixme
404 self.screens.append(self.screen)
407 if name[:3] == "e2:":
410 tag = [' %s="%s"' %(key,val) for (key, val) in attrs.items()]
414 tag = ''.join(tag)#.encode('utf-8')
418 elif self.mode == 1: # expect "<e2:element>"
419 assert name == "e2:element", "found %s instead of e2:element" % name
420 source = attrs["source"]
421 self.source_id = str(attrs.get("id", source))
422 self.source = self.screen[source]
423 self.is_streaming = "streaming" in attrs
424 elif self.mode == 2: # expect "<e2:convert>"
425 if name[:3] == "e2:":
426 assert name == "e2:convert"
428 ctype = attrs["type"]
430 # TODO: we need something better here
431 if ctype[:4] == "web:": # for now
432 self.converter = eval(ctype[4:])
435 self.converter = my_import('.'.join(["Components", "Converter", ctype])).__dict__.get(ctype)
437 self.converter = my_import('.'.join(["Plugins", "Extensions", "WebInterface", "WebComponents", "Converter", ctype])).__dict__.get(ctype)
442 assert name == "e2:item", "found %s instead of e2:item!" % name
443 assert "name" in attrs, "e2:item must have a name= attribute!"
444 filter = {"": 1, "javascript_escape": 2, "xml": 3, "uri": 4}[attrs.get("filter", "")]
445 self.sub.append(ListItem(attrs["name"], filter))
447 def endElement(self, name):
448 if name == "e2:screen":
452 tag = "</" + name + ">"
455 elif self.mode == 2 and name[:3] != "e2:":
457 elif self.mode == 2: # closed 'convert' -> sub
458 if len(self.sub) == 1:
459 self.sub = self.sub[0]
460 c = self.converter(self.sub)
461 c.connect(self.source)
464 elif self.mode == 1: # closed 'element'
465 # instatiate either a StreamingElement or a OneTimeElement, depending on what's required.
466 if not self.is_streaming:
467 c = OneTimeElement(self.source_id)
469 c = StreamingElement(self.source_id)
471 c.connect(self.source)
473 self.screen.renderer.append(c)
476 if name[:3] == "e2:":
479 def processingInstruction(self, target, data):
480 self.res.append('<?' + target + ' ' + data + '>')
482 def characters(self, ch):
483 ch = ch.encode('utf-8')
489 def startEntity(self, name):
490 self.res.append('&' + name + ';');
493 for screen in self.screens:
497 print "screen cleanup!"
498 for screen in self.screens:
503 def renderPage(stream, path, req, session):
505 # read in the template, create required screens
506 # we don't have persistense yet.
507 # if we had, this first part would only be done once.
508 handler = webifHandler(session,req)
509 parser = make_parser()
510 parser.setFeature(feature_namespaces, 0)
511 parser.setContentHandler(handler)
512 parser.parse(open(util.sibpath(__file__, path)))
514 # by default, we have non-streaming pages
517 # first, apply "commands" (aka. URL argument)
518 for x in handler.res:
519 if isinstance(x, Element):
520 x.handleCommand(req.args)
524 # now, we have a list with static texts mixed
525 # with non-static Elements.
526 # flatten this list, write into the stream.
527 for x in handler.res:
528 if isinstance(x, Element):
529 if isinstance(x, StreamingElement):
537 from twisted.internet import reactor
539 reactor.callLater(3, ping, s)
541 # if we met a "StreamingElement", there is at least one
542 # element which wants to output data more than once,
543 # i.e. on host-originated changes.
544 # in this case, don't finish yet, don't cleanup yet,
545 # but instead do that when the client disconnects.
551 # you *need* something which constantly sends something in a regular interval,
552 # in order to detect disconnected clients.
553 # i agree that this "ping" sucks terrible, so better be sure to have something
554 # similar. A "CurrentTime" is fine. Or anything that creates *some* output.
556 stream.closed_callback = lambda: handler.cleanup()