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)
157 class TimerWebScreen(WebScreen):
158 def __init__(self, session,request):
159 WebScreen.__init__(self, session,request)
160 self["TimerList"] = Timer(session,func = Timer.LIST)
161 self["TimerAddEventID"] = Timer(session,func = Timer.ADDBYID)
162 self["TimerAdd"] = Timer(session,func = Timer.ADD)
163 self["TimerDel"] = Timer(session,func = Timer.DEL)
164 self["TimerChange"] = Timer(session,func = Timer.CHANGE)
165 self["TimerListWrite"] = Timer(session,func = Timer.WRITE)
166 self["TVBrowser"] = Timer(session,func = Timer.TVBROWSER)
167 self["RecordNow"] = Timer(session,func = Timer.RECNOW)
169 class RemoteWebScreen(WebScreen):
170 def __init__(self, session,request):
171 WebScreen.__init__(self, session,request)
172 self["RemoteControl"] = RemoteControl(session)
174 class PowerWebScreen(WebScreen):
175 def __init__(self, session,request):
176 WebScreen.__init__(self, session,request)
177 self["PowerState"] = PowerState(session)
179 class ParentControlWebScreen(WebScreen):
180 def __init__(self, session,request):
181 WebScreen.__init__(self, session,request)
182 self["ParentControlList"] = ParentControl(session)
184 class WAPWebScreen(WebScreen):
185 def __init__(self, session,request):
186 WebScreen.__init__(self, session,request)
187 self["WAPFillOptionListSyear"] = WAPfunctions(session,func = WAPfunctions.LISTTIME)
188 self["WAPFillOptionListSday"] = WAPfunctions(session,func = WAPfunctions.LISTTIME)
189 self["WAPFillOptionListSmonth"] = WAPfunctions(session,func = WAPfunctions.LISTTIME)
190 self["WAPFillOptionListShour"] = WAPfunctions(session,func = WAPfunctions.LISTTIME)
191 self["WAPFillOptionListSmin"] = WAPfunctions(session,func = WAPfunctions.LISTTIME)
193 self["WAPFillOptionListEyear"] = WAPfunctions(session,func = WAPfunctions.LISTTIME)
194 self["WAPFillOptionListEday"] = WAPfunctions(session,func = WAPfunctions.LISTTIME)
195 self["WAPFillOptionListEmonth"] = WAPfunctions(session,func = WAPfunctions.LISTTIME)
196 self["WAPFillOptionListEhour"] = WAPfunctions(session,func = WAPfunctions.LISTTIME)
197 self["WAPFillOptionListEmin"] = WAPfunctions(session,func = WAPfunctions.LISTTIME)
199 self["WAPFillOptionListRecord"] = WAPfunctions(session,func = WAPfunctions.OPTIONLIST)
200 self["WAPFillOptionListAfterEvent"] = WAPfunctions(session,func = WAPfunctions.OPTIONLIST)
202 self["WAPFillValueName"] = WAPfunctions(session,func = WAPfunctions.FILLVALUE)
203 self["WAPFillValueDescr"] = WAPfunctions(session,func = WAPfunctions.FILLVALUE)
205 self["WAPFillOptionListRepeated"] = WAPfunctions(session,func = WAPfunctions.REPEATED)
206 self["WAPServiceList"] = WAPfunctions(session, func = WAPfunctions.SERVICELIST)
208 self["WAPdeleteOldOnSave"] = WAPfunctions(session,func = WAPfunctions.DELETEOLD)
210 class StreamingWebScreen(WebScreen):
211 def __init__(self, session,request):
212 WebScreen.__init__(self, session,request)
213 from Components.Sources.StreamService import StreamService
214 self["StreamService"] = StreamService(self.session.nav)
216 class M3UStreamingWebScreen(WebScreen):
217 def __init__(self, session,request):
218 WebScreen.__init__(self, session,request)
219 from Components.Sources.StaticText import StaticText
220 from Components.Sources.Config import Config
221 from Components.config import config
222 self["ref"] = StaticText()
223 self["localip"] = RequestData(request,what=RequestData.HOST)
225 class TsM3U(WebScreen):
226 def __init__(self, session,request):
227 WebScreen.__init__(self, session,request)
228 from Components.Sources.StaticText import StaticText
229 from Components.Sources.Config import Config
230 from Components.config import config
231 self["file"] = StaticText()
232 self["localip"] = RequestData(request,what=RequestData.HOST)
234 class RestartWebScreen(WebScreen):
235 def __init__(self, session,request):
236 WebScreen.__init__(self, session,request)
238 plugin.restartWebserver()
240 class GetPid(WebScreen):
241 def __init__(self, session,request):
242 WebScreen.__init__(self, session,request)
243 from Components.Sources.StaticText import StaticText
244 from enigma import iServiceInformation
245 pids = self.session.nav.getCurrentService()
247 pidinfo = pids.info()
248 VPID = hex(pidinfo.getInfo(iServiceInformation.sVideoPID))
249 APID = hex(pidinfo.getInfo(iServiceInformation.sAudioPID))
250 PPID = hex(pidinfo.getInfo(iServiceInformation.sPMTPID))
251 self["pids"] = StaticText("%s,%s,%s"%(PPID.lstrip("0x"),VPID.lstrip("0x"),APID.lstrip("0x")))
252 self["localip"] = RequestData(request,what=RequestData.HOST)
255 # implements the 'render'-call.
256 # this will act as a downstream_element, like a renderer.
257 class OneTimeElement(Element):
258 def __init__(self, id):
259 Element.__init__(self)
262 # CHECKME: is this ok performance-wise?
263 def handleCommand(self, args):
264 if self.source_id.find(",") >=0:
265 paramlist = self.source_id.split(",")
267 for key in paramlist:
268 arg = args.get(key, [])
272 list[key] = "".join(arg)
275 self.source.handleCommand(list)
277 for c in args.get(self.source_id, []):
278 self.source.handleCommand(c)
280 def render(self, stream):
281 t = self.source.getHTML(self.source_id)
299 class StreamingElement(OneTimeElement):
300 def __init__(self, id):
301 OneTimeElement.__init__(self, id)
304 def changed(self, what):
306 self.render(self.stream)
308 def setStream(self, stream):
311 # a to-be-filled list item
313 def __init__(self, name, filternum):
315 self.filternum = filternum
317 class TextToHTML(Converter):
318 def __init__(self, arg):
319 Converter.__init__(self, arg)
321 def getHTML(self, id):
322 return self.source.text # encode & etc. here!
324 class TextToURL(Converter):
325 def __init__(self, arg):
326 Converter.__init__(self, arg)
328 def getHTML(self, id):
329 return self.source.text.replace(" ","%20")
331 class ReturnEmptyXML(Converter):
332 def __init__(self, arg):
333 Converter.__init__(self, arg)
335 def getHTML(self, id):
336 return "<rootElement></rootElement>"
338 # a null-output. Useful if you only want to issue a command.
339 class Null(Converter):
340 def __init__(self, arg):
341 Converter.__init__(self, arg)
343 def getHTML(self, id):
346 class JavascriptUpdate(Converter):
347 def __init__(self, arg):
348 Converter.__init__(self, arg)
350 def getHTML(self, id):
351 # 3c5x9, added parent. , this is because the ie loads this in a iframe. an the set is in index.html.xml
352 # all other will replace this in JS
353 return '<script>parent.set("%s", "%s");</script>\n'%(id, self.source.text.replace("\\", "\\\\").replace("\n", "\\n").replace('"', '\\"').replace('\xb0', '°'))
355 # the performant 'listfiller'-engine (plfe)
356 class ListFiller(Converter):
357 def __init__(self, arg):
358 Converter.__init__(self, arg)
359 # print "ListFiller-arg: ",arg
363 lut = self.source.lut
364 conv_args = self.converter_arguments
366 # now build a ["string", 1, "string", 2]-styled list, with indices into the
367 # list to avoid lookup of item name for each entry
368 lutlist = [ isinstance(element, basestring) and (element, None) or (lut[element.name], element.filternum) for element in conv_args ]
370 # now, for the huge list, do:
372 append = strlist.append
374 for (element, filternum) in lutlist:
378 append(str(item[element]).replace("\\", "\\\\").replace("\n", "\\n").replace('"', '\\"'))
380 append(str(item[element]).replace("&", "&").replace("<", "<").replace('"', '"').replace(">", ">"))
382 append(str(item[element]).replace("%", "%25").replace("+", "%2B").replace('&', '%26').replace('?', '%3f').replace(' ', '+'))
384 append(str(item[element]))
385 # (this will be done in c++ later!)
386 return ''.join(strlist)
388 text = property(getText)
390 class webifHandler(ContentHandler):
391 def __init__(self, session,request):
395 self.session = session
397 self.request = request
399 def startElement(self, name, attrs):
400 if name == "e2:screen":
401 self.screen = eval(attrs["name"])(self.session,self.request) # fixme
402 self.screens.append(self.screen)
405 if name[:3] == "e2:":
408 tag = [' %s="%s"' %(key,val) for (key, val) in attrs.items()]
412 tag = ''.join(tag)#.encode('utf-8')
416 elif self.mode == 1: # expect "<e2:element>"
417 assert name == "e2:element", "found %s instead of e2:element" % name
418 source = attrs["source"]
419 self.source_id = str(attrs.get("id", source))
420 self.source = self.screen[source]
421 self.is_streaming = "streaming" in attrs
422 elif self.mode == 2: # expect "<e2:convert>"
423 if name[:3] == "e2:":
424 assert name == "e2:convert"
426 ctype = attrs["type"]
428 # TODO: we need something better here
429 if ctype[:4] == "web:": # for now
430 self.converter = eval(ctype[4:])
433 self.converter = my_import('.'.join(["Components", "Converter", ctype])).__dict__.get(ctype)
435 self.converter = my_import('.'.join(["Plugins", "Extensions", "WebInterface", "WebComponents", "Converter", ctype])).__dict__.get(ctype)
440 assert name == "e2:item", "found %s instead of e2:item!" % name
441 assert "name" in attrs, "e2:item must have a name= attribute!"
442 filter = {"": 1, "javascript_escape": 2, "xml": 3, "uri": 4}[attrs.get("filter", "")]
443 self.sub.append(ListItem(attrs["name"], filter))
445 def endElement(self, name):
446 if name == "e2:screen":
450 tag = "</" + name + ">"
453 elif self.mode == 2 and name[:3] != "e2:":
455 elif self.mode == 2: # closed 'convert' -> sub
456 if len(self.sub) == 1:
457 self.sub = self.sub[0]
458 c = self.converter(self.sub)
459 c.connect(self.source)
462 elif self.mode == 1: # closed 'element'
463 # instatiate either a StreamingElement or a OneTimeElement, depending on what's required.
464 if not self.is_streaming:
465 c = OneTimeElement(self.source_id)
467 c = StreamingElement(self.source_id)
469 c.connect(self.source)
471 self.screen.renderer.append(c)
474 if name[:3] == "e2:":
477 def processingInstruction(self, target, data):
478 self.res.append('<?' + target + ' ' + data + '>')
480 def characters(self, ch):
481 ch = ch.encode('utf-8')
487 def startEntity(self, name):
488 self.res.append('&' + name + ';');
491 for screen in self.screens:
495 print "screen cleanup!"
496 for screen in self.screens:
501 def renderPage(stream, path, req, session):
503 # read in the template, create required screens
504 # we don't have persistense yet.
505 # if we had, this first part would only be done once.
506 handler = webifHandler(session,req)
507 parser = make_parser()
508 parser.setFeature(feature_namespaces, 0)
509 parser.setContentHandler(handler)
510 parser.parse(open(util.sibpath(__file__, path)))
512 # by default, we have non-streaming pages
515 # first, apply "commands" (aka. URL argument)
516 for x in handler.res:
517 if isinstance(x, Element):
518 x.handleCommand(req.args)
522 # now, we have a list with static texts mixed
523 # with non-static Elements.
524 # flatten this list, write into the stream.
525 for x in handler.res:
526 if isinstance(x, Element):
527 if isinstance(x, StreamingElement):
535 from twisted.internet import reactor
537 reactor.callLater(3, ping, s)
539 # if we met a "StreamingElement", there is at least one
540 # element which wants to output data more than once,
541 # i.e. on host-originated changes.
542 # in this case, don't finish yet, don't cleanup yet,
543 # but instead do that when the client disconnects.
549 # you *need* something which constantly sends something in a regular interval,
550 # in order to detect disconnected clients.
551 # i agree that this "ping" sucks terrible, so better be sure to have something
552 # similar. A "CurrentTime" is fine. Or anything that creates *some* output.
554 stream.closed_callback = lambda: handler.cleanup()