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
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.FileList import FileList
11 from Components.MenuList import MenuList
12 from Components.ServiceEventTracker import ServiceEventTracker, InfoBarBase
13 from Components.config import config
14 from Tools.Directories import pathExists, fileExists
16 import servicedvd # load c++ part of dvd player plugin
20 class FileBrowser(Screen):
22 <screen name="FileBrowser" position="100,100" size="520,376" title="DVD File Browser" >
23 <widget name="filelist" position="0,0" size="520,376" scrollbarMode="showOnDemand" />
25 def __init__(self, session, dvd_filelist = None):
26 Screen.__init__(self, session)
29 self.dvd_filelist = dvd_filelist
30 self["filelist"] = MenuList(self.dvd_filelist)
33 self.dvd_filelist = None
35 if lastpath is not None:
36 currDir = lastpath + "/"
38 currDir = "/media/dvd/"
39 if not pathExists(currDir):
42 self.filelist = FileList(currDir, matchingPattern = "(?i)^.*\.(iso)", useServiceRef = True)
43 self["filelist"] = self.filelist
45 self["FilelistActions"] = ActionMap(["OkCancelActions"],
53 print "OK " + self["filelist"].getCurrent()
54 self.close(self["filelist"].getCurrent())
57 filename = self["filelist"].getFilename()
58 if filename is not None:
59 if filename.upper().endswith("VIDEO_TS/"):
60 print "dvd structure found, trying to open..."
61 dvdpath = filename[0:-9]
62 lastpath = (dvdpath.rstrip("/").rsplit("/",1))[0]
63 print "lastpath video_ts/=", lastpath
66 if self["filelist"].canDescent(): # isDir
67 self["filelist"].descent()
68 pathname = self["filelist"].getCurrentDirectory() or ""
69 if fileExists(pathname+"VIDEO_TS.IFO"):
70 print "dvd structure found, trying to open..."
71 lastpath = (pathname.rstrip("/").rsplit("/",1))[0]
72 print "lastpath video_ts.ifo=", lastpath
75 lastpath = filename[0:filename.rfind("/")]
76 print "lastpath directory=", lastpath
82 class DVDSummary(Screen):
84 <screen position="0,0" size="132,64">
85 <widget source="session.CurrentService" render="Label" position="5,4" size="120,28" font="Regular;12" transparent="1" >
86 <convert type="ServiceName">Name</convert>
88 <widget name="DVDPlayer" position="5,30" size="66,16" font="Regular;12" transparent="1" />
89 <widget name="Chapter" position="72,30" size="54,16" font="Regular;12" transparent="1" halign="right" />
90 <widget source="session.CurrentService" render="Label" position="66,46" size="60,18" font="Regular;16" transparent="1" halign="right" >
91 <convert type="ServicePosition">Position</convert>
93 <widget source="session.CurrentService" render="Progress" position="6,46" size="60,18" borderWidth="1" >
94 <convert type="ServicePosition">Position</convert>
98 def __init__(self, session, parent):
99 Screen.__init__(self, session, parent)
101 self["DVDPlayer"] = Label("DVD Player")
102 self["Title"] = Label("")
103 self["Time"] = Label("")
104 self["Chapter"] = Label("")
106 def updateChapter(self, chapter):
107 self["Chapter"].setText(chapter)
109 def setTitle(self, title):
110 self["Title"].setText(title)
112 class DVDOverlay(Screen):
113 skin = """<screen name="DVDOverlay" position="0,0" size="720,576" flags="wfNoBorder" zPosition="-1" backgroundColor="transparent" />"""
114 def __init__(self, session, args = None):
115 Screen.__init__(self, session)
117 class ChapterZap(Screen):
119 <screen name="ChapterZap" position="235,255" size="250,60" title="Chapter" >
120 <widget name="chapter" position="35,15" size="110,25" font="Regular;23" />
121 <widget name="number" position="145,15" size="80,25" halign="right" font="Regular;23" />
130 self.close(int(self["number"].getText()))
132 def keyNumberGlobal(self, number):
133 self.Timer.start(3000, True) #reset timer
134 self.field = self.field + str(number)
135 self["number"].setText(self.field)
136 if len(self.field) >= 4:
139 def __init__(self, session, number):
140 Screen.__init__(self, session)
141 self.field = str(number)
143 self["chapter"] = Label(_("Chapter:"))
145 self["number"] = Label(self.field)
147 self["actions"] = NumberActionMap( [ "SetupActions" ],
151 "1": self.keyNumberGlobal,
152 "2": self.keyNumberGlobal,
153 "3": self.keyNumberGlobal,
154 "4": self.keyNumberGlobal,
155 "5": self.keyNumberGlobal,
156 "6": self.keyNumberGlobal,
157 "7": self.keyNumberGlobal,
158 "8": self.keyNumberGlobal,
159 "9": self.keyNumberGlobal,
160 "0": self.keyNumberGlobal
163 self.Timer = eTimer()
164 self.Timer.callback.append(self.keyOK)
165 self.Timer.start(3000, True)
167 class DVDPlayer(Screen, InfoBarBase, InfoBarNotifications, InfoBarSeek, InfoBarPVRState, InfoBarShowHide, HelpableScreen, InfoBarCueSheetSupport):
168 # ALLOW_SUSPEND = True
169 ENABLE_RESUME_SUPPORT = True
172 <screen name="DVDPlayer" flags="wfNoBorder" position="0,380" size="720,160" title="InfoBar" backgroundColor="transparent" >
174 <ePixmap position="0,0" zPosition="-2" size="720,160" pixmap="skin_default/info-bg_mp.png" alphatest="off" />
175 <ePixmap position="29,40" zPosition="0" size="665,104" pixmap="skin_default/screws_mp.png" alphatest="on" transparent="1" />
176 <!-- colorbuttons -->
177 <ePixmap position="48,70" zPosition="0" size="108,13" pixmap="skin_default/icons/mp_buttons.png" alphatest="on" />
179 <ePixmap pixmap="skin_default/icons/icon_event.png" position="207,78" zPosition="1" size="15,10" alphatest="on" />
180 <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">
181 <convert type="ServiceName">Name</convert>
183 <!-- Chapter info -->
184 <widget name="chapterLabel" position="230,96" size="360,22" font="Regular;20" foregroundColor="#c3c3c9" backgroundColor="#263c59" transparent="1" />
185 <!-- Audio track info -->
186 <ePixmap pixmap="skin_default/icons/icon_dolby.png" position="540,73" zPosition="1" size="26,16" alphatest="on"/>
187 <widget name="audioLabel" position="570,73" size="130,22" font="Regular;18" backgroundColor="#263c59" shadowColor="#1d354c" shadowOffset="-1,-1" transparent="1" />
188 <!-- Subtitle track info -->
189 <widget source="session.CurrentService" render="Pixmap" pixmap="skin_default/icons/icon_txt.png" position="540,96" zPosition="1" size="26,16" alphatest="on" >
190 <convert type="ServiceInfo">HasTelext</convert>
191 <convert type="ConditionalShowHide" />
193 <widget name="subtitleLabel" position="570,96" size="130,22" font="Regular;18" backgroundColor="#263c59" shadowColor="#1d354c" shadowOffset="-1,-1" transparent="1" />
194 <!-- Elapsed time -->
195 <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" >
196 <convert type="ServicePosition">Position,ShowHours</convert>
198 <!-- Progressbar (movie position)-->
199 <widget source="session.CurrentService" render="PositionGauge" position="300,133" size="270,10" zPosition="2" pointer="skin_default/position_pointer.png:540,0" transparent="1" >
200 <convert type="ServicePosition">Gauge</convert>
202 <!-- Remaining time -->
203 <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" >
204 <convert type="ServicePosition">Remaining,Negate,ShowHours</convert>
208 def save_infobar_seek_config(self):
209 self.saved_config_speeds_forward = config.seek.speeds_forward.value
210 self.saved_config_speeds_backward = config.seek.speeds_backward.value
211 self.saved_config_enter_forward = config.seek.enter_forward.value
212 self.saved_config_enter_backward = config.seek.enter_backward.value
213 self.saved_config_seek_stepwise_minspeed = config.seek.stepwise_minspeed.value
214 self.saved_config_seek_stepwise_repeat = config.seek.stepwise_repeat.value
215 self.saved_config_seek_on_pause = config.seek.on_pause.value
216 self.saved_config_seek_speeds_slowmotion = config.seek.speeds_slowmotion.value
218 def change_infobar_seek_config(self):
219 config.seek.speeds_forward.value = [2, 4, 8, 16, 32, 64]
220 config.seek.speeds_backward.value = [8, 16, 32, 64]
221 config.seek.speeds_slowmotion.value = [ ]
222 config.seek.enter_forward.value = "2"
223 config.seek.enter_backward.value = "2"
224 config.seek.stepwise_minspeed.value = "Never"
225 config.seek.stepwise_repeat.value = "3"
226 config.seek.on_pause.value = "play"
228 def restore_infobar_seek_config(self):
229 config.seek.speeds_forward.value = self.saved_config_speeds_forward
230 config.seek.speeds_backward.value = self.saved_config_speeds_backward
231 config.seek.speeds_slowmotion.value = self.saved_config_seek_speeds_slowmotion
232 config.seek.enter_forward.value = self.saved_config_enter_forward
233 config.seek.enter_backward.value = self.saved_config_enter_backward
234 config.seek.stepwise_minspeed.value = self.saved_config_seek_stepwise_minspeed
235 config.seek.stepwise_repeat.value = self.saved_config_seek_stepwise_repeat
236 config.seek.on_pause.value = self.saved_config_seek_on_pause
238 def __init__(self, session, dvd_device = None, dvd_filelist = None, args = None):
239 Screen.__init__(self, session)
240 InfoBarBase.__init__(self)
241 InfoBarNotifications.__init__(self)
242 InfoBarCueSheetSupport.__init__(self, actionmap = "MediaPlayerCueSheetActions")
243 InfoBarShowHide.__init__(self)
244 HelpableScreen.__init__(self)
245 self.save_infobar_seek_config()
246 self.change_infobar_seek_config()
247 InfoBarSeek.__init__(self, useSeekBackHack=False)
248 InfoBarPVRState.__init__(self)
249 self.dvdScreen = self.session.instantiateDialog(DVDOverlay)
251 self.oldService = self.session.nav.getCurrentlyPlayingServiceReference()
252 self.session.nav.stopService()
253 self["audioLabel"] = Label("n/a")
254 self["subtitleLabel"] = Label("")
255 self["chapterLabel"] = Label("")
256 self.last_audioTuple = None
257 self.last_subtitleTuple = None
258 self.totalChapters = 0
259 self.currentChapter = 0
261 self.currentTitle = 0
263 self.__event_tracker = ServiceEventTracker(screen=self, eventmap=
265 iPlayableService.evStopped: self.__serviceStopped,
266 iPlayableService.evUser: self.__timeUpdated,
267 iPlayableService.evUser+1: self.__statePlay,
268 iPlayableService.evUser+2: self.__statePause,
269 iPlayableService.evUser+3: self.__osdFFwdInfoAvail,
270 iPlayableService.evUser+4: self.__osdFBwdInfoAvail,
271 iPlayableService.evUser+5: self.__osdStringAvail,
272 iPlayableService.evUser+6: self.__osdAudioInfoAvail,
273 iPlayableService.evUser+7: self.__osdSubtitleInfoAvail,
274 iPlayableService.evUser+8: self.__chapterUpdated,
275 iPlayableService.evUser+9: self.__titleUpdated,
276 iPlayableService.evUser+11: self.__menuOpened,
277 iPlayableService.evUser+12: self.__menuClosed
280 self["DVDPlayerDirectionActions"] = HelpableActionMap(self, "DirectionActions",
282 #MENU KEY DOWN ACTIONS
283 "left": (self.keyLeft, _("DVD left key")),
284 "right": (self.keyRight, _("DVD right key")),
285 "up": (self.keyUp, _("DVD up key")),
286 "down": (self.keyDown, _("DVD down key")),
288 #MENU KEY REPEATED ACTIONS
289 "leftRepeated": self.doNothing,
290 "rightRepeated": self.doNothing,
291 "upRepeated": self.doNothing,
292 "downRepeated": self.doNothing,
295 "leftUp": self.doNothing,
296 "rightUp": self.doNothing,
297 "upUp": self.doNothing,
298 "downUp": self.doNothing,
301 self["OkCancelActions"] = HelpableActionMap(self, "OkCancelActions",
303 "ok": (self.keyOk, _("DVD ENTER key")),
304 "cancel": self.keyCancel,
307 self["DVDPlayerPlaybackActions"] = HelpableActionMap(self, "DVDPlayerActions",
310 "dvdMenu": (self.enterDVDMenu, _("show DVD main menu")),
311 "toggleInfo": (self.toggleInfo, _("toggle time, chapter, audio, subtitle info")),
312 "nextChapter": (self.nextChapter, _("forward to the next chapter")),
313 "prevChapter": (self.prevChapter, _("rewind to the previous chapter")),
314 "nextTitle": (self.nextTitle, _("jump forward to the next title")),
315 "prevTitle": (self.prevTitle, _("jump back to the previous title")),
316 "tv": (self.askLeavePlayer, _("exit DVD player or return to file browser")),
317 "dvdAudioMenu": (self.enterDVDAudioMenu, _("(show optional DVD audio menu)")),
318 "nextAudioTrack": (self.nextAudioTrack, _("switch to the next audio track")),
319 "nextSubtitleTrack": (self.nextSubtitleTrack, _("switch to the next subtitle language")),
320 "seekBeginning": (self.seekBeginning, _("Jump to video title 1 (play movie from start)")),
323 self["NumberActions"] = NumberActionMap( [ "NumberActions"],
325 "1": self.keyNumberGlobal,
326 "2": self.keyNumberGlobal,
327 "3": self.keyNumberGlobal,
328 "4": self.keyNumberGlobal,
329 "5": self.keyNumberGlobal,
330 "6": self.keyNumberGlobal,
331 "7": self.keyNumberGlobal,
332 "8": self.keyNumberGlobal,
333 "9": self.keyNumberGlobal,
334 "0": self.keyNumberGlobal,
337 self.onClose.append(self.__onClose)
340 self.dvd_device = dvd_device
341 self.physicalDVD = True
343 if fileExists("/dev/cdroms/cdrom0"):
344 print "physical dvd found (/dev/cdroms/cdrom0)"
345 self.dvd_device = "/dev/cdroms/cdrom0"
346 self.physicalDVD = True
348 self.dvd_device = None
349 self.physicalDVD = False
351 self.dvd_filelist = dvd_filelist
352 self.onFirstExecBegin.append(self.showFileBrowser)
355 self.old_aspect = open("/proc/stb/video/aspect", "r").read()
356 self.old_policy = open("/proc/stb/video/policy", "r").read()
357 self.old_wss = open("/proc/stb/denc/0/wss", "r").read()
359 def keyNumberGlobal(self, number):
360 print "You pressed number " + str(number)
361 self.session.openWithCallback(self.numberEntered, ChapterZap, number)
363 def numberEntered(self, retval):
364 # print self.servicelist
366 self.zapToNumber(retval)
368 def __serviceStopped(self):
369 self.dvdScreen.hide()
370 self.service.subtitle().disableSubtitles(self.session.current_dialog.instance)
372 def serviceStarted(self): #override InfoBarShowHide function
373 self.dvdScreen.show()
374 self.service.subtitle().enableSubtitles(self.dvdScreen.instance, None)
376 def doEofInternal(self, playing):
380 def __menuOpened(self):
383 self["NumberActions"].setEnabled(False)
385 def __menuClosed(self):
388 self["NumberActions"].setEnabled(True)
390 def setChapterLabel(self):
392 chapterOSD = "DVD Menu"
393 if self.currentTitle > 0:
394 chapterLCD = "%s %d" % (_("Chap."), self.currentChapter)
395 chapterOSD = "DVD %s %d/%d" % (_("Chapter"), self.currentChapter, self.totalChapters)
396 chapterOSD += " (%s %d/%d)" % (_("Title"), self.currentTitle, self.totalTitles)
397 self["chapterLabel"].setText(chapterOSD)
399 self.session.summary.updateChapter(chapterLCD)
406 def toggleInfo(self):
411 def __timeUpdated(self):
414 def __statePlay(self):
417 def __statePause(self):
420 def __osdFFwdInfoAvail(self):
421 self.setChapterLabel()
422 print "FFwdInfoAvail"
424 def __osdFBwdInfoAvail(self):
425 self.setChapterLabel()
426 print "FBwdInfoAvail"
428 def __osdStringAvail(self):
431 def __osdAudioInfoAvail(self):
432 audioTuple = self.service.info().getInfoObject(iServiceInformation.sUser+6)
433 print "AudioInfoAvail ", repr(audioTuple)
434 audioString = "%d: %s (%s)" % (audioTuple[0],audioTuple[1],audioTuple[2])
435 self["audioLabel"].setText(audioString)
436 if audioTuple != self.last_audioTuple and not self.in_menu:
438 self.last_audioTuple = audioTuple
440 def __osdSubtitleInfoAvail(self):
441 subtitleTuple = self.service.info().getInfoObject(iServiceInformation.sUser+7)
442 print "SubtitleInfoAvail ", repr(subtitleTuple)
444 if subtitleTuple[0] is not 0:
445 subtitleString = "%d: %s" % (subtitleTuple[0],subtitleTuple[1])
446 self["subtitleLabel"].setText(subtitleString)
447 if subtitleTuple != self.last_subtitleTuple and not self.in_menu:
449 self.last_subtitleTuple = subtitleTuple
451 def __chapterUpdated(self):
452 self.currentChapter = self.service.info().getInfo(iServiceInformation.sCurrentChapter)
453 self.totalChapters = self.service.info().getInfo(iServiceInformation.sTotalChapters)
454 self.setChapterLabel()
455 print "__chapterUpdated: %d/%d" % (self.currentChapter, self.totalChapters)
457 def __titleUpdated(self):
458 self.currentTitle = self.service.info().getInfo(iServiceInformation.sCurrentTitle)
459 self.totalTitles = self.service.info().getInfo(iServiceInformation.sTotalTitles)
460 self.setChapterLabel()
461 print "__titleUpdated: %d/%d" % (self.currentTitle, self.totalTitles)
465 def askLeavePlayer(self):
466 choices = [(_("Continue playing"), "play"), (_("Exit"), "exit")]
467 if not self.physicalDVD:
468 choices.insert(1,(_("Return to file browser"), "browser"))
469 self.session.openWithCallback(self.exitCB, ChoiceBox, title=_("Leave DVD Player?"), list = choices)
471 def nextAudioTrack(self):
473 self.service.keys().keyPressed(iServiceKeys.keyUser)
475 def nextSubtitleTrack(self):
477 self.service.keys().keyPressed(iServiceKeys.keyUser+1)
479 def enterDVDAudioMenu(self):
481 self.service.keys().keyPressed(iServiceKeys.keyUser+2)
483 def nextChapter(self):
485 self.service.keys().keyPressed(iServiceKeys.keyUser+3)
487 def prevChapter(self):
489 self.service.keys().keyPressed(iServiceKeys.keyUser+4)
493 self.service.keys().keyPressed(iServiceKeys.keyUser+5)
497 self.service.keys().keyPressed(iServiceKeys.keyUser+6)
499 def enterDVDMenu(self):
501 self.service.keys().keyPressed(iServiceKeys.keyUser+7)
503 def seekBeginning(self):
505 seekable = self.getSeek()
506 if seekable is not None:
509 def zapToNumber(self, number):
511 seekable = self.getSeek()
512 if seekable is not None:
513 print "seek to chapter %d" % number
514 seekable.seekChapter(number)
519 self.service.keys().keyPressed(iServiceKeys.keyRight)
523 self.service.keys().keyPressed(iServiceKeys.keyLeft)
527 self.service.keys().keyPressed(iServiceKeys.keyUp)
531 self.service.keys().keyPressed(iServiceKeys.keyDown)
537 self.service.keys().keyPressed(iServiceKeys.keyOk)
540 self.askLeavePlayer()
542 def showFileBrowser(self):
544 if self.dvd_device == "/dev/cdroms/cdrom0":
545 self.session.openWithCallback(self.DVDdriveCB, MessageBox, text=_("Do you want to play DVD in drive?"), timeout=5 )
547 self.DVDdriveCB(True)
549 self.session.openWithCallback(self.FileBrowserClosed, FileBrowser, self.dvd_filelist)
551 def DVDdriveCB(self, answer):
553 self.FileBrowserClosed(self.dvd_device)
555 self.session.openWithCallback(self.FileBrowserClosed, FileBrowser)
556 self.physicalDVD = False
558 def FileBrowserClosed(self, val):
559 curref = self.session.nav.getCurrentlyPlayingServiceReference()
560 print "FileBrowserClosed", val
562 self.askLeavePlayer()
564 newref = eServiceReference(4369, 0, val)
565 print "play", newref.toString()
566 if curref is None or curref != newref:
567 self.session.nav.playService(newref)
568 self.service = self.session.nav.getCurrentService()
569 print "self.service", self.service
570 print "cur_dlg", self.session.current_dialog
572 def exitCB(self, answer):
573 if answer is not None:
574 if answer[1] == "exit":
578 if answer[1] == "browser":
579 #TODO check here if a paused dvd playback is already running... then re-start it...
583 self.showFileBrowser()
588 for i in (("/proc/stb/video/aspect", self.old_aspect), ("/proc/stb/video/policy", self.old_policy), ("/proc/stb/denc/0/wss", self.old_wss)):
590 open(i[0], "w").write(i[1])
592 print "restore", i[0], "failed"
593 self.restore_infobar_seek_config()
594 self.session.nav.playService(self.oldService)
596 def playLastCB(self, answer): # overwrite infobar cuesheet function
597 print "playLastCB", answer, self.resume_point
599 seek = self.service.seek()
601 seek.seekTo(self.resume_point)
602 pause = self.service.pause()
604 self.hideAfterResume()
606 def showAfterCuesheetOperation(self):
610 def createSummary(self):
613 #override some InfoBarSeek functions
615 self.setSeekState(self.SEEK_STATE_PLAY)
617 def calcRemainingTime(self):
620 def main(session, **kwargs):
621 session.open(DVDPlayer)
623 def menu(menuid, **kwargs):
624 if menuid == "mainmenu":
625 return [(_("DVD Player"), main, "dvd_player", 46)]
628 from Plugins.Plugin import PluginDescriptor
630 def filescan_open(list, session, **kwargs):
631 if len(list) == 1 and list[0].mimetype == "video/x-dvd":
632 splitted = list[0].path.split('/')
633 print "splitted", splitted
634 if len(splitted) > 2:
635 if splitted[1] == 'autofs':
636 session.open(DVDPlayer, dvd_device="/dev/%s" %(splitted[2]))
639 print "splitted[0]", splitted[1]
643 if x.mimetype == "video/x-dvd-iso":
644 dvd_filelist.append(x.path)
645 if x.mimetype == "video/x-dvd":
646 dvd_filelist.append(x.path.rsplit('/',1)[0])
647 session.open(DVDPlayer, dvd_filelist=dvd_filelist)
649 def filescan(**kwargs):
650 from Components.Scanner import Scanner, ScanPath
652 # Overwrite checkFile to only detect local
653 class LocalScanner(Scanner):
654 def checkFile(self, file):
655 return fileExists(file.path)
658 LocalScanner(mimetypes = ["video/x-dvd","video/x-dvd-iso"],
661 ScanPath(path = "video_ts", with_subdirs = False),
662 ScanPath(path = "", with_subdirs = False),
665 description = "Play DVD",
666 openfnc = filescan_open,
669 def Plugins(**kwargs):
670 return [PluginDescriptor(name = "DVDPlayer", description = "Play DVDs", where = PluginDescriptor.WHERE_MENU, fnc = menu),
671 PluginDescriptor(where = PluginDescriptor.WHERE_FILESCAN, fnc = filescan)]