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 = [ ]):
26 Screen.__init__(self, session)
28 self.dvd_filelist = dvd_filelist
30 self["filelist"] = MenuList(self.dvd_filelist)
33 if lastpath is not None:
34 currDir = lastpath + "/"
36 currDir = "/media/dvd/"
37 if not pathExists(currDir):
40 self.filelist = FileList(currDir, matchingPattern = "(?i)^.*\.(iso)", useServiceRef = True)
41 self["filelist"] = self.filelist
43 self["FilelistActions"] = ActionMap(["OkCancelActions"],
50 if len(self.dvd_filelist):
51 print "OK " + self["filelist"].getCurrent()
52 self.close(self["filelist"].getCurrent())
55 filename = self["filelist"].getFilename()
56 if filename is not None:
57 if filename.upper().endswith("VIDEO_TS/"):
58 print "dvd structure found, trying to open..."
59 dvdpath = filename[0:-9]
60 lastpath = (dvdpath.rstrip("/").rsplit("/",1))[0]
61 print "lastpath video_ts/=", lastpath
64 if self["filelist"].canDescent(): # isDir
65 self["filelist"].descent()
66 pathname = self["filelist"].getCurrentDirectory() or ""
67 if fileExists(pathname+"VIDEO_TS.IFO"):
68 print "dvd structure found, trying to open..."
69 lastpath = (pathname.rstrip("/").rsplit("/",1))[0]
70 print "lastpath video_ts.ifo=", lastpath
73 lastpath = filename[0:filename.rfind("/")]
74 print "lastpath directory=", lastpath
80 class DVDSummary(Screen):
82 <screen position="0,0" size="132,64">
83 <widget source="session.CurrentService" render="Label" position="5,4" size="120,28" font="Regular;12" transparent="1" >
84 <convert type="ServiceName">Name</convert>
86 <widget name="DVDPlayer" position="5,30" size="66,16" font="Regular;12" transparent="1" />
87 <widget name="Chapter" position="72,30" size="54,16" font="Regular;12" transparent="1" halign="right" />
88 <widget source="session.CurrentService" render="Label" position="66,46" size="60,18" font="Regular;16" transparent="1" halign="right" >
89 <convert type="ServicePosition">Position</convert>
91 <widget source="session.CurrentService" render="Progress" position="6,46" size="60,18" borderWidth="1" >
92 <convert type="ServicePosition">Position</convert>
96 def __init__(self, session, parent):
97 Screen.__init__(self, session, parent)
99 self["DVDPlayer"] = Label("DVD Player")
100 self["Title"] = Label("")
101 self["Time"] = Label("")
102 self["Chapter"] = Label("")
104 def updateChapter(self, chapter):
105 self["Chapter"].setText(chapter)
107 def setTitle(self, title):
108 self["Title"].setText(title)
110 class DVDOverlay(Screen):
111 skin = """<screen name="DVDOverlay" position="0,0" size="720,576" flags="wfNoBorder" zPosition="-1" backgroundColor="transparent" />"""
112 def __init__(self, session, args = None):
113 Screen.__init__(self, session)
115 class ChapterZap(Screen):
117 <screen name="ChapterZap" position="235,255" size="250,60" title="Chapter" >
118 <widget name="chapter" position="35,15" size="110,25" font="Regular;23" />
119 <widget name="number" position="145,15" size="80,25" halign="right" font="Regular;23" />
128 self.close(int(self["number"].getText()))
130 def keyNumberGlobal(self, number):
131 self.Timer.start(3000, True) #reset timer
132 self.field = self.field + str(number)
133 self["number"].setText(self.field)
134 if len(self.field) >= 4:
137 def __init__(self, session, number):
138 Screen.__init__(self, session)
139 self.field = str(number)
141 self["chapter"] = Label(_("Chapter:"))
143 self["number"] = Label(self.field)
145 self["actions"] = NumberActionMap( [ "SetupActions" ],
149 "1": self.keyNumberGlobal,
150 "2": self.keyNumberGlobal,
151 "3": self.keyNumberGlobal,
152 "4": self.keyNumberGlobal,
153 "5": self.keyNumberGlobal,
154 "6": self.keyNumberGlobal,
155 "7": self.keyNumberGlobal,
156 "8": self.keyNumberGlobal,
157 "9": self.keyNumberGlobal,
158 "0": self.keyNumberGlobal
161 self.Timer = eTimer()
162 self.Timer.callback.append(self.keyOK)
163 self.Timer.start(3000, True)
165 class DVDPlayer(Screen, InfoBarBase, InfoBarNotifications, InfoBarSeek, InfoBarPVRState, InfoBarShowHide, HelpableScreen, InfoBarCueSheetSupport):
166 # ALLOW_SUSPEND = True
167 ENABLE_RESUME_SUPPORT = True
170 <screen name="DVDPlayer" flags="wfNoBorder" position="0,380" size="720,160" title="InfoBar" backgroundColor="transparent" >
172 <ePixmap position="0,0" zPosition="-2" size="720,160" pixmap="skin_default/info-bg_mp.png" alphatest="off" />
173 <ePixmap position="29,40" zPosition="0" size="665,104" pixmap="skin_default/screws_mp.png" alphatest="on" transparent="1" />
174 <!-- colorbuttons -->
175 <ePixmap position="48,70" zPosition="0" size="108,13" pixmap="skin_default/icons/mp_buttons.png" alphatest="on" />
177 <ePixmap pixmap="skin_default/icons/icon_event.png" position="207,78" zPosition="1" size="15,10" alphatest="on" />
178 <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">
179 <convert type="ServiceName">Name</convert>
181 <!-- Chapter info -->
182 <widget name="chapterLabel" position="230,96" size="360,22" font="Regular;20" foregroundColor="#c3c3c9" backgroundColor="#263c59" transparent="1" />
183 <!-- Audio track info -->
184 <ePixmap pixmap="skin_default/icons/icon_dolby.png" position="540,73" zPosition="1" size="26,16" alphatest="on"/>
185 <widget name="audioLabel" position="570,73" size="130,22" font="Regular;18" backgroundColor="#263c59" shadowColor="#1d354c" shadowOffset="-1,-1" transparent="1" />
186 <!-- Subtitle track info -->
187 <widget source="session.CurrentService" render="Pixmap" pixmap="skin_default/icons/icon_txt.png" position="540,96" zPosition="1" size="26,16" alphatest="on" >
188 <convert type="ServiceInfo">HasTelext</convert>
189 <convert type="ConditionalShowHide" />
191 <widget name="subtitleLabel" position="570,96" size="130,22" font="Regular;18" backgroundColor="#263c59" shadowColor="#1d354c" shadowOffset="-1,-1" transparent="1" />
192 <!-- Elapsed time -->
193 <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" >
194 <convert type="ServicePosition">Position,ShowHours</convert>
196 <!-- Progressbar (movie position)-->
197 <widget source="session.CurrentService" render="PositionGauge" position="300,133" size="270,10" zPosition="2" pointer="skin_default/position_pointer.png:540,0" transparent="1" >
198 <convert type="ServicePosition">Gauge</convert>
200 <!-- Remaining time -->
201 <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" >
202 <convert type="ServicePosition">Remaining,Negate,ShowHours</convert>
206 def save_infobar_seek_config(self):
207 self.saved_config_speeds_forward = config.seek.speeds_forward.value
208 self.saved_config_speeds_backward = config.seek.speeds_backward.value
209 self.saved_config_enter_forward = config.seek.enter_forward.value
210 self.saved_config_enter_backward = config.seek.enter_backward.value
211 self.saved_config_seek_stepwise_minspeed = config.seek.stepwise_minspeed.value
212 self.saved_config_seek_stepwise_repeat = config.seek.stepwise_repeat.value
213 self.saved_config_seek_on_pause = config.seek.on_pause.value
214 self.saved_config_seek_speeds_slowmotion = config.seek.speeds_slowmotion.value
216 def change_infobar_seek_config(self):
217 config.seek.speeds_forward.value = [2, 4, 8, 16, 32, 64]
218 config.seek.speeds_backward.value = [8, 16, 32, 64]
219 config.seek.speeds_slowmotion.value = [ ]
220 config.seek.enter_forward.value = "2"
221 config.seek.enter_backward.value = "2"
222 config.seek.stepwise_minspeed.value = "Never"
223 config.seek.stepwise_repeat.value = "3"
224 config.seek.on_pause.value = "play"
226 def restore_infobar_seek_config(self):
227 config.seek.speeds_forward.value = self.saved_config_speeds_forward
228 config.seek.speeds_backward.value = self.saved_config_speeds_backward
229 config.seek.speeds_slowmotion.value = self.saved_config_seek_speeds_slowmotion
230 config.seek.enter_forward.value = self.saved_config_enter_forward
231 config.seek.enter_backward.value = self.saved_config_enter_backward
232 config.seek.stepwise_minspeed.value = self.saved_config_seek_stepwise_minspeed
233 config.seek.stepwise_repeat.value = self.saved_config_seek_stepwise_repeat
234 config.seek.on_pause.value = self.saved_config_seek_on_pause
236 def __init__(self, session, dvd_device = None, dvd_filelist = [ ], args = None):
237 Screen.__init__(self, session)
238 InfoBarBase.__init__(self)
239 InfoBarNotifications.__init__(self)
240 InfoBarCueSheetSupport.__init__(self, actionmap = "MediaPlayerCueSheetActions")
241 InfoBarShowHide.__init__(self)
242 HelpableScreen.__init__(self)
243 self.save_infobar_seek_config()
244 self.change_infobar_seek_config()
245 InfoBarSeek.__init__(self, useSeekBackHack=False)
246 InfoBarPVRState.__init__(self)
247 self.dvdScreen = self.session.instantiateDialog(DVDOverlay)
249 self.oldService = self.session.nav.getCurrentlyPlayingServiceReference()
250 self.session.nav.stopService()
251 self["audioLabel"] = Label("n/a")
252 self["subtitleLabel"] = Label("")
253 self["chapterLabel"] = Label("")
254 self.last_audioTuple = None
255 self.last_subtitleTuple = None
256 self.totalChapters = 0
257 self.currentChapter = 0
259 self.currentTitle = 0
261 self.__event_tracker = ServiceEventTracker(screen=self, eventmap=
263 iPlayableService.evStopped: self.__serviceStopped,
264 iPlayableService.evUser: self.__timeUpdated,
265 iPlayableService.evUser+1: self.__statePlay,
266 iPlayableService.evUser+2: self.__statePause,
267 iPlayableService.evUser+3: self.__osdFFwdInfoAvail,
268 iPlayableService.evUser+4: self.__osdFBwdInfoAvail,
269 iPlayableService.evUser+5: self.__osdStringAvail,
270 iPlayableService.evUser+6: self.__osdAudioInfoAvail,
271 iPlayableService.evUser+7: self.__osdSubtitleInfoAvail,
272 iPlayableService.evUser+8: self.__chapterUpdated,
273 iPlayableService.evUser+9: self.__titleUpdated,
274 iPlayableService.evUser+11: self.__menuOpened,
275 iPlayableService.evUser+12: self.__menuClosed
278 self["DVDPlayerDirectionActions"] = HelpableActionMap(self, "DirectionActions",
280 #MENU KEY DOWN ACTIONS
281 "left": (self.keyLeft, _("DVD left key")),
282 "right": (self.keyRight, _("DVD right key")),
283 "up": (self.keyUp, _("DVD up key")),
284 "down": (self.keyDown, _("DVD down key")),
286 #MENU KEY REPEATED ACTIONS
287 "leftRepeated": self.doNothing,
288 "rightRepeated": self.doNothing,
289 "upRepeated": self.doNothing,
290 "downRepeated": self.doNothing,
293 "leftUp": self.doNothing,
294 "rightUp": self.doNothing,
295 "upUp": self.doNothing,
296 "downUp": self.doNothing,
299 self["OkCancelActions"] = HelpableActionMap(self, "OkCancelActions",
301 "ok": (self.keyOk, _("DVD ENTER key")),
302 "cancel": self.keyCancel,
305 self["DVDPlayerPlaybackActions"] = HelpableActionMap(self, "DVDPlayerActions",
308 "dvdMenu": (self.enterDVDMenu, _("show DVD main menu")),
309 "toggleInfo": (self.toggleInfo, _("toggle time, chapter, audio, subtitle info")),
310 "nextChapter": (self.nextChapter, _("forward to the next chapter")),
311 "prevChapter": (self.prevChapter, _("rewind to the previous chapter")),
312 "nextTitle": (self.nextTitle, _("jump forward to the next title")),
313 "prevTitle": (self.prevTitle, _("jump back to the previous title")),
314 "tv": (self.askLeavePlayer, _("exit DVD player or return to file browser")),
315 "dvdAudioMenu": (self.enterDVDAudioMenu, _("(show optional DVD audio menu)")),
316 "nextAudioTrack": (self.nextAudioTrack, _("switch to the next audio track")),
317 "nextSubtitleTrack": (self.nextSubtitleTrack, _("switch to the next subtitle language")),
318 "seekBeginning": (self.seekBeginning, _("Jump to video title 1 (play movie from start)")),
321 self["NumberActions"] = NumberActionMap( [ "NumberActions"],
323 "1": self.keyNumberGlobal,
324 "2": self.keyNumberGlobal,
325 "3": self.keyNumberGlobal,
326 "4": self.keyNumberGlobal,
327 "5": self.keyNumberGlobal,
328 "6": self.keyNumberGlobal,
329 "7": self.keyNumberGlobal,
330 "8": self.keyNumberGlobal,
331 "9": self.keyNumberGlobal,
332 "0": self.keyNumberGlobal,
335 self.onClose.append(self.__onClose)
338 self.dvd_device = dvd_device
339 self.physicalDVD = True
341 if fileExists("/dev/cdroms/cdrom0"):
342 print "physical dvd found (/dev/cdroms/cdrom0)"
343 self.dvd_device = "/dev/cdroms/cdrom0"
344 self.physicalDVD = True
346 self.dvd_device = None
347 self.physicalDVD = False
349 self.dvd_filelist = dvd_filelist
350 self.onFirstExecBegin.append(self.showFileBrowser)
353 self.old_aspect = open("/proc/stb/video/aspect", "r").read()
354 self.old_policy = open("/proc/stb/video/policy", "r").read()
355 self.old_wss = open("/proc/stb/denc/0/wss", "r").read()
357 def keyNumberGlobal(self, number):
358 print "You pressed number " + str(number)
359 self.session.openWithCallback(self.numberEntered, ChapterZap, number)
361 def numberEntered(self, retval):
362 # print self.servicelist
364 self.zapToNumber(retval)
366 def __serviceStopped(self):
367 self.dvdScreen.hide()
368 self.service.subtitle().disableSubtitles(self.session.current_dialog.instance)
370 def serviceStarted(self): #override InfoBarShowHide function
371 self.dvdScreen.show()
372 self.service.subtitle().enableSubtitles(self.dvdScreen.instance, None)
374 def doEofInternal(self, playing):
378 def __menuOpened(self):
381 self["NumberActions"].setEnabled(False)
383 def __menuClosed(self):
386 self["NumberActions"].setEnabled(True)
388 def setChapterLabel(self):
390 chapterOSD = "DVD Menu"
391 if self.currentTitle > 0:
392 chapterLCD = "%s %d" % (_("Chap."), self.currentChapter)
393 chapterOSD = "DVD %s %d/%d" % (_("Chapter"), self.currentChapter, self.totalChapters)
394 chapterOSD += " (%s %d/%d)" % (_("Title"), self.currentTitle, self.totalTitles)
395 self["chapterLabel"].setText(chapterOSD)
397 self.session.summary.updateChapter(chapterLCD)
404 def toggleInfo(self):
409 def __timeUpdated(self):
412 def __statePlay(self):
415 def __statePause(self):
418 def __osdFFwdInfoAvail(self):
419 self.setChapterLabel()
420 print "FFwdInfoAvail"
422 def __osdFBwdInfoAvail(self):
423 self.setChapterLabel()
424 print "FBwdInfoAvail"
426 def __osdStringAvail(self):
429 def __osdAudioInfoAvail(self):
430 audioTuple = self.service.info().getInfoObject(iServiceInformation.sUser+6)
431 print "AudioInfoAvail ", repr(audioTuple)
433 audioString = "%d: %s (%s)" % (audioTuple[0],audioTuple[1],audioTuple[2])
434 self["audioLabel"].setText(audioString)
435 if audioTuple != self.last_audioTuple and not self.in_menu:
437 self.last_audioTuple = audioTuple
439 def __osdSubtitleInfoAvail(self):
440 subtitleTuple = self.service.info().getInfoObject(iServiceInformation.sUser+7)
441 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):
543 if self.physicalDVD and len(self.dvd_filelist) == 0:
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)
548 elif len(self.dvd_filelist) == 1:
549 self.FileBrowserClosed(self.dvd_filelist[0])
551 self.session.openWithCallback(self.FileBrowserClosed, FileBrowser, self.dvd_filelist)
553 def DVDdriveCB(self, answer):
555 self.FileBrowserClosed(self.dvd_device)
557 self.session.openWithCallback(self.FileBrowserClosed, FileBrowser)
558 self.physicalDVD = False
560 def FileBrowserClosed(self, val):
561 curref = self.session.nav.getCurrentlyPlayingServiceReference()
562 print "FileBrowserClosed", val
564 self.askLeavePlayer()
566 newref = eServiceReference(4369, 0, val)
567 print "play", newref.toString()
568 if curref is None or curref != newref:
569 self.session.nav.playService(newref)
570 self.service = self.session.nav.getCurrentService()
571 print "self.service", self.service
572 print "cur_dlg", self.session.current_dialog
574 def exitCB(self, answer):
575 if answer is not None:
576 if answer[1] == "exit":
580 if answer[1] == "browser":
581 #TODO check here if a paused dvd playback is already running... then re-start it...
585 self.showFileBrowser()
590 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)):
592 open(i[0], "w").write(i[1])
594 print "restore", i[0], "failed"
595 self.restore_infobar_seek_config()
596 self.session.nav.playService(self.oldService)
598 def playLastCB(self, answer): # overwrite infobar cuesheet function
599 print "playLastCB", answer, self.resume_point
601 seek = self.service.seek()
603 seek.seekTo(self.resume_point)
604 pause = self.service.pause()
606 self.hideAfterResume()
608 def showAfterCuesheetOperation(self):
612 def createSummary(self):
615 #override some InfoBarSeek functions
617 self.setSeekState(self.SEEK_STATE_PLAY)
619 def calcRemainingTime(self):
622 def main(session, **kwargs):
623 session.open(DVDPlayer)
625 def menu(menuid, **kwargs):
626 if menuid == "mainmenu":
627 return [(_("DVD Player"), main, "dvd_player", 46)]
630 from Plugins.Plugin import PluginDescriptor
632 def filescan_open(list, session, **kwargs):
633 if len(list) == 1 and list[0].mimetype == "video/x-dvd":
634 splitted = list[0].path.split('/')
635 print "splitted", splitted
636 if len(splitted) > 2:
637 if splitted[1] == 'autofs':
638 session.open(DVDPlayer, dvd_device="/dev/%s" %(splitted[2]))
641 print "splitted[0]", splitted[1]
645 if x.mimetype == "video/x-dvd-iso":
646 dvd_filelist.append(x.path)
647 if x.mimetype == "video/x-dvd":
648 dvd_filelist.append(x.path.rsplit('/',1)[0])
649 session.open(DVDPlayer, dvd_filelist=dvd_filelist)
651 def filescan(**kwargs):
652 from Components.Scanner import Scanner, ScanPath
654 # Overwrite checkFile to only detect local
655 class LocalScanner(Scanner):
656 def checkFile(self, file):
657 return fileExists(file.path)
660 LocalScanner(mimetypes = ["video/x-dvd","video/x-dvd-iso"],
663 ScanPath(path = "video_ts", with_subdirs = False),
664 ScanPath(path = "", with_subdirs = False),
667 description = "Play DVD",
668 openfnc = filescan_open,
671 def Plugins(**kwargs):
672 return [PluginDescriptor(name = "DVDPlayer", description = "Play DVDs", where = PluginDescriptor.WHERE_MENU, fnc = menu),
673 PluginDescriptor(where = PluginDescriptor.WHERE_FILESCAN, fnc = filescan)]