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