fix *required* kwargs. plugins not having **kwargs will probably not run with newer...
[enigma2.git] / lib / python / Plugins / Extensions / PicturePlayer / plugin.py
1 from enigma import eTimer, loadPic, getExif
2 from Screens.Screen import Screen
3 from Screens.ServiceInfo import ServiceInfoList, ServiceInfoListEntry
4 from Components.ActionMap import ActionMap, NumberActionMap
5 from Components.Pixmap import Pixmap, MovingPixmap
6 from Components.Label import Label
7
8 from Components.ConfigList import ConfigList
9 from Components.config import *
10
11 from Tools.Directories import resolveFilename, pathExists, createDir, SCOPE_MEDIA
12 from Components.FileList import FileList
13 from Components.AVSwitch import AVSwitch
14
15 from Plugins.Plugin import PluginDescriptor
16
17 config.pic = ConfigSubsection()
18 config.pic.slidetime = ConfigInteger(default=10, limits=(5, 60))
19 config.pic.resize = ConfigSelection(default="0", choices = [("0", _("simple")), ("1", _("better"))])
20 config.pic.cache = ConfigEnableDisable(default=True)
21 config.pic.lastDir = ConfigText(default=resolveFilename(SCOPE_MEDIA))
22 config.pic.rotate = ConfigSelection(default="0", choices = [("0", _("none")), ("1", _("manual")), ("2", _("by Exif"))])
23
24 def getAspect():
25         val = AVSwitch().getAspectRatioSetting()
26         return val/2
27
28 #------------------------------------------------------------------------------------------
29
30 class ThumbView(Screen):
31         skin = """
32                 <screen position="0,0" size="720,576" flags="wfNoBorder" title="ThumbView" >
33                         <eLabel position="0,0" zPosition="0" size="720,576" backgroundColor="black" />
34                         <widget name="frame" position="50,63" size="190,200" pixmap="pic_frame.png" zPosition="1" alphatest="on" />
35                         <widget name="label0" position="55,240" size="180,20" font="Regular;13" halign="center" zPosition="2" transparent="1" />
36                         <widget name="label1" position="270,240" size="180,20" font="Regular;13" halign="center" zPosition="2" transparent="1" />
37                         <widget name="label2" position="485,240" size="180,20" font="Regular;13" halign="center" zPosition="2" transparent="1" />
38                         <widget name="label3" position="55,465" size="180,20" font="Regular;13" halign="center" zPosition="2" transparent="1" />
39                         <widget name="label4" position="270,465" size="180,20" font="Regular;13" halign="center" zPosition="2" transparent="1" />
40                         <widget name="label5" position="485,465" size="180,20" font="Regular;13" halign="center" zPosition="2" transparent="1" />
41                         <widget name="thumb0" position="55,68" size="180,160" zPosition="2" transparent="1"  />
42                         <widget name="thumb1" position="270,68" size="180,160" zPosition="2" transparent="1" />
43                         <widget name="thumb2" position="485,68" size="180,160" zPosition="2" transparent="1" />
44                         <widget name="thumb3" position="55,293" size="180,160" zPosition="2" transparent="1" />
45                         <widget name="thumb4" position="270,293" size="180,160" zPosition="2" transparent="1" />
46                         <widget name="thumb5" position="485,293" size="180,160" zPosition="2" transparent="1" />
47                 </screen>"""
48         
49         def __init__(self, session, filelist, name, path):
50                 self.skin = ThumbView.skin
51                 Screen.__init__(self, session)
52
53                 self["actions"] = ActionMap(["OkCancelActions", "DirectionActions", "MovieSelectionActions"],
54                 {
55                         "cancel": self.Exit,
56                         "ok": self.KeyOk,
57                         "showEventInfo": self.StartExif,
58                         "right": self.key_right,
59                         "left": self.key_left,
60                         "up": self.key_up,
61                         "down": self.key_down
62                 }, -1)
63                 
64                 for x in range(6):
65                         self["label"+str(x)] = Label()
66                         self["thumb"+str(x)] = Pixmap()
67                 self["frame"] = MovingPixmap()
68                 
69                 self.aspect = getAspect()
70                 self.path = path
71                 self.filelist = filelist
72                 self.currPage = -1
73                 self.index = 0
74                 self.old_index = 0
75                 self.thumblist = []
76                 self.thumbindex = 0
77                 self.list = []
78                 self.poslist = [[50,63],[265,63],[480,63],[50,288],[265,288],[480,288]]
79                 
80                 count=0
81                 pos=0
82                 for x in self.filelist:
83                         if x[0][1] == False:
84                                 self.list.append((x[0][0], self.path + x[0][0], count/6, pos, "(" + str(count+1) + ")  "))
85                                 pos += 1
86                                 if pos == 6:
87                                         pos = 0
88                                 if x[0][0] == name:
89                                         self.index = count
90                                 count += 1
91                 self.maxentry = len(self.list)-1
92                 
93                 if self.maxentry < 0:
94                         self["label0"].setText(_("no Picture found"))
95                 
96                 self.ThumbTimer = eTimer()
97                 self.ThumbTimer.timeout.get().append(self.showThumb)
98
99                 self.fillPage()
100                 
101         def key_left(self):
102                 self.index -= 1
103                 if self.index < 0:
104                         self.index = self.maxentry
105                 self.fillPage()
106                 
107         def key_right(self):
108                 self.index += 1
109                 if self.index > self.maxentry:
110                         self.index = 0
111                 self.fillPage()
112                 
113         def key_up(self):
114                 self.index -= 3
115                 if self.index < 0:
116                         self.index = 0
117                 self.fillPage()
118                 
119         def key_down(self):
120                 self.index += 3
121                 if self.index > self.maxentry:
122                         self.index = self.maxentry
123                 self.fillPage()
124                 
125         def fillPage(self):
126                 if self.maxentry < 0:
127                         return
128
129                 self["frame"].moveTo(self.poslist[self.list[self.index][3]][0], self.poslist[self.list[self.index][3]][1], 1)
130                 self["frame"].startMoving()
131                 
132                 if self.list[self.index][2] != self.currPage:
133                         self.currPage = self.list[self.index][2]
134                         textlist = ["","","","","",""]
135                         self.thumblist = ["","","","","",""]
136                         
137                         for x in self.list:
138                                 if x[2] == self.currPage:
139                                         textlist[x[3]] = x[4] + x[0]
140                                         self.thumblist[x[3]] = x[0]
141                                         
142                         for x in range(6):
143                                 self["label"+str(x)].setText(textlist[x])
144                                 self["thumb"+str(x)].hide()
145                                 
146                         self.ThumbTimer.start(500, True)
147                 
148         def showThumb(self):
149                 if self.thumblist[self.thumbindex] != "":
150                         cachefile = ""
151                         if config.pic.cache.value:
152                                 cachedir = self.path + ".Thumbnails/"
153                                 cachefile = cachedir + self.thumblist[self.thumbindex] + str(180) + str(160) + str(self.aspect)
154                                 if not pathExists(cachedir):
155                                         if not createDir(cachedir):
156                                                 cachefile = ""
157
158                         ptr = loadPic(self.path + self.thumblist[self.thumbindex], 180, 160, self.aspect, int(config.pic.resize.value), int(config.pic.rotate.value),1, cachefile)
159                         if ptr != None:
160                                 self["thumb"+str(self.thumbindex)].show()
161                                 self["thumb"+str(self.thumbindex)].instance.setPixmap(ptr)
162                         
163                         self.thumbindex += 1
164                         if self.thumbindex < 6:
165                                 self.ThumbTimer.start(500, True)
166                         else:
167                                 self.thumbindex = 0
168                 else:
169                         self.thumbindex = 0
170                 
171         def StartExif(self):
172                 if self.maxentry < 0:
173                         return
174
175                 self.session.open(ExifView, self.list[self.index][1], self.list[self.index][0])
176
177         def KeyOk(self):
178                 if self.maxentry < 0:
179                         return
180
181                 self.old_index = self.index
182                 self.session.openWithCallback(self.returnView ,PicView, self.filelist, self.list[self.index][0], self.path)
183                 
184         def returnView(self, val=0):
185                 self.index = val
186                 if self.old_index != self.index:
187                         self.fillPage()
188                 
189         def Exit(self):
190                 self.close(self.index)
191
192 #------------------------------------------------------------------------------------------
193
194 class PicView(Screen):
195         skin = """
196                 <screen position="0,0" size="720,576" flags="wfNoBorder" title="PicturePlayer" >
197                         <eLabel position="0,0" zPosition="0" size="720,576" backgroundColor="black" />
198                         <widget name="picture" position="80,50" size="560,450" zPosition="1" transparent="1" />
199                         <widget name="point" position="80,515" size="15,15" zPosition="1" pixmap="BlinkingPoint-fs8.png" alphatest="on" />
200                         <widget name="file" position="150,510" size="350,30" font="Regular;20" halign="center" zPosition="1" transparent="1" />
201                         <ePixmap position="500,515" size="36,20" pixmap="key_info-fs8.png" zPosition="1" alphatest="on" />
202                         <ePixmap position="550,515" size="20,20" pixmap="ico_mp_rewind.png"  zPosition="1" alphatest="on" />
203                         <widget name="play" position="575,515" size="20,20" pixmap="ico_mp_play.png"  zPosition="1" alphatest="on" />
204                         <widget name="pause" position="600,515" size="20,20" pixmap="ico_mp_pause.png"  zPosition="1" alphatest="on" />
205                         <ePixmap position="625,515" size="20,20" pixmap="ico_mp_forward.png"  zPosition="1" alphatest="on" />
206                 </screen>"""
207         
208         def __init__(self, session, filelist, name, path):
209                 self.skin = PicView.skin
210                 Screen.__init__(self, session)
211
212                 self["actions"] = ActionMap(["OkCancelActions", "ColorActions", "MovieSelectionActions"],
213                 {
214                         "cancel": self.Exit,
215                         "showEventInfo": self.StartExif,
216                         "green": self.Play,
217                         "yellow": self.Pause,
218                         "blue": self.nextPic,
219                         "red": self.prevPic
220                 }, -1)
221
222                 self.aspect = getAspect()
223                 self.blinking = False
224                 self.autoShow = True
225                 self.slideOn = False
226                 self.pauseOn = False
227                 self.index = 0
228                 self.old = 0
229                 self.list = []
230                 
231                 count=0
232                 for x in filelist:
233                         if x[0][1] == False:
234                                 self.list.append((x[0][0], path + x[0][0], 0))
235                                 if x[0][0] == name:
236                                         self.index = count
237                                 count += 1
238                 self.maxentry = len(self.list)-1
239
240                 self["file"] = Label(_("please wait, loading picture..."))
241                 self["picture"] = Pixmap()
242                 self["point"] = Pixmap()
243                 self["play"] = Pixmap()
244                 self["pause"] = Pixmap()
245                 
246                 self.decodeTimer = eTimer()
247                 self.decodeTimer.timeout.get().append(self.decodePic)
248                 self.decodeTimer.start(300, True)
249
250                 self.slideTimer = eTimer()
251                 self.slideTimer.timeout.get().append(self.slidePic)
252                 
253                 
254         def Pause(self):
255                 if self.slideOn:
256                         if self.pauseOn:
257                                 self.pauseOn=False
258                                 self["pause"].show()
259                         else:
260                                 self.pauseOn=True
261                                 self["play"].show()
262                                 self.slideValue = 0
263                 
264         def Play(self):
265                 if self.pauseOn == False:
266                         if self.slideOn:
267                                 self.slideOn=False
268                                 self["play"].show()
269                         else:
270                                 self.slideOn=True
271                                 self.slideTimer.start(1000, True)
272                         
273                         self.slideValue = int(config.pic.slidetime.value)
274                 
275         def slidePic(self):
276                 if self.slideOn == True and self.pauseOn == False:
277                         self.blinkingWidget("play")
278                         self.slideValue -= 1
279                         if self.slideValue <= 0:
280                                 self.slideValue = int(config.pic.slidetime.value)
281                                 self.nextPic()
282                 
283                         self.slideTimer.start(1000, True)
284
285                 if self.pauseOn:
286                         self.blinkingWidget("pause")
287                         self.slideTimer.start(1000, True)
288
289         def decodePic(self):
290                 self.currPic = loadPic(self.list[self.index][1], 560, 450, self.aspect, int(config.pic.resize.value), int(config.pic.rotate.value),1)
291                 self["point"].hide()
292                 if self.autoShow:
293                         self.showPic()
294                         self.autoShow = False
295                 
296         def showPic(self):
297                 if self.currPic != None:
298                         self.old = self.index
299                         self["file"].setText(self.list[self.old][0] + "  (" + str(self.old+1) + "/" + str(self.maxentry+1) + ")")
300                         self["picture"].instance.setPixmap(self.currPic)
301
302                 self.next()
303                 self["point"].show()
304                 self.decodeTimer.start(300, True)
305                 
306         def nextPic(self):
307                 self.showPic()
308                 
309         def prevPic(self):
310                 self.index = self.old
311                 self.prev()
312                 self.autoShow = True
313                 self["point"].show()
314                 self.decodeTimer.start(300, True)
315                 
316         def next(self):
317                 self.index += 1
318                 if self.index > self.maxentry:
319                         self.index = 0
320
321         def prev(self):
322                 self.index -= 1
323                 if self.index < 0:
324                         self.index = self.maxentry
325                         
326         def blinkingWidget(self, name):
327                 if self.blinking:
328                         self.blinking=False
329                         self[name].show()
330                 else:
331                         self.blinking=True
332                         self[name].hide()
333
334         def StartExif(self):
335                 if self.pauseOn == False:
336                         self.Pause()
337                 self.session.openWithCallback(self.StopExif ,ExifView, self.list[self.old][1], self.list[self.old][0])
338                 
339         def StopExif(self):
340                 if self.pauseOn:
341                         self.Pause()
342
343         def Exit(self):
344                 self.close(self.old)
345
346 #------------------------------------------------------------------------------------------
347
348 class ExifView(Screen):
349         skin = """
350                 <screen position="80,130" size="560,320" title="Exif-Data" >
351                         <widget name="exiflist" position="5,5" size="550,310" selectionDisabled="1" />
352                 </screen>"""
353         
354         def __init__(self, session, fullname, name):
355                 self.skin = ExifView.skin
356                 Screen.__init__(self, session)
357
358                 self["actions"] = ActionMap(["OkCancelActions"],
359                 {
360                         "cancel": self.close
361                 }, -1)
362                 
363                 dlist = ["Name:", "EXIF-Version:", "Camera-Make:", "Camera-Model:", "Date/Time:", "User Comments:", "Width / Height:", "Orientation:", "Metering Mode:", "Exposure Program:", "Light Source:", "Flash used:", "CompressedBitsPerPixel:", "ISO Speed Rating:", "X-Resolution:", "Y-Resolution:", "Resolution Unit:", "Brightness:", "Exposure Time:", "Exposure Bias:", "Distance:", "CCD-Width:", "ApertureFNumber:"]
364                 tlist = [ ]
365                 self["exiflist"] = ServiceInfoList(tlist)
366                 tlist.append(ServiceInfoListEntry(dlist[0], name))
367                 count=1
368                 for x in getExif(fullname):
369                         tlist.append(ServiceInfoListEntry(dlist[count], x))
370                         count += 1
371
372 #------------------------------------------------------------------------------------------
373
374 class PicSetup(Screen):
375         skin = """
376                 <screen position="160,220" size="400,120" title="Settings" >
377                         <widget name="liste" position="10,10" size="380,100" />
378                 </screen>"""
379         
380         def __init__(self, session):
381                 self.skin = PicSetup.skin
382                 Screen.__init__(self, session)
383
384                 self["actions"] = NumberActionMap(["SetupActions"],
385                 {
386                         "cancel": self.close,
387                         "left": self.keyLeft,
388                         "right": self.keyRight,
389                         "0": self.keyNumber,
390                         "1": self.keyNumber,
391                         "2": self.keyNumber,
392                         "3": self.keyNumber,
393                         "4": self.keyNumber,
394                         "5": self.keyNumber,
395                         "6": self.keyNumber,
396                         "7": self.keyNumber,
397                         "8": self.keyNumber,
398                         "9": self.keyNumber
399                 }, -1)
400                 
401                 self.list = []
402                 self["liste"] = ConfigList(self.list)
403                 self.list.append(getConfigListEntry(_("Slideshow Interval (sec.)"), config.pic.slidetime))
404                 self.list.append(getConfigListEntry(_("Scaling Mode"), config.pic.resize))
405                 self.list.append(getConfigListEntry(_("Cache Thumbnails"), config.pic.cache))
406                 #self.list.append(getConfigListEntry(_("Rotate Picture"), config.pic.rotate))
407                 
408         def keyLeft(self):
409                 self["liste"].handleKey(KEY_LEFT)
410
411         def keyRight(self):
412                 self["liste"].handleKey(KEY_RIGHT)
413                 
414         def keyNumber(self, number):
415                 self["liste"].handleKey(KEY_0 + number)
416
417
418 #------------------------------------------------------------------------------------------
419
420 class picmain(Screen):
421         skin = """
422                 <screen position="160,90" size="400,390" title="PicturePlayer" >
423                         <ePixmap position="10,40" size="36,20" pixmap="key_menu-fs8.png" transparent="1" alphatest="on" />
424                         <ePixmap position="10,70" size="36,20" pixmap="key_info-fs8.png" transparent="1" alphatest="on" />
425                         <ePixmap position="12,100" size="36,20" pixmap="key_red-fs8.png" transparent="1" alphatest="on" />
426                         <eLabel text="Settings" position="60,40" size="120,25" font="Regular;20" transparent="1" />
427                         <eLabel text="Exif-Data" position="60,70" size="120,25" font="Regular;20" transparent="1" />
428                         <eLabel text="Thumbnails" position="60,100" size="120,25" font="Regular;20" transparent="1" />
429                         <widget name="thumbnail" position="200,5" size="180,160" alphatest="on" />
430                         <widget name="filelist" position="5,170" zPosition="2" size="390,210" scrollbarMode="showOnDemand" />
431                 </screen>"""
432         
433         def __init__(self, session):
434                 self.skin = picmain.skin
435                 Screen.__init__(self, session)
436
437                 self["actions"] = ActionMap(["OkCancelActions", "DirectionActions", "ColorActions", "MovieSelectionActions"],
438                 {
439                         "ok": self.KeyOk,
440                         "cancel": self.Exit,
441                         "right": self.rightDown,
442                         "left": self.leftUp,
443                         "up": self.up,
444                         "down": self.down,
445                         "showEventInfo": self.StartExif,
446                         "contextMenu": self.Settings,
447                         "red": self.StartThumb
448                 }, -1)
449                 
450                 self.aspect = getAspect()
451                 currDir = config.pic.lastDir.value
452                 if not pathExists(currDir):
453                         currDir = "/"
454
455                 self.filelist = FileList(currDir, matchingPattern = "(?i)^.*\.(jpeg|jpg|jpe|png|bmp)")
456                 self["filelist"] = self.filelist
457                 self["thumbnail"] = Pixmap()
458                 
459                 self.ThumbTimer = eTimer()
460                 self.ThumbTimer.timeout.get().append(self.showThumb)
461                 self.ThumbTimer.start(500, True)
462                 
463         def up(self):
464                 self["filelist"].up()
465                 self.ThumbTimer.start(1500, True)
466
467         def down(self):
468                 self["filelist"].down()
469                 self.ThumbTimer.start(1500, True)
470                 
471         def leftUp(self):
472                 self["filelist"].pageUp()
473                 self.ThumbTimer.start(1500, True)
474                 
475         def rightDown(self):
476                 self["filelist"].pageDown()
477                 self.ThumbTimer.start(1500, True)
478
479         def showThumb(self):
480                 if not self.filelist.canDescent():
481                         cachefile = ""
482                         if config.pic.cache.value:
483                                 cachedir = self.filelist.getCurrentDirectory() + ".Thumbnails/"
484                                 cachefile = cachedir + self.filelist.getFilename() + str(180) + str(160) + str(self.aspect)
485                                 if not pathExists(cachedir):
486                                         if not createDir(cachedir):
487                                                 cachefile = ""
488
489                         ptr = loadPic(self.filelist.getCurrentDirectory() + self.filelist.getFilename(), 180, 160, self.aspect, int(config.pic.resize.value), 0, 0, cachefile)
490                         if ptr != None:
491                                 self["thumbnail"].show()
492                                 self["thumbnail"].instance.setPixmap(ptr)
493                 else:
494                         self["thumbnail"].hide()
495
496         def KeyOk(self):
497                 if self.filelist.canDescent():
498                         self.filelist.descent()
499                 else:
500                         self.session.openWithCallback(self.returnVal, PicView, self.filelist.getFileList(), self.filelist.getFilename(), self.filelist.getCurrentDirectory())
501                         
502         def StartThumb(self):
503                 self.session.openWithCallback(self.returnVal, ThumbView, self.filelist.getFileList(), self.filelist.getFilename(), self.filelist.getCurrentDirectory())
504
505         def returnVal(self, val=0):
506                 if val > 0:
507                         for x in self.filelist.getFileList():
508                                 if x[0][1] == True:
509                                         val += 1
510                         self.filelist.moveToIndex(val)
511
512         def StartExif(self):
513                 if not self.filelist.canDescent():
514                         self.session.open(ExifView, self.filelist.getCurrentDirectory() + self.filelist.getFilename(), self.filelist.getFilename())
515
516         def Settings(self):
517                 self.session.open(PicSetup)
518         
519         def Exit(self):
520                 if self.filelist.getCurrentDirectory() is None:
521                         config.pic.lastDir.value = "/"
522                 else:
523                         config.pic.lastDir.value = self.filelist.getCurrentDirectory()
524
525                 config.pic.save()
526                 self.close()
527
528 #------------------------------------------------------------------------------------------
529
530 def main(session, **kwargs):
531         session.open(picmain)
532
533 def filescan_open(list, session, **kwargs):
534         session.open(picmain) # list
535
536 def filescan(**kwargs):
537         # we expect not to be called if the MediaScanner plugin is not available,
538         # thus we don't catch an ImportError exception here
539         from Plugins.Extensions.MediaScanner.plugin import Scanner, ScanPath
540         return \
541                 Scanner(extensions = ["jpg", "jpe", "jpeg"], 
542                         paths_to_scan = 
543                                 [
544                                         ScanPath(path = "DCIM", with_subdirs = True),
545                                         ScanPath(path = "", with_subdirs = False),
546                                 ],
547                         name = "Pictures", 
548                         description = "View Photos...",
549                         openfnc = filescan_open,
550                 )
551
552 def Plugins(**kwargs):
553         return \
554                 [PluginDescriptor(name="PicturePlayer", description="Picture Viewer (BMP, PNG, JPG)", icon="pictureplayer.png", where = PluginDescriptor.WHERE_PLUGINMENU, fnc=main),
555                  PluginDescriptor(name="PicturePlayer", where = PluginDescriptor.WHERE_FILESCAN, fnc = filescan)]