do not rely on Tools.XMLTools.elementsWithTag
[enigma2-plugins.git] / webcamviewer / src / plugin.py
1 from enigma import eListbox
2 from enigma import eListboxPythonMultiContent
3 from enigma import ePicLoad
4 from enigma import loadPNG
5 from enigma import gFont
6 ### Picturelist
7 from Screens.Screen import Screen
8 from Screens.MessageBox import MessageBox
9 from Screens.InputBox import InputBox
10 from Screens.ChoiceBox import ChoiceBox
11 from Components.ActionMap import ActionMap
12 from Components.Label import Label
13 from Components.MenuList import MenuList
14 from Components.FileList import EXTENSIONS
15 ## configmenu
16 from Components.config import config, ConfigSubsection,ConfigSelection,ConfigText,ConfigYesNo
17 ####
18 from Components.Input import Input
19 from Components.Pixmap import Pixmap
20 from Plugins.Plugin import PluginDescriptor
21 ### System
22 import os
23 from re import compile
24 ## XML
25 from pyexpat import ExpatError
26 import xml.dom.minidom
27
28 ### my
29 from WebcamViewConfig import WebcamViewerMenu
30 from PictureScreen import PictureScreen
31 ###
32 myname = "Webcam/Picture Viewer"
33 myversion = "1.1"
34
35 config.plugins.pictureviewer = ConfigSubsection()
36 config.plugins.pictureviewer.slideshowtime = ConfigSelection(default="5000", choices = [("5000", _("5 Sekunden")), ("10000", _("10 Sekunden")), ("20000", _("20 Sekunden")), ("60000", _("1 Minute"))])
37 config.plugins.pictureviewer.slideshowmode = ConfigSelection(default="0", choices = [("0", _("normal")), ("1", _("endlos"))])
38 #not editable configs
39 config.plugins.pictureviewer.slideshowext = ConfigText(default=".3ssl")
40 config.plugins.pictureviewer.matchingPattern = ConfigText(default="(?i)^.*\.(jpeg|jpg|jpe|png|bmp|gif)")
41 config.plugins.pictureviewer.slideshowdir = ConfigText(default="/media/hdd/slideshows/")
42 config.plugins.pictureviewer.rootdir = ConfigText(default="/media/")
43 config.plugins.pictureviewer.stopserviceonstart = ConfigYesNo(default = False)
44 SLIDESHOWMODE_NORMAL = 0
45 SLIDESHOWMODE_REPEAT = 1
46
47 originalservice = None
48 mysession = None
49
50 def main1(session, **kwargs):
51         global originalservice, mysession
52         mysession = session
53         originalservice = session.nav.getCurrentlyPlayingServiceReference()
54         if config.plugins.pictureviewer.stopserviceonstart.value:
55                 session.nav.stopService()
56         session.openWithCallback(mainCB, PictureViewer)
57
58 def main2(session, **kwargs):
59         global originalservice, mysession
60         mysession = session
61         originalservice = session.nav.getCurrentlyPlayingServiceReference()
62         if config.plugins.pictureviewer.stopserviceonstart.value:
63                 session.nav.stopService()
64         xmlfile = "/usr/lib/enigma2/python/Plugins/Extensions/WebcamViewer/webcam.xml"
65         if os.path.isfile(xmlfile):
66                 try:
67                         xmlnode = xml.dom.minidom.parse(open(xmlfile))
68                         session.openWithCallback(mainCB, WebcamViewer, xmlnode.childNodes[1])
69                 except ExpatError,e:
70                         session.open(
71                                 MessageBox,
72                                 _("Loading config file failed!\n\n%s") % e,
73                                 MessageBox.TYPE_WARNING
74                         )
75         else:
76                 session.open(
77                         MessageBox,
78                         _("Loading config file failed!\n\nconfigfile not found!"),
79                         MessageBox.TYPE_WARNING
80                 )
81
82 def mainCB():
83         global originalservice, mysession
84         if config.plugins.pictureviewer.stopserviceonstart.value:
85                 mysession.nav.playService(originalservice)
86
87 def Plugins(path, **kwargs):
88         p = [
89                         PluginDescriptor(
90                                                         name="PictureViewer",
91                                                         description="browse your local pictures",
92                                                         where = PluginDescriptor.WHERE_PLUGINMENU,
93                                                         fnc = main1,
94                                                         icon="pictureviewer.png"
95                           ),
96                         PluginDescriptor(
97                                                         name="WebcamViewer",
98                                                         description="view webcams around the world",
99                                                         where = PluginDescriptor.WHERE_PLUGINMENU,
100                                                         fnc = main2,
101                                                         icon="webcamviewer.png"
102                         )
103                  ]
104         return p
105
106 ###################
107 class Slideshow:
108         filelist = []
109         currentslideshowitem = 0
110         wbviewer = False
111
112         def __init__(self, session, callback):
113                 self.session = session
114                 self.callback = callback
115
116         def setfiles(self, filelist):
117                 self.filelist = filelist
118
119         def start(self):
120                 if len(self.filelist) > 0:
121                         self.currentslideshowitem = -1
122                         self.nextSlideshowItem()
123
124         def nextSlideshowItem(self, prev = False):
125                 currentslideshowitem = self.currentslideshowitem
126                 if prev:
127                         currentslideshowitem -= 2
128                 if currentslideshowitem < 0:
129                         currentslideshowitem = -1
130                 if currentslideshowitem is not (len(self.filelist) - 1):
131                         currentslideshowitem += 1
132                         filetoshow = self.filelist[currentslideshowitem][1]
133                         if not self.wbviewer:
134                                 self.wbviewer = self.session.openWithCallback(
135                                                                         self.cb,
136                                                                         PictureScreen,
137                                                                         filetoshow.split("/")[-1],
138                                                                         filetoshow,
139                                                                         slideshowcallback = self.nextSlideshowItem
140                                 )
141                         else:
142                                 self.wbviewer.filename = filetoshow
143                                 self.wbviewer.do()
144                         self.currentslideshowitem = currentslideshowitem
145                 elif int(config.plugins.pictureviewer.slideshowmode.value) is SLIDESHOWMODE_REPEAT:
146                         print "["+myname+"] restarting slideshow"
147                         self.start()
148                 else:
149                         print "["+myname+"] slideshow finished"
150                         self.wbviewer.exit()
151                         self.cb()
152
153         def cb(self):
154                 self.callback()
155 ###################
156 class PictureViewer(Screen):
157         skin = ""
158         filelist = []
159         currList = "slideshowlist"
160         wbviewer = False
161         loadedslideshowlistlistname = False
162
163         def __init__(self, session, args = 0):
164                 skin =  """<screen position="93,70" size="550,450" title="%s">
165                 <widget name="menu" position="1,1" size="275,400"  scrollbarMode="showOnDemand" />
166                 <widget name="pixmap" position="550,450" size="275,200" backgroundColor="red" />
167                 <widget name="slist" position="275,200" size="275,200"  scrollbarMode="showOnDemand" />
168                 <widget name="buttonred" position="6,405" size="130,40" backgroundColor="red" valign="center" halign="center" zPosition="2" foregroundColor="white" font="Regular;18" />
169                 <widget name="buttongreen" position="142,405" size="130,40" backgroundColor="green" valign="center" halign="center" zPosition="2" foregroundColor="white" font="Regular;18" />
170                 <widget name="buttonyellow" position="278,405" size="130,40" backgroundColor="yellow" valign="center" halign="center" zPosition="2" foregroundColor="white" font="Regular;18" />
171                 <widget name="buttonblue" position="414,405" size="130,40" backgroundColor="blue" valign="center" halign="center" zPosition="2" foregroundColor="white" font="Regular;18" />
172                 </screen>""" % config.plugins.pictureviewer.rootdir.value
173                 self.skin = skin
174                 Screen.__init__(self, session)
175
176                 self.filelist = PictureList(config.plugins.pictureviewer.rootdir.value, matchingPattern = config.plugins.pictureviewer.matchingPattern.value)
177                 self["menu"] = self.filelist
178
179                 self.picload = ePicLoad()
180                 self.picload.PictureData.get().append(self.updateInfoPanelCB)
181                 self.picload.setPara((275, 200, 1, 1, False, 1, "#ff000000"))
182                 self.preview = Pixmap()
183                 self["pixmap"] = self.preview
184
185                 self.slideshowfiles = []
186                 self.slideshowlist =MenuList(self.slideshowfiles)
187                 self["slist"] = self.slideshowlist
188
189                 self["buttonred"] = Label("")
190                 self["buttongreen"] = Label("")
191                 self["buttonyellow"] = Label("")
192                 self["buttonblue"] = Label("")
193
194                 self["actions"] = ActionMap(["WizardActions", "MenuActions", "DirectionActions", "ShortcutActions"],
195                         {
196                          "ok": self.go,
197                          "back": self.close,
198                          "menu": self.openMenu,
199                          "up": self.up,
200                          "down": self.down,
201                          "left": self.leftUp,
202                          "right": self.rightUp,
203                          "red": self.KeyRed,
204                          "green": self.KeyGreen,
205                          "yellow": self.KeyYellow,
206                          "blue": self.switchList,
207                          }, -1)
208
209                 self.onLayoutFinish.append(self.switchList)
210                 self.onLayoutFinish.append(self.updateInfoPanel)
211
212         def KeyGreen(self):
213                 if self.currList is "filelist":
214                         # adding all files in current dir to slideshowlist
215                         dirname = self["menu"].getCurrentDir()
216                         if os.path.isdir(dirname):
217                                 s = os.listdir(dirname)
218                                 s.sort()
219                                 for file in s:
220                                         if compile(config.plugins.pictureviewer.matchingPattern.value).search(dirname + file):
221                                                 self.slideshowfiles.append((_(file),dirname + file))
222                                 self["slist"].l.setList(self.slideshowfiles)
223                 else:
224                         #loading list
225                         list = []
226                         try:
227                                 for file in os.listdir(config.plugins.pictureviewer.slideshowdir.value):
228                                         if file.endswith(config.plugins.pictureviewer.slideshowext.value):
229                                                 list.append((_(file.split("/")[-1]),file))
230                                 self.session.openWithCallback(
231                                                 self.fileToLoadFilelistEntered,
232                                                 ChoiceBox,
233                                                 _("select List to load"),
234                                                 list
235                                 )
236                         except IOError,e:
237                                 print "["+myname+"] IOError:",e
238                         except OSError,e:
239                                 print "["+myname+"] OSError:",e
240
241         def KeyRed(self):
242                 if self.currList is "filelist" :
243                         #do slideshow
244                         self.hide()
245                         x = Slideshow(self.session, self.show)
246                         x.setfiles(self.slideshowfiles)
247                         x.start()
248                 else:
249                         # save filelist
250                         if not self.loadedslideshowlistlistname:
251                                 newname = "slideshowlist"
252                         else:
253                                 newname = self.loadedslideshowlistlistname
254                         self.session.openWithCallback(
255                                         self.fileToSaveFilelistEntered,
256                                         InputBox,
257                                         title = _("Enter filename to save the List:"),
258                                         text = newname,
259                                         maxSize = False,
260                                         type = Input.TEXT
261                         )
262
263         def fileToLoadFilelistEntered(self, fileselection):
264                 if fileselection is not None:
265                            try:
266                                    filename = fileselection[1]
267                                    fp = open(config.plugins.pictureviewer.slideshowdir.value + filename)
268                                    list = []
269                                    for x in fp.readlines():
270                                            file = x.replace("\n","")
271                                            if x.startswith("#"):
272                                                    pass
273                                            elif not os.path.exists(file):
274                                                    print "["+myname+"] loaded file from filelist isnt avaible! ignoreing ->", file
275                                            else:
276                                                    list.append((_(file.split("/")[-1]), file))
277                                    self.slideshowfiles = list
278                                    self["slist"].l.setList(self.slideshowfiles)
279                                    self.loadedslideshowlistlistname = filename.replace(config.plugins.pictureviewer.slideshowext.value, "")
280                            except IOError, e:
281                                    print "["+myname+"] error:", e
282
283         def fileToSaveFilelistEntered(self, filename):
284                 if filename is not None:
285                         print "["+myname+"] saving list to ", config.plugins.pictureviewer.slideshowdir.value+filename + config.plugins.pictureviewer.slideshowext.value
286                         try:
287                                 if not os.path.exists(config.plugins.pictureviewer.slideshowdir.value):
288                                         print "+" * 10, os.path.basename(filename)
289                                         os.mkdir(config.plugins.pictureviewer.slideshowdir.value)
290                                 fp = open(config.plugins.pictureviewer.slideshowdir.value + filename+config.plugins.pictureviewer.slideshowext.value, "w")
291                                 fp.write("# this is a slideshow file for "+myname+" made by V"+myversion+"\n")
292                                 fp.write("# you can make your own... each line with full path of the imagefile\n")
293                                 fp.write("# by importing this file,we will ignoring a file if is doesnt exist\n")
294                                 for x in self.slideshowfiles:
295                                         fp.write(x[1] + "\n")
296                                 fp.close()
297                         except IOError, e:
298                                 print "["+myname+"] error:", e
299
300         def KeyYellow(self):
301                 if self.currList is "filelist":
302                         # add picture to list
303                         fullfile = self["menu"].getSelection()[0]
304                         if os.path.isfile(fullfile):
305                                 self.slideshowfiles.append((_(fullfile.split("/")[-1]), fullfile))
306                                 self["slist"].l.setList(self.slideshowfiles)
307                 else:
308                         # deleting an Picture
309                         if len(self.slideshowfiles) >= 1:
310                                 indexinlist = self["slist"].l.getCurrentSelectionIndex()
311                                 self.slideshowfiles.pop(indexinlist)
312                                 self["slist"].l.setList(self.slideshowfiles)
313
314         def switchList(self):
315                 if self.currList is "filelist" :
316                         # Slideshow activieren
317                         self.filelist.selectionEnabled(0)
318                         self.slideshowlist.selectionEnabled(1)
319                         self["buttonred"].setText("speichern")
320                         self["buttongreen"].setText("laden")
321                         self["buttonyellow"].setText("loeschen")
322                         self["buttonblue"].setText("Dateien")
323                         self.currList = "slideshowlist"
324                 else:
325                         # filelist activieren
326                         self.filelist.selectionEnabled(1)
327                         self.slideshowlist.selectionEnabled(0)
328                         self["buttonred"].setText("starte Slideshow")
329                         self["buttongreen"].setText("alle hinzufuegen")
330                         self["buttonyellow"].setText("hinzufuegen")
331                         self["buttonblue"].setText("Slideshow bearbeiten")
332                         self.currList = "filelist"
333
334         def go(self):
335                 if self.currList is "filelist" :
336                         selection = self["menu"].getSelection()
337                         if self.filelist.canDescent():
338                                 self.setTitle(selection[0])
339                                 self.filelist.descent()
340                         else:
341                                 if selection[1] == True: # isDir
342                                         pass
343                                 else:
344                                         print "["+myname+"] file selected ", selection[0]
345                                         if os.path.isfile(selection[0]):
346                                                 self.session.open(PictureScreen,selection[0].split("/")[-1], selection[0])
347                                         else:
348                                                 print "["+myname+"] file not found ", selection[0]
349                 else:
350                         self.updateInfoPanel()
351
352         def up(self):
353                  if self.currList is "filelist":
354                          self.filelist.up()
355                          self.updateInfoPanel()
356                  else:
357                          self.slideshowlist.up()
358
359         def leftUp(self):
360                  if self.currList is "filelist":
361                          self.filelist.pageUp()
362                          self.updateInfoPanel()
363                  else:
364                          self.slideshowlist.pageUp()
365
366         def rightUp(self):
367                 if self.currList is "filelist":
368                          self.filelist.pageDown()
369                          self.updateInfoPanel()
370                 else:
371                          self.slideshowlist.pageDown()
372
373         def down(self):
374                  if self.currList is "filelist":
375                          self.filelist.down()
376                          self.updateInfoPanel()
377                  else:
378                          self.slideshowlist.down()
379
380         def updateInfoPanel(self):
381                 if self.currList is "filelist":
382                         selectedfile = self["menu"].getSelection()[0]
383                 else:
384                         selectedfile = self["slist"].l.getCurrentSelection()[1]
385                 self.picload.startDecode(selectedfile)
386
387         def updateInfoPanelCB(self, picInfo = None):
388                 ptr = self.picload.getData()
389                 if ptr is not None:
390                         self["pixmap"].instance.setPixmap(ptr.__deref__())
391                         self["pixmap"].move(275,0)
392                 else:
393                         pass
394
395         def output(self,str):
396                 print "+" * 10, str
397
398         def openMenu(self):
399                 self.session.open(WebcamViewerMenu)
400 ###################
401 class WebcamViewer(Screen):
402         skin = ""
403         filelist = []
404         def __init__(self, session,xmlnode, args = 0):
405                 self.xmlnode = xmlnode
406                 screen_x = 736
407                 screen_y = 576
408                 size_x = 350
409                 size_y = 250
410                 pos_x = (screen_x/2)-(size_x/2)
411                 pos_y = (screen_y/2)-(size_y/2)
412                 skin = """
413                 <screen position="%i,%i" size="%i,%i" title="%s">
414                         <widget name="menu" position="1,1" size="%i,%i"  scrollbarMode="showOnDemand"/>
415                 </screen>""" % (pos_x,pos_y,size_x,size_y,myname,size_x,size_y)
416                 self.skin = skin
417                 Screen.__init__(self, session)
418
419                 self.filelist = MenuList(self.getMenuData())
420                 self["menu"] = self.filelist
421                 self["actions"] = ActionMap(["WizardActions", "DirectionActions"],
422                         {
423                          "ok": self.go,
424                          "back": self.close,
425                          }, -1)
426                 self.onLayoutFinish.append(self.settingTitle)
427
428         def settingTitle(self):
429                 self.setTitle(myname + ": " + self.menutitle)
430
431         def go(self):
432                 selected = self["menu"].l.getCurrentSelection()[1]
433                 menuitemtitle = self["menu"].l.getCurrentSelection()[0]
434                 type = selected[0]
435                 data = selected[1]
436                 if type.startswith("cam"):
437                         self.session.open(PictureScreen, menuitemtitle, data)
438                 else:
439                         self.hide()
440                         self.session.openWithCallback(self.cb, WebcamViewer, data)
441
442         def cb(self):
443                 self.show()
444
445         def getMenuData(self):
446                 xloader = XMLloader()
447                 self.menutitle = xloader.getScreenXMLTitle(self.xmlnode)
448                 data =[]
449                 for node in self.xmlnode.childNodes:
450                         if node.nodeType != xml.dom.minidom.Element.nodeType or node.tagName != 'menu':
451                                 continue
452                         nodex = {}
453                         nodex['name'] = xloader.get_txt(node, "name", "no name")
454                         data.append((_("*" + nodex['name']), ["node", node]))
455
456                 for node in self.xmlnode.childNodes:
457                         if node.nodeType != xml.dom.minidom.Element.nodeType or node.tagName != 'cam':
458                                 continue
459                         nodex = {}
460                         nodex['name'] = xloader.get_txt(node, "name", "no name")
461                         nodex['url'] =xloader.get_txt(node, "url", "no url")
462                         data.append((_(nodex['name']), ["cam", nodex['url']]))
463                 return data
464 ###################
465
466 ##################
467 class PictureList(MenuList):
468         def __init__(self, directory, matchingPattern = None, enableWrapAround = False):
469                 MenuList.__init__(self, None, enableWrapAround, eListboxPythonMultiContent)
470                 self.showDirectories = True
471                 self.showFiles = True
472                 self.isTop = False
473                 self.matchingPattern = matchingPattern
474                 self.changeDir(directory)
475                 self.l.setFont(0, gFont("Regular", 18))
476                 self.currentDir = directory
477
478         def getCurrentDir(self):
479                 return self.currentDir
480
481         def getSelection(self):
482                 return self.l.getCurrentSelection()[0]
483
484         def getFileList(self):
485                 return self.list
486
487         def changeDir(self, directory):
488                 self.currentDir = directory
489                 self.list = []
490
491                 directories = []
492                 files = []
493                 files = os.listdir(directory)
494                 files.sort()
495                 tmpfiles = files[:]
496                 for x in tmpfiles:
497                         if os.path.isdir(directory + "/" + x):
498                                 directories.append(x)
499                                 files.remove(x)
500                 directories.sort()
501                 files.sort()
502                 if directory != "/" and self.showDirectories and not self.isTop:
503                         self.list.append(self.getPictureEntryComponent("..", '/'.join(directory.split('/')[:-2]) + '/', True))
504
505                 if self.showDirectories:
506                         for x in directories:
507                                 name = (directory+x).split('/')[-1]
508                                 self.list.append(self.getPictureEntryComponent(name, '/'.join(directory.split('/')[:-1]) + '/' + x + '/', True))
509
510                 if self.showFiles:
511                         for x in files:
512                                 path = directory + x
513                                 name = x
514                                 if self.matchingPattern is not None:
515                                         if compile(self.matchingPattern).search(path):
516                                                 self.list.append(self.getPictureEntryComponent(name,path, False))
517                                 else:
518                                         pass
519
520                 self.l.setList(self.list)
521
522         def canDescent(self):
523                 return self.getSelection()[1]
524
525         def descent(self):
526                 self.changeDir(self.getSelection()[0])
527
528         def getFilename(self):
529                 return self.getSelection()[0].getPath()
530
531         def getServiceRef(self):
532                 return self.getSelection()[0]
533
534         def postWidgetCreate(self, instance):
535                 MenuList.postWidgetCreate(self, instance)
536                 instance.setItemHeight(23)
537
538         def getPictureEntryComponent(self,name, absolute, isDir):
539                 """ name={angezeigter Name}, absolute={vollstaendiger Pfad}, isDir={True,False} """
540                 res = [ (absolute, isDir) ]
541                 res.append((eListboxPythonMultiContent.TYPE_TEXT, 35, 1, 200, 20, 0, 0, name))
542                 if isDir:
543                         png = loadPNG("/usr/share/enigma2/extensions/directory.png")
544                 else:
545                         extension = name.split('.')
546                         extension = extension[-1].lower()
547                         if EXTENSIONS.has_key(extension):
548                                 png = loadPNG("/usr/share/enigma2/extensions/" + EXTENSIONS[extension] + ".png")
549                         else:
550                                 png = None
551                 if png is not None:
552                         res.append((eListboxPythonMultiContent.TYPE_PIXMAP_ALPHATEST, 10, 2, 20, 20, png))
553                 return res
554
555
556 ##################
557 class XMLloader:
558         DEFAULT_NAMESPACES = (
559                   None, # RSS 0.91, 0.92, 0.93, 0.94, 2.0
560                   'http://purl.org/rss/1.0/', # RSS 1.0
561                   'http://my.netscape.com/rdf/simple/0.9/' # RSS 0.90
562                 )
563         DUBLIN_CORE = ('http://purl.org/dc/elements/1.1/',)
564         def getElementsByTagName(self, node, tagName, possibleNamespaces = DEFAULT_NAMESPACES):
565                 for namespace in possibleNamespaces:
566                         children = node.getElementsByTagNameNS(namespace, tagName)
567                         if len(children):
568                                 return children
569                 return []
570
571         def node_data(self, node, tagName, possibleNamespaces = DEFAULT_NAMESPACES):
572                 children = self.getElementsByTagName(node, tagName, possibleNamespaces)
573                 node = len(children) and children[0] or None
574                 return node and "".join([child.data.encode("utf-8") for child in node.childNodes]) or None
575
576         def get_txt(self, node, tagName, default_txt = ""):
577                 """
578                 Liefert den Inhalt >tagName< des >node< zurueck, ist dieser nicht
579                 vorhanden, wird >default_txt< zurueck gegeben.
580                 """
581                 return self.node_data(node, tagName) or self.node_data(node, tagName, self.DUBLIN_CORE) or default_txt
582
583         def getScreenXMLTitle(self,node):
584                 return self.get_txt(node, "name", "no title")
585