Extensions/DVDPlayer: add ".img" file extension to playable dvd formats. refs #439
[enigma2.git] / lib / python / Plugins / Extensions / DVDPlayer / plugin.py
1 from os import path as os_path, remove as os_remove, listdir as os_listdir, system
2 from enigma import eTimer, iPlayableService, iServiceInformation, eServiceReference, iServiceKeys, getDesktop
3 from Screens.Screen import Screen
4 from Screens.MessageBox import MessageBox
5 from Screens.ChoiceBox import ChoiceBox
6 from Screens.HelpMenu import HelpableScreen
7 from Screens.InfoBarGenerics import InfoBarSeek, InfoBarPVRState, InfoBarCueSheetSupport, InfoBarShowHide, InfoBarNotifications
8 from Components.ActionMap import ActionMap, NumberActionMap, HelpableActionMap
9 from Components.Label import Label
10 from Components.Sources.StaticText import StaticText
11 from Components.Pixmap import Pixmap
12 from Components.FileList import FileList
13 from Components.MenuList import MenuList
14 from Components.ServiceEventTracker import ServiceEventTracker, InfoBarBase
15 from Components.config import config
16 from Tools.Directories import pathExists, fileExists
17 from Components.Harddisk import harddiskmanager
18
19 import servicedvd # load c++ part of dvd player plugin
20
21 lastpath = ""
22
23 class FileBrowser(Screen):
24
25         def __init__(self, session, dvd_filelist = [ ]):
26                 Screen.__init__(self, session)
27
28                 # for the skin: first try FileBrowser_DVDPlayer, then FileBrowser, this allows individual skinning
29                 self.skinName = ["FileBrowser_DVDPlayer", "FileBrowser" ]
30
31                 self.dvd_filelist = dvd_filelist
32                 if len(dvd_filelist):   
33                         self["filelist"] = MenuList(self.dvd_filelist)
34                 else:
35                         global lastpath
36                         if lastpath is not None:
37                                 currDir = lastpath + "/"
38                         else:
39                                 currDir = "/media/dvd/"
40                         if not pathExists(currDir):
41                                 currDir = "/"
42                         if lastpath == "":  # 'None' is magic to start at the list of mountpoints
43                                 currDir = None
44
45                         inhibitDirs = ["/bin", "/boot", "/dev", "/etc", "/home", "/lib", "/proc", "/sbin", "/share", "/sys", "/tmp", "/usr", "/var"]
46                         self.filelist = FileList(currDir, matchingPattern = "(?i)^.*\.(iso|img)", useServiceRef = True)
47                         self["filelist"] = self.filelist
48
49                 self["FilelistActions"] = ActionMap(["SetupActions"],
50                         {
51                                 "save": self.ok,
52                                 "ok": self.ok,
53                                 "cancel": self.exit
54                         })
55                 self["key_red"] = StaticText(_("Cancel"))
56                 self["key_green"] = StaticText(_("OK"))
57                 self.onLayoutFinish.append(self.layoutFinished)
58
59         def layoutFinished(self):
60                 self.setTitle(_("DVD File Browser"))
61
62         def ok(self):
63                 if len(self.dvd_filelist):
64                         print "OK " + self["filelist"].getCurrent()
65                         self.close(self["filelist"].getCurrent())
66                 else:
67                         global lastpath
68                         filename = self["filelist"].getFilename()
69                         if filename is not None:
70                                 if filename.upper().endswith("VIDEO_TS/"):
71                                         print "dvd structure found, trying to open..."
72                                         dvdpath = filename[0:-9]
73                                         lastpath = (dvdpath.rstrip("/").rsplit("/",1))[0]
74                                         print "lastpath video_ts/=", lastpath
75                                         self.close(dvdpath)
76                                         return
77                         if self["filelist"].canDescent(): # isDir
78                                 self["filelist"].descent()
79                                 pathname = self["filelist"].getCurrentDirectory() or ""
80                                 if fileExists(pathname+"VIDEO_TS.IFO"):
81                                         print "dvd structure found, trying to open..."
82                                         lastpath = (pathname.rstrip("/").rsplit("/",1))[0]
83                                         print "lastpath video_ts.ifo=", lastpath
84                                         self.close(pathname)
85                                 if fileExists(pathname+"VIDEO_TS/VIDEO_TS.IFO"):
86                                         print "dvd structure found, trying to open..."
87                                         lastpath = (pathname.rstrip("/").rsplit("/",1))[0]
88                                         print "lastpath video_ts.ifo=", lastpath
89                                         pathname += "VIDEO_TS"
90                                         self.close(pathname)
91                         else:
92                                 lastpath = filename[0:filename.rfind("/")]
93                                 print "lastpath directory=", lastpath
94                                 self.close(filename)
95
96         def exit(self):
97                 self.close(None)
98
99 class DVDSummary(Screen):
100         skin = (
101         """<screen name="DVDSummary" position="0,0" size="132,64" id="1">
102                 <widget source="session.CurrentService" render="Label" position="5,4" size="120,28" font="Regular;12" transparent="1" >
103                         <convert type="ServiceName">Name</convert>
104                 </widget>
105                 <widget name="DVDPlayer" position="5,30" size="66,16" font="Regular;11" transparent="1" />
106                 <widget name="Chapter" position="72,30" size="54,16" font="Regular;12" transparent="1" halign="right" />
107                 <widget source="session.CurrentService" render="Label" position="66,46" size="60,18" font="Regular;16" transparent="1" halign="right" >
108                         <convert type="ServicePosition">Position</convert>
109                 </widget>
110                 <widget source="session.CurrentService" render="Progress" position="6,46" size="60,18" borderWidth="1" >
111                         <convert type="ServicePosition">Position</convert>
112                 </widget>
113         </screen>""",
114         """<screen name="DVDSummary" position="0,0" size="96,64" id="2">
115                 <widget source="session.CurrentService" render="Label" position="0,0" size="96,25" font="Regular;12" transparent="1" >
116                         <convert type="ServiceName">Name</convert>
117                 </widget>
118                 <widget name="DVDPlayer" position="0,26" size="96,12" font="Regular;10" transparent="1" />
119                 <widget name="Chapter" position="0,40" size="66,12" font="Regular;10" transparent="1" halign="left" />
120                 <widget source="session.CurrentService" render="Label" position="66,40" size="30,12" font="Regular;10" transparent="1" halign="right" >
121                         <convert type="ServicePosition">Position</convert>
122                 </widget>
123                 <widget source="session.CurrentService" render="Progress" position="0,52" size="96,12" borderWidth="1" >
124                         <convert type="ServicePosition">Position</convert>
125                 </widget>
126         </screen>""")
127
128         def __init__(self, session, parent):
129                 Screen.__init__(self, session, parent)
130
131                 self["DVDPlayer"] = Label("DVD Player")
132                 self["Title"] = Label("")
133                 self["Time"] = Label("")
134                 self["Chapter"] = Label("")
135
136         def updateChapter(self, chapter):
137                 self["Chapter"].setText(chapter)
138
139         def setTitle(self, title):
140                 self["Title"].setText(title)
141
142 class DVDOverlay(Screen):
143         def __init__(self, session, args = None):
144                 desktop_size = getDesktop(0).size()
145                 DVDOverlay.skin = """<screen name="DVDOverlay" position="0,0" size="%d,%d" flags="wfNoBorder" zPosition="-1" backgroundColor="transparent" />""" %(desktop_size.width(), desktop_size.height())
146                 Screen.__init__(self, session)
147
148 class ChapterZap(Screen):
149         skin = """
150         <screen name="ChapterZap" position="235,255" size="250,60" title="Chapter" >
151                 <widget name="chapter" position="35,15" size="110,25" font="Regular;23" />
152                 <widget name="number" position="145,15" size="80,25" halign="right" font="Regular;23" />
153         </screen>"""
154         
155         def quit(self):
156                 self.Timer.stop()
157                 self.close(0)
158
159         def keyOK(self):
160                 self.Timer.stop()
161                 self.close(int(self["number"].getText()))
162
163         def keyNumberGlobal(self, number):
164                 self.Timer.start(3000, True)            #reset timer
165                 self.field = self.field + str(number)
166                 self["number"].setText(self.field)
167                 if len(self.field) >= 4:
168                         self.keyOK()
169
170         def __init__(self, session, number):
171                 Screen.__init__(self, session)
172                 self.field = str(number)
173
174                 self["chapter"] = Label(_("Chapter:"))
175
176                 self["number"] = Label(self.field)
177
178                 self["actions"] = NumberActionMap( [ "SetupActions" ],
179                         {
180                                 "cancel": self.quit,
181                                 "ok": self.keyOK,
182                                 "1": self.keyNumberGlobal,
183                                 "2": self.keyNumberGlobal,
184                                 "3": self.keyNumberGlobal,
185                                 "4": self.keyNumberGlobal,
186                                 "5": self.keyNumberGlobal,
187                                 "6": self.keyNumberGlobal,
188                                 "7": self.keyNumberGlobal,
189                                 "8": self.keyNumberGlobal,
190                                 "9": self.keyNumberGlobal,
191                                 "0": self.keyNumberGlobal
192                         })
193
194                 self.Timer = eTimer()
195                 self.Timer.callback.append(self.keyOK)
196                 self.Timer.start(3000, True)
197
198 class DVDPlayer(Screen, InfoBarBase, InfoBarNotifications, InfoBarSeek, InfoBarPVRState, InfoBarShowHide, HelpableScreen, InfoBarCueSheetSupport):
199         ALLOW_SUSPEND = Screen.SUSPEND_PAUSES
200         ENABLE_RESUME_SUPPORT = True
201         
202         skin = """
203         <screen name="DVDPlayer" flags="wfNoBorder" position="0,380" size="720,160" title="InfoBar" backgroundColor="transparent" >
204                 <!-- Background -->
205                 <ePixmap position="0,0" zPosition="-2" size="720,160" pixmap="skin_default/info-bg_mp.png" alphatest="off" />
206                 <ePixmap position="29,40" zPosition="0" size="665,104" pixmap="skin_default/screws_mp.png" alphatest="on" transparent="1" />
207                 <!-- colorbuttons -->
208                 <ePixmap position="48,70" zPosition="0" size="108,13" pixmap="skin_default/icons/mp_buttons.png" alphatest="on" />
209                 <!-- Servicename -->
210                 <ePixmap pixmap="skin_default/icons/icon_event.png" position="207,78" zPosition="1" size="15,10" alphatest="on" />
211                 <widget source="session.CurrentService" render="Label" position="230,73" size="300,22" font="Regular;20" backgroundColor="#263c59" shadowColor="#1d354c" shadowOffset="-1,-1" transparent="1" noWrap="1">
212                         <convert type="ServiceName">Name</convert>
213                 </widget>
214                 <!-- Chapter info -->
215                 <widget name="chapterLabel" position="230,96" size="360,22" font="Regular;20" foregroundColor="#c3c3c9" backgroundColor="#263c59" transparent="1" />
216                 <!-- Audio track info -->
217                 <ePixmap pixmap="skin_default/icons/icon_dolby.png" position="540,60" zPosition="1" size="26,16" alphatest="on"/>
218                 <widget name="audioLabel" position="570,60" size="130,22" font="Regular;18" backgroundColor="#263c59" shadowColor="#1d354c" shadowOffset="-1,-1" transparent="1" />
219                 <!-- Subtitle track info -->
220                 <widget source="session.CurrentService" render="Pixmap" pixmap="skin_default/icons/icon_txt.png" position="540,83" zPosition="1" size="26,16" alphatest="on" >
221                         <convert type="ServiceInfo">HasTelext</convert>
222                         <convert type="ConditionalShowHide" />
223                 </widget>
224                 <widget name="subtitleLabel" position="570,83" size="130,22" font="Regular;18" backgroundColor="#263c59" shadowColor="#1d354c" shadowOffset="-1,-1" transparent="1" />
225                 <!-- Angle info -->
226                 <widget name="anglePix" pixmap="skin_default/icons/icon_view.png" position="540,106" size="26,16" alphatest="on" />
227                 <widget name="angleLabel" position="570,106" size="130,22" font="Regular;18" backgroundColor="#263c59" shadowColor="#1d354c" shadowOffset="-1,-1" transparent="1" />
228                 <!-- Elapsed time -->
229                 <widget source="session.CurrentService" render="Label" position="205,129" size="100,20" font="Regular;18" halign="center" valign="center" backgroundColor="#06224f" shadowColor="#1d354c" shadowOffset="-1,-1" transparent="1" >
230                         <convert type="ServicePosition">Position,ShowHours</convert>
231                 </widget>
232                 <!-- Progressbar (movie position)-->
233                 <widget source="session.CurrentService" render="PositionGauge" position="300,133" size="270,10" zPosition="2" pointer="skin_default/position_pointer.png:540,0" transparent="1" >
234                         <convert type="ServicePosition">Gauge</convert>
235                 </widget>
236                 <!-- Remaining time -->
237                 <widget source="session.CurrentService" render="Label" position="576,129" size="100,20" font="Regular;18" halign="center" valign="center" backgroundColor="#06224f" shadowColor="#1d354c" shadowOffset="-1,-1" transparent="1" >
238                         <convert type="ServicePosition">Remaining,Negate,ShowHours</convert>
239                 </widget>
240         </screen>"""
241
242         def save_infobar_seek_config(self):
243                 self.saved_config_speeds_forward = config.seek.speeds_forward.value
244                 self.saved_config_speeds_backward = config.seek.speeds_backward.value
245                 self.saved_config_enter_forward = config.seek.enter_forward.value
246                 self.saved_config_enter_backward = config.seek.enter_backward.value
247                 self.saved_config_seek_stepwise_minspeed = config.seek.stepwise_minspeed.value
248                 self.saved_config_seek_stepwise_repeat = config.seek.stepwise_repeat.value
249                 self.saved_config_seek_on_pause = config.seek.on_pause.value
250                 self.saved_config_seek_speeds_slowmotion = config.seek.speeds_slowmotion.value
251
252         def change_infobar_seek_config(self):
253                 config.seek.speeds_forward.value = [2, 4, 8, 16, 32, 64]
254                 config.seek.speeds_backward.value = [8, 16, 32, 64]
255                 config.seek.speeds_slowmotion.value = [ ]
256                 config.seek.enter_forward.value = "2"
257                 config.seek.enter_backward.value = "2"
258                 config.seek.stepwise_minspeed.value = "Never"
259                 config.seek.stepwise_repeat.value = "3"
260                 config.seek.on_pause.value = "play"
261
262         def restore_infobar_seek_config(self):
263                 config.seek.speeds_forward.value = self.saved_config_speeds_forward
264                 config.seek.speeds_backward.value = self.saved_config_speeds_backward
265                 config.seek.speeds_slowmotion.value = self.saved_config_seek_speeds_slowmotion
266                 config.seek.enter_forward.value = self.saved_config_enter_forward
267                 config.seek.enter_backward.value = self.saved_config_enter_backward
268                 config.seek.stepwise_minspeed.value = self.saved_config_seek_stepwise_minspeed
269                 config.seek.stepwise_repeat.value = self.saved_config_seek_stepwise_repeat
270                 config.seek.on_pause.value = self.saved_config_seek_on_pause
271
272         def __init__(self, session, dvd_device = None, dvd_filelist = [ ], args = None):
273                 Screen.__init__(self, session)
274                 InfoBarBase.__init__(self)
275                 InfoBarNotifications.__init__(self)
276                 InfoBarCueSheetSupport.__init__(self, actionmap = "MediaPlayerCueSheetActions")
277                 InfoBarShowHide.__init__(self)
278                 HelpableScreen.__init__(self)
279                 self.save_infobar_seek_config()
280                 self.change_infobar_seek_config()
281                 InfoBarSeek.__init__(self, useSeekBackHack=False)
282                 InfoBarPVRState.__init__(self)
283                 self.dvdScreen = self.session.instantiateDialog(DVDOverlay)
284
285                 self.oldService = self.session.nav.getCurrentlyPlayingServiceReference()
286                 self.session.nav.stopService()
287                 self["audioLabel"] = Label("n/a")
288                 self["subtitleLabel"] = Label("")
289                 self["angleLabel"] = Label("")
290                 self["chapterLabel"] = Label("")
291                 self["anglePix"] = Pixmap()
292                 self["anglePix"].hide()
293                 self.last_audioTuple = None
294                 self.last_subtitleTuple = None
295                 self.last_angleTuple = None
296                 self.totalChapters = 0
297                 self.currentChapter = 0
298                 self.totalTitles = 0
299                 self.currentTitle = 0
300
301                 self.__event_tracker = ServiceEventTracker(screen=self, eventmap=
302                         {
303                                 iPlayableService.evStopped: self.__serviceStopped,
304                                 iPlayableService.evUser: self.__timeUpdated,
305                                 iPlayableService.evUser+1: self.__statePlay,
306                                 iPlayableService.evUser+2: self.__statePause,
307                                 iPlayableService.evUser+3: self.__osdFFwdInfoAvail,
308                                 iPlayableService.evUser+4: self.__osdFBwdInfoAvail,
309                                 iPlayableService.evUser+5: self.__osdStringAvail,
310                                 iPlayableService.evUser+6: self.__osdAudioInfoAvail,
311                                 iPlayableService.evUser+7: self.__osdSubtitleInfoAvail,
312                                 iPlayableService.evUser+8: self.__chapterUpdated,
313                                 iPlayableService.evUser+9: self.__titleUpdated,
314                                 iPlayableService.evUser+11: self.__menuOpened,
315                                 iPlayableService.evUser+12: self.__menuClosed,
316                                 iPlayableService.evUser+13: self.__osdAngleInfoAvail
317                         })
318
319                 self["DVDPlayerDirectionActions"] = ActionMap(["DirectionActions"],
320                         {
321                                 #MENU KEY DOWN ACTIONS
322                                 "left": self.keyLeft,
323                                 "right": self.keyRight,
324                                 "up": self.keyUp,
325                                 "down": self.keyDown,
326
327                                 #MENU KEY REPEATED ACTIONS
328                                 "leftRepeated": self.doNothing,
329                                 "rightRepeated": self.doNothing,
330                                 "upRepeated": self.doNothing,
331                                 "downRepeated": self.doNothing,
332
333                                 #MENU KEY UP ACTIONS
334                                 "leftUp": self.doNothing,
335                                 "rightUp": self.doNothing,
336                                 "upUp": self.doNothing,
337                                 "downUp": self.doNothing,
338                         })
339
340                 self["OkCancelActions"] = ActionMap(["OkCancelActions"],
341                         {
342                                 "ok": self.keyOk,
343                                 "cancel": self.keyCancel,
344                         })
345
346                 self["DVDPlayerPlaybackActions"] = HelpableActionMap(self, "DVDPlayerActions",
347                         {
348                                 #PLAYER ACTIONS
349                                 "dvdMenu": (self.enterDVDMenu, _("show DVD main menu")),
350                                 "toggleInfo": (self.toggleInfo, _("toggle time, chapter, audio, subtitle info")),
351                                 "nextChapter": (self.nextChapter, _("forward to the next chapter")),
352                                 "prevChapter": (self.prevChapter, _("rewind to the previous chapter")),
353                                 "nextTitle": (self.nextTitle, _("jump forward to the next title")),
354                                 "prevTitle": (self.prevTitle, _("jump back to the previous title")),
355                                 "tv": (self.askLeavePlayer, _("exit DVD player or return to file browser")),
356                                 "dvdAudioMenu": (self.enterDVDAudioMenu, _("(show optional DVD audio menu)")),
357                                 "nextAudioTrack": (self.nextAudioTrack, _("switch to the next audio track")),
358                                 "nextSubtitleTrack": (self.nextSubtitleTrack, _("switch to the next subtitle language")),
359                                 "nextAngle": (self.nextAngle, _("switch to the next angle")),
360                                 "seekBeginning": self.seekBeginning,
361                         }, -2)
362                         
363                 self["NumberActions"] = NumberActionMap( [ "NumberActions"],
364                         {
365                                 "1": self.keyNumberGlobal,
366                                 "2": self.keyNumberGlobal,
367                                 "3": self.keyNumberGlobal,
368                                 "4": self.keyNumberGlobal,
369                                 "5": self.keyNumberGlobal,
370                                 "6": self.keyNumberGlobal,
371                                 "7": self.keyNumberGlobal,
372                                 "8": self.keyNumberGlobal,
373                                 "9": self.keyNumberGlobal,
374                                 "0": self.keyNumberGlobal,
375                         })
376
377                 self.onClose.append(self.__onClose)
378
379                 from Plugins.SystemPlugins.Hotplug.plugin import hotplugNotifier
380                 hotplugNotifier.append(self.hotplugCB)
381                 
382                 self.autoplay = dvd_device or dvd_filelist
383
384                 if dvd_device:
385                         self.physicalDVD = True
386                 else:
387                         self.scanHotplug()
388
389                 self.dvd_filelist = dvd_filelist
390                 self.onFirstExecBegin.append(self.opened)
391                 self.service = None
392                 self.in_menu = False
393
394         def keyNumberGlobal(self, number):
395                 print "You pressed number " + str(number)
396                 self.session.openWithCallback(self.numberEntered, ChapterZap, number)
397
398         def numberEntered(self, retval):
399 #               print self.servicelist
400                 if retval > 0:
401                         self.zapToNumber(retval)
402
403         def getServiceInterface(self, iface):
404                 service = self.service
405                 if service:
406                         attr = getattr(service, iface, None)
407                         if callable(attr):
408                                 return attr()
409                 return None
410
411         def __serviceStopped(self):
412                 self.dvdScreen.hide()
413                 subs = self.getServiceInterface("subtitle")
414                 if subs:
415                         subs.disableSubtitles(self.session.current_dialog.instance)
416
417         def serviceStarted(self): #override InfoBarShowHide function
418                 self.dvdScreen.show()
419
420         def doEofInternal(self, playing):
421                 if self.in_menu:
422                         self.hide()
423
424         def __menuOpened(self):
425                 self.hide()
426                 self.in_menu = True
427                 self["NumberActions"].setEnabled(False)
428
429         def __menuClosed(self):
430                 self.show()
431                 self.in_menu = False
432                 self["NumberActions"].setEnabled(True)
433
434         def setChapterLabel(self):
435                 chapterLCD = "Menu"
436                 chapterOSD = "DVD Menu"
437                 if self.currentTitle > 0:
438                         chapterLCD = "%s %d" % (_("Chap."), self.currentChapter)
439                         chapterOSD = "DVD %s %d/%d" % (_("Chapter"), self.currentChapter, self.totalChapters)
440                         chapterOSD += " (%s %d/%d)" % (_("Title"), self.currentTitle, self.totalTitles)
441                 self["chapterLabel"].setText(chapterOSD)
442                 try:
443                         self.session.summary.updateChapter(chapterLCD)
444                 except:
445                         pass
446
447         def doNothing(self):
448                 pass
449
450         def toggleInfo(self):
451                 if not self.in_menu:
452                         self.toggleShow()
453                         print "toggleInfo"
454
455         def __timeUpdated(self):
456                 print "timeUpdated"
457
458         def __statePlay(self):
459                 print "statePlay"
460
461         def __statePause(self):
462                 print "statePause"
463
464         def __osdFFwdInfoAvail(self):
465                 self.setChapterLabel()
466                 print "FFwdInfoAvail"
467
468         def __osdFBwdInfoAvail(self):
469                 self.setChapterLabel()
470                 print "FBwdInfoAvail"
471
472         def __osdStringAvail(self):
473                 print "StringAvail"
474
475         def __osdAudioInfoAvail(self):
476                 info = self.getServiceInterface("info")
477                 audioTuple = info and info.getInfoObject(iServiceInformation.sUser+6)
478                 print "AudioInfoAvail ", repr(audioTuple)
479                 if audioTuple:
480                         audioString = "%d: %s (%s)" % (audioTuple[0],audioTuple[1],audioTuple[2])
481                         self["audioLabel"].setText(audioString)
482                         if audioTuple != self.last_audioTuple and not self.in_menu:
483                                 self.doShow()
484                 self.last_audioTuple = audioTuple
485
486         def __osdSubtitleInfoAvail(self):
487                 info = self.getServiceInterface("info")
488                 subtitleTuple = info and info.getInfoObject(iServiceInformation.sUser+7)
489                 print "SubtitleInfoAvail ", repr(subtitleTuple)
490                 if subtitleTuple:
491                         subtitleString = ""
492                         if subtitleTuple[0] is not 0:
493                                 subtitleString = "%d: %s" % (subtitleTuple[0],subtitleTuple[1])
494                         self["subtitleLabel"].setText(subtitleString)
495                         if subtitleTuple != self.last_subtitleTuple and not self.in_menu:
496                                 self.doShow()
497                 self.last_subtitleTuple = subtitleTuple
498         
499         def __osdAngleInfoAvail(self):
500                 info = self.getServiceInterface("info")
501                 angleTuple = info and info.getInfoObject(iServiceInformation.sUser+8)
502                 print "AngleInfoAvail ", repr(angleTuple)
503                 if angleTuple:
504                         angleString = ""
505                         if angleTuple[1] > 1:
506                                 angleString = "%d / %d" % (angleTuple[0],angleTuple[1])
507                                 self["anglePix"].show()
508                         else:
509                                 self["anglePix"].hide()
510                         self["angleLabel"].setText(angleString)
511                         if angleTuple != self.last_angleTuple and not self.in_menu:
512                                 self.doShow()
513                 self.last_angleTuple = angleTuple
514
515         def __chapterUpdated(self):
516                 info = self.getServiceInterface("info")
517                 if info:
518                         self.currentChapter = info.getInfo(iServiceInformation.sCurrentChapter)
519                         self.totalChapters = info.getInfo(iServiceInformation.sTotalChapters)
520                         self.setChapterLabel()
521                         print "__chapterUpdated: %d/%d" % (self.currentChapter, self.totalChapters)
522
523         def __titleUpdated(self):
524                 info = self.getServiceInterface("info")
525                 if info:
526                         self.currentTitle = info.getInfo(iServiceInformation.sCurrentTitle)
527                         self.totalTitles = info.getInfo(iServiceInformation.sTotalTitles)
528                         self.setChapterLabel()
529                         print "__titleUpdated: %d/%d" % (self.currentTitle, self.totalTitles)
530                         if not self.in_menu:
531                                 self.doShow()
532                 
533         def askLeavePlayer(self):
534                 choices = [(_("Exit"), "exit"), (_("Continue playing"), "play")]
535                 if True or not self.physicalDVD:
536                         choices.insert(1,(_("Return to file browser"), "browser"))
537                 if self.physicalDVD:
538                         cur = self.session.nav.getCurrentlyPlayingServiceReference()
539                         if cur and not cur.toString().endswith(harddiskmanager.getAutofsMountpoint(harddiskmanager.getCD())):
540                             choices.insert(0,(_("Play DVD"), "playPhysical" ))
541                 self.session.openWithCallback(self.exitCB, ChoiceBox, title=_("Leave DVD Player?"), list = choices)
542
543         def sendKey(self, key):
544                 keys = self.getServiceInterface("keys")
545                 if keys:
546                         keys.keyPressed(key)
547                 return keys
548
549         def nextAudioTrack(self):
550                 self.sendKey(iServiceKeys.keyUser)
551
552         def nextSubtitleTrack(self):
553                 self.sendKey(iServiceKeys.keyUser+1)
554
555         def enterDVDAudioMenu(self):
556                 self.sendKey(iServiceKeys.keyUser+2)
557
558         def nextChapter(self):
559                 self.sendKey(iServiceKeys.keyUser+3)
560
561         def prevChapter(self):
562                 self.sendKey(iServiceKeys.keyUser+4)
563
564         def nextTitle(self):
565                 self.sendKey(iServiceKeys.keyUser+5)
566
567         def prevTitle(self):
568                 self.sendKey(iServiceKeys.keyUser+6)
569
570         def enterDVDMenu(self):
571                 self.sendKey(iServiceKeys.keyUser+7)
572         
573         def nextAngle(self):
574                 self.sendKey(iServiceKeys.keyUser+8)
575
576         def seekBeginning(self):
577                 if self.service:
578                         seekable = self.getSeek()
579                         if seekable:
580                                 seekable.seekTo(0)
581
582         def zapToNumber(self, number):
583                 if self.service:
584                         seekable = self.getSeek()
585                         if seekable:
586                                 print "seek to chapter %d" % number
587                                 seekable.seekChapter(number)
588
589 #       MENU ACTIONS
590         def keyRight(self):
591                 self.sendKey(iServiceKeys.keyRight)
592
593         def keyLeft(self):
594                 self.sendKey(iServiceKeys.keyLeft)
595
596         def keyUp(self):
597                 self.sendKey(iServiceKeys.keyUp)
598
599         def keyDown(self):
600                 self.sendKey(iServiceKeys.keyDown)
601
602         def keyOk(self):
603                 if self.sendKey(iServiceKeys.keyOk) and not self.in_menu:
604                         self.toggleInfo()
605
606         def keyCancel(self):
607                 self.askLeavePlayer()
608
609         def opened(self):
610                 if self.autoplay and self.dvd_filelist:
611                         # opened via autoplay
612                         self.FileBrowserClosed(self.dvd_filelist[0])
613                 elif self.autoplay and self.physicalDVD:
614                         self.playPhysicalCB(True)
615                 elif self.physicalDVD:
616                         # opened from menu with dvd in drive
617                         self.session.openWithCallback(self.playPhysicalCB, MessageBox, text=_("Do you want to play DVD in drive?"), timeout=5 )
618                 else:
619                         # opened from menu without dvd in drive
620                         self.session.openWithCallback(self.FileBrowserClosed, FileBrowser, self.dvd_filelist)
621
622         def playPhysicalCB(self, answer):
623                 if answer == True:
624                         self.FileBrowserClosed(harddiskmanager.getAutofsMountpoint(harddiskmanager.getCD()))
625                 else:
626                         self.session.openWithCallback(self.FileBrowserClosed, FileBrowser)
627
628         def FileBrowserClosed(self, val):
629                 curref = self.session.nav.getCurrentlyPlayingServiceReference()
630                 print "FileBrowserClosed", val
631                 if val is None:
632                         self.askLeavePlayer()
633                 else:
634                         newref = eServiceReference(4369, 0, val)
635                         print "play", newref.toString()
636                         if curref is None or curref != newref:
637                                 if newref.toString().endswith("/VIDEO_TS") or newref.toString().endswith("/"):
638                                         names = newref.toString().rsplit("/",3)
639                                         if names[2].startswith("Disk ") or names[2].startswith("DVD "):
640                                                 name = str(names[1]) + " - " + str(names[2])
641                                         else:
642                                                 name = names[2]
643                                         print "setting name to: ", self.service
644                                         newref.setName(str(name))
645                                 self.session.nav.playService(newref)
646                                 self.service = self.session.nav.getCurrentService()
647                                 print "self.service", self.service
648                                 print "cur_dlg", self.session.current_dialog
649                                 subs = self.getServiceInterface("subtitle")
650                                 if subs:
651                                         subs.enableSubtitles(self.dvdScreen.instance, None)
652
653         def exitCB(self, answer):
654                 if answer is not None:
655                         if answer[1] == "exit":
656                                 if self.service:
657                                         self.service = None
658                                 self.close()
659                         if answer[1] == "browser":
660                                 #TODO check here if a paused dvd playback is already running... then re-start it...
661                                 #else
662                                 if self.service:
663                                         self.service = None
664                                 self.session.openWithCallback(self.FileBrowserClosed, FileBrowser)
665                         if answer[1] == "playPhysical":
666                                 if self.service:
667                                         self.service = None
668                                 self.playPhysicalCB(True)
669                         else:
670                                 pass
671
672         def __onClose(self):
673                 self.restore_infobar_seek_config()
674                 self.session.nav.playService(self.oldService)
675                 from Plugins.SystemPlugins.Hotplug.plugin import hotplugNotifier
676                 hotplugNotifier.remove(self.hotplugCB)
677
678         def playLastCB(self, answer): # overwrite infobar cuesheet function
679                 print "playLastCB", answer, self.resume_point
680                 if self.service:
681                         if answer == True:
682                                 seekable = self.getSeek()
683                                 if seekable:
684                                         seekable.seekTo(self.resume_point)
685                         pause = self.service.pause()
686                         pause.unpause()
687                 self.hideAfterResume()
688
689         def showAfterCuesheetOperation(self):
690                 if not self.in_menu:
691                         self.show()
692
693         def createSummary(self):
694                 return DVDSummary
695
696 #override some InfoBarSeek functions
697         def doEof(self):
698                 self.setSeekState(self.SEEK_STATE_PLAY)
699
700         def calcRemainingTime(self):
701                 return 0
702
703         def hotplugCB(self, dev, media_state):
704                 print "[hotplugCB]", dev, media_state
705                 if dev == harddiskmanager.getCD():
706                         if media_state == "1":
707                                 self.scanHotplug()
708                         else:
709                                 self.physicalDVD = False
710
711         def scanHotplug(self):
712                 devicepath = harddiskmanager.getAutofsMountpoint(harddiskmanager.getCD())
713                 if pathExists(devicepath):
714                         from Components.Scanner import scanDevice
715                         res = scanDevice(devicepath)
716                         list = [ (r.description, r, res[r], self.session) for r in res ]
717                         if list:
718                                 (desc, scanner, files, session) = list[0]
719                                 for file in files:
720                                         print file
721                                         if file.mimetype == "video/x-dvd":
722                                                 print "physical dvd found:", devicepath
723                                                 self.physicalDVD = True
724                                                 return
725                 self.physicalDVD = False
726
727 def main(session, **kwargs):
728         session.open(DVDPlayer)
729
730 def menu(menuid, **kwargs):
731         if menuid == "mainmenu":
732                 return [(_("DVD Player"), main, "dvd_player", 46)]
733         return []
734
735 from Plugins.Plugin import PluginDescriptor
736
737 def filescan_open(list, session, **kwargs):
738         if len(list) == 1 and list[0].mimetype == "video/x-dvd":
739                 splitted = list[0].path.split('/')
740                 print "splitted", splitted
741                 if len(splitted) > 2:
742                         if splitted[1] == 'autofs':
743                                 session.open(DVDPlayer, dvd_device="/dev/%s" %(splitted[2]))
744                                 return
745                         else:
746                                 print "splitted[0]", splitted[1]
747         else:
748                 dvd_filelist = []
749                 for x in list:
750                         if x.mimetype == "video/x-dvd-iso":
751                                 dvd_filelist.append(x.path)
752                         if x.mimetype == "video/x-dvd":
753                                 dvd_filelist.append(x.path.rsplit('/',1)[0])                    
754                 session.open(DVDPlayer, dvd_filelist=dvd_filelist)
755
756 def filescan(**kwargs):
757         from Components.Scanner import Scanner, ScanPath
758
759         # Overwrite checkFile to only detect local
760         class LocalScanner(Scanner):
761                 def checkFile(self, file):
762                         return fileExists(file.path)
763
764         return [
765                 LocalScanner(mimetypes = ["video/x-dvd","video/x-dvd-iso"],
766                         paths_to_scan =
767                                 [
768                                         ScanPath(path = "video_ts", with_subdirs = False),
769                                         ScanPath(path = "VIDEO_TS", with_subdirs = False),
770                                         ScanPath(path = "", with_subdirs = False),
771                                 ],
772                         name = "DVD",
773                         description = _("Play DVD"),
774                         openfnc = filescan_open,
775                 )]              
776
777 def Plugins(**kwargs):
778         return [PluginDescriptor(name = "DVDPlayer", description = "Play DVDs", where = PluginDescriptor.WHERE_MENU, fnc = menu),
779                         PluginDescriptor(where = PluginDescriptor.WHERE_FILESCAN, fnc = filescan)]