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)
432 audioString = "%d: %s (%s)" % (audioTuple[0],audioTuple[1],audioTuple[2])
433 self["audioLabel"].setText(audioString)
434 if audioTuple != self.last_audioTuple and not self.in_menu:
436 self.last_audioTuple = audioTuple
438 def __osdSubtitleInfoAvail(self):
439 subtitleTuple = self.service.info().getInfoObject(iServiceInformation.sUser+7)
440 print "SubtitleInfoAvail ", repr(subtitleTuple)
442 if subtitleTuple[0] is not 0:
443 subtitleString = "%d: %s" % (subtitleTuple[0],subtitleTuple[1])
444 self["subtitleLabel"].setText(subtitleString)
445 if subtitleTuple != self.last_subtitleTuple and not self.in_menu:
447 self.last_subtitleTuple = subtitleTuple
449 def __chapterUpdated(self):
450 self.currentChapter = self.service.info().getInfo(iServiceInformation.sCurrentChapter)
451 self.totalChapters = self.service.info().getInfo(iServiceInformation.sTotalChapters)
452 self.setChapterLabel()
453 print "__chapterUpdated: %d/%d" % (self.currentChapter, self.totalChapters)
455 def __titleUpdated(self):
456 self.currentTitle = self.service.info().getInfo(iServiceInformation.sCurrentTitle)
457 self.totalTitles = self.service.info().getInfo(iServiceInformation.sTotalTitles)
458 self.setChapterLabel()
459 print "__titleUpdated: %d/%d" % (self.currentTitle, self.totalTitles)
463 def askLeavePlayer(self):
464 choices = [(_("Continue playing"), "play"), (_("Exit"), "exit")]
465 if not self.physicalDVD:
466 choices.insert(1,(_("Return to file browser"), "browser"))
467 self.session.openWithCallback(self.exitCB, ChoiceBox, title=_("Leave DVD Player?"), list = choices)
469 def nextAudioTrack(self):
471 self.service.keys().keyPressed(iServiceKeys.keyUser)
473 def nextSubtitleTrack(self):
475 self.service.keys().keyPressed(iServiceKeys.keyUser+1)
477 def enterDVDAudioMenu(self):
479 self.service.keys().keyPressed(iServiceKeys.keyUser+2)
481 def nextChapter(self):
483 self.service.keys().keyPressed(iServiceKeys.keyUser+3)
485 def prevChapter(self):
487 self.service.keys().keyPressed(iServiceKeys.keyUser+4)
491 self.service.keys().keyPressed(iServiceKeys.keyUser+5)
495 self.service.keys().keyPressed(iServiceKeys.keyUser+6)
497 def enterDVDMenu(self):
499 self.service.keys().keyPressed(iServiceKeys.keyUser+7)
501 def seekBeginning(self):
503 seekable = self.getSeek()
504 if seekable is not None:
507 def zapToNumber(self, number):
509 seekable = self.getSeek()
510 if seekable is not None:
511 print "seek to chapter %d" % number
512 seekable.seekChapter(number)
517 self.service.keys().keyPressed(iServiceKeys.keyRight)
521 self.service.keys().keyPressed(iServiceKeys.keyLeft)
525 self.service.keys().keyPressed(iServiceKeys.keyUp)
529 self.service.keys().keyPressed(iServiceKeys.keyDown)
535 self.service.keys().keyPressed(iServiceKeys.keyOk)
538 self.askLeavePlayer()
540 def showFileBrowser(self):
541 if self.physicalDVD and len(self.dvd_filelist) == 0:
542 if self.dvd_device == "/dev/cdroms/cdrom0":
543 self.session.openWithCallback(self.DVDdriveCB, MessageBox, text=_("Do you want to play DVD in drive?"), timeout=5 )
545 self.DVDdriveCB(True)
546 elif len(self.dvd_filelist) == 1:
547 self.FileBrowserClosed(self.dvd_filelist[0])
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)]