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.doSeek(-5) # seek some gops before end
1209 self.ts_rewind_timer.start(200, 1)
1211 self.doSeek(-1) # seek 1 gop before end
1213 def rewindService(self):
1214 self.setSeekState(self.makeStateBackward(int(config.seek.enter_backward.value)))
1216 # same as activateTimeshiftEnd, but pauses afterwards.
1217 def activateTimeshiftEndAndPause(self):
1218 print "activateTimeshiftEndAndPause"
1219 #state = self.seekstate
1220 self.activateTimeshiftEnd(False)
1222 def __seekableStatusChanged(self):
1225 # print "self.isSeekable", self.isSeekable()
1226 # print "self.timeshift_enabled", self.timeshift_enabled
1228 # when this service is not seekable, but timeshift
1229 # is enabled, this means we can activate
1231 if not self.isSeekable() and self.timeshift_enabled:
1234 # print "timeshift activate:", enabled
1235 self["TimeshiftActivateActions"].setEnabled(enabled)
1237 def __serviceStarted(self):
1238 self.timeshift_enabled = False
1239 self.__seekableStatusChanged()
1241 from Screens.PiPSetup import PiPSetup
1243 class InfoBarExtensions:
1244 EXTENSION_SINGLE = 0
1250 self["InstantExtensionsActions"] = HelpableActionMap(self, "InfobarExtensions",
1252 "extensions": (self.showExtensionSelection, _("view extensions...")),
1253 }, 1) # lower priority
1255 def addExtension(self, extension, key = None, type = EXTENSION_SINGLE):
1256 self.list.append((type, extension, key))
1258 def updateExtension(self, extension, key = None):
1259 self.extensionsList.append(extension)
1261 if self.extensionKeys.has_key(key):
1265 for x in self.availableKeys:
1266 if not self.extensionKeys.has_key(x):
1271 self.extensionKeys[key] = len(self.extensionsList) - 1
1273 def updateExtensions(self):
1274 self.extensionsList = []
1275 self.availableKeys = [ "1", "2", "3", "4", "5", "6", "7", "8", "9", "0", "red", "green", "yellow", "blue" ]
1276 self.extensionKeys = {}
1278 if x[0] == self.EXTENSION_SINGLE:
1279 self.updateExtension(x[1], x[2])
1282 self.updateExtension(y[0], y[1])
1285 def showExtensionSelection(self):
1286 self.updateExtensions()
1287 extensionsList = self.extensionsList[:]
1290 for x in self.availableKeys:
1291 if self.extensionKeys.has_key(x):
1292 entry = self.extensionKeys[x]
1293 extension = self.extensionsList[entry]
1295 name = str(extension[0]())
1296 list.append((extension[0](), extension))
1298 extensionsList.remove(extension)
1300 extensionsList.remove(extension)
1301 list.extend([(x[0](), x) for x in extensionsList])
1303 keys += [""] * len(extensionsList)
1304 self.session.openWithCallback(self.extensionCallback, ChoiceBox, title=_("Please choose an extension..."), list = list, keys = keys, skin_name = "ExtensionsList")
1306 def extensionCallback(self, answer):
1307 if answer is not None:
1310 from Tools.BoundFunction import boundFunction
1312 # depends on InfoBarExtensions
1314 class InfoBarPlugins:
1316 self.addExtension(extension = self.getPluginList, type = InfoBarExtensions.EXTENSION_LIST)
1318 def getPluginName(self, name):
1321 def getPluginList(self):
1322 list = [((boundFunction(self.getPluginName, p.name), boundFunction(self.runPlugin, p), lambda: True), None, p.name) for p in plugins.getPlugins(where = PluginDescriptor.WHERE_EXTENSIONSMENU)]
1323 list.sort(key = lambda e: e[2]) # sort by name
1326 def runPlugin(self, plugin):
1327 if isinstance(self, InfoBarChannelSelection):
1328 plugin(session = self.session, servicelist = self.servicelist)
1330 plugin(session = self.session)
1332 from Components.Task import job_manager
1333 class InfoBarJobman:
1335 self.addExtension(extension = self.getJobList, type = InfoBarExtensions.EXTENSION_LIST)
1337 def getJobList(self):
1338 return [((boundFunction(self.getJobName, job), boundFunction(self.showJobView, job), lambda: True), None) for job in job_manager.getPendingJobs()]
1340 def getJobName(self, job):
1341 return "%s: %s (%d%%)" % (job.getStatustext(), job.name, int(100*job.progress/float(job.end)))
1343 def showJobView(self, job):
1344 from Screens.TaskView import JobView
1345 job_manager.in_background = False
1346 self.session.openWithCallback(self.JobViewCB, JobView, job)
1348 def JobViewCB(self, in_background):
1349 job_manager.in_background = in_background
1351 # depends on InfoBarExtensions
1355 self.session.pipshown
1357 self.session.pipshown = False
1358 if SystemInfo.get("NumVideoDecoders", 1) > 1:
1360 self.addExtension((self.getShowHideName, self.showPiP, lambda: True), "blue")
1361 self.addExtension((self.getMoveName, self.movePiP, self.pipShown), "green")
1362 self.addExtension((self.getSwapName, self.swapPiP, self.pipShown), "yellow")
1364 self.addExtension((self.getShowHideName, self.showPiP, self.pipShown), "blue")
1365 self.addExtension((self.getMoveName, self.movePiP, self.pipShown), "green")
1368 return self.session.pipshown
1370 def pipHandles0Action(self):
1371 return self.pipShown() and config.usage.pip_zero_button.value != "standard"
1373 def getShowHideName(self):
1374 if self.session.pipshown:
1375 return _("Disable Picture in Picture")
1377 return _("Activate Picture in Picture")
1379 def getSwapName(self):
1380 return _("Swap Services")
1382 def getMoveName(self):
1383 return _("Move Picture in Picture")
1386 if self.session.pipshown:
1387 del self.session.pip
1388 self.session.pipshown = False
1390 self.session.pip = self.session.instantiateDialog(PictureInPicture)
1391 self.session.pip.show()
1392 newservice = self.session.nav.getCurrentlyPlayingServiceReference()
1393 if self.session.pip.playService(newservice):
1394 self.session.pipshown = True
1395 self.session.pip.servicePath = self.servicelist.getCurrentServicePath()
1397 self.session.pipshown = False
1398 del self.session.pip
1399 self.session.nav.playService(newservice)
1402 swapservice = self.session.nav.getCurrentlyPlayingServiceReference()
1403 if self.session.pip.servicePath:
1404 servicepath = self.servicelist.getCurrentServicePath()
1405 ref=servicepath[len(servicepath)-1]
1406 pipref=self.session.pip.getCurrentService()
1407 self.session.pip.playService(swapservice)
1408 self.servicelist.setCurrentServicePath(self.session.pip.servicePath)
1409 if pipref.toString() != ref.toString(): # is a subservice ?
1410 self.session.nav.stopService() # stop portal
1411 self.session.nav.playService(pipref) # start subservice
1412 self.session.pip.servicePath=servicepath
1415 self.session.open(PiPSetup, pip = self.session.pip)
1417 def pipDoHandle0Action(self):
1418 use = config.usage.pip_zero_button.value
1421 elif "swapstop" == use:
1427 from RecordTimer import parseEvent, RecordTimerEntry
1429 class InfoBarInstantRecord:
1430 """Instant Record - handles the instantRecord action in order to
1431 start/stop instant records"""
1433 self["InstantRecordActions"] = HelpableActionMap(self, "InfobarInstantRecord",
1435 "instantRecord": (self.instantRecord, _("Instant Record...")),
1439 def stopCurrentRecording(self, entry = -1):
1440 if entry is not None and entry != -1:
1441 self.session.nav.RecordTimer.removeEntry(self.recording[entry])
1442 self.recording.remove(self.recording[entry])
1444 def startInstantRecording(self, limitEvent = False):
1445 serviceref = self.session.nav.getCurrentlyPlayingServiceReference()
1447 # try to get event info
1450 service = self.session.nav.getCurrentService()
1451 epg = eEPGCache.getInstance()
1452 event = epg.lookupEventTime(serviceref, -1, 0)
1454 info = service.info()
1455 ev = info.getEvent(0)
1461 end = begin + 3600 # dummy
1462 name = "instant record"
1466 if event is not None:
1467 curEvent = parseEvent(event)
1469 description = curEvent[3]
1470 eventid = curEvent[4]
1475 self.session.open(MessageBox, _("No event info found, recording indefinitely."), MessageBox.TYPE_INFO)
1477 if isinstance(serviceref, eServiceReference):
1478 serviceref = ServiceReference(serviceref)
1480 recording = RecordTimerEntry(serviceref, begin, end, name, description, eventid, dirname = config.movielist.last_videodir.value)
1481 recording.dontSave = True
1483 if event is None or limitEvent == False:
1484 recording.autoincrease = True
1485 if recording.setAutoincreaseEnd():
1486 self.session.nav.RecordTimer.record(recording)
1487 self.recording.append(recording)
1489 simulTimerList = self.session.nav.RecordTimer.record(recording)
1490 if simulTimerList is not None: # conflict with other recording
1491 name = simulTimerList[1].name
1492 name_date = ' '.join((name, strftime('%c', localtime(simulTimerList[1].begin))))
1493 print "[TIMER] conflicts with", name_date
1494 recording.autoincrease = True # start with max available length, then increment
1495 if recording.setAutoincreaseEnd():
1496 self.session.nav.RecordTimer.record(recording)
1497 self.recording.append(recording)
1498 self.session.open(MessageBox, _("Record time limited due to conflicting timer %s") % name_date, MessageBox.TYPE_INFO)
1500 self.session.open(MessageBox, _("Couldn't record due to conflicting timer %s") % name, MessageBox.TYPE_INFO)
1501 recording.autoincrease = False
1503 self.recording.append(recording)
1505 def isInstantRecordRunning(self):
1506 print "self.recording:", self.recording
1508 for x in self.recording:
1513 def recordQuestionCallback(self, answer):
1514 print "pre:\n", self.recording
1516 if answer is None or answer[1] == "no":
1519 recording = self.recording[:]
1521 if not x in self.session.nav.RecordTimer.timer_list:
1522 self.recording.remove(x)
1523 elif x.dontSave and x.isRunning():
1524 list.append((x, False))
1526 if answer[1] == "changeduration":
1527 if len(self.recording) == 1:
1528 self.changeDuration(0)
1530 self.session.openWithCallback(self.changeDuration, TimerSelection, list)
1531 elif answer[1] == "changeendtime":
1532 if len(self.recording) == 1:
1535 self.session.openWithCallback(self.setEndtime, TimerSelection, list)
1536 elif answer[1] == "stop":
1537 if len(self.recording) == 1:
1538 self.stopCurrentRecording(0)
1540 self.session.openWithCallback(self.stopCurrentRecording, TimerSelection, list)
1541 elif answer[1] in ( "indefinitely" , "manualduration", "manualendtime", "event"):
1542 self.startInstantRecording(limitEvent = answer[1] in ("event", "manualendtime") or False)
1543 if answer[1] == "manualduration":
1544 self.changeDuration(len(self.recording)-1)
1545 elif answer[1] == "manualendtime":
1546 self.setEndtime(len(self.recording)-1)
1547 print "after:\n", self.recording
1549 def setEndtime(self, entry):
1550 if entry is not None and entry >= 0:
1551 self.selectedEntry = entry
1552 self.endtime=ConfigClock(default = self.recording[self.selectedEntry].end)
1553 dlg = self.session.openWithCallback(self.TimeDateInputClosed, TimeDateInput, self.endtime)
1554 dlg.setTitle(_("Please change recording endtime"))
1556 def TimeDateInputClosed(self, ret):
1559 localendtime = localtime(ret[1])
1560 print "stopping recording at", strftime("%c", localendtime)
1561 if self.recording[self.selectedEntry].end != ret[1]:
1562 self.recording[self.selectedEntry].autoincrease = False
1563 self.recording[self.selectedEntry].end = ret[1]
1564 self.session.nav.RecordTimer.timeChanged(self.recording[self.selectedEntry])
1566 def changeDuration(self, entry):
1567 if entry is not None and entry >= 0:
1568 self.selectedEntry = entry
1569 self.session.openWithCallback(self.inputCallback, InputBox, title=_("How many minutes do you want to record?"), text="5", maxSize=False, type=Input.NUMBER)
1571 def inputCallback(self, value):
1572 if value is not None:
1573 print "stopping recording after", int(value), "minutes."
1574 entry = self.recording[self.selectedEntry]
1576 entry.autoincrease = False
1577 entry.end = int(time()) + 60 * int(value)
1578 self.session.nav.RecordTimer.timeChanged(entry)
1580 def instantRecord(self):
1581 dir = config.movielist.last_videodir.value
1582 if not fileExists(dir, 'w'):
1583 dir = resolveFilename(SCOPE_HDD)
1587 # XXX: this message is a little odd as we might be recording to a remote device
1588 self.session.open(MessageBox, _("No HDD found or HDD not initialized!"), MessageBox.TYPE_ERROR)
1591 if self.isInstantRecordRunning():
1592 self.session.openWithCallback(self.recordQuestionCallback, ChoiceBox, \
1593 title=_("A recording is currently running.\nWhat do you want to do?"), \
1594 list=((_("stop recording"), "stop"), \
1595 (_("add recording (stop after current event)"), "event"), \
1596 (_("add recording (indefinitely)"), "indefinitely"), \
1597 (_("add recording (enter recording duration)"), "manualduration"), \
1598 (_("add recording (enter recording endtime)"), "manualendtime"), \
1599 (_("change recording (duration)"), "changeduration"), \
1600 (_("change recording (endtime)"), "changeendtime"), \
1601 (_("do nothing"), "no")))
1603 self.session.openWithCallback(self.recordQuestionCallback, ChoiceBox, \
1604 title=_("Start recording?"), \
1605 list=((_("add recording (stop after current event)"), "event"), \
1606 (_("add recording (indefinitely)"), "indefinitely"), \
1607 (_("add recording (enter recording duration)"), "manualduration"), \
1608 (_("add recording (enter recording endtime)"), "manualendtime"), \
1609 (_("don't record"), "no")))
1611 from Tools.ISO639 import LanguageCodes
1613 class InfoBarAudioSelection:
1615 self["AudioSelectionAction"] = HelpableActionMap(self, "InfobarAudioSelectionActions",
1617 "audioSelection": (self.audioSelection, _("Audio Options...")),
1620 def audioSelection(self):
1621 service = self.session.nav.getCurrentService()
1622 self.audioTracks = audio = service and service.audioTracks()
1623 n = audio and audio.getNumberOfTracks() or 0
1626 self.audioChannel = service.audioChannel()
1631 i = audio.getTrackInfo(idx)
1632 languages = i.getLanguage().split('/')
1633 description = i.getDescription()
1636 for lang in languages:
1639 if LanguageCodes.has_key(lang):
1640 language += LanguageCodes[lang][0]
1645 if len(description):
1646 description += " (" + language + ")"
1648 description = language
1650 tlist.append((description, idx))
1653 tlist.sort(key=lambda x: x[0])
1655 selectedAudio = self.audioTracks.getCurrentTrack()
1660 if x[1] != selectedAudio:
1665 if SystemInfo["CanDownmixAC3"]:
1666 tlist = [(_("AC3 downmix") + " - " +(_("Off"), _("On"))[config.av.downmix_ac3.value and 1 or 0], "CALLFUNC", self.changeAC3Downmix),
1667 ((_("Left"), _("Stereo"), _("Right"))[self.audioChannel.getCurrentChannel()], "mode"),
1669 keys = [ "red", "green", "", "1", "2", "3", "4", "5", "6", "7", "8", "9", "0"] + [""]*n
1672 tlist = [((_("Left"), _("Stereo"), _("Right"))[self.audioChannel.getCurrentChannel()], "mode"), ("--", "")] + tlist
1673 keys = [ "red", "", "1", "2", "3", "4", "5", "6", "7", "8", "9", "0"] + [""]*n
1675 self.session.openWithCallback(self.audioSelected, ChoiceBox, title=_("Select audio track"), list = tlist, selection = selection, keys = keys, skin_name = "AudioTrackSelection")
1677 del self.audioTracks
1679 def changeAC3Downmix(self, arg):
1680 choicelist = self.session.current_dialog["list"]
1681 list = choicelist.list
1683 list[0][1]=(t[0], t[1], t[2], t[3], t[4], t[5], t[6],
1684 _("AC3 downmix") + " - " + (_("On"), _("Off"))[config.av.downmix_ac3.value and 1 or 0])
1685 choicelist.setList(list)
1686 if config.av.downmix_ac3.value:
1687 config.av.downmix_ac3.value = False
1689 config.av.downmix_ac3.value = True
1690 config.av.downmix_ac3.save()
1692 def audioSelected(self, audio):
1693 if audio is not None:
1694 if isinstance(audio[1], str):
1695 if audio[1] == "mode":
1696 keys = ["red", "green", "yellow"]
1697 selection = self.audioChannel.getCurrentChannel()
1698 tlist = ((_("left"), 0), (_("stereo"), 1), (_("right"), 2))
1699 self.session.openWithCallback(self.modeSelected, ChoiceBox, title=_("Select audio mode"), list = tlist, selection = selection, keys = keys, skin_name ="AudioModeSelection")
1701 del self.audioChannel
1702 if self.session.nav.getCurrentService().audioTracks().getNumberOfTracks() > audio[1]:
1703 self.audioTracks.selectTrack(audio[1])
1705 del self.audioChannel
1706 del self.audioTracks
1708 def modeSelected(self, mode):
1709 if mode is not None:
1710 self.audioChannel.selectChannel(mode[1])
1711 del self.audioChannel
1713 class InfoBarSubserviceSelection:
1715 self["SubserviceSelectionAction"] = HelpableActionMap(self, "InfobarSubserviceSelectionActions",
1717 "subserviceSelection": (self.subserviceSelection, _("Subservice list...")),
1720 self["SubserviceQuickzapAction"] = HelpableActionMap(self, "InfobarSubserviceQuickzapActions",
1722 "nextSubservice": (self.nextSubservice, _("Switch to next subservice")),
1723 "prevSubservice": (self.prevSubservice, _("Switch to previous subservice"))
1725 self["SubserviceQuickzapAction"].setEnabled(False)
1727 self.__event_tracker = ServiceEventTracker(screen=self, eventmap=
1729 iPlayableService.evUpdatedEventInfo: self.checkSubservicesAvail
1734 def checkSubservicesAvail(self):
1735 service = self.session.nav.getCurrentService()
1736 subservices = service and service.subServices()
1737 if not subservices or subservices.getNumberOfSubservices() == 0:
1738 self["SubserviceQuickzapAction"].setEnabled(False)
1740 def nextSubservice(self):
1741 self.changeSubservice(+1)
1743 def prevSubservice(self):
1744 self.changeSubservice(-1)
1746 def changeSubservice(self, direction):
1747 service = self.session.nav.getCurrentService()
1748 subservices = service and service.subServices()
1749 n = subservices and subservices.getNumberOfSubservices()
1752 ref = self.session.nav.getCurrentlyPlayingServiceReference()
1755 if subservices.getSubservice(idx).toString() == ref.toString():
1760 selection += direction
1765 newservice = subservices.getSubservice(selection)
1766 if newservice.valid():
1769 self.session.nav.playService(newservice, False)
1771 def subserviceSelection(self):
1772 service = self.session.nav.getCurrentService()
1773 subservices = service and service.subServices()
1774 self.bouquets = self.servicelist.getBouquetList()
1775 n = subservices and subservices.getNumberOfSubservices()
1778 ref = self.session.nav.getCurrentlyPlayingServiceReference()
1782 i = subservices.getSubservice(idx)
1783 if i.toString() == ref.toString():
1785 tlist.append((i.getName(), i))
1788 if self.bouquets and len(self.bouquets):
1789 keys = ["red", "blue", "", "0", "1", "2", "3", "4", "5", "6", "7", "8", "9" ] + [""] * n
1790 if config.usage.multibouquet.value:
1791 tlist = [(_("Quickzap"), "quickzap", service.subServices()), (_("Add to bouquet"), "CALLFUNC", self.addSubserviceToBouquetCallback), ("--", "")] + tlist
1793 tlist = [(_("Quickzap"), "quickzap", service.subServices()), (_("Add to favourites"), "CALLFUNC", self.addSubserviceToBouquetCallback), ("--", "")] + tlist
1796 tlist = [(_("Quickzap"), "quickzap", service.subServices()), ("--", "")] + tlist
1797 keys = ["red", "", "0", "1", "2", "3", "4", "5", "6", "7", "8", "9" ] + [""] * n
1800 self.session.openWithCallback(self.subserviceSelected, ChoiceBox, title=_("Please select a subservice..."), list = tlist, selection = selection, keys = keys, skin_name = "SubserviceSelection")
1802 def subserviceSelected(self, service):
1804 if not service is None:
1805 if isinstance(service[1], str):
1806 if service[1] == "quickzap":
1807 from Screens.SubservicesQuickzap import SubservicesQuickzap
1808 self.session.open(SubservicesQuickzap, service[2])
1810 self["SubserviceQuickzapAction"].setEnabled(True)
1811 self.session.nav.playService(service[1], False)
1813 def addSubserviceToBouquetCallback(self, service):
1814 if len(service) > 1 and isinstance(service[1], eServiceReference):
1815 self.selectedSubservice = service
1816 if self.bouquets is None:
1819 cnt = len(self.bouquets)
1820 if cnt > 1: # show bouquet list
1821 self.bsel = self.session.openWithCallback(self.bouquetSelClosed, BouquetSelector, self.bouquets, self.addSubserviceToBouquet)
1822 elif cnt == 1: # add to only one existing bouquet
1823 self.addSubserviceToBouquet(self.bouquets[0][1])
1824 self.session.open(MessageBox, _("Service has been added to the favourites."), MessageBox.TYPE_INFO)
1826 def bouquetSelClosed(self, confirmed):
1828 del self.selectedSubservice
1830 self.session.open(MessageBox, _("Service has been added to the selected bouquet."), MessageBox.TYPE_INFO)
1832 def addSubserviceToBouquet(self, dest):
1833 self.servicelist.addServiceToBouquet(dest, self.selectedSubservice[1])
1835 self.bsel.close(True)
1837 del self.selectedSubservice
1839 class InfoBarAdditionalInfo:
1842 self["RecordingPossible"] = Boolean(fixed=harddiskmanager.HDDCount() > 0 and config.misc.rcused.value == 1)
1843 self["TimeshiftPossible"] = self["RecordingPossible"]
1844 self["ShowTimeshiftOnYellow"] = Boolean(fixed=(not config.misc.rcused.value == 0))
1845 self["ShowAudioOnYellow"] = Boolean(fixed=config.misc.rcused.value == 0)
1846 self["ShowRecordOnRed"] = Boolean(fixed=config.misc.rcused.value == 1)
1847 self["ExtensionsAvailable"] = Boolean(fixed=1)
1849 class InfoBarNotifications:
1851 self.onExecBegin.append(self.checkNotifications)
1852 Notifications.notificationAdded.append(self.checkNotificationsIfExecing)
1853 self.onClose.append(self.__removeNotification)
1855 def __removeNotification(self):
1856 Notifications.notificationAdded.remove(self.checkNotificationsIfExecing)
1858 def checkNotificationsIfExecing(self):
1860 self.checkNotifications()
1862 def checkNotifications(self):
1863 notifications = Notifications.notifications
1865 n = notifications[0]
1867 del notifications[0]
1870 if n[3].has_key("onSessionOpenCallback"):
1871 n[3]["onSessionOpenCallback"]()
1872 del n[3]["onSessionOpenCallback"]
1875 dlg = self.session.openWithCallback(cb, n[1], *n[2], **n[3])
1877 dlg = self.session.open(n[1], *n[2], **n[3])
1879 # remember that this notification is currently active
1881 Notifications.current_notifications.append(d)
1882 dlg.onClose.append(boundFunction(self.__notificationClosed, d))
1884 def __notificationClosed(self, d):
1885 Notifications.current_notifications.remove(d)
1887 class InfoBarServiceNotifications:
1889 self.__event_tracker = ServiceEventTracker(screen=self, eventmap=
1891 iPlayableService.evEnd: self.serviceHasEnded
1894 def serviceHasEnded(self):
1895 print "service end!"
1898 self.setSeekState(self.SEEK_STATE_PLAY)
1902 class InfoBarCueSheetSupport:
1908 ENABLE_RESUME_SUPPORT = False
1910 def __init__(self, actionmap = "InfobarCueSheetActions"):
1911 self["CueSheetActions"] = HelpableActionMap(self, actionmap,
1913 "jumpPreviousMark": (self.jumpPreviousMark, _("jump to previous marked position")),
1914 "jumpNextMark": (self.jumpNextMark, _("jump to next marked position")),
1915 "toggleMark": (self.toggleMark, _("toggle a cut mark at the current position"))
1919 self.is_closing = False
1920 self.__event_tracker = ServiceEventTracker(screen=self, eventmap=
1922 iPlayableService.evStart: self.__serviceStarted,
1925 def __serviceStarted(self):
1928 print "new service started! trying to download cuts!"
1929 self.downloadCuesheet()
1931 if self.ENABLE_RESUME_SUPPORT:
1934 for (pts, what) in self.cut_list:
1935 if what == self.CUT_TYPE_LAST:
1938 if last is not None:
1939 self.resume_point = last
1940 if config.usage.on_movie_start.value == "ask":
1941 Notifications.AddNotificationWithCallback(self.playLastCB, MessageBox, _("Do you want to resume this playback?"), timeout=10)
1942 elif config.usage.on_movie_start.value == "resume":
1943 # TRANSLATORS: The string "Resuming playback" flashes for a moment
1944 # TRANSLATORS: at the start of a movie, when the user has selected
1945 # TRANSLATORS: "Resume from last position" as start behavior.
1946 # TRANSLATORS: The purpose is to notify the user that the movie starts
1947 # TRANSLATORS: in the middle somewhere and not from the beginning.
1948 # TRANSLATORS: (Some translators seem to have interpreted it as a
1949 # TRANSLATORS: question or a choice, but it is a statement.)
1950 Notifications.AddNotificationWithCallback(self.playLastCB, MessageBox, _("Resuming playback"), timeout=2, type=MessageBox.TYPE_INFO)
1952 def playLastCB(self, answer):
1954 self.doSeek(self.resume_point)
1955 self.hideAfterResume()
1957 def hideAfterResume(self):
1958 if isinstance(self, InfoBarShowHide):
1961 def __getSeekable(self):
1962 service = self.session.nav.getCurrentService()
1965 return service.seek()
1967 def cueGetCurrentPosition(self):
1968 seek = self.__getSeekable()
1971 r = seek.getPlayPosition()
1976 def cueGetEndCutPosition(self):
1979 for cp in self.cut_list:
1980 if cp[1] == self.CUT_TYPE_OUT:
1984 elif cp[1] == self.CUT_TYPE_IN:
1988 def jumpPreviousNextMark(self, cmp, start=False):
1989 current_pos = self.cueGetCurrentPosition()
1990 if current_pos is None:
1992 mark = self.getNearestCutPoint(current_pos, cmp=cmp, start=start)
1993 if mark is not None:
2001 def jumpPreviousMark(self):
2002 # we add 2 seconds, so if the play position is <2s after
2003 # the mark, the mark before will be used
2004 self.jumpPreviousNextMark(lambda x: -x-5*90000, start=True)
2006 def jumpNextMark(self):
2007 if not self.jumpPreviousNextMark(lambda x: x):
2010 def getNearestCutPoint(self, pts, cmp=abs, start=False):
2016 bestdiff = cmp(0 - pts)
2018 nearest = [0, False]
2019 for cp in self.cut_list:
2020 if beforecut and cp[1] in (self.CUT_TYPE_IN, self.CUT_TYPE_OUT):
2022 if cp[1] == self.CUT_TYPE_IN: # Start is here, disregard previous marks
2023 diff = cmp(cp[0] - pts)
2029 if cp[1] in (self.CUT_TYPE_MARK, self.CUT_TYPE_LAST):
2030 diff = cmp(cp[0] - pts)
2031 if diff >= 0 and (nearest is None or bestdiff > diff):
2036 def toggleMark(self, onlyremove=False, onlyadd=False, tolerance=5*90000, onlyreturn=False):
2037 current_pos = self.cueGetCurrentPosition()
2038 if current_pos is None:
2039 print "not seekable"
2042 nearest_cutpoint = self.getNearestCutPoint(current_pos)
2044 if nearest_cutpoint is not None and abs(nearest_cutpoint[0] - current_pos) < tolerance:
2046 return nearest_cutpoint
2048 self.removeMark(nearest_cutpoint)
2049 elif not onlyremove and not onlyreturn:
2050 self.addMark((current_pos, self.CUT_TYPE_MARK))
2055 def addMark(self, point):
2056 insort(self.cut_list, point)
2057 self.uploadCuesheet()
2058 self.showAfterCuesheetOperation()
2060 def removeMark(self, point):
2061 self.cut_list.remove(point)
2062 self.uploadCuesheet()
2063 self.showAfterCuesheetOperation()
2065 def showAfterCuesheetOperation(self):
2066 if isinstance(self, InfoBarShowHide):
2069 def __getCuesheet(self):
2070 service = self.session.nav.getCurrentService()
2073 return service.cueSheet()
2075 def uploadCuesheet(self):
2076 cue = self.__getCuesheet()
2079 print "upload failed, no cuesheet interface"
2081 cue.setCutList(self.cut_list)
2083 def downloadCuesheet(self):
2084 cue = self.__getCuesheet()
2087 print "download failed, no cuesheet interface"
2090 self.cut_list = cue.getCutList()
2092 class InfoBarSummary(Screen):
2094 <screen position="0,0" size="132,64">
2095 <widget source="global.CurrentTime" render="Label" position="62,46" size="82,18" font="Regular;16" >
2096 <convert type="ClockToText">WithSeconds</convert>
2098 <widget source="session.RecordState" render="FixedLabel" text=" " position="62,46" size="82,18" zPosition="1" >
2099 <convert type="ConfigEntryTest">config.usage.blinking_display_clock_during_recording,True,CheckSourceBoolean</convert>
2100 <convert type="ConditionalShowHide">Blink</convert>
2102 <widget source="session.CurrentService" render="Label" position="6,4" size="120,42" font="Regular;18" >
2103 <convert type="ServiceName">Name</convert>
2105 <widget source="session.Event_Now" render="Progress" position="6,46" size="46,18" borderWidth="1" >
2106 <convert type="EventTime">Progress</convert>
2110 # for picon: (path="piconlcd" will use LCD picons)
2111 # <widget source="session.CurrentService" render="Picon" position="6,0" size="120,64" path="piconlcd" >
2112 # <convert type="ServiceName">Reference</convert>
2115 class InfoBarSummarySupport:
2119 def createSummary(self):
2120 return InfoBarSummary
2122 class InfoBarMoviePlayerSummary(Screen):
2124 <screen position="0,0" size="132,64">
2125 <widget source="global.CurrentTime" render="Label" position="62,46" size="64,18" font="Regular;16" halign="right" >
2126 <convert type="ClockToText">WithSeconds</convert>
2128 <widget source="session.RecordState" render="FixedLabel" text=" " position="62,46" size="64,18" zPosition="1" >
2129 <convert type="ConfigEntryTest">config.usage.blinking_display_clock_during_recording,True,CheckSourceBoolean</convert>
2130 <convert type="ConditionalShowHide">Blink</convert>
2132 <widget source="session.CurrentService" render="Label" position="6,4" size="120,42" font="Regular;18" >
2133 <convert type="ServiceName">Name</convert>
2135 <widget source="session.CurrentService" render="Progress" position="6,46" size="56,18" borderWidth="1" >
2136 <convert type="ServicePosition">Position</convert>
2140 class InfoBarMoviePlayerSummarySupport:
2144 def createSummary(self):
2145 return InfoBarMoviePlayerSummary
2147 class InfoBarTeletextPlugin:
2149 self.teletext_plugin = None
2151 for p in plugins.getPlugins(PluginDescriptor.WHERE_TELETEXT):
2152 self.teletext_plugin = p
2154 if self.teletext_plugin is not None:
2155 self["TeletextActions"] = HelpableActionMap(self, "InfobarTeletextActions",
2157 "startTeletext": (self.startTeletext, _("View teletext..."))
2160 print "no teletext plugin found!"
2162 def startTeletext(self):
2163 self.teletext_plugin(session=self.session, service=self.session.nav.getCurrentService())
2165 class InfoBarSubtitleSupport(object):
2167 object.__init__(self)
2168 self.subtitle_window = self.session.instantiateDialog(SubtitleDisplay)
2169 self.__subtitles_enabled = False
2171 self.__event_tracker = ServiceEventTracker(screen=self, eventmap=
2173 iPlayableService.evEnd: self.__serviceStopped,
2174 iPlayableService.evUpdatedInfo: self.__updatedInfo
2176 self.cached_subtitle_checked = False
2177 self.__selected_subtitle = None
2179 def __serviceStopped(self):
2180 self.cached_subtitle_checked = False
2181 if self.__subtitles_enabled:
2182 self.subtitle_window.hide()
2183 self.__subtitles_enabled = False
2184 self.__selected_subtitle = None
2186 def __updatedInfo(self):
2187 if not self.cached_subtitle_checked:
2188 self.cached_subtitle_checked = True
2189 subtitle = self.getCurrentServiceSubtitle()
2190 self.setSelectedSubtitle(subtitle and subtitle.getCachedSubtitle())
2191 if self.__selected_subtitle:
2192 self.setSubtitlesEnable(True)
2194 def getCurrentServiceSubtitle(self):
2195 service = self.session.nav.getCurrentService()
2196 return service and service.subtitle()
2198 def setSubtitlesEnable(self, enable=True):
2199 subtitle = self.getCurrentServiceSubtitle()
2201 if self.__selected_subtitle:
2202 if subtitle and not self.__subtitles_enabled:
2203 subtitle.enableSubtitles(self.subtitle_window.instance, self.selected_subtitle)
2204 self.subtitle_window.show()
2205 self.__subtitles_enabled = True
2208 subtitle.disableSubtitles(self.subtitle_window.instance)
2209 self.__selected_subtitle = False
2210 self.__subtitles_enabled = False
2211 self.subtitle_window.hide()
2213 def setSelectedSubtitle(self, subtitle):
2214 self.__selected_subtitle = subtitle
2216 subtitles_enabled = property(lambda self: self.__subtitles_enabled, setSubtitlesEnable)
2217 selected_subtitle = property(lambda self: self.__selected_subtitle, setSelectedSubtitle)
2219 class InfoBarServiceErrorPopupSupport:
2221 self.__event_tracker = ServiceEventTracker(screen=self, eventmap=
2223 iPlayableService.evTuneFailed: self.__tuneFailed,
2224 iPlayableService.evStart: self.__serviceStarted
2226 self.__serviceStarted()
2228 def __serviceStarted(self):
2229 self.last_error = None
2230 Notifications.RemovePopup(id = "ZapError")
2232 def __tuneFailed(self):
2233 service = self.session.nav.getCurrentService()
2234 info = service and service.info()
2235 error = info and info.getInfo(iServiceInformation.sDVBState)
2237 if error == self.last_error:
2240 self.last_error = error
2243 eDVBServicePMTHandler.eventNoResources: _("No free tuner!"),
2244 eDVBServicePMTHandler.eventTuneFailed: _("Tune failed!"),
2245 eDVBServicePMTHandler.eventNoPAT: _("No data on transponder!\n(Timeout reading PAT)"),
2246 eDVBServicePMTHandler.eventNoPATEntry: _("Service not found!\n(SID not found in PAT)"),
2247 eDVBServicePMTHandler.eventNoPMT: _("Service invalid!\n(Timeout reading PMT)"),
2248 eDVBServicePMTHandler.eventNewProgramInfo: None,
2249 eDVBServicePMTHandler.eventTuned: None,
2250 eDVBServicePMTHandler.eventSOF: None,
2251 eDVBServicePMTHandler.eventEOF: None,
2252 eDVBServicePMTHandler.eventMisconfiguration: _("Service unavailable!\nCheck tuner configuration!"),
2253 }.get(error) #this returns None when the key not exist in the dict
2255 if error is not None:
2256 Notifications.AddPopup(text = error, type = MessageBox.TYPE_ERROR, timeout = 5, id = "ZapError")
2258 Notifications.RemovePopup(id = "ZapError")