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 self.minSpeedBackward = useSeekBackHack and 16 or 0
696 class InfoBarSeekActionMap(HelpableActionMap):
697 def __init__(self, screen, *args, **kwargs):
698 HelpableActionMap.__init__(self, screen, *args, **kwargs)
701 def action(self, contexts, action):
702 print "action:", action
703 if action[:5] == "seek:":
704 time = int(action[5:])
705 self.screen.doSeekRelative(time * 90000)
707 elif action[:8] == "seekdef:":
708 key = int(action[8:])
709 time = (-config.seek.selfdefined_13.value, False, config.seek.selfdefined_13.value,
710 -config.seek.selfdefined_46.value, False, config.seek.selfdefined_46.value,
711 -config.seek.selfdefined_79.value, False, config.seek.selfdefined_79.value)[key-1]
712 self.screen.doSeekRelative(time * 90000)
715 return HelpableActionMap.action(self, contexts, action)
717 self["SeekActions"] = InfoBarSeekActionMap(self, actionmap,
719 "playpauseService": self.playpauseService,
720 "pauseService": (self.pauseService, _("pause")),
721 "unPauseService": (self.unPauseService, _("continue")),
723 "seekFwd": (self.seekFwd, _("skip forward")),
724 "seekFwdManual": (self.seekFwdManual, _("skip forward (enter time)")),
725 "seekBack": (self.seekBack, _("skip backward")),
726 "seekBackManual": (self.seekBackManual, _("skip backward (enter time)"))
728 # give them a little more priority to win over color buttons
730 self["SeekActions"].setEnabled(False)
732 self.seekstate = self.SEEK_STATE_PLAY
733 self.lastseekstate = self.SEEK_STATE_PLAY
735 self.onPlayStateChanged = [ ]
737 self.lockedBecauseOfSkipping = False
739 self.__seekableStatusChanged()
741 def makeStateForward(self, n):
742 minspeed = config.seek.stepwise_minspeed.value
743 repeat = int(config.seek.stepwise_repeat.value)
744 if minspeed != "Never" and n >= int(minspeed) and repeat > 1:
745 return (0, n * repeat, repeat, ">> %dx" % n)
747 return (0, n, 0, ">> %dx" % n)
749 def makeStateBackward(self, n):
750 minspeed = config.seek.stepwise_minspeed.value
751 repeat = int(config.seek.stepwise_repeat.value)
752 if self.minSpeedBackward and n < self.minSpeedBackward:
753 r = (self.minSpeedBackward - 1)/ n + 1
754 if minspeed != "Never" and n >= int(minspeed) and repeat > 1:
756 return (0, -n * r, r, "<< %dx" % n)
757 elif minspeed != "Never" and n >= int(minspeed) and repeat > 1:
758 return (0, -n * repeat, repeat, "<< %dx" % n)
760 return (0, -n, 0, "<< %dx" % n)
762 def makeStateSlowMotion(self, n):
763 return (0, 0, n, "/%d" % n)
765 def isStateForward(self, state):
768 def isStateBackward(self, state):
771 def isStateSlowMotion(self, state):
772 return state[1] == 0 and state[2] > 1
774 def getHigher(self, n, lst):
780 def getLower(self, n, lst):
788 def showAfterSeek(self):
789 if isinstance(self, InfoBarShowHide):
799 service = self.session.nav.getCurrentService()
803 seek = service.seek()
805 if seek is None or not seek.isCurrentlySeekable():
810 def isSeekable(self):
811 if self.getSeek() is None:
815 def __seekableStatusChanged(self):
816 # print "seekable status changed!"
817 if not self.isSeekable():
818 self["SeekActions"].setEnabled(False)
819 # print "not seekable, return to play"
820 self.setSeekState(self.SEEK_STATE_PLAY)
822 self["SeekActions"].setEnabled(True)
825 def __serviceStarted(self):
826 self.seekstate = self.SEEK_STATE_PLAY
827 self.__seekableStatusChanged()
829 def setSeekState(self, state):
830 service = self.session.nav.getCurrentService()
835 if not self.isSeekable():
836 if state not in (self.SEEK_STATE_PLAY, self.SEEK_STATE_PAUSE):
837 state = self.SEEK_STATE_PLAY
839 pauseable = service.pause()
841 if pauseable is None:
842 print "not pauseable."
843 state = self.SEEK_STATE_PLAY
845 self.seekstate = state
847 if pauseable is not None:
848 if self.seekstate[0]:
849 print "resolved to PAUSE"
851 elif self.seekstate[1]:
852 print "resolved to FAST FORWARD"
853 pauseable.setFastForward(self.seekstate[1])
854 elif self.seekstate[2]:
855 print "resolved to SLOW MOTION"
856 pauseable.setSlowMotion(self.seekstate[2])
858 print "resolved to PLAY"
861 for c in self.onPlayStateChanged:
864 self.checkSkipShowHideLock()
868 def playpauseService(self):
869 if self.seekstate != self.SEEK_STATE_PLAY:
870 self.unPauseService()
874 def pauseService(self):
875 if self.seekstate == self.SEEK_STATE_PAUSE:
876 if config.seek.on_pause.value == "play":
877 self.unPauseService()
878 elif config.seek.on_pause.value == "step":
879 self.doSeekRelative(0)
880 elif config.seek.on_pause.value == "last":
881 self.setSeekState(self.lastseekstate)
882 self.lastseekstate = self.SEEK_STATE_PLAY
884 if self.seekstate != self.SEEK_STATE_EOF:
885 self.lastseekstate = self.seekstate
886 self.setSeekState(self.SEEK_STATE_PAUSE);
888 def unPauseService(self):
890 if self.seekstate == self.SEEK_STATE_PLAY:
892 self.setSeekState(self.SEEK_STATE_PLAY)
894 def doSeek(self, pts):
895 seekable = self.getSeek()
900 def doSeekRelative(self, pts):
901 seekable = self.getSeek()
904 prevstate = self.seekstate
906 if self.seekstate == self.SEEK_STATE_EOF:
907 if prevstate == self.SEEK_STATE_PAUSE:
908 self.setSeekState(self.SEEK_STATE_PAUSE)
910 self.setSeekState(self.SEEK_STATE_PLAY)
911 seekable.seekRelative(pts<0 and -1 or 1, abs(pts))
912 if abs(pts) > 100 and config.usage.show_infobar_on_skip.value:
916 if self.seekstate == self.SEEK_STATE_PLAY:
917 self.setSeekState(self.makeStateForward(int(config.seek.enter_forward.value)))
918 elif self.seekstate == self.SEEK_STATE_PAUSE:
919 if len(config.seek.speeds_slowmotion.value):
920 self.setSeekState(self.makeStateSlowMotion(config.seek.speeds_slowmotion.value[-1]))
922 self.setSeekState(self.makeStateForward(int(config.seek.enter_forward.value)))
923 elif self.seekstate == self.SEEK_STATE_EOF:
925 elif self.isStateForward(self.seekstate):
926 speed = self.seekstate[1]
927 if self.seekstate[2]:
928 speed /= self.seekstate[2]
929 speed = self.getHigher(speed, config.seek.speeds_forward.value) or config.seek.speeds_forward.value[-1]
930 self.setSeekState(self.makeStateForward(speed))
931 elif self.isStateBackward(self.seekstate):
932 speed = -self.seekstate[1]
933 if self.seekstate[2]:
934 speed /= self.seekstate[2]
935 speed = self.getLower(speed, config.seek.speeds_backward.value)
937 self.setSeekState(self.makeStateBackward(speed))
939 self.setSeekState(self.SEEK_STATE_PLAY)
940 elif self.isStateSlowMotion(self.seekstate):
941 speed = self.getLower(self.seekstate[2], config.seek.speeds_slowmotion.value) or config.seek.speeds_slowmotion.value[0]
942 self.setSeekState(self.makeStateSlowMotion(speed))
945 seekstate = self.seekstate
946 if seekstate == self.SEEK_STATE_PLAY:
947 self.setSeekState(self.makeStateBackward(int(config.seek.enter_backward.value)))
948 elif seekstate == self.SEEK_STATE_EOF:
949 self.setSeekState(self.makeStateBackward(int(config.seek.enter_backward.value)))
950 self.doSeekRelative(-6)
951 elif seekstate == self.SEEK_STATE_PAUSE:
952 self.doSeekRelative(-3)
953 elif self.isStateForward(seekstate):
956 speed /= seekstate[2]
957 speed = self.getLower(speed, config.seek.speeds_forward.value)
959 self.setSeekState(self.makeStateForward(speed))
961 self.setSeekState(self.SEEK_STATE_PLAY)
962 elif self.isStateBackward(seekstate):
963 speed = -seekstate[1]
965 speed /= seekstate[2]
966 speed = self.getHigher(speed, config.seek.speeds_backward.value) or config.seek.speeds_backward.value[-1]
967 self.setSeekState(self.makeStateBackward(speed))
968 elif self.isStateSlowMotion(seekstate):
969 speed = self.getHigher(seekstate[2], config.seek.speeds_slowmotion.value)
971 self.setSeekState(self.makeStateSlowMotion(speed))
973 self.setSeekState(self.SEEK_STATE_PAUSE)
975 def seekFwdManual(self):
976 self.session.openWithCallback(self.fwdSeekTo, MinuteInput)
978 def fwdSeekTo(self, minutes):
979 print "Seek", minutes, "minutes forward"
980 self.doSeekRelative(minutes * 60 * 90000)
982 def seekBackManual(self):
983 self.session.openWithCallback(self.rwdSeekTo, MinuteInput)
985 def rwdSeekTo(self, minutes):
987 self.doSeekRelative(-minutes * 60 * 90000)
989 def checkSkipShowHideLock(self):
990 wantlock = self.seekstate != self.SEEK_STATE_PLAY
992 if config.usage.show_infobar_on_skip.value:
993 if self.lockedBecauseOfSkipping and not wantlock:
995 self.lockedBecauseOfSkipping = False
997 if wantlock and not self.lockedBecauseOfSkipping:
999 self.lockedBecauseOfSkipping = True
1001 def calcRemainingTime(self):
1002 seekable = self.getSeek()
1003 if seekable is not None:
1004 len = seekable.getLength()
1006 tmp = self.cueGetEndCutPosition()
1011 pos = seekable.getPlayPosition()
1012 speednom = self.seekstate[1] or 1
1013 speedden = self.seekstate[2] or 1
1014 if not len[0] and not pos[0]:
1015 if len[1] <= pos[1]:
1017 time = (len[1] - pos[1])*speedden/(90*speednom)
1022 if self.seekstate == self.SEEK_STATE_EOF:
1025 # if we are seeking forward, we try to end up ~1s before the end, and pause there.
1026 seekstate = self.seekstate
1027 if self.seekstate != self.SEEK_STATE_PAUSE:
1028 self.setSeekState(self.SEEK_STATE_EOF)
1030 if seekstate not in (self.SEEK_STATE_PLAY, self.SEEK_STATE_PAUSE): # if we are seeking
1031 seekable = self.getSeek()
1032 if seekable is not None:
1034 if seekstate == self.SEEK_STATE_PLAY: # regular EOF
1035 self.doEofInternal(True)
1037 self.doEofInternal(False)
1039 def doEofInternal(self, playing):
1040 pass # Defined in subclasses
1043 self.setSeekState(self.SEEK_STATE_PLAY)
1046 from Screens.PVRState import PVRState, TimeshiftState
1048 class InfoBarPVRState:
1049 def __init__(self, screen=PVRState, force_show = False):
1050 self.onPlayStateChanged.append(self.__playStateChanged)
1051 self.pvrStateDialog = self.session.instantiateDialog(screen)
1052 self.onShow.append(self._mayShow)
1053 self.onHide.append(self.pvrStateDialog.hide)
1054 self.force_show = force_show
1057 if self.execing and self.seekstate != self.SEEK_STATE_PLAY:
1058 self.pvrStateDialog.show()
1060 def __playStateChanged(self, state):
1061 playstateString = state[3]
1062 self.pvrStateDialog["state"].setText(playstateString)
1064 # if we return into "PLAY" state, ensure that the dialog gets hidden if there will be no infobar displayed
1065 if not config.usage.show_infobar_on_skip.value and self.seekstate == self.SEEK_STATE_PLAY and not self.force_show:
1066 self.pvrStateDialog.hide()
1071 class InfoBarTimeshiftState(InfoBarPVRState):
1073 InfoBarPVRState.__init__(self, screen=TimeshiftState, force_show = True)
1076 if self.execing and self.timeshift_enabled and self.seekstate != self.SEEK_STATE_PLAY:
1077 self.pvrStateDialog.show()
1079 class InfoBarShowMovies:
1081 # i don't really like this class.
1082 # it calls a not further specified "movie list" on up/down/movieList,
1083 # so this is not more than an action map
1085 self["MovieListActions"] = HelpableActionMap(self, "InfobarMovieListActions",
1087 "movieList": (self.showMovies, _("movie list")),
1088 "up": (self.showMovies, _("movie list")),
1089 "down": (self.showMovies, _("movie list"))
1092 # InfoBarTimeshift requires InfoBarSeek, instantiated BEFORE!
1096 # Timeshift works the following way:
1097 # demux0 demux1 "TimeshiftActions" "TimeshiftActivateActions" "SeekActions"
1098 # - normal playback TUNER unused PLAY enable disable disable
1099 # - user presses "yellow" button. FILE record PAUSE enable disable enable
1100 # - user presess pause again FILE record PLAY enable disable enable
1101 # - user fast forwards FILE record FF enable disable enable
1102 # - end of timeshift buffer reached TUNER record PLAY enable enable disable
1103 # - user backwards FILE record BACK # !! enable disable enable
1107 # - when a service is playing, pressing the "timeshiftStart" button ("yellow") enables recording ("enables timeshift"),
1108 # freezes the picture (to indicate timeshift), sets timeshiftMode ("activates timeshift")
1109 # now, the service becomes seekable, so "SeekActions" are enabled, "TimeshiftEnableActions" are disabled.
1110 # - the user can now PVR around
1111 # - if it hits the end, the service goes into live mode ("deactivates timeshift", it's of course still "enabled")
1112 # the service looses it's "seekable" state. It can still be paused, but just to activate timeshift right
1114 # the seek actions will be disabled, but the timeshiftActivateActions will be enabled
1115 # - if the user rewinds, or press pause, timeshift will be activated again
1117 # note that a timeshift can be enabled ("recording") and
1118 # activated (currently time-shifting).
1120 class InfoBarTimeshift:
1122 self["TimeshiftActions"] = HelpableActionMap(self, "InfobarTimeshiftActions",
1124 "timeshiftStart": (self.startTimeshift, _("start timeshift")), # the "yellow key"
1125 "timeshiftStop": (self.stopTimeshift, _("stop timeshift")) # currently undefined :), probably 'TV'
1127 self["TimeshiftActivateActions"] = ActionMap(["InfobarTimeshiftActivateActions"],
1129 "timeshiftActivateEnd": self.activateTimeshiftEnd, # something like "rewind key"
1130 "timeshiftActivateEndAndPause": self.activateTimeshiftEndAndPause # something like "pause key"
1131 }, prio=-1) # priority over record
1133 self.timeshift_enabled = 0
1134 self.timeshift_state = 0
1135 self.ts_rewind_timer = eTimer()
1136 self.ts_rewind_timer.callback.append(self.rewindService)
1138 self.__event_tracker = ServiceEventTracker(screen=self, eventmap=
1140 iPlayableService.evStart: self.__serviceStarted,
1141 iPlayableService.evSeekableStatusChanged: self.__seekableStatusChanged
1144 def getTimeshift(self):
1145 service = self.session.nav.getCurrentService()
1146 return service and service.timeshift()
1148 def startTimeshift(self):
1149 print "enable timeshift"
1150 ts = self.getTimeshift()
1152 self.session.open(MessageBox, _("Timeshift not possible!"), MessageBox.TYPE_ERROR)
1153 print "no ts interface"
1156 if self.timeshift_enabled:
1157 print "hu, timeshift already enabled?"
1159 if not ts.startTimeshift():
1160 self.timeshift_enabled = 1
1162 # we remove the "relative time" for now.
1163 #self.pvrStateDialog["timeshift"].setRelative(time.time())
1166 #self.setSeekState(self.SEEK_STATE_PAUSE)
1167 self.activateTimeshiftEnd(False)
1169 # enable the "TimeshiftEnableActions", which will override
1170 # the startTimeshift actions
1171 self.__seekableStatusChanged()
1173 print "timeshift failed"
1175 def stopTimeshift(self):
1176 if not self.timeshift_enabled:
1178 print "disable timeshift"
1179 ts = self.getTimeshift()
1182 self.session.openWithCallback(self.stopTimeshiftConfirmed, MessageBox, _("Stop Timeshift?"), MessageBox.TYPE_YESNO)
1184 def stopTimeshiftConfirmed(self, confirmed):
1188 ts = self.getTimeshift()
1193 self.timeshift_enabled = 0
1196 self.__seekableStatusChanged()
1198 # activates timeshift, and seeks to (almost) the end
1199 def activateTimeshiftEnd(self, back = True):
1200 ts = self.getTimeshift()
1201 print "activateTimeshiftEnd"
1206 if ts.isTimeshiftActive():
1207 print "!! activate timeshift called - but shouldn't this be a normal pause?"
1211 ts.activateTimeshift() # activate timeshift will automatically pause
1212 self.setSeekState(self.SEEK_STATE_PAUSE)
1215 self.doSeek(-5) # seek some gops before end
1216 self.ts_rewind_timer.start(200, 1)
1218 self.doSeek(-1) # seek 1 gop before end
1220 def rewindService(self):
1221 self.setSeekState(self.makeStateBackward(int(config.seek.enter_backward.value)))
1223 # same as activateTimeshiftEnd, but pauses afterwards.
1224 def activateTimeshiftEndAndPause(self):
1225 print "activateTimeshiftEndAndPause"
1226 #state = self.seekstate
1227 self.activateTimeshiftEnd(False)
1229 def __seekableStatusChanged(self):
1232 # print "self.isSeekable", self.isSeekable()
1233 # print "self.timeshift_enabled", self.timeshift_enabled
1235 # when this service is not seekable, but timeshift
1236 # is enabled, this means we can activate
1238 if not self.isSeekable() and self.timeshift_enabled:
1241 # print "timeshift activate:", enabled
1242 self["TimeshiftActivateActions"].setEnabled(enabled)
1244 def __serviceStarted(self):
1245 self.timeshift_enabled = False
1246 self.__seekableStatusChanged()
1248 from Screens.PiPSetup import PiPSetup
1250 class InfoBarExtensions:
1251 EXTENSION_SINGLE = 0
1257 self["InstantExtensionsActions"] = HelpableActionMap(self, "InfobarExtensions",
1259 "extensions": (self.showExtensionSelection, _("view extensions...")),
1260 }, 1) # lower priority
1262 def addExtension(self, extension, key = None, type = EXTENSION_SINGLE):
1263 self.list.append((type, extension, key))
1265 def updateExtension(self, extension, key = None):
1266 self.extensionsList.append(extension)
1268 if self.extensionKeys.has_key(key):
1272 for x in self.availableKeys:
1273 if not self.extensionKeys.has_key(x):
1278 self.extensionKeys[key] = len(self.extensionsList) - 1
1280 def updateExtensions(self):
1281 self.extensionsList = []
1282 self.availableKeys = [ "1", "2", "3", "4", "5", "6", "7", "8", "9", "0", "red", "green", "yellow", "blue" ]
1283 self.extensionKeys = {}
1285 if x[0] == self.EXTENSION_SINGLE:
1286 self.updateExtension(x[1], x[2])
1289 self.updateExtension(y[0], y[1])
1292 def showExtensionSelection(self):
1293 self.updateExtensions()
1294 extensionsList = self.extensionsList[:]
1297 for x in self.availableKeys:
1298 if self.extensionKeys.has_key(x):
1299 entry = self.extensionKeys[x]
1300 extension = self.extensionsList[entry]
1302 name = str(extension[0]())
1303 list.append((extension[0](), extension))
1305 extensionsList.remove(extension)
1307 extensionsList.remove(extension)
1308 list.extend([(x[0](), x) for x in extensionsList])
1310 keys += [""] * len(extensionsList)
1311 self.session.openWithCallback(self.extensionCallback, ChoiceBox, title=_("Please choose an extension..."), list = list, keys = keys, skin_name = "ExtensionsList")
1313 def extensionCallback(self, answer):
1314 if answer is not None:
1317 from Tools.BoundFunction import boundFunction
1319 # depends on InfoBarExtensions
1321 class InfoBarPlugins:
1323 self.addExtension(extension = self.getPluginList, type = InfoBarExtensions.EXTENSION_LIST)
1325 def getPluginName(self, name):
1328 def getPluginList(self):
1329 list = [((boundFunction(self.getPluginName, p.name), boundFunction(self.runPlugin, p), lambda: True), None, p.name) for p in plugins.getPlugins(where = PluginDescriptor.WHERE_EXTENSIONSMENU)]
1330 list.sort(key = lambda e: e[2]) # sort by name
1333 def runPlugin(self, plugin):
1334 if isinstance(self, InfoBarChannelSelection):
1335 plugin(session = self.session, servicelist = self.servicelist)
1337 plugin(session = self.session)
1339 from Components.Task import job_manager
1340 class InfoBarJobman:
1342 self.addExtension(extension = self.getJobList, type = InfoBarExtensions.EXTENSION_LIST)
1344 def getJobList(self):
1345 return [((boundFunction(self.getJobName, job), boundFunction(self.showJobView, job), lambda: True), None) for job in job_manager.getPendingJobs()]
1347 def getJobName(self, job):
1348 return "%s: %s (%d%%)" % (job.getStatustext(), job.name, int(100*job.progress/float(job.end)))
1350 def showJobView(self, job):
1351 from Screens.TaskView import JobView
1352 job_manager.in_background = False
1353 self.session.openWithCallback(self.JobViewCB, JobView, job)
1355 def JobViewCB(self, in_background):
1356 job_manager.in_background = in_background
1358 # depends on InfoBarExtensions
1362 self.session.pipshown
1364 self.session.pipshown = False
1365 if SystemInfo.get("NumVideoDecoders", 1) > 1:
1367 self.addExtension((self.getShowHideName, self.showPiP, lambda: True), "blue")
1368 self.addExtension((self.getMoveName, self.movePiP, self.pipShown), "green")
1369 self.addExtension((self.getSwapName, self.swapPiP, self.pipShown), "yellow")
1371 self.addExtension((self.getShowHideName, self.showPiP, self.pipShown), "blue")
1372 self.addExtension((self.getMoveName, self.movePiP, self.pipShown), "green")
1375 return self.session.pipshown
1377 def pipHandles0Action(self):
1378 return self.pipShown() and config.usage.pip_zero_button.value != "standard"
1380 def getShowHideName(self):
1381 if self.session.pipshown:
1382 return _("Disable Picture in Picture")
1384 return _("Activate Picture in Picture")
1386 def getSwapName(self):
1387 return _("Swap Services")
1389 def getMoveName(self):
1390 return _("Move Picture in Picture")
1393 if self.session.pipshown:
1394 del self.session.pip
1395 self.session.pipshown = False
1397 self.session.pip = self.session.instantiateDialog(PictureInPicture)
1398 self.session.pip.show()
1399 newservice = self.session.nav.getCurrentlyPlayingServiceReference()
1400 if self.session.pip.playService(newservice):
1401 self.session.pipshown = True
1402 self.session.pip.servicePath = self.servicelist.getCurrentServicePath()
1404 self.session.pipshown = False
1405 del self.session.pip
1406 self.session.nav.playService(newservice)
1409 swapservice = self.session.nav.getCurrentlyPlayingServiceReference()
1410 if self.session.pip.servicePath:
1411 servicepath = self.servicelist.getCurrentServicePath()
1412 ref=servicepath[len(servicepath)-1]
1413 pipref=self.session.pip.getCurrentService()
1414 self.session.pip.playService(swapservice)
1415 self.servicelist.setCurrentServicePath(self.session.pip.servicePath)
1416 if pipref.toString() != ref.toString(): # is a subservice ?
1417 self.session.nav.stopService() # stop portal
1418 self.session.nav.playService(pipref) # start subservice
1419 self.session.pip.servicePath=servicepath
1422 self.session.open(PiPSetup, pip = self.session.pip)
1424 def pipDoHandle0Action(self):
1425 use = config.usage.pip_zero_button.value
1428 elif "swapstop" == use:
1434 from RecordTimer import parseEvent, RecordTimerEntry
1436 class InfoBarInstantRecord:
1437 """Instant Record - handles the instantRecord action in order to
1438 start/stop instant records"""
1440 self["InstantRecordActions"] = HelpableActionMap(self, "InfobarInstantRecord",
1442 "instantRecord": (self.instantRecord, _("Instant Record...")),
1446 def stopCurrentRecording(self, entry = -1):
1447 if entry is not None and entry != -1:
1448 self.session.nav.RecordTimer.removeEntry(self.recording[entry])
1449 self.recording.remove(self.recording[entry])
1451 def startInstantRecording(self, limitEvent = False):
1452 serviceref = self.session.nav.getCurrentlyPlayingServiceReference()
1454 # try to get event info
1457 service = self.session.nav.getCurrentService()
1458 epg = eEPGCache.getInstance()
1459 event = epg.lookupEventTime(serviceref, -1, 0)
1461 info = service.info()
1462 ev = info.getEvent(0)
1468 end = begin + 3600 # dummy
1469 name = "instant record"
1473 if event is not None:
1474 curEvent = parseEvent(event)
1476 description = curEvent[3]
1477 eventid = curEvent[4]
1482 self.session.open(MessageBox, _("No event info found, recording indefinitely."), MessageBox.TYPE_INFO)
1484 if isinstance(serviceref, eServiceReference):
1485 serviceref = ServiceReference(serviceref)
1487 recording = RecordTimerEntry(serviceref, begin, end, name, description, eventid, dirname = config.movielist.last_videodir.value)
1488 recording.dontSave = True
1490 if event is None or limitEvent == False:
1491 recording.autoincrease = True
1492 if recording.setAutoincreaseEnd():
1493 self.session.nav.RecordTimer.record(recording)
1494 self.recording.append(recording)
1496 simulTimerList = self.session.nav.RecordTimer.record(recording)
1497 if simulTimerList is not None: # conflict with other recording
1498 name = simulTimerList[1].name
1499 name_date = ' '.join((name, strftime('%c', localtime(simulTimerList[1].begin))))
1500 print "[TIMER] conflicts with", name_date
1501 recording.autoincrease = True # start with max available length, then increment
1502 if recording.setAutoincreaseEnd():
1503 self.session.nav.RecordTimer.record(recording)
1504 self.recording.append(recording)
1505 self.session.open(MessageBox, _("Record time limited due to conflicting timer %s") % name_date, MessageBox.TYPE_INFO)
1507 self.session.open(MessageBox, _("Couldn't record due to conflicting timer %s") % name, MessageBox.TYPE_INFO)
1508 recording.autoincrease = False
1510 self.recording.append(recording)
1512 def isInstantRecordRunning(self):
1513 print "self.recording:", self.recording
1515 for x in self.recording:
1520 def recordQuestionCallback(self, answer):
1521 print "pre:\n", self.recording
1523 if answer is None or answer[1] == "no":
1526 recording = self.recording[:]
1528 if not x in self.session.nav.RecordTimer.timer_list:
1529 self.recording.remove(x)
1530 elif x.dontSave and x.isRunning():
1531 list.append((x, False))
1533 if answer[1] == "changeduration":
1534 if len(self.recording) == 1:
1535 self.changeDuration(0)
1537 self.session.openWithCallback(self.changeDuration, TimerSelection, list)
1538 elif answer[1] == "changeendtime":
1539 if len(self.recording) == 1:
1542 self.session.openWithCallback(self.setEndtime, TimerSelection, list)
1543 elif answer[1] == "stop":
1544 if len(self.recording) == 1:
1545 self.stopCurrentRecording(0)
1547 self.session.openWithCallback(self.stopCurrentRecording, TimerSelection, list)
1548 elif answer[1] in ( "indefinitely" , "manualduration", "manualendtime", "event"):
1549 self.startInstantRecording(limitEvent = answer[1] in ("event", "manualendtime") or False)
1550 if answer[1] == "manualduration":
1551 self.changeDuration(len(self.recording)-1)
1552 elif answer[1] == "manualendtime":
1553 self.setEndtime(len(self.recording)-1)
1554 print "after:\n", self.recording
1556 def setEndtime(self, entry):
1557 if entry is not None and entry >= 0:
1558 self.selectedEntry = entry
1559 self.endtime=ConfigClock(default = self.recording[self.selectedEntry].end)
1560 dlg = self.session.openWithCallback(self.TimeDateInputClosed, TimeDateInput, self.endtime)
1561 dlg.setTitle(_("Please change recording endtime"))
1563 def TimeDateInputClosed(self, ret):
1566 localendtime = localtime(ret[1])
1567 print "stopping recording at", strftime("%c", localendtime)
1568 if self.recording[self.selectedEntry].end != ret[1]:
1569 self.recording[self.selectedEntry].autoincrease = False
1570 self.recording[self.selectedEntry].end = ret[1]
1571 self.session.nav.RecordTimer.timeChanged(self.recording[self.selectedEntry])
1573 def changeDuration(self, entry):
1574 if entry is not None and entry >= 0:
1575 self.selectedEntry = entry
1576 self.session.openWithCallback(self.inputCallback, InputBox, title=_("How many minutes do you want to record?"), text="5", maxSize=False, type=Input.NUMBER)
1578 def inputCallback(self, value):
1579 if value is not None:
1580 print "stopping recording after", int(value), "minutes."
1581 entry = self.recording[self.selectedEntry]
1583 entry.autoincrease = False
1584 entry.end = int(time()) + 60 * int(value)
1585 self.session.nav.RecordTimer.timeChanged(entry)
1587 def instantRecord(self):
1588 dir = config.movielist.last_videodir.value
1589 if not fileExists(dir, 'w'):
1590 dir = resolveFilename(SCOPE_HDD)
1594 # XXX: this message is a little odd as we might be recording to a remote device
1595 self.session.open(MessageBox, _("No HDD found or HDD not initialized!"), MessageBox.TYPE_ERROR)
1598 if self.isInstantRecordRunning():
1599 self.session.openWithCallback(self.recordQuestionCallback, ChoiceBox, \
1600 title=_("A recording is currently running.\nWhat do you want to do?"), \
1601 list=((_("stop recording"), "stop"), \
1602 (_("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 (_("change recording (duration)"), "changeduration"), \
1607 (_("change recording (endtime)"), "changeendtime"), \
1608 (_("do nothing"), "no")))
1610 self.session.openWithCallback(self.recordQuestionCallback, ChoiceBox, \
1611 title=_("Start recording?"), \
1612 list=((_("add recording (stop after current event)"), "event"), \
1613 (_("add recording (indefinitely)"), "indefinitely"), \
1614 (_("add recording (enter recording duration)"), "manualduration"), \
1615 (_("add recording (enter recording endtime)"), "manualendtime"), \
1616 (_("don't record"), "no")))
1618 from Tools.ISO639 import LanguageCodes
1620 class InfoBarAudioSelection:
1622 self["AudioSelectionAction"] = HelpableActionMap(self, "InfobarAudioSelectionActions",
1624 "audioSelection": (self.audioSelection, _("Audio Options...")),
1627 def audioSelection(self):
1628 service = self.session.nav.getCurrentService()
1629 self.audioTracks = audio = service and service.audioTracks()
1630 n = audio and audio.getNumberOfTracks() or 0
1633 self.audioChannel = service.audioChannel()
1638 i = audio.getTrackInfo(idx)
1639 languages = i.getLanguage().split('/')
1640 description = i.getDescription()
1643 for lang in languages:
1646 if LanguageCodes.has_key(lang):
1647 language += LanguageCodes[lang][0]
1652 if len(description):
1653 description += " (" + language + ")"
1655 description = language
1657 tlist.append((description, idx))
1660 tlist.sort(key=lambda x: x[0])
1662 selectedAudio = self.audioTracks.getCurrentTrack()
1667 if x[1] != selectedAudio:
1672 if SystemInfo["CanDownmixAC3"]:
1673 tlist = [(_("AC3 downmix") + " - " +(_("Off"), _("On"))[config.av.downmix_ac3.value and 1 or 0], "CALLFUNC", self.changeAC3Downmix),
1674 ((_("Left"), _("Stereo"), _("Right"))[self.audioChannel.getCurrentChannel()], "mode"),
1676 keys = [ "red", "green", "", "1", "2", "3", "4", "5", "6", "7", "8", "9", "0"] + [""]*n
1679 tlist = [((_("Left"), _("Stereo"), _("Right"))[self.audioChannel.getCurrentChannel()], "mode"), ("--", "")] + tlist
1680 keys = [ "red", "", "1", "2", "3", "4", "5", "6", "7", "8", "9", "0"] + [""]*n
1682 self.session.openWithCallback(self.audioSelected, ChoiceBox, title=_("Select audio track"), list = tlist, selection = selection, keys = keys, skin_name = "AudioTrackSelection")
1684 del self.audioTracks
1686 def changeAC3Downmix(self, arg):
1687 choicelist = self.session.current_dialog["list"]
1688 list = choicelist.list
1690 list[0][1]=(t[0], t[1], t[2], t[3], t[4], t[5], t[6],
1691 _("AC3 downmix") + " - " + (_("On"), _("Off"))[config.av.downmix_ac3.value and 1 or 0])
1692 choicelist.setList(list)
1693 if config.av.downmix_ac3.value:
1694 config.av.downmix_ac3.value = False
1696 config.av.downmix_ac3.value = True
1697 config.av.downmix_ac3.save()
1699 def audioSelected(self, audio):
1700 if audio is not None:
1701 if isinstance(audio[1], str):
1702 if audio[1] == "mode":
1703 keys = ["red", "green", "yellow"]
1704 selection = self.audioChannel.getCurrentChannel()
1705 tlist = ((_("left"), 0), (_("stereo"), 1), (_("right"), 2))
1706 self.session.openWithCallback(self.modeSelected, ChoiceBox, title=_("Select audio mode"), list = tlist, selection = selection, keys = keys, skin_name ="AudioModeSelection")
1708 del self.audioChannel
1709 if self.session.nav.getCurrentService().audioTracks().getNumberOfTracks() > audio[1]:
1710 self.audioTracks.selectTrack(audio[1])
1712 del self.audioChannel
1713 del self.audioTracks
1715 def modeSelected(self, mode):
1716 if mode is not None:
1717 self.audioChannel.selectChannel(mode[1])
1718 del self.audioChannel
1720 class InfoBarSubserviceSelection:
1722 self["SubserviceSelectionAction"] = HelpableActionMap(self, "InfobarSubserviceSelectionActions",
1724 "subserviceSelection": (self.subserviceSelection, _("Subservice list...")),
1727 self["SubserviceQuickzapAction"] = HelpableActionMap(self, "InfobarSubserviceQuickzapActions",
1729 "nextSubservice": (self.nextSubservice, _("Switch to next subservice")),
1730 "prevSubservice": (self.prevSubservice, _("Switch to previous subservice"))
1732 self["SubserviceQuickzapAction"].setEnabled(False)
1734 self.__event_tracker = ServiceEventTracker(screen=self, eventmap=
1736 iPlayableService.evUpdatedEventInfo: self.checkSubservicesAvail
1741 def checkSubservicesAvail(self):
1742 service = self.session.nav.getCurrentService()
1743 subservices = service and service.subServices()
1744 if not subservices or subservices.getNumberOfSubservices() == 0:
1745 self["SubserviceQuickzapAction"].setEnabled(False)
1747 def nextSubservice(self):
1748 self.changeSubservice(+1)
1750 def prevSubservice(self):
1751 self.changeSubservice(-1)
1753 def changeSubservice(self, direction):
1754 service = self.session.nav.getCurrentService()
1755 subservices = service and service.subServices()
1756 n = subservices and subservices.getNumberOfSubservices()
1759 ref = self.session.nav.getCurrentlyPlayingServiceReference()
1762 if subservices.getSubservice(idx).toString() == ref.toString():
1767 selection += direction
1772 newservice = subservices.getSubservice(selection)
1773 if newservice.valid():
1776 self.session.nav.playService(newservice, False)
1778 def subserviceSelection(self):
1779 service = self.session.nav.getCurrentService()
1780 subservices = service and service.subServices()
1781 self.bouquets = self.servicelist.getBouquetList()
1782 n = subservices and subservices.getNumberOfSubservices()
1785 ref = self.session.nav.getCurrentlyPlayingServiceReference()
1789 i = subservices.getSubservice(idx)
1790 if i.toString() == ref.toString():
1792 tlist.append((i.getName(), i))
1795 if self.bouquets and len(self.bouquets):
1796 keys = ["red", "blue", "", "0", "1", "2", "3", "4", "5", "6", "7", "8", "9" ] + [""] * n
1797 if config.usage.multibouquet.value:
1798 tlist = [(_("Quickzap"), "quickzap", service.subServices()), (_("Add to bouquet"), "CALLFUNC", self.addSubserviceToBouquetCallback), ("--", "")] + tlist
1800 tlist = [(_("Quickzap"), "quickzap", service.subServices()), (_("Add to favourites"), "CALLFUNC", self.addSubserviceToBouquetCallback), ("--", "")] + tlist
1803 tlist = [(_("Quickzap"), "quickzap", service.subServices()), ("--", "")] + tlist
1804 keys = ["red", "", "0", "1", "2", "3", "4", "5", "6", "7", "8", "9" ] + [""] * n
1807 self.session.openWithCallback(self.subserviceSelected, ChoiceBox, title=_("Please select a subservice..."), list = tlist, selection = selection, keys = keys, skin_name = "SubserviceSelection")
1809 def subserviceSelected(self, service):
1811 if not service is None:
1812 if isinstance(service[1], str):
1813 if service[1] == "quickzap":
1814 from Screens.SubservicesQuickzap import SubservicesQuickzap
1815 self.session.open(SubservicesQuickzap, service[2])
1817 self["SubserviceQuickzapAction"].setEnabled(True)
1818 self.session.nav.playService(service[1], False)
1820 def addSubserviceToBouquetCallback(self, service):
1821 if len(service) > 1 and isinstance(service[1], eServiceReference):
1822 self.selectedSubservice = service
1823 if self.bouquets is None:
1826 cnt = len(self.bouquets)
1827 if cnt > 1: # show bouquet list
1828 self.bsel = self.session.openWithCallback(self.bouquetSelClosed, BouquetSelector, self.bouquets, self.addSubserviceToBouquet)
1829 elif cnt == 1: # add to only one existing bouquet
1830 self.addSubserviceToBouquet(self.bouquets[0][1])
1831 self.session.open(MessageBox, _("Service has been added to the favourites."), MessageBox.TYPE_INFO)
1833 def bouquetSelClosed(self, confirmed):
1835 del self.selectedSubservice
1837 self.session.open(MessageBox, _("Service has been added to the selected bouquet."), MessageBox.TYPE_INFO)
1839 def addSubserviceToBouquet(self, dest):
1840 self.servicelist.addServiceToBouquet(dest, self.selectedSubservice[1])
1842 self.bsel.close(True)
1844 del self.selectedSubservice
1846 class InfoBarAdditionalInfo:
1849 self["RecordingPossible"] = Boolean(fixed=harddiskmanager.HDDCount() > 0 and config.misc.rcused.value == 1)
1850 self["TimeshiftPossible"] = self["RecordingPossible"]
1851 self["ShowTimeshiftOnYellow"] = Boolean(fixed=(not config.misc.rcused.value == 0))
1852 self["ShowAudioOnYellow"] = Boolean(fixed=config.misc.rcused.value == 0)
1853 self["ShowRecordOnRed"] = Boolean(fixed=config.misc.rcused.value == 1)
1854 self["ExtensionsAvailable"] = Boolean(fixed=1)
1856 class InfoBarNotifications:
1858 self.onExecBegin.append(self.checkNotifications)
1859 Notifications.notificationAdded.append(self.checkNotificationsIfExecing)
1860 self.onClose.append(self.__removeNotification)
1862 def __removeNotification(self):
1863 Notifications.notificationAdded.remove(self.checkNotificationsIfExecing)
1865 def checkNotificationsIfExecing(self):
1867 self.checkNotifications()
1869 def checkNotifications(self):
1870 notifications = Notifications.notifications
1872 n = notifications[0]
1874 del notifications[0]
1877 if n[3].has_key("onSessionOpenCallback"):
1878 n[3]["onSessionOpenCallback"]()
1879 del n[3]["onSessionOpenCallback"]
1882 dlg = self.session.openWithCallback(cb, n[1], *n[2], **n[3])
1884 dlg = self.session.open(n[1], *n[2], **n[3])
1886 # remember that this notification is currently active
1888 Notifications.current_notifications.append(d)
1889 dlg.onClose.append(boundFunction(self.__notificationClosed, d))
1891 def __notificationClosed(self, d):
1892 Notifications.current_notifications.remove(d)
1894 class InfoBarServiceNotifications:
1896 self.__event_tracker = ServiceEventTracker(screen=self, eventmap=
1898 iPlayableService.evEnd: self.serviceHasEnded
1901 def serviceHasEnded(self):
1902 print "service end!"
1905 self.setSeekState(self.SEEK_STATE_PLAY)
1909 class InfoBarCueSheetSupport:
1915 ENABLE_RESUME_SUPPORT = False
1917 def __init__(self, actionmap = "InfobarCueSheetActions"):
1918 self["CueSheetActions"] = HelpableActionMap(self, actionmap,
1920 "jumpPreviousMark": (self.jumpPreviousMark, _("jump to previous marked position")),
1921 "jumpNextMark": (self.jumpNextMark, _("jump to next marked position")),
1922 "toggleMark": (self.toggleMark, _("toggle a cut mark at the current position"))
1926 self.is_closing = False
1927 self.__event_tracker = ServiceEventTracker(screen=self, eventmap=
1929 iPlayableService.evStart: self.__serviceStarted,
1932 def __serviceStarted(self):
1935 print "new service started! trying to download cuts!"
1936 self.downloadCuesheet()
1938 if self.ENABLE_RESUME_SUPPORT:
1941 for (pts, what) in self.cut_list:
1942 if what == self.CUT_TYPE_LAST:
1945 if last is not None:
1946 self.resume_point = last
1947 if config.usage.on_movie_start.value == "ask":
1948 Notifications.AddNotificationWithCallback(self.playLastCB, MessageBox, _("Do you want to resume this playback?"), timeout=10)
1949 elif config.usage.on_movie_start.value == "resume":
1950 # TRANSLATORS: The string "Resuming playback" flashes for a moment
1951 # TRANSLATORS: at the start of a movie, when the user has selected
1952 # TRANSLATORS: "Resume from last position" as start behavior.
1953 # TRANSLATORS: The purpose is to notify the user that the movie starts
1954 # TRANSLATORS: in the middle somewhere and not from the beginning.
1955 # TRANSLATORS: (Some translators seem to have interpreted it as a
1956 # TRANSLATORS: question or a choice, but it is a statement.)
1957 Notifications.AddNotificationWithCallback(self.playLastCB, MessageBox, _("Resuming playback"), timeout=2, type=MessageBox.TYPE_INFO)
1959 def playLastCB(self, answer):
1961 self.doSeek(self.resume_point)
1962 self.hideAfterResume()
1964 def hideAfterResume(self):
1965 if isinstance(self, InfoBarShowHide):
1968 def __getSeekable(self):
1969 service = self.session.nav.getCurrentService()
1972 return service.seek()
1974 def cueGetCurrentPosition(self):
1975 seek = self.__getSeekable()
1978 r = seek.getPlayPosition()
1983 def cueGetEndCutPosition(self):
1986 for cp in self.cut_list:
1987 if cp[1] == self.CUT_TYPE_OUT:
1991 elif cp[1] == self.CUT_TYPE_IN:
1995 def jumpPreviousNextMark(self, cmp, start=False):
1996 current_pos = self.cueGetCurrentPosition()
1997 if current_pos is None:
1999 mark = self.getNearestCutPoint(current_pos, cmp=cmp, start=start)
2000 if mark is not None:
2008 def jumpPreviousMark(self):
2009 # we add 2 seconds, so if the play position is <2s after
2010 # the mark, the mark before will be used
2011 self.jumpPreviousNextMark(lambda x: -x-5*90000, start=True)
2013 def jumpNextMark(self):
2014 if not self.jumpPreviousNextMark(lambda x: x):
2017 def getNearestCutPoint(self, pts, cmp=abs, start=False):
2023 bestdiff = cmp(0 - pts)
2025 nearest = [0, False]
2026 for cp in self.cut_list:
2027 if beforecut and cp[1] in (self.CUT_TYPE_IN, self.CUT_TYPE_OUT):
2029 if cp[1] == self.CUT_TYPE_IN: # Start is here, disregard previous marks
2030 diff = cmp(cp[0] - pts)
2036 if cp[1] in (self.CUT_TYPE_MARK, self.CUT_TYPE_LAST):
2037 diff = cmp(cp[0] - pts)
2038 if diff >= 0 and (nearest is None or bestdiff > diff):
2043 def toggleMark(self, onlyremove=False, onlyadd=False, tolerance=5*90000, onlyreturn=False):
2044 current_pos = self.cueGetCurrentPosition()
2045 if current_pos is None:
2046 print "not seekable"
2049 nearest_cutpoint = self.getNearestCutPoint(current_pos)
2051 if nearest_cutpoint is not None and abs(nearest_cutpoint[0] - current_pos) < tolerance:
2053 return nearest_cutpoint
2055 self.removeMark(nearest_cutpoint)
2056 elif not onlyremove and not onlyreturn:
2057 self.addMark((current_pos, self.CUT_TYPE_MARK))
2062 def addMark(self, point):
2063 insort(self.cut_list, point)
2064 self.uploadCuesheet()
2065 self.showAfterCuesheetOperation()
2067 def removeMark(self, point):
2068 self.cut_list.remove(point)
2069 self.uploadCuesheet()
2070 self.showAfterCuesheetOperation()
2072 def showAfterCuesheetOperation(self):
2073 if isinstance(self, InfoBarShowHide):
2076 def __getCuesheet(self):
2077 service = self.session.nav.getCurrentService()
2080 return service.cueSheet()
2082 def uploadCuesheet(self):
2083 cue = self.__getCuesheet()
2086 print "upload failed, no cuesheet interface"
2088 cue.setCutList(self.cut_list)
2090 def downloadCuesheet(self):
2091 cue = self.__getCuesheet()
2094 print "download failed, no cuesheet interface"
2097 self.cut_list = cue.getCutList()
2099 class InfoBarSummary(Screen):
2101 <screen position="0,0" size="132,64">
2102 <widget source="global.CurrentTime" render="Label" position="62,46" size="82,18" font="Regular;16" >
2103 <convert type="ClockToText">WithSeconds</convert>
2105 <widget source="session.RecordState" render="FixedLabel" text=" " position="62,46" size="82,18" zPosition="1" >
2106 <convert type="ConfigEntryTest">config.usage.blinking_display_clock_during_recording,True,CheckSourceBoolean</convert>
2107 <convert type="ConditionalShowHide">Blink</convert>
2109 <widget source="session.CurrentService" render="Label" position="6,4" size="120,42" font="Regular;18" >
2110 <convert type="ServiceName">Name</convert>
2112 <widget source="session.Event_Now" render="Progress" position="6,46" size="46,18" borderWidth="1" >
2113 <convert type="EventTime">Progress</convert>
2117 # for picon: (path="piconlcd" will use LCD picons)
2118 # <widget source="session.CurrentService" render="Picon" position="6,0" size="120,64" path="piconlcd" >
2119 # <convert type="ServiceName">Reference</convert>
2122 class InfoBarSummarySupport:
2126 def createSummary(self):
2127 return InfoBarSummary
2129 class InfoBarMoviePlayerSummary(Screen):
2131 <screen position="0,0" size="132,64">
2132 <widget source="global.CurrentTime" render="Label" position="62,46" size="64,18" font="Regular;16" halign="right" >
2133 <convert type="ClockToText">WithSeconds</convert>
2135 <widget source="session.RecordState" render="FixedLabel" text=" " position="62,46" size="64,18" zPosition="1" >
2136 <convert type="ConfigEntryTest">config.usage.blinking_display_clock_during_recording,True,CheckSourceBoolean</convert>
2137 <convert type="ConditionalShowHide">Blink</convert>
2139 <widget source="session.CurrentService" render="Label" position="6,4" size="120,42" font="Regular;18" >
2140 <convert type="ServiceName">Name</convert>
2142 <widget source="session.CurrentService" render="Progress" position="6,46" size="56,18" borderWidth="1" >
2143 <convert type="ServicePosition">Position</convert>
2147 class InfoBarMoviePlayerSummarySupport:
2151 def createSummary(self):
2152 return InfoBarMoviePlayerSummary
2154 class InfoBarTeletextPlugin:
2156 self.teletext_plugin = None
2158 for p in plugins.getPlugins(PluginDescriptor.WHERE_TELETEXT):
2159 self.teletext_plugin = p
2161 if self.teletext_plugin is not None:
2162 self["TeletextActions"] = HelpableActionMap(self, "InfobarTeletextActions",
2164 "startTeletext": (self.startTeletext, _("View teletext..."))
2167 print "no teletext plugin found!"
2169 def startTeletext(self):
2170 self.teletext_plugin(session=self.session, service=self.session.nav.getCurrentService())
2172 class InfoBarSubtitleSupport(object):
2174 object.__init__(self)
2175 self.subtitle_window = self.session.instantiateDialog(SubtitleDisplay)
2176 self.__subtitles_enabled = False
2178 self.__event_tracker = ServiceEventTracker(screen=self, eventmap=
2180 iPlayableService.evEnd: self.__serviceStopped,
2181 iPlayableService.evUpdatedInfo: self.__updatedInfo
2183 self.cached_subtitle_checked = False
2184 self.__selected_subtitle = None
2186 def __serviceStopped(self):
2187 self.cached_subtitle_checked = False
2188 if self.__subtitles_enabled:
2189 self.subtitle_window.hide()
2190 self.__subtitles_enabled = False
2191 self.__selected_subtitle = None
2193 def __updatedInfo(self):
2194 if not self.cached_subtitle_checked:
2195 self.cached_subtitle_checked = True
2196 subtitle = self.getCurrentServiceSubtitle()
2197 self.setSelectedSubtitle(subtitle and subtitle.getCachedSubtitle())
2198 if self.__selected_subtitle:
2199 self.setSubtitlesEnable(True)
2201 def getCurrentServiceSubtitle(self):
2202 service = self.session.nav.getCurrentService()
2203 return service and service.subtitle()
2205 def setSubtitlesEnable(self, enable=True):
2206 subtitle = self.getCurrentServiceSubtitle()
2208 if self.__selected_subtitle:
2209 if subtitle and not self.__subtitles_enabled:
2210 subtitle.enableSubtitles(self.subtitle_window.instance, self.selected_subtitle)
2211 self.subtitle_window.show()
2212 self.__subtitles_enabled = True
2215 subtitle.disableSubtitles(self.subtitle_window.instance)
2216 self.__selected_subtitle = False
2217 self.__subtitles_enabled = False
2218 self.subtitle_window.hide()
2220 def setSelectedSubtitle(self, subtitle):
2221 self.__selected_subtitle = subtitle
2223 subtitles_enabled = property(lambda self: self.__subtitles_enabled, setSubtitlesEnable)
2224 selected_subtitle = property(lambda self: self.__selected_subtitle, setSelectedSubtitle)
2226 class InfoBarServiceErrorPopupSupport:
2228 self.__event_tracker = ServiceEventTracker(screen=self, eventmap=
2230 iPlayableService.evTuneFailed: self.__tuneFailed,
2231 iPlayableService.evStart: self.__serviceStarted
2233 self.__serviceStarted()
2235 def __serviceStarted(self):
2236 self.last_error = None
2237 Notifications.RemovePopup(id = "ZapError")
2239 def __tuneFailed(self):
2240 service = self.session.nav.getCurrentService()
2241 info = service and service.info()
2242 error = info and info.getInfo(iServiceInformation.sDVBState)
2244 if error == self.last_error:
2247 self.last_error = error
2250 eDVBServicePMTHandler.eventNoResources: _("No free tuner!"),
2251 eDVBServicePMTHandler.eventTuneFailed: _("Tune failed!"),
2252 eDVBServicePMTHandler.eventNoPAT: _("No data on transponder!\n(Timeout reading PAT)"),
2253 eDVBServicePMTHandler.eventNoPATEntry: _("Service not found!\n(SID not found in PAT)"),
2254 eDVBServicePMTHandler.eventNoPMT: _("Service invalid!\n(Timeout reading PMT)"),
2255 eDVBServicePMTHandler.eventNewProgramInfo: None,
2256 eDVBServicePMTHandler.eventTuned: None,
2257 eDVBServicePMTHandler.eventSOF: None,
2258 eDVBServicePMTHandler.eventEOF: None,
2259 eDVBServicePMTHandler.eventMisconfiguration: _("Service unavailable!\nCheck tuner configuration!"),
2260 }.get(error) #this returns None when the key not exist in the dict
2262 if error is not None:
2263 Notifications.AddPopup(text = error, type = MessageBox.TYPE_ERROR, timeout = 5, id = "ZapError")
2265 Notifications.RemovePopup(id = "ZapError")