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