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