1 from ChannelSelection import ChannelSelection, BouquetSelector
3 from Components.ActionMap import ActionMap, HelpableActionMap
4 from Components.ActionMap import NumberActionMap
5 from Components.Harddisk import harddiskmanager
6 from Components.Input import Input
7 from Components.Label import Label
8 from Components.PluginComponent import plugins
9 from Components.ServiceEventTracker import ServiceEventTracker
10 from Components.Sources.Boolean import Boolean
11 from Components.config import config, ConfigBoolean, ConfigClock
12 from Components.SystemInfo import SystemInfo
13 from EpgSelection import EPGSelection
14 from Plugins.Plugin import PluginDescriptor
16 from Screen import Screen
17 from Screens.ChoiceBox import ChoiceBox
18 from Screens.Dish import Dish
19 from Screens.EventView import EventViewEPGSelect, EventViewSimple
20 from Screens.InputBox import InputBox
21 from Screens.MessageBox import MessageBox
22 from Screens.MinuteInput import MinuteInput
23 from Screens.TimerSelection import TimerSelection
24 from Screens.PictureInPicture import PictureInPicture
25 from Screens.SubtitleDisplay import SubtitleDisplay
26 from Screens.RdsDisplay import RdsInfoDisplay, RassInteractive
27 from Screens.SleepTimerEdit import SleepTimerEdit
28 from Screens.TimeDateInput import TimeDateInput
29 from ServiceReference import ServiceReference
31 from Tools import Notifications
32 from Tools.Directories import SCOPE_HDD, resolveFilename, fileExists
34 from enigma import eTimer, eServiceCenter, eDVBServicePMTHandler, iServiceInformation, \
35 iPlayableService, eServiceReference, eEPGCache
37 from time import time, localtime, strftime
38 from os import stat as os_stat
39 from bisect import insort
41 from RecordTimer import RecordTimerEntry, RecordTimer
44 from Menu import MainMenu, mdom
48 self.dishDialog = self.session.instantiateDialog(Dish)
50 class InfoBarShowHide:
51 """ InfoBar show/hide control, accepts toggleShow and hide actions, might start
59 self["ShowHideActions"] = ActionMap( ["InfobarShowHideActions"] ,
61 "toggleShow": self.toggleShow,
63 }, 1) # lower prio to make it possible to override ok and cancel..
65 self.__event_tracker = ServiceEventTracker(screen=self, eventmap=
67 iPlayableService.evStart: self.serviceStarted,
70 self.__state = self.STATE_SHOWN
73 self.hideTimer = eTimer()
74 self.hideTimer.callback.append(self.doTimerHide)
75 self.hideTimer.start(5000, True)
77 self.onShow.append(self.__onShow)
78 self.onHide.append(self.__onHide)
80 def serviceStarted(self):
82 if config.usage.show_infobar_on_zap.value:
86 self.__state = self.STATE_SHOWN
89 def startHideTimer(self):
90 if self.__state == self.STATE_SHOWN and not self.__locked:
91 idx = config.usage.infobar_timeout.index
93 self.hideTimer.start(idx*1000, True)
96 self.__state = self.STATE_HIDDEN
100 self.startHideTimer()
102 def doTimerHide(self):
103 self.hideTimer.stop()
104 if self.__state == self.STATE_SHOWN:
107 def toggleShow(self):
108 if self.__state == self.STATE_SHOWN:
110 self.hideTimer.stop()
111 elif self.__state == self.STATE_HIDDEN:
115 self.__locked = self.__locked + 1
118 self.hideTimer.stop()
120 def unlockShow(self):
121 self.__locked = self.__locked - 1
123 self.startHideTimer()
125 # def startShow(self):
126 # self.instance.m_animation.startMoveAnimation(ePoint(0, 600), ePoint(0, 380), 100)
127 # self.__state = self.STATE_SHOWN
129 # def startHide(self):
130 # self.instance.m_animation.startMoveAnimation(ePoint(0, 380), ePoint(0, 600), 100)
131 # self.__state = self.STATE_HIDDEN
133 class NumberZap(Screen):
140 self.close(int(self["number"].getText()))
142 def keyNumberGlobal(self, number):
143 self.Timer.start(3000, True) #reset timer
144 self.field = self.field + str(number)
145 self["number"].setText(self.field)
146 if len(self.field) >= 4:
149 def __init__(self, session, number):
150 Screen.__init__(self, session)
151 self.field = str(number)
153 self["channel"] = Label(_("Channel:"))
155 self["number"] = Label(self.field)
157 self["actions"] = NumberActionMap( [ "SetupActions" ],
161 "1": self.keyNumberGlobal,
162 "2": self.keyNumberGlobal,
163 "3": self.keyNumberGlobal,
164 "4": self.keyNumberGlobal,
165 "5": self.keyNumberGlobal,
166 "6": self.keyNumberGlobal,
167 "7": self.keyNumberGlobal,
168 "8": self.keyNumberGlobal,
169 "9": self.keyNumberGlobal,
170 "0": self.keyNumberGlobal
173 self.Timer = eTimer()
174 self.Timer.callback.append(self.keyOK)
175 self.Timer.start(3000, True)
177 class InfoBarNumberZap:
178 """ Handles an initial number for NumberZapping """
180 self["NumberActions"] = NumberActionMap( [ "NumberActions"],
182 "1": self.keyNumberGlobal,
183 "2": self.keyNumberGlobal,
184 "3": self.keyNumberGlobal,
185 "4": self.keyNumberGlobal,
186 "5": self.keyNumberGlobal,
187 "6": self.keyNumberGlobal,
188 "7": self.keyNumberGlobal,
189 "8": self.keyNumberGlobal,
190 "9": self.keyNumberGlobal,
191 "0": self.keyNumberGlobal,
194 def keyNumberGlobal(self, number):
195 # print "You pressed number " + str(number)
197 if isinstance(self, InfoBarPiP) and self.pipHandles0Action():
198 self.pipDoHandle0Action()
200 self.servicelist.recallPrevService()
202 if self.has_key("TimeshiftActions") and not self.timeshift_enabled:
203 self.session.openWithCallback(self.numberEntered, NumberZap, number)
205 def numberEntered(self, retval):
206 # print self.servicelist
208 self.zapToNumber(retval)
210 def searchNumberHelper(self, serviceHandler, num, bouquet):
211 servicelist = serviceHandler.list(bouquet)
212 if not servicelist is None:
214 serviceIterator = servicelist.getNext()
215 if not serviceIterator.valid(): #check end of list
217 playable = not (serviceIterator.flags & (eServiceReference.isMarker|eServiceReference.isDirectory))
220 if not num: #found service with searched number ?
221 return serviceIterator, 0
224 def zapToNumber(self, number):
225 bouquet = self.servicelist.bouquet_root
227 serviceHandler = eServiceCenter.getInstance()
228 if not config.usage.multibouquet.value:
229 service, number = self.searchNumberHelper(serviceHandler, number, bouquet)
231 bouquetlist = serviceHandler.list(bouquet)
232 if not bouquetlist is None:
234 bouquet = bouquetlist.getNext()
235 if not bouquet.valid(): #check end of list
237 if bouquet.flags & eServiceReference.isDirectory:
238 service, number = self.searchNumberHelper(serviceHandler, number, bouquet)
239 if not service is None:
240 if self.servicelist.getRoot() != bouquet: #already in correct bouquet?
241 self.servicelist.clearPath()
242 if self.servicelist.bouquet_root != bouquet:
243 self.servicelist.enterPath(self.servicelist.bouquet_root)
244 self.servicelist.enterPath(bouquet)
245 self.servicelist.setCurrentSelection(service) #select the service in servicelist
246 self.servicelist.zap()
248 config.misc.initialchannelselection = ConfigBoolean(default = True)
250 class InfoBarChannelSelection:
251 """ ChannelSelection - handles the channelSelection dialog and the initial
252 channelChange actions which open the channelSelection dialog """
255 self.servicelist = self.session.instantiateDialog(ChannelSelection)
257 if config.misc.initialchannelselection.value:
258 self.onShown.append(self.firstRun)
260 self["ChannelSelectActions"] = HelpableActionMap(self, "InfobarChannelSelection",
262 "switchChannelUp": (self.switchChannelUp, _("open servicelist(up)")),
263 "switchChannelDown": (self.switchChannelDown, _("open servicelist(down)")),
264 "zapUp": (self.zapUp, _("previous channel")),
265 "zapDown": (self.zapDown, _("next channel")),
266 "historyBack": (self.historyBack, _("previous channel in history")),
267 "historyNext": (self.historyNext, _("next channel in history")),
268 "openServiceList": (self.openServiceList, _("open servicelist")),
271 def showTvChannelList(self, zap=False):
272 self.servicelist.setModeTv()
274 self.servicelist.zap()
275 self.session.execDialog(self.servicelist)
277 def showRadioChannelList(self, zap=False):
278 self.servicelist.setModeRadio()
280 self.servicelist.zap()
281 self.session.execDialog(self.servicelist)
284 self.onShown.remove(self.firstRun)
285 config.misc.initialchannelselection.value = False
286 config.misc.initialchannelselection.save()
287 self.switchChannelDown()
289 def historyBack(self):
290 self.servicelist.historyBack()
292 def historyNext(self):
293 self.servicelist.historyNext()
295 def switchChannelUp(self):
296 self.servicelist.moveUp()
297 self.session.execDialog(self.servicelist)
299 def switchChannelDown(self):
300 self.servicelist.moveDown()
301 self.session.execDialog(self.servicelist)
303 def openServiceList(self):
304 self.session.execDialog(self.servicelist)
307 if self.servicelist.inBouquet():
308 prev = self.servicelist.getCurrentSelection()
310 prev = prev.toString()
312 if config.usage.quickzap_bouquet_change.value:
313 if self.servicelist.atBegin():
314 self.servicelist.prevBouquet()
315 self.servicelist.moveUp()
316 cur = self.servicelist.getCurrentSelection()
317 if not cur or (not (cur.flags & 64)) or cur.toString() == prev:
320 self.servicelist.moveUp()
321 self.servicelist.zap()
324 if self.servicelist.inBouquet():
325 prev = self.servicelist.getCurrentSelection()
327 prev = prev.toString()
329 if config.usage.quickzap_bouquet_change.value and self.servicelist.atEnd():
330 self.servicelist.nextBouquet()
332 self.servicelist.moveDown()
333 cur = self.servicelist.getCurrentSelection()
334 if not cur or (not (cur.flags & 64)) or cur.toString() == prev:
337 self.servicelist.moveDown()
338 self.servicelist.zap()
341 """ Handles a menu action, to open the (main) menu """
343 self["MenuActions"] = HelpableActionMap(self, "InfobarMenuActions",
345 "mainMenu": (self.mainMenu, _("Enter main menu...")),
347 self.session.infobar = None
350 print "loading mainmenu XML..."
351 menu = mdom.getroot()
352 assert menu.tag == "menu", "root element in menu must be 'menu'!"
354 self.session.infobar = self
355 # so we can access the currently active infobar from screens opened from within the mainmenu
356 # at the moment used from the SubserviceSelection
358 self.session.openWithCallback(self.mainMenuClosed, MainMenu, menu)
360 def mainMenuClosed(self, *val):
361 self.session.infobar = None
363 class InfoBarSimpleEventView:
364 """ Opens the Eventview for now/next """
366 self["EPGActions"] = HelpableActionMap(self, "InfobarEPGActions",
368 "showEventInfo": (self.openEventView, _("show event details")),
371 def openEventView(self):
373 self.epglist = epglist
374 service = self.session.nav.getCurrentService()
375 ref = self.session.nav.getCurrentlyPlayingServiceReference()
376 info = service.info()
384 self.session.open(EventViewSimple, epglist[0], ServiceReference(ref), self.eventViewCallback)
386 def eventViewCallback(self, setEvent, setService, val): #used for now/next displaying
387 epglist = self.epglist
390 epglist[0] = epglist[1]
395 """ EPG - Opens an EPG list when the showEPGList action fires """
397 self.__event_tracker = ServiceEventTracker(screen=self, eventmap=
399 iPlayableService.evUpdatedEventInfo: self.__evEventInfoChanged,
402 self.is_now_next = False
404 self.bouquetSel = None
405 self.eventView = None
406 self["EPGActions"] = HelpableActionMap(self, "InfobarEPGActions",
408 "showEventInfo": (self.openEventView, _("show EPG...")),
409 "showEventInfoPlugin": (self.showEventInfoPlugins, _("show single service EPG...")),
410 "showInfobarOrEpgWhenInfobarAlreadyVisible": self.showEventInfoWhenNotVisible,
413 def showEventInfoWhenNotVisible(self):
420 def zapToService(self, service):
421 if not service is None:
422 if self.servicelist.getRoot() != self.epg_bouquet: #already in correct bouquet?
423 self.servicelist.clearPath()
424 if self.servicelist.bouquet_root != self.epg_bouquet:
425 self.servicelist.enterPath(self.servicelist.bouquet_root)
426 self.servicelist.enterPath(self.epg_bouquet)
427 self.servicelist.setCurrentSelection(service) #select the service in servicelist
428 self.servicelist.zap()
430 def getBouquetServices(self, bouquet):
432 servicelist = eServiceCenter.getInstance().list(bouquet)
433 if not servicelist is None:
435 service = servicelist.getNext()
436 if not service.valid(): #check if end of list
438 if service.flags & (eServiceReference.isDirectory | eServiceReference.isMarker): #ignore non playable services
440 services.append(ServiceReference(service))
443 def openBouquetEPG(self, bouquet, withCallback=True):
444 services = self.getBouquetServices(bouquet)
446 self.epg_bouquet = bouquet
448 self.dlg_stack.append(self.session.openWithCallback(self.closed, EPGSelection, services, self.zapToService, None, self.changeBouquetCB))
450 self.session.open(EPGSelection, services, self.zapToService, None, self.changeBouquetCB)
452 def changeBouquetCB(self, direction, epg):
455 self.bouquetSel.down()
458 bouquet = self.bouquetSel.getCurrent()
459 services = self.getBouquetServices(bouquet)
461 self.epg_bouquet = bouquet
462 epg.setServices(services)
464 def closed(self, ret=False):
465 closedScreen = self.dlg_stack.pop()
466 if self.bouquetSel and closedScreen == self.bouquetSel:
467 self.bouquetSel = None
468 elif self.eventView and closedScreen == self.eventView:
469 self.eventView = None
471 dlgs=len(self.dlg_stack)
473 self.dlg_stack[dlgs-1].close(dlgs > 1)
475 def openMultiServiceEPG(self, withCallback=True):
476 bouquets = self.servicelist.getBouquetList()
481 if cnt > 1: # show bouquet list
483 self.bouquetSel = self.session.openWithCallback(self.closed, BouquetSelector, bouquets, self.openBouquetEPG, enableWrapAround=True)
484 self.dlg_stack.append(self.bouquetSel)
486 self.bouquetSel = self.session.open(BouquetSelector, bouquets, self.openBouquetEPG, enableWrapAround=True)
488 self.openBouquetEPG(bouquets[0][1], withCallback)
490 def openSingleServiceEPG(self):
491 ref=self.session.nav.getCurrentlyPlayingServiceReference()
492 self.session.open(EPGSelection, ref)
494 def showEventInfoPlugins(self):
495 list = [(p.name, boundFunction(self.runPlugin, p)) for p in plugins.getPlugins(where = PluginDescriptor.WHERE_EVENTINFO)]
498 list.append((_("show single service EPG..."), self.openSingleServiceEPG))
499 self.session.openWithCallback(self.EventInfoPluginChosen, ChoiceBox, title=_("Please choose an extension..."), list = list)
501 self.openSingleServiceEPG()
503 def runPlugin(self, plugin):
504 plugin(session = self.session, servicelist = self.servicelist)
506 def EventInfoPluginChosen(self, answer):
507 if answer is not None:
510 def openSimilarList(self, eventid, refstr):
511 self.session.open(EPGSelection, refstr, None, eventid)
513 def getNowNext(self):
515 service = self.session.nav.getCurrentService()
516 info = service and service.info()
517 ptr = info and info.getEvent(0)
520 ptr = info and info.getEvent(1)
523 self.epglist = epglist
525 def __evEventInfoChanged(self):
526 if self.is_now_next and len(self.dlg_stack) == 1:
528 assert self.eventView
530 self.eventView.setEvent(self.epglist[0])
532 def openEventView(self):
533 ref = self.session.nav.getCurrentlyPlayingServiceReference()
535 epglist = self.epglist
537 self.is_now_next = False
538 epg = eEPGCache.getInstance()
539 ptr = ref and ref.valid() and epg.lookupEventTime(ref, -1)
542 ptr = epg.lookupEventTime(ref, ptr.getBeginTime(), +1)
546 self.is_now_next = True
548 self.eventView = self.session.openWithCallback(self.closed, EventViewEPGSelect, self.epglist[0], ServiceReference(ref), self.eventViewCallback, self.openSingleServiceEPG, self.openMultiServiceEPG, self.openSimilarList)
549 self.dlg_stack.append(self.eventView)
551 print "no epg for the service avail.. so we show multiepg instead of eventinfo"
552 self.openMultiServiceEPG(False)
554 def eventViewCallback(self, setEvent, setService, val): #used for now/next displaying
555 epglist = self.epglist
558 epglist[0]=epglist[1]
562 class InfoBarRdsDecoder:
563 """provides RDS and Rass support/display"""
565 self.rds_display = self.session.instantiateDialog(RdsInfoDisplay)
566 self.rass_interactive = None
568 self.__event_tracker = ServiceEventTracker(screen=self, eventmap=
570 iPlayableService.evEnd: self.__serviceStopped,
571 iPlayableService.evUpdatedRassSlidePic: self.RassSlidePicChanged
574 self["RdsActions"] = ActionMap(["InfobarRdsActions"],
576 "startRassInteractive": self.startRassInteractive
579 self["RdsActions"].setEnabled(False)
581 self.onLayoutFinish.append(self.rds_display.show)
582 self.rds_display.onRassInteractivePossibilityChanged.append(self.RassInteractivePossibilityChanged)
584 def RassInteractivePossibilityChanged(self, state):
585 self["RdsActions"].setEnabled(state)
587 def RassSlidePicChanged(self):
588 if not self.rass_interactive:
589 service = self.session.nav.getCurrentService()
590 decoder = service and service.rdsDecoder()
592 decoder.showRassSlidePicture()
594 def __serviceStopped(self):
595 if self.rass_interactive is not None:
596 rass_interactive = self.rass_interactive
597 self.rass_interactive = None
598 rass_interactive.close()
600 def startRassInteractive(self):
601 self.rds_display.hide()
602 self.rass_interactive = self.session.openWithCallback(self.RassInteractiveClosed, RassInteractive)
604 def RassInteractiveClosed(self, *val):
605 if self.rass_interactive is not None:
606 self.rass_interactive = None
607 self.RassSlidePicChanged()
608 self.rds_display.show()
611 """handles actions like seeking, pause"""
613 SEEK_STATE_PLAY = (0, 0, 0, ">")
614 SEEK_STATE_PAUSE = (1, 0, 0, "||")
615 SEEK_STATE_EOF = (1, 0, 0, "END")
617 def __init__(self, actionmap = "InfobarSeekActions", useSeekBackHack=True):
618 self.__event_tracker = ServiceEventTracker(screen=self, eventmap=
620 iPlayableService.evSeekableStatusChanged: self.__seekableStatusChanged,
621 iPlayableService.evStart: self.__serviceStarted,
623 iPlayableService.evEOF: self.__evEOF,
624 iPlayableService.evSOF: self.__evSOF,
627 self.eofTimer = eTimer()
628 self.eofTimer.timeout.get().append(self.doEof)
629 self.eofInhibitTimer = eTimer()
630 self.eofInhibitTimer.timeout.get().append(self.inhibitEof)
632 self.minSpeedBackward = useSeekBackHack and 16 or 0
634 class InfoBarSeekActionMap(HelpableActionMap):
635 def __init__(self, screen, *args, **kwargs):
636 HelpableActionMap.__init__(self, screen, *args, **kwargs)
639 def action(self, contexts, action):
640 print "action:", action
641 if action[:5] == "seek:":
642 time = int(action[5:])
643 self.screen.doSeekRelative(time * 90000)
645 elif action[:8] == "seekdef:":
646 key = int(action[8:])
647 time = (-config.seek.selfdefined_13.value, False, config.seek.selfdefined_13.value,
648 -config.seek.selfdefined_46.value, False, config.seek.selfdefined_46.value,
649 -config.seek.selfdefined_79.value, False, config.seek.selfdefined_79.value)[key-1]
650 self.screen.doSeekRelative(time * 90000)
653 return HelpableActionMap.action(self, contexts, action)
655 self["SeekActions"] = InfoBarSeekActionMap(self, actionmap,
657 "playpauseService": self.playpauseService,
658 "pauseService": (self.pauseService, _("pause")),
659 "unPauseService": (self.unPauseService, _("continue")),
661 "seekFwd": (self.seekFwd, _("skip forward")),
662 "seekFwdManual": (self.seekFwdManual, _("skip forward (enter time)")),
663 "seekBack": (self.seekBack, _("skip backward")),
664 "seekBackManual": (self.seekBackManual, _("skip backward (enter time)"))
666 # give them a little more priority to win over color buttons
668 self["SeekActions"].setEnabled(False)
670 self.seekstate = self.SEEK_STATE_PLAY
671 self.lastseekstate = self.SEEK_STATE_PLAY
673 self.onPlayStateChanged = [ ]
675 self.lockedBecauseOfSkipping = False
677 self.__seekableStatusChanged()
679 def makeStateForward(self, n):
680 minspeed = config.seek.stepwise_minspeed.value
681 repeat = int(config.seek.stepwise_repeat.value)
682 if minspeed != "Never" and n >= int(minspeed) and repeat > 1:
683 return (0, n * repeat, repeat, ">> %dx" % n)
685 return (0, n, 0, ">> %dx" % n)
687 def makeStateBackward(self, n):
688 minspeed = config.seek.stepwise_minspeed.value
689 repeat = int(config.seek.stepwise_repeat.value)
690 if self.minSpeedBackward and n < self.minSpeedBackward:
691 r = (self.minSpeedBackward - 1)/ n + 1
692 if minspeed != "Never" and n >= int(minspeed) and repeat > 1:
694 return (0, -n * r, r, "<< %dx" % n)
695 elif minspeed != "Never" and n >= int(minspeed) and repeat > 1:
696 return (0, -n * repeat, repeat, "<< %dx" % n)
698 return (0, -n, 0, "<< %dx" % n)
700 def makeStateSlowMotion(self, n):
701 return (0, 0, n, "/%d" % n)
703 def isStateForward(self, state):
706 def isStateBackward(self, state):
709 def isStateSlowMotion(self, state):
710 return state[1] == 0 and state[2] > 1
712 def getHigher(self, n, lst):
718 def getLower(self, n, lst):
726 def showAfterSeek(self):
727 if isinstance(self, InfoBarShowHide):
737 service = self.session.nav.getCurrentService()
741 seek = service.seek()
743 if seek is None or not seek.isCurrentlySeekable():
748 def isSeekable(self):
749 if self.getSeek() is None:
753 def __seekableStatusChanged(self):
754 # print "seekable status changed!"
755 if not self.isSeekable():
756 self["SeekActions"].setEnabled(False)
757 # print "not seekable, return to play"
758 self.setSeekState(self.SEEK_STATE_PLAY)
760 self["SeekActions"].setEnabled(True)
763 def __serviceStarted(self):
764 self.seekstate = self.SEEK_STATE_PLAY
765 self.__seekableStatusChanged()
766 if self.eofState != 0:
770 def setSeekState(self, state):
771 service = self.session.nav.getCurrentService()
776 if not self.isSeekable():
777 if state not in (self.SEEK_STATE_PLAY, self.SEEK_STATE_PAUSE):
778 state = self.SEEK_STATE_PLAY
780 pauseable = service.pause()
782 if pauseable is None:
783 print "not pauseable."
784 state = self.SEEK_STATE_PLAY
786 oldstate = self.seekstate
787 self.seekstate = state
790 if oldstate[i] != self.seekstate[i]:
791 (self.session.nav.pause, pauseable.setFastForward, pauseable.setSlowMotion)[i](self.seekstate[i])
793 for c in self.onPlayStateChanged:
796 self.checkSkipShowHideLock()
800 def playpauseService(self):
801 if self.seekstate != self.SEEK_STATE_PLAY:
802 self.unPauseService()
806 def pauseService(self):
807 if self.seekstate == self.SEEK_STATE_PAUSE:
808 if config.seek.on_pause.value == "play":
809 self.unPauseService()
810 elif config.seek.on_pause.value == "step":
811 self.doSeekRelative(0)
812 elif config.seek.on_pause.value == "last":
813 self.setSeekState(self.lastseekstate)
814 self.lastseekstate = self.SEEK_STATE_PLAY
816 if self.seekstate != self.SEEK_STATE_EOF:
817 self.lastseekstate = self.seekstate
818 self.setSeekState(self.SEEK_STATE_PAUSE);
820 def unPauseService(self):
822 if self.seekstate == self.SEEK_STATE_PLAY:
824 self.setSeekState(self.SEEK_STATE_PLAY)
826 def doSeek(self, pts):
827 seekable = self.getSeek()
830 prevstate = self.seekstate
831 if self.eofState == 1:
834 if self.seekstate == self.SEEK_STATE_EOF:
835 if prevstate == self.SEEK_STATE_PAUSE:
836 self.setSeekState(self.SEEK_STATE_PAUSE)
838 self.setSeekState(self.SEEK_STATE_PLAY)
839 self.eofInhibitTimer.start(200, True)
842 def doSeekRelative(self, pts):
843 seekable = self.getSeek()
846 prevstate = self.seekstate
847 if self.eofState == 1:
850 if self.seekstate == self.SEEK_STATE_EOF:
851 if prevstate == self.SEEK_STATE_PAUSE:
852 self.setSeekState(self.SEEK_STATE_PAUSE)
854 self.setSeekState(self.SEEK_STATE_PLAY)
855 self.eofInhibitTimer.start(200, True)
856 seekable.seekRelative(pts<0 and -1 or 1, abs(pts))
857 if abs(pts) > 100 and config.usage.show_infobar_on_skip.value:
861 if self.seekstate == self.SEEK_STATE_PLAY:
862 self.setSeekState(self.makeStateForward(int(config.seek.enter_forward.value)))
863 elif self.seekstate == self.SEEK_STATE_PAUSE:
864 if len(config.seek.speeds_slowmotion.value):
865 self.setSeekState(self.makeStateSlowMotion(config.seek.speeds_slowmotion.value[-1]))
867 self.setSeekState(self.makeStateForward(int(config.seek.enter_forward.value)))
868 elif self.seekstate == self.SEEK_STATE_EOF:
870 elif self.isStateForward(self.seekstate):
871 speed = self.seekstate[1]
872 if self.seekstate[2]:
873 speed /= self.seekstate[2]
874 speed = self.getHigher(speed, config.seek.speeds_forward.value) or config.seek.speeds_forward.value[-1]
875 self.setSeekState(self.makeStateForward(speed))
876 elif self.isStateBackward(self.seekstate):
877 speed = -self.seekstate[1]
878 if self.seekstate[2]:
879 speed /= self.seekstate[2]
880 speed = self.getLower(speed, config.seek.speeds_backward.value)
882 self.setSeekState(self.makeStateBackward(speed))
884 self.setSeekState(self.SEEK_STATE_PLAY)
885 elif self.isStateSlowMotion(self.seekstate):
886 speed = self.getLower(self.seekstate[2], config.seek.speeds_slowmotion.value) or config.seek.speeds_slowmotion.value[0]
887 self.setSeekState(self.makeStateSlowMotion(speed))
890 seekstate = self.seekstate
891 if seekstate == self.SEEK_STATE_PLAY:
892 self.setSeekState(self.makeStateBackward(int(config.seek.enter_backward.value)))
893 elif seekstate == self.SEEK_STATE_EOF:
894 self.setSeekState(self.makeStateBackward(int(config.seek.enter_backward.value)))
895 self.doSeekRelative(-6)
896 elif seekstate == self.SEEK_STATE_PAUSE:
897 self.doSeekRelative(-3)
898 elif self.isStateForward(seekstate):
901 speed /= seekstate[2]
902 speed = self.getLower(speed, config.seek.speeds_forward.value)
904 self.setSeekState(self.makeStateForward(speed))
906 self.setSeekState(self.SEEK_STATE_PLAY)
907 elif self.isStateBackward(seekstate):
908 speed = -seekstate[1]
910 speed /= seekstate[2]
911 speed = self.getHigher(speed, config.seek.speeds_backward.value) or config.seek.speeds_backward.value[-1]
912 self.setSeekState(self.makeStateBackward(speed))
913 elif self.isStateSlowMotion(seekstate):
914 speed = self.getHigher(seekstate[2], config.seek.speeds_slowmotion.value)
916 self.setSeekState(self.makeStateSlowMotion(speed))
918 self.setSeekState(self.SEEK_STATE_PAUSE)
920 def seekFwdManual(self):
921 self.session.openWithCallback(self.fwdSeekTo, MinuteInput)
923 def fwdSeekTo(self, minutes):
924 print "Seek", minutes, "minutes forward"
925 self.doSeekRelative(minutes * 60 * 90000)
927 def seekBackManual(self):
928 self.session.openWithCallback(self.rwdSeekTo, MinuteInput)
930 def rwdSeekTo(self, minutes):
932 self.doSeekRelative(-minutes * 60 * 90000)
934 def checkSkipShowHideLock(self):
935 wantlock = self.seekstate != self.SEEK_STATE_PLAY
937 if config.usage.show_infobar_on_skip.value:
938 if self.lockedBecauseOfSkipping and not wantlock:
940 self.lockedBecauseOfSkipping = False
942 if wantlock and not self.lockedBecauseOfSkipping:
944 self.lockedBecauseOfSkipping = True
946 def calcRemainingTime(self):
947 seekable = self.getSeek()
948 if seekable is not None:
949 len = seekable.getLength()
951 tmp = self.cueGetEndCutPosition()
956 pos = seekable.getPlayPosition()
957 speednom = self.seekstate[1] or 1
958 speedden = self.seekstate[2] or 1
959 if not len[0] and not pos[0]:
962 time = (len[1] - pos[1])*speedden/(90*speednom)
967 if self.eofState == 0 and self.seekstate != self.SEEK_STATE_EOF:
969 time = self.calcRemainingTime()
971 time = 3000 # Failed to calc, use default
973 time = 300 # Passed end, shortest wait
975 self.eofState = -2 # Too long, block eof
978 time += 1000 # Add margin
979 self.eofTimer.start(time, True)
981 def inhibitEof(self):
982 if self.eofState >= 1:
983 self.eofState = -self.eofState
988 if self.seekstate == self.SEEK_STATE_EOF:
990 if self.eofState == -2 or self.isStateBackward(self.seekstate):
994 # if we are seeking, we try to end up ~1s before the end, and pause there.
995 eofstate = self.eofState
996 seekstate = self.seekstate
998 if not self.seekstate == self.SEEK_STATE_PAUSE:
999 self.setSeekState(self.SEEK_STATE_EOF)
1000 if eofstate == -1 or not seekstate in (self.SEEK_STATE_PLAY, self.SEEK_STATE_PAUSE):
1001 seekable = self.getSeek()
1002 if seekable is not None:
1004 if eofstate == 1 and seekstate == self.SEEK_STATE_PLAY:
1005 self.doEofInternal(True)
1007 self.doEofInternal(False)
1009 def doEofInternal(self, playing):
1010 pass # Defined in subclasses
1013 self.setSeekState(self.SEEK_STATE_PLAY)
1016 from Screens.PVRState import PVRState, TimeshiftState
1018 class InfoBarPVRState:
1019 def __init__(self, screen=PVRState, force_show = False):
1020 self.onPlayStateChanged.append(self.__playStateChanged)
1021 self.pvrStateDialog = self.session.instantiateDialog(screen)
1022 self.onShow.append(self._mayShow)
1023 self.onHide.append(self.pvrStateDialog.hide)
1024 self.force_show = force_show
1027 if self.execing and self.seekstate != self.SEEK_STATE_PLAY:
1028 self.pvrStateDialog.show()
1030 def __playStateChanged(self, state):
1031 playstateString = state[3]
1032 self.pvrStateDialog["state"].setText(playstateString)
1034 # if we return into "PLAY" state, ensure that the dialog gets hidden if there will be no infobar displayed
1035 if not config.usage.show_infobar_on_skip.value and self.seekstate == self.SEEK_STATE_PLAY and not self.force_show:
1036 self.pvrStateDialog.hide()
1041 class InfoBarTimeshiftState(InfoBarPVRState):
1043 InfoBarPVRState.__init__(self, screen=TimeshiftState, force_show = True)
1046 if self.execing and self.timeshift_enabled and self.seekstate != self.SEEK_STATE_PLAY:
1047 self.pvrStateDialog.show()
1049 class InfoBarShowMovies:
1051 # i don't really like this class.
1052 # it calls a not further specified "movie list" on up/down/movieList,
1053 # so this is not more than an action map
1055 self["MovieListActions"] = HelpableActionMap(self, "InfobarMovieListActions",
1057 "movieList": (self.showMovies, _("movie list")),
1058 "up": (self.showMovies, _("movie list")),
1059 "down": (self.showMovies, _("movie list"))
1062 # InfoBarTimeshift requires InfoBarSeek, instantiated BEFORE!
1066 # Timeshift works the following way:
1067 # demux0 demux1 "TimeshiftActions" "TimeshiftActivateActions" "SeekActions"
1068 # - normal playback TUNER unused PLAY enable disable disable
1069 # - user presses "yellow" button. FILE record PAUSE enable disable enable
1070 # - user presess pause again FILE record PLAY enable disable enable
1071 # - user fast forwards FILE record FF enable disable enable
1072 # - end of timeshift buffer reached TUNER record PLAY enable enable disable
1073 # - user backwards FILE record BACK # !! enable disable enable
1077 # - when a service is playing, pressing the "timeshiftStart" button ("yellow") enables recording ("enables timeshift"),
1078 # freezes the picture (to indicate timeshift), sets timeshiftMode ("activates timeshift")
1079 # now, the service becomes seekable, so "SeekActions" are enabled, "TimeshiftEnableActions" are disabled.
1080 # - the user can now PVR around
1081 # - if it hits the end, the service goes into live mode ("deactivates timeshift", it's of course still "enabled")
1082 # the service looses it's "seekable" state. It can still be paused, but just to activate timeshift right
1084 # the seek actions will be disabled, but the timeshiftActivateActions will be enabled
1085 # - if the user rewinds, or press pause, timeshift will be activated again
1087 # note that a timeshift can be enabled ("recording") and
1088 # activated (currently time-shifting).
1090 class InfoBarTimeshift:
1092 self["TimeshiftActions"] = HelpableActionMap(self, "InfobarTimeshiftActions",
1094 "timeshiftStart": (self.startTimeshift, _("start timeshift")), # the "yellow key"
1095 "timeshiftStop": (self.stopTimeshift, _("stop timeshift")) # currently undefined :), probably 'TV'
1097 self["TimeshiftActivateActions"] = ActionMap(["InfobarTimeshiftActivateActions"],
1099 "timeshiftActivateEnd": self.activateTimeshiftEnd, # something like "rewind key"
1100 "timeshiftActivateEndAndPause": self.activateTimeshiftEndAndPause # something like "pause key"
1101 }, prio=-1) # priority over record
1103 self.timeshift_enabled = 0
1104 self.timeshift_state = 0
1105 self.ts_rewind_timer = eTimer()
1106 self.ts_rewind_timer.callback.append(self.rewindService)
1108 self.__event_tracker = ServiceEventTracker(screen=self, eventmap=
1110 iPlayableService.evStart: self.__serviceStarted,
1111 iPlayableService.evSeekableStatusChanged: self.__seekableStatusChanged
1114 def getTimeshift(self):
1115 service = self.session.nav.getCurrentService()
1116 return service and service.timeshift()
1118 def startTimeshift(self):
1119 print "enable timeshift"
1120 ts = self.getTimeshift()
1122 self.session.open(MessageBox, _("Timeshift not possible!"), MessageBox.TYPE_ERROR)
1123 print "no ts interface"
1126 if self.timeshift_enabled:
1127 print "hu, timeshift already enabled?"
1129 if not ts.startTimeshift():
1130 self.timeshift_enabled = 1
1132 # we remove the "relative time" for now.
1133 #self.pvrStateDialog["timeshift"].setRelative(time.time())
1136 #self.setSeekState(self.SEEK_STATE_PAUSE)
1137 self.activateTimeshiftEnd(False)
1139 # enable the "TimeshiftEnableActions", which will override
1140 # the startTimeshift actions
1141 self.__seekableStatusChanged()
1143 print "timeshift failed"
1145 def stopTimeshift(self):
1146 if not self.timeshift_enabled:
1148 print "disable timeshift"
1149 ts = self.getTimeshift()
1152 self.session.openWithCallback(self.stopTimeshiftConfirmed, MessageBox, _("Stop Timeshift?"), MessageBox.TYPE_YESNO)
1154 def stopTimeshiftConfirmed(self, confirmed):
1158 ts = self.getTimeshift()
1163 self.timeshift_enabled = 0
1166 self.__seekableStatusChanged()
1168 # activates timeshift, and seeks to (almost) the end
1169 def activateTimeshiftEnd(self, back = True):
1170 ts = self.getTimeshift()
1171 print "activateTimeshiftEnd"
1176 if ts.isTimeshiftActive():
1177 print "!! activate timeshift called - but shouldn't this be a normal pause?"
1181 ts.activateTimeshift() # activate timeshift will automatically pause
1182 self.setSeekState(self.SEEK_STATE_PAUSE)
1185 self.doSeek(-5) # seek some gops before end
1186 self.ts_rewind_timer.start(200, 1)
1188 self.doSeek(-1) # seek 1 gop before end
1190 def rewindService(self):
1191 self.setSeekState(self.makeStateBackward(int(config.seek.enter_backward.value)))
1193 # same as activateTimeshiftEnd, but pauses afterwards.
1194 def activateTimeshiftEndAndPause(self):
1195 print "activateTimeshiftEndAndPause"
1196 #state = self.seekstate
1197 self.activateTimeshiftEnd(False)
1199 def __seekableStatusChanged(self):
1202 # print "self.isSeekable", self.isSeekable()
1203 # print "self.timeshift_enabled", self.timeshift_enabled
1205 # when this service is not seekable, but timeshift
1206 # is enabled, this means we can activate
1208 if not self.isSeekable() and self.timeshift_enabled:
1211 # print "timeshift activate:", enabled
1212 self["TimeshiftActivateActions"].setEnabled(enabled)
1214 def __serviceStarted(self):
1215 self.timeshift_enabled = False
1216 self.__seekableStatusChanged()
1218 from Screens.PiPSetup import PiPSetup
1220 class InfoBarExtensions:
1221 EXTENSION_SINGLE = 0
1227 self["InstantExtensionsActions"] = HelpableActionMap(self, "InfobarExtensions",
1229 "extensions": (self.showExtensionSelection, _("view extensions...")),
1230 }, 1) # lower priority
1232 def addExtension(self, extension, key = None, type = EXTENSION_SINGLE):
1233 self.list.append((type, extension, key))
1235 def updateExtension(self, extension, key = None):
1236 self.extensionsList.append(extension)
1238 if self.extensionKeys.has_key(key):
1242 for x in self.availableKeys:
1243 if not self.extensionKeys.has_key(x):
1248 self.extensionKeys[key] = len(self.extensionsList) - 1
1250 def updateExtensions(self):
1251 self.extensionsList = []
1252 self.availableKeys = [ "1", "2", "3", "4", "5", "6", "7", "8", "9", "0", "red", "green", "yellow", "blue" ]
1253 self.extensionKeys = {}
1255 if x[0] == self.EXTENSION_SINGLE:
1256 self.updateExtension(x[1], x[2])
1259 self.updateExtension(y[0], y[1])
1262 def showExtensionSelection(self):
1263 self.updateExtensions()
1264 extensionsList = self.extensionsList[:]
1267 for x in self.availableKeys:
1268 if self.extensionKeys.has_key(x):
1269 entry = self.extensionKeys[x]
1270 extension = self.extensionsList[entry]
1272 name = str(extension[0]())
1273 list.append((extension[0](), extension))
1275 extensionsList.remove(extension)
1277 extensionsList.remove(extension)
1278 list.extend([(x[0](), x) for x in extensionsList])
1280 keys += [""] * len(extensionsList)
1281 self.session.openWithCallback(self.extensionCallback, ChoiceBox, title=_("Please choose an extension..."), list = list, keys = keys)
1283 def extensionCallback(self, answer):
1284 if answer is not None:
1287 from Tools.BoundFunction import boundFunction
1289 # depends on InfoBarExtensions
1291 class InfoBarPlugins:
1293 self.addExtension(extension = self.getPluginList, type = InfoBarExtensions.EXTENSION_LIST)
1295 def getPluginName(self, name):
1298 def getPluginList(self):
1299 return [((boundFunction(self.getPluginName, p.name), boundFunction(self.runPlugin, p), lambda: True), None) for p in plugins.getPlugins(where = PluginDescriptor.WHERE_EXTENSIONSMENU)]
1301 def runPlugin(self, plugin):
1302 if isinstance(self, InfoBarChannelSelection):
1303 plugin(session = self.session, servicelist = self.servicelist)
1305 plugin(session = self.session)
1307 from Components.Task import job_manager
1308 class InfoBarJobman:
1310 self.addExtension(extension = self.getJobList, type = InfoBarExtensions.EXTENSION_LIST)
1312 def getJobList(self):
1313 return [((boundFunction(self.getJobName, job), boundFunction(self.showJobView, job), lambda: True), None) for job in job_manager.getPendingJobs()]
1315 def getJobName(self, job):
1316 return "%s: %s (%d%%)" % (job.getStatustext(), job.name, int(100*job.progress/float(job.end)))
1318 def showJobView(self, job):
1319 from Screens.TaskView import JobView
1320 job_manager.in_background = False
1321 self.session.openWithCallback(self.JobViewCB, JobView, job)
1323 def JobViewCB(self, in_background):
1324 job_manager.in_background = in_background
1326 # depends on InfoBarExtensions
1327 class InfoBarSleepTimer:
1329 self.addExtension((self.getSleepTimerName, self.showSleepTimerSetup, lambda: True), "1")
1331 def getSleepTimerName(self):
1332 return _("Sleep Timer")
1334 def showSleepTimerSetup(self):
1335 self.session.open(SleepTimerEdit)
1337 # depends on InfoBarExtensions
1340 self.session.pipshown = False
1341 if SystemInfo.get("NumVideoDecoders", 1) > 1:
1342 self.addExtension((self.getShowHideName, self.showPiP, lambda: True), "blue")
1343 self.addExtension((self.getMoveName, self.movePiP, self.pipShown), "green")
1344 self.addExtension((self.getSwapName, self.swapPiP, self.pipShown), "yellow")
1347 return self.session.pipshown
1349 def pipHandles0Action(self):
1350 return self.pipShown() and config.usage.pip_zero_button.value != "standard"
1352 def getShowHideName(self):
1353 if self.session.pipshown:
1354 return _("Disable Picture in Picture")
1356 return _("Activate Picture in Picture")
1358 def getSwapName(self):
1359 return _("Swap Services")
1361 def getMoveName(self):
1362 return _("Move Picture in Picture")
1365 if self.session.pipshown:
1366 del self.session.pip
1367 self.session.pipshown = False
1369 self.session.pip = self.session.instantiateDialog(PictureInPicture)
1370 self.session.pip.show()
1371 newservice = self.session.nav.getCurrentlyPlayingServiceReference()
1372 if self.session.pip.playService(newservice):
1373 self.session.pipshown = True
1374 self.session.pip.servicePath = self.servicelist.getCurrentServicePath()
1376 self.session.pipshown = False
1377 del self.session.pip
1378 self.session.nav.playService(newservice)
1381 swapservice = self.session.nav.getCurrentlyPlayingServiceReference()
1382 if self.session.pip.servicePath:
1383 servicepath = self.servicelist.getCurrentServicePath()
1384 ref=servicepath[len(servicepath)-1]
1385 pipref=self.session.pip.getCurrentService()
1386 self.session.pip.playService(swapservice)
1387 self.servicelist.setCurrentServicePath(self.session.pip.servicePath)
1388 if pipref.toString() != ref.toString(): # is a subservice ?
1389 self.session.nav.stopService() # stop portal
1390 self.session.nav.playService(pipref) # start subservice
1391 self.session.pip.servicePath=servicepath
1394 self.session.open(PiPSetup, pip = self.session.pip)
1396 def pipDoHandle0Action(self):
1397 use = config.usage.pip_zero_button.value
1400 elif "swapstop" == use:
1406 from RecordTimer import parseEvent, RecordTimerEntry
1408 class InfoBarInstantRecord:
1409 """Instant Record - handles the instantRecord action in order to
1410 start/stop instant records"""
1412 self["InstantRecordActions"] = HelpableActionMap(self, "InfobarInstantRecord",
1414 "instantRecord": (self.instantRecord, _("Instant Record...")),
1418 def stopCurrentRecording(self, entry = -1):
1419 if entry is not None and entry != -1:
1420 self.session.nav.RecordTimer.removeEntry(self.recording[entry])
1421 self.recording.remove(self.recording[entry])
1423 def startInstantRecording(self, limitEvent = False):
1424 serviceref = self.session.nav.getCurrentlyPlayingServiceReference()
1426 # try to get event info
1429 service = self.session.nav.getCurrentService()
1430 epg = eEPGCache.getInstance()
1431 event = epg.lookupEventTime(serviceref, -1, 0)
1433 info = service.info()
1434 ev = info.getEvent(0)
1440 end = begin + 3600 # dummy
1441 name = "instant record"
1445 if event is not None:
1446 curEvent = parseEvent(event)
1448 description = curEvent[3]
1449 eventid = curEvent[4]
1454 self.session.open(MessageBox, _("No event info found, recording indefinitely."), MessageBox.TYPE_INFO)
1456 if isinstance(serviceref, eServiceReference):
1457 serviceref = ServiceReference(serviceref)
1459 recording = RecordTimerEntry(serviceref, begin, end, name, description, eventid, dirname = config.movielist.last_videodir.value)
1460 recording.dontSave = True
1462 if event is None or limitEvent == False:
1463 recording.autoincrease = True
1464 if recording.setAutoincreaseEnd():
1465 self.session.nav.RecordTimer.record(recording)
1466 self.recording.append(recording)
1468 simulTimerList = self.session.nav.RecordTimer.record(recording)
1469 if simulTimerList is not None: # conflict with other recording
1470 name = simulTimerList[1].name
1471 name_date = ' '.join((name, strftime('%c', localtime(simulTimerList[1].begin))))
1472 print "[TIMER] conflicts with", name_date
1473 recording.autoincrease = True # start with max available length, then increment
1474 if recording.setAutoincreaseEnd():
1475 self.session.nav.RecordTimer.record(recording)
1476 self.recording.append(recording)
1477 self.session.open(MessageBox, _("Record time limited due to conflicting timer %s") % name_date, MessageBox.TYPE_INFO)
1479 self.session.open(MessageBox, _("Couldn't record due to conflicting timer %s") % name, MessageBox.TYPE_INFO)
1480 recording.autoincrease = False
1482 self.recording.append(recording)
1484 def isInstantRecordRunning(self):
1485 print "self.recording:", self.recording
1487 for x in self.recording:
1492 def recordQuestionCallback(self, answer):
1493 print "pre:\n", self.recording
1495 if answer is None or answer[1] == "no":
1498 recording = self.recording[:]
1500 if not x in self.session.nav.RecordTimer.timer_list:
1501 self.recording.remove(x)
1502 elif x.dontSave and x.isRunning():
1503 list.append((x, False))
1505 if answer[1] == "changeduration":
1506 if len(self.recording) == 1:
1507 self.changeDuration(0)
1509 self.session.openWithCallback(self.changeDuration, TimerSelection, list)
1510 elif answer[1] == "changeendtime":
1511 if len(self.recording) == 1:
1514 self.session.openWithCallback(self.setEndtime, TimerSelection, list)
1515 elif answer[1] == "stop":
1516 if len(self.recording) == 1:
1517 self.stopCurrentRecording(0)
1519 self.session.openWithCallback(self.stopCurrentRecording, TimerSelection, list)
1520 elif answer[1] in ( "indefinitely" , "manualduration", "manualendtime", "event"):
1521 self.startInstantRecording(limitEvent = answer[1] in ("event", "manualendtime") or False)
1522 if answer[1] == "manualduration":
1523 self.changeDuration(len(self.recording)-1)
1524 elif answer[1] == "manualendtime":
1525 self.setEndtime(len(self.recording)-1)
1526 print "after:\n", self.recording
1528 def setEndtime(self, entry):
1529 if entry is not None:
1530 self.selectedEntry = entry
1531 self.endtime=ConfigClock(default = self.recording[self.selectedEntry].end)
1532 dlg = self.session.openWithCallback(self.TimeDateInputClosed, TimeDateInput, self.endtime)
1533 dlg.setTitle(_("Please change recording endtime"))
1535 def TimeDateInputClosed(self, ret):
1538 localendtime = localtime(ret[1])
1539 print "stopping recording at", strftime("%c", localendtime)
1540 if self.recording[self.selectedEntry].end != ret[1]:
1541 self.recording[self.selectedEntry].autoincrease = False
1542 self.recording[self.selectedEntry].end = ret[1]
1543 self.session.nav.RecordTimer.timeChanged(self.recording[self.selectedEntry])
1545 def changeDuration(self, entry):
1546 if entry is not None:
1547 self.selectedEntry = entry
1548 self.session.openWithCallback(self.inputCallback, InputBox, title=_("How many minutes do you want to record?"), text="5", maxSize=False, type=Input.NUMBER)
1550 def inputCallback(self, value):
1551 if value is not None:
1552 print "stopping recording after", int(value), "minutes."
1553 entry = self.recording[self.selectedEntry]
1555 entry.autoincrease = False
1556 entry.end = int(time()) + 60 * int(value)
1557 self.session.nav.RecordTimer.timeChanged(entry)
1559 def instantRecord(self):
1560 dir = config.movielist.last_videodir.value
1561 if not fileExists(dir, 'w'):
1562 dir = resolveFilename(SCOPE_HDD)
1566 # XXX: this message is a little odd as we might be recording to a remote device
1567 self.session.open(MessageBox, _("No HDD found or HDD not initialized!"), MessageBox.TYPE_ERROR)
1570 if self.isInstantRecordRunning():
1571 self.session.openWithCallback(self.recordQuestionCallback, ChoiceBox, \
1572 title=_("A recording is currently running.\nWhat do you want to do?"), \
1573 list=((_("add recording (stop after current event)"), "event"), \
1574 (_("add recording (enter recording duration)"), "manualduration"), \
1575 (_("add recording (enter recording endtime)"), "manualendtime"), \
1576 (_("add recording (indefinitely)"), "indefinitely"), \
1577 (_("change recording (duration)"), "changeduration"), \
1578 (_("change recording (endtime)"), "changeendtime"), \
1579 (_("stop recording"), "stop"), \
1580 (_("do nothing"), "no")))
1582 self.session.openWithCallback(self.recordQuestionCallback, ChoiceBox, \
1583 title=_("Start recording?"), \
1584 list=((_("add recording (stop after current event)"), "event"), \
1585 (_("add recording (enter recording duration)"), "manualduration"), \
1586 (_("add recording (enter recording endtime)"), "manualendtime"), \
1587 (_("add recording (indefinitely)"), "indefinitely"), \
1588 (_("don't record"), "no")))
1590 from Tools.ISO639 import LanguageCodes
1592 class InfoBarAudioSelection:
1594 self["AudioSelectionAction"] = HelpableActionMap(self, "InfobarAudioSelectionActions",
1596 "audioSelection": (self.audioSelection, _("Audio Options...")),
1599 def audioSelection(self):
1600 service = self.session.nav.getCurrentService()
1601 self.audioTracks = audio = service and service.audioTracks()
1602 n = audio and audio.getNumberOfTracks() or 0
1605 self.audioChannel = service.audioChannel()
1609 i = audio.getTrackInfo(idx)
1610 language = i.getLanguage()
1611 description = i.getDescription()
1613 if LanguageCodes.has_key(language):
1614 language = LanguageCodes[language][0]
1616 if len(description):
1617 description += " (" + language + ")"
1619 description = language
1621 tlist.append((description, idx))
1624 tlist.sort(key=lambda x: x[0])
1626 selectedAudio = self.audioTracks.getCurrentTrack()
1631 if x[1] != selectedAudio:
1636 if SystemInfo["CanDownmixAC3"]:
1637 tlist = [(_("AC3 downmix") + " - " +(_("Off"), _("On"))[config.av.downmix_ac3.value and 1 or 0], "CALLFUNC", self.changeAC3Downmix),
1638 ((_("Left"), _("Stereo"), _("Right"))[self.audioChannel.getCurrentChannel()], "mode"),
1640 keys = [ "red", "green", "", "1", "2", "3", "4", "5", "6", "7", "8", "9", "0"] + [""]*n
1643 tlist = [((_("Left"), _("Stereo"), _("Right"))[self.audioChannel.getCurrentChannel()], "mode"), ("--", "")] + tlist
1644 keys = [ "red", "", "1", "2", "3", "4", "5", "6", "7", "8", "9", "0"] + [""]*n
1646 self.session.openWithCallback(self.audioSelected, ChoiceBox, title=_("Select audio track"), list = tlist, selection = selection, keys = keys)
1648 del self.audioTracks
1650 def changeAC3Downmix(self, arg):
1651 choicelist = self.session.current_dialog["list"]
1652 list = choicelist.list
1654 list[0][1]=(t[0], t[1], t[2], t[3], t[4], t[5], t[6],
1655 _("AC3 downmix") + " - " + (_("On"), _("Off"))[config.av.downmix_ac3.value and 1 or 0])
1656 choicelist.setList(list)
1657 if config.av.downmix_ac3.value:
1658 config.av.downmix_ac3.value = False
1660 config.av.downmix_ac3.value = True
1661 config.av.downmix_ac3.save()
1663 def audioSelected(self, audio):
1664 if audio is not None:
1665 if isinstance(audio[1], str):
1666 if audio[1] == "mode":
1667 keys = ["red", "green", "yellow"]
1668 selection = self.audioChannel.getCurrentChannel()
1669 tlist = ((_("left"), 0), (_("stereo"), 1), (_("right"), 2))
1670 self.session.openWithCallback(self.modeSelected, ChoiceBox, title=_("Select audio mode"), list = tlist, selection = selection, keys = keys)
1672 del self.audioChannel
1673 if self.session.nav.getCurrentService().audioTracks().getNumberOfTracks() > audio[1]:
1674 self.audioTracks.selectTrack(audio[1])
1676 del self.audioChannel
1677 del self.audioTracks
1679 def modeSelected(self, mode):
1680 if mode is not None:
1681 self.audioChannel.selectChannel(mode[1])
1682 del self.audioChannel
1684 class InfoBarSubserviceSelection:
1686 self["SubserviceSelectionAction"] = HelpableActionMap(self, "InfobarSubserviceSelectionActions",
1688 "subserviceSelection": (self.subserviceSelection, _("Subservice list...")),
1691 self["SubserviceQuickzapAction"] = HelpableActionMap(self, "InfobarSubserviceQuickzapActions",
1693 "nextSubservice": (self.nextSubservice, _("Switch to next subservice")),
1694 "prevSubservice": (self.prevSubservice, _("Switch to previous subservice"))
1696 self["SubserviceQuickzapAction"].setEnabled(False)
1698 self.__event_tracker = ServiceEventTracker(screen=self, eventmap=
1700 iPlayableService.evUpdatedEventInfo: self.checkSubservicesAvail
1705 def checkSubservicesAvail(self):
1706 service = self.session.nav.getCurrentService()
1707 subservices = service and service.subServices()
1708 if not subservices or subservices.getNumberOfSubservices() == 0:
1709 self["SubserviceQuickzapAction"].setEnabled(False)
1711 def nextSubservice(self):
1712 self.changeSubservice(+1)
1714 def prevSubservice(self):
1715 self.changeSubservice(-1)
1717 def changeSubservice(self, direction):
1718 service = self.session.nav.getCurrentService()
1719 subservices = service and service.subServices()
1720 n = subservices and subservices.getNumberOfSubservices()
1723 ref = self.session.nav.getCurrentlyPlayingServiceReference()
1726 if subservices.getSubservice(idx).toString() == ref.toString():
1731 selection += direction
1736 newservice = subservices.getSubservice(selection)
1737 if newservice.valid():
1740 self.session.nav.playService(newservice, False)
1742 def subserviceSelection(self):
1743 service = self.session.nav.getCurrentService()
1744 subservices = service and service.subServices()
1745 self.bouquets = self.servicelist.getBouquetList()
1746 n = subservices and subservices.getNumberOfSubservices()
1749 ref = self.session.nav.getCurrentlyPlayingServiceReference()
1753 i = subservices.getSubservice(idx)
1754 if i.toString() == ref.toString():
1756 tlist.append((i.getName(), i))
1759 if self.bouquets and len(self.bouquets):
1760 keys = ["red", "blue", "", "0", "1", "2", "3", "4", "5", "6", "7", "8", "9" ] + [""] * n
1761 if config.usage.multibouquet.value:
1762 tlist = [(_("Quickzap"), "quickzap", service.subServices()), (_("Add to bouquet"), "CALLFUNC", self.addSubserviceToBouquetCallback), ("--", "")] + tlist
1764 tlist = [(_("Quickzap"), "quickzap", service.subServices()), (_("Add to favourites"), "CALLFUNC", self.addSubserviceToBouquetCallback), ("--", "")] + tlist
1767 tlist = [(_("Quickzap"), "quickzap", service.subServices()), ("--", "")] + tlist
1768 keys = ["red", "", "0", "1", "2", "3", "4", "5", "6", "7", "8", "9" ] + [""] * n
1771 self.session.openWithCallback(self.subserviceSelected, ChoiceBox, title=_("Please select a subservice..."), list = tlist, selection = selection, keys = keys)
1773 def subserviceSelected(self, service):
1775 if not service is None:
1776 if isinstance(service[1], str):
1777 if service[1] == "quickzap":
1778 from Screens.SubservicesQuickzap import SubservicesQuickzap
1779 self.session.open(SubservicesQuickzap, service[2])
1781 self["SubserviceQuickzapAction"].setEnabled(True)
1782 self.session.nav.playService(service[1], False)
1784 def addSubserviceToBouquetCallback(self, service):
1785 if len(service) > 1 and isinstance(service[1], eServiceReference):
1786 self.selectedSubservice = service
1787 if self.bouquets is None:
1790 cnt = len(self.bouquets)
1791 if cnt > 1: # show bouquet list
1792 self.bsel = self.session.openWithCallback(self.bouquetSelClosed, BouquetSelector, self.bouquets, self.addSubserviceToBouquet)
1793 elif cnt == 1: # add to only one existing bouquet
1794 self.addSubserviceToBouquet(self.bouquets[0][1])
1795 self.session.open(MessageBox, _("Service has been added to the favourites."), MessageBox.TYPE_INFO)
1797 def bouquetSelClosed(self, confirmed):
1799 del self.selectedSubservice
1801 self.session.open(MessageBox, _("Service has been added to the selected bouquet."), MessageBox.TYPE_INFO)
1803 def addSubserviceToBouquet(self, dest):
1804 self.servicelist.addServiceToBouquet(dest, self.selectedSubservice[1])
1806 self.bsel.close(True)
1808 del self.selectedSubservice
1810 class InfoBarAdditionalInfo:
1813 self["RecordingPossible"] = Boolean(fixed=harddiskmanager.HDDCount() > 0 and config.misc.rcused.value == 1)
1814 self["TimeshiftPossible"] = self["RecordingPossible"]
1815 self["ShowTimeshiftOnYellow"] = Boolean(fixed=(not config.misc.rcused.value == 0))
1816 self["ShowAudioOnYellow"] = Boolean(fixed=config.misc.rcused.value == 0)
1817 self["ShowRecordOnRed"] = Boolean(fixed=config.misc.rcused.value == 1)
1818 self["ExtensionsAvailable"] = Boolean(fixed=1)
1820 class InfoBarNotifications:
1822 self.onExecBegin.append(self.checkNotifications)
1823 Notifications.notificationAdded.append(self.checkNotificationsIfExecing)
1824 self.onClose.append(self.__removeNotification)
1826 def __removeNotification(self):
1827 Notifications.notificationAdded.remove(self.checkNotificationsIfExecing)
1829 def checkNotificationsIfExecing(self):
1831 self.checkNotifications()
1833 def checkNotifications(self):
1834 notifications = Notifications.notifications
1836 n = notifications[0]
1838 del notifications[0]
1841 if n[3].has_key("onSessionOpenCallback"):
1842 n[3]["onSessionOpenCallback"]()
1843 del n[3]["onSessionOpenCallback"]
1846 dlg = self.session.openWithCallback(cb, n[1], *n[2], **n[3])
1848 dlg = self.session.open(n[1], *n[2], **n[3])
1850 # remember that this notification is currently active
1852 Notifications.current_notifications.append(d)
1853 dlg.onClose.append(boundFunction(self.__notificationClosed, d))
1855 def __notificationClosed(self, d):
1856 Notifications.current_notifications.remove(d)
1858 class InfoBarServiceNotifications:
1860 self.__event_tracker = ServiceEventTracker(screen=self, eventmap=
1862 iPlayableService.evEnd: self.serviceHasEnded
1865 def serviceHasEnded(self):
1866 print "service end!"
1869 self.setSeekState(self.SEEK_STATE_PLAY)
1873 class InfoBarCueSheetSupport:
1879 ENABLE_RESUME_SUPPORT = False
1881 def __init__(self, actionmap = "InfobarCueSheetActions"):
1882 self["CueSheetActions"] = HelpableActionMap(self, actionmap,
1884 "jumpPreviousMark": (self.jumpPreviousMark, _("jump to previous marked position")),
1885 "jumpNextMark": (self.jumpNextMark, _("jump to next marked position")),
1886 "toggleMark": (self.toggleMark, _("toggle a cut mark at the current position"))
1890 self.is_closing = False
1891 self.__event_tracker = ServiceEventTracker(screen=self, eventmap=
1893 iPlayableService.evStart: self.__serviceStarted,
1896 def __serviceStarted(self):
1899 print "new service started! trying to download cuts!"
1900 self.downloadCuesheet()
1902 if self.ENABLE_RESUME_SUPPORT:
1905 for (pts, what) in self.cut_list:
1906 if what == self.CUT_TYPE_LAST:
1909 if last is not None:
1910 self.resume_point = last
1911 if config.usage.on_movie_start.value == "ask":
1912 Notifications.AddNotificationWithCallback(self.playLastCB, MessageBox, _("Do you want to resume this playback?"), timeout=10)
1913 elif config.usage.on_movie_start.value == "resume":
1914 # TRANSLATORS: The string "Resuming playback" flashes for a moment
1915 # TRANSLATORS: at the start of a movie, when the user has selected
1916 # TRANSLATORS: "Resume from last position" as start behavior.
1917 # TRANSLATORS: The purpose is to notify the user that the movie starts
1918 # TRANSLATORS: in the middle somewhere and not from the beginning.
1919 # TRANSLATORS: (Some translators seem to have interpreted it as a
1920 # TRANSLATORS: question or a choice, but it is a statement.)
1921 Notifications.AddNotificationWithCallback(self.playLastCB, MessageBox, _("Resuming playback"), timeout=2, type=MessageBox.TYPE_INFO)
1923 def playLastCB(self, answer):
1925 self.doSeek(self.resume_point)
1926 self.hideAfterResume()
1928 def hideAfterResume(self):
1929 if isinstance(self, InfoBarShowHide):
1932 def __getSeekable(self):
1933 service = self.session.nav.getCurrentService()
1936 return service.seek()
1938 def cueGetCurrentPosition(self):
1939 seek = self.__getSeekable()
1942 r = seek.getPlayPosition()
1947 def cueGetEndCutPosition(self):
1950 for cp in self.cut_list:
1951 if cp[1] == self.CUT_TYPE_OUT:
1955 elif cp[1] == self.CUT_TYPE_IN:
1959 def jumpPreviousNextMark(self, cmp, start=False):
1960 current_pos = self.cueGetCurrentPosition()
1961 if current_pos is None:
1963 mark = self.getNearestCutPoint(current_pos, cmp=cmp, start=start)
1964 if mark is not None:
1972 def jumpPreviousMark(self):
1973 # we add 2 seconds, so if the play position is <2s after
1974 # the mark, the mark before will be used
1975 self.jumpPreviousNextMark(lambda x: -x-5*90000, start=True)
1977 def jumpNextMark(self):
1978 if not self.jumpPreviousNextMark(lambda x: x):
1981 def getNearestCutPoint(self, pts, cmp=abs, start=False):
1987 bestdiff = cmp(0 - pts)
1989 nearest = [0, False]
1990 for cp in self.cut_list:
1991 if beforecut and cp[1] in (self.CUT_TYPE_IN, self.CUT_TYPE_OUT):
1993 if cp[1] == self.CUT_TYPE_IN: # Start is here, disregard previous marks
1994 diff = cmp(cp[0] - pts)
2000 if cp[1] in (self.CUT_TYPE_MARK, self.CUT_TYPE_LAST):
2001 diff = cmp(cp[0] - pts)
2002 if diff >= 0 and (nearest is None or bestdiff > diff):
2007 def toggleMark(self, onlyremove=False, onlyadd=False, tolerance=5*90000, onlyreturn=False):
2008 current_pos = self.cueGetCurrentPosition()
2009 if current_pos is None:
2010 print "not seekable"
2013 nearest_cutpoint = self.getNearestCutPoint(current_pos)
2015 if nearest_cutpoint is not None and abs(nearest_cutpoint[0] - current_pos) < tolerance:
2017 return nearest_cutpoint
2019 self.removeMark(nearest_cutpoint)
2020 elif not onlyremove and not onlyreturn:
2021 self.addMark((current_pos, self.CUT_TYPE_MARK))
2026 def addMark(self, point):
2027 insort(self.cut_list, point)
2028 self.uploadCuesheet()
2029 self.showAfterCuesheetOperation()
2031 def removeMark(self, point):
2032 self.cut_list.remove(point)
2033 self.uploadCuesheet()
2034 self.showAfterCuesheetOperation()
2036 def showAfterCuesheetOperation(self):
2037 if isinstance(self, InfoBarShowHide):
2040 def __getCuesheet(self):
2041 service = self.session.nav.getCurrentService()
2044 return service.cueSheet()
2046 def uploadCuesheet(self):
2047 cue = self.__getCuesheet()
2050 print "upload failed, no cuesheet interface"
2052 cue.setCutList(self.cut_list)
2054 def downloadCuesheet(self):
2055 cue = self.__getCuesheet()
2058 print "download failed, no cuesheet interface"
2061 self.cut_list = cue.getCutList()
2063 class InfoBarSummary(Screen):
2065 <screen position="0,0" size="132,64">
2066 <widget source="global.CurrentTime" render="Label" position="62,46" size="82,18" font="Regular;16" >
2067 <convert type="ClockToText">WithSeconds</convert>
2069 <widget source="session.RecordState" render="FixedLabel" text=" " position="62,46" size="82,18" zPosition="1" >
2070 <convert type="ConfigEntryTest">config.usage.blinking_display_clock_during_recording,True,CheckSourceBoolean</convert>
2071 <convert type="ConditionalShowHide">Blink</convert>
2073 <widget source="session.CurrentService" render="Label" position="6,4" size="120,42" font="Regular;18" >
2074 <convert type="ServiceName">Name</convert>
2076 <widget source="session.Event_Now" render="Progress" position="6,46" size="46,18" borderWidth="1" >
2077 <convert type="EventTime">Progress</convert>
2081 # for picon: (path="piconlcd" will use LCD picons)
2082 # <widget source="session.CurrentService" render="Picon" position="6,0" size="120,64" path="piconlcd" >
2083 # <convert type="ServiceName">Reference</convert>
2086 class InfoBarSummarySupport:
2090 def createSummary(self):
2091 return InfoBarSummary
2093 class InfoBarMoviePlayerSummary(Screen):
2095 <screen position="0,0" size="132,64">
2096 <widget source="global.CurrentTime" render="Label" position="62,46" size="64,18" font="Regular;16" halign="right" >
2097 <convert type="ClockToText">WithSeconds</convert>
2099 <widget source="session.RecordState" render="FixedLabel" text=" " position="62,46" size="64,18" zPosition="1" >
2100 <convert type="ConfigEntryTest">config.usage.blinking_display_clock_during_recording,True,CheckSourceBoolean</convert>
2101 <convert type="ConditionalShowHide">Blink</convert>
2103 <widget source="session.CurrentService" render="Label" position="6,4" size="120,42" font="Regular;18" >
2104 <convert type="ServiceName">Name</convert>
2106 <widget source="session.CurrentService" render="Progress" position="6,46" size="56,18" borderWidth="1" >
2107 <convert type="ServicePosition">Position</convert>
2111 class InfoBarMoviePlayerSummarySupport:
2115 def createSummary(self):
2116 return InfoBarMoviePlayerSummary
2118 class InfoBarTeletextPlugin:
2120 self.teletext_plugin = None
2122 for p in plugins.getPlugins(PluginDescriptor.WHERE_TELETEXT):
2123 self.teletext_plugin = p
2125 if self.teletext_plugin is not None:
2126 self["TeletextActions"] = HelpableActionMap(self, "InfobarTeletextActions",
2128 "startTeletext": (self.startTeletext, _("View teletext..."))
2131 print "no teletext plugin found!"
2133 def startTeletext(self):
2134 self.teletext_plugin(session=self.session, service=self.session.nav.getCurrentService())
2136 class InfoBarSubtitleSupport(object):
2138 object.__init__(self)
2139 self.subtitle_window = self.session.instantiateDialog(SubtitleDisplay)
2140 self.__subtitles_enabled = False
2142 self.__event_tracker = ServiceEventTracker(screen=self, eventmap=
2144 iPlayableService.evEnd: self.__serviceStopped,
2145 iPlayableService.evUpdatedInfo: self.__updatedInfo
2147 self.cached_subtitle_checked = False
2148 self.__selected_subtitle = None
2150 def __serviceStopped(self):
2151 self.subtitle_window.hide()
2152 self.__subtitles_enabled = False
2153 self.cached_subtitle_checked = False
2155 def __updatedInfo(self):
2156 if not self.cached_subtitle_checked:
2157 subtitle = self.getCurrentServiceSubtitle()
2158 self.cached_subtitle_checked = True
2159 self.__selected_subtitle = subtitle and subtitle.getCachedSubtitle()
2160 if self.__selected_subtitle:
2161 subtitle.enableSubtitles(self.subtitle_window.instance, self.selected_subtitle)
2162 self.subtitle_window.show()
2163 self.__subtitles_enabled = True
2165 def getCurrentServiceSubtitle(self):
2166 service = self.session.nav.getCurrentService()
2167 return service and service.subtitle()
2169 def setSubtitlesEnable(self, enable=True):
2170 subtitle = self.getCurrentServiceSubtitle()
2171 if enable and self.__selected_subtitle is not None:
2172 if subtitle and not self.__subtitles_enabled:
2173 subtitle.enableSubtitles(self.subtitle_window.instance, self.selected_subtitle)
2174 self.subtitle_window.show()
2175 self.__subtitles_enabled = True
2178 subtitle.disableSubtitles(self.subtitle_window.instance)
2179 self.__subtitles_enabled = False
2180 self.subtitle_window.hide()
2182 def setSelectedSubtitle(self, subtitle):
2183 self.__selected_subtitle = subtitle
2185 subtitles_enabled = property(lambda self: self.__subtitles_enabled, setSubtitlesEnable)
2186 selected_subtitle = property(lambda self: self.__selected_subtitle, setSelectedSubtitle)
2188 class InfoBarServiceErrorPopupSupport:
2190 self.__event_tracker = ServiceEventTracker(screen=self, eventmap=
2192 iPlayableService.evTuneFailed: self.__tuneFailed,
2193 iPlayableService.evStart: self.__serviceStarted
2195 self.__serviceStarted()
2197 def __serviceStarted(self):
2198 self.last_error = None
2199 Notifications.RemovePopup(id = "ZapError")
2201 def __tuneFailed(self):
2202 service = self.session.nav.getCurrentService()
2203 info = service and service.info()
2204 error = info and info.getInfo(iServiceInformation.sDVBState)
2206 if error == self.last_error:
2209 self.last_error = error
2212 eDVBServicePMTHandler.eventNoResources: _("No free tuner!"),
2213 eDVBServicePMTHandler.eventTuneFailed: _("Tune failed!"),
2214 eDVBServicePMTHandler.eventNoPAT: _("No data on transponder!\n(Timeout reading PAT)"),
2215 eDVBServicePMTHandler.eventNoPATEntry: _("Service not found!\n(SID not found in PAT)"),
2216 eDVBServicePMTHandler.eventNoPMT: _("Service invalid!\n(Timeout reading PMT)"),
2217 eDVBServicePMTHandler.eventNewProgramInfo: None,
2218 eDVBServicePMTHandler.eventTuned: None,
2219 eDVBServicePMTHandler.eventSOF: None,
2220 eDVBServicePMTHandler.eventEOF: None,
2221 eDVBServicePMTHandler.eventMisconfiguration: _("Service unavailable!\nCheck tuner configuration!"),
2222 }.get(error) #this returns None when the key not exist in the dict
2224 if error is not None:
2225 Notifications.AddPopup(text = error, type = MessageBox.TYPE_ERROR, timeout = 5, id = "ZapError")
2227 Notifications.RemovePopup(id = "ZapError")