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.TimeDateInput import TimeDateInput
28 from ServiceReference import ServiceReference
30 from Tools import Notifications
31 from Tools.Directories import SCOPE_HDD, resolveFilename, fileExists
33 from enigma import eTimer, eServiceCenter, eDVBServicePMTHandler, iServiceInformation, \
34 iPlayableService, eServiceReference, eEPGCache
36 from time import time, localtime, strftime
37 from os import stat as os_stat
38 from bisect import insort
40 from RecordTimer import RecordTimerEntry, RecordTimer
43 from Menu import MainMenu, mdom
47 self.dishDialog = self.session.instantiateDialog(Dish)
49 class InfoBarShowHide:
50 """ InfoBar show/hide control, accepts toggleShow and hide actions, might start
58 self["ShowHideActions"] = ActionMap( ["InfobarShowHideActions"] ,
60 "toggleShow": self.toggleShow,
62 }, 1) # lower prio to make it possible to override ok and cancel..
64 self.__event_tracker = ServiceEventTracker(screen=self, eventmap=
66 iPlayableService.evStart: self.serviceStarted,
69 self.__state = self.STATE_SHOWN
72 self.hideTimer = eTimer()
73 self.hideTimer.callback.append(self.doTimerHide)
74 self.hideTimer.start(5000, True)
76 self.onShow.append(self.__onShow)
77 self.onHide.append(self.__onHide)
79 def serviceStarted(self):
81 if config.usage.show_infobar_on_zap.value:
85 self.__state = self.STATE_SHOWN
88 def startHideTimer(self):
89 if self.__state == self.STATE_SHOWN and not self.__locked:
90 idx = config.usage.infobar_timeout.index
92 self.hideTimer.start(idx*1000, True)
95 self.__state = self.STATE_HIDDEN
101 def doTimerHide(self):
102 self.hideTimer.stop()
103 if self.__state == self.STATE_SHOWN:
106 def toggleShow(self):
107 if self.__state == self.STATE_SHOWN:
109 self.hideTimer.stop()
110 elif self.__state == self.STATE_HIDDEN:
114 self.__locked = self.__locked + 1
117 self.hideTimer.stop()
119 def unlockShow(self):
120 self.__locked = self.__locked - 1
122 self.startHideTimer()
124 # def startShow(self):
125 # self.instance.m_animation.startMoveAnimation(ePoint(0, 600), ePoint(0, 380), 100)
126 # self.__state = self.STATE_SHOWN
128 # def startHide(self):
129 # self.instance.m_animation.startMoveAnimation(ePoint(0, 380), ePoint(0, 600), 100)
130 # self.__state = self.STATE_HIDDEN
132 class NumberZap(Screen):
139 self.close(int(self["number"].getText()))
141 def keyNumberGlobal(self, number):
142 self.Timer.start(3000, True) #reset timer
143 self.field = self.field + str(number)
144 self["number"].setText(self.field)
145 if len(self.field) >= 4:
148 def __init__(self, session, number):
149 Screen.__init__(self, session)
150 self.field = str(number)
152 self["channel"] = Label(_("Channel:"))
154 self["number"] = Label(self.field)
156 self["actions"] = NumberActionMap( [ "SetupActions" ],
160 "1": self.keyNumberGlobal,
161 "2": self.keyNumberGlobal,
162 "3": self.keyNumberGlobal,
163 "4": self.keyNumberGlobal,
164 "5": self.keyNumberGlobal,
165 "6": self.keyNumberGlobal,
166 "7": self.keyNumberGlobal,
167 "8": self.keyNumberGlobal,
168 "9": self.keyNumberGlobal,
169 "0": self.keyNumberGlobal
172 self.Timer = eTimer()
173 self.Timer.callback.append(self.keyOK)
174 self.Timer.start(3000, True)
176 class InfoBarNumberZap:
177 """ Handles an initial number for NumberZapping """
179 self["NumberActions"] = NumberActionMap( [ "NumberActions"],
181 "1": self.keyNumberGlobal,
182 "2": self.keyNumberGlobal,
183 "3": self.keyNumberGlobal,
184 "4": self.keyNumberGlobal,
185 "5": self.keyNumberGlobal,
186 "6": self.keyNumberGlobal,
187 "7": self.keyNumberGlobal,
188 "8": self.keyNumberGlobal,
189 "9": self.keyNumberGlobal,
190 "0": self.keyNumberGlobal,
193 def keyNumberGlobal(self, number):
194 # print "You pressed number " + str(number)
196 if isinstance(self, InfoBarPiP) and self.pipHandles0Action():
197 self.pipDoHandle0Action()
199 self.servicelist.recallPrevService()
201 if self.has_key("TimeshiftActions") and not self.timeshift_enabled:
202 self.session.openWithCallback(self.numberEntered, NumberZap, number)
204 def numberEntered(self, retval):
205 # print self.servicelist
207 self.zapToNumber(retval)
209 def searchNumberHelper(self, serviceHandler, num, bouquet):
210 servicelist = serviceHandler.list(bouquet)
211 if not servicelist is None:
213 serviceIterator = servicelist.getNext()
214 if not serviceIterator.valid(): #check end of list
216 playable = not (serviceIterator.flags & (eServiceReference.isMarker|eServiceReference.isDirectory))
219 if not num: #found service with searched number ?
220 return serviceIterator, 0
223 def zapToNumber(self, number):
224 bouquet = self.servicelist.bouquet_root
226 serviceHandler = eServiceCenter.getInstance()
227 if not config.usage.multibouquet.value:
228 service, number = self.searchNumberHelper(serviceHandler, number, bouquet)
230 bouquetlist = serviceHandler.list(bouquet)
231 if not bouquetlist is None:
233 bouquet = bouquetlist.getNext()
234 if not bouquet.valid(): #check end of list
236 if bouquet.flags & eServiceReference.isDirectory:
237 service, number = self.searchNumberHelper(serviceHandler, number, bouquet)
238 if not service is None:
239 if self.servicelist.getRoot() != bouquet: #already in correct bouquet?
240 self.servicelist.clearPath()
241 if self.servicelist.bouquet_root != bouquet:
242 self.servicelist.enterPath(self.servicelist.bouquet_root)
243 self.servicelist.enterPath(bouquet)
244 self.servicelist.setCurrentSelection(service) #select the service in servicelist
245 self.servicelist.zap()
247 config.misc.initialchannelselection = ConfigBoolean(default = True)
249 class InfoBarChannelSelection:
250 """ ChannelSelection - handles the channelSelection dialog and the initial
251 channelChange actions which open the channelSelection dialog """
254 self.servicelist = self.session.instantiateDialog(ChannelSelection)
256 if config.misc.initialchannelselection.value:
257 self.onShown.append(self.firstRun)
259 self["ChannelSelectActions"] = HelpableActionMap(self, "InfobarChannelSelection",
261 "switchChannelUp": (self.switchChannelUp, _("open servicelist(up)")),
262 "switchChannelDown": (self.switchChannelDown, _("open servicelist(down)")),
263 "zapUp": (self.zapUp, _("previous channel")),
264 "zapDown": (self.zapDown, _("next channel")),
265 "historyBack": (self.historyBack, _("previous channel in history")),
266 "historyNext": (self.historyNext, _("next channel in history")),
267 "openServiceList": (self.openServiceList, _("open servicelist")),
270 def showTvChannelList(self, zap=False):
271 self.servicelist.setModeTv()
273 self.servicelist.zap()
274 self.session.execDialog(self.servicelist)
276 def showRadioChannelList(self, zap=False):
277 self.servicelist.setModeRadio()
279 self.servicelist.zap()
280 self.session.execDialog(self.servicelist)
283 self.onShown.remove(self.firstRun)
284 config.misc.initialchannelselection.value = False
285 config.misc.initialchannelselection.save()
286 self.switchChannelDown()
288 def historyBack(self):
289 self.servicelist.historyBack()
291 def historyNext(self):
292 self.servicelist.historyNext()
294 def switchChannelUp(self):
295 self.servicelist.moveUp()
296 self.session.execDialog(self.servicelist)
298 def switchChannelDown(self):
299 self.servicelist.moveDown()
300 self.session.execDialog(self.servicelist)
302 def openServiceList(self):
303 self.session.execDialog(self.servicelist)
306 if self.servicelist.inBouquet():
307 prev = self.servicelist.getCurrentSelection()
309 prev = prev.toString()
311 if config.usage.quickzap_bouquet_change.value:
312 if self.servicelist.atBegin():
313 self.servicelist.prevBouquet()
314 self.servicelist.moveUp()
315 cur = self.servicelist.getCurrentSelection()
316 if not cur or (not (cur.flags & 64)) or cur.toString() == prev:
319 self.servicelist.moveUp()
320 self.servicelist.zap()
323 if self.servicelist.inBouquet():
324 prev = self.servicelist.getCurrentSelection()
326 prev = prev.toString()
328 if config.usage.quickzap_bouquet_change.value and self.servicelist.atEnd():
329 self.servicelist.nextBouquet()
331 self.servicelist.moveDown()
332 cur = self.servicelist.getCurrentSelection()
333 if not cur or (not (cur.flags & 64)) or cur.toString() == prev:
336 self.servicelist.moveDown()
337 self.servicelist.zap()
340 """ Handles a menu action, to open the (main) menu """
342 self["MenuActions"] = HelpableActionMap(self, "InfobarMenuActions",
344 "mainMenu": (self.mainMenu, _("Enter main menu...")),
346 self.session.infobar = None
349 print "loading mainmenu XML..."
350 menu = mdom.getroot()
351 assert menu.tag == "menu", "root element in menu must be 'menu'!"
353 self.session.infobar = self
354 # so we can access the currently active infobar from screens opened from within the mainmenu
355 # at the moment used from the SubserviceSelection
357 self.session.openWithCallback(self.mainMenuClosed, MainMenu, menu)
359 def mainMenuClosed(self, *val):
360 self.session.infobar = None
362 class InfoBarSimpleEventView:
363 """ Opens the Eventview for now/next """
365 self["EPGActions"] = HelpableActionMap(self, "InfobarEPGActions",
367 "showEventInfo": (self.openEventView, _("show event details")),
368 "showInfobarOrEpgWhenInfobarAlreadyVisible": self.showEventInfoWhenNotVisible,
371 def showEventInfoWhenNotVisible(self):
378 def openEventView(self):
380 self.epglist = epglist
381 service = self.session.nav.getCurrentService()
382 ref = self.session.nav.getCurrentlyPlayingServiceReference()
383 info = service.info()
391 self.session.open(EventViewSimple, epglist[0], ServiceReference(ref), self.eventViewCallback)
393 def eventViewCallback(self, setEvent, setService, val): #used for now/next displaying
394 epglist = self.epglist
397 epglist[0] = epglist[1]
401 class SimpleServicelist:
402 def __init__(self, services):
403 self.services = services
404 self.length = len(services)
407 def selectService(self, service):
413 while self.services[self.current].ref != service:
415 if self.current >= self.length:
419 def nextService(self):
422 if self.current+1 < self.length:
427 def prevService(self):
430 if self.current-1 > -1:
433 self.current = self.length - 1
435 def currentService(self):
436 if not self.length or self.current >= self.length:
438 return self.services[self.current]
441 """ EPG - Opens an EPG list when the showEPGList action fires """
443 self.__event_tracker = ServiceEventTracker(screen=self, eventmap=
445 iPlayableService.evUpdatedEventInfo: self.__evEventInfoChanged,
448 self.is_now_next = False
450 self.bouquetSel = None
451 self.eventView = None
452 self["EPGActions"] = HelpableActionMap(self, "InfobarEPGActions",
454 "showEventInfo": (self.openEventView, _("show EPG...")),
455 "showEventInfoPlugin": (self.showEventInfoPlugins, _("show single service EPG...")),
456 "showInfobarOrEpgWhenInfobarAlreadyVisible": self.showEventInfoWhenNotVisible,
459 def showEventInfoWhenNotVisible(self):
466 def zapToService(self, service):
467 if not service is None:
468 if self.servicelist.getRoot() != self.epg_bouquet: #already in correct bouquet?
469 self.servicelist.clearPath()
470 if self.servicelist.bouquet_root != self.epg_bouquet:
471 self.servicelist.enterPath(self.servicelist.bouquet_root)
472 self.servicelist.enterPath(self.epg_bouquet)
473 self.servicelist.setCurrentSelection(service) #select the service in servicelist
474 self.servicelist.zap()
476 def getBouquetServices(self, bouquet):
478 servicelist = eServiceCenter.getInstance().list(bouquet)
479 if not servicelist is None:
481 service = servicelist.getNext()
482 if not service.valid(): #check if end of list
484 if service.flags & (eServiceReference.isDirectory | eServiceReference.isMarker): #ignore non playable services
486 services.append(ServiceReference(service))
489 def openBouquetEPG(self, bouquet, withCallback=True):
490 services = self.getBouquetServices(bouquet)
492 self.epg_bouquet = bouquet
494 self.dlg_stack.append(self.session.openWithCallback(self.closed, EPGSelection, services, self.zapToService, None, self.changeBouquetCB))
496 self.session.open(EPGSelection, services, self.zapToService, None, self.changeBouquetCB)
498 def changeBouquetCB(self, direction, epg):
501 self.bouquetSel.down()
504 bouquet = self.bouquetSel.getCurrent()
505 services = self.getBouquetServices(bouquet)
507 self.epg_bouquet = bouquet
508 epg.setServices(services)
510 def closed(self, ret=False):
511 closedScreen = self.dlg_stack.pop()
512 if self.bouquetSel and closedScreen == self.bouquetSel:
513 self.bouquetSel = None
514 elif self.eventView and closedScreen == self.eventView:
515 self.eventView = None
517 dlgs=len(self.dlg_stack)
519 self.dlg_stack[dlgs-1].close(dlgs > 1)
521 def openMultiServiceEPG(self, withCallback=True):
522 bouquets = self.servicelist.getBouquetList()
527 if cnt > 1: # show bouquet list
529 self.bouquetSel = self.session.openWithCallback(self.closed, BouquetSelector, bouquets, self.openBouquetEPG, enableWrapAround=True)
530 self.dlg_stack.append(self.bouquetSel)
532 self.bouquetSel = self.session.open(BouquetSelector, bouquets, self.openBouquetEPG, enableWrapAround=True)
534 self.openBouquetEPG(bouquets[0][1], withCallback)
536 def changeServiceCB(self, direction, epg):
539 self.serviceSel.nextService()
541 self.serviceSel.prevService()
542 epg.setService(self.serviceSel.currentService())
544 def SingleServiceEPGClosed(self, ret=False):
545 self.serviceSel = None
547 def openSingleServiceEPG(self):
548 ref=self.session.nav.getCurrentlyPlayingServiceReference()
550 if self.servicelist.getMutableList() is not None: # bouquet in channellist
551 current_path = self.servicelist.getRoot()
552 services = self.getBouquetServices(current_path)
553 self.serviceSel = SimpleServicelist(services)
554 if self.serviceSel.selectService(ref):
555 self.session.openWithCallback(self.SingleServiceEPGClosed, EPGSelection, ref, serviceChangeCB = self.changeServiceCB)
557 self.session.openWithCallback(self.SingleServiceEPGClosed, EPGSelection, ref)
559 self.session.open(EPGSelection, ref)
561 def showEventInfoPlugins(self):
562 list = [(p.name, boundFunction(self.runPlugin, p)) for p in plugins.getPlugins(where = PluginDescriptor.WHERE_EVENTINFO)]
565 list.append((_("show single service EPG..."), self.openSingleServiceEPG))
566 self.session.openWithCallback(self.EventInfoPluginChosen, ChoiceBox, title=_("Please choose an extension..."), list = list, skin_name = "EPGExtensionsList")
568 self.openSingleServiceEPG()
570 def runPlugin(self, plugin):
571 plugin(session = self.session, servicelist = self.servicelist)
573 def EventInfoPluginChosen(self, answer):
574 if answer is not None:
577 def openSimilarList(self, eventid, refstr):
578 self.session.open(EPGSelection, refstr, None, eventid)
580 def getNowNext(self):
582 service = self.session.nav.getCurrentService()
583 info = service and service.info()
584 ptr = info and info.getEvent(0)
587 ptr = info and info.getEvent(1)
590 self.epglist = epglist
592 def __evEventInfoChanged(self):
593 if self.is_now_next and len(self.dlg_stack) == 1:
595 assert self.eventView
597 self.eventView.setEvent(self.epglist[0])
599 def openEventView(self):
600 ref = self.session.nav.getCurrentlyPlayingServiceReference()
602 epglist = self.epglist
604 self.is_now_next = False
605 epg = eEPGCache.getInstance()
606 ptr = ref and ref.valid() and epg.lookupEventTime(ref, -1)
609 ptr = epg.lookupEventTime(ref, ptr.getBeginTime(), +1)
613 self.is_now_next = True
615 self.eventView = self.session.openWithCallback(self.closed, EventViewEPGSelect, self.epglist[0], ServiceReference(ref), self.eventViewCallback, self.openSingleServiceEPG, self.openMultiServiceEPG, self.openSimilarList)
616 self.dlg_stack.append(self.eventView)
618 print "no epg for the service avail.. so we show multiepg instead of eventinfo"
619 self.openMultiServiceEPG(False)
621 def eventViewCallback(self, setEvent, setService, val): #used for now/next displaying
622 epglist = self.epglist
625 epglist[0]=epglist[1]
629 class InfoBarRdsDecoder:
630 """provides RDS and Rass support/display"""
632 self.rds_display = self.session.instantiateDialog(RdsInfoDisplay)
633 self.rass_interactive = None
635 self.__event_tracker = ServiceEventTracker(screen=self, eventmap=
637 iPlayableService.evEnd: self.__serviceStopped,
638 iPlayableService.evUpdatedRassSlidePic: self.RassSlidePicChanged
641 self["RdsActions"] = ActionMap(["InfobarRdsActions"],
643 "startRassInteractive": self.startRassInteractive
646 self["RdsActions"].setEnabled(False)
648 self.onLayoutFinish.append(self.rds_display.show)
649 self.rds_display.onRassInteractivePossibilityChanged.append(self.RassInteractivePossibilityChanged)
651 def RassInteractivePossibilityChanged(self, state):
652 self["RdsActions"].setEnabled(state)
654 def RassSlidePicChanged(self):
655 if not self.rass_interactive:
656 service = self.session.nav.getCurrentService()
657 decoder = service and service.rdsDecoder()
659 decoder.showRassSlidePicture()
661 def __serviceStopped(self):
662 if self.rass_interactive is not None:
663 rass_interactive = self.rass_interactive
664 self.rass_interactive = None
665 rass_interactive.close()
667 def startRassInteractive(self):
668 self.rds_display.hide()
669 self.rass_interactive = self.session.openWithCallback(self.RassInteractiveClosed, RassInteractive)
671 def RassInteractiveClosed(self, *val):
672 if self.rass_interactive is not None:
673 self.rass_interactive = None
674 self.RassSlidePicChanged()
675 self.rds_display.show()
678 """handles actions like seeking, pause"""
680 SEEK_STATE_PLAY = (0, 0, 0, ">")
681 SEEK_STATE_PAUSE = (1, 0, 0, "||")
682 SEEK_STATE_EOF = (1, 0, 0, "END")
684 def __init__(self, actionmap = "InfobarSeekActions", useSeekBackHack=True):
685 self.__event_tracker = ServiceEventTracker(screen=self, eventmap=
687 iPlayableService.evSeekableStatusChanged: self.__seekableStatusChanged,
688 iPlayableService.evStart: self.__serviceStarted,
690 iPlayableService.evEOF: self.__evEOF,
691 iPlayableService.evSOF: self.__evSOF,
694 class InfoBarSeekActionMap(HelpableActionMap):
695 def __init__(self, screen, *args, **kwargs):
696 HelpableActionMap.__init__(self, screen, *args, **kwargs)
699 def action(self, contexts, action):
700 print "action:", action
701 if action[:5] == "seek:":
702 time = int(action[5:])
703 self.screen.doSeekRelative(time * 90000)
705 elif action[:8] == "seekdef:":
706 key = int(action[8:])
707 time = (-config.seek.selfdefined_13.value, False, config.seek.selfdefined_13.value,
708 -config.seek.selfdefined_46.value, False, config.seek.selfdefined_46.value,
709 -config.seek.selfdefined_79.value, False, config.seek.selfdefined_79.value)[key-1]
710 self.screen.doSeekRelative(time * 90000)
713 return HelpableActionMap.action(self, contexts, action)
715 self["SeekActions"] = InfoBarSeekActionMap(self, actionmap,
717 "playpauseService": self.playpauseService,
718 "pauseService": (self.pauseService, _("pause")),
719 "unPauseService": (self.unPauseService, _("continue")),
721 "seekFwd": (self.seekFwd, _("skip forward")),
722 "seekFwdManual": (self.seekFwdManual, _("skip forward (enter time)")),
723 "seekBack": (self.seekBack, _("skip backward")),
724 "seekBackManual": (self.seekBackManual, _("skip backward (enter time)"))
726 # give them a little more priority to win over color buttons
728 self["SeekActions"].setEnabled(False)
730 self.seekstate = self.SEEK_STATE_PLAY
731 self.lastseekstate = self.SEEK_STATE_PLAY
733 self.onPlayStateChanged = [ ]
735 self.lockedBecauseOfSkipping = False
737 self.__seekableStatusChanged()
739 def makeStateForward(self, n):
740 # minspeed = config.seek.stepwise_minspeed.value
741 # repeat = int(config.seek.stepwise_repeat.value)
742 # if minspeed != "Never" and n >= int(minspeed) and repeat > 1:
743 # return (0, n * repeat, repeat, ">> %dx" % n)
745 return (0, n, 0, ">> %dx" % n)
747 def makeStateBackward(self, n):
748 # minspeed = config.seek.stepwise_minspeed.value
749 # repeat = int(config.seek.stepwise_repeat.value)
750 # if minspeed != "Never" and n >= int(minspeed) and repeat > 1:
751 # return (0, -n * repeat, repeat, "<< %dx" % n)
753 return (0, -n, 0, "<< %dx" % n)
755 def makeStateSlowMotion(self, n):
756 return (0, 0, n, "/%d" % n)
758 def isStateForward(self, state):
761 def isStateBackward(self, state):
764 def isStateSlowMotion(self, state):
765 return state[1] == 0 and state[2] > 1
767 def getHigher(self, n, lst):
773 def getLower(self, n, lst):
781 def showAfterSeek(self):
782 if isinstance(self, InfoBarShowHide):
792 service = self.session.nav.getCurrentService()
796 seek = service.seek()
798 if seek is None or not seek.isCurrentlySeekable():
803 def isSeekable(self):
804 if self.getSeek() is None:
808 def __seekableStatusChanged(self):
809 # print "seekable status changed!"
810 if not self.isSeekable():
811 self["SeekActions"].setEnabled(False)
812 # print "not seekable, return to play"
813 self.setSeekState(self.SEEK_STATE_PLAY)
815 self["SeekActions"].setEnabled(True)
818 def __serviceStarted(self):
819 self.seekstate = self.SEEK_STATE_PLAY
820 self.__seekableStatusChanged()
822 def setSeekState(self, state):
823 service = self.session.nav.getCurrentService()
828 if not self.isSeekable():
829 if state not in (self.SEEK_STATE_PLAY, self.SEEK_STATE_PAUSE):
830 state = self.SEEK_STATE_PLAY
832 pauseable = service.pause()
834 if pauseable is None:
835 print "not pauseable."
836 state = self.SEEK_STATE_PLAY
838 self.seekstate = state
840 if pauseable is not None:
841 if self.seekstate[0]:
842 print "resolved to PAUSE"
844 elif self.seekstate[1]:
845 print "resolved to FAST FORWARD"
846 pauseable.setFastForward(self.seekstate[1])
847 elif self.seekstate[2]:
848 print "resolved to SLOW MOTION"
849 pauseable.setSlowMotion(self.seekstate[2])
851 print "resolved to PLAY"
854 for c in self.onPlayStateChanged:
857 self.checkSkipShowHideLock()
861 def playpauseService(self):
862 if self.seekstate != self.SEEK_STATE_PLAY:
863 self.unPauseService()
867 def pauseService(self):
868 if self.seekstate == self.SEEK_STATE_PAUSE:
869 if config.seek.on_pause.value == "play":
870 self.unPauseService()
871 elif config.seek.on_pause.value == "step":
872 self.doSeekRelative(1)
873 elif config.seek.on_pause.value == "last":
874 self.setSeekState(self.lastseekstate)
875 self.lastseekstate = self.SEEK_STATE_PLAY
877 if self.seekstate != self.SEEK_STATE_EOF:
878 self.lastseekstate = self.seekstate
879 self.setSeekState(self.SEEK_STATE_PAUSE);
881 def unPauseService(self):
883 if self.seekstate == self.SEEK_STATE_PLAY:
885 self.setSeekState(self.SEEK_STATE_PLAY)
887 def doSeek(self, pts):
888 seekable = self.getSeek()
893 def doSeekRelative(self, pts):
894 seekable = self.getSeek()
897 prevstate = self.seekstate
899 if self.seekstate == self.SEEK_STATE_EOF:
900 if prevstate == self.SEEK_STATE_PAUSE:
901 self.setSeekState(self.SEEK_STATE_PAUSE)
903 self.setSeekState(self.SEEK_STATE_PLAY)
904 seekable.seekRelative(pts<0 and -1 or 1, abs(pts))
905 if abs(pts) > 100 and config.usage.show_infobar_on_skip.value:
909 if self.seekstate == self.SEEK_STATE_PLAY:
910 self.setSeekState(self.makeStateForward(int(config.seek.enter_forward.value)))
911 elif self.seekstate == self.SEEK_STATE_PAUSE:
912 if len(config.seek.speeds_slowmotion.value):
913 self.setSeekState(self.makeStateSlowMotion(config.seek.speeds_slowmotion.value[-1]))
915 self.setSeekState(self.makeStateForward(int(config.seek.enter_forward.value)))
916 elif self.seekstate == self.SEEK_STATE_EOF:
918 elif self.isStateForward(self.seekstate):
919 speed = self.seekstate[1]
920 if self.seekstate[2]:
921 speed /= self.seekstate[2]
922 speed = self.getHigher(speed, config.seek.speeds_forward.value) or config.seek.speeds_forward.value[-1]
923 self.setSeekState(self.makeStateForward(speed))
924 elif self.isStateBackward(self.seekstate):
925 speed = -self.seekstate[1]
926 if self.seekstate[2]:
927 speed /= self.seekstate[2]
928 speed = self.getLower(speed, config.seek.speeds_backward.value)
930 self.setSeekState(self.makeStateBackward(speed))
932 self.setSeekState(self.SEEK_STATE_PLAY)
933 elif self.isStateSlowMotion(self.seekstate):
934 speed = self.getLower(self.seekstate[2], config.seek.speeds_slowmotion.value) or config.seek.speeds_slowmotion.value[0]
935 self.setSeekState(self.makeStateSlowMotion(speed))
938 seekstate = self.seekstate
939 if seekstate == self.SEEK_STATE_PLAY:
940 self.setSeekState(self.makeStateBackward(int(config.seek.enter_backward.value)))
941 elif seekstate == self.SEEK_STATE_EOF:
942 self.setSeekState(self.makeStateBackward(int(config.seek.enter_backward.value)))
943 self.doSeekRelative(-6)
944 elif seekstate == self.SEEK_STATE_PAUSE:
945 self.doSeekRelative(-1)
946 elif self.isStateForward(seekstate):
949 speed /= seekstate[2]
950 speed = self.getLower(speed, config.seek.speeds_forward.value)
952 self.setSeekState(self.makeStateForward(speed))
954 self.setSeekState(self.SEEK_STATE_PLAY)
955 elif self.isStateBackward(seekstate):
956 speed = -seekstate[1]
958 speed /= seekstate[2]
959 speed = self.getHigher(speed, config.seek.speeds_backward.value) or config.seek.speeds_backward.value[-1]
960 self.setSeekState(self.makeStateBackward(speed))
961 elif self.isStateSlowMotion(seekstate):
962 speed = self.getHigher(seekstate[2], config.seek.speeds_slowmotion.value)
964 self.setSeekState(self.makeStateSlowMotion(speed))
966 self.setSeekState(self.SEEK_STATE_PAUSE)
968 def seekFwdManual(self):
969 self.session.openWithCallback(self.fwdSeekTo, MinuteInput)
971 def fwdSeekTo(self, minutes):
972 print "Seek", minutes, "minutes forward"
973 self.doSeekRelative(minutes * 60 * 90000)
975 def seekBackManual(self):
976 self.session.openWithCallback(self.rwdSeekTo, MinuteInput)
978 def rwdSeekTo(self, minutes):
980 self.doSeekRelative(-minutes * 60 * 90000)
982 def checkSkipShowHideLock(self):
983 wantlock = self.seekstate != self.SEEK_STATE_PLAY
985 if config.usage.show_infobar_on_skip.value:
986 if self.lockedBecauseOfSkipping and not wantlock:
988 self.lockedBecauseOfSkipping = False
990 if wantlock and not self.lockedBecauseOfSkipping:
992 self.lockedBecauseOfSkipping = True
994 def calcRemainingTime(self):
995 seekable = self.getSeek()
996 if seekable is not None:
997 len = seekable.getLength()
999 tmp = self.cueGetEndCutPosition()
1004 pos = seekable.getPlayPosition()
1005 speednom = self.seekstate[1] or 1
1006 speedden = self.seekstate[2] or 1
1007 if not len[0] and not pos[0]:
1008 if len[1] <= pos[1]:
1010 time = (len[1] - pos[1])*speedden/(90*speednom)
1015 if self.seekstate == self.SEEK_STATE_EOF:
1018 # if we are seeking forward, we try to end up ~1s before the end, and pause there.
1019 seekstate = self.seekstate
1020 if self.seekstate != self.SEEK_STATE_PAUSE:
1021 self.setSeekState(self.SEEK_STATE_EOF)
1023 if seekstate not in (self.SEEK_STATE_PLAY, self.SEEK_STATE_PAUSE): # if we are seeking
1024 seekable = self.getSeek()
1025 if seekable is not None:
1027 if seekstate == self.SEEK_STATE_PLAY: # regular EOF
1028 self.doEofInternal(True)
1030 self.doEofInternal(False)
1032 def doEofInternal(self, playing):
1033 pass # Defined in subclasses
1036 self.setSeekState(self.SEEK_STATE_PLAY)
1039 from Screens.PVRState import PVRState, TimeshiftState
1041 class InfoBarPVRState:
1042 def __init__(self, screen=PVRState, force_show = False):
1043 self.onPlayStateChanged.append(self.__playStateChanged)
1044 self.pvrStateDialog = self.session.instantiateDialog(screen)
1045 self.onShow.append(self._mayShow)
1046 self.onHide.append(self.pvrStateDialog.hide)
1047 self.force_show = force_show
1050 if self.execing and self.seekstate != self.SEEK_STATE_PLAY:
1051 self.pvrStateDialog.show()
1053 def __playStateChanged(self, state):
1054 playstateString = state[3]
1055 self.pvrStateDialog["state"].setText(playstateString)
1057 # if we return into "PLAY" state, ensure that the dialog gets hidden if there will be no infobar displayed
1058 if not config.usage.show_infobar_on_skip.value and self.seekstate == self.SEEK_STATE_PLAY and not self.force_show:
1059 self.pvrStateDialog.hide()
1064 class InfoBarTimeshiftState(InfoBarPVRState):
1066 InfoBarPVRState.__init__(self, screen=TimeshiftState, force_show = True)
1069 if self.execing and self.timeshift_enabled and self.seekstate != self.SEEK_STATE_PLAY:
1070 self.pvrStateDialog.show()
1072 class InfoBarShowMovies:
1074 # i don't really like this class.
1075 # it calls a not further specified "movie list" on up/down/movieList,
1076 # so this is not more than an action map
1078 self["MovieListActions"] = HelpableActionMap(self, "InfobarMovieListActions",
1080 "movieList": (self.showMovies, _("movie list")),
1081 "up": (self.showMovies, _("movie list")),
1082 "down": (self.showMovies, _("movie list"))
1085 # InfoBarTimeshift requires InfoBarSeek, instantiated BEFORE!
1089 # Timeshift works the following way:
1090 # demux0 demux1 "TimeshiftActions" "TimeshiftActivateActions" "SeekActions"
1091 # - normal playback TUNER unused PLAY enable disable disable
1092 # - user presses "yellow" button. FILE record PAUSE enable disable enable
1093 # - user presess pause again FILE record PLAY enable disable enable
1094 # - user fast forwards FILE record FF enable disable enable
1095 # - end of timeshift buffer reached TUNER record PLAY enable enable disable
1096 # - user backwards FILE record BACK # !! enable disable enable
1100 # - when a service is playing, pressing the "timeshiftStart" button ("yellow") enables recording ("enables timeshift"),
1101 # freezes the picture (to indicate timeshift), sets timeshiftMode ("activates timeshift")
1102 # now, the service becomes seekable, so "SeekActions" are enabled, "TimeshiftEnableActions" are disabled.
1103 # - the user can now PVR around
1104 # - if it hits the end, the service goes into live mode ("deactivates timeshift", it's of course still "enabled")
1105 # the service looses it's "seekable" state. It can still be paused, but just to activate timeshift right
1107 # the seek actions will be disabled, but the timeshiftActivateActions will be enabled
1108 # - if the user rewinds, or press pause, timeshift will be activated again
1110 # note that a timeshift can be enabled ("recording") and
1111 # activated (currently time-shifting).
1113 class InfoBarTimeshift:
1115 self["TimeshiftActions"] = HelpableActionMap(self, "InfobarTimeshiftActions",
1117 "timeshiftStart": (self.startTimeshift, _("start timeshift")), # the "yellow key"
1118 "timeshiftStop": (self.stopTimeshift, _("stop timeshift")) # currently undefined :), probably 'TV'
1120 self["TimeshiftActivateActions"] = ActionMap(["InfobarTimeshiftActivateActions"],
1122 "timeshiftActivateEnd": self.activateTimeshiftEnd, # something like "rewind key"
1123 "timeshiftActivateEndAndPause": self.activateTimeshiftEndAndPause # something like "pause key"
1124 }, prio=-1) # priority over record
1126 self.timeshift_enabled = 0
1127 self.timeshift_state = 0
1128 self.ts_rewind_timer = eTimer()
1129 self.ts_rewind_timer.callback.append(self.rewindService)
1131 self.__event_tracker = ServiceEventTracker(screen=self, eventmap=
1133 iPlayableService.evStart: self.__serviceStarted,
1134 iPlayableService.evSeekableStatusChanged: self.__seekableStatusChanged
1137 def getTimeshift(self):
1138 service = self.session.nav.getCurrentService()
1139 return service and service.timeshift()
1141 def startTimeshift(self):
1142 print "enable timeshift"
1143 ts = self.getTimeshift()
1145 self.session.open(MessageBox, _("Timeshift not possible!"), MessageBox.TYPE_ERROR)
1146 print "no ts interface"
1149 if self.timeshift_enabled:
1150 print "hu, timeshift already enabled?"
1152 if not ts.startTimeshift():
1153 self.timeshift_enabled = 1
1155 # we remove the "relative time" for now.
1156 #self.pvrStateDialog["timeshift"].setRelative(time.time())
1159 #self.setSeekState(self.SEEK_STATE_PAUSE)
1160 self.activateTimeshiftEnd(False)
1162 # enable the "TimeshiftEnableActions", which will override
1163 # the startTimeshift actions
1164 self.__seekableStatusChanged()
1166 print "timeshift failed"
1168 def stopTimeshift(self):
1169 if not self.timeshift_enabled:
1171 print "disable timeshift"
1172 ts = self.getTimeshift()
1175 self.session.openWithCallback(self.stopTimeshiftConfirmed, MessageBox, _("Stop Timeshift?"), MessageBox.TYPE_YESNO)
1177 def stopTimeshiftConfirmed(self, confirmed):
1181 ts = self.getTimeshift()
1186 self.timeshift_enabled = 0
1189 self.__seekableStatusChanged()
1191 # activates timeshift, and seeks to (almost) the end
1192 def activateTimeshiftEnd(self, back = True):
1193 ts = self.getTimeshift()
1194 print "activateTimeshiftEnd"
1199 if ts.isTimeshiftActive():
1200 print "!! activate timeshift called - but shouldn't this be a normal pause?"
1204 ts.activateTimeshift() # activate timeshift will automatically pause
1205 self.setSeekState(self.SEEK_STATE_PAUSE)
1208 self.ts_rewind_timer.start(200, 1)
1210 def rewindService(self):
1211 self.setSeekState(self.makeStateBackward(int(config.seek.enter_backward.value)))
1213 # same as activateTimeshiftEnd, but pauses afterwards.
1214 def activateTimeshiftEndAndPause(self):
1215 print "activateTimeshiftEndAndPause"
1216 #state = self.seekstate
1217 self.activateTimeshiftEnd(False)
1219 def __seekableStatusChanged(self):
1222 # print "self.isSeekable", self.isSeekable()
1223 # print "self.timeshift_enabled", self.timeshift_enabled
1225 # when this service is not seekable, but timeshift
1226 # is enabled, this means we can activate
1228 if not self.isSeekable() and self.timeshift_enabled:
1231 # print "timeshift activate:", enabled
1232 self["TimeshiftActivateActions"].setEnabled(enabled)
1234 def __serviceStarted(self):
1235 self.timeshift_enabled = False
1236 self.__seekableStatusChanged()
1238 from Screens.PiPSetup import PiPSetup
1240 class InfoBarExtensions:
1241 EXTENSION_SINGLE = 0
1247 self["InstantExtensionsActions"] = HelpableActionMap(self, "InfobarExtensions",
1249 "extensions": (self.showExtensionSelection, _("view extensions...")),
1250 }, 1) # lower priority
1252 def addExtension(self, extension, key = None, type = EXTENSION_SINGLE):
1253 self.list.append((type, extension, key))
1255 def updateExtension(self, extension, key = None):
1256 self.extensionsList.append(extension)
1258 if self.extensionKeys.has_key(key):
1262 for x in self.availableKeys:
1263 if not self.extensionKeys.has_key(x):
1268 self.extensionKeys[key] = len(self.extensionsList) - 1
1270 def updateExtensions(self):
1271 self.extensionsList = []
1272 self.availableKeys = [ "1", "2", "3", "4", "5", "6", "7", "8", "9", "0", "red", "green", "yellow", "blue" ]
1273 self.extensionKeys = {}
1275 if x[0] == self.EXTENSION_SINGLE:
1276 self.updateExtension(x[1], x[2])
1279 self.updateExtension(y[0], y[1])
1282 def showExtensionSelection(self):
1283 self.updateExtensions()
1284 extensionsList = self.extensionsList[:]
1287 for x in self.availableKeys:
1288 if self.extensionKeys.has_key(x):
1289 entry = self.extensionKeys[x]
1290 extension = self.extensionsList[entry]
1292 name = str(extension[0]())
1293 list.append((extension[0](), extension))
1295 extensionsList.remove(extension)
1297 extensionsList.remove(extension)
1298 list.extend([(x[0](), x) for x in extensionsList])
1300 keys += [""] * len(extensionsList)
1301 self.session.openWithCallback(self.extensionCallback, ChoiceBox, title=_("Please choose an extension..."), list = list, keys = keys, skin_name = "ExtensionsList")
1303 def extensionCallback(self, answer):
1304 if answer is not None:
1307 from Tools.BoundFunction import boundFunction
1309 # depends on InfoBarExtensions
1311 class InfoBarPlugins:
1313 self.addExtension(extension = self.getPluginList, type = InfoBarExtensions.EXTENSION_LIST)
1315 def getPluginName(self, name):
1318 def getPluginList(self):
1319 list = [((boundFunction(self.getPluginName, p.name), boundFunction(self.runPlugin, p), lambda: True), None, p.name) for p in plugins.getPlugins(where = PluginDescriptor.WHERE_EXTENSIONSMENU)]
1320 list.sort(key = lambda e: e[2]) # sort by name
1323 def runPlugin(self, plugin):
1324 if isinstance(self, InfoBarChannelSelection):
1325 plugin(session = self.session, servicelist = self.servicelist)
1327 plugin(session = self.session)
1329 from Components.Task import job_manager
1330 class InfoBarJobman:
1332 self.addExtension(extension = self.getJobList, type = InfoBarExtensions.EXTENSION_LIST)
1334 def getJobList(self):
1335 return [((boundFunction(self.getJobName, job), boundFunction(self.showJobView, job), lambda: True), None) for job in job_manager.getPendingJobs()]
1337 def getJobName(self, job):
1338 return "%s: %s (%d%%)" % (job.getStatustext(), job.name, int(100*job.progress/float(job.end)))
1340 def showJobView(self, job):
1341 from Screens.TaskView import JobView
1342 job_manager.in_background = False
1343 self.session.openWithCallback(self.JobViewCB, JobView, job)
1345 def JobViewCB(self, in_background):
1346 job_manager.in_background = in_background
1348 # depends on InfoBarExtensions
1352 self.session.pipshown
1354 self.session.pipshown = False
1355 if SystemInfo.get("NumVideoDecoders", 1) > 1:
1357 self.addExtension((self.getShowHideName, self.showPiP, lambda: True), "blue")
1358 self.addExtension((self.getMoveName, self.movePiP, self.pipShown), "green")
1359 self.addExtension((self.getSwapName, self.swapPiP, self.pipShown), "yellow")
1361 self.addExtension((self.getShowHideName, self.showPiP, self.pipShown), "blue")
1362 self.addExtension((self.getMoveName, self.movePiP, self.pipShown), "green")
1365 return self.session.pipshown
1367 def pipHandles0Action(self):
1368 return self.pipShown() and config.usage.pip_zero_button.value != "standard"
1370 def getShowHideName(self):
1371 if self.session.pipshown:
1372 return _("Disable Picture in Picture")
1374 return _("Activate Picture in Picture")
1376 def getSwapName(self):
1377 return _("Swap Services")
1379 def getMoveName(self):
1380 return _("Move Picture in Picture")
1383 if self.session.pipshown:
1384 del self.session.pip
1385 self.session.pipshown = False
1387 self.session.pip = self.session.instantiateDialog(PictureInPicture)
1388 self.session.pip.show()
1389 newservice = self.session.nav.getCurrentlyPlayingServiceReference()
1390 if self.session.pip.playService(newservice):
1391 self.session.pipshown = True
1392 self.session.pip.servicePath = self.servicelist.getCurrentServicePath()
1394 self.session.pipshown = False
1395 del self.session.pip
1396 self.session.nav.playService(newservice)
1399 swapservice = self.session.nav.getCurrentlyPlayingServiceReference()
1400 if self.session.pip.servicePath:
1401 servicepath = self.servicelist.getCurrentServicePath()
1402 ref=servicepath[len(servicepath)-1]
1403 pipref=self.session.pip.getCurrentService()
1404 self.session.pip.playService(swapservice)
1405 self.servicelist.setCurrentServicePath(self.session.pip.servicePath)
1406 if pipref.toString() != ref.toString(): # is a subservice ?
1407 self.session.nav.stopService() # stop portal
1408 self.session.nav.playService(pipref) # start subservice
1409 self.session.pip.servicePath=servicepath
1412 self.session.open(PiPSetup, pip = self.session.pip)
1414 def pipDoHandle0Action(self):
1415 use = config.usage.pip_zero_button.value
1418 elif "swapstop" == use:
1424 from RecordTimer import parseEvent, RecordTimerEntry
1426 class InfoBarInstantRecord:
1427 """Instant Record - handles the instantRecord action in order to
1428 start/stop instant records"""
1430 self["InstantRecordActions"] = HelpableActionMap(self, "InfobarInstantRecord",
1432 "instantRecord": (self.instantRecord, _("Instant Record...")),
1436 def stopCurrentRecording(self, entry = -1):
1437 if entry is not None and entry != -1:
1438 self.session.nav.RecordTimer.removeEntry(self.recording[entry])
1439 self.recording.remove(self.recording[entry])
1441 def startInstantRecording(self, limitEvent = False):
1442 serviceref = self.session.nav.getCurrentlyPlayingServiceReference()
1444 # try to get event info
1447 service = self.session.nav.getCurrentService()
1448 epg = eEPGCache.getInstance()
1449 event = epg.lookupEventTime(serviceref, -1, 0)
1451 info = service.info()
1452 ev = info.getEvent(0)
1458 end = begin + 3600 # dummy
1459 name = "instant record"
1463 if event is not None:
1464 curEvent = parseEvent(event)
1466 description = curEvent[3]
1467 eventid = curEvent[4]
1472 self.session.open(MessageBox, _("No event info found, recording indefinitely."), MessageBox.TYPE_INFO)
1474 if isinstance(serviceref, eServiceReference):
1475 serviceref = ServiceReference(serviceref)
1477 recording = RecordTimerEntry(serviceref, begin, end, name, description, eventid, dirname = config.movielist.last_videodir.value)
1478 recording.dontSave = True
1480 if event is None or limitEvent == False:
1481 recording.autoincrease = True
1482 if recording.setAutoincreaseEnd():
1483 self.session.nav.RecordTimer.record(recording)
1484 self.recording.append(recording)
1486 simulTimerList = self.session.nav.RecordTimer.record(recording)
1487 if simulTimerList is not None: # conflict with other recording
1488 name = simulTimerList[1].name
1489 name_date = ' '.join((name, strftime('%c', localtime(simulTimerList[1].begin))))
1490 print "[TIMER] conflicts with", name_date
1491 recording.autoincrease = True # start with max available length, then increment
1492 if recording.setAutoincreaseEnd():
1493 self.session.nav.RecordTimer.record(recording)
1494 self.recording.append(recording)
1495 self.session.open(MessageBox, _("Record time limited due to conflicting timer %s") % name_date, MessageBox.TYPE_INFO)
1497 self.session.open(MessageBox, _("Couldn't record due to conflicting timer %s") % name, MessageBox.TYPE_INFO)
1498 recording.autoincrease = False
1500 self.recording.append(recording)
1502 def isInstantRecordRunning(self):
1503 print "self.recording:", self.recording
1505 for x in self.recording:
1510 def recordQuestionCallback(self, answer):
1511 print "pre:\n", self.recording
1513 if answer is None or answer[1] == "no":
1516 recording = self.recording[:]
1518 if not x in self.session.nav.RecordTimer.timer_list:
1519 self.recording.remove(x)
1520 elif x.dontSave and x.isRunning():
1521 list.append((x, False))
1523 if answer[1] == "changeduration":
1524 if len(self.recording) == 1:
1525 self.changeDuration(0)
1527 self.session.openWithCallback(self.changeDuration, TimerSelection, list)
1528 elif answer[1] == "changeendtime":
1529 if len(self.recording) == 1:
1532 self.session.openWithCallback(self.setEndtime, TimerSelection, list)
1533 elif answer[1] == "stop":
1534 if len(self.recording) == 1:
1535 self.stopCurrentRecording(0)
1537 self.session.openWithCallback(self.stopCurrentRecording, TimerSelection, list)
1538 elif answer[1] in ( "indefinitely" , "manualduration", "manualendtime", "event"):
1539 self.startInstantRecording(limitEvent = answer[1] in ("event", "manualendtime") or False)
1540 if answer[1] == "manualduration":
1541 self.changeDuration(len(self.recording)-1)
1542 elif answer[1] == "manualendtime":
1543 self.setEndtime(len(self.recording)-1)
1544 print "after:\n", self.recording
1546 def setEndtime(self, entry):
1547 if entry is not None and entry >= 0:
1548 self.selectedEntry = entry
1549 self.endtime=ConfigClock(default = self.recording[self.selectedEntry].end)
1550 dlg = self.session.openWithCallback(self.TimeDateInputClosed, TimeDateInput, self.endtime)
1551 dlg.setTitle(_("Please change recording endtime"))
1553 def TimeDateInputClosed(self, ret):
1556 localendtime = localtime(ret[1])
1557 print "stopping recording at", strftime("%c", localendtime)
1558 if self.recording[self.selectedEntry].end != ret[1]:
1559 self.recording[self.selectedEntry].autoincrease = False
1560 self.recording[self.selectedEntry].end = ret[1]
1561 self.session.nav.RecordTimer.timeChanged(self.recording[self.selectedEntry])
1563 def changeDuration(self, entry):
1564 if entry is not None and entry >= 0:
1565 self.selectedEntry = entry
1566 self.session.openWithCallback(self.inputCallback, InputBox, title=_("How many minutes do you want to record?"), text="5", maxSize=False, type=Input.NUMBER)
1568 def inputCallback(self, value):
1569 if value is not None:
1570 print "stopping recording after", int(value), "minutes."
1571 entry = self.recording[self.selectedEntry]
1573 entry.autoincrease = False
1574 entry.end = int(time()) + 60 * int(value)
1575 self.session.nav.RecordTimer.timeChanged(entry)
1577 def instantRecord(self):
1578 dir = config.movielist.last_videodir.value
1579 if not fileExists(dir, 'w'):
1580 dir = resolveFilename(SCOPE_HDD)
1584 # XXX: this message is a little odd as we might be recording to a remote device
1585 self.session.open(MessageBox, _("No HDD found or HDD not initialized!"), MessageBox.TYPE_ERROR)
1588 if self.isInstantRecordRunning():
1589 self.session.openWithCallback(self.recordQuestionCallback, ChoiceBox, \
1590 title=_("A recording is currently running.\nWhat do you want to do?"), \
1591 list=((_("stop recording"), "stop"), \
1592 (_("add recording (stop after current event)"), "event"), \
1593 (_("add recording (indefinitely)"), "indefinitely"), \
1594 (_("add recording (enter recording duration)"), "manualduration"), \
1595 (_("add recording (enter recording endtime)"), "manualendtime"), \
1596 (_("change recording (duration)"), "changeduration"), \
1597 (_("change recording (endtime)"), "changeendtime"), \
1598 (_("do nothing"), "no")))
1600 self.session.openWithCallback(self.recordQuestionCallback, ChoiceBox, \
1601 title=_("Start recording?"), \
1602 list=((_("add recording (stop after current event)"), "event"), \
1603 (_("add recording (indefinitely)"), "indefinitely"), \
1604 (_("add recording (enter recording duration)"), "manualduration"), \
1605 (_("add recording (enter recording endtime)"), "manualendtime"), \
1606 (_("don't record"), "no")))
1608 from Tools.ISO639 import LanguageCodes
1610 class InfoBarAudioSelection:
1612 self["AudioSelectionAction"] = HelpableActionMap(self, "InfobarAudioSelectionActions",
1614 "audioSelection": (self.audioSelection, _("Audio Options...")),
1617 def audioSelection(self):
1618 service = self.session.nav.getCurrentService()
1619 self.audioTracks = audio = service and service.audioTracks()
1620 n = audio and audio.getNumberOfTracks() or 0
1623 self.audioChannel = service.audioChannel()
1628 i = audio.getTrackInfo(idx)
1629 languages = i.getLanguage().split('/')
1630 description = i.getDescription()
1633 for lang in languages:
1636 if LanguageCodes.has_key(lang):
1637 language += LanguageCodes[lang][0]
1642 if len(description):
1643 description += " (" + language + ")"
1645 description = language
1647 tlist.append((description, idx))
1650 tlist.sort(key=lambda x: x[0])
1652 selectedAudio = self.audioTracks.getCurrentTrack()
1657 if x[1] != selectedAudio:
1662 if SystemInfo["CanDownmixAC3"]:
1663 tlist = [(_("AC3 downmix") + " - " +(_("Off"), _("On"))[config.av.downmix_ac3.value and 1 or 0], "CALLFUNC", self.changeAC3Downmix),
1664 ((_("Left"), _("Stereo"), _("Right"))[self.audioChannel.getCurrentChannel()], "mode"),
1666 keys = [ "red", "green", "", "1", "2", "3", "4", "5", "6", "7", "8", "9", "0"] + [""]*n
1669 tlist = [((_("Left"), _("Stereo"), _("Right"))[self.audioChannel.getCurrentChannel()], "mode"), ("--", "")] + tlist
1670 keys = [ "red", "", "1", "2", "3", "4", "5", "6", "7", "8", "9", "0"] + [""]*n
1672 self.session.openWithCallback(self.audioSelected, ChoiceBox, title=_("Select audio track"), list = tlist, selection = selection, keys = keys, skin_name = "AudioTrackSelection")
1674 del self.audioTracks
1676 def changeAC3Downmix(self, arg):
1677 choicelist = self.session.current_dialog["list"]
1678 list = choicelist.list
1680 list[0][1]=(t[0], t[1], t[2], t[3], t[4], t[5], t[6],
1681 _("AC3 downmix") + " - " + (_("On"), _("Off"))[config.av.downmix_ac3.value and 1 or 0])
1682 choicelist.setList(list)
1683 if config.av.downmix_ac3.value:
1684 config.av.downmix_ac3.value = False
1686 config.av.downmix_ac3.value = True
1687 config.av.downmix_ac3.save()
1689 def audioSelected(self, audio):
1690 if audio is not None:
1691 if isinstance(audio[1], str):
1692 if audio[1] == "mode":
1693 keys = ["red", "green", "yellow"]
1694 selection = self.audioChannel.getCurrentChannel()
1695 tlist = ((_("left"), 0), (_("stereo"), 1), (_("right"), 2))
1696 self.session.openWithCallback(self.modeSelected, ChoiceBox, title=_("Select audio mode"), list = tlist, selection = selection, keys = keys, skin_name ="AudioModeSelection")
1698 del self.audioChannel
1699 if self.session.nav.getCurrentService().audioTracks().getNumberOfTracks() > audio[1]:
1700 self.audioTracks.selectTrack(audio[1])
1702 del self.audioChannel
1703 del self.audioTracks
1705 def modeSelected(self, mode):
1706 if mode is not None:
1707 self.audioChannel.selectChannel(mode[1])
1708 del self.audioChannel
1710 class InfoBarSubserviceSelection:
1712 self["SubserviceSelectionAction"] = HelpableActionMap(self, "InfobarSubserviceSelectionActions",
1714 "subserviceSelection": (self.subserviceSelection, _("Subservice list...")),
1717 self["SubserviceQuickzapAction"] = HelpableActionMap(self, "InfobarSubserviceQuickzapActions",
1719 "nextSubservice": (self.nextSubservice, _("Switch to next subservice")),
1720 "prevSubservice": (self.prevSubservice, _("Switch to previous subservice"))
1722 self["SubserviceQuickzapAction"].setEnabled(False)
1724 self.__event_tracker = ServiceEventTracker(screen=self, eventmap=
1726 iPlayableService.evUpdatedEventInfo: self.checkSubservicesAvail
1731 def checkSubservicesAvail(self):
1732 service = self.session.nav.getCurrentService()
1733 subservices = service and service.subServices()
1734 if not subservices or subservices.getNumberOfSubservices() == 0:
1735 self["SubserviceQuickzapAction"].setEnabled(False)
1737 def nextSubservice(self):
1738 self.changeSubservice(+1)
1740 def prevSubservice(self):
1741 self.changeSubservice(-1)
1743 def changeSubservice(self, direction):
1744 service = self.session.nav.getCurrentService()
1745 subservices = service and service.subServices()
1746 n = subservices and subservices.getNumberOfSubservices()
1749 ref = self.session.nav.getCurrentlyPlayingServiceReference()
1752 if subservices.getSubservice(idx).toString() == ref.toString():
1757 selection += direction
1762 newservice = subservices.getSubservice(selection)
1763 if newservice.valid():
1766 self.session.nav.playService(newservice, False)
1768 def subserviceSelection(self):
1769 service = self.session.nav.getCurrentService()
1770 subservices = service and service.subServices()
1771 self.bouquets = self.servicelist.getBouquetList()
1772 n = subservices and subservices.getNumberOfSubservices()
1775 ref = self.session.nav.getCurrentlyPlayingServiceReference()
1779 i = subservices.getSubservice(idx)
1780 if i.toString() == ref.toString():
1782 tlist.append((i.getName(), i))
1785 if self.bouquets and len(self.bouquets):
1786 keys = ["red", "blue", "", "0", "1", "2", "3", "4", "5", "6", "7", "8", "9" ] + [""] * n
1787 if config.usage.multibouquet.value:
1788 tlist = [(_("Quickzap"), "quickzap", service.subServices()), (_("Add to bouquet"), "CALLFUNC", self.addSubserviceToBouquetCallback), ("--", "")] + tlist
1790 tlist = [(_("Quickzap"), "quickzap", service.subServices()), (_("Add to favourites"), "CALLFUNC", self.addSubserviceToBouquetCallback), ("--", "")] + tlist
1793 tlist = [(_("Quickzap"), "quickzap", service.subServices()), ("--", "")] + tlist
1794 keys = ["red", "", "0", "1", "2", "3", "4", "5", "6", "7", "8", "9" ] + [""] * n
1797 self.session.openWithCallback(self.subserviceSelected, ChoiceBox, title=_("Please select a subservice..."), list = tlist, selection = selection, keys = keys, skin_name = "SubserviceSelection")
1799 def subserviceSelected(self, service):
1801 if not service is None:
1802 if isinstance(service[1], str):
1803 if service[1] == "quickzap":
1804 from Screens.SubservicesQuickzap import SubservicesQuickzap
1805 self.session.open(SubservicesQuickzap, service[2])
1807 self["SubserviceQuickzapAction"].setEnabled(True)
1808 self.session.nav.playService(service[1], False)
1810 def addSubserviceToBouquetCallback(self, service):
1811 if len(service) > 1 and isinstance(service[1], eServiceReference):
1812 self.selectedSubservice = service
1813 if self.bouquets is None:
1816 cnt = len(self.bouquets)
1817 if cnt > 1: # show bouquet list
1818 self.bsel = self.session.openWithCallback(self.bouquetSelClosed, BouquetSelector, self.bouquets, self.addSubserviceToBouquet)
1819 elif cnt == 1: # add to only one existing bouquet
1820 self.addSubserviceToBouquet(self.bouquets[0][1])
1821 self.session.open(MessageBox, _("Service has been added to the favourites."), MessageBox.TYPE_INFO)
1823 def bouquetSelClosed(self, confirmed):
1825 del self.selectedSubservice
1827 self.session.open(MessageBox, _("Service has been added to the selected bouquet."), MessageBox.TYPE_INFO)
1829 def addSubserviceToBouquet(self, dest):
1830 self.servicelist.addServiceToBouquet(dest, self.selectedSubservice[1])
1832 self.bsel.close(True)
1834 del self.selectedSubservice
1836 class InfoBarAdditionalInfo:
1839 self["RecordingPossible"] = Boolean(fixed=harddiskmanager.HDDCount() > 0 and config.misc.rcused.value == 1)
1840 self["TimeshiftPossible"] = self["RecordingPossible"]
1841 self["ShowTimeshiftOnYellow"] = Boolean(fixed=(not config.misc.rcused.value == 0))
1842 self["ShowAudioOnYellow"] = Boolean(fixed=config.misc.rcused.value == 0)
1843 self["ShowRecordOnRed"] = Boolean(fixed=config.misc.rcused.value == 1)
1844 self["ExtensionsAvailable"] = Boolean(fixed=1)
1846 class InfoBarNotifications:
1848 self.onExecBegin.append(self.checkNotifications)
1849 Notifications.notificationAdded.append(self.checkNotificationsIfExecing)
1850 self.onClose.append(self.__removeNotification)
1852 def __removeNotification(self):
1853 Notifications.notificationAdded.remove(self.checkNotificationsIfExecing)
1855 def checkNotificationsIfExecing(self):
1857 self.checkNotifications()
1859 def checkNotifications(self):
1860 notifications = Notifications.notifications
1862 n = notifications[0]
1864 del notifications[0]
1867 if n[3].has_key("onSessionOpenCallback"):
1868 n[3]["onSessionOpenCallback"]()
1869 del n[3]["onSessionOpenCallback"]
1872 dlg = self.session.openWithCallback(cb, n[1], *n[2], **n[3])
1874 dlg = self.session.open(n[1], *n[2], **n[3])
1876 # remember that this notification is currently active
1878 Notifications.current_notifications.append(d)
1879 dlg.onClose.append(boundFunction(self.__notificationClosed, d))
1881 def __notificationClosed(self, d):
1882 Notifications.current_notifications.remove(d)
1884 class InfoBarServiceNotifications:
1886 self.__event_tracker = ServiceEventTracker(screen=self, eventmap=
1888 iPlayableService.evEnd: self.serviceHasEnded
1891 def serviceHasEnded(self):
1892 print "service end!"
1895 self.setSeekState(self.SEEK_STATE_PLAY)
1899 class InfoBarCueSheetSupport:
1905 ENABLE_RESUME_SUPPORT = False
1907 def __init__(self, actionmap = "InfobarCueSheetActions"):
1908 self["CueSheetActions"] = HelpableActionMap(self, actionmap,
1910 "jumpPreviousMark": (self.jumpPreviousMark, _("jump to previous marked position")),
1911 "jumpNextMark": (self.jumpNextMark, _("jump to next marked position")),
1912 "toggleMark": (self.toggleMark, _("toggle a cut mark at the current position"))
1916 self.is_closing = False
1917 self.__event_tracker = ServiceEventTracker(screen=self, eventmap=
1919 iPlayableService.evStart: self.__serviceStarted,
1922 def __serviceStarted(self):
1925 print "new service started! trying to download cuts!"
1926 self.downloadCuesheet()
1928 if self.ENABLE_RESUME_SUPPORT:
1931 for (pts, what) in self.cut_list:
1932 if what == self.CUT_TYPE_LAST:
1935 if last is not None:
1936 self.resume_point = last
1937 if config.usage.on_movie_start.value == "ask":
1938 Notifications.AddNotificationWithCallback(self.playLastCB, MessageBox, _("Do you want to resume this playback?"), timeout=10)
1939 elif config.usage.on_movie_start.value == "resume":
1940 # TRANSLATORS: The string "Resuming playback" flashes for a moment
1941 # TRANSLATORS: at the start of a movie, when the user has selected
1942 # TRANSLATORS: "Resume from last position" as start behavior.
1943 # TRANSLATORS: The purpose is to notify the user that the movie starts
1944 # TRANSLATORS: in the middle somewhere and not from the beginning.
1945 # TRANSLATORS: (Some translators seem to have interpreted it as a
1946 # TRANSLATORS: question or a choice, but it is a statement.)
1947 Notifications.AddNotificationWithCallback(self.playLastCB, MessageBox, _("Resuming playback"), timeout=2, type=MessageBox.TYPE_INFO)
1949 def playLastCB(self, answer):
1951 self.doSeek(self.resume_point)
1952 self.hideAfterResume()
1954 def hideAfterResume(self):
1955 if isinstance(self, InfoBarShowHide):
1958 def __getSeekable(self):
1959 service = self.session.nav.getCurrentService()
1962 return service.seek()
1964 def cueGetCurrentPosition(self):
1965 seek = self.__getSeekable()
1968 r = seek.getPlayPosition()
1973 def cueGetEndCutPosition(self):
1976 for cp in self.cut_list:
1977 if cp[1] == self.CUT_TYPE_OUT:
1981 elif cp[1] == self.CUT_TYPE_IN:
1985 def jumpPreviousNextMark(self, cmp, start=False):
1986 current_pos = self.cueGetCurrentPosition()
1987 if current_pos is None:
1989 mark = self.getNearestCutPoint(current_pos, cmp=cmp, start=start)
1990 if mark is not None:
1998 def jumpPreviousMark(self):
1999 # we add 2 seconds, so if the play position is <2s after
2000 # the mark, the mark before will be used
2001 self.jumpPreviousNextMark(lambda x: -x-5*90000, start=True)
2003 def jumpNextMark(self):
2004 if not self.jumpPreviousNextMark(lambda x: x):
2007 def getNearestCutPoint(self, pts, cmp=abs, start=False):
2013 bestdiff = cmp(0 - pts)
2015 nearest = [0, False]
2016 for cp in self.cut_list:
2017 if beforecut and cp[1] in (self.CUT_TYPE_IN, self.CUT_TYPE_OUT):
2019 if cp[1] == self.CUT_TYPE_IN: # Start is here, disregard previous marks
2020 diff = cmp(cp[0] - pts)
2026 if cp[1] in (self.CUT_TYPE_MARK, self.CUT_TYPE_LAST):
2027 diff = cmp(cp[0] - pts)
2028 if diff >= 0 and (nearest is None or bestdiff > diff):
2033 def toggleMark(self, onlyremove=False, onlyadd=False, tolerance=5*90000, onlyreturn=False):
2034 current_pos = self.cueGetCurrentPosition()
2035 if current_pos is None:
2036 print "not seekable"
2039 nearest_cutpoint = self.getNearestCutPoint(current_pos)
2041 if nearest_cutpoint is not None and abs(nearest_cutpoint[0] - current_pos) < tolerance:
2043 return nearest_cutpoint
2045 self.removeMark(nearest_cutpoint)
2046 elif not onlyremove and not onlyreturn:
2047 self.addMark((current_pos, self.CUT_TYPE_MARK))
2052 def addMark(self, point):
2053 insort(self.cut_list, point)
2054 self.uploadCuesheet()
2055 self.showAfterCuesheetOperation()
2057 def removeMark(self, point):
2058 self.cut_list.remove(point)
2059 self.uploadCuesheet()
2060 self.showAfterCuesheetOperation()
2062 def showAfterCuesheetOperation(self):
2063 if isinstance(self, InfoBarShowHide):
2066 def __getCuesheet(self):
2067 service = self.session.nav.getCurrentService()
2070 return service.cueSheet()
2072 def uploadCuesheet(self):
2073 cue = self.__getCuesheet()
2076 print "upload failed, no cuesheet interface"
2078 cue.setCutList(self.cut_list)
2080 def downloadCuesheet(self):
2081 cue = self.__getCuesheet()
2084 print "download failed, no cuesheet interface"
2087 self.cut_list = cue.getCutList()
2089 class InfoBarSummary(Screen):
2091 <screen position="0,0" size="132,64">
2092 <widget source="global.CurrentTime" render="Label" position="62,46" size="82,18" font="Regular;16" >
2093 <convert type="ClockToText">WithSeconds</convert>
2095 <widget source="session.RecordState" render="FixedLabel" text=" " position="62,46" size="82,18" zPosition="1" >
2096 <convert type="ConfigEntryTest">config.usage.blinking_display_clock_during_recording,True,CheckSourceBoolean</convert>
2097 <convert type="ConditionalShowHide">Blink</convert>
2099 <widget source="session.CurrentService" render="Label" position="6,4" size="120,42" font="Regular;18" >
2100 <convert type="ServiceName">Name</convert>
2102 <widget source="session.Event_Now" render="Progress" position="6,46" size="46,18" borderWidth="1" >
2103 <convert type="EventTime">Progress</convert>
2107 # for picon: (path="piconlcd" will use LCD picons)
2108 # <widget source="session.CurrentService" render="Picon" position="6,0" size="120,64" path="piconlcd" >
2109 # <convert type="ServiceName">Reference</convert>
2112 class InfoBarSummarySupport:
2116 def createSummary(self):
2117 return InfoBarSummary
2119 class InfoBarMoviePlayerSummary(Screen):
2121 <screen position="0,0" size="132,64">
2122 <widget source="global.CurrentTime" render="Label" position="62,46" size="64,18" font="Regular;16" halign="right" >
2123 <convert type="ClockToText">WithSeconds</convert>
2125 <widget source="session.RecordState" render="FixedLabel" text=" " position="62,46" size="64,18" zPosition="1" >
2126 <convert type="ConfigEntryTest">config.usage.blinking_display_clock_during_recording,True,CheckSourceBoolean</convert>
2127 <convert type="ConditionalShowHide">Blink</convert>
2129 <widget source="session.CurrentService" render="Label" position="6,4" size="120,42" font="Regular;18" >
2130 <convert type="ServiceName">Name</convert>
2132 <widget source="session.CurrentService" render="Progress" position="6,46" size="56,18" borderWidth="1" >
2133 <convert type="ServicePosition">Position</convert>
2137 class InfoBarMoviePlayerSummarySupport:
2141 def createSummary(self):
2142 return InfoBarMoviePlayerSummary
2144 class InfoBarTeletextPlugin:
2146 self.teletext_plugin = None
2148 for p in plugins.getPlugins(PluginDescriptor.WHERE_TELETEXT):
2149 self.teletext_plugin = p
2151 if self.teletext_plugin is not None:
2152 self["TeletextActions"] = HelpableActionMap(self, "InfobarTeletextActions",
2154 "startTeletext": (self.startTeletext, _("View teletext..."))
2157 print "no teletext plugin found!"
2159 def startTeletext(self):
2160 self.teletext_plugin(session=self.session, service=self.session.nav.getCurrentService())
2162 class InfoBarSubtitleSupport(object):
2164 object.__init__(self)
2165 self.subtitle_window = self.session.instantiateDialog(SubtitleDisplay)
2166 self.__subtitles_enabled = False
2168 self.__event_tracker = ServiceEventTracker(screen=self, eventmap=
2170 iPlayableService.evEnd: self.__serviceStopped,
2171 iPlayableService.evUpdatedInfo: self.__updatedInfo
2173 self.cached_subtitle_checked = False
2174 self.__selected_subtitle = None
2176 def __serviceStopped(self):
2177 self.cached_subtitle_checked = False
2178 if self.__subtitles_enabled:
2179 self.subtitle_window.hide()
2180 self.__subtitles_enabled = False
2181 self.__selected_subtitle = None
2183 def __updatedInfo(self):
2184 if not self.cached_subtitle_checked:
2185 self.cached_subtitle_checked = True
2186 subtitle = self.getCurrentServiceSubtitle()
2187 self.setSelectedSubtitle(subtitle and subtitle.getCachedSubtitle())
2188 if self.__selected_subtitle:
2189 self.setSubtitlesEnable(True)
2191 def getCurrentServiceSubtitle(self):
2192 service = self.session.nav.getCurrentService()
2193 return service and service.subtitle()
2195 def setSubtitlesEnable(self, enable=True):
2196 subtitle = self.getCurrentServiceSubtitle()
2198 if self.__selected_subtitle:
2199 if subtitle and not self.__subtitles_enabled:
2200 subtitle.enableSubtitles(self.subtitle_window.instance, self.selected_subtitle)
2201 self.subtitle_window.show()
2202 self.__subtitles_enabled = True
2205 subtitle.disableSubtitles(self.subtitle_window.instance)
2206 self.__selected_subtitle = False
2207 self.__subtitles_enabled = False
2208 self.subtitle_window.hide()
2210 def setSelectedSubtitle(self, subtitle):
2211 self.__selected_subtitle = subtitle
2213 subtitles_enabled = property(lambda self: self.__subtitles_enabled, setSubtitlesEnable)
2214 selected_subtitle = property(lambda self: self.__selected_subtitle, setSelectedSubtitle)
2216 class InfoBarServiceErrorPopupSupport:
2218 self.__event_tracker = ServiceEventTracker(screen=self, eventmap=
2220 iPlayableService.evTuneFailed: self.__tuneFailed,
2221 iPlayableService.evStart: self.__serviceStarted
2223 self.__serviceStarted()
2225 def __serviceStarted(self):
2226 self.last_error = None
2227 Notifications.RemovePopup(id = "ZapError")
2229 def __tuneFailed(self):
2230 service = self.session.nav.getCurrentService()
2231 info = service and service.info()
2232 error = info and info.getInfo(iServiceInformation.sDVBState)
2234 if error == self.last_error:
2237 self.last_error = error
2240 eDVBServicePMTHandler.eventNoResources: _("No free tuner!"),
2241 eDVBServicePMTHandler.eventTuneFailed: _("Tune failed!"),
2242 eDVBServicePMTHandler.eventNoPAT: _("No data on transponder!\n(Timeout reading PAT)"),
2243 eDVBServicePMTHandler.eventNoPATEntry: _("Service not found!\n(SID not found in PAT)"),
2244 eDVBServicePMTHandler.eventNoPMT: _("Service invalid!\n(Timeout reading PMT)"),
2245 eDVBServicePMTHandler.eventNewProgramInfo: None,
2246 eDVBServicePMTHandler.eventTuned: None,
2247 eDVBServicePMTHandler.eventSOF: None,
2248 eDVBServicePMTHandler.eventEOF: None,
2249 eDVBServicePMTHandler.eventMisconfiguration: _("Service unavailable!\nCheck tuner configuration!"),
2250 }.get(error) #this returns None when the key not exist in the dict
2252 if error is not None:
2253 Notifications.AddPopup(text = error, type = MessageBox.TYPE_ERROR, timeout = 5, id = "ZapError")
2255 Notifications.RemovePopup(id = "ZapError")