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