a44d042bf9b973a12d53d6d388503575ee41424b
[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 time import strftime
3 from enigma import eTimer, iPlayableService, eServiceCenter, iServiceInformation, eServiceReference, iServiceKeys
4 from Screens.Screen import Screen
5 from Screens.MessageBox import MessageBox
6 from Screens.ChoiceBox import ChoiceBox
7 from Screens.InputBox import InputBox
8 from Screens.HelpMenu import HelpableScreen
9 from Screens.InfoBarGenerics import InfoBarSeek, InfoBarPVRState, InfoBarCueSheetSupport, InfoBarShowHide, InfoBarNotifications
10 from Components.ActionMap import ActionMap, NumberActionMap, HelpableActionMap
11 from Components.Label import Label
12 from Components.FileList import FileList
13 from Components.ServiceEventTracker import ServiceEventTracker, InfoBarBase
14 from Components.config import config
15 from Components.ProgressBar import ProgressBar
16 from ServiceReference import ServiceReference
17 from Tools.Directories import pathExists, fileExists
18
19 import random
20 import servicedvd # load c++ part of dvd player plugin
21
22 lastpath = ""
23
24 class FileBrowser(Screen):
25         skin = """
26         <screen name="FileBrowser" position="100,100" size="520,376" title="DVD File Browser" >
27                 <widget name="filelist" position="0,0" size="520,376" scrollbarMode="showOnDemand" />
28         </screen>"""
29         def __init__(self, session):
30                 Screen.__init__(self, session)
31                 global lastpath
32                 if lastpath is not None:
33                         currDir = lastpath + "/"
34                 else:
35                         currDir = "/media/dvd/"
36                 if not pathExists(currDir):
37                         currDir = "/"
38                 #else:
39                         #print system("mount "+currDir)
40                 self.filelist = FileList(currDir, matchingPattern = "(?i)^.*\.(iso)", useServiceRef = True)
41                 self["filelist"] = self.filelist
42
43                 self["FilelistActions"] = ActionMap(["OkCancelActions"],
44                         {
45                                 "ok": self.ok,
46                                 "cancel": self.exit
47                         })
48
49         def ok(self):
50                 global lastpath
51                 filename = self["filelist"].getFilename()
52                 if filename is not None:
53                         lastpath = filename[0:filename.rfind("/")]
54                         if filename.upper().endswith("VIDEO_TS/"):
55                                 print "dvd structure found, trying to open..."
56                                 self.close(filename[0:-9])
57                 if self["filelist"].canDescent(): # isDir
58                         self["filelist"].descent()
59                 else:
60                         self.close(filename)
61
62         def exit(self):
63                 self.close(None)
64                 
65 class DVDSummary(Screen):
66         skin = """
67         <screen position="0,0" size="132,64">
68                 <widget source="session.CurrentService" render="Label" position="5,4" size="120,28" font="Regular;12" transparent="1" >
69                         <convert type="ServiceName">Name</convert>
70                 </widget>
71                 <widget name="DVDPlayer" position="5,30" size="66,16" font="Regular;12" transparent="1" />
72                 <widget name="Chapter" position="72,30" size="54,16" font="Regular;12" transparent="1" halign="right" />
73                 <widget source="session.CurrentService" render="Label" position="66,46" size="60,18" font="Regular;16" transparent="1" halign="right" >
74                         <convert type="ServicePosition">Position</convert>
75                 </widget>
76                 <widget source="session.CurrentService" render="Progress" position="6,46" size="60,18" borderWidth="1" >
77                         <convert type="ServicePosition">Position</convert>
78                 </widget>
79         </screen>"""
80
81         def __init__(self, session, parent):
82                 Screen.__init__(self, session, parent)
83
84                 self["DVDPlayer"] = Label("DVD Player")
85                 self["Title"] = Label("")
86                 self["Time"] = Label("")
87                 self["Chapter"] = Label("")
88
89         def updateChapter(self, chapter):
90                 self["Chapter"].setText(chapter)
91
92         def setTitle(self, title):
93                 self["Title"].setText(title)
94
95 class DVDOverlay(Screen):
96         skin = """<screen name="DVDOverlay" position="0,0" size="720,576" flags="wfNoBorder" zPosition="-1" backgroundColor="transparent" />"""
97         def __init__(self, session, args = None):
98                 Screen.__init__(self, session)
99                 
100 class ChapterZap(Screen):
101         skin = """
102         <screen name="ChapterZap" position="235,255" size="250,60" title="Chapter" >
103                 <widget name="chapter" position="35,15" size="110,25" font="Regular;23" />
104                 <widget name="number" position="145,15" size="80,25" halign="right" font="Regular;23" />
105         </screen>"""
106         
107         def quit(self):
108                 self.Timer.stop()
109                 self.close(0)
110
111         def keyOK(self):
112                 self.Timer.stop()
113                 self.close(int(self["number"].getText()))
114
115         def keyNumberGlobal(self, number):
116                 self.Timer.start(3000, True)            #reset timer
117                 self.field = self.field + str(number)
118                 self["number"].setText(self.field)
119                 if len(self.field) >= 4:
120                         self.keyOK()
121
122         def __init__(self, session, number):
123                 Screen.__init__(self, session)
124                 self.field = str(number)
125
126                 self["chapter"] = Label(_("Chapter:"))
127
128                 self["number"] = Label(self.field)
129
130                 self["actions"] = NumberActionMap( [ "SetupActions" ],
131                         {
132                                 "cancel": self.quit,
133                                 "ok": self.keyOK,
134                                 "1": self.keyNumberGlobal,
135                                 "2": self.keyNumberGlobal,
136                                 "3": self.keyNumberGlobal,
137                                 "4": self.keyNumberGlobal,
138                                 "5": self.keyNumberGlobal,
139                                 "6": self.keyNumberGlobal,
140                                 "7": self.keyNumberGlobal,
141                                 "8": self.keyNumberGlobal,
142                                 "9": self.keyNumberGlobal,
143                                 "0": self.keyNumberGlobal
144                         })
145
146                 self.Timer = eTimer()
147                 self.Timer.callback.append(self.keyOK)
148                 self.Timer.start(3000, True)
149
150 class DVDPlayer(Screen, InfoBarBase, InfoBarNotifications, InfoBarSeek, InfoBarCueSheetSupport, InfoBarPVRState, InfoBarShowHide, HelpableScreen):
151         ALLOW_SUSPEND = True
152         ENABLE_RESUME_SUPPORT = True
153         
154         skin = """
155         <screen name="DVDPlayer" flags="wfNoBorder" position="0,380" size="720,160" title="InfoBar" backgroundColor="transparent" >
156                 <!-- Background -->
157                 <ePixmap position="0,0" zPosition="-2" size="720,160" pixmap="skin_default/info-bg_mp.png" alphatest="off" />
158                 <ePixmap position="29,40" zPosition="0" size="665,104" pixmap="skin_default/screws_mp.png" alphatest="on" transparent="1" />
159                 <!-- colorbuttons -->
160                 <ePixmap position="48,70" zPosition="0" size="108,13" pixmap="skin_default/icons/mp_buttons.png" alphatest="on" />
161                 <!-- Servicename -->
162                 <ePixmap pixmap="skin_default/icons/icon_event.png" position="207,78" zPosition="1" size="15,10" alphatest="on" />
163                 <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">
164                         <convert type="ServiceName">Name</convert>
165                 </widget>
166                 <!-- Chapter info -->
167                 <widget name="chapterLabel" position="230,96" size="360,22" font="Regular;20" foregroundColor="#c3c3c9" backgroundColor="#263c59" transparent="1" />
168                 <!-- Audio track info -->
169                 <ePixmap pixmap="skin_default/icons/icon_dolby.png" position="540,73" zPosition="1" size="26,16" alphatest="on"/>
170                 <widget name="audioLabel" position="570,73" size="130,22" font="Regular;18" backgroundColor="#263c59" shadowColor="#1d354c" shadowOffset="-1,-1" transparent="1" />
171                 <!-- Subtitle track info -->
172                 <widget source="session.CurrentService" render="Pixmap" pixmap="skin_default/icons/icon_txt.png" position="540,96" zPosition="1" size="26,16" alphatest="on" >
173                         <convert type="ServiceInfo">HasTelext</convert>
174                         <convert type="ConditionalShowHide" />
175                 </widget>
176                 <widget name="subtitleLabel" position="570,96" size="130,22" font="Regular;18" backgroundColor="#263c59" shadowColor="#1d354c" shadowOffset="-1,-1" transparent="1" />
177                 <!-- Elapsed time -->
178                 <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" >
179                         <convert type="ServicePosition">Position,ShowHours</convert>
180                 </widget>
181                 <!-- Progressbar (movie position)-->
182                 <widget source="session.CurrentService" render="PositionGauge" position="300,133" size="270,10" zPosition="2" pointer="skin_default/position_pointer.png:540,0" transparent="1" >
183                         <convert type="ServicePosition">Gauge</convert>
184                 </widget>
185                 <!-- Remaining time -->
186                 <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" >
187                         <convert type="ServicePosition">Remaining,Negate,ShowHours</convert>
188                 </widget>
189         </screen>"""
190
191         def save_infobar_seek_config(self):
192                 self.saved_config_speeds_forward = config.seek.speeds_forward.value
193                 self.saved_config_speeds_backward = config.seek.speeds_backward.value
194                 self.saved_config_enter_forward = config.seek.enter_forward.value
195                 self.saved_config_enter_backward = config.seek.enter_backward.value
196                 self.saved_config_seek_stepwise_minspeed = config.seek.stepwise_minspeed.value
197                 self.saved_config_seek_stepwise_repeat = config.seek.stepwise_repeat.value
198                 self.saved_config_seek_on_pause = config.seek.on_pause.value
199                 self.saved_config_seek_speeds_slowmotion = config.seek.speeds_slowmotion.value
200
201         def change_infobar_seek_config(self):
202                 config.seek.speeds_forward.value = [2, 4, 8, 16, 32, 64]
203                 config.seek.speeds_backward.value = [8, 16, 32, 64]
204                 config.seek.speeds_slowmotion.value = [ ]
205                 config.seek.enter_forward.value = "2"
206                 config.seek.enter_backward.value = "2"
207                 config.seek.stepwise_minspeed.value = "Never"
208                 config.seek.stepwise_repeat.value = "3"
209                 config.seek.on_pause.value = "play"
210
211         def restore_infobar_seek_config(self):
212                 config.seek.speeds_forward.value = self.saved_config_speeds_forward
213                 config.seek.speeds_backward.value = self.saved_config_speeds_backward
214                 config.seek.speeds_slowmotion.value = self.saved_config_seek_speeds_slowmotion
215                 config.seek.enter_forward.value = self.saved_config_enter_forward
216                 config.seek.enter_backward.value = self.saved_config_enter_backward
217                 config.seek.stepwise_minspeed.value = self.saved_config_seek_stepwise_minspeed
218                 config.seek.stepwise_repeat.value = self.saved_config_seek_stepwise_repeat
219                 config.seek.on_pause.value = self.saved_config_seek_on_pause
220
221         def __init__(self, session, args = None):
222                 Screen.__init__(self, session)
223                 InfoBarBase.__init__(self)
224                 InfoBarNotifications.__init__(self)
225                 InfoBarCueSheetSupport.__init__(self, actionmap = "MediaPlayerCueSheetActions")
226                 InfoBarShowHide.__init__(self)
227                 HelpableScreen.__init__(self)
228                 self.save_infobar_seek_config()
229                 self.change_infobar_seek_config()
230                 InfoBarSeek.__init__(self)
231                 InfoBarPVRState.__init__(self)
232                 self.dvdScreen = self.session.instantiateDialog(DVDOverlay)
233
234                 self.oldService = self.session.nav.getCurrentlyPlayingServiceReference()
235                 self.session.nav.stopService()
236                 self["audioLabel"] = Label("1")
237                 self["subtitleLabel"] = Label("")
238                 self["chapterLabel"] = Label("")
239                 self.totalChapters = 0
240                 self.currentChapter = 0
241                 self.totalTitles = 0
242                 self.currentTitle = 0
243
244                 self.__event_tracker = ServiceEventTracker(screen=self, eventmap=
245                         {
246                                 iPlayableService.evUser: self.__timeUpdated,
247                                 iPlayableService.evUser+1: self.__statePlay,
248                                 iPlayableService.evUser+2: self.__statePause,
249                                 iPlayableService.evUser+3: self.__osdFFwdInfoAvail,
250                                 iPlayableService.evUser+4: self.__osdFBwdInfoAvail,
251                                 iPlayableService.evUser+5: self.__osdStringAvail,
252                                 iPlayableService.evUser+6: self.__osdAudioInfoAvail,
253                                 iPlayableService.evUser+7: self.__osdSubtitleInfoAvail,
254                                 iPlayableService.evUser+8: self.__chapterUpdated,
255                                 iPlayableService.evUser+9: self.__titleUpdated,
256                                 #iPlayableService.evUser+10: self.__initializeDVDinfo,
257                                 iPlayableService.evUser+11: self.__menuOpened,
258                                 iPlayableService.evUser+12: self.__menuClosed
259                         })
260
261                 self["DVDPlayerDirectionActions"] = HelpableActionMap(self, "DirectionActions",
262                         {
263                                 #MENU KEY DOWN ACTIONS
264                                 "left": (self.keyLeft, _("DVD left key")),
265                                 "right": (self.keyRight, _("DVD right key")),
266                                 "up": (self.keyUp, _("DVD up key")),
267                                 "down": (self.keyDown, _("DVD down key")),
268
269                                 #MENU KEY REPEATED ACTIONS
270                                 "leftRepeated": self.doNothing,
271                                 "rightRepeated": self.doNothing,
272                                 "upRepeated": self.doNothing,
273                                 "downRepeated": self.doNothing,
274
275                                 #MENU KEY UP ACTIONS
276                                 "leftUp": self.doNothing,
277                                 "rightUp": self.doNothing,
278                                 "upUp": self.doNothing,
279                                 "downUp": self.doNothing,
280                         }, -2)
281
282                 self["OkCancelActions"] = HelpableActionMap(self, "OkCancelActions",
283                         {
284                                 "ok": (self.keyOk, _("DVD ENTER key")),
285                                 "cancel": self.keyCancel,
286                         }, -2)
287
288                 self["DVDPlayerPlaybackActions"] = HelpableActionMap(self, "DVDPlayerActions",
289                         {
290                                 #PLAYER ACTIONS
291                                 "dvdMenu": (self.enterDVDMenu, _("show DVD main menu")),
292                                 "toggleInfo": (self.toggleInfo, _("toggle time, chapter, audio, subtitle info")),
293                                 "nextChapter": (self.nextChapter, _("forward to the next chapter")),
294                                 "prevChapter": (self.prevChapter, _("rewind to the previous chapter")),
295                                 "nextTitle": (self.nextTitle, _("jump forward to the next title")),
296                                 "prevTitle": (self.prevTitle, _("jump back to the previous title")),
297                                 "tv": (self.askLeavePlayer, _("exit DVD player or return to file browser")),
298                                 "dvdAudioMenu": (self.enterDVDAudioMenu, _("(show optional DVD audio menu)")),
299                                 "nextAudioTrack": (self.nextAudioTrack, _("switch to the next audio track")),
300                                 "nextSubtitleTrack": (self.nextSubtitleTrack, _("switch to the next subtitle language")),
301                                 "seekBeginning": (self.seekBeginning, _("Jump to video title 1 (play movie from start)")),
302                         }, -2)
303                         
304                 self["NumberActions"] = NumberActionMap( [ "NumberActions"],
305                         {
306                                 "1": self.keyNumberGlobal,
307                                 "2": self.keyNumberGlobal,
308                                 "3": self.keyNumberGlobal,
309                                 "4": self.keyNumberGlobal,
310                                 "5": self.keyNumberGlobal,
311                                 "6": self.keyNumberGlobal,
312                                 "7": self.keyNumberGlobal,
313                                 "8": self.keyNumberGlobal,
314                                 "9": self.keyNumberGlobal,
315                                 "0": self.keyNumberGlobal,
316                         })
317
318                 self.onClose.append(self.__onClose)
319                 self.onFirstExecBegin.append(self.showFileBrowser)
320                 self.service = None
321                 self.in_menu = False
322                 
323         def keyNumberGlobal(self, number):
324                 print "You pressed number " + str(number)
325                 self.session.openWithCallback(self.numberEntered, ChapterZap, number)
326
327         def numberEntered(self, retval):
328 #               print self.servicelist
329                 if retval > 0:
330                         self.zapToNumber(retval)
331
332         def serviceStarted(self): #override InfoBarShowHide function
333                 pass
334
335         def doEofInternal(self, playing):
336                 if self.in_menu:
337                         self.hide()
338
339         def __menuOpened(self):
340                 self.hide()
341                 self.in_menu = True
342                 self["NumberActions"].setEnabled(False)
343
344         def __menuClosed(self):
345                 self.show()
346                 self.in_menu = False
347                 self["NumberActions"].setEnabled(True)
348
349         def setChapterLabel(self):
350                 chapterLCD = "Menu"
351                 chapterOSD = "DVD Menu"
352                 if self.currentTitle > 0:
353                         chapterLCD = "%s %d" % (_("Chap."), self.currentChapter)
354                         chapterOSD = "DVD %s %d/%d" % (_("Chapter"), self.currentChapter, self.totalChapters)
355                         chapterOSD += " (%s %d/%d)" % (_("Title"), self.currentTitle, self.totalTitles)
356                 self["chapterLabel"].setText(chapterOSD)
357                 try:
358                         self.session.summary.updateChapter(chapterLCD)
359                 except:
360                         pass
361
362         def doNothing(self):
363                 pass
364
365         def toggleInfo(self):
366                 if not self.in_menu:
367                         self.toggleShow()
368                         print "toggleInfo"
369
370         def __timeUpdated(self):
371                 print "timeUpdated"
372
373         def __statePlay(self):
374                 print "statePlay"
375
376         def __statePause(self):
377                 print "statePause"
378
379         def __osdFFwdInfoAvail(self):
380                 self.setChapterLabel()
381                 print "FFwdInfoAvail"
382
383         def __osdFBwdInfoAvail(self):
384                 self.setChapterLabel()
385                 print "FBwdInfoAvail"
386
387         def __osdStringAvail(self):
388                 print "StringAvail"
389
390         def __osdAudioInfoAvail(self):
391                 audioString = self.service.info().getInfoString(iPlayableService.evUser+6)
392                 print "AudioInfoAvail "+audioString
393                 self["audioLabel"].setText(audioString)
394                 if not self.in_menu:
395                         self.doShow()
396
397         def __osdSubtitleInfoAvail(self):
398                 subtitleString = self.service.info().getInfoString(iPlayableService.evUser+7)
399                 print "SubtitleInfoAvail "+subtitleString
400                 self["subtitleLabel"].setText(subtitleString)
401                 if not self.in_menu:
402                         self.doShow()
403
404         def __chapterUpdated(self):
405                 self.currentChapter = self.service.info().getInfo(iPlayableService.evUser+8)
406                 self.totalChapters = self.service.info().getInfo(iPlayableService.evUser+80)
407                 self.setChapterLabel()
408                 print "__chapterUpdated: %d/%d" % (self.currentChapter, self.totalChapters)
409
410         def __titleUpdated(self):
411                 self.currentTitle = self.service.info().getInfo(iPlayableService.evUser+9)
412                 self.totalTitles = self.service.info().getInfo(iPlayableService.evUser+90)
413                 self.setChapterLabel()
414                 print "__titleUpdated: %d/%d" % (self.currentTitle, self.totalTitles)
415                 if not self.in_menu:
416                         self.doShow()
417                 
418         #def __initializeDVDinfo(self):
419                 #self.__osdAudioInfoAvail()
420                 #self.__osdSubtitleInfoAvail()
421
422         def askLeavePlayer(self):
423                 self.session.openWithCallback(self.exitCB, ChoiceBox, title=_("Leave DVD Player?"), list=[(_("Exit"), "exit"), (_("Return to file browser"), "browser"), (_("Continue playing"), "play")])
424
425         def nextAudioTrack(self):
426                 if self.service:
427                         self.service.keys().keyPressed(iServiceKeys.keyUser)
428
429         def nextSubtitleTrack(self):
430                 if self.service:
431                         self.service.keys().keyPressed(iServiceKeys.keyUser+1)
432
433         def enterDVDAudioMenu(self):
434                 if self.service:
435                         self.service.keys().keyPressed(iServiceKeys.keyUser+2)
436
437         def nextChapter(self):
438                 if self.service:
439                         self.service.keys().keyPressed(iServiceKeys.keyUser+3)
440
441         def prevChapter(self):
442                 if self.service:
443                         self.service.keys().keyPressed(iServiceKeys.keyUser+4)
444
445         def nextTitle(self):
446                 if self.service:
447                         self.service.keys().keyPressed(iServiceKeys.keyUser+5)
448
449         def prevTitle(self):
450                 if self.service:
451                         self.service.keys().keyPressed(iServiceKeys.keyUser+6)
452
453         def enterDVDMenu(self):
454                 if self.service:
455                         self.service.keys().keyPressed(iServiceKeys.keyUser+7)
456                         
457         def seekBeginning(self):
458                 if self.service:
459                         seekable = self.getSeek()
460                         if seekable is not None:
461                                 seekable.seekTo(0)
462                                 
463         def zapToNumber(self, number):
464                 if self.service:
465                         seekable = self.getSeek()
466                         if seekable is not None:
467                                 print "seek to chapter %d" % number
468                                 seekable.seekChapter(number)
469
470 #       MENU ACTIONS
471         def keyRight(self):
472                 if self.service:
473                         self.service.keys().keyPressed(iServiceKeys.keyRight)
474
475         def keyLeft(self):
476                 if self.service:
477                         self.service.keys().keyPressed(iServiceKeys.keyLeft)
478
479         def keyUp(self):
480                 if self.service:
481                         self.service.keys().keyPressed(iServiceKeys.keyUp)
482
483         def keyDown(self):
484                 if self.service:
485                         self.service.keys().keyPressed(iServiceKeys.keyDown)
486
487         def keyOk(self):
488                 if self.service:
489                         if not self.in_menu:
490                                 self.toggleInfo()
491                         self.service.keys().keyPressed(iServiceKeys.keyOk)
492
493         def keyCancel(self):
494                 self.askLeavePlayer()
495
496         def showFileBrowser(self):
497                 self.session.openWithCallback(self.FileBrowserClosed, FileBrowser)
498
499         def FileBrowserClosed(self, val):
500                 curref = self.session.nav.getCurrentlyPlayingServiceReference()
501                 print "FileBrowserClosed", val
502                 if val is None:
503                         self.askLeavePlayer()
504                 else:
505                         newref = eServiceReference(4369, 0, val)
506                         print "play", newref.toString()
507                         if curref is None or curref != newref:
508                                 self.session.nav.playService(newref)
509                                 self.service = self.session.nav.getCurrentService()
510                                 print "self.service", self.service
511                                 print "cur_dlg", self.session.current_dialog
512                                 self.dvdScreen.show()
513                                 self.service.subtitle().enableSubtitles(self.dvdScreen.instance, None)
514
515         def exitCB(self, answer):
516                 if answer is not None:
517                         if answer[1] == "exit":
518                                 if self.service:
519                                         self.dvdScreen.hide()
520                                         self.service.subtitle().disableSubtitles(self.session.current_dialog.instance)
521                                         self.service = None
522                                 self.close()
523                         if answer[1] == "browser":
524                                 #TODO check here if a paused dvd playback is already running... then re-start it...
525                                 #else
526                                 self.showFileBrowser()
527                         else:
528                                 pass
529
530         def __onClose(self):
531                 self.restore_infobar_seek_config()
532                 self.session.nav.playService(self.oldService)
533
534         def playLastCB(self, answer): # overwrite infobar cuesheet function
535                 print "playLastCB", answer, self.resume_point
536 #               if answer == True:
537 #                       self.doSeek(self.resume_point)
538                 self.hideAfterResume()
539
540         def showAfterCuesheetOperation(self):
541                 if not self.in_menu:
542                         self.show()
543
544         def createSummary(self):
545                 print "DVDCreateSummary"
546                 return DVDSummary
547
548 #override some InfoBarSeek functions
549         def doEof(self):
550                 self.setSeekState(self.SEEK_STATE_PLAY)
551
552         def calcRemainingTime(self):
553                 return 0
554
555 def main(session, **kwargs):
556         session.open(DVDPlayer)
557
558 def menu(menuid, **kwargs):
559         if menuid == "mainmenu":
560                 return [(_("DVD Player"), main, "dvd_player", 46)]
561         return []
562
563 from Plugins.Plugin import PluginDescriptor
564 def Plugins(**kwargs):
565         return [PluginDescriptor(name = "DVDPlayer", description = "Play DVDs", where = PluginDescriptor.WHERE_MENU, fnc = menu)]